From 163f9eb95a10371ead59b62087e0d4e7ce53112e Mon Sep 17 00:00:00 2001 From: David Howells Date: Wed, 21 Jan 2015 20:03:40 +0000 Subject: debugfs: Provide a file creation function that also takes an initial size Provide a file creation function that also takes an initial size so that the caller doesn't have to set i_size, thus meaning that we don't have to call deal with ->d_inode in the callers. Signed-off-by: David Howells --- include/linux/debugfs.h | 13 +++++++++++++ 1 file changed, 13 insertions(+) (limited to 'include') diff --git a/include/linux/debugfs.h b/include/linux/debugfs.h index ea149a24a1f2..cb25af461054 100644 --- a/include/linux/debugfs.h +++ b/include/linux/debugfs.h @@ -51,6 +51,11 @@ struct dentry *debugfs_create_file(const char *name, umode_t mode, struct dentry *parent, void *data, const struct file_operations *fops); +struct dentry *debugfs_create_file_size(const char *name, umode_t mode, + struct dentry *parent, void *data, + const struct file_operations *fops, + loff_t file_size); + struct dentry *debugfs_create_dir(const char *name, struct dentry *parent); struct dentry *debugfs_create_symlink(const char *name, struct dentry *parent, @@ -129,6 +134,14 @@ static inline struct dentry *debugfs_create_file(const char *name, umode_t mode, return ERR_PTR(-ENODEV); } +static inline struct dentry *debugfs_create_file_size(const char *name, umode_t mode, + struct dentry *parent, void *data, + const struct file_operations *fops, + loff_t file_size) +{ + return ERR_PTR(-ENODEV); +} + static inline struct dentry *debugfs_create_dir(const char *name, struct dentry *parent) { -- cgit v1.2.3 From 4282d60689d4f21b40692029080440cc58e8a17d Mon Sep 17 00:00:00 2001 From: "Steven Rostedt (Red Hat)" Date: Tue, 20 Jan 2015 11:36:55 -0500 Subject: tracefs: Add new tracefs file system Add a separate file system to handle the tracing directory. Currently it is part of debugfs, but that is starting to show its limits. One thing is that in order to access the tracing infrastructure, you need to mount debugfs. As that includes debugging from all sorts of sub systems in the kernel, it is not considered advisable to mount such an all encompassing debugging system. Having the tracing system in its own file systems gives access to the tracing sub system without needing to include all other systems. Another problem with tracing using the debugfs system is that the instances use mkdir to create sub buffers. debugfs does not support mkdir from userspace so to implement it, special hacks were used. By controlling the file system that the tracing infrastructure uses, this can be properly done without hacks. Signed-off-by: Steven Rostedt --- fs/Makefile | 1 + fs/tracefs/Makefile | 4 + fs/tracefs/inode.c | 522 +++++++++++++++++++++++++++++++++++++++++++++ include/linux/tracefs.h | 41 ++++ include/uapi/linux/magic.h | 2 + 5 files changed, 570 insertions(+) create mode 100644 fs/tracefs/Makefile create mode 100644 fs/tracefs/inode.c create mode 100644 include/linux/tracefs.h (limited to 'include') diff --git a/fs/Makefile b/fs/Makefile index bedff48e8fdc..d244b8d973ac 100644 --- a/fs/Makefile +++ b/fs/Makefile @@ -118,6 +118,7 @@ obj-$(CONFIG_HOSTFS) += hostfs/ obj-$(CONFIG_HPPFS) += hppfs/ obj-$(CONFIG_CACHEFILES) += cachefiles/ obj-$(CONFIG_DEBUG_FS) += debugfs/ +obj-$(CONFIG_TRACING) += tracefs/ obj-$(CONFIG_OCFS2_FS) += ocfs2/ obj-$(CONFIG_BTRFS_FS) += btrfs/ obj-$(CONFIG_GFS2_FS) += gfs2/ diff --git a/fs/tracefs/Makefile b/fs/tracefs/Makefile new file mode 100644 index 000000000000..82fa35b656c4 --- /dev/null +++ b/fs/tracefs/Makefile @@ -0,0 +1,4 @@ +tracefs-objs := inode.o + +obj-$(CONFIG_TRACING) += tracefs.o + diff --git a/fs/tracefs/inode.c b/fs/tracefs/inode.c new file mode 100644 index 000000000000..5b1547a452d8 --- /dev/null +++ b/fs/tracefs/inode.c @@ -0,0 +1,522 @@ +/* + * inode.c - part of tracefs, a pseudo file system for activating tracing + * + * Based on debugfs by: Greg Kroah-Hartman + * + * Copyright (C) 2014 Red Hat Inc, author: Steven Rostedt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version + * 2 as published by the Free Software Foundation. + * + * tracefs is the file system that is used by the tracing infrastructure. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define TRACEFS_DEFAULT_MODE 0700 + +static struct vfsmount *tracefs_mount; +static int tracefs_mount_count; +static bool tracefs_registered; + +static ssize_t default_read_file(struct file *file, char __user *buf, + size_t count, loff_t *ppos) +{ + return 0; +} + +static ssize_t default_write_file(struct file *file, const char __user *buf, + size_t count, loff_t *ppos) +{ + return count; +} + +static const struct file_operations tracefs_file_operations = { + .read = default_read_file, + .write = default_write_file, + .open = simple_open, + .llseek = noop_llseek, +}; + +static struct inode *tracefs_get_inode(struct super_block *sb) +{ + struct inode *inode = new_inode(sb); + if (inode) { + inode->i_ino = get_next_ino(); + inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME; + } + return inode; +} + +struct tracefs_mount_opts { + kuid_t uid; + kgid_t gid; + umode_t mode; +}; + +enum { + Opt_uid, + Opt_gid, + Opt_mode, + Opt_err +}; + +static const match_table_t tokens = { + {Opt_uid, "uid=%u"}, + {Opt_gid, "gid=%u"}, + {Opt_mode, "mode=%o"}, + {Opt_err, NULL} +}; + +struct tracefs_fs_info { + struct tracefs_mount_opts mount_opts; +}; + +static int tracefs_parse_options(char *data, struct tracefs_mount_opts *opts) +{ + substring_t args[MAX_OPT_ARGS]; + int option; + int token; + kuid_t uid; + kgid_t gid; + char *p; + + opts->mode = TRACEFS_DEFAULT_MODE; + + while ((p = strsep(&data, ",")) != NULL) { + if (!*p) + continue; + + token = match_token(p, tokens, args); + switch (token) { + case Opt_uid: + if (match_int(&args[0], &option)) + return -EINVAL; + uid = make_kuid(current_user_ns(), option); + if (!uid_valid(uid)) + return -EINVAL; + opts->uid = uid; + break; + case Opt_gid: + if (match_int(&args[0], &option)) + return -EINVAL; + gid = make_kgid(current_user_ns(), option); + if (!gid_valid(gid)) + return -EINVAL; + opts->gid = gid; + break; + case Opt_mode: + if (match_octal(&args[0], &option)) + return -EINVAL; + opts->mode = option & S_IALLUGO; + break; + /* + * We might like to report bad mount options here; + * but traditionally tracefs has ignored all mount options + */ + } + } + + return 0; +} + +static int tracefs_apply_options(struct super_block *sb) +{ + struct tracefs_fs_info *fsi = sb->s_fs_info; + struct inode *inode = sb->s_root->d_inode; + struct tracefs_mount_opts *opts = &fsi->mount_opts; + + inode->i_mode &= ~S_IALLUGO; + inode->i_mode |= opts->mode; + + inode->i_uid = opts->uid; + inode->i_gid = opts->gid; + + return 0; +} + +static int tracefs_remount(struct super_block *sb, int *flags, char *data) +{ + int err; + struct tracefs_fs_info *fsi = sb->s_fs_info; + + sync_filesystem(sb); + err = tracefs_parse_options(data, &fsi->mount_opts); + if (err) + goto fail; + + tracefs_apply_options(sb); + +fail: + return err; +} + +static int tracefs_show_options(struct seq_file *m, struct dentry *root) +{ + struct tracefs_fs_info *fsi = root->d_sb->s_fs_info; + struct tracefs_mount_opts *opts = &fsi->mount_opts; + + if (!uid_eq(opts->uid, GLOBAL_ROOT_UID)) + seq_printf(m, ",uid=%u", + from_kuid_munged(&init_user_ns, opts->uid)); + if (!gid_eq(opts->gid, GLOBAL_ROOT_GID)) + seq_printf(m, ",gid=%u", + from_kgid_munged(&init_user_ns, opts->gid)); + if (opts->mode != TRACEFS_DEFAULT_MODE) + seq_printf(m, ",mode=%o", opts->mode); + + return 0; +} + +static const struct super_operations tracefs_super_operations = { + .statfs = simple_statfs, + .remount_fs = tracefs_remount, + .show_options = tracefs_show_options, +}; + +static int trace_fill_super(struct super_block *sb, void *data, int silent) +{ + static struct tree_descr trace_files[] = {{""}}; + struct tracefs_fs_info *fsi; + int err; + + save_mount_options(sb, data); + + fsi = kzalloc(sizeof(struct tracefs_fs_info), GFP_KERNEL); + sb->s_fs_info = fsi; + if (!fsi) { + err = -ENOMEM; + goto fail; + } + + err = tracefs_parse_options(data, &fsi->mount_opts); + if (err) + goto fail; + + err = simple_fill_super(sb, TRACEFS_MAGIC, trace_files); + if (err) + goto fail; + + sb->s_op = &tracefs_super_operations; + + tracefs_apply_options(sb); + + return 0; + +fail: + kfree(fsi); + sb->s_fs_info = NULL; + return err; +} + +static struct dentry *trace_mount(struct file_system_type *fs_type, + int flags, const char *dev_name, + void *data) +{ + return mount_single(fs_type, flags, data, trace_fill_super); +} + +static struct file_system_type trace_fs_type = { + .owner = THIS_MODULE, + .name = "tracefs", + .mount = trace_mount, + .kill_sb = kill_litter_super, +}; +MODULE_ALIAS_FS("tracefs"); + +static struct dentry *start_creating(const char *name, struct dentry *parent) +{ + struct dentry *dentry; + int error; + + pr_debug("tracefs: creating file '%s'\n",name); + + error = simple_pin_fs(&trace_fs_type, &tracefs_mount, + &tracefs_mount_count); + if (error) + return ERR_PTR(error); + + /* If the parent is not specified, we create it in the root. + * We need the root dentry to do this, which is in the super + * block. A pointer to that is in the struct vfsmount that we + * have around. + */ + if (!parent) + parent = tracefs_mount->mnt_root; + + mutex_lock(&parent->d_inode->i_mutex); + dentry = lookup_one_len(name, parent, strlen(name)); + if (!IS_ERR(dentry) && dentry->d_inode) { + dput(dentry); + dentry = ERR_PTR(-EEXIST); + } + if (IS_ERR(dentry)) + mutex_unlock(&parent->d_inode->i_mutex); + return dentry; +} + +static struct dentry *failed_creating(struct dentry *dentry) +{ + mutex_unlock(&dentry->d_parent->d_inode->i_mutex); + dput(dentry); + simple_release_fs(&tracefs_mount, &tracefs_mount_count); + return NULL; +} + +static struct dentry *end_creating(struct dentry *dentry) +{ + mutex_unlock(&dentry->d_parent->d_inode->i_mutex); + return dentry; +} + +/** + * tracefs_create_file - create a file in the tracefs filesystem + * @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 tracefs filesystem. + * @data: a pointer to something that the caller will want to get to later + * on. The inode.i_private pointer will point to this value on + * the open() call. + * @fops: a pointer to a struct file_operations that should be used for + * this file. + * + * This is the basic "create a file" function for tracefs. It allows for a + * wide range of flexibility in creating a file, or a directory (if you want + * to create a directory, the tracefs_create_dir() function is + * recommended to be used instead.) + * + * This function will return a pointer to a dentry if it succeeds. This + * pointer must be passed to the tracefs_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, %NULL will be returned. + * + * If tracefs is not enabled in the kernel, the value -%ENODEV will be + * returned. + */ +struct dentry *tracefs_create_file(const char *name, umode_t mode, + struct dentry *parent, void *data, + const struct file_operations *fops) +{ + struct dentry *dentry; + struct inode *inode; + + if (!(mode & S_IFMT)) + mode |= S_IFREG; + BUG_ON(!S_ISREG(mode)); + dentry = start_creating(name, parent); + + if (IS_ERR(dentry)) + return NULL; + + inode = tracefs_get_inode(dentry->d_sb); + if (unlikely(!inode)) + return failed_creating(dentry); + + inode->i_mode = mode; + inode->i_fop = fops ? fops : &tracefs_file_operations; + inode->i_private = data; + d_instantiate(dentry, inode); + fsnotify_create(dentry->d_parent->d_inode, dentry); + return end_creating(dentry); +} + +/** + * tracefs_create_dir - create a directory in the tracefs filesystem + * @name: a pointer to a string containing the name of the directory to + * create. + * @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 + * directory will be created in the root of the tracefs filesystem. + * + * This function creates a directory in tracefs with the given name. + * + * This function will return a pointer to a dentry if it succeeds. This + * pointer must be passed to the tracefs_remove() function when the file is + * to be removed. If an error occurs, %NULL will be returned. + * + * If tracing is not enabled in the kernel, the value -%ENODEV will be + * returned. + */ +struct dentry *tracefs_create_dir(const char *name, struct dentry *parent) +{ + struct dentry *dentry = start_creating(name, parent); + struct inode *inode; + + if (IS_ERR(dentry)) + return NULL; + + inode = tracefs_get_inode(dentry->d_sb); + if (unlikely(!inode)) + return failed_creating(dentry); + + inode->i_mode = S_IFDIR | S_IRWXU | S_IRUGO | S_IXUGO; + inode->i_op = &simple_dir_inode_operations; + inode->i_fop = &simple_dir_operations; + + /* directory inodes start off with i_nlink == 2 (for "." entry) */ + inc_nlink(inode); + d_instantiate(dentry, inode); + inc_nlink(dentry->d_parent->d_inode); + fsnotify_mkdir(dentry->d_parent->d_inode, dentry); + return end_creating(dentry); +} + +static inline int tracefs_positive(struct dentry *dentry) +{ + return dentry->d_inode && !d_unhashed(dentry); +} + +static int __tracefs_remove(struct dentry *dentry, struct dentry *parent) +{ + int ret = 0; + + if (tracefs_positive(dentry)) { + if (dentry->d_inode) { + dget(dentry); + switch (dentry->d_inode->i_mode & S_IFMT) { + case S_IFDIR: + ret = simple_rmdir(parent->d_inode, dentry); + break; + default: + simple_unlink(parent->d_inode, dentry); + break; + } + if (!ret) + d_delete(dentry); + dput(dentry); + } + } + return ret; +} + +/** + * tracefs_remove - removes a file or directory from the tracefs filesystem + * @dentry: a pointer to a the dentry of the file or directory to be + * removed. + * + * This function removes a file or directory in tracefs that was previously + * created with a call to another tracefs function (like + * tracefs_create_file() or variants thereof.) + */ +void tracefs_remove(struct dentry *dentry) +{ + struct dentry *parent; + int ret; + + if (IS_ERR_OR_NULL(dentry)) + return; + + parent = dentry->d_parent; + if (!parent || !parent->d_inode) + return; + + mutex_lock(&parent->d_inode->i_mutex); + ret = __tracefs_remove(dentry, parent); + mutex_unlock(&parent->d_inode->i_mutex); + if (!ret) + simple_release_fs(&tracefs_mount, &tracefs_mount_count); +} + +/** + * tracefs_remove_recursive - recursively removes a directory + * @dentry: a pointer to a the dentry of the directory to be removed. + * + * This function recursively removes a directory tree in tracefs that + * was previously created with a call to another tracefs function + * (like tracefs_create_file() or variants thereof.) + */ +void tracefs_remove_recursive(struct dentry *dentry) +{ + struct dentry *child, *parent; + + if (IS_ERR_OR_NULL(dentry)) + return; + + parent = dentry->d_parent; + if (!parent || !parent->d_inode) + return; + + parent = dentry; + down: + mutex_lock(&parent->d_inode->i_mutex); + loop: + /* + * The parent->d_subdirs is protected by the d_lock. Outside that + * lock, the child can be unlinked and set to be freed which can + * use the d_u.d_child as the rcu head and corrupt this list. + */ + spin_lock(&parent->d_lock); + list_for_each_entry(child, &parent->d_subdirs, d_child) { + if (!tracefs_positive(child)) + continue; + + /* perhaps simple_empty(child) makes more sense */ + if (!list_empty(&child->d_subdirs)) { + spin_unlock(&parent->d_lock); + mutex_unlock(&parent->d_inode->i_mutex); + parent = child; + goto down; + } + + spin_unlock(&parent->d_lock); + + if (!__tracefs_remove(child, parent)) + simple_release_fs(&tracefs_mount, &tracefs_mount_count); + + /* + * The parent->d_lock protects agaist child from unlinking + * from d_subdirs. When releasing the parent->d_lock we can + * no longer trust that the next pointer is valid. + * Restart the loop. We'll skip this one with the + * tracefs_positive() check. + */ + goto loop; + } + spin_unlock(&parent->d_lock); + + mutex_unlock(&parent->d_inode->i_mutex); + child = parent; + parent = parent->d_parent; + mutex_lock(&parent->d_inode->i_mutex); + + if (child != dentry) + /* go up */ + goto loop; + + if (!__tracefs_remove(child, parent)) + simple_release_fs(&tracefs_mount, &tracefs_mount_count); + mutex_unlock(&parent->d_inode->i_mutex); +} + +/** + * tracefs_initialized - Tells whether tracefs has been registered + */ +bool tracefs_initialized(void) +{ + return tracefs_registered; +} + +static int __init tracefs_init(void) +{ + int retval; + + retval = register_filesystem(&trace_fs_type); + if (!retval) + tracefs_registered = true; + + return retval; +} +core_initcall(tracefs_init); diff --git a/include/linux/tracefs.h b/include/linux/tracefs.h new file mode 100644 index 000000000000..23e04ce21749 --- /dev/null +++ b/include/linux/tracefs.h @@ -0,0 +1,41 @@ +/* + * tracefs.h - a pseudo file system for activating tracing + * + * Based on debugfs by: 2004 Greg Kroah-Hartman + * + * Copyright (C) 2014 Red Hat Inc, author: Steven Rostedt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version + * 2 as published by the Free Software Foundation. + * + * tracefs is the file system that is used by the tracing infrastructure. + * + */ + +#ifndef _TRACEFS_H_ +#define _TRACEFS_H_ + +#include +#include + +#include + +struct file_operations; + +#ifdef CONFIG_TRACING + +struct dentry *tracefs_create_file(const char *name, umode_t mode, + struct dentry *parent, void *data, + const struct file_operations *fops); + +struct dentry *tracefs_create_dir(const char *name, struct dentry *parent); + +void tracefs_remove(struct dentry *dentry); +void tracefs_remove_recursive(struct dentry *dentry); + +bool tracefs_initialized(void); + +#endif /* CONFIG_TRACING */ + +#endif diff --git a/include/uapi/linux/magic.h b/include/uapi/linux/magic.h index 7d664ea85ebd..7b1425a6b370 100644 --- a/include/uapi/linux/magic.h +++ b/include/uapi/linux/magic.h @@ -58,6 +58,8 @@ #define STACK_END_MAGIC 0x57AC6E9D +#define TRACEFS_MAGIC 0x74726163 + #define V9FS_MAGIC 0x01021997 #define BDEVFS_MAGIC 0x62646576 -- cgit v1.2.3 From eae473581cf93dad94ca833aa961c033c6a43924 Mon Sep 17 00:00:00 2001 From: "Steven Rostedt (Red Hat)" Date: Wed, 21 Jan 2015 10:01:39 -0500 Subject: tracing: Have mkdir and rmdir be part of tracefs The tracing "instances" directory can create sub tracing buffers with mkdir, and remove them with rmdir. As a mkdir will also create all the files and directories that control the sub buffer the inode mutexes need to be released before this is done, to avoid deadlocks. It is better to let the tracing system unlock the inode mutexes before calling the functions that create the files within the new directory (or deletes the files from the one being destroyed). Now that tracing has been converted over to tracefs, the tracefs file system can be modified to accommodate this feature. It still releases the locks, but the filesystem itself can take care of the ugly business and let the user just do what it needs. The tracing system now attaches a descriptor to the directory dentry that can have userspace create or remove sub directories. If this descriptor does not exist for a dentry, then that dentry can not be used to create other directories. This descriptor holds a mkdir and rmdir method that only takes a character string as an argument. The tracefs file system will first make a copy of the dentry name before releasing the locks. Then it will pass the copied name to the methods. It is up to the tracing system that supplied the methods to handle races with duplicate names and such as all the inode mutexes would be released when the functions are called. Cc: Al Viro Signed-off-by: Steven Rostedt --- fs/tracefs/inode.c | 151 +++++++++++++++++++++++++++++++++++++++++++----- include/linux/tracefs.h | 4 ++ kernel/trace/trace.c | 75 ++---------------------- 3 files changed, 145 insertions(+), 85 deletions(-) (limited to 'include') diff --git a/fs/tracefs/inode.c b/fs/tracefs/inode.c index 0b9cf5cf24c9..d92bdf3b079a 100644 --- a/fs/tracefs/inode.c +++ b/fs/tracefs/inode.c @@ -50,6 +50,84 @@ static const struct file_operations tracefs_file_operations = { .llseek = noop_llseek, }; +static struct tracefs_dir_ops { + int (*mkdir)(const char *name); + int (*rmdir)(const char *name); +} tracefs_ops; + +static char *get_dname(struct dentry *dentry) +{ + const char *dname; + char *name; + int len = dentry->d_name.len; + + dname = dentry->d_name.name; + name = kmalloc(len + 1, GFP_KERNEL); + if (!name) + return NULL; + memcpy(name, dname, len); + name[len] = 0; + return name; +} + +static int tracefs_syscall_mkdir(struct inode *inode, struct dentry *dentry, umode_t mode) +{ + char *name; + int ret; + + name = get_dname(dentry); + if (!name) + return -ENOMEM; + + /* + * The mkdir call can call the generic functions that create + * the files within the tracefs system. It is up to the individual + * mkdir routine to handle races. + */ + mutex_unlock(&inode->i_mutex); + ret = tracefs_ops.mkdir(name); + mutex_lock(&inode->i_mutex); + + kfree(name); + + return ret; +} + +static int tracefs_syscall_rmdir(struct inode *inode, struct dentry *dentry) +{ + char *name; + int ret; + + name = get_dname(dentry); + if (!name) + return -ENOMEM; + + /* + * The rmdir call can call the generic functions that create + * the files within the tracefs system. It is up to the individual + * rmdir routine to handle races. + * This time we need to unlock not only the parent (inode) but + * also the directory that is being deleted. + */ + mutex_unlock(&inode->i_mutex); + mutex_unlock(&dentry->d_inode->i_mutex); + + ret = tracefs_ops.rmdir(name); + + mutex_lock_nested(&inode->i_mutex, I_MUTEX_PARENT); + mutex_lock(&dentry->d_inode->i_mutex); + + kfree(name); + + return ret; +} + +static const struct inode_operations tracefs_dir_inode_operations = { + .lookup = simple_lookup, + .mkdir = tracefs_syscall_mkdir, + .rmdir = tracefs_syscall_rmdir, +}; + static struct inode *tracefs_get_inode(struct super_block *sb) { struct inode *inode = new_inode(sb); @@ -334,6 +412,31 @@ struct dentry *tracefs_create_file(const char *name, umode_t mode, return end_creating(dentry); } +static struct dentry *__create_dir(const char *name, struct dentry *parent, + const struct inode_operations *ops) +{ + struct dentry *dentry = start_creating(name, parent); + struct inode *inode; + + if (IS_ERR(dentry)) + return NULL; + + inode = tracefs_get_inode(dentry->d_sb); + if (unlikely(!inode)) + return failed_creating(dentry); + + inode->i_mode = S_IFDIR | S_IRWXU | S_IRUGO | S_IXUGO; + inode->i_op = ops; + inode->i_fop = &simple_dir_operations; + + /* directory inodes start off with i_nlink == 2 (for "." entry) */ + inc_nlink(inode); + d_instantiate(dentry, inode); + inc_nlink(dentry->d_parent->d_inode); + fsnotify_mkdir(dentry->d_parent->d_inode, dentry); + return end_creating(dentry); +} + /** * tracefs_create_dir - create a directory in the tracefs filesystem * @name: a pointer to a string containing the name of the directory to @@ -353,26 +456,44 @@ struct dentry *tracefs_create_file(const char *name, umode_t mode, */ struct dentry *tracefs_create_dir(const char *name, struct dentry *parent) { - struct dentry *dentry = start_creating(name, parent); - struct inode *inode; + return __create_dir(name, parent, &simple_dir_inode_operations); +} - if (IS_ERR(dentry)) +/** + * tracefs_create_instance_dir - create the tracing instances directory + * @name: The name of the instances directory to create + * @parent: The parent directory that the instances directory will exist + * @mkdir: The function to call when a mkdir is performed. + * @rmdir: The function to call when a rmdir is performed. + * + * Only one instances directory is allowed. + * + * The instances directory is special as it allows for mkdir and rmdir to + * to be done by userspace. When a mkdir or rmdir is performed, the inode + * locks are released and the methhods passed in (@mkdir and @rmdir) are + * called without locks and with the name of the directory being created + * within the instances directory. + * + * Returns the dentry of the instances directory. + */ +struct dentry *tracefs_create_instance_dir(const char *name, struct dentry *parent, + int (*mkdir)(const char *name), + int (*rmdir)(const char *name)) +{ + struct dentry *dentry; + + /* Only allow one instance of the instances directory. */ + if (WARN_ON(tracefs_ops.mkdir || tracefs_ops.rmdir)) return NULL; - inode = tracefs_get_inode(dentry->d_sb); - if (unlikely(!inode)) - return failed_creating(dentry); + dentry = __create_dir(name, parent, &tracefs_dir_inode_operations); + if (!dentry) + return NULL; - inode->i_mode = S_IFDIR | S_IRWXU | S_IRUGO | S_IXUGO; - inode->i_op = &simple_dir_inode_operations; - inode->i_fop = &simple_dir_operations; + tracefs_ops.mkdir = mkdir; + tracefs_ops.rmdir = rmdir; - /* directory inodes start off with i_nlink == 2 (for "." entry) */ - inc_nlink(inode); - d_instantiate(dentry, inode); - inc_nlink(dentry->d_parent->d_inode); - fsnotify_mkdir(dentry->d_parent->d_inode, dentry); - return end_creating(dentry); + return dentry; } static inline int tracefs_positive(struct dentry *dentry) diff --git a/include/linux/tracefs.h b/include/linux/tracefs.h index 23e04ce21749..5b727a17beee 100644 --- a/include/linux/tracefs.h +++ b/include/linux/tracefs.h @@ -34,6 +34,10 @@ struct dentry *tracefs_create_dir(const char *name, struct dentry *parent); void tracefs_remove(struct dentry *dentry); void tracefs_remove_recursive(struct dentry *dentry); +struct dentry *tracefs_create_instance_dir(const char *name, struct dentry *parent, + int (*mkdir)(const char *name), + int (*rmdir)(const char *name)); + bool tracefs_initialized(void); #endif /* CONFIG_TRACING */ diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c index b4aa936509d2..3c8913bac204 100644 --- a/kernel/trace/trace.c +++ b/kernel/trace/trace.c @@ -6292,7 +6292,7 @@ static void free_trace_buffers(struct trace_array *tr) #endif } -static int new_instance_create(const char *name) +static int instance_mkdir(const char *name) { struct trace_array *tr; int ret; @@ -6362,7 +6362,7 @@ static int new_instance_create(const char *name) } -static int instance_delete(const char *name) +static int instance_rmdir(const char *name) { struct trace_array *tr; int found = 0; @@ -6403,78 +6403,13 @@ static int instance_delete(const char *name) return ret; } -static int instance_mkdir (struct inode *inode, struct dentry *dentry, umode_t mode) -{ - struct dentry *parent; - int ret; - - /* Paranoid: Make sure the parent is the "instances" directory */ - parent = hlist_entry(inode->i_dentry.first, struct dentry, d_u.d_alias); - if (WARN_ON_ONCE(parent != trace_instance_dir)) - return -ENOENT; - - /* - * The inode mutex is locked, but tracefs_create_dir() will also - * take the mutex. As the instances directory can not be destroyed - * or changed in any other way, it is safe to unlock it, and - * let the dentry try. If two users try to make the same dir at - * the same time, then the new_instance_create() will determine the - * winner. - */ - mutex_unlock(&inode->i_mutex); - - ret = new_instance_create(dentry->d_iname); - - mutex_lock(&inode->i_mutex); - - return ret; -} - -static int instance_rmdir(struct inode *inode, struct dentry *dentry) -{ - struct dentry *parent; - int ret; - - /* Paranoid: Make sure the parent is the "instances" directory */ - parent = hlist_entry(inode->i_dentry.first, struct dentry, d_u.d_alias); - if (WARN_ON_ONCE(parent != trace_instance_dir)) - return -ENOENT; - - /* The caller did a dget() on dentry */ - mutex_unlock(&dentry->d_inode->i_mutex); - - /* - * The inode mutex is locked, but tracefs_create_dir() will also - * take the mutex. As the instances directory can not be destroyed - * or changed in any other way, it is safe to unlock it, and - * let the dentry try. If two users try to make the same dir at - * the same time, then the instance_delete() will determine the - * winner. - */ - mutex_unlock(&inode->i_mutex); - - ret = instance_delete(dentry->d_iname); - - mutex_lock_nested(&inode->i_mutex, I_MUTEX_PARENT); - mutex_lock(&dentry->d_inode->i_mutex); - - return ret; -} - -static const struct inode_operations instance_dir_inode_operations = { - .lookup = simple_lookup, - .mkdir = instance_mkdir, - .rmdir = instance_rmdir, -}; - static __init void create_trace_instances(struct dentry *d_tracer) { - trace_instance_dir = tracefs_create_dir("instances", d_tracer); + trace_instance_dir = tracefs_create_instance_dir("instances", d_tracer, + instance_mkdir, + instance_rmdir); if (WARN_ON(!trace_instance_dir)) return; - - /* Hijack the dir inode operations, to allow mkdir */ - trace_instance_dir->d_inode->i_op = &instance_dir_inode_operations; } static void -- cgit v1.2.3 From 96bd6224f07b8bf73e0359f15a3d7678118494e6 Mon Sep 17 00:00:00 2001 From: Chanwoo Choi Date: Mon, 2 Feb 2015 23:23:56 +0900 Subject: clk: samsung: exynos5433: Add clocks using common clock framework This patch adds support for the CMU (Clock Management Units) of Exynos5433 which is an Octa-core 64bit SoC. This patch supports necessary clocks (PLL/MMC/UART/MCT/I2C/SPI) for kernel boot and includes binding documentation for Exynos5433 clock controller. Signed-off-by: Chanwoo Choi Acked-by: Inki Dae [s.nawrocki@samsung.com: whitespace cleanup in dt-bindings/clock/exynos5433.h] [ added U suffix to first arguments of PLL_35XX_RATE()] Signed-off-by: Sylwester Nawrocki --- drivers/clk/samsung/Makefile | 1 + drivers/clk/samsung/clk-exynos5433.c | 963 +++++++++++++++++++++++++++++++++ include/dt-bindings/clock/exynos5433.h | 199 +++++++ 3 files changed, 1163 insertions(+) create mode 100644 drivers/clk/samsung/clk-exynos5433.c create mode 100644 include/dt-bindings/clock/exynos5433.h (limited to 'include') diff --git a/drivers/clk/samsung/Makefile b/drivers/clk/samsung/Makefile index 006c6f294310..17e9af7fe81f 100644 --- a/drivers/clk/samsung/Makefile +++ b/drivers/clk/samsung/Makefile @@ -10,6 +10,7 @@ obj-$(CONFIG_SOC_EXYNOS5250) += clk-exynos5250.o obj-$(CONFIG_SOC_EXYNOS5260) += clk-exynos5260.o obj-$(CONFIG_SOC_EXYNOS5410) += clk-exynos5410.o obj-$(CONFIG_SOC_EXYNOS5420) += clk-exynos5420.o +obj-$(CONFIG_ARCH_EXYNOS5433) += clk-exynos5433.o obj-$(CONFIG_SOC_EXYNOS5440) += clk-exynos5440.o obj-$(CONFIG_ARCH_EXYNOS) += clk-exynos-audss.o obj-$(CONFIG_ARCH_EXYNOS) += clk-exynos-clkout.o diff --git a/drivers/clk/samsung/clk-exynos5433.c b/drivers/clk/samsung/clk-exynos5433.c new file mode 100644 index 000000000000..a4047390bfc2 --- /dev/null +++ b/drivers/clk/samsung/clk-exynos5433.c @@ -0,0 +1,963 @@ +/* + * Copyright (c) 2014 Samsung Electronics Co., Ltd. + * Author: Chanwoo Choi + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Common Clock Framework support for Exynos5443 SoC. + */ + +#include +#include +#include +#include + +#include + +#include "clk.h" +#include "clk-pll.h" + +/* + * Register offset definitions for CMU_TOP + */ +#define ISP_PLL_LOCK 0x0000 +#define AUD_PLL_LOCK 0x0004 +#define ISP_PLL_CON0 0x0100 +#define ISP_PLL_CON1 0x0104 +#define ISP_PLL_FREQ_DET 0x0108 +#define AUD_PLL_CON0 0x0110 +#define AUD_PLL_CON1 0x0114 +#define AUD_PLL_CON2 0x0118 +#define AUD_PLL_FREQ_DET 0x011c +#define MUX_SEL_TOP0 0x0200 +#define MUX_SEL_TOP1 0x0204 +#define MUX_SEL_TOP2 0x0208 +#define MUX_SEL_TOP3 0x020c +#define MUX_SEL_TOP4 0x0210 +#define MUX_SEL_TOP_MSCL 0x0220 +#define MUX_SEL_TOP_CAM1 0x0224 +#define MUX_SEL_TOP_DISP 0x0228 +#define MUX_SEL_TOP_FSYS0 0x0230 +#define MUX_SEL_TOP_FSYS1 0x0234 +#define MUX_SEL_TOP_PERIC0 0x0238 +#define MUX_SEL_TOP_PERIC1 0x023c +#define MUX_ENABLE_TOP0 0x0300 +#define MUX_ENABLE_TOP1 0x0304 +#define MUX_ENABLE_TOP2 0x0308 +#define MUX_ENABLE_TOP3 0x030c +#define MUX_ENABLE_TOP4 0x0310 +#define MUX_ENABLE_TOP_MSCL 0x0320 +#define MUX_ENABLE_TOP_CAM1 0x0324 +#define MUX_ENABLE_TOP_DISP 0x0328 +#define MUX_ENABLE_TOP_FSYS0 0x0330 +#define MUX_ENABLE_TOP_FSYS1 0x0334 +#define MUX_ENABLE_TOP_PERIC0 0x0338 +#define MUX_ENABLE_TOP_PERIC1 0x033c +#define MUX_STAT_TOP0 0x0400 +#define MUX_STAT_TOP1 0x0404 +#define MUX_STAT_TOP2 0x0408 +#define MUX_STAT_TOP3 0x040c +#define MUX_STAT_TOP4 0x0410 +#define MUX_STAT_TOP_MSCL 0x0420 +#define MUX_STAT_TOP_CAM1 0x0424 +#define MUX_STAT_TOP_FSYS0 0x0430 +#define MUX_STAT_TOP_FSYS1 0x0434 +#define MUX_STAT_TOP_PERIC0 0x0438 +#define MUX_STAT_TOP_PERIC1 0x043c +#define DIV_TOP0 0x0600 +#define DIV_TOP1 0x0604 +#define DIV_TOP2 0x0608 +#define DIV_TOP3 0x060c +#define DIV_TOP4 0x0610 +#define DIV_TOP_MSCL 0x0618 +#define DIV_TOP_CAM10 0x061c +#define DIV_TOP_CAM11 0x0620 +#define DIV_TOP_FSYS0 0x062c +#define DIV_TOP_FSYS1 0x0630 +#define DIV_TOP_FSYS2 0x0634 +#define DIV_TOP_PERIC0 0x0638 +#define DIV_TOP_PERIC1 0x063c +#define DIV_TOP_PERIC2 0x0640 +#define DIV_TOP_PERIC3 0x0644 +#define DIV_TOP_PERIC4 0x0648 +#define DIV_TOP_PLL_FREQ_DET 0x064c +#define DIV_STAT_TOP0 0x0700 +#define DIV_STAT_TOP1 0x0704 +#define DIV_STAT_TOP2 0x0708 +#define DIV_STAT_TOP3 0x070c +#define DIV_STAT_TOP4 0x0710 +#define DIV_STAT_TOP_MSCL 0x0718 +#define DIV_STAT_TOP_CAM10 0x071c +#define DIV_STAT_TOP_CAM11 0x0720 +#define DIV_STAT_TOP_FSYS0 0x072c +#define DIV_STAT_TOP_FSYS1 0x0730 +#define DIV_STAT_TOP_FSYS2 0x0734 +#define DIV_STAT_TOP_PERIC0 0x0738 +#define DIV_STAT_TOP_PERIC1 0x073c +#define DIV_STAT_TOP_PERIC2 0x0740 +#define DIV_STAT_TOP_PERIC3 0x0744 +#define DIV_STAT_TOP_PLL_FREQ_DET 0x074c +#define ENABLE_ACLK_TOP 0x0800 +#define ENABLE_SCLK_TOP 0x0a00 +#define ENABLE_SCLK_TOP_MSCL 0x0a04 +#define ENABLE_SCLK_TOP_CAM1 0x0a08 +#define ENABLE_SCLK_TOP_DISP 0x0a0c +#define ENABLE_SCLK_TOP_FSYS 0x0a10 +#define ENABLE_SCLK_TOP_PERIC 0x0a14 +#define ENABLE_IP_TOP 0x0b00 +#define ENABLE_CMU_TOP 0x0c00 +#define ENABLE_CMU_TOP_DIV_STAT 0x0c04 + +static unsigned long top_clk_regs[] __initdata = { + ISP_PLL_LOCK, + AUD_PLL_LOCK, + ISP_PLL_CON0, + ISP_PLL_CON1, + ISP_PLL_FREQ_DET, + AUD_PLL_CON0, + AUD_PLL_CON1, + AUD_PLL_CON2, + AUD_PLL_FREQ_DET, + MUX_SEL_TOP0, + MUX_SEL_TOP1, + MUX_SEL_TOP2, + MUX_SEL_TOP3, + MUX_SEL_TOP4, + MUX_SEL_TOP_MSCL, + MUX_SEL_TOP_CAM1, + MUX_SEL_TOP_DISP, + MUX_SEL_TOP_FSYS0, + MUX_SEL_TOP_FSYS1, + MUX_SEL_TOP_PERIC0, + MUX_SEL_TOP_PERIC1, + MUX_ENABLE_TOP0, + MUX_ENABLE_TOP1, + MUX_ENABLE_TOP2, + MUX_ENABLE_TOP3, + MUX_ENABLE_TOP4, + MUX_ENABLE_TOP_MSCL, + MUX_ENABLE_TOP_CAM1, + MUX_ENABLE_TOP_DISP, + MUX_ENABLE_TOP_FSYS0, + MUX_ENABLE_TOP_FSYS1, + MUX_ENABLE_TOP_PERIC0, + MUX_ENABLE_TOP_PERIC1, + MUX_STAT_TOP0, + MUX_STAT_TOP1, + MUX_STAT_TOP2, + MUX_STAT_TOP3, + MUX_STAT_TOP4, + MUX_STAT_TOP_MSCL, + MUX_STAT_TOP_CAM1, + MUX_STAT_TOP_FSYS0, + MUX_STAT_TOP_FSYS1, + MUX_STAT_TOP_PERIC0, + MUX_STAT_TOP_PERIC1, + DIV_TOP0, + DIV_TOP1, + DIV_TOP2, + DIV_TOP3, + DIV_TOP4, + DIV_TOP_MSCL, + DIV_TOP_CAM10, + DIV_TOP_CAM11, + DIV_TOP_FSYS0, + DIV_TOP_FSYS1, + DIV_TOP_FSYS2, + DIV_TOP_PERIC0, + DIV_TOP_PERIC1, + DIV_TOP_PERIC2, + DIV_TOP_PERIC3, + DIV_TOP_PERIC4, + DIV_TOP_PLL_FREQ_DET, + DIV_STAT_TOP0, + DIV_STAT_TOP1, + DIV_STAT_TOP2, + DIV_STAT_TOP3, + DIV_STAT_TOP4, + DIV_STAT_TOP_MSCL, + DIV_STAT_TOP_CAM10, + DIV_STAT_TOP_CAM11, + DIV_STAT_TOP_FSYS0, + DIV_STAT_TOP_FSYS1, + DIV_STAT_TOP_FSYS2, + DIV_STAT_TOP_PERIC0, + DIV_STAT_TOP_PERIC1, + DIV_STAT_TOP_PERIC2, + DIV_STAT_TOP_PERIC3, + DIV_STAT_TOP_PLL_FREQ_DET, + ENABLE_ACLK_TOP, + ENABLE_SCLK_TOP, + ENABLE_SCLK_TOP_MSCL, + ENABLE_SCLK_TOP_CAM1, + ENABLE_SCLK_TOP_DISP, + ENABLE_SCLK_TOP_FSYS, + ENABLE_SCLK_TOP_PERIC, + ENABLE_IP_TOP, + ENABLE_CMU_TOP, + ENABLE_CMU_TOP_DIV_STAT, +}; + +/* list of all parent clock list */ +PNAME(mout_aud_pll_p) = { "oscclk", "fout_aud_pll", }; +PNAME(mout_isp_pll_p) = { "oscclk", "fout_isp_pll", }; +PNAME(mout_aud_pll_user_p) = { "oscclk", "mout_aud_pll", }; +PNAME(mout_mphy_pll_user_p) = { "oscclk", "sclk_mphy_pll", }; +PNAME(mout_mfc_pll_user_p) = { "oscclk", "sclk_mfc_pll", }; +PNAME(mout_bus_pll_user_p) = { "oscclk", "sclk_bus_pll", }; +PNAME(mout_bus_pll_user_t_p) = { "oscclk", "mout_bus_pll_user", }; + +PNAME(mout_bus_mfc_pll_user_p) = { "mout_bus_pll_user", "mout_mfc_pll_user",}; +PNAME(mout_mfc_bus_pll_user_p) = { "mout_mfc_pll_user", "mout_bus_pll_user",}; +PNAME(mout_aclk_cam1_552_b_p) = { "mout_aclk_cam1_552_a", + "mout_mfc_pll_user", }; +PNAME(mout_aclk_cam1_552_a_p) = { "mout_isp_pll", "mout_bus_pll_user", }; + +PNAME(mout_bus_mphy_pll_user_p) = { "mout_bus_pll_user", + "mout_mphy_pll_user", }; +PNAME(mout_aclk_mscl_b_p) = { "mout_aclk_mscl_400_a", + "mout_mphy_pll_user", }; +PNAME(mout_aclk_g2d_400_b_p) = { "mout_aclk_g2d_400_a", + "mout_mphy_pll_user", }; + +PNAME(mout_sclk_jpeg_c_p) = { "mout_sclk_jpeg_b", "mout_mphy_pll_user",}; +PNAME(mout_sclk_jpeg_b_p) = { "mout_sclk_jpeg_a", "mout_mfc_pll_user", }; + +PNAME(mout_sclk_mmc2_b_p) = { "mout_sclk_mmc2_a", "mout_mfc_pll_user",}; +PNAME(mout_sclk_mmc1_b_p) = { "mout_sclk_mmc1_a", "mout_mfc_pll_user",}; +PNAME(mout_sclk_mmc0_d_p) = { "mout_sclk_mmc0_c", "mout_isp_pll", }; +PNAME(mout_sclk_mmc0_c_p) = { "mout_sclk_mmc0_b", "mout_mphy_pll_user",}; +PNAME(mout_sclk_mmc0_b_p) = { "mout_sclk_mmc0_a", "mout_mfc_pll_user", }; + +static struct samsung_mux_clock top_mux_clks[] __initdata = { + /* MUX_SEL_TOP0 */ + MUX(CLK_MOUT_AUD_PLL, "mout_aud_pll", mout_aud_pll_p, MUX_SEL_TOP0, + 4, 1), + MUX(CLK_MOUT_ISP_PLL, "mout_isp_pll", mout_isp_pll_p, MUX_SEL_TOP0, + 0, 1), + + /* MUX_SEL_TOP1 */ + MUX(CLK_MOUT_AUD_PLL_USER_T, "mout_aud_pll_user_t", + mout_aud_pll_user_p, MUX_SEL_TOP1, 12, 1), + MUX(CLK_MOUT_MPHY_PLL_USER, "mout_mphy_pll_user", mout_mphy_pll_user_p, + MUX_SEL_TOP1, 8, 1), + MUX(CLK_MOUT_MFC_PLL_USER, "mout_mfc_pll_user", mout_mfc_pll_user_p, + MUX_SEL_TOP1, 4, 1), + MUX(CLK_MOUT_BUS_PLL_USER, "mout_bus_pll_user", mout_bus_pll_user_p, + MUX_SEL_TOP1, 0, 1), + + /* MUX_SEL_TOP2 */ + MUX(CLK_MOUT_ACLK_HEVC_400, "mout_aclk_hevc_400", + mout_bus_mfc_pll_user_p, MUX_SEL_TOP2, 28, 1), + MUX(CLK_MOUT_ACLK_CAM1_333, "mout_aclk_cam1_333", + mout_mfc_bus_pll_user_p, MUX_SEL_TOP2, 16, 1), + MUX(CLK_MOUT_ACLK_CAM1_552_B, "mout_aclk_cam1_552_b", + mout_aclk_cam1_552_b_p, MUX_SEL_TOP2, 12, 1), + MUX(CLK_MOUT_ACLK_CAM1_552_A, "mout_aclk_cam1_552_a", + mout_aclk_cam1_552_a_p, MUX_SEL_TOP2, 8, 1), + MUX(CLK_MOUT_ACLK_ISP_DIS_400, "mout_aclk_isp_dis_400", + mout_bus_mfc_pll_user_p, MUX_SEL_TOP2, 4, 1), + MUX(CLK_MOUT_ACLK_ISP_400, "mout_aclk_isp_400", + mout_bus_mfc_pll_user_p, MUX_SEL_TOP2, 0, 1), + + /* MUX_SEL_TOP3 */ + MUX(CLK_MOUT_ACLK_BUS0_400, "mout_aclk_bus0_400", + mout_bus_mphy_pll_user_p, MUX_SEL_TOP3, 20, 1), + MUX(CLK_MOUT_ACLK_MSCL_400_B, "mout_aclk_mscl_400_b", + mout_aclk_mscl_b_p, MUX_SEL_TOP3, 16, 1), + MUX(CLK_MOUT_ACLK_MSCL_400_A, "mout_aclk_mscl_400_a", + mout_bus_mfc_pll_user_p, MUX_SEL_TOP3, 12, 1), + MUX(CLK_MOUT_ACLK_GSCL_333, "mout_aclk_gscl_333", + mout_mfc_bus_pll_user_p, MUX_SEL_TOP3, 8, 1), + MUX(CLK_MOUT_ACLK_G2D_400_B, "mout_aclk_g2d_400_b", + mout_aclk_g2d_400_b_p, MUX_SEL_TOP3, 4, 1), + MUX(CLK_MOUT_ACLK_G2D_400_A, "mout_aclk_g2d_400_a", + mout_bus_mfc_pll_user_p, MUX_SEL_TOP3, 0, 1), + + /* MUX_SEL_TOP_MSCL */ + MUX(CLK_MOUT_SCLK_JPEG_C, "mout_sclk_jpeg_c", mout_sclk_jpeg_c_p, + MUX_SEL_TOP_MSCL, 8, 1), + MUX(CLK_MOUT_SCLK_JPEG_B, "mout_sclk_jpeg_b", mout_sclk_jpeg_b_p, + MUX_SEL_TOP_MSCL, 4, 1), + MUX(CLK_MOUT_SCLK_JPEG_A, "mout_sclk_jpeg_a", mout_bus_pll_user_t_p, + MUX_SEL_TOP_MSCL, 0, 1), + + /* MUX_SEL_TOP_FSYS0 */ + MUX(CLK_MOUT_SCLK_MMC2_B, "mout_sclk_mmc2_b", mout_sclk_mmc2_b_p, + MUX_SEL_TOP_FSYS0, 28, 1), + MUX(CLK_MOUT_SCLK_MMC2_A, "mout_sclk_mmc2_a", mout_bus_pll_user_t_p, + MUX_SEL_TOP_FSYS0, 24, 1), + MUX(CLK_MOUT_SCLK_MMC1_B, "mout_sclk_mmc1_b", mout_sclk_mmc1_b_p, + MUX_SEL_TOP_FSYS0, 20, 1), + MUX(CLK_MOUT_SCLK_MMC1_A, "mout_sclk_mmc1_a", mout_bus_pll_user_t_p, + MUX_SEL_TOP_FSYS0, 16, 1), + MUX(CLK_MOUT_SCLK_MMC0_D, "mout_sclk_mmc0_d", mout_sclk_mmc0_d_p, + MUX_SEL_TOP_FSYS0, 12, 1), + MUX(CLK_MOUT_SCLK_MMC0_C, "mout_sclk_mmc0_c", mout_sclk_mmc0_c_p, + MUX_SEL_TOP_FSYS0, 8, 1), + MUX(CLK_MOUT_SCLK_MMC0_B, "mout_sclk_mmc0_b", mout_sclk_mmc0_b_p, + MUX_SEL_TOP_FSYS0, 4, 1), + MUX(CLK_MOUT_SCLK_MMC0_A, "mout_sclk_mmc0_a", mout_bus_pll_user_t_p, + MUX_SEL_TOP_FSYS0, 0, 1), + + /* MUX_SEL_TOP_PERIC0 */ + MUX(CLK_MOUT_SCLK_SPI4, "mout_sclk_spi4", mout_bus_pll_user_t_p, + MUX_SEL_TOP_PERIC0, 28, 1), + MUX(CLK_MOUT_SCLK_SPI3, "mout_sclk_spi3", mout_bus_pll_user_t_p, + MUX_SEL_TOP_PERIC0, 24, 1), + MUX(CLK_MOUT_SCLK_UART2, "mout_sclk_uart2", mout_bus_pll_user_t_p, + MUX_SEL_TOP_PERIC0, 20, 1), + MUX(CLK_MOUT_SCLK_UART1, "mout_sclk_uart1", mout_bus_pll_user_t_p, + MUX_SEL_TOP_PERIC0, 16, 1), + MUX(CLK_MOUT_SCLK_UART0, "mout_sclk_uart0", mout_bus_pll_user_t_p, + MUX_SEL_TOP_PERIC0, 12, 1), + MUX(CLK_MOUT_SCLK_SPI2, "mout_sclk_spi2", mout_bus_pll_user_t_p, + MUX_SEL_TOP_PERIC0, 8, 1), + MUX(CLK_MOUT_SCLK_SPI1, "mout_sclk_spi1", mout_bus_pll_user_t_p, + MUX_SEL_TOP_PERIC0, 4, 1), + MUX(CLK_MOUT_SCLK_SPI0, "mout_sclk_spi0", mout_bus_pll_user_t_p, + MUX_SEL_TOP_PERIC0, 0, 1), +}; + +static struct samsung_div_clock top_div_clks[] __initdata = { + /* DIV_TOP2 */ + DIV(CLK_DIV_ACLK_FSYS_200, "div_aclk_fsys_200", "mout_bus_pll_user", + DIV_TOP2, 0, 3), + + /* DIV_TOP3 */ + DIV(CLK_DIV_ACLK_IMEM_SSSX_266, "div_aclk_imem_sssx_266", + "mout_bus_pll_user", DIV_TOP3, 24, 3), + DIV(CLK_DIV_ACLK_IMEM_200, "div_aclk_imem_200", + "mout_bus_pll_user", DIV_TOP3, 20, 3), + DIV(CLK_DIV_ACLK_IMEM_266, "div_aclk_imem_266", + "mout_bus_pll_user", DIV_TOP3, 16, 3), + DIV(CLK_DIV_ACLK_PERIC_66_B, "div_aclk_peric_66_b", + "div_aclk_peric_66_a", DIV_TOP3, 12, 3), + DIV(CLK_DIV_ACLK_PERIC_66_A, "div_aclk_peric_66_a", + "mout_bus_pll_user", DIV_TOP3, 8, 3), + DIV(CLK_DIV_ACLK_PERIS_66_B, "div_aclk_peris_66_b", + "div_aclk_peris_66_a", DIV_TOP3, 4, 3), + DIV(CLK_DIV_ACLK_PERIS_66_A, "div_aclk_peris_66_a", + "mout_bus_pll_user", DIV_TOP3, 0, 3), + + /* DIV_TOP_FSYS0 */ + DIV(CLK_DIV_SCLK_MMC1_B, "div_sclk_mmc1_b", "div_sclk_mmc1_a", + DIV_TOP_FSYS0, 16, 8), + DIV(CLK_DIV_SCLK_MMC1_A, "div_sclk_mmc1_a", "mout_sclk_mmc1_b", + DIV_TOP_FSYS0, 12, 4), + DIV_F(CLK_DIV_SCLK_MMC0_B, "div_sclk_mmc0_b", "div_sclk_mmc0_a", + DIV_TOP_FSYS0, 4, 8, CLK_SET_RATE_PARENT, 0), + DIV_F(CLK_DIV_SCLK_MMC0_A, "div_sclk_mmc0_a", "mout_sclk_mmc0_d", + DIV_TOP_FSYS0, 0, 4, CLK_SET_RATE_PARENT, 0), + + /* DIV_TOP_FSYS1 */ + DIV(CLK_DIV_SCLK_MMC2_B, "div_sclk_mmc2_b", "div_sclk_mmc2_a", + DIV_TOP_FSYS1, 4, 8), + DIV(CLK_DIV_SCLK_MMC2_A, "div_sclk_mmc2_a", "mout_sclk_mmc2_b", + DIV_TOP_FSYS1, 0, 4), + + /* DIV_TOP_PERIC0 */ + DIV(CLK_DIV_SCLK_SPI1_B, "div_sclk_spi1_b", "div_sclk_spi1_a", + DIV_TOP_PERIC0, 16, 8), + DIV(CLK_DIV_SCLK_SPI1_A, "div_sclk_spi1_a", "mout_sclk_spi1", + DIV_TOP_PERIC0, 12, 4), + DIV(CLK_DIV_SCLK_SPI0_B, "div_sclk_spi0_b", "div_sclk_spi0_a", + DIV_TOP_PERIC0, 4, 8), + DIV(CLK_DIV_SCLK_SPI0_A, "div_sclk_spi0_a", "mout_sclk_spi0", + DIV_TOP_PERIC0, 0, 4), + + /* DIV_TOP_PERIC1 */ + DIV(CLK_DIV_SCLK_SPI2_B, "div_sclk_spi2_b", "div_sclk_spi2_a", + DIV_TOP_PERIC1, 4, 8), + DIV(CLK_DIV_SCLK_SPI2_A, "div_sclk_spi2_a", "mout_sclk_spi2", + DIV_TOP_PERIC1, 0, 4), + + /* DIV_TOP_PERIC2 */ + DIV(CLK_DIV_SCLK_UART2, "div_sclk_uart2", "mout_sclk_uart2", + DIV_TOP_PERIC2, 8, 4), + DIV(CLK_DIV_SCLK_UART1, "div_sclk_uart1", "mout_sclk_uart0", + DIV_TOP_PERIC2, 4, 4), + DIV(CLK_DIV_SCLK_UART0, "div_sclk_uart0", "mout_sclk_uart1", + DIV_TOP_PERIC2, 0, 4), + + /* DIV_TOP_PERIC4 */ + DIV(CLK_DIV_SCLK_SPI4_B, "div_sclk_spi4_b", "div_sclk_spi4_a", + DIV_TOP_PERIC4, 16, 8), + DIV(CLK_DIV_SCLK_SPI4_A, "div_sclk_spi4_a", "mout_sclk_spi4", + DIV_TOP_PERIC4, 12, 4), + DIV(CLK_DIV_SCLK_SPI3_B, "div_sclk_spi3_b", "div_sclk_spi3_a", + DIV_TOP_PERIC4, 4, 8), + DIV(CLK_DIV_SCLK_SPI3_A, "div_sclk_spi3_a", "mout_sclk_spi3", + DIV_TOP_PERIC4, 0, 4), +}; + +static struct samsung_gate_clock top_gate_clks[] __initdata = { + /* ENABLE_ACLK_TOP */ + GATE(CLK_ACLK_PERIC_66, "aclk_peric_66", "div_aclk_peric_66_b", + ENABLE_ACLK_TOP, 22, + CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_PERIS_66, "aclk_peris_66", "div_aclk_peris_66_b", + ENABLE_ACLK_TOP, 21, + CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_FSYS_200, "aclk_fsys_200", "div_aclk_fsys_200", + ENABLE_ACLK_TOP, 18, + CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED, 0), + + /* ENABLE_SCLK_TOP_FSYS */ + GATE(CLK_SCLK_MMC2_FSYS, "sclk_mmc2_fsys", "div_sclk_mmc2_b", + ENABLE_SCLK_TOP_FSYS, 6, CLK_SET_RATE_PARENT, 0), + GATE(CLK_SCLK_MMC1_FSYS, "sclk_mmc1_fsys", "div_sclk_mmc1_b", + ENABLE_SCLK_TOP_FSYS, 5, CLK_SET_RATE_PARENT, 0), + GATE(CLK_SCLK_MMC0_FSYS, "sclk_mmc0_fsys", "div_sclk_mmc0_b", + ENABLE_SCLK_TOP_FSYS, 4, CLK_SET_RATE_PARENT, 0), + + /* ENABLE_SCLK_TOP_PERIC */ + GATE(CLK_SCLK_SPI4_PERIC, "sclk_spi4_peric", "div_sclk_spi4_b", + ENABLE_SCLK_TOP_PERIC, 12, CLK_SET_RATE_PARENT, 0), + GATE(CLK_SCLK_SPI3_PERIC, "sclk_spi3_peric", "div_sclk_spi3_b", + ENABLE_SCLK_TOP_PERIC, 11, CLK_SET_RATE_PARENT, 0), + GATE(CLK_SCLK_UART2_PERIC, "sclk_uart2_peric", "div_sclk_uart2", + ENABLE_SCLK_TOP_PERIC, 5, CLK_SET_RATE_PARENT, 0), + GATE(CLK_SCLK_UART1_PERIC, "sclk_uart1_peric", "div_sclk_uart1", + ENABLE_SCLK_TOP_PERIC, 4, CLK_SET_RATE_PARENT, 0), + GATE(CLK_SCLK_UART0_PERIC, "sclk_uart0_peric", "div_sclk_uart0", + ENABLE_SCLK_TOP_PERIC, 3, CLK_SET_RATE_PARENT, 0), + GATE(CLK_SCLK_SPI2_PERIC, "sclk_spi2_peric", "div_sclk_spi2_b", + ENABLE_SCLK_TOP_PERIC, 2, CLK_SET_RATE_PARENT, 0), + GATE(CLK_SCLK_SPI1_PERIC, "sclk_spi1_peric", "div_sclk_spi1_b", + ENABLE_SCLK_TOP_PERIC, 1, CLK_SET_RATE_PARENT, 0), + GATE(CLK_SCLK_SPI0_PERIC, "sclk_spi0_peric", "div_sclk_spi0_b", + ENABLE_SCLK_TOP_PERIC, 0, CLK_SET_RATE_PARENT, 0), +}; + +/* + * ATLAS_PLL & APOLLO_PLL & MEM0_PLL & MEM1_PLL & BUS_PLL & MFC_PLL + * & MPHY_PLL & G3D_PLL & DISP_PLL & ISP_PLL + */ +static struct samsung_pll_rate_table exynos5443_pll_rates[] = { + PLL_35XX_RATE(2500000000U, 625, 6, 0), + PLL_35XX_RATE(2400000000U, 500, 5, 0), + PLL_35XX_RATE(2300000000U, 575, 6, 0), + PLL_35XX_RATE(2200000000U, 550, 6, 0), + PLL_35XX_RATE(2100000000U, 350, 4, 0), + PLL_35XX_RATE(2000000000U, 500, 6, 0), + PLL_35XX_RATE(1900000000U, 475, 6, 0), + PLL_35XX_RATE(1800000000U, 375, 5, 0), + PLL_35XX_RATE(1700000000U, 425, 6, 0), + PLL_35XX_RATE(1600000000U, 400, 6, 0), + PLL_35XX_RATE(1500000000U, 250, 4, 0), + PLL_35XX_RATE(1400000000U, 350, 6, 0), + PLL_35XX_RATE(1332000000U, 222, 4, 0), + PLL_35XX_RATE(1300000000U, 325, 6, 0), + PLL_35XX_RATE(1200000000U, 500, 5, 1), + PLL_35XX_RATE(1100000000U, 550, 6, 1), + PLL_35XX_RATE(1086000000U, 362, 4, 1), + PLL_35XX_RATE(1066000000U, 533, 6, 1), + PLL_35XX_RATE(1000000000U, 500, 6, 1), + PLL_35XX_RATE(933000000U, 311, 4, 1), + PLL_35XX_RATE(921000000U, 307, 4, 1), + PLL_35XX_RATE(900000000U, 375, 5, 1), + PLL_35XX_RATE(825000000U, 275, 4, 1), + PLL_35XX_RATE(800000000U, 400, 6, 1), + PLL_35XX_RATE(733000000U, 733, 12, 1), + PLL_35XX_RATE(700000000U, 360, 6, 1), + PLL_35XX_RATE(667000000U, 222, 4, 1), + PLL_35XX_RATE(633000000U, 211, 4, 1), + PLL_35XX_RATE(600000000U, 500, 5, 2), + PLL_35XX_RATE(552000000U, 460, 5, 2), + PLL_35XX_RATE(550000000U, 550, 6, 2), + PLL_35XX_RATE(543000000U, 362, 4, 2), + PLL_35XX_RATE(533000000U, 533, 6, 2), + PLL_35XX_RATE(500000000U, 500, 6, 2), + PLL_35XX_RATE(444000000U, 370, 5, 2), + PLL_35XX_RATE(420000000U, 350, 5, 2), + PLL_35XX_RATE(400000000U, 400, 6, 2), + PLL_35XX_RATE(350000000U, 360, 6, 2), + PLL_35XX_RATE(333000000U, 222, 4, 2), + PLL_35XX_RATE(300000000U, 500, 5, 3), + PLL_35XX_RATE(266000000U, 532, 6, 3), + PLL_35XX_RATE(200000000U, 400, 6, 3), + PLL_35XX_RATE(166000000U, 332, 6, 3), + PLL_35XX_RATE(160000000U, 320, 6, 3), + PLL_35XX_RATE(133000000U, 552, 6, 4), + PLL_35XX_RATE(100000000U, 400, 6, 4), + { /* sentinel */ } +}; + +/* AUD_PLL */ +static struct samsung_pll_rate_table exynos5443_aud_pll_rates[] = { + PLL_36XX_RATE(400000000U, 200, 3, 2, 0), + PLL_36XX_RATE(393216000U, 197, 3, 2, -25690), + PLL_36XX_RATE(384000000U, 128, 2, 2, 0), + PLL_36XX_RATE(368640000U, 246, 4, 2, -15729), + PLL_36XX_RATE(361507200U, 181, 3, 2, -16148), + PLL_36XX_RATE(338688000U, 113, 2, 2, -6816), + PLL_36XX_RATE(294912000U, 98, 1, 3, 19923), + PLL_36XX_RATE(288000000U, 96, 1, 3, 0), + PLL_36XX_RATE(252000000U, 84, 1, 3, 0), + { /* sentinel */ } +}; + +static struct samsung_pll_clock top_pll_clks[] __initdata = { + PLL(pll_35xx, CLK_FOUT_ISP_PLL, "fout_isp_pll", "oscclk", + ISP_PLL_LOCK, ISP_PLL_CON0, exynos5443_pll_rates), + PLL(pll_36xx, CLK_FOUT_AUD_PLL, "fout_aud_pll", "oscclk", + AUD_PLL_LOCK, AUD_PLL_CON0, exynos5443_aud_pll_rates), +}; + +static struct samsung_cmu_info top_cmu_info __initdata = { + .pll_clks = top_pll_clks, + .nr_pll_clks = ARRAY_SIZE(top_pll_clks), + .mux_clks = top_mux_clks, + .nr_mux_clks = ARRAY_SIZE(top_mux_clks), + .div_clks = top_div_clks, + .nr_div_clks = ARRAY_SIZE(top_div_clks), + .gate_clks = top_gate_clks, + .nr_gate_clks = ARRAY_SIZE(top_gate_clks), + .nr_clk_ids = TOP_NR_CLK, + .clk_regs = top_clk_regs, + .nr_clk_regs = ARRAY_SIZE(top_clk_regs), +}; + +static void __init exynos5433_cmu_top_init(struct device_node *np) +{ + samsung_cmu_register_one(np, &top_cmu_info); +} +CLK_OF_DECLARE(exynos5433_cmu_top, "samsung,exynos5433-cmu-top", + exynos5433_cmu_top_init); + +/* + * Register offset definitions for CMU_CPIF + */ +#define MPHY_PLL_LOCK 0x0000 +#define MPHY_PLL_CON0 0x0100 +#define MPHY_PLL_CON1 0x0104 +#define MPHY_PLL_FREQ_DET 0x010c +#define MUX_SEL_CPIF0 0x0200 +#define DIV_CPIF 0x0600 +#define ENABLE_SCLK_CPIF 0x0a00 + +static unsigned long cpif_clk_regs[] __initdata = { + MPHY_PLL_LOCK, + MPHY_PLL_CON0, + MPHY_PLL_CON1, + MPHY_PLL_FREQ_DET, + MUX_SEL_CPIF0, + ENABLE_SCLK_CPIF, +}; + +/* list of all parent clock list */ +PNAME(mout_mphy_pll_p) = { "oscclk", "fout_mphy_pll", }; + +static struct samsung_pll_clock cpif_pll_clks[] __initdata = { + PLL(pll_35xx, CLK_FOUT_MPHY_PLL, "fout_mphy_pll", "oscclk", + MPHY_PLL_LOCK, MPHY_PLL_CON0, exynos5443_pll_rates), +}; + +static struct samsung_mux_clock cpif_mux_clks[] __initdata = { + /* MUX_SEL_CPIF0 */ + MUX(CLK_MOUT_MPHY_PLL, "mout_mphy_pll", mout_mphy_pll_p, MUX_SEL_CPIF0, + 0, 1), +}; + +static struct samsung_div_clock cpif_div_clks[] __initdata = { + /* DIV_CPIF */ + DIV(CLK_DIV_SCLK_MPHY, "div_sclk_mphy", "mout_mphy_pll", DIV_CPIF, + 0, 6), +}; + +static struct samsung_gate_clock cpif_gate_clks[] __initdata = { + /* ENABLE_SCLK_CPIF */ + GATE(CLK_SCLK_MPHY_PLL, "sclk_mphy_pll", "mout_mphy_pll", + ENABLE_SCLK_CPIF, 9, 0, 0), + GATE(CLK_SCLK_UFS_MPHY, "sclk_ufs_mphy", "div_sclk_mphy", + ENABLE_SCLK_CPIF, 4, 0, 0), +}; + +static struct samsung_cmu_info cpif_cmu_info __initdata = { + .pll_clks = cpif_pll_clks, + .nr_pll_clks = ARRAY_SIZE(cpif_pll_clks), + .mux_clks = cpif_mux_clks, + .nr_mux_clks = ARRAY_SIZE(cpif_mux_clks), + .div_clks = cpif_div_clks, + .nr_div_clks = ARRAY_SIZE(cpif_div_clks), + .gate_clks = cpif_gate_clks, + .nr_gate_clks = ARRAY_SIZE(cpif_gate_clks), + .nr_clk_ids = CPIF_NR_CLK, + .clk_regs = cpif_clk_regs, + .nr_clk_regs = ARRAY_SIZE(cpif_clk_regs), +}; + +static void __init exynos5433_cmu_cpif_init(struct device_node *np) +{ + samsung_cmu_register_one(np, &cpif_cmu_info); +} +CLK_OF_DECLARE(exynos5433_cmu_cpif, "samsung,exynos5433-cmu-cpif", + exynos5433_cmu_cpif_init); + +/* + * Register offset definitions for CMU_MIF + */ +#define MEM0_PLL_LOCK 0x0000 +#define MEM1_PLL_LOCK 0x0004 +#define BUS_PLL_LOCK 0x0008 +#define MFC_PLL_LOCK 0x000c +#define MEM0_PLL_CON0 0x0100 +#define MEM0_PLL_CON1 0x0104 +#define MEM0_PLL_FREQ_DET 0x010c +#define MEM1_PLL_CON0 0x0110 +#define MEM1_PLL_CON1 0x0114 +#define MEM1_PLL_FREQ_DET 0x011c +#define BUS_PLL_CON0 0x0120 +#define BUS_PLL_CON1 0x0124 +#define BUS_PLL_FREQ_DET 0x012c +#define MFC_PLL_CON0 0x0130 +#define MFC_PLL_CON1 0x0134 +#define MFC_PLL_FREQ_DET 0x013c + +static unsigned long mif_clk_regs[] __initdata = { + MEM0_PLL_LOCK, + MEM1_PLL_LOCK, + BUS_PLL_LOCK, + MFC_PLL_LOCK, + MEM0_PLL_CON0, + MEM0_PLL_CON1, + MEM0_PLL_FREQ_DET, + MEM1_PLL_CON0, + MEM1_PLL_CON1, + MEM1_PLL_FREQ_DET, + BUS_PLL_CON0, + BUS_PLL_CON1, + BUS_PLL_FREQ_DET, + MFC_PLL_CON0, + MFC_PLL_CON1, + MFC_PLL_FREQ_DET, +}; + +static struct samsung_pll_clock mif_pll_clks[] __initdata = { + PLL(pll_35xx, CLK_FOUT_MEM0_PLL, "fout_mem0_pll", "oscclk", + MEM0_PLL_LOCK, MEM0_PLL_CON0, exynos5443_pll_rates), + PLL(pll_35xx, CLK_FOUT_MEM1_PLL, "fout_mem1_pll", "oscclk", + MEM1_PLL_LOCK, MEM1_PLL_CON0, exynos5443_pll_rates), + PLL(pll_35xx, CLK_FOUT_BUS_PLL, "fout_bus_pll", "oscclk", + BUS_PLL_LOCK, BUS_PLL_CON0, exynos5443_pll_rates), + PLL(pll_35xx, CLK_FOUT_MFC_PLL, "fout_mfc_pll", "oscclk", + MFC_PLL_LOCK, MFC_PLL_CON0, exynos5443_pll_rates), +}; + +static struct samsung_cmu_info mif_cmu_info __initdata = { + .pll_clks = mif_pll_clks, + .nr_pll_clks = ARRAY_SIZE(mif_pll_clks), + .nr_clk_ids = MIF_NR_CLK, + .clk_regs = mif_clk_regs, + .nr_clk_regs = ARRAY_SIZE(mif_clk_regs), +}; + +static void __init exynos5433_cmu_mif_init(struct device_node *np) +{ + samsung_cmu_register_one(np, &mif_cmu_info); +} +CLK_OF_DECLARE(exynos5433_cmu_mif, "samsung,exynos5433-cmu-mif", + exynos5433_cmu_mif_init); + +/* + * Register offset definitions for CMU_PERIC + */ +#define DIV_PERIC 0x0600 +#define ENABLE_ACLK_PERIC 0x0800 +#define ENABLE_PCLK_PERIC0 0x0900 +#define ENABLE_PCLK_PERIC1 0x0904 +#define ENABLE_SCLK_PERIC 0x0A00 +#define ENABLE_IP_PERIC0 0x0B00 +#define ENABLE_IP_PERIC1 0x0B04 +#define ENABLE_IP_PERIC2 0x0B08 + +static unsigned long peric_clk_regs[] __initdata = { + DIV_PERIC, + ENABLE_ACLK_PERIC, + ENABLE_PCLK_PERIC0, + ENABLE_PCLK_PERIC1, + ENABLE_SCLK_PERIC, + ENABLE_IP_PERIC0, + ENABLE_IP_PERIC1, + ENABLE_IP_PERIC2, +}; + +static struct samsung_gate_clock peric_gate_clks[] __initdata = { + /* ENABLE_PCLK_PERIC0 */ + GATE(CLK_PCLK_SPI2, "pclk_spi2", "aclk_peric_66", ENABLE_PCLK_PERIC0, + 23, CLK_SET_RATE_PARENT, 0), + GATE(CLK_PCLK_SPI1, "pclk_spi1", "aclk_peric_66", ENABLE_PCLK_PERIC0, + 22, CLK_SET_RATE_PARENT, 0), + GATE(CLK_PCLK_SPI0, "pclk_spi0", "aclk_peric_66", ENABLE_PCLK_PERIC0, + 21, CLK_SET_RATE_PARENT, 0), + GATE(CLK_PCLK_UART2, "pclk_uart2", "aclk_peric_66", ENABLE_PCLK_PERIC0, + 14, CLK_SET_RATE_PARENT, 0), + GATE(CLK_PCLK_UART1, "pclk_uart1", "aclk_peric_66", ENABLE_PCLK_PERIC0, + 13, CLK_SET_RATE_PARENT, 0), + GATE(CLK_PCLK_UART0, "pclk_uart0", "aclk_peric_66", ENABLE_PCLK_PERIC0, + 12, CLK_SET_RATE_PARENT, 0), + GATE(CLK_PCLK_HSI2C3, "pclk_hsi2c3", "aclk_peric_66", + ENABLE_PCLK_PERIC0, 11, CLK_SET_RATE_PARENT, 0), + GATE(CLK_PCLK_HSI2C2, "pclk_hsi2c2", "aclk_peric_66", + ENABLE_PCLK_PERIC0, 10, CLK_SET_RATE_PARENT, 0), + GATE(CLK_PCLK_HSI2C1, "pclk_hsi2c1", "aclk_peric_66", + ENABLE_PCLK_PERIC0, 9, CLK_SET_RATE_PARENT, 0), + GATE(CLK_PCLK_HSI2C0, "pclk_hsi2c0", "aclk_peric_66", + ENABLE_PCLK_PERIC0, 8, CLK_SET_RATE_PARENT, 0), + GATE(CLK_PCLK_I2C7, "pclk_i2c7", "aclk_peric_66", ENABLE_PCLK_PERIC0, + 7, CLK_SET_RATE_PARENT, 0), + GATE(CLK_PCLK_I2C6, "pclk_i2c6", "aclk_peric_66", ENABLE_PCLK_PERIC0, + 6, CLK_SET_RATE_PARENT, 0), + GATE(CLK_PCLK_I2C5, "pclk_i2c5", "aclk_peric_66", ENABLE_PCLK_PERIC0, + 5, CLK_SET_RATE_PARENT, 0), + GATE(CLK_PCLK_I2C4, "pclk_i2c4", "aclk_peric_66", ENABLE_PCLK_PERIC0, + 4, CLK_SET_RATE_PARENT, 0), + GATE(CLK_PCLK_I2C3, "pclk_i2c3", "aclk_peric_66", ENABLE_PCLK_PERIC0, + 3, CLK_SET_RATE_PARENT, 0), + GATE(CLK_PCLK_I2C2, "pclk_i2c2", "aclk_peric_66", ENABLE_PCLK_PERIC0, + 2, CLK_SET_RATE_PARENT, 0), + GATE(CLK_PCLK_I2C1, "pclk_i2c1", "aclk_peric_66", ENABLE_PCLK_PERIC0, + 1, CLK_SET_RATE_PARENT, 0), + GATE(CLK_PCLK_I2C0, "pclk_i2c0", "aclk_peric_66", ENABLE_PCLK_PERIC0, + 0, CLK_SET_RATE_PARENT, 0), + + /* ENABLE_PCLK_PERIC1 */ + GATE(CLK_PCLK_SPI4, "pclk_spi4", "aclk_peric_66", ENABLE_PCLK_PERIC1, + 9, CLK_SET_RATE_PARENT, 0), + GATE(CLK_PCLK_SPI3, "pclk_spi3", "aclk_peric_66", ENABLE_PCLK_PERIC1, + 8, CLK_SET_RATE_PARENT, 0), + GATE(CLK_PCLK_HSI2C11, "pclk_hsi2c11", "aclk_peric_66", + ENABLE_PCLK_PERIC1, 7, CLK_SET_RATE_PARENT, 0), + GATE(CLK_PCLK_HSI2C10, "pclk_hsi2c10", "aclk_peric_66", + ENABLE_PCLK_PERIC1, 6, CLK_SET_RATE_PARENT, 0), + GATE(CLK_PCLK_HSI2C9, "pclk_hsi2c9", "aclk_peric_66", + ENABLE_PCLK_PERIC1, 5, CLK_SET_RATE_PARENT, 0), + GATE(CLK_PCLK_HSI2C8, "pclk_hsi2c8", "aclk_peric_66", + ENABLE_PCLK_PERIC1, 4, CLK_SET_RATE_PARENT, 0), + GATE(CLK_PCLK_HSI2C7, "pclk_hsi2c7", "aclk_peric_66", + ENABLE_PCLK_PERIC1, 3, CLK_SET_RATE_PARENT, 0), + GATE(CLK_PCLK_HSI2C6, "pclk_hsi2c6", "aclk_peric_66", + ENABLE_PCLK_PERIC1, 2, CLK_SET_RATE_PARENT, 0), + GATE(CLK_PCLK_HSI2C5, "pclk_hsi2c5", "aclk_peric_66", + ENABLE_PCLK_PERIC1, 1, CLK_SET_RATE_PARENT, 0), + GATE(CLK_PCLK_HSI2C4, "pclk_hsi2c4", "aclk_peric_66", + ENABLE_PCLK_PERIC1, 0, CLK_SET_RATE_PARENT, 0), + + /* ENABLE_SCLK_PERIC */ + GATE(CLK_SCLK_SPI4, "sclk_spi4", "sclk_spi4_peric", ENABLE_SCLK_PERIC, + 19, CLK_SET_RATE_PARENT, 0), + GATE(CLK_SCLK_SPI3, "sclk_spi3", "sclk_spi3_peric", ENABLE_SCLK_PERIC, + 18, CLK_SET_RATE_PARENT, 0), + + GATE(CLK_SCLK_SPI2, "sclk_spi2", "sclk_spi2_peric", ENABLE_SCLK_PERIC, + 5, CLK_SET_RATE_PARENT, 0), + GATE(CLK_SCLK_SPI1, "sclk_spi1", "sclk_spi1_peric", ENABLE_SCLK_PERIC, + 4, CLK_SET_RATE_PARENT, 0), + GATE(CLK_SCLK_SPI0, "sclk_spi0", "sclk_spi0_peric", ENABLE_SCLK_PERIC, + 3, CLK_SET_RATE_PARENT, 0), + GATE(CLK_SCLK_UART2, "sclk_uart2", "sclk_uart2_peric", + ENABLE_SCLK_PERIC, 2, CLK_SET_RATE_PARENT, 0), + GATE(CLK_SCLK_UART1, "sclk_uart1", "sclk_uart1_peric", + ENABLE_SCLK_PERIC, 1, CLK_SET_RATE_PARENT, 0), + GATE(CLK_SCLK_UART0, "sclk_uart0", "sclk_uart0_peric", + ENABLE_SCLK_PERIC, 0, CLK_SET_RATE_PARENT, 0), +}; + +static struct samsung_cmu_info peric_cmu_info __initdata = { + .gate_clks = peric_gate_clks, + .nr_gate_clks = ARRAY_SIZE(peric_gate_clks), + .nr_clk_ids = PERIC_NR_CLK, + .clk_regs = peric_clk_regs, + .nr_clk_regs = ARRAY_SIZE(peric_clk_regs), +}; + +static void __init exynos5433_cmu_peric_init(struct device_node *np) +{ + samsung_cmu_register_one(np, &peric_cmu_info); +} + +CLK_OF_DECLARE(exynos5433_cmu_peric, "samsung,exynos5433-cmu-peric", + exynos5433_cmu_peric_init); + +/* + * Register offset definitions for CMU_PERIS + */ +#define ENABLE_ACLK_PERIS 0x0800 +#define ENABLE_PCLK_PERIS 0x0900 + +static unsigned long peris_clk_regs[] __initdata = { + ENABLE_ACLK_PERIS, + ENABLE_PCLK_PERIS, +}; + +static struct samsung_gate_clock peris_gate_clks[] __initdata = { + /* ENABLE_PCLK_PERIS */ + GATE(CLK_PCLK_HPM_APBIF, "pclk_hpm_apbif", "aclk_peris_66", + ENABLE_PCLK_PERIS, 30, CLK_IGNORE_UNUSED, 0), + GATE(CLK_PCLK_TMU1_APBIF, "pclk_tmu1_apbif", "aclk_peris_66", + ENABLE_PCLK_PERIS, 24, CLK_IGNORE_UNUSED, 0), + GATE(CLK_PCLK_TMU0_APBIF, "pclk_tmu0_apbif", "aclk_peris_66", + ENABLE_PCLK_PERIS, 23, CLK_IGNORE_UNUSED, 0), + GATE(CLK_PCLK_PMU_PERIS, "pclk_pmu_peris", "aclk_peris_66", + ENABLE_PCLK_PERIS, 22, CLK_IGNORE_UNUSED, 0), + GATE(CLK_PCLK_SYSREG_PERIS, "pclk_sysreg_peris", "aclk_peris_66", + ENABLE_PCLK_PERIS, 21, CLK_IGNORE_UNUSED, 0), + GATE(CLK_PCLK_CMU_TOP_APBIF, "pclk_cmu_top_apbif", "aclk_peris_66", + ENABLE_PCLK_PERIS, 20, CLK_IGNORE_UNUSED, 0), + GATE(CLK_PCLK_WDT_APOLLO, "pclk_wdt_apollo", "aclk_peris_66", + ENABLE_PCLK_PERIS, 17, CLK_IGNORE_UNUSED, 0), + GATE(CLK_PCLK_WDT_ATLAS, "pclk_wdt_atlas", "aclk_peris_66", + ENABLE_PCLK_PERIS, 16, CLK_IGNORE_UNUSED, 0), + GATE(CLK_PCLK_MCT, "pclk_mct", "aclk_peris_66", + ENABLE_PCLK_PERIS, 15, CLK_IGNORE_UNUSED, 0), + GATE(CLK_PCLK_HDMI_CEC, "pclk_hdmi_cec", "aclk_peris_66", + ENABLE_PCLK_PERIS, 14, CLK_IGNORE_UNUSED, 0), +}; + +static struct samsung_cmu_info peris_cmu_info __initdata = { + .gate_clks = peris_gate_clks, + .nr_gate_clks = ARRAY_SIZE(peris_gate_clks), + .nr_clk_ids = PERIS_NR_CLK, + .clk_regs = peris_clk_regs, + .nr_clk_regs = ARRAY_SIZE(peris_clk_regs), +}; + +static void __init exynos5433_cmu_peris_init(struct device_node *np) +{ + samsung_cmu_register_one(np, &peris_cmu_info); +} + +CLK_OF_DECLARE(exynos5433_cmu_peris, "samsung,exynos5433-cmu-peris", + exynos5433_cmu_peris_init); + +/* + * Register offset definitions for CMU_FSYS + */ +#define MUX_SEL_FSYS0 0x0200 +#define MUX_SEL_FSYS1 0x0204 +#define MUX_SEL_FSYS2 0x0208 +#define MUX_SEL_FSYS3 0x020c +#define MUX_SEL_FSYS4 0x0210 +#define MUX_ENABLE_FSYS0 0x0300 +#define MUX_ENABLE_FSYS1 0x0304 +#define MUX_ENABLE_FSYS2 0x0308 +#define MUX_ENABLE_FSYS3 0x030c +#define MUX_ENABLE_FSYS4 0x0310 +#define MUX_STAT_FSYS0 0x0400 +#define MUX_STAT_FSYS1 0x0404 +#define MUX_STAT_FSYS2 0x0408 +#define MUX_STAT_FSYS3 0x040c +#define MUX_STAT_FSYS4 0x0410 +#define MUX_IGNORE_FSYS2 0x0508 +#define MUX_IGNORE_FSYS3 0x050c +#define ENABLE_ACLK_FSYS0 0x0800 +#define ENABLE_ACLK_FSYS1 0x0804 +#define ENABLE_PCLK_FSYS 0x0900 +#define ENABLE_SCLK_FSYS 0x0a00 +#define ENABLE_IP_FSYS0 0x0b00 +#define ENABLE_IP_FSYS1 0x0b04 + +/* list of all parent clock list */ +PNAME(mout_aclk_fsys_200_user_p) = { "oscclk", "div_aclk_fsys_200", }; +PNAME(mout_sclk_mmc2_user_p) = { "oscclk", "sclk_mmc2_fsys", }; +PNAME(mout_sclk_mmc1_user_p) = { "oscclk", "sclk_mmc1_fsys", }; +PNAME(mout_sclk_mmc0_user_p) = { "oscclk", "sclk_mmc0_fsys", }; + +static unsigned long fsys_clk_regs[] __initdata = { + MUX_SEL_FSYS0, + MUX_SEL_FSYS1, + MUX_SEL_FSYS2, + MUX_SEL_FSYS3, + MUX_SEL_FSYS4, + MUX_ENABLE_FSYS0, + MUX_ENABLE_FSYS1, + MUX_ENABLE_FSYS2, + MUX_ENABLE_FSYS3, + MUX_ENABLE_FSYS4, + MUX_STAT_FSYS0, + MUX_STAT_FSYS1, + MUX_STAT_FSYS2, + MUX_STAT_FSYS3, + MUX_STAT_FSYS4, + MUX_IGNORE_FSYS2, + MUX_IGNORE_FSYS3, + ENABLE_ACLK_FSYS0, + ENABLE_ACLK_FSYS1, + ENABLE_PCLK_FSYS, + ENABLE_SCLK_FSYS, + ENABLE_IP_FSYS0, + ENABLE_IP_FSYS1, +}; + +static struct samsung_mux_clock fsys_mux_clks[] __initdata = { + /* MUX_SEL_FSYS0 */ + MUX(CLK_MOUT_ACLK_FSYS_200_USER, "mout_aclk_fsys_200_user", + mout_aclk_fsys_200_user_p, MUX_SEL_FSYS0, 0, 1), + + /* MUX_SEL_FSYS1 */ + MUX(CLK_MOUT_SCLK_MMC2_USER, "mout_sclk_mmc2_user", + mout_sclk_mmc2_user_p, MUX_SEL_FSYS1, 20, 1), + MUX(CLK_MOUT_SCLK_MMC1_USER, "mout_sclk_mmc1_user", + mout_sclk_mmc1_user_p, MUX_SEL_FSYS1, 16, 1), + MUX(CLK_MOUT_SCLK_MMC0_USER, "mout_sclk_mmc0_user", + mout_sclk_mmc0_user_p, MUX_SEL_FSYS1, 12, 1), +}; + +static struct samsung_gate_clock fsys_gate_clks[] __initdata = { + /* ENABLE_ACLK_FSYS0 */ + GATE(CLK_ACLK_PCIE, "aclk_pcie", "mout_aclk_fsys_200_user", + ENABLE_ACLK_FSYS0, 13, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_PDMA1, "aclk_pdma1", "mout_aclk_fsys_200_user", + ENABLE_ACLK_FSYS0, 11, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_TSI, "aclk_tsi", "mout_aclk_fsys_200_user", + ENABLE_ACLK_FSYS0, 10, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_MMC2, "aclk_mmc2", "mout_aclk_fsys_200_user", + ENABLE_ACLK_FSYS0, 8, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_MMC1, "aclk_mmc1", "mout_aclk_fsys_200_user", + ENABLE_ACLK_FSYS0, 7, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_MMC0, "aclk_mmc0", "mout_aclk_fsys_200_user", + ENABLE_ACLK_FSYS0, 6, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_UFS, "aclk_ufs", "mout_aclk_fsys_200_user", + ENABLE_ACLK_FSYS0, 5, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_USBHOST20, "aclk_usbhost20", "mout_aclk_fsys_200_user", + ENABLE_ACLK_FSYS0, 3, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_USBHOST30, "aclk_usbhost30", "mout_aclk_fsys_200_user", + ENABLE_ACLK_FSYS0, 2, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_USBDRD30, "aclk_usbdrd30", "mout_aclk_fsys_200_user", + ENABLE_ACLK_FSYS0, 1, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_PDMA0, "aclk_pdma0", "mout_aclk_fsys_200_user", + ENABLE_ACLK_FSYS0, 0, CLK_IGNORE_UNUSED, 0), + + /* ENABLE_SCLK_FSYS */ + GATE(CLK_SCLK_MMC2, "sclk_mmc2", "mout_sclk_mmc2_user", + ENABLE_SCLK_FSYS, 4, CLK_SET_RATE_PARENT, 0), + GATE(CLK_SCLK_MMC1, "sclk_mmc1", "mout_sclk_mmc1_user", + ENABLE_SCLK_FSYS, 3, CLK_SET_RATE_PARENT, 0), + GATE(CLK_SCLK_MMC0, "sclk_mmc0", "mout_sclk_mmc0_user", + ENABLE_SCLK_FSYS, 2, CLK_SET_RATE_PARENT, 0), + + /* ENABLE_IP_FSYS0 */ + GATE(CLK_PDMA1, "pdma1", "aclk_pdma1", ENABLE_IP_FSYS0, 15, 0, 0), + GATE(CLK_PDMA0, "pdma0", "aclk_pdma0", ENABLE_IP_FSYS0, 0, 0, 0), +}; + +static struct samsung_cmu_info fsys_cmu_info __initdata = { + .mux_clks = fsys_mux_clks, + .nr_mux_clks = ARRAY_SIZE(fsys_mux_clks), + .gate_clks = fsys_gate_clks, + .nr_gate_clks = ARRAY_SIZE(fsys_gate_clks), + .nr_clk_ids = FSYS_NR_CLK, + .clk_regs = fsys_clk_regs, + .nr_clk_regs = ARRAY_SIZE(fsys_clk_regs), +}; + +static void __init exynos5433_cmu_fsys_init(struct device_node *np) +{ + samsung_cmu_register_one(np, &fsys_cmu_info); +} + +CLK_OF_DECLARE(exynos5433_cmu_fsys, "samsung,exynos5433-cmu-fsys", + exynos5433_cmu_fsys_init); diff --git a/include/dt-bindings/clock/exynos5433.h b/include/dt-bindings/clock/exynos5433.h new file mode 100644 index 000000000000..5c4e2e39ce49 --- /dev/null +++ b/include/dt-bindings/clock/exynos5433.h @@ -0,0 +1,199 @@ +/* + * Copyright (c) 2014 Samsung Electronics Co., Ltd. + * Author: Chanwoo Choi + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef _DT_BINDINGS_CLOCK_EXYNOS5433_H +#define _DT_BINDINGS_CLOCK_EXYNOS5433_H + +/* CMU_TOP */ +#define CLK_FOUT_ISP_PLL 1 +#define CLK_FOUT_AUD_PLL 2 + +#define CLK_MOUT_AUD_PLL 10 +#define CLK_MOUT_ISP_PLL 11 +#define CLK_MOUT_AUD_PLL_USER_T 12 +#define CLK_MOUT_MPHY_PLL_USER 13 +#define CLK_MOUT_MFC_PLL_USER 14 +#define CLK_MOUT_BUS_PLL_USER 15 +#define CLK_MOUT_ACLK_HEVC_400 16 +#define CLK_MOUT_ACLK_CAM1_333 17 +#define CLK_MOUT_ACLK_CAM1_552_B 18 +#define CLK_MOUT_ACLK_CAM1_552_A 19 +#define CLK_MOUT_ACLK_ISP_DIS_400 20 +#define CLK_MOUT_ACLK_ISP_400 21 +#define CLK_MOUT_ACLK_BUS0_400 22 +#define CLK_MOUT_ACLK_MSCL_400_B 23 +#define CLK_MOUT_ACLK_MSCL_400_A 24 +#define CLK_MOUT_ACLK_GSCL_333 25 +#define CLK_MOUT_ACLK_G2D_400_B 26 +#define CLK_MOUT_ACLK_G2D_400_A 27 +#define CLK_MOUT_SCLK_JPEG_C 28 +#define CLK_MOUT_SCLK_JPEG_B 29 +#define CLK_MOUT_SCLK_JPEG_A 30 +#define CLK_MOUT_SCLK_MMC2_B 31 +#define CLK_MOUT_SCLK_MMC2_A 32 +#define CLK_MOUT_SCLK_MMC1_B 33 +#define CLK_MOUT_SCLK_MMC1_A 34 +#define CLK_MOUT_SCLK_MMC0_D 35 +#define CLK_MOUT_SCLK_MMC0_C 36 +#define CLK_MOUT_SCLK_MMC0_B 37 +#define CLK_MOUT_SCLK_MMC0_A 38 +#define CLK_MOUT_SCLK_SPI4 39 +#define CLK_MOUT_SCLK_SPI3 40 +#define CLK_MOUT_SCLK_UART2 41 +#define CLK_MOUT_SCLK_UART1 42 +#define CLK_MOUT_SCLK_UART0 43 +#define CLK_MOUT_SCLK_SPI2 44 +#define CLK_MOUT_SCLK_SPI1 45 +#define CLK_MOUT_SCLK_SPI0 46 + +#define CLK_DIV_ACLK_FSYS_200 100 +#define CLK_DIV_ACLK_IMEM_SSSX_266 101 +#define CLK_DIV_ACLK_IMEM_200 102 +#define CLK_DIV_ACLK_IMEM_266 103 +#define CLK_DIV_ACLK_PERIC_66_B 104 +#define CLK_DIV_ACLK_PERIC_66_A 105 +#define CLK_DIV_ACLK_PERIS_66_B 106 +#define CLK_DIV_ACLK_PERIS_66_A 107 +#define CLK_DIV_SCLK_MMC1_B 108 +#define CLK_DIV_SCLK_MMC1_A 109 +#define CLK_DIV_SCLK_MMC0_B 110 +#define CLK_DIV_SCLK_MMC0_A 111 +#define CLK_DIV_SCLK_MMC2_B 112 +#define CLK_DIV_SCLK_MMC2_A 113 +#define CLK_DIV_SCLK_SPI1_B 114 +#define CLK_DIV_SCLK_SPI1_A 115 +#define CLK_DIV_SCLK_SPI0_B 116 +#define CLK_DIV_SCLK_SPI0_A 117 +#define CLK_DIV_SCLK_SPI2_B 118 +#define CLK_DIV_SCLK_SPI2_A 119 +#define CLK_DIV_SCLK_UART2 120 +#define CLK_DIV_SCLK_UART1 121 +#define CLK_DIV_SCLK_UART0 122 +#define CLK_DIV_SCLK_SPI4_B 123 +#define CLK_DIV_SCLK_SPI4_A 124 +#define CLK_DIV_SCLK_SPI3_B 125 +#define CLK_DIV_SCLK_SPI3_A 126 + +#define CLK_ACLK_PERIC_66 200 +#define CLK_ACLK_PERIS_66 201 +#define CLK_ACLK_FSYS_200 202 +#define CLK_SCLK_MMC2_FSYS 203 +#define CLK_SCLK_MMC1_FSYS 204 +#define CLK_SCLK_MMC0_FSYS 205 +#define CLK_SCLK_SPI4_PERIC 206 +#define CLK_SCLK_SPI3_PERIC 207 +#define CLK_SCLK_UART2_PERIC 208 +#define CLK_SCLK_UART1_PERIC 209 +#define CLK_SCLK_UART0_PERIC 210 +#define CLK_SCLK_SPI2_PERIC 211 +#define CLK_SCLK_SPI1_PERIC 212 +#define CLK_SCLK_SPI0_PERIC 213 + +#define TOP_NR_CLK 214 + +/* CMU_CPIF */ +#define CLK_FOUT_MPHY_PLL 1 + +#define CLK_MOUT_MPHY_PLL 2 + +#define CLK_DIV_SCLK_MPHY 10 + +#define CLK_SCLK_MPHY_PLL 11 +#define CLK_SCLK_UFS_MPHY 11 + +#define CPIF_NR_CLK 12 + +/* CMU_MIF */ +#define CLK_FOUT_MEM0_PLL 1 +#define CLK_FOUT_MEM1_PLL 2 +#define CLK_FOUT_BUS_PLL 3 +#define CLK_FOUT_MFC_PLL 4 + +#define MIF_NR_CLK 5 + +/* CMU_PERIC */ +#define CLK_PCLK_SPI2 1 +#define CLK_PCLK_SPI1 2 +#define CLK_PCLK_SPI0 3 +#define CLK_PCLK_UART2 4 +#define CLK_PCLK_UART1 5 +#define CLK_PCLK_UART0 6 +#define CLK_PCLK_HSI2C3 7 +#define CLK_PCLK_HSI2C2 8 +#define CLK_PCLK_HSI2C1 9 +#define CLK_PCLK_HSI2C0 10 +#define CLK_PCLK_I2C7 11 +#define CLK_PCLK_I2C6 12 +#define CLK_PCLK_I2C5 13 +#define CLK_PCLK_I2C4 14 +#define CLK_PCLK_I2C3 15 +#define CLK_PCLK_I2C2 16 +#define CLK_PCLK_I2C1 17 +#define CLK_PCLK_I2C0 18 +#define CLK_PCLK_SPI4 19 +#define CLK_PCLK_SPI3 20 +#define CLK_PCLK_HSI2C11 21 +#define CLK_PCLK_HSI2C10 22 +#define CLK_PCLK_HSI2C9 23 +#define CLK_PCLK_HSI2C8 24 +#define CLK_PCLK_HSI2C7 25 +#define CLK_PCLK_HSI2C6 26 +#define CLK_PCLK_HSI2C5 27 +#define CLK_PCLK_HSI2C4 28 +#define CLK_SCLK_SPI4 29 +#define CLK_SCLK_SPI3 30 +#define CLK_SCLK_SPI2 31 +#define CLK_SCLK_SPI1 32 +#define CLK_SCLK_SPI0 33 +#define CLK_SCLK_UART2 34 +#define CLK_SCLK_UART1 35 +#define CLK_SCLK_UART0 36 + +#define PERIC_NR_CLK 37 + +/* CMU_PERIS */ +#define CLK_PCLK_HPM_APBIF 1 +#define CLK_PCLK_TMU1_APBIF 2 +#define CLK_PCLK_TMU0_APBIF 3 +#define CLK_PCLK_PMU_PERIS 4 +#define CLK_PCLK_SYSREG_PERIS 5 +#define CLK_PCLK_CMU_TOP_APBIF 6 +#define CLK_PCLK_WDT_APOLLO 7 +#define CLK_PCLK_WDT_ATLAS 8 +#define CLK_PCLK_MCT 9 +#define CLK_PCLK_HDMI_CEC 10 + +#define PERIS_NR_CLK 11 + +/* CMU_FSYS */ +#define CLK_MOUT_ACLK_FSYS_200_USER 1 +#define CLK_MOUT_SCLK_MMC2_USER 2 +#define CLK_MOUT_SCLK_MMC1_USER 3 +#define CLK_MOUT_SCLK_MMC0_USER 4 + +#define CLK_ACLK_PCIE 50 +#define CLK_ACLK_PDMA1 51 +#define CLK_ACLK_TSI 52 +#define CLK_ACLK_MMC2 53 +#define CLK_ACLK_MMC1 54 +#define CLK_ACLK_MMC0 55 +#define CLK_ACLK_UFS 56 +#define CLK_ACLK_USBHOST20 57 +#define CLK_ACLK_USBHOST30 58 +#define CLK_ACLK_USBDRD30 59 +#define CLK_ACLK_PDMA0 60 +#define CLK_SCLK_MMC2 61 +#define CLK_SCLK_MMC1 62 +#define CLK_SCLK_MMC0 63 +#define CLK_PDMA1 64 +#define CLK_PDMA0 65 + +#define FSYS_NR_CLK 66 + +#endif /* _DT_BINDINGS_CLOCK_EXYNOS5433_H */ -- cgit v1.2.3 From 232364969d8a8a17c52fd9b754d15924abf98d6a Mon Sep 17 00:00:00 2001 From: Chanwoo Choi Date: Mon, 2 Feb 2015 23:23:57 +0900 Subject: clk: samsung: exynos5433: Add MUX clocks of CMU_TOP domain This patch adds the MUX (multiplexer) clocks for CMU_TOP domain of Exynos5433. CMU_TOP domain provides source clocks to other CMU domains. Signed-off-by: Chanwoo Choi Acked-by: Inki Dae Reviewed-by: Pankaj Dubey Signed-off-by: Sylwester Nawrocki --- drivers/clk/samsung/clk-exynos5433.c | 90 ++++++++++++++++++++++++++++++++++ include/dt-bindings/clock/exynos5433.h | 31 +++++++++++- 2 files changed, 119 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/drivers/clk/samsung/clk-exynos5433.c b/drivers/clk/samsung/clk-exynos5433.c index a4047390bfc2..5585dce04a38 100644 --- a/drivers/clk/samsung/clk-exynos5433.c +++ b/drivers/clk/samsung/clk-exynos5433.c @@ -208,6 +208,7 @@ PNAME(mout_mphy_pll_user_p) = { "oscclk", "sclk_mphy_pll", }; PNAME(mout_mfc_pll_user_p) = { "oscclk", "sclk_mfc_pll", }; PNAME(mout_bus_pll_user_p) = { "oscclk", "sclk_bus_pll", }; PNAME(mout_bus_pll_user_t_p) = { "oscclk", "mout_bus_pll_user", }; +PNAME(mout_mphy_pll_user_t_p) = { "oscclk", "mout_mphy_pll_user", }; PNAME(mout_bus_mfc_pll_user_p) = { "mout_bus_pll_user", "mout_mfc_pll_user",}; PNAME(mout_mfc_bus_pll_user_p) = { "mout_mfc_pll_user", "mout_bus_pll_user",}; @@ -215,6 +216,12 @@ PNAME(mout_aclk_cam1_552_b_p) = { "mout_aclk_cam1_552_a", "mout_mfc_pll_user", }; PNAME(mout_aclk_cam1_552_a_p) = { "mout_isp_pll", "mout_bus_pll_user", }; +PNAME(mout_aclk_mfc_400_c_p) = { "mout_aclk_mfc_400_b", + "mout_mphy_pll_user", }; +PNAME(mout_aclk_mfc_400_b_p) = { "mout_aclk_mfc_400_a", + "mout_bus_pll_user", }; +PNAME(mout_aclk_mfc_400_a_p) = { "mout_mfc_pll_user", "mout_isp_pll", }; + PNAME(mout_bus_mphy_pll_user_p) = { "mout_bus_pll_user", "mout_mphy_pll_user", }; PNAME(mout_aclk_mscl_b_p) = { "mout_aclk_mscl_400_a", @@ -231,6 +238,21 @@ PNAME(mout_sclk_mmc0_d_p) = { "mout_sclk_mmc0_c", "mout_isp_pll", }; PNAME(mout_sclk_mmc0_c_p) = { "mout_sclk_mmc0_b", "mout_mphy_pll_user",}; PNAME(mout_sclk_mmc0_b_p) = { "mout_sclk_mmc0_a", "mout_mfc_pll_user", }; +PNAME(mout_sclk_spdif_p) = { "sclk_audio0", "sclk_audio1", + "oscclk", "ioclk_spdif_extclk", }; +PNAME(mout_sclk_audio1_p) = { "ioclk_audiocdclk1", "oscclk", + "mout_aud_pll_user_t",}; +PNAME(mout_sclk_audio0_p) = { "ioclk_audiocdclk0", "oscclk", + "mout_aud_pll_user_t",}; + +static struct samsung_fixed_rate_clock top_fixed_clks[] __initdata = { + /* Xi2s{0|1}CDCLK input clock for I2S/PCM */ + FRATE(0, "ioclk_audiocdclk1", NULL, CLK_IS_ROOT, 100000000), + FRATE(0, "ioclk_audiocdclk0", NULL, CLK_IS_ROOT, 100000000), + /* Xi2s1SDI input clock for SPDIF */ + FRATE(0, "ioclk_spdif_extclk", NULL, CLK_IS_ROOT, 100000000), +}; + static struct samsung_mux_clock top_mux_clks[] __initdata = { /* MUX_SEL_TOP0 */ MUX(CLK_MOUT_AUD_PLL, "mout_aud_pll", mout_aud_pll_p, MUX_SEL_TOP0, @@ -276,6 +298,14 @@ static struct samsung_mux_clock top_mux_clks[] __initdata = { MUX(CLK_MOUT_ACLK_G2D_400_A, "mout_aclk_g2d_400_a", mout_bus_mfc_pll_user_p, MUX_SEL_TOP3, 0, 1), + /* MUX_SEL_TOP4 */ + MUX(CLK_MOUT_ACLK_MFC_400_C, "mout_aclk_mfc_400_c", + mout_aclk_mfc_400_c_p, MUX_SEL_TOP4, 8, 1), + MUX(CLK_MOUT_ACLK_MFC_400_B, "mout_aclk_mfc_400_b", + mout_aclk_mfc_400_b_p, MUX_SEL_TOP4, 4, 1), + MUX(CLK_MOUT_ACLK_MFC_400_A, "mout_aclk_mfc_400_a", + mout_aclk_mfc_400_a_p, MUX_SEL_TOP4, 0, 1), + /* MUX_SEL_TOP_MSCL */ MUX(CLK_MOUT_SCLK_JPEG_C, "mout_sclk_jpeg_c", mout_sclk_jpeg_c_p, MUX_SEL_TOP_MSCL, 8, 1), @@ -284,6 +314,20 @@ static struct samsung_mux_clock top_mux_clks[] __initdata = { MUX(CLK_MOUT_SCLK_JPEG_A, "mout_sclk_jpeg_a", mout_bus_pll_user_t_p, MUX_SEL_TOP_MSCL, 0, 1), + /* MUX_SEL_TOP_CAM1 */ + MUX(CLK_MOUT_SCLK_ISP_SENSOR2, "mout_sclk_isp_sensor2", + mout_bus_pll_user_t_p, MUX_SEL_TOP_CAM1, 24, 1), + MUX(CLK_MOUT_SCLK_ISP_SENSOR1, "mout_sclk_isp_sensor1", + mout_bus_pll_user_t_p, MUX_SEL_TOP_CAM1, 20, 1), + MUX(CLK_MOUT_SCLK_ISP_SENSOR0, "mout_sclk_isp_sensor0", + mout_bus_pll_user_t_p, MUX_SEL_TOP_CAM1, 16, 1), + MUX(CLK_MOUT_SCLK_ISP_UART, "mout_sclk_isp_uart", + mout_bus_pll_user_t_p, MUX_SEL_TOP_CAM1, 8, 1), + MUX(CLK_MOUT_SCLK_ISP_SPI1, "mout_sclk_isp_spi1", + mout_bus_pll_user_t_p, MUX_SEL_TOP_CAM1, 4, 1), + MUX(CLK_MOUT_SCLK_ISP_SPI0, "mout_sclk_isp_spi0", + mout_bus_pll_user_t_p, MUX_SEL_TOP_CAM1, 0, 1), + /* MUX_SEL_TOP_FSYS0 */ MUX(CLK_MOUT_SCLK_MMC2_B, "mout_sclk_mmc2_b", mout_sclk_mmc2_b_p, MUX_SEL_TOP_FSYS0, 28, 1), @@ -302,6 +346,16 @@ static struct samsung_mux_clock top_mux_clks[] __initdata = { MUX(CLK_MOUT_SCLK_MMC0_A, "mout_sclk_mmc0_a", mout_bus_pll_user_t_p, MUX_SEL_TOP_FSYS0, 0, 1), + /* MUX_SEL_TOP_FSYS1 */ + MUX(CLK_MOUT_SCLK_PCIE_100, "mout_sclk_pcie_100", mout_bus_pll_user_t_p, + MUX_SEL_TOP_FSYS1, 12, 1), + MUX(CLK_MOUT_SCLK_UFSUNIPRO, "mout_sclk_ufsunipro", + mout_mphy_pll_user_t_p, MUX_SEL_TOP_FSYS1, 8, 1), + MUX(CLK_MOUT_SCLK_USBHOST30, "mout_sclk_usbhost30", + mout_bus_pll_user_t_p, MUX_SEL_TOP_FSYS1, 4, 1), + MUX(CLK_MOUT_SCLK_USBDRD30, "mout_sclk_usbdrd30", + mout_bus_pll_user_t_p, MUX_SEL_TOP_FSYS1, 0, 1), + /* MUX_SEL_TOP_PERIC0 */ MUX(CLK_MOUT_SCLK_SPI4, "mout_sclk_spi4", mout_bus_pll_user_t_p, MUX_SEL_TOP_PERIC0, 28, 1), @@ -319,6 +373,16 @@ static struct samsung_mux_clock top_mux_clks[] __initdata = { MUX_SEL_TOP_PERIC0, 4, 1), MUX(CLK_MOUT_SCLK_SPI0, "mout_sclk_spi0", mout_bus_pll_user_t_p, MUX_SEL_TOP_PERIC0, 0, 1), + + /* MUX_SEL_TOP_PERIC1 */ + MUX(CLK_MOUT_SCLK_SLIMBUS, "mout_sclk_slimbus", mout_aud_pll_user_p, + MUX_SEL_TOP_PERIC1, 16, 1), + MUX(CLK_MOUT_SCLK_SPDIF, "mout_sclk_spdif", mout_sclk_spdif_p, + MUX_SEL_TOP_PERIC1, 12, 2), + MUX(CLK_MOUT_SCLK_AUDIO1, "mout_sclk_audio1", mout_sclk_audio1_p, + MUX_SEL_TOP_PERIC1, 4, 2), + MUX(CLK_MOUT_SCLK_AUDIO0, "mout_sclk_audio0", mout_sclk_audio0_p, + MUX_SEL_TOP_PERIC1, 0, 2), }; static struct samsung_div_clock top_div_clks[] __initdata = { @@ -382,6 +446,16 @@ static struct samsung_div_clock top_div_clks[] __initdata = { DIV(CLK_DIV_SCLK_UART0, "div_sclk_uart0", "mout_sclk_uart1", DIV_TOP_PERIC2, 0, 4), + /* DIV_TOP_PERIC3 */ + DIV(CLK_DIV_SCLK_I2S1, "div_sclk_i2s1", "sclk_audio1", + DIV_TOP_PERIC3, 16, 6), + DIV(CLK_DIV_SCLK_PCM1, "div_sclk_pcm1", "sclk_audio1", + DIV_TOP_PERIC3, 8, 8), + DIV(CLK_DIV_SCLK_AUDIO1, "div_sclk_audio1", "mout_sclk_audio1", + DIV_TOP_PERIC3, 4, 4), + DIV(CLK_DIV_SCLK_AUDIO0, "div_sclk_audio0", "mout_sclk_audio0", + DIV_TOP_PERIC3, 0, 4), + /* DIV_TOP_PERIC4 */ DIV(CLK_DIV_SCLK_SPI4_B, "div_sclk_spi4_b", "div_sclk_spi4_a", DIV_TOP_PERIC4, 16, 8), @@ -418,6 +492,12 @@ static struct samsung_gate_clock top_gate_clks[] __initdata = { ENABLE_SCLK_TOP_PERIC, 12, CLK_SET_RATE_PARENT, 0), GATE(CLK_SCLK_SPI3_PERIC, "sclk_spi3_peric", "div_sclk_spi3_b", ENABLE_SCLK_TOP_PERIC, 11, CLK_SET_RATE_PARENT, 0), + GATE(CLK_SCLK_SPDIF_PERIC, "sclk_spdif_peric", "mout_sclk_spdif", + ENABLE_SCLK_TOP_PERIC, 9, CLK_SET_RATE_PARENT, 0), + GATE(CLK_SCLK_I2S1_PERIC, "sclk_i2s1_peric", "div_sclk_i2s1", + ENABLE_SCLK_TOP_PERIC, 8, CLK_SET_RATE_PARENT, 0), + GATE(CLK_SCLK_PCM1_PERIC, "sclk_pcm1_peric", "div_sclk_pcm1", + ENABLE_SCLK_TOP_PERIC, 7, CLK_SET_RATE_PARENT, 0), GATE(CLK_SCLK_UART2_PERIC, "sclk_uart2_peric", "div_sclk_uart2", ENABLE_SCLK_TOP_PERIC, 5, CLK_SET_RATE_PARENT, 0), GATE(CLK_SCLK_UART1_PERIC, "sclk_uart1_peric", "div_sclk_uart1", @@ -430,6 +510,14 @@ static struct samsung_gate_clock top_gate_clks[] __initdata = { ENABLE_SCLK_TOP_PERIC, 1, CLK_SET_RATE_PARENT, 0), GATE(CLK_SCLK_SPI0_PERIC, "sclk_spi0_peric", "div_sclk_spi0_b", ENABLE_SCLK_TOP_PERIC, 0, CLK_SET_RATE_PARENT, 0), + + /* MUX_ENABLE_TOP_PERIC1 */ + GATE(CLK_SCLK_SLIMBUS, "sclk_slimbus", "mout_sclk_slimbus", + MUX_ENABLE_TOP_PERIC1, 16, 0, 0), + GATE(CLK_SCLK_AUDIO1, "sclk_audio1", "div_sclk_audio1", + MUX_ENABLE_TOP_PERIC1, 4, 0, 0), + GATE(CLK_SCLK_AUDIO0, "sclk_audio0", "div_sclk_audio0", + MUX_ENABLE_TOP_PERIC1, 0, 0, 0), }; /* @@ -516,6 +604,8 @@ static struct samsung_cmu_info top_cmu_info __initdata = { .nr_div_clks = ARRAY_SIZE(top_div_clks), .gate_clks = top_gate_clks, .nr_gate_clks = ARRAY_SIZE(top_gate_clks), + .fixed_clks = top_fixed_clks, + .nr_fixed_clks = ARRAY_SIZE(top_fixed_clks), .nr_clk_ids = TOP_NR_CLK, .clk_regs = top_clk_regs, .nr_clk_regs = ARRAY_SIZE(top_clk_regs), diff --git a/include/dt-bindings/clock/exynos5433.h b/include/dt-bindings/clock/exynos5433.h index 5c4e2e39ce49..8f0ee5a13db4 100644 --- a/include/dt-bindings/clock/exynos5433.h +++ b/include/dt-bindings/clock/exynos5433.h @@ -51,6 +51,23 @@ #define CLK_MOUT_SCLK_SPI2 44 #define CLK_MOUT_SCLK_SPI1 45 #define CLK_MOUT_SCLK_SPI0 46 +#define CLK_MOUT_ACLK_MFC_400_C 47 +#define CLK_MOUT_ACLK_MFC_400_B 48 +#define CLK_MOUT_ACLK_MFC_400_A 49 +#define CLK_MOUT_SCLK_ISP_SENSOR2 50 +#define CLK_MOUT_SCLK_ISP_SENSOR1 51 +#define CLK_MOUT_SCLK_ISP_SENSOR0 52 +#define CLK_MOUT_SCLK_ISP_UART 53 +#define CLK_MOUT_SCLK_ISP_SPI1 54 +#define CLK_MOUT_SCLK_ISP_SPI0 55 +#define CLK_MOUT_SCLK_PCIE_100 56 +#define CLK_MOUT_SCLK_UFSUNIPRO 57 +#define CLK_MOUT_SCLK_USBHOST30 58 +#define CLK_MOUT_SCLK_USBDRD30 59 +#define CLK_MOUT_SCLK_SLIMBUS 60 +#define CLK_MOUT_SCLK_SPDIF 61 +#define CLK_MOUT_SCLK_AUDIO1 62 +#define CLK_MOUT_SCLK_AUDIO0 63 #define CLK_DIV_ACLK_FSYS_200 100 #define CLK_DIV_ACLK_IMEM_SSSX_266 101 @@ -79,6 +96,10 @@ #define CLK_DIV_SCLK_SPI4_A 124 #define CLK_DIV_SCLK_SPI3_B 125 #define CLK_DIV_SCLK_SPI3_A 126 +#define CLK_DIV_SCLK_I2S1 127 +#define CLK_DIV_SCLK_PCM1 128 +#define CLK_DIV_SCLK_AUDIO1 129 +#define CLK_DIV_SCLK_AUDIO0 130 #define CLK_ACLK_PERIC_66 200 #define CLK_ACLK_PERIS_66 201 @@ -94,8 +115,14 @@ #define CLK_SCLK_SPI2_PERIC 211 #define CLK_SCLK_SPI1_PERIC 212 #define CLK_SCLK_SPI0_PERIC 213 - -#define TOP_NR_CLK 214 +#define CLK_SCLK_SPDIF_PERIC 214 +#define CLK_SCLK_I2S1_PERIC 215 +#define CLK_SCLK_PCM1_PERIC 216 +#define CLK_SCLK_SLIMBUS 217 +#define CLK_SCLK_AUDIO1 218 +#define CLK_SCLK_AUDIO0 219 + +#define TOP_NR_CLK 220 /* CMU_CPIF */ #define CLK_FOUT_MPHY_PLL 1 -- cgit v1.2.3 From d0f5de6677de4405c9acdb88db7c7cf7b9cc954e Mon Sep 17 00:00:00 2001 From: Chanwoo Choi Date: Mon, 2 Feb 2015 23:23:58 +0900 Subject: clk: samsung: exynos5433: Add clocks for CMU_PERIC domain This patch adds missing divider/gate clocks of CMU_PERIC domain which includes I2S/PCM/SPDIF/PWM/SLIMBUS IPs. The SPI/I2S may use external input clock which has 'ioclk_*' prefix. Signed-off-by: Chanwoo Choi [ideal.song: Change clk flags of to pclk_gpio_* clk, pclk_gpio_* should be always on] Signed-off-by: Inha Song Acked-by: Inki Dae Signed-off-by: Sylwester Nawrocki --- drivers/clk/samsung/clk-exynos5433.c | 83 +++++++++++++++++++++++++++++++++- include/dt-bindings/clock/exynos5433.h | 34 +++++++++++++- 2 files changed, 114 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/drivers/clk/samsung/clk-exynos5433.c b/drivers/clk/samsung/clk-exynos5433.c index 5585dce04a38..10a9f8313c24 100644 --- a/drivers/clk/samsung/clk-exynos5433.c +++ b/drivers/clk/samsung/clk-exynos5433.c @@ -251,6 +251,14 @@ static struct samsung_fixed_rate_clock top_fixed_clks[] __initdata = { FRATE(0, "ioclk_audiocdclk0", NULL, CLK_IS_ROOT, 100000000), /* Xi2s1SDI input clock for SPDIF */ FRATE(0, "ioclk_spdif_extclk", NULL, CLK_IS_ROOT, 100000000), + /* XspiCLK[4:0] input clock for SPI */ + FRATE(0, "ioclk_spi4_clk_in", NULL, CLK_IS_ROOT, 50000000), + FRATE(0, "ioclk_spi3_clk_in", NULL, CLK_IS_ROOT, 50000000), + FRATE(0, "ioclk_spi2_clk_in", NULL, CLK_IS_ROOT, 50000000), + FRATE(0, "ioclk_spi1_clk_in", NULL, CLK_IS_ROOT, 50000000), + FRATE(0, "ioclk_spi0_clk_in", NULL, CLK_IS_ROOT, 50000000), + /* Xi2s1SCLK input clock for I2S1_BCLK */ + FRATE(0, "ioclk_i2s1_bclk_in", NULL, CLK_IS_ROOT, 12288000), }; static struct samsung_mux_clock top_mux_clks[] __initdata = { @@ -756,6 +764,7 @@ CLK_OF_DECLARE(exynos5433_cmu_mif, "samsung,exynos5433-cmu-mif", * Register offset definitions for CMU_PERIC */ #define DIV_PERIC 0x0600 +#define DIV_STAT_PERIC 0x0700 #define ENABLE_ACLK_PERIC 0x0800 #define ENABLE_PCLK_PERIC0 0x0900 #define ENABLE_PCLK_PERIC1 0x0904 @@ -766,6 +775,7 @@ CLK_OF_DECLARE(exynos5433_cmu_mif, "samsung,exynos5433-cmu-mif", static unsigned long peric_clk_regs[] __initdata = { DIV_PERIC, + DIV_STAT_PERIC, ENABLE_ACLK_PERIC, ENABLE_PCLK_PERIC0, ENABLE_PCLK_PERIC1, @@ -775,14 +785,57 @@ static unsigned long peric_clk_regs[] __initdata = { ENABLE_IP_PERIC2, }; +static struct samsung_div_clock peric_div_clks[] __initdata = { + /* DIV_PERIC */ + DIV(CLK_DIV_SCLK_SCI, "div_sclk_sci", "oscclk", DIV_PERIC, 4, 4), + DIV(CLK_DIV_SCLK_SC_IN, "div_sclk_sc_in", "oscclk", DIV_PERIC, 0, 4), +}; + static struct samsung_gate_clock peric_gate_clks[] __initdata = { + /* ENABLE_ACLK_PERIC */ + GATE(CLK_ACLK_AHB2APB_PERIC2P, "aclk_ahb2apb_peric2p", "aclk_peric_66", + ENABLE_ACLK_PERIC, 3, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_AHB2APB_PERIC1P, "aclk_ahb2apb_peric1p", "aclk_peric_66", + ENABLE_ACLK_PERIC, 2, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_AHB2APB_PERIC0P, "aclk_ahb2apb_peric0p", "aclk_peric_66", + ENABLE_ACLK_PERIC, 1, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_PERICNP_66, "aclk_pericnp_66", "aclk_peric_66", + ENABLE_ACLK_PERIC, 0, CLK_IGNORE_UNUSED, 0), + /* ENABLE_PCLK_PERIC0 */ + GATE(CLK_PCLK_SCI, "pclk_sci", "aclk_peric_66", ENABLE_PCLK_PERIC0, + 31, CLK_SET_RATE_PARENT, 0), + GATE(CLK_PCLK_GPIO_FINGER, "pclk_gpio_finger", "aclk_peric_66", + ENABLE_PCLK_PERIC0, 30, CLK_IGNORE_UNUSED, 0), + GATE(CLK_PCLK_GPIO_ESE, "pclk_gpio_ese", "aclk_peric_66", + ENABLE_PCLK_PERIC0, 29, CLK_IGNORE_UNUSED, 0), + GATE(CLK_PCLK_PWM, "pclk_pwm", "aclk_peric_66", ENABLE_PCLK_PERIC0, + 28, CLK_SET_RATE_PARENT, 0), + GATE(CLK_PCLK_SPDIF, "pclk_spdif", "aclk_peric_66", ENABLE_PCLK_PERIC0, + 26, CLK_SET_RATE_PARENT, 0), + GATE(CLK_PCLK_PCM1, "pclk_pcm1", "aclk_peric_66", ENABLE_PCLK_PERIC0, + 25, CLK_SET_RATE_PARENT, 0), + GATE(CLK_PCLK_I2S1, "pclk_i2s", "aclk_peric_66", ENABLE_PCLK_PERIC0, + 24, CLK_SET_RATE_PARENT, 0), GATE(CLK_PCLK_SPI2, "pclk_spi2", "aclk_peric_66", ENABLE_PCLK_PERIC0, 23, CLK_SET_RATE_PARENT, 0), GATE(CLK_PCLK_SPI1, "pclk_spi1", "aclk_peric_66", ENABLE_PCLK_PERIC0, 22, CLK_SET_RATE_PARENT, 0), GATE(CLK_PCLK_SPI0, "pclk_spi0", "aclk_peric_66", ENABLE_PCLK_PERIC0, 21, CLK_SET_RATE_PARENT, 0), + GATE(CLK_PCLK_ADCIF, "pclk_adcif", "aclk_peric_66", ENABLE_PCLK_PERIC0, + 20, CLK_SET_RATE_PARENT, 0), + GATE(CLK_PCLK_GPIO_TOUCH, "pclk_gpio_touch", "aclk_peric_66", + ENABLE_PCLK_PERIC0, 19, CLK_IGNORE_UNUSED, 0), + GATE(CLK_PCLK_GPIO_NFC, "pclk_gpio_nfc", "aclk_peric_66", + ENABLE_PCLK_PERIC0, 18, CLK_IGNORE_UNUSED, 0), + GATE(CLK_PCLK_GPIO_PERIC, "pclk_gpio_peric", "aclk_peric_66", + ENABLE_PCLK_PERIC0, 17, CLK_IGNORE_UNUSED, 0), + GATE(CLK_PCLK_PMU_PERIC, "pclk_pmu_peric", "aclk_peric_66", + ENABLE_PCLK_PERIC0, 16, CLK_SET_RATE_PARENT, 0), + GATE(CLK_PCLK_SYSREG_PERIC, "pclk_sysreg_peric", "aclk_peric_66", + ENABLE_PCLK_PERIC0, 15, + CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED, 0), GATE(CLK_PCLK_UART2, "pclk_uart2", "aclk_peric_66", ENABLE_PCLK_PERIC0, 14, CLK_SET_RATE_PARENT, 0), GATE(CLK_PCLK_UART1, "pclk_uart1", "aclk_peric_66", ENABLE_PCLK_PERIC0, @@ -837,15 +890,39 @@ static struct samsung_gate_clock peric_gate_clks[] __initdata = { ENABLE_PCLK_PERIC1, 0, CLK_SET_RATE_PARENT, 0), /* ENABLE_SCLK_PERIC */ + GATE(CLK_SCLK_IOCLK_SPI4, "sclk_ioclk_spi4", "ioclk_spi4_clk_in", + ENABLE_SCLK_PERIC, 21, CLK_SET_RATE_PARENT, 0), + GATE(CLK_SCLK_IOCLK_SPI3, "sclk_ioclk_spi3", "ioclk_spi3_clk_in", + ENABLE_SCLK_PERIC, 20, CLK_SET_RATE_PARENT, 0), GATE(CLK_SCLK_SPI4, "sclk_spi4", "sclk_spi4_peric", ENABLE_SCLK_PERIC, 19, CLK_SET_RATE_PARENT, 0), GATE(CLK_SCLK_SPI3, "sclk_spi3", "sclk_spi3_peric", ENABLE_SCLK_PERIC, 18, CLK_SET_RATE_PARENT, 0), - + GATE(CLK_SCLK_SCI, "sclk_sci", "div_sclk_sci", ENABLE_SCLK_PERIC, + 17, 0, 0), + GATE(CLK_SCLK_SC_IN, "sclk_sc_in", "div_sclk_sc_in", ENABLE_SCLK_PERIC, + 16, 0, 0), + GATE(CLK_SCLK_PWM, "sclk_pwm", "oscclk", ENABLE_SCLK_PERIC, 15, 0, 0), + GATE(CLK_SCLK_IOCLK_SPI2, "sclk_ioclk_spi2", "ioclk_spi2_clk_in", + ENABLE_SCLK_PERIC, 13, CLK_SET_RATE_PARENT, 0), + GATE(CLK_SCLK_IOCLK_SPI1, "sclk_ioclk_spi1", "ioclk_spi1_clk_in", + ENABLE_SCLK_PERIC, 12, + CLK_IGNORE_UNUSED | CLK_SET_RATE_PARENT, 0), + GATE(CLK_SCLK_IOCLK_SPI0, "sclk_ioclk_spi0", "ioclk_spi0_clk_in", + ENABLE_SCLK_PERIC, 11, CLK_SET_RATE_PARENT, 0), + GATE(CLK_SCLK_IOCLK_I2S1_BCLK, "sclk_ioclk_i2s1_bclk", + "ioclk_i2s1_bclk_in", ENABLE_SCLK_PERIC, 10, + CLK_SET_RATE_PARENT, 0), + GATE(CLK_SCLK_SPDIF, "sclk_spdif", "sclk_spdif_peric", + ENABLE_SCLK_PERIC, 8, CLK_SET_RATE_PARENT, 0), + GATE(CLK_SCLK_PCM1, "sclk_pcm1", "sclk_pcm1_peric", + ENABLE_SCLK_PERIC, 7, CLK_SET_RATE_PARENT, 0), + GATE(CLK_SCLK_I2S1, "sclk_i2s1", "sclk_i2s1_peric", + ENABLE_SCLK_PERIC, 6, CLK_SET_RATE_PARENT, 0), GATE(CLK_SCLK_SPI2, "sclk_spi2", "sclk_spi2_peric", ENABLE_SCLK_PERIC, 5, CLK_SET_RATE_PARENT, 0), GATE(CLK_SCLK_SPI1, "sclk_spi1", "sclk_spi1_peric", ENABLE_SCLK_PERIC, - 4, CLK_SET_RATE_PARENT, 0), + 4, CLK_IGNORE_UNUSED | CLK_SET_RATE_PARENT, 0), GATE(CLK_SCLK_SPI0, "sclk_spi0", "sclk_spi0_peric", ENABLE_SCLK_PERIC, 3, CLK_SET_RATE_PARENT, 0), GATE(CLK_SCLK_UART2, "sclk_uart2", "sclk_uart2_peric", @@ -857,6 +934,8 @@ static struct samsung_gate_clock peric_gate_clks[] __initdata = { }; static struct samsung_cmu_info peric_cmu_info __initdata = { + .div_clks = peric_div_clks, + .nr_div_clks = ARRAY_SIZE(peric_div_clks), .gate_clks = peric_gate_clks, .nr_gate_clks = ARRAY_SIZE(peric_gate_clks), .nr_clk_ids = PERIC_NR_CLK, diff --git a/include/dt-bindings/clock/exynos5433.h b/include/dt-bindings/clock/exynos5433.h index 8f0ee5a13db4..b5c66f7b83be 100644 --- a/include/dt-bindings/clock/exynos5433.h +++ b/include/dt-bindings/clock/exynos5433.h @@ -181,8 +181,40 @@ #define CLK_SCLK_UART2 34 #define CLK_SCLK_UART1 35 #define CLK_SCLK_UART0 36 +#define CLK_ACLK_AHB2APB_PERIC2P 37 +#define CLK_ACLK_AHB2APB_PERIC1P 38 +#define CLK_ACLK_AHB2APB_PERIC0P 39 +#define CLK_ACLK_PERICNP_66 40 +#define CLK_PCLK_SCI 41 +#define CLK_PCLK_GPIO_FINGER 42 +#define CLK_PCLK_GPIO_ESE 43 +#define CLK_PCLK_PWM 44 +#define CLK_PCLK_SPDIF 45 +#define CLK_PCLK_PCM1 46 +#define CLK_PCLK_I2S1 47 +#define CLK_PCLK_ADCIF 48 +#define CLK_PCLK_GPIO_TOUCH 49 +#define CLK_PCLK_GPIO_NFC 50 +#define CLK_PCLK_GPIO_PERIC 51 +#define CLK_PCLK_PMU_PERIC 52 +#define CLK_PCLK_SYSREG_PERIC 53 +#define CLK_SCLK_IOCLK_SPI4 54 +#define CLK_SCLK_IOCLK_SPI3 55 +#define CLK_SCLK_SCI 56 +#define CLK_SCLK_SC_IN 57 +#define CLK_SCLK_PWM 58 +#define CLK_SCLK_IOCLK_SPI2 59 +#define CLK_SCLK_IOCLK_SPI1 60 +#define CLK_SCLK_IOCLK_SPI0 61 +#define CLK_SCLK_IOCLK_I2S1_BCLK 62 +#define CLK_SCLK_SPDIF 63 +#define CLK_SCLK_PCM1 64 +#define CLK_SCLK_I2S1 65 -#define PERIC_NR_CLK 37 +#define CLK_DIV_SCLK_SCI 70 +#define CLK_DIV_SCLK_SC_IN 71 + +#define PERIC_NR_CLK 72 /* CMU_PERIS */ #define CLK_PCLK_HPM_APBIF 1 -- cgit v1.2.3 From 56bcf3f3ea39402acff09cba9558a0d3b13fc56f Mon Sep 17 00:00:00 2001 From: Chanwoo Choi Date: Mon, 2 Feb 2015 23:23:59 +0900 Subject: clk: samsung: exynos5433: Add clocks for CMU_PERIS domain This patch adds missing gate clocks of CMU_PERIS domain which includes TMU/TZPC/SECKEY/CHIPID/TOPRTC/EFUSE IPs. The special clocks of CMU_PERIS use oscclk source clock directly. Signed-off-by: Chanwoo Choi Acked-by: Inki Dae Reviewed-by: Pankaj Dubey Signed-off-by: Sylwester Nawrocki --- drivers/clk/samsung/clk-exynos5433.c | 151 ++++++++++++++++++++++++++++++++- include/dt-bindings/clock/exynos5433.h | 33 ++++++- 2 files changed, 181 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/drivers/clk/samsung/clk-exynos5433.c b/drivers/clk/samsung/clk-exynos5433.c index 10a9f8313c24..5ba9311624aa 100644 --- a/drivers/clk/samsung/clk-exynos5433.c +++ b/drivers/clk/samsung/clk-exynos5433.c @@ -245,6 +245,10 @@ PNAME(mout_sclk_audio1_p) = { "ioclk_audiocdclk1", "oscclk", PNAME(mout_sclk_audio0_p) = { "ioclk_audiocdclk0", "oscclk", "mout_aud_pll_user_t",}; +static struct samsung_fixed_factor_clock top_fixed_factor_clks[] __initdata = { + FFACTOR(0, "oscclk_efuse_common", "oscclk", 1, 1, 0), +}; + static struct samsung_fixed_rate_clock top_fixed_clks[] __initdata = { /* Xi2s{0|1}CDCLK input clock for I2S/PCM */ FRATE(0, "ioclk_audiocdclk1", NULL, CLK_IS_ROOT, 100000000), @@ -614,6 +618,8 @@ static struct samsung_cmu_info top_cmu_info __initdata = { .nr_gate_clks = ARRAY_SIZE(top_gate_clks), .fixed_clks = top_fixed_clks, .nr_fixed_clks = ARRAY_SIZE(top_fixed_clks), + .fixed_factor_clks = top_fixed_factor_clks, + .nr_fixed_factor_clks = ARRAY_SIZE(top_fixed_factor_clks), .nr_clk_ids = TOP_NR_CLK, .clk_regs = top_clk_regs, .nr_clk_regs = ARRAY_SIZE(top_clk_regs), @@ -954,15 +960,69 @@ CLK_OF_DECLARE(exynos5433_cmu_peric, "samsung,exynos5433-cmu-peric", /* * Register offset definitions for CMU_PERIS */ -#define ENABLE_ACLK_PERIS 0x0800 -#define ENABLE_PCLK_PERIS 0x0900 +#define ENABLE_ACLK_PERIS 0x0800 +#define ENABLE_PCLK_PERIS 0x0900 +#define ENABLE_PCLK_PERIS_SECURE_TZPC 0x0904 +#define ENABLE_PCLK_PERIS_SECURE_SECKEY_APBIF 0x0908 +#define ENABLE_PCLK_PERIS_SECURE_CHIPID_APBIF 0x090c +#define ENABLE_PCLK_PERIS_SECURE_TOPRTC 0x0910 +#define ENABLE_PCLK_PERIS_SECURE_CUSTOM_EFUSE_APBIF 0x0914 +#define ENABLE_PCLK_PERIS_SECURE_ANTIRBK_CNT_APBIF 0x0918 +#define ENABLE_PCLK_PERIS_SECURE_OTP_CON_APBIF 0x091c +#define ENABLE_SCLK_PERIS 0x0a00 +#define ENABLE_SCLK_PERIS_SECURE_SECKEY 0x0a04 +#define ENABLE_SCLK_PERIS_SECURE_CHIPID 0x0a08 +#define ENABLE_SCLK_PERIS_SECURE_TOPRTC 0x0a0c +#define ENABLE_SCLK_PERIS_SECURE_CUSTOM_EFUSE 0x0a10 +#define ENABLE_SCLK_PERIS_SECURE_ANTIRBK_CNT 0x0a14 +#define ENABLE_SCLK_PERIS_SECURE_OTP_CON 0x0a18 +#define ENABLE_IP_PERIS0 0x0b00 +#define ENABLE_IP_PERIS1 0x0b04 +#define ENABLE_IP_PERIS_SECURE_TZPC 0x0b08 +#define ENABLE_IP_PERIS_SECURE_SECKEY 0x0b0c +#define ENABLE_IP_PERIS_SECURE_CHIPID 0x0b10 +#define ENABLE_IP_PERIS_SECURE_TOPRTC 0x0b14 +#define ENABLE_IP_PERIS_SECURE_CUSTOM_EFUSE 0x0b18 +#define ENABLE_IP_PERIS_SECURE_ANTIBRK_CNT 0x0b1c +#define ENABLE_IP_PERIS_SECURE_OTP_CON 0x0b20 static unsigned long peris_clk_regs[] __initdata = { ENABLE_ACLK_PERIS, ENABLE_PCLK_PERIS, + ENABLE_PCLK_PERIS_SECURE_TZPC, + ENABLE_PCLK_PERIS_SECURE_SECKEY_APBIF, + ENABLE_PCLK_PERIS_SECURE_CHIPID_APBIF, + ENABLE_PCLK_PERIS_SECURE_TOPRTC, + ENABLE_PCLK_PERIS_SECURE_CUSTOM_EFUSE_APBIF, + ENABLE_PCLK_PERIS_SECURE_ANTIRBK_CNT_APBIF, + ENABLE_PCLK_PERIS_SECURE_OTP_CON_APBIF, + ENABLE_SCLK_PERIS, + ENABLE_SCLK_PERIS_SECURE_SECKEY, + ENABLE_SCLK_PERIS_SECURE_CHIPID, + ENABLE_SCLK_PERIS_SECURE_TOPRTC, + ENABLE_SCLK_PERIS_SECURE_CUSTOM_EFUSE, + ENABLE_SCLK_PERIS_SECURE_ANTIRBK_CNT, + ENABLE_SCLK_PERIS_SECURE_OTP_CON, + ENABLE_IP_PERIS0, + ENABLE_IP_PERIS1, + ENABLE_IP_PERIS_SECURE_TZPC, + ENABLE_IP_PERIS_SECURE_SECKEY, + ENABLE_IP_PERIS_SECURE_CHIPID, + ENABLE_IP_PERIS_SECURE_TOPRTC, + ENABLE_IP_PERIS_SECURE_CUSTOM_EFUSE, + ENABLE_IP_PERIS_SECURE_ANTIBRK_CNT, + ENABLE_IP_PERIS_SECURE_OTP_CON, }; static struct samsung_gate_clock peris_gate_clks[] __initdata = { + /* ENABLE_ACLK_PERIS */ + GATE(CLK_ACLK_AHB2APB_PERIS1P, "aclk_ahb2apb_peris1p", "aclk_peris_66", + ENABLE_ACLK_PERIS, 2, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_AHB2APB_PERIS0P, "aclk_ahb2apb_peris0p", "aclk_peris_66", + ENABLE_ACLK_PERIS, 1, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_PERISNP_66, "aclk_perisnp_66", "aclk_peris_66", + ENABLE_ACLK_PERIS, 0, CLK_IGNORE_UNUSED, 0), + /* ENABLE_PCLK_PERIS */ GATE(CLK_PCLK_HPM_APBIF, "pclk_hpm_apbif", "aclk_peris_66", ENABLE_PCLK_PERIS, 30, CLK_IGNORE_UNUSED, 0), @@ -984,6 +1044,93 @@ static struct samsung_gate_clock peris_gate_clks[] __initdata = { ENABLE_PCLK_PERIS, 15, CLK_IGNORE_UNUSED, 0), GATE(CLK_PCLK_HDMI_CEC, "pclk_hdmi_cec", "aclk_peris_66", ENABLE_PCLK_PERIS, 14, CLK_IGNORE_UNUSED, 0), + + /* ENABLE_PCLK_PERIS_SECURE_TZPC */ + GATE(CLK_PCLK_TZPC12, "pclk_tzpc12", "aclk_peris_66", + ENABLE_PCLK_PERIS_SECURE_TZPC, 12, 0, 0), + GATE(CLK_PCLK_TZPC11, "pclk_tzpc11", "aclk_peris_66", + ENABLE_PCLK_PERIS_SECURE_TZPC, 11, 0, 0), + GATE(CLK_PCLK_TZPC10, "pclk_tzpc10", "aclk_peris_66", + ENABLE_PCLK_PERIS_SECURE_TZPC, 10, 0, 0), + GATE(CLK_PCLK_TZPC9, "pclk_tzpc9", "aclk_peris_66", + ENABLE_PCLK_PERIS_SECURE_TZPC, 9, 0, 0), + GATE(CLK_PCLK_TZPC8, "pclk_tzpc8", "aclk_peris_66", + ENABLE_PCLK_PERIS_SECURE_TZPC, 8, 0, 0), + GATE(CLK_PCLK_TZPC7, "pclk_tzpc7", "aclk_peris_66", + ENABLE_PCLK_PERIS_SECURE_TZPC, 7, 0, 0), + GATE(CLK_PCLK_TZPC6, "pclk_tzpc6", "aclk_peris_66", + ENABLE_PCLK_PERIS_SECURE_TZPC, 6, 0, 0), + GATE(CLK_PCLK_TZPC5, "pclk_tzpc5", "aclk_peris_66", + ENABLE_PCLK_PERIS_SECURE_TZPC, 5, 0, 0), + GATE(CLK_PCLK_TZPC4, "pclk_tzpc4", "aclk_peris_66", + ENABLE_PCLK_PERIS_SECURE_TZPC, 4, 0, 0), + GATE(CLK_PCLK_TZPC3, "pclk_tzpc3", "aclk_peris_66", + ENABLE_PCLK_PERIS_SECURE_TZPC, 3, 0, 0), + GATE(CLK_PCLK_TZPC2, "pclk_tzpc2", "aclk_peris_66", + ENABLE_PCLK_PERIS_SECURE_TZPC, 2, 0, 0), + GATE(CLK_PCLK_TZPC1, "pclk_tzpc1", "aclk_peris_66", + ENABLE_PCLK_PERIS_SECURE_TZPC, 1, 0, 0), + GATE(CLK_PCLK_TZPC0, "pclk_tzpc0", "aclk_peris_66", + ENABLE_PCLK_PERIS_SECURE_TZPC, 0, 0, 0), + + /* ENABLE_PCLK_PERIS_SECURE_SECKEY_APBIF */ + GATE(CLK_PCLK_SECKEY_APBIF, "pclk_seckey_apbif", "aclk_peris_66", + ENABLE_PCLK_PERIS_SECURE_SECKEY_APBIF, 0, 0, 0), + + /* ENABLE_PCLK_PERIS_SECURE_CHIPID_APBIF */ + GATE(CLK_PCLK_CHIPID_APBIF, "pclk_chipid_apbif", "aclk_peris_66", + ENABLE_PCLK_PERIS_SECURE_CHIPID_APBIF, 0, 0, 0), + + /* ENABLE_PCLK_PERIS_SECURE_TOPRTC */ + GATE(CLK_PCLK_TOPRTC, "pclk_toprtc", "aclk_peris_66", + ENABLE_PCLK_PERIS_SECURE_TOPRTC, 0, 0, 0), + + /* ENABLE_PCLK_PERIS_SECURE_CUSTOM_EFUSE_APBIF */ + GATE(CLK_PCLK_CUSTOM_EFUSE_APBIF, "pclk_custom_efuse_apbif", + "aclk_peris_66", + ENABLE_PCLK_PERIS_SECURE_CUSTOM_EFUSE_APBIF, 0, 0, 0), + + /* ENABLE_PCLK_PERIS_SECURE_ANTIRBK_CNT_APBIF */ + GATE(CLK_PCLK_ANTIRBK_CNT_APBIF, "pclk_antirbk_cnt_apbif", + "aclk_peris_66", + ENABLE_PCLK_PERIS_SECURE_ANTIRBK_CNT_APBIF, 0, 0, 0), + + /* ENABLE_PCLK_PERIS_SECURE_OTP_CON_APBIF */ + GATE(CLK_PCLK_OTP_CON_APBIF, "pclk_otp_con_apbif", + "aclk_peris_66", + ENABLE_PCLK_PERIS_SECURE_OTP_CON_APBIF, 0, 0, 0), + + /* ENABLE_SCLK_PERIS */ + GATE(CLK_SCLK_ASV_TB, "sclk_asv_tb", "oscclk_efuse_common", + ENABLE_SCLK_PERIS, 10, 0, 0), + GATE(CLK_SCLK_TMU1, "sclk_tmu1", "oscclk_efuse_common", + ENABLE_SCLK_PERIS, 4, 0, 0), + GATE(CLK_SCLK_TMU0, "sclk_tmu0", "oscclk_efuse_common", + ENABLE_SCLK_PERIS, 3, 0, 0), + + /* ENABLE_SCLK_PERIS_SECURE_SECKEY */ + GATE(CLK_SCLK_SECKEY, "sclk_seckey", "oscclk_efuse_common", + ENABLE_SCLK_PERIS_SECURE_SECKEY, 0, 0, 0), + + /* ENABLE_SCLK_PERIS_SECURE_CHIPID */ + GATE(CLK_SCLK_CHIPID, "sclk_chipid", "oscclk_efuse_common", + ENABLE_SCLK_PERIS_SECURE_CHIPID, 0, 0, 0), + + /* ENABLE_SCLK_PERIS_SECURE_TOPRTC */ + GATE(CLK_SCLK_TOPRTC, "sclk_toprtc", "oscclk_efuse_common", + ENABLE_SCLK_PERIS_SECURE_TOPRTC, 0, 0, 0), + + /* ENABLE_SCLK_PERIS_SECURE_CUSTOM_EFUSE */ + GATE(CLK_SCLK_CUSTOM_EFUSE, "sclk_custom_efuse", "oscclk_efuse_common", + ENABLE_SCLK_PERIS_SECURE_CUSTOM_EFUSE, 0, 0, 0), + + /* ENABLE_SCLK_PERIS_SECURE_ANTIRBK_CNT */ + GATE(CLK_SCLK_ANTIRBK_CNT, "sclk_antirbk_cnt", "oscclk_efuse_common", + ENABLE_SCLK_PERIS_SECURE_ANTIRBK_CNT, 0, 0, 0), + + /* ENABLE_SCLK_PERIS_SECURE_OTP_CON */ + GATE(CLK_SCLK_OTP_CON, "sclk_otp_con", "oscclk_efuse_common", + ENABLE_SCLK_PERIS_SECURE_OTP_CON, 0, 0, 0), }; static struct samsung_cmu_info peris_cmu_info __initdata = { diff --git a/include/dt-bindings/clock/exynos5433.h b/include/dt-bindings/clock/exynos5433.h index b5c66f7b83be..5b3397d9843a 100644 --- a/include/dt-bindings/clock/exynos5433.h +++ b/include/dt-bindings/clock/exynos5433.h @@ -227,8 +227,39 @@ #define CLK_PCLK_WDT_ATLAS 8 #define CLK_PCLK_MCT 9 #define CLK_PCLK_HDMI_CEC 10 +#define CLK_ACLK_AHB2APB_PERIS1P 11 +#define CLK_ACLK_AHB2APB_PERIS0P 12 +#define CLK_ACLK_PERISNP_66 13 +#define CLK_PCLK_TZPC12 14 +#define CLK_PCLK_TZPC11 15 +#define CLK_PCLK_TZPC10 16 +#define CLK_PCLK_TZPC9 17 +#define CLK_PCLK_TZPC8 18 +#define CLK_PCLK_TZPC7 19 +#define CLK_PCLK_TZPC6 20 +#define CLK_PCLK_TZPC5 21 +#define CLK_PCLK_TZPC4 22 +#define CLK_PCLK_TZPC3 23 +#define CLK_PCLK_TZPC2 24 +#define CLK_PCLK_TZPC1 25 +#define CLK_PCLK_TZPC0 26 +#define CLK_PCLK_SECKEY_APBIF 27 +#define CLK_PCLK_CHIPID_APBIF 28 +#define CLK_PCLK_TOPRTC 29 +#define CLK_PCLK_CUSTOM_EFUSE_APBIF 30 +#define CLK_PCLK_ANTIRBK_CNT_APBIF 31 +#define CLK_PCLK_OTP_CON_APBIF 32 +#define CLK_SCLK_ASV_TB 33 +#define CLK_SCLK_TMU1 34 +#define CLK_SCLK_TMU0 35 +#define CLK_SCLK_SECKEY 36 +#define CLK_SCLK_CHIPID 37 +#define CLK_SCLK_TOPRTC 38 +#define CLK_SCLK_CUSTOM_EFUSE 39 +#define CLK_SCLK_ANTIRBK_CNT 40 +#define CLK_SCLK_OTP_CON 41 -#define PERIS_NR_CLK 11 +#define PERIS_NR_CLK 42 /* CMU_FSYS */ #define CLK_MOUT_ACLK_FSYS_200_USER 1 -- cgit v1.2.3 From a29308dad5dc4695a344ed9042cae8a1b8e35267 Mon Sep 17 00:00:00 2001 From: Chanwoo Choi Date: Mon, 2 Feb 2015 23:24:00 +0900 Subject: clk: samsung: exynos5433: Add clocks for CMU_G2D domain This patch adds ths mux/divider/gate clocks of CMU_G2D domain which includes G2D/MDMA IPs. The CMU_G2D requires its parent defined in the CMU_TOP domain. Hence this patch adds G2D related clocks to the CMU_TOP domain. Signed-off-by: Chanwoo Choi Acked-by: Inki Dae Reviewed-by: Pankaj Dubey Signed-off-by: Sylwester Nawrocki --- drivers/clk/samsung/clk-exynos5433.c | 146 +++++++++++++++++++++++++++++++++ include/dt-bindings/clock/exynos5433.h | 42 +++++++++- 2 files changed, 187 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/drivers/clk/samsung/clk-exynos5433.c b/drivers/clk/samsung/clk-exynos5433.c index 5ba9311624aa..24218dba8218 100644 --- a/drivers/clk/samsung/clk-exynos5433.c +++ b/drivers/clk/samsung/clk-exynos5433.c @@ -398,6 +398,20 @@ static struct samsung_mux_clock top_mux_clks[] __initdata = { }; static struct samsung_div_clock top_div_clks[] __initdata = { + /* DIV_TOP1 */ + DIV(CLK_DIV_ACLK_GSCL_111, "div_aclk_gscl_111", "mout_aclk_gscl_333", + DIV_TOP1, 28, 3), + DIV(CLK_DIV_ACLK_GSCL_333, "div_aclk_gscl_333", "mout_aclk_gscl_333", + DIV_TOP1, 24, 3), + DIV(CLK_DIV_ACLK_HEVC_400, "div_aclk_hevc_400", "mout_aclk_hevc_400", + DIV_TOP1, 20, 3), + DIV(CLK_DIV_ACLK_MFC_400, "div_aclk_mfc_400", "mout_aclk_mfc_400_c", + DIV_TOP1, 12, 3), + DIV(CLK_DIV_ACLK_G2D_266, "div_aclk_g2d_266", "mout_bus_pll_user", + DIV_TOP1, 8, 3), + DIV(CLK_DIV_ACLK_G2D_400, "div_aclk_g2d_400", "mout_aclk_g2d_400_b", + DIV_TOP1, 0, 3), + /* DIV_TOP2 */ DIV(CLK_DIV_ACLK_FSYS_200, "div_aclk_fsys_200", "mout_bus_pll_user", DIV_TOP2, 0, 3), @@ -490,6 +504,12 @@ static struct samsung_gate_clock top_gate_clks[] __initdata = { GATE(CLK_ACLK_FSYS_200, "aclk_fsys_200", "div_aclk_fsys_200", ENABLE_ACLK_TOP, 18, CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_G2D_266, "aclk_g2d_266", "div_aclk_g2d_266", + ENABLE_ACLK_TOP, 2, + CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_G2D_400, "aclk_g2d_400", "div_aclk_g2d_400", + ENABLE_ACLK_TOP, 0, + CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED, 0), /* ENABLE_SCLK_TOP_FSYS */ GATE(CLK_SCLK_MMC2_FSYS, "sclk_mmc2_fsys", "div_sclk_mmc2_b", @@ -1277,3 +1297,129 @@ static void __init exynos5433_cmu_fsys_init(struct device_node *np) CLK_OF_DECLARE(exynos5433_cmu_fsys, "samsung,exynos5433-cmu-fsys", exynos5433_cmu_fsys_init); + +/* + * Register offset definitions for CMU_G2D + */ +#define MUX_SEL_G2D0 0x0200 +#define MUX_SEL_ENABLE_G2D0 0x0300 +#define MUX_SEL_STAT_G2D0 0x0400 +#define DIV_G2D 0x0600 +#define DIV_STAT_G2D 0x0700 +#define DIV_ENABLE_ACLK_G2D 0x0800 +#define DIV_ENABLE_ACLK_G2D_SECURE_SMMU_G2D 0x0804 +#define DIV_ENABLE_PCLK_G2D 0x0900 +#define DIV_ENABLE_PCLK_G2D_SECURE_SMMU_G2D 0x0904 +#define DIV_ENABLE_IP_G2D0 0x0b00 +#define DIV_ENABLE_IP_G2D1 0x0b04 +#define DIV_ENABLE_IP_G2D_SECURE_SMMU_G2D 0x0b08 + +static unsigned long g2d_clk_regs[] __initdata = { + MUX_SEL_G2D0, + MUX_SEL_ENABLE_G2D0, + MUX_SEL_STAT_G2D0, + DIV_G2D, + DIV_STAT_G2D, + DIV_ENABLE_ACLK_G2D, + DIV_ENABLE_ACLK_G2D_SECURE_SMMU_G2D, + DIV_ENABLE_PCLK_G2D, + DIV_ENABLE_PCLK_G2D_SECURE_SMMU_G2D, + DIV_ENABLE_IP_G2D0, + DIV_ENABLE_IP_G2D1, + DIV_ENABLE_IP_G2D_SECURE_SMMU_G2D, +}; + +/* list of all parent clock list */ +PNAME(mout_aclk_g2d_266_user_p) = { "oscclk", "aclk_g2d_266", }; +PNAME(mout_aclk_g2d_400_user_p) = { "oscclk", "aclk_g2d_400", }; + +static struct samsung_mux_clock g2d_mux_clks[] __initdata = { + /* MUX_SEL_G2D0 */ + MUX(CLK_MUX_ACLK_G2D_266_USER, "mout_aclk_g2d_266_user", + mout_aclk_g2d_266_user_p, MUX_SEL_G2D0, 4, 1), + MUX(CLK_MUX_ACLK_G2D_400_USER, "mout_aclk_g2d_400_user", + mout_aclk_g2d_400_user_p, MUX_SEL_G2D0, 0, 1), +}; + +static struct samsung_div_clock g2d_div_clks[] __initdata = { + /* DIV_G2D */ + DIV(CLK_DIV_PCLK_G2D, "div_pclk_g2d", "mout_aclk_g2d_266_user", + DIV_G2D, 0, 2), +}; + +static struct samsung_gate_clock g2d_gate_clks[] __initdata = { + /* DIV_ENABLE_ACLK_G2D */ + GATE(CLK_ACLK_SMMU_MDMA1, "aclk_smmu_mdma1", "mout_aclk_g2d_266_user", + DIV_ENABLE_ACLK_G2D, 12, 0, 0), + GATE(CLK_ACLK_BTS_MDMA1, "aclk_bts_mdam1", "mout_aclk_g2d_266_user", + DIV_ENABLE_ACLK_G2D, 11, 0, 0), + GATE(CLK_ACLK_BTS_G2D, "aclk_bts_g2d", "mout_aclk_g2d_400_user", + DIV_ENABLE_ACLK_G2D, 10, 0, 0), + GATE(CLK_ACLK_ALB_G2D, "aclk_alb_g2d", "mout_aclk_g2d_400_user", + DIV_ENABLE_ACLK_G2D, 9, 0, 0), + GATE(CLK_ACLK_AXIUS_G2DX, "aclk_axius_g2dx", "mout_aclk_g2d_400_user", + DIV_ENABLE_ACLK_G2D, 8, 0, 0), + GATE(CLK_ACLK_ASYNCAXI_SYSX, "aclk_asyncaxi_sysx", + "mout_aclk_g2d_400_user", DIV_ENABLE_ACLK_G2D, + 7, 0, 0), + GATE(CLK_ACLK_AHB2APB_G2D1P, "aclk_ahb2apb_g2d1p", "div_pclk_g2d", + DIV_ENABLE_ACLK_G2D, 6, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_AHB2APB_G2D0P, "aclk_ahb2apb_g2d0p", "div_pclk_g2d", + DIV_ENABLE_ACLK_G2D, 5, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_XIU_G2DX, "aclk_xiu_g2dx", "mout_aclk_g2d_400_user", + DIV_ENABLE_ACLK_G2D, 4, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_G2DNP_133, "aclk_g2dnp_133", "div_pclk_g2d", + DIV_ENABLE_ACLK_G2D, 3, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_G2DND_400, "aclk_g2dnd_400", "mout_aclk_g2d_400_user", + DIV_ENABLE_ACLK_G2D, 2, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_MDMA1, "aclk_mdma1", "mout_aclk_g2d_266_user", + DIV_ENABLE_ACLK_G2D, 1, 0, 0), + GATE(CLK_ACLK_G2D, "aclk_g2d", "mout_aclk_g2d_400_user", + DIV_ENABLE_ACLK_G2D, 0, 0, 0), + + /* DIV_ENABLE_ACLK_G2D_SECURE_SMMU_G2D */ + GATE(CLK_ACLK_SMMU_G2D, "aclk_smmu_g2d", "mout_aclk_g2d_400_user", + DIV_ENABLE_ACLK_G2D_SECURE_SMMU_G2D, 0, 0, 0), + + /* DIV_ENABLE_PCLK_G2D */ + GATE(CLK_PCLK_SMMU_MDMA1, "pclk_smmu_mdma1", "div_pclk_g2d", + DIV_ENABLE_PCLK_G2D, 7, 0, 0), + GATE(CLK_PCLK_BTS_MDMA1, "pclk_bts_mdam1", "div_pclk_g2d", + DIV_ENABLE_PCLK_G2D, 6, 0, 0), + GATE(CLK_PCLK_BTS_G2D, "pclk_bts_g2d", "div_pclk_g2d", + DIV_ENABLE_PCLK_G2D, 5, 0, 0), + GATE(CLK_PCLK_ALB_G2D, "pclk_alb_g2d", "div_pclk_g2d", + DIV_ENABLE_PCLK_G2D, 4, 0, 0), + GATE(CLK_PCLK_ASYNCAXI_SYSX, "pclk_asyncaxi_sysx", "div_pclk_g2d", + DIV_ENABLE_PCLK_G2D, 3, 0, 0), + GATE(CLK_PCLK_PMU_G2D, "pclk_pmu_g2d", "div_pclk_g2d", + DIV_ENABLE_PCLK_G2D, 2, CLK_IGNORE_UNUSED, 0), + GATE(CLK_PCLK_SYSREG_G2D, "pclk_sysreg_g2d", "div_pclk_g2d", + DIV_ENABLE_PCLK_G2D, 1, CLK_IGNORE_UNUSED, 0), + GATE(CLK_PCLK_G2D, "pclk_g2d", "div_pclk_g2d", DIV_ENABLE_PCLK_G2D, + 0, 0, 0), + + /* DIV_ENABLE_PCLK_G2D_SECURE_SMMU_G2D */ + GATE(CLK_PCLK_SMMU_G2D, "pclk_smmu_g2d", "div_pclk_g2d", + DIV_ENABLE_PCLK_G2D_SECURE_SMMU_G2D, 0, 0, 0), +}; + +static struct samsung_cmu_info g2d_cmu_info __initdata = { + .mux_clks = g2d_mux_clks, + .nr_mux_clks = ARRAY_SIZE(g2d_mux_clks), + .div_clks = g2d_div_clks, + .nr_div_clks = ARRAY_SIZE(g2d_div_clks), + .gate_clks = g2d_gate_clks, + .nr_gate_clks = ARRAY_SIZE(g2d_gate_clks), + .nr_clk_ids = G2D_NR_CLK, + .clk_regs = g2d_clk_regs, + .nr_clk_regs = ARRAY_SIZE(g2d_clk_regs), +}; + +static void __init exynos5433_cmu_g2d_init(struct device_node *np) +{ + samsung_cmu_register_one(np, &g2d_cmu_info); +} + +CLK_OF_DECLARE(exynos5433_cmu_g2d, "samsung,exynos5433-cmu-g2d", + exynos5433_cmu_g2d_init); diff --git a/include/dt-bindings/clock/exynos5433.h b/include/dt-bindings/clock/exynos5433.h index 5b3397d9843a..818d6b6bbdc4 100644 --- a/include/dt-bindings/clock/exynos5433.h +++ b/include/dt-bindings/clock/exynos5433.h @@ -100,6 +100,12 @@ #define CLK_DIV_SCLK_PCM1 128 #define CLK_DIV_SCLK_AUDIO1 129 #define CLK_DIV_SCLK_AUDIO0 130 +#define CLK_DIV_ACLK_GSCL_111 131 +#define CLK_DIV_ACLK_GSCL_333 132 +#define CLK_DIV_ACLK_HEVC_400 133 +#define CLK_DIV_ACLK_MFC_400 134 +#define CLK_DIV_ACLK_G2D_266 135 +#define CLK_DIV_ACLK_G2D_400 136 #define CLK_ACLK_PERIC_66 200 #define CLK_ACLK_PERIS_66 201 @@ -121,8 +127,10 @@ #define CLK_SCLK_SLIMBUS 217 #define CLK_SCLK_AUDIO1 218 #define CLK_SCLK_AUDIO0 219 +#define CLK_ACLK_G2D_266 220 +#define CLK_ACLK_G2D_400 221 -#define TOP_NR_CLK 220 +#define TOP_NR_CLK 222 /* CMU_CPIF */ #define CLK_FOUT_MPHY_PLL 1 @@ -286,4 +294,36 @@ #define FSYS_NR_CLK 66 +/* CMU_G2D */ +#define CLK_MUX_ACLK_G2D_266_USER 1 +#define CLK_MUX_ACLK_G2D_400_USER 2 + +#define CLK_DIV_PCLK_G2D 3 + +#define CLK_ACLK_SMMU_MDMA1 4 +#define CLK_ACLK_BTS_MDMA1 5 +#define CLK_ACLK_BTS_G2D 6 +#define CLK_ACLK_ALB_G2D 7 +#define CLK_ACLK_AXIUS_G2DX 8 +#define CLK_ACLK_ASYNCAXI_SYSX 9 +#define CLK_ACLK_AHB2APB_G2D1P 10 +#define CLK_ACLK_AHB2APB_G2D0P 11 +#define CLK_ACLK_XIU_G2DX 12 +#define CLK_ACLK_G2DNP_133 13 +#define CLK_ACLK_G2DND_400 14 +#define CLK_ACLK_MDMA1 15 +#define CLK_ACLK_G2D 16 +#define CLK_ACLK_SMMU_G2D 17 +#define CLK_PCLK_SMMU_MDMA1 18 +#define CLK_PCLK_BTS_MDMA1 19 +#define CLK_PCLK_BTS_G2D 20 +#define CLK_PCLK_ALB_G2D 21 +#define CLK_PCLK_ASYNCAXI_SYSX 22 +#define CLK_PCLK_PMU_G2D 23 +#define CLK_PCLK_SYSREG_G2D 24 +#define CLK_PCLK_G2D 25 +#define CLK_PCLK_SMMU_G2D 26 + +#define G2D_NR_CLK 27 + #endif /* _DT_BINDINGS_CLOCK_EXYNOS5433_H */ -- cgit v1.2.3 From 06d2f9dfa663367e8cc1690d7e5ce4113e5dbcc1 Mon Sep 17 00:00:00 2001 From: Chanwoo Choi Date: Mon, 2 Feb 2015 23:24:01 +0900 Subject: clk: samsung: exynos5433: Add clocks for CMU_MIF domain This patch adds the mux/divider/gate clocks of CMU_MIF domain which includes the clocks for DMC(DRAM memory controller) and CCI(Cache Coherent Interconnect). The CMU_MIF domain provides the source clocks for CMU_DISP/CMU_BUS2. Signed-off-by: Chanwoo Choi Acked-by: Inki Dae Signed-off-by: Sylwester Nawrocki --- drivers/clk/samsung/clk-exynos5433.c | 599 +++++++++++++++++++++++++++++++++ include/dt-bindings/clock/exynos5433.h | 190 ++++++++++- 2 files changed, 788 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/drivers/clk/samsung/clk-exynos5433.c b/drivers/clk/samsung/clk-exynos5433.c index 24218dba8218..09ccf11bab64 100644 --- a/drivers/clk/samsung/clk-exynos5433.c +++ b/drivers/clk/samsung/clk-exynos5433.c @@ -740,6 +740,66 @@ CLK_OF_DECLARE(exynos5433_cmu_cpif, "samsung,exynos5433-cmu-cpif", #define MFC_PLL_CON0 0x0130 #define MFC_PLL_CON1 0x0134 #define MFC_PLL_FREQ_DET 0x013c +#define MUX_SEL_MIF0 0x0200 +#define MUX_SEL_MIF1 0x0204 +#define MUX_SEL_MIF2 0x0208 +#define MUX_SEL_MIF3 0x020c +#define MUX_SEL_MIF4 0x0210 +#define MUX_SEL_MIF5 0x0214 +#define MUX_SEL_MIF6 0x0218 +#define MUX_SEL_MIF7 0x021c +#define MUX_ENABLE_MIF0 0x0300 +#define MUX_ENABLE_MIF1 0x0304 +#define MUX_ENABLE_MIF2 0x0308 +#define MUX_ENABLE_MIF3 0x030c +#define MUX_ENABLE_MIF4 0x0310 +#define MUX_ENABLE_MIF5 0x0314 +#define MUX_ENABLE_MIF6 0x0318 +#define MUX_ENABLE_MIF7 0x031c +#define MUX_STAT_MIF0 0x0400 +#define MUX_STAT_MIF1 0x0404 +#define MUX_STAT_MIF2 0x0408 +#define MUX_STAT_MIF3 0x040c +#define MUX_STAT_MIF4 0x0410 +#define MUX_STAT_MIF5 0x0414 +#define MUX_STAT_MIF6 0x0418 +#define MUX_STAT_MIF7 0x041c +#define DIV_MIF1 0x0604 +#define DIV_MIF2 0x0608 +#define DIV_MIF3 0x060c +#define DIV_MIF4 0x0610 +#define DIV_MIF5 0x0614 +#define DIV_MIF_PLL_FREQ_DET 0x0618 +#define DIV_STAT_MIF1 0x0704 +#define DIV_STAT_MIF2 0x0708 +#define DIV_STAT_MIF3 0x070c +#define DIV_STAT_MIF4 0x0710 +#define DIV_STAT_MIF5 0x0714 +#define DIV_STAT_MIF_PLL_FREQ_DET 0x0718 +#define ENABLE_ACLK_MIF0 0x0800 +#define ENABLE_ACLK_MIF1 0x0804 +#define ENABLE_ACLK_MIF2 0x0808 +#define ENABLE_ACLK_MIF3 0x080c +#define ENABLE_PCLK_MIF 0x0900 +#define ENABLE_PCLK_MIF_SECURE_DREX0_TZ 0x0904 +#define ENABLE_PCLK_MIF_SECURE_DREX1_TZ 0x0908 +#define ENABLE_PCLK_MIF_SECURE_MONOTONIC_CNT 0x090c +#define ENABLE_PCLK_MIF_SECURE_RTC 0x0910 +#define ENABLE_SCLK_MIF 0x0a00 +#define ENABLE_IP_MIF0 0x0b00 +#define ENABLE_IP_MIF1 0x0b04 +#define ENABLE_IP_MIF2 0x0b08 +#define ENABLE_IP_MIF3 0x0b0c +#define ENABLE_IP_MIF_SECURE_DREX0_TZ 0x0b10 +#define ENABLE_IP_MIF_SECURE_DREX1_TZ 0x0b14 +#define ENABLE_IP_MIF_SECURE_MONOTONIC_CNT 0x0b18 +#define ENABLE_IP_MIF_SECURE_RTC 0x0b1c +#define CLKOUT_CMU_MIF 0x0c00 +#define CLKOUT_CMU_MIF_DIV_STAT 0x0c04 +#define DREX_FREQ_CTRL0 0x1000 +#define DREX_FREQ_CTRL1 0x1004 +#define PAUSE 0x1008 +#define DDRPHY_LOCK_CTRL 0x100c static unsigned long mif_clk_regs[] __initdata = { MEM0_PLL_LOCK, @@ -758,6 +818,66 @@ static unsigned long mif_clk_regs[] __initdata = { MFC_PLL_CON0, MFC_PLL_CON1, MFC_PLL_FREQ_DET, + MUX_SEL_MIF0, + MUX_SEL_MIF1, + MUX_SEL_MIF2, + MUX_SEL_MIF3, + MUX_SEL_MIF4, + MUX_SEL_MIF5, + MUX_SEL_MIF6, + MUX_SEL_MIF7, + MUX_ENABLE_MIF0, + MUX_ENABLE_MIF1, + MUX_ENABLE_MIF2, + MUX_ENABLE_MIF3, + MUX_ENABLE_MIF4, + MUX_ENABLE_MIF5, + MUX_ENABLE_MIF6, + MUX_ENABLE_MIF7, + MUX_STAT_MIF0, + MUX_STAT_MIF1, + MUX_STAT_MIF2, + MUX_STAT_MIF3, + MUX_STAT_MIF4, + MUX_STAT_MIF5, + MUX_STAT_MIF6, + MUX_STAT_MIF7, + DIV_MIF1, + DIV_MIF2, + DIV_MIF3, + DIV_MIF4, + DIV_MIF5, + DIV_MIF_PLL_FREQ_DET, + DIV_STAT_MIF1, + DIV_STAT_MIF2, + DIV_STAT_MIF3, + DIV_STAT_MIF4, + DIV_STAT_MIF5, + DIV_STAT_MIF_PLL_FREQ_DET, + ENABLE_ACLK_MIF0, + ENABLE_ACLK_MIF1, + ENABLE_ACLK_MIF2, + ENABLE_ACLK_MIF3, + ENABLE_PCLK_MIF, + ENABLE_PCLK_MIF_SECURE_DREX0_TZ, + ENABLE_PCLK_MIF_SECURE_DREX1_TZ, + ENABLE_PCLK_MIF_SECURE_MONOTONIC_CNT, + ENABLE_PCLK_MIF_SECURE_RTC, + ENABLE_SCLK_MIF, + ENABLE_IP_MIF0, + ENABLE_IP_MIF1, + ENABLE_IP_MIF2, + ENABLE_IP_MIF3, + ENABLE_IP_MIF_SECURE_DREX0_TZ, + ENABLE_IP_MIF_SECURE_DREX1_TZ, + ENABLE_IP_MIF_SECURE_MONOTONIC_CNT, + ENABLE_IP_MIF_SECURE_RTC, + CLKOUT_CMU_MIF, + CLKOUT_CMU_MIF_DIV_STAT, + DREX_FREQ_CTRL0, + DREX_FREQ_CTRL1, + PAUSE, + DDRPHY_LOCK_CTRL, }; static struct samsung_pll_clock mif_pll_clks[] __initdata = { @@ -771,9 +891,488 @@ static struct samsung_pll_clock mif_pll_clks[] __initdata = { MFC_PLL_LOCK, MFC_PLL_CON0, exynos5443_pll_rates), }; +/* list of all parent clock list */ +PNAME(mout_mfc_pll_div2_p) = { "mout_mfc_pll", "dout_mfc_pll", }; +PNAME(mout_bus_pll_div2_p) = { "mout_bus_pll", "dout_bus_pll", }; +PNAME(mout_mem1_pll_div2_p) = { "mout_mem1_pll", "dout_mem1_pll", }; +PNAME(mout_mem0_pll_div2_p) = { "mout_mem0_pll", "dout_mem0_pll", }; +PNAME(mout_mfc_pll_p) = { "oscclk", "fout_mfc_pll", }; +PNAME(mout_bus_pll_p) = { "oscclk", "fout_bus_pll", }; +PNAME(mout_mem1_pll_p) = { "oscclk", "fout_mem1_pll", }; +PNAME(mout_mem0_pll_p) = { "oscclk", "fout_mem0_pll", }; + +PNAME(mout_clk2x_phy_c_p) = { "mout_mem0_pll_div2", "mout_clkm_phy_b", }; +PNAME(mout_clk2x_phy_b_p) = { "mout_bus_pll_div2", "mout_clkm_phy_a", }; +PNAME(mout_clk2x_phy_a_p) = { "mout_bus_pll_div2", "mout_mfc_pll_div2", }; +PNAME(mout_clkm_phy_b_p) = { "mout_mem1_pll_div2", "mout_clkm_phy_a", }; + +PNAME(mout_aclk_mifnm_200_p) = { "mout_mem0_pll_div2", "div_mif_pre", }; +PNAME(mout_aclk_mifnm_400_p) = { "mout_mem1_pll_div2", "mout_bus_pll_div2",}; + +PNAME(mout_aclk_disp_333_b_p) = { "mout_aclk_disp_333_a", + "mout_bus_pll_div2", }; +PNAME(mout_aclk_disp_333_a_p) = { "mout_mfc_pll_div2", "sclk_mphy_pll", }; + +PNAME(mout_sclk_decon_vclk_c_p) = { "mout_sclk_decon_vclk_b", + "sclk_mphy_pll", }; +PNAME(mout_sclk_decon_vclk_b_p) = { "mout_sclk_decon_vclk_a", + "mout_mfc_pll_div2", }; +PNAME(mout_sclk_decon_p) = { "oscclk", "mout_bus_pll_div2", }; +PNAME(mout_sclk_decon_eclk_c_p) = { "mout_sclk_decon_eclk_b", + "sclk_mphy_pll", }; +PNAME(mout_sclk_decon_eclk_b_p) = { "mout_sclk_decon_eclk_a", + "mout_mfc_pll_div2", }; + +PNAME(mout_sclk_decon_tv_eclk_c_p) = { "mout_sclk_decon_tv_eclk_b", + "sclk_mphy_pll", }; +PNAME(mout_sclk_decon_tv_eclk_b_p) = { "mout_sclk_decon_tv_eclk_a", + "mout_mfc_pll_div2", }; +PNAME(mout_sclk_dsd_c_p) = { "mout_sclk_dsd_b", "mout_bus_pll_div2", }; +PNAME(mout_sclk_dsd_b_p) = { "mout_sclk_dsd_a", "sclk_mphy_pll", }; +PNAME(mout_sclk_dsd_a_p) = { "oscclk", "mout_mfc_pll_div2", }; + +PNAME(mout_sclk_dsim0_c_p) = { "mout_sclk_dsim0_b", "sclk_mphy_pll", }; +PNAME(mout_sclk_dsim0_b_p) = { "mout_sclk_dsim0_a", "mout_mfc_pll_div2" }; + +PNAME(mout_sclk_decon_tv_vclk_c_p) = { "mout_sclk_decon_tv_vclk_b", + "sclk_mphy_pll", }; +PNAME(mout_sclk_decon_tv_vclk_b_p) = { "mout_sclk_decon_tv_vclk_a", + "mout_mfc_pll_div2", }; +PNAME(mout_sclk_dsim1_c_p) = { "mout_sclk_dsim1_b", "sclk_mphy_pll", }; +PNAME(mout_sclk_dsim1_b_p) = { "mout_sclk_dsim1_a", "mout_mfc_pll_div2",}; + +static struct samsung_fixed_factor_clock mif_fixed_factor_clks[] __initdata = { + /* dout_{mfc|bus|mem1|mem0}_pll is half fixed rate from parent mux */ + FFACTOR(CLK_DOUT_MFC_PLL, "dout_mfc_pll", "mout_mfc_pll", 1, 1, 0), + FFACTOR(CLK_DOUT_BUS_PLL, "dout_bus_pll", "mout_bus_pll", 1, 1, 0), + FFACTOR(CLK_DOUT_MEM1_PLL, "dout_mem1_pll", "mout_mem1_pll", 1, 1, 0), + FFACTOR(CLK_DOUT_MEM0_PLL, "dout_mem0_pll", "mout_mem0_pll", 1, 1, 0), +}; + +static struct samsung_mux_clock mif_mux_clks[] __initdata = { + /* MUX_SEL_MIF0 */ + MUX(CLK_MOUT_MFC_PLL_DIV2, "mout_mfc_pll_div2", mout_mfc_pll_div2_p, + MUX_SEL_MIF0, 28, 1), + MUX(CLK_MOUT_BUS_PLL_DIV2, "mout_bus_pll_div2", mout_bus_pll_div2_p, + MUX_SEL_MIF0, 24, 1), + MUX(CLK_MOUT_MEM1_PLL_DIV2, "mout_mem1_pll_div2", mout_mem1_pll_div2_p, + MUX_SEL_MIF0, 20, 1), + MUX(CLK_MOUT_MEM0_PLL_DIV2, "mout_mem0_pll_div2", mout_mem0_pll_div2_p, + MUX_SEL_MIF0, 16, 1), + MUX(CLK_MOUT_MFC_PLL, "mout_mfc_pll", mout_mfc_pll_p, MUX_SEL_MIF0, + 12, 1), + MUX(CLK_MOUT_BUS_PLL, "mout_bus_pll", mout_bus_pll_p, MUX_SEL_MIF0, + 8, 1), + MUX(CLK_MOUT_MEM1_PLL, "mout_mem1_pll", mout_mem1_pll_p, MUX_SEL_MIF0, + 4, 1), + MUX(CLK_MOUT_MEM0_PLL, "mout_mem0_pll", mout_mem0_pll_p, MUX_SEL_MIF0, + 0, 1), + + /* MUX_SEL_MIF1 */ + MUX(CLK_MOUT_CLK2X_PHY_C, "mout_clk2x_phy_c", mout_clk2x_phy_c_p, + MUX_SEL_MIF1, 24, 1), + MUX(CLK_MOUT_CLK2X_PHY_B, "mout_clk2x_phy_b", mout_clk2x_phy_b_p, + MUX_SEL_MIF1, 20, 1), + MUX(CLK_MOUT_CLK2X_PHY_A, "mout_clk2x_phy_a", mout_clk2x_phy_a_p, + MUX_SEL_MIF1, 16, 1), + MUX(CLK_MOUT_CLKM_PHY_C, "mout_clkm_phy_c", mout_clk2x_phy_c_p, + MUX_SEL_MIF1, 12, 1), + MUX(CLK_MOUT_CLKM_PHY_B, "mout_clkm_phy_b", mout_clkm_phy_b_p, + MUX_SEL_MIF1, 8, 1), + MUX(CLK_MOUT_CLKM_PHY_A, "mout_clkm_phy_a", mout_clk2x_phy_a_p, + MUX_SEL_MIF1, 4, 1), + + /* MUX_SEL_MIF2 */ + MUX(CLK_MOUT_ACLK_MIFNM_200, "mout_aclk_mifnm_200", + mout_aclk_mifnm_200_p, MUX_SEL_MIF2, 8, 1), + MUX(CLK_MOUT_ACLK_MIFNM_400, "mout_aclk_mifnm_400", + mout_aclk_mifnm_400_p, MUX_SEL_MIF2, 0, 1), + + /* MUX_SEL_MIF3 */ + MUX(CLK_MOUT_ACLK_DISP_333_B, "mout_aclk_disp_333_b", + mout_aclk_disp_333_b_p, MUX_SEL_MIF3, 4, 1), + MUX(CLK_MOUT_ACLK_DISP_333_A, "mout_aclk_disp_333_a", + mout_aclk_disp_333_a_p, MUX_SEL_MIF3, 0, 1), + + /* MUX_SEL_MIF4 */ + MUX(CLK_MOUT_SCLK_DECON_VCLK_C, "mout_sclk_decon_vclk_c", + mout_sclk_decon_vclk_c_p, MUX_SEL_MIF4, 24, 1), + MUX(CLK_MOUT_SCLK_DECON_VCLK_B, "mout_sclk_decon_vclk_b", + mout_sclk_decon_vclk_b_p, MUX_SEL_MIF4, 20, 1), + MUX(CLK_MOUT_SCLK_DECON_VCLK_A, "mout_sclk_decon_vclk_a", + mout_sclk_decon_p, MUX_SEL_MIF4, 16, 1), + MUX(CLK_MOUT_SCLK_DECON_ECLK_C, "mout_sclk_decon_eclk_c", + mout_sclk_decon_eclk_c_p, MUX_SEL_MIF4, 8, 1), + MUX(CLK_MOUT_SCLK_DECON_ECLK_B, "mout_sclk_decon_eclk_b", + mout_sclk_decon_eclk_b_p, MUX_SEL_MIF4, 4, 1), + MUX(CLK_MOUT_SCLK_DECON_ECLK_A, "mout_sclk_decon_eclk_a", + mout_sclk_decon_p, MUX_SEL_MIF4, 0, 1), + + /* MUX_SEL_MIF5 */ + MUX(CLK_MOUT_SCLK_DECON_TV_ECLK_C, "mout_sclk_decon_tv_eclk_c", + mout_sclk_decon_tv_eclk_c_p, MUX_SEL_MIF5, 24, 1), + MUX(CLK_MOUT_SCLK_DECON_TV_ECLK_B, "mout_sclk_decon_tv_eclk_b", + mout_sclk_decon_tv_eclk_b_p, MUX_SEL_MIF5, 20, 1), + MUX(CLK_MOUT_SCLK_DECON_TV_ECLK_A, "mout_sclk_decon_tv_eclk_a", + mout_sclk_decon_p, MUX_SEL_MIF5, 16, 1), + MUX(CLK_MOUT_SCLK_DSD_C, "mout_sclk_dsd_c", mout_sclk_dsd_c_p, + MUX_SEL_MIF5, 8, 1), + MUX(CLK_MOUT_SCLK_DSD_B, "mout_sclk_dsd_b", mout_sclk_dsd_b_p, + MUX_SEL_MIF5, 4, 1), + MUX(CLK_MOUT_SCLK_DSD_A, "mout_sclk_dsd_a", mout_sclk_dsd_a_p, + MUX_SEL_MIF5, 0, 1), + + /* MUX_SEL_MIF6 */ + MUX(CLK_MOUT_SCLK_DSIM0_C, "mout_sclk_dsim0_c", mout_sclk_dsim0_c_p, + MUX_SEL_MIF6, 8, 1), + MUX(CLK_MOUT_SCLK_DSIM0_B, "mout_sclk_dsim0_b", mout_sclk_dsim0_b_p, + MUX_SEL_MIF6, 4, 1), + MUX(CLK_MOUT_SCLK_DSIM0_A, "mout_sclk_dsim0_a", mout_sclk_decon_p, + MUX_SEL_MIF6, 0, 1), + + /* MUX_SEL_MIF7 */ + MUX(CLK_MOUT_SCLK_DECON_TV_VCLK_C, "mout_sclk_decon_tv_vclk_c", + mout_sclk_decon_tv_vclk_c_p, MUX_SEL_MIF7, 24, 1), + MUX(CLK_MOUT_SCLK_DECON_TV_VCLK_B, "mout_sclk_decon_tv_vclk_b", + mout_sclk_decon_tv_vclk_b_p, MUX_SEL_MIF7, 20, 1), + MUX(CLK_MOUT_SCLK_DECON_TV_VCLK_A, "mout_sclk_decon_tv_vclk_a", + mout_sclk_decon_p, MUX_SEL_MIF7, 16, 1), + MUX(CLK_MOUT_SCLK_DSIM1_C, "mout_sclk_dsim1_c", mout_sclk_dsim1_c_p, + MUX_SEL_MIF7, 8, 1), + MUX(CLK_MOUT_SCLK_DSIM1_B, "mout_sclk_dsim1_b", mout_sclk_dsim1_b_p, + MUX_SEL_MIF7, 4, 1), + MUX(CLK_MOUT_SCLK_DSIM1_A, "mout_sclk_dsim1_a", mout_sclk_decon_p, + MUX_SEL_MIF7, 0, 1), +}; + +static struct samsung_div_clock mif_div_clks[] __initdata = { + /* DIV_MIF1 */ + DIV(CLK_DIV_SCLK_HPM_MIF, "div_sclk_hpm_mif", "div_clk2x_phy", + DIV_MIF1, 16, 2), + DIV(CLK_DIV_ACLK_DREX1, "div_aclk_drex1", "div_clk2x_phy", DIV_MIF1, + 12, 2), + DIV(CLK_DIV_ACLK_DREX0, "div_aclk_drex0", "div_clk2x_phy", DIV_MIF1, + 8, 2), + DIV(CLK_DIV_CLK2XPHY, "div_clk2x_phy", "mout_clk2x_phy_c", DIV_MIF1, + 4, 4), + + /* DIV_MIF2 */ + DIV(CLK_DIV_ACLK_MIF_266, "div_aclk_mif_266", "mout_bus_pll_div2", + DIV_MIF2, 20, 3), + DIV(CLK_DIV_ACLK_MIFND_133, "div_aclk_mifnd_133", "div_mif_pre", + DIV_MIF2, 16, 4), + DIV(CLK_DIV_ACLK_MIF_133, "div_aclk_mif_133", "div_mif_pre", + DIV_MIF2, 12, 4), + DIV(CLK_DIV_ACLK_MIFNM_200, "div_aclk_mifnm_200", + "mout_aclk_mifnm_200", DIV_MIF2, 8, 3), + DIV(CLK_DIV_ACLK_MIF_200, "div_aclk_mif_200", "div_aclk_mif_400", + DIV_MIF2, 4, 2), + DIV(CLK_DIV_ACLK_MIF_400, "div_aclk_mif_400", "mout_aclk_mifnm_400", + DIV_MIF2, 0, 3), + + /* DIV_MIF3 */ + DIV(CLK_DIV_ACLK_BUS2_400, "div_aclk_bus2_400", "div_mif_pre", + DIV_MIF3, 16, 4), + DIV(CLK_DIV_ACLK_DISP_333, "div_aclk_disp_333", "mout_aclk_disp_333_b", + DIV_MIF3, 4, 3), + DIV(CLK_DIV_ACLK_CPIF_200, "div_aclk_cpif_200", "mout_aclk_mifnm_200", + DIV_MIF3, 0, 3), + + /* DIV_MIF4 */ + DIV(CLK_DIV_SCLK_DSIM1, "div_sclk_dsim1", "mout_sclk_dsim1_c", + DIV_MIF4, 24, 4), + DIV(CLK_DIV_SCLK_DECON_TV_VCLK, "div_sclk_decon_tv_vclk", + "mout_sclk_decon_tv_vclk_c", DIV_MIF4, 20, 4), + DIV(CLK_DIV_SCLK_DSIM0, "div_sclk_dsim0", "mout_sclk_dsim0_c", + DIV_MIF4, 16, 4), + DIV(CLK_DIV_SCLK_DSD, "div_sclk_dsd", "mout_sclk_dsd_c", + DIV_MIF4, 12, 4), + DIV(CLK_DIV_SCLK_DECON_TV_ECLK, "div_sclk_decon_tv_eclk", + "mout_sclk_decon_tv_eclk_c", DIV_MIF4, 8, 4), + DIV(CLK_DIV_SCLK_DECON_VCLK, "div_sclk_decon_vclk", + "mout_sclk_decon_vclk_c", DIV_MIF4, 4, 4), + DIV(CLK_DIV_SCLK_DECON_ECLK, "div_sclk_decon_eclk", + "mout_sclk_decon_eclk_c", DIV_MIF4, 0, 4), + + /* DIV_MIF5 */ + DIV(CLK_DIV_MIF_PRE, "div_mif_pre", "mout_bus_pll_div2", DIV_MIF5, + 0, 3), +}; + +static struct samsung_gate_clock mif_gate_clks[] __initdata = { + /* ENABLE_ACLK_MIF0 */ + GATE(CLK_CLK2X_PHY1, "clk2k_phy1", "div_clk2x_phy", ENABLE_ACLK_MIF0, + 19, CLK_IGNORE_UNUSED, 0), + GATE(CLK_CLK2X_PHY0, "clk2x_phy0", "div_clk2x_phy", ENABLE_ACLK_MIF0, + 18, CLK_IGNORE_UNUSED, 0), + GATE(CLK_CLKM_PHY1, "clkm_phy1", "mout_clkm_phy_c", ENABLE_ACLK_MIF0, + 17, CLK_IGNORE_UNUSED, 0), + GATE(CLK_CLKM_PHY0, "clkm_phy0", "mout_clkm_phy_c", ENABLE_ACLK_MIF0, + 16, CLK_IGNORE_UNUSED, 0), + GATE(CLK_RCLK_DREX1, "rclk_drex1", "oscclk", ENABLE_ACLK_MIF0, + 15, CLK_IGNORE_UNUSED, 0), + GATE(CLK_RCLK_DREX0, "rclk_drex0", "oscclk", ENABLE_ACLK_MIF0, + 14, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_DREX1_TZ, "aclk_drex1_tz", "div_aclk_drex1", + ENABLE_ACLK_MIF0, 13, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_DREX0_TZ, "aclk_drex0_tz", "div_aclk_drex0", + ENABLE_ACLK_MIF0, 12, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_DREX1_PEREV, "aclk_drex1_perev", "div_aclk_drex1", + ENABLE_ACLK_MIF0, 11, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_DREX0_PEREV, "aclk_drex0_perev", "div_aclk_drex0", + ENABLE_ACLK_MIF0, 10, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_DREX1_MEMIF, "aclk_drex1_memif", "div_aclk_drex1", + ENABLE_ACLK_MIF0, 9, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_DREX0_MEMIF, "aclk_drex0_memif", "div_aclk_drex0", + ENABLE_ACLK_MIF0, 8, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_DREX1_SCH, "aclk_drex1_sch", "div_aclk_drex1", + ENABLE_ACLK_MIF0, 7, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_DREX0_SCH, "aclk_drex0_sch", "div_aclk_drex0", + ENABLE_ACLK_MIF0, 6, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_DREX1_BUSIF, "aclk_drex1_busif", "div_aclk_drex1", + ENABLE_ACLK_MIF0, 5, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_DREX0_BUSIF, "aclk_drex0_busif", "div_aclk_drex0", + ENABLE_ACLK_MIF0, 4, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_DREX1_BUSIF_RD, "aclk_drex1_busif_rd", "div_aclk_drex1", + ENABLE_ACLK_MIF0, 3, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_DREX0_BUSIF_RD, "aclk_drex0_busif_rd", "div_aclk_drex0", + ENABLE_ACLK_MIF0, 2, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_DREX1, "aclk_drex1", "div_aclk_drex1", + ENABLE_ACLK_MIF0, 2, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_DREX0, "aclk_drex0", "div_aclk_drex0", + ENABLE_ACLK_MIF0, 1, CLK_IGNORE_UNUSED, 0), + + /* ENABLE_ACLK_MIF1 */ + GATE(CLK_ACLK_ASYNCAXIS_MIF_IMEM, "aclk_asyncaxis_mif_imem", + "div_aclk_mif_200", ENABLE_ACLK_MIF1, 28, + CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_ASYNCAXIS_NOC_P_CCI, "aclk_asyncaxis_noc_p_cci", + "div_aclk_mif_200", ENABLE_ACLK_MIF1, + 27, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_ASYNCAXIM_NOC_P_CCI, "aclk_asyncaxim_noc_p_cci", + "div_aclk_mif_133", ENABLE_ACLK_MIF1, + 26, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_ASYNCAXIS_CP1, "aclk_asyncaxis_cp1", + "div_aclk_mifnm_200", ENABLE_ACLK_MIF1, + 25, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_ASYNCAXIM_CP1, "aclk_asyncaxim_cp1", + "div_aclk_drex1", ENABLE_ACLK_MIF1, + 24, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_ASYNCAXIS_CP0, "aclk_asyncaxis_cp0", + "div_aclk_mifnm_200", ENABLE_ACLK_MIF1, + 23, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_ASYNCAXIM_CP0, "aclk_asyncaxim_cp0", + "div_aclk_drex0", ENABLE_ACLK_MIF1, + 22, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_ASYNCAXIS_DREX1_3, "aclk_asyncaxis_drex1_3", + "div_aclk_mif_133", ENABLE_ACLK_MIF1, + 21, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_ASYNCAXIM_DREX1_3, "aclk_asyncaxim_drex1_3", + "div_aclk_drex1", ENABLE_ACLK_MIF1, + 20, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_ASYNCAXIS_DREX1_1, "aclk_asyncaxis_drex1_1", + "div_aclk_mif_133", ENABLE_ACLK_MIF1, + 19, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_ASYNCAXIM_DREX1_1, "aclk_asyncaxim_drex1_1", + "div_aclk_drex1", ENABLE_ACLK_MIF1, + 18, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_ASYNCAXIS_DREX1_0, "aclk_asyncaxis_drex1_0", + "div_aclk_mif_133", ENABLE_ACLK_MIF1, + 17, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_ASYNCAXIM_DREX1_0, "aclk_asyncaxim_drex1_0", + "div_aclk_drex1", ENABLE_ACLK_MIF1, + 16, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_ASYNCAXIS_DREX0_3, "aclk_asyncaxis_drex0_3", + "div_aclk_mif_133", ENABLE_ACLK_MIF1, + 15, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_ASYNCAXIM_DREX0_3, "aclk_asyncaxim_drex0_3", + "div_aclk_drex0", ENABLE_ACLK_MIF1, + 14, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_ASYNCAXIS_DREX0_1, "aclk_asyncaxis_drex0_1", + "div_aclk_mif_133", ENABLE_ACLK_MIF1, + 13, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_ASYNCAXIM_DREX0_1, "aclk_asyncaxim_drex0_1", + "div_aclk_drex0", ENABLE_ACLK_MIF1, + 12, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_ASYNCAXIS_DREX0_0, "aclk_asyncaxis_drex0_0", + "div_aclk_mif_133", ENABLE_ACLK_MIF1, + 11, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_ASYNCAXIM_DREX0_0, "aclk_asyncaxim_drex0_0", + "div_aclk_drex0", ENABLE_ACLK_MIF1, + 10, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_AHB2APB_MIF2P, "aclk_ahb2apb_mif2p", "div_aclk_mif_133", + ENABLE_ACLK_MIF1, 9, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_AHB2APB_MIF1P, "aclk_ahb2apb_mif1p", "div_aclk_mif_133", + ENABLE_ACLK_MIF1, 8, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_AHB2APB_MIF0P, "aclk_ahb2apb_mif0p", "div_aclk_mif_133", + ENABLE_ACLK_MIF1, 7, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_IXIU_CCI, "aclk_ixiu_cci", "div_aclk_mif_400", + ENABLE_ACLK_MIF1, 6, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_XIU_MIFSFRX, "aclk_xiu_mifsfrx", "div_aclk_mif_200", + ENABLE_ACLK_MIF1, 5, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_MIFNP_133, "aclk_mifnp_133", "div_aclk_mif_133", + ENABLE_ACLK_MIF1, 4, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_MIFNM_200, "aclk_mifnm_200", "div_aclk_mifnm_200", + ENABLE_ACLK_MIF1, 3, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_MIFND_133, "aclk_mifnd_133", "div_aclk_mifnd_133", + ENABLE_ACLK_MIF1, 2, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_MIFND_400, "aclk_mifnd_400", "div_aclk_mif_400", + ENABLE_ACLK_MIF1, 1, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_CCI, "aclk_cci", "div_aclk_mif_400", ENABLE_ACLK_MIF1, + 0, CLK_IGNORE_UNUSED, 0), + + /* ENABLE_ACLK_MIF2 */ + GATE(CLK_ACLK_MIFND_266, "aclk_mifnd_266", "div_aclk_mif_266", + ENABLE_ACLK_MIF2, 20, 0, 0), + GATE(CLK_ACLK_PPMU_DREX1S3, "aclk_ppmu_drex1s3", "div_aclk_drex1", + ENABLE_ACLK_MIF2, 17, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_PPMU_DREX1S1, "aclk_ppmu_drex1s1", "div_aclk_drex1", + ENABLE_ACLK_MIF2, 16, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_PPMU_DREX1S0, "aclk_ppmu_drex1s0", "div_aclk_drex1", + ENABLE_ACLK_MIF2, 15, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_PPMU_DREX0S3, "aclk_ppmu_drex0s3", "div_aclk_drex0", + ENABLE_ACLK_MIF2, 14, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_PPMU_DREX0S1, "aclk_ppmu_drex0s1", "div_aclk_drex0", + ENABLE_ACLK_MIF2, 13, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_PPMU_DREX0S0, "aclk_ppmu_drex0s0", "div_aclk_drex0", + ENABLE_ACLK_MIF2, 12, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_AXIDS_CCI_MIFSFRX, "aclk_axids_cci_mifsfrx", + "div_aclk_mif_200", ENABLE_ACLK_MIF2, 7, + CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_AXISYNCDNS_CCI, "aclk_axisyncdns_cci", + "div_aclk_mif_400", ENABLE_ACLK_MIF2, + 5, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_AXISYNCDN_CCI, "aclk_axisyncdn_cci", "div_aclk_mif_400", + ENABLE_ACLK_MIF2, 4, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_AXISYNCDN_NOC_D, "aclk_axisyncdn_noc_d", + "div_aclk_mif_200", ENABLE_ACLK_MIF2, + 3, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_ASYNCAPBS_MIF_CSSYS, "aclk_asyncapbs_mif_cssys", + "div_aclk_mifnd_133", ENABLE_ACLK_MIF2, 0, 0, 0), + + /* ENABLE_ACLK_MIF3 */ + GATE(CLK_ACLK_BUS2_400, "aclk_bus2_400", "div_aclk_bus2_400", + ENABLE_ACLK_MIF3, 4, + CLK_IGNORE_UNUSED | CLK_SET_RATE_PARENT, 0), + GATE(CLK_ACLK_DISP_333, "aclk_disp_333", "div_aclk_disp_333", + ENABLE_ACLK_MIF3, 1, + CLK_IGNORE_UNUSED | CLK_SET_RATE_PARENT, 0), + GATE(CLK_ACLK_CPIF_200, "aclk_cpif_200", "div_aclk_cpif_200", + ENABLE_ACLK_MIF3, 0, + CLK_IGNORE_UNUSED | CLK_SET_RATE_PARENT, 0), + + /* ENABLE_PCLK_MIF */ + GATE(CLK_PCLK_PPMU_DREX1S3, "pclk_ppmu_drex1s3", "div_aclk_drex1", + ENABLE_PCLK_MIF, 29, CLK_IGNORE_UNUSED, 0), + GATE(CLK_PCLK_PPMU_DREX1S1, "pclk_ppmu_drex1s1", "div_aclk_drex1", + ENABLE_PCLK_MIF, 28, CLK_IGNORE_UNUSED, 0), + GATE(CLK_PCLK_PPMU_DREX1S0, "pclk_ppmu_drex1s0", "div_aclk_drex1", + ENABLE_PCLK_MIF, 27, CLK_IGNORE_UNUSED, 0), + GATE(CLK_PCLK_PPMU_DREX0S3, "pclk_ppmu_drex0s3", "div_aclk_drex0", + ENABLE_PCLK_MIF, 26, CLK_IGNORE_UNUSED, 0), + GATE(CLK_PCLK_PPMU_DREX0S1, "pclk_ppmu_drex0s1", "div_aclk_drex0", + ENABLE_PCLK_MIF, 25, CLK_IGNORE_UNUSED, 0), + GATE(CLK_PCLK_PPMU_DREX0S0, "pclk_ppmu_drex0s0", "div_aclk_drex0", + ENABLE_PCLK_MIF, 24, CLK_IGNORE_UNUSED, 0), + GATE(CLK_PCLK_ASYNCAXI_NOC_P_CCI, "pclk_asyncaxi_noc_p_cci", + "div_aclk_mif_133", ENABLE_PCLK_MIF, 21, + CLK_IGNORE_UNUSED, 0), + GATE(CLK_PCLK_ASYNCAXI_CP1, "pclk_asyncaxi_cp1", "div_aclk_mif_133", + ENABLE_PCLK_MIF, 19, 0, 0), + GATE(CLK_PCLK_ASYNCAXI_CP0, "pclk_asyncaxi_cp0", "div_aclk_mif_133", + ENABLE_PCLK_MIF, 18, 0, 0), + GATE(CLK_PCLK_ASYNCAXI_DREX1_3, "pclk_asyncaxi_drex1_3", + "div_aclk_mif_133", ENABLE_PCLK_MIF, 17, 0, 0), + GATE(CLK_PCLK_ASYNCAXI_DREX1_1, "pclk_asyncaxi_drex1_1", + "div_aclk_mif_133", ENABLE_PCLK_MIF, 16, 0, 0), + GATE(CLK_PCLK_ASYNCAXI_DREX1_0, "pclk_asyncaxi_drex1_0", + "div_aclk_mif_133", ENABLE_PCLK_MIF, 15, 0, 0), + GATE(CLK_PCLK_ASYNCAXI_DREX0_3, "pclk_asyncaxi_drex0_3", + "div_aclk_mif_133", ENABLE_PCLK_MIF, 14, 0, 0), + GATE(CLK_PCLK_ASYNCAXI_DREX0_1, "pclk_asyncaxi_drex0_1", + "div_aclk_mif_133", ENABLE_PCLK_MIF, 13, 0, 0), + GATE(CLK_PCLK_ASYNCAXI_DREX0_0, "pclk_asyncaxi_drex0_0", + "div_aclk_mif_133", ENABLE_PCLK_MIF, 12, 0, 0), + GATE(CLK_PCLK_MIFSRVND_133, "pclk_mifsrvnd_133", "div_aclk_mif_133", + ENABLE_PCLK_MIF, 11, 0, 0), + GATE(CLK_PCLK_PMU_MIF, "pclk_pmu_mif", "div_aclk_mif_133", + ENABLE_PCLK_MIF, 10, CLK_IGNORE_UNUSED, 0), + GATE(CLK_PCLK_SYSREG_MIF, "pclk_sysreg_mif", "div_aclk_mif_133", + ENABLE_PCLK_MIF, 9, CLK_IGNORE_UNUSED, 0), + GATE(CLK_PCLK_GPIO_ALIVE, "pclk_gpio_alive", "div_aclk_mif_133", + ENABLE_PCLK_MIF, 8, CLK_IGNORE_UNUSED, 0), + GATE(CLK_PCLK_ABB, "pclk_abb", "div_aclk_mif_133", + ENABLE_PCLK_MIF, 7, 0, 0), + GATE(CLK_PCLK_PMU_APBIF, "pclk_pmu_apbif", "div_aclk_mif_133", + ENABLE_PCLK_MIF, 6, CLK_IGNORE_UNUSED, 0), + GATE(CLK_PCLK_DDR_PHY1, "pclk_ddr_phy1", "div_aclk_mif_133", + ENABLE_PCLK_MIF, 5, 0, 0), + GATE(CLK_PCLK_DREX1, "pclk_drex1", "div_aclk_mif_133", + ENABLE_PCLK_MIF, 3, CLK_IGNORE_UNUSED, 0), + GATE(CLK_PCLK_DDR_PHY0, "pclk_ddr_phy0", "div_aclk_mif_133", + ENABLE_PCLK_MIF, 2, 0, 0), + GATE(CLK_PCLK_DREX0, "pclk_drex0", "div_aclk_mif_133", + ENABLE_PCLK_MIF, 0, CLK_IGNORE_UNUSED, 0), + + /* ENABLE_PCLK_MIF_SECURE_DREX0_TZ */ + GATE(CLK_PCLK_DREX0_TZ, "pclk_drex0_tz", "div_aclk_mif_133", + ENABLE_PCLK_MIF_SECURE_DREX0_TZ, 0, 0, 0), + + /* ENABLE_PCLK_MIF_SECURE_DREX1_TZ */ + GATE(CLK_PCLK_DREX1_TZ, "pclk_drex1_tz", "div_aclk_mif_133", + ENABLE_PCLK_MIF_SECURE_DREX1_TZ, 0, 0, 0), + + /* ENABLE_PCLK_MIF_SECURE_MONOTONIC_CNT */ + GATE(CLK_PCLK_MONOTONIC_CNT, "pclk_monotonic_cnt", "div_aclk_mif_133", + ENABLE_PCLK_MIF_SECURE_RTC, 0, 0, 0), + + /* ENABLE_PCLK_MIF_SECURE_RTC */ + GATE(CLK_PCLK_RTC, "pclk_rtc", "div_aclk_mif_133", + ENABLE_PCLK_MIF_SECURE_RTC, 0, 0, 0), + + /* ENABLE_SCLK_MIF */ + GATE(CLK_SCLK_DSIM1_DISP, "sclk_dsim1_disp", "div_sclk_dsim1", + ENABLE_SCLK_MIF, 15, CLK_IGNORE_UNUSED, 0), + GATE(CLK_SCLK_DECON_TV_VCLK_DISP, "sclk_decon_tv_vclk_disp", + "div_sclk_decon_tv_vclk", ENABLE_SCLK_MIF, + 14, CLK_IGNORE_UNUSED, 0), + GATE(CLK_SCLK_DSIM0_DISP, "sclk_dsim0_disp", "div_sclk_dsim0", + ENABLE_SCLK_MIF, 9, CLK_IGNORE_UNUSED, 0), + GATE(CLK_SCLK_DSD_DISP, "sclk_dsd_disp", "div_sclk_dsd", + ENABLE_SCLK_MIF, 8, CLK_IGNORE_UNUSED, 0), + GATE(CLK_SCLK_DECON_TV_ECLK_DISP, "sclk_decon_tv_eclk_disp", + "div_sclk_decon_tv_eclk", ENABLE_SCLK_MIF, + 7, CLK_IGNORE_UNUSED, 0), + GATE(CLK_SCLK_DECON_VCLK_DISP, "sclk_decon_vclk_disp", + "div_sclk_decon_vclk", ENABLE_SCLK_MIF, + 6, CLK_IGNORE_UNUSED, 0), + GATE(CLK_SCLK_DECON_ECLK_DISP, "sclk_decon_eclk_disp", + "div_sclk_decon_eclk", ENABLE_SCLK_MIF, + 5, CLK_IGNORE_UNUSED, 0), + GATE(CLK_SCLK_HPM_MIF, "sclk_hpm_mif", "div_sclk_hpm_mif", + ENABLE_SCLK_MIF, 4, + CLK_IGNORE_UNUSED | CLK_SET_RATE_PARENT, 0), + GATE(CLK_SCLK_MFC_PLL, "sclk_mfc_pll", "mout_mfc_pll_div2", + ENABLE_SCLK_MIF, 3, CLK_IGNORE_UNUSED, 0), + GATE(CLK_SCLK_BUS_PLL, "sclk_bus_pll", "mout_bus_pll_div2", + ENABLE_SCLK_MIF, 2, CLK_IGNORE_UNUSED, 0), + GATE(CLK_SCLK_BUS_PLL_APOLLO, "sclk_bus_pll_apollo", "sclk_bus_pll", + ENABLE_SCLK_MIF, 1, CLK_IGNORE_UNUSED, 0), + GATE(CLK_SCLK_BUS_PLL_ATLAS, "sclk_bus_pll_atlas", "sclk_bus_pll", + ENABLE_SCLK_MIF, 0, CLK_IGNORE_UNUSED, 0), +}; + static struct samsung_cmu_info mif_cmu_info __initdata = { .pll_clks = mif_pll_clks, .nr_pll_clks = ARRAY_SIZE(mif_pll_clks), + .mux_clks = mif_mux_clks, + .nr_mux_clks = ARRAY_SIZE(mif_mux_clks), + .div_clks = mif_div_clks, + .nr_div_clks = ARRAY_SIZE(mif_div_clks), + .gate_clks = mif_gate_clks, + .nr_gate_clks = ARRAY_SIZE(mif_gate_clks), + .fixed_factor_clks = mif_fixed_factor_clks, + .nr_fixed_factor_clks = ARRAY_SIZE(mif_fixed_factor_clks), .nr_clk_ids = MIF_NR_CLK, .clk_regs = mif_clk_regs, .nr_clk_regs = ARRAY_SIZE(mif_clk_regs), diff --git a/include/dt-bindings/clock/exynos5433.h b/include/dt-bindings/clock/exynos5433.h index 818d6b6bbdc4..6a3ce113e1e5 100644 --- a/include/dt-bindings/clock/exynos5433.h +++ b/include/dt-bindings/clock/exynos5433.h @@ -149,8 +149,196 @@ #define CLK_FOUT_MEM1_PLL 2 #define CLK_FOUT_BUS_PLL 3 #define CLK_FOUT_MFC_PLL 4 +#define CLK_DOUT_MFC_PLL 5 +#define CLK_DOUT_BUS_PLL 6 +#define CLK_DOUT_MEM1_PLL 7 +#define CLK_DOUT_MEM0_PLL 8 -#define MIF_NR_CLK 5 +#define CLK_MOUT_MFC_PLL_DIV2 10 +#define CLK_MOUT_BUS_PLL_DIV2 11 +#define CLK_MOUT_MEM1_PLL_DIV2 12 +#define CLK_MOUT_MEM0_PLL_DIV2 13 +#define CLK_MOUT_MFC_PLL 14 +#define CLK_MOUT_BUS_PLL 15 +#define CLK_MOUT_MEM1_PLL 16 +#define CLK_MOUT_MEM0_PLL 17 +#define CLK_MOUT_CLK2X_PHY_C 18 +#define CLK_MOUT_CLK2X_PHY_B 19 +#define CLK_MOUT_CLK2X_PHY_A 20 +#define CLK_MOUT_CLKM_PHY_C 21 +#define CLK_MOUT_CLKM_PHY_B 22 +#define CLK_MOUT_CLKM_PHY_A 23 +#define CLK_MOUT_ACLK_MIFNM_200 24 +#define CLK_MOUT_ACLK_MIFNM_400 25 +#define CLK_MOUT_ACLK_DISP_333_B 26 +#define CLK_MOUT_ACLK_DISP_333_A 27 +#define CLK_MOUT_SCLK_DECON_VCLK_C 28 +#define CLK_MOUT_SCLK_DECON_VCLK_B 29 +#define CLK_MOUT_SCLK_DECON_VCLK_A 30 +#define CLK_MOUT_SCLK_DECON_ECLK_C 31 +#define CLK_MOUT_SCLK_DECON_ECLK_B 32 +#define CLK_MOUT_SCLK_DECON_ECLK_A 33 +#define CLK_MOUT_SCLK_DECON_TV_ECLK_C 34 +#define CLK_MOUT_SCLK_DECON_TV_ECLK_B 35 +#define CLK_MOUT_SCLK_DECON_TV_ECLK_A 36 +#define CLK_MOUT_SCLK_DSD_C 37 +#define CLK_MOUT_SCLK_DSD_B 38 +#define CLK_MOUT_SCLK_DSD_A 39 +#define CLK_MOUT_SCLK_DSIM0_C 40 +#define CLK_MOUT_SCLK_DSIM0_B 41 +#define CLK_MOUT_SCLK_DSIM0_A 42 +#define CLK_MOUT_SCLK_DECON_TV_VCLK_C 46 +#define CLK_MOUT_SCLK_DECON_TV_VCLK_B 47 +#define CLK_MOUT_SCLK_DECON_TV_VCLK_A 48 +#define CLK_MOUT_SCLK_DSIM1_C 49 +#define CLK_MOUT_SCLK_DSIM1_B 50 +#define CLK_MOUT_SCLK_DSIM1_A 51 + +#define CLK_DIV_SCLK_HPM_MIF 55 +#define CLK_DIV_ACLK_DREX1 56 +#define CLK_DIV_ACLK_DREX0 57 +#define CLK_DIV_CLK2XPHY 58 +#define CLK_DIV_ACLK_MIF_266 59 +#define CLK_DIV_ACLK_MIFND_133 60 +#define CLK_DIV_ACLK_MIF_133 61 +#define CLK_DIV_ACLK_MIFNM_200 62 +#define CLK_DIV_ACLK_MIF_200 63 +#define CLK_DIV_ACLK_MIF_400 64 +#define CLK_DIV_ACLK_BUS2_400 65 +#define CLK_DIV_ACLK_DISP_333 66 +#define CLK_DIV_ACLK_CPIF_200 67 +#define CLK_DIV_SCLK_DSIM1 68 +#define CLK_DIV_SCLK_DECON_TV_VCLK 69 +#define CLK_DIV_SCLK_DSIM0 70 +#define CLK_DIV_SCLK_DSD 71 +#define CLK_DIV_SCLK_DECON_TV_ECLK 72 +#define CLK_DIV_SCLK_DECON_VCLK 73 +#define CLK_DIV_SCLK_DECON_ECLK 74 +#define CLK_DIV_MIF_PRE 75 + +#define CLK_CLK2X_PHY1 80 +#define CLK_CLK2X_PHY0 81 +#define CLK_CLKM_PHY1 82 +#define CLK_CLKM_PHY0 83 +#define CLK_RCLK_DREX1 84 +#define CLK_RCLK_DREX0 85 +#define CLK_ACLK_DREX1_TZ 86 +#define CLK_ACLK_DREX0_TZ 87 +#define CLK_ACLK_DREX1_PEREV 88 +#define CLK_ACLK_DREX0_PEREV 89 +#define CLK_ACLK_DREX1_MEMIF 90 +#define CLK_ACLK_DREX0_MEMIF 91 +#define CLK_ACLK_DREX1_SCH 92 +#define CLK_ACLK_DREX0_SCH 93 +#define CLK_ACLK_DREX1_BUSIF 94 +#define CLK_ACLK_DREX0_BUSIF 95 +#define CLK_ACLK_DREX1_BUSIF_RD 96 +#define CLK_ACLK_DREX0_BUSIF_RD 97 +#define CLK_ACLK_DREX1 98 +#define CLK_ACLK_DREX0 99 +#define CLK_ACLK_ASYNCAXIM_ATLAS_CCIX 100 +#define CLK_ACLK_ASYNCAXIS_ATLAS_MIF 101 +#define CLK_ACLK_ASYNCAXIM_ATLAS_MIF 102 +#define CLK_ACLK_ASYNCAXIS_MIF_IMEM 103 +#define CLK_ACLK_ASYNCAXIS_NOC_P_CCI 104 +#define CLK_ACLK_ASYNCAXIM_NOC_P_CCI 105 +#define CLK_ACLK_ASYNCAXIS_CP1 106 +#define CLK_ACLK_ASYNCAXIM_CP1 107 +#define CLK_ACLK_ASYNCAXIS_CP0 108 +#define CLK_ACLK_ASYNCAXIM_CP0 109 +#define CLK_ACLK_ASYNCAXIS_DREX1_3 110 +#define CLK_ACLK_ASYNCAXIM_DREX1_3 111 +#define CLK_ACLK_ASYNCAXIS_DREX1_1 112 +#define CLK_ACLK_ASYNCAXIM_DREX1_1 113 +#define CLK_ACLK_ASYNCAXIS_DREX1_0 114 +#define CLK_ACLK_ASYNCAXIM_DREX1_0 115 +#define CLK_ACLK_ASYNCAXIS_DREX0_3 116 +#define CLK_ACLK_ASYNCAXIM_DREX0_3 117 +#define CLK_ACLK_ASYNCAXIS_DREX0_1 118 +#define CLK_ACLK_ASYNCAXIM_DREX0_1 119 +#define CLK_ACLK_ASYNCAXIS_DREX0_0 120 +#define CLK_ACLK_ASYNCAXIM_DREX0_0 121 +#define CLK_ACLK_AHB2APB_MIF2P 122 +#define CLK_ACLK_AHB2APB_MIF1P 123 +#define CLK_ACLK_AHB2APB_MIF0P 124 +#define CLK_ACLK_IXIU_CCI 125 +#define CLK_ACLK_XIU_MIFSFRX 126 +#define CLK_ACLK_MIFNP_133 127 +#define CLK_ACLK_MIFNM_200 128 +#define CLK_ACLK_MIFND_133 129 +#define CLK_ACLK_MIFND_400 130 +#define CLK_ACLK_CCI 131 +#define CLK_ACLK_MIFND_266 132 +#define CLK_ACLK_PPMU_DREX1S3 133 +#define CLK_ACLK_PPMU_DREX1S1 134 +#define CLK_ACLK_PPMU_DREX1S0 135 +#define CLK_ACLK_PPMU_DREX0S3 136 +#define CLK_ACLK_PPMU_DREX0S1 137 +#define CLK_ACLK_PPMU_DREX0S0 138 +#define CLK_ACLK_BTS_APOLLO 139 +#define CLK_ACLK_BTS_ATLAS 140 +#define CLK_ACLK_ACE_SEL_APOLL 141 +#define CLK_ACLK_ACE_SEL_ATLAS 142 +#define CLK_ACLK_AXIDS_CCI_MIFSFRX 143 +#define CLK_ACLK_AXIUS_ATLAS_CCI 144 +#define CLK_ACLK_AXISYNCDNS_CCI 145 +#define CLK_ACLK_AXISYNCDN_CCI 146 +#define CLK_ACLK_AXISYNCDN_NOC_D 147 +#define CLK_ACLK_ASYNCACEM_APOLLO_CCI 148 +#define CLK_ACLK_ASYNCACEM_ATLAS_CCI 149 +#define CLK_ACLK_ASYNCAPBS_MIF_CSSYS 150 +#define CLK_ACLK_BUS2_400 151 +#define CLK_ACLK_DISP_333 152 +#define CLK_ACLK_CPIF_200 153 +#define CLK_PCLK_PPMU_DREX1S3 154 +#define CLK_PCLK_PPMU_DREX1S1 155 +#define CLK_PCLK_PPMU_DREX1S0 156 +#define CLK_PCLK_PPMU_DREX0S3 157 +#define CLK_PCLK_PPMU_DREX0S1 158 +#define CLK_PCLK_PPMU_DREX0S0 159 +#define CLK_PCLK_BTS_APOLLO 160 +#define CLK_PCLK_BTS_ATLAS 161 +#define CLK_PCLK_ASYNCAXI_NOC_P_CCI 162 +#define CLK_PCLK_ASYNCAXI_CP1 163 +#define CLK_PCLK_ASYNCAXI_CP0 164 +#define CLK_PCLK_ASYNCAXI_DREX1_3 165 +#define CLK_PCLK_ASYNCAXI_DREX1_1 166 +#define CLK_PCLK_ASYNCAXI_DREX1_0 167 +#define CLK_PCLK_ASYNCAXI_DREX0_3 168 +#define CLK_PCLK_ASYNCAXI_DREX0_1 169 +#define CLK_PCLK_ASYNCAXI_DREX0_0 170 +#define CLK_PCLK_MIFSRVND_133 171 +#define CLK_PCLK_PMU_MIF 172 +#define CLK_PCLK_SYSREG_MIF 173 +#define CLK_PCLK_GPIO_ALIVE 174 +#define CLK_PCLK_ABB 175 +#define CLK_PCLK_PMU_APBIF 176 +#define CLK_PCLK_DDR_PHY1 177 +#define CLK_PCLK_DREX1 178 +#define CLK_PCLK_DDR_PHY0 179 +#define CLK_PCLK_DREX0 180 +#define CLK_PCLK_DREX0_TZ 181 +#define CLK_PCLK_DREX1_TZ 182 +#define CLK_PCLK_MONOTONIC_CNT 183 +#define CLK_PCLK_RTC 184 +#define CLK_SCLK_DSIM1_DISP 185 +#define CLK_SCLK_DECON_TV_VCLK_DISP 186 +#define CLK_SCLK_FREQ_DET_BUS_PLL 187 +#define CLK_SCLK_FREQ_DET_MFC_PLL 188 +#define CLK_SCLK_FREQ_DET_MEM0_PLL 189 +#define CLK_SCLK_FREQ_DET_MEM1_PLL 190 +#define CLK_SCLK_DSIM0_DISP 191 +#define CLK_SCLK_DSD_DISP 192 +#define CLK_SCLK_DECON_TV_ECLK_DISP 193 +#define CLK_SCLK_DECON_VCLK_DISP 194 +#define CLK_SCLK_DECON_ECLK_DISP 195 +#define CLK_SCLK_HPM_MIF 196 +#define CLK_SCLK_MFC_PLL 197 +#define CLK_SCLK_BUS_PLL 198 +#define CLK_SCLK_BUS_PLL_APOLLO 199 +#define CLK_SCLK_BUS_PLL_ATLAS 200 + +#define MIF_NR_CLK 201 /* CMU_PERIC */ #define CLK_PCLK_SPI2 1 -- cgit v1.2.3 From 2a1808a6c00fb6d75ebfa596add57638b9290926 Mon Sep 17 00:00:00 2001 From: Chanwoo Choi Date: Mon, 2 Feb 2015 23:24:02 +0900 Subject: clk: samsung: exynos5433: Add clocks for CMU_DISP domain This patch adds the the mux/divider/gate clocks for CMU_DISP domain which includes clocks of the display IPs (DECON/HDMI/DSIM/MIXER). Also, CMU_DISP requires 'sclk_hdmi_spdif_disp' source clock from CMU_TOP domain. This patch adds the clocks of CMU_TOP related to HDMI. Signed-off-by: Chanwoo Choi Acked-by: Inki Dae Signed-off-by: Sylwester Nawrocki --- drivers/clk/samsung/clk-exynos5433.c | 437 +++++++++++++++++++++++++++++++++ include/dt-bindings/clock/exynos5433.h | 114 ++++++++- 2 files changed, 550 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/drivers/clk/samsung/clk-exynos5433.c b/drivers/clk/samsung/clk-exynos5433.c index 09ccf11bab64..3e6c3d595e96 100644 --- a/drivers/clk/samsung/clk-exynos5433.c +++ b/drivers/clk/samsung/clk-exynos5433.c @@ -245,6 +245,8 @@ PNAME(mout_sclk_audio1_p) = { "ioclk_audiocdclk1", "oscclk", PNAME(mout_sclk_audio0_p) = { "ioclk_audiocdclk0", "oscclk", "mout_aud_pll_user_t",}; +PNAME(mout_sclk_hdmi_spdif_p) = { "sclk_audio1", "ioclk_spdif_extclk", }; + static struct samsung_fixed_factor_clock top_fixed_factor_clks[] __initdata = { FFACTOR(0, "oscclk_efuse_common", "oscclk", 1, 1, 0), }; @@ -395,6 +397,10 @@ static struct samsung_mux_clock top_mux_clks[] __initdata = { MUX_SEL_TOP_PERIC1, 4, 2), MUX(CLK_MOUT_SCLK_AUDIO0, "mout_sclk_audio0", mout_sclk_audio0_p, MUX_SEL_TOP_PERIC1, 0, 2), + + /* MUX_SEL_TOP_DISP */ + MUX(CLK_MOUT_SCLK_HDMI_SPDIF, "mout_sclk_hdmi_spdif", + mout_sclk_hdmi_spdif_p, MUX_SEL_TOP_DISP, 0, 1), }; static struct samsung_div_clock top_div_clks[] __initdata = { @@ -1360,6 +1366,11 @@ static struct samsung_gate_clock mif_gate_clks[] __initdata = { ENABLE_SCLK_MIF, 1, CLK_IGNORE_UNUSED, 0), GATE(CLK_SCLK_BUS_PLL_ATLAS, "sclk_bus_pll_atlas", "sclk_bus_pll", ENABLE_SCLK_MIF, 0, CLK_IGNORE_UNUSED, 0), + + /* ENABLE_SCLK_TOP_DISP */ + GATE(CLK_SCLK_HDMI_SPDIF_DISP, "sclk_hdmi_spdif_disp", + "mout_sclk_hdmi_spdif", ENABLE_SCLK_TOP_DISP, 0, + CLK_IGNORE_UNUSED, 0), }; static struct samsung_cmu_info mif_cmu_info __initdata = { @@ -2022,3 +2033,429 @@ static void __init exynos5433_cmu_g2d_init(struct device_node *np) CLK_OF_DECLARE(exynos5433_cmu_g2d, "samsung,exynos5433-cmu-g2d", exynos5433_cmu_g2d_init); + +/* + * Register offset definitions for CMU_DISP + */ +#define DISP_PLL_LOCK 0x0000 +#define DISP_PLL_CON0 0x0100 +#define DISP_PLL_CON1 0x0104 +#define DISP_PLL_FREQ_DET 0x0108 +#define MUX_SEL_DISP0 0x0200 +#define MUX_SEL_DISP1 0x0204 +#define MUX_SEL_DISP2 0x0208 +#define MUX_SEL_DISP3 0x020c +#define MUX_SEL_DISP4 0x0210 +#define MUX_ENABLE_DISP0 0x0300 +#define MUX_ENABLE_DISP1 0x0304 +#define MUX_ENABLE_DISP2 0x0308 +#define MUX_ENABLE_DISP3 0x030c +#define MUX_ENABLE_DISP4 0x0310 +#define MUX_STAT_DISP0 0x0400 +#define MUX_STAT_DISP1 0x0404 +#define MUX_STAT_DISP2 0x0408 +#define MUX_STAT_DISP3 0x040c +#define MUX_STAT_DISP4 0x0410 +#define MUX_IGNORE_DISP2 0x0508 +#define DIV_DISP 0x0600 +#define DIV_DISP_PLL_FREQ_DET 0x0604 +#define DIV_STAT_DISP 0x0700 +#define DIV_STAT_DISP_PLL_FREQ_DET 0x0704 +#define ENABLE_ACLK_DISP0 0x0800 +#define ENABLE_ACLK_DISP1 0x0804 +#define ENABLE_PCLK_DISP 0x0900 +#define ENABLE_SCLK_DISP 0x0a00 +#define ENABLE_IP_DISP0 0x0b00 +#define ENABLE_IP_DISP1 0x0b04 +#define CLKOUT_CMU_DISP 0x0c00 +#define CLKOUT_CMU_DISP_DIV_STAT 0x0c04 + +static unsigned long disp_clk_regs[] __initdata = { + DISP_PLL_LOCK, + DISP_PLL_CON0, + DISP_PLL_CON1, + DISP_PLL_FREQ_DET, + MUX_SEL_DISP0, + MUX_SEL_DISP1, + MUX_SEL_DISP2, + MUX_SEL_DISP3, + MUX_SEL_DISP4, + MUX_ENABLE_DISP0, + MUX_ENABLE_DISP1, + MUX_ENABLE_DISP2, + MUX_ENABLE_DISP3, + MUX_ENABLE_DISP4, + MUX_STAT_DISP0, + MUX_STAT_DISP1, + MUX_STAT_DISP2, + MUX_STAT_DISP3, + MUX_STAT_DISP4, + MUX_IGNORE_DISP2, + DIV_DISP, + DIV_DISP_PLL_FREQ_DET, + DIV_STAT_DISP, + DIV_STAT_DISP_PLL_FREQ_DET, + ENABLE_ACLK_DISP0, + ENABLE_ACLK_DISP1, + ENABLE_PCLK_DISP, + ENABLE_SCLK_DISP, + ENABLE_IP_DISP0, + ENABLE_IP_DISP1, + CLKOUT_CMU_DISP, + CLKOUT_CMU_DISP_DIV_STAT, +}; + +/* list of all parent clock list */ +PNAME(mout_disp_pll_p) = { "oscclk", "fout_disp_pll", }; +PNAME(mout_sclk_dsim1_user_p) = { "oscclk", "sclk_dsim1_disp", }; +PNAME(mout_sclk_dsim0_user_p) = { "oscclk", "sclk_dsim0_disp", }; +PNAME(mout_sclk_dsd_user_p) = { "oscclk", "sclk_dsd_disp", }; +PNAME(mout_sclk_decon_tv_eclk_user_p) = { "oscclk", + "sclk_decon_tv_eclk_disp", }; +PNAME(mout_sclk_decon_vclk_user_p) = { "oscclk", + "sclk_decon_vclk_disp", }; +PNAME(mout_sclk_decon_eclk_user_p) = { "oscclk", + "sclk_decon_eclk_disp", }; +PNAME(mout_sclk_decon_tv_vlkc_user_p) = { "oscclk", + "sclk_decon_tv_vclk_disp", }; +PNAME(mout_aclk_disp_333_user_p) = { "oscclk", "aclk_disp_333", }; + +PNAME(mout_phyclk_mipidphy1_bitclkdiv8_user_p) = { "oscclk", + "phyclk_mipidphy1_bitclkdiv8_phy", }; +PNAME(mout_phyclk_mipidphy1_rxclkesc0_user_p) = { "oscclk", + "phyclk_mipidphy1_rxclkesc0_phy", }; +PNAME(mout_phyclk_mipidphy0_bitclkdiv8_user_p) = { "oscclk", + "phyclk_mipidphy0_bitclkdiv8_phy", }; +PNAME(mout_phyclk_mipidphy0_rxclkesc0_user_p) = { "oscclk", + "phyclk_mipidphy0_rxclkesc0_phy", }; +PNAME(mout_phyclk_hdmiphy_tmds_clko_user_p) = { "oscclk", + "phyclk_hdmiphy_tmds_clko_phy", }; +PNAME(mout_phyclk_hdmiphy_pixel_clko_user_p) = { "oscclk", + "phyclk_hdmiphy_pixel_clko_phy", }; + +PNAME(mout_sclk_dsim0_p) = { "mout_disp_pll", + "mout_sclk_dsim0_user", }; +PNAME(mout_sclk_decon_tv_eclk_p) = { "mout_disp_pll", + "mout_sclk_decon_tv_eclk_user", }; +PNAME(mout_sclk_decon_vclk_p) = { "mout_disp_pll", + "mout_sclk_decon_vclk_user", }; +PNAME(mout_sclk_decon_eclk_p) = { "mout_disp_pll", + "mout_sclk_decon_eclk_user", }; + +PNAME(mout_sclk_dsim1_b_disp_p) = { "mout_sclk_dsim1_a_disp", + "mout_sclk_dsim1_user", }; +PNAME(mout_sclk_decon_tv_vclk_c_disp_p) = { + "mout_phyclk_hdmiphy_pixel_clko_user", + "mout_sclk_decon_tv_vclk_b_disp", }; +PNAME(mout_sclk_decon_tv_vclk_b_disp_p) = { "mout_sclk_decon_tv_vclk_a_disp", + "mout_sclk_decon_tv_vclk_user", }; + +static struct samsung_pll_clock disp_pll_clks[] __initdata = { + PLL(pll_35xx, CLK_FOUT_DISP_PLL, "fout_disp_pll", "oscclk", + DISP_PLL_LOCK, DISP_PLL_CON0, exynos5443_pll_rates), +}; + +static struct samsung_fixed_factor_clock disp_fixed_factor_clks[] __initdata = { + /* + * sclk_rgb_{vclk|tv_vclk} is half clock of sclk_decon_{vclk|tv_vclk}. + * The divider has fixed value (2) between sclk_rgb_{vclk|tv_vclk} + * and sclk_decon_{vclk|tv_vclk}. + */ + FFACTOR(CLK_SCLK_RGB_VCLK, "sclk_rgb_vclk", "sclk_decon_vclk", + 1, 2, 0), + FFACTOR(CLK_SCLK_RGB_TV_VCLK, "sclk_rgb_tv_vclk", "sclk_decon_tv_vclk", + 1, 2, 0), +}; + +static struct samsung_fixed_rate_clock disp_fixed_clks[] __initdata = { + /* PHY clocks from MIPI_DPHY1 */ + FRATE(0, "phyclk_mipidphy1_bitclkdiv8_phy", NULL, CLK_IS_ROOT, + 188000000), + FRATE(0, "phyclk_mipidphy1_rxclkesc0_phy", NULL, CLK_IS_ROOT, + 100000000), + /* PHY clocks from MIPI_DPHY0 */ + FRATE(0, "phyclk_mipidphy0_bitclkdiv8_phy", NULL, CLK_IS_ROOT, + 188000000), + FRATE(0, "phyclk_mipidphy0_rxclkesc0_phy", NULL, CLK_IS_ROOT, + 100000000), + /* PHY clocks from HDMI_PHY */ + FRATE(0, "phyclk_hdmiphy_tmds_clko_phy", NULL, CLK_IS_ROOT, 300000000), + FRATE(0, "phyclk_hdmiphy_pixel_clko_phy", NULL, CLK_IS_ROOT, 166000000), +}; + +static struct samsung_mux_clock disp_mux_clks[] __initdata = { + /* MUX_SEL_DISP0 */ + MUX(CLK_MOUT_DISP_PLL, "mout_disp_pll", mout_disp_pll_p, MUX_SEL_DISP0, + 0, 1), + + /* MUX_SEL_DISP1 */ + MUX(CLK_MOUT_SCLK_DSIM1_USER, "mout_sclk_dsim1_user", + mout_sclk_dsim1_user_p, MUX_SEL_DISP1, 28, 1), + MUX(CLK_MOUT_SCLK_DSIM0_USER, "mout_sclk_dsim0_user", + mout_sclk_dsim0_user_p, MUX_SEL_DISP1, 24, 1), + MUX(CLK_MOUT_SCLK_DSD_USER, "mout_sclk_dsd_user", mout_sclk_dsd_user_p, + MUX_SEL_DISP1, 20, 1), + MUX(CLK_MOUT_SCLK_DECON_TV_ECLK_USER, "mout_sclk_decon_tv_eclk_user", + mout_sclk_decon_tv_eclk_user_p, MUX_SEL_DISP1, 16, 1), + MUX(CLK_MOUT_SCLK_DECON_VCLK_USER, "mout_sclk_decon_vclk_user", + mout_sclk_decon_vclk_user_p, MUX_SEL_DISP1, 12, 1), + MUX(CLK_MOUT_SCLK_DECON_ECLK_USER, "mout_sclk_decon_eclk_user", + mout_sclk_decon_eclk_user_p, MUX_SEL_DISP1, 8, 1), + MUX(CLK_MOUT_SCLK_DECON_TV_VCLK_USER, "mout_sclk_decon_tv_vclk_user", + mout_sclk_decon_tv_vlkc_user_p, MUX_SEL_DISP1, 4, 1), + MUX(CLK_MOUT_ACLK_DISP_333_USER, "mout_aclk_disp_333_user", + mout_aclk_disp_333_user_p, MUX_SEL_DISP1, 0, 1), + + /* MUX_SEL_DISP2 */ + MUX(CLK_MOUT_PHYCLK_MIPIDPHY1_BITCLKDIV8_USER, + "mout_phyclk_mipidphy1_bitclkdiv8_user", + mout_phyclk_mipidphy1_bitclkdiv8_user_p, MUX_SEL_DISP2, + 20, 1), + MUX(CLK_MOUT_PHYCLK_MIPIDPHY1_RXCLKESC0_USER, + "mout_phyclk_mipidphy1_rxclkesc0_user", + mout_phyclk_mipidphy1_rxclkesc0_user_p, MUX_SEL_DISP2, + 16, 1), + MUX(CLK_MOUT_PHYCLK_MIPIDPHY0_BITCLKDIV8_USER, + "mout_phyclk_mipidphy0_bitclkdiv8_user", + mout_phyclk_mipidphy0_bitclkdiv8_user_p, MUX_SEL_DISP2, + 12, 1), + MUX(CLK_MOUT_PHYCLK_MIPIDPHY0_RXCLKESC0_USER, + "mout_phyclk_mipidphy0_rxclkesc0_user", + mout_phyclk_mipidphy0_rxclkesc0_user_p, MUX_SEL_DISP2, + 8, 1), + MUX(CLK_MOUT_PHYCLK_HDMIPHY_TMDS_CLKO_USER, + "mout_phyclk_hdmiphy_tmds_clko_user", + mout_phyclk_hdmiphy_tmds_clko_user_p, MUX_SEL_DISP2, + 4, 1), + MUX(CLK_MOUT_PHYCLK_HDMIPHY_PIXEL_CLKO_USER, + "mout_phyclk_hdmiphy_pixel_clko_user", + mout_phyclk_hdmiphy_pixel_clko_user_p, MUX_SEL_DISP2, + 0, 1), + + /* MUX_SEL_DISP3 */ + MUX(CLK_MOUT_SCLK_DSIM0, "mout_sclk_dsim0", mout_sclk_dsim0_p, + MUX_SEL_DISP3, 12, 1), + MUX(CLK_MOUT_SCLK_DECON_TV_ECLK, "mout_sclk_decon_tv_eclk", + mout_sclk_decon_tv_eclk_p, MUX_SEL_DISP3, 8, 1), + MUX(CLK_MOUT_SCLK_DECON_VCLK, "mout_sclk_decon_vclk", + mout_sclk_decon_vclk_p, MUX_SEL_DISP3, 4, 1), + MUX(CLK_MOUT_SCLK_DECON_ECLK, "mout_sclk_decon_eclk", + mout_sclk_decon_eclk_p, MUX_SEL_DISP3, 0, 1), + + /* MUX_SEL_DISP4 */ + MUX(CLK_MOUT_SCLK_DSIM1_B_DISP, "mout_sclk_dsim1_b_disp", + mout_sclk_dsim1_b_disp_p, MUX_SEL_DISP4, 16, 1), + MUX(CLK_MOUT_SCLK_DSIM1_A_DISP, "mout_sclk_dsim1_a_disp", + mout_sclk_dsim0_p, MUX_SEL_DISP4, 12, 1), + MUX(CLK_MOUT_SCLK_DECON_TV_VCLK_C_DISP, + "mout_sclk_decon_tv_vclk_c_disp", + mout_sclk_decon_tv_vclk_c_disp_p, MUX_SEL_DISP4, 8, 1), + MUX(CLK_MOUT_SCLK_DECON_TV_VCLK_B_DISP, + "mout_sclk_decon_tv_vclk_b_disp", + mout_sclk_decon_tv_vclk_b_disp_p, MUX_SEL_DISP4, 4, 1), + MUX(CLK_MOUT_SCLK_DECON_TV_VCLK_A_DISP, + "mout_sclk_decon_tv_vclk_a_disp", + mout_sclk_decon_vclk_p, MUX_SEL_DISP4, 0, 1), +}; + +static struct samsung_div_clock disp_div_clks[] __initdata = { + /* DIV_DISP */ + DIV(CLK_DIV_SCLK_DSIM1_DISP, "div_sclk_dsim1_disp", + "mout_sclk_dsim1_b_disp", DIV_DISP, 24, 3), + DIV(CLK_DIV_SCLK_DECON_TV_VCLK_DISP, "div_sclk_decon_tv_vclk_disp", + "mout_sclk_decon_tv_vclk_c_disp", DIV_DISP, 20, 3), + DIV(CLK_DIV_SCLK_DSIM0_DISP, "div_sclk_dsim0_disp", "mout_sclk_dsim0", + DIV_DISP, 16, 3), + DIV(CLK_DIV_SCLK_DECON_TV_ECLK_DISP, "div_sclk_decon_tv_eclk_disp", + "mout_sclk_decon_tv_eclk", DIV_DISP, 12, 3), + DIV(CLK_DIV_SCLK_DECON_VCLK_DISP, "div_sclk_decon_vclk_disp", + "mout_sclk_decon_vclk", DIV_DISP, 8, 3), + DIV(CLK_DIV_SCLK_DECON_ECLK_DISP, "div_sclk_decon_eclk_disp", + "mout_sclk_decon_eclk", DIV_DISP, 4, 3), + DIV(CLK_DIV_PCLK_DISP, "div_pclk_disp", "mout_aclk_disp_333_user", + DIV_DISP, 0, 2), +}; + +static struct samsung_gate_clock disp_gate_clks[] __initdata = { + /* ENABLE_ACLK_DISP0 */ + GATE(CLK_ACLK_DECON_TV, "aclk_decon_tv", "mout_aclk_disp_333_user", + ENABLE_ACLK_DISP0, 2, 0, 0), + GATE(CLK_ACLK_DECON, "aclk_decon", "mout_aclk_disp_333_user", + ENABLE_ACLK_DISP0, 0, 0, 0), + + /* ENABLE_ACLK_DISP1 */ + GATE(CLK_ACLK_SMMU_TV1X, "aclk_smmu_tv1x", "mout_aclk_disp_333_user", + ENABLE_ACLK_DISP1, 25, 0, 0), + GATE(CLK_ACLK_SMMU_TV0X, "aclk_smmu_tv0x", "mout_aclk_disp_333_user", + ENABLE_ACLK_DISP1, 24, 0, 0), + GATE(CLK_ACLK_SMMU_DECON1X, "aclk_smmu_decon1x", + "mout_aclk_disp_333_user", ENABLE_ACLK_DISP1, 23, 0, 0), + GATE(CLK_ACLK_SMMU_DECON0X, "aclk_smmu_decon0x", + "mout_aclk_disp_333_user", ENABLE_ACLK_DISP1, 22, 0, 0), + GATE(CLK_ACLK_BTS_DECON_TV_M3, "aclk_bts_decon_tv_m3", + "mout_aclk_disp_333_user", ENABLE_ACLK_DISP1, 21, 0, 0), + GATE(CLK_ACLK_BTS_DECON_TV_M2, "aclk_bts_decon_tv_m2", + "mout_aclk_disp_333_user", ENABLE_ACLK_DISP1, 20, 0, 0), + GATE(CLK_ACLK_BTS_DECON_TV_M1, "aclk_bts_decon_tv_m1", + "mout_aclk_disp_333_user", ENABLE_ACLK_DISP1, 19, 0, 0), + GATE(CLK_ACLK_BTS_DECON_TV_M0, "aclk-bts_decon_tv_m0", + "mout_aclk_disp_333_user", ENABLE_ACLK_DISP1, 18, 0, 0), + GATE(CLK_ACLK_BTS_DECON_NM4, "aclk_bts_decon_nm4", + "mout_aclk_disp_333_user", ENABLE_ACLK_DISP1, 17, 0, 0), + GATE(CLK_ACLK_BTS_DECON_NM3, "aclk_bts_decon_nm3", + "mout_aclk_disp_333_user", ENABLE_ACLK_DISP1, 16, 0, 0), + GATE(CLK_ACLK_BTS_DECON_NM2, "aclk_bts_decon_nm2", + "mout_aclk_disp_333_user", ENABLE_ACLK_DISP1, 15, 0, 0), + GATE(CLK_ACLK_BTS_DECON_NM1, "aclk_bts_decon_nm1", + "mout_aclk_disp_333_user", ENABLE_ACLK_DISP1, 14, 0, 0), + GATE(CLK_ACLK_BTS_DECON_NM0, "aclk_bts_decon_nm0", + "mout_aclk_disp_333_user", ENABLE_ACLK_DISP1, 13, 0, 0), + GATE(CLK_ACLK_AHB2APB_DISPSFR2P, "aclk_ahb2apb_dispsfr2p", + "div_pclk_disp", ENABLE_ACLK_DISP1, + 12, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_AHB2APB_DISPSFR1P, "aclk_ahb2apb_dispsfr1p", + "div_pclk_disp", ENABLE_ACLK_DISP1, + 11, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_AHB2APB_DISPSFR0P, "aclk_ahb2apb_dispsfr0p", + "div_pclk_disp", ENABLE_ACLK_DISP1, + 10, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_AHB_DISPH, "aclk_ahb_disph", "div_pclk_disp", + ENABLE_ACLK_DISP1, 8, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_XIU_TV1X, "aclk_xiu_tv1x", "mout_aclk_disp_333_user", + ENABLE_ACLK_DISP1, 7, 0, 0), + GATE(CLK_ACLK_XIU_TV0X, "aclk_xiu_tv0x", "mout_aclk_disp_333_user", + ENABLE_ACLK_DISP1, 6, 0, 0), + GATE(CLK_ACLK_XIU_DECON1X, "aclk_xiu_decon1x", + "mout_aclk_disp_333_user", ENABLE_ACLK_DISP1, 5, 0, 0), + GATE(CLK_ACLK_XIU_DECON0X, "aclk_xiu_decon0x", + "mout_aclk_disp_333_user", ENABLE_ACLK_DISP1, 4, 0, 0), + GATE(CLK_ACLK_XIU_DISP1X, "aclk_xiu_disp1x", "mout_aclk_disp_333_user", + ENABLE_ACLK_DISP1, 3, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_XIU_DISPNP_100, "aclk_xiu_dispnp_100", "div_pclk_disp", + ENABLE_ACLK_DISP1, 2, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_DISP1ND_333, "aclk_disp1nd_333", + "mout_aclk_disp_333_user", ENABLE_ACLK_DISP1, 1, + CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_DISP0ND_333, "aclk_disp0nd_333", + "mout_aclk_disp_333_user", ENABLE_ACLK_DISP1, + 0, CLK_IGNORE_UNUSED, 0), + + /* ENABLE_PCLK_DISP */ + GATE(CLK_PCLK_SMMU_TV1X, "pclk_smmu_tv1x", "div_pclk_disp", + ENABLE_PCLK_DISP, 23, 0, 0), + GATE(CLK_PCLK_SMMU_TV0X, "pclk_smmu_tv0x", "div_pclk_disp", + ENABLE_PCLK_DISP, 22, 0, 0), + GATE(CLK_PCLK_SMMU_DECON1X, "pclk_smmu_decon1x", "div_pclk_disp", + ENABLE_PCLK_DISP, 21, 0, 0), + GATE(CLK_PCLK_SMMU_DECON0X, "pclk_smmu_decon0x", "div_pclk_disp", + ENABLE_PCLK_DISP, 20, 0, 0), + GATE(CLK_PCLK_BTS_DECON_TV_M3, "pclk_bts_decon_tv_m3", "div_pclk_disp", + ENABLE_PCLK_DISP, 19, 0, 0), + GATE(CLK_PCLK_BTS_DECON_TV_M2, "pclk_bts_decon_tv_m2", "div_pclk_disp", + ENABLE_PCLK_DISP, 18, 0, 0), + GATE(CLK_PCLK_BTS_DECON_TV_M1, "pclk_bts_decon_tv_m1", "div_pclk_disp", + ENABLE_PCLK_DISP, 17, 0, 0), + GATE(CLK_PCLK_BTS_DECON_TV_M0, "pclk_bts_decon_tv_m0", "div_pclk_disp", + ENABLE_PCLK_DISP, 16, 0, 0), + GATE(CLK_PCLK_BTS_DECONM4, "pclk_bts_deconm4", "div_pclk_disp", + ENABLE_PCLK_DISP, 15, 0, 0), + GATE(CLK_PCLK_BTS_DECONM3, "pclk_bts_deconm3", "div_pclk_disp", + ENABLE_PCLK_DISP, 14, 0, 0), + GATE(CLK_PCLK_BTS_DECONM2, "pclk_bts_deconm2", "div_pclk_disp", + ENABLE_PCLK_DISP, 13, 0, 0), + GATE(CLK_PCLK_BTS_DECONM1, "pclk_bts_deconm1", "div_pclk_disp", + ENABLE_PCLK_DISP, 12, 0, 0), + GATE(CLK_PCLK_BTS_DECONM0, "pclk_bts_deconm0", "div_pclk_disp", + ENABLE_PCLK_DISP, 11, 0, 0), + GATE(CLK_PCLK_MIC1, "pclk_mic1", "div_pclk_disp", + ENABLE_PCLK_DISP, 10, 0, 0), + GATE(CLK_PCLK_PMU_DISP, "pclk_pmu_disp", "div_pclk_disp", + ENABLE_PCLK_DISP, 9, CLK_IGNORE_UNUSED, 0), + GATE(CLK_PCLK_SYSREG_DISP, "pclk_sysreg_disp", "div_pclk_disp", + ENABLE_PCLK_DISP, 8, CLK_IGNORE_UNUSED, 0), + GATE(CLK_PCLK_HDMIPHY, "pclk_hdmiphy", "div_pclk_disp", + ENABLE_PCLK_DISP, 7, 0, 0), + GATE(CLK_PCLK_HDMI, "pclk_hdmi", "div_pclk_disp", + ENABLE_PCLK_DISP, 6, 0, 0), + GATE(CLK_PCLK_MIC0, "pclk_mic0", "div_pclk_disp", + ENABLE_PCLK_DISP, 5, 0, 0), + GATE(CLK_PCLK_DSIM1, "pclk_dsim1", "div_pclk_disp", + ENABLE_PCLK_DISP, 3, 0, 0), + GATE(CLK_PCLK_DSIM0, "pclk_dsim0", "div_pclk_disp", + ENABLE_PCLK_DISP, 2, 0, 0), + GATE(CLK_PCLK_DECON_TV, "pclk_decon_tv", "div_pclk_disp", + ENABLE_PCLK_DISP, 1, 0, 0), + + /* ENABLE_SCLK_DISP */ + GATE(CLK_PHYCLK_MIPIDPHY1_BITCLKDIV8, "phyclk_mipidphy1_bitclkdiv8", + "mout_phyclk_mipidphy1_bitclkdiv8_user", + ENABLE_SCLK_DISP, 26, 0, 0), + GATE(CLK_PHYCLK_MIPIDPHY1_RXCLKESC0, "phyclk_mipidphy1_rxclkesc0", + "mout_phyclk_mipidphy1_rxclkesc0_user", + ENABLE_SCLK_DISP, 25, 0, 0), + GATE(CLK_SCLK_RGB_TV_VCLK_TO_DSIM1, "sclk_rgb_tv_vclk_to_dsim1", + "sclk_rgb_tv_vclk", ENABLE_SCLK_DISP, 24, 0, 0), + GATE(CLK_SCLK_RGB_TV_VCLK_TO_MIC1, "sclk_rgb_tv_vclk_to_mic1", + "sclk_rgb_tv_vclk", ENABLE_SCLK_DISP, 23, 0, 0), + GATE(CLK_SCLK_DSIM1, "sclk_dsim1", "div_sclk_dsim1_disp", + ENABLE_SCLK_DISP, 22, 0, 0), + GATE(CLK_SCLK_DECON_TV_VCLK, "sclk_decon_tv_vclk", + "div_sclk_decon_tv_vclk_disp", + ENABLE_SCLK_DISP, 21, 0, 0), + GATE(CLK_PHYCLK_MIPIDPHY0_BITCLKDIV8, "phyclk_mipidphy0_bitclkdiv8", + "mout_phyclk_mipidphy0_bitclkdiv8_user", + ENABLE_SCLK_DISP, 15, 0, 0), + GATE(CLK_PHYCLK_MIPIDPHY0_RXCLKESC0, "phyclk_mipidphy0_rxclkesc0", + "mout_phyclk_mipidphy0_rxclkesc0_user", + ENABLE_SCLK_DISP, 14, 0, 0), + GATE(CLK_PHYCLK_HDMIPHY_TMDS_CLKO, "phyclk_hdmiphy_tmds_clko", + "mout_phyclk_hdmiphy_tmds_clko_user", + ENABLE_SCLK_DISP, 13, 0, 0), + GATE(CLK_PHYCLK_HDMI_PIXEL, "phyclk_hdmi_pixel", + "sclk_rgb_tv_vclk", ENABLE_SCLK_DISP, 12, 0, 0), + GATE(CLK_SCLK_RGB_VCLK_TO_SMIES, "sclk_rgb_vclk_to_smies", + "sclk_rgb_vclk", ENABLE_SCLK_DISP, 11, 0, 0), + GATE(CLK_SCLK_RGB_VCLK_TO_DSIM0, "sclk_rgb_vclk_to_dsim0", + "sclk_rgb_vclk", ENABLE_SCLK_DISP, 9, 0, 0), + GATE(CLK_SCLK_RGB_VCLK_TO_MIC0, "sclk_rgb_vclk_to_mic0", + "sclk_rgb_vclk", ENABLE_SCLK_DISP, 8, 0, 0), + GATE(CLK_SCLK_DSD, "sclk_dsd", "mout_sclk_dsd_user", + ENABLE_SCLK_DISP, 7, 0, 0), + GATE(CLK_SCLK_HDMI_SPDIF, "sclk_hdmi_spdif", "sclk_hdmi_spdif_disp", + ENABLE_SCLK_DISP, 6, 0, 0), + GATE(CLK_SCLK_DSIM0, "sclk_dsim0", "div_sclk_dsim0_disp", + ENABLE_SCLK_DISP, 5, 0, 0), + GATE(CLK_SCLK_DECON_TV_ECLK, "sclk_decon_tv_eclk", + "div_sclk_decon_tv_eclk_disp", + ENABLE_SCLK_DISP, 4, 0, 0), + GATE(CLK_SCLK_DECON_VCLK, "sclk_decon_vclk", + "div_sclk_decon_vclk_disp", ENABLE_SCLK_DISP, 3, 0, 0), + GATE(CLK_SCLK_DECON_ECLK, "sclk_decon_eclk", + "div_sclk_decon_eclk_disp", ENABLE_SCLK_DISP, 2, 0, 0), +}; + +static struct samsung_cmu_info disp_cmu_info __initdata = { + .pll_clks = disp_pll_clks, + .nr_pll_clks = ARRAY_SIZE(disp_pll_clks), + .mux_clks = disp_mux_clks, + .nr_mux_clks = ARRAY_SIZE(disp_mux_clks), + .div_clks = disp_div_clks, + .nr_div_clks = ARRAY_SIZE(disp_div_clks), + .gate_clks = disp_gate_clks, + .nr_gate_clks = ARRAY_SIZE(disp_gate_clks), + .fixed_clks = disp_fixed_clks, + .nr_fixed_clks = ARRAY_SIZE(disp_fixed_clks), + .fixed_factor_clks = disp_fixed_factor_clks, + .nr_fixed_factor_clks = ARRAY_SIZE(disp_fixed_factor_clks), + .nr_clk_ids = DISP_NR_CLK, + .clk_regs = disp_clk_regs, + .nr_clk_regs = ARRAY_SIZE(disp_clk_regs), +}; + +static void __init exynos5433_cmu_disp_init(struct device_node *np) +{ + samsung_cmu_register_one(np, &disp_cmu_info); +} + +CLK_OF_DECLARE(exynos5433_cmu_disp, "samsung,exynos5433-cmu-disp", + exynos5433_cmu_disp_init); diff --git a/include/dt-bindings/clock/exynos5433.h b/include/dt-bindings/clock/exynos5433.h index 6a3ce113e1e5..fe0650fad766 100644 --- a/include/dt-bindings/clock/exynos5433.h +++ b/include/dt-bindings/clock/exynos5433.h @@ -68,6 +68,7 @@ #define CLK_MOUT_SCLK_SPDIF 61 #define CLK_MOUT_SCLK_AUDIO1 62 #define CLK_MOUT_SCLK_AUDIO0 63 +#define CLK_MOUT_SCLK_HDMI_SPDIF 64 #define CLK_DIV_ACLK_FSYS_200 100 #define CLK_DIV_ACLK_IMEM_SSSX_266 101 @@ -337,8 +338,9 @@ #define CLK_SCLK_BUS_PLL 198 #define CLK_SCLK_BUS_PLL_APOLLO 199 #define CLK_SCLK_BUS_PLL_ATLAS 200 +#define CLK_SCLK_HDMI_SPDIF_DISP 201 -#define MIF_NR_CLK 201 +#define MIF_NR_CLK 202 /* CMU_PERIC */ #define CLK_PCLK_SPI2 1 @@ -514,4 +516,114 @@ #define G2D_NR_CLK 27 +/* CMU_DISP */ +#define CLK_FOUT_DISP_PLL 1 + +#define CLK_MOUT_DISP_PLL 2 +#define CLK_MOUT_SCLK_DSIM1_USER 3 +#define CLK_MOUT_SCLK_DSIM0_USER 4 +#define CLK_MOUT_SCLK_DSD_USER 5 +#define CLK_MOUT_SCLK_DECON_TV_ECLK_USER 6 +#define CLK_MOUT_SCLK_DECON_VCLK_USER 7 +#define CLK_MOUT_SCLK_DECON_ECLK_USER 8 +#define CLK_MOUT_SCLK_DECON_TV_VCLK_USER 9 +#define CLK_MOUT_ACLK_DISP_333_USER 10 +#define CLK_MOUT_PHYCLK_MIPIDPHY1_BITCLKDIV8_USER 11 +#define CLK_MOUT_PHYCLK_MIPIDPHY1_RXCLKESC0_USER 12 +#define CLK_MOUT_PHYCLK_MIPIDPHY0_BITCLKDIV8_USER 13 +#define CLK_MOUT_PHYCLK_MIPIDPHY0_RXCLKESC0_USER 14 +#define CLK_MOUT_PHYCLK_HDMIPHY_TMDS_CLKO_USER 15 +#define CLK_MOUT_PHYCLK_HDMIPHY_PIXEL_CLKO_USER 16 +#define CLK_MOUT_SCLK_DSIM0 17 +#define CLK_MOUT_SCLK_DECON_TV_ECLK 18 +#define CLK_MOUT_SCLK_DECON_VCLK 19 +#define CLK_MOUT_SCLK_DECON_ECLK 20 +#define CLK_MOUT_SCLK_DSIM1_B_DISP 21 +#define CLK_MOUT_SCLK_DSIM1_A_DISP 22 +#define CLK_MOUT_SCLK_DECON_TV_VCLK_C_DISP 23 +#define CLK_MOUT_SCLK_DECON_TV_VCLK_B_DISP 24 +#define CLK_MOUT_SCLK_DECON_TV_VCLK_A_DISP 25 + +#define CLK_DIV_SCLK_DSIM1_DISP 30 +#define CLK_DIV_SCLK_DECON_TV_VCLK_DISP 31 +#define CLK_DIV_SCLK_DSIM0_DISP 32 +#define CLK_DIV_SCLK_DECON_TV_ECLK_DISP 33 +#define CLK_DIV_SCLK_DECON_VCLK_DISP 34 +#define CLK_DIV_SCLK_DECON_ECLK_DISP 35 +#define CLK_DIV_PCLK_DISP 36 + +#define CLK_ACLK_DECON_TV 40 +#define CLK_ACLK_DECON 41 +#define CLK_ACLK_SMMU_TV1X 42 +#define CLK_ACLK_SMMU_TV0X 43 +#define CLK_ACLK_SMMU_DECON1X 44 +#define CLK_ACLK_SMMU_DECON0X 45 +#define CLK_ACLK_BTS_DECON_TV_M3 46 +#define CLK_ACLK_BTS_DECON_TV_M2 47 +#define CLK_ACLK_BTS_DECON_TV_M1 48 +#define CLK_ACLK_BTS_DECON_TV_M0 49 +#define CLK_ACLK_BTS_DECON_NM4 50 +#define CLK_ACLK_BTS_DECON_NM3 51 +#define CLK_ACLK_BTS_DECON_NM2 52 +#define CLK_ACLK_BTS_DECON_NM1 53 +#define CLK_ACLK_BTS_DECON_NM0 54 +#define CLK_ACLK_AHB2APB_DISPSFR2P 55 +#define CLK_ACLK_AHB2APB_DISPSFR1P 56 +#define CLK_ACLK_AHB2APB_DISPSFR0P 57 +#define CLK_ACLK_AHB_DISPH 58 +#define CLK_ACLK_XIU_TV1X 59 +#define CLK_ACLK_XIU_TV0X 60 +#define CLK_ACLK_XIU_DECON1X 61 +#define CLK_ACLK_XIU_DECON0X 62 +#define CLK_ACLK_XIU_DISP1X 63 +#define CLK_ACLK_XIU_DISPNP_100 64 +#define CLK_ACLK_DISP1ND_333 65 +#define CLK_ACLK_DISP0ND_333 66 +#define CLK_PCLK_SMMU_TV1X 67 +#define CLK_PCLK_SMMU_TV0X 68 +#define CLK_PCLK_SMMU_DECON1X 69 +#define CLK_PCLK_SMMU_DECON0X 70 +#define CLK_PCLK_BTS_DECON_TV_M3 71 +#define CLK_PCLK_BTS_DECON_TV_M2 72 +#define CLK_PCLK_BTS_DECON_TV_M1 73 +#define CLK_PCLK_BTS_DECON_TV_M0 74 +#define CLK_PCLK_BTS_DECONM4 75 +#define CLK_PCLK_BTS_DECONM3 76 +#define CLK_PCLK_BTS_DECONM2 77 +#define CLK_PCLK_BTS_DECONM1 78 +#define CLK_PCLK_BTS_DECONM0 79 +#define CLK_PCLK_MIC1 80 +#define CLK_PCLK_PMU_DISP 81 +#define CLK_PCLK_SYSREG_DISP 82 +#define CLK_PCLK_HDMIPHY 83 +#define CLK_PCLK_HDMI 84 +#define CLK_PCLK_MIC0 85 +#define CLK_PCLK_DSIM1 86 +#define CLK_PCLK_DSIM0 87 +#define CLK_PCLK_DECON_TV 88 +#define CLK_PHYCLK_MIPIDPHY1_BITCLKDIV8 89 +#define CLK_PHYCLK_MIPIDPHY1_RXCLKESC0 90 +#define CLK_SCLK_RGB_TV_VCLK_TO_DSIM1 91 +#define CLK_SCLK_RGB_TV_VCLK_TO_MIC1 92 +#define CLK_SCLK_DSIM1 93 +#define CLK_SCLK_DECON_TV_VCLK 94 +#define CLK_PHYCLK_MIPIDPHY0_BITCLKDIV8 95 +#define CLK_PHYCLK_MIPIDPHY0_RXCLKESC0 96 +#define CLK_PHYCLK_HDMIPHY_TMDS_CLKO 97 +#define CLK_PHYCLK_HDMI_PIXEL 98 +#define CLK_SCLK_RGB_VCLK_TO_SMIES 99 +#define CLK_SCLK_FREQ_DET_DISP_PLL 100 +#define CLK_SCLK_RGB_VCLK_TO_DSIM0 101 +#define CLK_SCLK_RGB_VCLK_TO_MIC0 102 +#define CLK_SCLK_DSD 103 +#define CLK_SCLK_HDMI_SPDIF 104 +#define CLK_SCLK_DSIM0 105 +#define CLK_SCLK_DECON_TV_ECLK 106 +#define CLK_SCLK_DECON_VCLK 107 +#define CLK_SCLK_DECON_ECLK 108 +#define CLK_SCLK_RGB_VCLK 109 +#define CLK_SCLK_RGB_TV_VCLK 110 + +#define DISP_NR_CLK 111 + #endif /* _DT_BINDINGS_CLOCK_EXYNOS5433_H */ -- cgit v1.2.3 From 2e997c035945784fb8c564305c0f0ddacc374fe4 Mon Sep 17 00:00:00 2001 From: Chanwoo Choi Date: Mon, 2 Feb 2015 23:24:03 +0900 Subject: clk: samsung: exynos5433: Add clocks for CMU_AUD domain This patch adds the mux/divider/gate clocks for CMU_AUD domain which includes the clocks of Cortex-A5/Bus/Audio clocks. Signed-off-by: Chanwoo Choi Acked-by: Inki Dae Signed-off-by: Sylwester Nawrocki --- drivers/clk/samsung/clk-exynos5433.c | 172 +++++++++++++++++++++++++++++++++ include/dt-bindings/clock/exynos5433.h | 53 ++++++++++ 2 files changed, 225 insertions(+) (limited to 'include') diff --git a/drivers/clk/samsung/clk-exynos5433.c b/drivers/clk/samsung/clk-exynos5433.c index 3e6c3d595e96..ad0105aa0de6 100644 --- a/drivers/clk/samsung/clk-exynos5433.c +++ b/drivers/clk/samsung/clk-exynos5433.c @@ -2459,3 +2459,175 @@ static void __init exynos5433_cmu_disp_init(struct device_node *np) CLK_OF_DECLARE(exynos5433_cmu_disp, "samsung,exynos5433-cmu-disp", exynos5433_cmu_disp_init); + +/* + * Register offset definitions for CMU_AUD + */ +#define MUX_SEL_AUD0 0x0200 +#define MUX_SEL_AUD1 0x0204 +#define MUX_ENABLE_AUD0 0x0300 +#define MUX_ENABLE_AUD1 0x0304 +#define MUX_STAT_AUD0 0x0400 +#define DIV_AUD0 0x0600 +#define DIV_AUD1 0x0604 +#define DIV_STAT_AUD0 0x0700 +#define DIV_STAT_AUD1 0x0704 +#define ENABLE_ACLK_AUD 0x0800 +#define ENABLE_PCLK_AUD 0x0900 +#define ENABLE_SCLK_AUD0 0x0a00 +#define ENABLE_SCLK_AUD1 0x0a04 +#define ENABLE_IP_AUD0 0x0b00 +#define ENABLE_IP_AUD1 0x0b04 + +static unsigned long aud_clk_regs[] __initdata = { + MUX_SEL_AUD0, + MUX_SEL_AUD1, + MUX_ENABLE_AUD0, + MUX_ENABLE_AUD1, + MUX_STAT_AUD0, + DIV_AUD0, + DIV_AUD1, + DIV_STAT_AUD0, + DIV_STAT_AUD1, + ENABLE_ACLK_AUD, + ENABLE_PCLK_AUD, + ENABLE_SCLK_AUD0, + ENABLE_SCLK_AUD1, + ENABLE_IP_AUD0, + ENABLE_IP_AUD1, +}; + +/* list of all parent clock list */ +PNAME(mout_aud_pll_user_aud_p) = { "oscclk", "fout_aud_pll", }; +PNAME(mout_sclk_aud_pcm_p) = { "mout_aud_pll_user", "ioclk_audiocdclk0",}; + +static struct samsung_fixed_rate_clock aud_fixed_clks[] __initdata = { + FRATE(0, "ioclk_jtag_tclk", NULL, CLK_IS_ROOT, 33000000), + FRATE(0, "ioclk_slimbus_clk", NULL, CLK_IS_ROOT, 25000000), + FRATE(0, "ioclk_i2s_bclk", NULL, CLK_IS_ROOT, 50000000), +}; + +static struct samsung_mux_clock aud_mux_clks[] __initdata = { + /* MUX_SEL_AUD0 */ + MUX(CLK_MOUT_AUD_PLL_USER, "mout_aud_pll_user", + mout_aud_pll_user_aud_p, MUX_SEL_AUD0, 0, 1), + + /* MUX_SEL_AUD1 */ + MUX(CLK_MOUT_SCLK_AUD_PCM, "mout_sclk_aud_pcm", mout_sclk_aud_pcm_p, + MUX_SEL_AUD1, 8, 1), + MUX(CLK_MOUT_SCLK_AUD_I2S, "mout_sclk_aud_i2s", mout_sclk_aud_pcm_p, + MUX_SEL_AUD1, 0, 1), +}; + +static struct samsung_div_clock aud_div_clks[] __initdata = { + /* DIV_AUD0 */ + DIV(CLK_DIV_ATCLK_AUD, "div_atclk_aud", "div_aud_ca5", DIV_AUD0, + 12, 4), + DIV(CLK_DIV_PCLK_DBG_AUD, "div_pclk_dbg_aud", "div_aud_ca5", DIV_AUD0, + 8, 4), + DIV(CLK_DIV_ACLK_AUD, "div_aclk_aud", "div_aud_ca5", DIV_AUD0, + 4, 4), + DIV(CLK_DIV_AUD_CA5, "div_aud_ca5", "mout_aud_pll_user", DIV_AUD0, + 0, 4), + + /* DIV_AUD1 */ + DIV(CLK_DIV_SCLK_AUD_SLIMBUS, "div_sclk_aud_slimbus", + "mout_aud_pll_user", DIV_AUD1, 16, 5), + DIV(CLK_DIV_SCLK_AUD_UART, "div_sclk_aud_uart", "mout_aud_pll_user", + DIV_AUD1, 12, 4), + DIV(CLK_DIV_SCLK_AUD_PCM, "div_sclk_aud_pcm", "mout_sclk_aud_pcm", + DIV_AUD1, 4, 8), + DIV(CLK_DIV_SCLK_AUD_I2S, "div_sclk_aud_i2s", "mout_sclk_aud_i2s", + DIV_AUD1, 0, 4), +}; + +static struct samsung_gate_clock aud_gate_clks[] __initdata = { + /* ENABLE_ACLK_AUD */ + GATE(CLK_ACLK_INTR_CTRL, "aclk_intr_ctrl", "div_aclk_aud", + ENABLE_ACLK_AUD, 12, 0, 0), + GATE(CLK_ACLK_SMMU_LPASSX, "aclk_smmu_lpassx", "div_aclk_aud", + ENABLE_ACLK_AUD, 7, 0, 0), + GATE(CLK_ACLK_XIU_LPASSX, "aclk_xiu_lpassx", "div_aclk_aud", + ENABLE_ACLK_AUD, 0, 4, 0), + GATE(CLK_ACLK_AUDNP_133, "aclk_audnp_133", "div_aclk_aud", + ENABLE_ACLK_AUD, 0, 3, 0), + GATE(CLK_ACLK_AUDND_133, "aclk_audnd_133", "div_aclk_aud", + ENABLE_ACLK_AUD, 0, 2, 0), + GATE(CLK_ACLK_SRAMC, "aclk_sramc", "div_aclk_aud", ENABLE_ACLK_AUD, + 0, 1, 0), + GATE(CLK_ACLK_DMAC, "aclk_dmac", "div_aclk_aud", ENABLE_ACLK_AUD, + 0, CLK_IGNORE_UNUSED, 0), + + /* ENABLE_PCLK_AUD */ + GATE(CLK_PCLK_WDT1, "pclk_wdt1", "div_aclk_aud", ENABLE_PCLK_AUD, + 13, 0, 0), + GATE(CLK_PCLK_WDT0, "pclk_wdt0", "div_aclk_aud", ENABLE_PCLK_AUD, + 12, 0, 0), + GATE(CLK_PCLK_SFR1, "pclk_sfr1", "div_aclk_aud", ENABLE_PCLK_AUD, + 11, 0, 0), + GATE(CLK_PCLK_SMMU_LPASSX, "pclk_smmu_lpassx", "div_aclk_aud", + ENABLE_PCLK_AUD, 10, 0, 0), + GATE(CLK_PCLK_GPIO_AUD, "pclk_gpio_aud", "div_aclk_aud", + ENABLE_PCLK_AUD, 9, CLK_IGNORE_UNUSED, 0), + GATE(CLK_PCLK_PMU_AUD, "pclk_pmu_aud", "div_aclk_aud", + ENABLE_PCLK_AUD, 8, CLK_IGNORE_UNUSED, 0), + GATE(CLK_PCLK_SYSREG_AUD, "pclk_sysreg_aud", "div_aclk_aud", + ENABLE_PCLK_AUD, 7, CLK_IGNORE_UNUSED, 0), + GATE(CLK_PCLK_AUD_SLIMBUS, "pclk_aud_slimbus", "div_aclk_aud", + ENABLE_PCLK_AUD, 6, 0, 0), + GATE(CLK_PCLK_AUD_UART, "pclk_aud_uart", "div_aclk_aud", + ENABLE_PCLK_AUD, 5, 0, 0), + GATE(CLK_PCLK_AUD_PCM, "pclk_aud_pcm", "div_aclk_aud", + ENABLE_PCLK_AUD, 4, 0, 0), + GATE(CLK_PCLK_AUD_I2S, "pclk_aud_i2s", "div_aclk_aud", + ENABLE_PCLK_AUD, 3, 0, 0), + GATE(CLK_PCLK_TIMER, "pclk_timer", "div_aclk_aud", ENABLE_PCLK_AUD, + 2, 0, 0), + GATE(CLK_PCLK_SFR0_CTRL, "pclk_sfr0_ctrl", "div_aclk_aud", + ENABLE_PCLK_AUD, 0, 0, 0), + + /* ENABLE_SCLK_AUD0 */ + GATE(CLK_ATCLK_AUD, "atclk_aud", "div_atclk_aud", ENABLE_SCLK_AUD0, + 2, 0, 0), + GATE(CLK_PCLK_DBG_AUD, "pclk_dbg_aud", "div_pclk_dbg_aud", + ENABLE_SCLK_AUD0, 1, 0, 0), + GATE(CLK_SCLK_AUD_CA5, "sclk_aud_ca5", "div_aud_ca5", ENABLE_SCLK_AUD0, + 0, 0, 0), + + /* ENABLE_SCLK_AUD1 */ + GATE(CLK_SCLK_JTAG_TCK, "sclk_jtag_tck", "ioclk_jtag_tclk", + ENABLE_SCLK_AUD1, 6, 0, 0), + GATE(CLK_SCLK_SLIMBUS_CLKIN, "sclk_slimbus_clkin", "ioclk_slimbus_clk", + ENABLE_SCLK_AUD1, 5, 0, 0), + GATE(CLK_SCLK_AUD_SLIMBUS, "sclk_aud_slimbus", "div_sclk_aud_slimbus", + ENABLE_SCLK_AUD1, 4, 0, 0), + GATE(CLK_SCLK_AUD_UART, "sclk_aud_uart", "div_sclk_aud_uart", + ENABLE_SCLK_AUD1, 3, 0, 0), + GATE(CLK_SCLK_AUD_PCM, "sclk_aud_pcm", "div_sclk_aud_pcm", + ENABLE_SCLK_AUD1, 2, 0, 0), + GATE(CLK_SCLK_I2S_BCLK, "sclk_i2s_bclk", "ioclk_i2s_bclk", + ENABLE_SCLK_AUD1, 1, CLK_IGNORE_UNUSED, 0), + GATE(CLK_SCLK_AUD_I2S, "sclk_aud_i2s", "div_sclk_aud_i2s", + ENABLE_SCLK_AUD1, 0, CLK_IGNORE_UNUSED, 0), +}; + +static struct samsung_cmu_info aud_cmu_info __initdata = { + .mux_clks = aud_mux_clks, + .nr_mux_clks = ARRAY_SIZE(aud_mux_clks), + .div_clks = aud_div_clks, + .nr_div_clks = ARRAY_SIZE(aud_div_clks), + .gate_clks = aud_gate_clks, + .nr_gate_clks = ARRAY_SIZE(aud_gate_clks), + .fixed_clks = aud_fixed_clks, + .nr_fixed_clks = ARRAY_SIZE(aud_fixed_clks), + .nr_clk_ids = AUD_NR_CLK, + .clk_regs = aud_clk_regs, + .nr_clk_regs = ARRAY_SIZE(aud_clk_regs), +}; + +static void __init exynos5433_cmu_aud_init(struct device_node *np) +{ + samsung_cmu_register_one(np, &aud_cmu_info); +} +CLK_OF_DECLARE(exynos5433_cmu_aud, "samsung,exynos5433-cmu-aud", + exynos5433_cmu_aud_init); diff --git a/include/dt-bindings/clock/exynos5433.h b/include/dt-bindings/clock/exynos5433.h index fe0650fad766..4d150e244057 100644 --- a/include/dt-bindings/clock/exynos5433.h +++ b/include/dt-bindings/clock/exynos5433.h @@ -626,4 +626,57 @@ #define DISP_NR_CLK 111 +/* CMU_AUD */ +#define CLK_MOUT_AUD_PLL_USER 1 +#define CLK_MOUT_SCLK_AUD_PCM 2 +#define CLK_MOUT_SCLK_AUD_I2S 3 + +#define CLK_DIV_ATCLK_AUD 4 +#define CLK_DIV_PCLK_DBG_AUD 5 +#define CLK_DIV_ACLK_AUD 6 +#define CLK_DIV_AUD_CA5 7 +#define CLK_DIV_SCLK_AUD_SLIMBUS 8 +#define CLK_DIV_SCLK_AUD_UART 9 +#define CLK_DIV_SCLK_AUD_PCM 10 +#define CLK_DIV_SCLK_AUD_I2S 11 + +#define CLK_ACLK_INTR_CTRL 12 +#define CLK_ACLK_AXIDS2_LPASSP 13 +#define CLK_ACLK_AXIDS1_LPASSP 14 +#define CLK_ACLK_AXI2APB1_LPASSP 15 +#define CLK_ACLK_AXI2APH_LPASSP 16 +#define CLK_ACLK_SMMU_LPASSX 17 +#define CLK_ACLK_AXIDS0_LPASSP 18 +#define CLK_ACLK_AXI2APB0_LPASSP 19 +#define CLK_ACLK_XIU_LPASSX 20 +#define CLK_ACLK_AUDNP_133 21 +#define CLK_ACLK_AUDND_133 22 +#define CLK_ACLK_SRAMC 23 +#define CLK_ACLK_DMAC 24 +#define CLK_PCLK_WDT1 25 +#define CLK_PCLK_WDT0 26 +#define CLK_PCLK_SFR1 27 +#define CLK_PCLK_SMMU_LPASSX 28 +#define CLK_PCLK_GPIO_AUD 29 +#define CLK_PCLK_PMU_AUD 30 +#define CLK_PCLK_SYSREG_AUD 31 +#define CLK_PCLK_AUD_SLIMBUS 32 +#define CLK_PCLK_AUD_UART 33 +#define CLK_PCLK_AUD_PCM 34 +#define CLK_PCLK_AUD_I2S 35 +#define CLK_PCLK_TIMER 36 +#define CLK_PCLK_SFR0_CTRL 37 +#define CLK_ATCLK_AUD 38 +#define CLK_PCLK_DBG_AUD 39 +#define CLK_SCLK_AUD_CA5 40 +#define CLK_SCLK_JTAG_TCK 41 +#define CLK_SCLK_SLIMBUS_CLKIN 42 +#define CLK_SCLK_AUD_SLIMBUS 43 +#define CLK_SCLK_AUD_UART 44 +#define CLK_SCLK_AUD_PCM 45 +#define CLK_SCLK_I2S_BCLK 46 +#define CLK_SCLK_AUD_I2S 47 + +#define AUD_NR_CLK 48 + #endif /* _DT_BINDINGS_CLOCK_EXYNOS5433_H */ -- cgit v1.2.3 From 5785d6e61f27f7af4d239c1647d5a22e0dbff19b Mon Sep 17 00:00:00 2001 From: Chanwoo Choi Date: Mon, 2 Feb 2015 23:24:04 +0900 Subject: clk: samsung: exynos5433: Add clocks for CMU_BUS{0|1|2} domains This patch adds the mux/divider/gate clocks for CMU_BUS{0|1|2} domains which contain global data buses clocked at up the 400MHz. These blocks transfer data between DRAM and various sub-blocks. These clock domains also contain global peripheral buses clocked at 67/111/200/222/266/333/400 MHz and used for register accesses. Signed-off-by: Chanwoo Choi Acked-by: Inki Dae Reviewed-by: Pankaj Dubey Signed-off-by: Sylwester Nawrocki --- drivers/clk/samsung/clk-exynos5433.c | 187 +++++++++++++++++++++++++++++++++ include/dt-bindings/clock/exynos5433.h | 27 ++++- 2 files changed, 213 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/drivers/clk/samsung/clk-exynos5433.c b/drivers/clk/samsung/clk-exynos5433.c index ad0105aa0de6..7c4e91a440e5 100644 --- a/drivers/clk/samsung/clk-exynos5433.c +++ b/drivers/clk/samsung/clk-exynos5433.c @@ -438,6 +438,14 @@ static struct samsung_div_clock top_div_clks[] __initdata = { DIV(CLK_DIV_ACLK_PERIS_66_A, "div_aclk_peris_66_a", "mout_bus_pll_user", DIV_TOP3, 0, 3), + /* DIV_TOP4 */ + DIV(CLK_DIV_ACLK_G3D_400, "div_aclk_g3d_400", "mout_bus_pll_user", + DIV_TOP4, 8, 3), + DIV(CLK_DIV_ACLK_BUS0_400, "div_aclk_bus0_400", "mout_aclk_bus0_400", + DIV_TOP4, 4, 3), + DIV(CLK_DIV_ACLK_BUS1_400, "div_aclk_bus1_400", "mout_bus_pll_user", + DIV_TOP4, 0, 3), + /* DIV_TOP_FSYS0 */ DIV(CLK_DIV_SCLK_MMC1_B, "div_sclk_mmc1_b", "div_sclk_mmc1_a", DIV_TOP_FSYS0, 16, 8), @@ -501,6 +509,23 @@ static struct samsung_div_clock top_div_clks[] __initdata = { static struct samsung_gate_clock top_gate_clks[] __initdata = { /* ENABLE_ACLK_TOP */ + GATE(CLK_ACLK_G3D_400, "aclk_g3d_400", "div_aclk_g3d_400", + ENABLE_ACLK_TOP, 30, 0, 0), + GATE(CLK_ACLK_IMEM_SSX_266, "aclk_imem_ssx_266", + "div_aclk_imem_sssx_266", ENABLE_ACLK_TOP, + 29, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_BUS0_400, "aclk_bus0_400", "div_aclk_bus0_400", + ENABLE_ACLK_TOP, 26, + CLK_IGNORE_UNUSED | CLK_SET_RATE_PARENT, 0), + GATE(CLK_ACLK_BUS1_400, "aclk_bus1_400", "div_aclk_bus1_400", + ENABLE_ACLK_TOP, 25, + CLK_IGNORE_UNUSED | CLK_SET_RATE_PARENT, 0), + GATE(CLK_ACLK_IMEM_200, "aclk_imem_200", "div_aclk_imem_266", + ENABLE_ACLK_TOP, 24, + CLK_IGNORE_UNUSED | CLK_SET_RATE_PARENT, 0), + GATE(CLK_ACLK_IMEM_266, "aclk_imem_266", "div_aclk_imem_200", + ENABLE_ACLK_TOP, 23, + CLK_IGNORE_UNUSED | CLK_SET_RATE_PARENT, 0), GATE(CLK_ACLK_PERIC_66, "aclk_peric_66", "div_aclk_peric_66_b", ENABLE_ACLK_TOP, 22, CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED, 0), @@ -2631,3 +2656,165 @@ static void __init exynos5433_cmu_aud_init(struct device_node *np) } CLK_OF_DECLARE(exynos5433_cmu_aud, "samsung,exynos5433-cmu-aud", exynos5433_cmu_aud_init); + + +/* + * Register offset definitions for CMU_BUS{0|1|2} + */ +#define DIV_BUS 0x0600 +#define DIV_STAT_BUS 0x0700 +#define ENABLE_ACLK_BUS 0x0800 +#define ENABLE_PCLK_BUS 0x0900 +#define ENABLE_IP_BUS0 0x0b00 +#define ENABLE_IP_BUS1 0x0b04 + +#define MUX_SEL_BUS2 0x0200 /* Only for CMU_BUS2 */ +#define MUX_ENABLE_BUS2 0x0300 /* Only for CMU_BUS2 */ +#define MUX_STAT_BUS2 0x0400 /* Only for CMU_BUS2 */ + +/* list of all parent clock list */ +PNAME(mout_aclk_bus2_400_p) = { "oscclk", "aclk_bus2_400", }; + +#define CMU_BUS_COMMON_CLK_REGS \ + DIV_BUS, \ + DIV_STAT_BUS, \ + ENABLE_ACLK_BUS, \ + ENABLE_PCLK_BUS, \ + ENABLE_IP_BUS0, \ + ENABLE_IP_BUS1 + +static unsigned long bus01_clk_regs[] __initdata = { + CMU_BUS_COMMON_CLK_REGS, +}; + +static unsigned long bus2_clk_regs[] __initdata = { + MUX_SEL_BUS2, + MUX_ENABLE_BUS2, + MUX_STAT_BUS2, + CMU_BUS_COMMON_CLK_REGS, +}; + +static struct samsung_div_clock bus0_div_clks[] __initdata = { + /* DIV_BUS0 */ + DIV(CLK_DIV_PCLK_BUS_133, "div_pclk_bus0_133", "aclk_bus0_400", + DIV_BUS, 0, 3), +}; + +/* CMU_BUS0 clocks */ +static struct samsung_gate_clock bus0_gate_clks[] __initdata = { + /* ENABLE_ACLK_BUS0 */ + GATE(CLK_ACLK_AHB2APB_BUSP, "aclk_ahb2apb_bus0p", "div_pclk_bus0_133", + ENABLE_ACLK_BUS, 4, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_BUSNP_133, "aclk_bus0np_133", "div_pclk_bus0_133", + ENABLE_ACLK_BUS, 2, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_BUSND_400, "aclk_bus0nd_400", "aclk_bus0_400", + ENABLE_ACLK_BUS, 0, CLK_IGNORE_UNUSED, 0), + + /* ENABLE_PCLK_BUS0 */ + GATE(CLK_PCLK_BUSSRVND_133, "pclk_bus0srvnd_133", "div_pclk_bus0_133", + ENABLE_PCLK_BUS, 2, 0, 0), + GATE(CLK_PCLK_PMU_BUS, "pclk_pmu_bus0", "div_pclk_bus0_133", + ENABLE_PCLK_BUS, 1, CLK_IGNORE_UNUSED, 0), + GATE(CLK_PCLK_SYSREG_BUS, "pclk_sysreg_bus0", "div_pclk_bus0_133", + ENABLE_PCLK_BUS, 0, CLK_IGNORE_UNUSED, 0), +}; + +/* CMU_BUS1 clocks */ +static struct samsung_div_clock bus1_div_clks[] __initdata = { + /* DIV_BUS1 */ + DIV(CLK_DIV_PCLK_BUS_133, "div_pclk_bus1_133", "aclk_bus1_400", + DIV_BUS, 0, 3), +}; + +static struct samsung_gate_clock bus1_gate_clks[] __initdata = { + /* ENABLE_ACLK_BUS1 */ + GATE(CLK_ACLK_AHB2APB_BUSP, "aclk_ahb2apb_bus1p", "div_pclk_bus1_133", + ENABLE_ACLK_BUS, 4, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_BUSNP_133, "aclk_bus1np_133", "div_pclk_bus1_133", + ENABLE_ACLK_BUS, 2, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_BUSND_400, "aclk_bus1nd_400", "aclk_bus1_400", + ENABLE_ACLK_BUS, 0, CLK_IGNORE_UNUSED, 0), + + /* ENABLE_PCLK_BUS1 */ + GATE(CLK_PCLK_BUSSRVND_133, "pclk_bus1srvnd_133", "div_pclk_bus1_133", + ENABLE_PCLK_BUS, 2, 0, 0), + GATE(CLK_PCLK_PMU_BUS, "pclk_pmu_bus1", "div_pclk_bus1_133", + ENABLE_PCLK_BUS, 1, CLK_IGNORE_UNUSED, 0), + GATE(CLK_PCLK_SYSREG_BUS, "pclk_sysreg_bus1", "div_pclk_bus1_133", + ENABLE_PCLK_BUS, 0, CLK_IGNORE_UNUSED, 0), +}; + +/* CMU_BUS2 clocks */ +static struct samsung_mux_clock bus2_mux_clks[] __initdata = { + /* MUX_SEL_BUS2 */ + MUX(CLK_MOUT_ACLK_BUS2_400_USER, "mout_aclk_bus2_400_user", + mout_aclk_bus2_400_p, MUX_SEL_BUS2, 0, 1), +}; + +static struct samsung_div_clock bus2_div_clks[] __initdata = { + /* DIV_BUS2 */ + DIV(CLK_DIV_PCLK_BUS_133, "div_pclk_bus2_133", + "mout_aclk_bus2_400_user", DIV_BUS, 0, 3), +}; + +static struct samsung_gate_clock bus2_gate_clks[] __initdata = { + /* ENABLE_ACLK_BUS2 */ + GATE(CLK_ACLK_AHB2APB_BUSP, "aclk_ahb2apb_bus2p", "div_pclk_bus2_133", + ENABLE_ACLK_BUS, 3, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_BUSNP_133, "aclk_bus2np_133", "div_pclk_bus2_133", + ENABLE_ACLK_BUS, 2, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_BUS2BEND_400, "aclk_bus2bend_400", + "mout_aclk_bus2_400_user", ENABLE_ACLK_BUS, + 1, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_BUS2RTND_400, "aclk_bus2rtnd_400", + "mout_aclk_bus2_400_user", ENABLE_ACLK_BUS, + 0, CLK_IGNORE_UNUSED, 0), + + /* ENABLE_PCLK_BUS2 */ + GATE(CLK_PCLK_BUSSRVND_133, "pclk_bus2srvnd_133", "div_pclk_bus2_133", + ENABLE_PCLK_BUS, 2, 0, 0), + GATE(CLK_PCLK_PMU_BUS, "pclk_pmu_bus2", "div_pclk_bus2_133", + ENABLE_PCLK_BUS, 1, CLK_IGNORE_UNUSED, 0), + GATE(CLK_PCLK_SYSREG_BUS, "pclk_sysreg_bus2", "div_pclk_bus2_133", + ENABLE_PCLK_BUS, 0, CLK_IGNORE_UNUSED, 0), +}; + +#define CMU_BUS_INFO_CLKS(id) \ + .div_clks = bus##id##_div_clks, \ + .nr_div_clks = ARRAY_SIZE(bus##id##_div_clks), \ + .gate_clks = bus##id##_gate_clks, \ + .nr_gate_clks = ARRAY_SIZE(bus##id##_gate_clks), \ + .nr_clk_ids = BUSx_NR_CLK + +static struct samsung_cmu_info bus0_cmu_info __initdata = { + CMU_BUS_INFO_CLKS(0), + .clk_regs = bus01_clk_regs, + .nr_clk_regs = ARRAY_SIZE(bus01_clk_regs), +}; + +static struct samsung_cmu_info bus1_cmu_info __initdata = { + CMU_BUS_INFO_CLKS(1), + .clk_regs = bus01_clk_regs, + .nr_clk_regs = ARRAY_SIZE(bus01_clk_regs), +}; + +static struct samsung_cmu_info bus2_cmu_info __initdata = { + CMU_BUS_INFO_CLKS(2), + .mux_clks = bus2_mux_clks, + .nr_mux_clks = ARRAY_SIZE(bus2_mux_clks), + .clk_regs = bus2_clk_regs, + .nr_clk_regs = ARRAY_SIZE(bus2_clk_regs), +}; + +#define exynos5433_cmu_bus_init(id) \ +static void __init exynos5433_cmu_bus##id##_init(struct device_node *np)\ +{ \ + samsung_cmu_register_one(np, &bus##id##_cmu_info); \ +} \ +CLK_OF_DECLARE(exynos5433_cmu_bus##id, \ + "samsung,exynos5433-cmu-bus"#id, \ + exynos5433_cmu_bus##id##_init) + +exynos5433_cmu_bus_init(0); +exynos5433_cmu_bus_init(1); +exynos5433_cmu_bus_init(2); diff --git a/include/dt-bindings/clock/exynos5433.h b/include/dt-bindings/clock/exynos5433.h index 4d150e244057..8d388e700a80 100644 --- a/include/dt-bindings/clock/exynos5433.h +++ b/include/dt-bindings/clock/exynos5433.h @@ -107,6 +107,9 @@ #define CLK_DIV_ACLK_MFC_400 134 #define CLK_DIV_ACLK_G2D_266 135 #define CLK_DIV_ACLK_G2D_400 136 +#define CLK_DIV_ACLK_G3D_400 137 +#define CLK_DIV_ACLK_BUS0_400 138 +#define CLK_DIV_ACLK_BUS1_400 139 #define CLK_ACLK_PERIC_66 200 #define CLK_ACLK_PERIS_66 201 @@ -130,8 +133,14 @@ #define CLK_SCLK_AUDIO0 219 #define CLK_ACLK_G2D_266 220 #define CLK_ACLK_G2D_400 221 +#define CLK_ACLK_G3D_400 222 +#define CLK_ACLK_IMEM_SSX_266 223 +#define CLK_ACLK_BUS0_400 224 +#define CLK_ACLK_BUS1_400 225 +#define CLK_ACLK_IMEM_200 226 +#define CLK_ACLK_IMEM_266 227 -#define TOP_NR_CLK 222 +#define TOP_NR_CLK 228 /* CMU_CPIF */ #define CLK_FOUT_MPHY_PLL 1 @@ -679,4 +688,20 @@ #define AUD_NR_CLK 48 +/* CMU_BUS{0|1|2} */ +#define CLK_DIV_PCLK_BUS_133 1 + +#define CLK_ACLK_AHB2APB_BUSP 2 +#define CLK_ACLK_BUSNP_133 3 +#define CLK_ACLK_BUSND_400 4 +#define CLK_PCLK_BUSSRVND_133 5 +#define CLK_PCLK_PMU_BUS 6 +#define CLK_PCLK_SYSREG_BUS 7 + +#define CLK_MOUT_ACLK_BUS2_400_USER 8 /* Only CMU_BUS2 */ +#define CLK_ACLK_BUS2BEND_400 9 /* Only CMU_BUS2 */ +#define CLK_ACLK_BUS2RTND_400 10 /* Only CMU_BUS2 */ + +#define BUSx_NR_CLK 11 + #endif /* _DT_BINDINGS_CLOCK_EXYNOS5433_H */ -- cgit v1.2.3 From 4b8013554b0454984e71bc20bc31966886079e15 Mon Sep 17 00:00:00 2001 From: Chanwoo Choi Date: Mon, 2 Feb 2015 23:24:05 +0900 Subject: clk: samsung: exynos5433: Add missing clocks for CMU_FSYS domain This patch adds the mux/divider/gate clocks for CMU_FSYS domain which contains the clocks of USB/UFS/SDMMC/TSI/PDMA IPs. Signed-off-by: Chanwoo Choi Acked-by: Inki Dae Signed-off-by: Sylwester Nawrocki --- drivers/clk/samsung/clk-exynos5433.c | 302 +++++++++++++++++++++++++++++++++ include/dt-bindings/clock/exynos5433.h | 96 ++++++++++- 2 files changed, 395 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/drivers/clk/samsung/clk-exynos5433.c b/drivers/clk/samsung/clk-exynos5433.c index 7c4e91a440e5..1cdc47e05ac1 100644 --- a/drivers/clk/samsung/clk-exynos5433.c +++ b/drivers/clk/samsung/clk-exynos5433.c @@ -462,6 +462,16 @@ static struct samsung_div_clock top_div_clks[] __initdata = { DIV(CLK_DIV_SCLK_MMC2_A, "div_sclk_mmc2_a", "mout_sclk_mmc2_b", DIV_TOP_FSYS1, 0, 4), + /* DIV_TOP_FSYS2 */ + DIV(CLK_DIV_SCLK_PCIE_100, "div_sclk_pcie_100", "mout_sclk_pcie_100", + DIV_TOP_FSYS2, 12, 3), + DIV(CLK_DIV_SCLK_USBHOST30, "div_sclk_usbhost30", + "mout_sclk_usbhost30", DIV_TOP_FSYS2, 8, 4), + DIV(CLK_DIV_SCLK_UFSUNIPRO, "div_sclk_ufsunipro", + "mout_sclk_ufsunipro", DIV_TOP_FSYS2, 4, 4), + DIV(CLK_DIV_SCLK_USBDRD30, "div_sclk_usbdrd30", "mout_sclk_usbdrd30", + DIV_TOP_FSYS2, 0, 4), + /* DIV_TOP_PERIC0 */ DIV(CLK_DIV_SCLK_SPI1_B, "div_sclk_spi1_b", "div_sclk_spi1_a", DIV_TOP_PERIC0, 16, 8), @@ -543,12 +553,23 @@ static struct samsung_gate_clock top_gate_clks[] __initdata = { CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED, 0), /* ENABLE_SCLK_TOP_FSYS */ + GATE(CLK_SCLK_PCIE_100_FSYS, "sclk_pcie_100_fsys", "div_sclk_pcie_100", + ENABLE_SCLK_TOP_FSYS, 7, 0, 0), GATE(CLK_SCLK_MMC2_FSYS, "sclk_mmc2_fsys", "div_sclk_mmc2_b", ENABLE_SCLK_TOP_FSYS, 6, CLK_SET_RATE_PARENT, 0), GATE(CLK_SCLK_MMC1_FSYS, "sclk_mmc1_fsys", "div_sclk_mmc1_b", ENABLE_SCLK_TOP_FSYS, 5, CLK_SET_RATE_PARENT, 0), GATE(CLK_SCLK_MMC0_FSYS, "sclk_mmc0_fsys", "div_sclk_mmc0_b", ENABLE_SCLK_TOP_FSYS, 4, CLK_SET_RATE_PARENT, 0), + GATE(CLK_SCLK_UFSUNIPRO_FSYS, "sclk_ufsunipro_fsys", + "div_sclk_ufsunipro", ENABLE_SCLK_TOP_FSYS, + 3, CLK_SET_RATE_PARENT, 0), + GATE(CLK_SCLK_USBHOST30_FSYS, "sclk_usbhost30_fsys", + "div_sclk_usbhost30", ENABLE_SCLK_TOP_FSYS, + 1, CLK_SET_RATE_PARENT, 0), + GATE(CLK_SCLK_USBDRD30_FSYS, "sclk_usbdrd30_fsys", + "div_sclk_usbdrd30", ENABLE_SCLK_TOP_FSYS, + 0, CLK_SET_RATE_PARENT, 0), /* ENABLE_SCLK_TOP_PERIC */ GATE(CLK_SCLK_SPI4_PERIC, "sclk_spi4_peric", "div_sclk_spi4_b", @@ -1832,10 +1853,45 @@ CLK_OF_DECLARE(exynos5433_cmu_peris, "samsung,exynos5433-cmu-peris", #define ENABLE_IP_FSYS1 0x0b04 /* list of all parent clock list */ +PNAME(mout_sclk_ufs_mphy_user_p) = { "oscclk", "sclk_ufs_mphy", }; PNAME(mout_aclk_fsys_200_user_p) = { "oscclk", "div_aclk_fsys_200", }; +PNAME(mout_sclk_pcie_100_user_p) = { "oscclk", "sclk_pcie_100_fsys",}; +PNAME(mout_sclk_ufsunipro_user_p) = { "oscclk", "sclk_ufsunipro_fsys",}; PNAME(mout_sclk_mmc2_user_p) = { "oscclk", "sclk_mmc2_fsys", }; PNAME(mout_sclk_mmc1_user_p) = { "oscclk", "sclk_mmc1_fsys", }; PNAME(mout_sclk_mmc0_user_p) = { "oscclk", "sclk_mmc0_fsys", }; +PNAME(mout_sclk_usbhost30_user_p) = { "oscclk", "sclk_usbhost30_fsys",}; +PNAME(mout_sclk_usbdrd30_user_p) = { "oscclk", "sclk_usbdrd30_fsys", }; + +PNAME(mout_phyclk_usbhost30_uhost30_pipe_pclk_user_p) + = { "oscclk", "phyclk_usbhost30_uhost30_pipe_pclk_phy", }; +PNAME(mout_phyclk_usbhost30_uhost30_phyclock_user_p) + = { "oscclk", "phyclk_usbhost30_uhost30_phyclock_phy", }; +PNAME(mout_phyclk_usbhost20_phy_hsic1_p) + = { "oscclk", "phyclk_usbhost20_phy_hsic1_phy", }; +PNAME(mout_phyclk_usbhost20_phy_clk48mohci_user_p) + = { "oscclk", "phyclk_usbhost20_phy_clk48mohci_phy", }; +PNAME(mout_phyclk_usbhost20_phy_phyclock_user_p) + = { "oscclk", "phyclk_usbhost20_phy_phyclock_phy", }; +PNAME(mout_phyclk_usbhost20_phy_freeclk_user_p) + = { "oscclk", "phyclk_usbhost20_phy_freeclk_phy", }; +PNAME(mout_phyclk_usbdrd30_udrd30_pipe_pclk_p) + = { "oscclk", "phyclk_usbdrd30_udrd30_pipe_pclk_phy", }; +PNAME(mout_phyclk_usbdrd30_udrd30_phyclock_user_p) + = { "oscclk", "phyclk_usbdrd30_udrd30_phyclock_phy", }; +PNAME(mout_phyclk_ufs_rx1_symbol_user_p) + = { "oscclk", "phyclk_ufs_rx1_symbol_phy", }; +PNAME(mout_phyclk_ufs_rx0_symbol_user_p) + = { "oscclk", "phyclk_ufs_rx0_symbol_phy", }; +PNAME(mout_phyclk_ufs_tx1_symbol_user_p) + = { "oscclk", "phyclk_ufs_tx1_symbol_phy", }; +PNAME(mout_phyclk_ufs_tx0_symbol_user_p) + = { "oscclk", "phyclk_ufs_tx0_symbol_phy", }; +PNAME(mout_phyclk_lli_mphy_to_ufs_user_p) + = { "oscclk", "phyclk_lli_mphy_to_ufs_phy", }; +PNAME(mout_sclk_mphy_p) + = { "mout_sclk_ufs_mphy_user", + "mout_phyclk_lli_mphy_to_ufs_user", }; static unsigned long fsys_clk_regs[] __initdata = { MUX_SEL_FSYS0, @@ -1863,18 +1919,130 @@ static unsigned long fsys_clk_regs[] __initdata = { ENABLE_IP_FSYS1, }; +static struct samsung_fixed_rate_clock fsys_fixed_clks[] __initdata = { + /* PHY clocks from USBDRD30_PHY */ + FRATE(CLK_PHYCLK_USBDRD30_UDRD30_PHYCLOCK_PHY, + "phyclk_usbdrd30_udrd30_phyclock_phy", NULL, + CLK_IS_ROOT, 60000000), + FRATE(CLK_PHYCLK_USBDRD30_UDRD30_PIPE_PCLK_PHY, + "phyclk_usbdrd30_udrd30_pipe_pclk_phy", NULL, + CLK_IS_ROOT, 125000000), + /* PHY clocks from USBHOST30_PHY */ + FRATE(CLK_PHYCLK_USBHOST30_UHOST30_PHYCLOCK_PHY, + "phyclk_usbhost30_uhost30_phyclock_phy", NULL, + CLK_IS_ROOT, 60000000), + FRATE(CLK_PHYCLK_USBHOST30_UHOST30_PIPE_PCLK_PHY, + "phyclk_usbhost30_uhost30_pipe_pclk_phy", NULL, + CLK_IS_ROOT, 125000000), + /* PHY clocks from USBHOST20_PHY */ + FRATE(CLK_PHYCLK_USBHOST20_PHY_FREECLK_PHY, + "phyclk_usbhost20_phy_freeclk_phy", NULL, CLK_IS_ROOT, + 60000000), + FRATE(CLK_PHYCLK_USBHOST20_PHY_PHYCLOCK_PHY, + "phyclk_usbhost20_phy_phyclock_phy", NULL, CLK_IS_ROOT, + 60000000), + FRATE(CLK_PHYCLK_USBHOST20_PHY_CLK48MOHCI_PHY, + "phyclk_usbhost20_phy_clk48mohci_phy", NULL, + CLK_IS_ROOT, 48000000), + FRATE(CLK_PHYCLK_USBHOST20_PHY_HSIC1_PHY, + "phyclk_usbhost20_phy_hsic1_phy", NULL, CLK_IS_ROOT, + 60000000), + /* PHY clocks from UFS_PHY */ + FRATE(CLK_PHYCLK_UFS_TX0_SYMBOL_PHY, "phyclk_ufs_tx0_symbol_phy", + NULL, CLK_IS_ROOT, 300000000), + FRATE(CLK_PHYCLK_UFS_RX0_SYMBOL_PHY, "phyclk_ufs_rx0_symbol_phy", + NULL, CLK_IS_ROOT, 300000000), + FRATE(CLK_PHYCLK_UFS_TX1_SYMBOL_PHY, "phyclk_ufs_tx1_symbol_phy", + NULL, CLK_IS_ROOT, 300000000), + FRATE(CLK_PHYCLK_UFS_RX1_SYMBOL_PHY, "phyclk_ufs_rx1_symbol_phy", + NULL, CLK_IS_ROOT, 300000000), + /* PHY clocks from LLI_PHY */ + FRATE(CLK_PHYCLK_LLI_MPHY_TO_UFS_PHY, "phyclk_lli_mphy_to_ufs_phy", + NULL, CLK_IS_ROOT, 26000000), +}; + static struct samsung_mux_clock fsys_mux_clks[] __initdata = { /* MUX_SEL_FSYS0 */ + MUX(CLK_MOUT_SCLK_UFS_MPHY_USER, "mout_sclk_ufs_mphy_user", + mout_sclk_ufs_mphy_user_p, MUX_SEL_FSYS0, 4, 1), MUX(CLK_MOUT_ACLK_FSYS_200_USER, "mout_aclk_fsys_200_user", mout_aclk_fsys_200_user_p, MUX_SEL_FSYS0, 0, 1), /* MUX_SEL_FSYS1 */ + MUX(CLK_MOUT_SCLK_PCIE_100_USER, "mout_sclk_pcie_100_user", + mout_sclk_pcie_100_user_p, MUX_SEL_FSYS1, 28, 1), + MUX(CLK_MOUT_SCLK_UFSUNIPRO_USER, "mout_sclk_ufsunipro_user", + mout_sclk_ufsunipro_user_p, MUX_SEL_FSYS1, 24, 1), MUX(CLK_MOUT_SCLK_MMC2_USER, "mout_sclk_mmc2_user", mout_sclk_mmc2_user_p, MUX_SEL_FSYS1, 20, 1), MUX(CLK_MOUT_SCLK_MMC1_USER, "mout_sclk_mmc1_user", mout_sclk_mmc1_user_p, MUX_SEL_FSYS1, 16, 1), MUX(CLK_MOUT_SCLK_MMC0_USER, "mout_sclk_mmc0_user", mout_sclk_mmc0_user_p, MUX_SEL_FSYS1, 12, 1), + MUX(CLK_MOUT_SCLK_USBHOST30_USER, "mout_sclk_usbhost30_user", + mout_sclk_usbhost30_user_p, MUX_SEL_FSYS1, 4, 1), + MUX(CLK_MOUT_SCLK_USBDRD30_USER, "mout_sclk_usbdrd30_user", + mout_sclk_usbdrd30_user_p, MUX_SEL_FSYS1, 0, 1), + + /* MUX_SEL_FSYS2 */ + MUX(CLK_MOUT_PHYCLK_USBHOST30_UHOST30_PIPE_PCLK_USER, + "mout_phyclk_usbhost30_uhost30_pipe_pclk_user", + mout_phyclk_usbhost30_uhost30_pipe_pclk_user_p, + MUX_SEL_FSYS2, 28, 1), + MUX(CLK_MOUT_PHYCLK_USBHOST30_UHOST30_PHYCLOCK_USER, + "mout_phyclk_usbhost30_uhost30_phyclock_user", + mout_phyclk_usbhost30_uhost30_phyclock_user_p, + MUX_SEL_FSYS2, 24, 1), + MUX(CLK_MOUT_PHYCLK_USBHOST20_PHY_HSIC1_USER, + "mout_phyclk_usbhost20_phy_hsic1", + mout_phyclk_usbhost20_phy_hsic1_p, + MUX_SEL_FSYS2, 20, 1), + MUX(CLK_MOUT_PHYCLK_USBHOST20_PHY_CLK48MOHCI_USER, + "mout_phyclk_usbhost20_phy_clk48mohci_user", + mout_phyclk_usbhost20_phy_clk48mohci_user_p, + MUX_SEL_FSYS2, 16, 1), + MUX(CLK_MOUT_PHYCLK_USBHOST20_PHY_PHYCLOCK_USER, + "mout_phyclk_usbhost20_phy_phyclock_user", + mout_phyclk_usbhost20_phy_phyclock_user_p, + MUX_SEL_FSYS2, 12, 1), + MUX(CLK_MOUT_PHYCLK_USBHOST20_PHY_PHY_FREECLK_USER, + "mout_phyclk_usbhost20_phy_freeclk_user", + mout_phyclk_usbhost20_phy_freeclk_user_p, + MUX_SEL_FSYS2, 8, 1), + MUX(CLK_MOUT_PHYCLK_USBDRD30_UDRD30_PIPE_PCLK_USER, + "mout_phyclk_usbdrd30_udrd30_pipe_pclk_user", + mout_phyclk_usbdrd30_udrd30_pipe_pclk_p, + MUX_SEL_FSYS2, 4, 1), + MUX(CLK_MOUT_PHYCLK_USBDRD30_UDRD30_PHYCLOCK_USER, + "mout_phyclk_usbdrd30_udrd30_phyclock_user", + mout_phyclk_usbdrd30_udrd30_phyclock_user_p, + MUX_SEL_FSYS2, 0, 1), + + /* MUX_SEL_FSYS3 */ + MUX(CLK_MOUT_PHYCLK_UFS_RX1_SYMBOL_USER, + "mout_phyclk_ufs_rx1_symbol_user", + mout_phyclk_ufs_rx1_symbol_user_p, + MUX_SEL_FSYS3, 16, 1), + MUX(CLK_MOUT_PHYCLK_UFS_RX0_SYMBOL_USER, + "mout_phyclk_ufs_rx0_symbol_user", + mout_phyclk_ufs_rx0_symbol_user_p, + MUX_SEL_FSYS3, 12, 1), + MUX(CLK_MOUT_PHYCLK_UFS_TX1_SYMBOL_USER, + "mout_phyclk_ufs_tx1_symbol_user", + mout_phyclk_ufs_tx1_symbol_user_p, + MUX_SEL_FSYS3, 8, 1), + MUX(CLK_MOUT_PHYCLK_UFS_TX0_SYMBOL_USER, + "mout_phyclk_ufs_tx0_symbol_user", + mout_phyclk_ufs_tx0_symbol_user_p, + MUX_SEL_FSYS3, 4, 1), + MUX(CLK_MOUT_PHYCLK_LLI_MPHY_TO_UFS_USER, + "mout_phyclk_lli_mphy_to_ufs_user", + mout_phyclk_lli_mphy_to_ufs_user_p, + MUX_SEL_FSYS3, 0, 1), + + /* MUX_SEL_FSYS4 */ + MUX(CLK_MOUT_SCLK_MPHY, "mout_sclk_mphy", mout_sclk_mphy_p, + MUX_SEL_FSYS4, 0, 1), }; static struct samsung_gate_clock fsys_gate_clks[] __initdata = { @@ -1902,13 +2070,145 @@ static struct samsung_gate_clock fsys_gate_clks[] __initdata = { GATE(CLK_ACLK_PDMA0, "aclk_pdma0", "mout_aclk_fsys_200_user", ENABLE_ACLK_FSYS0, 0, CLK_IGNORE_UNUSED, 0), + /* ENABLE_ACLK_FSYS1 */ + GATE(CLK_ACLK_XIU_FSYSPX, "aclk_xiu_fsyspx", "mout_aclk_fsys_200_user", + ENABLE_ACLK_FSYS1, 27, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_AHB_USBLINKH1, "aclk_ahb_usblinkh1", + "mout_aclk_fsys_200_user", ENABLE_ACLK_FSYS1, + 26, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_SMMU_PDMA1, "aclk_smmu_pdma1", "mout_aclk_fsys_200_user", + ENABLE_ACLK_FSYS1, 25, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_BTS_PCIE, "aclk_bts_pcie", "mout_aclk_fsys_200_user", + ENABLE_ACLK_FSYS1, 24, 0, 0), + GATE(CLK_ACLK_AXIUS_PDMA1, "aclk_axius_pdma1", + "mout_aclk_fsys_200_user", ENABLE_ACLK_FSYS1, + 22, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_SMMU_PDMA0, "aclk_smmu_pdma0", "mout_aclk_fsys_200_user", + ENABLE_ACLK_FSYS1, 17, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_BTS_UFS, "aclk_bts_ufs", "mout_aclk_fsys_200_user", + ENABLE_ACLK_FSYS1, 14, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_BTS_USBHOST30, "aclk_bts_usbhost30", + "mout_aclk_fsys_200_user", ENABLE_ACLK_FSYS1, + 13, 0, 0), + GATE(CLK_ACLK_BTS_USBDRD30, "aclk_bts_usbdrd30", + "mout_aclk_fsys_200_user", ENABLE_ACLK_FSYS1, + 12, 0, 0), + GATE(CLK_ACLK_AXIUS_PDMA0, "aclk_axius_pdma0", + "mout_aclk_fsys_200_user", ENABLE_ACLK_FSYS1, + 11, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_AXIUS_USBHS, "aclk_axius_usbhs", + "mout_aclk_fsys_200_user", ENABLE_ACLK_FSYS1, + 10, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_AXIUS_FSYSSX, "aclk_axius_fsyssx", + "mout_aclk_fsys_200_user", ENABLE_ACLK_FSYS1, + 9, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_AHB2APB_FSYSP, "aclk_ahb2apb_fsysp", + "mout_aclk_fsys_200_user", ENABLE_ACLK_FSYS1, + 8, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_AHB2AXI_USBHS, "aclk_ahb2axi_usbhs", + "mout_aclk_fsys_200_user", ENABLE_ACLK_FSYS1, + 7, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_AHB_USBLINKH0, "aclk_ahb_usblinkh0", + "mout_aclk_fsys_200_user", ENABLE_ACLK_FSYS1, + 6, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_AHB_USBHS, "aclk_ahb_usbhs", "mout_aclk_fsys_200_user", + ENABLE_ACLK_FSYS1, 5, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_AHB_FSYSH, "aclk_ahb_fsysh", "mout_aclk_fsys_200_user", + ENABLE_ACLK_FSYS1, 4, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_XIU_FSYSX, "aclk_xiu_fsysx", "mout_aclk_fsys_200_user", + ENABLE_ACLK_FSYS1, 3, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_XIU_FSYSSX, "aclk_xiu_fsyssx", "mout_aclk_fsys_200_user", + ENABLE_ACLK_FSYS1, 2, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_FSYSNP_200, "aclk_fsysnp_200", "mout_aclk_fsys_200_user", + ENABLE_ACLK_FSYS1, 1, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_FSYSND_200, "aclk_fsysnd_200", "mout_aclk_fsys_200_user", + ENABLE_ACLK_FSYS1, 0, CLK_IGNORE_UNUSED, 0), + + /* ENABLE_PCLK_FSYS */ + GATE(CLK_PCLK_PCIE_CTRL, "pclk_pcie_ctrl", "mout_aclk_fsys_200_user", + ENABLE_PCLK_FSYS, 17, 0, 0), + GATE(CLK_PCLK_SMMU_PDMA1, "pclk_smmu_pdma1", "mout_aclk_fsys_200_user", + ENABLE_PCLK_FSYS, 16, CLK_IGNORE_UNUSED, 0), + GATE(CLK_PCLK_PCIE_PHY, "pclk_pcie_phy", "mout_aclk_fsys_200_user", + ENABLE_PCLK_FSYS, 14, 0, 0), + GATE(CLK_PCLK_BTS_PCIE, "pclk_bts_pcie", "mout_aclk_fsys_200_user", + ENABLE_PCLK_FSYS, 13, 0, 0), + GATE(CLK_PCLK_SMMU_PDMA0, "pclk_smmu_pdma0", "mout_aclk_fsys_200_user", + ENABLE_PCLK_FSYS, 8, CLK_IGNORE_UNUSED, 0), + GATE(CLK_PCLK_BTS_UFS, "pclk_bts_ufs", "mout_aclk_fsys_200_user", + ENABLE_PCLK_FSYS, 5, 0, 0), + GATE(CLK_PCLK_BTS_USBHOST30, "pclk_bts_usbhost30", + "mout_aclk_fsys_200_user", ENABLE_PCLK_FSYS, 4, 0, 0), + GATE(CLK_PCLK_BTS_USBDRD30, "pclk_bts_usbdrd30", + "mout_aclk_fsys_200_user", ENABLE_PCLK_FSYS, 3, 0, 0), + GATE(CLK_PCLK_GPIO_FSYS, "pclk_gpio_fsys", "mout_aclk_fsys_200_user", + ENABLE_PCLK_FSYS, 2, CLK_IGNORE_UNUSED, 0), + GATE(CLK_PCLK_PMU_FSYS, "pclk_pmu_fsys", "mout_aclk_fsys_200_user", + ENABLE_PCLK_FSYS, 1, CLK_IGNORE_UNUSED, 0), + GATE(CLK_PCLK_SYSREG_FSYS, "pclk_sysreg_fsys", + "mout_aclk_fsys_200_user", ENABLE_PCLK_FSYS, + 0, CLK_IGNORE_UNUSED, 0), + /* ENABLE_SCLK_FSYS */ + GATE(CLK_SCLK_PCIE_100, "sclk_pcie_100", "mout_sclk_pcie_100_user", + ENABLE_SCLK_FSYS, 21, 0, 0), + GATE(CLK_PHYCLK_USBHOST30_UHOST30_PIPE_PCLK, + "phyclk_usbhost30_uhost30_pipe_pclk", + "mout_phyclk_usbhost30_uhost30_pipe_pclk_user", + ENABLE_SCLK_FSYS, 18, 0, 0), + GATE(CLK_PHYCLK_USBHOST30_UHOST30_PHYCLOCK, + "phyclk_usbhost30_uhost30_phyclock", + "mout_phyclk_usbhost30_uhost30_phyclock_user", + ENABLE_SCLK_FSYS, 17, 0, 0), + GATE(CLK_PHYCLK_UFS_RX1_SYMBOL, "phyclk_ufs_rx1_symbol", + "mout_phyclk_ufs_rx1_symbol_user", ENABLE_SCLK_FSYS, + 16, 0, 0), + GATE(CLK_PHYCLK_UFS_RX0_SYMBOL, "phyclk_ufs_rx0_symbol", + "mout_phyclk_ufs_rx0_symbol_user", ENABLE_SCLK_FSYS, + 15, 0, 0), + GATE(CLK_PHYCLK_UFS_TX1_SYMBOL, "phyclk_ufs_tx1_symbol", + "mout_phyclk_ufs_tx1_symbol_user", ENABLE_SCLK_FSYS, + 14, 0, 0), + GATE(CLK_PHYCLK_UFS_TX0_SYMBOL, "phyclk_ufs_tx0_symbol", + "mout_phyclk_ufs_tx0_symbol_user", ENABLE_SCLK_FSYS, + 13, 0, 0), + GATE(CLK_PHYCLK_USBHOST20_PHY_HSIC1, "phyclk_usbhost20_phy_hsic1", + "mout_phyclk_usbhost20_phy_hsic1", ENABLE_SCLK_FSYS, + 12, 0, 0), + GATE(CLK_PHYCLK_USBHOST20_PHY_CLK48MOHCI, + "phyclk_usbhost20_phy_clk48mohci", + "mout_phyclk_usbhost20_phy_clk48mohci_user", + ENABLE_SCLK_FSYS, 11, 0, 0), + GATE(CLK_PHYCLK_USBHOST20_PHY_PHYCLOCK, + "phyclk_usbhost20_phy_phyclock", + "mout_phyclk_usbhost20_phy_phyclock_user", + ENABLE_SCLK_FSYS, 10, 0, 0), + GATE(CLK_PHYCLK_USBHOST20_PHY_FREECLK, + "phyclk_usbhost20_phy_freeclk", + "mout_phyclk_usbhost20_phy_freeclk_user", + ENABLE_SCLK_FSYS, 9, 0, 0), + GATE(CLK_PHYCLK_USBDRD30_UDRD30_PIPE_PCLK, + "phyclk_usbdrd30_udrd30_pipe_pclk", + "mout_phyclk_usbdrd30_udrd30_pipe_pclk_user", + ENABLE_SCLK_FSYS, 8, 0, 0), + GATE(CLK_PHYCLK_USBDRD30_UDRD30_PHYCLOCK, + "phyclk_usbdrd30_udrd30_phyclock", + "mout_phyclk_usbdrd30_udrd30_phyclock_user", + ENABLE_SCLK_FSYS, 7, 0, 0), + GATE(CLK_SCLK_MPHY, "sclk_mphy", "mout_sclk_mphy", + ENABLE_SCLK_FSYS, 6, 0, 0), + GATE(CLK_SCLK_UFSUNIPRO, "sclk_ufsunipro", "mout_sclk_ufsunipro_user", + ENABLE_SCLK_FSYS, 5, 0, 0), GATE(CLK_SCLK_MMC2, "sclk_mmc2", "mout_sclk_mmc2_user", ENABLE_SCLK_FSYS, 4, CLK_SET_RATE_PARENT, 0), GATE(CLK_SCLK_MMC1, "sclk_mmc1", "mout_sclk_mmc1_user", ENABLE_SCLK_FSYS, 3, CLK_SET_RATE_PARENT, 0), GATE(CLK_SCLK_MMC0, "sclk_mmc0", "mout_sclk_mmc0_user", ENABLE_SCLK_FSYS, 2, CLK_SET_RATE_PARENT, 0), + GATE(CLK_SCLK_USBHOST30, "sclk_usbhost30", "mout_sclk_usbhost30_user", + ENABLE_SCLK_FSYS, 1, 0, 0), + GATE(CLK_SCLK_USBDRD30, "sclk_usbdrd30", "mout_sclk_usbdrd30_user", + ENABLE_SCLK_FSYS, 0, 0, 0), /* ENABLE_IP_FSYS0 */ GATE(CLK_PDMA1, "pdma1", "aclk_pdma1", ENABLE_IP_FSYS0, 15, 0, 0), @@ -1920,6 +2220,8 @@ static struct samsung_cmu_info fsys_cmu_info __initdata = { .nr_mux_clks = ARRAY_SIZE(fsys_mux_clks), .gate_clks = fsys_gate_clks, .nr_gate_clks = ARRAY_SIZE(fsys_gate_clks), + .fixed_clks = fsys_fixed_clks, + .nr_fixed_clks = ARRAY_SIZE(fsys_fixed_clks), .nr_clk_ids = FSYS_NR_CLK, .clk_regs = fsys_clk_regs, .nr_clk_regs = ARRAY_SIZE(fsys_clk_regs), diff --git a/include/dt-bindings/clock/exynos5433.h b/include/dt-bindings/clock/exynos5433.h index 8d388e700a80..294a2d0a67ff 100644 --- a/include/dt-bindings/clock/exynos5433.h +++ b/include/dt-bindings/clock/exynos5433.h @@ -110,6 +110,10 @@ #define CLK_DIV_ACLK_G3D_400 137 #define CLK_DIV_ACLK_BUS0_400 138 #define CLK_DIV_ACLK_BUS1_400 139 +#define CLK_DIV_SCLK_PCIE_100 140 +#define CLK_DIV_SCLK_USBHOST30 141 +#define CLK_DIV_SCLK_UFSUNIPRO 142 +#define CLK_DIV_SCLK_USBDRD30 143 #define CLK_ACLK_PERIC_66 200 #define CLK_ACLK_PERIS_66 201 @@ -139,8 +143,12 @@ #define CLK_ACLK_BUS1_400 225 #define CLK_ACLK_IMEM_200 226 #define CLK_ACLK_IMEM_266 227 +#define CLK_SCLK_PCIE_100_FSYS 228 +#define CLK_SCLK_UFSUNIPRO_FSYS 229 +#define CLK_SCLK_USBHOST30_FSYS 230 +#define CLK_SCLK_USBDRD30_FSYS 231 -#define TOP_NR_CLK 228 +#define TOP_NR_CLK 232 /* CMU_CPIF */ #define CLK_FOUT_MPHY_PLL 1 @@ -473,6 +481,39 @@ #define CLK_MOUT_SCLK_MMC2_USER 2 #define CLK_MOUT_SCLK_MMC1_USER 3 #define CLK_MOUT_SCLK_MMC0_USER 4 +#define CLK_MOUT_SCLK_UFS_MPHY_USER 5 +#define CLK_MOUT_SCLK_PCIE_100_USER 6 +#define CLK_MOUT_SCLK_UFSUNIPRO_USER 7 +#define CLK_MOUT_SCLK_USBHOST30_USER 8 +#define CLK_MOUT_SCLK_USBDRD30_USER 9 +#define CLK_MOUT_PHYCLK_USBHOST30_UHOST30_PIPE_PCLK_USER 10 +#define CLK_MOUT_PHYCLK_USBHOST30_UHOST30_PHYCLOCK_USER 11 +#define CLK_MOUT_PHYCLK_USBHOST20_PHY_HSIC1_USER 12 +#define CLK_MOUT_PHYCLK_USBHOST20_PHY_CLK48MOHCI_USER 13 +#define CLK_MOUT_PHYCLK_USBHOST20_PHY_PHYCLOCK_USER 14 +#define CLK_MOUT_PHYCLK_USBHOST20_PHY_PHY_FREECLK_USER 15 +#define CLK_MOUT_PHYCLK_USBDRD30_UDRD30_PIPE_PCLK_USER 16 +#define CLK_MOUT_PHYCLK_USBDRD30_UDRD30_PHYCLOCK_USER 17 +#define CLK_MOUT_PHYCLK_UFS_RX1_SYMBOL_USER 18 +#define CLK_MOUT_PHYCLK_UFS_RX0_SYMBOL_USER 19 +#define CLK_MOUT_PHYCLK_UFS_TX1_SYMBOL_USER 20 +#define CLK_MOUT_PHYCLK_UFS_TX0_SYMBOL_USER 21 +#define CLK_MOUT_PHYCLK_LLI_MPHY_TO_UFS_USER 22 +#define CLK_MOUT_SCLK_MPHY 23 + +#define CLK_PHYCLK_USBDRD30_UDRD30_PHYCLOCK_PHY 25 +#define CLK_PHYCLK_USBDRD30_UDRD30_PIPE_PCLK_PHY 26 +#define CLK_PHYCLK_USBHOST30_UHOST30_PHYCLOCK_PHY 27 +#define CLK_PHYCLK_USBHOST30_UHOST30_PIPE_PCLK_PHY 28 +#define CLK_PHYCLK_USBHOST20_PHY_FREECLK_PHY 29 +#define CLK_PHYCLK_USBHOST20_PHY_PHYCLOCK_PHY 30 +#define CLK_PHYCLK_USBHOST20_PHY_CLK48MOHCI_PHY 31 +#define CLK_PHYCLK_USBHOST20_PHY_HSIC1_PHY 32 +#define CLK_PHYCLK_UFS_TX0_SYMBOL_PHY 33 +#define CLK_PHYCLK_UFS_RX0_SYMBOL_PHY 34 +#define CLK_PHYCLK_UFS_TX1_SYMBOL_PHY 35 +#define CLK_PHYCLK_UFS_RX1_SYMBOL_PHY 36 +#define CLK_PHYCLK_LLI_MPHY_TO_UFS_PHY 37 #define CLK_ACLK_PCIE 50 #define CLK_ACLK_PDMA1 51 @@ -490,8 +531,57 @@ #define CLK_SCLK_MMC0 63 #define CLK_PDMA1 64 #define CLK_PDMA0 65 - -#define FSYS_NR_CLK 66 +#define CLK_ACLK_XIU_FSYSPX 66 +#define CLK_ACLK_AHB_USBLINKH1 67 +#define CLK_ACLK_SMMU_PDMA1 68 +#define CLK_ACLK_BTS_PCIE 69 +#define CLK_ACLK_AXIUS_PDMA1 70 +#define CLK_ACLK_SMMU_PDMA0 71 +#define CLK_ACLK_BTS_UFS 72 +#define CLK_ACLK_BTS_USBHOST30 73 +#define CLK_ACLK_BTS_USBDRD30 74 +#define CLK_ACLK_AXIUS_PDMA0 75 +#define CLK_ACLK_AXIUS_USBHS 76 +#define CLK_ACLK_AXIUS_FSYSSX 77 +#define CLK_ACLK_AHB2APB_FSYSP 78 +#define CLK_ACLK_AHB2AXI_USBHS 79 +#define CLK_ACLK_AHB_USBLINKH0 80 +#define CLK_ACLK_AHB_USBHS 81 +#define CLK_ACLK_AHB_FSYSH 82 +#define CLK_ACLK_XIU_FSYSX 83 +#define CLK_ACLK_XIU_FSYSSX 84 +#define CLK_ACLK_FSYSNP_200 85 +#define CLK_ACLK_FSYSND_200 86 +#define CLK_PCLK_PCIE_CTRL 87 +#define CLK_PCLK_SMMU_PDMA1 88 +#define CLK_PCLK_PCIE_PHY 89 +#define CLK_PCLK_BTS_PCIE 90 +#define CLK_PCLK_SMMU_PDMA0 91 +#define CLK_PCLK_BTS_UFS 92 +#define CLK_PCLK_BTS_USBHOST30 93 +#define CLK_PCLK_BTS_USBDRD30 94 +#define CLK_PCLK_GPIO_FSYS 95 +#define CLK_PCLK_PMU_FSYS 96 +#define CLK_PCLK_SYSREG_FSYS 97 +#define CLK_SCLK_PCIE_100 98 +#define CLK_PHYCLK_USBHOST30_UHOST30_PIPE_PCLK 99 +#define CLK_PHYCLK_USBHOST30_UHOST30_PHYCLOCK 100 +#define CLK_PHYCLK_UFS_RX1_SYMBOL 101 +#define CLK_PHYCLK_UFS_RX0_SYMBOL 102 +#define CLK_PHYCLK_UFS_TX1_SYMBOL 103 +#define CLK_PHYCLK_UFS_TX0_SYMBOL 104 +#define CLK_PHYCLK_USBHOST20_PHY_HSIC1 105 +#define CLK_PHYCLK_USBHOST20_PHY_CLK48MOHCI 106 +#define CLK_PHYCLK_USBHOST20_PHY_PHYCLOCK 107 +#define CLK_PHYCLK_USBHOST20_PHY_FREECLK 108 +#define CLK_PHYCLK_USBDRD30_UDRD30_PIPE_PCLK 109 +#define CLK_PHYCLK_USBDRD30_UDRD30_PHYCLOCK 110 +#define CLK_SCLK_MPHY 111 +#define CLK_SCLK_UFSUNIPRO 112 +#define CLK_SCLK_USBHOST30 113 +#define CLK_SCLK_USBDRD30 114 + +#define FSYS_NR_CLK 115 /* CMU_G2D */ #define CLK_MUX_ACLK_G2D_266_USER 1 -- cgit v1.2.3 From 453e519e5aed806c1b70bcbe92aeab39a93dda22 Mon Sep 17 00:00:00 2001 From: Chanwoo Choi Date: Mon, 2 Feb 2015 23:24:06 +0900 Subject: clk: samsung: exynos5433: Add clocks for CMU_G3D domain This patch adds the mux/divider/gate clocks for CMU_G3D domain which contains the clocks for GPU(3D Graphics Engine). Signed-off-by: Chanwoo Choi Acked-by: Inki Dae Reviewed-by: Pankaj Dubey Signed-off-by: Sylwester Nawrocki --- drivers/clk/samsung/clk-exynos5433.c | 127 +++++++++++++++++++++++++++++++++ include/dt-bindings/clock/exynos5433.h | 25 +++++++ 2 files changed, 152 insertions(+) (limited to 'include') diff --git a/drivers/clk/samsung/clk-exynos5433.c b/drivers/clk/samsung/clk-exynos5433.c index 1cdc47e05ac1..f422485a92c0 100644 --- a/drivers/clk/samsung/clk-exynos5433.c +++ b/drivers/clk/samsung/clk-exynos5433.c @@ -3120,3 +3120,130 @@ CLK_OF_DECLARE(exynos5433_cmu_bus##id, \ exynos5433_cmu_bus_init(0); exynos5433_cmu_bus_init(1); exynos5433_cmu_bus_init(2); + +/* + * Register offset definitions for CMU_G3D + */ +#define G3D_PLL_LOCK 0x0000 +#define G3D_PLL_CON0 0x0100 +#define G3D_PLL_CON1 0x0104 +#define G3D_PLL_FREQ_DET 0x010c +#define MUX_SEL_G3D 0x0200 +#define MUX_ENABLE_G3D 0x0300 +#define MUX_STAT_G3D 0x0400 +#define DIV_G3D 0x0600 +#define DIV_G3D_PLL_FREQ_DET 0x0604 +#define DIV_STAT_G3D 0x0700 +#define DIV_STAT_G3D_PLL_FREQ_DET 0x0704 +#define ENABLE_ACLK_G3D 0x0800 +#define ENABLE_PCLK_G3D 0x0900 +#define ENABLE_SCLK_G3D 0x0a00 +#define ENABLE_IP_G3D0 0x0b00 +#define ENABLE_IP_G3D1 0x0b04 +#define CLKOUT_CMU_G3D 0x0c00 +#define CLKOUT_CMU_G3D_DIV_STAT 0x0c04 +#define CLK_STOPCTRL 0x1000 + +static unsigned long g3d_clk_regs[] __initdata = { + G3D_PLL_LOCK, + G3D_PLL_CON0, + G3D_PLL_CON1, + G3D_PLL_FREQ_DET, + MUX_SEL_G3D, + MUX_ENABLE_G3D, + MUX_STAT_G3D, + DIV_G3D, + DIV_G3D_PLL_FREQ_DET, + DIV_STAT_G3D, + DIV_STAT_G3D_PLL_FREQ_DET, + ENABLE_ACLK_G3D, + ENABLE_PCLK_G3D, + ENABLE_SCLK_G3D, + ENABLE_IP_G3D0, + ENABLE_IP_G3D1, + CLKOUT_CMU_G3D, + CLKOUT_CMU_G3D_DIV_STAT, + CLK_STOPCTRL, +}; + +/* list of all parent clock list */ +PNAME(mout_aclk_g3d_400_p) = { "mout_g3d_pll", "aclk_g3d_400", }; +PNAME(mout_g3d_pll_p) = { "oscclk", "fout_g3d_pll", }; + +static struct samsung_pll_clock g3d_pll_clks[] __initdata = { + PLL(pll_35xx, CLK_FOUT_G3D_PLL, "fout_g3d_pll", "oscclk", + G3D_PLL_LOCK, G3D_PLL_CON0, exynos5443_pll_rates), +}; + +static struct samsung_mux_clock g3d_mux_clks[] __initdata = { + /* MUX_SEL_G3D */ + MUX(CLK_MOUT_ACLK_G3D_400, "mout_aclk_g3d_400", mout_aclk_g3d_400_p, + MUX_SEL_G3D, 8, 1), + MUX(CLK_MOUT_G3D_PLL, "mout_g3d_pll", mout_g3d_pll_p, + MUX_SEL_G3D, 0, 1), +}; + +static struct samsung_div_clock g3d_div_clks[] __initdata = { + /* DIV_G3D */ + DIV(CLK_DIV_SCLK_HPM_G3D, "div_sclk_hpm_g3d", "mout_g3d_pll", DIV_G3D, + 8, 2), + DIV(CLK_DIV_PCLK_G3D, "div_pclk_g3d", "div_aclk_g3d", DIV_G3D, + 4, 3), + DIV(CLK_DIV_ACLK_G3D, "div_aclk_g3d", "mout_aclk_g3d_400", DIV_G3D, + 0, 3), +}; + +static struct samsung_gate_clock g3d_gate_clks[] __initdata = { + /* ENABLE_ACLK_G3D */ + GATE(CLK_ACLK_BTS_G3D1, "aclk_bts_g3d1", "div_aclk_g3d", + ENABLE_ACLK_G3D, 7, 0, 0), + GATE(CLK_ACLK_BTS_G3D0, "aclk_bts_g3d0", "div_aclk_g3d", + ENABLE_ACLK_G3D, 6, 0, 0), + GATE(CLK_ACLK_ASYNCAPBS_G3D, "aclk_asyncapbs_g3d", "div_pclk_g3d", + ENABLE_ACLK_G3D, 5, 0, 0), + GATE(CLK_ACLK_ASYNCAPBM_G3D, "aclk_asyncapbm_g3d", "div_aclk_g3d", + ENABLE_ACLK_G3D, 4, 0, 0), + GATE(CLK_ACLK_AHB2APB_G3DP, "aclk_ahb2apb_g3dp", "div_pclk_g3d", + ENABLE_ACLK_G3D, 3, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_G3DNP_150, "aclk_g3dnp_150", "div_pclk_g3d", + ENABLE_ACLK_G3D, 2, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_G3DND_600, "aclk_g3dnd_600", "div_aclk_g3d", + ENABLE_ACLK_G3D, 1, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_G3D, "aclk_g3d", "div_aclk_g3d", + ENABLE_ACLK_G3D, 0, 0, 0), + + /* ENABLE_PCLK_G3D */ + GATE(CLK_PCLK_BTS_G3D1, "pclk_bts_g3d1", "div_pclk_g3d", + ENABLE_PCLK_G3D, 3, 0, 0), + GATE(CLK_PCLK_BTS_G3D0, "pclk_bts_g3d0", "div_pclk_g3d", + ENABLE_PCLK_G3D, 2, 0, 0), + GATE(CLK_PCLK_PMU_G3D, "pclk_pmu_g3d", "div_pclk_g3d", + ENABLE_PCLK_G3D, 1, CLK_IGNORE_UNUSED, 0), + GATE(CLK_PCLK_SYSREG_G3D, "pclk_sysreg_g3d", "div_pclk_g3d", + ENABLE_PCLK_G3D, 0, CLK_IGNORE_UNUSED, 0), + + /* ENABLE_SCLK_G3D */ + GATE(CLK_SCLK_HPM_G3D, "sclk_hpm_g3d", "div_sclk_hpm_g3d", + ENABLE_SCLK_G3D, 0, 0, 0), +}; + +static struct samsung_cmu_info g3d_cmu_info __initdata = { + .pll_clks = g3d_pll_clks, + .nr_pll_clks = ARRAY_SIZE(g3d_pll_clks), + .mux_clks = g3d_mux_clks, + .nr_mux_clks = ARRAY_SIZE(g3d_mux_clks), + .div_clks = g3d_div_clks, + .nr_div_clks = ARRAY_SIZE(g3d_div_clks), + .gate_clks = g3d_gate_clks, + .nr_gate_clks = ARRAY_SIZE(g3d_gate_clks), + .nr_clk_ids = G3D_NR_CLK, + .clk_regs = g3d_clk_regs, + .nr_clk_regs = ARRAY_SIZE(g3d_clk_regs), +}; + +static void __init exynos5433_cmu_g3d_init(struct device_node *np) +{ + samsung_cmu_register_one(np, &g3d_cmu_info); +} +CLK_OF_DECLARE(exynos5433_cmu_g3d, "samsung,exynos5433-cmu-g3d", + exynos5433_cmu_g3d_init); diff --git a/include/dt-bindings/clock/exynos5433.h b/include/dt-bindings/clock/exynos5433.h index 294a2d0a67ff..60ccc169e9ff 100644 --- a/include/dt-bindings/clock/exynos5433.h +++ b/include/dt-bindings/clock/exynos5433.h @@ -794,4 +794,29 @@ #define BUSx_NR_CLK 11 +/* CMU_G3D */ +#define CLK_FOUT_G3D_PLL 1 + +#define CLK_MOUT_ACLK_G3D_400 2 +#define CLK_MOUT_G3D_PLL 3 + +#define CLK_DIV_SCLK_HPM_G3D 4 +#define CLK_DIV_PCLK_G3D 5 +#define CLK_DIV_ACLK_G3D 6 +#define CLK_ACLK_BTS_G3D1 7 +#define CLK_ACLK_BTS_G3D0 8 +#define CLK_ACLK_ASYNCAPBS_G3D 9 +#define CLK_ACLK_ASYNCAPBM_G3D 10 +#define CLK_ACLK_AHB2APB_G3DP 11 +#define CLK_ACLK_G3DNP_150 12 +#define CLK_ACLK_G3DND_600 13 +#define CLK_ACLK_G3D 14 +#define CLK_PCLK_BTS_G3D1 15 +#define CLK_PCLK_BTS_G3D0 16 +#define CLK_PCLK_PMU_G3D 17 +#define CLK_PCLK_SYSREG_G3D 18 +#define CLK_SCLK_HPM_G3D 19 + +#define G3D_NR_CLK 20 + #endif /* _DT_BINDINGS_CLOCK_EXYNOS5433_H */ -- cgit v1.2.3 From 2a2f33e83ddb6c0abe3d32075f795aa14e4b9476 Mon Sep 17 00:00:00 2001 From: Chanwoo Choi Date: Mon, 2 Feb 2015 23:24:07 +0900 Subject: clk: samsung: exynos5433: Add clocks for CMU_GSCL domain This patch adds the divider/gate of CMU_GSCL domain which contains gscaler clocks. Signed-off-by: Chanwoo Choi Acked-by: Inki Dae Signed-off-by: Sylwester Nawrocki --- drivers/clk/samsung/clk-exynos5433.c | 146 +++++++++++++++++++++++++++++++++ include/dt-bindings/clock/exynos5433.h | 37 ++++++++- 2 files changed, 182 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/drivers/clk/samsung/clk-exynos5433.c b/drivers/clk/samsung/clk-exynos5433.c index f422485a92c0..12f60c6a5a7a 100644 --- a/drivers/clk/samsung/clk-exynos5433.c +++ b/drivers/clk/samsung/clk-exynos5433.c @@ -545,6 +545,12 @@ static struct samsung_gate_clock top_gate_clks[] __initdata = { GATE(CLK_ACLK_FSYS_200, "aclk_fsys_200", "div_aclk_fsys_200", ENABLE_ACLK_TOP, 18, CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_GSCL_111, "aclk_gscl_111", "div_aclk_gscl_111", + ENABLE_ACLK_TOP, 15, + CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_GSCL_333, "aclk_gscl_333", "div_aclk_gscl_333", + ENABLE_ACLK_TOP, 14, + CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED, 0), GATE(CLK_ACLK_G2D_266, "aclk_g2d_266", "div_aclk_g2d_266", ENABLE_ACLK_TOP, 2, CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED, 0), @@ -3247,3 +3253,143 @@ static void __init exynos5433_cmu_g3d_init(struct device_node *np) } CLK_OF_DECLARE(exynos5433_cmu_g3d, "samsung,exynos5433-cmu-g3d", exynos5433_cmu_g3d_init); + +/* + * Register offset definitions for CMU_GSCL + */ +#define MUX_SEL_GSCL 0x0200 +#define MUX_ENABLE_GSCL 0x0300 +#define MUX_STAT_GSCL 0x0400 +#define ENABLE_ACLK_GSCL 0x0800 +#define ENABLE_ACLK_GSCL_SECURE_SMMU_GSCL0 0x0804 +#define ENABLE_ACLK_GSCL_SECURE_SMMU_GSCL1 0x0808 +#define ENABLE_ACLK_GSCL_SECURE_SMMU_GSCL2 0x080c +#define ENABLE_PCLK_GSCL 0x0900 +#define ENABLE_PCLK_GSCL_SECURE_SMMU_GSCL0 0x0904 +#define ENABLE_PCLK_GSCL_SECURE_SMMU_GSCL1 0x0908 +#define ENABLE_PCLK_GSCL_SECURE_SMMU_GSCL2 0x090c +#define ENABLE_IP_GSCL0 0x0b00 +#define ENABLE_IP_GSCL1 0x0b04 +#define ENABLE_IP_GSCL_SECURE_SMMU_GSCL0 0x0b08 +#define ENABLE_IP_GSCL_SECURE_SMMU_GSCL1 0x0b0c +#define ENABLE_IP_GSCL_SECURE_SMMU_GSCL2 0x0b10 + +static unsigned long gscl_clk_regs[] __initdata = { + MUX_SEL_GSCL, + MUX_ENABLE_GSCL, + MUX_STAT_GSCL, + ENABLE_ACLK_GSCL, + ENABLE_ACLK_GSCL_SECURE_SMMU_GSCL0, + ENABLE_ACLK_GSCL_SECURE_SMMU_GSCL1, + ENABLE_ACLK_GSCL_SECURE_SMMU_GSCL2, + ENABLE_PCLK_GSCL, + ENABLE_PCLK_GSCL_SECURE_SMMU_GSCL0, + ENABLE_PCLK_GSCL_SECURE_SMMU_GSCL1, + ENABLE_PCLK_GSCL_SECURE_SMMU_GSCL2, + ENABLE_IP_GSCL0, + ENABLE_IP_GSCL1, + ENABLE_IP_GSCL_SECURE_SMMU_GSCL0, + ENABLE_IP_GSCL_SECURE_SMMU_GSCL1, + ENABLE_IP_GSCL_SECURE_SMMU_GSCL2, +}; + +/* list of all parent clock list */ +PNAME(aclk_gscl_111_user_p) = { "oscclk", "aclk_gscl_111", }; +PNAME(aclk_gscl_333_user_p) = { "oscclk", "aclk_gscl_333", }; + +static struct samsung_mux_clock gscl_mux_clks[] __initdata = { + /* MUX_SEL_GSCL */ + MUX(CLK_MOUT_ACLK_GSCL_111_USER, "mout_aclk_gscl_111_user", + aclk_gscl_111_user_p, MUX_SEL_GSCL, 4, 1), + MUX(CLK_MOUT_ACLK_GSCL_333_USER, "mout_aclk_gscl_333_user", + aclk_gscl_333_user_p, MUX_SEL_GSCL, 0, 1), +}; + +static struct samsung_gate_clock gscl_gate_clks[] __initdata = { + /* ENABLE_ACLK_GSCL */ + GATE(CLK_ACLK_BTS_GSCL2, "aclk_bts_gscl2", "mout_aclk_gscl_333_user", + ENABLE_ACLK_GSCL, 11, 0, 0), + GATE(CLK_ACLK_BTS_GSCL1, "aclk_bts_gscl1", "mout_aclk_gscl_333_user", + ENABLE_ACLK_GSCL, 10, 0, 0), + GATE(CLK_ACLK_BTS_GSCL0, "aclk_bts_gscl0", "mout_aclk_gscl_333_user", + ENABLE_ACLK_GSCL, 9, 0, 0), + GATE(CLK_ACLK_AHB2APB_GSCLP, "aclk_ahb2apb_gsclp", + "mout_aclk_gscl_111_user", ENABLE_ACLK_GSCL, + 8, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_XIU_GSCLX, "aclk_xiu_gsclx", "mout_aclk_gscl_333_user", + ENABLE_ACLK_GSCL, 7, 0, 0), + GATE(CLK_ACLK_GSCLNP_111, "aclk_gsclnp_111", "mout_aclk_gscl_111_user", + ENABLE_ACLK_GSCL, 6, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_GSCLRTND_333, "aclk_gsclrtnd_333", + "mout_aclk_gscl_333_user", ENABLE_ACLK_GSCL, 5, 0, 0), + GATE(CLK_ACLK_GSCLBEND_333, "aclk_gsclbend_333", + "mout_aclk_gscl_333_user", ENABLE_ACLK_GSCL, 4, 0, 0), + GATE(CLK_ACLK_GSD, "aclk_gsd", "mout_aclk_gscl_333_user", + ENABLE_ACLK_GSCL, 3, 0, 0), + GATE(CLK_ACLK_GSCL2, "aclk_gscl2", "mout_aclk_gscl_333_user", + ENABLE_ACLK_GSCL, 2, 0, 0), + GATE(CLK_ACLK_GSCL1, "aclk_gscl1", "mout_aclk_gscl_333_user", + ENABLE_ACLK_GSCL, 1, 0, 0), + GATE(CLK_ACLK_GSCL0, "aclk_gscl0", "mout_aclk_gscl_333_user", + ENABLE_ACLK_GSCL, 0, 0, 0), + + /* ENABLE_ACLK_GSCL_SECURE_SMMU_GSCL0 */ + GATE(CLK_ACLK_SMMU_GSCL0, "aclk_smmu_gscl0", "mout_aclk_gscl_333_user", + ENABLE_ACLK_GSCL_SECURE_SMMU_GSCL0, 0, 0, 0), + + /* ENABLE_ACLK_GSCL_SECURE_SMMU_GSCL1 */ + GATE(CLK_ACLK_SMMU_GSCL1, "aclk_smmu_gscl1", "mout_aclk_gscl_333_user", + ENABLE_ACLK_GSCL_SECURE_SMMU_GSCL1, 0, 0, 0), + + /* ENABLE_ACLK_GSCL_SECURE_SMMU_GSCL2 */ + GATE(CLK_ACLK_SMMU_GSCL2, "aclk_smmu_gscl2", "mout_aclk_gscl_333_user", + ENABLE_ACLK_GSCL_SECURE_SMMU_GSCL2, 0, 0, 0), + + /* ENABLE_PCLK_GSCL */ + GATE(CLK_PCLK_BTS_GSCL2, "pclk_bts_gscl2", "mout_aclk_gscl_111_user", + ENABLE_PCLK_GSCL, 7, 0, 0), + GATE(CLK_PCLK_BTS_GSCL1, "pclk_bts_gscl1", "mout_aclk_gscl_111_user", + ENABLE_PCLK_GSCL, 6, 0, 0), + GATE(CLK_PCLK_BTS_GSCL0, "pclk_bts_gscl0", "mout_aclk_gscl_111_user", + ENABLE_PCLK_GSCL, 5, 0, 0), + GATE(CLK_PCLK_PMU_GSCL, "pclk_pmu_gscl", "mout_aclk_gscl_111_user", + ENABLE_PCLK_GSCL, 4, CLK_IGNORE_UNUSED, 0), + GATE(CLK_PCLK_SYSREG_GSCL, "pclk_sysreg_gscl", + "mout_aclk_gscl_111_user", ENABLE_PCLK_GSCL, + 3, CLK_IGNORE_UNUSED, 0), + GATE(CLK_PCLK_GSCL2, "pclk_gscl2", "mout_aclk_gscl_111_user", + ENABLE_PCLK_GSCL, 2, 0, 0), + GATE(CLK_PCLK_GSCL1, "pclk_gscl1", "mout_aclk_gscl_111_user", + ENABLE_PCLK_GSCL, 1, 0, 0), + GATE(CLK_PCLK_GSCL0, "pclk_gscl0", "mout_aclk_gscl_111_user", + ENABLE_PCLK_GSCL, 0, 0, 0), + + /* ENABLE_PCLK_GSCL_SECURE_SMMU_GSCL0 */ + GATE(CLK_PCLK_SMMU_GSCL0, "pclk_smmu_gscl0", "mout_aclk_gscl_111_user", + ENABLE_PCLK_GSCL_SECURE_SMMU_GSCL0, 0, 0, 0), + + /* ENABLE_PCLK_GSCL_SECURE_SMMU_GSCL1 */ + GATE(CLK_PCLK_SMMU_GSCL1, "pclk_smmu_gscl1", "mout_aclk_gscl_111_user", + ENABLE_PCLK_GSCL_SECURE_SMMU_GSCL0, 0, 0, 0), + + /* ENABLE_PCLK_GSCL_SECURE_SMMU_GSCL2 */ + GATE(CLK_PCLK_SMMU_GSCL2, "pclk_smmu_gscl2", "mout_aclk_gscl_111_user", + ENABLE_PCLK_GSCL_SECURE_SMMU_GSCL0, 0, 0, 0), +}; + +static struct samsung_cmu_info gscl_cmu_info __initdata = { + .mux_clks = gscl_mux_clks, + .nr_mux_clks = ARRAY_SIZE(gscl_mux_clks), + .gate_clks = gscl_gate_clks, + .nr_gate_clks = ARRAY_SIZE(gscl_gate_clks), + .nr_clk_ids = GSCL_NR_CLK, + .clk_regs = gscl_clk_regs, + .nr_clk_regs = ARRAY_SIZE(gscl_clk_regs), +}; + +static void __init exynos5433_cmu_gscl_init(struct device_node *np) +{ + samsung_cmu_register_one(np, &gscl_cmu_info); +} +CLK_OF_DECLARE(exynos5433_cmu_gscl, "samsung,exynos5433-cmu-gscl", + exynos5433_cmu_gscl_init); diff --git a/include/dt-bindings/clock/exynos5433.h b/include/dt-bindings/clock/exynos5433.h index 60ccc169e9ff..fef8893c3ec2 100644 --- a/include/dt-bindings/clock/exynos5433.h +++ b/include/dt-bindings/clock/exynos5433.h @@ -147,8 +147,10 @@ #define CLK_SCLK_UFSUNIPRO_FSYS 229 #define CLK_SCLK_USBHOST30_FSYS 230 #define CLK_SCLK_USBDRD30_FSYS 231 +#define CLK_ACLK_GSCL_111 232 +#define CLK_ACLK_GSCL_333 233 -#define TOP_NR_CLK 232 +#define TOP_NR_CLK 234 /* CMU_CPIF */ #define CLK_FOUT_MPHY_PLL 1 @@ -819,4 +821,37 @@ #define G3D_NR_CLK 20 +/* CMU_GSCL */ +#define CLK_MOUT_ACLK_GSCL_111_USER 1 +#define CLK_MOUT_ACLK_GSCL_333_USER 2 + +#define CLK_ACLK_BTS_GSCL2 3 +#define CLK_ACLK_BTS_GSCL1 4 +#define CLK_ACLK_BTS_GSCL0 5 +#define CLK_ACLK_AHB2APB_GSCLP 6 +#define CLK_ACLK_XIU_GSCLX 7 +#define CLK_ACLK_GSCLNP_111 8 +#define CLK_ACLK_GSCLRTND_333 9 +#define CLK_ACLK_GSCLBEND_333 10 +#define CLK_ACLK_GSD 11 +#define CLK_ACLK_GSCL2 12 +#define CLK_ACLK_GSCL1 13 +#define CLK_ACLK_GSCL0 14 +#define CLK_ACLK_SMMU_GSCL0 15 +#define CLK_ACLK_SMMU_GSCL1 16 +#define CLK_ACLK_SMMU_GSCL2 17 +#define CLK_PCLK_BTS_GSCL2 18 +#define CLK_PCLK_BTS_GSCL1 19 +#define CLK_PCLK_BTS_GSCL0 20 +#define CLK_PCLK_PMU_GSCL 21 +#define CLK_PCLK_SYSREG_GSCL 22 +#define CLK_PCLK_GSCL2 23 +#define CLK_PCLK_GSCL1 24 +#define CLK_PCLK_GSCL0 25 +#define CLK_PCLK_SMMU_GSCL0 26 +#define CLK_PCLK_SMMU_GSCL1 27 +#define CLK_PCLK_SMMU_GSCL2 28 + +#define GSCL_NR_CLK 29 + #endif /* _DT_BINDINGS_CLOCK_EXYNOS5433_H */ -- cgit v1.2.3 From df40a13ca53e6f83ead88e718dd96654e75365ec Mon Sep 17 00:00:00 2001 From: Chanwoo Choi Date: Tue, 3 Feb 2015 09:13:49 +0900 Subject: clk: samsung: exynos5433: Add clocks for CMU_APOLLO domain This patch adds the mux/divider/gate clocks for CMU_APOLLO domain which generates the clocks for Cortex-A53 Quad-core processsor. Signed-off-by: Chanwoo Choi Acked-by: Inki Dae [s.nawrocki@samsung.com: Renamed pclk_pmu_sysreg_apollo to pclk_sysreg_apollo] Signed-off-by: Sylwester Nawrocki --- .../devicetree/bindings/clock/exynos5433-clock.txt | 15 ++ drivers/clk/samsung/clk-exynos5433.c | 193 +++++++++++++++++++++ include/dt-bindings/clock/exynos5433.h | 37 ++++ 3 files changed, 245 insertions(+) (limited to 'include') diff --git a/Documentation/devicetree/bindings/clock/exynos5433-clock.txt b/Documentation/devicetree/bindings/clock/exynos5433-clock.txt index 9e7ed2d43a15..0a7146861a27 100644 --- a/Documentation/devicetree/bindings/clock/exynos5433-clock.txt +++ b/Documentation/devicetree/bindings/clock/exynos5433-clock.txt @@ -32,6 +32,8 @@ Required Properties: which generates clocks for 3D Graphics Engine IP. - "samsung,exynos5433-cmu-gscl" - clock controller compatible for CMU_GSCL which generates clocks for GSCALER IPs. + - "samsung,exynos5433-cmu-apollo"- clock controller compatible for CMU_APOLLO + which generates clocks for Cortex-A53 Quad-core processor. - reg: physical base address of the controller and length of memory mapped region. @@ -105,6 +107,10 @@ Required Properties: - aclk_gscl_111 - aclk_gscl_333 + Input clocks for apollo clock controller: + - oscclk + - sclk_bus_pll_apollo + Each clock is assigned an identifier and client nodes can use this identifier to specify the clock which they consume. @@ -289,6 +295,15 @@ Example 2: Examples of clock controller nodes are listed below. <&cmu_top CLK_ACLK_GSCL_333>; }; + cmu_apollo: clock-controller@11900000 { + compatible = "samsung,exynos5433-cmu-apollo"; + reg = <0x11900000 0x1088>; + #clock-cells = <1>; + + clock-names = "oscclk", "sclk_bus_pll_apollo"; + clocks = <&xxti>, <&cmu_mif CLK_SCLK_BUS_PLL_APOLLO>; + }; + Example 3: UART controller node that consumes the clock generated by the clock controller. diff --git a/drivers/clk/samsung/clk-exynos5433.c b/drivers/clk/samsung/clk-exynos5433.c index 12f60c6a5a7a..f7d1e8390cdf 100644 --- a/drivers/clk/samsung/clk-exynos5433.c +++ b/drivers/clk/samsung/clk-exynos5433.c @@ -3393,3 +3393,196 @@ static void __init exynos5433_cmu_gscl_init(struct device_node *np) } CLK_OF_DECLARE(exynos5433_cmu_gscl, "samsung,exynos5433-cmu-gscl", exynos5433_cmu_gscl_init); + +/* + * Register offset definitions for CMU_APOLLO + */ +#define APOLLO_PLL_LOCK 0x0000 +#define APOLLO_PLL_CON0 0x0100 +#define APOLLO_PLL_CON1 0x0104 +#define APOLLO_PLL_FREQ_DET 0x010c +#define MUX_SEL_APOLLO0 0x0200 +#define MUX_SEL_APOLLO1 0x0204 +#define MUX_SEL_APOLLO2 0x0208 +#define MUX_ENABLE_APOLLO0 0x0300 +#define MUX_ENABLE_APOLLO1 0x0304 +#define MUX_ENABLE_APOLLO2 0x0308 +#define MUX_STAT_APOLLO0 0x0400 +#define MUX_STAT_APOLLO1 0x0404 +#define MUX_STAT_APOLLO2 0x0408 +#define DIV_APOLLO0 0x0600 +#define DIV_APOLLO1 0x0604 +#define DIV_APOLLO_PLL_FREQ_DET 0x0608 +#define DIV_STAT_APOLLO0 0x0700 +#define DIV_STAT_APOLLO1 0x0704 +#define DIV_STAT_APOLLO_PLL_FREQ_DET 0x0708 +#define ENABLE_ACLK_APOLLO 0x0800 +#define ENABLE_PCLK_APOLLO 0x0900 +#define ENABLE_SCLK_APOLLO 0x0a00 +#define ENABLE_IP_APOLLO0 0x0b00 +#define ENABLE_IP_APOLLO1 0x0b04 +#define CLKOUT_CMU_APOLLO 0x0c00 +#define CLKOUT_CMU_APOLLO_DIV_STAT 0x0c04 +#define ARMCLK_STOPCTRL 0x1000 +#define APOLLO_PWR_CTRL 0x1020 +#define APOLLO_PWR_CTRL2 0x1024 +#define APOLLO_INTR_SPREAD_ENABLE 0x1080 +#define APOLLO_INTR_SPREAD_USE_STANDBYWFI 0x1084 +#define APOLLO_INTR_SPREAD_BLOCKING_DURATION 0x1088 + +static unsigned long apollo_clk_regs[] __initdata = { + APOLLO_PLL_LOCK, + APOLLO_PLL_CON0, + APOLLO_PLL_CON1, + APOLLO_PLL_FREQ_DET, + MUX_SEL_APOLLO0, + MUX_SEL_APOLLO1, + MUX_SEL_APOLLO2, + MUX_ENABLE_APOLLO0, + MUX_ENABLE_APOLLO1, + MUX_ENABLE_APOLLO2, + MUX_STAT_APOLLO0, + MUX_STAT_APOLLO1, + MUX_STAT_APOLLO2, + DIV_APOLLO0, + DIV_APOLLO1, + DIV_APOLLO_PLL_FREQ_DET, + DIV_STAT_APOLLO0, + DIV_STAT_APOLLO1, + DIV_STAT_APOLLO_PLL_FREQ_DET, + ENABLE_ACLK_APOLLO, + ENABLE_PCLK_APOLLO, + ENABLE_SCLK_APOLLO, + ENABLE_IP_APOLLO0, + ENABLE_IP_APOLLO1, + CLKOUT_CMU_APOLLO, + CLKOUT_CMU_APOLLO_DIV_STAT, + ARMCLK_STOPCTRL, + APOLLO_PWR_CTRL, + APOLLO_PWR_CTRL2, + APOLLO_INTR_SPREAD_ENABLE, + APOLLO_INTR_SPREAD_USE_STANDBYWFI, + APOLLO_INTR_SPREAD_BLOCKING_DURATION, +}; + +/* list of all parent clock list */ +PNAME(mout_apollo_pll_p) = { "oscclk", "fout_apollo_pll", }; +PNAME(mout_bus_pll_apollo_user_p) = { "oscclk", "sclk_bus_pll_apollo", }; +PNAME(mout_apollo_p) = { "mout_apollo_pll", + "mout_bus_pll_apollo_user", }; + +static struct samsung_pll_clock apollo_pll_clks[] __initdata = { + PLL(pll_35xx, CLK_FOUT_APOLLO_PLL, "fout_apollo_pll", "oscclk", + APOLLO_PLL_LOCK, APOLLO_PLL_CON0, exynos5443_pll_rates), +}; + +static struct samsung_mux_clock apollo_mux_clks[] __initdata = { + /* MUX_SEL_APOLLO0 */ + MUX_F(CLK_MOUT_APOLLO_PLL, "mout_apollo_pll", mout_apollo_pll_p, + MUX_SEL_APOLLO0, 0, 1, 0, CLK_MUX_READ_ONLY), + + /* MUX_SEL_APOLLO1 */ + MUX(CLK_MOUT_BUS_PLL_APOLLO_USER, "mout_bus_pll_apollo_user", + mout_bus_pll_apollo_user_p, MUX_SEL_APOLLO1, 0, 1), + + /* MUX_SEL_APOLLO2 */ + MUX_F(CLK_MOUT_APOLLO, "mout_apollo", mout_apollo_p, MUX_SEL_APOLLO2, + 0, 1, 0, CLK_MUX_READ_ONLY), +}; + +static struct samsung_div_clock apollo_div_clks[] __initdata = { + /* DIV_APOLLO0 */ + DIV_F(CLK_DIV_CNTCLK_APOLLO, "div_cntclk_apollo", "div_apollo2", + DIV_APOLLO0, 24, 3, CLK_GET_RATE_NOCACHE, + CLK_DIVIDER_READ_ONLY), + DIV_F(CLK_DIV_PCLK_DBG_APOLLO, "div_pclk_dbg_apollo", "div_apollo2", + DIV_APOLLO0, 20, 3, CLK_GET_RATE_NOCACHE, + CLK_DIVIDER_READ_ONLY), + DIV_F(CLK_DIV_ATCLK_APOLLO, "div_atclk_apollo", "div_apollo2", + DIV_APOLLO0, 16, 3, CLK_GET_RATE_NOCACHE, + CLK_DIVIDER_READ_ONLY), + DIV_F(CLK_DIV_PCLK_APOLLO, "div_pclk_apollo", "div_apollo2", + DIV_APOLLO0, 12, 3, CLK_GET_RATE_NOCACHE, + CLK_DIVIDER_READ_ONLY), + DIV_F(CLK_DIV_ACLK_APOLLO, "div_aclk_apollo", "div_apollo2", + DIV_APOLLO0, 8, 3, CLK_GET_RATE_NOCACHE, + CLK_DIVIDER_READ_ONLY), + DIV_F(CLK_DIV_APOLLO2, "div_apollo2", "div_apollo1", + DIV_APOLLO0, 4, 3, CLK_GET_RATE_NOCACHE, + CLK_DIVIDER_READ_ONLY), + DIV_F(CLK_DIV_APOLLO1, "div_apollo1", "mout_apollo", + DIV_APOLLO0, 0, 3, CLK_GET_RATE_NOCACHE, + CLK_DIVIDER_READ_ONLY), + + /* DIV_APOLLO1 */ + DIV_F(CLK_DIV_SCLK_HPM_APOLLO, "div_sclk_hpm_apollo", "mout_apollo", + DIV_APOLLO1, 4, 3, CLK_GET_RATE_NOCACHE, + CLK_DIVIDER_READ_ONLY), + DIV_F(CLK_DIV_APOLLO_PLL, "div_apollo_pll", "mout_apollo", + DIV_APOLLO1, 0, 3, CLK_GET_RATE_NOCACHE, + CLK_DIVIDER_READ_ONLY), +}; + +static struct samsung_gate_clock apollo_gate_clks[] __initdata = { + /* ENABLE_ACLK_APOLLO */ + GATE(CLK_ACLK_ASATBSLV_APOLLO_3_CSSYS, "aclk_asatbslv_apollo_3_cssys", + "div_atclk_apollo", ENABLE_ACLK_APOLLO, + 6, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_ASATBSLV_APOLLO_2_CSSYS, "aclk_asatbslv_apollo_2_cssys", + "div_atclk_apollo", ENABLE_ACLK_APOLLO, + 5, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_ASATBSLV_APOLLO_1_CSSYS, "aclk_asatbslv_apollo_1_cssys", + "div_atclk_apollo", ENABLE_ACLK_APOLLO, + 4, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_ASATBSLV_APOLLO_0_CSSYS, "aclk_asatbslv_apollo_0_cssys", + "div_atclk_apollo", ENABLE_ACLK_APOLLO, + 3, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_ASYNCACES_APOLLO_CCI, "aclk_asyncaces_apollo_cci", + "div_aclk_apollo", ENABLE_ACLK_APOLLO, + 2, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_AHB2APB_APOLLOP, "aclk_ahb2apb_apollop", + "div_pclk_apollo", ENABLE_ACLK_APOLLO, + 1, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_APOLLONP_200, "aclk_apollonp_200", + "div_pclk_apollo", ENABLE_ACLK_APOLLO, + 0, CLK_IGNORE_UNUSED, 0), + + /* ENABLE_PCLK_APOLLO */ + GATE(CLK_PCLK_ASAPBMST_CSSYS_APOLLO, "pclk_asapbmst_cssys_apollo", + "div_pclk_dbg_apollo", ENABLE_PCLK_APOLLO, + 2, CLK_IGNORE_UNUSED, 0), + GATE(CLK_PCLK_PMU_APOLLO, "pclk_pmu_apollo", "div_pclk_apollo", + ENABLE_PCLK_APOLLO, 1, CLK_IGNORE_UNUSED, 0), + GATE(CLK_PCLK_SYSREG_APOLLO, "pclk_sysreg_apollo", + "div_pclk_apollo", ENABLE_PCLK_APOLLO, + 0, CLK_IGNORE_UNUSED, 0), + + /* ENABLE_SCLK_APOLLO */ + GATE(CLK_CNTCLK_APOLLO, "cntclk_apollo", "div_cntclk_apollo", + ENABLE_SCLK_APOLLO, 3, CLK_IGNORE_UNUSED, 0), + GATE(CLK_SCLK_HPM_APOLLO, "sclk_hpm_apollo", "div_sclk_hpm_apollo", + ENABLE_SCLK_APOLLO, 1, CLK_IGNORE_UNUSED, 0), + GATE(CLK_SCLK_APOLLO, "sclk_apollo", "div_apollo_pll", + ENABLE_SCLK_APOLLO, 0, CLK_IGNORE_UNUSED, 0), +}; + +static struct samsung_cmu_info apollo_cmu_info __initdata = { + .pll_clks = apollo_pll_clks, + .nr_pll_clks = ARRAY_SIZE(apollo_pll_clks), + .mux_clks = apollo_mux_clks, + .nr_mux_clks = ARRAY_SIZE(apollo_mux_clks), + .div_clks = apollo_div_clks, + .nr_div_clks = ARRAY_SIZE(apollo_div_clks), + .gate_clks = apollo_gate_clks, + .nr_gate_clks = ARRAY_SIZE(apollo_gate_clks), + .nr_clk_ids = APOLLO_NR_CLK, + .clk_regs = apollo_clk_regs, + .nr_clk_regs = ARRAY_SIZE(apollo_clk_regs), +}; + +static void __init exynos5433_cmu_apollo_init(struct device_node *np) +{ + samsung_cmu_register_one(np, &apollo_cmu_info); +} +CLK_OF_DECLARE(exynos5433_cmu_apollo, "samsung,exynos5433-cmu-apollo", + exynos5433_cmu_apollo_init); diff --git a/include/dt-bindings/clock/exynos5433.h b/include/dt-bindings/clock/exynos5433.h index fef8893c3ec2..90184e3a42d5 100644 --- a/include/dt-bindings/clock/exynos5433.h +++ b/include/dt-bindings/clock/exynos5433.h @@ -854,4 +854,41 @@ #define GSCL_NR_CLK 29 +/* CMU_APOLLO */ +#define CLK_FOUT_APOLLO_PLL 1 + +#define CLK_MOUT_APOLLO_PLL 2 +#define CLK_MOUT_BUS_PLL_APOLLO_USER 3 +#define CLK_MOUT_APOLLO 4 + +#define CLK_DIV_CNTCLK_APOLLO 5 +#define CLK_DIV_PCLK_DBG_APOLLO 6 +#define CLK_DIV_ATCLK_APOLLO 7 +#define CLK_DIV_PCLK_APOLLO 8 +#define CLK_DIV_ACLK_APOLLO 9 +#define CLK_DIV_APOLLO2 10 +#define CLK_DIV_APOLLO1 11 +#define CLK_DIV_SCLK_HPM_APOLLO 12 +#define CLK_DIV_APOLLO_PLL 13 + +#define CLK_ACLK_ATBDS_APOLLO_3 14 +#define CLK_ACLK_ATBDS_APOLLO_2 15 +#define CLK_ACLK_ATBDS_APOLLO_1 16 +#define CLK_ACLK_ATBDS_APOLLO_0 17 +#define CLK_ACLK_ASATBSLV_APOLLO_3_CSSYS 18 +#define CLK_ACLK_ASATBSLV_APOLLO_2_CSSYS 19 +#define CLK_ACLK_ASATBSLV_APOLLO_1_CSSYS 20 +#define CLK_ACLK_ASATBSLV_APOLLO_0_CSSYS 21 +#define CLK_ACLK_ASYNCACES_APOLLO_CCI 22 +#define CLK_ACLK_AHB2APB_APOLLOP 23 +#define CLK_ACLK_APOLLONP_200 24 +#define CLK_PCLK_ASAPBMST_CSSYS_APOLLO 25 +#define CLK_PCLK_PMU_APOLLO 26 +#define CLK_PCLK_SYSREG_APOLLO 27 +#define CLK_CNTCLK_APOLLO 28 +#define CLK_SCLK_HPM_APOLLO 29 +#define CLK_SCLK_APOLLO 30 + +#define APOLLO_NR_CLK 31 + #endif /* _DT_BINDINGS_CLOCK_EXYNOS5433_H */ -- cgit v1.2.3 From 6c5d76d15ab6da9b30af020a44e071eb5145e1a0 Mon Sep 17 00:00:00 2001 From: Chanwoo Choi Date: Tue, 3 Feb 2015 09:13:50 +0900 Subject: clk: samsung: exynos5433: Add clocks for CMU_ATLAS domain This patch adds the mux/divider/gate clocks for CMU_ATLAS domain which generates the clocks for Cortex-A57 Quad-core processsor, L2 cache controller and CoreSight. Signed-off-by: Chanwoo Choi Acked-by: Inki Dae Reviewed-by: Pankaj Dubey Signed-off-by: Sylwester Nawrocki --- .../devicetree/bindings/clock/exynos5433-clock.txt | 16 ++ drivers/clk/samsung/clk-exynos5433.c | 219 +++++++++++++++++++++ include/dt-bindings/clock/exynos5433.h | 46 +++++ 3 files changed, 281 insertions(+) (limited to 'include') diff --git a/Documentation/devicetree/bindings/clock/exynos5433-clock.txt b/Documentation/devicetree/bindings/clock/exynos5433-clock.txt index 0a7146861a27..7c1dccc4d72e 100644 --- a/Documentation/devicetree/bindings/clock/exynos5433-clock.txt +++ b/Documentation/devicetree/bindings/clock/exynos5433-clock.txt @@ -34,6 +34,9 @@ Required Properties: which generates clocks for GSCALER IPs. - "samsung,exynos5433-cmu-apollo"- clock controller compatible for CMU_APOLLO which generates clocks for Cortex-A53 Quad-core processor. + - "samsung,exynos5433-cmu-atlas" - clock controller compatible for CMU_ATLAS + which generates clocks for Cortex-A57 Quad-core processor, CoreSight and + L2 cache controller. - reg: physical base address of the controller and length of memory mapped region. @@ -111,6 +114,10 @@ Required Properties: - oscclk - sclk_bus_pll_apollo + Input clocks for atlas clock controller: + - oscclk + - sclk_bus_pll_atlas + Each clock is assigned an identifier and client nodes can use this identifier to specify the clock which they consume. @@ -304,6 +311,15 @@ Example 2: Examples of clock controller nodes are listed below. clocks = <&xxti>, <&cmu_mif CLK_SCLK_BUS_PLL_APOLLO>; }; + cmu_atlas: clock-controller@11800000 { + compatible = "samsung,exynos5433-cmu-atlas"; + reg = <0x11800000 0x1088>; + #clock-cells = <1>; + + clock-names = "oscclk", "sclk_bus_pll_atlas"; + clocks = <&xxti>, <&cmu_mif CLK_SCLK_BUS_PLL_ATLAS>; + }; + Example 3: UART controller node that consumes the clock generated by the clock controller. diff --git a/drivers/clk/samsung/clk-exynos5433.c b/drivers/clk/samsung/clk-exynos5433.c index f7d1e8390cdf..c44062d2904e 100644 --- a/drivers/clk/samsung/clk-exynos5433.c +++ b/drivers/clk/samsung/clk-exynos5433.c @@ -3586,3 +3586,222 @@ static void __init exynos5433_cmu_apollo_init(struct device_node *np) } CLK_OF_DECLARE(exynos5433_cmu_apollo, "samsung,exynos5433-cmu-apollo", exynos5433_cmu_apollo_init); + +/* + * Register offset definitions for CMU_ATLAS + */ +#define ATLAS_PLL_LOCK 0x0000 +#define ATLAS_PLL_CON0 0x0100 +#define ATLAS_PLL_CON1 0x0104 +#define ATLAS_PLL_FREQ_DET 0x010c +#define MUX_SEL_ATLAS0 0x0200 +#define MUX_SEL_ATLAS1 0x0204 +#define MUX_SEL_ATLAS2 0x0208 +#define MUX_ENABLE_ATLAS0 0x0300 +#define MUX_ENABLE_ATLAS1 0x0304 +#define MUX_ENABLE_ATLAS2 0x0308 +#define MUX_STAT_ATLAS0 0x0400 +#define MUX_STAT_ATLAS1 0x0404 +#define MUX_STAT_ATLAS2 0x0408 +#define DIV_ATLAS0 0x0600 +#define DIV_ATLAS1 0x0604 +#define DIV_ATLAS_PLL_FREQ_DET 0x0608 +#define DIV_STAT_ATLAS0 0x0700 +#define DIV_STAT_ATLAS1 0x0704 +#define DIV_STAT_ATLAS_PLL_FREQ_DET 0x0708 +#define ENABLE_ACLK_ATLAS 0x0800 +#define ENABLE_PCLK_ATLAS 0x0900 +#define ENABLE_SCLK_ATLAS 0x0a00 +#define ENABLE_IP_ATLAS0 0x0b00 +#define ENABLE_IP_ATLAS1 0x0b04 +#define CLKOUT_CMU_ATLAS 0x0c00 +#define CLKOUT_CMU_ATLAS_DIV_STAT 0x0c04 +#define ARMCLK_STOPCTRL 0x1000 +#define ATLAS_PWR_CTRL 0x1020 +#define ATLAS_PWR_CTRL2 0x1024 +#define ATLAS_INTR_SPREAD_ENABLE 0x1080 +#define ATLAS_INTR_SPREAD_USE_STANDBYWFI 0x1084 +#define ATLAS_INTR_SPREAD_BLOCKING_DURATION 0x1088 + +static unsigned long atlas_clk_regs[] __initdata = { + ATLAS_PLL_LOCK, + ATLAS_PLL_CON0, + ATLAS_PLL_CON1, + ATLAS_PLL_FREQ_DET, + MUX_SEL_ATLAS0, + MUX_SEL_ATLAS1, + MUX_SEL_ATLAS2, + MUX_ENABLE_ATLAS0, + MUX_ENABLE_ATLAS1, + MUX_ENABLE_ATLAS2, + MUX_STAT_ATLAS0, + MUX_STAT_ATLAS1, + MUX_STAT_ATLAS2, + DIV_ATLAS0, + DIV_ATLAS1, + DIV_ATLAS_PLL_FREQ_DET, + DIV_STAT_ATLAS0, + DIV_STAT_ATLAS1, + DIV_STAT_ATLAS_PLL_FREQ_DET, + ENABLE_ACLK_ATLAS, + ENABLE_PCLK_ATLAS, + ENABLE_SCLK_ATLAS, + ENABLE_IP_ATLAS0, + ENABLE_IP_ATLAS1, + CLKOUT_CMU_ATLAS, + CLKOUT_CMU_ATLAS_DIV_STAT, + ARMCLK_STOPCTRL, + ATLAS_PWR_CTRL, + ATLAS_PWR_CTRL2, + ATLAS_INTR_SPREAD_ENABLE, + ATLAS_INTR_SPREAD_USE_STANDBYWFI, + ATLAS_INTR_SPREAD_BLOCKING_DURATION, +}; + +/* list of all parent clock list */ +PNAME(mout_atlas_pll_p) = { "oscclk", "fout_atlas_pll", }; +PNAME(mout_bus_pll_atlas_user_p) = { "oscclk", "sclk_bus_pll_atlas", }; +PNAME(mout_atlas_p) = { "mout_atlas_pll", + "mout_bus_pll_atlas_user", }; + +static struct samsung_pll_clock atlas_pll_clks[] __initdata = { + PLL(pll_35xx, CLK_FOUT_ATLAS_PLL, "fout_atlas_pll", "oscclk", + ATLAS_PLL_LOCK, ATLAS_PLL_CON0, exynos5443_pll_rates), +}; + +static struct samsung_mux_clock atlas_mux_clks[] __initdata = { + /* MUX_SEL_ATLAS0 */ + MUX_F(CLK_MOUT_ATLAS_PLL, "mout_atlas_pll", mout_atlas_pll_p, + MUX_SEL_ATLAS0, 0, 1, 0, CLK_MUX_READ_ONLY), + + /* MUX_SEL_ATLAS1 */ + MUX(CLK_MOUT_BUS_PLL_ATLAS_USER, "mout_bus_pll_atlas_user", + mout_bus_pll_atlas_user_p, MUX_SEL_ATLAS1, 0, 1), + + /* MUX_SEL_ATLAS2 */ + MUX_F(CLK_MOUT_ATLAS, "mout_atlas", mout_atlas_p, MUX_SEL_ATLAS2, + 0, 1, 0, CLK_MUX_READ_ONLY), +}; + +static struct samsung_div_clock atlas_div_clks[] __initdata = { + /* DIV_ATLAS0 */ + DIV_F(CLK_DIV_CNTCLK_ATLAS, "div_cntclk_atlas", "div_atlas2", + DIV_ATLAS0, 24, 3, CLK_GET_RATE_NOCACHE, + CLK_DIVIDER_READ_ONLY), + DIV_F(CLK_DIV_PCLK_DBG_ATLAS, "div_pclk_dbg_atlas", "div_atclk_atlas", + DIV_ATLAS0, 20, 3, CLK_GET_RATE_NOCACHE, + CLK_DIVIDER_READ_ONLY), + DIV_F(CLK_DIV_ATCLK_ATLASO, "div_atclk_atlas", "div_atlas2", + DIV_ATLAS0, 16, 3, CLK_GET_RATE_NOCACHE, + CLK_DIVIDER_READ_ONLY), + DIV_F(CLK_DIV_PCLK_ATLAS, "div_pclk_atlas", "div_atlas2", + DIV_ATLAS0, 12, 3, CLK_GET_RATE_NOCACHE, + CLK_DIVIDER_READ_ONLY), + DIV_F(CLK_DIV_ACLK_ATLAS, "div_aclk_atlas", "div_atlas2", + DIV_ATLAS0, 8, 3, CLK_GET_RATE_NOCACHE, + CLK_DIVIDER_READ_ONLY), + DIV_F(CLK_DIV_ATLAS2, "div_atlas2", "div_atlas1", + DIV_ATLAS0, 4, 3, CLK_GET_RATE_NOCACHE, + CLK_DIVIDER_READ_ONLY), + DIV_F(CLK_DIV_ATLAS1, "div_atlas1", "mout_atlas", + DIV_ATLAS0, 0, 3, CLK_GET_RATE_NOCACHE, + CLK_DIVIDER_READ_ONLY), + + /* DIV_ATLAS1 */ + DIV_F(CLK_DIV_SCLK_HPM_ATLAS, "div_sclk_hpm_atlas", "mout_atlas", + DIV_ATLAS1, 4, 3, CLK_GET_RATE_NOCACHE, + CLK_DIVIDER_READ_ONLY), + DIV_F(CLK_DIV_ATLAS_PLL, "div_atlas_pll", "mout_atlas", + DIV_ATLAS1, 0, 3, CLK_GET_RATE_NOCACHE, + CLK_DIVIDER_READ_ONLY), +}; + +static struct samsung_gate_clock atlas_gate_clks[] __initdata = { + /* ENABLE_ACLK_ATLAS */ + GATE(CLK_ACLK_ATB_AUD_CSSYS, "aclk_atb_aud_cssys", + "div_atclk_atlas", ENABLE_ACLK_ATLAS, + 9, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_ATB_APOLLO3_CSSYS, "aclk_atb_apollo3_cssys", + "div_atclk_atlas", ENABLE_ACLK_ATLAS, + 8, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_ATB_APOLLO2_CSSYS, "aclk_atb_apollo2_cssys", + "div_atclk_atlas", ENABLE_ACLK_ATLAS, + 7, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_ATB_APOLLO1_CSSYS, "aclk_atb_apollo1_cssys", + "div_atclk_atlas", ENABLE_ACLK_ATLAS, + 6, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_ATB_APOLLO0_CSSYS, "aclk_atb_apollo0_cssys", + "div_atclk_atlas", ENABLE_ACLK_ATLAS, + 5, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_ASYNCAHBS_CSSYS_SSS, "aclk_asyncahbs_cssys_sss", + "div_atclk_atlas", ENABLE_ACLK_ATLAS, + 4, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_ASYNCAXIS_CSSYS_CCIX, "aclk_asyncaxis_cssys_ccix", + "div_pclk_dbg_atlas", ENABLE_ACLK_ATLAS, + 3, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_ASYNCACES_ATLAS_CCI, "aclk_asyncaces_atlas_cci", + "div_aclk_atlas", ENABLE_ACLK_ATLAS, + 2, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_AHB2APB_ATLASP, "aclk_ahb2apb_atlasp", "div_pclk_atlas", + ENABLE_ACLK_ATLAS, 1, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_ATLASNP_200, "aclk_atlasnp_200", "div_pclk_atlas", + ENABLE_ACLK_ATLAS, 0, CLK_IGNORE_UNUSED, 0), + + /* ENABLE_PCLK_ATLAS */ + GATE(CLK_PCLK_ASYNCAPB_AUD_CSSYS, "pclk_asyncapb_aud_cssys", + "div_pclk_dbg_atlas", ENABLE_PCLK_ATLAS, + 5, CLK_IGNORE_UNUSED, 0), + GATE(CLK_PCLK_ASYNCAPB_ISP_CSSYS, "pclk_asyncapb_isp_cssys", + "div_pclk_dbg_atlas", ENABLE_PCLK_ATLAS, + 4, CLK_IGNORE_UNUSED, 0), + GATE(CLK_PCLK_ASYNCAPB_APOLLO_CSSYS, "pclk_asyncapb_apollo_cssys", + "div_pclk_dbg_atlas", ENABLE_PCLK_ATLAS, + 3, CLK_IGNORE_UNUSED, 0), + GATE(CLK_PCLK_PMU_ATLAS, "pclk_pmu_atlas", "div_pclk_atlas", + ENABLE_PCLK_ATLAS, 2, CLK_IGNORE_UNUSED, 0), + GATE(CLK_PCLK_SYSREG_ATLAS, "pclk_sysreg_atlas", "div_pclk_atlas", + ENABLE_PCLK_ATLAS, 1, CLK_IGNORE_UNUSED, 0), + GATE(CLK_PCLK_SECJTAG, "pclk_secjtag", "div_pclk_dbg_atlas", + ENABLE_PCLK_ATLAS, 0, CLK_IGNORE_UNUSED, 0), + + /* ENABLE_SCLK_ATLAS */ + GATE(CLK_CNTCLK_ATLAS, "cntclk_atlas", "div_cntclk_atlas", + ENABLE_SCLK_ATLAS, 10, CLK_IGNORE_UNUSED, 0), + GATE(CLK_SCLK_HPM_ATLAS, "sclk_hpm_atlas", "div_sclk_hpm_atlas", + ENABLE_SCLK_ATLAS, 7, CLK_IGNORE_UNUSED, 0), + GATE(CLK_TRACECLK, "traceclk", "div_atclk_atlas", + ENABLE_SCLK_ATLAS, 6, CLK_IGNORE_UNUSED, 0), + GATE(CLK_CTMCLK, "ctmclk", "div_atclk_atlas", + ENABLE_SCLK_ATLAS, 5, CLK_IGNORE_UNUSED, 0), + GATE(CLK_HCLK_CSSYS, "hclk_cssys", "div_atclk_atlas", + ENABLE_SCLK_ATLAS, 4, CLK_IGNORE_UNUSED, 0), + GATE(CLK_PCLK_DBG_CSSYS, "pclk_dbg_cssys", "div_pclk_dbg_atlas", + ENABLE_SCLK_ATLAS, 3, CLK_IGNORE_UNUSED, 0), + GATE(CLK_PCLK_DBG, "pclk_dbg", "div_pclk_dbg_atlas", + ENABLE_SCLK_ATLAS, 2, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ATCLK, "atclk", "div_atclk_atlas", + ENABLE_SCLK_ATLAS, 1, CLK_IGNORE_UNUSED, 0), + GATE(CLK_SCLK_ATLAS, "sclk_atlas", "div_atlas2", + ENABLE_SCLK_ATLAS, 0, CLK_IGNORE_UNUSED, 0), +}; + +static struct samsung_cmu_info atlas_cmu_info __initdata = { + .pll_clks = atlas_pll_clks, + .nr_pll_clks = ARRAY_SIZE(atlas_pll_clks), + .mux_clks = atlas_mux_clks, + .nr_mux_clks = ARRAY_SIZE(atlas_mux_clks), + .div_clks = atlas_div_clks, + .nr_div_clks = ARRAY_SIZE(atlas_div_clks), + .gate_clks = atlas_gate_clks, + .nr_gate_clks = ARRAY_SIZE(atlas_gate_clks), + .nr_clk_ids = ATLAS_NR_CLK, + .clk_regs = atlas_clk_regs, + .nr_clk_regs = ARRAY_SIZE(atlas_clk_regs), +}; + +static void __init exynos5433_cmu_atlas_init(struct device_node *np) +{ + samsung_cmu_register_one(np, &atlas_cmu_info); +} +CLK_OF_DECLARE(exynos5433_cmu_atlas, "samsung,exynos5433-cmu-atlas", + exynos5433_cmu_atlas_init); diff --git a/include/dt-bindings/clock/exynos5433.h b/include/dt-bindings/clock/exynos5433.h index 90184e3a42d5..cdc91f7e6ec8 100644 --- a/include/dt-bindings/clock/exynos5433.h +++ b/include/dt-bindings/clock/exynos5433.h @@ -891,4 +891,50 @@ #define APOLLO_NR_CLK 31 +/* CMU_ATLAS */ +#define CLK_FOUT_ATLAS_PLL 1 + +#define CLK_MOUT_ATLAS_PLL 2 +#define CLK_MOUT_BUS_PLL_ATLAS_USER 3 +#define CLK_MOUT_ATLAS 4 + +#define CLK_DIV_CNTCLK_ATLAS 5 +#define CLK_DIV_PCLK_DBG_ATLAS 6 +#define CLK_DIV_ATCLK_ATLASO 7 +#define CLK_DIV_PCLK_ATLAS 8 +#define CLK_DIV_ACLK_ATLAS 9 +#define CLK_DIV_ATLAS2 10 +#define CLK_DIV_ATLAS1 11 +#define CLK_DIV_SCLK_HPM_ATLAS 12 +#define CLK_DIV_ATLAS_PLL 13 + +#define CLK_ACLK_ATB_AUD_CSSYS 14 +#define CLK_ACLK_ATB_APOLLO3_CSSYS 15 +#define CLK_ACLK_ATB_APOLLO2_CSSYS 16 +#define CLK_ACLK_ATB_APOLLO1_CSSYS 17 +#define CLK_ACLK_ATB_APOLLO0_CSSYS 18 +#define CLK_ACLK_ASYNCAHBS_CSSYS_SSS 19 +#define CLK_ACLK_ASYNCAXIS_CSSYS_CCIX 20 +#define CLK_ACLK_ASYNCACES_ATLAS_CCI 21 +#define CLK_ACLK_AHB2APB_ATLASP 22 +#define CLK_ACLK_ATLASNP_200 23 +#define CLK_PCLK_ASYNCAPB_AUD_CSSYS 24 +#define CLK_PCLK_ASYNCAPB_ISP_CSSYS 25 +#define CLK_PCLK_ASYNCAPB_APOLLO_CSSYS 26 +#define CLK_PCLK_PMU_ATLAS 27 +#define CLK_PCLK_SYSREG_ATLAS 28 +#define CLK_PCLK_SECJTAG 29 +#define CLK_CNTCLK_ATLAS 30 +#define CLK_SCLK_FREQ_DET_ATLAS_PLL 31 +#define CLK_SCLK_HPM_ATLAS 32 +#define CLK_TRACECLK 33 +#define CLK_CTMCLK 34 +#define CLK_HCLK_CSSYS 35 +#define CLK_PCLK_DBG_CSSYS 36 +#define CLK_PCLK_DBG 37 +#define CLK_ATCLK 38 +#define CLK_SCLK_ATLAS 39 + +#define ATLAS_NR_CLK 40 + #endif /* _DT_BINDINGS_CLOCK_EXYNOS5433_H */ -- cgit v1.2.3 From b274bbfd8b4a94cb5bd6fe21801264a27dd8ec75 Mon Sep 17 00:00:00 2001 From: Chanwoo Choi Date: Tue, 3 Feb 2015 09:13:51 +0900 Subject: clk: samsung: exynos5433: Add clocks for CMU_MSCL domain This patch adds the mux/divider/gate clocks for CMU_MSCL domain which generates the clocks for M2M (Memory to Memory) scaler, JPEG IPs. Signed-off-by: Chanwoo Choi Acked-by: Inki Dae Reviewed-by: Pankaj Dubey Signed-off-by: Sylwester Nawrocki --- .../devicetree/bindings/clock/exynos5433-clock.txt | 20 +++ drivers/clk/samsung/clk-exynos5433.c | 185 +++++++++++++++++++++ include/dt-bindings/clock/exynos5433.h | 41 ++++- 3 files changed, 245 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/Documentation/devicetree/bindings/clock/exynos5433-clock.txt b/Documentation/devicetree/bindings/clock/exynos5433-clock.txt index 7c1dccc4d72e..ecb9534c2ea6 100644 --- a/Documentation/devicetree/bindings/clock/exynos5433-clock.txt +++ b/Documentation/devicetree/bindings/clock/exynos5433-clock.txt @@ -37,6 +37,8 @@ Required Properties: - "samsung,exynos5433-cmu-atlas" - clock controller compatible for CMU_ATLAS which generates clocks for Cortex-A57 Quad-core processor, CoreSight and L2 cache controller. + - "samsung,exynos5433-cmu-mscl" - clock controller compatible for CMU_MSCL + which generates clocks for M2M (Memory to Memory) scaler and JPEG IPs. - reg: physical base address of the controller and length of memory mapped region. @@ -118,6 +120,11 @@ Required Properties: - oscclk - sclk_bus_pll_atlas + Input clocks for mscl clock controller: + - oscclk + - sclk_jpeg_mscl + - aclk_mscl_400 + Each clock is assigned an identifier and client nodes can use this identifier to specify the clock which they consume. @@ -320,6 +327,19 @@ Example 2: Examples of clock controller nodes are listed below. clocks = <&xxti>, <&cmu_mif CLK_SCLK_BUS_PLL_ATLAS>; }; + cmu_mscl: clock-controller@105d0000 { + compatible = "samsung,exynos5433-cmu-mscl"; + reg = <0x105d0000 0x0b10>; + #clock-cells = <1>; + + clock-names = "oscclk", + "sclk_jpeg_mscl", + "aclk_mscl_400"; + clocks = <&xxti>, + <&cmu_top CLK_SCLK_JPEG_MSCL>, + <&cmu_top CLK_ACLK_MSCL_400>; + }; + Example 3: UART controller node that consumes the clock generated by the clock controller. diff --git a/drivers/clk/samsung/clk-exynos5433.c b/drivers/clk/samsung/clk-exynos5433.c index c44062d2904e..d272e42eb48c 100644 --- a/drivers/clk/samsung/clk-exynos5433.c +++ b/drivers/clk/samsung/clk-exynos5433.c @@ -419,6 +419,8 @@ static struct samsung_div_clock top_div_clks[] __initdata = { DIV_TOP1, 0, 3), /* DIV_TOP2 */ + DIV(CLK_DIV_ACLK_MSCL_400, "div_aclk_mscl_400", "mout_aclk_mscl_400_b", + DIV_TOP2, 4, 3), DIV(CLK_DIV_ACLK_FSYS_200, "div_aclk_fsys_200", "mout_bus_pll_user", DIV_TOP2, 0, 3), @@ -446,6 +448,10 @@ static struct samsung_div_clock top_div_clks[] __initdata = { DIV(CLK_DIV_ACLK_BUS1_400, "div_aclk_bus1_400", "mout_bus_pll_user", DIV_TOP4, 0, 3), + /* DIV_TOP_MSCL */ + DIV(CLK_DIV_SCLK_JPEG, "div_sclk_jpeg", "mout_sclk_jpeg_c", + DIV_TOP_MSCL, 0, 4), + /* DIV_TOP_FSYS0 */ DIV(CLK_DIV_SCLK_MMC1_B, "div_sclk_mmc1_b", "div_sclk_mmc1_a", DIV_TOP_FSYS0, 16, 8), @@ -542,6 +548,9 @@ static struct samsung_gate_clock top_gate_clks[] __initdata = { GATE(CLK_ACLK_PERIS_66, "aclk_peris_66", "div_aclk_peris_66_b", ENABLE_ACLK_TOP, 21, CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_MSCL_400, "aclk_mscl_400", "div_aclk_mscl_400", + ENABLE_ACLK_TOP, 19, + CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED, 0), GATE(CLK_ACLK_FSYS_200, "aclk_fsys_200", "div_aclk_fsys_200", ENABLE_ACLK_TOP, 18, CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED, 0), @@ -558,6 +567,10 @@ static struct samsung_gate_clock top_gate_clks[] __initdata = { ENABLE_ACLK_TOP, 0, CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED, 0), + /* ENABLE_SCLK_TOP_MSCL */ + GATE(CLK_SCLK_JPEG_MSCL, "sclk_jpeg_mscl", "div_sclk_jpeg", + ENABLE_SCLK_TOP_MSCL, 0, 0, 0), + /* ENABLE_SCLK_TOP_FSYS */ GATE(CLK_SCLK_PCIE_100_FSYS, "sclk_pcie_100_fsys", "div_sclk_pcie_100", ENABLE_SCLK_TOP_FSYS, 7, 0, 0), @@ -3805,3 +3818,175 @@ static void __init exynos5433_cmu_atlas_init(struct device_node *np) } CLK_OF_DECLARE(exynos5433_cmu_atlas, "samsung,exynos5433-cmu-atlas", exynos5433_cmu_atlas_init); + +/* + * Register offset definitions for CMU_MSCL + */ +#define MUX_SEL_MSCL0 0x0200 +#define MUX_SEL_MSCL1 0x0204 +#define MUX_ENABLE_MSCL0 0x0300 +#define MUX_ENABLE_MSCL1 0x0304 +#define MUX_STAT_MSCL0 0x0400 +#define MUX_STAT_MSCL1 0x0404 +#define DIV_MSCL 0x0600 +#define DIV_STAT_MSCL 0x0700 +#define ENABLE_ACLK_MSCL 0x0800 +#define ENABLE_ACLK_MSCL_SECURE_SMMU_M2MSCALER0 0x0804 +#define ENABLE_ACLK_MSCL_SECURE_SMMU_M2MSCALER1 0x0808 +#define ENABLE_ACLK_MSCL_SECURE_SMMU_JPEG 0x080c +#define ENABLE_PCLK_MSCL 0x0900 +#define ENABLE_PCLK_MSCL_SECURE_SMMU_M2MSCALER0 0x0904 +#define ENABLE_PCLK_MSCL_SECURE_SMMU_M2MSCALER1 0x0908 +#define ENABLE_PCLK_MSCL_SECURE_SMMU_JPEG 0x000c +#define ENABLE_SCLK_MSCL 0x0a00 +#define ENABLE_IP_MSCL0 0x0b00 +#define ENABLE_IP_MSCL1 0x0b04 +#define ENABLE_IP_MSCL_SECURE_SMMU_M2MSCALER0 0x0b08 +#define ENABLE_IP_MSCL_SECURE_SMMU_M2MSCALER1 0x0b0c +#define ENABLE_IP_MSCL_SECURE_SMMU_JPEG 0x0b10 + +static unsigned long mscl_clk_regs[] __initdata = { + MUX_SEL_MSCL0, + MUX_SEL_MSCL1, + MUX_ENABLE_MSCL0, + MUX_ENABLE_MSCL1, + MUX_STAT_MSCL0, + MUX_STAT_MSCL1, + DIV_MSCL, + DIV_STAT_MSCL, + ENABLE_ACLK_MSCL, + ENABLE_ACLK_MSCL_SECURE_SMMU_M2MSCALER0, + ENABLE_ACLK_MSCL_SECURE_SMMU_M2MSCALER1, + ENABLE_ACLK_MSCL_SECURE_SMMU_JPEG, + ENABLE_PCLK_MSCL, + ENABLE_PCLK_MSCL_SECURE_SMMU_M2MSCALER0, + ENABLE_PCLK_MSCL_SECURE_SMMU_M2MSCALER1, + ENABLE_PCLK_MSCL_SECURE_SMMU_JPEG, + ENABLE_SCLK_MSCL, + ENABLE_IP_MSCL0, + ENABLE_IP_MSCL1, + ENABLE_IP_MSCL_SECURE_SMMU_M2MSCALER0, + ENABLE_IP_MSCL_SECURE_SMMU_M2MSCALER1, + ENABLE_IP_MSCL_SECURE_SMMU_JPEG, +}; + +/* list of all parent clock list */ +PNAME(mout_sclk_jpeg_user_p) = { "oscclk", "sclk_jpeg_mscl", }; +PNAME(mout_aclk_mscl_400_user_p) = { "oscclk", "aclk_mscl_400", }; +PNAME(mout_sclk_jpeg_p) = { "mout_sclk_jpeg_user", + "mout_aclk_mscl_400_user", }; + +static struct samsung_mux_clock mscl_mux_clks[] __initdata = { + /* MUX_SEL_MSCL0 */ + MUX(CLK_MOUT_SCLK_JPEG_USER, "mout_sclk_jpeg_user", + mout_sclk_jpeg_user_p, MUX_SEL_MSCL0, 4, 1), + MUX(CLK_MOUT_ACLK_MSCL_400_USER, "mout_aclk_mscl_400_user", + mout_aclk_mscl_400_user_p, MUX_SEL_MSCL0, 0, 1), + + /* MUX_SEL_MSCL1 */ + MUX(CLK_MOUT_SCLK_JPEG, "mout_sclk_jpeg", mout_sclk_jpeg_p, + MUX_SEL_MSCL1, 0, 1), +}; + +static struct samsung_div_clock mscl_div_clks[] __initdata = { + /* DIV_MSCL */ + DIV(CLK_DIV_PCLK_MSCL, "div_pclk_mscl", "mout_aclk_mscl_400_user", + DIV_MSCL, 0, 3), +}; + +static struct samsung_gate_clock mscl_gate_clks[] __initdata = { + /* ENABLE_ACLK_MSCL */ + GATE(CLK_ACLK_BTS_JPEG, "aclk_bts_jpeg", "mout_aclk_mscl_400_user", + ENABLE_ACLK_MSCL, 9, 0, 0), + GATE(CLK_ACLK_BTS_M2MSCALER1, "aclk_bts_m2mscaler1", + "mout_aclk_mscl_400_user", ENABLE_ACLK_MSCL, 8, 0, 0), + GATE(CLK_ACLK_BTS_M2MSCALER0, "aclk_bts_m2mscaler0", + "mout_aclk_mscl_400_user", ENABLE_ACLK_MSCL, 7, 0, 0), + GATE(CLK_ACLK_AHB2APB_MSCL0P, "aclk_abh2apb_mscl0p", "div_pclk_mscl", + ENABLE_ACLK_MSCL, 6, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_XIU_MSCLX, "aclk_xiu_msclx", "mout_aclk_mscl_400_user", + ENABLE_ACLK_MSCL, 5, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_MSCLNP_100, "aclk_msclnp_100", "div_pclk_mscl", + ENABLE_ACLK_MSCL, 4, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_MSCLND_400, "aclk_msclnd_400", "mout_aclk_mscl_400_user", + ENABLE_ACLK_MSCL, 3, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_JPEG, "aclk_jpeg", "mout_aclk_mscl_400_user", + ENABLE_ACLK_MSCL, 2, 0, 0), + GATE(CLK_ACLK_M2MSCALER1, "aclk_m2mscaler1", "mout_aclk_mscl_400_user", + ENABLE_ACLK_MSCL, 1, 0, 0), + GATE(CLK_ACLK_M2MSCALER0, "aclk_m2mscaler0", "mout_aclk_mscl_400_user", + ENABLE_ACLK_MSCL, 0, 0, 0), + + /* ENABLE_ACLK_MSCL_SECURE_SMMU_M2MSCALER0 */ + GATE(CLK_ACLK_SMMU_M2MSCALER0, "aclk_smmu_m2mscaler0", + "mout_aclk_mscl_400_user", + ENABLE_ACLK_MSCL_SECURE_SMMU_M2MSCALER0, + 0, CLK_IGNORE_UNUSED, 0), + + /* ENABLE_ACLK_MSCL_SECURE_SMMU_M2MSCALER1 */ + GATE(CLK_ACLK_SMMU_M2MSCALER1, "aclk_smmu_m2mscaler1", + "mout_aclk_mscl_400_user", + ENABLE_ACLK_MSCL_SECURE_SMMU_M2MSCALER1, + 0, CLK_IGNORE_UNUSED, 0), + + /* ENABLE_ACLK_MSCL_SECURE_SMMU_JPEG */ + GATE(CLK_ACLK_SMMU_JPEG, "aclk_smmu_jpeg", "mout_aclk_mscl_400_user", + ENABLE_ACLK_MSCL_SECURE_SMMU_JPEG, + 0, CLK_IGNORE_UNUSED, 0), + + /* ENABLE_PCLK_MSCL */ + GATE(CLK_PCLK_BTS_JPEG, "pclk_bts_jpeg", "div_pclk_mscl", + ENABLE_PCLK_MSCL, 7, 0, 0), + GATE(CLK_PCLK_BTS_M2MSCALER1, "pclk_bts_m2mscaler1", "div_pclk_mscl", + ENABLE_PCLK_MSCL, 6, 0, 0), + GATE(CLK_PCLK_BTS_M2MSCALER0, "pclk_bts_m2mscaler0", "div_pclk_mscl", + ENABLE_PCLK_MSCL, 5, 0, 0), + GATE(CLK_PCLK_PMU_MSCL, "pclk_pmu_mscl", "div_pclk_mscl", + ENABLE_PCLK_MSCL, 4, CLK_IGNORE_UNUSED, 0), + GATE(CLK_PCLK_SYSREG_MSCL, "pclk_sysreg_mscl", "div_pclk_mscl", + ENABLE_PCLK_MSCL, 3, CLK_IGNORE_UNUSED, 0), + GATE(CLK_PCLK_JPEG, "pclk_jpeg", "div_pclk_mscl", + ENABLE_PCLK_MSCL, 2, 0, 0), + GATE(CLK_PCLK_M2MSCALER1, "pclk_m2mscaler1", "div_pclk_mscl", + ENABLE_PCLK_MSCL, 1, 0, 0), + GATE(CLK_PCLK_M2MSCALER0, "pclk_m2mscaler0", "div_pclk_mscl", + ENABLE_PCLK_MSCL, 0, 0, 0), + + /* ENABLE_PCLK_MSCL_SECURE_SMMU_M2MSCALER0 */ + GATE(CLK_PCLK_SMMU_M2MSCALER0, "pclk_smmu_m2mscaler0", "div_pclk_mscl", + ENABLE_PCLK_MSCL_SECURE_SMMU_M2MSCALER0, + 0, CLK_IGNORE_UNUSED, 0), + + /* ENABLE_PCLK_MSCL_SECURE_SMMU_M2MSCALER1 */ + GATE(CLK_PCLK_SMMU_M2MSCALER1, "pclk_smmu_m2mscaler1", "div_pclk_mscl", + ENABLE_PCLK_MSCL_SECURE_SMMU_M2MSCALER1, + 0, CLK_IGNORE_UNUSED, 0), + + /* ENABLE_PCLK_MSCL_SECURE_SMMU_JPEG */ + GATE(CLK_PCLK_SMMU_JPEG, "pclk_smmu_jpeg", "div_pclk_mscl", + ENABLE_PCLK_MSCL_SECURE_SMMU_JPEG, + 0, CLK_IGNORE_UNUSED, 0), + + /* ENABLE_SCLK_MSCL */ + GATE(CLK_SCLK_JPEG, "sclk_jpeg", "mout_sclk_jpeg", ENABLE_SCLK_MSCL, 0, + CLK_IGNORE_UNUSED | CLK_SET_RATE_PARENT, 0), +}; + +static struct samsung_cmu_info mscl_cmu_info __initdata = { + .mux_clks = mscl_mux_clks, + .nr_mux_clks = ARRAY_SIZE(mscl_mux_clks), + .div_clks = mscl_div_clks, + .nr_div_clks = ARRAY_SIZE(mscl_div_clks), + .gate_clks = mscl_gate_clks, + .nr_gate_clks = ARRAY_SIZE(mscl_gate_clks), + .nr_clk_ids = MSCL_NR_CLK, + .clk_regs = mscl_clk_regs, + .nr_clk_regs = ARRAY_SIZE(mscl_clk_regs), +}; + +static void __init exynos5433_cmu_mscl_init(struct device_node *np) +{ + samsung_cmu_register_one(np, &mscl_cmu_info); +} +CLK_OF_DECLARE(exynos5433_cmu_mscl, "samsung,exynos5433-cmu-mscl", + exynos5433_cmu_mscl_init); diff --git a/include/dt-bindings/clock/exynos5433.h b/include/dt-bindings/clock/exynos5433.h index cdc91f7e6ec8..9898390710e6 100644 --- a/include/dt-bindings/clock/exynos5433.h +++ b/include/dt-bindings/clock/exynos5433.h @@ -114,6 +114,8 @@ #define CLK_DIV_SCLK_USBHOST30 141 #define CLK_DIV_SCLK_UFSUNIPRO 142 #define CLK_DIV_SCLK_USBDRD30 143 +#define CLK_DIV_SCLK_JPEG 144 +#define CLK_DIV_ACLK_MSCL_400 145 #define CLK_ACLK_PERIC_66 200 #define CLK_ACLK_PERIS_66 201 @@ -149,8 +151,10 @@ #define CLK_SCLK_USBDRD30_FSYS 231 #define CLK_ACLK_GSCL_111 232 #define CLK_ACLK_GSCL_333 233 +#define CLK_SCLK_JPEG_MSCL 234 +#define CLK_ACLK_MSCL_400 235 -#define TOP_NR_CLK 234 +#define TOP_NR_CLK 236 /* CMU_CPIF */ #define CLK_FOUT_MPHY_PLL 1 @@ -937,4 +941,39 @@ #define ATLAS_NR_CLK 40 +/* CMU_MSCL */ +#define CLK_MOUT_SCLK_JPEG_USER 1 +#define CLK_MOUT_ACLK_MSCL_400_USER 2 +#define CLK_MOUT_SCLK_JPEG 3 + +#define CLK_DIV_PCLK_MSCL 4 + +#define CLK_ACLK_BTS_JPEG 5 +#define CLK_ACLK_BTS_M2MSCALER1 6 +#define CLK_ACLK_BTS_M2MSCALER0 7 +#define CLK_ACLK_AHB2APB_MSCL0P 8 +#define CLK_ACLK_XIU_MSCLX 9 +#define CLK_ACLK_MSCLNP_100 10 +#define CLK_ACLK_MSCLND_400 11 +#define CLK_ACLK_JPEG 12 +#define CLK_ACLK_M2MSCALER1 13 +#define CLK_ACLK_M2MSCALER0 14 +#define CLK_ACLK_SMMU_M2MSCALER0 15 +#define CLK_ACLK_SMMU_M2MSCALER1 16 +#define CLK_ACLK_SMMU_JPEG 17 +#define CLK_PCLK_BTS_JPEG 18 +#define CLK_PCLK_BTS_M2MSCALER1 19 +#define CLK_PCLK_BTS_M2MSCALER0 20 +#define CLK_PCLK_PMU_MSCL 21 +#define CLK_PCLK_SYSREG_MSCL 22 +#define CLK_PCLK_JPEG 23 +#define CLK_PCLK_M2MSCALER1 24 +#define CLK_PCLK_M2MSCALER0 25 +#define CLK_PCLK_SMMU_M2MSCALER0 26 +#define CLK_PCLK_SMMU_M2MSCALER1 27 +#define CLK_PCLK_SMMU_JPEG 28 +#define CLK_SCLK_JPEG 29 + +#define MSCL_NR_CLK 30 + #endif /* _DT_BINDINGS_CLOCK_EXYNOS5433_H */ -- cgit v1.2.3 From 9910b6bbaa7b16cd3a8a7d8be53980fa1b8183a6 Mon Sep 17 00:00:00 2001 From: Chanwoo Choi Date: Tue, 3 Feb 2015 09:13:52 +0900 Subject: clk: samsung: exynos5433: Add clocks for CMU_MFC domain This patch adds the mux/divider/gate clocks for CMU_MFC domain which generates the clocks for MFC(Multi-Format Codec) IP. Signed-off-by: Chanwoo Choi Acked-by: Inki Dae Signed-off-by: Sylwester Nawrocki --- .../devicetree/bindings/clock/exynos5433-clock.txt | 15 +++ drivers/clk/samsung/clk-exynos5433.c | 113 +++++++++++++++++++++ include/dt-bindings/clock/exynos5433.h | 27 ++++- 3 files changed, 154 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/Documentation/devicetree/bindings/clock/exynos5433-clock.txt b/Documentation/devicetree/bindings/clock/exynos5433-clock.txt index ecb9534c2ea6..0f35167ec15c 100644 --- a/Documentation/devicetree/bindings/clock/exynos5433-clock.txt +++ b/Documentation/devicetree/bindings/clock/exynos5433-clock.txt @@ -39,6 +39,8 @@ Required Properties: L2 cache controller. - "samsung,exynos5433-cmu-mscl" - clock controller compatible for CMU_MSCL which generates clocks for M2M (Memory to Memory) scaler and JPEG IPs. + - "samsung,exynos5433-cmu-mfc" - clock controller compatible for CMU_MFC + which generates clocks for MFC(Multi-Format Codec) IP. - reg: physical base address of the controller and length of memory mapped region. @@ -125,6 +127,10 @@ Required Properties: - sclk_jpeg_mscl - aclk_mscl_400 + Input clocks for mfc clock controller: + - oscclk + - aclk_mfc_400 + Each clock is assigned an identifier and client nodes can use this identifier to specify the clock which they consume. @@ -340,6 +346,15 @@ Example 2: Examples of clock controller nodes are listed below. <&cmu_top CLK_ACLK_MSCL_400>; }; + cmu_mfc: clock-controller@15280000 { + compatible = "samsung,exynos5433-cmu-mfc"; + reg = <0x15280000 0x0b08>; + #clock-cells = <1>; + + clock-names = "oscclk", "aclk_mfc_400"; + clocks = <&xxti>, <&cmu_top CLK_ACLK_MFC_400>; + }; + Example 3: UART controller node that consumes the clock generated by the clock controller. diff --git a/drivers/clk/samsung/clk-exynos5433.c b/drivers/clk/samsung/clk-exynos5433.c index d272e42eb48c..40558930711f 100644 --- a/drivers/clk/samsung/clk-exynos5433.c +++ b/drivers/clk/samsung/clk-exynos5433.c @@ -560,6 +560,9 @@ static struct samsung_gate_clock top_gate_clks[] __initdata = { GATE(CLK_ACLK_GSCL_333, "aclk_gscl_333", "div_aclk_gscl_333", ENABLE_ACLK_TOP, 14, CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_MFC_400, "aclk_mfc_400", "div_aclk_mfc_400", + ENABLE_ACLK_TOP, 3, + CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED, 0), GATE(CLK_ACLK_G2D_266, "aclk_g2d_266", "div_aclk_g2d_266", ENABLE_ACLK_TOP, 2, CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED, 0), @@ -3990,3 +3993,113 @@ static void __init exynos5433_cmu_mscl_init(struct device_node *np) } CLK_OF_DECLARE(exynos5433_cmu_mscl, "samsung,exynos5433-cmu-mscl", exynos5433_cmu_mscl_init); + +/* + * Register offset definitions for CMU_MFC + */ +#define MUX_SEL_MFC 0x0200 +#define MUX_ENABLE_MFC 0x0300 +#define MUX_STAT_MFC 0x0400 +#define DIV_MFC 0x0600 +#define DIV_STAT_MFC 0x0700 +#define ENABLE_ACLK_MFC 0x0800 +#define ENABLE_ACLK_MFC_SECURE_SMMU_MFC 0x0804 +#define ENABLE_PCLK_MFC 0x0900 +#define ENABLE_PCLK_MFC_SECURE_SMMU_MFC 0x0904 +#define ENABLE_IP_MFC0 0x0b00 +#define ENABLE_IP_MFC1 0x0b04 +#define ENABLE_IP_MFC_SECURE_SMMU_MFC 0x0b08 + +static unsigned long mfc_clk_regs[] __initdata = { + MUX_SEL_MFC, + MUX_ENABLE_MFC, + MUX_STAT_MFC, + DIV_MFC, + DIV_STAT_MFC, + ENABLE_ACLK_MFC, + ENABLE_ACLK_MFC_SECURE_SMMU_MFC, + ENABLE_PCLK_MFC, + ENABLE_PCLK_MFC_SECURE_SMMU_MFC, + ENABLE_IP_MFC0, + ENABLE_IP_MFC1, + ENABLE_IP_MFC_SECURE_SMMU_MFC, +}; + +PNAME(mout_aclk_mfc_400_user_p) = { "oscclk", "aclk_mfc_400", }; + +static struct samsung_mux_clock mfc_mux_clks[] __initdata = { + /* MUX_SEL_MFC */ + MUX(CLK_MOUT_ACLK_MFC_400_USER, "mout_aclk_mfc_400_user", + mout_aclk_mfc_400_user_p, MUX_SEL_MFC, 0, 0), +}; + +static struct samsung_div_clock mfc_div_clks[] __initdata = { + /* DIV_MFC */ + DIV(CLK_DIV_PCLK_MFC, "div_pclk_mfc", "mout_aclk_mfc_400_user", + DIV_MFC, 0, 2), +}; + +static struct samsung_gate_clock mfc_gate_clks[] __initdata = { + /* ENABLE_ACLK_MFC */ + GATE(CLK_ACLK_BTS_MFC_1, "aclk_bts_mfc_1", "mout_aclk_mfc_400_user", + ENABLE_ACLK_MFC, 6, 0, 0), + GATE(CLK_ACLK_BTS_MFC_0, "aclk_bts_mfc_0", "mout_aclk_mfc_400_user", + ENABLE_ACLK_MFC, 5, 0, 0), + GATE(CLK_ACLK_AHB2APB_MFCP, "aclk_ahb2apb_mfcp", "div_pclk_mfc", + ENABLE_ACLK_MFC, 4, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_XIU_MFCX, "aclk_xiu_mfcx", "mout_aclk_mfc_400_user", + ENABLE_ACLK_MFC, 3, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_MFCNP_100, "aclk_mfcnp_100", "div_pclk_mfc", + ENABLE_ACLK_MFC, 2, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_MFCND_400, "aclk_mfcnd_400", "mout_aclk_mfc_400_user", + ENABLE_ACLK_MFC, 1, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_MFC, "aclk_mfc", "mout_aclk_mfc_400_user", + ENABLE_ACLK_MFC, 0, 0, 0), + + /* ENABLE_ACLK_MFC_SECURE_SMMU_MFC */ + GATE(CLK_ACLK_SMMU_MFC_1, "aclk_smmu_mfc_1", "mout_aclk_mfc_400_user", + ENABLE_ACLK_MFC_SECURE_SMMU_MFC, + 1, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_SMMU_MFC_0, "aclk_smmu_mfc_0", "mout_aclk_mfc_400_user", + ENABLE_ACLK_MFC_SECURE_SMMU_MFC, + 0, CLK_IGNORE_UNUSED, 0), + + /* ENABLE_PCLK_MFC */ + GATE(CLK_PCLK_BTS_MFC_1, "pclk_bts_mfc_1", "div_pclk_mfc", + ENABLE_PCLK_MFC, 4, 0, 0), + GATE(CLK_PCLK_BTS_MFC_0, "pclk_bts_mfc_0", "div_pclk_mfc", + ENABLE_PCLK_MFC, 3, 0, 0), + GATE(CLK_PCLK_PMU_MFC, "pclk_pmu_mfc", "div_pclk_mfc", + ENABLE_PCLK_MFC, 2, CLK_IGNORE_UNUSED, 0), + GATE(CLK_PCLK_SYSREG_MFC, "pclk_sysreg_mfc", "div_pclk_mfc", + ENABLE_PCLK_MFC, 1, CLK_IGNORE_UNUSED, 0), + GATE(CLK_PCLK_MFC, "pclk_mfc", "div_pclk_mfc", + ENABLE_PCLK_MFC, 4, CLK_IGNORE_UNUSED, 0), + + /* ENABLE_PCLK_MFC_SECURE_SMMU_MFC */ + GATE(CLK_PCLK_SMMU_MFC_1, "pclk_smmu_mfc_1", "div_pclk_mfc", + ENABLE_PCLK_MFC_SECURE_SMMU_MFC, + 1, CLK_IGNORE_UNUSED, 0), + GATE(CLK_PCLK_SMMU_MFC_0, "pclk_smmu_mfc_0", "div_pclk_mfc", + ENABLE_PCLK_MFC_SECURE_SMMU_MFC, + 0, CLK_IGNORE_UNUSED, 0), +}; + +static struct samsung_cmu_info mfc_cmu_info __initdata = { + .mux_clks = mfc_mux_clks, + .nr_mux_clks = ARRAY_SIZE(mfc_mux_clks), + .div_clks = mfc_div_clks, + .nr_div_clks = ARRAY_SIZE(mfc_div_clks), + .gate_clks = mfc_gate_clks, + .nr_gate_clks = ARRAY_SIZE(mfc_gate_clks), + .nr_clk_ids = MFC_NR_CLK, + .clk_regs = mfc_clk_regs, + .nr_clk_regs = ARRAY_SIZE(mfc_clk_regs), +}; + +static void __init exynos5433_cmu_mfc_init(struct device_node *np) +{ + samsung_cmu_register_one(np, &mfc_cmu_info); +} +CLK_OF_DECLARE(exynos5433_cmu_mfc, "samsung,exynos5433-cmu-mfc", + exynos5433_cmu_mfc_init); diff --git a/include/dt-bindings/clock/exynos5433.h b/include/dt-bindings/clock/exynos5433.h index 9898390710e6..3301ab72c80d 100644 --- a/include/dt-bindings/clock/exynos5433.h +++ b/include/dt-bindings/clock/exynos5433.h @@ -153,8 +153,9 @@ #define CLK_ACLK_GSCL_333 233 #define CLK_SCLK_JPEG_MSCL 234 #define CLK_ACLK_MSCL_400 235 +#define CLK_ACLK_MFC_400 236 -#define TOP_NR_CLK 236 +#define TOP_NR_CLK 237 /* CMU_CPIF */ #define CLK_FOUT_MPHY_PLL 1 @@ -976,4 +977,28 @@ #define MSCL_NR_CLK 30 +/* CMU_MFC */ +#define CLK_MOUT_ACLK_MFC_400_USER 1 + +#define CLK_DIV_PCLK_MFC 2 + +#define CLK_ACLK_BTS_MFC_1 3 +#define CLK_ACLK_BTS_MFC_0 4 +#define CLK_ACLK_AHB2APB_MFCP 5 +#define CLK_ACLK_XIU_MFCX 6 +#define CLK_ACLK_MFCNP_100 7 +#define CLK_ACLK_MFCND_400 8 +#define CLK_ACLK_MFC 9 +#define CLK_ACLK_SMMU_MFC_1 10 +#define CLK_ACLK_SMMU_MFC_0 11 +#define CLK_PCLK_BTS_MFC_1 12 +#define CLK_PCLK_BTS_MFC_0 13 +#define CLK_PCLK_PMU_MFC 14 +#define CLK_PCLK_SYSREG_MFC 15 +#define CLK_PCLK_MFC 16 +#define CLK_PCLK_SMMU_MFC_1 17 +#define CLK_PCLK_SMMU_MFC_0 18 + +#define MFC_NR_CLK 19 + #endif /* _DT_BINDINGS_CLOCK_EXYNOS5433_H */ -- cgit v1.2.3 From 45e58aa5f751fd861d46f7b6d438c1be147458c6 Mon Sep 17 00:00:00 2001 From: Chanwoo Choi Date: Tue, 3 Feb 2015 09:13:53 +0900 Subject: clk: samsung: exynos5433: Add clocks for CMU_HEVC domain This patch adds the mux/divider/gate clocks for CMU_HEVC domain which generates the clocks for HEVC(High Efficiency Video Codec) decoder IP. Signed-off-by: Chanwoo Choi Acked-by: Inki Dae Signed-off-by: Sylwester Nawrocki --- .../devicetree/bindings/clock/exynos5433-clock.txt | 15 +++ drivers/clk/samsung/clk-exynos5433.c | 115 +++++++++++++++++++++ include/dt-bindings/clock/exynos5433.h | 27 ++++- 3 files changed, 156 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/Documentation/devicetree/bindings/clock/exynos5433-clock.txt b/Documentation/devicetree/bindings/clock/exynos5433-clock.txt index 0f35167ec15c..acc1d8aac95c 100644 --- a/Documentation/devicetree/bindings/clock/exynos5433-clock.txt +++ b/Documentation/devicetree/bindings/clock/exynos5433-clock.txt @@ -41,6 +41,8 @@ Required Properties: which generates clocks for M2M (Memory to Memory) scaler and JPEG IPs. - "samsung,exynos5433-cmu-mfc" - clock controller compatible for CMU_MFC which generates clocks for MFC(Multi-Format Codec) IP. + - "samsung,exynos5433-cmu-hevc" - clock controller compatible for CMU_HEVC + which generates clocks for HEVC(High Efficiency Video Codec) decoder IP. - reg: physical base address of the controller and length of memory mapped region. @@ -131,6 +133,10 @@ Required Properties: - oscclk - aclk_mfc_400 + Input clocks for hevc clock controller: + - oscclk + - aclk_hevc_400 + Each clock is assigned an identifier and client nodes can use this identifier to specify the clock which they consume. @@ -355,6 +361,15 @@ Example 2: Examples of clock controller nodes are listed below. clocks = <&xxti>, <&cmu_top CLK_ACLK_MFC_400>; }; + cmu_hevc: clock-controller@14f80000 { + compatible = "samsung,exynos5433-cmu-hevc"; + reg = <0x14f80000 0x0b08>; + #clock-cells = <1>; + + clock-names = "oscclk", "aclk_hevc_400"; + clocks = <&xxti>, <&cmu_top CLK_ACLK_HEVC_400>; + }; + Example 3: UART controller node that consumes the clock generated by the clock controller. diff --git a/drivers/clk/samsung/clk-exynos5433.c b/drivers/clk/samsung/clk-exynos5433.c index 40558930711f..482a603c7e6b 100644 --- a/drivers/clk/samsung/clk-exynos5433.c +++ b/drivers/clk/samsung/clk-exynos5433.c @@ -560,6 +560,9 @@ static struct samsung_gate_clock top_gate_clks[] __initdata = { GATE(CLK_ACLK_GSCL_333, "aclk_gscl_333", "div_aclk_gscl_333", ENABLE_ACLK_TOP, 14, CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_HEVC_400, "aclk_hevc_400", "div_aclk_hevc_400", + ENABLE_ACLK_TOP, 5, + CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED, 0), GATE(CLK_ACLK_MFC_400, "aclk_mfc_400", "div_aclk_mfc_400", ENABLE_ACLK_TOP, 3, CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED, 0), @@ -4103,3 +4106,115 @@ static void __init exynos5433_cmu_mfc_init(struct device_node *np) } CLK_OF_DECLARE(exynos5433_cmu_mfc, "samsung,exynos5433-cmu-mfc", exynos5433_cmu_mfc_init); + +/* + * Register offset definitions for CMU_HEVC + */ +#define MUX_SEL_HEVC 0x0200 +#define MUX_ENABLE_HEVC 0x0300 +#define MUX_STAT_HEVC 0x0400 +#define DIV_HEVC 0x0600 +#define DIV_STAT_HEVC 0x0700 +#define ENABLE_ACLK_HEVC 0x0800 +#define ENABLE_ACLK_HEVC_SECURE_SMMU_HEVC 0x0804 +#define ENABLE_PCLK_HEVC 0x0900 +#define ENABLE_PCLK_HEVC_SECURE_SMMU_HEVC 0x0904 +#define ENABLE_IP_HEVC0 0x0b00 +#define ENABLE_IP_HEVC1 0x0b04 +#define ENABLE_IP_HEVC_SECURE_SMMU_HEVC 0x0b08 + +static unsigned long hevc_clk_regs[] __initdata = { + MUX_SEL_HEVC, + MUX_ENABLE_HEVC, + MUX_STAT_HEVC, + DIV_HEVC, + DIV_STAT_HEVC, + ENABLE_ACLK_HEVC, + ENABLE_ACLK_HEVC_SECURE_SMMU_HEVC, + ENABLE_PCLK_HEVC, + ENABLE_PCLK_HEVC_SECURE_SMMU_HEVC, + ENABLE_IP_HEVC0, + ENABLE_IP_HEVC1, + ENABLE_IP_HEVC_SECURE_SMMU_HEVC, +}; + +PNAME(mout_aclk_hevc_400_user_p) = { "oscclk", "aclk_hevc_400", }; + +static struct samsung_mux_clock hevc_mux_clks[] __initdata = { + /* MUX_SEL_HEVC */ + MUX(CLK_MOUT_ACLK_HEVC_400_USER, "mout_aclk_hevc_400_user", + mout_aclk_hevc_400_user_p, MUX_SEL_HEVC, 0, 0), +}; + +static struct samsung_div_clock hevc_div_clks[] __initdata = { + /* DIV_HEVC */ + DIV(CLK_DIV_PCLK_HEVC, "div_pclk_hevc", "mout_aclk_hevc_400_user", + DIV_HEVC, 0, 2), +}; + +static struct samsung_gate_clock hevc_gate_clks[] __initdata = { + /* ENABLE_ACLK_HEVC */ + GATE(CLK_ACLK_BTS_HEVC_1, "aclk_bts_hevc_1", "mout_aclk_hevc_400_user", + ENABLE_ACLK_HEVC, 6, 0, 0), + GATE(CLK_ACLK_BTS_HEVC_0, "aclk_bts_hevc_0", "mout_aclk_hevc_400_user", + ENABLE_ACLK_HEVC, 5, 0, 0), + GATE(CLK_ACLK_AHB2APB_HEVCP, "aclk_ahb2apb_hevcp", "div_pclk_hevc", + ENABLE_ACLK_HEVC, 4, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_XIU_HEVCX, "aclk_xiu_hevcx", "mout_aclk_hevc_400_user", + ENABLE_ACLK_HEVC, 3, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_HEVCNP_100, "aclk_hevcnp_100", "div_pclk_hevc", + ENABLE_ACLK_HEVC, 2, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_HEVCND_400, "aclk_hevcnd_400", "mout_aclk_hevc_400_user", + ENABLE_ACLK_HEVC, 1, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_HEVC, "aclk_hevc", "mout_aclk_hevc_400_user", + ENABLE_ACLK_HEVC, 0, 0, 0), + + /* ENABLE_ACLK_HEVC_SECURE_SMMU_HEVC */ + GATE(CLK_ACLK_SMMU_HEVC_1, "aclk_smmu_hevc_1", + "mout_aclk_hevc_400_user", + ENABLE_ACLK_HEVC_SECURE_SMMU_HEVC, + 1, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_SMMU_HEVC_0, "aclk_smmu_hevc_0", + "mout_aclk_hevc_400_user", + ENABLE_ACLK_HEVC_SECURE_SMMU_HEVC, + 0, CLK_IGNORE_UNUSED, 0), + + /* ENABLE_PCLK_HEVC */ + GATE(CLK_PCLK_BTS_HEVC_1, "pclk_bts_hevc_1", "div_pclk_hevc", + ENABLE_PCLK_HEVC, 4, 0, 0), + GATE(CLK_PCLK_BTS_HEVC_0, "pclk_bts_hevc_0", "div_pclk_hevc", + ENABLE_PCLK_HEVC, 3, 0, 0), + GATE(CLK_PCLK_PMU_HEVC, "pclk_pmu_hevc", "div_pclk_hevc", + ENABLE_PCLK_HEVC, 2, CLK_IGNORE_UNUSED, 0), + GATE(CLK_PCLK_SYSREG_HEVC, "pclk_sysreg_hevc", "div_pclk_hevc", + ENABLE_PCLK_HEVC, 1, CLK_IGNORE_UNUSED, 0), + GATE(CLK_PCLK_HEVC, "pclk_hevc", "div_pclk_hevc", + ENABLE_PCLK_HEVC, 4, CLK_IGNORE_UNUSED, 0), + + /* ENABLE_PCLK_HEVC_SECURE_SMMU_HEVC */ + GATE(CLK_PCLK_SMMU_HEVC_1, "pclk_smmu_hevc_1", "div_pclk_hevc", + ENABLE_PCLK_HEVC_SECURE_SMMU_HEVC, + 1, CLK_IGNORE_UNUSED, 0), + GATE(CLK_PCLK_SMMU_HEVC_0, "pclk_smmu_hevc_0", "div_pclk_hevc", + ENABLE_PCLK_HEVC_SECURE_SMMU_HEVC, + 0, CLK_IGNORE_UNUSED, 0), +}; + +static struct samsung_cmu_info hevc_cmu_info __initdata = { + .mux_clks = hevc_mux_clks, + .nr_mux_clks = ARRAY_SIZE(hevc_mux_clks), + .div_clks = hevc_div_clks, + .nr_div_clks = ARRAY_SIZE(hevc_div_clks), + .gate_clks = hevc_gate_clks, + .nr_gate_clks = ARRAY_SIZE(hevc_gate_clks), + .nr_clk_ids = HEVC_NR_CLK, + .clk_regs = hevc_clk_regs, + .nr_clk_regs = ARRAY_SIZE(hevc_clk_regs), +}; + +static void __init exynos5433_cmu_hevc_init(struct device_node *np) +{ + samsung_cmu_register_one(np, &hevc_cmu_info); +} +CLK_OF_DECLARE(exynos5433_cmu_hevc, "samsung,exynos5433-cmu-hevc", + exynos5433_cmu_hevc_init); diff --git a/include/dt-bindings/clock/exynos5433.h b/include/dt-bindings/clock/exynos5433.h index 3301ab72c80d..1b2d333c1786 100644 --- a/include/dt-bindings/clock/exynos5433.h +++ b/include/dt-bindings/clock/exynos5433.h @@ -154,8 +154,9 @@ #define CLK_SCLK_JPEG_MSCL 234 #define CLK_ACLK_MSCL_400 235 #define CLK_ACLK_MFC_400 236 +#define CLK_ACLK_HEVC_400 237 -#define TOP_NR_CLK 237 +#define TOP_NR_CLK 238 /* CMU_CPIF */ #define CLK_FOUT_MPHY_PLL 1 @@ -1001,4 +1002,28 @@ #define MFC_NR_CLK 19 +/* CMU_HEVC */ +#define CLK_MOUT_ACLK_HEVC_400_USER 1 + +#define CLK_DIV_PCLK_HEVC 2 + +#define CLK_ACLK_BTS_HEVC_1 3 +#define CLK_ACLK_BTS_HEVC_0 4 +#define CLK_ACLK_AHB2APB_HEVCP 5 +#define CLK_ACLK_XIU_HEVCX 6 +#define CLK_ACLK_HEVCNP_100 7 +#define CLK_ACLK_HEVCND_400 8 +#define CLK_ACLK_HEVC 9 +#define CLK_ACLK_SMMU_HEVC_1 10 +#define CLK_ACLK_SMMU_HEVC_0 11 +#define CLK_PCLK_BTS_HEVC_1 12 +#define CLK_PCLK_BTS_HEVC_0 13 +#define CLK_PCLK_PMU_HEVC 14 +#define CLK_PCLK_SYSREG_HEVC 15 +#define CLK_PCLK_HEVC 16 +#define CLK_PCLK_SMMU_HEVC_1 17 +#define CLK_PCLK_SMMU_HEVC_0 18 + +#define HEVC_NR_CLK 19 + #endif /* _DT_BINDINGS_CLOCK_EXYNOS5433_H */ -- cgit v1.2.3 From 8e46c4b84faf317773d5a4ec6d807ceae2d0eb41 Mon Sep 17 00:00:00 2001 From: Chanwoo Choi Date: Tue, 3 Feb 2015 09:13:54 +0900 Subject: clk: samsung: exynos5433: Add clocks for CMU_ISP domain This patch adds the mux/divider/gate clocks for CMU_ISP domain which generates the clocks for FIMC-ISP/DRC/SCLC/DIS/3DNR IPs. Signed-off-by: Chanwoo Choi Acked-by: Inki Dae Signed-off-by: Sylwester Nawrocki --- .../devicetree/bindings/clock/exynos5433-clock.txt | 20 ++ drivers/clk/samsung/clk-exynos5433.c | 267 +++++++++++++++++++++ include/dt-bindings/clock/exynos5433.h | 89 ++++++- 3 files changed, 375 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/Documentation/devicetree/bindings/clock/exynos5433-clock.txt b/Documentation/devicetree/bindings/clock/exynos5433-clock.txt index acc1d8aac95c..4f3bfcd366a3 100644 --- a/Documentation/devicetree/bindings/clock/exynos5433-clock.txt +++ b/Documentation/devicetree/bindings/clock/exynos5433-clock.txt @@ -43,6 +43,8 @@ Required Properties: which generates clocks for MFC(Multi-Format Codec) IP. - "samsung,exynos5433-cmu-hevc" - clock controller compatible for CMU_HEVC which generates clocks for HEVC(High Efficiency Video Codec) decoder IP. + - "samsung,exynos5433-cmu-isp" - clock controller compatible for CMU_ISP + which generates clocks for FIMC-ISP/DRC/SCLC/DIS/3DNR IPs. - reg: physical base address of the controller and length of memory mapped region. @@ -137,6 +139,11 @@ Required Properties: - oscclk - aclk_hevc_400 + Input clocks for isp clock controller: + - oscclk + - aclk_isp_dis_400 + - aclk_isp_400 + Each clock is assigned an identifier and client nodes can use this identifier to specify the clock which they consume. @@ -370,6 +377,19 @@ Example 2: Examples of clock controller nodes are listed below. clocks = <&xxti>, <&cmu_top CLK_ACLK_HEVC_400>; }; + cmu_isp: clock-controller@146d0000 { + compatible = "samsung,exynos5433-cmu-isp"; + reg = <0x146d0000 0x0b0c>; + #clock-cells = <1>; + + clock-names = "oscclk", + "aclk_isp_dis_400", + "aclk_isp_400"; + clocks = <&xxti>, + <&cmu_top CLK_ACLK_ISP_DIS_400>, + <&cmu_top CLK_ACLK_ISP_400>; + }; + Example 3: UART controller node that consumes the clock generated by the clock controller. diff --git a/drivers/clk/samsung/clk-exynos5433.c b/drivers/clk/samsung/clk-exynos5433.c index 482a603c7e6b..a8ea6e1fbffc 100644 --- a/drivers/clk/samsung/clk-exynos5433.c +++ b/drivers/clk/samsung/clk-exynos5433.c @@ -404,6 +404,12 @@ static struct samsung_mux_clock top_mux_clks[] __initdata = { }; static struct samsung_div_clock top_div_clks[] __initdata = { + /* DIV_TOP0 */ + DIV(CLK_DIV_ACLK_ISP_DIS_400, "div_aclk_isp_dis_400", + "mout_aclk_isp_dis_400", DIV_TOP0, 4, 4), + DIV(CLK_DIV_ACLK_ISP_400, "div_aclk_isp_400", + "mout_aclk_isp_400", DIV_TOP0, 0, 4), + /* DIV_TOP1 */ DIV(CLK_DIV_ACLK_GSCL_111, "div_aclk_gscl_111", "mout_aclk_gscl_333", DIV_TOP1, 28, 3), @@ -560,6 +566,12 @@ static struct samsung_gate_clock top_gate_clks[] __initdata = { GATE(CLK_ACLK_GSCL_333, "aclk_gscl_333", "div_aclk_gscl_333", ENABLE_ACLK_TOP, 14, CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_ISP_DIS_400, "aclk_isp_dis_400", "div_aclk_isp_dis_400", + ENABLE_ACLK_TOP, 7, + CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_ISP_400, "aclk_isp_400", "div_aclk_isp_400", + ENABLE_ACLK_TOP, 6, + CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED, 0), GATE(CLK_ACLK_HEVC_400, "aclk_hevc_400", "div_aclk_hevc_400", ENABLE_ACLK_TOP, 5, CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED, 0), @@ -4218,3 +4230,258 @@ static void __init exynos5433_cmu_hevc_init(struct device_node *np) } CLK_OF_DECLARE(exynos5433_cmu_hevc, "samsung,exynos5433-cmu-hevc", exynos5433_cmu_hevc_init); + +/* + * Register offset definitions for CMU_ISP + */ +#define MUX_SEL_ISP 0x0200 +#define MUX_ENABLE_ISP 0x0300 +#define MUX_STAT_ISP 0x0400 +#define DIV_ISP 0x0600 +#define DIV_STAT_ISP 0x0700 +#define ENABLE_ACLK_ISP0 0x0800 +#define ENABLE_ACLK_ISP1 0x0804 +#define ENABLE_ACLK_ISP2 0x0808 +#define ENABLE_PCLK_ISP 0x0900 +#define ENABLE_SCLK_ISP 0x0a00 +#define ENABLE_IP_ISP0 0x0b00 +#define ENABLE_IP_ISP1 0x0b04 +#define ENABLE_IP_ISP2 0x0b08 +#define ENABLE_IP_ISP3 0x0b0c + +static unsigned long isp_clk_regs[] __initdata = { + MUX_SEL_ISP, + MUX_ENABLE_ISP, + MUX_STAT_ISP, + DIV_ISP, + DIV_STAT_ISP, + ENABLE_ACLK_ISP0, + ENABLE_ACLK_ISP1, + ENABLE_ACLK_ISP2, + ENABLE_PCLK_ISP, + ENABLE_SCLK_ISP, + ENABLE_IP_ISP0, + ENABLE_IP_ISP1, + ENABLE_IP_ISP2, + ENABLE_IP_ISP3, +}; + +PNAME(mout_aclk_isp_dis_400_user_p) = { "oscclk", "aclk_isp_dis_400", }; +PNAME(mout_aclk_isp_400_user_p) = { "oscclk", "aclk_isp_400", }; + +static struct samsung_mux_clock isp_mux_clks[] __initdata = { + /* MUX_SEL_ISP */ + MUX(CLK_MOUT_ACLK_ISP_DIS_400_USER, "mout_aclk_isp_dis_400_user", + mout_aclk_isp_dis_400_user_p, MUX_SEL_ISP, 4, 0), + MUX(CLK_MOUT_ACLK_ISP_400_USER, "mout_aclk_isp_400_user", + mout_aclk_isp_400_user_p, MUX_SEL_ISP, 0, 0), +}; + +static struct samsung_div_clock isp_div_clks[] __initdata = { + /* DIV_ISP */ + DIV(CLK_DIV_PCLK_ISP_DIS, "div_pclk_isp_dis", + "mout_aclk_isp_dis_400_user", DIV_ISP, 12, 3), + DIV(CLK_DIV_PCLK_ISP, "div_pclk_isp", "mout_aclk_isp_400_user", + DIV_ISP, 8, 3), + DIV(CLK_DIV_ACLK_ISP_D_200, "div_aclk_isp_d_200", + "mout_aclk_isp_400_user", DIV_ISP, 4, 3), + DIV(CLK_DIV_ACLK_ISP_C_200, "div_aclk_isp_c_200", + "mout_aclk_isp_400_user", DIV_ISP, 0, 3), +}; + +static struct samsung_gate_clock isp_gate_clks[] __initdata = { + /* ENABLE_ACLK_ISP0 */ + GATE(CLK_ACLK_ISP_D_GLUE, "aclk_isp_d_glue", "mout_aclk_isp_400_user", + ENABLE_ACLK_ISP0, 6, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_SCALERP, "aclk_scalerp", "mout_aclk_isp_400_user", + ENABLE_ACLK_ISP0, 5, 0, 0), + GATE(CLK_ACLK_3DNR, "aclk_3dnr", "mout_aclk_isp_400_user", + ENABLE_ACLK_ISP0, 4, 0, 0), + GATE(CLK_ACLK_DIS, "aclk_dis", "mout_aclk_isp_dis_400_user", + ENABLE_ACLK_ISP0, 3, 0, 0), + GATE(CLK_ACLK_SCALERC, "aclk_scalerc", "mout_aclk_isp_400_user", + ENABLE_ACLK_ISP0, 2, 0, 0), + GATE(CLK_ACLK_DRC, "aclk_drc", "mout_aclk_isp_400_user", + ENABLE_ACLK_ISP0, 1, 0, 0), + GATE(CLK_ACLK_ISP, "aclk_isp", "mout_aclk_isp_400_user", + ENABLE_ACLK_ISP0, 0, 0, 0), + + /* ENABLE_ACLK_ISP1 */ + GATE(CLK_ACLK_AXIUS_SCALERP, "aclk_axius_scalerp", + "mout_aclk_isp_400_user", ENABLE_ACLK_ISP1, + 17, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_AXIUS_SCALERC, "aclk_axius_scalerc", + "mout_aclk_isp_400_user", ENABLE_ACLK_ISP1, + 16, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_AXIUS_DRC, "aclk_axius_drc", + "mout_aclk_isp_400_user", ENABLE_ACLK_ISP1, + 15, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_ASYNCAHBM_ISP2P, "aclk_asyncahbm_isp2p", + "div_pclk_isp", ENABLE_ACLK_ISP1, + 14, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_ASYNCAHBM_ISP1P, "aclk_asyncahbm_isp1p", + "div_pclk_isp", ENABLE_ACLK_ISP1, + 13, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_ASYNCAXIS_DIS1, "aclk_asyncaxis_dis1", + "mout_aclk_isp_dis_400_user", ENABLE_ACLK_ISP1, + 12, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_ASYNCAXIS_DIS0, "aclk_asyncaxis_dis0", + "mout_aclk_isp_dis_400_user", ENABLE_ACLK_ISP1, + 11, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_ASYNCAXIM_DIS1, "aclk_asyncaxim_dis1", + "mout_aclk_isp_400_user", ENABLE_ACLK_ISP1, + 10, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_ASYNCAXIM_DIS0, "aclk_asyncaxim_dis0", + "mout_aclk_isp_400_user", ENABLE_ACLK_ISP1, + 9, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_ASYNCAXIM_ISP2P, "aclk_asyncaxim_isp2p", + "div_aclk_isp_d_200", ENABLE_ACLK_ISP1, + 8, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_ASYNCAXIM_ISP1P, "aclk_asyncaxim_isp1p", + "div_aclk_isp_c_200", ENABLE_ACLK_ISP1, + 7, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_AHB2APB_ISP2P, "aclk_ahb2apb_isp2p", "div_pclk_isp", + ENABLE_ACLK_ISP1, 6, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_AHB2APB_ISP1P, "aclk_ahb2apb_isp1p", "div_pclk_isp", + ENABLE_ACLK_ISP1, 5, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_AXI2APB_ISP2P, "aclk_axi2apb_isp2p", + "div_aclk_isp_d_200", ENABLE_ACLK_ISP1, + 4, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_AXI2APB_ISP1P, "aclk_axi2apb_isp1p", + "div_aclk_isp_c_200", ENABLE_ACLK_ISP1, + 3, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_XIU_ISPEX1, "aclk_xiu_ispex1", "mout_aclk_isp_400_user", + ENABLE_ACLK_ISP1, 2, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_XIU_ISPEX0, "aclk_xiu_ispex0", "mout_aclk_isp_400_user", + ENABLE_ACLK_ISP1, 1, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_ISPND_400, "aclk_ispnd_400", "mout_aclk_isp_400_user", + ENABLE_ACLK_ISP1, 1, CLK_IGNORE_UNUSED, 0), + + /* ENABLE_ACLK_ISP2 */ + GATE(CLK_ACLK_SMMU_SCALERP, "aclk_smmu_scalerp", + "mout_aclk_isp_400_user", ENABLE_ACLK_ISP2, + 13, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_SMMU_3DNR, "aclk_smmu_3dnr", "mout_aclk_isp_400_user", + ENABLE_ACLK_ISP2, 12, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_SMMU_DIS1, "aclk_smmu_dis1", "mout_aclk_isp_400_user", + ENABLE_ACLK_ISP2, 11, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_SMMU_DIS0, "aclk_smmu_dis0", "mout_aclk_isp_400_user", + ENABLE_ACLK_ISP2, 10, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_SMMU_SCALERC, "aclk_smmu_scalerc", + "mout_aclk_isp_400_user", ENABLE_ACLK_ISP2, + 9, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_SMMU_DRC, "aclk_smmu_drc", "mout_aclk_isp_400_user", + ENABLE_ACLK_ISP2, 8, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_SMMU_ISP, "aclk_smmu_isp", "mout_aclk_isp_400_user", + ENABLE_ACLK_ISP2, 7, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_BTS_SCALERP, "aclk_bts_scalerp", + "mout_aclk_isp_400_user", ENABLE_ACLK_ISP2, + 6, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_BTS_3DR, "aclk_bts_3dnr", "mout_aclk_isp_400_user", + ENABLE_ACLK_ISP2, 5, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_BTS_DIS1, "aclk_bts_dis1", "mout_aclk_isp_400_user", + ENABLE_ACLK_ISP2, 4, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_BTS_DIS0, "aclk_bts_dis0", "mout_aclk_isp_400_user", + ENABLE_ACLK_ISP2, 3, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_BTS_SCALERC, "aclk_bts_scalerc", + "mout_aclk_isp_400_user", ENABLE_ACLK_ISP2, + 2, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_BTS_DRC, "aclk_bts_drc", "mout_aclk_isp_400_user", + ENABLE_ACLK_ISP2, 1, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_BTS_ISP, "aclk_bts_isp", "mout_aclk_isp_400_user", + ENABLE_ACLK_ISP2, 0, CLK_IGNORE_UNUSED, 0), + + /* ENABLE_PCLK_ISP */ + GATE(CLK_PCLK_SMMU_SCALERP, "pclk_smmu_scalerp", "div_aclk_isp_d_200", + ENABLE_PCLK_ISP, 25, CLK_IGNORE_UNUSED, 0), + GATE(CLK_PCLK_SMMU_3DNR, "pclk_smmu_3dnr", "div_aclk_isp_d_200", + ENABLE_PCLK_ISP, 24, CLK_IGNORE_UNUSED, 0), + GATE(CLK_PCLK_SMMU_DIS1, "pclk_smmu_dis1", "div_aclk_isp_d_200", + ENABLE_PCLK_ISP, 23, CLK_IGNORE_UNUSED, 0), + GATE(CLK_PCLK_SMMU_DIS0, "pclk_smmu_dis0", "div_aclk_isp_d_200", + ENABLE_PCLK_ISP, 22, CLK_IGNORE_UNUSED, 0), + GATE(CLK_PCLK_SMMU_SCALERC, "pclk_smmu_scalerc", "div_aclk_isp_c_200", + ENABLE_PCLK_ISP, 21, CLK_IGNORE_UNUSED, 0), + GATE(CLK_PCLK_SMMU_DRC, "pclk_smmu_drc", "div_aclk_isp_c_200", + ENABLE_PCLK_ISP, 20, CLK_IGNORE_UNUSED, 0), + GATE(CLK_PCLK_SMMU_ISP, "pclk_smmu_isp", "div_aclk_isp_c_200", + ENABLE_PCLK_ISP, 19, CLK_IGNORE_UNUSED, 0), + GATE(CLK_PCLK_BTS_SCALERP, "pclk_bts_scalerp", "div_pclk_isp", + ENABLE_PCLK_ISP, 18, CLK_IGNORE_UNUSED, 0), + GATE(CLK_PCLK_BTS_3DNR, "pclk_bts_3dnr", "div_pclk_isp", + ENABLE_PCLK_ISP, 17, CLK_IGNORE_UNUSED, 0), + GATE(CLK_PCLK_BTS_DIS1, "pclk_bts_dis1", "div_pclk_isp", + ENABLE_PCLK_ISP, 16, CLK_IGNORE_UNUSED, 0), + GATE(CLK_PCLK_BTS_DIS0, "pclk_bts_dis0", "div_pclk_isp", + ENABLE_PCLK_ISP, 15, CLK_IGNORE_UNUSED, 0), + GATE(CLK_PCLK_BTS_SCALERC, "pclk_bts_scalerc", "div_pclk_isp", + ENABLE_PCLK_ISP, 14, CLK_IGNORE_UNUSED, 0), + GATE(CLK_PCLK_BTS_DRC, "pclk_bts_drc", "div_pclk_isp", + ENABLE_PCLK_ISP, 13, CLK_IGNORE_UNUSED, 0), + GATE(CLK_PCLK_BTS_ISP, "pclk_bts_isp", "div_pclk_isp", + ENABLE_PCLK_ISP, 12, CLK_IGNORE_UNUSED, 0), + GATE(CLK_PCLK_ASYNCAXI_DIS1, "pclk_asyncaxi_dis1", "div_pclk_isp", + ENABLE_PCLK_ISP, 11, CLK_IGNORE_UNUSED, 0), + GATE(CLK_PCLK_ASYNCAXI_DIS0, "pclk_asyncaxi_dis0", "div_pclk_isp", + ENABLE_PCLK_ISP, 10, CLK_IGNORE_UNUSED, 0), + GATE(CLK_PCLK_PMU_ISP, "pclk_pmu_isp", "div_pclk_isp", + ENABLE_PCLK_ISP, 9, CLK_IGNORE_UNUSED, 0), + GATE(CLK_PCLK_SYSREG_ISP, "pclk_sysreg_isp", "div_pclk_isp", + ENABLE_PCLK_ISP, 8, CLK_IGNORE_UNUSED, 0), + GATE(CLK_PCLK_CMU_ISP_LOCAL, "pclk_cmu_isp_local", + "div_aclk_isp_c_200", ENABLE_PCLK_ISP, + 7, CLK_IGNORE_UNUSED, 0), + GATE(CLK_PCLK_SCALERP, "pclk_scalerp", "div_aclk_isp_d_200", + ENABLE_PCLK_ISP, 6, CLK_IGNORE_UNUSED, 0), + GATE(CLK_PCLK_3DNR, "pclk_3dnr", "div_aclk_isp_d_200", + ENABLE_PCLK_ISP, 5, CLK_IGNORE_UNUSED, 0), + GATE(CLK_PCLK_DIS_CORE, "pclk_dis_core", "div_pclk_isp_dis", + ENABLE_PCLK_ISP, 4, CLK_IGNORE_UNUSED, 0), + GATE(CLK_PCLK_DIS, "pclk_dis", "div_aclk_isp_d_200", + ENABLE_PCLK_ISP, 3, CLK_IGNORE_UNUSED, 0), + GATE(CLK_PCLK_SCALERC, "pclk_scalerc", "div_aclk_isp_c_200", + ENABLE_PCLK_ISP, 2, CLK_IGNORE_UNUSED, 0), + GATE(CLK_PCLK_DRC, "pclk_drc", "div_aclk_isp_c_200", + ENABLE_PCLK_ISP, 1, CLK_IGNORE_UNUSED, 0), + GATE(CLK_PCLK_ISP, "pclk_isp", "div_aclk_isp_c_200", + ENABLE_PCLK_ISP, 0, CLK_IGNORE_UNUSED, 0), + + /* ENABLE_SCLK_ISP */ + GATE(CLK_SCLK_PIXELASYNCS_DIS, "sclk_pixelasyncs_dis", + "mout_aclk_isp_dis_400_user", ENABLE_SCLK_ISP, + 5, CLK_IGNORE_UNUSED, 0), + GATE(CLK_SCLK_PIXELASYNCM_DIS, "sclk_pixelasyncm_dis", + "mout_aclk_isp_dis_400_user", ENABLE_SCLK_ISP, + 4, CLK_IGNORE_UNUSED, 0), + GATE(CLK_SCLK_PIXELASYNCS_SCALERP, "sclk_pixelasyncs_scalerp", + "mout_aclk_isp_400_user", ENABLE_SCLK_ISP, + 3, CLK_IGNORE_UNUSED, 0), + GATE(CLK_SCLK_PIXELASYNCM_ISPD, "sclk_pixelasyncm_ispd", + "mout_aclk_isp_400_user", ENABLE_SCLK_ISP, + 2, CLK_IGNORE_UNUSED, 0), + GATE(CLK_SCLK_PIXELASYNCS_ISPC, "sclk_pixelasyncs_ispc", + "mout_aclk_isp_400_user", ENABLE_SCLK_ISP, + 1, CLK_IGNORE_UNUSED, 0), + GATE(CLK_SCLK_PIXELASYNCM_ISPC, "sclk_pixelasyncm_ispc", + "mout_aclk_isp_400_user", ENABLE_SCLK_ISP, + 0, CLK_IGNORE_UNUSED, 0), +}; + +static struct samsung_cmu_info isp_cmu_info __initdata = { + .mux_clks = isp_mux_clks, + .nr_mux_clks = ARRAY_SIZE(isp_mux_clks), + .div_clks = isp_div_clks, + .nr_div_clks = ARRAY_SIZE(isp_div_clks), + .gate_clks = isp_gate_clks, + .nr_gate_clks = ARRAY_SIZE(isp_gate_clks), + .nr_clk_ids = ISP_NR_CLK, + .clk_regs = isp_clk_regs, + .nr_clk_regs = ARRAY_SIZE(isp_clk_regs), +}; + +static void __init exynos5433_cmu_isp_init(struct device_node *np) +{ + samsung_cmu_register_one(np, &isp_cmu_info); +} +CLK_OF_DECLARE(exynos5433_cmu_isp, "samsung,exynos5433-cmu-isp", + exynos5433_cmu_isp_init); diff --git a/include/dt-bindings/clock/exynos5433.h b/include/dt-bindings/clock/exynos5433.h index 1b2d333c1786..fbc81e3424a6 100644 --- a/include/dt-bindings/clock/exynos5433.h +++ b/include/dt-bindings/clock/exynos5433.h @@ -116,6 +116,8 @@ #define CLK_DIV_SCLK_USBDRD30 143 #define CLK_DIV_SCLK_JPEG 144 #define CLK_DIV_ACLK_MSCL_400 145 +#define CLK_DIV_ACLK_ISP_DIS_400 146 +#define CLK_DIV_ACLK_ISP_400 147 #define CLK_ACLK_PERIC_66 200 #define CLK_ACLK_PERIS_66 201 @@ -155,8 +157,10 @@ #define CLK_ACLK_MSCL_400 235 #define CLK_ACLK_MFC_400 236 #define CLK_ACLK_HEVC_400 237 +#define CLK_ACLK_ISP_DIS_400 238 +#define CLK_ACLK_ISP_400 239 -#define TOP_NR_CLK 238 +#define TOP_NR_CLK 240 /* CMU_CPIF */ #define CLK_FOUT_MPHY_PLL 1 @@ -1026,4 +1030,87 @@ #define HEVC_NR_CLK 19 +/* CMU_ISP */ +#define CLK_MOUT_ACLK_ISP_DIS_400_USER 1 +#define CLK_MOUT_ACLK_ISP_400_USER 2 + +#define CLK_DIV_PCLK_ISP_DIS 3 +#define CLK_DIV_PCLK_ISP 4 +#define CLK_DIV_ACLK_ISP_D_200 5 +#define CLK_DIV_ACLK_ISP_C_200 6 + +#define CLK_ACLK_ISP_D_GLUE 7 +#define CLK_ACLK_SCALERP 8 +#define CLK_ACLK_3DNR 9 +#define CLK_ACLK_DIS 10 +#define CLK_ACLK_SCALERC 11 +#define CLK_ACLK_DRC 12 +#define CLK_ACLK_ISP 13 +#define CLK_ACLK_AXIUS_SCALERP 14 +#define CLK_ACLK_AXIUS_SCALERC 15 +#define CLK_ACLK_AXIUS_DRC 16 +#define CLK_ACLK_ASYNCAHBM_ISP2P 17 +#define CLK_ACLK_ASYNCAHBM_ISP1P 18 +#define CLK_ACLK_ASYNCAXIS_DIS1 19 +#define CLK_ACLK_ASYNCAXIS_DIS0 20 +#define CLK_ACLK_ASYNCAXIM_DIS1 21 +#define CLK_ACLK_ASYNCAXIM_DIS0 22 +#define CLK_ACLK_ASYNCAXIM_ISP2P 23 +#define CLK_ACLK_ASYNCAXIM_ISP1P 24 +#define CLK_ACLK_AHB2APB_ISP2P 25 +#define CLK_ACLK_AHB2APB_ISP1P 26 +#define CLK_ACLK_AXI2APB_ISP2P 27 +#define CLK_ACLK_AXI2APB_ISP1P 28 +#define CLK_ACLK_XIU_ISPEX1 29 +#define CLK_ACLK_XIU_ISPEX0 30 +#define CLK_ACLK_ISPND_400 31 +#define CLK_ACLK_SMMU_SCALERP 32 +#define CLK_ACLK_SMMU_3DNR 33 +#define CLK_ACLK_SMMU_DIS1 34 +#define CLK_ACLK_SMMU_DIS0 35 +#define CLK_ACLK_SMMU_SCALERC 36 +#define CLK_ACLK_SMMU_DRC 37 +#define CLK_ACLK_SMMU_ISP 38 +#define CLK_ACLK_BTS_SCALERP 39 +#define CLK_ACLK_BTS_3DR 40 +#define CLK_ACLK_BTS_DIS1 41 +#define CLK_ACLK_BTS_DIS0 42 +#define CLK_ACLK_BTS_SCALERC 43 +#define CLK_ACLK_BTS_DRC 44 +#define CLK_ACLK_BTS_ISP 45 +#define CLK_PCLK_SMMU_SCALERP 46 +#define CLK_PCLK_SMMU_3DNR 47 +#define CLK_PCLK_SMMU_DIS1 48 +#define CLK_PCLK_SMMU_DIS0 49 +#define CLK_PCLK_SMMU_SCALERC 50 +#define CLK_PCLK_SMMU_DRC 51 +#define CLK_PCLK_SMMU_ISP 52 +#define CLK_PCLK_BTS_SCALERP 53 +#define CLK_PCLK_BTS_3DNR 54 +#define CLK_PCLK_BTS_DIS1 55 +#define CLK_PCLK_BTS_DIS0 56 +#define CLK_PCLK_BTS_SCALERC 57 +#define CLK_PCLK_BTS_DRC 58 +#define CLK_PCLK_BTS_ISP 59 +#define CLK_PCLK_ASYNCAXI_DIS1 60 +#define CLK_PCLK_ASYNCAXI_DIS0 61 +#define CLK_PCLK_PMU_ISP 62 +#define CLK_PCLK_SYSREG_ISP 63 +#define CLK_PCLK_CMU_ISP_LOCAL 64 +#define CLK_PCLK_SCALERP 65 +#define CLK_PCLK_3DNR 66 +#define CLK_PCLK_DIS_CORE 67 +#define CLK_PCLK_DIS 68 +#define CLK_PCLK_SCALERC 69 +#define CLK_PCLK_DRC 70 +#define CLK_PCLK_ISP 71 +#define CLK_SCLK_PIXELASYNCS_DIS 72 +#define CLK_SCLK_PIXELASYNCM_DIS 73 +#define CLK_SCLK_PIXELASYNCS_SCALERP 74 +#define CLK_SCLK_PIXELASYNCM_ISPD 75 +#define CLK_SCLK_PIXELASYNCS_ISPC 76 +#define CLK_SCLK_PIXELASYNCM_ISPC 77 + +#define ISP_NR_CLK 78 + #endif /* _DT_BINDINGS_CLOCK_EXYNOS5433_H */ -- cgit v1.2.3 From 6958f22f39f9292f6e871b4383a11f183c1271ed Mon Sep 17 00:00:00 2001 From: Chanwoo Choi Date: Tue, 3 Feb 2015 09:13:55 +0900 Subject: clk: samsung: exynos5433: Add clocks for CMU_CAM0 domain This patch adds the mux/divider/gate clocks for CMU_CAM0 domain which generates the clocks for MIPI_CSIS{0|1}/FIMC_LITE_{A|B|D}/FIMC_3AA{0|1} IPs. Signed-off-by: Chanwoo Choi Acked-by: Inki Dae Signed-off-by: Sylwester Nawrocki --- .../devicetree/bindings/clock/exynos5433-clock.txt | 24 + drivers/clk/samsung/clk-exynos5433.c | 501 +++++++++++++++++++++ include/dt-bindings/clock/exynos5433.h | 146 +++++- 3 files changed, 670 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/Documentation/devicetree/bindings/clock/exynos5433-clock.txt b/Documentation/devicetree/bindings/clock/exynos5433-clock.txt index 4f3bfcd366a3..84002e4b52e5 100644 --- a/Documentation/devicetree/bindings/clock/exynos5433-clock.txt +++ b/Documentation/devicetree/bindings/clock/exynos5433-clock.txt @@ -45,6 +45,9 @@ Required Properties: which generates clocks for HEVC(High Efficiency Video Codec) decoder IP. - "samsung,exynos5433-cmu-isp" - clock controller compatible for CMU_ISP which generates clocks for FIMC-ISP/DRC/SCLC/DIS/3DNR IPs. + - "samsung,exynos5433-cmu-cam0" - clock controller compatible for CMU_CAM0 + which generates clocks for MIPI_CSIS{0|1}/FIMC_LITE_{A|B|D}/FIMC_3AA{0|1} + IPs. - reg: physical base address of the controller and length of memory mapped region. @@ -144,6 +147,12 @@ Required Properties: - aclk_isp_dis_400 - aclk_isp_400 + Input clocks for cam0 clock controller: + - oscclk + - aclk_cam0_333 + - aclk_cam0_400 + - aclk_cam0_552 + Each clock is assigned an identifier and client nodes can use this identifier to specify the clock which they consume. @@ -390,6 +399,21 @@ Example 2: Examples of clock controller nodes are listed below. <&cmu_top CLK_ACLK_ISP_400>; }; + cmu_cam0: clock-controller@120d0000 { + compatible = "samsung,exynos5433-cmu-cam0"; + reg = <0x120d0000 0x0b0c>; + #clock-cells = <1>; + + clock-names = "oscclk", + "aclk_cam0_333", + "aclk_cam0_400", + "aclk_cam0_552"; + clocks = <&xxti>, + <&cmu_top CLK_ACLK_CAM0_333>, + <&cmu_top CLK_ACLK_CAM0_400>, + <&cmu_top CLK_ACLK_CAM0_552>; + }; + Example 3: UART controller node that consumes the clock generated by the clock controller. diff --git a/drivers/clk/samsung/clk-exynos5433.c b/drivers/clk/samsung/clk-exynos5433.c index a8ea6e1fbffc..ce6487375670 100644 --- a/drivers/clk/samsung/clk-exynos5433.c +++ b/drivers/clk/samsung/clk-exynos5433.c @@ -405,6 +405,12 @@ static struct samsung_mux_clock top_mux_clks[] __initdata = { static struct samsung_div_clock top_div_clks[] __initdata = { /* DIV_TOP0 */ + DIV(CLK_DIV_ACLK_CAM0_333, "div_aclk_cam0_333", "mout_mfc_pll_user", + DIV_TOP0, 16, 3), + DIV(CLK_DIV_ACLK_CAM0_400, "div_aclk_cam0_400", "mout_bus_pll_user", + DIV_TOP0, 12, 3), + DIV(CLK_DIV_ACLK_CAM0_552, "div_aclk_cam0_552", "mout_isp_pll", + DIV_TOP0, 8, 3), DIV(CLK_DIV_ACLK_ISP_DIS_400, "div_aclk_isp_dis_400", "mout_aclk_isp_dis_400", DIV_TOP0, 4, 4), DIV(CLK_DIV_ACLK_ISP_400, "div_aclk_isp_400", @@ -566,6 +572,15 @@ static struct samsung_gate_clock top_gate_clks[] __initdata = { GATE(CLK_ACLK_GSCL_333, "aclk_gscl_333", "div_aclk_gscl_333", ENABLE_ACLK_TOP, 14, CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_CAM0_333, "aclk_cam0_333", "div_aclk_cam0_333", + ENABLE_ACLK_TOP, 10, + CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_CAM0_400, "aclk_cam0_400", "div_aclk_cam0_400", + ENABLE_ACLK_TOP, 9, + CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_CAM0_552, "aclk_cam0_552", "div_aclk_cam0_552", + ENABLE_ACLK_TOP, 8, + CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED, 0), GATE(CLK_ACLK_ISP_DIS_400, "aclk_isp_dis_400", "div_aclk_isp_dis_400", ENABLE_ACLK_TOP, 7, CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED, 0), @@ -4485,3 +4500,489 @@ static void __init exynos5433_cmu_isp_init(struct device_node *np) } CLK_OF_DECLARE(exynos5433_cmu_isp, "samsung,exynos5433-cmu-isp", exynos5433_cmu_isp_init); + +/* + * Register offset definitions for CMU_CAM0 + */ +#define MUX_SEL_CAM00 0x0200 +#define MUX_SEL_CAM01 0x0204 +#define MUX_SEL_CAM02 0x0208 +#define MUX_SEL_CAM03 0x020c +#define MUX_SEL_CAM04 0x0210 +#define MUX_ENABLE_CAM00 0x0300 +#define MUX_ENABLE_CAM01 0x0304 +#define MUX_ENABLE_CAM02 0x0308 +#define MUX_ENABLE_CAM03 0x030c +#define MUX_ENABLE_CAM04 0x0310 +#define MUX_STAT_CAM00 0x0400 +#define MUX_STAT_CAM01 0x0404 +#define MUX_STAT_CAM02 0x0408 +#define MUX_STAT_CAM03 0x040c +#define MUX_STAT_CAM04 0x0410 +#define MUX_IGNORE_CAM01 0x0504 +#define DIV_CAM00 0x0600 +#define DIV_CAM01 0x0604 +#define DIV_CAM02 0x0608 +#define DIV_CAM03 0x060c +#define DIV_STAT_CAM00 0x0700 +#define DIV_STAT_CAM01 0x0704 +#define DIV_STAT_CAM02 0x0708 +#define DIV_STAT_CAM03 0x070c +#define ENABLE_ACLK_CAM00 0X0800 +#define ENABLE_ACLK_CAM01 0X0804 +#define ENABLE_ACLK_CAM02 0X0808 +#define ENABLE_PCLK_CAM0 0X0900 +#define ENABLE_SCLK_CAM0 0X0a00 +#define ENABLE_IP_CAM00 0X0b00 +#define ENABLE_IP_CAM01 0X0b04 +#define ENABLE_IP_CAM02 0X0b08 +#define ENABLE_IP_CAM03 0X0b0C + +static unsigned long cam0_clk_regs[] __initdata = { + MUX_SEL_CAM00, + MUX_SEL_CAM01, + MUX_SEL_CAM02, + MUX_SEL_CAM03, + MUX_SEL_CAM04, + MUX_ENABLE_CAM00, + MUX_ENABLE_CAM01, + MUX_ENABLE_CAM02, + MUX_ENABLE_CAM03, + MUX_ENABLE_CAM04, + MUX_STAT_CAM00, + MUX_STAT_CAM01, + MUX_STAT_CAM02, + MUX_STAT_CAM03, + MUX_STAT_CAM04, + MUX_IGNORE_CAM01, + DIV_CAM00, + DIV_CAM01, + DIV_CAM02, + DIV_CAM03, + DIV_STAT_CAM00, + DIV_STAT_CAM01, + DIV_STAT_CAM02, + DIV_STAT_CAM03, + ENABLE_ACLK_CAM00, + ENABLE_ACLK_CAM01, + ENABLE_ACLK_CAM02, + ENABLE_PCLK_CAM0, + ENABLE_SCLK_CAM0, + ENABLE_IP_CAM00, + ENABLE_IP_CAM01, + ENABLE_IP_CAM02, + ENABLE_IP_CAM03, +}; +PNAME(mout_aclk_cam0_333_user_p) = { "oscclk", "aclk_cam0_333", }; +PNAME(mout_aclk_cam0_400_user_p) = { "oscclk", "aclk_cam0_400", }; +PNAME(mout_aclk_cam0_552_user_p) = { "oscclk", "aclk_cam0_552", }; + +PNAME(mout_phyclk_rxbyteclkhs0_s4_user_p) = { "oscclk", + "phyclk_rxbyteclkhs0_s4_phy", }; +PNAME(mout_phyclk_rxbyteclkhs0_s2a_user_p) = { "oscclk", + "phyclk_rxbyteclkhs0_s2a_phy", }; + +PNAME(mout_aclk_lite_d_b_p) = { "mout_aclk_lite_d_a", + "mout_aclk_cam0_333_user", }; +PNAME(mout_aclk_lite_d_a_p) = { "mout_aclk_cam0_552_user", + "mout_aclk_cam0_400_user", }; +PNAME(mout_aclk_lite_b_b_p) = { "mout_aclk_lite_b_a", + "mout_aclk_cam0_333_user", }; +PNAME(mout_aclk_lite_b_a_p) = { "mout_aclk_cam0_552_user", + "mout_aclk_cam0_400_user", }; +PNAME(mout_aclk_lite_a_b_p) = { "mout_aclk_lite_a_a", + "mout_aclk_cam0_333_user", }; +PNAME(mout_aclk_lite_a_a_p) = { "mout_aclk_cam0_552_user", + "mout_aclk_cam0_400_user", }; +PNAME(mout_aclk_cam0_400_p) = { "mout_aclk_cam0_400_user", + "mout_aclk_cam0_333_user", }; + +PNAME(mout_aclk_csis1_b_p) = { "mout_aclk_csis1_a", + "mout_aclk_cam0_333_user" }; +PNAME(mout_aclk_csis1_a_p) = { "mout_aclk_cam0_552_user", + "mout_aclk_cam0_400_user", }; +PNAME(mout_aclk_csis0_b_p) = { "mout_aclk_csis0_a", + "mout_aclk_cam0_333_user", }; +PNAME(mout_aclk_csis0_a_p) = { "mout_aclk_cam0_552_user", + "mout_aclk-cam0_400_user", }; +PNAME(mout_aclk_3aa1_b_p) = { "mout_aclk_3aa1_a", + "mout_aclk_cam0_333_user", }; +PNAME(mout_aclk_3aa1_a_p) = { "mout_aclk_cam0_552_user", + "mout_aclk_cam0_400_user", }; +PNAME(mout_aclk_3aa0_b_p) = { "mout_aclk_3aa0_a", + "mout_aclk_cam0_333_user", }; +PNAME(mout_aclk_3aa0_a_p) = { "mout_aclk_cam0_552_user", + "mout_aclk_cam0_400_user", }; + +PNAME(mout_sclk_lite_freecnt_c_p) = { "mout_sclk_lite_freecnt_b", + "div_pclk_lite_d", }; +PNAME(mout_sclk_lite_freecnt_b_p) = { "mout_sclk_lite_freecnt_a", + "div_pclk_pixelasync_lite_c", }; +PNAME(mout_sclk_lite_freecnt_a_p) = { "div_pclk_lite_a", + "div_pclk_lite_b", }; +PNAME(mout_sclk_pixelasync_lite_c_b_p) = { "mout_sclk_pixelasync_lite_c_a", + "mout_aclk_cam0_333_user", }; +PNAME(mout_sclk_pixelasync_lite_c_a_p) = { "mout_aclk_cam0_552_user", + "mout_aclk_cam0_400_user", }; +PNAME(mout_sclk_pixelasync_lite_c_init_b_p) = { + "mout_sclk_pixelasync_lite_c_init_a", + "mout_aclk_cam0_400_user", }; +PNAME(mout_sclk_pixelasync_lite_c_init_a_p) = { + "mout_aclk_cam0_552_user", + "mout_aclk_cam0_400_user", }; + +static struct samsung_fixed_rate_clock cam0_fixed_clks[] __initdata = { + FRATE(CLK_PHYCLK_RXBYTEECLKHS0_S4_PHY, "phyclk_rxbyteclkhs0_s4_phy", + NULL, CLK_IS_ROOT, 100000000), + FRATE(CLK_PHYCLK_RXBYTEECLKHS0_S2A_PHY, "phyclk_rxbyteclkhs0_s2a_phy", + NULL, CLK_IS_ROOT, 100000000), +}; + +static struct samsung_mux_clock cam0_mux_clks[] __initdata = { + /* MUX_SEL_CAM00 */ + MUX(CLK_MOUT_ACLK_CAM0_333_USER, "mout_aclk_cam0_333_user", + mout_aclk_cam0_333_user_p, MUX_SEL_CAM00, 8, 1), + MUX(CLK_MOUT_ACLK_CAM0_400_USER, "mout_aclk_cam0_400_user", + mout_aclk_cam0_400_user_p, MUX_SEL_CAM00, 4, 1), + MUX(CLK_MOUT_ACLK_CAM0_552_USER, "mout_aclk_cam0_552_user", + mout_aclk_cam0_552_user_p, MUX_SEL_CAM00, 0, 1), + + /* MUX_SEL_CAM01 */ + MUX(CLK_MOUT_PHYCLK_RXBYTECLKHS0_S4_USER, + "mout_phyclk_rxbyteclkhs0_s4_user", + mout_phyclk_rxbyteclkhs0_s4_user_p, + MUX_SEL_CAM01, 4, 1), + MUX(CLK_MOUT_PHYCLK_RXBYTECLKHS0_S2A_USER, + "mout_phyclk_rxbyteclkhs0_s2a_user", + mout_phyclk_rxbyteclkhs0_s2a_user_p, + MUX_SEL_CAM01, 0, 1), + + /* MUX_SEL_CAM02 */ + MUX(CLK_MOUT_ACLK_LITE_D_B, "mout_aclk_lite_d_b", mout_aclk_lite_d_b_p, + MUX_SEL_CAM02, 24, 1), + MUX(CLK_MOUT_ACLK_LITE_D_A, "mout_aclk_lite_d_a", mout_aclk_lite_d_a_p, + MUX_SEL_CAM02, 20, 1), + MUX(CLK_MOUT_ACLK_LITE_B_B, "mout_aclk_lite_b_b", mout_aclk_lite_b_b_p, + MUX_SEL_CAM02, 16, 1), + MUX(CLK_MOUT_ACLK_LITE_B_A, "mout_aclk_lite_b_a", mout_aclk_lite_b_a_p, + MUX_SEL_CAM02, 12, 1), + MUX(CLK_MOUT_ACLK_LITE_A_B, "mout_aclk_lite_a_b", mout_aclk_lite_a_b_p, + MUX_SEL_CAM02, 8, 1), + MUX(CLK_MOUT_ACLK_LITE_A_A, "mout_aclk_lite_a_a", mout_aclk_lite_a_a_p, + MUX_SEL_CAM02, 4, 1), + MUX(CLK_MOUT_ACLK_CAM0_400, "mout_aclk_cam0_400", mout_aclk_cam0_400_p, + MUX_SEL_CAM02, 0, 1), + + /* MUX_SEL_CAM03 */ + MUX(CLK_MOUT_ACLK_CSIS1_B, "mout_aclk_csis1_b", mout_aclk_csis1_b_p, + MUX_SEL_CAM03, 28, 1), + MUX(CLK_MOUT_ACLK_CSIS1_A, "mout_aclk_csis1_a", mout_aclk_csis1_a_p, + MUX_SEL_CAM03, 24, 1), + MUX(CLK_MOUT_ACLK_CSIS0_B, "mout_aclk_csis0_b", mout_aclk_csis0_b_p, + MUX_SEL_CAM03, 20, 1), + MUX(CLK_MOUT_ACLK_CSIS0_A, "mout_aclk_csis0_a", mout_aclk_csis0_a_p, + MUX_SEL_CAM03, 16, 1), + MUX(CLK_MOUT_ACLK_3AA1_B, "mout_aclk_3aa1_b", mout_aclk_3aa1_b_p, + MUX_SEL_CAM03, 12, 1), + MUX(CLK_MOUT_ACLK_3AA1_A, "mout_aclk_3aa1_a", mout_aclk_3aa1_a_p, + MUX_SEL_CAM03, 8, 1), + MUX(CLK_MOUT_ACLK_3AA0_B, "mout_aclk_3aa0_b", mout_aclk_3aa0_b_p, + MUX_SEL_CAM03, 4, 1), + MUX(CLK_MOUT_ACLK_3AA0_A, "mout_aclk_3aa0_a", mout_aclk_3aa0_a_p, + MUX_SEL_CAM03, 0, 1), + + /* MUX_SEL_CAM04 */ + MUX(CLK_MOUT_SCLK_LITE_FREECNT_C, "mout_sclk_lite_freecnt_c", + mout_sclk_lite_freecnt_c_p, MUX_SEL_CAM04, 24, 1), + MUX(CLK_MOUT_SCLK_LITE_FREECNT_B, "mout_sclk_lite_freecnt_b", + mout_sclk_lite_freecnt_b_p, MUX_SEL_CAM04, 24, 1), + MUX(CLK_MOUT_SCLK_LITE_FREECNT_A, "mout_sclk_lite_freecnt_a", + mout_sclk_lite_freecnt_a_p, MUX_SEL_CAM04, 24, 1), + MUX(CLK_MOUT_SCLK_PIXELASYNC_LITE_C_B, "mout_sclk_pixelasync_lite_c_b", + mout_sclk_pixelasync_lite_c_b_p, MUX_SEL_CAM04, 24, 1), + MUX(CLK_MOUT_SCLK_PIXELASYNC_LITE_C_A, "mout_sclk_pixelasync_lite_c_a", + mout_sclk_pixelasync_lite_c_a_p, MUX_SEL_CAM04, 24, 1), + MUX(CLK_MOUT_SCLK_PIXELASYNC_LITE_C_INIT_B, + "mout_sclk_pixelasync_lite_c_init_b", + mout_sclk_pixelasync_lite_c_init_b_p, + MUX_SEL_CAM04, 24, 1), + MUX(CLK_MOUT_SCLK_PIXELASYNC_LITE_C_INIT_A, + "mout_sclk_pixelasync_lite_c_init_a", + mout_sclk_pixelasync_lite_c_init_a_p, + MUX_SEL_CAM04, 24, 1), +}; + +static struct samsung_div_clock cam0_div_clks[] __initdata = { + /* DIV_CAM00 */ + DIV(CLK_DIV_PCLK_CAM0_50, "div_pclk_cam0_50", "div_aclk_cam0_200", + DIV_CAM00, 8, 2), + DIV(CLK_DIV_ACLK_CAM0_200, "div_aclk_cam0_200", "mout_aclk_cam0_400", + DIV_CAM00, 4, 3), + DIV(CLK_DIV_ACLK_CAM0_BUS_400, "div_aclk_cam0_bus_400", + "mout_aclk_cam0_400", DIV_CAM00, 0, 3), + + /* DIV_CAM01 */ + DIV(CLK_DIV_PCLK_LITE_D, "div_pclk_lite_d", "div_aclk_lite_d", + DIV_CAM01, 20, 2), + DIV(CLK_DIV_ACLK_LITE_D, "div_aclk_lite_d", "mout_aclk_lite_d_b", + DIV_CAM01, 16, 3), + DIV(CLK_DIV_PCLK_LITE_B, "div_pclk_lite_b", "div_aclk_lite_b", + DIV_CAM01, 12, 2), + DIV(CLK_DIV_ACLK_LITE_B, "div_aclk_lite_b", "mout_aclk_lite_b_b", + DIV_CAM01, 8, 3), + DIV(CLK_DIV_PCLK_LITE_A, "div_pclk_lite_a", "div_aclk_lite_a", + DIV_CAM01, 4, 2), + DIV(CLK_DIV_ACLK_LITE_A, "div_aclk_lite_a", "mout_aclk_lite_a_b", + DIV_CAM01, 0, 3), + + /* DIV_CAM02 */ + DIV(CLK_DIV_ACLK_CSIS1, "div_aclk_csis1", "mout_aclk_csis1_b", + DIV_CAM02, 20, 3), + DIV(CLK_DIV_ACLK_CSIS0, "div_aclk_csis0", "mout_aclk_csis0_b", + DIV_CAM02, 16, 3), + DIV(CLK_DIV_PCLK_3AA1, "div_pclk_3aa1", "div_aclk_3aa1", + DIV_CAM02, 12, 2), + DIV(CLK_DIV_ACLK_3AA1, "div_aclk_3aa1", "mout_aclk_3aa1_b", + DIV_CAM02, 8, 3), + DIV(CLK_DIV_PCLK_3AA0, "div_pclk_3aa0", "div_aclk_3aa0", + DIV_CAM02, 4, 2), + DIV(CLK_DIV_ACLK_3AA0, "div_aclk_3aa0", "mout_aclk_3aa0_b", + DIV_CAM02, 0, 3), + + /* DIV_CAM03 */ + DIV(CLK_DIV_SCLK_PIXELASYNC_LITE_C, "div_sclk_pixelasync_lite_c", + "mout_sclk_pixelasync_lite_c_b", DIV_CAM03, 8, 3), + DIV(CLK_DIV_PCLK_PIXELASYNC_LITE_C, "div_pclk_pixelasync_lite_c", + "div_sclk_pixelasync_lite_c_init", DIV_CAM03, 4, 2), + DIV(CLK_DIV_SCLK_PIXELASYNC_LITE_C_INIT, + "div_sclk_pixelasync_lite_c_init", + "mout_sclk_pixelasync_lite_c_init_b", DIV_CAM03, 0, 3), +}; + +static struct samsung_gate_clock cam0_gate_clks[] __initdata = { + /* ENABLE_ACLK_CAM00 */ + GATE(CLK_ACLK_CSIS1, "aclk_csis1", "div_aclk_csis1", ENABLE_ACLK_CAM00, + 6, 0, 0), + GATE(CLK_ACLK_CSIS0, "aclk_csis0", "div_aclk_csis0", ENABLE_ACLK_CAM00, + 5, 0, 0), + GATE(CLK_ACLK_3AA1, "aclk_3aa1", "div_aclk_3aa1", ENABLE_ACLK_CAM00, + 4, 0, 0), + GATE(CLK_ACLK_3AA0, "aclk_3aa0", "div_aclk_3aa0", ENABLE_ACLK_CAM00, + 3, 0, 0), + GATE(CLK_ACLK_LITE_D, "aclk_lite_d", "div_aclk_lite_d", + ENABLE_ACLK_CAM00, 2, 0, 0), + GATE(CLK_ACLK_LITE_B, "aclk_lite_b", "div_aclk_lite_b", + ENABLE_ACLK_CAM00, 1, 0, 0), + GATE(CLK_ACLK_LITE_A, "aclk_lite_a", "div_aclk_lite_a", + ENABLE_ACLK_CAM00, 0, 0, 0), + + /* ENABLE_ACLK_CAM01 */ + GATE(CLK_ACLK_AHBSYNCDN, "aclk_ahbsyncdn", "div_aclk_cam0_200", + ENABLE_ACLK_CAM01, 31, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_AXIUS_LITE_D, "aclk_axius_lite_d", "div_aclk_cam0_bus_400", + ENABLE_ACLK_CAM01, 30, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_AXIUS_LITE_B, "aclk_axius_lite_b", "div_aclk_cam0_bus_400", + ENABLE_ACLK_CAM01, 29, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_AXIUS_LITE_A, "aclk_axius_lite_a", "div_aclk_cam0_bus_400", + ENABLE_ACLK_CAM01, 28, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_ASYNCAPBM_3AA1, "aclk_asyncapbm_3aa1", "div_pclk_3aa1", + ENABLE_ACLK_CAM01, 27, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_ASYNCAPBS_3AA1, "aclk_asyncapbs_3aa1", "div_aclk_3aa1", + ENABLE_ACLK_CAM01, 26, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_ASYNCAPBM_3AA0, "aclk_asyncapbm_3aa0", "div_pclk_3aa0", + ENABLE_ACLK_CAM01, 25, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_ASYNCAPBS_3AA0, "aclk_asyncapbs_3aa0", "div_aclk_3aa0", + ENABLE_ACLK_CAM01, 24, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_ASYNCAPBM_LITE_D, "aclk_asyncapbm_lite_d", + "div_pclk_lite_d", ENABLE_ACLK_CAM01, + 23, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_ASYNCAPBS_LITE_D, "aclk_asyncapbs_lite_d", + "div_aclk_cam0_200", ENABLE_ACLK_CAM01, + 22, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_ASYNCAPBM_LITE_B, "aclk_asyncapbm_lite_b", + "div_pclk_lite_b", ENABLE_ACLK_CAM01, + 21, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_ASYNCAPBS_LITE_B, "aclk_asyncapbs_lite_b", + "div_aclk_cam0_200", ENABLE_ACLK_CAM01, + 20, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_ASYNCAPBM_LITE_A, "aclk_asyncapbm_lite_a", + "div_pclk_lite_a", ENABLE_ACLK_CAM01, + 19, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_ASYNCAPBS_LITE_A, "aclk_asyncapbs_lite_a", + "div_aclk_cam0_200", ENABLE_ACLK_CAM01, + 18, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_ASYNCAXIM_ISP0P, "aclk_asyncaxim_isp0p", + "div_aclk_cam0_200", ENABLE_ACLK_CAM01, + 17, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_ASYNCAXIM_3AA1, "aclk_asyncaxim_3aa1", + "div_aclk_cam0_bus_400", ENABLE_ACLK_CAM01, + 16, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_ASYNCAXIS_3AA1, "aclk_asyncaxis_3aa1", + "div_aclk_3aa1", ENABLE_ACLK_CAM01, + 15, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_ASYNCAXIM_3AA0, "aclk_asyncaxim_3aa0", + "div_aclk_cam0_bus_400", ENABLE_ACLK_CAM01, + 14, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_ASYNCAXIS_3AA0, "aclk_asyncaxis_3aa0", + "div_aclk_3aa0", ENABLE_ACLK_CAM01, + 13, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_ASYNCAXIM_LITE_D, "aclk_asyncaxim_lite_d", + "div_aclk_cam0_bus_400", ENABLE_ACLK_CAM01, + 12, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_ASYNCAXIS_LITE_D, "aclk_asyncaxis_lite_d", + "div_aclk_lite_d", ENABLE_ACLK_CAM01, + 11, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_ASYNCAXIM_LITE_B, "aclk_asyncaxim_lite_b", + "div_aclk_cam0_bus_400", ENABLE_ACLK_CAM01, + 10, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_ASYNCAXIS_LITE_B, "aclk_asyncaxis_lite_b", + "div_aclk_lite_b", ENABLE_ACLK_CAM01, + 9, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_ASYNCAXIM_LITE_A, "aclk_asyncaxim_lite_a", + "div_aclk_cam0_bus_400", ENABLE_ACLK_CAM01, + 8, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_ASYNCAXIS_LITE_A, "aclk_asyncaxis_lite_a", + "div_aclk_lite_a", ENABLE_ACLK_CAM01, + 7, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_AHB2APB_ISPSFRP, "aclk_ahb2apb_ispsfrp", + "div_pclk_cam0_50", ENABLE_ACLK_CAM01, + 6, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_AXI2APB_ISP0P, "aclk_axi2apb_isp0p", "div_aclk_cam0_200", + ENABLE_ACLK_CAM01, 5, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_AXI2AHB_ISP0P, "aclk_axi2ahb_isp0p", "div_aclk_cam0_200", + ENABLE_ACLK_CAM01, 4, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_XIU_IS0X, "aclk_xiu_is0x", "div_aclk_cam0_200", + ENABLE_ACLK_CAM01, 3, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_XIU_ISP0EX, "aclk_xiu_isp0ex", "div_aclk_cam0_bus_400", + ENABLE_ACLK_CAM01, 2, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_CAM0NP_276, "aclk_cam0np_276", "div_aclk_cam0_200", + ENABLE_ACLK_CAM01, 1, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_CAM0ND_400, "aclk_cam0nd_400", "div_aclk_cam0_bus_400", + ENABLE_ACLK_CAM01, 0, CLK_IGNORE_UNUSED, 0), + + /* ENABLE_ACLK_CAM02 */ + GATE(CLK_ACLK_SMMU_3AA1, "aclk_smmu_3aa1", "div_aclk_cam0_bus_400", + ENABLE_ACLK_CAM02, 9, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_SMMU_3AA0, "aclk_smmu_3aa0", "div_aclk_cam0_bus_400", + ENABLE_ACLK_CAM02, 8, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_SMMU_LITE_D, "aclk_smmu_lite_d", "div_aclk_cam0_bus_400", + ENABLE_ACLK_CAM02, 7, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_SMMU_LITE_B, "aclk_smmu_lite_b", "div_aclk_cam0_bus_400", + ENABLE_ACLK_CAM02, 6, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_SMMU_LITE_A, "aclk_smmu_lite_a", "div_aclk_cam0_bus_400", + ENABLE_ACLK_CAM02, 5, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_BTS_3AA1, "aclk_bts_3aa1", "div_aclk_cam0_bus_400", + ENABLE_ACLK_CAM02, 4, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_BTS_3AA0, "aclk_bts_3aa0", "div_aclk_cam0_bus_400", + ENABLE_ACLK_CAM02, 3, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_BTS_LITE_D, "aclk_bts_lite_d", "div_aclk_cam0_bus_400", + ENABLE_ACLK_CAM02, 2, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_BTS_LITE_B, "aclk_bts_lite_b", "div_aclk_cam0_bus_400", + ENABLE_ACLK_CAM02, 1, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_BTS_LITE_A, "aclk_bts_lite_a", "div_aclk_cam0_bus_400", + ENABLE_ACLK_CAM02, 0, CLK_IGNORE_UNUSED, 0), + + /* ENABLE_PCLK_CAM0 */ + GATE(CLK_PCLK_SMMU_3AA1, "pclk_smmu_3aa1", "div_aclk_cam0_200", + ENABLE_PCLK_CAM0, 25, CLK_IGNORE_UNUSED, 0), + GATE(CLK_PCLK_SMMU_3AA0, "pclk_smmu_3aa0", "div_aclk_cam0_200", + ENABLE_PCLK_CAM0, 24, CLK_IGNORE_UNUSED, 0), + GATE(CLK_PCLK_SMMU_LITE_D, "pclk_smmu_lite_d", "div_aclk_cam0_200", + ENABLE_PCLK_CAM0, 23, CLK_IGNORE_UNUSED, 0), + GATE(CLK_PCLK_SMMU_LITE_B, "pclk_smmu_lite_b", "div_aclk_cam0_200", + ENABLE_PCLK_CAM0, 22, CLK_IGNORE_UNUSED, 0), + GATE(CLK_PCLK_SMMU_LITE_A, "pclk_smmu_lite_a", "div_aclk_cam0_200", + ENABLE_PCLK_CAM0, 21, CLK_IGNORE_UNUSED, 0), + GATE(CLK_PCLK_BTS_3AA1, "pclk_bts_3aa1", "div_pclk_cam0_50", + ENABLE_PCLK_CAM0, 20, CLK_IGNORE_UNUSED, 0), + GATE(CLK_PCLK_BTS_3AA0, "pclk_bts_3aa0", "div_pclk_cam0_50", + ENABLE_PCLK_CAM0, 19, CLK_IGNORE_UNUSED, 0), + GATE(CLK_PCLK_BTS_LITE_D, "pclk_bts_lite_d", "div_pclk_cam0_50", + ENABLE_PCLK_CAM0, 18, CLK_IGNORE_UNUSED, 0), + GATE(CLK_PCLK_BTS_LITE_B, "pclk_bts_lite_b", "div_pclk_cam0_50", + ENABLE_PCLK_CAM0, 17, CLK_IGNORE_UNUSED, 0), + GATE(CLK_PCLK_BTS_LITE_A, "pclk_bts_lite_a", "div_pclk_cam0_50", + ENABLE_PCLK_CAM0, 16, CLK_IGNORE_UNUSED, 0), + GATE(CLK_PCLK_ASYNCAXI_CAM1, "pclk_asyncaxi_cam1", "div_pclk_cam0_50", + ENABLE_PCLK_CAM0, 15, CLK_IGNORE_UNUSED, 0), + GATE(CLK_PCLK_ASYNCAXI_3AA1, "pclk_asyncaxi_3aa1", "div_pclk_cam0_50", + ENABLE_PCLK_CAM0, 14, CLK_IGNORE_UNUSED, 0), + GATE(CLK_PCLK_ASYNCAXI_3AA0, "pclk_asyncaxi_3aa0", "div_pclk_cam0_50", + ENABLE_PCLK_CAM0, 13, CLK_IGNORE_UNUSED, 0), + GATE(CLK_PCLK_ASYNCAXI_LITE_D, "pclk_asyncaxi_lite_d", + "div_pclk_cam0_50", ENABLE_PCLK_CAM0, + 12, CLK_IGNORE_UNUSED, 0), + GATE(CLK_PCLK_ASYNCAXI_LITE_B, "pclk_asyncaxi_lite_b", + "div_pclk_cam0_50", ENABLE_PCLK_CAM0, + 11, CLK_IGNORE_UNUSED, 0), + GATE(CLK_PCLK_ASYNCAXI_LITE_A, "pclk_asyncaxi_lite_a", + "div_pclk_cam0_50", ENABLE_PCLK_CAM0, + 10, CLK_IGNORE_UNUSED, 0), + GATE(CLK_PCLK_PMU_CAM0, "pclk_pmu_cam0", "div_pclk_cam0_50", + ENABLE_PCLK_CAM0, 9, CLK_IGNORE_UNUSED, 0), + GATE(CLK_PCLK_SYSREG_CAM0, "pclk_sysreg_cam0", "div_pclk_cam0_50", + ENABLE_PCLK_CAM0, 8, CLK_IGNORE_UNUSED, 0), + GATE(CLK_PCLK_CMU_CAM0_LOCAL, "pclk_cmu_cam0_local", + "div_aclk_cam0_200", ENABLE_PCLK_CAM0, + 7, CLK_IGNORE_UNUSED, 0), + GATE(CLK_PCLK_CSIS1, "pclk_csis1", "div_aclk_cam0_200", + ENABLE_PCLK_CAM0, 6, CLK_IGNORE_UNUSED, 0), + GATE(CLK_PCLK_CSIS0, "pclk_csis0", "div_aclk_cam0_200", + ENABLE_PCLK_CAM0, 5, CLK_IGNORE_UNUSED, 0), + GATE(CLK_PCLK_3AA1, "pclk_3aa1", "div_pclk_3aa1", + ENABLE_PCLK_CAM0, 4, CLK_IGNORE_UNUSED, 0), + GATE(CLK_PCLK_3AA0, "pclk_3aa0", "div_pclk_3aa0", + ENABLE_PCLK_CAM0, 3, CLK_IGNORE_UNUSED, 0), + GATE(CLK_PCLK_LITE_D, "pclk_lite_d", "div_pclk_lite_d", + ENABLE_PCLK_CAM0, 2, CLK_IGNORE_UNUSED, 0), + GATE(CLK_PCLK_LITE_B, "pclk_lite_b", "div_pclk_lite_b", + ENABLE_PCLK_CAM0, 1, CLK_IGNORE_UNUSED, 0), + GATE(CLK_PCLK_LITE_A, "pclk_lite_a", "div_pclk_lite_a", + ENABLE_PCLK_CAM0, 0, CLK_IGNORE_UNUSED, 0), + + /* ENABLE_SCLK_CAM0 */ + GATE(CLK_PHYCLK_RXBYTECLKHS0_S4, "phyclk_rxbyteclkhs0_s4", + "mout_phyclk_rxbyteclkhs0_s4_user", + ENABLE_SCLK_CAM0, 8, 0, 0), + GATE(CLK_PHYCLK_RXBYTECLKHS0_S2A, "phyclk_rxbyteclkhs0_s2a", + "mout_phyclk_rxbyteclkhs0_s2a_user", + ENABLE_SCLK_CAM0, 7, 0, 0), + GATE(CLK_SCLK_LITE_FREECNT, "sclk_lite_freecnt", + "mout_sclk_lite_freecnt_c", ENABLE_SCLK_CAM0, 6, 0, 0), + GATE(CLK_SCLK_PIXELASYNCM_3AA1, "sclk_pixelasycm_3aa1", + "div_aclk_3aa1", ENABLE_SCLK_CAM0, 5, 0, 0), + GATE(CLK_SCLK_PIXELASYNCM_3AA0, "sclk_pixelasycm_3aa0", + "div_aclk_3aa0", ENABLE_SCLK_CAM0, 4, 0, 0), + GATE(CLK_SCLK_PIXELASYNCS_3AA0, "sclk_pixelasycs_3aa0", + "div_aclk_3aa0", ENABLE_SCLK_CAM0, 3, 0, 0), + GATE(CLK_SCLK_PIXELASYNCM_LITE_C, "sclk_pixelasyncm_lite_c", + "div_sclk_pixelasync_lite_c", + ENABLE_SCLK_CAM0, 2, 0, 0), + GATE(CLK_SCLK_PIXELASYNCM_LITE_C_INIT, "sclk_pixelasyncm_lite_c_init", + "div_sclk_pixelasync_lite_c_init", + ENABLE_SCLK_CAM0, 1, 0, 0), + GATE(CLK_SCLK_PIXELASYNCS_LITE_C_INIT, "sclk_pixelasyncs_lite_c_init", + "div_sclk_pixelasync_lite_c", + ENABLE_SCLK_CAM0, 0, 0, 0), +}; + +static struct samsung_cmu_info cam0_cmu_info __initdata = { + .mux_clks = cam0_mux_clks, + .nr_mux_clks = ARRAY_SIZE(cam0_mux_clks), + .div_clks = cam0_div_clks, + .nr_div_clks = ARRAY_SIZE(cam0_div_clks), + .gate_clks = cam0_gate_clks, + .nr_gate_clks = ARRAY_SIZE(cam0_gate_clks), + .fixed_clks = cam0_fixed_clks, + .nr_fixed_clks = ARRAY_SIZE(cam0_fixed_clks), + .nr_clk_ids = CAM0_NR_CLK, + .clk_regs = cam0_clk_regs, + .nr_clk_regs = ARRAY_SIZE(cam0_clk_regs), +}; + +static void __init exynos5433_cmu_cam0_init(struct device_node *np) +{ + samsung_cmu_register_one(np, &cam0_cmu_info); +} +CLK_OF_DECLARE(exynos5433_cmu_cam0, "samsung,exynos5433-cmu-cam0", + exynos5433_cmu_cam0_init); diff --git a/include/dt-bindings/clock/exynos5433.h b/include/dt-bindings/clock/exynos5433.h index fbc81e3424a6..f99cde7a278d 100644 --- a/include/dt-bindings/clock/exynos5433.h +++ b/include/dt-bindings/clock/exynos5433.h @@ -118,6 +118,9 @@ #define CLK_DIV_ACLK_MSCL_400 145 #define CLK_DIV_ACLK_ISP_DIS_400 146 #define CLK_DIV_ACLK_ISP_400 147 +#define CLK_DIV_ACLK_CAM0_333 148 +#define CLK_DIV_ACLK_CAM0_400 149 +#define CLK_DIV_ACLK_CAM0_552 150 #define CLK_ACLK_PERIC_66 200 #define CLK_ACLK_PERIS_66 201 @@ -159,8 +162,11 @@ #define CLK_ACLK_HEVC_400 237 #define CLK_ACLK_ISP_DIS_400 238 #define CLK_ACLK_ISP_400 239 +#define CLK_ACLK_CAM0_333 240 +#define CLK_ACLK_CAM0_400 241 +#define CLK_ACLK_CAM0_552 242 -#define TOP_NR_CLK 240 +#define TOP_NR_CLK 243 /* CMU_CPIF */ #define CLK_FOUT_MPHY_PLL 1 @@ -1113,4 +1119,142 @@ #define ISP_NR_CLK 78 +/* CMU_CAM0 */ +#define CLK_PHYCLK_RXBYTEECLKHS0_S4_PHY 1 +#define CLK_PHYCLK_RXBYTEECLKHS0_S2A_PHY 2 + +#define CLK_MOUT_ACLK_CAM0_333_USER 3 +#define CLK_MOUT_ACLK_CAM0_400_USER 4 +#define CLK_MOUT_ACLK_CAM0_552_USER 5 +#define CLK_MOUT_PHYCLK_RXBYTECLKHS0_S4_USER 6 +#define CLK_MOUT_PHYCLK_RXBYTECLKHS0_S2A_USER 7 +#define CLK_MOUT_ACLK_LITE_D_B 8 +#define CLK_MOUT_ACLK_LITE_D_A 9 +#define CLK_MOUT_ACLK_LITE_B_B 10 +#define CLK_MOUT_ACLK_LITE_B_A 11 +#define CLK_MOUT_ACLK_LITE_A_B 12 +#define CLK_MOUT_ACLK_LITE_A_A 13 +#define CLK_MOUT_ACLK_CAM0_400 14 +#define CLK_MOUT_ACLK_CSIS1_B 15 +#define CLK_MOUT_ACLK_CSIS1_A 16 +#define CLK_MOUT_ACLK_CSIS0_B 17 +#define CLK_MOUT_ACLK_CSIS0_A 18 +#define CLK_MOUT_ACLK_3AA1_B 19 +#define CLK_MOUT_ACLK_3AA1_A 20 +#define CLK_MOUT_ACLK_3AA0_B 21 +#define CLK_MOUT_ACLK_3AA0_A 22 +#define CLK_MOUT_SCLK_LITE_FREECNT_C 23 +#define CLK_MOUT_SCLK_LITE_FREECNT_B 24 +#define CLK_MOUT_SCLK_LITE_FREECNT_A 25 +#define CLK_MOUT_SCLK_PIXELASYNC_LITE_C_B 26 +#define CLK_MOUT_SCLK_PIXELASYNC_LITE_C_A 27 +#define CLK_MOUT_SCLK_PIXELASYNC_LITE_C_INIT_B 28 +#define CLK_MOUT_SCLK_PIXELASYNC_LITE_C_INIT_A 29 + +#define CLK_DIV_PCLK_CAM0_50 30 +#define CLK_DIV_ACLK_CAM0_200 31 +#define CLK_DIV_ACLK_CAM0_BUS_400 32 +#define CLK_DIV_PCLK_LITE_D 33 +#define CLK_DIV_ACLK_LITE_D 34 +#define CLK_DIV_PCLK_LITE_B 35 +#define CLK_DIV_ACLK_LITE_B 36 +#define CLK_DIV_PCLK_LITE_A 37 +#define CLK_DIV_ACLK_LITE_A 38 +#define CLK_DIV_ACLK_CSIS1 39 +#define CLK_DIV_ACLK_CSIS0 40 +#define CLK_DIV_PCLK_3AA1 41 +#define CLK_DIV_ACLK_3AA1 42 +#define CLK_DIV_PCLK_3AA0 43 +#define CLK_DIV_ACLK_3AA0 44 +#define CLK_DIV_SCLK_PIXELASYNC_LITE_C 45 +#define CLK_DIV_PCLK_PIXELASYNC_LITE_C 46 +#define CLK_DIV_SCLK_PIXELASYNC_LITE_C_INIT 47 + +#define CLK_ACLK_CSIS1 50 +#define CLK_ACLK_CSIS0 51 +#define CLK_ACLK_3AA1 52 +#define CLK_ACLK_3AA0 53 +#define CLK_ACLK_LITE_D 54 +#define CLK_ACLK_LITE_B 55 +#define CLK_ACLK_LITE_A 56 +#define CLK_ACLK_AHBSYNCDN 57 +#define CLK_ACLK_AXIUS_LITE_D 58 +#define CLK_ACLK_AXIUS_LITE_B 59 +#define CLK_ACLK_AXIUS_LITE_A 60 +#define CLK_ACLK_ASYNCAPBM_3AA1 61 +#define CLK_ACLK_ASYNCAPBS_3AA1 62 +#define CLK_ACLK_ASYNCAPBM_3AA0 63 +#define CLK_ACLK_ASYNCAPBS_3AA0 64 +#define CLK_ACLK_ASYNCAPBM_LITE_D 65 +#define CLK_ACLK_ASYNCAPBS_LITE_D 66 +#define CLK_ACLK_ASYNCAPBM_LITE_B 67 +#define CLK_ACLK_ASYNCAPBS_LITE_B 68 +#define CLK_ACLK_ASYNCAPBM_LITE_A 69 +#define CLK_ACLK_ASYNCAPBS_LITE_A 70 +#define CLK_ACLK_ASYNCAXIM_ISP0P 71 +#define CLK_ACLK_ASYNCAXIM_3AA1 72 +#define CLK_ACLK_ASYNCAXIS_3AA1 73 +#define CLK_ACLK_ASYNCAXIM_3AA0 74 +#define CLK_ACLK_ASYNCAXIS_3AA0 75 +#define CLK_ACLK_ASYNCAXIM_LITE_D 76 +#define CLK_ACLK_ASYNCAXIS_LITE_D 77 +#define CLK_ACLK_ASYNCAXIM_LITE_B 78 +#define CLK_ACLK_ASYNCAXIS_LITE_B 79 +#define CLK_ACLK_ASYNCAXIM_LITE_A 80 +#define CLK_ACLK_ASYNCAXIS_LITE_A 81 +#define CLK_ACLK_AHB2APB_ISPSFRP 82 +#define CLK_ACLK_AXI2APB_ISP0P 83 +#define CLK_ACLK_AXI2AHB_ISP0P 84 +#define CLK_ACLK_XIU_IS0X 85 +#define CLK_ACLK_XIU_ISP0EX 86 +#define CLK_ACLK_CAM0NP_276 87 +#define CLK_ACLK_CAM0ND_400 88 +#define CLK_ACLK_SMMU_3AA1 89 +#define CLK_ACLK_SMMU_3AA0 90 +#define CLK_ACLK_SMMU_LITE_D 91 +#define CLK_ACLK_SMMU_LITE_B 92 +#define CLK_ACLK_SMMU_LITE_A 93 +#define CLK_ACLK_BTS_3AA1 94 +#define CLK_ACLK_BTS_3AA0 95 +#define CLK_ACLK_BTS_LITE_D 96 +#define CLK_ACLK_BTS_LITE_B 97 +#define CLK_ACLK_BTS_LITE_A 98 +#define CLK_PCLK_SMMU_3AA1 99 +#define CLK_PCLK_SMMU_3AA0 100 +#define CLK_PCLK_SMMU_LITE_D 101 +#define CLK_PCLK_SMMU_LITE_B 102 +#define CLK_PCLK_SMMU_LITE_A 103 +#define CLK_PCLK_BTS_3AA1 104 +#define CLK_PCLK_BTS_3AA0 105 +#define CLK_PCLK_BTS_LITE_D 106 +#define CLK_PCLK_BTS_LITE_B 107 +#define CLK_PCLK_BTS_LITE_A 108 +#define CLK_PCLK_ASYNCAXI_CAM1 109 +#define CLK_PCLK_ASYNCAXI_3AA1 110 +#define CLK_PCLK_ASYNCAXI_3AA0 111 +#define CLK_PCLK_ASYNCAXI_LITE_D 112 +#define CLK_PCLK_ASYNCAXI_LITE_B 113 +#define CLK_PCLK_ASYNCAXI_LITE_A 114 +#define CLK_PCLK_PMU_CAM0 115 +#define CLK_PCLK_SYSREG_CAM0 116 +#define CLK_PCLK_CMU_CAM0_LOCAL 117 +#define CLK_PCLK_CSIS1 118 +#define CLK_PCLK_CSIS0 119 +#define CLK_PCLK_3AA1 120 +#define CLK_PCLK_3AA0 121 +#define CLK_PCLK_LITE_D 122 +#define CLK_PCLK_LITE_B 123 +#define CLK_PCLK_LITE_A 124 +#define CLK_PHYCLK_RXBYTECLKHS0_S4 125 +#define CLK_PHYCLK_RXBYTECLKHS0_S2A 126 +#define CLK_SCLK_LITE_FREECNT 127 +#define CLK_SCLK_PIXELASYNCM_3AA1 128 +#define CLK_SCLK_PIXELASYNCM_3AA0 129 +#define CLK_SCLK_PIXELASYNCS_3AA0 130 +#define CLK_SCLK_PIXELASYNCM_LITE_C 131 +#define CLK_SCLK_PIXELASYNCM_LITE_C_INIT 132 +#define CLK_SCLK_PIXELASYNCS_LITE_C_INIT 133 + +#define CAM0_NR_CLK 134 + #endif /* _DT_BINDINGS_CLOCK_EXYNOS5433_H */ -- cgit v1.2.3 From a5958a939bbf93e6b77cb3626c6aebde237ad759 Mon Sep 17 00:00:00 2001 From: Chanwoo Choi Date: Tue, 3 Feb 2015 09:13:56 +0900 Subject: clk: samsung: exynos5433: Add clocks for CMU_CAM1 domain This patch adds the mux/divider/gate clocks for CMU_CAM1 domain which generates the clocks for Cortex-A5/MIPI_CSIS2/FIMC-LITE_C/FIMC-FD IPs. Signed-off-by: Chanwoo Choi Acked-by: Inki Dae Signed-off-by: Sylwester Nawrocki --- .../devicetree/bindings/clock/exynos5433-clock.txt | 32 ++ drivers/clk/samsung/clk-exynos5433.c | 435 +++++++++++++++++++++ include/dt-bindings/clock/exynos5433.h | 147 ++++++- 3 files changed, 612 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/Documentation/devicetree/bindings/clock/exynos5433-clock.txt b/Documentation/devicetree/bindings/clock/exynos5433-clock.txt index 84002e4b52e5..63379b04e052 100644 --- a/Documentation/devicetree/bindings/clock/exynos5433-clock.txt +++ b/Documentation/devicetree/bindings/clock/exynos5433-clock.txt @@ -48,6 +48,8 @@ Required Properties: - "samsung,exynos5433-cmu-cam0" - clock controller compatible for CMU_CAM0 which generates clocks for MIPI_CSIS{0|1}/FIMC_LITE_{A|B|D}/FIMC_3AA{0|1} IPs. + - "samsung,exynos5433-cmu-cam1" - clock controller compatible for CMU_CAM1 + which generates clocks for Cortex-A5/MIPI_CSIS2/FIMC-LITE_C/FIMC-FD IPs. - reg: physical base address of the controller and length of memory mapped region. @@ -153,6 +155,15 @@ Required Properties: - aclk_cam0_400 - aclk_cam0_552 + Input clocks for cam1 clock controller: + - oscclk + - sclk_isp_uart_cam1 + - sclk_isp_spi1_cam1 + - sclk_isp_spi0_cam1 + - aclk_cam1_333 + - aclk_cam1_400 + - aclk_cam1_552 + Each clock is assigned an identifier and client nodes can use this identifier to specify the clock which they consume. @@ -414,6 +425,27 @@ Example 2: Examples of clock controller nodes are listed below. <&cmu_top CLK_ACLK_CAM0_552>; }; + cmu_cam1: clock-controller@145d0000 { + compatible = "samsung,exynos5433-cmu-cam1"; + reg = <0x145d0000 0x0b08>; + #clock-cells = <1>; + + clock-names = "oscclk", + "sclk_isp_uart_cam1", + "sclk_isp_spi1_cam1", + "sclk_isp_spi0_cam1", + "aclk_cam1_333", + "aclk_cam1_400", + "aclk_cam1_552"; + clocks = <&xxti>, + <&cmu_top CLK_SCLK_ISP_UART_CAM1>, + <&cmu_top CLK_SCLK_ISP_SPI1_CAM1>, + <&cmu_top CLK_SCLK_ISP_SPI0_CAM1>, + <&cmu_top CLK_ACLK_CAM1_333>, + <&cmu_top CLK_ACLK_CAM1_400>, + <&cmu_top CLK_ACLK_CAM1_552>; + }; + Example 3: UART controller node that consumes the clock generated by the clock controller. diff --git a/drivers/clk/samsung/clk-exynos5433.c b/drivers/clk/samsung/clk-exynos5433.c index ce6487375670..1a005c1f7c4b 100644 --- a/drivers/clk/samsung/clk-exynos5433.c +++ b/drivers/clk/samsung/clk-exynos5433.c @@ -405,6 +405,12 @@ static struct samsung_mux_clock top_mux_clks[] __initdata = { static struct samsung_div_clock top_div_clks[] __initdata = { /* DIV_TOP0 */ + DIV(CLK_DIV_ACLK_CAM1_333, "div_aclk_cam1_333", "mout_aclk_cam1_333", + DIV_TOP0, 28, 3), + DIV(CLK_DIV_ACLK_CAM1_400, "div_aclk_cam1_400", "mout_bus_pll_user", + DIV_TOP0, 24, 3), + DIV(CLK_DIV_ACLK_CAM1_552, "div_aclk_cam1_552", "mout_aclk_cam1_552_b", + DIV_TOP0, 20, 3), DIV(CLK_DIV_ACLK_CAM0_333, "div_aclk_cam0_333", "mout_mfc_pll_user", DIV_TOP0, 16, 3), DIV(CLK_DIV_ACLK_CAM0_400, "div_aclk_cam0_400", "mout_bus_pll_user", @@ -464,6 +470,32 @@ static struct samsung_div_clock top_div_clks[] __initdata = { DIV(CLK_DIV_SCLK_JPEG, "div_sclk_jpeg", "mout_sclk_jpeg_c", DIV_TOP_MSCL, 0, 4), + /* DIV_TOP_CAM10 */ + DIV(CLK_DIV_SCLK_ISP_UART, "div_sclk_isp_uart", "mout_sclk_isp_uart", + DIV_TOP_CAM10, 24, 5), + DIV(CLK_DIV_SCLK_ISP_SPI1_B, "div_sclk_isp_spi1_b", + "div_sclk_isp_spi1_a", DIV_TOP_CAM10, 16, 8), + DIV(CLK_DIV_SCLK_ISP_SPI1_A, "div_sclk_isp_spi1_a", + "mout_sclk_isp_spi1", DIV_TOP_CAM10, 12, 4), + DIV(CLK_DIV_SCLK_ISP_SPI0_B, "div_sclk_isp_spi0_b", + "div_sclk_isp_spi0_a", DIV_TOP_CAM10, 4, 8), + DIV(CLK_DIV_SCLK_ISP_SPI0_A, "div_sclk_isp_spi0_a", + "mout_sclk_isp_spi0", DIV_TOP_CAM10, 0, 4), + + /* DIV_TOP_CAM11 */ + DIV(CLK_DIV_SCLK_ISP_SENSOR2_B, "div_sclk_isp_sensor2_b", + "div_sclk_isp_sensor2_a", DIV_TOP_CAM11, 20, 4), + DIV(CLK_DIV_SCLK_ISP_SENSOR2_A, "div_sclk_isp_sensor2_a", + "mout_sclk_isp_sensor2", DIV_TOP_CAM11, 16, 4), + DIV(CLK_DIV_SCLK_ISP_SENSOR1_B, "div_sclk_isp_sensor1_b", + "div_sclk_isp_sensor1_a", DIV_TOP_CAM11, 12, 4), + DIV(CLK_DIV_SCLK_ISP_SENSOR1_A, "div_sclk_isp_sensor1_a", + "mout_sclk_isp_sensor1", DIV_TOP_CAM11, 8, 4), + DIV(CLK_DIV_SCLK_ISP_SENSOR0_B, "div_sclk_isp_sensor0_b", + "div_sclk_isp_sensor0_a", DIV_TOP_CAM11, 12, 4), + DIV(CLK_DIV_SCLK_ISP_SENSOR0_A, "div_sclk_isp_sensor0_a", + "mout_sclk_isp_sensor0", DIV_TOP_CAM11, 8, 4), + /* DIV_TOP_FSYS0 */ DIV(CLK_DIV_SCLK_MMC1_B, "div_sclk_mmc1_b", "div_sclk_mmc1_a", DIV_TOP_FSYS0, 16, 8), @@ -572,6 +604,15 @@ static struct samsung_gate_clock top_gate_clks[] __initdata = { GATE(CLK_ACLK_GSCL_333, "aclk_gscl_333", "div_aclk_gscl_333", ENABLE_ACLK_TOP, 14, CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_CAM1_333, "aclk_cam1_333", "div_aclk_cam1_333", + ENABLE_ACLK_TOP, 13, + CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_CAM1_400, "aclk_cam1_400", "div_aclk_cam1_400", + ENABLE_ACLK_TOP, 12, + CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_CAM1_552, "aclk_cam1_552", "div_aclk_cam1_552", + ENABLE_ACLK_TOP, 11, + CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED, 0), GATE(CLK_ACLK_CAM0_333, "aclk_cam0_333", "div_aclk_cam0_333", ENABLE_ACLK_TOP, 10, CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED, 0), @@ -604,6 +645,22 @@ static struct samsung_gate_clock top_gate_clks[] __initdata = { GATE(CLK_SCLK_JPEG_MSCL, "sclk_jpeg_mscl", "div_sclk_jpeg", ENABLE_SCLK_TOP_MSCL, 0, 0, 0), + /* ENABLE_SCLK_TOP_CAM1 */ + GATE(CLK_SCLK_ISP_SENSOR2, "sclk_isp_sensor2", "div_sclk_isp_sensor2_b", + ENABLE_SCLK_TOP_CAM1, 7, 0, 0), + GATE(CLK_SCLK_ISP_SENSOR1, "sclk_isp_sensor1", "div_sclk_isp_sensor1_b", + ENABLE_SCLK_TOP_CAM1, 6, 0, 0), + GATE(CLK_SCLK_ISP_SENSOR0, "sclk_isp_sensor0", "div_sclk_isp_sensor0_b", + ENABLE_SCLK_TOP_CAM1, 5, 0, 0), + GATE(CLK_SCLK_ISP_MCTADC_CAM1, "sclk_isp_mctadc_cam1", "oscclk", + ENABLE_SCLK_TOP_CAM1, 4, 0, 0), + GATE(CLK_SCLK_ISP_UART_CAM1, "sclk_isp_uart_cam1", "div_sclk_isp_uart", + ENABLE_SCLK_TOP_CAM1, 2, 0, 0), + GATE(CLK_SCLK_ISP_SPI1_CAM1, "sclk_isp_spi1_cam1", "div_sclk_isp_spi1_b", + ENABLE_SCLK_TOP_CAM1, 1, 0, 0), + GATE(CLK_SCLK_ISP_SPI0_CAM1, "sclk_isp_spi0_cam1", "div_sclk_isp_spi0_b", + ENABLE_SCLK_TOP_CAM1, 0, 0, 0), + /* ENABLE_SCLK_TOP_FSYS */ GATE(CLK_SCLK_PCIE_100_FSYS, "sclk_pcie_100_fsys", "div_sclk_pcie_100", ENABLE_SCLK_TOP_FSYS, 7, 0, 0), @@ -4986,3 +5043,381 @@ static void __init exynos5433_cmu_cam0_init(struct device_node *np) } CLK_OF_DECLARE(exynos5433_cmu_cam0, "samsung,exynos5433-cmu-cam0", exynos5433_cmu_cam0_init); + +/* + * Register offset definitions for CMU_CAM1 + */ +#define MUX_SEL_CAM10 0x0200 +#define MUX_SEL_CAM11 0x0204 +#define MUX_SEL_CAM12 0x0208 +#define MUX_ENABLE_CAM10 0x0300 +#define MUX_ENABLE_CAM11 0x0304 +#define MUX_ENABLE_CAM12 0x0308 +#define MUX_STAT_CAM10 0x0400 +#define MUX_STAT_CAM11 0x0404 +#define MUX_STAT_CAM12 0x0408 +#define MUX_IGNORE_CAM11 0x0504 +#define DIV_CAM10 0x0600 +#define DIV_CAM11 0x0604 +#define DIV_STAT_CAM10 0x0700 +#define DIV_STAT_CAM11 0x0704 +#define ENABLE_ACLK_CAM10 0X0800 +#define ENABLE_ACLK_CAM11 0X0804 +#define ENABLE_ACLK_CAM12 0X0808 +#define ENABLE_PCLK_CAM1 0X0900 +#define ENABLE_SCLK_CAM1 0X0a00 +#define ENABLE_IP_CAM10 0X0b00 +#define ENABLE_IP_CAM11 0X0b04 +#define ENABLE_IP_CAM12 0X0b08 + +static unsigned long cam1_clk_regs[] __initdata = { + MUX_SEL_CAM10, + MUX_SEL_CAM11, + MUX_SEL_CAM12, + MUX_ENABLE_CAM10, + MUX_ENABLE_CAM11, + MUX_ENABLE_CAM12, + MUX_STAT_CAM10, + MUX_STAT_CAM11, + MUX_STAT_CAM12, + MUX_IGNORE_CAM11, + DIV_CAM10, + DIV_CAM11, + DIV_STAT_CAM10, + DIV_STAT_CAM11, + ENABLE_ACLK_CAM10, + ENABLE_ACLK_CAM11, + ENABLE_ACLK_CAM12, + ENABLE_PCLK_CAM1, + ENABLE_SCLK_CAM1, + ENABLE_IP_CAM10, + ENABLE_IP_CAM11, + ENABLE_IP_CAM12, +}; + +PNAME(mout_sclk_isp_uart_user_p) = { "oscclk", "sclk_isp_uart_cam1", }; +PNAME(mout_sclk_isp_spi1_user_p) = { "oscclk", "sclk_isp_spi1_cam1", }; +PNAME(mout_sclk_isp_spi0_user_p) = { "oscclk", "sclk_isp_spi0_cam1", }; + +PNAME(mout_aclk_cam1_333_user_p) = { "oscclk", "aclk_cam1_333", }; +PNAME(mout_aclk_cam1_400_user_p) = { "oscclk", "aclk_cam1_400", }; +PNAME(mout_aclk_cam1_552_user_p) = { "oscclk", "aclk_cam1_552", }; + +PNAME(mout_phyclk_rxbyteclkhs0_s2b_user_p) = { "oscclk", + "phyclk_rxbyteclkhs0_s2b_phy", }; + +PNAME(mout_aclk_csis2_b_p) = { "mout_aclk_csis2_a", + "mout_aclk_cam1_333_user", }; +PNAME(mout_aclk_csis2_a_p) = { "mout_aclk_cam1_552_user", + "mout_aclk_cam1_400_user", }; + +PNAME(mout_aclk_fd_b_p) = { "mout_aclk_fd_a", + "mout_aclk_cam1_333_user", }; +PNAME(mout_aclk_fd_a_p) = { "mout_aclk_cam1_552_user", + "mout_aclk_cam1_400_user", }; + +PNAME(mout_aclk_lite_c_b_p) = { "mout_aclk_lite_c_a", + "mout_aclk_cam1_333_user", }; +PNAME(mout_aclk_lite_c_a_p) = { "mout_aclk_cam1_552_user", + "mout_aclk_cam1_400_user", }; + +static struct samsung_fixed_rate_clock cam1_fixed_clks[] __initdata = { + FRATE(CLK_PHYCLK_RXBYTEECLKHS0_S2B, "phyclk_rxbyteclkhs0_s2b_phy", NULL, + CLK_IS_ROOT, 100000000), +}; + +static struct samsung_mux_clock cam1_mux_clks[] __initdata = { + /* MUX_SEL_CAM10 */ + MUX(CLK_MOUT_SCLK_ISP_UART_USER, "mout_sclk_isp_uart_user", + mout_sclk_isp_uart_user_p, MUX_SEL_CAM10, 20, 1), + MUX(CLK_MOUT_SCLK_ISP_SPI1_USER, "mout_sclk_isp_spi1_user", + mout_sclk_isp_spi1_user_p, MUX_SEL_CAM10, 16, 1), + MUX(CLK_MOUT_SCLK_ISP_SPI0_USER, "mout_sclk_isp_spi0_user", + mout_sclk_isp_spi0_user_p, MUX_SEL_CAM10, 12, 1), + MUX(CLK_MOUT_ACLK_CAM1_333_USER, "mout_aclk_cam1_333_user", + mout_aclk_cam1_333_user_p, MUX_SEL_CAM10, 8, 1), + MUX(CLK_MOUT_ACLK_CAM1_400_USER, "mout_aclk_cam1_400_user", + mout_aclk_cam1_400_user_p, MUX_SEL_CAM01, 4, 1), + MUX(CLK_MOUT_ACLK_CAM1_552_USER, "mout_aclk_cam1_552_user", + mout_aclk_cam1_552_user_p, MUX_SEL_CAM01, 0, 1), + + /* MUX_SEL_CAM11 */ + MUX(CLK_MOUT_PHYCLK_RXBYTECLKHS0_S2B_USER, + "mout_phyclk_rxbyteclkhs0_s2b_user", + mout_phyclk_rxbyteclkhs0_s2b_user_p, + MUX_SEL_CAM11, 0, 1), + + /* MUX_SEL_CAM12 */ + MUX(CLK_MOUT_ACLK_CSIS2_B, "mout_aclk_csis2_b", mout_aclk_csis2_b_p, + MUX_SEL_CAM12, 20, 1), + MUX(CLK_MOUT_ACLK_CSIS2_A, "mout_aclk_csis2_a", mout_aclk_csis2_a_p, + MUX_SEL_CAM12, 16, 1), + MUX(CLK_MOUT_ACLK_FD_B, "mout_aclk_fd_b", mout_aclk_fd_b_p, + MUX_SEL_CAM12, 12, 1), + MUX(CLK_MOUT_ACLK_FD_A, "mout_aclk_fd_a", mout_aclk_fd_a_p, + MUX_SEL_CAM12, 8, 1), + MUX(CLK_MOUT_ACLK_LITE_C_B, "mout_aclk_lite_c_b", mout_aclk_lite_c_b_p, + MUX_SEL_CAM12, 4, 1), + MUX(CLK_MOUT_ACLK_LITE_C_A, "mout_aclk_lite_c_a", mout_aclk_lite_c_a_p, + MUX_SEL_CAM12, 0, 1), +}; + +static struct samsung_div_clock cam1_div_clks[] __initdata = { + /* DIV_CAM10 */ + DIV(CLK_DIV_SCLK_ISP_WPWM, "div_sclk_isp_wpwm", + "div_pclk_cam1_83", DIV_CAM10, 16, 2), + DIV(CLK_DIV_PCLK_CAM1_83, "div_pclk_cam1_83", + "mout_aclk_cam1_333_user", DIV_CAM10, 12, 2), + DIV(CLK_DIV_PCLK_CAM1_166, "div_pclk_cam1_166", + "mout_aclk_cam1_333_user", DIV_CAM10, 8, 2), + DIV(CLK_DIV_PCLK_DBG_CAM1, "div_pclk_dbg_cam1", + "mout_aclk_cam1_552_user", DIV_CAM10, 4, 3), + DIV(CLK_DIV_ATCLK_CAM1, "div_atclk_cam1", "mout_aclk_cam1_552_user", + DIV_CAM10, 0, 3), + + /* DIV_CAM11 */ + DIV(CLK_DIV_ACLK_CSIS2, "div_aclk_csis2", "mout_aclk_csis2_b", + DIV_CAM11, 16, 3), + DIV(CLK_DIV_PCLK_FD, "div_pclk_fd", "div_aclk_fd", DIV_CAM11, 12, 2), + DIV(CLK_DIV_ACLK_FD, "div_aclk_fd", "mout_aclk_fd_b", DIV_CAM11, 8, 3), + DIV(CLK_DIV_PCLK_LITE_C, "div_pclk_lite_c", "div_aclk_lite_c", + DIV_CAM11, 4, 2), + DIV(CLK_DIV_ACLK_LITE_C, "div_aclk_lite_c", "mout_aclk_lite_c_b", + DIV_CAM11, 0, 3), +}; + +static struct samsung_gate_clock cam1_gate_clks[] __initdata = { + /* ENABLE_ACLK_CAM10 */ + GATE(CLK_ACLK_ISP_GIC, "aclk_isp_gic", "mout_aclk_cam1_333_user", + ENABLE_ACLK_CAM10, 4, 0, 0), + GATE(CLK_ACLK_FD, "aclk_fd", "div_aclk_fd", + ENABLE_ACLK_CAM10, 3, 0, 0), + GATE(CLK_ACLK_LITE_C, "aclk_lite_c", "div_aclk_lite_c", + ENABLE_ACLK_CAM10, 1, 0, 0), + GATE(CLK_ACLK_CSIS2, "aclk_csis2", "div_aclk_csis2", + ENABLE_ACLK_CAM10, 0, 0, 0), + + /* ENABLE_ACLK_CAM11 */ + GATE(CLK_ACLK_ASYNCAPBM_FD, "aclk_asyncapbm_fd", "div_pclk_fd", + ENABLE_ACLK_CAM11, 29, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_ASYNCAPBS_FD, "aclk_asyncapbs_fd", "div_pclk_cam1_166", + ENABLE_ACLK_CAM11, 28, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_ASYNCAPBM_LITE_C, "aclk_asyncapbm_lite_c", + "div_pclk_lite_c", ENABLE_ACLK_CAM11, + 27, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_ASYNCAPBS_LITE_C, "aclk_asyncapbs_lite_c", + "div_pclk_cam1_166", ENABLE_ACLK_CAM11, + 26, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_ASYNCAHBS_SFRISP2H2, "aclk_asyncahbs_sfrisp2h2", + "div_pclk_cam1_83", ENABLE_ACLK_CAM11, + 25, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_ASYNCAHBS_SFRISP2H1, "aclk_asyncahbs_sfrisp2h1", + "div_pclk_cam1_83", ENABLE_ACLK_CAM11, + 24, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_ASYNCAXIM_CA5, "aclk_asyncaxim_ca5", + "mout_aclk_cam1_333_user", ENABLE_ACLK_CAM11, + 23, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_ASYNCAXIS_CA5, "aclk_asyncaxis_ca5", + "mout_aclk_cam1_552_user", ENABLE_ACLK_CAM11, + 22, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_ASYNCAXIS_ISPX2, "aclk_asyncaxis_ispx2", + "mout_aclk_cam1_333_user", ENABLE_ACLK_CAM11, + 21, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_ASYNCAXIS_ISPX1, "aclk_asyncaxis_ispx1", + "mout_aclk_cam1_333_user", ENABLE_ACLK_CAM11, + 20, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_ASYNCAXIS_ISPX0, "aclk_asyncaxis_ispx0", + "mout_aclk_cam1_333_user", ENABLE_ACLK_CAM11, + 19, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_ASYNCAXIM_ISPEX, "aclk_asyncaxim_ispex", + "mout_aclk_cam1_400_user", ENABLE_ACLK_CAM11, + 18, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_ASYNCAXIM_ISP3P, "aclk_asyncaxim_isp3p", + "mout_aclk_cam1_400_user", ENABLE_ACLK_CAM11, + 17, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_ASYNCAXIS_ISP3P, "aclk_asyncaxis_isp3p", + "mout_aclk_cam1_333_user", ENABLE_ACLK_CAM11, + 16, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_ASYNCAXIM_FD, "aclk_asyncaxim_fd", + "mout_aclk_cam1_400_user", ENABLE_ACLK_CAM11, + 15, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_ASYNCAXIS_FD, "aclk_asyncaxis_fd", "div_aclk_fd", + ENABLE_ACLK_CAM11, 14, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_ASYNCAXIM_LITE_C, "aclk_asyncaxim_lite_c", + "mout_aclk_cam1_400_user", ENABLE_ACLK_CAM11, + 13, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_ASYNCAXIS_LITE_C, "aclk_asyncaxis_lite_c", + "div_aclk_lite_c", ENABLE_ACLK_CAM11, + 12, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_AHB2APB_ISP5P, "aclk_ahb2apb_isp5p", "div_pclk_cam1_83", + ENABLE_ACLK_CAM11, 11, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_AHB2APB_ISP3P, "aclk_ahb2apb_isp3p", "div_pclk_cam1_83", + ENABLE_ACLK_CAM11, 10, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_AXI2APB_ISP3P, "aclk_axi2apb_isp3p", + "mout_aclk_cam1_333_user", ENABLE_ACLK_CAM11, + 9, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_AHB_SFRISP2H, "aclk_ahb_sfrisp2h", "div_pclk_cam1_83", + ENABLE_ACLK_CAM11, 8, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_AXI_ISP_HX_R, "aclk_axi_isp_hx_r", "div_pclk_cam1_166", + ENABLE_ACLK_CAM11, 7, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_AXI_ISP_CX_R, "aclk_axi_isp_cx_r", "div_pclk_cam1_166", + ENABLE_ACLK_CAM11, 6, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_AXI_ISP_HX, "aclk_axi_isp_hx", "mout_aclk_cam1_333_user", + ENABLE_ACLK_CAM11, 5, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_AXI_ISP_CX, "aclk_axi_isp_cx", "mout_aclk_cam1_333_user", + ENABLE_ACLK_CAM11, 4, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_XIU_ISPX, "aclk_xiu_ispx", "mout_aclk_cam1_333_user", + ENABLE_ACLK_CAM11, 3, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_XIU_ISPEX, "aclk_xiu_ispex", "mout_aclk_cam1_400_user", + ENABLE_ACLK_CAM11, 2, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_CAM1NP_333, "aclk_cam1np_333", "mout_aclk_cam1_333_user", + ENABLE_ACLK_CAM11, 1, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_CAM1ND_400, "aclk_cam1nd_400", "mout_aclk_cam1_400_user", + ENABLE_ACLK_CAM11, 0, CLK_IGNORE_UNUSED, 0), + + /* ENABLE_ACLK_CAM12 */ + GATE(CLK_ACLK_SMMU_ISPCPU, "aclk_smmu_ispcpu", + "mout_aclk_cam1_400_user", ENABLE_ACLK_CAM12, + 10, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_SMMU_FD, "aclk_smmu_fd", "mout_aclk_cam1_400_user", + ENABLE_ACLK_CAM12, 9, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_SMMU_LITE_C, "aclk_smmu_lite_c", + "mout_aclk_cam1_400_user", ENABLE_ACLK_CAM12, + 8, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_BTS_ISP3P, "aclk_bts_isp3p", "mout_aclk_cam1_400_user", + ENABLE_ACLK_CAM12, 7, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_BTS_FD, "aclk_bts_fd", "mout_aclk_cam1_400_user", + ENABLE_ACLK_CAM12, 6, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_BTS_LITE_C, "aclk_bts_lite_c", "mout_aclk_cam1_400_user", + ENABLE_ACLK_CAM12, 5, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_AHBDN_SFRISP2H, "aclk_ahbdn_sfrisp2h", + "mout_aclk_cam1_333_user", ENABLE_ACLK_CAM12, + 4, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_AHBDN_ISP5P, "aclk_aclk-shbdn_isp5p", + "mout_aclk_cam1_333_user", ENABLE_ACLK_CAM12, + 3, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_AXIUS_ISP3P, "aclk_axius_isp3p", + "mout_aclk_cam1_400_user", ENABLE_ACLK_CAM12, + 2, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_AXIUS_FD, "aclk_axius_fd", "mout_aclk_cam1_400_user", + ENABLE_ACLK_CAM12, 1, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ACLK_AXIUS_LITE_C, "aclk_axius_lite_c", + "mout_aclk_cam1_400_user", ENABLE_ACLK_CAM12, + 0, CLK_IGNORE_UNUSED, 0), + + /* ENABLE_PCLK_CAM1 */ + GATE(CLK_PCLK_SMMU_ISPCPU, "pclk_smmu_ispcpu", "div_pclk_cam1_166", + ENABLE_PCLK_CAM1, 27, CLK_IGNORE_UNUSED, 0), + GATE(CLK_PCLK_SMMU_FD, "pclk_smmu_fd", "div_pclk_cam1_166", + ENABLE_PCLK_CAM1, 26, CLK_IGNORE_UNUSED, 0), + GATE(CLK_PCLK_SMMU_LITE_C, "pclk_smmu_lite_c", "div_pclk_cam1_166", + ENABLE_PCLK_CAM1, 25, CLK_IGNORE_UNUSED, 0), + GATE(CLK_PCLK_BTS_ISP3P, "pclk_bts_isp3p", "div_pclk_cam1_83", + ENABLE_PCLK_CAM1, 24, CLK_IGNORE_UNUSED, 0), + GATE(CLK_PCLK_BTS_FD, "pclk_bts_fd", "div_pclk_cam1_83", + ENABLE_PCLK_CAM1, 23, CLK_IGNORE_UNUSED, 0), + GATE(CLK_PCLK_BTS_LITE_C, "pclk_bts_lite_c", "div_pclk_cam1_83", + ENABLE_PCLK_CAM1, 22, CLK_IGNORE_UNUSED, 0), + GATE(CLK_PCLK_ASYNCAXIM_CA5, "pclk_asyncaxim_ca5", "div_pclk_cam1_166", + ENABLE_PCLK_CAM1, 21, CLK_IGNORE_UNUSED, 0), + GATE(CLK_PCLK_ASYNCAXIM_ISPEX, "pclk_asyncaxim_ispex", + "div_pclk_cam1_83", ENABLE_PCLK_CAM1, + 20, CLK_IGNORE_UNUSED, 0), + GATE(CLK_PCLK_ASYNCAXIM_ISP3P, "pclk_asyncaxim_isp3p", + "div_pclk_cam1_83", ENABLE_PCLK_CAM1, + 19, CLK_IGNORE_UNUSED, 0), + GATE(CLK_PCLK_ASYNCAXIM_FD, "pclk_asyncaxim_fd", "div_pclk_cam1_83", + ENABLE_PCLK_CAM1, 18, CLK_IGNORE_UNUSED, 0), + GATE(CLK_PCLK_ASYNCAXIM_LITE_C, "pclk_asyncaxim_lite_c", + "div_pclk_cam1_83", ENABLE_PCLK_CAM1, + 17, CLK_IGNORE_UNUSED, 0), + GATE(CLK_PCLK_PMU_CAM1, "pclk_pmu_cam1", "div_pclk_cam1_83", + ENABLE_PCLK_CAM1, 16, CLK_IGNORE_UNUSED, 0), + GATE(CLK_PCLK_SYSREG_CAM1, "pclk_sysreg_cam1", "div_pclk_cam1_83", + ENABLE_PCLK_CAM1, 15, CLK_IGNORE_UNUSED, 0), + GATE(CLK_PCLK_CMU_CAM1_LOCAL, "pclk_cmu_cam1_local", + "div_pclk_cam1_166", ENABLE_PCLK_CAM1, + 14, CLK_IGNORE_UNUSED, 0), + GATE(CLK_PCLK_ISP_MCTADC, "pclk_isp_mctadc", "div_pclk_cam1_83", + ENABLE_PCLK_CAM1, 13, CLK_IGNORE_UNUSED, 0), + GATE(CLK_PCLK_ISP_WDT, "pclk_isp_wdt", "div_pclk_cam1_83", + ENABLE_PCLK_CAM1, 12, CLK_IGNORE_UNUSED, 0), + GATE(CLK_PCLK_ISP_PWM, "pclk_isp_pwm", "div_pclk_cam1_83", + ENABLE_PCLK_CAM1, 11, CLK_IGNORE_UNUSED, 0), + GATE(CLK_PCLK_ISP_UART, "pclk_isp_uart", "div_pclk_cam1_83", + ENABLE_PCLK_CAM1, 10, CLK_IGNORE_UNUSED, 0), + GATE(CLK_PCLK_ISP_MCUCTL, "pclk_isp_mcuctl", "div_pclk_cam1_83", + ENABLE_PCLK_CAM1, 9, CLK_IGNORE_UNUSED, 0), + GATE(CLK_PCLK_ISP_SPI1, "pclk_isp_spi1", "div_pclk_cam1_83", + ENABLE_PCLK_CAM1, 8, CLK_IGNORE_UNUSED, 0), + GATE(CLK_PCLK_ISP_SPI0, "pclk_isp_spi0", "div_pclk_cam1_83", + ENABLE_PCLK_CAM1, 7, CLK_IGNORE_UNUSED, 0), + GATE(CLK_PCLK_ISP_I2C2, "pclk_isp_i2c2", "div_pclk_cam1_83", + ENABLE_PCLK_CAM1, 6, CLK_IGNORE_UNUSED, 0), + GATE(CLK_PCLK_ISP_I2C1, "pclk_isp_i2c1", "div_pclk_cam1_83", + ENABLE_PCLK_CAM1, 5, CLK_IGNORE_UNUSED, 0), + GATE(CLK_PCLK_ISP_I2C0, "pclk_isp_i2c0", "div_pclk_cam1_83", + ENABLE_PCLK_CAM1, 4, CLK_IGNORE_UNUSED, 0), + GATE(CLK_PCLK_ISP_MPWM, "pclk_isp_wpwm", "div_pclk_cam1_83", + ENABLE_PCLK_CAM1, 3, CLK_IGNORE_UNUSED, 0), + GATE(CLK_PCLK_FD, "pclk_fd", "div_pclk_fd", + ENABLE_PCLK_CAM1, 3, CLK_IGNORE_UNUSED, 0), + GATE(CLK_PCLK_LITE_C, "pclk_lite_c", "div_pclk_lite_c", + ENABLE_PCLK_CAM1, 1, CLK_IGNORE_UNUSED, 0), + GATE(CLK_PCLK_CSIS2, "pclk_csis2", "div_pclk_cam1_166", + ENABLE_PCLK_CAM1, 0, CLK_IGNORE_UNUSED, 0), + + /* ENABLE_SCLK_CAM1 */ + GATE(CLK_SCLK_ISP_I2C2, "sclk_isp_i2c2", "oscclk", ENABLE_SCLK_CAM1, + 15, 0, 0), + GATE(CLK_SCLK_ISP_I2C1, "sclk_isp_i2c1", "oscclk", ENABLE_SCLK_CAM1, + 14, 0, 0), + GATE(CLK_SCLK_ISP_I2C0, "sclk_isp_i2c0", "oscclk", ENABLE_SCLK_CAM1, + 13, 0, 0), + GATE(CLK_SCLK_ISP_PWM, "sclk_isp_pwm", "oscclk", ENABLE_SCLK_CAM1, + 12, 0, 0), + GATE(CLK_PHYCLK_RXBYTECLKHS0_S2B, "phyclk_rxbyteclkhs0_s2b", + "mout_phyclk_rxbyteclkhs0_s2b_user", + ENABLE_SCLK_CAM1, 11, 0, 0), + GATE(CLK_SCLK_LITE_C_FREECNT, "sclk_lite_c_freecnt", "div_pclk_lite_c", + ENABLE_SCLK_CAM1, 10, 0, 0), + GATE(CLK_SCLK_PIXELASYNCM_FD, "sclk_pixelasyncm_fd", "div_aclk_fd", + ENABLE_SCLK_CAM1, 9, 0, 0), + GATE(CLK_SCLK_ISP_MCTADC, "sclk_isp_mctadc", "sclk_isp_mctadc_cam1", + ENABLE_SCLK_CAM1, 7, 0, 0), + GATE(CLK_SCLK_ISP_UART, "sclk_isp_uart", "mout_sclk_isp_uart_user", + ENABLE_SCLK_CAM1, 6, 0, 0), + GATE(CLK_SCLK_ISP_SPI1, "sclk_isp_spi1", "mout_sclk_isp_spi1_user", + ENABLE_SCLK_CAM1, 5, 0, 0), + GATE(CLK_SCLK_ISP_SPI0, "sclk_isp_spi0", "mout_sclk_isp_spi0_user", + ENABLE_SCLK_CAM1, 4, 0, 0), + GATE(CLK_SCLK_ISP_MPWM, "sclk_isp_wpwm", "div_sclk_isp_wpwm", + ENABLE_SCLK_CAM1, 3, 0, 0), + GATE(CLK_PCLK_DBG_ISP, "sclk_dbg_isp", "div_pclk_dbg_cam1", + ENABLE_SCLK_CAM1, 2, 0, 0), + GATE(CLK_ATCLK_ISP, "atclk_isp", "div_atclk_cam1", + ENABLE_SCLK_CAM1, 1, 0, 0), + GATE(CLK_SCLK_ISP_CA5, "sclk_isp_ca5", "mout_aclk_cam1_552_user", + ENABLE_SCLK_CAM1, 0, 0, 0), +}; + +static struct samsung_cmu_info cam1_cmu_info __initdata = { + .mux_clks = cam1_mux_clks, + .nr_mux_clks = ARRAY_SIZE(cam1_mux_clks), + .div_clks = cam1_div_clks, + .nr_div_clks = ARRAY_SIZE(cam1_div_clks), + .gate_clks = cam1_gate_clks, + .nr_gate_clks = ARRAY_SIZE(cam1_gate_clks), + .fixed_clks = cam1_fixed_clks, + .nr_fixed_clks = ARRAY_SIZE(cam1_fixed_clks), + .nr_clk_ids = CAM1_NR_CLK, + .clk_regs = cam1_clk_regs, + .nr_clk_regs = ARRAY_SIZE(cam1_clk_regs), +}; + +static void __init exynos5433_cmu_cam1_init(struct device_node *np) +{ + samsung_cmu_register_one(np, &cam1_cmu_info); +} +CLK_OF_DECLARE(exynos5433_cmu_cam1, "samsung,exynos5433-cmu-cam1", + exynos5433_cmu_cam1_init); diff --git a/include/dt-bindings/clock/exynos5433.h b/include/dt-bindings/clock/exynos5433.h index f99cde7a278d..4853bc598b57 100644 --- a/include/dt-bindings/clock/exynos5433.h +++ b/include/dt-bindings/clock/exynos5433.h @@ -121,6 +121,20 @@ #define CLK_DIV_ACLK_CAM0_333 148 #define CLK_DIV_ACLK_CAM0_400 149 #define CLK_DIV_ACLK_CAM0_552 150 +#define CLK_DIV_ACLK_CAM1_333 151 +#define CLK_DIV_ACLK_CAM1_400 152 +#define CLK_DIV_ACLK_CAM1_552 153 +#define CLK_DIV_SCLK_ISP_UART 154 +#define CLK_DIV_SCLK_ISP_SPI1_B 155 +#define CLK_DIV_SCLK_ISP_SPI1_A 156 +#define CLK_DIV_SCLK_ISP_SPI0_B 157 +#define CLK_DIV_SCLK_ISP_SPI0_A 158 +#define CLK_DIV_SCLK_ISP_SENSOR2_B 159 +#define CLK_DIV_SCLK_ISP_SENSOR2_A 160 +#define CLK_DIV_SCLK_ISP_SENSOR1_B 161 +#define CLK_DIV_SCLK_ISP_SENSOR1_A 162 +#define CLK_DIV_SCLK_ISP_SENSOR0_B 163 +#define CLK_DIV_SCLK_ISP_SENSOR0_A 164 #define CLK_ACLK_PERIC_66 200 #define CLK_ACLK_PERIS_66 201 @@ -165,8 +179,18 @@ #define CLK_ACLK_CAM0_333 240 #define CLK_ACLK_CAM0_400 241 #define CLK_ACLK_CAM0_552 242 - -#define TOP_NR_CLK 243 +#define CLK_ACLK_CAM1_333 243 +#define CLK_ACLK_CAM1_400 244 +#define CLK_ACLK_CAM1_552 245 +#define CLK_SCLK_ISP_SENSOR2 246 +#define CLK_SCLK_ISP_SENSOR1 247 +#define CLK_SCLK_ISP_SENSOR0 248 +#define CLK_SCLK_ISP_MCTADC_CAM1 249 +#define CLK_SCLK_ISP_UART_CAM1 250 +#define CLK_SCLK_ISP_SPI1_CAM1 251 +#define CLK_SCLK_ISP_SPI0_CAM1 252 + +#define TOP_NR_CLK 253 /* CMU_CPIF */ #define CLK_FOUT_MPHY_PLL 1 @@ -1257,4 +1281,123 @@ #define CAM0_NR_CLK 134 +/* CMU_CAM1 */ +#define CLK_PHYCLK_RXBYTEECLKHS0_S2B 1 + +#define CLK_MOUT_SCLK_ISP_UART_USER 2 +#define CLK_MOUT_SCLK_ISP_SPI1_USER 3 +#define CLK_MOUT_SCLK_ISP_SPI0_USER 4 +#define CLK_MOUT_ACLK_CAM1_333_USER 5 +#define CLK_MOUT_ACLK_CAM1_400_USER 6 +#define CLK_MOUT_ACLK_CAM1_552_USER 7 +#define CLK_MOUT_PHYCLK_RXBYTECLKHS0_S2B_USER 8 +#define CLK_MOUT_ACLK_CSIS2_B 9 +#define CLK_MOUT_ACLK_CSIS2_A 10 +#define CLK_MOUT_ACLK_FD_B 11 +#define CLK_MOUT_ACLK_FD_A 12 +#define CLK_MOUT_ACLK_LITE_C_B 13 +#define CLK_MOUT_ACLK_LITE_C_A 14 + +#define CLK_DIV_SCLK_ISP_WPWM 15 +#define CLK_DIV_PCLK_CAM1_83 16 +#define CLK_DIV_PCLK_CAM1_166 17 +#define CLK_DIV_PCLK_DBG_CAM1 18 +#define CLK_DIV_ATCLK_CAM1 19 +#define CLK_DIV_ACLK_CSIS2 20 +#define CLK_DIV_PCLK_FD 21 +#define CLK_DIV_ACLK_FD 22 +#define CLK_DIV_PCLK_LITE_C 23 +#define CLK_DIV_ACLK_LITE_C 24 + +#define CLK_ACLK_ISP_GIC 25 +#define CLK_ACLK_FD 26 +#define CLK_ACLK_LITE_C 27 +#define CLK_ACLK_CSIS2 28 +#define CLK_ACLK_ASYNCAPBM_FD 29 +#define CLK_ACLK_ASYNCAPBS_FD 30 +#define CLK_ACLK_ASYNCAPBM_LITE_C 31 +#define CLK_ACLK_ASYNCAPBS_LITE_C 32 +#define CLK_ACLK_ASYNCAHBS_SFRISP2H2 33 +#define CLK_ACLK_ASYNCAHBS_SFRISP2H1 34 +#define CLK_ACLK_ASYNCAXIM_CA5 35 +#define CLK_ACLK_ASYNCAXIS_CA5 36 +#define CLK_ACLK_ASYNCAXIS_ISPX2 37 +#define CLK_ACLK_ASYNCAXIS_ISPX1 38 +#define CLK_ACLK_ASYNCAXIS_ISPX0 39 +#define CLK_ACLK_ASYNCAXIM_ISPEX 40 +#define CLK_ACLK_ASYNCAXIM_ISP3P 41 +#define CLK_ACLK_ASYNCAXIS_ISP3P 42 +#define CLK_ACLK_ASYNCAXIM_FD 43 +#define CLK_ACLK_ASYNCAXIS_FD 44 +#define CLK_ACLK_ASYNCAXIM_LITE_C 45 +#define CLK_ACLK_ASYNCAXIS_LITE_C 46 +#define CLK_ACLK_AHB2APB_ISP5P 47 +#define CLK_ACLK_AHB2APB_ISP3P 48 +#define CLK_ACLK_AXI2APB_ISP3P 49 +#define CLK_ACLK_AHB_SFRISP2H 50 +#define CLK_ACLK_AXI_ISP_HX_R 51 +#define CLK_ACLK_AXI_ISP_CX_R 52 +#define CLK_ACLK_AXI_ISP_HX 53 +#define CLK_ACLK_AXI_ISP_CX 54 +#define CLK_ACLK_XIU_ISPX 55 +#define CLK_ACLK_XIU_ISPEX 56 +#define CLK_ACLK_CAM1NP_333 57 +#define CLK_ACLK_CAM1ND_400 58 +#define CLK_ACLK_SMMU_ISPCPU 59 +#define CLK_ACLK_SMMU_FD 60 +#define CLK_ACLK_SMMU_LITE_C 61 +#define CLK_ACLK_BTS_ISP3P 62 +#define CLK_ACLK_BTS_FD 63 +#define CLK_ACLK_BTS_LITE_C 64 +#define CLK_ACLK_AHBDN_SFRISP2H 65 +#define CLK_ACLK_AHBDN_ISP5P 66 +#define CLK_ACLK_AXIUS_ISP3P 67 +#define CLK_ACLK_AXIUS_FD 68 +#define CLK_ACLK_AXIUS_LITE_C 69 +#define CLK_PCLK_SMMU_ISPCPU 70 +#define CLK_PCLK_SMMU_FD 71 +#define CLK_PCLK_SMMU_LITE_C 72 +#define CLK_PCLK_BTS_ISP3P 73 +#define CLK_PCLK_BTS_FD 74 +#define CLK_PCLK_BTS_LITE_C 75 +#define CLK_PCLK_ASYNCAXIM_CA5 76 +#define CLK_PCLK_ASYNCAXIM_ISPEX 77 +#define CLK_PCLK_ASYNCAXIM_ISP3P 78 +#define CLK_PCLK_ASYNCAXIM_FD 79 +#define CLK_PCLK_ASYNCAXIM_LITE_C 80 +#define CLK_PCLK_PMU_CAM1 81 +#define CLK_PCLK_SYSREG_CAM1 82 +#define CLK_PCLK_CMU_CAM1_LOCAL 83 +#define CLK_PCLK_ISP_MCTADC 84 +#define CLK_PCLK_ISP_WDT 85 +#define CLK_PCLK_ISP_PWM 86 +#define CLK_PCLK_ISP_UART 87 +#define CLK_PCLK_ISP_MCUCTL 88 +#define CLK_PCLK_ISP_SPI1 89 +#define CLK_PCLK_ISP_SPI0 90 +#define CLK_PCLK_ISP_I2C2 91 +#define CLK_PCLK_ISP_I2C1 92 +#define CLK_PCLK_ISP_I2C0 93 +#define CLK_PCLK_ISP_MPWM 94 +#define CLK_PCLK_FD 95 +#define CLK_PCLK_LITE_C 96 +#define CLK_PCLK_CSIS2 97 +#define CLK_SCLK_ISP_I2C2 98 +#define CLK_SCLK_ISP_I2C1 99 +#define CLK_SCLK_ISP_I2C0 100 +#define CLK_SCLK_ISP_PWM 101 +#define CLK_PHYCLK_RXBYTECLKHS0_S2B 102 +#define CLK_SCLK_LITE_C_FREECNT 103 +#define CLK_SCLK_PIXELASYNCM_FD 104 +#define CLK_SCLK_ISP_MCTADC 105 +#define CLK_SCLK_ISP_UART 106 +#define CLK_SCLK_ISP_SPI1 107 +#define CLK_SCLK_ISP_SPI0 108 +#define CLK_SCLK_ISP_MPWM 109 +#define CLK_PCLK_DBG_ISP 110 +#define CLK_ATCLK_ISP 111 +#define CLK_SCLK_ISP_CA5 112 + +#define CAM1_NR_CLK 113 + #endif /* _DT_BINDINGS_CLOCK_EXYNOS5433_H */ -- cgit v1.2.3 From b2f0e5f28e0686c0d5db238357a2e32555e97633 Mon Sep 17 00:00:00 2001 From: Chanwoo Choi Date: Wed, 4 Feb 2015 10:12:59 +0900 Subject: clk: samsung: exynos5433: Move CLK_SCLK_HDMI_SPDIF_DISP clock to CMU_TOP domain This patch fixes the bug of CLK_SCLK_HDMI_SPDIF_DISP clock because this clock should be included in CMU_TOP domain. So, this patch moves the CLK_SCLK_HDMI_ SPDIF_DISP clock from CMU_MIF to CMU_TOP domain. Reported-by: Sylwester Nawrocki Signed-off-by: Chanwoo Choi Signed-off-by: Sylwester Nawrocki --- drivers/clk/samsung/clk-exynos5433.c | 10 +++++----- include/dt-bindings/clock/exynos5433.h | 6 +++--- 2 files changed, 8 insertions(+), 8 deletions(-) (limited to 'include') diff --git a/drivers/clk/samsung/clk-exynos5433.c b/drivers/clk/samsung/clk-exynos5433.c index 1a005c1f7c4b..387e3e39e635 100644 --- a/drivers/clk/samsung/clk-exynos5433.c +++ b/drivers/clk/samsung/clk-exynos5433.c @@ -661,6 +661,11 @@ static struct samsung_gate_clock top_gate_clks[] __initdata = { GATE(CLK_SCLK_ISP_SPI0_CAM1, "sclk_isp_spi0_cam1", "div_sclk_isp_spi0_b", ENABLE_SCLK_TOP_CAM1, 0, 0, 0), + /* ENABLE_SCLK_TOP_DISP */ + GATE(CLK_SCLK_HDMI_SPDIF_DISP, "sclk_hdmi_spdif_disp", + "mout_sclk_hdmi_spdif", ENABLE_SCLK_TOP_DISP, 0, + CLK_IGNORE_UNUSED, 0), + /* ENABLE_SCLK_TOP_FSYS */ GATE(CLK_SCLK_PCIE_100_FSYS, "sclk_pcie_100_fsys", "div_sclk_pcie_100", ENABLE_SCLK_TOP_FSYS, 7, 0, 0), @@ -1521,11 +1526,6 @@ static struct samsung_gate_clock mif_gate_clks[] __initdata = { ENABLE_SCLK_MIF, 1, CLK_IGNORE_UNUSED, 0), GATE(CLK_SCLK_BUS_PLL_ATLAS, "sclk_bus_pll_atlas", "sclk_bus_pll", ENABLE_SCLK_MIF, 0, CLK_IGNORE_UNUSED, 0), - - /* ENABLE_SCLK_TOP_DISP */ - GATE(CLK_SCLK_HDMI_SPDIF_DISP, "sclk_hdmi_spdif_disp", - "mout_sclk_hdmi_spdif", ENABLE_SCLK_TOP_DISP, 0, - CLK_IGNORE_UNUSED, 0), }; static struct samsung_cmu_info mif_cmu_info __initdata = { diff --git a/include/dt-bindings/clock/exynos5433.h b/include/dt-bindings/clock/exynos5433.h index 4853bc598b57..5bd80d5ecd0f 100644 --- a/include/dt-bindings/clock/exynos5433.h +++ b/include/dt-bindings/clock/exynos5433.h @@ -189,8 +189,9 @@ #define CLK_SCLK_ISP_UART_CAM1 250 #define CLK_SCLK_ISP_SPI1_CAM1 251 #define CLK_SCLK_ISP_SPI0_CAM1 252 +#define CLK_SCLK_HDMI_SPDIF_DISP 253 -#define TOP_NR_CLK 253 +#define TOP_NR_CLK 254 /* CMU_CPIF */ #define CLK_FOUT_MPHY_PLL 1 @@ -397,9 +398,8 @@ #define CLK_SCLK_BUS_PLL 198 #define CLK_SCLK_BUS_PLL_APOLLO 199 #define CLK_SCLK_BUS_PLL_ATLAS 200 -#define CLK_SCLK_HDMI_SPDIF_DISP 201 -#define MIF_NR_CLK 202 +#define MIF_NR_CLK 201 /* CMU_PERIC */ #define CLK_PCLK_SPI2 1 -- cgit v1.2.3 From cd67cd5eb25ae9a7bafbfd3d52d4c05e1d80af3b Mon Sep 17 00:00:00 2001 From: Julian Anastasov Date: Fri, 6 Feb 2015 09:44:44 +0200 Subject: ipvs: use 64-bit rates in stats IPVS stats are limited to 2^(32-10) conns/s and packets/s, 2^(32-5) bytes/s. It is time to use 64 bits: * Change all conn/packet kernel counters to 64-bit and update them in u64_stats_update_{begin,end} section * In kernel use struct ip_vs_kstats instead of the user-space struct ip_vs_stats_user and use new func ip_vs_export_stats_user to export it to sockopt users to preserve compatibility with 32-bit values * Rename cpu counters "ustats" to "cnt" * To netlink users provide additionally 64-bit stats: IPVS_SVC_ATTR_STATS64 and IPVS_DEST_ATTR_STATS64. Old stats remain for old binaries. * We can use ip_vs_copy_stats in ip_vs_stats_percpu_show Thanks to Chris Caputo for providing initial patch for ip_vs_est.c Signed-off-by: Chris Caputo Signed-off-by: Julian Anastasov Signed-off-by: Simon Horman --- include/net/ip_vs.h | 50 ++++++++---- include/uapi/linux/ip_vs.h | 7 +- net/netfilter/ipvs/ip_vs_core.c | 36 +++++---- net/netfilter/ipvs/ip_vs_ctl.c | 174 ++++++++++++++++++++++++++-------------- net/netfilter/ipvs/ip_vs_est.c | 102 ++++++++++++----------- 5 files changed, 226 insertions(+), 143 deletions(-) (limited to 'include') diff --git a/include/net/ip_vs.h b/include/net/ip_vs.h index 615b20b58545..a627fe690c19 100644 --- a/include/net/ip_vs.h +++ b/include/net/ip_vs.h @@ -365,15 +365,15 @@ struct ip_vs_seq { /* counters per cpu */ struct ip_vs_counters { - __u32 conns; /* connections scheduled */ - __u32 inpkts; /* incoming packets */ - __u32 outpkts; /* outgoing packets */ + __u64 conns; /* connections scheduled */ + __u64 inpkts; /* incoming packets */ + __u64 outpkts; /* outgoing packets */ __u64 inbytes; /* incoming bytes */ __u64 outbytes; /* outgoing bytes */ }; /* Stats per cpu */ struct ip_vs_cpu_stats { - struct ip_vs_counters ustats; + struct ip_vs_counters cnt; struct u64_stats_sync syncp; }; @@ -383,23 +383,40 @@ struct ip_vs_estimator { u64 last_inbytes; u64 last_outbytes; - u32 last_conns; - u32 last_inpkts; - u32 last_outpkts; - - u32 cps; - u32 inpps; - u32 outpps; - u32 inbps; - u32 outbps; + u64 last_conns; + u64 last_inpkts; + u64 last_outpkts; + + u64 cps; + u64 inpps; + u64 outpps; + u64 inbps; + u64 outbps; +}; + +/* + * IPVS statistics object, 64-bit kernel version of struct ip_vs_stats_user + */ +struct ip_vs_kstats { + u64 conns; /* connections scheduled */ + u64 inpkts; /* incoming packets */ + u64 outpkts; /* outgoing packets */ + u64 inbytes; /* incoming bytes */ + u64 outbytes; /* outgoing bytes */ + + u64 cps; /* current connection rate */ + u64 inpps; /* current in packet rate */ + u64 outpps; /* current out packet rate */ + u64 inbps; /* current in byte rate */ + u64 outbps; /* current out byte rate */ }; struct ip_vs_stats { - struct ip_vs_stats_user ustats; /* statistics */ + struct ip_vs_kstats kstats; /* kernel statistics */ struct ip_vs_estimator est; /* estimator */ struct ip_vs_cpu_stats __percpu *cpustats; /* per cpu counters */ spinlock_t lock; /* spin lock */ - struct ip_vs_stats_user ustats0; /* reset values */ + struct ip_vs_kstats kstats0; /* reset values */ }; struct dst_entry; @@ -1388,8 +1405,7 @@ void ip_vs_sync_conn(struct net *net, struct ip_vs_conn *cp, int pkts); void ip_vs_start_estimator(struct net *net, struct ip_vs_stats *stats); void ip_vs_stop_estimator(struct net *net, struct ip_vs_stats *stats); void ip_vs_zero_estimator(struct ip_vs_stats *stats); -void ip_vs_read_estimator(struct ip_vs_stats_user *dst, - struct ip_vs_stats *stats); +void ip_vs_read_estimator(struct ip_vs_kstats *dst, struct ip_vs_stats *stats); /* Various IPVS packet transmitters (from ip_vs_xmit.c) */ int ip_vs_null_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, diff --git a/include/uapi/linux/ip_vs.h b/include/uapi/linux/ip_vs.h index cabe95d5b461..3199243f2028 100644 --- a/include/uapi/linux/ip_vs.h +++ b/include/uapi/linux/ip_vs.h @@ -358,6 +358,8 @@ enum { IPVS_SVC_ATTR_PE_NAME, /* name of ct retriever */ + IPVS_SVC_ATTR_STATS64, /* nested attribute for service stats */ + __IPVS_SVC_ATTR_MAX, }; @@ -387,6 +389,8 @@ enum { IPVS_DEST_ATTR_ADDR_FAMILY, /* Address family of address */ + IPVS_DEST_ATTR_STATS64, /* nested attribute for dest stats */ + __IPVS_DEST_ATTR_MAX, }; @@ -410,7 +414,8 @@ enum { /* * Attributes used to describe service or destination entry statistics * - * Used inside nested attributes IPVS_SVC_ATTR_STATS and IPVS_DEST_ATTR_STATS + * Used inside nested attributes IPVS_SVC_ATTR_STATS, IPVS_DEST_ATTR_STATS, + * IPVS_SVC_ATTR_STATS64 and IPVS_DEST_ATTR_STATS64. */ enum { IPVS_STATS_ATTR_UNSPEC = 0, diff --git a/net/netfilter/ipvs/ip_vs_core.c b/net/netfilter/ipvs/ip_vs_core.c index 990decba1fe4..c9470c86308f 100644 --- a/net/netfilter/ipvs/ip_vs_core.c +++ b/net/netfilter/ipvs/ip_vs_core.c @@ -119,24 +119,24 @@ ip_vs_in_stats(struct ip_vs_conn *cp, struct sk_buff *skb) struct ip_vs_service *svc; s = this_cpu_ptr(dest->stats.cpustats); - s->ustats.inpkts++; u64_stats_update_begin(&s->syncp); - s->ustats.inbytes += skb->len; + s->cnt.inpkts++; + s->cnt.inbytes += skb->len; u64_stats_update_end(&s->syncp); rcu_read_lock(); svc = rcu_dereference(dest->svc); s = this_cpu_ptr(svc->stats.cpustats); - s->ustats.inpkts++; u64_stats_update_begin(&s->syncp); - s->ustats.inbytes += skb->len; + s->cnt.inpkts++; + s->cnt.inbytes += skb->len; u64_stats_update_end(&s->syncp); rcu_read_unlock(); s = this_cpu_ptr(ipvs->tot_stats.cpustats); - s->ustats.inpkts++; u64_stats_update_begin(&s->syncp); - s->ustats.inbytes += skb->len; + s->cnt.inpkts++; + s->cnt.inbytes += skb->len; u64_stats_update_end(&s->syncp); } } @@ -153,24 +153,24 @@ ip_vs_out_stats(struct ip_vs_conn *cp, struct sk_buff *skb) struct ip_vs_service *svc; s = this_cpu_ptr(dest->stats.cpustats); - s->ustats.outpkts++; u64_stats_update_begin(&s->syncp); - s->ustats.outbytes += skb->len; + s->cnt.outpkts++; + s->cnt.outbytes += skb->len; u64_stats_update_end(&s->syncp); rcu_read_lock(); svc = rcu_dereference(dest->svc); s = this_cpu_ptr(svc->stats.cpustats); - s->ustats.outpkts++; u64_stats_update_begin(&s->syncp); - s->ustats.outbytes += skb->len; + s->cnt.outpkts++; + s->cnt.outbytes += skb->len; u64_stats_update_end(&s->syncp); rcu_read_unlock(); s = this_cpu_ptr(ipvs->tot_stats.cpustats); - s->ustats.outpkts++; u64_stats_update_begin(&s->syncp); - s->ustats.outbytes += skb->len; + s->cnt.outpkts++; + s->cnt.outbytes += skb->len; u64_stats_update_end(&s->syncp); } } @@ -183,13 +183,19 @@ ip_vs_conn_stats(struct ip_vs_conn *cp, struct ip_vs_service *svc) struct ip_vs_cpu_stats *s; s = this_cpu_ptr(cp->dest->stats.cpustats); - s->ustats.conns++; + u64_stats_update_begin(&s->syncp); + s->cnt.conns++; + u64_stats_update_end(&s->syncp); s = this_cpu_ptr(svc->stats.cpustats); - s->ustats.conns++; + u64_stats_update_begin(&s->syncp); + s->cnt.conns++; + u64_stats_update_end(&s->syncp); s = this_cpu_ptr(ipvs->tot_stats.cpustats); - s->ustats.conns++; + u64_stats_update_begin(&s->syncp); + s->cnt.conns++; + u64_stats_update_end(&s->syncp); } diff --git a/net/netfilter/ipvs/ip_vs_ctl.c b/net/netfilter/ipvs/ip_vs_ctl.c index e55759056361..6fd60059faf0 100644 --- a/net/netfilter/ipvs/ip_vs_ctl.c +++ b/net/netfilter/ipvs/ip_vs_ctl.c @@ -729,9 +729,9 @@ static void ip_vs_trash_cleanup(struct net *net) } static void -ip_vs_copy_stats(struct ip_vs_stats_user *dst, struct ip_vs_stats *src) +ip_vs_copy_stats(struct ip_vs_kstats *dst, struct ip_vs_stats *src) { -#define IP_VS_SHOW_STATS_COUNTER(c) dst->c = src->ustats.c - src->ustats0.c +#define IP_VS_SHOW_STATS_COUNTER(c) dst->c = src->kstats.c - src->kstats0.c spin_lock_bh(&src->lock); @@ -746,6 +746,21 @@ ip_vs_copy_stats(struct ip_vs_stats_user *dst, struct ip_vs_stats *src) spin_unlock_bh(&src->lock); } +static void +ip_vs_export_stats_user(struct ip_vs_stats_user *dst, struct ip_vs_kstats *src) +{ + dst->conns = (u32)src->conns; + dst->inpkts = (u32)src->inpkts; + dst->outpkts = (u32)src->outpkts; + dst->inbytes = src->inbytes; + dst->outbytes = src->outbytes; + dst->cps = (u32)src->cps; + dst->inpps = (u32)src->inpps; + dst->outpps = (u32)src->outpps; + dst->inbps = (u32)src->inbps; + dst->outbps = (u32)src->outbps; +} + static void ip_vs_zero_stats(struct ip_vs_stats *stats) { @@ -753,7 +768,7 @@ ip_vs_zero_stats(struct ip_vs_stats *stats) /* get current counters as zero point, rates are zeroed */ -#define IP_VS_ZERO_STATS_COUNTER(c) stats->ustats0.c = stats->ustats.c +#define IP_VS_ZERO_STATS_COUNTER(c) stats->kstats0.c = stats->kstats.c IP_VS_ZERO_STATS_COUNTER(conns); IP_VS_ZERO_STATS_COUNTER(inpkts); @@ -2044,7 +2059,7 @@ static const struct file_operations ip_vs_info_fops = { static int ip_vs_stats_show(struct seq_file *seq, void *v) { struct net *net = seq_file_single_net(seq); - struct ip_vs_stats_user show; + struct ip_vs_kstats show; /* 01234567 01234567 01234567 0123456701234567 0123456701234567 */ seq_puts(seq, @@ -2053,17 +2068,22 @@ static int ip_vs_stats_show(struct seq_file *seq, void *v) " Conns Packets Packets Bytes Bytes\n"); ip_vs_copy_stats(&show, &net_ipvs(net)->tot_stats); - seq_printf(seq, "%8X %8X %8X %16LX %16LX\n\n", show.conns, - show.inpkts, show.outpkts, - (unsigned long long) show.inbytes, - (unsigned long long) show.outbytes); - -/* 01234567 01234567 01234567 0123456701234567 0123456701234567 */ + seq_printf(seq, "%8LX %8LX %8LX %16LX %16LX\n\n", + (unsigned long long)show.conns, + (unsigned long long)show.inpkts, + (unsigned long long)show.outpkts, + (unsigned long long)show.inbytes, + (unsigned long long)show.outbytes); + +/* 01234567 01234567 01234567 0123456701234567 0123456701234567*/ seq_puts(seq, - " Conns/s Pkts/s Pkts/s Bytes/s Bytes/s\n"); - seq_printf(seq, "%8X %8X %8X %16X %16X\n", - show.cps, show.inpps, show.outpps, - show.inbps, show.outbps); + " Conns/s Pkts/s Pkts/s Bytes/s Bytes/s\n"); + seq_printf(seq, "%8LX %8LX %8LX %16LX %16LX\n", + (unsigned long long)show.cps, + (unsigned long long)show.inpps, + (unsigned long long)show.outpps, + (unsigned long long)show.inbps, + (unsigned long long)show.outbps); return 0; } @@ -2086,7 +2106,7 @@ static int ip_vs_stats_percpu_show(struct seq_file *seq, void *v) struct net *net = seq_file_single_net(seq); struct ip_vs_stats *tot_stats = &net_ipvs(net)->tot_stats; struct ip_vs_cpu_stats __percpu *cpustats = tot_stats->cpustats; - struct ip_vs_stats_user rates; + struct ip_vs_kstats kstats; int i; /* 01234567 01234567 01234567 0123456701234567 0123456701234567 */ @@ -2098,41 +2118,41 @@ static int ip_vs_stats_percpu_show(struct seq_file *seq, void *v) for_each_possible_cpu(i) { struct ip_vs_cpu_stats *u = per_cpu_ptr(cpustats, i); unsigned int start; - __u64 inbytes, outbytes; + u64 conns, inpkts, outpkts, inbytes, outbytes; do { start = u64_stats_fetch_begin_irq(&u->syncp); - inbytes = u->ustats.inbytes; - outbytes = u->ustats.outbytes; + conns = u->cnt.conns; + inpkts = u->cnt.inpkts; + outpkts = u->cnt.outpkts; + inbytes = u->cnt.inbytes; + outbytes = u->cnt.outbytes; } while (u64_stats_fetch_retry_irq(&u->syncp, start)); - seq_printf(seq, "%3X %8X %8X %8X %16LX %16LX\n", - i, u->ustats.conns, u->ustats.inpkts, - u->ustats.outpkts, (__u64)inbytes, - (__u64)outbytes); + seq_printf(seq, "%3X %8LX %8LX %8LX %16LX %16LX\n", + i, (u64)conns, (u64)inpkts, + (u64)outpkts, (u64)inbytes, + (u64)outbytes); } - spin_lock_bh(&tot_stats->lock); - - seq_printf(seq, " ~ %8X %8X %8X %16LX %16LX\n\n", - tot_stats->ustats.conns, tot_stats->ustats.inpkts, - tot_stats->ustats.outpkts, - (unsigned long long) tot_stats->ustats.inbytes, - (unsigned long long) tot_stats->ustats.outbytes); - - ip_vs_read_estimator(&rates, tot_stats); + ip_vs_copy_stats(&kstats, tot_stats); - spin_unlock_bh(&tot_stats->lock); + seq_printf(seq, " ~ %8LX %8LX %8LX %16LX %16LX\n\n", + (unsigned long long)kstats.conns, + (unsigned long long)kstats.inpkts, + (unsigned long long)kstats.outpkts, + (unsigned long long)kstats.inbytes, + (unsigned long long)kstats.outbytes); -/* 01234567 01234567 01234567 0123456701234567 0123456701234567 */ +/* ... 01234567 01234567 01234567 0123456701234567 0123456701234567 */ seq_puts(seq, - " Conns/s Pkts/s Pkts/s Bytes/s Bytes/s\n"); - seq_printf(seq, " %8X %8X %8X %16X %16X\n", - rates.cps, - rates.inpps, - rates.outpps, - rates.inbps, - rates.outbps); + " Conns/s Pkts/s Pkts/s Bytes/s Bytes/s\n"); + seq_printf(seq, " %8LX %8LX %8LX %16LX %16LX\n", + kstats.cps, + kstats.inpps, + kstats.outpps, + kstats.inbps, + kstats.outbps); return 0; } @@ -2400,6 +2420,7 @@ static void ip_vs_copy_service(struct ip_vs_service_entry *dst, struct ip_vs_service *src) { struct ip_vs_scheduler *sched; + struct ip_vs_kstats kstats; sched = rcu_dereference_protected(src->scheduler, 1); dst->protocol = src->protocol; @@ -2411,7 +2432,8 @@ ip_vs_copy_service(struct ip_vs_service_entry *dst, struct ip_vs_service *src) dst->timeout = src->timeout / HZ; dst->netmask = src->netmask; dst->num_dests = src->num_dests; - ip_vs_copy_stats(&dst->stats, &src->stats); + ip_vs_copy_stats(&kstats, &src->stats); + ip_vs_export_stats_user(&dst->stats, &kstats); } static inline int @@ -2485,6 +2507,7 @@ __ip_vs_get_dest_entries(struct net *net, const struct ip_vs_get_dests *get, int count = 0; struct ip_vs_dest *dest; struct ip_vs_dest_entry entry; + struct ip_vs_kstats kstats; memset(&entry, 0, sizeof(entry)); list_for_each_entry(dest, &svc->destinations, n_list) { @@ -2506,7 +2529,8 @@ __ip_vs_get_dest_entries(struct net *net, const struct ip_vs_get_dests *get, entry.activeconns = atomic_read(&dest->activeconns); entry.inactconns = atomic_read(&dest->inactconns); entry.persistconns = atomic_read(&dest->persistconns); - ip_vs_copy_stats(&entry.stats, &dest->stats); + ip_vs_copy_stats(&kstats, &dest->stats); + ip_vs_export_stats_user(&entry.stats, &kstats); if (copy_to_user(&uptr->entrytable[count], &entry, sizeof(entry))) { ret = -EFAULT; @@ -2798,25 +2822,51 @@ static const struct nla_policy ip_vs_dest_policy[IPVS_DEST_ATTR_MAX + 1] = { }; static int ip_vs_genl_fill_stats(struct sk_buff *skb, int container_type, - struct ip_vs_stats *stats) + struct ip_vs_kstats *kstats) +{ + struct nlattr *nl_stats = nla_nest_start(skb, container_type); + + if (!nl_stats) + return -EMSGSIZE; + + if (nla_put_u32(skb, IPVS_STATS_ATTR_CONNS, (u32)kstats->conns) || + nla_put_u32(skb, IPVS_STATS_ATTR_INPKTS, (u32)kstats->inpkts) || + nla_put_u32(skb, IPVS_STATS_ATTR_OUTPKTS, (u32)kstats->outpkts) || + nla_put_u64(skb, IPVS_STATS_ATTR_INBYTES, kstats->inbytes) || + nla_put_u64(skb, IPVS_STATS_ATTR_OUTBYTES, kstats->outbytes) || + nla_put_u32(skb, IPVS_STATS_ATTR_CPS, (u32)kstats->cps) || + nla_put_u32(skb, IPVS_STATS_ATTR_INPPS, (u32)kstats->inpps) || + nla_put_u32(skb, IPVS_STATS_ATTR_OUTPPS, (u32)kstats->outpps) || + nla_put_u32(skb, IPVS_STATS_ATTR_INBPS, (u32)kstats->inbps) || + nla_put_u32(skb, IPVS_STATS_ATTR_OUTBPS, (u32)kstats->outbps)) + goto nla_put_failure; + nla_nest_end(skb, nl_stats); + + return 0; + +nla_put_failure: + nla_nest_cancel(skb, nl_stats); + return -EMSGSIZE; +} + +static int ip_vs_genl_fill_stats64(struct sk_buff *skb, int container_type, + struct ip_vs_kstats *kstats) { - struct ip_vs_stats_user ustats; struct nlattr *nl_stats = nla_nest_start(skb, container_type); + if (!nl_stats) return -EMSGSIZE; - ip_vs_copy_stats(&ustats, stats); - - if (nla_put_u32(skb, IPVS_STATS_ATTR_CONNS, ustats.conns) || - nla_put_u32(skb, IPVS_STATS_ATTR_INPKTS, ustats.inpkts) || - nla_put_u32(skb, IPVS_STATS_ATTR_OUTPKTS, ustats.outpkts) || - nla_put_u64(skb, IPVS_STATS_ATTR_INBYTES, ustats.inbytes) || - nla_put_u64(skb, IPVS_STATS_ATTR_OUTBYTES, ustats.outbytes) || - nla_put_u32(skb, IPVS_STATS_ATTR_CPS, ustats.cps) || - nla_put_u32(skb, IPVS_STATS_ATTR_INPPS, ustats.inpps) || - nla_put_u32(skb, IPVS_STATS_ATTR_OUTPPS, ustats.outpps) || - nla_put_u32(skb, IPVS_STATS_ATTR_INBPS, ustats.inbps) || - nla_put_u32(skb, IPVS_STATS_ATTR_OUTBPS, ustats.outbps)) + if (nla_put_u64(skb, IPVS_STATS_ATTR_CONNS, kstats->conns) || + nla_put_u64(skb, IPVS_STATS_ATTR_INPKTS, kstats->inpkts) || + nla_put_u64(skb, IPVS_STATS_ATTR_OUTPKTS, kstats->outpkts) || + nla_put_u64(skb, IPVS_STATS_ATTR_INBYTES, kstats->inbytes) || + nla_put_u64(skb, IPVS_STATS_ATTR_OUTBYTES, kstats->outbytes) || + nla_put_u64(skb, IPVS_STATS_ATTR_CPS, kstats->cps) || + nla_put_u64(skb, IPVS_STATS_ATTR_INPPS, kstats->inpps) || + nla_put_u64(skb, IPVS_STATS_ATTR_OUTPPS, kstats->outpps) || + nla_put_u64(skb, IPVS_STATS_ATTR_INBPS, kstats->inbps) || + nla_put_u64(skb, IPVS_STATS_ATTR_OUTBPS, kstats->outbps)) goto nla_put_failure; nla_nest_end(skb, nl_stats); @@ -2835,6 +2885,7 @@ static int ip_vs_genl_fill_service(struct sk_buff *skb, struct nlattr *nl_service; struct ip_vs_flags flags = { .flags = svc->flags, .mask = ~0 }; + struct ip_vs_kstats kstats; nl_service = nla_nest_start(skb, IPVS_CMD_ATTR_SERVICE); if (!nl_service) @@ -2860,7 +2911,10 @@ static int ip_vs_genl_fill_service(struct sk_buff *skb, nla_put_u32(skb, IPVS_SVC_ATTR_TIMEOUT, svc->timeout / HZ) || nla_put_be32(skb, IPVS_SVC_ATTR_NETMASK, svc->netmask)) goto nla_put_failure; - if (ip_vs_genl_fill_stats(skb, IPVS_SVC_ATTR_STATS, &svc->stats)) + ip_vs_copy_stats(&kstats, &svc->stats); + if (ip_vs_genl_fill_stats(skb, IPVS_SVC_ATTR_STATS, &kstats)) + goto nla_put_failure; + if (ip_vs_genl_fill_stats64(skb, IPVS_SVC_ATTR_STATS64, &kstats)) goto nla_put_failure; nla_nest_end(skb, nl_service); @@ -3032,6 +3086,7 @@ static struct ip_vs_service *ip_vs_genl_find_service(struct net *net, static int ip_vs_genl_fill_dest(struct sk_buff *skb, struct ip_vs_dest *dest) { struct nlattr *nl_dest; + struct ip_vs_kstats kstats; nl_dest = nla_nest_start(skb, IPVS_CMD_ATTR_DEST); if (!nl_dest) @@ -3054,7 +3109,10 @@ static int ip_vs_genl_fill_dest(struct sk_buff *skb, struct ip_vs_dest *dest) atomic_read(&dest->persistconns)) || nla_put_u16(skb, IPVS_DEST_ATTR_ADDR_FAMILY, dest->af)) goto nla_put_failure; - if (ip_vs_genl_fill_stats(skb, IPVS_DEST_ATTR_STATS, &dest->stats)) + ip_vs_copy_stats(&kstats, &dest->stats); + if (ip_vs_genl_fill_stats(skb, IPVS_DEST_ATTR_STATS, &kstats)) + goto nla_put_failure; + if (ip_vs_genl_fill_stats64(skb, IPVS_DEST_ATTR_STATS64, &kstats)) goto nla_put_failure; nla_nest_end(skb, nl_dest); diff --git a/net/netfilter/ipvs/ip_vs_est.c b/net/netfilter/ipvs/ip_vs_est.c index 1425e9a924c4..ef0eb0a8d552 100644 --- a/net/netfilter/ipvs/ip_vs_est.c +++ b/net/netfilter/ipvs/ip_vs_est.c @@ -45,17 +45,19 @@ NOTES. - * The stored value for average bps is scaled by 2^5, so that maximal - rate is ~2.15Gbits/s, average pps and cps are scaled by 2^10. + * Average bps is scaled by 2^5, while average pps and cps are scaled by 2^10. - * A lot code is taken from net/sched/estimator.c + * Netlink users can see 64-bit values but sockopt users are restricted + to 32-bit values for conns, packets, bps, cps and pps. + + * A lot of code is taken from net/core/gen_estimator.c */ /* * Make a summary from each cpu */ -static void ip_vs_read_cpu_stats(struct ip_vs_stats_user *sum, +static void ip_vs_read_cpu_stats(struct ip_vs_kstats *sum, struct ip_vs_cpu_stats __percpu *stats) { int i; @@ -64,27 +66,31 @@ static void ip_vs_read_cpu_stats(struct ip_vs_stats_user *sum, for_each_possible_cpu(i) { struct ip_vs_cpu_stats *s = per_cpu_ptr(stats, i); unsigned int start; - __u64 inbytes, outbytes; + u64 conns, inpkts, outpkts, inbytes, outbytes; + if (add) { - sum->conns += s->ustats.conns; - sum->inpkts += s->ustats.inpkts; - sum->outpkts += s->ustats.outpkts; do { start = u64_stats_fetch_begin(&s->syncp); - inbytes = s->ustats.inbytes; - outbytes = s->ustats.outbytes; + conns = s->cnt.conns; + inpkts = s->cnt.inpkts; + outpkts = s->cnt.outpkts; + inbytes = s->cnt.inbytes; + outbytes = s->cnt.outbytes; } while (u64_stats_fetch_retry(&s->syncp, start)); + sum->conns += conns; + sum->inpkts += inpkts; + sum->outpkts += outpkts; sum->inbytes += inbytes; sum->outbytes += outbytes; } else { add = true; - sum->conns = s->ustats.conns; - sum->inpkts = s->ustats.inpkts; - sum->outpkts = s->ustats.outpkts; do { start = u64_stats_fetch_begin(&s->syncp); - sum->inbytes = s->ustats.inbytes; - sum->outbytes = s->ustats.outbytes; + sum->conns = s->cnt.conns; + sum->inpkts = s->cnt.inpkts; + sum->outpkts = s->cnt.outpkts; + sum->inbytes = s->cnt.inbytes; + sum->outbytes = s->cnt.outbytes; } while (u64_stats_fetch_retry(&s->syncp, start)); } } @@ -95,10 +101,7 @@ static void estimation_timer(unsigned long arg) { struct ip_vs_estimator *e; struct ip_vs_stats *s; - u32 n_conns; - u32 n_inpkts, n_outpkts; - u64 n_inbytes, n_outbytes; - u32 rate; + u64 rate; struct net *net = (struct net *)arg; struct netns_ipvs *ipvs; @@ -108,33 +111,29 @@ static void estimation_timer(unsigned long arg) s = container_of(e, struct ip_vs_stats, est); spin_lock(&s->lock); - ip_vs_read_cpu_stats(&s->ustats, s->cpustats); - n_conns = s->ustats.conns; - n_inpkts = s->ustats.inpkts; - n_outpkts = s->ustats.outpkts; - n_inbytes = s->ustats.inbytes; - n_outbytes = s->ustats.outbytes; + ip_vs_read_cpu_stats(&s->kstats, s->cpustats); /* scaled by 2^10, but divided 2 seconds */ - rate = (n_conns - e->last_conns) << 9; - e->last_conns = n_conns; - e->cps += ((long)rate - (long)e->cps) >> 2; - - rate = (n_inpkts - e->last_inpkts) << 9; - e->last_inpkts = n_inpkts; - e->inpps += ((long)rate - (long)e->inpps) >> 2; - - rate = (n_outpkts - e->last_outpkts) << 9; - e->last_outpkts = n_outpkts; - e->outpps += ((long)rate - (long)e->outpps) >> 2; - - rate = (n_inbytes - e->last_inbytes) << 4; - e->last_inbytes = n_inbytes; - e->inbps += ((long)rate - (long)e->inbps) >> 2; - - rate = (n_outbytes - e->last_outbytes) << 4; - e->last_outbytes = n_outbytes; - e->outbps += ((long)rate - (long)e->outbps) >> 2; + rate = (s->kstats.conns - e->last_conns) << 9; + e->last_conns = s->kstats.conns; + e->cps += ((s64)rate - (s64)e->cps) >> 2; + + rate = (s->kstats.inpkts - e->last_inpkts) << 9; + e->last_inpkts = s->kstats.inpkts; + e->inpps += ((s64)rate - (s64)e->inpps) >> 2; + + rate = (s->kstats.outpkts - e->last_outpkts) << 9; + e->last_outpkts = s->kstats.outpkts; + e->outpps += ((s64)rate - (s64)e->outpps) >> 2; + + /* scaled by 2^5, but divided 2 seconds */ + rate = (s->kstats.inbytes - e->last_inbytes) << 4; + e->last_inbytes = s->kstats.inbytes; + e->inbps += ((s64)rate - (s64)e->inbps) >> 2; + + rate = (s->kstats.outbytes - e->last_outbytes) << 4; + e->last_outbytes = s->kstats.outbytes; + e->outbps += ((s64)rate - (s64)e->outbps) >> 2; spin_unlock(&s->lock); } spin_unlock(&ipvs->est_lock); @@ -166,14 +165,14 @@ void ip_vs_stop_estimator(struct net *net, struct ip_vs_stats *stats) void ip_vs_zero_estimator(struct ip_vs_stats *stats) { struct ip_vs_estimator *est = &stats->est; - struct ip_vs_stats_user *u = &stats->ustats; + struct ip_vs_kstats *k = &stats->kstats; /* reset counters, caller must hold the stats->lock lock */ - est->last_inbytes = u->inbytes; - est->last_outbytes = u->outbytes; - est->last_conns = u->conns; - est->last_inpkts = u->inpkts; - est->last_outpkts = u->outpkts; + est->last_inbytes = k->inbytes; + est->last_outbytes = k->outbytes; + est->last_conns = k->conns; + est->last_inpkts = k->inpkts; + est->last_outpkts = k->outpkts; est->cps = 0; est->inpps = 0; est->outpps = 0; @@ -182,8 +181,7 @@ void ip_vs_zero_estimator(struct ip_vs_stats *stats) } /* Get decoded rates */ -void ip_vs_read_estimator(struct ip_vs_stats_user *dst, - struct ip_vs_stats *stats) +void ip_vs_read_estimator(struct ip_vs_kstats *dst, struct ip_vs_stats *stats) { struct ip_vs_estimator *e = &stats->est; -- cgit v1.2.3 From 72496edcf85e048b4c5373d518e4f27938d9594e Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 11 Feb 2015 22:39:51 +0100 Subject: ALSA: seq: Don't compile snd_seq_device_load_drivers() for built-in Signed-off-by: Takashi Iwai --- include/sound/seq_device.h | 4 ++++ sound/core/seq/seq_device.c | 9 +++------ 2 files changed, 7 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/include/sound/seq_device.h b/include/sound/seq_device.h index 2b5f24cc7548..d52433563da2 100644 --- a/include/sound/seq_device.h +++ b/include/sound/seq_device.h @@ -67,7 +67,11 @@ struct snd_seq_dev_ops { /* * prototypes */ +#ifdef CONFIG_MODULES void snd_seq_device_load_drivers(void); +#else +#define snd_seq_device_load_drivers() +#endif int snd_seq_device_new(struct snd_card *card, int device, char *id, int argsize, struct snd_seq_device **result); int snd_seq_device_register_driver(char *id, struct snd_seq_dev_ops *entry, int argsize); int snd_seq_device_unregister_driver(char *id); diff --git a/sound/core/seq/seq_device.c b/sound/core/seq/seq_device.c index a752a79a8d3a..075a66c0cc6a 100644 --- a/sound/core/seq/seq_device.c +++ b/sound/core/seq/seq_device.c @@ -198,19 +198,16 @@ void snd_seq_autoload_init(void) #endif } EXPORT_SYMBOL(snd_seq_autoload_init); -#else -#define try_autoload(ops) /* NOP */ -#endif - void snd_seq_device_load_drivers(void) { -#ifdef CONFIG_MODULES queue_autoload_drivers(); flush_work(&autoload_work); -#endif } EXPORT_SYMBOL(snd_seq_device_load_drivers); +#else +#define try_autoload(ops) /* NOP */ +#endif /* * register a sequencer device -- cgit v1.2.3 From 7c37ae5c625aaa4836466cfaea829a3199dfc571 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 12 Feb 2015 10:51:59 +0100 Subject: ALSA: seq: Rewrite sequencer device binding with standard bus We've used the old house-made code for binding the sequencer device and driver. This can be far better implemented with the standard bus nowadays. This patch refactors the whole sequencer binding code with the bus /sys/bus/snd_seq. The devices appear as id-card-device on this bus and are bound with the drivers corresponding to the given id like the former implementation. The module autoload is also kept like before. There is no change in API functions by this patch, and almost all transitions are kept inside seq_device.c. The proc file output will change slightly but kept compatible as much as possible. Further integration works will follow in later patches. Signed-off-by: Takashi Iwai --- include/sound/seq_device.h | 3 + sound/core/seq/seq_device.c | 541 ++++++++++++++------------------------------ 2 files changed, 170 insertions(+), 374 deletions(-) (limited to 'include') diff --git a/include/sound/seq_device.h b/include/sound/seq_device.h index d52433563da2..ea256b825146 100644 --- a/include/sound/seq_device.h +++ b/include/sound/seq_device.h @@ -43,8 +43,11 @@ struct snd_seq_device { void *private_data; /* private data for the caller */ void (*private_free)(struct snd_seq_device *device); struct list_head list; /* link to next device */ + struct device dev; }; +#define to_seq_dev(_dev) \ + container_of(_dev, struct snd_seq_device, dev) /* driver operators * init_device: diff --git a/sound/core/seq/seq_device.c b/sound/core/seq/seq_device.c index 075a66c0cc6a..d3320ffe43c2 100644 --- a/sound/core/seq/seq_device.c +++ b/sound/core/seq/seq_device.c @@ -36,6 +36,7 @@ * */ +#include #include #include #include @@ -51,77 +52,57 @@ MODULE_AUTHOR("Takashi Iwai "); MODULE_DESCRIPTION("ALSA sequencer device management"); MODULE_LICENSE("GPL"); -/* driver state */ -#define DRIVER_EMPTY 0 -#define DRIVER_LOADED (1<<0) -#define DRIVER_REQUESTED (1<<1) -#define DRIVER_LOCKED (1<<2) -#define DRIVER_REQUESTING (1<<3) +struct snd_seq_driver { + struct device_driver driver; + char id[ID_LEN]; + int argsize; + struct snd_seq_dev_ops ops; +}; -struct ops_list { - char id[ID_LEN]; /* driver id */ - int driver; /* driver state */ - int used; /* reference counter */ - int argsize; /* argument size */ +#define to_seq_drv(_drv) \ + container_of(_drv, struct snd_seq_driver, driver) - /* operators */ - struct snd_seq_dev_ops ops; +/* + * bus definition + */ +static int snd_seq_bus_match(struct device *dev, struct device_driver *drv) +{ + struct snd_seq_device *sdev = to_seq_dev(dev); + struct snd_seq_driver *sdrv = to_seq_drv(drv); - /* registered devices */ - struct list_head dev_list; /* list of devices */ - int num_devices; /* number of associated devices */ - int num_init_devices; /* number of initialized devices */ - struct mutex reg_mutex; + return strcmp(sdrv->id, sdev->id) == 0 && + sdrv->argsize == sdev->argsize; +} - struct list_head list; /* next driver */ +static struct bus_type snd_seq_bus_type = { + .name = "snd_seq", + .match = snd_seq_bus_match, }; - -static LIST_HEAD(opslist); -static int num_ops; -static DEFINE_MUTEX(ops_mutex); +/* + * proc interface -- just for compatibility + */ #ifdef CONFIG_PROC_FS static struct snd_info_entry *info_entry; -#endif -/* - * prototypes - */ -static int snd_seq_device_free(struct snd_seq_device *dev); -static int snd_seq_device_dev_free(struct snd_device *device); -static int snd_seq_device_dev_register(struct snd_device *device); -static int snd_seq_device_dev_disconnect(struct snd_device *device); - -static int init_device(struct snd_seq_device *dev, struct ops_list *ops); -static int free_device(struct snd_seq_device *dev, struct ops_list *ops); -static struct ops_list *find_driver(char *id, int create_if_empty); -static struct ops_list *create_driver(char *id); -static void unlock_driver(struct ops_list *ops); -static void remove_drivers(void); +static int print_dev_info(struct device *dev, void *data) +{ + struct snd_seq_device *sdev = to_seq_dev(dev); + struct snd_info_buffer *buffer = data; -/* - * show all drivers and their status - */ + snd_iprintf(buffer, "snd-%s,%s,%d\n", sdev->id, + dev->driver ? "loaded" : "empty", + dev->driver ? 1 : 0); + return 0; +} -#ifdef CONFIG_PROC_FS static void snd_seq_device_info(struct snd_info_entry *entry, struct snd_info_buffer *buffer) { - struct ops_list *ops; - - mutex_lock(&ops_mutex); - list_for_each_entry(ops, &opslist, list) { - snd_iprintf(buffer, "snd-%s%s%s%s,%d\n", - ops->id, - ops->driver & DRIVER_LOADED ? ",loaded" : (ops->driver == DRIVER_EMPTY ? ",empty" : ""), - ops->driver & DRIVER_REQUESTED ? ",requested" : "", - ops->driver & DRIVER_LOCKED ? ",locked" : "", - ops->num_devices); - } - mutex_unlock(&ops_mutex); + bus_for_each_dev(&snd_seq_bus_type, NULL, buffer, print_dev_info); } #endif - + /* * load all registered drivers (called from seq_clientmgr.c) */ @@ -141,52 +122,29 @@ void snd_seq_autoload_unlock(void) } EXPORT_SYMBOL(snd_seq_autoload_unlock); -static void autoload_drivers(void) +static int request_seq_drv(struct device *dev, void *data) { - /* avoid reentrance */ - if (atomic_inc_return(&snd_seq_in_init) == 1) { - struct ops_list *ops; - - mutex_lock(&ops_mutex); - list_for_each_entry(ops, &opslist, list) { - if ((ops->driver & DRIVER_REQUESTING) && - !(ops->driver & DRIVER_REQUESTED)) { - ops->used++; - mutex_unlock(&ops_mutex); - ops->driver |= DRIVER_REQUESTED; - request_module("snd-%s", ops->id); - mutex_lock(&ops_mutex); - ops->used--; - } - } - mutex_unlock(&ops_mutex); - } - atomic_dec(&snd_seq_in_init); -} + struct snd_seq_device *sdev = to_seq_dev(dev); -static void call_autoload(struct work_struct *work) -{ - autoload_drivers(); + if (!dev->driver) + request_module("snd-%s", sdev->id); + return 0; } -static DECLARE_WORK(autoload_work, call_autoload); - -static void try_autoload(struct ops_list *ops) +static void autoload_drivers(struct work_struct *work) { - if (!ops->driver) { - ops->driver |= DRIVER_REQUESTING; - schedule_work(&autoload_work); - } + /* avoid reentrance */ + if (atomic_inc_return(&snd_seq_in_init) == 1) + bus_for_each_dev(&snd_seq_bus_type, NULL, NULL, + request_seq_drv); + atomic_dec(&snd_seq_in_init); } +static DECLARE_WORK(autoload_work, autoload_drivers); + static void queue_autoload_drivers(void) { - struct ops_list *ops; - - mutex_lock(&ops_mutex); - list_for_each_entry(ops, &opslist, list) - try_autoload(ops); - mutex_unlock(&ops_mutex); + schedule_work(&autoload_work); } void snd_seq_autoload_init(void) @@ -206,9 +164,50 @@ void snd_seq_device_load_drivers(void) } EXPORT_SYMBOL(snd_seq_device_load_drivers); #else -#define try_autoload(ops) /* NOP */ +#define queue_autoload_drivers() /* NOP */ #endif +/* + * device management + */ +static int snd_seq_device_dev_free(struct snd_device *device) +{ + struct snd_seq_device *dev = device->device_data; + + put_device(&dev->dev); + return 0; +} + +static int snd_seq_device_dev_register(struct snd_device *device) +{ + struct snd_seq_device *dev = device->device_data; + int err; + + err = device_add(&dev->dev); + if (err < 0) + return err; + if (!dev->dev.driver) + queue_autoload_drivers(); + return 0; +} + +static int snd_seq_device_dev_disconnect(struct snd_device *device) +{ + struct snd_seq_device *dev = device->device_data; + + device_del(&dev->dev); + return 0; +} + +static void snd_seq_dev_release(struct device *dev) +{ + struct snd_seq_device *sdev = to_seq_dev(dev); + + if (sdev->private_free) + sdev->private_free(sdev); + kfree(sdev); +} + /* * register a sequencer device * card = card info @@ -220,7 +219,6 @@ int snd_seq_device_new(struct snd_card *card, int device, char *id, int argsize, struct snd_seq_device **result) { struct snd_seq_device *dev; - struct ops_list *ops; int err; static struct snd_device_ops dops = { .dev_free = snd_seq_device_dev_free, @@ -234,15 +232,9 @@ int snd_seq_device_new(struct snd_card *card, int device, char *id, int argsize, if (snd_BUG_ON(!id)) return -EINVAL; - ops = find_driver(id, 1); - if (ops == NULL) - return -ENOMEM; - - dev = kzalloc(sizeof(*dev)*2 + argsize, GFP_KERNEL); - if (dev == NULL) { - unlock_driver(ops); + dev = kzalloc(sizeof(*dev) + argsize, GFP_KERNEL); + if (!dev) return -ENOMEM; - } /* set up device info */ dev->card = card; @@ -251,20 +243,19 @@ int snd_seq_device_new(struct snd_card *card, int device, char *id, int argsize, dev->argsize = argsize; dev->status = SNDRV_SEQ_DEVICE_FREE; - /* add this device to the list */ - mutex_lock(&ops->reg_mutex); - list_add_tail(&dev->list, &ops->dev_list); - ops->num_devices++; - mutex_unlock(&ops->reg_mutex); + device_initialize(&dev->dev); + dev->dev.parent = &card->card_dev; + dev->dev.bus = &snd_seq_bus_type; + dev->dev.release = snd_seq_dev_release; + dev_set_name(&dev->dev, "%s-%d-%d", dev->id, card->number, device); - if ((err = snd_device_new(card, SNDRV_DEV_SEQUENCER, dev, &dops)) < 0) { - snd_seq_device_free(dev); + /* add this device to the list */ + err = snd_device_new(card, SNDRV_DEV_SEQUENCER, dev, &dops); + if (err < 0) { + put_device(&dev->dev); return err; } - try_autoload(ops); - unlock_driver(ops); - if (result) *result = dev; @@ -273,82 +264,33 @@ int snd_seq_device_new(struct snd_card *card, int device, char *id, int argsize, EXPORT_SYMBOL(snd_seq_device_new); /* - * free the existing device - */ -static int snd_seq_device_free(struct snd_seq_device *dev) -{ - struct ops_list *ops; - - if (snd_BUG_ON(!dev)) - return -EINVAL; - - ops = find_driver(dev->id, 0); - if (ops == NULL) - return -ENXIO; - - /* remove the device from the list */ - mutex_lock(&ops->reg_mutex); - list_del(&dev->list); - ops->num_devices--; - mutex_unlock(&ops->reg_mutex); - - free_device(dev, ops); - if (dev->private_free) - dev->private_free(dev); - kfree(dev); - - unlock_driver(ops); - - return 0; -} - -static int snd_seq_device_dev_free(struct snd_device *device) -{ - struct snd_seq_device *dev = device->device_data; - return snd_seq_device_free(dev); -} - -/* - * register the device + * driver binding - just pass to each driver callback */ -static int snd_seq_device_dev_register(struct snd_device *device) +static int snd_seq_drv_probe(struct device *dev) { - struct snd_seq_device *dev = device->device_data; - struct ops_list *ops; - - ops = find_driver(dev->id, 0); - if (ops == NULL) - return -ENOENT; - - /* initialize this device if the corresponding driver was - * already loaded - */ - if (ops->driver & DRIVER_LOADED) - init_device(dev, ops); + struct snd_seq_driver *sdrv = to_seq_drv(dev->driver); + struct snd_seq_device *sdev = to_seq_dev(dev); + int err; - unlock_driver(ops); + err = sdrv->ops.init_device(sdev); + if (err < 0) + return err; + sdev->status = SNDRV_SEQ_DEVICE_REGISTERED; return 0; } -EXPORT_SYMBOL(snd_seq_device_register_driver); -/* - * disconnect the device - */ -static int snd_seq_device_dev_disconnect(struct snd_device *device) +static int snd_seq_drv_remove(struct device *dev) { - struct snd_seq_device *dev = device->device_data; - struct ops_list *ops; - - ops = find_driver(dev->id, 0); - if (ops == NULL) - return -ENOENT; - - free_device(dev, ops); + struct snd_seq_driver *sdrv = to_seq_drv(dev->driver); + struct snd_seq_device *sdev = to_seq_dev(dev); + int err; - unlock_driver(ops); + err = sdrv->ops.free_device(sdev); + if (err < 0) + return err; + sdev->status = SNDRV_SEQ_DEVICE_FREE; return 0; } -EXPORT_SYMBOL(snd_seq_device_unregister_driver); /* * register device driver @@ -358,226 +300,66 @@ EXPORT_SYMBOL(snd_seq_device_unregister_driver); int snd_seq_device_register_driver(char *id, struct snd_seq_dev_ops *entry, int argsize) { - struct ops_list *ops; - struct snd_seq_device *dev; + struct snd_seq_driver *sdrv; + int err; if (id == NULL || entry == NULL || entry->init_device == NULL || entry->free_device == NULL) return -EINVAL; - ops = find_driver(id, 1); - if (ops == NULL) + sdrv = kzalloc(sizeof(*sdrv), GFP_KERNEL); + if (!sdrv) return -ENOMEM; - if (ops->driver & DRIVER_LOADED) { - pr_warn("ALSA: seq: driver_register: driver '%s' already exists\n", id); - unlock_driver(ops); - return -EBUSY; - } - - mutex_lock(&ops->reg_mutex); - /* copy driver operators */ - ops->ops = *entry; - ops->driver |= DRIVER_LOADED; - ops->argsize = argsize; - /* initialize existing devices if necessary */ - list_for_each_entry(dev, &ops->dev_list, list) { - init_device(dev, ops); - } - mutex_unlock(&ops->reg_mutex); - - unlock_driver(ops); - - return 0; + sdrv->driver.name = id; + sdrv->driver.bus = &snd_seq_bus_type; + sdrv->driver.probe = snd_seq_drv_probe; + sdrv->driver.remove = snd_seq_drv_remove; + strlcpy(sdrv->id, id, sizeof(sdrv->id)); + sdrv->argsize = argsize; + sdrv->ops = *entry; + + err = driver_register(&sdrv->driver); + if (err < 0) + kfree(sdrv); + return err; } +EXPORT_SYMBOL(snd_seq_device_register_driver); - -/* - * create driver record +/* callback to find a specific driver; data is a pointer to the id string ptr. + * when the id matches, store the driver pointer in return and break the loop. */ -static struct ops_list * create_driver(char *id) +static int find_drv(struct device_driver *drv, void *data) { - struct ops_list *ops; - - ops = kzalloc(sizeof(*ops), GFP_KERNEL); - if (ops == NULL) - return ops; - - /* set up driver entry */ - strlcpy(ops->id, id, sizeof(ops->id)); - mutex_init(&ops->reg_mutex); - /* - * The ->reg_mutex locking rules are per-driver, so we create - * separate per-driver lock classes: - */ - lockdep_set_class(&ops->reg_mutex, (struct lock_class_key *)id); - - ops->driver = DRIVER_EMPTY; - INIT_LIST_HEAD(&ops->dev_list); - /* lock this instance */ - ops->used = 1; - - /* register driver entry */ - mutex_lock(&ops_mutex); - list_add_tail(&ops->list, &opslist); - num_ops++; - mutex_unlock(&ops_mutex); - - return ops; -} + struct snd_seq_driver *sdrv = to_seq_drv(drv); + void **ptr = (void **)data; + if (strcmp(sdrv->id, *ptr)) + return 0; /* id don't match, continue the loop */ + *ptr = sdrv; + return 1; /* break the loop */ +} /* * unregister the specified driver */ int snd_seq_device_unregister_driver(char *id) { - struct ops_list *ops; - struct snd_seq_device *dev; + struct snd_seq_driver *sdrv = (struct snd_seq_driver *)id; - ops = find_driver(id, 0); - if (ops == NULL) + if (!bus_for_each_drv(&snd_seq_bus_type, NULL, &sdrv, find_drv)) return -ENXIO; - if (! (ops->driver & DRIVER_LOADED) || - (ops->driver & DRIVER_LOCKED)) { - pr_err("ALSA: seq: driver_unregister: cannot unload driver '%s': status=%x\n", - id, ops->driver); - unlock_driver(ops); - return -EBUSY; - } - - /* close and release all devices associated with this driver */ - mutex_lock(&ops->reg_mutex); - ops->driver |= DRIVER_LOCKED; /* do not remove this driver recursively */ - list_for_each_entry(dev, &ops->dev_list, list) { - free_device(dev, ops); - } - - ops->driver = 0; - if (ops->num_init_devices > 0) - pr_err("ALSA: seq: free_driver: init_devices > 0!! (%d)\n", - ops->num_init_devices); - mutex_unlock(&ops->reg_mutex); - - unlock_driver(ops); - - /* remove empty driver entries */ - remove_drivers(); - + driver_unregister(&sdrv->driver); + kfree(sdrv); return 0; } - - -/* - * remove empty driver entries - */ -static void remove_drivers(void) -{ - struct list_head *head; - - mutex_lock(&ops_mutex); - head = opslist.next; - while (head != &opslist) { - struct ops_list *ops = list_entry(head, struct ops_list, list); - if (! (ops->driver & DRIVER_LOADED) && - ops->used == 0 && ops->num_devices == 0) { - head = head->next; - list_del(&ops->list); - kfree(ops); - num_ops--; - } else - head = head->next; - } - mutex_unlock(&ops_mutex); -} - -/* - * initialize the device - call init_device operator - */ -static int init_device(struct snd_seq_device *dev, struct ops_list *ops) -{ - if (! (ops->driver & DRIVER_LOADED)) - return 0; /* driver is not loaded yet */ - if (dev->status != SNDRV_SEQ_DEVICE_FREE) - return 0; /* already initialized */ - if (ops->argsize != dev->argsize) { - pr_err("ALSA: seq: incompatible device '%s' for plug-in '%s' (%d %d)\n", - dev->name, ops->id, ops->argsize, dev->argsize); - return -EINVAL; - } - if (ops->ops.init_device(dev) >= 0) { - dev->status = SNDRV_SEQ_DEVICE_REGISTERED; - ops->num_init_devices++; - } else { - pr_err("ALSA: seq: init_device failed: %s: %s\n", - dev->name, dev->id); - } - - return 0; -} - -/* - * release the device - call free_device operator - */ -static int free_device(struct snd_seq_device *dev, struct ops_list *ops) -{ - int result; - - if (! (ops->driver & DRIVER_LOADED)) - return 0; /* driver is not loaded yet */ - if (dev->status != SNDRV_SEQ_DEVICE_REGISTERED) - return 0; /* not registered */ - if (ops->argsize != dev->argsize) { - pr_err("ALSA: seq: incompatible device '%s' for plug-in '%s' (%d %d)\n", - dev->name, ops->id, ops->argsize, dev->argsize); - return -EINVAL; - } - if ((result = ops->ops.free_device(dev)) >= 0 || result == -ENXIO) { - dev->status = SNDRV_SEQ_DEVICE_FREE; - dev->driver_data = NULL; - ops->num_init_devices--; - } else { - pr_err("ALSA: seq: free_device failed: %s: %s\n", - dev->name, dev->id); - } - - return 0; -} - -/* - * find the matching driver with given id - */ -static struct ops_list * find_driver(char *id, int create_if_empty) -{ - struct ops_list *ops; - - mutex_lock(&ops_mutex); - list_for_each_entry(ops, &opslist, list) { - if (strcmp(ops->id, id) == 0) { - ops->used++; - mutex_unlock(&ops_mutex); - return ops; - } - } - mutex_unlock(&ops_mutex); - if (create_if_empty) - return create_driver(id); - return NULL; -} - -static void unlock_driver(struct ops_list *ops) -{ - mutex_lock(&ops_mutex); - ops->used--; - mutex_unlock(&ops_mutex); -} - +EXPORT_SYMBOL(snd_seq_device_unregister_driver); /* * module part */ -static int __init alsa_seq_device_init(void) +static int __init seq_dev_proc_init(void) { #ifdef CONFIG_PROC_FS info_entry = snd_info_create_module_entry(THIS_MODULE, "drivers", @@ -594,17 +376,28 @@ static int __init alsa_seq_device_init(void) return 0; } +static int __init alsa_seq_device_init(void) +{ + int err; + + err = bus_register(&snd_seq_bus_type); + if (err < 0) + return err; + err = seq_dev_proc_init(); + if (err < 0) + bus_unregister(&snd_seq_bus_type); + return err; +} + static void __exit alsa_seq_device_exit(void) { #ifdef CONFIG_MODULES cancel_work_sync(&autoload_work); #endif - remove_drivers(); #ifdef CONFIG_PROC_FS snd_info_free_entry(info_entry); #endif - if (num_ops) - pr_err("ALSA: seq: drivers not released (%d)\n", num_ops); + bus_unregister(&snd_seq_bus_type); } module_init(alsa_seq_device_init) -- cgit v1.2.3 From af03c243a1f014145dae34368fe975b2f08ed964 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 12 Feb 2015 13:40:50 +0100 Subject: ALSA: seq: Clean up device and driver structs Use const string pointer instead of copying the id string to each object. Also drop the status and list fields of snd_seq_device struct that are no longer used. Signed-off-by: Takashi Iwai --- include/sound/seq_device.h | 18 ++++++------------ sound/core/seq/seq_device.c | 31 ++++++++++--------------------- 2 files changed, 16 insertions(+), 33 deletions(-) (limited to 'include') diff --git a/include/sound/seq_device.h b/include/sound/seq_device.h index ea256b825146..b13cd2930d32 100644 --- a/include/sound/seq_device.h +++ b/include/sound/seq_device.h @@ -25,24 +25,16 @@ * registered device information */ -#define ID_LEN 32 - -/* status flag */ -#define SNDRV_SEQ_DEVICE_FREE 0 -#define SNDRV_SEQ_DEVICE_REGISTERED 1 - struct snd_seq_device { /* device info */ struct snd_card *card; /* sound card */ int device; /* device number */ - char id[ID_LEN]; /* driver id */ + const char *id; /* driver id */ char name[80]; /* device name */ int argsize; /* size of the argument */ void *driver_data; /* private data for driver */ - int status; /* flag - read only */ void *private_data; /* private data for the caller */ void (*private_free)(struct snd_seq_device *device); - struct list_head list; /* link to next device */ struct device dev; }; @@ -75,9 +67,11 @@ void snd_seq_device_load_drivers(void); #else #define snd_seq_device_load_drivers() #endif -int snd_seq_device_new(struct snd_card *card, int device, char *id, int argsize, struct snd_seq_device **result); -int snd_seq_device_register_driver(char *id, struct snd_seq_dev_ops *entry, int argsize); -int snd_seq_device_unregister_driver(char *id); +int snd_seq_device_new(struct snd_card *card, int device, const char *id, + int argsize, struct snd_seq_device **result); +int snd_seq_device_register_driver(const char *id, + struct snd_seq_dev_ops *entry, int argsize); +int snd_seq_device_unregister_driver(const char *id); #define SNDRV_SEQ_DEVICE_ARGPTR(dev) (void *)((char *)(dev) + sizeof(struct snd_seq_device)) diff --git a/sound/core/seq/seq_device.c b/sound/core/seq/seq_device.c index d3320ffe43c2..49daf6e3a387 100644 --- a/sound/core/seq/seq_device.c +++ b/sound/core/seq/seq_device.c @@ -54,7 +54,7 @@ MODULE_LICENSE("GPL"); struct snd_seq_driver { struct device_driver driver; - char id[ID_LEN]; + const char *id; int argsize; struct snd_seq_dev_ops ops; }; @@ -215,8 +215,8 @@ static void snd_seq_dev_release(struct device *dev) * id = id of driver * result = return pointer (NULL allowed if unnecessary) */ -int snd_seq_device_new(struct snd_card *card, int device, char *id, int argsize, - struct snd_seq_device **result) +int snd_seq_device_new(struct snd_card *card, int device, const char *id, + int argsize, struct snd_seq_device **result) { struct snd_seq_device *dev; int err; @@ -239,9 +239,8 @@ int snd_seq_device_new(struct snd_card *card, int device, char *id, int argsize, /* set up device info */ dev->card = card; dev->device = device; - strlcpy(dev->id, id, sizeof(dev->id)); + dev->id = id; dev->argsize = argsize; - dev->status = SNDRV_SEQ_DEVICE_FREE; device_initialize(&dev->dev); dev->dev.parent = &card->card_dev; @@ -270,26 +269,16 @@ static int snd_seq_drv_probe(struct device *dev) { struct snd_seq_driver *sdrv = to_seq_drv(dev->driver); struct snd_seq_device *sdev = to_seq_dev(dev); - int err; - err = sdrv->ops.init_device(sdev); - if (err < 0) - return err; - sdev->status = SNDRV_SEQ_DEVICE_REGISTERED; - return 0; + return sdrv->ops.init_device(sdev); } static int snd_seq_drv_remove(struct device *dev) { struct snd_seq_driver *sdrv = to_seq_drv(dev->driver); struct snd_seq_device *sdev = to_seq_dev(dev); - int err; - err = sdrv->ops.free_device(sdev); - if (err < 0) - return err; - sdev->status = SNDRV_SEQ_DEVICE_FREE; - return 0; + return sdrv->ops.free_device(sdev); } /* @@ -297,8 +286,8 @@ static int snd_seq_drv_remove(struct device *dev) * id = driver id * entry = driver operators - duplicated to each instance */ -int snd_seq_device_register_driver(char *id, struct snd_seq_dev_ops *entry, - int argsize) +int snd_seq_device_register_driver(const char *id, + struct snd_seq_dev_ops *entry, int argsize) { struct snd_seq_driver *sdrv; int err; @@ -315,7 +304,7 @@ int snd_seq_device_register_driver(char *id, struct snd_seq_dev_ops *entry, sdrv->driver.bus = &snd_seq_bus_type; sdrv->driver.probe = snd_seq_drv_probe; sdrv->driver.remove = snd_seq_drv_remove; - strlcpy(sdrv->id, id, sizeof(sdrv->id)); + sdrv->id = id; sdrv->argsize = argsize; sdrv->ops = *entry; @@ -343,7 +332,7 @@ static int find_drv(struct device_driver *drv, void *data) /* * unregister the specified driver */ -int snd_seq_device_unregister_driver(char *id) +int snd_seq_device_unregister_driver(const char *id) { struct snd_seq_driver *sdrv = (struct snd_seq_driver *)id; -- cgit v1.2.3 From 056622053b8ae02978678ac1321b5bd956e7c812 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 12 Feb 2015 13:43:22 +0100 Subject: ALSA: seq: Define driver object in each driver This patch moves the driver object initialization and allocation to each driver's module init/exit code like other normal drivers. The snd_seq_driver struct is now published in seq_device.h, and each driver is responsible to define it with proper driver attributes (name, probe and remove) with snd_seq_driver specific attributes as id and argsize fields. The helper functions snd_seq_driver_register(), snd_seq_driver_unregister() and module_snd_seq_driver() are used for simplifying codes. Signed-off-by: Takashi Iwai --- include/sound/seq_device.h | 27 +++++++---- sound/core/seq/oss/seq_oss.c | 20 +++++--- sound/core/seq/oss/seq_oss_synth.c | 6 ++- sound/core/seq/oss/seq_oss_synth.h | 4 +- sound/core/seq/seq_device.c | 93 ++++---------------------------------- sound/core/seq/seq_midi.c | 28 ++++++++---- sound/drivers/opl3/opl3_seq.c | 34 ++++++-------- sound/drivers/opl4/opl4_seq.c | 33 ++++++-------- sound/isa/sb/emu8000_synth.c | 35 ++++++-------- sound/pci/emu10k1/emu10k1_synth.c | 35 ++++++-------- 10 files changed, 124 insertions(+), 191 deletions(-) (limited to 'include') diff --git a/include/sound/seq_device.h b/include/sound/seq_device.h index b13cd2930d32..ddc0d504cf39 100644 --- a/include/sound/seq_device.h +++ b/include/sound/seq_device.h @@ -41,8 +41,10 @@ struct snd_seq_device { #define to_seq_dev(_dev) \ container_of(_dev, struct snd_seq_device, dev) +/* sequencer driver */ + /* driver operators - * init_device: + * probe: * Initialize the device with given parameters. * Typically, * 1. call snd_hwdep_new @@ -50,15 +52,19 @@ struct snd_seq_device { * 3. call snd_hwdep_register * 4. store the instance to dev->driver_data pointer. * - * free_device: + * remove: * Release the private data. * Typically, call snd_device_free(dev->card, dev->driver_data) */ -struct snd_seq_dev_ops { - int (*init_device)(struct snd_seq_device *dev); - int (*free_device)(struct snd_seq_device *dev); +struct snd_seq_driver { + struct device_driver driver; + char *id; + int argsize; }; +#define to_seq_drv(_drv) \ + container_of(_drv, struct snd_seq_driver, driver) + /* * prototypes */ @@ -69,12 +75,17 @@ void snd_seq_device_load_drivers(void); #endif int snd_seq_device_new(struct snd_card *card, int device, const char *id, int argsize, struct snd_seq_device **result); -int snd_seq_device_register_driver(const char *id, - struct snd_seq_dev_ops *entry, int argsize); -int snd_seq_device_unregister_driver(const char *id); #define SNDRV_SEQ_DEVICE_ARGPTR(dev) (void *)((char *)(dev) + sizeof(struct snd_seq_device)) +int __must_check __snd_seq_driver_register(struct snd_seq_driver *drv, + struct module *mod); +#define snd_seq_driver_register(drv) \ + __snd_seq_driver_register(drv, THIS_MODULE) +void snd_seq_driver_unregister(struct snd_seq_driver *drv); + +#define module_snd_seq_driver(drv) \ + module_driver(drv, snd_seq_driver_register, snd_seq_driver_unregister) /* * id strings for generic devices diff --git a/sound/core/seq/oss/seq_oss.c b/sound/core/seq/oss/seq_oss.c index 16d42679e43f..ae1814aa767e 100644 --- a/sound/core/seq/oss/seq_oss.c +++ b/sound/core/seq/oss/seq_oss.c @@ -65,13 +65,19 @@ static unsigned int odev_poll(struct file *file, poll_table * wait); * module interface */ +static struct snd_seq_driver seq_oss_synth_driver = { + .driver = { + .name = KBUILD_MODNAME, + .probe = snd_seq_oss_synth_probe, + .remove = snd_seq_oss_synth_remove, + }, + .id = SNDRV_SEQ_DEV_ID_OSS, + .argsize = sizeof(struct snd_seq_oss_reg), +}; + static int __init alsa_seq_oss_init(void) { int rc; - static struct snd_seq_dev_ops ops = { - snd_seq_oss_synth_register, - snd_seq_oss_synth_unregister, - }; snd_seq_autoload_lock(); if ((rc = register_device()) < 0) @@ -86,8 +92,8 @@ static int __init alsa_seq_oss_init(void) goto error; } - if ((rc = snd_seq_device_register_driver(SNDRV_SEQ_DEV_ID_OSS, &ops, - sizeof(struct snd_seq_oss_reg))) < 0) { + rc = snd_seq_driver_register(&seq_oss_synth_driver); + if (rc < 0) { snd_seq_oss_delete_client(); unregister_proc(); unregister_device(); @@ -104,7 +110,7 @@ static int __init alsa_seq_oss_init(void) static void __exit alsa_seq_oss_exit(void) { - snd_seq_device_unregister_driver(SNDRV_SEQ_DEV_ID_OSS); + snd_seq_driver_unregister(&seq_oss_synth_driver); snd_seq_oss_delete_client(); unregister_proc(); unregister_device(); diff --git a/sound/core/seq/oss/seq_oss_synth.c b/sound/core/seq/oss/seq_oss_synth.c index 701feb71b700..835edc80f918 100644 --- a/sound/core/seq/oss/seq_oss_synth.c +++ b/sound/core/seq/oss/seq_oss_synth.c @@ -98,8 +98,9 @@ snd_seq_oss_synth_init(void) * registration of the synth device */ int -snd_seq_oss_synth_register(struct snd_seq_device *dev) +snd_seq_oss_synth_probe(struct device *_dev) { + struct snd_seq_device *dev = to_seq_dev(_dev); int i; struct seq_oss_synth *rec; struct snd_seq_oss_reg *reg = SNDRV_SEQ_DEVICE_ARGPTR(dev); @@ -149,8 +150,9 @@ snd_seq_oss_synth_register(struct snd_seq_device *dev) int -snd_seq_oss_synth_unregister(struct snd_seq_device *dev) +snd_seq_oss_synth_remove(struct device *_dev) { + struct snd_seq_device *dev = to_seq_dev(_dev); int index; struct seq_oss_synth *rec = dev->driver_data; unsigned long flags; diff --git a/sound/core/seq/oss/seq_oss_synth.h b/sound/core/seq/oss/seq_oss_synth.h index dbdfcbb80eaa..74ac55f166b6 100644 --- a/sound/core/seq/oss/seq_oss_synth.h +++ b/sound/core/seq/oss/seq_oss_synth.h @@ -28,8 +28,8 @@ #include void snd_seq_oss_synth_init(void); -int snd_seq_oss_synth_register(struct snd_seq_device *dev); -int snd_seq_oss_synth_unregister(struct snd_seq_device *dev); +int snd_seq_oss_synth_probe(struct device *dev); +int snd_seq_oss_synth_remove(struct device *dev); void snd_seq_oss_synth_setup(struct seq_oss_devinfo *dp); void snd_seq_oss_synth_setup_midi(struct seq_oss_devinfo *dp); void snd_seq_oss_synth_cleanup(struct seq_oss_devinfo *dp); diff --git a/sound/core/seq/seq_device.c b/sound/core/seq/seq_device.c index 49daf6e3a387..48b20f009598 100644 --- a/sound/core/seq/seq_device.c +++ b/sound/core/seq/seq_device.c @@ -52,16 +52,6 @@ MODULE_AUTHOR("Takashi Iwai "); MODULE_DESCRIPTION("ALSA sequencer device management"); MODULE_LICENSE("GPL"); -struct snd_seq_driver { - struct device_driver driver; - const char *id; - int argsize; - struct snd_seq_dev_ops ops; -}; - -#define to_seq_drv(_drv) \ - container_of(_drv, struct snd_seq_driver, driver) - /* * bus definition */ @@ -263,86 +253,23 @@ int snd_seq_device_new(struct snd_card *card, int device, const char *id, EXPORT_SYMBOL(snd_seq_device_new); /* - * driver binding - just pass to each driver callback + * driver registration */ -static int snd_seq_drv_probe(struct device *dev) +int __snd_seq_driver_register(struct snd_seq_driver *drv, struct module *mod) { - struct snd_seq_driver *sdrv = to_seq_drv(dev->driver); - struct snd_seq_device *sdev = to_seq_dev(dev); - - return sdrv->ops.init_device(sdev); -} - -static int snd_seq_drv_remove(struct device *dev) -{ - struct snd_seq_driver *sdrv = to_seq_drv(dev->driver); - struct snd_seq_device *sdev = to_seq_dev(dev); - - return sdrv->ops.free_device(sdev); -} - -/* - * register device driver - * id = driver id - * entry = driver operators - duplicated to each instance - */ -int snd_seq_device_register_driver(const char *id, - struct snd_seq_dev_ops *entry, int argsize) -{ - struct snd_seq_driver *sdrv; - int err; - - if (id == NULL || entry == NULL || - entry->init_device == NULL || entry->free_device == NULL) + if (WARN_ON(!drv->driver.name || !drv->id)) return -EINVAL; - - sdrv = kzalloc(sizeof(*sdrv), GFP_KERNEL); - if (!sdrv) - return -ENOMEM; - - sdrv->driver.name = id; - sdrv->driver.bus = &snd_seq_bus_type; - sdrv->driver.probe = snd_seq_drv_probe; - sdrv->driver.remove = snd_seq_drv_remove; - sdrv->id = id; - sdrv->argsize = argsize; - sdrv->ops = *entry; - - err = driver_register(&sdrv->driver); - if (err < 0) - kfree(sdrv); - return err; -} -EXPORT_SYMBOL(snd_seq_device_register_driver); - -/* callback to find a specific driver; data is a pointer to the id string ptr. - * when the id matches, store the driver pointer in return and break the loop. - */ -static int find_drv(struct device_driver *drv, void *data) -{ - struct snd_seq_driver *sdrv = to_seq_drv(drv); - void **ptr = (void **)data; - - if (strcmp(sdrv->id, *ptr)) - return 0; /* id don't match, continue the loop */ - *ptr = sdrv; - return 1; /* break the loop */ + drv->driver.bus = &snd_seq_bus_type; + drv->driver.owner = mod; + return driver_register(&drv->driver); } +EXPORT_SYMBOL_GPL(__snd_seq_driver_register); -/* - * unregister the specified driver - */ -int snd_seq_device_unregister_driver(const char *id) +void snd_seq_driver_unregister(struct snd_seq_driver *drv) { - struct snd_seq_driver *sdrv = (struct snd_seq_driver *)id; - - if (!bus_for_each_drv(&snd_seq_bus_type, NULL, &sdrv, find_drv)) - return -ENXIO; - driver_unregister(&sdrv->driver); - kfree(sdrv); - return 0; + driver_unregister(&drv->driver); } -EXPORT_SYMBOL(snd_seq_device_unregister_driver); +EXPORT_SYMBOL_GPL(snd_seq_driver_unregister); /* * module part diff --git a/sound/core/seq/seq_midi.c b/sound/core/seq/seq_midi.c index 68fec776da26..79c73119cedc 100644 --- a/sound/core/seq/seq_midi.c +++ b/sound/core/seq/seq_midi.c @@ -273,8 +273,9 @@ static void snd_seq_midisynth_delete(struct seq_midisynth *msynth) /* register new midi synth port */ static int -snd_seq_midisynth_register_port(struct snd_seq_device *dev) +snd_seq_midisynth_probe(struct device *_dev) { + struct snd_seq_device *dev = to_seq_dev(_dev); struct seq_midisynth_client *client; struct seq_midisynth *msynth, *ms; struct snd_seq_port_info *port; @@ -427,8 +428,9 @@ snd_seq_midisynth_register_port(struct snd_seq_device *dev) /* release midi synth port */ static int -snd_seq_midisynth_unregister_port(struct snd_seq_device *dev) +snd_seq_midisynth_remove(struct device *_dev) { + struct snd_seq_device *dev = to_seq_dev(_dev); struct seq_midisynth_client *client; struct seq_midisynth *msynth; struct snd_card *card = dev->card; @@ -457,23 +459,29 @@ snd_seq_midisynth_unregister_port(struct snd_seq_device *dev) return 0; } +static struct snd_seq_driver seq_midisynth_driver = { + .driver = { + .name = KBUILD_MODNAME, + .probe = snd_seq_midisynth_probe, + .remove = snd_seq_midisynth_remove, + }, + .id = SNDRV_SEQ_DEV_ID_MIDISYNTH, + .argsize = 0, +}; static int __init alsa_seq_midi_init(void) { - static struct snd_seq_dev_ops ops = { - snd_seq_midisynth_register_port, - snd_seq_midisynth_unregister_port, - }; - memset(&synths, 0, sizeof(synths)); + int err; + snd_seq_autoload_lock(); - snd_seq_device_register_driver(SNDRV_SEQ_DEV_ID_MIDISYNTH, &ops, 0); + err = snd_seq_driver_register(&seq_midisynth_driver); snd_seq_autoload_unlock(); - return 0; + return err; } static void __exit alsa_seq_midi_exit(void) { - snd_seq_device_unregister_driver(SNDRV_SEQ_DEV_ID_MIDISYNTH); + snd_seq_driver_unregister(&seq_midisynth_driver); } module_init(alsa_seq_midi_init) diff --git a/sound/drivers/opl3/opl3_seq.c b/sound/drivers/opl3/opl3_seq.c index a9f618e06a22..fdae5d7f421f 100644 --- a/sound/drivers/opl3/opl3_seq.c +++ b/sound/drivers/opl3/opl3_seq.c @@ -216,8 +216,9 @@ static int snd_opl3_synth_create_port(struct snd_opl3 * opl3) /* ------------------------------ */ -static int snd_opl3_seq_new_device(struct snd_seq_device *dev) +static int snd_opl3_seq_probe(struct device *_dev) { + struct snd_seq_device *dev = to_seq_dev(_dev); struct snd_opl3 *opl3; int client, err; char name[32]; @@ -257,8 +258,9 @@ static int snd_opl3_seq_new_device(struct snd_seq_device *dev) return 0; } -static int snd_opl3_seq_delete_device(struct snd_seq_device *dev) +static int snd_opl3_seq_remove(struct device *_dev) { + struct snd_seq_device *dev = to_seq_dev(_dev); struct snd_opl3 *opl3; opl3 = *(struct snd_opl3 **)SNDRV_SEQ_DEVICE_ARGPTR(dev); @@ -275,22 +277,14 @@ static int snd_opl3_seq_delete_device(struct snd_seq_device *dev) return 0; } -static int __init alsa_opl3_seq_init(void) -{ - static struct snd_seq_dev_ops ops = - { - snd_opl3_seq_new_device, - snd_opl3_seq_delete_device - }; - - return snd_seq_device_register_driver(SNDRV_SEQ_DEV_ID_OPL3, &ops, - sizeof(struct snd_opl3 *)); -} - -static void __exit alsa_opl3_seq_exit(void) -{ - snd_seq_device_unregister_driver(SNDRV_SEQ_DEV_ID_OPL3); -} +static struct snd_seq_driver opl3_seq_driver = { + .driver = { + .name = KBUILD_MODNAME, + .probe = snd_opl3_seq_probe, + .remove = snd_opl3_seq_remove, + }, + .id = SNDRV_SEQ_DEV_ID_OPL3, + .argsize = sizeof(struct snd_opl3 *), +}; -module_init(alsa_opl3_seq_init) -module_exit(alsa_opl3_seq_exit) +module_snd_seq_driver(opl3_seq_driver); diff --git a/sound/drivers/opl4/opl4_seq.c b/sound/drivers/opl4/opl4_seq.c index 99197699c55a..03d6202f4829 100644 --- a/sound/drivers/opl4/opl4_seq.c +++ b/sound/drivers/opl4/opl4_seq.c @@ -124,8 +124,9 @@ static void snd_opl4_seq_free_port(void *private_data) snd_midi_channel_free_set(opl4->chset); } -static int snd_opl4_seq_new_device(struct snd_seq_device *dev) +static int snd_opl4_seq_probe(struct device *_dev) { + struct snd_seq_device *dev = to_seq_dev(_dev); struct snd_opl4 *opl4; int client; struct snd_seq_port_callback pcallbacks; @@ -180,8 +181,9 @@ static int snd_opl4_seq_new_device(struct snd_seq_device *dev) return 0; } -static int snd_opl4_seq_delete_device(struct snd_seq_device *dev) +static int snd_opl4_seq_remove(struct device *_dev) { + struct snd_seq_device *dev = to_seq_dev(_dev); struct snd_opl4 *opl4; opl4 = *(struct snd_opl4 **)SNDRV_SEQ_DEVICE_ARGPTR(dev); @@ -195,21 +197,14 @@ static int snd_opl4_seq_delete_device(struct snd_seq_device *dev) return 0; } -static int __init alsa_opl4_synth_init(void) -{ - static struct snd_seq_dev_ops ops = { - snd_opl4_seq_new_device, - snd_opl4_seq_delete_device - }; - - return snd_seq_device_register_driver(SNDRV_SEQ_DEV_ID_OPL4, &ops, - sizeof(struct snd_opl4 *)); -} - -static void __exit alsa_opl4_synth_exit(void) -{ - snd_seq_device_unregister_driver(SNDRV_SEQ_DEV_ID_OPL4); -} +static struct snd_seq_driver opl4_seq_driver = { + .driver = { + .name = KBUILD_MODNAME, + .probe = snd_opl4_seq_probe, + .remove = snd_opl4_seq_remove, + }, + .id = SNDRV_SEQ_DEV_ID_OPL4, + .argsize = sizeof(struct snd_opl4 *), +}; -module_init(alsa_opl4_synth_init) -module_exit(alsa_opl4_synth_exit) +module_snd_seq_driver(opl4_seq_driver); diff --git a/sound/isa/sb/emu8000_synth.c b/sound/isa/sb/emu8000_synth.c index 72332dfada9a..4aa719cad331 100644 --- a/sound/isa/sb/emu8000_synth.c +++ b/sound/isa/sb/emu8000_synth.c @@ -34,8 +34,9 @@ MODULE_LICENSE("GPL"); /* * create a new hardware dependent device for Emu8000 */ -static int snd_emu8000_new_device(struct snd_seq_device *dev) +static int snd_emu8000_probe(struct device *_dev) { + struct snd_seq_device *dev = to_seq_dev(_dev); struct snd_emu8000 *hw; struct snd_emux *emu; @@ -93,8 +94,9 @@ static int snd_emu8000_new_device(struct snd_seq_device *dev) /* * free all resources */ -static int snd_emu8000_delete_device(struct snd_seq_device *dev) +static int snd_emu8000_remove(struct device *_dev) { + struct snd_seq_device *dev = to_seq_dev(_dev); struct snd_emu8000 *hw; if (dev->driver_data == NULL) @@ -114,21 +116,14 @@ static int snd_emu8000_delete_device(struct snd_seq_device *dev) * INIT part */ -static int __init alsa_emu8000_init(void) -{ - - static struct snd_seq_dev_ops ops = { - snd_emu8000_new_device, - snd_emu8000_delete_device, - }; - return snd_seq_device_register_driver(SNDRV_SEQ_DEV_ID_EMU8000, &ops, - sizeof(struct snd_emu8000*)); -} - -static void __exit alsa_emu8000_exit(void) -{ - snd_seq_device_unregister_driver(SNDRV_SEQ_DEV_ID_EMU8000); -} - -module_init(alsa_emu8000_init) -module_exit(alsa_emu8000_exit) +static struct snd_seq_driver emu8000_driver = { + .driver = { + .name = KBUILD_MODNAME, + .probe = snd_emu8000_probe, + .remove = snd_emu8000_remove, + }, + .id = SNDRV_SEQ_DEV_ID_EMU8000, + .argsize = sizeof(struct snd_emu8000 *), +}; + +module_snd_seq_driver(emu8000_driver); diff --git a/sound/pci/emu10k1/emu10k1_synth.c b/sound/pci/emu10k1/emu10k1_synth.c index 4c41c903a840..5457d5613f6b 100644 --- a/sound/pci/emu10k1/emu10k1_synth.c +++ b/sound/pci/emu10k1/emu10k1_synth.c @@ -29,8 +29,9 @@ MODULE_LICENSE("GPL"); /* * create a new hardware dependent device for Emu10k1 */ -static int snd_emu10k1_synth_new_device(struct snd_seq_device *dev) +static int snd_emu10k1_synth_probe(struct device *_dev) { + struct snd_seq_device *dev = to_seq_dev(_dev); struct snd_emux *emux; struct snd_emu10k1 *hw; struct snd_emu10k1_synth_arg *arg; @@ -79,8 +80,9 @@ static int snd_emu10k1_synth_new_device(struct snd_seq_device *dev) return 0; } -static int snd_emu10k1_synth_delete_device(struct snd_seq_device *dev) +static int snd_emu10k1_synth_remove(struct device *_dev) { + struct snd_seq_device *dev = to_seq_dev(_dev); struct snd_emux *emux; struct snd_emu10k1 *hw; unsigned long flags; @@ -104,21 +106,14 @@ static int snd_emu10k1_synth_delete_device(struct snd_seq_device *dev) * INIT part */ -static int __init alsa_emu10k1_synth_init(void) -{ - - static struct snd_seq_dev_ops ops = { - snd_emu10k1_synth_new_device, - snd_emu10k1_synth_delete_device, - }; - return snd_seq_device_register_driver(SNDRV_SEQ_DEV_ID_EMU10K1_SYNTH, &ops, - sizeof(struct snd_emu10k1_synth_arg)); -} - -static void __exit alsa_emu10k1_synth_exit(void) -{ - snd_seq_device_unregister_driver(SNDRV_SEQ_DEV_ID_EMU10K1_SYNTH); -} - -module_init(alsa_emu10k1_synth_init) -module_exit(alsa_emu10k1_synth_exit) +static struct snd_seq_driver emu10k1_synth_driver = { + .driver = { + .name = KBUILD_MODNAME, + .probe = snd_emu10k1_synth_probe, + .remove = snd_emu10k1_synth_remove, + }, + .id = SNDRV_SEQ_DEV_ID_EMU10K1_SYNTH, + .argsize = sizeof(struct snd_emu10k1_synth_arg), +}; + +module_snd_seq_driver(emu10k1_synth_driver); -- cgit v1.2.3 From 54a721abd7953a58e5479065c0cfdd8679d785c9 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 12 Feb 2015 14:20:24 +0100 Subject: ALSA: seq: Drop snd_seq_autoload_lock() and _unlock() The autoload lock became already superfluous due to the recent rework of autoload code. Let's drop them now. This allows us to simplify a few codes nicely. Signed-off-by: Takashi Iwai --- include/sound/seq_kernel.h | 6 +----- sound/core/seq/oss/seq_oss.c | 2 -- sound/core/seq/seq_device.c | 19 +++++++------------ sound/core/seq/seq_dummy.c | 6 +----- sound/core/seq/seq_midi.c | 18 +----------------- 5 files changed, 10 insertions(+), 41 deletions(-) (limited to 'include') diff --git a/include/sound/seq_kernel.h b/include/sound/seq_kernel.h index 18a2ac58b88f..feb58d455560 100644 --- a/include/sound/seq_kernel.h +++ b/include/sound/seq_kernel.h @@ -99,13 +99,9 @@ int snd_seq_event_port_attach(int client, struct snd_seq_port_callback *pcbp, int snd_seq_event_port_detach(int client, int port); #ifdef CONFIG_MODULES -void snd_seq_autoload_lock(void); -void snd_seq_autoload_unlock(void); void snd_seq_autoload_init(void); -#define snd_seq_autoload_exit() snd_seq_autoload_lock() +void snd_seq_autoload_exit(void); #else -#define snd_seq_autoload_lock() -#define snd_seq_autoload_unlock() #define snd_seq_autoload_init() #define snd_seq_autoload_exit() #endif diff --git a/sound/core/seq/oss/seq_oss.c b/sound/core/seq/oss/seq_oss.c index ae1814aa767e..72873a46afeb 100644 --- a/sound/core/seq/oss/seq_oss.c +++ b/sound/core/seq/oss/seq_oss.c @@ -79,7 +79,6 @@ static int __init alsa_seq_oss_init(void) { int rc; - snd_seq_autoload_lock(); if ((rc = register_device()) < 0) goto error; if ((rc = register_proc()) < 0) { @@ -104,7 +103,6 @@ static int __init alsa_seq_oss_init(void) snd_seq_oss_synth_init(); error: - snd_seq_autoload_unlock(); return rc; } diff --git a/sound/core/seq/seq_device.c b/sound/core/seq/seq_device.c index 48b20f009598..355b34269bd1 100644 --- a/sound/core/seq/seq_device.c +++ b/sound/core/seq/seq_device.c @@ -98,19 +98,8 @@ static void snd_seq_device_info(struct snd_info_entry *entry, */ #ifdef CONFIG_MODULES -/* avoid auto-loading during module_init() */ +/* flag to block auto-loading */ static atomic_t snd_seq_in_init = ATOMIC_INIT(1); /* blocked as default */ -void snd_seq_autoload_lock(void) -{ - atomic_inc(&snd_seq_in_init); -} -EXPORT_SYMBOL(snd_seq_autoload_lock); - -void snd_seq_autoload_unlock(void) -{ - atomic_dec(&snd_seq_in_init); -} -EXPORT_SYMBOL(snd_seq_autoload_unlock); static int request_seq_drv(struct device *dev, void *data) { @@ -147,6 +136,12 @@ void snd_seq_autoload_init(void) } EXPORT_SYMBOL(snd_seq_autoload_init); +void snd_seq_autoload_exit(void) +{ + atomic_inc(&snd_seq_in_init); +} +EXPORT_SYMBOL(snd_seq_autoload_exit); + void snd_seq_device_load_drivers(void) { queue_autoload_drivers(); diff --git a/sound/core/seq/seq_dummy.c b/sound/core/seq/seq_dummy.c index 5d905d90d504..d3a2ec4f0561 100644 --- a/sound/core/seq/seq_dummy.c +++ b/sound/core/seq/seq_dummy.c @@ -214,11 +214,7 @@ delete_client(void) static int __init alsa_seq_dummy_init(void) { - int err; - snd_seq_autoload_lock(); - err = register_client(); - snd_seq_autoload_unlock(); - return err; + return register_client(); } static void __exit alsa_seq_dummy_exit(void) diff --git a/sound/core/seq/seq_midi.c b/sound/core/seq/seq_midi.c index 79c73119cedc..5dd0ee258359 100644 --- a/sound/core/seq/seq_midi.c +++ b/sound/core/seq/seq_midi.c @@ -469,20 +469,4 @@ static struct snd_seq_driver seq_midisynth_driver = { .argsize = 0, }; -static int __init alsa_seq_midi_init(void) -{ - int err; - - snd_seq_autoload_lock(); - err = snd_seq_driver_register(&seq_midisynth_driver); - snd_seq_autoload_unlock(); - return err; -} - -static void __exit alsa_seq_midi_exit(void) -{ - snd_seq_driver_unregister(&seq_midisynth_driver); -} - -module_init(alsa_seq_midi_init) -module_exit(alsa_seq_midi_exit) +module_snd_seq_driver(seq_midisynth_driver); -- cgit v1.2.3 From bf2b8a515530ee7e3b25214d7ec52c6827f12d70 Mon Sep 17 00:00:00 2001 From: Damien Lespiau Date: Thu, 29 Jan 2015 14:13:38 +0000 Subject: drm/i915/skl: Split the SKL PCI ids by GT We need to have a separate GT3 struct intel_device_info to declare they have a second VCS. Let's start by splitting the PCI ids per-GT. Signed-off-by: Damien Lespiau Reviewed-by: Rodrigo Vivi Signed-off-by: Daniel Vetter --- include/drm/i915_pciids.h | 28 +++++++++++++++++++--------- 1 file changed, 19 insertions(+), 9 deletions(-) (limited to 'include') diff --git a/include/drm/i915_pciids.h b/include/drm/i915_pciids.h index 180ad0e6de21..38a7c8049e47 100644 --- a/include/drm/i915_pciids.h +++ b/include/drm/i915_pciids.h @@ -259,21 +259,31 @@ INTEL_VGA_DEVICE(0x22b2, info), \ INTEL_VGA_DEVICE(0x22b3, info) -#define INTEL_SKL_IDS(info) \ - INTEL_VGA_DEVICE(0x1916, info), /* ULT GT2 */ \ +#define INTEL_SKL_GT1_IDS(info) \ INTEL_VGA_DEVICE(0x1906, info), /* ULT GT1 */ \ - INTEL_VGA_DEVICE(0x1926, info), /* ULT GT3 */ \ - INTEL_VGA_DEVICE(0x1921, info), /* ULT GT2F */ \ INTEL_VGA_DEVICE(0x190E, info), /* ULX GT1 */ \ + INTEL_VGA_DEVICE(0x1902, info), /* DT GT1 */ \ + INTEL_VGA_DEVICE(0x190B, info), /* Halo GT1 */ \ + INTEL_VGA_DEVICE(0x190A, info) /* SRV GT1 */ + +#define INTEL_SKL_GT2_IDS(info) \ + INTEL_VGA_DEVICE(0x1916, info), /* ULT GT2 */ \ + INTEL_VGA_DEVICE(0x1921, info), /* ULT GT2F */ \ INTEL_VGA_DEVICE(0x191E, info), /* ULX GT2 */ \ INTEL_VGA_DEVICE(0x1912, info), /* DT GT2 */ \ - INTEL_VGA_DEVICE(0x1902, info), /* DT GT1 */ \ INTEL_VGA_DEVICE(0x191B, info), /* Halo GT2 */ \ - INTEL_VGA_DEVICE(0x192B, info), /* Halo GT3 */ \ - INTEL_VGA_DEVICE(0x190B, info), /* Halo GT1 */ \ INTEL_VGA_DEVICE(0x191A, info), /* SRV GT2 */ \ - INTEL_VGA_DEVICE(0x192A, info), /* SRV GT3 */ \ - INTEL_VGA_DEVICE(0x190A, info), /* SRV GT1 */ \ INTEL_VGA_DEVICE(0x191D, info) /* WKS GT2 */ +#define INTEL_SKL_GT3_IDS(info) \ + INTEL_VGA_DEVICE(0x1926, info), /* ULT GT3 */ \ + INTEL_VGA_DEVICE(0x192B, info), /* Halo GT3 */ \ + INTEL_VGA_DEVICE(0x192A, info) /* SRV GT3 */ \ + +#define INTEL_SKL_IDS(info) \ + INTEL_SKL_GT1_IDS(info), \ + INTEL_SKL_GT2_IDS(info), \ + INTEL_SKL_GT3_IDS(info) + + #endif /* _I915_PCIIDS_H */ -- cgit v1.2.3 From e3eb3250d84ef97b766312345774367b6a310db8 Mon Sep 17 00:00:00 2001 From: Rob Clark Date: Thu, 5 Feb 2015 14:41:52 +0000 Subject: drm: add support for tiled/compressed/etc modifier in addfb2 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In DRM/KMS we are lacking a good way to deal with tiled/compressed formats. Especially in the case of dmabuf/prime buffer sharing, where we cannot always rely on under-the-hood flags passed to driver specific gem-create ioctl to pass around these extra flags. The proposal is to add a per-plane format modifier. This allows to, if necessary, use different tiling patters for sub-sampled planes, etc. The format modifiers are added at the end of the ioctl struct, so for legacy userspace it will be zero padded. v1: original v1.5: increase modifier to 64b v2: Incorporate review comments from the big thread, plus a few more. - Add a getcap so that userspace doesn't have to jump through hoops. - Allow modifiers only when a flag is set. That way drivers know when they're dealing with old userspace and need to fish out e.g. tiling from other information. - After rolling out checks for ->modifier to all drivers I've decided that this is way too fragile and needs an explicit opt-in flag. So do that instead. - Add a define (just for documentation really) for the "NONE" modifier. Imo we don't need to add mask #defines since drivers really should only do exact matches against values defined with fourcc_mod_code. - Drop the Samsung tiling modifier on Rob's request since he's not yet sure whether that one is accurate. v3: - Also add a new ->modifier[] array to struct drm_framebuffer and fill it in drm_helper_mode_fill_fb_struct. Requested by Tvrkto Uruslin. - Remove TODO in comment and add code comment that modifiers should be properly documented, requested by Rob. Cc: Rob Clark Cc: Tvrtko Ursulin Cc: Laurent Pinchart Cc: Daniel Stone Cc: Ville Syrjälä Cc: Michel Dänzer Signed-off-by: Rob Clark (v1.5) Reviewed-by: Rob Clark Reviewed-by: Daniel Stone Acked-by: Dave Airlie Signed-off-by: Daniel Vetter --- drivers/gpu/drm/drm_crtc.c | 14 +++++++++++++- drivers/gpu/drm/drm_crtc_helper.c | 1 + drivers/gpu/drm/drm_ioctl.c | 3 +++ include/drm/drm_crtc.h | 4 ++++ include/uapi/drm/drm.h | 1 + include/uapi/drm/drm_fourcc.h | 32 ++++++++++++++++++++++++++++++++ include/uapi/drm/drm_mode.h | 9 +++++++++ 7 files changed, 63 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index ad2934ba0bd2..b15d720eda4c 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -3314,6 +3314,12 @@ static int framebuffer_check(const struct drm_mode_fb_cmd2 *r) DRM_DEBUG_KMS("bad pitch %u for plane %d\n", r->pitches[i], i); return -EINVAL; } + + if (r->modifier[i] && !(r->flags & DRM_MODE_FB_MODIFIERS)) { + DRM_DEBUG_KMS("bad fb modifier %llu for plane %d\n", + r->modifier[i], i); + return -EINVAL; + } } return 0; @@ -3327,7 +3333,7 @@ static struct drm_framebuffer *add_framebuffer_internal(struct drm_device *dev, struct drm_framebuffer *fb; int ret; - if (r->flags & ~DRM_MODE_FB_INTERLACED) { + if (r->flags & ~(DRM_MODE_FB_INTERLACED | DRM_MODE_FB_MODIFIERS)) { DRM_DEBUG_KMS("bad framebuffer flags 0x%08x\n", r->flags); return ERR_PTR(-EINVAL); } @@ -3343,6 +3349,12 @@ static struct drm_framebuffer *add_framebuffer_internal(struct drm_device *dev, return ERR_PTR(-EINVAL); } + if (r->flags & DRM_MODE_FB_MODIFIERS && + !dev->mode_config.allow_fb_modifiers) { + DRM_DEBUG_KMS("driver does not support fb modifiers\n"); + return ERR_PTR(-EINVAL); + } + ret = framebuffer_check(r); if (ret) return ERR_PTR(ret); diff --git a/drivers/gpu/drm/drm_crtc_helper.c b/drivers/gpu/drm/drm_crtc_helper.c index b1979e7bdc88..3053aab968f9 100644 --- a/drivers/gpu/drm/drm_crtc_helper.c +++ b/drivers/gpu/drm/drm_crtc_helper.c @@ -837,6 +837,7 @@ void drm_helper_mode_fill_fb_struct(struct drm_framebuffer *fb, for (i = 0; i < 4; i++) { fb->pitches[i] = mode_cmd->pitches[i]; fb->offsets[i] = mode_cmd->offsets[i]; + fb->modifier[i] = mode_cmd->modifier[i]; } drm_fb_get_bpp_depth(mode_cmd->pixel_format, &fb->depth, &fb->bits_per_pixel); diff --git a/drivers/gpu/drm/drm_ioctl.c b/drivers/gpu/drm/drm_ioctl.c index 3785d66721f2..a6d773a61c2d 100644 --- a/drivers/gpu/drm/drm_ioctl.c +++ b/drivers/gpu/drm/drm_ioctl.c @@ -321,6 +321,9 @@ static int drm_getcap(struct drm_device *dev, void *data, struct drm_file *file_ else req->value = 64; break; + case DRM_CAP_ADDFB2_MODIFIERS: + req->value = dev->mode_config.allow_fb_modifiers; + break; default: return -EINVAL; } diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index 0ebd9286b332..cee54c45eba9 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h @@ -202,6 +202,7 @@ struct drm_framebuffer { const struct drm_framebuffer_funcs *funcs; unsigned int pitches[4]; unsigned int offsets[4]; + uint64_t modifier[4]; unsigned int width; unsigned int height; /* depth can be 15 or 16 */ @@ -1152,6 +1153,9 @@ struct drm_mode_config { /* whether async page flip is supported or not */ bool async_page_flip; + /* whether the driver supports fb modifiers */ + bool allow_fb_modifiers; + /* cursor size */ uint32_t cursor_width, cursor_height; }; diff --git a/include/uapi/drm/drm.h b/include/uapi/drm/drm.h index 01b2d6d0e355..ff6ef62d084b 100644 --- a/include/uapi/drm/drm.h +++ b/include/uapi/drm/drm.h @@ -630,6 +630,7 @@ struct drm_gem_open { */ #define DRM_CAP_CURSOR_WIDTH 0x8 #define DRM_CAP_CURSOR_HEIGHT 0x9 +#define DRM_CAP_ADDFB2_MODIFIERS 0x10 /** DRM_IOCTL_GET_CAP ioctl argument type */ struct drm_get_cap { diff --git a/include/uapi/drm/drm_fourcc.h b/include/uapi/drm/drm_fourcc.h index 646ae5f39f42..622109677747 100644 --- a/include/uapi/drm/drm_fourcc.h +++ b/include/uapi/drm/drm_fourcc.h @@ -132,4 +132,36 @@ #define DRM_FORMAT_YUV444 fourcc_code('Y', 'U', '2', '4') /* non-subsampled Cb (1) and Cr (2) planes */ #define DRM_FORMAT_YVU444 fourcc_code('Y', 'V', '2', '4') /* non-subsampled Cr (1) and Cb (2) planes */ + +/* + * Format Modifiers: + * + * Format modifiers describe, typically, a re-ordering or modification + * of the data in a plane of an FB. This can be used to express tiled/ + * swizzled formats, or compression, or a combination of the two. + * + * The upper 8 bits of the format modifier are a vendor-id as assigned + * below. The lower 56 bits are assigned as vendor sees fit. + */ + +/* Vendor Ids: */ +#define DRM_FORMAT_MOD_NONE 0 +#define DRM_FORMAT_MOD_VENDOR_INTEL 0x01 +#define DRM_FORMAT_MOD_VENDOR_AMD 0x02 +#define DRM_FORMAT_MOD_VENDOR_NV 0x03 +#define DRM_FORMAT_MOD_VENDOR_SAMSUNG 0x04 +#define DRM_FORMAT_MOD_VENDOR_QCOM 0x05 +/* add more to the end as needed */ + +#define fourcc_mod_code(vendor, val) \ + ((((u64)DRM_FORMAT_MOD_VENDOR_## vendor) << 56) | (val & 0x00ffffffffffffffL)) + +/* + * Format Modifier tokens: + * + * When adding a new token please document the layout with a code comment, + * similar to the fourcc codes above. drm_fourcc.h is considered the + * authoritative source for all of these. + */ + #endif /* DRM_FOURCC_H */ diff --git a/include/uapi/drm/drm_mode.h b/include/uapi/drm/drm_mode.h index ca788e01dab2..dbeba949462a 100644 --- a/include/uapi/drm/drm_mode.h +++ b/include/uapi/drm/drm_mode.h @@ -336,6 +336,7 @@ struct drm_mode_fb_cmd { }; #define DRM_MODE_FB_INTERLACED (1<<0) /* for interlaced framebuffers */ +#define DRM_MODE_FB_MODIFIERS (1<<1) /* enables ->modifer[] */ struct drm_mode_fb_cmd2 { __u32 fb_id; @@ -356,10 +357,18 @@ struct drm_mode_fb_cmd2 { * So it would consist of Y as offsets[0] and UV as * offsets[1]. Note that offsets[0] will generally * be 0 (but this is not required). + * + * To accommodate tiled, compressed, etc formats, a per-plane + * modifier can be specified. The default value of zero + * indicates "native" format as specified by the fourcc. + * Vendor specific modifier token. This allows, for example, + * different tiling/swizzling pattern on different planes. + * See discussion above of DRM_FORMAT_MOD_xxx. */ __u32 handles[4]; __u32 pitches[4]; /* pitch for each plane */ __u32 offsets[4]; /* offset of each plane */ + __u64 modifier[4]; /* ie, tiling, compressed (per plane) */ }; #define DRM_MODE_FB_DIRTY_ANNOTATE_COPY 0x01 -- cgit v1.2.3 From 93b81f5102a7cd270a305c2741b17c8d44bb0629 Mon Sep 17 00:00:00 2001 From: Tvrtko Ursulin Date: Tue, 10 Feb 2015 17:16:05 +0000 Subject: drm/i915: Add tiled framebuffer modifiers To be used from the new addfb2 extension. v2: - Drop Intel-specific untiled modfier. - Move to drm_fourcc.h. - Document layouts a bit and denote them as platform-specific and not useable for cross-driver sharing. - Add Y-tiling for completeness. - Drop special docstring markers to avoid confusing kerneldoc. v3: Give Y-tiling a unique idea, noticed by Tvrtko. Signed-off-by: Tvrtko Ursulin (v1) Cc: Tvrtko Ursulin Reviewed-by: Tvrtko Ursulin Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_drv.h | 1 + include/uapi/drm/drm_fourcc.h | 31 +++++++++++++++++++++++++++++++ 2 files changed, 32 insertions(+) (limited to 'include') diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 3da3dc527315..99b25928df2f 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -31,6 +31,7 @@ #define _I915_DRV_H_ #include +#include #include "i915_reg.h" #include "intel_bios.h" diff --git a/include/uapi/drm/drm_fourcc.h b/include/uapi/drm/drm_fourcc.h index 622109677747..4837c3d2319a 100644 --- a/include/uapi/drm/drm_fourcc.h +++ b/include/uapi/drm/drm_fourcc.h @@ -164,4 +164,35 @@ * authoritative source for all of these. */ +/* Intel framebuffer modifiers */ + +/* + * Intel X-tiling layout + * + * This is a tiled layout using 4Kb tiles (except on gen2 where the tiles 2Kb) + * in row-major layout. Within the tile bytes are laid out row-major, with + * a platform-dependent stride. On top of that the memory can apply + * platform-depending swizzling of some higher address bits into bit6. + * + * This format is highly platforms specific and not useful for cross-driver + * sharing. It exists since on a given platform it does uniquely identify the + * layout in a simple way for i915-specific userspace. + */ +#define I915_FORMAT_MOD_X_TILED fourcc_mod_code(INTEL, 1) + +/* + * Intel Y-tiling layout + * + * This is a tiled layout using 4Kb tiles (except on gen2 where the tiles 2Kb) + * in row-major layout. Within the tile bytes are laid out in OWORD (16 bytes) + * chunks column-major, with a platform-dependent height. On top of that the + * memory can apply platform-depending swizzling of some higher address bits + * into bit6. + * + * This format is highly platforms specific and not useful for cross-driver + * sharing. It exists since on a given platform it does uniquely identify the + * layout in a simple way for i915-specific userspace. + */ +#define I915_FORMAT_MOD_Y_TILED fourcc_mod_code(INTEL, 2) + #endif /* DRM_FOURCC_H */ -- cgit v1.2.3 From e31a0ba7df6ce21ac4ed58c4182ec12ca8fd78fb Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Fri, 2 Jan 2015 12:18:23 -0300 Subject: [media] media: Fix DVB devnode representation at media controller The previous provision for DVB media controller support were to define an ID (likely meaning the adapter number) for the DVB devnodes. This is just plain wrong. Just like V4L, DVB devices (and any other device node)) are uniquely identified via a (major, minor) tuple. This is enough to uniquely identify a devnode, no matter what API it implements. So, before we go too far, let's mark the old v4l, fb, dvb and alsa "devnode" info as deprecated, and just call it as "dev". We can latter add fields specific to each API if needed. As we don't want to break compilation on already existing apps, let's just keep the old definitions as-is, adding a note that those are deprecated at media-entity.h. Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-dev.c | 4 ++-- drivers/media/v4l2-core/v4l2-device.c | 4 ++-- include/media/media-entity.h | 12 +----------- include/uapi/linux/media.h | 15 +++++++++++++++ 4 files changed, 20 insertions(+), 15 deletions(-) (limited to 'include') diff --git a/drivers/media/v4l2-core/v4l2-dev.c b/drivers/media/v4l2-core/v4l2-dev.c index 86bb93fd7db8..d89d5cb465d9 100644 --- a/drivers/media/v4l2-core/v4l2-dev.c +++ b/drivers/media/v4l2-core/v4l2-dev.c @@ -943,8 +943,8 @@ int __video_register_device(struct video_device *vdev, int type, int nr, vdev->vfl_type != VFL_TYPE_SUBDEV) { vdev->entity.type = MEDIA_ENT_T_DEVNODE_V4L; vdev->entity.name = vdev->name; - vdev->entity.info.v4l.major = VIDEO_MAJOR; - vdev->entity.info.v4l.minor = vdev->minor; + vdev->entity.info.dev.major = VIDEO_MAJOR; + vdev->entity.info.dev.minor = vdev->minor; ret = media_device_register_entity(vdev->v4l2_dev->mdev, &vdev->entity); if (ret < 0) diff --git a/drivers/media/v4l2-core/v4l2-device.c b/drivers/media/v4l2-core/v4l2-device.c index 015f92aab44a..204cc67c84e8 100644 --- a/drivers/media/v4l2-core/v4l2-device.c +++ b/drivers/media/v4l2-core/v4l2-device.c @@ -248,8 +248,8 @@ int v4l2_device_register_subdev_nodes(struct v4l2_device *v4l2_dev) goto clean_up; } #if defined(CONFIG_MEDIA_CONTROLLER) - sd->entity.info.v4l.major = VIDEO_MAJOR; - sd->entity.info.v4l.minor = vdev->minor; + sd->entity.info.dev.major = VIDEO_MAJOR; + sd->entity.info.dev.minor = vdev->minor; #endif sd->devnode = vdev; } diff --git a/include/media/media-entity.h b/include/media/media-entity.h index e00459185d20..d6d74bcfe183 100644 --- a/include/media/media-entity.h +++ b/include/media/media-entity.h @@ -87,17 +87,7 @@ struct media_entity { struct { u32 major; u32 minor; - } v4l; - struct { - u32 major; - u32 minor; - } fb; - struct { - u32 card; - u32 device; - u32 subdevice; - } alsa; - int dvb; + } dev; /* Sub-device specifications */ /* Nothing needed yet */ diff --git a/include/uapi/linux/media.h b/include/uapi/linux/media.h index d847c760e8f0..418f4fec391a 100644 --- a/include/uapi/linux/media.h +++ b/include/uapi/linux/media.h @@ -75,6 +75,20 @@ struct media_entity_desc { union { /* Node specifications */ + struct { + __u32 major; + __u32 minor; + } dev; + +#if 1 + /* + * DEPRECATED: previous node specifications. Kept just to + * avoid breaking compilation, but media_entity_desc.dev + * should be used instead. In particular, alsa and dvb + * fields below are wrong: for all devnodes, there should + * be just major/minor inside the struct, as this is enough + * to represent any devnode, no matter what type. + */ struct { __u32 major; __u32 minor; @@ -89,6 +103,7 @@ struct media_entity_desc { __u32 subdevice; } alsa; int dvb; +#endif /* Sub-device specifications */ /* Nothing needed yet */ -- cgit v1.2.3 From 1d20f9f6330c988505e51f5010656978fd70cd0c Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sat, 3 Jan 2015 11:09:39 -0300 Subject: [media] media: add new types for DVB devnodes Most of the DVB subdevs have already their own devnode. Add support for them at the media controller API. Signed-off-by: Mauro Carvalho Chehab --- include/uapi/linux/media.h | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/uapi/linux/media.h b/include/uapi/linux/media.h index 418f4fec391a..4c8f26243252 100644 --- a/include/uapi/linux/media.h +++ b/include/uapi/linux/media.h @@ -50,7 +50,14 @@ struct media_device_info { #define MEDIA_ENT_T_DEVNODE_V4L (MEDIA_ENT_T_DEVNODE + 1) #define MEDIA_ENT_T_DEVNODE_FB (MEDIA_ENT_T_DEVNODE + 2) #define MEDIA_ENT_T_DEVNODE_ALSA (MEDIA_ENT_T_DEVNODE + 3) -#define MEDIA_ENT_T_DEVNODE_DVB (MEDIA_ENT_T_DEVNODE + 4) +#define MEDIA_ENT_T_DEVNODE_DVB_FE (MEDIA_ENT_T_DEVNODE + 4) +#define MEDIA_ENT_T_DEVNODE_DVB_DEMUX (MEDIA_ENT_T_DEVNODE + 5) +#define MEDIA_ENT_T_DEVNODE_DVB_DVR (MEDIA_ENT_T_DEVNODE + 6) +#define MEDIA_ENT_T_DEVNODE_DVB_CA (MEDIA_ENT_T_DEVNODE + 7) +#define MEDIA_ENT_T_DEVNODE_DVB_NET (MEDIA_ENT_T_DEVNODE + 8) + +/* Legacy symbol. Use it to avoid userspace compilation breakages */ +#define MEDIA_ENT_T_DEVNODE_DVB MEDIA_ENT_T_DEVNODE_DVB_FE #define MEDIA_ENT_T_V4L2_SUBDEV (2 << MEDIA_ENT_TYPE_SHIFT) #define MEDIA_ENT_T_V4L2_SUBDEV_SENSOR (MEDIA_ENT_T_V4L2_SUBDEV + 1) -- cgit v1.2.3 From 91b0f3a06e432d01c66cd05973628a2ec3bde9ed Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Mon, 26 Jan 2015 08:53:18 -0300 Subject: [media] media: add a subdev type for tuner Add MEDIA_ENT_T_V4L2_SUBDEV_TUNER to represent the V4L2 (and dvb) tuner subdevices. Signed-off-by: Mauro Carvalho Chehab --- include/uapi/linux/media.h | 2 ++ 1 file changed, 2 insertions(+) (limited to 'include') diff --git a/include/uapi/linux/media.h b/include/uapi/linux/media.h index 4c8f26243252..52cc2a6b19b7 100644 --- a/include/uapi/linux/media.h +++ b/include/uapi/linux/media.h @@ -66,6 +66,8 @@ struct media_device_info { /* A converter of analogue video to its digital representation. */ #define MEDIA_ENT_T_V4L2_SUBDEV_DECODER (MEDIA_ENT_T_V4L2_SUBDEV + 4) +#define MEDIA_ENT_T_V4L2_SUBDEV_TUNER (MEDIA_ENT_T_V4L2_SUBDEV + 5) + #define MEDIA_ENT_FL_DEFAULT (1 << 0) struct media_entity_desc { -- cgit v1.2.3 From 306f7aa1807be7588f115d7cafa475f65e72e3d1 Mon Sep 17 00:00:00 2001 From: Alexander Aring Date: Wed, 11 Feb 2015 14:39:15 +0100 Subject: ieee802154: correct ieee802154_is_valid_psdu_len This patch corrects the ieee802154_is_valid_psdu_len function that this function also checks on reserved values 6-8 for validation the psdu length. Signed-off-by: Alexander Aring Signed-off-by: Marcel Holtmann --- include/linux/ieee802154.h | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/linux/ieee802154.h b/include/linux/ieee802154.h index 6e82d888287c..40b0ab953937 100644 --- a/include/linux/ieee802154.h +++ b/include/linux/ieee802154.h @@ -28,7 +28,8 @@ #include #define IEEE802154_MTU 127 -#define IEEE802154_MIN_PSDU_LEN 5 +#define IEEE802154_ACK_PSDU_LEN 5 +#define IEEE802154_MIN_PSDU_LEN 9 #define IEEE802154_PAN_ID_BROADCAST 0xffff #define IEEE802154_ADDR_SHORT_BROADCAST 0xffff @@ -204,11 +205,18 @@ enum { /** * ieee802154_is_valid_psdu_len - check if psdu len is valid + * available lengths: + * 0-4 Reserved + * 5 MPDU (Acknowledgment) + * 6-8 Reserved + * 9-127 MPDU + * * @len: psdu len with (MHR + payload + MFR) */ static inline bool ieee802154_is_valid_psdu_len(const u8 len) { - return (len >= IEEE802154_MIN_PSDU_LEN && len <= IEEE802154_MTU); + return (len == IEEE802154_ACK_PSDU_LEN || + (len >= IEEE802154_MIN_PSDU_LEN && len <= IEEE802154_MTU)); } /** -- cgit v1.2.3 From ba5bf17e8343c0b5b87a1240aa75c35c76b88e5e Mon Sep 17 00:00:00 2001 From: Alexander Aring Date: Wed, 11 Feb 2015 14:39:16 +0100 Subject: ieee802154: cleanup ieee802154_be64_to_le64 This patch cleanups the ieee802154_be64_to_le64 function. This patch removes an unnecessary temporary variable. Signed-off-by: Alexander Aring Signed-off-by: Marcel Holtmann --- include/net/mac802154.h | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/include/net/mac802154.h b/include/net/mac802154.h index 850647811749..c5c64455bcfa 100644 --- a/include/net/mac802154.h +++ b/include/net/mac802154.h @@ -19,6 +19,7 @@ #include #include #include +#include #include @@ -233,9 +234,7 @@ struct ieee802154_ops { */ static inline void ieee802154_be64_to_le64(void *le64_dst, const void *be64_src) { - __le64 tmp = (__force __le64)swab64p(be64_src); - - memcpy(le64_dst, &tmp, IEEE802154_EXTENDED_ADDR_LEN); + __put_unaligned_memmove64(swab64p(be64_src), le64_dst); } /** -- cgit v1.2.3 From b976796950c7a41fe1b6b51236ddd08dd6480b80 Mon Sep 17 00:00:00 2001 From: Alexander Aring Date: Wed, 11 Feb 2015 14:39:17 +0100 Subject: ieee802154: cleanup ieee802154_le64_to_be64 This patch cleanups the ieee802154_le64_to_be64 function. This patch removes an unnecessary temporary variable. Signed-off-by: Alexander Aring Signed-off-by: Marcel Holtmann --- include/net/mac802154.h | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'include') diff --git a/include/net/mac802154.h b/include/net/mac802154.h index c5c64455bcfa..fb4e8a3d6229 100644 --- a/include/net/mac802154.h +++ b/include/net/mac802154.h @@ -244,9 +244,7 @@ static inline void ieee802154_be64_to_le64(void *le64_dst, const void *be64_src) */ static inline void ieee802154_le64_to_be64(void *be64_dst, const void *le64_src) { - __be64 tmp = (__force __be64)swab64p(le64_src); - - memcpy(be64_dst, &tmp, IEEE802154_EXTENDED_ADDR_LEN); + __put_unaligned_memmove64(swab64p(le64_src), be64_dst); } /* Basic interface to register ieee802154 hwice */ -- cgit v1.2.3 From a44fecbd52a4d9c36f07eb2161c153047d8765d4 Mon Sep 17 00:00:00 2001 From: Tedd Ho-Jeong An Date: Fri, 13 Feb 2015 09:20:50 -0800 Subject: Bluetooth: Add shutdown callback before closing the device This callback allows a vendor to send the vendor specific commands before cloing the hci interface. Signed-off-by: Tedd Ho-Jeong An Signed-off-by: Marcel Holtmann --- include/net/bluetooth/hci_core.h | 1 + net/bluetooth/hci_core.c | 6 ++++++ 2 files changed, 7 insertions(+) (limited to 'include') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 52863c3e0b13..5f1ca3359c1a 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -373,6 +373,7 @@ struct hci_dev { int (*close)(struct hci_dev *hdev); int (*flush)(struct hci_dev *hdev); int (*setup)(struct hci_dev *hdev); + int (*shutdown)(struct hci_dev *hdev); int (*send)(struct hci_dev *hdev, struct sk_buff *skb); void (*notify)(struct hci_dev *hdev, unsigned int evt); void (*hw_error)(struct hci_dev *hdev, u8 code); diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 3322d3f4c85a..4135a4406aed 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -1591,6 +1591,12 @@ static int hci_dev_do_close(struct hci_dev *hdev) { BT_DBG("%s %p", hdev->name, hdev); + if (!test_bit(HCI_UNREGISTER, &hdev->dev_flags)) { + /* Execute vendor specific shutdown routine */ + if (hdev->shutdown) + hdev->shutdown(hdev); + } + cancel_delayed_work(&hdev->power_off); hci_req_cancel(hdev, ENODEV); -- cgit v1.2.3 From 76a3aeac2f6c02ecf065fa9baa279dd54bf2d819 Mon Sep 17 00:00:00 2001 From: Mikko Rapeli Date: Tue, 17 Feb 2015 00:05:27 +0100 Subject: hdspm.h: include stdint.h in userspace MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes compilation error: sound/hdspm.h:43:2: error: unknown type name ‘uint32_t’ Signed-off-by: Mikko Rapeli Signed-off-by: Takashi Iwai --- include/uapi/sound/hdspm.h | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'include') diff --git a/include/uapi/sound/hdspm.h b/include/uapi/sound/hdspm.h index b357f1a5e29c..5737332d38f2 100644 --- a/include/uapi/sound/hdspm.h +++ b/include/uapi/sound/hdspm.h @@ -20,6 +20,12 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ +#ifdef __KERNEL__ +#include +#else +#include +#endif + /* Maximum channels is 64 even on 56Mode you have 64playbacks to matrix */ #define HDSPM_MAX_CHANNELS 64 -- cgit v1.2.3 From 4bebf7091aa15ec60edf0dcbc654410a87ca21fe Mon Sep 17 00:00:00 2001 From: Mikko Rapeli Date: Tue, 17 Feb 2015 00:05:38 +0100 Subject: include/uapi/sound/asound.h: include stdlib.h in userspace MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes compiler errors like: error: field ‘trigger_tstamp’ has incomplete type error: invalid application of ‘sizeof’ to incomplete t ype ‘struct timespec’ Signed-off-by: Mikko Rapeli Signed-off-by: Takashi Iwai --- include/uapi/sound/asound.h | 3 +++ 1 file changed, 3 insertions(+) (limited to 'include') diff --git a/include/uapi/sound/asound.h b/include/uapi/sound/asound.h index 1f23cd635957..1fe3f4f3d696 100644 --- a/include/uapi/sound/asound.h +++ b/include/uapi/sound/asound.h @@ -25,6 +25,9 @@ #include +#ifndef __KERNEL__ +#include +#endif /* * protocol version -- cgit v1.2.3 From bbf91c1c5bfc00c2961f15657359ee7e87de4269 Mon Sep 17 00:00:00 2001 From: Mikko Rapeli Date: Tue, 17 Feb 2015 00:05:42 +0100 Subject: include/uapi/sound/asequencer.h: include sound/asound.h MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes userspace compilation error: error: unknown type name ‘snd_seq_client_type_t’ snd_seq_client_type_t type; /* client type */ Signed-off-by: Mikko Rapeli Signed-off-by: Takashi Iwai --- include/uapi/sound/asequencer.h | 1 + 1 file changed, 1 insertion(+) (limited to 'include') diff --git a/include/uapi/sound/asequencer.h b/include/uapi/sound/asequencer.h index 09c8a00ea503..5a5fa4956ebd 100644 --- a/include/uapi/sound/asequencer.h +++ b/include/uapi/sound/asequencer.h @@ -22,6 +22,7 @@ #ifndef _UAPI__SOUND_ASEQUENCER_H #define _UAPI__SOUND_ASEQUENCER_H +#include /** version of the sequencer */ #define SNDRV_SEQ_VERSION SNDRV_PROTOCOL_VERSION (1, 0, 1) -- cgit v1.2.3 From b9956409c281931c74ba8d0a2b61a98076a58602 Mon Sep 17 00:00:00 2001 From: Mikko Rapeli Date: Tue, 17 Feb 2015 00:05:43 +0100 Subject: include/uapi/sound/emu10k1.h: include sound/asound.h MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes userspace compilation errors like: error: field ‘id’ has incomplete type struct snd_ctl_elem_id id; /* full control ID definition */ Signed-off-by: Mikko Rapeli Signed-off-by: Takashi Iwai --- include/uapi/sound/emu10k1.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'include') diff --git a/include/uapi/sound/emu10k1.h b/include/uapi/sound/emu10k1.h index d1bbaf78457a..ec1535bb6aed 100644 --- a/include/uapi/sound/emu10k1.h +++ b/include/uapi/sound/emu10k1.h @@ -23,8 +23,7 @@ #define _UAPI__SOUND_EMU10K1_H #include - - +#include /* * ---- FX8010 ---- -- cgit v1.2.3 From 27ac905b8f88d28779b0661809286b5ba2817d37 Mon Sep 17 00:00:00 2001 From: "Yan, Zheng" Date: Tue, 4 Nov 2014 21:55:57 -0500 Subject: perf/x86/intel: Reduce lbr_sel_map[] size The index of lbr_sel_map is bit value of perf branch_sample_type. PERF_SAMPLE_BRANCH_MAX is 1024 at present, so each lbr_sel_map uses 4096 bytes. By using bit shift as index, we can reduce lbr_sel_map size to 40 bytes. This patch defines 'bit shift' for branch types, and use 'bit shift' to define lbr_sel_maps. Signed-off-by: Yan, Zheng Signed-off-by: Kan Liang Signed-off-by: Peter Zijlstra (Intel) Reviewed-by: Stephane Eranian Cc: Andy Lutomirski Cc: Arnaldo Carvalho de Melo Cc: Linus Torvalds Cc: Paul Mackerras Cc: Vince Weaver Cc: jolsa@redhat.com Cc: linux-api@vger.kernel.org Link: http://lkml.kernel.org/r/1415156173-10035-2-git-send-email-kan.liang@intel.com Signed-off-by: Ingo Molnar --- arch/x86/kernel/cpu/perf_event.h | 4 +++ arch/x86/kernel/cpu/perf_event_intel_lbr.c | 54 ++++++++++++++---------------- include/uapi/linux/perf_event.h | 49 +++++++++++++++++++-------- 3 files changed, 64 insertions(+), 43 deletions(-) (limited to 'include') diff --git a/arch/x86/kernel/cpu/perf_event.h b/arch/x86/kernel/cpu/perf_event.h index df525d2be1e8..0c45b22495dc 100644 --- a/arch/x86/kernel/cpu/perf_event.h +++ b/arch/x86/kernel/cpu/perf_event.h @@ -515,6 +515,10 @@ struct x86_pmu { struct perf_guest_switch_msr *(*guest_get_msrs)(int *nr); }; +enum { + PERF_SAMPLE_BRANCH_SELECT_MAP_SIZE = PERF_SAMPLE_BRANCH_MAX_SHIFT, +}; + #define x86_add_quirk(func_) \ do { \ static struct x86_pmu_quirk __quirk __initdata = { \ diff --git a/arch/x86/kernel/cpu/perf_event_intel_lbr.c b/arch/x86/kernel/cpu/perf_event_intel_lbr.c index 58f1a94beaf0..8bc078f43a82 100644 --- a/arch/x86/kernel/cpu/perf_event_intel_lbr.c +++ b/arch/x86/kernel/cpu/perf_event_intel_lbr.c @@ -69,10 +69,6 @@ static enum { #define LBR_FROM_FLAG_IN_TX (1ULL << 62) #define LBR_FROM_FLAG_ABORT (1ULL << 61) -#define for_each_branch_sample_type(x) \ - for ((x) = PERF_SAMPLE_BRANCH_USER; \ - (x) < PERF_SAMPLE_BRANCH_MAX; (x) <<= 1) - /* * x86control flow change classification * x86control flow changes include branches, interrupts, traps, faults @@ -403,14 +399,14 @@ static int intel_pmu_setup_hw_lbr_filter(struct perf_event *event) { struct hw_perf_event_extra *reg; u64 br_type = event->attr.branch_sample_type; - u64 mask = 0, m; - u64 v; + u64 mask = 0, v; + int i; - for_each_branch_sample_type(m) { - if (!(br_type & m)) + for (i = 0; i < PERF_SAMPLE_BRANCH_SELECT_MAP_SIZE; i++) { + if (!(br_type & (1ULL << i))) continue; - v = x86_pmu.lbr_sel_map[m]; + v = x86_pmu.lbr_sel_map[i]; if (v == LBR_NOT_SUPP) return -EOPNOTSUPP; @@ -678,35 +674,35 @@ intel_pmu_lbr_filter(struct cpu_hw_events *cpuc) /* * Map interface branch filters onto LBR filters */ -static const int nhm_lbr_sel_map[PERF_SAMPLE_BRANCH_MAX] = { - [PERF_SAMPLE_BRANCH_ANY] = LBR_ANY, - [PERF_SAMPLE_BRANCH_USER] = LBR_USER, - [PERF_SAMPLE_BRANCH_KERNEL] = LBR_KERNEL, - [PERF_SAMPLE_BRANCH_HV] = LBR_IGN, - [PERF_SAMPLE_BRANCH_ANY_RETURN] = LBR_RETURN | LBR_REL_JMP - | LBR_IND_JMP | LBR_FAR, +static const int nhm_lbr_sel_map[PERF_SAMPLE_BRANCH_SELECT_MAP_SIZE] = { + [PERF_SAMPLE_BRANCH_ANY_SHIFT] = LBR_ANY, + [PERF_SAMPLE_BRANCH_USER_SHIFT] = LBR_USER, + [PERF_SAMPLE_BRANCH_KERNEL_SHIFT] = LBR_KERNEL, + [PERF_SAMPLE_BRANCH_HV_SHIFT] = LBR_IGN, + [PERF_SAMPLE_BRANCH_ANY_RETURN_SHIFT] = LBR_RETURN | LBR_REL_JMP + | LBR_IND_JMP | LBR_FAR, /* * NHM/WSM erratum: must include REL_JMP+IND_JMP to get CALL branches */ - [PERF_SAMPLE_BRANCH_ANY_CALL] = + [PERF_SAMPLE_BRANCH_ANY_CALL_SHIFT] = LBR_REL_CALL | LBR_IND_CALL | LBR_REL_JMP | LBR_IND_JMP | LBR_FAR, /* * NHM/WSM erratum: must include IND_JMP to capture IND_CALL */ - [PERF_SAMPLE_BRANCH_IND_CALL] = LBR_IND_CALL | LBR_IND_JMP, - [PERF_SAMPLE_BRANCH_COND] = LBR_JCC, + [PERF_SAMPLE_BRANCH_IND_CALL_SHIFT] = LBR_IND_CALL | LBR_IND_JMP, + [PERF_SAMPLE_BRANCH_COND_SHIFT] = LBR_JCC, }; -static const int snb_lbr_sel_map[PERF_SAMPLE_BRANCH_MAX] = { - [PERF_SAMPLE_BRANCH_ANY] = LBR_ANY, - [PERF_SAMPLE_BRANCH_USER] = LBR_USER, - [PERF_SAMPLE_BRANCH_KERNEL] = LBR_KERNEL, - [PERF_SAMPLE_BRANCH_HV] = LBR_IGN, - [PERF_SAMPLE_BRANCH_ANY_RETURN] = LBR_RETURN | LBR_FAR, - [PERF_SAMPLE_BRANCH_ANY_CALL] = LBR_REL_CALL | LBR_IND_CALL - | LBR_FAR, - [PERF_SAMPLE_BRANCH_IND_CALL] = LBR_IND_CALL, - [PERF_SAMPLE_BRANCH_COND] = LBR_JCC, +static const int snb_lbr_sel_map[PERF_SAMPLE_BRANCH_SELECT_MAP_SIZE] = { + [PERF_SAMPLE_BRANCH_ANY_SHIFT] = LBR_ANY, + [PERF_SAMPLE_BRANCH_USER_SHIFT] = LBR_USER, + [PERF_SAMPLE_BRANCH_KERNEL_SHIFT] = LBR_KERNEL, + [PERF_SAMPLE_BRANCH_HV_SHIFT] = LBR_IGN, + [PERF_SAMPLE_BRANCH_ANY_RETURN_SHIFT] = LBR_RETURN | LBR_FAR, + [PERF_SAMPLE_BRANCH_ANY_CALL_SHIFT] = LBR_REL_CALL | LBR_IND_CALL + | LBR_FAR, + [PERF_SAMPLE_BRANCH_IND_CALL_SHIFT] = LBR_IND_CALL, + [PERF_SAMPLE_BRANCH_COND_SHIFT] = LBR_JCC, }; /* core */ diff --git a/include/uapi/linux/perf_event.h b/include/uapi/linux/perf_event.h index 9b79abbd1ab8..e46b93279e3d 100644 --- a/include/uapi/linux/perf_event.h +++ b/include/uapi/linux/perf_event.h @@ -152,21 +152,42 @@ enum perf_event_sample_format { * The branch types can be combined, however BRANCH_ANY covers all types * of branches and therefore it supersedes all the other types. */ +enum perf_branch_sample_type_shift { + PERF_SAMPLE_BRANCH_USER_SHIFT = 0, /* user branches */ + PERF_SAMPLE_BRANCH_KERNEL_SHIFT = 1, /* kernel branches */ + PERF_SAMPLE_BRANCH_HV_SHIFT = 2, /* hypervisor branches */ + + PERF_SAMPLE_BRANCH_ANY_SHIFT = 3, /* any branch types */ + PERF_SAMPLE_BRANCH_ANY_CALL_SHIFT = 4, /* any call branch */ + PERF_SAMPLE_BRANCH_ANY_RETURN_SHIFT = 5, /* any return branch */ + PERF_SAMPLE_BRANCH_IND_CALL_SHIFT = 6, /* indirect calls */ + PERF_SAMPLE_BRANCH_ABORT_TX_SHIFT = 7, /* transaction aborts */ + PERF_SAMPLE_BRANCH_IN_TX_SHIFT = 8, /* in transaction */ + PERF_SAMPLE_BRANCH_NO_TX_SHIFT = 9, /* not in transaction */ + PERF_SAMPLE_BRANCH_COND_SHIFT = 10, /* conditional branches */ + + PERF_SAMPLE_BRANCH_MAX_SHIFT /* non-ABI */ +}; + enum perf_branch_sample_type { - PERF_SAMPLE_BRANCH_USER = 1U << 0, /* user branches */ - PERF_SAMPLE_BRANCH_KERNEL = 1U << 1, /* kernel branches */ - PERF_SAMPLE_BRANCH_HV = 1U << 2, /* hypervisor branches */ - - PERF_SAMPLE_BRANCH_ANY = 1U << 3, /* any branch types */ - PERF_SAMPLE_BRANCH_ANY_CALL = 1U << 4, /* any call branch */ - PERF_SAMPLE_BRANCH_ANY_RETURN = 1U << 5, /* any return branch */ - PERF_SAMPLE_BRANCH_IND_CALL = 1U << 6, /* indirect calls */ - PERF_SAMPLE_BRANCH_ABORT_TX = 1U << 7, /* transaction aborts */ - PERF_SAMPLE_BRANCH_IN_TX = 1U << 8, /* in transaction */ - PERF_SAMPLE_BRANCH_NO_TX = 1U << 9, /* not in transaction */ - PERF_SAMPLE_BRANCH_COND = 1U << 10, /* conditional branches */ - - PERF_SAMPLE_BRANCH_MAX = 1U << 11, /* non-ABI */ + PERF_SAMPLE_BRANCH_USER = 1U << PERF_SAMPLE_BRANCH_USER_SHIFT, + PERF_SAMPLE_BRANCH_KERNEL = 1U << PERF_SAMPLE_BRANCH_KERNEL_SHIFT, + PERF_SAMPLE_BRANCH_HV = 1U << PERF_SAMPLE_BRANCH_HV_SHIFT, + + PERF_SAMPLE_BRANCH_ANY = 1U << PERF_SAMPLE_BRANCH_ANY_SHIFT, + PERF_SAMPLE_BRANCH_ANY_CALL = + 1U << PERF_SAMPLE_BRANCH_ANY_CALL_SHIFT, + PERF_SAMPLE_BRANCH_ANY_RETURN = + 1U << PERF_SAMPLE_BRANCH_ANY_RETURN_SHIFT, + PERF_SAMPLE_BRANCH_IND_CALL = + 1U << PERF_SAMPLE_BRANCH_IND_CALL_SHIFT, + PERF_SAMPLE_BRANCH_ABORT_TX = + 1U << PERF_SAMPLE_BRANCH_ABORT_TX_SHIFT, + PERF_SAMPLE_BRANCH_IN_TX = 1U << PERF_SAMPLE_BRANCH_IN_TX_SHIFT, + PERF_SAMPLE_BRANCH_NO_TX = 1U << PERF_SAMPLE_BRANCH_NO_TX_SHIFT, + PERF_SAMPLE_BRANCH_COND = 1U << PERF_SAMPLE_BRANCH_COND_SHIFT, + + PERF_SAMPLE_BRANCH_MAX = 1U << PERF_SAMPLE_BRANCH_MAX_SHIFT, }; #define PERF_SAMPLE_BRANCH_PLM_ALL \ -- cgit v1.2.3 From ba532500c5651a4be4108acc64ed99a95cb005b3 Mon Sep 17 00:00:00 2001 From: "Yan, Zheng" Date: Tue, 4 Nov 2014 21:55:58 -0500 Subject: perf: Introduce pmu context switch callback The callback is invoked when process is scheduled in or out. It provides mechanism for later patches to save/store the LBR stack. For the schedule in case, the callback is invoked at the same place that flush branch stack callback is invoked. So it also can replace the flush branch stack callback. To avoid unnecessary overhead, the callback is enabled only when there are events use the LBR stack. Signed-off-by: Yan, Zheng Signed-off-by: Kan Liang Signed-off-by: Peter Zijlstra (Intel) Cc: Andy Lutomirski Cc: Arnaldo Carvalho de Melo Cc: Linus Torvalds Cc: Paul Mackerras Cc: Vince Weaver Cc: eranian@google.com Cc: jolsa@redhat.com Link: http://lkml.kernel.org/r/1415156173-10035-3-git-send-email-kan.liang@intel.com Signed-off-by: Ingo Molnar --- arch/x86/kernel/cpu/perf_event.c | 7 +++++ arch/x86/kernel/cpu/perf_event.h | 2 ++ include/linux/perf_event.h | 9 +++++++ kernel/events/core.c | 57 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 75 insertions(+) (limited to 'include') diff --git a/arch/x86/kernel/cpu/perf_event.c b/arch/x86/kernel/cpu/perf_event.c index b71a7f86d68a..0efbd6cc2966 100644 --- a/arch/x86/kernel/cpu/perf_event.c +++ b/arch/x86/kernel/cpu/perf_event.c @@ -1914,6 +1914,12 @@ static const struct attribute_group *x86_pmu_attr_groups[] = { NULL, }; +static void x86_pmu_sched_task(struct perf_event_context *ctx, bool sched_in) +{ + if (x86_pmu.sched_task) + x86_pmu.sched_task(ctx, sched_in); +} + static void x86_pmu_flush_branch_stack(void) { if (x86_pmu.flush_branch_stack) @@ -1950,6 +1956,7 @@ static struct pmu pmu = { .event_idx = x86_pmu_event_idx, .flush_branch_stack = x86_pmu_flush_branch_stack, + .sched_task = x86_pmu_sched_task, }; void arch_perf_update_userpage(struct perf_event *event, diff --git a/arch/x86/kernel/cpu/perf_event.h b/arch/x86/kernel/cpu/perf_event.h index 0c45b22495dc..211b54c0c00c 100644 --- a/arch/x86/kernel/cpu/perf_event.h +++ b/arch/x86/kernel/cpu/perf_event.h @@ -473,6 +473,8 @@ struct x86_pmu { void (*check_microcode)(void); void (*flush_branch_stack)(void); + void (*sched_task)(struct perf_event_context *ctx, + bool sched_in); /* * Intel Arch Perfmon v2+ diff --git a/include/linux/perf_event.h b/include/linux/perf_event.h index 33262004c310..fbab6235d053 100644 --- a/include/linux/perf_event.h +++ b/include/linux/perf_event.h @@ -265,6 +265,13 @@ struct pmu { * flush branch stack on context-switches (needed in cpu-wide mode) */ void (*flush_branch_stack) (void); + + /* + * context-switches callback + */ + void (*sched_task) (struct perf_event_context *ctx, + bool sched_in); + }; /** @@ -558,6 +565,8 @@ extern void perf_event_delayed_put(struct task_struct *task); extern void perf_event_print_debug(void); extern void perf_pmu_disable(struct pmu *pmu); extern void perf_pmu_enable(struct pmu *pmu); +extern void perf_sched_cb_dec(struct pmu *pmu); +extern void perf_sched_cb_inc(struct pmu *pmu); extern int perf_event_task_disable(void); extern int perf_event_task_enable(void); extern int perf_event_refresh(struct perf_event *event, int refresh); diff --git a/kernel/events/core.c b/kernel/events/core.c index fef45b4bb5f8..6c8b31b7efb6 100644 --- a/kernel/events/core.c +++ b/kernel/events/core.c @@ -154,6 +154,7 @@ enum event_type_t { struct static_key_deferred perf_sched_events __read_mostly; static DEFINE_PER_CPU(atomic_t, perf_cgroup_events); static DEFINE_PER_CPU(atomic_t, perf_branch_stack_events); +static DEFINE_PER_CPU(int, perf_sched_cb_usages); static atomic_t nr_mmap_events __read_mostly; static atomic_t nr_comm_events __read_mostly; @@ -2577,6 +2578,56 @@ unlock: } } +void perf_sched_cb_dec(struct pmu *pmu) +{ + this_cpu_dec(perf_sched_cb_usages); +} + +void perf_sched_cb_inc(struct pmu *pmu) +{ + this_cpu_inc(perf_sched_cb_usages); +} + +/* + * This function provides the context switch callback to the lower code + * layer. It is invoked ONLY when the context switch callback is enabled. + */ +static void perf_pmu_sched_task(struct task_struct *prev, + struct task_struct *next, + bool sched_in) +{ + struct perf_cpu_context *cpuctx; + struct pmu *pmu; + unsigned long flags; + + if (prev == next) + return; + + local_irq_save(flags); + + rcu_read_lock(); + + list_for_each_entry_rcu(pmu, &pmus, entry) { + if (pmu->sched_task) { + cpuctx = this_cpu_ptr(pmu->pmu_cpu_context); + + perf_ctx_lock(cpuctx, cpuctx->task_ctx); + + perf_pmu_disable(pmu); + + pmu->sched_task(cpuctx->task_ctx, sched_in); + + perf_pmu_enable(pmu); + + perf_ctx_unlock(cpuctx, cpuctx->task_ctx); + } + } + + rcu_read_unlock(); + + local_irq_restore(flags); +} + #define for_each_task_context_nr(ctxn) \ for ((ctxn) = 0; (ctxn) < perf_nr_task_contexts; (ctxn)++) @@ -2596,6 +2647,9 @@ void __perf_event_task_sched_out(struct task_struct *task, { int ctxn; + if (__this_cpu_read(perf_sched_cb_usages)) + perf_pmu_sched_task(task, next, false); + for_each_task_context_nr(ctxn) perf_event_context_sched_out(task, ctxn, next); @@ -2847,6 +2901,9 @@ void __perf_event_task_sched_in(struct task_struct *prev, /* check for system-wide branch_stack events */ if (atomic_read(this_cpu_ptr(&perf_branch_stack_events))) perf_branch_stack_sched_in(prev, task); + + if (__this_cpu_read(perf_sched_cb_usages)) + perf_pmu_sched_task(prev, task, true); } static u64 perf_calculate_period(struct perf_event *event, u64 nsec, u64 count) -- cgit v1.2.3 From 2a0ad3b326a9024ba86dca4028499d31fa0c6c4d Mon Sep 17 00:00:00 2001 From: "Yan, Zheng" Date: Tue, 4 Nov 2014 21:55:59 -0500 Subject: perf/x86/intel: Use context switch callback to flush LBR stack Previous commit introduces context switch callback, its function overlaps with the flush branch stack callback. So we can use the context switch callback to flush LBR stack. This patch adds code that uses the flush branch callback to flush the LBR stack when task is being scheduled in. The callback is enabled only when there are events use the LBR hardware. This patch also removes all old flush branch stack code. Signed-off-by: Yan, Zheng Signed-off-by: Kan Liang Signed-off-by: Peter Zijlstra (Intel) Cc: Andy Lutomirski Cc: Arnaldo Carvalho de Melo Cc: Linus Torvalds Cc: Paul Mackerras Cc: Vince Weaver Cc: eranian@google.com Cc: jolsa@redhat.com Link: http://lkml.kernel.org/r/1415156173-10035-4-git-send-email-kan.liang@intel.com Signed-off-by: Ingo Molnar --- arch/x86/kernel/cpu/perf_event.c | 7 --- arch/x86/kernel/cpu/perf_event.h | 3 +- arch/x86/kernel/cpu/perf_event_intel.c | 14 +----- arch/x86/kernel/cpu/perf_event_intel_lbr.c | 27 +++++++++++ include/linux/perf_event.h | 1 - kernel/events/core.c | 77 ------------------------------ 6 files changed, 30 insertions(+), 99 deletions(-) (limited to 'include') diff --git a/arch/x86/kernel/cpu/perf_event.c b/arch/x86/kernel/cpu/perf_event.c index 0efbd6cc2966..6b1fd26a37cf 100644 --- a/arch/x86/kernel/cpu/perf_event.c +++ b/arch/x86/kernel/cpu/perf_event.c @@ -1920,12 +1920,6 @@ static void x86_pmu_sched_task(struct perf_event_context *ctx, bool sched_in) x86_pmu.sched_task(ctx, sched_in); } -static void x86_pmu_flush_branch_stack(void) -{ - if (x86_pmu.flush_branch_stack) - x86_pmu.flush_branch_stack(); -} - void perf_check_microcode(void) { if (x86_pmu.check_microcode) @@ -1955,7 +1949,6 @@ static struct pmu pmu = { .commit_txn = x86_pmu_commit_txn, .event_idx = x86_pmu_event_idx, - .flush_branch_stack = x86_pmu_flush_branch_stack, .sched_task = x86_pmu_sched_task, }; diff --git a/arch/x86/kernel/cpu/perf_event.h b/arch/x86/kernel/cpu/perf_event.h index 211b54c0c00c..949d0083a29e 100644 --- a/arch/x86/kernel/cpu/perf_event.h +++ b/arch/x86/kernel/cpu/perf_event.h @@ -472,7 +472,6 @@ struct x86_pmu { void (*cpu_dead)(int cpu); void (*check_microcode)(void); - void (*flush_branch_stack)(void); void (*sched_task)(struct perf_event_context *ctx, bool sched_in); @@ -733,6 +732,8 @@ void intel_pmu_pebs_disable_all(void); void intel_ds_init(void); +void intel_pmu_lbr_sched_task(struct perf_event_context *ctx, bool sched_in); + void intel_pmu_lbr_reset(void); void intel_pmu_lbr_enable(struct perf_event *event); diff --git a/arch/x86/kernel/cpu/perf_event_intel.c b/arch/x86/kernel/cpu/perf_event_intel.c index 498b6d967138..424fbf74dee7 100644 --- a/arch/x86/kernel/cpu/perf_event_intel.c +++ b/arch/x86/kernel/cpu/perf_event_intel.c @@ -2044,18 +2044,6 @@ static void intel_pmu_cpu_dying(int cpu) fini_debug_store_on_cpu(cpu); } -static void intel_pmu_flush_branch_stack(void) -{ - /* - * Intel LBR does not tag entries with the - * PID of the current task, then we need to - * flush it on ctxsw - * For now, we simply reset it - */ - if (x86_pmu.lbr_nr) - intel_pmu_lbr_reset(); -} - PMU_FORMAT_ATTR(offcore_rsp, "config1:0-63"); PMU_FORMAT_ATTR(ldlat, "config1:0-15"); @@ -2107,7 +2095,7 @@ static __initconst const struct x86_pmu intel_pmu = { .cpu_starting = intel_pmu_cpu_starting, .cpu_dying = intel_pmu_cpu_dying, .guest_get_msrs = intel_guest_get_msrs, - .flush_branch_stack = intel_pmu_flush_branch_stack, + .sched_task = intel_pmu_lbr_sched_task, }; static __init void intel_clovertown_quirk(void) diff --git a/arch/x86/kernel/cpu/perf_event_intel_lbr.c b/arch/x86/kernel/cpu/perf_event_intel_lbr.c index 8bc078f43a82..c0e23c5f85bb 100644 --- a/arch/x86/kernel/cpu/perf_event_intel_lbr.c +++ b/arch/x86/kernel/cpu/perf_event_intel_lbr.c @@ -177,6 +177,31 @@ void intel_pmu_lbr_reset(void) intel_pmu_lbr_reset_64(); } +void intel_pmu_lbr_sched_task(struct perf_event_context *ctx, bool sched_in) +{ + struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events); + + if (!x86_pmu.lbr_nr) + return; + + /* + * When sampling the branck stack in system-wide, it may be + * necessary to flush the stack on context switch. This happens + * when the branch stack does not tag its entries with the pid + * of the current task. Otherwise it becomes impossible to + * associate a branch entry with a task. This ambiguity is more + * likely to appear when the branch stack supports priv level + * filtering and the user sets it to monitor only at the user + * level (which could be a useful measurement in system-wide + * mode). In that case, the risk is high of having a branch + * stack with branch from multiple tasks. + */ + if (sched_in) { + intel_pmu_lbr_reset(); + cpuc->lbr_context = ctx; + } +} + void intel_pmu_lbr_enable(struct perf_event *event) { struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events); @@ -195,6 +220,7 @@ void intel_pmu_lbr_enable(struct perf_event *event) cpuc->br_sel = event->hw.branch_reg.reg; cpuc->lbr_users++; + perf_sched_cb_inc(event->ctx->pmu); } void intel_pmu_lbr_disable(struct perf_event *event) @@ -206,6 +232,7 @@ void intel_pmu_lbr_disable(struct perf_event *event) cpuc->lbr_users--; WARN_ON_ONCE(cpuc->lbr_users < 0); + perf_sched_cb_dec(event->ctx->pmu); if (cpuc->enabled && !cpuc->lbr_users) { __intel_pmu_lbr_disable(); diff --git a/include/linux/perf_event.h b/include/linux/perf_event.h index fbab6235d053..c7007a564440 100644 --- a/include/linux/perf_event.h +++ b/include/linux/perf_event.h @@ -511,7 +511,6 @@ struct perf_event_context { u64 generation; int pin_count; int nr_cgroups; /* cgroup evts */ - int nr_branch_stack; /* branch_stack evt */ struct rcu_head rcu_head; struct delayed_work orphans_remove; diff --git a/kernel/events/core.c b/kernel/events/core.c index 6c8b31b7efb6..f563ce767f93 100644 --- a/kernel/events/core.c +++ b/kernel/events/core.c @@ -153,7 +153,6 @@ enum event_type_t { */ struct static_key_deferred perf_sched_events __read_mostly; static DEFINE_PER_CPU(atomic_t, perf_cgroup_events); -static DEFINE_PER_CPU(atomic_t, perf_branch_stack_events); static DEFINE_PER_CPU(int, perf_sched_cb_usages); static atomic_t nr_mmap_events __read_mostly; @@ -1240,9 +1239,6 @@ list_add_event(struct perf_event *event, struct perf_event_context *ctx) if (is_cgroup_event(event)) ctx->nr_cgroups++; - if (has_branch_stack(event)) - ctx->nr_branch_stack++; - list_add_rcu(&event->event_entry, &ctx->event_list); ctx->nr_events++; if (event->attr.inherit_stat) @@ -1409,9 +1405,6 @@ list_del_event(struct perf_event *event, struct perf_event_context *ctx) cpuctx->cgrp = NULL; } - if (has_branch_stack(event)) - ctx->nr_branch_stack--; - ctx->nr_events--; if (event->attr.inherit_stat) ctx->nr_stat--; @@ -2808,64 +2801,6 @@ static void perf_event_context_sched_in(struct perf_event_context *ctx, perf_ctx_unlock(cpuctx, ctx); } -/* - * When sampling the branck stack in system-wide, it may be necessary - * to flush the stack on context switch. This happens when the branch - * stack does not tag its entries with the pid of the current task. - * Otherwise it becomes impossible to associate a branch entry with a - * task. This ambiguity is more likely to appear when the branch stack - * supports priv level filtering and the user sets it to monitor only - * at the user level (which could be a useful measurement in system-wide - * mode). In that case, the risk is high of having a branch stack with - * branch from multiple tasks. Flushing may mean dropping the existing - * entries or stashing them somewhere in the PMU specific code layer. - * - * This function provides the context switch callback to the lower code - * layer. It is invoked ONLY when there is at least one system-wide context - * with at least one active event using taken branch sampling. - */ -static void perf_branch_stack_sched_in(struct task_struct *prev, - struct task_struct *task) -{ - struct perf_cpu_context *cpuctx; - struct pmu *pmu; - unsigned long flags; - - /* no need to flush branch stack if not changing task */ - if (prev == task) - return; - - local_irq_save(flags); - - rcu_read_lock(); - - list_for_each_entry_rcu(pmu, &pmus, entry) { - cpuctx = this_cpu_ptr(pmu->pmu_cpu_context); - - /* - * check if the context has at least one - * event using PERF_SAMPLE_BRANCH_STACK - */ - if (cpuctx->ctx.nr_branch_stack > 0 - && pmu->flush_branch_stack) { - - perf_ctx_lock(cpuctx, cpuctx->task_ctx); - - perf_pmu_disable(pmu); - - pmu->flush_branch_stack(); - - perf_pmu_enable(pmu); - - perf_ctx_unlock(cpuctx, cpuctx->task_ctx); - } - } - - rcu_read_unlock(); - - local_irq_restore(flags); -} - /* * Called from scheduler to add the events of the current task * with interrupts disabled. @@ -2898,10 +2833,6 @@ void __perf_event_task_sched_in(struct task_struct *prev, if (atomic_read(this_cpu_ptr(&perf_cgroup_events))) perf_cgroup_sched_in(prev, task); - /* check for system-wide branch_stack events */ - if (atomic_read(this_cpu_ptr(&perf_branch_stack_events))) - perf_branch_stack_sched_in(prev, task); - if (__this_cpu_read(perf_sched_cb_usages)) perf_pmu_sched_task(prev, task, true); } @@ -3480,10 +3411,6 @@ static void unaccount_event_cpu(struct perf_event *event, int cpu) if (event->parent) return; - if (has_branch_stack(event)) { - if (!(event->attach_state & PERF_ATTACH_TASK)) - atomic_dec(&per_cpu(perf_branch_stack_events, cpu)); - } if (is_cgroup_event(event)) atomic_dec(&per_cpu(perf_cgroup_events, cpu)); } @@ -7139,10 +7066,6 @@ static void account_event_cpu(struct perf_event *event, int cpu) if (event->parent) return; - if (has_branch_stack(event)) { - if (!(event->attach_state & PERF_ATTACH_TASK)) - atomic_inc(&per_cpu(perf_branch_stack_events, cpu)); - } if (is_cgroup_event(event)) atomic_inc(&per_cpu(perf_cgroup_events, cpu)); } -- cgit v1.2.3 From 4af57ef28c2c1047fda9e1a5be02aa7a6a69cf9e Mon Sep 17 00:00:00 2001 From: "Yan, Zheng" Date: Tue, 4 Nov 2014 21:56:01 -0500 Subject: perf: Add pmu specific data for perf task context Introduce a new flag PERF_ATTACH_TASK_DATA for perf event's attach stata. The flag is set by PMU's event_init() callback, it indicates that perf event needs PMU specific data. The PMU specific data are initialized to zeros. Later patches will use PMU specific data to save LBR stack. Signed-off-by: Yan, Zheng Signed-off-by: Kan Liang Signed-off-by: Peter Zijlstra (Intel) Cc: Arnaldo Carvalho de Melo Cc: Linus Torvalds Cc: Paul Mackerras Cc: eranian@google.com Cc: jolsa@redhat.com Link: http://lkml.kernel.org/r/1415156173-10035-6-git-send-email-kan.liang@intel.com Signed-off-by: Ingo Molnar --- include/linux/perf_event.h | 6 ++++++ kernel/events/core.c | 40 ++++++++++++++++++++++++++++++++++++---- 2 files changed, 42 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/include/linux/perf_event.h b/include/linux/perf_event.h index c7007a564440..270cd0173e61 100644 --- a/include/linux/perf_event.h +++ b/include/linux/perf_event.h @@ -271,6 +271,10 @@ struct pmu { */ void (*sched_task) (struct perf_event_context *ctx, bool sched_in); + /* + * PMU specific data size + */ + size_t task_ctx_size; }; @@ -307,6 +311,7 @@ struct swevent_hlist { #define PERF_ATTACH_CONTEXT 0x01 #define PERF_ATTACH_GROUP 0x02 #define PERF_ATTACH_TASK 0x04 +#define PERF_ATTACH_TASK_DATA 0x08 struct perf_cgroup; struct ring_buffer; @@ -511,6 +516,7 @@ struct perf_event_context { u64 generation; int pin_count; int nr_cgroups; /* cgroup evts */ + void *task_ctx_data; /* pmu specific data */ struct rcu_head rcu_head; struct delayed_work orphans_remove; diff --git a/kernel/events/core.c b/kernel/events/core.c index f563ce767f93..688086bb7144 100644 --- a/kernel/events/core.c +++ b/kernel/events/core.c @@ -905,6 +905,15 @@ static void get_ctx(struct perf_event_context *ctx) WARN_ON(!atomic_inc_not_zero(&ctx->refcount)); } +static void free_ctx(struct rcu_head *head) +{ + struct perf_event_context *ctx; + + ctx = container_of(head, struct perf_event_context, rcu_head); + kfree(ctx->task_ctx_data); + kfree(ctx); +} + static void put_ctx(struct perf_event_context *ctx) { if (atomic_dec_and_test(&ctx->refcount)) { @@ -912,7 +921,7 @@ static void put_ctx(struct perf_event_context *ctx) put_ctx(ctx->parent_ctx); if (ctx->task) put_task_struct(ctx->task); - kfree_rcu(ctx, rcu_head); + call_rcu(&ctx->rcu_head, free_ctx); } } @@ -3309,12 +3318,15 @@ errout: * Returns a matching context with refcount and pincount. */ static struct perf_event_context * -find_get_context(struct pmu *pmu, struct task_struct *task, int cpu) +find_get_context(struct pmu *pmu, struct task_struct *task, + struct perf_event *event) { struct perf_event_context *ctx, *clone_ctx = NULL; struct perf_cpu_context *cpuctx; + void *task_ctx_data = NULL; unsigned long flags; int ctxn, err; + int cpu = event->cpu; if (!task) { /* Must be root to operate on a CPU event: */ @@ -3342,11 +3354,24 @@ find_get_context(struct pmu *pmu, struct task_struct *task, int cpu) if (ctxn < 0) goto errout; + if (event->attach_state & PERF_ATTACH_TASK_DATA) { + task_ctx_data = kzalloc(pmu->task_ctx_size, GFP_KERNEL); + if (!task_ctx_data) { + err = -ENOMEM; + goto errout; + } + } + retry: ctx = perf_lock_task_context(task, ctxn, &flags); if (ctx) { clone_ctx = unclone_ctx(ctx); ++ctx->pin_count; + + if (task_ctx_data && !ctx->task_ctx_data) { + ctx->task_ctx_data = task_ctx_data; + task_ctx_data = NULL; + } raw_spin_unlock_irqrestore(&ctx->lock, flags); if (clone_ctx) @@ -3357,6 +3382,11 @@ retry: if (!ctx) goto errout; + if (task_ctx_data) { + ctx->task_ctx_data = task_ctx_data; + task_ctx_data = NULL; + } + err = 0; mutex_lock(&task->perf_event_mutex); /* @@ -3383,9 +3413,11 @@ retry: } } + kfree(task_ctx_data); return ctx; errout: + kfree(task_ctx_data); return ERR_PTR(err); } @@ -7559,7 +7591,7 @@ SYSCALL_DEFINE5(perf_event_open, /* * Get the target context (task or percpu): */ - ctx = find_get_context(pmu, task, event->cpu); + ctx = find_get_context(pmu, task, event); if (IS_ERR(ctx)) { err = PTR_ERR(ctx); goto err_alloc; @@ -7765,7 +7797,7 @@ perf_event_create_kernel_counter(struct perf_event_attr *attr, int cpu, account_event(event); - ctx = find_get_context(event->pmu, task, cpu); + ctx = find_get_context(event->pmu, task, event); if (IS_ERR(ctx)) { err = PTR_ERR(ctx); goto err_free; -- cgit v1.2.3 From a46a23000198d929391aa9dac8de68734efa2703 Mon Sep 17 00:00:00 2001 From: "Yan, Zheng" Date: Tue, 4 Nov 2014 21:56:06 -0500 Subject: perf: Simplify the branch stack check Use event->attr.branch_sample_type to replace intel_pmu_needs_lbr_smpl() for avoiding duplicated code that implicitly enables the LBR. Currently, branch stack can be enabled by user explicitly requesting branch sampling or implicit branch sampling to correct PEBS skid. For user explicitly requested branch sampling, the branch_sample_type is explicitly set by user. For PEBS case, the branch_sample_type is also implicitly set to PERF_SAMPLE_BRANCH_ANY in x86_pmu_hw_config. Signed-off-by: Yan, Zheng Signed-off-by: Kan Liang Signed-off-by: Peter Zijlstra (Intel) Cc: Arnaldo Carvalho de Melo Cc: Linus Torvalds Cc: Paul Mackerras Cc: eranian@google.com Cc: jolsa@redhat.com Link: http://lkml.kernel.org/r/1415156173-10035-11-git-send-email-kan.liang@intel.com Signed-off-by: Ingo Molnar --- arch/x86/kernel/cpu/perf_event_intel.c | 20 +++----------------- include/linux/perf_event.h | 5 +++++ kernel/events/core.c | 3 +++ 3 files changed, 11 insertions(+), 17 deletions(-) (limited to 'include') diff --git a/arch/x86/kernel/cpu/perf_event_intel.c b/arch/x86/kernel/cpu/perf_event_intel.c index a485ba124476..9f1dd18fa395 100644 --- a/arch/x86/kernel/cpu/perf_event_intel.c +++ b/arch/x86/kernel/cpu/perf_event_intel.c @@ -1029,20 +1029,6 @@ static __initconst const u64 slm_hw_cache_event_ids }, }; -static inline bool intel_pmu_needs_lbr_smpl(struct perf_event *event) -{ - /* user explicitly requested branch sampling */ - if (has_branch_stack(event)) - return true; - - /* implicit branch sampling to correct PEBS skid */ - if (x86_pmu.intel_cap.pebs_trap && event->attr.precise_ip > 1 && - x86_pmu.intel_cap.pebs_format < 2) - return true; - - return false; -} - static void intel_pmu_disable_all(void) { struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events); @@ -1207,7 +1193,7 @@ static void intel_pmu_disable_event(struct perf_event *event) * must disable before any actual event * because any event may be combined with LBR */ - if (intel_pmu_needs_lbr_smpl(event)) + if (needs_branch_stack(event)) intel_pmu_lbr_disable(event); if (unlikely(hwc->config_base == MSR_ARCH_PERFMON_FIXED_CTR_CTRL)) { @@ -1268,7 +1254,7 @@ static void intel_pmu_enable_event(struct perf_event *event) * must enabled before any actual event * because any event may be combined with LBR */ - if (intel_pmu_needs_lbr_smpl(event)) + if (needs_branch_stack(event)) intel_pmu_lbr_enable(event); if (event->attr.exclude_host) @@ -1747,7 +1733,7 @@ static int intel_pmu_hw_config(struct perf_event *event) if (event->attr.precise_ip && x86_pmu.pebs_aliases) x86_pmu.pebs_aliases(event); - if (intel_pmu_needs_lbr_smpl(event)) { + if (needs_branch_stack(event)) { ret = intel_pmu_setup_lbr_filter(event); if (ret) return ret; diff --git a/include/linux/perf_event.h b/include/linux/perf_event.h index 270cd0173e61..43cc158487e6 100644 --- a/include/linux/perf_event.h +++ b/include/linux/perf_event.h @@ -814,6 +814,11 @@ static inline bool has_branch_stack(struct perf_event *event) return event->attr.sample_type & PERF_SAMPLE_BRANCH_STACK; } +static inline bool needs_branch_stack(struct perf_event *event) +{ + return event->attr.branch_sample_type != 0; +} + extern int perf_output_begin(struct perf_output_handle *handle, struct perf_event *event, unsigned int size); extern void perf_output_end(struct perf_output_handle *handle); diff --git a/kernel/events/core.c b/kernel/events/core.c index 84451c0debba..257eccf9afd4 100644 --- a/kernel/events/core.c +++ b/kernel/events/core.c @@ -7232,6 +7232,9 @@ perf_event_alloc(struct perf_event_attr *attr, int cpu, if (attr->inherit && (attr->read_format & PERF_FORMAT_GROUP)) goto err_ns; + if (!has_branch_stack(event)) + event->attr.branch_sample_type = 0; + pmu = perf_init_event(event); if (!pmu) goto err_ns; -- cgit v1.2.3 From 2c44b1936bb3b135a3fac8b3493394d42e51cf70 Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Wed, 5 Nov 2014 10:36:45 +0100 Subject: perf/x86/intel: Expose LBR callstack to user space tooling With LBR call stack feature enable, there are three callchain options. Enable the 3rd callchain option (LBR callstack) to user space tooling. Signed-off-by: Peter Zijlstra (Intel) Cc: Jiri Olsa Cc: Arnaldo Carvalho de Melo Cc: Andy Lutomirski Cc: Kan Liang Cc: Linus Torvalds Cc: Paul Mackerras Cc: Vince Weaver Cc: linux-api@vger.kernel.org Link: http://lkml.kernel.org/r/20141105093759.GQ10501@worktop.programming.kicks-ass.net Signed-off-by: Ingo Molnar --- arch/x86/kernel/cpu/perf_event.h | 8 -------- arch/x86/kernel/cpu/perf_event_intel_lbr.c | 8 ++++---- include/uapi/linux/perf_event.h | 16 ++++++++-------- 3 files changed, 12 insertions(+), 20 deletions(-) (limited to 'include') diff --git a/arch/x86/kernel/cpu/perf_event.h b/arch/x86/kernel/cpu/perf_event.h index 69c26b396cf4..a371d27d6795 100644 --- a/arch/x86/kernel/cpu/perf_event.h +++ b/arch/x86/kernel/cpu/perf_event.h @@ -523,14 +523,6 @@ struct x86_perf_task_context { int lbr_stack_state; }; -enum { - PERF_SAMPLE_BRANCH_CALL_STACK_SHIFT = PERF_SAMPLE_BRANCH_MAX_SHIFT, - PERF_SAMPLE_BRANCH_SELECT_MAP_SIZE, - - PERF_SAMPLE_BRANCH_CALL_STACK = - 1U << PERF_SAMPLE_BRANCH_CALL_STACK_SHIFT, -}; - #define x86_add_quirk(func_) \ do { \ static struct x86_pmu_quirk __quirk __initdata = { \ diff --git a/arch/x86/kernel/cpu/perf_event_intel_lbr.c b/arch/x86/kernel/cpu/perf_event_intel_lbr.c index 084f2eb20c8b..0473874109cb 100644 --- a/arch/x86/kernel/cpu/perf_event_intel_lbr.c +++ b/arch/x86/kernel/cpu/perf_event_intel_lbr.c @@ -537,7 +537,7 @@ static int intel_pmu_setup_hw_lbr_filter(struct perf_event *event) u64 mask = 0, v; int i; - for (i = 0; i < PERF_SAMPLE_BRANCH_SELECT_MAP_SIZE; i++) { + for (i = 0; i < PERF_SAMPLE_BRANCH_MAX_SHIFT; i++) { if (!(br_type & (1ULL << i))) continue; @@ -821,7 +821,7 @@ intel_pmu_lbr_filter(struct cpu_hw_events *cpuc) /* * Map interface branch filters onto LBR filters */ -static const int nhm_lbr_sel_map[PERF_SAMPLE_BRANCH_SELECT_MAP_SIZE] = { +static const int nhm_lbr_sel_map[PERF_SAMPLE_BRANCH_MAX_SHIFT] = { [PERF_SAMPLE_BRANCH_ANY_SHIFT] = LBR_ANY, [PERF_SAMPLE_BRANCH_USER_SHIFT] = LBR_USER, [PERF_SAMPLE_BRANCH_KERNEL_SHIFT] = LBR_KERNEL, @@ -840,7 +840,7 @@ static const int nhm_lbr_sel_map[PERF_SAMPLE_BRANCH_SELECT_MAP_SIZE] = { [PERF_SAMPLE_BRANCH_COND_SHIFT] = LBR_JCC, }; -static const int snb_lbr_sel_map[PERF_SAMPLE_BRANCH_SELECT_MAP_SIZE] = { +static const int snb_lbr_sel_map[PERF_SAMPLE_BRANCH_MAX_SHIFT] = { [PERF_SAMPLE_BRANCH_ANY_SHIFT] = LBR_ANY, [PERF_SAMPLE_BRANCH_USER_SHIFT] = LBR_USER, [PERF_SAMPLE_BRANCH_KERNEL_SHIFT] = LBR_KERNEL, @@ -852,7 +852,7 @@ static const int snb_lbr_sel_map[PERF_SAMPLE_BRANCH_SELECT_MAP_SIZE] = { [PERF_SAMPLE_BRANCH_COND_SHIFT] = LBR_JCC, }; -static const int hsw_lbr_sel_map[PERF_SAMPLE_BRANCH_SELECT_MAP_SIZE] = { +static const int hsw_lbr_sel_map[PERF_SAMPLE_BRANCH_MAX_SHIFT] = { [PERF_SAMPLE_BRANCH_ANY_SHIFT] = LBR_ANY, [PERF_SAMPLE_BRANCH_USER_SHIFT] = LBR_USER, [PERF_SAMPLE_BRANCH_KERNEL_SHIFT] = LBR_KERNEL, diff --git a/include/uapi/linux/perf_event.h b/include/uapi/linux/perf_event.h index e46b93279e3d..1e3cd07cf76e 100644 --- a/include/uapi/linux/perf_event.h +++ b/include/uapi/linux/perf_event.h @@ -166,6 +166,8 @@ enum perf_branch_sample_type_shift { PERF_SAMPLE_BRANCH_NO_TX_SHIFT = 9, /* not in transaction */ PERF_SAMPLE_BRANCH_COND_SHIFT = 10, /* conditional branches */ + PERF_SAMPLE_BRANCH_CALL_STACK_SHIFT = 11, /* call/ret stack */ + PERF_SAMPLE_BRANCH_MAX_SHIFT /* non-ABI */ }; @@ -175,18 +177,16 @@ enum perf_branch_sample_type { PERF_SAMPLE_BRANCH_HV = 1U << PERF_SAMPLE_BRANCH_HV_SHIFT, PERF_SAMPLE_BRANCH_ANY = 1U << PERF_SAMPLE_BRANCH_ANY_SHIFT, - PERF_SAMPLE_BRANCH_ANY_CALL = - 1U << PERF_SAMPLE_BRANCH_ANY_CALL_SHIFT, - PERF_SAMPLE_BRANCH_ANY_RETURN = - 1U << PERF_SAMPLE_BRANCH_ANY_RETURN_SHIFT, - PERF_SAMPLE_BRANCH_IND_CALL = - 1U << PERF_SAMPLE_BRANCH_IND_CALL_SHIFT, - PERF_SAMPLE_BRANCH_ABORT_TX = - 1U << PERF_SAMPLE_BRANCH_ABORT_TX_SHIFT, + PERF_SAMPLE_BRANCH_ANY_CALL = 1U << PERF_SAMPLE_BRANCH_ANY_CALL_SHIFT, + PERF_SAMPLE_BRANCH_ANY_RETURN = 1U << PERF_SAMPLE_BRANCH_ANY_RETURN_SHIFT, + PERF_SAMPLE_BRANCH_IND_CALL = 1U << PERF_SAMPLE_BRANCH_IND_CALL_SHIFT, + PERF_SAMPLE_BRANCH_ABORT_TX = 1U << PERF_SAMPLE_BRANCH_ABORT_TX_SHIFT, PERF_SAMPLE_BRANCH_IN_TX = 1U << PERF_SAMPLE_BRANCH_IN_TX_SHIFT, PERF_SAMPLE_BRANCH_NO_TX = 1U << PERF_SAMPLE_BRANCH_NO_TX_SHIFT, PERF_SAMPLE_BRANCH_COND = 1U << PERF_SAMPLE_BRANCH_COND_SHIFT, + PERF_SAMPLE_BRANCH_CALL_STACK = 1U << PERF_SAMPLE_BRANCH_CALL_STACK_SHIFT, + PERF_SAMPLE_BRANCH_MAX = 1U << PERF_SAMPLE_BRANCH_MAX_SHIFT, }; -- cgit v1.2.3 From acba3c7e4652ca5fcb2fd9376d58c2dffd8ddf2a Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Wed, 14 Jan 2015 14:15:39 +0100 Subject: perf, powerpc: Fix up flush_branch_stack() users The recent LBR rework for x86 left a stray flush_branch_stack() user in the PowerPC code, fix that up. Signed-off-by: Peter Zijlstra (Intel) Cc: Anshuman Khandual Cc: Anton Blanchard Cc: Arnaldo Carvalho de Melo Cc: Benjamin Herrenschmidt Cc: Christoph Lameter Cc: Joel Stanley Cc: Linus Torvalds Cc: Michael Ellerman Cc: Michael Neuling Cc: Paul Mackerras Cc: Tejun Heo Cc: linuxppc-dev@lists.ozlabs.org Signed-off-by: Ingo Molnar --- arch/powerpc/perf/core-book3s.c | 13 +++++++++---- include/linux/perf_event.h | 5 ----- 2 files changed, 9 insertions(+), 9 deletions(-) (limited to 'include') diff --git a/arch/powerpc/perf/core-book3s.c b/arch/powerpc/perf/core-book3s.c index 7c4f6690533a..7fd60dcb2cb0 100644 --- a/arch/powerpc/perf/core-book3s.c +++ b/arch/powerpc/perf/core-book3s.c @@ -124,7 +124,7 @@ static unsigned long ebb_switch_in(bool ebb, struct cpu_hw_events *cpuhw) static inline void power_pmu_bhrb_enable(struct perf_event *event) {} static inline void power_pmu_bhrb_disable(struct perf_event *event) {} -static void power_pmu_flush_branch_stack(void) {} +static void power_pmu_sched_task(struct perf_event_context *ctx, bool sched_in) {} static inline void power_pmu_bhrb_read(struct cpu_hw_events *cpuhw) {} static void pmao_restore_workaround(bool ebb) { } #endif /* CONFIG_PPC32 */ @@ -350,6 +350,7 @@ static void power_pmu_bhrb_enable(struct perf_event *event) cpuhw->bhrb_context = event->ctx; } cpuhw->bhrb_users++; + perf_sched_cb_inc(event->ctx->pmu); } static void power_pmu_bhrb_disable(struct perf_event *event) @@ -361,6 +362,7 @@ static void power_pmu_bhrb_disable(struct perf_event *event) cpuhw->bhrb_users--; WARN_ON_ONCE(cpuhw->bhrb_users < 0); + perf_sched_cb_dec(event->ctx->pmu); if (!cpuhw->disabled && !cpuhw->bhrb_users) { /* BHRB cannot be turned off when other @@ -375,9 +377,12 @@ static void power_pmu_bhrb_disable(struct perf_event *event) /* Called from ctxsw to prevent one process's branch entries to * mingle with the other process's entries during context switch. */ -static void power_pmu_flush_branch_stack(void) +static void power_pmu_sched_task(struct perf_event_context *ctx, bool sched_in) { - if (ppmu->bhrb_nr) + if (!ppmu->bhrb_nr) + return; + + if (sched_in) power_pmu_bhrb_reset(); } /* Calculate the to address for a branch */ @@ -1901,7 +1906,7 @@ static struct pmu power_pmu = { .cancel_txn = power_pmu_cancel_txn, .commit_txn = power_pmu_commit_txn, .event_idx = power_pmu_event_idx, - .flush_branch_stack = power_pmu_flush_branch_stack, + .sched_task = power_pmu_sched_task, }; /* diff --git a/include/linux/perf_event.h b/include/linux/perf_event.h index 43cc158487e6..724d3720c9b1 100644 --- a/include/linux/perf_event.h +++ b/include/linux/perf_event.h @@ -261,11 +261,6 @@ struct pmu { */ int (*event_idx) (struct perf_event *event); /*optional */ - /* - * flush branch stack on context-switches (needed in cpu-wide mode) - */ - void (*flush_branch_stack) (void); - /* * context-switches callback */ -- cgit v1.2.3 From 4421f8f0fa02bc982b410cd773223cc280791c54 Mon Sep 17 00:00:00 2001 From: Miroslav Benes Date: Wed, 18 Feb 2015 15:21:07 +0100 Subject: livepatch: remove extern specifier from header files Storage-class specifier 'extern' is redundant in front of the function declaration. According to the C specification it has the same meaning as if not present at all. So remove it. Signed-off-by: Miroslav Benes Acked-by: Josh Poimboeuf Reviewed-by: Masami Hiramatsu Signed-off-by: Jiri Kosina --- arch/x86/include/asm/livepatch.h | 4 ++-- include/linux/livepatch.h | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/arch/x86/include/asm/livepatch.h b/arch/x86/include/asm/livepatch.h index a455a53d789a..2d29197bd2fb 100644 --- a/arch/x86/include/asm/livepatch.h +++ b/arch/x86/include/asm/livepatch.h @@ -32,8 +32,8 @@ static inline int klp_check_compiler_support(void) #endif return 0; } -extern int klp_write_module_reloc(struct module *mod, unsigned long type, - unsigned long loc, unsigned long value); +int klp_write_module_reloc(struct module *mod, unsigned long type, + unsigned long loc, unsigned long value); static inline void klp_arch_set_pc(struct pt_regs *regs, unsigned long ip) { diff --git a/include/linux/livepatch.h b/include/linux/livepatch.h index 95023fd8b00d..ee6dbb39a809 100644 --- a/include/linux/livepatch.h +++ b/include/linux/livepatch.h @@ -123,10 +123,10 @@ struct klp_patch { enum klp_state state; }; -extern int klp_register_patch(struct klp_patch *); -extern int klp_unregister_patch(struct klp_patch *); -extern int klp_enable_patch(struct klp_patch *); -extern int klp_disable_patch(struct klp_patch *); +int klp_register_patch(struct klp_patch *); +int klp_unregister_patch(struct klp_patch *); +int klp_enable_patch(struct klp_patch *); +int klp_disable_patch(struct klp_patch *); #endif /* CONFIG_LIVEPATCH */ -- cgit v1.2.3 From fba7ecf09bc458b15f9d578e4213c8c349f9592d Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Wed, 18 Feb 2015 14:53:55 +0200 Subject: Bluetooth: Convert hci_cb_list_lock to a mutex We'll soon need to be able to sleep inside the loops that iterate the hci_cb list, so neither a spinlock, rwlock or rcu are usable. This patch changes the lock to a mutex which permits sleeping while holding the lock. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- include/net/bluetooth/hci_core.h | 18 +++++++++--------- net/bluetooth/hci_core.c | 10 +++++----- 2 files changed, 14 insertions(+), 14 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 5f1ca3359c1a..137a18226975 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -499,7 +499,7 @@ struct hci_conn_params { extern struct list_head hci_dev_list; extern struct list_head hci_cb_list; extern rwlock_t hci_dev_list_lock; -extern rwlock_t hci_cb_list_lock; +extern struct mutex hci_cb_list_lock; /* ----- HCI interface to upper protocols ----- */ int l2cap_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr); @@ -1160,12 +1160,12 @@ static inline void hci_auth_cfm(struct hci_conn *conn, __u8 status) encrypt = test_bit(HCI_CONN_ENCRYPT, &conn->flags) ? 0x01 : 0x00; - read_lock(&hci_cb_list_lock); + mutex_lock(&hci_cb_list_lock); list_for_each_entry(cb, &hci_cb_list, list) { if (cb->security_cfm) cb->security_cfm(conn, status, encrypt); } - read_unlock(&hci_cb_list_lock); + mutex_unlock(&hci_cb_list_lock); } static inline void hci_encrypt_cfm(struct hci_conn *conn, __u8 status, @@ -1181,24 +1181,24 @@ static inline void hci_encrypt_cfm(struct hci_conn *conn, __u8 status, hci_proto_encrypt_cfm(conn, status, encrypt); - read_lock(&hci_cb_list_lock); + mutex_lock(&hci_cb_list_lock); list_for_each_entry(cb, &hci_cb_list, list) { if (cb->security_cfm) cb->security_cfm(conn, status, encrypt); } - read_unlock(&hci_cb_list_lock); + mutex_unlock(&hci_cb_list_lock); } static inline void hci_key_change_cfm(struct hci_conn *conn, __u8 status) { struct hci_cb *cb; - read_lock(&hci_cb_list_lock); + mutex_lock(&hci_cb_list_lock); list_for_each_entry(cb, &hci_cb_list, list) { if (cb->key_change_cfm) cb->key_change_cfm(conn, status); } - read_unlock(&hci_cb_list_lock); + mutex_unlock(&hci_cb_list_lock); } static inline void hci_role_switch_cfm(struct hci_conn *conn, __u8 status, @@ -1206,12 +1206,12 @@ static inline void hci_role_switch_cfm(struct hci_conn *conn, __u8 status, { struct hci_cb *cb; - read_lock(&hci_cb_list_lock); + mutex_lock(&hci_cb_list_lock); list_for_each_entry(cb, &hci_cb_list, list) { if (cb->role_switch_cfm) cb->role_switch_cfm(conn, status, role); } - read_unlock(&hci_cb_list_lock); + mutex_unlock(&hci_cb_list_lock); } static inline bool eir_has_data_type(u8 *data, size_t data_len, u8 type) diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 91f557b0318a..dbd26bcb9210 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -51,7 +51,7 @@ DEFINE_RWLOCK(hci_dev_list_lock); /* HCI callback list */ LIST_HEAD(hci_cb_list); -DEFINE_RWLOCK(hci_cb_list_lock); +DEFINE_MUTEX(hci_cb_list_lock); /* HCI ID Numbering */ static DEFINE_IDA(hci_index_ida); @@ -3464,9 +3464,9 @@ int hci_register_cb(struct hci_cb *cb) { BT_DBG("%p name %s", cb, cb->name); - write_lock(&hci_cb_list_lock); + mutex_lock(&hci_cb_list_lock); list_add_tail(&cb->list, &hci_cb_list); - write_unlock(&hci_cb_list_lock); + mutex_unlock(&hci_cb_list_lock); return 0; } @@ -3476,9 +3476,9 @@ int hci_unregister_cb(struct hci_cb *cb) { BT_DBG("%p name %s", cb, cb->name); - write_lock(&hci_cb_list_lock); + mutex_lock(&hci_cb_list_lock); list_del(&cb->list); - write_unlock(&hci_cb_list_lock); + mutex_unlock(&hci_cb_list_lock); return 0; } -- cgit v1.2.3 From 354fe804edb29625eee6dd7b1f3c72b43392704d Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Wed, 18 Feb 2015 14:53:56 +0200 Subject: Bluetooth: Convert L2CAP security callback to use hci_cb There's no reason to have the custom hci_proto_auth/encrypt_cfm helpers when the hci_cb list works equally well. This patch adds L2CAP to the hci_cb list and makes l2cap_security_cfm a private function of l2cap_core.c. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- include/net/bluetooth/hci_core.h | 40 ++++++---------------------------------- net/bluetooth/l2cap_core.c | 14 ++++++++++---- 2 files changed, 16 insertions(+), 38 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 137a18226975..4a2db5e62699 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -506,7 +506,6 @@ int l2cap_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr); void l2cap_connect_cfm(struct hci_conn *hcon, u8 status); int l2cap_disconn_ind(struct hci_conn *hcon); void l2cap_disconn_cfm(struct hci_conn *hcon, u8 reason); -int l2cap_security_cfm(struct hci_conn *hcon, u8 status, u8 encrypt); int l2cap_recv_acldata(struct hci_conn *hcon, struct sk_buff *skb, u16 flags); int sco_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 *flags); @@ -1107,35 +1106,6 @@ static inline void hci_proto_disconn_cfm(struct hci_conn *conn, __u8 reason) conn->disconn_cfm_cb(conn, reason); } -static inline void hci_proto_auth_cfm(struct hci_conn *conn, __u8 status) -{ - __u8 encrypt; - - if (conn->type != ACL_LINK && conn->type != LE_LINK) - return; - - if (test_bit(HCI_CONN_ENCRYPT_PEND, &conn->flags)) - return; - - encrypt = test_bit(HCI_CONN_ENCRYPT, &conn->flags) ? 0x01 : 0x00; - l2cap_security_cfm(conn, status, encrypt); - - if (conn->security_cfm_cb) - conn->security_cfm_cb(conn, status); -} - -static inline void hci_proto_encrypt_cfm(struct hci_conn *conn, __u8 status, - __u8 encrypt) -{ - if (conn->type != ACL_LINK && conn->type != LE_LINK) - return; - - l2cap_security_cfm(conn, status, encrypt); - - if (conn->security_cfm_cb) - conn->security_cfm_cb(conn, status); -} - /* ----- HCI callbacks ----- */ struct hci_cb { struct list_head list; @@ -1153,8 +1123,6 @@ static inline void hci_auth_cfm(struct hci_conn *conn, __u8 status) struct hci_cb *cb; __u8 encrypt; - hci_proto_auth_cfm(conn, status); - if (test_bit(HCI_CONN_ENCRYPT_PEND, &conn->flags)) return; @@ -1166,6 +1134,9 @@ static inline void hci_auth_cfm(struct hci_conn *conn, __u8 status) cb->security_cfm(conn, status, encrypt); } mutex_unlock(&hci_cb_list_lock); + + if (conn->security_cfm_cb) + conn->security_cfm_cb(conn, status); } static inline void hci_encrypt_cfm(struct hci_conn *conn, __u8 status, @@ -1179,14 +1150,15 @@ static inline void hci_encrypt_cfm(struct hci_conn *conn, __u8 status, if (conn->pending_sec_level > conn->sec_level) conn->sec_level = conn->pending_sec_level; - hci_proto_encrypt_cfm(conn, status, encrypt); - mutex_lock(&hci_cb_list_lock); list_for_each_entry(cb, &hci_cb_list, list) { if (cb->security_cfm) cb->security_cfm(conn, status, encrypt); } mutex_unlock(&hci_cb_list_lock); + + if (conn->security_cfm_cb) + conn->security_cfm_cb(conn, status); } static inline void hci_key_change_cfm(struct hci_conn *conn, __u8 status) diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index ec6f78e481dc..424fcc5c4980 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -7345,13 +7345,13 @@ static inline void l2cap_check_encryption(struct l2cap_chan *chan, u8 encrypt) } } -int l2cap_security_cfm(struct hci_conn *hcon, u8 status, u8 encrypt) +static void l2cap_security_cfm(struct hci_conn *hcon, u8 status, u8 encrypt) { struct l2cap_conn *conn = hcon->l2cap_data; struct l2cap_chan *chan; if (!conn) - return 0; + return; BT_DBG("conn %p status 0x%2.2x encrypt %u", conn, status, encrypt); @@ -7434,8 +7434,6 @@ int l2cap_security_cfm(struct hci_conn *hcon, u8 status, u8 encrypt) } mutex_unlock(&conn->chan_lock); - - return 0; } int l2cap_recv_acldata(struct hci_conn *hcon, struct sk_buff *skb, u16 flags) @@ -7543,6 +7541,11 @@ drop: return 0; } +static struct hci_cb l2cap_cb = { + .name = "L2CAP", + .security_cfm = l2cap_security_cfm, +}; + static int l2cap_debugfs_show(struct seq_file *f, void *p) { struct l2cap_chan *c; @@ -7584,6 +7587,8 @@ int __init l2cap_init(void) if (err < 0) return err; + hci_register_cb(&l2cap_cb); + if (IS_ERR_OR_NULL(bt_debugfs)) return 0; @@ -7601,6 +7606,7 @@ int __init l2cap_init(void) void l2cap_exit(void) { debugfs_remove(l2cap_debugfs); + hci_unregister_cb(&l2cap_cb); l2cap_cleanup_sockets(); } -- cgit v1.2.3 From 539c496d88f7f96d42abde4e9d901c8f8167d615 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Wed, 18 Feb 2015 14:53:57 +0200 Subject: Bluetooth: Convert connect_cfm to be triggered through hci_cb This patch moves all the connect_cfm callbacks to be based on the hci_cb list. This means making l2cap_connect_cfm private to l2cap_core.c and sco_connect_cb private to sco.c respectively. Since the hci_conn type filtering isn't done any more on the wrapper level the callbacks themselves need to check that they were passed a relevant type of connection. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- include/net/bluetooth/hci_core.h | 40 ++++++++++++++++------------------------ net/bluetooth/hci_conn.c | 4 ++-- net/bluetooth/hci_event.c | 36 ++++++++++++++++++------------------ net/bluetooth/l2cap_core.c | 6 +++++- net/bluetooth/sco.c | 15 ++++++++++++++- 5 files changed, 55 insertions(+), 46 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 4a2db5e62699..0f00f0e9f257 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -503,13 +503,11 @@ extern struct mutex hci_cb_list_lock; /* ----- HCI interface to upper protocols ----- */ int l2cap_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr); -void l2cap_connect_cfm(struct hci_conn *hcon, u8 status); int l2cap_disconn_ind(struct hci_conn *hcon); void l2cap_disconn_cfm(struct hci_conn *hcon, u8 reason); int l2cap_recv_acldata(struct hci_conn *hcon, struct sk_buff *skb, u16 flags); int sco_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 *flags); -void sco_connect_cfm(struct hci_conn *hcon, __u8 status); void sco_disconn_cfm(struct hci_conn *hcon, __u8 reason); int sco_recv_scodata(struct hci_conn *hcon, struct sk_buff *skb); @@ -1050,28 +1048,6 @@ static inline int hci_proto_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, } } -static inline void hci_proto_connect_cfm(struct hci_conn *conn, __u8 status) -{ - switch (conn->type) { - case ACL_LINK: - case LE_LINK: - l2cap_connect_cfm(conn, status); - break; - - case SCO_LINK: - case ESCO_LINK: - sco_connect_cfm(conn, status); - break; - - default: - BT_ERR("unknown link type %d", conn->type); - break; - } - - if (conn->connect_cfm_cb) - conn->connect_cfm_cb(conn, status); -} - static inline int hci_proto_disconn_ind(struct hci_conn *conn) { if (conn->type != ACL_LINK && conn->type != LE_LINK) @@ -1112,12 +1088,28 @@ struct hci_cb { char *name; + void (*connect_cfm) (struct hci_conn *conn, __u8 status); void (*security_cfm) (struct hci_conn *conn, __u8 status, __u8 encrypt); void (*key_change_cfm) (struct hci_conn *conn, __u8 status); void (*role_switch_cfm) (struct hci_conn *conn, __u8 status, __u8 role); }; +static inline void hci_connect_cfm(struct hci_conn *conn, __u8 status) +{ + struct hci_cb *cb; + + mutex_lock(&hci_cb_list_lock); + list_for_each_entry(cb, &hci_cb_list, list) { + if (cb->connect_cfm) + cb->connect_cfm(conn, status); + } + mutex_unlock(&hci_cb_list_lock); + + if (conn->connect_cfm_cb) + conn->connect_cfm_cb(conn, status); +} + static inline void hci_auth_cfm(struct hci_conn *conn, __u8 status) { struct hci_cb *cb; diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c index e3263b61bcf3..e9206734e024 100644 --- a/net/bluetooth/hci_conn.c +++ b/net/bluetooth/hci_conn.c @@ -309,7 +309,7 @@ void hci_sco_setup(struct hci_conn *conn, __u8 status) else hci_add_sco(sco, conn->handle); } else { - hci_proto_connect_cfm(sco, status); + hci_connect_cfm(sco, status); hci_conn_del(sco); } } @@ -618,7 +618,7 @@ void hci_le_conn_failed(struct hci_conn *conn, u8 status) mgmt_connect_failed(hdev, &conn->dst, conn->type, conn->dst_type, status); - hci_proto_connect_cfm(conn, status); + hci_connect_cfm(conn, status); hci_conn_del(conn); diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index a3fb094822b6..0b599129c64c 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -1537,7 +1537,7 @@ static void hci_cs_create_conn(struct hci_dev *hdev, __u8 status) if (conn && conn->state == BT_CONNECT) { if (status != 0x0c || conn->attempt > 2) { conn->state = BT_CLOSED; - hci_proto_connect_cfm(conn, status); + hci_connect_cfm(conn, status); hci_conn_del(conn); } else conn->state = BT_CONNECT2; @@ -1581,7 +1581,7 @@ static void hci_cs_add_sco(struct hci_dev *hdev, __u8 status) if (sco) { sco->state = BT_CLOSED; - hci_proto_connect_cfm(sco, status); + hci_connect_cfm(sco, status); hci_conn_del(sco); } } @@ -1608,7 +1608,7 @@ static void hci_cs_auth_requested(struct hci_dev *hdev, __u8 status) conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(cp->handle)); if (conn) { if (conn->state == BT_CONFIG) { - hci_proto_connect_cfm(conn, status); + hci_connect_cfm(conn, status); hci_conn_drop(conn); } } @@ -1635,7 +1635,7 @@ static void hci_cs_set_conn_encrypt(struct hci_dev *hdev, __u8 status) conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(cp->handle)); if (conn) { if (conn->state == BT_CONFIG) { - hci_proto_connect_cfm(conn, status); + hci_connect_cfm(conn, status); hci_conn_drop(conn); } } @@ -1811,7 +1811,7 @@ static void hci_cs_read_remote_features(struct hci_dev *hdev, __u8 status) conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(cp->handle)); if (conn) { if (conn->state == BT_CONFIG) { - hci_proto_connect_cfm(conn, status); + hci_connect_cfm(conn, status); hci_conn_drop(conn); } } @@ -1838,7 +1838,7 @@ static void hci_cs_read_remote_ext_features(struct hci_dev *hdev, __u8 status) conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(cp->handle)); if (conn) { if (conn->state == BT_CONFIG) { - hci_proto_connect_cfm(conn, status); + hci_connect_cfm(conn, status); hci_conn_drop(conn); } } @@ -1873,7 +1873,7 @@ static void hci_cs_setup_sync_conn(struct hci_dev *hdev, __u8 status) if (sco) { sco->state = BT_CLOSED; - hci_proto_connect_cfm(sco, status); + hci_connect_cfm(sco, status); hci_conn_del(sco); } } @@ -2255,10 +2255,10 @@ static void hci_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *skb) hci_sco_setup(conn, ev->status); if (ev->status) { - hci_proto_connect_cfm(conn, ev->status); + hci_connect_cfm(conn, ev->status); hci_conn_del(conn); } else if (ev->link_type != ACL_LINK) - hci_proto_connect_cfm(conn, ev->status); + hci_connect_cfm(conn, ev->status); unlock: hci_dev_unlock(hdev); @@ -2366,7 +2366,7 @@ static void hci_conn_request_evt(struct hci_dev *hdev, struct sk_buff *skb) &cp); } else { conn->state = BT_CONNECT2; - hci_proto_connect_cfm(conn, 0); + hci_connect_cfm(conn, 0); } } @@ -2501,7 +2501,7 @@ static void hci_auth_complete_evt(struct hci_dev *hdev, struct sk_buff *skb) &cp); } else { conn->state = BT_CONNECTED; - hci_proto_connect_cfm(conn, ev->status); + hci_connect_cfm(conn, ev->status); hci_conn_drop(conn); } } else { @@ -2629,12 +2629,12 @@ static void hci_encrypt_change_evt(struct hci_dev *hdev, struct sk_buff *skb) if (test_bit(HCI_SC_ONLY, &hdev->dev_flags) && (!test_bit(HCI_CONN_AES_CCM, &conn->flags) || conn->key_type != HCI_LK_AUTH_COMBINATION_P256)) { - hci_proto_connect_cfm(conn, HCI_ERROR_AUTH_FAILURE); + hci_connect_cfm(conn, HCI_ERROR_AUTH_FAILURE); hci_conn_drop(conn); goto unlock; } - hci_proto_connect_cfm(conn, ev->status); + hci_connect_cfm(conn, ev->status); hci_conn_drop(conn); } else hci_encrypt_cfm(conn, ev->status, ev->encrypt); @@ -2707,7 +2707,7 @@ static void hci_remote_features_evt(struct hci_dev *hdev, if (!hci_outgoing_auth_needed(hdev, conn)) { conn->state = BT_CONNECTED; - hci_proto_connect_cfm(conn, ev->status); + hci_connect_cfm(conn, ev->status); hci_conn_drop(conn); } @@ -3679,7 +3679,7 @@ static void hci_remote_ext_features_evt(struct hci_dev *hdev, if (!hci_outgoing_auth_needed(hdev, conn)) { conn->state = BT_CONNECTED; - hci_proto_connect_cfm(conn, ev->status); + hci_connect_cfm(conn, ev->status); hci_conn_drop(conn); } @@ -3738,7 +3738,7 @@ static void hci_sync_conn_complete_evt(struct hci_dev *hdev, break; } - hci_proto_connect_cfm(conn, ev->status); + hci_connect_cfm(conn, ev->status); if (ev->status) hci_conn_del(conn); @@ -3849,7 +3849,7 @@ static void hci_key_refresh_complete_evt(struct hci_dev *hdev, if (!ev->status) conn->state = BT_CONNECTED; - hci_proto_connect_cfm(conn, ev->status); + hci_connect_cfm(conn, ev->status); hci_conn_drop(conn); } else { hci_auth_cfm(conn, ev->status); @@ -4512,7 +4512,7 @@ static void hci_le_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *skb) hci_debugfs_create_conn(conn); hci_conn_add_sysfs(conn); - hci_proto_connect_cfm(conn, ev->status); + hci_connect_cfm(conn, ev->status); params = hci_pend_le_action_lookup(&hdev->pend_le_conns, &conn->dst, conn->dst_type); diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index 424fcc5c4980..6e2c3bdda7d3 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -7252,13 +7252,16 @@ static struct l2cap_chan *l2cap_global_fixed_chan(struct l2cap_chan *c, return NULL; } -void l2cap_connect_cfm(struct hci_conn *hcon, u8 status) +static void l2cap_connect_cfm(struct hci_conn *hcon, u8 status) { struct hci_dev *hdev = hcon->hdev; struct l2cap_conn *conn; struct l2cap_chan *pchan; u8 dst_type; + if (hcon->type != ACL_LINK && hcon->type != LE_LINK) + return; + BT_DBG("hcon %p bdaddr %pMR status %d", hcon, &hcon->dst, status); if (status) { @@ -7543,6 +7546,7 @@ drop: static struct hci_cb l2cap_cb = { .name = "L2CAP", + .connect_cfm = l2cap_connect_cfm, .security_cfm = l2cap_security_cfm, }; diff --git a/net/bluetooth/sco.c b/net/bluetooth/sco.c index 76321b546e84..3c2e36f94b65 100644 --- a/net/bluetooth/sco.c +++ b/net/bluetooth/sco.c @@ -1083,9 +1083,13 @@ int sco_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 *flags) return lm; } -void sco_connect_cfm(struct hci_conn *hcon, __u8 status) +static void sco_connect_cfm(struct hci_conn *hcon, __u8 status) { + if (hcon->type != SCO_LINK && hcon->type != ESCO_LINK) + return; + BT_DBG("hcon %p bdaddr %pMR status %d", hcon, &hcon->dst, status); + if (!status) { struct sco_conn *conn; @@ -1122,6 +1126,11 @@ drop: return 0; } +static struct hci_cb sco_cb = { + .name = "SCO", + .connect_cfm = sco_connect_cfm, +}; + static int sco_debugfs_show(struct seq_file *f, void *p) { struct sock *sk; @@ -1203,6 +1212,8 @@ int __init sco_init(void) BT_INFO("SCO socket layer initialized"); + hci_register_cb(&sco_cb); + if (IS_ERR_OR_NULL(bt_debugfs)) return 0; @@ -1222,6 +1233,8 @@ void __exit sco_exit(void) debugfs_remove(sco_debugfs); + hci_unregister_cb(&sco_cb); + bt_sock_unregister(BTPROTO_SCO); proto_unregister(&sco_proto); -- cgit v1.2.3 From 3a6d576be9fe02b0c3ffa89ef6eac048e14eec84 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Wed, 18 Feb 2015 14:53:58 +0200 Subject: Bluetooth: Convert disconn_cfm to be triggered through hci_cb This patch moves all the disconn_cfm callbacks to be based on the hci_cb list. This means making l2cap_disconn_cfm private to l2cap_core.c and sco_conn_cb private to sco.c respectively. Since the hci_conn type filtering isn't done any more on the wrapper level the callbacks themselves need to check that they were passed a relevant type of connection. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- include/net/bluetooth/hci_core.h | 44 +++++++++++++++------------------------- net/bluetooth/hci_conn.c | 2 +- net/bluetooth/hci_event.c | 2 +- net/bluetooth/l2cap_core.c | 6 +++++- net/bluetooth/sco.c | 6 +++++- 5 files changed, 28 insertions(+), 32 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 0f00f0e9f257..a7bf77384464 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -504,11 +504,9 @@ extern struct mutex hci_cb_list_lock; /* ----- HCI interface to upper protocols ----- */ int l2cap_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr); int l2cap_disconn_ind(struct hci_conn *hcon); -void l2cap_disconn_cfm(struct hci_conn *hcon, u8 reason); int l2cap_recv_acldata(struct hci_conn *hcon, struct sk_buff *skb, u16 flags); int sco_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 *flags); -void sco_disconn_cfm(struct hci_conn *hcon, __u8 reason); int sco_recv_scodata(struct hci_conn *hcon, struct sk_buff *skb); /* ----- Inquiry cache ----- */ @@ -1056,32 +1054,6 @@ static inline int hci_proto_disconn_ind(struct hci_conn *conn) return l2cap_disconn_ind(conn); } -static inline void hci_proto_disconn_cfm(struct hci_conn *conn, __u8 reason) -{ - switch (conn->type) { - case ACL_LINK: - case LE_LINK: - l2cap_disconn_cfm(conn, reason); - break; - - case SCO_LINK: - case ESCO_LINK: - sco_disconn_cfm(conn, reason); - break; - - /* L2CAP would be handled for BREDR chan */ - case AMP_LINK: - break; - - default: - BT_ERR("unknown link type %d", conn->type); - break; - } - - if (conn->disconn_cfm_cb) - conn->disconn_cfm_cb(conn, reason); -} - /* ----- HCI callbacks ----- */ struct hci_cb { struct list_head list; @@ -1089,6 +1061,7 @@ struct hci_cb { char *name; void (*connect_cfm) (struct hci_conn *conn, __u8 status); + void (*disconn_cfm) (struct hci_conn *conn, __u8 status); void (*security_cfm) (struct hci_conn *conn, __u8 status, __u8 encrypt); void (*key_change_cfm) (struct hci_conn *conn, __u8 status); @@ -1110,6 +1083,21 @@ static inline void hci_connect_cfm(struct hci_conn *conn, __u8 status) conn->connect_cfm_cb(conn, status); } +static inline void hci_disconn_cfm(struct hci_conn *conn, __u8 reason) +{ + struct hci_cb *cb; + + mutex_lock(&hci_cb_list_lock); + list_for_each_entry(cb, &hci_cb_list, list) { + if (cb->disconn_cfm) + cb->disconn_cfm(conn, reason); + } + mutex_unlock(&hci_cb_list_lock); + + if (conn->disconn_cfm_cb) + conn->disconn_cfm_cb(conn, reason); +} + static inline void hci_auth_cfm(struct hci_conn *conn, __u8 status) { struct hci_cb *cb; diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c index e9206734e024..91ebb9cb31de 100644 --- a/net/bluetooth/hci_conn.c +++ b/net/bluetooth/hci_conn.c @@ -1151,7 +1151,7 @@ void hci_conn_hash_flush(struct hci_dev *hdev) list_for_each_entry_safe(c, n, &h->list, list) { c->state = BT_CLOSED; - hci_proto_disconn_cfm(c, HCI_ERROR_LOCAL_HOST_TERM); + hci_disconn_cfm(c, HCI_ERROR_LOCAL_HOST_TERM); hci_conn_del(c); } } diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 0b599129c64c..e9b17b585ee8 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -2444,7 +2444,7 @@ static void hci_disconn_complete_evt(struct hci_dev *hdev, struct sk_buff *skb) type = conn->type; - hci_proto_disconn_cfm(conn, ev->reason); + hci_disconn_cfm(conn, ev->reason); hci_conn_del(conn); /* Re-enable advertising if necessary, since it might diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index 6e2c3bdda7d3..91c682846bcf 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -7324,8 +7324,11 @@ int l2cap_disconn_ind(struct hci_conn *hcon) return conn->disc_reason; } -void l2cap_disconn_cfm(struct hci_conn *hcon, u8 reason) +static void l2cap_disconn_cfm(struct hci_conn *hcon, u8 reason) { + if (hcon->type != ACL_LINK && hcon->type != LE_LINK) + return; + BT_DBG("hcon %p reason %d", hcon, reason); l2cap_conn_del(hcon, bt_to_errno(reason)); @@ -7547,6 +7550,7 @@ drop: static struct hci_cb l2cap_cb = { .name = "L2CAP", .connect_cfm = l2cap_connect_cfm, + .disconn_cfm = l2cap_disconn_cfm, .security_cfm = l2cap_security_cfm, }; diff --git a/net/bluetooth/sco.c b/net/bluetooth/sco.c index 3c2e36f94b65..b94c3151896e 100644 --- a/net/bluetooth/sco.c +++ b/net/bluetooth/sco.c @@ -1100,8 +1100,11 @@ static void sco_connect_cfm(struct hci_conn *hcon, __u8 status) sco_conn_del(hcon, bt_to_errno(status)); } -void sco_disconn_cfm(struct hci_conn *hcon, __u8 reason) +static void sco_disconn_cfm(struct hci_conn *hcon, __u8 reason) { + if (hcon->type != SCO_LINK && hcon->type != ESCO_LINK) + return; + BT_DBG("hcon %p reason %d", hcon, reason); sco_conn_del(hcon, bt_to_errno(reason)); @@ -1129,6 +1132,7 @@ drop: static struct hci_cb sco_cb = { .name = "SCO", .connect_cfm = sco_connect_cfm, + .disconn_cfm = sco_disconn_cfm, }; static int sco_debugfs_show(struct seq_file *f, void *p) -- cgit v1.2.3 From 229d043096ea8e58829d37d35767afeac15997f5 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Fri, 13 Feb 2015 15:14:03 -0600 Subject: ALSA: core: selection of audio_tstamp type and accuracy reports Audio timestamps can be extracted from sample counters, wall clocks, PHC clocks (Ethernet AVB), on-demand synchronized snapshots. This patch provides the ability to report timestamping capabilities, select timestamp types and retrieve timestamp accuracy, if supported. Details can be found in Documentations/sound/alsa/timestamping.txt This functionality is introduced by reclaiming the reserved_aligned field introduced by commit9c7066aef4a5eb8e4063de28f06c508bf6f2963a in snd_pcm_status to provide userspace with selection/query capabilities. Additional driver_tstamp and audio_tstamp_accuracy fields are also added. snd_pcm_mmap_status remains a read-only structure with only the audio timestamp value accessible from user space. The selection of audio timestamp type is done through snd_pcm_status only This commit does not impact ABI and does not impact the default behavior. By default audio timestamp is aligned with hw_pointer and reports the DMA position. Backwards compatibility is handled by using the HDAudio wall clock for playback and the hw_ptr for all other cases. For timestamp selection a new STATUS_EXT ioctl is introduced with read/write parameters. Alsa-lib will be modified to make use of STATUS_EXT. Signed-off-by: Pierre-Louis Bossart Signed-off-by: Takashi Iwai --- Documentation/sound/alsa/timestamping.txt | 200 ++++++++++++++++++++++++++++++ include/sound/pcm.h | 60 +++++++++ include/uapi/sound/asound.h | 34 ++++- 3 files changed, 290 insertions(+), 4 deletions(-) create mode 100644 Documentation/sound/alsa/timestamping.txt (limited to 'include') diff --git a/Documentation/sound/alsa/timestamping.txt b/Documentation/sound/alsa/timestamping.txt new file mode 100644 index 000000000000..0b191a23f534 --- /dev/null +++ b/Documentation/sound/alsa/timestamping.txt @@ -0,0 +1,200 @@ +The ALSA API can provide two different system timestamps: + +- Trigger_tstamp is the system time snapshot taken when the .trigger +callback is invoked. This snapshot is taken by the ALSA core in the +general case, but specific hardware may have synchronization +capabilities or conversely may only be able to provide a correct +estimate with a delay. In the latter two cases, the low-level driver +is responsible for updating the trigger_tstamp at the most appropriate +and precise moment. Applications should not rely solely on the first +trigger_tstamp but update their internal calculations if the driver +provides a refined estimate with a delay. + +- tstamp is the current system timestamp updated during the last +event or application query. +The difference (tstamp - trigger_tstamp) defines the elapsed time. + +The ALSA API provides reports two basic pieces of information, avail +and delay, which combined with the trigger and current system +timestamps allow for applications to keep track of the 'fullness' of +the ring buffer and the amount of queued samples. + +The use of these different pointers and time information depends on +the application needs: + +- 'avail' reports how much can be written in the ring buffer +- 'delay' reports the time it will take to hear a new sample after all +queued samples have been played out. + +When timestamps are enabled, the avail/delay information is reported +along with a snapshot of system time. Applications can select from +CLOCK_REALTIME (NTP corrections including going backwards), +CLOCK_MONOTONIC (NTP corrections but never going backwards), +CLOCK_MONOTIC_RAW (without NTP corrections) and change the mode +dynamically with sw_params + + +The ALSA API also provide an audio_tstamp which reflects the passage +of time as measured by different components of audio hardware. In +ascii-art, this could be represented as follows (for the playback +case): + + +--------------------------------------------------------------> time + ^ ^ ^ ^ ^ + | | | | | + analog link dma app FullBuffer + time time time time time + | | | | | + |< codec delay >|<--hw delay-->||<---avail->| + |<----------------- delay---------------------->| | + |<----ring buffer length---->| + +The analog time is taken at the last stage of the playback, as close +as possible to the actual transducer + +The link time is taken at the output of the SOC/chipset as the samples +are pushed on a link. The link time can be directly measured if +supported in hardware by sample counters or wallclocks (e.g. with +HDAudio 24MHz or PTP clock for networked solutions) or indirectly +estimated (e.g. with the frame counter in USB). + +The DMA time is measured using counters - typically the least reliable +of all measurements due to the bursty natured of DMA transfers. + +The app time corresponds to the time tracked by an application after +writing in the ring buffer. + +The application can query what the hardware supports, define which +audio time it wants reported by selecting the relevant settings in +audio_tstamp_config fields, get an estimate of the timestamp +accuracy. It can also request the delay-to-analog be included in the +measurement. Direct access to the link time is very interesting on +platforms that provide an embedded DSP; measuring directly the link +time with dedicated hardware, possibly synchronized with system time, +removes the need to keep track of internal DSP processing times and +latency. + +In case the application requests an audio tstamp that is not supported +in hardware/low-level driver, the type is overridden as DEFAULT and the +timestamp will report the DMA time based on the hw_pointer value. + +For backwards compatibility with previous implementations that did not +provide timestamp selection, with a zero-valued COMPAT timestamp type +the results will default to the HDAudio wall clock for playback +streams and to the DMA time (hw_ptr) in all other cases. + +The audio timestamp accuracy can be returned to user-space, so that +appropriate decisions are made: + +- for dma time (default), the granularity of the transfers can be + inferred from the steps between updates and in turn provide + information on how much the application pointer can be rewound + safely. + +- the link time can be used to track long-term drifts between audio + and system time using the (tstamp-trigger_tstamp)/audio_tstamp + ratio, the precision helps define how much smoothing/low-pass + filtering is required. The link time can be either reset on startup + or reported as is (the latter being useful to compare progress of + different streams - but may require the wallclock to be always + running and not wrap-around during idle periods). If supported in + hardware, the absolute link time could also be used to define a + precise start time (patches WIP) + +- including the delay in the audio timestamp may + counter-intuitively not increase the precision of timestamps, e.g. if a + codec includes variable-latency DSP processing or a chain of + hardware components the delay is typically not known with precision. + +The accuracy is reported in nanosecond units (using an unsigned 32-bit +word), which gives a max precision of 4.29s, more than enough for +audio applications... + +Due to the varied nature of timestamping needs, even for a single +application, the audio_tstamp_config can be changed dynamically. In +the STATUS ioctl, the parameters are read-only and do not allow for +any application selection. To work around this limitation without +impacting legacy applications, a new STATUS_EXT ioctl is introduced +with read/write parameters. ALSA-lib will be modified to make use of +STATUS_EXT and effectively deprecate STATUS. + +The ALSA API only allows for a single audio timestamp to be reported +at a time. This is a conscious design decision, reading the audio +timestamps from hardware registers or from IPC takes time, the more +timestamps are read the more imprecise the combined measurements +are. To avoid any interpretation issues, a single (system, audio) +timestamp is reported. Applications that need different timestamps +will be required to issue multiple queries and perform an +interpolation of the results + +In some hardware-specific configuration, the system timestamp is +latched by a low-level audio subsytem, and the information provided +back to the driver. Due to potential delays in the communication with +the hardware, there is a risk of misalignment with the avail and delay +information. To make sure applications are not confused, a +driver_timestamp field is added in the snd_pcm_status structure; this +timestamp shows when the information is put together by the driver +before returning from the STATUS and STATUS_EXT ioctl. in most cases +this driver_timestamp will be identical to the regular system tstamp. + +Examples of typestamping with HDaudio: + +1. DMA timestamp, no compensation for DMA+analog delay +$ ./audio_time -p --ts_type=1 +playback: systime: 341121338 nsec, audio time 342000000 nsec, systime delta -878662 +playback: systime: 426236663 nsec, audio time 427187500 nsec, systime delta -950837 +playback: systime: 597080580 nsec, audio time 598000000 nsec, systime delta -919420 +playback: systime: 682059782 nsec, audio time 683020833 nsec, systime delta -961051 +playback: systime: 852896415 nsec, audio time 853854166 nsec, systime delta -957751 +playback: systime: 937903344 nsec, audio time 938854166 nsec, systime delta -950822 + +2. DMA timestamp, compensation for DMA+analog delay +$ ./audio_time -p --ts_type=1 -d +playback: systime: 341053347 nsec, audio time 341062500 nsec, systime delta -9153 +playback: systime: 426072447 nsec, audio time 426062500 nsec, systime delta 9947 +playback: systime: 596899518 nsec, audio time 596895833 nsec, systime delta 3685 +playback: systime: 681915317 nsec, audio time 681916666 nsec, systime delta -1349 +playback: systime: 852741306 nsec, audio time 852750000 nsec, systime delta -8694 + +3. link timestamp, compensation for DMA+analog delay +$ ./audio_time -p --ts_type=2 -d +playback: systime: 341060004 nsec, audio time 341062791 nsec, systime delta -2787 +playback: systime: 426242074 nsec, audio time 426244875 nsec, systime delta -2801 +playback: systime: 597080992 nsec, audio time 597084583 nsec, systime delta -3591 +playback: systime: 682084512 nsec, audio time 682088291 nsec, systime delta -3779 +playback: systime: 852936229 nsec, audio time 852940916 nsec, systime delta -4687 +playback: systime: 938107562 nsec, audio time 938112708 nsec, systime delta -5146 + +Example 1 shows that the timestamp at the DMA level is close to 1ms +ahead of the actual playback time (as a side time this sort of +measurement can help define rewind safeguards). Compensating for the +DMA-link delay in example 2 helps remove the hardware buffering abut +the information is still very jittery, with up to one sample of +error. In example 3 where the timestamps are measured with the link +wallclock, the timestamps show a monotonic behavior and a lower +dispersion. + +Example 3 and 4 are with USB audio class. Example 3 shows a high +offset between audio time and system time due to buffering. Example 4 +shows how compensating for the delay exposes a 1ms accuracy (due to +the use of the frame counter by the driver) + +Example 3: DMA timestamp, no compensation for delay, delta of ~5ms +$ ./audio_time -p -Dhw:1 -t1 +playback: systime: 120174019 nsec, audio time 125000000 nsec, systime delta -4825981 +playback: systime: 245041136 nsec, audio time 250000000 nsec, systime delta -4958864 +playback: systime: 370106088 nsec, audio time 375000000 nsec, systime delta -4893912 +playback: systime: 495040065 nsec, audio time 500000000 nsec, systime delta -4959935 +playback: systime: 620038179 nsec, audio time 625000000 nsec, systime delta -4961821 +playback: systime: 745087741 nsec, audio time 750000000 nsec, systime delta -4912259 +playback: systime: 870037336 nsec, audio time 875000000 nsec, systime delta -4962664 + +Example 4: DMA timestamp, compensation for delay, delay of ~1ms +$ ./audio_time -p -Dhw:1 -t1 -d +playback: systime: 120190520 nsec, audio time 120000000 nsec, systime delta 190520 +playback: systime: 245036740 nsec, audio time 244000000 nsec, systime delta 1036740 +playback: systime: 370034081 nsec, audio time 369000000 nsec, systime delta 1034081 +playback: systime: 495159907 nsec, audio time 494000000 nsec, systime delta 1159907 +playback: systime: 620098824 nsec, audio time 619000000 nsec, systime delta 1098824 +playback: systime: 745031847 nsec, audio time 744000000 nsec, systime delta 1031847 diff --git a/include/sound/pcm.h b/include/sound/pcm.h index c0ddb7e69c28..60f0e48f7905 100644 --- a/include/sound/pcm.h +++ b/include/sound/pcm.h @@ -60,6 +60,9 @@ struct snd_pcm_hardware { struct snd_pcm_substream; +struct snd_pcm_audio_tstamp_config; /* definitions further down */ +struct snd_pcm_audio_tstamp_report; + struct snd_pcm_ops { int (*open)(struct snd_pcm_substream *substream); int (*close)(struct snd_pcm_substream *substream); @@ -281,6 +284,58 @@ struct snd_pcm_hw_constraint_ranges { struct snd_pcm_hwptr_log; +/* + * userspace-provided audio timestamp config to kernel, + * structure is for internal use only and filled with dedicated unpack routine + */ +struct snd_pcm_audio_tstamp_config { + /* 5 of max 16 bits used */ + u32 type_requested:4; + u32 report_delay:1; /* add total delay to A/D or D/A */ +}; + +static inline void snd_pcm_unpack_audio_tstamp_config(__u32 data, + struct snd_pcm_audio_tstamp_config *config) +{ + config->type_requested = data & 0xF; + config->report_delay = (data >> 4) & 1; +} + +/* + * kernel-provided audio timestamp report to user-space + * structure is for internal use only and read by dedicated pack routine + */ +struct snd_pcm_audio_tstamp_report { + /* 6 of max 16 bits used for bit-fields */ + + /* for backwards compatibility */ + u32 valid:1; + + /* actual type if hardware could not support requested timestamp */ + u32 actual_type:4; + + /* accuracy represented in ns units */ + u32 accuracy_report:1; /* 0 if accuracy unknown, 1 if accuracy field is valid */ + u32 accuracy; /* up to 4.29s, will be packed in separate field */ +}; + +static inline void snd_pcm_pack_audio_tstamp_report(__u32 *data, __u32 *accuracy, + const struct snd_pcm_audio_tstamp_report *report) +{ + u32 tmp; + + tmp = report->accuracy_report; + tmp <<= 4; + tmp |= report->actual_type; + tmp <<= 1; + tmp |= report->valid; + + *data &= 0xffff; /* zero-clear MSBs */ + *data |= (tmp << 16); + *accuracy = report->accuracy; +} + + struct snd_pcm_runtime { /* -- Status -- */ struct snd_pcm_substream *trigger_master; @@ -361,6 +416,11 @@ struct snd_pcm_runtime { struct snd_dma_buffer *dma_buffer_p; /* allocated buffer */ + /* -- audio timestamp config -- */ + struct snd_pcm_audio_tstamp_config audio_tstamp_config; + struct snd_pcm_audio_tstamp_report audio_tstamp_report; + struct timespec driver_tstamp; + #if defined(CONFIG_SND_PCM_OSS) || defined(CONFIG_SND_PCM_OSS_MODULE) /* -- OSS things -- */ struct snd_pcm_oss_runtime oss; diff --git a/include/uapi/sound/asound.h b/include/uapi/sound/asound.h index 0e88e7a0f0eb..acef4e4d2735 100644 --- a/include/uapi/sound/asound.h +++ b/include/uapi/sound/asound.h @@ -267,10 +267,17 @@ typedef int __bitwise snd_pcm_subformat_t; #define SNDRV_PCM_INFO_JOINT_DUPLEX 0x00200000 /* playback and capture stream are somewhat correlated */ #define SNDRV_PCM_INFO_SYNC_START 0x00400000 /* pcm support some kind of sync go */ #define SNDRV_PCM_INFO_NO_PERIOD_WAKEUP 0x00800000 /* period wakeup can be disabled */ -#define SNDRV_PCM_INFO_HAS_WALL_CLOCK 0x01000000 /* has audio wall clock for audio/system time sync */ +#define SNDRV_PCM_INFO_HAS_WALL_CLOCK 0x01000000 /* (Deprecated)has audio wall clock for audio/system time sync */ +#define SNDRV_PCM_INFO_HAS_LINK_ATIME 0x01000000 /* report hardware link audio time, reset on startup */ +#define SNDRV_PCM_INFO_HAS_LINK_ABSOLUTE_ATIME 0x02000000 /* report absolute hardware link audio time, not reset on startup */ +#define SNDRV_PCM_INFO_HAS_LINK_ESTIMATED_ATIME 0x04000000 /* report estimated link audio time */ +#define SNDRV_PCM_INFO_HAS_LINK_SYNCHRONIZED_ATIME 0x08000000 /* report synchronized audio/system time */ + #define SNDRV_PCM_INFO_DRAIN_TRIGGER 0x40000000 /* internal kernel flag - trigger in drain */ #define SNDRV_PCM_INFO_FIFO_IN_FRAMES 0x80000000 /* internal kernel flag - FIFO size is in frames */ + + typedef int __bitwise snd_pcm_state_t; #define SNDRV_PCM_STATE_OPEN ((__force snd_pcm_state_t) 0) /* stream is open */ #define SNDRV_PCM_STATE_SETUP ((__force snd_pcm_state_t) 1) /* stream has a setup */ @@ -408,6 +415,22 @@ struct snd_pcm_channel_info { unsigned int step; /* samples distance in bits */ }; +enum { + /* + * first definition for backwards compatibility only, + * maps to wallclock/link time for HDAudio playback and DEFAULT/DMA time for everything else + */ + SNDRV_PCM_AUDIO_TSTAMP_TYPE_COMPAT = 0, + + /* timestamp definitions */ + SNDRV_PCM_AUDIO_TSTAMP_TYPE_DEFAULT = 1, /* DMA time, reported as per hw_ptr */ + SNDRV_PCM_AUDIO_TSTAMP_TYPE_LINK = 2, /* link time reported by sample or wallclock counter, reset on startup */ + SNDRV_PCM_AUDIO_TSTAMP_TYPE_LINK_ABSOLUTE = 3, /* link time reported by sample or wallclock counter, not reset on startup */ + SNDRV_PCM_AUDIO_TSTAMP_TYPE_LINK_ESTIMATED = 4, /* link time estimated indirectly */ + SNDRV_PCM_AUDIO_TSTAMP_TYPE_LINK_SYNCHRONIZED = 5, /* link time synchronized with system time */ + SNDRV_PCM_AUDIO_TSTAMP_TYPE_LAST = SNDRV_PCM_AUDIO_TSTAMP_TYPE_LINK_SYNCHRONIZED +}; + struct snd_pcm_status { snd_pcm_state_t state; /* stream state */ struct timespec trigger_tstamp; /* time when stream was started/stopped/paused */ @@ -419,9 +442,11 @@ struct snd_pcm_status { snd_pcm_uframes_t avail_max; /* max frames available on hw since last status */ snd_pcm_uframes_t overrange; /* count of ADC (capture) overrange detections from last status */ snd_pcm_state_t suspended_state; /* suspended stream state */ - __u32 reserved_alignment; /* must be filled with zero */ - struct timespec audio_tstamp; /* from sample counter or wall clock */ - unsigned char reserved[56-sizeof(struct timespec)]; /* must be filled with zero */ + __u32 audio_tstamp_data; /* needed for 64-bit alignment, used for configs/report to/from userspace */ + struct timespec audio_tstamp; /* sample counter, wall clock, PHC or on-demand sync'ed */ + struct timespec driver_tstamp; /* useful in case reference system tstamp is reported with delay */ + __u32 audio_tstamp_accuracy; /* in ns units, only valid if indicated in audio_tstamp_data */ + unsigned char reserved[52-2*sizeof(struct timespec)]; /* must be filled with zero */ }; struct snd_pcm_mmap_status { @@ -534,6 +559,7 @@ enum { #define SNDRV_PCM_IOCTL_DELAY _IOR('A', 0x21, snd_pcm_sframes_t) #define SNDRV_PCM_IOCTL_HWSYNC _IO('A', 0x22) #define SNDRV_PCM_IOCTL_SYNC_PTR _IOWR('A', 0x23, struct snd_pcm_sync_ptr) +#define SNDRV_PCM_IOCTL_STATUS_EXT _IOWR('A', 0x24, struct snd_pcm_status) #define SNDRV_PCM_IOCTL_CHANNEL_INFO _IOR('A', 0x32, struct snd_pcm_channel_info) #define SNDRV_PCM_IOCTL_PREPARE _IO('A', 0x40) #define SNDRV_PCM_IOCTL_RESET _IO('A', 0x41) -- cgit v1.2.3 From 3179f62001880e588e229db3006a59ad87b7792a Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Fri, 13 Feb 2015 15:14:06 -0600 Subject: ALSA: core: add .get_time_info Introduce more generic .get_time_info to retrieve system timestamp and audio timestamp in single routine. Backwards compatibility is preserved with same functionality as with .wall_clock method (to be removed in following commits to avoid breaking git bisect) Signed-off-by: Pierre-Louis Bossart Signed-off-by: Takashi Iwai --- include/sound/pcm.h | 4 +++ sound/core/pcm_lib.c | 88 +++++++++++++++++++++++++++++++++---------------- sound/core/pcm_native.c | 24 ++++++++++++++ 3 files changed, 87 insertions(+), 29 deletions(-) (limited to 'include') diff --git a/include/sound/pcm.h b/include/sound/pcm.h index 60f0e48f7905..04f2d492ae57 100644 --- a/include/sound/pcm.h +++ b/include/sound/pcm.h @@ -76,6 +76,10 @@ struct snd_pcm_ops { snd_pcm_uframes_t (*pointer)(struct snd_pcm_substream *substream); int (*wall_clock)(struct snd_pcm_substream *substream, struct timespec *audio_ts); + int (*get_time_info)(struct snd_pcm_substream *substream, + struct timespec *system_ts, struct timespec *audio_ts, + struct snd_pcm_audio_tstamp_config *audio_tstamp_config, + struct snd_pcm_audio_tstamp_report *audio_tstamp_report); int (*copy)(struct snd_pcm_substream *substream, int channel, snd_pcm_uframes_t pos, void __user *buf, snd_pcm_uframes_t count); diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c index ffd656012ab8..ac6b33f3779c 100644 --- a/sound/core/pcm_lib.c +++ b/sound/core/pcm_lib.c @@ -232,6 +232,49 @@ int snd_pcm_update_state(struct snd_pcm_substream *substream, return 0; } +static void update_audio_tstamp(struct snd_pcm_substream *substream, + struct timespec *curr_tstamp, + struct timespec *audio_tstamp) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + u64 audio_frames, audio_nsecs; + struct timespec driver_tstamp; + + if (runtime->tstamp_mode != SNDRV_PCM_TSTAMP_ENABLE) + return; + + if (!(substream->ops->get_time_info) || + (runtime->audio_tstamp_report.actual_type == + SNDRV_PCM_AUDIO_TSTAMP_TYPE_DEFAULT)) { + + /* + * provide audio timestamp derived from pointer position + * add delay only if requested + */ + + audio_frames = runtime->hw_ptr_wrap + runtime->status->hw_ptr; + + if (runtime->audio_tstamp_config.report_delay) { + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + audio_frames -= runtime->delay; + else + audio_frames += runtime->delay; + } + audio_nsecs = div_u64(audio_frames * 1000000000LL, + runtime->rate); + *audio_tstamp = ns_to_timespec(audio_nsecs); + } + runtime->status->audio_tstamp = *audio_tstamp; + runtime->status->tstamp = *curr_tstamp; + + /* + * re-take a driver timestamp to let apps detect if the reference tstamp + * read by low-level hardware was provided with a delay + */ + snd_pcm_gettime(substream->runtime, (struct timespec *)&driver_tstamp); + runtime->driver_tstamp = driver_tstamp; +} + static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream, unsigned int in_interrupt) { @@ -256,11 +299,18 @@ static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream, pos = substream->ops->pointer(substream); curr_jiffies = jiffies; if (runtime->tstamp_mode == SNDRV_PCM_TSTAMP_ENABLE) { - snd_pcm_gettime(runtime, (struct timespec *)&curr_tstamp); - - if ((runtime->hw.info & SNDRV_PCM_INFO_HAS_WALL_CLOCK) && - (substream->ops->wall_clock)) - substream->ops->wall_clock(substream, &audio_tstamp); + if ((substream->ops->get_time_info) && + (runtime->audio_tstamp_config.type_requested != SNDRV_PCM_AUDIO_TSTAMP_TYPE_DEFAULT)) { + substream->ops->get_time_info(substream, &curr_tstamp, + &audio_tstamp, + &runtime->audio_tstamp_config, + &runtime->audio_tstamp_report); + + /* re-test in case tstamp type is not supported in hardware and was demoted to DEFAULT */ + if (runtime->audio_tstamp_report.actual_type == SNDRV_PCM_AUDIO_TSTAMP_TYPE_DEFAULT) + snd_pcm_gettime(runtime, (struct timespec *)&curr_tstamp); + } else + snd_pcm_gettime(runtime, (struct timespec *)&curr_tstamp); } if (pos == SNDRV_PCM_POS_XRUN) { @@ -403,8 +453,10 @@ static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream, } no_delta_check: - if (runtime->status->hw_ptr == new_hw_ptr) + if (runtime->status->hw_ptr == new_hw_ptr) { + update_audio_tstamp(substream, &curr_tstamp, &audio_tstamp); return 0; + } if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK && runtime->silence_size > 0) @@ -426,30 +478,8 @@ static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream, snd_BUG_ON(crossed_boundary != 1); runtime->hw_ptr_wrap += runtime->boundary; } - if (runtime->tstamp_mode == SNDRV_PCM_TSTAMP_ENABLE) { - runtime->status->tstamp = curr_tstamp; - if (!(runtime->hw.info & SNDRV_PCM_INFO_HAS_WALL_CLOCK)) { - /* - * no wall clock available, provide audio timestamp - * derived from pointer position+delay - */ - u64 audio_frames, audio_nsecs; - - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - audio_frames = runtime->hw_ptr_wrap - + runtime->status->hw_ptr - - runtime->delay; - else - audio_frames = runtime->hw_ptr_wrap - + runtime->status->hw_ptr - + runtime->delay; - audio_nsecs = div_u64(audio_frames * 1000000000LL, - runtime->rate); - audio_tstamp = ns_to_timespec(audio_nsecs); - } - runtime->status->audio_tstamp = audio_tstamp; - } + update_audio_tstamp(substream, &curr_tstamp, &audio_tstamp); return snd_pcm_update_state(substream, runtime); } diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c index 72323a8c35c8..9c2c6f801fed 100644 --- a/sound/core/pcm_native.c +++ b/sound/core/pcm_native.c @@ -707,6 +707,23 @@ int snd_pcm_status(struct snd_pcm_substream *substream, struct snd_pcm_runtime *runtime = substream->runtime; snd_pcm_stream_lock_irq(substream); + + snd_pcm_unpack_audio_tstamp_config(status->audio_tstamp_data, + &runtime->audio_tstamp_config); + + /* backwards compatible behavior */ + if (runtime->audio_tstamp_config.type_requested == + SNDRV_PCM_AUDIO_TSTAMP_TYPE_COMPAT) { + if (runtime->hw.info & SNDRV_PCM_INFO_HAS_WALL_CLOCK) + runtime->audio_tstamp_config.type_requested = + SNDRV_PCM_AUDIO_TSTAMP_TYPE_LINK; + else + runtime->audio_tstamp_config.type_requested = + SNDRV_PCM_AUDIO_TSTAMP_TYPE_DEFAULT; + runtime->audio_tstamp_report.valid = 0; + } else + runtime->audio_tstamp_report.valid = 1; + status->state = runtime->status->state; status->suspended_state = runtime->status->suspended_state; if (status->state == SNDRV_PCM_STATE_OPEN) @@ -716,8 +733,15 @@ int snd_pcm_status(struct snd_pcm_substream *substream, snd_pcm_update_hw_ptr(substream); if (runtime->tstamp_mode == SNDRV_PCM_TSTAMP_ENABLE) { status->tstamp = runtime->status->tstamp; + status->driver_tstamp = runtime->driver_tstamp; status->audio_tstamp = runtime->status->audio_tstamp; + if (runtime->audio_tstamp_report.valid == 1) + /* backwards compatibility, no report provided in COMPAT mode */ + snd_pcm_pack_audio_tstamp_report(&status->audio_tstamp_data, + &status->audio_tstamp_accuracy, + &runtime->audio_tstamp_report); + goto _tstamp_end; } } else { -- cgit v1.2.3 From 2d52a5abd5be35340296a251092c8a594679df54 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Fri, 13 Feb 2015 15:14:08 -0600 Subject: ALSA: core: remove .wall_clock can be removed without breaking git-bisect now Signed-off-by: Pierre-Louis Bossart Signed-off-by: Takashi Iwai --- include/sound/pcm.h | 2 -- 1 file changed, 2 deletions(-) (limited to 'include') diff --git a/include/sound/pcm.h b/include/sound/pcm.h index 04f2d492ae57..0cb7f3f5df7b 100644 --- a/include/sound/pcm.h +++ b/include/sound/pcm.h @@ -74,8 +74,6 @@ struct snd_pcm_ops { int (*prepare)(struct snd_pcm_substream *substream); int (*trigger)(struct snd_pcm_substream *substream, int cmd); snd_pcm_uframes_t (*pointer)(struct snd_pcm_substream *substream); - int (*wall_clock)(struct snd_pcm_substream *substream, - struct timespec *audio_ts); int (*get_time_info)(struct snd_pcm_substream *substream, struct timespec *system_ts, struct timespec *audio_ts, struct snd_pcm_audio_tstamp_config *audio_tstamp_config, -- cgit v1.2.3 From c72638bdaabe9ea4b09003b9db7e1754f472fbed Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Fri, 13 Feb 2015 15:14:09 -0600 Subject: ALSA: bump PCM protocol to 2.0.13 Bump PCM protocol to enable use of STATUS_EXT ioctls, older apps will still use STATUS and audio timestamp configuration is not supported (backwards compatible behavior). Signed-off-by: Pierre-Louis Bossart Signed-off-by: Takashi Iwai --- include/uapi/sound/asound.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/uapi/sound/asound.h b/include/uapi/sound/asound.h index acef4e4d2735..3d46e9a0cd2e 100644 --- a/include/uapi/sound/asound.h +++ b/include/uapi/sound/asound.h @@ -140,7 +140,7 @@ struct snd_hwdep_dsp_image { * * *****************************************************************************/ -#define SNDRV_PCM_VERSION SNDRV_PROTOCOL_VERSION(2, 0, 12) +#define SNDRV_PCM_VERSION SNDRV_PROTOCOL_VERSION(2, 0, 13) typedef unsigned long snd_pcm_uframes_t; typedef signed long snd_pcm_sframes_t; -- cgit v1.2.3 From 7129069e84056ba28954550beb208b2645863299 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Fri, 20 Feb 2015 13:26:23 +0200 Subject: Bluetooth: Rename hci_send_to_control to hci_send_to_channel The hci_send_to_control() can be made more general purpose with a small change of passing the desired HCI channel as a parameter to it. This allows using it for the monitor channel as well as e.g. 6lowpan in the future. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- include/net/bluetooth/hci_core.h | 3 ++- net/bluetooth/hci_sock.c | 9 +++++---- net/bluetooth/mgmt.c | 3 ++- 3 files changed, 9 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index a7bf77384464..a831c8ad10f1 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -1265,7 +1265,8 @@ void *hci_sent_cmd_data(struct hci_dev *hdev, __u16 opcode); /* ----- HCI Sockets ----- */ void hci_send_to_sock(struct hci_dev *hdev, struct sk_buff *skb); -void hci_send_to_control(struct sk_buff *skb, struct sock *skip_sk); +void hci_send_to_channel(unsigned short channel, struct sk_buff *skb, + struct sock *skip_sk); void hci_send_to_monitor(struct hci_dev *hdev, struct sk_buff *skb); void hci_sock_dev_event(struct hci_dev *hdev, int event); diff --git a/net/bluetooth/hci_sock.c b/net/bluetooth/hci_sock.c index 1d65c5be7c82..ba5d45f8aac1 100644 --- a/net/bluetooth/hci_sock.c +++ b/net/bluetooth/hci_sock.c @@ -183,12 +183,13 @@ void hci_send_to_sock(struct hci_dev *hdev, struct sk_buff *skb) kfree_skb(skb_copy); } -/* Send frame to control socket */ -void hci_send_to_control(struct sk_buff *skb, struct sock *skip_sk) +/* Send frame to sockets with specific channel */ +void hci_send_to_channel(unsigned short channel, struct sk_buff *skb, + struct sock *skip_sk) { struct sock *sk; - BT_DBG("len %d", skb->len); + BT_DBG("channel %u len %d", channel, skb->len); read_lock(&hci_sk_list.lock); @@ -202,7 +203,7 @@ void hci_send_to_control(struct sk_buff *skb, struct sock *skip_sk) if (sk->sk_state != BT_BOUND) continue; - if (hci_pi(sk)->channel != HCI_CHANNEL_CONTROL) + if (hci_pi(sk)->channel != channel) continue; nskb = skb_clone(skb, GFP_ATOMIC); diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 3a1b537c9aa6..d5d46e7676f1 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -29,6 +29,7 @@ #include #include +#include #include #include @@ -242,7 +243,7 @@ static int mgmt_event(u16 event, struct hci_dev *hdev, void *data, u16 data_len, /* Time stamp */ __net_timestamp(skb); - hci_send_to_control(skb, skip_sk); + hci_send_to_channel(HCI_CHANNEL_CONTROL, skb, skip_sk); kfree_skb(skb); return 0; -- cgit v1.2.3 From 959d10f6bbf6ab5b8813c4e37540a2e43ca2ae96 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Tue, 17 Feb 2015 03:19:24 -0800 Subject: igmp: add __ip_mc_{join|leave}_group() There is a need to perform igmp join/leave operations while RTNL is held. Make ip_mc_{join|leave}_group() wrappers around __ip_mc_{join|leave}_group() to avoid the proliferation of work queues. For example, vxlan_igmp_join() could possibly be removed. Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- include/linux/igmp.h | 2 ++ net/ipv4/igmp.c | 52 ++++++++++++++++++++++++++++++++++------------------ 2 files changed, 36 insertions(+), 18 deletions(-) (limited to 'include') diff --git a/include/linux/igmp.h b/include/linux/igmp.h index 2c677afeea47..b5a6470e686c 100644 --- a/include/linux/igmp.h +++ b/include/linux/igmp.h @@ -111,7 +111,9 @@ struct ip_mc_list { extern int ip_check_mc_rcu(struct in_device *dev, __be32 mc_addr, __be32 src_addr, u16 proto); extern int igmp_rcv(struct sk_buff *); +extern int __ip_mc_join_group(struct sock *sk, struct ip_mreqn *imr); extern int ip_mc_join_group(struct sock *sk, struct ip_mreqn *imr); +extern int __ip_mc_leave_group(struct sock *sk, struct ip_mreqn *imr); extern int ip_mc_leave_group(struct sock *sk, struct ip_mreqn *imr); extern void ip_mc_drop_socket(struct sock *sk); extern int ip_mc_source(int add, int omode, struct sock *sk, diff --git a/net/ipv4/igmp.c b/net/ipv4/igmp.c index 666cf364df86..4b1172d73e03 100644 --- a/net/ipv4/igmp.c +++ b/net/ipv4/igmp.c @@ -1849,30 +1849,25 @@ static void ip_mc_clear_src(struct ip_mc_list *pmc) pmc->sfcount[MCAST_EXCLUDE] = 1; } - -/* - * Join a multicast group - */ -int ip_mc_join_group(struct sock *sk , struct ip_mreqn *imr) +int __ip_mc_join_group(struct sock *sk, struct ip_mreqn *imr) { - int err; __be32 addr = imr->imr_multiaddr.s_addr; - struct ip_mc_socklist *iml = NULL, *i; + struct ip_mc_socklist *iml, *i; struct in_device *in_dev; struct inet_sock *inet = inet_sk(sk); struct net *net = sock_net(sk); int ifindex; int count = 0; + int err; + + ASSERT_RTNL(); if (!ipv4_is_multicast(addr)) return -EINVAL; - rtnl_lock(); - in_dev = ip_mc_find_dev(net, imr); if (!in_dev) { - iml = NULL; err = -ENODEV; goto done; } @@ -1900,9 +1895,22 @@ int ip_mc_join_group(struct sock *sk , struct ip_mreqn *imr) ip_mc_inc_group(in_dev, addr); err = 0; done: - rtnl_unlock(); return err; } +EXPORT_SYMBOL(__ip_mc_join_group); + +/* Join a multicast group + */ +int ip_mc_join_group(struct sock *sk, struct ip_mreqn *imr) +{ + int ret; + + rtnl_lock(); + ret = __ip_mc_join_group(sk, imr); + rtnl_unlock(); + + return ret; +} EXPORT_SYMBOL(ip_mc_join_group); static int ip_mc_leave_src(struct sock *sk, struct ip_mc_socklist *iml, @@ -1925,11 +1933,7 @@ static int ip_mc_leave_src(struct sock *sk, struct ip_mc_socklist *iml, return err; } -/* - * Ask a socket to leave a group. - */ - -int ip_mc_leave_group(struct sock *sk, struct ip_mreqn *imr) +int __ip_mc_leave_group(struct sock *sk, struct ip_mreqn *imr) { struct inet_sock *inet = inet_sk(sk); struct ip_mc_socklist *iml; @@ -1940,7 +1944,8 @@ int ip_mc_leave_group(struct sock *sk, struct ip_mreqn *imr) u32 ifindex; int ret = -EADDRNOTAVAIL; - rtnl_lock(); + ASSERT_RTNL(); + in_dev = ip_mc_find_dev(net, imr); if (!in_dev) { ret = -ENODEV; @@ -1964,14 +1969,25 @@ int ip_mc_leave_group(struct sock *sk, struct ip_mreqn *imr) *imlp = iml->next_rcu; ip_mc_dec_group(in_dev, group); - rtnl_unlock(); + /* decrease mem now to avoid the memleak warning */ atomic_sub(sizeof(*iml), &sk->sk_omem_alloc); kfree_rcu(iml, rcu); return 0; } out: + return ret; +} +EXPORT_SYMBOL(__ip_mc_leave_group); + +int ip_mc_leave_group(struct sock *sk, struct ip_mreqn *imr) +{ + int ret; + + rtnl_lock(); + ret = __ip_mc_leave_group(sk, imr); rtnl_unlock(); + return ret; } EXPORT_SYMBOL(ip_mc_leave_group); -- cgit v1.2.3 From 059a2440fd3cf4ec57735db2c0a90401cde84fca Mon Sep 17 00:00:00 2001 From: Bojan Prtvar Date: Sun, 22 Feb 2015 11:46:35 +0100 Subject: net: Remove state argument from skb_find_text() Although it is clear that textsearch state is intentionally passed to skb_find_text() as uninitialized argument, it was never used by the callers. Therefore, we can simplify skb_find_text() by making it local variable. Signed-off-by: Bojan Prtvar Signed-off-by: David S. Miller --- include/linux/skbuff.h | 3 +-- net/core/skbuff.c | 9 ++++----- net/netfilter/nf_conntrack_amanda.c | 10 +++------- net/netfilter/xt_string.c | 3 +-- net/sched/em_text.c | 3 +-- 5 files changed, 10 insertions(+), 18 deletions(-) (limited to 'include') diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h index 30007afe70b3..d898b32dedcc 100644 --- a/include/linux/skbuff.h +++ b/include/linux/skbuff.h @@ -870,8 +870,7 @@ unsigned int skb_seq_read(unsigned int consumed, const u8 **data, void skb_abort_seq_read(struct skb_seq_state *st); unsigned int skb_find_text(struct sk_buff *skb, unsigned int from, - unsigned int to, struct ts_config *config, - struct ts_state *state); + unsigned int to, struct ts_config *config); /* * Packet hash types specify the type of hash in skb_set_hash. diff --git a/net/core/skbuff.c b/net/core/skbuff.c index 88c613eab142..374e43bc6b80 100644 --- a/net/core/skbuff.c +++ b/net/core/skbuff.c @@ -2865,7 +2865,6 @@ static void skb_ts_finish(struct ts_config *conf, struct ts_state *state) * @from: search offset * @to: search limit * @config: textsearch configuration - * @state: uninitialized textsearch state variable * * Finds a pattern in the skb data according to the specified * textsearch configuration. Use textsearch_next() to retrieve @@ -2873,17 +2872,17 @@ static void skb_ts_finish(struct ts_config *conf, struct ts_state *state) * to the first occurrence or UINT_MAX if no match was found. */ unsigned int skb_find_text(struct sk_buff *skb, unsigned int from, - unsigned int to, struct ts_config *config, - struct ts_state *state) + unsigned int to, struct ts_config *config) { + struct ts_state state; unsigned int ret; config->get_next_block = skb_ts_get_next_block; config->finish = skb_ts_finish; - skb_prepare_seq_read(skb, from, to, TS_SKB_CB(state)); + skb_prepare_seq_read(skb, from, to, TS_SKB_CB(&state)); - ret = textsearch_find(config, state); + ret = textsearch_find(config, &state); return (ret <= to - from ? ret : UINT_MAX); } EXPORT_SYMBOL(skb_find_text); diff --git a/net/netfilter/nf_conntrack_amanda.c b/net/netfilter/nf_conntrack_amanda.c index b8b95f4027ca..57a26cc90c9f 100644 --- a/net/netfilter/nf_conntrack_amanda.c +++ b/net/netfilter/nf_conntrack_amanda.c @@ -88,7 +88,6 @@ static int amanda_help(struct sk_buff *skb, struct nf_conn *ct, enum ip_conntrack_info ctinfo) { - struct ts_state ts; struct nf_conntrack_expect *exp; struct nf_conntrack_tuple *tuple; unsigned int dataoff, start, stop, off, i; @@ -113,23 +112,20 @@ static int amanda_help(struct sk_buff *skb, return NF_ACCEPT; } - memset(&ts, 0, sizeof(ts)); start = skb_find_text(skb, dataoff, skb->len, - search[SEARCH_CONNECT].ts, &ts); + search[SEARCH_CONNECT].ts); if (start == UINT_MAX) goto out; start += dataoff + search[SEARCH_CONNECT].len; - memset(&ts, 0, sizeof(ts)); stop = skb_find_text(skb, start, skb->len, - search[SEARCH_NEWLINE].ts, &ts); + search[SEARCH_NEWLINE].ts); if (stop == UINT_MAX) goto out; stop += start; for (i = SEARCH_DATA; i <= SEARCH_INDEX; i++) { - memset(&ts, 0, sizeof(ts)); - off = skb_find_text(skb, start, stop, search[i].ts, &ts); + off = skb_find_text(skb, start, stop, search[i].ts); if (off == UINT_MAX) continue; off += start + search[i].len; diff --git a/net/netfilter/xt_string.c b/net/netfilter/xt_string.c index 5699adb97652..0bc3460319c8 100644 --- a/net/netfilter/xt_string.c +++ b/net/netfilter/xt_string.c @@ -26,13 +26,12 @@ static bool string_mt(const struct sk_buff *skb, struct xt_action_param *par) { const struct xt_string_info *conf = par->matchinfo; - struct ts_state state; bool invert; invert = conf->u.v1.flags & XT_STRING_FLAG_INVERT; return (skb_find_text((struct sk_buff *)skb, conf->from_offset, - conf->to_offset, conf->config, &state) + conf->to_offset, conf->config) != UINT_MAX) ^ invert; } diff --git a/net/sched/em_text.c b/net/sched/em_text.c index f03c3de16c27..73e2ed576ceb 100644 --- a/net/sched/em_text.c +++ b/net/sched/em_text.c @@ -34,7 +34,6 @@ static int em_text_match(struct sk_buff *skb, struct tcf_ematch *m, { struct text_match *tm = EM_TEXT_PRIV(m); int from, to; - struct ts_state state; from = tcf_get_base_ptr(skb, tm->from_layer) - skb->data; from += tm->from_offset; @@ -42,7 +41,7 @@ static int em_text_match(struct sk_buff *skb, struct tcf_ematch *m, to = tcf_get_base_ptr(skb, tm->to_layer) - skb->data; to += tm->to_offset; - return skb_find_text(skb, from, to, tm->config, &state) != UINT_MAX; + return skb_find_text(skb, from, to, tm->config) != UINT_MAX; } static int em_text_change(struct net *net, void *data, int len, -- cgit v1.2.3 From 9625604cebcb3f5be2e692408274734e8ae63979 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Fri, 13 Feb 2015 21:03:42 +0100 Subject: drm/irq: Add drm_crtc_vblank_reset MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit At driver load we need to tell the vblank code about the state of the pipes, so that the logic around reject vblank_get when the pipe is off works correctly. Thus far i915 used drm_vblank_off, but one of the side-effects of it is that it also saves the vblank counter. And for that it calls down into the ->get_vblank_counter hook. Which isn't really a good idea when the pipe is off for a few reasons: - With runtime pm the register might not respond. - If the pipe is off some datastructures might not be around or unitialized. The later is what blew up on gen3: We look at intel_crtc->config to compute the vblank counter, and for a disabled pipe at boot-up that's just not there. Thus far this was papered over by a check for intel_crtc->active, but I want to get rid of that (since it's fairly race, vblank hooks are called from all kinds of places). So prep for that by adding a _reset functions which only does what we really need to be done at driver load: Mark the vblank pipe as off, but don't do any vblank counter saving or event flushing - neither of that is required. v2: Clarify the code flow slightly as suggested by Ville. v3: Fix kerneldoc spelling, spotted by Laurent. Cc: Ville Syrjälä Cc: Laurent Pinchart Cc: Imre Deak Reviewed-by: Imre Deak (v2) Acked-by: Dave Airlie Acked-by: Laurent Pinchart Signed-off-by: Daniel Vetter --- drivers/gpu/drm/drm_irq.c | 32 ++++++++++++++++++++++++++++++++ drivers/gpu/drm/i915/intel_display.c | 6 +++--- include/drm/drmP.h | 1 + 3 files changed, 36 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/drm_irq.c b/drivers/gpu/drm/drm_irq.c index 75647e7f012b..e78a1f5cad9c 100644 --- a/drivers/gpu/drm/drm_irq.c +++ b/drivers/gpu/drm/drm_irq.c @@ -1225,6 +1225,38 @@ void drm_crtc_vblank_off(struct drm_crtc *crtc) } EXPORT_SYMBOL(drm_crtc_vblank_off); +/** + * drm_crtc_vblank_reset - reset vblank state to off on a CRTC + * @crtc: CRTC in question + * + * Drivers can use this function to reset the vblank state to off at load time. + * Drivers should use this together with the drm_crtc_vblank_off() and + * drm_crtc_vblank_on() functions. The difference compared to + * drm_crtc_vblank_off() is that this function doesn't save the vblank counter + * and hence doesn't need to call any driver hooks. + */ +void drm_crtc_vblank_reset(struct drm_crtc *drm_crtc) +{ + struct drm_device *dev = drm_crtc->dev; + unsigned long irqflags; + int crtc = drm_crtc_index(drm_crtc); + struct drm_vblank_crtc *vblank = &dev->vblank[crtc]; + + spin_lock_irqsave(&dev->vbl_lock, irqflags); + /* + * Prevent subsequent drm_vblank_get() from enabling the vblank + * interrupt by bumping the refcount. + */ + if (!vblank->inmodeset) { + atomic_inc(&vblank->refcount); + vblank->inmodeset = 1; + } + spin_unlock_irqrestore(&dev->vbl_lock, irqflags); + + WARN_ON(!list_empty(&dev->vblank_event_list)); +} +EXPORT_SYMBOL(drm_crtc_vblank_reset); + /** * drm_vblank_on - enable vblank events on a CRTC * @dev: DRM device diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 3b0fe9f1f3c9..b901b00a8f2e 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -13316,11 +13316,11 @@ static void intel_sanitize_crtc(struct intel_crtc *crtc) I915_WRITE(reg, I915_READ(reg) & ~PIPECONF_FRAME_START_DELAY_MASK); /* restore vblank interrupts to correct state */ + drm_crtc_vblank_reset(&crtc->base); if (crtc->active) { update_scanline_offset(crtc); - drm_vblank_on(dev, crtc->pipe); - } else - drm_vblank_off(dev, crtc->pipe); + drm_crtc_vblank_on(&crtc->base); + } /* We need to sanitize the plane -> pipe mapping first because this will * disable the crtc (and hence change the state) if it is wrong. Note diff --git a/include/drm/drmP.h b/include/drm/drmP.h index e928625a9da0..54c6ea1e5866 100644 --- a/include/drm/drmP.h +++ b/include/drm/drmP.h @@ -922,6 +922,7 @@ extern void drm_crtc_wait_one_vblank(struct drm_crtc *crtc); extern void drm_vblank_off(struct drm_device *dev, int crtc); extern void drm_vblank_on(struct drm_device *dev, int crtc); extern void drm_crtc_vblank_off(struct drm_crtc *crtc); +extern void drm_crtc_vblank_reset(struct drm_crtc *crtc); extern void drm_crtc_vblank_on(struct drm_crtc *crtc); extern void drm_vblank_cleanup(struct drm_device *dev); -- cgit v1.2.3 From 498b8738485ab1a327163f771953f32cf35ca3b5 Mon Sep 17 00:00:00 2001 From: Damien Lespiau Date: Mon, 16 Feb 2015 15:12:31 +0000 Subject: drm: Fix the CRTC_STEREO_DOUBLE_ONLY define to include stero modes The CRTC_STEREO_DOUBLE_ONLY define was introduced in commit: commit ecb7e16bf187bc369cf6a5cd108582c01329980d Author: Gustavo Padovan Date: Mon Dec 1 15:40:09 2014 -0800 drm: add helper to get crtc timings (v5) but if we want the stereo h/v adjustments, we need to set the CRTC_STEREO_DOUBLE flag. Otherwise, we'll get the wrong h/v for frame packing stereo 3d modes. Cc: Gustavo Padovan Cc: Matt Roper Cc: Ander Conselvan de Oliveira Signed-off-by: Damien Lespiau Reviewed-by: Matt Roper Signed-off-by: Daniel Vetter --- include/drm/drm_modes.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/drm/drm_modes.h b/include/drm/drm_modes.h index d92f6dd1fb11..0616188c7801 100644 --- a/include/drm/drm_modes.h +++ b/include/drm/drm_modes.h @@ -92,7 +92,7 @@ enum drm_mode_status { #define CRTC_STEREO_DOUBLE (1 << 1) /* adjust timings for stereo modes */ #define CRTC_NO_DBLSCAN (1 << 2) /* don't adjust doublescan */ #define CRTC_NO_VSCAN (1 << 3) /* don't adjust doublescan */ -#define CRTC_STEREO_DOUBLE_ONLY (CRTC_NO_DBLSCAN | CRTC_NO_VSCAN) +#define CRTC_STEREO_DOUBLE_ONLY (CRTC_STEREO_DOUBLE | CRTC_NO_DBLSCAN | CRTC_NO_VSCAN) #define DRM_MODE_FLAG_3D_MAX DRM_MODE_FLAG_3D_SIDE_BY_SIDE_HALF -- cgit v1.2.3 From 17a38d9c254bf4e3b0d8b7ccd5c1988cb63730ff Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Sun, 22 Feb 2015 12:24:16 +0100 Subject: drm: Add DRM_DEBUG_ATOMIC MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Atomic state handling adds a lot of indirection and complexity between simple updates and drivers. For easier debugging the diagnostic output is therefore rather chatty. Which is great for tracking down atomic issues, but really annoying otherwise. Add a new DRM_DEBUG_ATOMIC to be able to filter this out. Cc: Ville Syrjälä Reviewed-by: Rob Clark Signed-off-by: Daniel Vetter --- drivers/gpu/drm/drm_atomic.c | 100 +++++++++++++++-------------- drivers/gpu/drm/drm_atomic_helper.c | 124 ++++++++++++++++++------------------ include/drm/drmP.h | 9 +++ 3 files changed, 122 insertions(+), 111 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c index c2e9c5283136..321e098ddf04 100644 --- a/drivers/gpu/drm/drm_atomic.c +++ b/drivers/gpu/drm/drm_atomic.c @@ -92,7 +92,7 @@ drm_atomic_state_alloc(struct drm_device *dev) state->dev = dev; - DRM_DEBUG_KMS("Allocate atomic state %p\n", state); + DRM_DEBUG_ATOMIC("Allocate atomic state %p\n", state); return state; fail: @@ -122,7 +122,7 @@ void drm_atomic_state_clear(struct drm_atomic_state *state) struct drm_mode_config *config = &dev->mode_config; int i; - DRM_DEBUG_KMS("Clearing atomic state %p\n", state); + DRM_DEBUG_ATOMIC("Clearing atomic state %p\n", state); for (i = 0; i < state->num_connector; i++) { struct drm_connector *connector = state->connectors[i]; @@ -172,7 +172,7 @@ void drm_atomic_state_free(struct drm_atomic_state *state) { drm_atomic_state_clear(state); - DRM_DEBUG_KMS("Freeing atomic state %p\n", state); + DRM_DEBUG_ATOMIC("Freeing atomic state %p\n", state); kfree_state(state); } @@ -217,8 +217,8 @@ drm_atomic_get_crtc_state(struct drm_atomic_state *state, state->crtcs[index] = crtc; crtc_state->state = state; - DRM_DEBUG_KMS("Added [CRTC:%d] %p state to %p\n", - crtc->base.id, crtc_state, state); + DRM_DEBUG_ATOMIC("Added [CRTC:%d] %p state to %p\n", + crtc->base.id, crtc_state, state); return crtc_state; } @@ -293,8 +293,8 @@ static int drm_atomic_crtc_check(struct drm_crtc *crtc, */ if (state->active && !state->enable) { - DRM_DEBUG_KMS("[CRTC:%d] active without enabled\n", - crtc->base.id); + DRM_DEBUG_ATOMIC("[CRTC:%d] active without enabled\n", + crtc->base.id); return -EINVAL; } @@ -340,8 +340,8 @@ drm_atomic_get_plane_state(struct drm_atomic_state *state, state->planes[index] = plane; plane_state->state = state; - DRM_DEBUG_KMS("Added [PLANE:%d] %p state to %p\n", - plane->base.id, plane_state, state); + DRM_DEBUG_ATOMIC("Added [PLANE:%d] %p state to %p\n", + plane->base.id, plane_state, state); if (plane_state->crtc) { struct drm_crtc_state *crtc_state; @@ -477,10 +477,10 @@ static int drm_atomic_plane_check(struct drm_plane *plane, /* either *both* CRTC and FB must be set, or neither */ if (WARN_ON(state->crtc && !state->fb)) { - DRM_DEBUG_KMS("CRTC set but no FB\n"); + DRM_DEBUG_ATOMIC("CRTC set but no FB\n"); return -EINVAL; } else if (WARN_ON(state->fb && !state->crtc)) { - DRM_DEBUG_KMS("FB set but no CRTC\n"); + DRM_DEBUG_ATOMIC("FB set but no CRTC\n"); return -EINVAL; } @@ -490,7 +490,7 @@ static int drm_atomic_plane_check(struct drm_plane *plane, /* Check whether this plane is usable on this CRTC */ if (!(plane->possible_crtcs & drm_crtc_mask(state->crtc))) { - DRM_DEBUG_KMS("Invalid crtc for plane\n"); + DRM_DEBUG_ATOMIC("Invalid crtc for plane\n"); return -EINVAL; } @@ -499,8 +499,8 @@ static int drm_atomic_plane_check(struct drm_plane *plane, if (state->fb->pixel_format == plane->format_types[i]) break; if (i == plane->format_count) { - DRM_DEBUG_KMS("Invalid pixel format %s\n", - drm_get_format_name(state->fb->pixel_format)); + DRM_DEBUG_ATOMIC("Invalid pixel format %s\n", + drm_get_format_name(state->fb->pixel_format)); return -EINVAL; } @@ -509,9 +509,9 @@ static int drm_atomic_plane_check(struct drm_plane *plane, state->crtc_x > INT_MAX - (int32_t) state->crtc_w || state->crtc_h > INT_MAX || state->crtc_y > INT_MAX - (int32_t) state->crtc_h) { - DRM_DEBUG_KMS("Invalid CRTC coordinates %ux%u+%d+%d\n", - state->crtc_w, state->crtc_h, - state->crtc_x, state->crtc_y); + DRM_DEBUG_ATOMIC("Invalid CRTC coordinates %ux%u+%d+%d\n", + state->crtc_w, state->crtc_h, + state->crtc_x, state->crtc_y); return -ERANGE; } @@ -523,12 +523,12 @@ static int drm_atomic_plane_check(struct drm_plane *plane, state->src_x > fb_width - state->src_w || state->src_h > fb_height || state->src_y > fb_height - state->src_h) { - DRM_DEBUG_KMS("Invalid source coordinates " - "%u.%06ux%u.%06u+%u.%06u+%u.%06u\n", - state->src_w >> 16, ((state->src_w & 0xffff) * 15625) >> 10, - state->src_h >> 16, ((state->src_h & 0xffff) * 15625) >> 10, - state->src_x >> 16, ((state->src_x & 0xffff) * 15625) >> 10, - state->src_y >> 16, ((state->src_y & 0xffff) * 15625) >> 10); + DRM_DEBUG_ATOMIC("Invalid source coordinates " + "%u.%06ux%u.%06u+%u.%06u+%u.%06u\n", + state->src_w >> 16, ((state->src_w & 0xffff) * 15625) >> 10, + state->src_h >> 16, ((state->src_h & 0xffff) * 15625) >> 10, + state->src_x >> 16, ((state->src_x & 0xffff) * 15625) >> 10, + state->src_y >> 16, ((state->src_y & 0xffff) * 15625) >> 10); return -ENOSPC; } @@ -575,7 +575,7 @@ drm_atomic_get_connector_state(struct drm_atomic_state *state, * at most the array is a bit too large. */ if (index >= state->num_connector) { - DRM_DEBUG_KMS("Hot-added connector would overflow state array, restarting\n"); + DRM_DEBUG_ATOMIC("Hot-added connector would overflow state array, restarting\n"); return ERR_PTR(-EAGAIN); } @@ -590,8 +590,8 @@ drm_atomic_get_connector_state(struct drm_atomic_state *state, state->connectors[index] = connector; connector_state->state = state; - DRM_DEBUG_KMS("Added [CONNECTOR:%d] %p state to %p\n", - connector->base.id, connector_state, state); + DRM_DEBUG_ATOMIC("Added [CONNECTOR:%d] %p state to %p\n", + connector->base.id, connector_state, state); if (connector_state->crtc) { struct drm_crtc_state *crtc_state; @@ -752,10 +752,11 @@ drm_atomic_set_crtc_for_plane(struct drm_plane_state *plane_state, } if (crtc) - DRM_DEBUG_KMS("Link plane state %p to [CRTC:%d]\n", - plane_state, crtc->base.id); + DRM_DEBUG_ATOMIC("Link plane state %p to [CRTC:%d]\n", + plane_state, crtc->base.id); else - DRM_DEBUG_KMS("Link plane state %p to [NOCRTC]\n", plane_state); + DRM_DEBUG_ATOMIC("Link plane state %p to [NOCRTC]\n", + plane_state); return 0; } @@ -782,10 +783,11 @@ drm_atomic_set_fb_for_plane(struct drm_plane_state *plane_state, plane_state->fb = fb; if (fb) - DRM_DEBUG_KMS("Set [FB:%d] for plane state %p\n", - fb->base.id, plane_state); + DRM_DEBUG_ATOMIC("Set [FB:%d] for plane state %p\n", + fb->base.id, plane_state); else - DRM_DEBUG_KMS("Set [NOFB] for plane state %p\n", plane_state); + DRM_DEBUG_ATOMIC("Set [NOFB] for plane state %p\n", + plane_state); } EXPORT_SYMBOL(drm_atomic_set_fb_for_plane); @@ -818,11 +820,11 @@ drm_atomic_set_crtc_for_connector(struct drm_connector_state *conn_state, conn_state->crtc = crtc; if (crtc) - DRM_DEBUG_KMS("Link connector state %p to [CRTC:%d]\n", - conn_state, crtc->base.id); + DRM_DEBUG_ATOMIC("Link connector state %p to [CRTC:%d]\n", + conn_state, crtc->base.id); else - DRM_DEBUG_KMS("Link connector state %p to [NOCRTC]\n", - conn_state); + DRM_DEBUG_ATOMIC("Link connector state %p to [NOCRTC]\n", + conn_state); return 0; } @@ -858,8 +860,8 @@ drm_atomic_add_affected_connectors(struct drm_atomic_state *state, if (ret) return ret; - DRM_DEBUG_KMS("Adding all current connectors for [CRTC:%d] to %p\n", - crtc->base.id, state); + DRM_DEBUG_ATOMIC("Adding all current connectors for [CRTC:%d] to %p\n", + crtc->base.id, state); /* * Changed connectors are already in @state, so only need to look at the @@ -901,8 +903,8 @@ drm_atomic_connectors_for_crtc(struct drm_atomic_state *state, num_connected_connectors++; } - DRM_DEBUG_KMS("State %p has %i connectors for [CRTC:%d]\n", - state, num_connected_connectors, crtc->base.id); + DRM_DEBUG_ATOMIC("State %p has %i connectors for [CRTC:%d]\n", + state, num_connected_connectors, crtc->base.id); return num_connected_connectors; } @@ -953,7 +955,7 @@ int drm_atomic_check_only(struct drm_atomic_state *state) int ncrtcs = config->num_crtc; int i, ret = 0; - DRM_DEBUG_KMS("checking %p\n", state); + DRM_DEBUG_ATOMIC("checking %p\n", state); for (i = 0; i < nplanes; i++) { struct drm_plane *plane = state->planes[i]; @@ -963,8 +965,8 @@ int drm_atomic_check_only(struct drm_atomic_state *state) ret = drm_atomic_plane_check(plane, state->plane_states[i]); if (ret) { - DRM_DEBUG_KMS("[PLANE:%d] atomic core check failed\n", - plane->base.id); + DRM_DEBUG_ATOMIC("[PLANE:%d] atomic core check failed\n", + plane->base.id); return ret; } } @@ -977,8 +979,8 @@ int drm_atomic_check_only(struct drm_atomic_state *state) ret = drm_atomic_crtc_check(crtc, state->crtc_states[i]); if (ret) { - DRM_DEBUG_KMS("[CRTC:%d] atomic core check failed\n", - crtc->base.id); + DRM_DEBUG_ATOMIC("[CRTC:%d] atomic core check failed\n", + crtc->base.id); return ret; } } @@ -996,8 +998,8 @@ int drm_atomic_check_only(struct drm_atomic_state *state) if (crtc_state->mode_changed || crtc_state->active_changed) { - DRM_DEBUG_KMS("[CRTC:%d] requires full modeset\n", - crtc->base.id); + DRM_DEBUG_ATOMIC("[CRTC:%d] requires full modeset\n", + crtc->base.id); return -EINVAL; } } @@ -1032,7 +1034,7 @@ int drm_atomic_commit(struct drm_atomic_state *state) if (ret) return ret; - DRM_DEBUG_KMS("commiting %p\n", state); + DRM_DEBUG_ATOMIC("commiting %p\n", state); return config->funcs->atomic_commit(state->dev, state, false); } @@ -1063,7 +1065,7 @@ int drm_atomic_async_commit(struct drm_atomic_state *state) if (ret) return ret; - DRM_DEBUG_KMS("commiting %p asynchronously\n", state); + DRM_DEBUG_ATOMIC("commiting %p asynchronously\n", state); return config->funcs->atomic_commit(state->dev, state, true); } diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c index e70adfcb9e84..9e8767118edb 100644 --- a/drivers/gpu/drm/drm_atomic_helper.c +++ b/drivers/gpu/drm/drm_atomic_helper.c @@ -116,9 +116,9 @@ steal_encoder(struct drm_atomic_state *state, */ WARN_ON(!drm_modeset_is_locked(&config->connection_mutex)); - DRM_DEBUG_KMS("[ENCODER:%d:%s] in use on [CRTC:%d], stealing it\n", - encoder->base.id, encoder->name, - encoder_crtc->base.id); + DRM_DEBUG_ATOMIC("[ENCODER:%d:%s] in use on [CRTC:%d], stealing it\n", + encoder->base.id, encoder->name, + encoder_crtc->base.id); crtc_state = drm_atomic_get_crtc_state(state, encoder_crtc); if (IS_ERR(crtc_state)) @@ -130,9 +130,9 @@ steal_encoder(struct drm_atomic_state *state, if (connector->state->best_encoder != encoder) continue; - DRM_DEBUG_KMS("Stealing encoder from [CONNECTOR:%d:%s]\n", - connector->base.id, - connector->name); + DRM_DEBUG_ATOMIC("Stealing encoder from [CONNECTOR:%d:%s]\n", + connector->base.id, + connector->name); connector_state = drm_atomic_get_connector_state(state, connector); @@ -165,9 +165,9 @@ update_connector_routing(struct drm_atomic_state *state, int conn_idx) if (!connector) return 0; - DRM_DEBUG_KMS("Updating routing for [CONNECTOR:%d:%s]\n", - connector->base.id, - connector->name); + DRM_DEBUG_ATOMIC("Updating routing for [CONNECTOR:%d:%s]\n", + connector->base.id, + connector->name); if (connector->state->crtc != connector_state->crtc) { if (connector->state->crtc) { @@ -186,7 +186,7 @@ update_connector_routing(struct drm_atomic_state *state, int conn_idx) } if (!connector_state->crtc) { - DRM_DEBUG_KMS("Disabling [CONNECTOR:%d:%s]\n", + DRM_DEBUG_ATOMIC("Disabling [CONNECTOR:%d:%s]\n", connector->base.id, connector->name); @@ -199,19 +199,19 @@ update_connector_routing(struct drm_atomic_state *state, int conn_idx) new_encoder = funcs->best_encoder(connector); if (!new_encoder) { - DRM_DEBUG_KMS("No suitable encoder found for [CONNECTOR:%d:%s]\n", - connector->base.id, - connector->name); + DRM_DEBUG_ATOMIC("No suitable encoder found for [CONNECTOR:%d:%s]\n", + connector->base.id, + connector->name); return -EINVAL; } if (new_encoder == connector_state->best_encoder) { - DRM_DEBUG_KMS("[CONNECTOR:%d:%s] keeps [ENCODER:%d:%s], now on [CRTC:%d]\n", - connector->base.id, - connector->name, - new_encoder->base.id, - new_encoder->name, - connector_state->crtc->base.id); + DRM_DEBUG_ATOMIC("[CONNECTOR:%d:%s] keeps [ENCODER:%d:%s], now on [CRTC:%d]\n", + connector->base.id, + connector->name, + new_encoder->base.id, + new_encoder->name, + connector_state->crtc->base.id); return 0; } @@ -222,9 +222,9 @@ update_connector_routing(struct drm_atomic_state *state, int conn_idx) if (encoder_crtc) { ret = steal_encoder(state, new_encoder, encoder_crtc); if (ret) { - DRM_DEBUG_KMS("Encoder stealing failed for [CONNECTOR:%d:%s]\n", - connector->base.id, - connector->name); + DRM_DEBUG_ATOMIC("Encoder stealing failed for [CONNECTOR:%d:%s]\n", + connector->base.id, + connector->name); return ret; } } @@ -235,12 +235,12 @@ update_connector_routing(struct drm_atomic_state *state, int conn_idx) crtc_state = state->crtc_states[idx]; crtc_state->mode_changed = true; - DRM_DEBUG_KMS("[CONNECTOR:%d:%s] using [ENCODER:%d:%s] on [CRTC:%d]\n", - connector->base.id, - connector->name, - new_encoder->base.id, - new_encoder->name, - connector_state->crtc->base.id); + DRM_DEBUG_ATOMIC("[CONNECTOR:%d:%s] using [ENCODER:%d:%s] on [CRTC:%d]\n", + connector->base.id, + connector->name, + new_encoder->base.id, + new_encoder->name, + connector_state->crtc->base.id); return 0; } @@ -292,7 +292,7 @@ mode_fixup(struct drm_atomic_state *state) encoder->bridge, &crtc_state->mode, &crtc_state->adjusted_mode); if (!ret) { - DRM_DEBUG_KMS("Bridge fixup failed\n"); + DRM_DEBUG_ATOMIC("Bridge fixup failed\n"); return -EINVAL; } } @@ -301,16 +301,16 @@ mode_fixup(struct drm_atomic_state *state) ret = funcs->atomic_check(encoder, crtc_state, conn_state); if (ret) { - DRM_DEBUG_KMS("[ENCODER:%d:%s] check failed\n", - encoder->base.id, encoder->name); + DRM_DEBUG_ATOMIC("[ENCODER:%d:%s] check failed\n", + encoder->base.id, encoder->name); return ret; } } else { ret = funcs->mode_fixup(encoder, &crtc_state->mode, &crtc_state->adjusted_mode); if (!ret) { - DRM_DEBUG_KMS("[ENCODER:%d:%s] fixup failed\n", - encoder->base.id, encoder->name); + DRM_DEBUG_ATOMIC("[ENCODER:%d:%s] fixup failed\n", + encoder->base.id, encoder->name); return -EINVAL; } } @@ -330,8 +330,8 @@ mode_fixup(struct drm_atomic_state *state) ret = funcs->mode_fixup(crtc, &crtc_state->mode, &crtc_state->adjusted_mode); if (!ret) { - DRM_DEBUG_KMS("[CRTC:%d] fixup failed\n", - crtc->base.id); + DRM_DEBUG_ATOMIC("[CRTC:%d] fixup failed\n", + crtc->base.id); return -EINVAL; } } @@ -384,14 +384,14 @@ drm_atomic_helper_check_modeset(struct drm_device *dev, continue; if (!drm_mode_equal(&crtc->state->mode, &crtc_state->mode)) { - DRM_DEBUG_KMS("[CRTC:%d] mode changed\n", - crtc->base.id); + DRM_DEBUG_ATOMIC("[CRTC:%d] mode changed\n", + crtc->base.id); crtc_state->mode_changed = true; } if (crtc->state->enable != crtc_state->enable) { - DRM_DEBUG_KMS("[CRTC:%d] enable changed\n", - crtc->base.id); + DRM_DEBUG_ATOMIC("[CRTC:%d] enable changed\n", + crtc->base.id); crtc_state->mode_changed = true; } } @@ -428,17 +428,17 @@ drm_atomic_helper_check_modeset(struct drm_device *dev, * a full modeset because update_connector_routing force that. */ if (crtc->state->active != crtc_state->active) { - DRM_DEBUG_KMS("[CRTC:%d] active changed\n", - crtc->base.id); + DRM_DEBUG_ATOMIC("[CRTC:%d] active changed\n", + crtc->base.id); crtc_state->active_changed = true; } if (!needs_modeset(crtc_state)) continue; - DRM_DEBUG_KMS("[CRTC:%d] needs all connectors, enable: %c, active: %c\n", - crtc->base.id, - crtc_state->enable ? 'y' : 'n', + DRM_DEBUG_ATOMIC("[CRTC:%d] needs all connectors, enable: %c, active: %c\n", + crtc->base.id, + crtc_state->enable ? 'y' : 'n', crtc_state->active ? 'y' : 'n'); ret = drm_atomic_add_affected_connectors(state, crtc); @@ -449,8 +449,8 @@ drm_atomic_helper_check_modeset(struct drm_device *dev, crtc); if (crtc_state->enable != !!num_connectors) { - DRM_DEBUG_KMS("[CRTC:%d] enabled/connectors mismatch\n", - crtc->base.id); + DRM_DEBUG_ATOMIC("[CRTC:%d] enabled/connectors mismatch\n", + crtc->base.id); return -EINVAL; } @@ -497,8 +497,8 @@ drm_atomic_helper_check_planes(struct drm_device *dev, ret = funcs->atomic_check(plane, plane_state); if (ret) { - DRM_DEBUG_KMS("[PLANE:%d] atomic driver check failed\n", - plane->base.id); + DRM_DEBUG_ATOMIC("[PLANE:%d] atomic driver check failed\n", + plane->base.id); return ret; } } @@ -517,8 +517,8 @@ drm_atomic_helper_check_planes(struct drm_device *dev, ret = funcs->atomic_check(crtc, state->crtc_states[i]); if (ret) { - DRM_DEBUG_KMS("[CRTC:%d] atomic driver check failed\n", - crtc->base.id); + DRM_DEBUG_ATOMIC("[CRTC:%d] atomic driver check failed\n", + crtc->base.id); return ret; } } @@ -600,8 +600,8 @@ disable_outputs(struct drm_device *dev, struct drm_atomic_state *old_state) funcs = encoder->helper_private; - DRM_DEBUG_KMS("disabling [ENCODER:%d:%s]\n", - encoder->base.id, encoder->name); + DRM_DEBUG_ATOMIC("disabling [ENCODER:%d:%s]\n", + encoder->base.id, encoder->name); /* * Each encoder has at most one connector (since we always steal @@ -639,8 +639,8 @@ disable_outputs(struct drm_device *dev, struct drm_atomic_state *old_state) funcs = crtc->helper_private; - DRM_DEBUG_KMS("disabling [CRTC:%d]\n", - crtc->base.id); + DRM_DEBUG_ATOMIC("disabling [CRTC:%d]\n", + crtc->base.id); /* Right function depends upon target state. */ @@ -724,8 +724,8 @@ crtc_set_mode(struct drm_device *dev, struct drm_atomic_state *old_state) funcs = crtc->helper_private; if (crtc->state->enable) { - DRM_DEBUG_KMS("modeset on [CRTC:%d]\n", - crtc->base.id); + DRM_DEBUG_ATOMIC("modeset on [CRTC:%d]\n", + crtc->base.id); funcs->mode_set_nofb(crtc); } @@ -752,8 +752,8 @@ crtc_set_mode(struct drm_device *dev, struct drm_atomic_state *old_state) if (!new_crtc_state->mode_changed) continue; - DRM_DEBUG_KMS("modeset on [ENCODER:%d:%s]\n", - encoder->base.id, encoder->name); + DRM_DEBUG_ATOMIC("modeset on [ENCODER:%d:%s]\n", + encoder->base.id, encoder->name); /* * Each encoder has at most one connector (since we always steal @@ -816,8 +816,8 @@ void drm_atomic_helper_commit_post_planes(struct drm_device *dev, funcs = crtc->helper_private; if (crtc->state->enable) { - DRM_DEBUG_KMS("enabling [CRTC:%d]\n", - crtc->base.id); + DRM_DEBUG_ATOMIC("enabling [CRTC:%d]\n", + crtc->base.id); if (funcs->enable) funcs->enable(crtc); @@ -842,8 +842,8 @@ void drm_atomic_helper_commit_post_planes(struct drm_device *dev, encoder = connector->state->best_encoder; funcs = encoder->helper_private; - DRM_DEBUG_KMS("enabling [ENCODER:%d:%s]\n", - encoder->base.id, encoder->name); + DRM_DEBUG_ATOMIC("enabling [ENCODER:%d:%s]\n", + encoder->base.id, encoder->name); /* * Each encoder has at most one connector (since we always steal diff --git a/include/drm/drmP.h b/include/drm/drmP.h index e928625a9da0..52999ba9fbaf 100644 --- a/include/drm/drmP.h +++ b/include/drm/drmP.h @@ -104,6 +104,9 @@ struct dma_buf_attachment; * PRIME: used in the prime code. * This is the category used by the DRM_DEBUG_PRIME() macro. * + * ATOMIC: used in the atomic code. + * This is the category used by the DRM_DEBUG_ATOMIC() macro. + * * Enabling verbose debug messages is done through the drm.debug parameter, * each category being enabled by a bit. * @@ -121,6 +124,7 @@ struct dma_buf_attachment; #define DRM_UT_DRIVER 0x02 #define DRM_UT_KMS 0x04 #define DRM_UT_PRIME 0x08 +#define DRM_UT_ATOMIC 0x10 extern __printf(2, 3) void drm_ut_debug_printk(const char *function_name, @@ -207,6 +211,11 @@ void drm_err(const char *format, ...); if (unlikely(drm_debug & DRM_UT_PRIME)) \ drm_ut_debug_printk(__func__, fmt, ##args); \ } while (0) +#define DRM_DEBUG_ATOMIC(fmt, args...) \ + do { \ + if (unlikely(drm_debug & DRM_UT_ATOMIC)) \ + drm_ut_debug_printk(__func__, fmt, ##args); \ + } while (0) /*@}*/ -- cgit v1.2.3 From ee8905963ed0bc9dfc0952dc35e16e233c10e212 Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Mon, 14 Apr 2014 17:53:25 +0200 Subject: of: Add for_each_endpoint_of_node helper macro Note that while of_graph_get_next_endpoint decrements the reference count of the child node passed to it, of_node_put(child) still has to be called manually when breaking out of the loop. Signed-off-by: Philipp Zabel Acked-by: Laurent Pinchart --- include/linux/of_graph.h | 11 +++++++++++ 1 file changed, 11 insertions(+) (limited to 'include') diff --git a/include/linux/of_graph.h b/include/linux/of_graph.h index befef42e015b..e43442efcbe5 100644 --- a/include/linux/of_graph.h +++ b/include/linux/of_graph.h @@ -26,6 +26,17 @@ struct of_endpoint { const struct device_node *local_node; }; +/** + * for_each_endpoint_of_node - iterate over every endpoint in a device node + * @parent: parent device node containing ports and endpoints + * @child: loop variable pointing to the current endpoint node + * + * When breaking out of the loop, of_node_put(child) has to be called manually. + */ +#define for_each_endpoint_of_node(parent, child) \ + for (child = of_graph_get_next_endpoint(parent, NULL); child != NULL; \ + child = of_graph_get_next_endpoint(parent, child)) + #ifdef CONFIG_OF int of_graph_parse_endpoint(const struct device_node *node, struct of_endpoint *endpoint); -- cgit v1.2.3 From 411fdaf846afb0be1b54383c184f58a42fa416ff Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Tue, 17 Feb 2015 01:46:49 +0000 Subject: dmaengine: shdma: use normal interface for passing slave id in dma_slave_config, which is incompatible with the way that the dmaengine API normally works. I've had a closer look at the existing code now and found that all slave drivers that pass a slave_id in dma_slave_config for SH do that right after passing the same ID into shdma_chan_filter, so we can just rely on that. However, the various shdma drivers currently do not remember the slave ID that was passed into the filter function when used in non-DT mode and only check the value to find a matching channel, unlike all other drivers. There might still be drivers that are not part of the kernel that rely on setting the slave_id to some other value, so to be on the safe side, this adds another 'real_slave_id' field to shdma_chan that remembers the ID and uses it when a driver passes a zero slave_id in dma_slave_config, like most drivers do. Eventually, the real_slave_id and slave_id fields should just get merged into one field, but that requires other changes. Signed-off-by: Arnd Bergmann Signed-off-by: Kuninori Morimoto Signed-off-by: Vinod Koul --- drivers/dma/sh/shdma-base.c | 73 ++++++++++++++++++++++++++++++++------------- include/linux/shdma-base.h | 1 + 2 files changed, 54 insertions(+), 20 deletions(-) (limited to 'include') diff --git a/drivers/dma/sh/shdma-base.c b/drivers/dma/sh/shdma-base.c index 8ee383d339a5..10fcabad80f3 100644 --- a/drivers/dma/sh/shdma-base.c +++ b/drivers/dma/sh/shdma-base.c @@ -171,8 +171,7 @@ static struct shdma_desc *shdma_get_desc(struct shdma_chan *schan) return NULL; } -static int shdma_setup_slave(struct shdma_chan *schan, int slave_id, - dma_addr_t slave_addr) +static int shdma_setup_slave(struct shdma_chan *schan, dma_addr_t slave_addr) { struct shdma_dev *sdev = to_shdma_dev(schan->dma_chan.device); const struct shdma_ops *ops = sdev->ops; @@ -183,25 +182,23 @@ static int shdma_setup_slave(struct shdma_chan *schan, int slave_id, ret = ops->set_slave(schan, match, slave_addr, true); if (ret < 0) return ret; - - slave_id = schan->slave_id; } else { - match = slave_id; + match = schan->real_slave_id; } - if (slave_id < 0 || slave_id >= slave_num) + if (schan->real_slave_id < 0 || schan->real_slave_id >= slave_num) return -EINVAL; - if (test_and_set_bit(slave_id, shdma_slave_used)) + if (test_and_set_bit(schan->real_slave_id, shdma_slave_used)) return -EBUSY; ret = ops->set_slave(schan, match, slave_addr, false); if (ret < 0) { - clear_bit(slave_id, shdma_slave_used); + clear_bit(schan->real_slave_id, shdma_slave_used); return ret; } - schan->slave_id = slave_id; + schan->slave_id = schan->real_slave_id; return 0; } @@ -221,10 +218,12 @@ static int shdma_alloc_chan_resources(struct dma_chan *chan) */ if (slave) { /* Legacy mode: .private is set in filter */ - ret = shdma_setup_slave(schan, slave->slave_id, 0); + schan->real_slave_id = slave->slave_id; + ret = shdma_setup_slave(schan, 0); if (ret < 0) goto esetslave; } else { + /* Normal mode: real_slave_id was set by filter */ schan->slave_id = -EINVAL; } @@ -258,11 +257,14 @@ esetslave: /* * This is the standard shdma filter function to be used as a replacement to the - * "old" method, using the .private pointer. If for some reason you allocate a - * channel without slave data, use something like ERR_PTR(-EINVAL) as a filter + * "old" method, using the .private pointer. + * You always have to pass a valid slave id as the argument, old drivers that + * pass ERR_PTR(-EINVAL) as a filter parameter and set it up in dma_slave_config + * need to be updated so we can remove the slave_id field from dma_slave_config. * parameter. If this filter is used, the slave driver, after calling * dma_request_channel(), will also have to call dmaengine_slave_config() with - * .slave_id, .direction, and either .src_addr or .dst_addr set. + * .direction, and either .src_addr or .dst_addr set. + * * NOTE: this filter doesn't support multiple DMAC drivers with the DMA_SLAVE * capability! If this becomes a requirement, hardware glue drivers, using this * services would have to provide their own filters, which first would check @@ -276,7 +278,7 @@ bool shdma_chan_filter(struct dma_chan *chan, void *arg) { struct shdma_chan *schan; struct shdma_dev *sdev; - int match = (long)arg; + int slave_id = (long)arg; int ret; /* Only support channels handled by this driver. */ @@ -284,19 +286,39 @@ bool shdma_chan_filter(struct dma_chan *chan, void *arg) shdma_alloc_chan_resources) return false; - if (match < 0) + schan = to_shdma_chan(chan); + sdev = to_shdma_dev(chan->device); + + /* + * For DT, the schan->slave_id field is generated by the + * set_slave function from the slave ID that is passed in + * from xlate. For the non-DT case, the slave ID is + * directly passed into the filter function by the driver + */ + if (schan->dev->of_node) { + ret = sdev->ops->set_slave(schan, slave_id, 0, true); + if (ret < 0) + return false; + + schan->real_slave_id = schan->slave_id; + return true; + } + + if (slave_id < 0) { /* No slave requested - arbitrary channel */ + dev_warn(sdev->dma_dev.dev, "invalid slave ID passed to dma_request_slave\n"); return true; + } - schan = to_shdma_chan(chan); - if (!schan->dev->of_node && match >= slave_num) + if (slave_id >= slave_num) return false; - sdev = to_shdma_dev(schan->dma_chan.device); - ret = sdev->ops->set_slave(schan, match, 0, true); + ret = sdev->ops->set_slave(schan, slave_id, 0, true); if (ret < 0) return false; + schan->real_slave_id = slave_id; + return true; } EXPORT_SYMBOL(shdma_chan_filter); @@ -452,6 +474,8 @@ static void shdma_free_chan_resources(struct dma_chan *chan) chan->private = NULL; } + schan->real_slave_id = 0; + spin_lock_irq(&schan->chan_lock); list_splice_init(&schan->ld_free, &list); @@ -764,11 +788,20 @@ static int shdma_config(struct dma_chan *chan, */ if (!config) return -EINVAL; + + /* + * overriding the slave_id through dma_slave_config is deprecated, + * but possibly some out-of-tree drivers still do it. + */ + if (WARN_ON_ONCE(config->slave_id && + config->slave_id != schan->real_slave_id)) + schan->real_slave_id = config->slave_id; + /* * We could lock this, but you shouldn't be configuring the * channel, while using it... */ - return shdma_setup_slave(schan, config->slave_id, + return shdma_setup_slave(schan, config->direction == DMA_DEV_TO_MEM ? config->src_addr : config->dst_addr); } diff --git a/include/linux/shdma-base.h b/include/linux/shdma-base.h index abdf1f229dc3..dd0ba502ccb3 100644 --- a/include/linux/shdma-base.h +++ b/include/linux/shdma-base.h @@ -69,6 +69,7 @@ struct shdma_chan { int id; /* Raw id of this channel */ int irq; /* Channel IRQ */ int slave_id; /* Client ID for slave DMA */ + int real_slave_id; /* argument passed to filter function */ int hw_req; /* DMA request line for slave DMA - same * as MID/RID, used with DT */ enum shdma_pm_state pm_state; -- cgit v1.2.3 From bfe446e37c4efd8ade454911e8f80414bcbfc10d Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Tue, 11 Mar 2014 11:21:11 +0100 Subject: of: Add of_graph_get_port_by_id function This patch adds a function to get a port device tree node by port id, or reg property value. Signed-off-by: Philipp Zabel Acked-by: Laurent Pinchart --- drivers/of/base.c | 32 ++++++++++++++++++++++++++++++++ include/linux/of_graph.h | 7 +++++++ 2 files changed, 39 insertions(+) (limited to 'include') diff --git a/drivers/of/base.c b/drivers/of/base.c index 05b20f1cca7b..6398b9ca9157 100644 --- a/drivers/of/base.c +++ b/drivers/of/base.c @@ -2080,6 +2080,38 @@ int of_graph_parse_endpoint(const struct device_node *node, } EXPORT_SYMBOL(of_graph_parse_endpoint); +/** + * of_graph_get_port_by_id() - get the port matching a given id + * @parent: pointer to the parent device node + * @id: id of the port + * + * Return: A 'port' node pointer with refcount incremented. The caller + * has to use of_node_put() on it when done. + */ +struct device_node *of_graph_get_port_by_id(struct device_node *parent, u32 id) +{ + struct device_node *node, *port; + + node = of_get_child_by_name(parent, "ports"); + if (node) + parent = node; + + for_each_child_of_node(parent, port) { + u32 port_id = 0; + + if (of_node_cmp(port->name, "port") != 0) + continue; + of_property_read_u32(port, "reg", &port_id); + if (id == port_id) + break; + } + + of_node_put(node); + + return port; +} +EXPORT_SYMBOL(of_graph_get_port_by_id); + /** * of_graph_get_next_endpoint() - get next endpoint node * @parent: pointer to the parent device node diff --git a/include/linux/of_graph.h b/include/linux/of_graph.h index e43442efcbe5..3c1c95a39e0c 100644 --- a/include/linux/of_graph.h +++ b/include/linux/of_graph.h @@ -40,6 +40,7 @@ struct of_endpoint { #ifdef CONFIG_OF int of_graph_parse_endpoint(const struct device_node *node, struct of_endpoint *endpoint); +struct device_node *of_graph_get_port_by_id(struct device_node *node, u32 id); struct device_node *of_graph_get_next_endpoint(const struct device_node *parent, struct device_node *previous); struct device_node *of_graph_get_remote_port_parent( @@ -53,6 +54,12 @@ static inline int of_graph_parse_endpoint(const struct device_node *node, return -ENOSYS; } +static inline struct device_node *of_graph_get_port_by_id( + struct device_node *node, u32 id) +{ + return NULL; +} + static inline struct device_node *of_graph_get_next_endpoint( const struct device_node *parent, struct device_node *previous) -- cgit v1.2.3 From ca8895d9bb41e743271c42a4438a296de891b73b Mon Sep 17 00:00:00 2001 From: Goldwyn Rodrigues Date: Wed, 26 Nov 2014 12:22:03 -0600 Subject: Return MD_SB_CLUSTERED if mddev is clustered Signed-off-by: Goldwyn Rodrigues --- drivers/md/md.c | 3 +++ include/uapi/linux/raid/md_p.h | 1 + 2 files changed, 4 insertions(+) (limited to 'include') diff --git a/drivers/md/md.c b/drivers/md/md.c index 3387f940140b..5ed57688e5c5 100644 --- a/drivers/md/md.c +++ b/drivers/md/md.c @@ -5634,6 +5634,9 @@ static int get_array_info(struct mddev *mddev, void __user *arg) info.state = (1<bitmap && mddev->bitmap_info.offset) info.state |= (1< Date: Mon, 9 Feb 2015 00:18:11 -0800 Subject: ALSA: Add params_set_format helper Add a helper to set pcm format directly from params Signed-off-by: Fang, Yang A Reviewed-by: Takashi Iwai Signed-off-by: Mark Brown --- include/sound/pcm_params.h | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'include') diff --git a/include/sound/pcm_params.h b/include/sound/pcm_params.h index 3c45f3924ba7..c704357775fc 100644 --- a/include/sound/pcm_params.h +++ b/include/sound/pcm_params.h @@ -366,4 +366,11 @@ static inline int params_physical_width(const struct snd_pcm_hw_params *p) return snd_pcm_format_physical_width(params_format(p)); } +static inline void +params_set_format(struct snd_pcm_hw_params *p, snd_pcm_format_t fmt) +{ + snd_mask_set(hw_param_mask(p, SNDRV_PCM_HW_PARAM_FORMAT), + (__force int)fmt); +} + #endif /* __SOUND_PCM_PARAMS_H */ -- cgit v1.2.3 From 48c7699fb2c799d084ce490bceea14fe04ad12a1 Mon Sep 17 00:00:00 2001 From: Vinod Koul Date: Thu, 12 Feb 2015 09:59:53 +0530 Subject: ASoC: core: allow pcms to be registered as nonatomic ALSA core with commit 257f8cce5d40 - "ALSA: pcm: Allow nonatomic trigger operations" allows trigger ops to implemented as nonatomic. For ASoC, we can specify this in dailinks and is updated while snd_pcm is created Signed-off-by: Subhransu S. Prusty Signed-off-by: Vinod Koul Cc: Takashi Iwai Signed-off-by: Mark Brown --- include/sound/soc.h | 3 +++ sound/soc/soc-pcm.c | 1 + 2 files changed, 4 insertions(+) (limited to 'include') diff --git a/include/sound/soc.h b/include/sound/soc.h index 0d1ade195628..76bc944dcb5c 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -954,6 +954,9 @@ struct snd_soc_dai_link { unsigned int symmetric_channels:1; unsigned int symmetric_samplebits:1; + /* Mark this pcm with non atomic ops */ + bool nonatomic; + /* Do not create a PCM for this DAI link (Backend link) */ unsigned int no_pcm:1; diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c index 6b0136e7cb88..6e3781e88f9a 100644 --- a/sound/soc/soc-pcm.c +++ b/sound/soc/soc-pcm.c @@ -2511,6 +2511,7 @@ int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num) /* DAPM dai link stream work */ INIT_DELAYED_WORK(&rtd->delayed_work, close_delayed_work); + pcm->nonatomic = rtd->dai_link->nonatomic; rtd->pcm = pcm; pcm->private_data = rtd; -- cgit v1.2.3 From 1aee41f637694d4bbf91c24195f2b63e3f6badd2 Mon Sep 17 00:00:00 2001 From: Goldwyn Rodrigues Date: Wed, 29 Oct 2014 18:51:31 -0500 Subject: Add new disk to clustered array Algorithm: 1. Node 1 issues mdadm --manage /dev/mdX --add /dev/sdYY which issues ioctl(ADD_NEW_DISC with disc.state set to MD_DISK_CLUSTER_ADD) 2. Node 1 sends NEWDISK with uuid and slot number 3. Other nodes issue kobject_uevent_env with uuid and slot number (Steps 4,5 could be a udev rule) 4. In userspace, the node searches for the disk, perhaps using blkid -t SUB_UUID="" 5. Other nodes issue either of the following depending on whether the disk was found: ioctl(ADD_NEW_DISK with disc.state set to MD_DISK_CANDIDATE and disc.number set to slot number) ioctl(CLUSTERED_DISK_NACK) 6. Other nodes drop lock on no-new-devs (CR) if device is found 7. Node 1 attempts EX lock on no-new-devs 8. If node 1 gets the lock, it sends METADATA_UPDATED after unmarking the disk as SpareLocal 9. If not (get no-new-dev lock), it fails the operation and sends METADATA_UPDATED 10. Other nodes understand if the device is added or not by reading the superblock again after receiving the METADATA_UPDATED message. Signed-off-by: Lidong Zhong Signed-off-by: Goldwyn Rodrigues --- drivers/md/md-cluster.c | 104 ++++++++++++++++++++++++++++++++++++++++- drivers/md/md-cluster.h | 4 ++ drivers/md/md.c | 52 +++++++++++++++++++-- drivers/md/md.h | 5 ++ drivers/md/raid1.c | 1 + include/uapi/linux/raid/md_p.h | 6 +++ include/uapi/linux/raid/md_u.h | 1 + 7 files changed, 169 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/drivers/md/md-cluster.c b/drivers/md/md-cluster.c index d85a6ca4443e..03e521a9ca7d 100644 --- a/drivers/md/md-cluster.c +++ b/drivers/md/md-cluster.c @@ -12,11 +12,13 @@ #include #include #include +#include #include "md.h" #include "bitmap.h" #include "md-cluster.h" #define LVB_SIZE 64 +#define NEW_DEV_TIMEOUT 5000 struct dlm_lock_resource { dlm_lockspace_t *ls; @@ -56,19 +58,25 @@ struct md_cluster_info { struct dlm_lock_resource *ack_lockres; struct dlm_lock_resource *message_lockres; struct dlm_lock_resource *token_lockres; + struct dlm_lock_resource *no_new_dev_lockres; struct md_thread *recv_thread; + struct completion newdisk_completion; }; enum msg_type { METADATA_UPDATED = 0, RESYNCING, + NEWDISK, }; struct cluster_msg { int type; int slot; + /* TODO: Unionize this for smaller footprint */ sector_t low; sector_t high; + char uuid[16]; + int raid_slot; }; static void sync_ast(void *arg) @@ -358,13 +366,41 @@ static void process_suspend_info(struct md_cluster_info *cinfo, spin_unlock_irq(&cinfo->suspend_lock); } +static void process_add_new_disk(struct mddev *mddev, struct cluster_msg *cmsg) +{ + char disk_uuid[64]; + struct md_cluster_info *cinfo = mddev->cluster_info; + char event_name[] = "EVENT=ADD_DEVICE"; + char raid_slot[16]; + char *envp[] = {event_name, disk_uuid, raid_slot, NULL}; + int len; + + len = snprintf(disk_uuid, 64, "DEVICE_UUID="); + pretty_uuid(disk_uuid + len, cmsg->uuid); + snprintf(raid_slot, 16, "RAID_DISK=%d", cmsg->raid_slot); + pr_info("%s:%d Sending kobject change with %s and %s\n", __func__, __LINE__, disk_uuid, raid_slot); + init_completion(&cinfo->newdisk_completion); + kobject_uevent_env(&disk_to_dev(mddev->gendisk)->kobj, KOBJ_CHANGE, envp); + wait_for_completion_timeout(&cinfo->newdisk_completion, + NEW_DEV_TIMEOUT); +} + + +static void process_metadata_update(struct mddev *mddev, struct cluster_msg *msg) +{ + struct md_cluster_info *cinfo = mddev->cluster_info; + + md_reload_sb(mddev); + dlm_lock_sync(cinfo->no_new_dev_lockres, DLM_LOCK_CR); +} + static void process_recvd_msg(struct mddev *mddev, struct cluster_msg *msg) { switch (msg->type) { case METADATA_UPDATED: pr_info("%s: %d Received message: METADATA_UPDATE from %d\n", __func__, __LINE__, msg->slot); - md_reload_sb(mddev); + process_metadata_update(mddev, msg); break; case RESYNCING: pr_info("%s: %d Received message: RESYNCING from %d\n", @@ -372,6 +408,10 @@ static void process_recvd_msg(struct mddev *mddev, struct cluster_msg *msg) process_suspend_info(mddev->cluster_info, msg->slot, msg->low, msg->high); break; + case NEWDISK: + pr_info("%s: %d Received message: NEWDISK from %d\n", + __func__, __LINE__, msg->slot); + process_add_new_disk(mddev, msg); }; } @@ -593,10 +633,18 @@ static int join(struct mddev *mddev, int nodes) cinfo->ack_lockres = lockres_init(mddev, "ack", ack_bast, 0); if (!cinfo->ack_lockres) goto err; + cinfo->no_new_dev_lockres = lockres_init(mddev, "no-new-dev", NULL, 0); + if (!cinfo->no_new_dev_lockres) + goto err; + /* get sync CR lock on ACK. */ if (dlm_lock_sync(cinfo->ack_lockres, DLM_LOCK_CR)) pr_err("md-cluster: failed to get a sync CR lock on ACK!(%d)\n", ret); + /* get sync CR lock on no-new-dev. */ + if (dlm_lock_sync(cinfo->no_new_dev_lockres, DLM_LOCK_CR)) + pr_err("md-cluster: failed to get a sync CR lock on no-new-dev!(%d)\n", ret); + pr_info("md-cluster: Joined cluster %s slot %d\n", str, cinfo->slot_number); snprintf(str, 64, "bitmap%04d", cinfo->slot_number - 1); @@ -621,6 +669,7 @@ err: lockres_free(cinfo->message_lockres); lockres_free(cinfo->token_lockres); lockres_free(cinfo->ack_lockres); + lockres_free(cinfo->no_new_dev_lockres); lockres_free(cinfo->bitmap_lockres); lockres_free(cinfo->sb_lock); if (cinfo->lockspace) @@ -642,6 +691,7 @@ static int leave(struct mddev *mddev) lockres_free(cinfo->message_lockres); lockres_free(cinfo->token_lockres); lockres_free(cinfo->ack_lockres); + lockres_free(cinfo->no_new_dev_lockres); lockres_free(cinfo->sb_lock); lockres_free(cinfo->bitmap_lockres); dlm_release_lockspace(cinfo->lockspace, 2); @@ -742,6 +792,55 @@ out: return ret; } +static int add_new_disk_start(struct mddev *mddev, struct md_rdev *rdev) +{ + struct md_cluster_info *cinfo = mddev->cluster_info; + struct cluster_msg cmsg; + int ret = 0; + struct mdp_superblock_1 *sb = page_address(rdev->sb_page); + char *uuid = sb->device_uuid; + + memset(&cmsg, 0, sizeof(cmsg)); + cmsg.type = cpu_to_le32(NEWDISK); + memcpy(cmsg.uuid, uuid, 16); + cmsg.raid_slot = rdev->desc_nr; + lock_comm(cinfo); + ret = __sendmsg(cinfo, &cmsg); + if (ret) + return ret; + cinfo->no_new_dev_lockres->flags |= DLM_LKF_NOQUEUE; + ret = dlm_lock_sync(cinfo->no_new_dev_lockres, DLM_LOCK_EX); + cinfo->no_new_dev_lockres->flags &= ~DLM_LKF_NOQUEUE; + /* Some node does not "see" the device */ + if (ret == -EAGAIN) + ret = -ENOENT; + else + dlm_lock_sync(cinfo->no_new_dev_lockres, DLM_LOCK_CR); + return ret; +} + +static int add_new_disk_finish(struct mddev *mddev) +{ + struct cluster_msg cmsg; + struct md_cluster_info *cinfo = mddev->cluster_info; + int ret; + /* Write sb and inform others */ + md_update_sb(mddev, 1); + cmsg.type = METADATA_UPDATED; + ret = __sendmsg(cinfo, &cmsg); + unlock_comm(cinfo); + return ret; +} + +static void new_disk_ack(struct mddev *mddev, bool ack) +{ + struct md_cluster_info *cinfo = mddev->cluster_info; + + if (ack) + dlm_unlock_sync(cinfo->no_new_dev_lockres); + complete(&cinfo->newdisk_completion); +} + static struct md_cluster_operations cluster_ops = { .join = join, .leave = leave, @@ -753,6 +852,9 @@ static struct md_cluster_operations cluster_ops = { .metadata_update_finish = metadata_update_finish, .metadata_update_cancel = metadata_update_cancel, .area_resyncing = area_resyncing, + .add_new_disk_start = add_new_disk_start, + .add_new_disk_finish = add_new_disk_finish, + .new_disk_ack = new_disk_ack, }; static int __init cluster_init(void) diff --git a/drivers/md/md-cluster.h b/drivers/md/md-cluster.h index 03785402afaa..60d7e58964f5 100644 --- a/drivers/md/md-cluster.h +++ b/drivers/md/md-cluster.h @@ -6,6 +6,7 @@ #include "md.h" struct mddev; +struct md_rdev; struct md_cluster_operations { int (*join)(struct mddev *mddev, int nodes); @@ -18,6 +19,9 @@ struct md_cluster_operations { int (*metadata_update_finish)(struct mddev *mddev); int (*metadata_update_cancel)(struct mddev *mddev); int (*area_resyncing)(struct mddev *mddev, sector_t lo, sector_t hi); + int (*add_new_disk_start)(struct mddev *mddev, struct md_rdev *rdev); + int (*add_new_disk_finish)(struct mddev *mddev); + void (*new_disk_ack)(struct mddev *mddev, bool ack); }; #endif /* _MD_CLUSTER_H */ diff --git a/drivers/md/md.c b/drivers/md/md.c index fe0484648de4..5703c2e89f3a 100644 --- a/drivers/md/md.c +++ b/drivers/md/md.c @@ -2210,7 +2210,7 @@ static void sync_sbs(struct mddev *mddev, int nospares) } } -static void md_update_sb(struct mddev *mddev, int force_change) +void md_update_sb(struct mddev *mddev, int force_change) { struct md_rdev *rdev; int sync_req; @@ -2371,6 +2371,7 @@ repeat: wake_up(&rdev->blocked_wait); } } +EXPORT_SYMBOL(md_update_sb); /* words written to sysfs files may, or may not, be \n terminated. * We want to accept with case. For this we use cmd_match. @@ -3151,7 +3152,7 @@ static void analyze_sbs(struct mddev *mddev) kick_rdev_from_array(rdev); continue; } - if (rdev != freshest) + if (rdev != freshest) { if (super_types[mddev->major_version]. validate_super(mddev, rdev)) { printk(KERN_WARNING "md: kicking non-fresh %s" @@ -3160,6 +3161,15 @@ static void analyze_sbs(struct mddev *mddev) kick_rdev_from_array(rdev); continue; } + /* No device should have a Candidate flag + * when reading devices + */ + if (test_bit(Candidate, &rdev->flags)) { + pr_info("md: kicking Cluster Candidate %s from array!\n", + bdevname(rdev->bdev, b)); + kick_rdev_from_array(rdev); + } + } if (mddev->level == LEVEL_MULTIPATH) { rdev->desc_nr = i++; rdev->raid_disk = rdev->desc_nr; @@ -5655,7 +5665,6 @@ static int get_array_info(struct mddev *mddev, void __user *arg) info.state |= (1<major,info->minor); + if (mddev_is_clustered(mddev) && + !(info->state & ((1 << MD_DISK_CLUSTER_ADD) | (1 << MD_DISK_CANDIDATE)))) { + pr_err("%s: Cannot add to clustered mddev. Try --cluster-add\n", + mdname(mddev)); + return -EINVAL; + } + if (info->major != MAJOR(dev) || info->minor != MINOR(dev)) return -EOVERFLOW; @@ -5830,6 +5846,25 @@ static int add_new_disk(struct mddev *mddev, mdu_disk_info_t *info) else clear_bit(WriteMostly, &rdev->flags); + /* + * check whether the device shows up in other nodes + */ + if (mddev_is_clustered(mddev)) { + if (info->state & (1 << MD_DISK_CANDIDATE)) { + /* Through --cluster-confirm */ + set_bit(Candidate, &rdev->flags); + md_cluster_ops->new_disk_ack(mddev, true); + } else if (info->state & (1 << MD_DISK_CLUSTER_ADD)) { + /* --add initiated by this node */ + err = md_cluster_ops->add_new_disk_start(mddev, rdev); + if (err) { + md_cluster_ops->add_new_disk_finish(mddev); + export_rdev(rdev); + return err; + } + } + } + rdev->raid_disk = -1; err = bind_rdev_to_array(rdev, mddev); if (!err && !mddev->pers->hot_remove_disk) { @@ -5855,6 +5890,9 @@ static int add_new_disk(struct mddev *mddev, mdu_disk_info_t *info) if (!err) md_new_event(mddev); md_wakeup_thread(mddev->thread); + if (mddev_is_clustered(mddev) && + (info->state & (1 << MD_DISK_CLUSTER_ADD))) + md_cluster_ops->add_new_disk_finish(mddev); return err; } @@ -6456,6 +6494,7 @@ static inline bool md_ioctl_valid(unsigned int cmd) case SET_DISK_FAULTY: case STOP_ARRAY: case STOP_ARRAY_RO: + case CLUSTERED_DISK_NACK: return true; default: return false; @@ -6728,6 +6767,13 @@ static int md_ioctl(struct block_device *bdev, fmode_t mode, goto unlock; } + case CLUSTERED_DISK_NACK: + if (mddev_is_clustered(mddev)) + md_cluster_ops->new_disk_ack(mddev, false); + else + err = -EINVAL; + goto unlock; + case HOT_ADD_DISK: err = hot_add_disk(mddev, new_decode_dev(arg)); goto unlock; diff --git a/drivers/md/md.h b/drivers/md/md.h index bfebcfdf54e6..6dc0ce09f50c 100644 --- a/drivers/md/md.h +++ b/drivers/md/md.h @@ -171,6 +171,10 @@ enum flag_bits { * a want_replacement device with same * raid_disk number. */ + Candidate, /* For clustered environments only: + * This device is seen locally but not + * by the whole cluster + */ }; #define BB_LEN_MASK (0x00000000000001FFULL) @@ -666,6 +670,7 @@ extern struct bio *bio_alloc_mddev(gfp_t gfp_mask, int nr_iovecs, extern void md_unplug(struct blk_plug_cb *cb, bool from_schedule); extern void md_reload_sb(struct mddev *mddev); +extern void md_update_sb(struct mddev *mddev, int force); static inline int mddev_check_plugged(struct mddev *mddev) { return !!blk_check_plugged(md_unplug, mddev, diff --git a/drivers/md/raid1.c b/drivers/md/raid1.c index f70d74189d16..53ed5d48308f 100644 --- a/drivers/md/raid1.c +++ b/drivers/md/raid1.c @@ -1571,6 +1571,7 @@ static int raid1_spare_active(struct mddev *mddev) struct md_rdev *rdev = conf->mirrors[i].rdev; struct md_rdev *repl = conf->mirrors[conf->raid_disks + i].rdev; if (repl + && !test_bit(Candidate, &repl->flags) && repl->recovery_offset == MaxSector && !test_bit(Faulty, &repl->flags) && !test_and_set_bit(In_sync, &repl->flags)) { diff --git a/include/uapi/linux/raid/md_p.h b/include/uapi/linux/raid/md_p.h index 643489d33e68..2ae6131e69a5 100644 --- a/include/uapi/linux/raid/md_p.h +++ b/include/uapi/linux/raid/md_p.h @@ -78,6 +78,12 @@ #define MD_DISK_ACTIVE 1 /* disk is running or spare disk */ #define MD_DISK_SYNC 2 /* disk is in sync with the raid set */ #define MD_DISK_REMOVED 3 /* disk is in sync with the raid set */ +#define MD_DISK_CLUSTER_ADD 4 /* Initiate a disk add across the cluster + * For clustered enviroments only. + */ +#define MD_DISK_CANDIDATE 5 /* disk is added as spare (local) until confirmed + * For clustered enviroments only. + */ #define MD_DISK_WRITEMOSTLY 9 /* disk is "write-mostly" is RAID1 config. * read requests will only be sent here in diff --git a/include/uapi/linux/raid/md_u.h b/include/uapi/linux/raid/md_u.h index 74e7c60c4716..1cb8aa6850b5 100644 --- a/include/uapi/linux/raid/md_u.h +++ b/include/uapi/linux/raid/md_u.h @@ -62,6 +62,7 @@ #define STOP_ARRAY _IO (MD_MAJOR, 0x32) #define STOP_ARRAY_RO _IO (MD_MAJOR, 0x33) #define RESTART_ARRAY_RW _IO (MD_MAJOR, 0x34) +#define CLUSTERED_DISK_NACK _IO (MD_MAJOR, 0x35) /* 63 partitions with the alternate major number (mdp) */ #define MdpMinorShift 6 -- cgit v1.2.3 From 56a215d66b1eee870a6b90a8c245dfc3e503a137 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Tue, 6 Jan 2015 20:56:05 +0100 Subject: ARM: shmobile: sh73a0 dtsi: Add missing INTCA0 clock for irqpin module This clock drives the irqpin controller modules. Before, it was assumed enabled by the bootloader or reset state. By making it available to the driver, we make sure it gets enabled when needed, and allow it to be managed by system or runtime PM. Signed-off-by: Geert Uytterhoeven Signed-off-by: Simon Horman --- arch/arm/boot/dts/sh73a0.dtsi | 15 +++++++++++++++ include/dt-bindings/clock/sh73a0-clock.h | 3 +++ 2 files changed, 18 insertions(+) (limited to 'include') diff --git a/arch/arm/boot/dts/sh73a0.dtsi b/arch/arm/boot/dts/sh73a0.dtsi index 076708797ef6..08f736d52179 100644 --- a/arch/arm/boot/dts/sh73a0.dtsi +++ b/arch/arm/boot/dts/sh73a0.dtsi @@ -94,6 +94,7 @@ 0 6 IRQ_TYPE_LEVEL_HIGH 0 7 IRQ_TYPE_LEVEL_HIGH 0 8 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&mstp5_clks SH73A0_CLK_INTCA0>; control-parent; }; @@ -114,6 +115,7 @@ 0 14 IRQ_TYPE_LEVEL_HIGH 0 15 IRQ_TYPE_LEVEL_HIGH 0 16 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&mstp5_clks SH73A0_CLK_INTCA0>; control-parent; }; @@ -134,6 +136,7 @@ 0 22 IRQ_TYPE_LEVEL_HIGH 0 23 IRQ_TYPE_LEVEL_HIGH 0 24 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&mstp5_clks SH73A0_CLK_INTCA0>; control-parent; }; @@ -154,6 +157,7 @@ 0 30 IRQ_TYPE_LEVEL_HIGH 0 31 IRQ_TYPE_LEVEL_HIGH 0 32 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&mstp5_clks SH73A0_CLK_INTCA0>; control-parent; }; @@ -698,5 +702,16 @@ clock-output-names = "iic3", "iic4", "keysc"; }; + mstp5_clks: mstp5_clks@e6150144 { + compatible = "renesas,sh73a0-mstp-clocks", "renesas,cpg-mstp-clocks"; + reg = <0xe6150144 4>, <0xe615003c 4>; + clocks = <&cpg_clocks SH73A0_CLK_HP>; + #clock-cells = <1>; + clock-indices = < + SH73A0_CLK_INTCA0 + >; + clock-output-names = + "intca0"; + }; }; }; diff --git a/include/dt-bindings/clock/sh73a0-clock.h b/include/dt-bindings/clock/sh73a0-clock.h index 1dd3eb2b7d90..53369568c24c 100644 --- a/include/dt-bindings/clock/sh73a0-clock.h +++ b/include/dt-bindings/clock/sh73a0-clock.h @@ -76,4 +76,7 @@ #define SH73A0_CLK_IIC4 10 #define SH73A0_CLK_KEYSC 3 +/* MSTP5 */ +#define SH73A0_CLK_INTCA0 8 + #endif -- cgit v1.2.3 From 41650f406cdc4a420d5c08e1b407f6420323a04b Mon Sep 17 00:00:00 2001 From: Sergei Shtylyov Date: Tue, 6 Jan 2015 00:33:25 +0300 Subject: ARM: shmobile: r8a7790: add CAN clocks The R-Car CAN controllers can derive the CAN bus clock not only from their peripheral clock input (clkp1) but also from the other internal clock (clkp2) and external clock fed on CAN_CLK pin. Describe those clocks in the device tree, along with the USB_EXTAL clock from which clkp2 is derived. Signed-off-by: Sergei Shtylyov Acked-by: Geert Uytterhoeven Signed-off-by: Simon Horman --- arch/arm/boot/dts/r8a7790.dtsi | 22 ++++++++++++++++++++-- include/dt-bindings/clock/r8a7790-clock.h | 1 + 2 files changed, 21 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/arch/arm/boot/dts/r8a7790.dtsi b/arch/arm/boot/dts/r8a7790.dtsi index 24de994443e1..e872854b4ba9 100644 --- a/arch/arm/boot/dts/r8a7790.dtsi +++ b/arch/arm/boot/dts/r8a7790.dtsi @@ -838,16 +838,34 @@ clock-output-names = "audio_clk_c"; }; + /* External USB clock - can be overridden by the board */ + usb_extal_clk: usb_extal_clk { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <48000000>; + clock-output-names = "usb_extal"; + }; + + /* External CAN clock */ + can_clk: can_clk { + compatible = "fixed-clock"; + #clock-cells = <0>; + /* This value must be overridden by the board. */ + clock-frequency = <0>; + clock-output-names = "can_clk"; + status = "disabled"; + }; + /* Special CPG clocks */ cpg_clocks: cpg_clocks@e6150000 { compatible = "renesas,r8a7790-cpg-clocks", "renesas,rcar-gen2-cpg-clocks"; reg = <0 0xe6150000 0 0x1000>; - clocks = <&extal_clk>; + clocks = <&extal_clk &usb_extal_clk>; #clock-cells = <1>; clock-output-names = "main", "pll0", "pll1", "pll3", "lb", "qspi", "sdh", "sd0", "sd1", - "z"; + "z", "rcan"; }; /* Variable factor clocks */ diff --git a/include/dt-bindings/clock/r8a7790-clock.h b/include/dt-bindings/clock/r8a7790-clock.h index 91940271cf83..ffa8c11be68a 100644 --- a/include/dt-bindings/clock/r8a7790-clock.h +++ b/include/dt-bindings/clock/r8a7790-clock.h @@ -21,6 +21,7 @@ #define R8A7790_CLK_SD0 7 #define R8A7790_CLK_SD1 8 #define R8A7790_CLK_Z 9 +#define R8A7790_CLK_RCAN 10 /* MSTP0 */ #define R8A7790_CLK_MSIOF0 0 -- cgit v1.2.3 From b324252cc18d52d5752942a8574c22229153c58f Mon Sep 17 00:00:00 2001 From: Sergei Shtylyov Date: Tue, 6 Jan 2015 01:24:08 +0300 Subject: ARM: shmobile: r8a7791: add CAN clocks The R-Car CAN controllers can derive the CAN bus clock not only from their peripheral clock input (clkp1) but also from the other internal clock (clkp2) and external clock fed on CAN_CLK pin. Describe those clocks in the device tree, along with the USB_EXTAL clock from which clkp2 is derived. Signed-off-by: Sergei Shtylyov Acked-by: Geert Uytterhoeven Signed-off-by: Simon Horman --- arch/arm/boot/dts/r8a7791.dtsi | 23 +++++++++++++++++++++-- include/dt-bindings/clock/r8a7791-clock.h | 1 + 2 files changed, 22 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/arch/arm/boot/dts/r8a7791.dtsi b/arch/arm/boot/dts/r8a7791.dtsi index afba8af1a5e4..1ebffef5b908 100644 --- a/arch/arm/boot/dts/r8a7791.dtsi +++ b/arch/arm/boot/dts/r8a7791.dtsi @@ -862,15 +862,34 @@ status = "disabled"; }; + /* External USB clock - can be overridden by the board */ + usb_extal_clk: usb_extal_clk { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <48000000>; + clock-output-names = "usb_extal"; + }; + + /* External CAN clock */ + can_clk: can_clk { + compatible = "fixed-clock"; + #clock-cells = <0>; + /* This value must be overridden by the board. */ + clock-frequency = <0>; + clock-output-names = "can_clk"; + status = "disabled"; + }; + /* Special CPG clocks */ cpg_clocks: cpg_clocks@e6150000 { compatible = "renesas,r8a7791-cpg-clocks", "renesas,rcar-gen2-cpg-clocks"; reg = <0 0xe6150000 0 0x1000>; - clocks = <&extal_clk>; + clocks = <&extal_clk &usb_extal_clk>; #clock-cells = <1>; clock-output-names = "main", "pll0", "pll1", "pll3", - "lb", "qspi", "sdh", "sd0", "z"; + "lb", "qspi", "sdh", "sd0", "z", + "rcan"; }; /* Variable factor clocks */ diff --git a/include/dt-bindings/clock/r8a7791-clock.h b/include/dt-bindings/clock/r8a7791-clock.h index f096f3f6c16a..a45a36307895 100644 --- a/include/dt-bindings/clock/r8a7791-clock.h +++ b/include/dt-bindings/clock/r8a7791-clock.h @@ -20,6 +20,7 @@ #define R8A7791_CLK_SDH 6 #define R8A7791_CLK_SD0 7 #define R8A7791_CLK_Z 8 +#define R8A7791_CLK_RCAN 9 /* MSTP0 */ #define R8A7791_CLK_MSIOF0 0 -- cgit v1.2.3 From ae65a8ae4c25c7ea01204c9d033cd47da3000cf8 Mon Sep 17 00:00:00 2001 From: Sergei Shtylyov Date: Tue, 30 Dec 2014 23:20:34 +0300 Subject: ARM: shmobile: r8a7791: add ADSP clocks Add the ADSP clocks to the CPG and MSTP5 nodes of the R8A7791 device tree. Based on the original patch by Konstantin Kozhevnikov . Signed-off-by: Sergei Shtylyov Acked-by: Geert Uytterhoeven Signed-off-by: Simon Horman --- arch/arm/boot/dts/r8a7791.dtsi | 11 +++++++---- include/dt-bindings/clock/r8a7791-clock.h | 2 ++ 2 files changed, 9 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/arch/arm/boot/dts/r8a7791.dtsi b/arch/arm/boot/dts/r8a7791.dtsi index ff49b95df0ec..1e593a2b55fa 100644 --- a/arch/arm/boot/dts/r8a7791.dtsi +++ b/arch/arm/boot/dts/r8a7791.dtsi @@ -909,7 +909,7 @@ #clock-cells = <1>; clock-output-names = "main", "pll0", "pll1", "pll3", "lb", "qspi", "sdh", "sd0", "z", - "rcan"; + "rcan", "adsp"; }; /* Variable factor clocks */ @@ -1164,13 +1164,16 @@ mstp5_clks: mstp5_clks@e6150144 { compatible = "renesas,r8a7791-mstp-clocks", "renesas,cpg-mstp-clocks"; reg = <0 0xe6150144 0 4>, <0 0xe615003c 0 4>; - clocks = <&hp_clk>, <&hp_clk>, <&extal_clk>, <&p_clk>; + clocks = <&hp_clk>, <&hp_clk>, <&cpg_clocks R8A7791_CLK_ADSP>, + <&extal_clk>, <&p_clk>; #clock-cells = <1>; clock-indices = < R8A7791_CLK_AUDIO_DMAC0 R8A7791_CLK_AUDIO_DMAC1 - R8A7791_CLK_THERMAL R8A7791_CLK_PWM + R8A7791_CLK_ADSP_MOD R8A7791_CLK_THERMAL + R8A7791_CLK_PWM >; - clock-output-names = "audmac0", "audmac1", "thermal", "pwm"; + clock-output-names = "audmac0", "audmac1", "adsp_mod", + "thermal", "pwm"; }; mstp7_clks: mstp7_clks@e615014c { compatible = "renesas,r8a7791-mstp-clocks", "renesas,cpg-mstp-clocks"; diff --git a/include/dt-bindings/clock/r8a7791-clock.h b/include/dt-bindings/clock/r8a7791-clock.h index a45a36307895..8fc5dc8faeea 100644 --- a/include/dt-bindings/clock/r8a7791-clock.h +++ b/include/dt-bindings/clock/r8a7791-clock.h @@ -21,6 +21,7 @@ #define R8A7791_CLK_SD0 7 #define R8A7791_CLK_Z 8 #define R8A7791_CLK_RCAN 9 +#define R8A7791_CLK_ADSP 10 /* MSTP0 */ #define R8A7791_CLK_MSIOF0 0 @@ -72,6 +73,7 @@ /* MSTP5 */ #define R8A7791_CLK_AUDIO_DMAC1 1 #define R8A7791_CLK_AUDIO_DMAC0 2 +#define R8A7791_CLK_ADSP_MOD 6 #define R8A7791_CLK_THERMAL 22 #define R8A7791_CLK_PWM 23 -- cgit v1.2.3 From 3453ca9e4f51a0e6865d303f4e428cb6f630594f Mon Sep 17 00:00:00 2001 From: Sergei Shtylyov Date: Tue, 30 Dec 2014 23:21:45 +0300 Subject: ARM: shmobile: r8a7790: add ADSP clocks Add the ADSP clocks to the CPG and MSTP5 nodes of the R8A7790 device tree. Based on the original patch by Konstantin Kozhevnikov . Signed-off-by: Sergei Shtylyov Acked-by: Geert Uytterhoeven Signed-off-by: Simon Horman --- arch/arm/boot/dts/r8a7790.dtsi | 11 +++++++---- include/dt-bindings/clock/r8a7790-clock.h | 2 ++ 2 files changed, 9 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/arch/arm/boot/dts/r8a7790.dtsi b/arch/arm/boot/dts/r8a7790.dtsi index cd7fc05f5e99..c6c0a0c8f1be 100644 --- a/arch/arm/boot/dts/r8a7790.dtsi +++ b/arch/arm/boot/dts/r8a7790.dtsi @@ -885,7 +885,7 @@ #clock-cells = <1>; clock-output-names = "main", "pll0", "pll1", "pll3", "lb", "qspi", "sdh", "sd0", "sd1", - "z", "rcan"; + "z", "rcan", "adsp"; }; /* Variable factor clocks */ @@ -1159,13 +1159,16 @@ mstp5_clks: mstp5_clks@e6150144 { compatible = "renesas,r8a7790-mstp-clocks", "renesas,cpg-mstp-clocks"; reg = <0 0xe6150144 0 4>, <0 0xe615003c 0 4>; - clocks = <&hp_clk>, <&hp_clk>, <&extal_clk>, <&p_clk>; + clocks = <&hp_clk>, <&hp_clk>, <&cpg_clocks R8A7790_CLK_ADSP>, + <&extal_clk>, <&p_clk>; #clock-cells = <1>; clock-indices = < R8A7790_CLK_AUDIO_DMAC0 R8A7790_CLK_AUDIO_DMAC1 - R8A7790_CLK_THERMAL R8A7790_CLK_PWM + R8A7790_CLK_ADSP_MOD R8A7790_CLK_THERMAL + R8A7790_CLK_PWM >; - clock-output-names = "audmac0", "audmac1", "thermal", "pwm"; + clock-output-names = "audmac0", "audmac1", "adsp_mod", + "thermal", "pwm"; }; mstp7_clks: mstp7_clks@e615014c { compatible = "renesas,r8a7790-mstp-clocks", "renesas,cpg-mstp-clocks"; diff --git a/include/dt-bindings/clock/r8a7790-clock.h b/include/dt-bindings/clock/r8a7790-clock.h index ffa8c11be68a..3f2c6b198d4a 100644 --- a/include/dt-bindings/clock/r8a7790-clock.h +++ b/include/dt-bindings/clock/r8a7790-clock.h @@ -22,6 +22,7 @@ #define R8A7790_CLK_SD1 8 #define R8A7790_CLK_Z 9 #define R8A7790_CLK_RCAN 10 +#define R8A7790_CLK_ADSP 11 /* MSTP0 */ #define R8A7790_CLK_MSIOF0 0 @@ -81,6 +82,7 @@ /* MSTP5 */ #define R8A7790_CLK_AUDIO_DMAC1 1 #define R8A7790_CLK_AUDIO_DMAC0 2 +#define R8A7790_CLK_ADSP_MOD 6 #define R8A7790_CLK_THERMAL 22 #define R8A7790_CLK_PWM 23 -- cgit v1.2.3 From bdba0101c7e90e60481018005654227bdfd67aec Mon Sep 17 00:00:00 2001 From: Ulrich Hecht Date: Tue, 20 Jan 2015 13:51:38 +0100 Subject: ARM: shmobile: r8a73a4: Add CPG register bits header Signed-off-by: Ulrich Hecht Acked-by: Geert Uytterhoeven Acked-by: Laurent Pinchart Signed-off-by: Geert Uytterhoeven Signed-off-by: Simon Horman --- include/dt-bindings/clock/r8a73a4-clock.h | 62 +++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) create mode 100644 include/dt-bindings/clock/r8a73a4-clock.h (limited to 'include') diff --git a/include/dt-bindings/clock/r8a73a4-clock.h b/include/dt-bindings/clock/r8a73a4-clock.h new file mode 100644 index 000000000000..9a4b4c9ca44a --- /dev/null +++ b/include/dt-bindings/clock/r8a73a4-clock.h @@ -0,0 +1,62 @@ +/* + * Copyright 2014 Ulrich Hecht + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#ifndef __DT_BINDINGS_CLOCK_R8A73A4_H__ +#define __DT_BINDINGS_CLOCK_R8A73A4_H__ + +/* CPG */ +#define R8A73A4_CLK_MAIN 0 +#define R8A73A4_CLK_PLL0 1 +#define R8A73A4_CLK_PLL1 2 +#define R8A73A4_CLK_PLL2 3 +#define R8A73A4_CLK_PLL2S 4 +#define R8A73A4_CLK_PLL2H 5 +#define R8A73A4_CLK_Z 6 +#define R8A73A4_CLK_Z2 7 +#define R8A73A4_CLK_I 8 +#define R8A73A4_CLK_M3 9 +#define R8A73A4_CLK_B 10 +#define R8A73A4_CLK_M1 11 +#define R8A73A4_CLK_M2 12 +#define R8A73A4_CLK_ZX 13 +#define R8A73A4_CLK_ZS 14 +#define R8A73A4_CLK_HP 15 + +/* MSTP2 */ +#define R8A73A4_CLK_DMAC 18 +#define R8A73A4_CLK_SCIFB3 17 +#define R8A73A4_CLK_SCIFB2 16 +#define R8A73A4_CLK_SCIFB1 7 +#define R8A73A4_CLK_SCIFB0 6 +#define R8A73A4_CLK_SCIFA0 4 +#define R8A73A4_CLK_SCIFA1 3 + +/* MSTP3 */ +#define R8A73A4_CLK_CMT1 29 +#define R8A73A4_CLK_IIC1 23 +#define R8A73A4_CLK_IIC0 18 +#define R8A73A4_CLK_IIC7 17 +#define R8A73A4_CLK_IIC6 16 +#define R8A73A4_CLK_MMCIF0 15 +#define R8A73A4_CLK_SDHI0 14 +#define R8A73A4_CLK_SDHI1 13 +#define R8A73A4_CLK_SDHI2 12 +#define R8A73A4_CLK_MMCIF1 5 +#define R8A73A4_CLK_IIC2 0 + +/* MSTP4 */ +#define R8A73A4_CLK_IIC3 11 +#define R8A73A4_CLK_IIC4 10 +#define R8A73A4_CLK_IIC5 9 + +/* MSTP5 */ +#define R8A73A4_CLK_THERMAL 22 +#define R8A73A4_CLK_IIC8 15 + +#endif /* __DT_BINDINGS_CLOCK_R8A73A4_H__ */ -- cgit v1.2.3 From 83054671d28db9f1dbac1d3d1bf3b50b128e06ba Mon Sep 17 00:00:00 2001 From: Ulrich Hecht Date: Mon, 16 Feb 2015 17:58:46 +0100 Subject: ARM: shmobile: r8a7778: add CPG register bits header Enumerates CPG driver custom clocks and MSTP clock enable bits. Signed-off-by: Ulrich Hecht Acked-by: Laurent Pinchart Acked-by: Geert Uytterhoeven Signed-off-by: Simon Horman --- include/dt-bindings/clock/r8a7778-clock.h | 71 +++++++++++++++++++++++++++++++ 1 file changed, 71 insertions(+) create mode 100644 include/dt-bindings/clock/r8a7778-clock.h (limited to 'include') diff --git a/include/dt-bindings/clock/r8a7778-clock.h b/include/dt-bindings/clock/r8a7778-clock.h new file mode 100644 index 000000000000..f6b07c5399de --- /dev/null +++ b/include/dt-bindings/clock/r8a7778-clock.h @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2014 Ulrich Hecht + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#ifndef __DT_BINDINGS_CLOCK_R8A7778_H__ +#define __DT_BINDINGS_CLOCK_R8A7778_H__ + +/* CPG */ +#define R8A7778_CLK_PLLA 0 +#define R8A7778_CLK_PLLB 1 +#define R8A7778_CLK_B 2 +#define R8A7778_CLK_OUT 3 +#define R8A7778_CLK_P 4 +#define R8A7778_CLK_S 5 +#define R8A7778_CLK_S1 6 + +/* MSTP0 */ +#define R8A7778_CLK_I2C0 30 +#define R8A7778_CLK_I2C1 29 +#define R8A7778_CLK_I2C2 28 +#define R8A7778_CLK_I2C3 27 +#define R8A7778_CLK_SCIF0 26 +#define R8A7778_CLK_SCIF1 25 +#define R8A7778_CLK_SCIF2 24 +#define R8A7778_CLK_SCIF3 23 +#define R8A7778_CLK_SCIF4 22 +#define R8A7778_CLK_SCIF5 21 +#define R8A7778_CLK_TMU0 16 +#define R8A7778_CLK_TMU1 15 +#define R8A7778_CLK_TMU2 14 +#define R8A7778_CLK_SSI0 12 +#define R8A7778_CLK_SSI1 11 +#define R8A7778_CLK_SSI2 10 +#define R8A7778_CLK_SSI3 9 +#define R8A7778_CLK_SRU 8 +#define R8A7778_CLK_HSPI 7 + +/* MSTP1 */ +#define R8A7778_CLK_ETHER 14 +#define R8A7778_CLK_VIN0 10 +#define R8A7778_CLK_VIN1 9 +#define R8A7778_CLK_USB 0 + +/* MSTP3 */ +#define R8A7778_CLK_MMC 31 +#define R8A7778_CLK_SDHI0 23 +#define R8A7778_CLK_SDHI1 22 +#define R8A7778_CLK_SDHI2 21 +#define R8A7778_CLK_SSI4 11 +#define R8A7778_CLK_SSI5 10 +#define R8A7778_CLK_SSI6 9 +#define R8A7778_CLK_SSI7 8 +#define R8A7778_CLK_SSI8 7 + +/* MSTP5 */ +#define R8A7778_CLK_SRU_SRC0 31 +#define R8A7778_CLK_SRU_SRC1 30 +#define R8A7778_CLK_SRU_SRC2 29 +#define R8A7778_CLK_SRU_SRC3 28 +#define R8A7778_CLK_SRU_SRC4 27 +#define R8A7778_CLK_SRU_SRC5 26 +#define R8A7778_CLK_SRU_SRC6 25 +#define R8A7778_CLK_SRU_SRC7 24 +#define R8A7778_CLK_SRU_SRC8 23 + +#endif /* __DT_BINDINGS_CLOCK_R8A7778_H__ */ -- cgit v1.2.3 From 1af434a92871af93d97ce28e35497532a4167a0c Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Sun, 22 Feb 2015 12:24:19 +0100 Subject: drm/atomic-helper: Rename commmit_post/pre_planes These names only make sense because of backwards compatability with the order used by the crtc helper library. There's not really any real requirement in the ordering here. So rename them to something more descriptive and update the kerneldoc a bit. Motivated in a discussion with Laurent about how to restore plane state for dpms for drivers with runtime pm. v2: Squash in fixup from Stephen Rothwell to fix a conflict with tegra. Cc: Laurent Pinchart Reviewed-by: Rob Clark Acked-by: Laurent Pinchart Signed-off-by: Daniel Vetter --- drivers/gpu/drm/drm_atomic_helper.c | 40 +++++++++++++++++++++++-------------- drivers/gpu/drm/i915/intel_atomic.c | 4 ++-- drivers/gpu/drm/msm/msm_atomic.c | 4 ++-- drivers/gpu/drm/tegra/drm.c | 4 ++-- include/drm/drm_atomic_helper.h | 6 +++--- 5 files changed, 34 insertions(+), 24 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c index 65b9adf5777c..9c528bbcd8d2 100644 --- a/drivers/gpu/drm/drm_atomic_helper.c +++ b/drivers/gpu/drm/drm_atomic_helper.c @@ -768,34 +768,44 @@ crtc_set_mode(struct drm_device *dev, struct drm_atomic_state *old_state) } /** - * drm_atomic_helper_commit_pre_planes - modeset commit before plane updates + * drm_atomic_helper_commit_modeset_disables - modeset commit to disable outputs * @dev: DRM device * @old_state: atomic state object with old state structures * - * This function commits the modeset changes that need to be committed before - * updating planes. It shuts down all the outputs that need to be shut down and + * This function shuts down all the outputs that need to be shut down and * prepares them (if required) with the new mode. + * + * For compatability with legacy crtc helpers this should be called before + * drm_atomic_helper_commit_planes(), which is what the default commit function + * does. But drivers with different needs can group the modeset commits together + * and do the plane commits at the end. This is useful for drivers doing runtime + * PM since planes updates then only happen when the CRTC is actually enabled. */ -void drm_atomic_helper_commit_pre_planes(struct drm_device *dev, - struct drm_atomic_state *old_state) +void drm_atomic_helper_commit_modeset_disables(struct drm_device *dev, + struct drm_atomic_state *old_state) { disable_outputs(dev, old_state); set_routing_links(dev, old_state); crtc_set_mode(dev, old_state); } -EXPORT_SYMBOL(drm_atomic_helper_commit_pre_planes); +EXPORT_SYMBOL(drm_atomic_helper_commit_modeset_disables); /** - * drm_atomic_helper_commit_post_planes - modeset commit after plane updates + * drm_atomic_helper_commit_modeset_enables - modeset commit to enable outputs * @dev: DRM device * @old_state: atomic state object with old state structures * - * This function commits the modeset changes that need to be committed after - * updating planes: It enables all the outputs with the new configuration which - * had to be turned off for the update. + * This function enables all the outputs with the new configuration which had to + * be turned off for the update. + * + * For compatability with legacy crtc helpers this should be called after + * drm_atomic_helper_commit_planes(), which is what the default commit function + * does. But drivers with different needs can group the modeset commits together + * and do the plane commits at the end. This is useful for drivers doing runtime + * PM since planes updates then only happen when the CRTC is actually enabled. */ -void drm_atomic_helper_commit_post_planes(struct drm_device *dev, - struct drm_atomic_state *old_state) +void drm_atomic_helper_commit_modeset_enables(struct drm_device *dev, + struct drm_atomic_state *old_state) { int ncrtcs = old_state->dev->mode_config.num_crtc; int i; @@ -861,7 +871,7 @@ void drm_atomic_helper_commit_post_planes(struct drm_device *dev, encoder->bridge->funcs->enable(encoder->bridge); } } -EXPORT_SYMBOL(drm_atomic_helper_commit_post_planes); +EXPORT_SYMBOL(drm_atomic_helper_commit_modeset_enables); static void wait_for_fences(struct drm_device *dev, struct drm_atomic_state *state) @@ -1030,11 +1040,11 @@ int drm_atomic_helper_commit(struct drm_device *dev, wait_for_fences(dev, state); - drm_atomic_helper_commit_pre_planes(dev, state); + drm_atomic_helper_commit_modeset_disables(dev, state); drm_atomic_helper_commit_planes(dev, state); - drm_atomic_helper_commit_post_planes(dev, state); + drm_atomic_helper_commit_modeset_enables(dev, state); drm_atomic_helper_wait_for_vblanks(dev, state); diff --git a/drivers/gpu/drm/i915/intel_atomic.c b/drivers/gpu/drm/i915/intel_atomic.c index 19a9dd5408f3..011b8960fd75 100644 --- a/drivers/gpu/drm/i915/intel_atomic.c +++ b/drivers/gpu/drm/i915/intel_atomic.c @@ -134,9 +134,9 @@ int intel_atomic_commit(struct drm_device *dev, * FIXME: The proper sequence here will eventually be: * * drm_atomic_helper_swap_state(dev, state) - * drm_atomic_helper_commit_pre_planes(dev, state); + * drm_atomic_helper_commit_modeset_disables(dev, state); * drm_atomic_helper_commit_planes(dev, state); - * drm_atomic_helper_commit_post_planes(dev, state); + * drm_atomic_helper_commit_modeset_enables(dev, state); * drm_atomic_helper_wait_for_vblanks(dev, state); * drm_atomic_helper_cleanup_planes(dev, state); * drm_atomic_state_free(state); diff --git a/drivers/gpu/drm/msm/msm_atomic.c b/drivers/gpu/drm/msm/msm_atomic.c index 871aa2108dc6..7c412292a0ff 100644 --- a/drivers/gpu/drm/msm/msm_atomic.c +++ b/drivers/gpu/drm/msm/msm_atomic.c @@ -96,11 +96,11 @@ static void complete_commit(struct msm_commit *c) kms->funcs->prepare_commit(kms, state); - drm_atomic_helper_commit_pre_planes(dev, state); + drm_atomic_helper_commit_modeset_disables(dev, state); drm_atomic_helper_commit_planes(dev, state); - drm_atomic_helper_commit_post_planes(dev, state); + drm_atomic_helper_commit_modeset_enables(dev, state); /* NOTE: _wait_for_vblanks() only waits for vblank on * enabled CRTCs. So we end up faulting when disabling diff --git a/drivers/gpu/drm/tegra/drm.c b/drivers/gpu/drm/tegra/drm.c index 7dd328d77996..5f1880766110 100644 --- a/drivers/gpu/drm/tegra/drm.c +++ b/drivers/gpu/drm/tegra/drm.c @@ -55,9 +55,9 @@ static void tegra_atomic_complete(struct tegra_drm *tegra, * current layout. */ - drm_atomic_helper_commit_pre_planes(drm, state); + drm_atomic_helper_commit_modeset_disables(drm, state); drm_atomic_helper_commit_planes(drm, state); - drm_atomic_helper_commit_post_planes(drm, state); + drm_atomic_helper_commit_modeset_enables(drm, state); drm_atomic_helper_wait_for_vblanks(drm, state); diff --git a/include/drm/drm_atomic_helper.h b/include/drm/drm_atomic_helper.h index 8039d54a7441..829280b56874 100644 --- a/include/drm/drm_atomic_helper.h +++ b/include/drm/drm_atomic_helper.h @@ -43,9 +43,9 @@ int drm_atomic_helper_commit(struct drm_device *dev, void drm_atomic_helper_wait_for_vblanks(struct drm_device *dev, struct drm_atomic_state *old_state); -void drm_atomic_helper_commit_pre_planes(struct drm_device *dev, - struct drm_atomic_state *state); -void drm_atomic_helper_commit_post_planes(struct drm_device *dev, +void drm_atomic_helper_commit_modeset_disables(struct drm_device *dev, + struct drm_atomic_state *state); +void drm_atomic_helper_commit_modeset_enables(struct drm_device *dev, struct drm_atomic_state *old_state); int drm_atomic_helper_prepare_planes(struct drm_device *dev, -- cgit v1.2.3 From c982bd90f58681d00363538167477e60e2c8f731 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Sun, 22 Feb 2015 12:24:20 +0100 Subject: drm/atomic-helpers: make mode_set hooks optional With runtime PM the hw might still be off while doing the ->mode_set callbacks - runtime PM get/put should only happen in the enable/disable hooks to properly support DPMS. Which essentially makes these callbacks useless for drivers support runtime PM, so make them optional. Again motivated by discussions with Laurent. Cc: Laurent Pinchart Acked-by: Laurent Pinchart Signed-off-by: Daniel Vetter --- drivers/gpu/drm/drm_atomic_helper.c | 5 +++-- include/drm/drm_crtc_helper.h | 3 ++- 2 files changed, 5 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c index 9c528bbcd8d2..28aa87510551 100644 --- a/drivers/gpu/drm/drm_atomic_helper.c +++ b/drivers/gpu/drm/drm_atomic_helper.c @@ -723,7 +723,7 @@ crtc_set_mode(struct drm_device *dev, struct drm_atomic_state *old_state) funcs = crtc->helper_private; - if (crtc->state->enable) { + if (crtc->state->enable && funcs->mode_set_nofb) { DRM_DEBUG_ATOMIC("modeset on [CRTC:%d]\n", crtc->base.id); @@ -759,7 +759,8 @@ crtc_set_mode(struct drm_device *dev, struct drm_atomic_state *old_state) * Each encoder has at most one connector (since we always steal * it away), so we won't call call mode_set hooks twice. */ - funcs->mode_set(encoder, mode, adjusted_mode); + if (funcs->mode_set) + funcs->mode_set(encoder, mode, adjusted_mode); if (encoder->bridge && encoder->bridge->funcs->mode_set) encoder->bridge->funcs->mode_set(encoder->bridge, diff --git a/include/drm/drm_crtc_helper.h b/include/drm/drm_crtc_helper.h index c250a22b39ab..92d5135b55d2 100644 --- a/include/drm/drm_crtc_helper.h +++ b/include/drm/drm_crtc_helper.h @@ -89,6 +89,7 @@ struct drm_crtc_helper_funcs { int (*mode_set)(struct drm_crtc *crtc, struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode, int x, int y, struct drm_framebuffer *old_fb); + /* Actually set the mode for atomic helpers, optional */ void (*mode_set_nofb)(struct drm_crtc *crtc); /* Move the crtc on the current fb to the given position *optional* */ @@ -119,7 +120,7 @@ struct drm_crtc_helper_funcs { * @mode_fixup: try to fixup proposed mode for this connector * @prepare: part of the disable sequence, called before the CRTC modeset * @commit: called after the CRTC modeset - * @mode_set: set this mode + * @mode_set: set this mode, optional for atomic helpers * @get_crtc: return CRTC that the encoder is currently attached to * @detect: connection status detection * @disable: disable encoder when not in use (overrides DPMS off) -- cgit v1.2.3 From e045d20bef41707dbba676e58624b54f9f39e172 Mon Sep 17 00:00:00 2001 From: Sonika Jindal Date: Thu, 19 Feb 2015 13:16:44 +0530 Subject: drm: Adding edp1.4 specific dpcd macros Adding dpcd macros related to edp1.4 and link rates v2: Added DP_SUPPORTED_LINK_RATES macros Signed-off-by: Sonika Jindal Reviewed-by: Todd Previte Signed-off-by: Daniel Vetter --- include/drm/drm_dp_helper.h | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'include') diff --git a/include/drm/drm_dp_helper.h b/include/drm/drm_dp_helper.h index 7e25030a6aa2..d4803224028f 100644 --- a/include/drm/drm_dp_helper.h +++ b/include/drm/drm_dp_helper.h @@ -92,6 +92,9 @@ # define DP_MSA_TIMING_PAR_IGNORED (1 << 6) /* eDP */ # define DP_OUI_SUPPORT (1 << 7) +#define DP_SUPPORTED_LINK_RATES 0x010 /*eDP 1.4*/ +#define DP_MAX_SUPPORTED_RATES 0x8 + #define DP_I2C_SPEED_CAP 0x00c /* DPI */ # define DP_I2C_SPEED_1K 0x01 # define DP_I2C_SPEED_5K 0x02 @@ -101,6 +104,7 @@ # define DP_I2C_SPEED_1M 0x20 #define DP_EDP_CONFIGURATION_CAP 0x00d /* XXX 1.2? */ +# define DP_DPCD_DISPLAY_CONTROL_CAPABLE (1 << 3) /* edp v1.2 or higher */ #define DP_TRAINING_AUX_RD_INTERVAL 0x00e /* XXX 1.2? */ /* Multiple stream transport */ @@ -221,6 +225,8 @@ # define DP_UP_REQ_EN (1 << 1) # define DP_UPSTREAM_IS_SRC (1 << 2) +#define DP_LINK_RATE_SET 0x115 + #define DP_PSR_EN_CFG 0x170 /* XXX 1.2? */ # define DP_PSR_ENABLE (1 << 0) # define DP_PSR_MAIN_LINK_ACTIVE (1 << 1) @@ -332,6 +338,8 @@ # define DP_SET_POWER_D3 0x2 # define DP_SET_POWER_MASK 0x3 +#define DP_EDP_DPCD_REV 0x700 + #define DP_SIDEBAND_MSG_DOWN_REQ_BASE 0x1000 /* 1.2 MST */ #define DP_SIDEBAND_MSG_UP_REP_BASE 0x1200 /* 1.2 MST */ #define DP_SIDEBAND_MSG_DOWN_REP_BASE 0x1400 /* 1.2 MST */ -- cgit v1.2.3 From f23e860edbb3f2208c0ab3448e756689bb4a3760 Mon Sep 17 00:00:00 2001 From: Nicolin Chen Date: Sat, 14 Feb 2015 17:22:49 -0800 Subject: ASoC: core: Add extra dapm properties for Device Tree The current helper functions, snd_soc_of_parse_audio_simple_widgets() and snd_soc_of_parse_audio_routing(), set dapm_widgets and dapm_routes without caring if they are already set by using build-in widgets and routes in the card driver. So there could be one of them, build-in one or Device Tree one, overrided by the other depending on which one was assigned later. This patch adds an extra pair of dapm_widgets and dapm_routes for DT use only so as to prevent unexpected overriding. Signed-off-by: Nicolin Chen Signed-off-by: Mark Brown --- include/sound/soc.h | 5 +++++ sound/soc/soc-core.c | 16 ++++++++++++---- 2 files changed, 17 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/include/sound/soc.h b/include/sound/soc.h index 0d1ade195628..f66a1ef98a40 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -1071,11 +1071,16 @@ struct snd_soc_card { /* * Card-specific routes and widgets. + * Note: of_dapm_xxx for Device Tree; Otherwise for driver build-in. */ const struct snd_soc_dapm_widget *dapm_widgets; int num_dapm_widgets; const struct snd_soc_dapm_route *dapm_routes; int num_dapm_routes; + const struct snd_soc_dapm_widget *of_dapm_widgets; + int num_of_dapm_widgets; + const struct snd_soc_dapm_route *of_dapm_routes; + int num_of_dapm_routes; bool fully_routed; struct work_struct deferred_resume_work; diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 30579ca5bacb..5c0658d49609 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -1561,6 +1561,10 @@ static int snd_soc_instantiate_card(struct snd_soc_card *card) snd_soc_dapm_new_controls(&card->dapm, card->dapm_widgets, card->num_dapm_widgets); + if (card->of_dapm_widgets) + snd_soc_dapm_new_controls(&card->dapm, card->of_dapm_widgets, + card->num_of_dapm_widgets); + /* initialise the sound card only once */ if (card->probe) { ret = card->probe(card); @@ -1616,6 +1620,10 @@ static int snd_soc_instantiate_card(struct snd_soc_card *card) snd_soc_dapm_add_routes(&card->dapm, card->dapm_routes, card->num_dapm_routes); + if (card->of_dapm_routes) + snd_soc_dapm_add_routes(&card->dapm, card->of_dapm_routes, + card->num_of_dapm_routes); + for (i = 0; i < card->num_links; i++) { if (card->dai_link[i].dai_fmt) snd_soc_runtime_set_dai_fmt(&card->rtd[i], @@ -3223,8 +3231,8 @@ int snd_soc_of_parse_audio_simple_widgets(struct snd_soc_card *card, widgets[i].name = wname; } - card->dapm_widgets = widgets; - card->num_dapm_widgets = num_widgets; + card->of_dapm_widgets = widgets; + card->num_of_dapm_widgets = num_widgets; return 0; } @@ -3308,8 +3316,8 @@ int snd_soc_of_parse_audio_routing(struct snd_soc_card *card, } } - card->num_dapm_routes = num_routes; - card->dapm_routes = routes; + card->num_of_dapm_routes = num_routes; + card->of_dapm_routes = routes; return 0; } -- cgit v1.2.3 From 0c571785813cf3294cdbb1f2fb1a9b19e11935f6 Mon Sep 17 00:00:00 2001 From: Jacek Anaszewski Date: Fri, 13 Feb 2015 08:06:47 -0800 Subject: leds: flash: remove stray include directive Avoid including v4l2-controls.h, as this is stray code from the early versions of the LED / V4L2 flash API integration patches. LED Flash class doesn't depend on V4L2 subsystem. Signed-off-by: Jacek Anaszewski Signed-off-by: Bryan Wu --- include/linux/led-class-flash.h | 1 - 1 file changed, 1 deletion(-) (limited to 'include') diff --git a/include/linux/led-class-flash.h b/include/linux/led-class-flash.h index 5ba2facd7a51..3b5b9643cea1 100644 --- a/include/linux/led-class-flash.h +++ b/include/linux/led-class-flash.h @@ -13,7 +13,6 @@ #define __LINUX_FLASH_LEDS_H_INCLUDED #include -#include struct device_node; struct led_classdev_flash; -- cgit v1.2.3 From 14c9551a32eba086c9f20c9d6a8e378481f15333 Mon Sep 17 00:00:00 2001 From: Mahesh Bandewar Date: Mon, 23 Feb 2015 17:50:11 -0800 Subject: bonding: Implement port churn-machine (AD standard 43.4.17). The Churn Detection machines detect the situation where a port is operable, but the Actor and Partner have not attached the link to an Aggregator and brought the link into operation within a bound time period. Under normal operation of the LACP, agreement between Actor and Partner should be reached very rapidly. Continued failure to reach agreement can be symptomatic of device failure. Actor-churn-detection state-machine Reviewed-by: Nikolay Aleksandrov =================================== BEGIN=True + PortEnable=False | v +------------------------+ ActorPort.Sync=True +------------------+ | ACTOR_CHURN_MONITOR | ---------------------> | NO_ACTOR_CHURN | |========================| |==================| | ActorChurn=False | ActorPort.Sync=False | ActorChurn=False | | ActorChurn.Timer=Start | <--------------------- | | +------------------------+ +------------------+ | ^ | | ActorChurn.Timer=Expired | | ActorPort.Sync=True | | | +-----------------+ | | | ACTOR_CHURN | | | |=================| | +--------------> | ActorChurn=True | ------------+ | | +-----------------+ Similar for the Partner-churn-detection. Signed-off-by: Mahesh Bandewar Signed-off-by: David S. Miller --- drivers/net/bonding/bond_3ad.c | 57 +++++++++++++++++++++++++++++++++++++-- drivers/net/bonding/bond_procfs.c | 43 +++++++++++++++++++++++++---- include/net/bond_3ad.h | 29 ++++++++++++++++++++ 3 files changed, 122 insertions(+), 7 deletions(-) (limited to 'include') diff --git a/drivers/net/bonding/bond_3ad.c b/drivers/net/bonding/bond_3ad.c index 9b436696b95e..f61b2870cddf 100644 --- a/drivers/net/bonding/bond_3ad.c +++ b/drivers/net/bonding/bond_3ad.c @@ -38,6 +38,7 @@ #define AD_STANDBY 0x2 #define AD_MAX_TX_IN_SECOND 3 #define AD_COLLECTOR_MAX_DELAY 0 +#define AD_MONITOR_CHURNED 0x1000 /* Timer definitions (43.4.4 in the 802.3ad standard) */ #define AD_FAST_PERIODIC_TIME 1 @@ -1013,16 +1014,19 @@ static void ad_rx_machine(struct lacpdu *lacpdu, struct port *port) /* check if state machine should change state */ /* first, check if port was reinitialized */ - if (port->sm_vars & AD_PORT_BEGIN) + if (port->sm_vars & AD_PORT_BEGIN) { port->sm_rx_state = AD_RX_INITIALIZE; + port->sm_vars |= AD_MONITOR_CHURNED; /* check if port is not enabled */ - else if (!(port->sm_vars & AD_PORT_BEGIN) + } else if (!(port->sm_vars & AD_PORT_BEGIN) && !port->is_enabled && !(port->sm_vars & AD_PORT_MOVED)) port->sm_rx_state = AD_RX_PORT_DISABLED; /* check if new lacpdu arrived */ else if (lacpdu && ((port->sm_rx_state == AD_RX_EXPIRED) || (port->sm_rx_state == AD_RX_DEFAULTED) || (port->sm_rx_state == AD_RX_CURRENT))) { + if (port->sm_rx_state != AD_RX_CURRENT) + port->sm_vars |= AD_MONITOR_CHURNED; port->sm_rx_timer_counter = 0; port->sm_rx_state = AD_RX_CURRENT; } else { @@ -1100,9 +1104,11 @@ static void ad_rx_machine(struct lacpdu *lacpdu, struct port *port) */ port->partner_oper.port_state &= ~AD_STATE_SYNCHRONIZATION; port->sm_vars &= ~AD_PORT_MATCHED; + port->partner_oper.port_state |= AD_STATE_LACP_TIMEOUT; port->partner_oper.port_state |= AD_STATE_LACP_ACTIVITY; port->sm_rx_timer_counter = __ad_timer_to_ticks(AD_CURRENT_WHILE_TIMER, (u16)(AD_SHORT_TIMEOUT)); port->actor_oper_port_state |= AD_STATE_EXPIRED; + port->sm_vars |= AD_MONITOR_CHURNED; break; case AD_RX_DEFAULTED: __update_default_selected(port); @@ -1131,6 +1137,45 @@ static void ad_rx_machine(struct lacpdu *lacpdu, struct port *port) } } +/** + * ad_churn_machine - handle port churn's state machine + * @port: the port we're looking at + * + */ +static void ad_churn_machine(struct port *port) +{ + if (port->sm_vars & AD_MONITOR_CHURNED) { + port->sm_vars &= ~AD_MONITOR_CHURNED; + port->sm_churn_actor_state = AD_CHURN_MONITOR; + port->sm_churn_partner_state = AD_CHURN_MONITOR; + port->sm_churn_actor_timer_counter = + __ad_timer_to_ticks(AD_ACTOR_CHURN_TIMER, 0); + port->sm_churn_partner_timer_counter = + __ad_timer_to_ticks(AD_PARTNER_CHURN_TIMER, 0); + return; + } + if (port->sm_churn_actor_timer_counter && + !(--port->sm_churn_actor_timer_counter) && + port->sm_churn_actor_state == AD_CHURN_MONITOR) { + if (port->actor_oper_port_state & AD_STATE_SYNCHRONIZATION) { + port->sm_churn_actor_state = AD_NO_CHURN; + } else { + port->churn_actor_count++; + port->sm_churn_actor_state = AD_CHURN; + } + } + if (port->sm_churn_partner_timer_counter && + !(--port->sm_churn_partner_timer_counter) && + port->sm_churn_partner_state == AD_CHURN_MONITOR) { + if (port->partner_oper.port_state & AD_STATE_SYNCHRONIZATION) { + port->sm_churn_partner_state = AD_NO_CHURN; + } else { + port->churn_partner_count++; + port->sm_churn_partner_state = AD_CHURN; + } + } +} + /** * ad_tx_machine - handle a port's tx state machine * @port: the port we're looking at @@ -1745,6 +1790,13 @@ static void ad_initialize_port(struct port *port, int lacp_fast) port->next_port_in_aggregator = NULL; port->transaction_id = 0; + port->sm_churn_actor_timer_counter = 0; + port->sm_churn_actor_state = 0; + port->churn_actor_count = 0; + port->sm_churn_partner_timer_counter = 0; + port->sm_churn_partner_state = 0; + port->churn_partner_count = 0; + memcpy(&port->lacpdu, &lacpdu, sizeof(lacpdu)); } } @@ -2164,6 +2216,7 @@ void bond_3ad_state_machine_handler(struct work_struct *work) ad_port_selection_logic(port, &update_slave_arr); ad_mux_machine(port, &update_slave_arr); ad_tx_machine(port); + ad_churn_machine(port); /* turn off the BEGIN bit, since we already handled it */ if (port->sm_vars & AD_PORT_BEGIN) diff --git a/drivers/net/bonding/bond_procfs.c b/drivers/net/bonding/bond_procfs.c index 976f5ad2a0f2..62694cfc05b6 100644 --- a/drivers/net/bonding/bond_procfs.c +++ b/drivers/net/bonding/bond_procfs.c @@ -176,18 +176,51 @@ static void bond_info_show_slave(struct seq_file *seq, slave->link_failure_count); seq_printf(seq, "Permanent HW addr: %pM\n", slave->perm_hwaddr); + seq_printf(seq, "Slave queue ID: %d\n", slave->queue_id); if (BOND_MODE(bond) == BOND_MODE_8023AD) { - const struct aggregator *agg - = SLAVE_AD_INFO(slave)->port.aggregator; + const struct port *port = &SLAVE_AD_INFO(slave)->port; + const struct aggregator *agg = port->aggregator; - if (agg) + if (agg) { seq_printf(seq, "Aggregator ID: %d\n", agg->aggregator_identifier); - else + seq_printf(seq, "Actor Churn State: %s\n", + bond_3ad_churn_desc(port->sm_churn_actor_state)); + seq_printf(seq, "Partner Churn State: %s\n", + bond_3ad_churn_desc(port->sm_churn_partner_state)); + seq_printf(seq, "Actor Churned Count: %d\n", + port->churn_actor_count); + seq_printf(seq, "Partner Churned Count: %d\n", + port->churn_partner_count); + + seq_puts(seq, "details actor lacp pdu:\n"); + seq_printf(seq, " system priority: %d\n", + port->actor_system_priority); + seq_printf(seq, " port key: %d\n", + port->actor_oper_port_key); + seq_printf(seq, " port priority: %d\n", + port->actor_port_priority); + seq_printf(seq, " port number: %d\n", + port->actor_port_number); + seq_printf(seq, " port state: %d\n", + port->actor_oper_port_state); + + seq_puts(seq, "details partner lacp pdu:\n"); + seq_printf(seq, " system priority: %d\n", + port->partner_oper.system_priority); + seq_printf(seq, " oper key: %d\n", + port->partner_oper.key); + seq_printf(seq, " port priority: %d\n", + port->partner_oper.port_priority); + seq_printf(seq, " port number: %d\n", + port->partner_oper.port_number); + seq_printf(seq, " port state: %d\n", + port->partner_oper.port_state); + } else { seq_puts(seq, "Aggregator ID: N/A\n"); + } } - seq_printf(seq, "Slave queue ID: %d\n", slave->queue_id); } static int bond_info_seq_show(struct seq_file *seq, void *v) diff --git a/include/net/bond_3ad.h b/include/net/bond_3ad.h index f04cdbb7848e..c2a40a172fcd 100644 --- a/include/net/bond_3ad.h +++ b/include/net/bond_3ad.h @@ -82,6 +82,13 @@ typedef enum { AD_TRANSMIT /* tx Machine */ } tx_states_t; +/* churn machine states(43.4.17 in the 802.3ad standard) */ +typedef enum { + AD_CHURN_MONITOR, /* monitoring for churn */ + AD_CHURN, /* churn detected (error) */ + AD_NO_CHURN /* no churn (no error) */ +} churn_state_t; + /* rx indication types */ typedef enum { AD_TYPE_LACPDU = 1, /* type lacpdu */ @@ -229,6 +236,12 @@ typedef struct port { u16 sm_mux_timer_counter; /* state machine mux timer counter */ tx_states_t sm_tx_state; /* state machine tx state */ u16 sm_tx_timer_counter; /* state machine tx timer counter(allways on - enter to transmit state 3 time per second) */ + u16 sm_churn_actor_timer_counter; + u16 sm_churn_partner_timer_counter; + u32 churn_actor_count; + u32 churn_partner_count; + churn_state_t sm_churn_actor_state; + churn_state_t sm_churn_partner_state; struct slave *slave; /* pointer to the bond slave that this port belongs to */ struct aggregator *aggregator; /* pointer to an aggregator that this port related to */ struct port *next_port_in_aggregator; /* Next port on the linked list of the parent aggregator */ @@ -262,6 +275,22 @@ struct ad_slave_info { u16 id; }; +static inline const char *bond_3ad_churn_desc(churn_state_t state) +{ + static const char *const churn_description[] = { + "monitoring", + "churned", + "none", + "unknown" + }; + int max_size = sizeof(churn_description) / sizeof(churn_description[0]); + + if (state >= max_size) + state = max_size - 1; + + return churn_description[state]; +} + /* ========== AD Exported functions to the main bonding code ========== */ void bond_3ad_initialize(struct bonding *bond, u16 tick_resolution); void bond_3ad_bind_slave(struct slave *slave); -- cgit v1.2.3 From d752c364571743d696c2a54a449ce77550c35ac5 Mon Sep 17 00:00:00 2001 From: Marcelo Ricardo Leitner Date: Mon, 23 Feb 2015 15:02:34 -0300 Subject: ipvs: allow rescheduling of new connections when port reuse is detected Currently, when TCP/SCTP port reusing happens, IPVS will find the old entry and use it for the new one, behaving like a forced persistence. But if you consider a cluster with a heavy load of small connections, such reuse will happen often and may lead to a not optimal load balancing and might prevent a new node from getting a fair load. This patch introduces a new sysctl, conn_reuse_mode, that allows controlling how to proceed when port reuse is detected. The default value will allow rescheduling of new connections only if the old entry was in TIME_WAIT state for TCP or CLOSED for SCTP. Signed-off-by: Marcelo Ricardo Leitner Signed-off-by: Julian Anastasov Signed-off-by: Simon Horman --- Documentation/networking/ipvs-sysctl.txt | 21 ++++++++++++++++++++ include/net/ip_vs.h | 11 +++++++++++ net/netfilter/ipvs/ip_vs_core.c | 33 ++++++++++++++++++++++++++++---- net/netfilter/ipvs/ip_vs_ctl.c | 8 ++++++++ net/netfilter/ipvs/ip_vs_sync.c | 21 ++++++++++++++++++-- 5 files changed, 88 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/Documentation/networking/ipvs-sysctl.txt b/Documentation/networking/ipvs-sysctl.txt index 7a3c04729591..3ba709531adb 100644 --- a/Documentation/networking/ipvs-sysctl.txt +++ b/Documentation/networking/ipvs-sysctl.txt @@ -22,6 +22,27 @@ backup_only - BOOLEAN If set, disable the director function while the server is in backup mode to avoid packet loops for DR/TUN methods. +conn_reuse_mode - INTEGER + 1 - default + + Controls how ipvs will deal with connections that are detected + port reuse. It is a bitmap, with the values being: + + 0: disable any special handling on port reuse. The new + connection will be delivered to the same real server that was + servicing the previous connection. This will effectively + disable expire_nodest_conn. + + bit 1: enable rescheduling of new connections when it is safe. + That is, whenever expire_nodest_conn and for TCP sockets, when + the connection is in TIME_WAIT state (which is only possible if + you use NAT mode). + + bit 2: it is bit 1 plus, for TCP connections, when connections + are in FIN_WAIT state, as this is the last state seen by load + balancer in Direct Routing mode. This bit helps on adding new + real servers to a very busy cluster. + conntrack - BOOLEAN 0 - disabled (default) not 0 - enabled diff --git a/include/net/ip_vs.h b/include/net/ip_vs.h index a627fe690c19..20fd23398537 100644 --- a/include/net/ip_vs.h +++ b/include/net/ip_vs.h @@ -941,6 +941,7 @@ struct netns_ipvs { int sysctl_nat_icmp_send; int sysctl_pmtu_disc; int sysctl_backup_only; + int sysctl_conn_reuse_mode; /* ip_vs_lblc */ int sysctl_lblc_expiration; @@ -1059,6 +1060,11 @@ static inline int sysctl_backup_only(struct netns_ipvs *ipvs) ipvs->sysctl_backup_only; } +static inline int sysctl_conn_reuse_mode(struct netns_ipvs *ipvs) +{ + return ipvs->sysctl_conn_reuse_mode; +} + #else static inline int sysctl_sync_threshold(struct netns_ipvs *ipvs) @@ -1126,6 +1132,11 @@ static inline int sysctl_backup_only(struct netns_ipvs *ipvs) return 0; } +static inline int sysctl_conn_reuse_mode(struct netns_ipvs *ipvs) +{ + return 1; +} + #endif /* IPVS core functions diff --git a/net/netfilter/ipvs/ip_vs_core.c b/net/netfilter/ipvs/ip_vs_core.c index c9470c86308f..6103ab933c5b 100644 --- a/net/netfilter/ipvs/ip_vs_core.c +++ b/net/netfilter/ipvs/ip_vs_core.c @@ -1042,6 +1042,26 @@ static inline bool is_new_conn(const struct sk_buff *skb, } } +static inline bool is_new_conn_expected(const struct ip_vs_conn *cp, + int conn_reuse_mode) +{ + /* Controlled (FTP DATA or persistence)? */ + if (cp->control) + return false; + + switch (cp->protocol) { + case IPPROTO_TCP: + return (cp->state == IP_VS_TCP_S_TIME_WAIT) || + ((conn_reuse_mode & 2) && + (cp->state == IP_VS_TCP_S_FIN_WAIT) && + (cp->flags & IP_VS_CONN_F_NOOUTPUT)); + case IPPROTO_SCTP: + return cp->state == IP_VS_SCTP_S_CLOSED; + default: + return false; + } +} + /* Handle response packets: rewrite addresses and send away... */ static unsigned int @@ -1580,6 +1600,7 @@ ip_vs_in(unsigned int hooknum, struct sk_buff *skb, int af) struct ip_vs_conn *cp; int ret, pkts; struct netns_ipvs *ipvs; + int conn_reuse_mode; /* Already marked as IPVS request or reply? */ if (skb->ipvs_property) @@ -1648,10 +1669,14 @@ ip_vs_in(unsigned int hooknum, struct sk_buff *skb, int af) */ cp = pp->conn_in_get(af, skb, &iph, 0); - if (unlikely(sysctl_expire_nodest_conn(ipvs)) && cp && cp->dest && - unlikely(!atomic_read(&cp->dest->weight)) && !iph.fragoffs && - is_new_conn(skb, &iph)) { - ip_vs_conn_expire_now(cp); + conn_reuse_mode = sysctl_conn_reuse_mode(ipvs); + if (conn_reuse_mode && !iph.fragoffs && + is_new_conn(skb, &iph) && cp && + ((unlikely(sysctl_expire_nodest_conn(ipvs)) && cp->dest && + unlikely(!atomic_read(&cp->dest->weight))) || + unlikely(is_new_conn_expected(cp, conn_reuse_mode)))) { + if (!atomic_read(&cp->n_control)) + ip_vs_conn_expire_now(cp); __ip_vs_conn_put(cp); cp = NULL; } diff --git a/net/netfilter/ipvs/ip_vs_ctl.c b/net/netfilter/ipvs/ip_vs_ctl.c index 6fd60059faf0..76cc9ffd87fa 100644 --- a/net/netfilter/ipvs/ip_vs_ctl.c +++ b/net/netfilter/ipvs/ip_vs_ctl.c @@ -1823,6 +1823,12 @@ static struct ctl_table vs_vars[] = { .mode = 0644, .proc_handler = proc_dointvec, }, + { + .procname = "conn_reuse_mode", + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = proc_dointvec, + }, #ifdef CONFIG_IP_VS_DEBUG { .procname = "debug_level", @@ -3790,6 +3796,8 @@ static int __net_init ip_vs_control_net_init_sysctl(struct net *net) ipvs->sysctl_pmtu_disc = 1; tbl[idx++].data = &ipvs->sysctl_pmtu_disc; tbl[idx++].data = &ipvs->sysctl_backup_only; + ipvs->sysctl_conn_reuse_mode = 1; + tbl[idx++].data = &ipvs->sysctl_conn_reuse_mode; ipvs->sysctl_hdr = register_net_sysctl(net, "net/ipv4/vs", tbl); diff --git a/net/netfilter/ipvs/ip_vs_sync.c b/net/netfilter/ipvs/ip_vs_sync.c index c47ffd7a0a70..f96229cdb6e1 100644 --- a/net/netfilter/ipvs/ip_vs_sync.c +++ b/net/netfilter/ipvs/ip_vs_sync.c @@ -845,10 +845,27 @@ static void ip_vs_proc_conn(struct net *net, struct ip_vs_conn_param *param, struct ip_vs_conn *cp; struct netns_ipvs *ipvs = net_ipvs(net); - if (!(flags & IP_VS_CONN_F_TEMPLATE)) + if (!(flags & IP_VS_CONN_F_TEMPLATE)) { cp = ip_vs_conn_in_get(param); - else + if (cp && ((cp->dport != dport) || + !ip_vs_addr_equal(cp->daf, &cp->daddr, daddr))) { + if (!(flags & IP_VS_CONN_F_INACTIVE)) { + ip_vs_conn_expire_now(cp); + __ip_vs_conn_put(cp); + cp = NULL; + } else { + /* This is the expiration message for the + * connection that was already replaced, so we + * just ignore it. + */ + __ip_vs_conn_put(cp); + kfree(param->pe_data); + return; + } + } + } else { cp = ip_vs_ct_in_get(param); + } if (cp) { /* Free pe_data */ -- cgit v1.2.3 From 39bed6cbb842d8edf5a26b01122b391d36775b5e Mon Sep 17 00:00:00 2001 From: Matt Fleming Date: Fri, 23 Jan 2015 18:45:40 +0000 Subject: perf: Make perf_cgroup_from_task() global Move perf_cgroup_from_task() from kernel/events/ to include/linux/ along with the necessary struct definitions, so that it can be used by the PMU code. When the upcoming Intel Cache Monitoring PMU driver assigns monitoring IDs to perf events, it needs to be able to check whether any two monitoring events overlap (say, a cgroup and task event), which means we need to be able to lookup the cgroup associated with a task (if any). Signed-off-by: Matt Fleming Signed-off-by: Peter Zijlstra (Intel) Cc: Arnaldo Carvalho de Melo Cc: Arnaldo Carvalho de Melo Cc: H. Peter Anvin Cc: Jiri Olsa Cc: Kanaka Juvva Cc: Linus Torvalds Cc: Paul Mackerras Cc: Vikas Shivappa Link: http://lkml.kernel.org/r/1422038748-21397-2-git-send-email-matt@codeblueprint.co.uk Signed-off-by: Ingo Molnar --- include/linux/perf_event.h | 30 ++++++++++++++++++++++++++++++ kernel/events/core.c | 28 +--------------------------- 2 files changed, 31 insertions(+), 27 deletions(-) (limited to 'include') diff --git a/include/linux/perf_event.h b/include/linux/perf_event.h index 724d3720c9b1..cae4a9481777 100644 --- a/include/linux/perf_event.h +++ b/include/linux/perf_event.h @@ -53,6 +53,7 @@ struct perf_guest_info_callbacks { #include #include #include +#include #include struct perf_callchain_entry { @@ -547,6 +548,35 @@ struct perf_output_handle { int page; }; +#ifdef CONFIG_CGROUP_PERF + +/* + * perf_cgroup_info keeps track of time_enabled for a cgroup. + * This is a per-cpu dynamically allocated data structure. + */ +struct perf_cgroup_info { + u64 time; + u64 timestamp; +}; + +struct perf_cgroup { + struct cgroup_subsys_state css; + struct perf_cgroup_info __percpu *info; +}; + +/* + * Must ensure cgroup is pinned (css_get) before calling + * this function. In other words, we cannot call this function + * if there is no cgroup event for the current CPU context. + */ +static inline struct perf_cgroup * +perf_cgroup_from_task(struct task_struct *task) +{ + return container_of(task_css(task, perf_event_cgrp_id), + struct perf_cgroup, css); +} +#endif /* CONFIG_CGROUP_PERF */ + #ifdef CONFIG_PERF_EVENTS extern int perf_pmu_register(struct pmu *pmu, const char *name, int type); diff --git a/kernel/events/core.c b/kernel/events/core.c index 20cece0a7aea..072de3143244 100644 --- a/kernel/events/core.c +++ b/kernel/events/core.c @@ -34,11 +34,11 @@ #include #include #include +#include #include #include #include #include -#include #include #include #include @@ -351,32 +351,6 @@ static void perf_ctx_unlock(struct perf_cpu_context *cpuctx, #ifdef CONFIG_CGROUP_PERF -/* - * perf_cgroup_info keeps track of time_enabled for a cgroup. - * This is a per-cpu dynamically allocated data structure. - */ -struct perf_cgroup_info { - u64 time; - u64 timestamp; -}; - -struct perf_cgroup { - struct cgroup_subsys_state css; - struct perf_cgroup_info __percpu *info; -}; - -/* - * Must ensure cgroup is pinned (css_get) before calling - * this function. In other words, we cannot call this function - * if there is no cgroup event for the current CPU context. - */ -static inline struct perf_cgroup * -perf_cgroup_from_task(struct task_struct *task) -{ - return container_of(task_css(task, perf_event_cgrp_id), - struct perf_cgroup, css); -} - static inline bool perf_cgroup_match(struct perf_event *event) { -- cgit v1.2.3 From eacd3ecc34472ce3751eedfc94e44c7cc6eb6305 Mon Sep 17 00:00:00 2001 From: Matt Fleming Date: Fri, 23 Jan 2015 18:45:41 +0000 Subject: perf: Add ->count() function to read per-package counters For PMU drivers that record per-package counters, the ->count variable cannot be used to record an accurate aggregated value, since it's not possible to perform SMP cross-calls to cpus on other packages from the context in which we update ->count. Introduce a new optional ->count() accessor function that can be used to customize how values are collected. If a PMU driver doesn't provide a ->count() function, we fallback to the existing code. There is necessarily a window of staleness with this approach because the task that generated the counter value may not have been scheduled by the cpu recently. An alternative and more complex approach would be to use a hrtimer to periodically refresh the values from a more permissive scheduling context. So, we're trading off complexity for accuracy. Signed-off-by: Matt Fleming Signed-off-by: Peter Zijlstra (Intel) Cc: Arnaldo Carvalho de Melo Cc: Arnaldo Carvalho de Melo Cc: H. Peter Anvin Cc: Jiri Olsa Cc: Kanaka Juvva Cc: Linus Torvalds Cc: Vikas Shivappa Link: http://lkml.kernel.org/r/1422038748-21397-3-git-send-email-matt@codeblueprint.co.uk Signed-off-by: Ingo Molnar --- include/linux/perf_event.h | 10 ++++++++++ kernel/events/core.c | 5 ++++- 2 files changed, 14 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/perf_event.h b/include/linux/perf_event.h index cae4a9481777..9fc9b0d31442 100644 --- a/include/linux/perf_event.h +++ b/include/linux/perf_event.h @@ -272,6 +272,11 @@ struct pmu { */ size_t task_ctx_size; + + /* + * Return the count value for a counter. + */ + u64 (*count) (struct perf_event *event); /*optional*/ }; /** @@ -770,6 +775,11 @@ static inline void perf_event_task_sched_out(struct task_struct *prev, __perf_event_task_sched_out(prev, next); } +static inline u64 __perf_event_count(struct perf_event *event) +{ + return local64_read(&event->count) + atomic64_read(&event->child_count); +} + extern void perf_event_mmap(struct vm_area_struct *vma); extern struct perf_guest_info_callbacks *perf_guest_cbs; extern int perf_register_guest_info_callbacks(struct perf_guest_info_callbacks *callbacks); diff --git a/kernel/events/core.c b/kernel/events/core.c index 072de3143244..4e8dc596f101 100644 --- a/kernel/events/core.c +++ b/kernel/events/core.c @@ -3194,7 +3194,10 @@ static void __perf_event_read(void *info) static inline u64 perf_event_count(struct perf_event *event) { - return local64_read(&event->count) + atomic64_read(&event->child_count); + if (event->pmu->count) + return event->pmu->count(event); + + return __perf_event_count(event); } static u64 perf_event_read(struct perf_event *event) -- cgit v1.2.3 From 4afbb24ce5e723c8a093a6674a3c33062175078a Mon Sep 17 00:00:00 2001 From: Matt Fleming Date: Fri, 23 Jan 2015 18:45:44 +0000 Subject: perf/x86/intel: Add Intel Cache QoS Monitoring support Future Intel Xeon processors support a Cache QoS Monitoring feature that allows tracking of the LLC occupancy for a task or task group, i.e. the amount of data in pulled into the LLC for the task (group). Currently the PMU only supports per-cpu events. We create an event for each cpu and read out all the LLC occupancy values. Because this results in duplicate values being written out to userspace, we also export a .per-pkg event file so that the perf tools only accumulate values for one cpu per package. Signed-off-by: Matt Fleming Signed-off-by: Peter Zijlstra (Intel) Cc: Arnaldo Carvalho de Melo Cc: Arnaldo Carvalho de Melo Cc: H. Peter Anvin Cc: Jiri Olsa Cc: Kanaka Juvva Cc: Linus Torvalds Cc: Vikas Shivappa Link: http://lkml.kernel.org/r/1422038748-21397-6-git-send-email-matt@codeblueprint.co.uk Signed-off-by: Ingo Molnar --- arch/x86/kernel/cpu/perf_event_intel_cqm.c | 530 +++++++++++++++++++++++++++++ include/linux/perf_event.h | 7 + 2 files changed, 537 insertions(+) create mode 100644 arch/x86/kernel/cpu/perf_event_intel_cqm.c (limited to 'include') diff --git a/arch/x86/kernel/cpu/perf_event_intel_cqm.c b/arch/x86/kernel/cpu/perf_event_intel_cqm.c new file mode 100644 index 000000000000..05b4cd26f426 --- /dev/null +++ b/arch/x86/kernel/cpu/perf_event_intel_cqm.c @@ -0,0 +1,530 @@ +/* + * Intel Cache Quality-of-Service Monitoring (CQM) support. + * + * Based very, very heavily on work by Peter Zijlstra. + */ + +#include +#include +#include +#include "perf_event.h" + +#define MSR_IA32_PQR_ASSOC 0x0c8f +#define MSR_IA32_QM_CTR 0x0c8e +#define MSR_IA32_QM_EVTSEL 0x0c8d + +static unsigned int cqm_max_rmid = -1; +static unsigned int cqm_l3_scale; /* supposedly cacheline size */ + +struct intel_cqm_state { + raw_spinlock_t lock; + int rmid; + int cnt; +}; + +static DEFINE_PER_CPU(struct intel_cqm_state, cqm_state); + +/* + * Protects cache_cgroups. + */ +static DEFINE_MUTEX(cache_mutex); + +/* + * Groups of events that have the same target(s), one RMID per group. + */ +static LIST_HEAD(cache_groups); + +/* + * Mask of CPUs for reading CQM values. We only need one per-socket. + */ +static cpumask_t cqm_cpumask; + +#define RMID_VAL_ERROR (1ULL << 63) +#define RMID_VAL_UNAVAIL (1ULL << 62) + +#define QOS_L3_OCCUP_EVENT_ID (1 << 0) + +#define QOS_EVENT_MASK QOS_L3_OCCUP_EVENT_ID + +static u64 __rmid_read(unsigned long rmid) +{ + u64 val; + + /* + * Ignore the SDM, this thing is _NOTHING_ like a regular perfcnt, + * it just says that to increase confusion. + */ + wrmsr(MSR_IA32_QM_EVTSEL, QOS_L3_OCCUP_EVENT_ID, rmid); + rdmsrl(MSR_IA32_QM_CTR, val); + + /* + * Aside from the ERROR and UNAVAIL bits, assume this thing returns + * the number of cachelines tagged with @rmid. + */ + return val; +} + +static unsigned long *cqm_rmid_bitmap; + +/* + * Returns < 0 on fail. + */ +static int __get_rmid(void) +{ + return bitmap_find_free_region(cqm_rmid_bitmap, cqm_max_rmid, 0); +} + +static void __put_rmid(int rmid) +{ + bitmap_release_region(cqm_rmid_bitmap, rmid, 0); +} + +static int intel_cqm_setup_rmid_cache(void) +{ + cqm_rmid_bitmap = kmalloc(sizeof(long) * BITS_TO_LONGS(cqm_max_rmid), GFP_KERNEL); + if (!cqm_rmid_bitmap) + return -ENOMEM; + + bitmap_zero(cqm_rmid_bitmap, cqm_max_rmid); + + /* + * RMID 0 is special and is always allocated. It's used for all + * tasks that are not monitored. + */ + bitmap_allocate_region(cqm_rmid_bitmap, 0, 0); + + return 0; +} + +/* + * Determine if @a and @b measure the same set of tasks. + */ +static bool __match_event(struct perf_event *a, struct perf_event *b) +{ + if ((a->attach_state & PERF_ATTACH_TASK) != + (b->attach_state & PERF_ATTACH_TASK)) + return false; + + /* not task */ + + return true; /* if not task, we're machine wide */ +} + +/* + * Determine if @a's tasks intersect with @b's tasks + */ +static bool __conflict_event(struct perf_event *a, struct perf_event *b) +{ + /* + * If one of them is not a task, same story as above with cgroups. + */ + if (!(a->attach_state & PERF_ATTACH_TASK) || + !(b->attach_state & PERF_ATTACH_TASK)) + return true; + + /* + * Must be non-overlapping. + */ + return false; +} + +/* + * Find a group and setup RMID. + * + * If we're part of a group, we use the group's RMID. + */ +static int intel_cqm_setup_event(struct perf_event *event, + struct perf_event **group) +{ + struct perf_event *iter; + int rmid; + + list_for_each_entry(iter, &cache_groups, hw.cqm_groups_entry) { + if (__match_event(iter, event)) { + /* All tasks in a group share an RMID */ + event->hw.cqm_rmid = iter->hw.cqm_rmid; + *group = iter; + return 0; + } + + if (__conflict_event(iter, event)) + return -EBUSY; + } + + rmid = __get_rmid(); + if (rmid < 0) + return rmid; + + event->hw.cqm_rmid = rmid; + return 0; +} + +static void intel_cqm_event_read(struct perf_event *event) +{ + unsigned long rmid = event->hw.cqm_rmid; + u64 val; + + val = __rmid_read(rmid); + + /* + * Ignore this reading on error states and do not update the value. + */ + if (val & (RMID_VAL_ERROR | RMID_VAL_UNAVAIL)) + return; + + local64_set(&event->count, val); +} + +static void intel_cqm_event_start(struct perf_event *event, int mode) +{ + struct intel_cqm_state *state = this_cpu_ptr(&cqm_state); + unsigned long rmid = event->hw.cqm_rmid; + unsigned long flags; + + if (!(event->hw.cqm_state & PERF_HES_STOPPED)) + return; + + event->hw.cqm_state &= ~PERF_HES_STOPPED; + + raw_spin_lock_irqsave(&state->lock, flags); + + if (state->cnt++) + WARN_ON_ONCE(state->rmid != rmid); + else + WARN_ON_ONCE(state->rmid); + + state->rmid = rmid; + wrmsrl(MSR_IA32_PQR_ASSOC, state->rmid); + + raw_spin_unlock_irqrestore(&state->lock, flags); +} + +static void intel_cqm_event_stop(struct perf_event *event, int mode) +{ + struct intel_cqm_state *state = this_cpu_ptr(&cqm_state); + unsigned long flags; + + if (event->hw.cqm_state & PERF_HES_STOPPED) + return; + + event->hw.cqm_state |= PERF_HES_STOPPED; + + raw_spin_lock_irqsave(&state->lock, flags); + intel_cqm_event_read(event); + + if (!--state->cnt) { + state->rmid = 0; + wrmsrl(MSR_IA32_PQR_ASSOC, 0); + } else { + WARN_ON_ONCE(!state->rmid); + } + + raw_spin_unlock_irqrestore(&state->lock, flags); +} + +static int intel_cqm_event_add(struct perf_event *event, int mode) +{ + int rmid; + + event->hw.cqm_state = PERF_HES_STOPPED; + rmid = event->hw.cqm_rmid; + WARN_ON_ONCE(!rmid); + + if (mode & PERF_EF_START) + intel_cqm_event_start(event, mode); + + return 0; +} + +static void intel_cqm_event_del(struct perf_event *event, int mode) +{ + intel_cqm_event_stop(event, mode); +} + +static void intel_cqm_event_destroy(struct perf_event *event) +{ + struct perf_event *group_other = NULL; + + mutex_lock(&cache_mutex); + + /* + * If there's another event in this group... + */ + if (!list_empty(&event->hw.cqm_group_entry)) { + group_other = list_first_entry(&event->hw.cqm_group_entry, + struct perf_event, + hw.cqm_group_entry); + list_del(&event->hw.cqm_group_entry); + } + + /* + * And we're the group leader.. + */ + if (!list_empty(&event->hw.cqm_groups_entry)) { + /* + * If there was a group_other, make that leader, otherwise + * destroy the group and return the RMID. + */ + if (group_other) { + list_replace(&event->hw.cqm_groups_entry, + &group_other->hw.cqm_groups_entry); + } else { + int rmid = event->hw.cqm_rmid; + + __put_rmid(rmid); + list_del(&event->hw.cqm_groups_entry); + } + } + + mutex_unlock(&cache_mutex); +} + +static struct pmu intel_cqm_pmu; + +/* + * XXX there's a bit of a problem in that we cannot simply do the one + * event per node as one would want, since that one event would one get + * scheduled on the one cpu. But we want to 'schedule' the RMID on all + * CPUs. + * + * This means we want events for each CPU, however, that generates a lot + * of duplicate values out to userspace -- this is not to be helped + * unless we want to change the core code in some way. Fore more info, + * see intel_cqm_event_read(). + */ +static int intel_cqm_event_init(struct perf_event *event) +{ + struct perf_event *group = NULL; + int err; + + if (event->attr.type != intel_cqm_pmu.type) + return -ENOENT; + + if (event->attr.config & ~QOS_EVENT_MASK) + return -EINVAL; + + if (event->cpu == -1) + return -EINVAL; + + /* unsupported modes and filters */ + if (event->attr.exclude_user || + event->attr.exclude_kernel || + event->attr.exclude_hv || + event->attr.exclude_idle || + event->attr.exclude_host || + event->attr.exclude_guest || + event->attr.sample_period) /* no sampling */ + return -EINVAL; + + INIT_LIST_HEAD(&event->hw.cqm_group_entry); + INIT_LIST_HEAD(&event->hw.cqm_groups_entry); + + event->destroy = intel_cqm_event_destroy; + + mutex_lock(&cache_mutex); + + err = intel_cqm_setup_event(event, &group); /* will also set rmid */ + if (err) + goto out; + + if (group) { + list_add_tail(&event->hw.cqm_group_entry, + &group->hw.cqm_group_entry); + } else { + list_add_tail(&event->hw.cqm_groups_entry, + &cache_groups); + } + +out: + mutex_unlock(&cache_mutex); + return err; +} + +EVENT_ATTR_STR(llc_occupancy, intel_cqm_llc, "event=0x01"); +EVENT_ATTR_STR(llc_occupancy.per-pkg, intel_cqm_llc_pkg, "1"); +EVENT_ATTR_STR(llc_occupancy.unit, intel_cqm_llc_unit, "Bytes"); +EVENT_ATTR_STR(llc_occupancy.scale, intel_cqm_llc_scale, NULL); +EVENT_ATTR_STR(llc_occupancy.snapshot, intel_cqm_llc_snapshot, "1"); + +static struct attribute *intel_cqm_events_attr[] = { + EVENT_PTR(intel_cqm_llc), + EVENT_PTR(intel_cqm_llc_pkg), + EVENT_PTR(intel_cqm_llc_unit), + EVENT_PTR(intel_cqm_llc_scale), + EVENT_PTR(intel_cqm_llc_snapshot), + NULL, +}; + +static struct attribute_group intel_cqm_events_group = { + .name = "events", + .attrs = intel_cqm_events_attr, +}; + +PMU_FORMAT_ATTR(event, "config:0-7"); +static struct attribute *intel_cqm_formats_attr[] = { + &format_attr_event.attr, + NULL, +}; + +static struct attribute_group intel_cqm_format_group = { + .name = "format", + .attrs = intel_cqm_formats_attr, +}; + +static const struct attribute_group *intel_cqm_attr_groups[] = { + &intel_cqm_events_group, + &intel_cqm_format_group, + NULL, +}; + +static struct pmu intel_cqm_pmu = { + .attr_groups = intel_cqm_attr_groups, + .task_ctx_nr = perf_sw_context, + .event_init = intel_cqm_event_init, + .add = intel_cqm_event_add, + .del = intel_cqm_event_del, + .start = intel_cqm_event_start, + .stop = intel_cqm_event_stop, + .read = intel_cqm_event_read, +}; + +static inline void cqm_pick_event_reader(int cpu) +{ + int phys_id = topology_physical_package_id(cpu); + int i; + + for_each_cpu(i, &cqm_cpumask) { + if (phys_id == topology_physical_package_id(i)) + return; /* already got reader for this socket */ + } + + cpumask_set_cpu(cpu, &cqm_cpumask); +} + +static void intel_cqm_cpu_prepare(unsigned int cpu) +{ + struct intel_cqm_state *state = &per_cpu(cqm_state, cpu); + struct cpuinfo_x86 *c = &cpu_data(cpu); + + raw_spin_lock_init(&state->lock); + state->rmid = 0; + state->cnt = 0; + + WARN_ON(c->x86_cache_max_rmid != cqm_max_rmid); + WARN_ON(c->x86_cache_occ_scale != cqm_l3_scale); +} + +static void intel_cqm_cpu_exit(unsigned int cpu) +{ + int phys_id = topology_physical_package_id(cpu); + int i; + + /* + * Is @cpu a designated cqm reader? + */ + if (!cpumask_test_and_clear_cpu(cpu, &cqm_cpumask)) + return; + + for_each_online_cpu(i) { + if (i == cpu) + continue; + + if (phys_id == topology_physical_package_id(i)) { + cpumask_set_cpu(i, &cqm_cpumask); + break; + } + } +} + +static int intel_cqm_cpu_notifier(struct notifier_block *nb, + unsigned long action, void *hcpu) +{ + unsigned int cpu = (unsigned long)hcpu; + + switch (action & ~CPU_TASKS_FROZEN) { + case CPU_UP_PREPARE: + intel_cqm_cpu_prepare(cpu); + break; + case CPU_DOWN_PREPARE: + intel_cqm_cpu_exit(cpu); + break; + case CPU_STARTING: + cqm_pick_event_reader(cpu); + break; + } + + return NOTIFY_OK; +} + +static const struct x86_cpu_id intel_cqm_match[] = { + { .vendor = X86_VENDOR_INTEL, .feature = X86_FEATURE_CQM_OCCUP_LLC }, + {} +}; + +static int __init intel_cqm_init(void) +{ + char *str, scale[20]; + int i, cpu, ret; + + if (!x86_match_cpu(intel_cqm_match)) + return -ENODEV; + + cqm_l3_scale = boot_cpu_data.x86_cache_occ_scale; + + /* + * It's possible that not all resources support the same number + * of RMIDs. Instead of making scheduling much more complicated + * (where we have to match a task's RMID to a cpu that supports + * that many RMIDs) just find the minimum RMIDs supported across + * all cpus. + * + * Also, check that the scales match on all cpus. + */ + cpu_notifier_register_begin(); + + for_each_online_cpu(cpu) { + struct cpuinfo_x86 *c = &cpu_data(cpu); + + if (c->x86_cache_max_rmid < cqm_max_rmid) + cqm_max_rmid = c->x86_cache_max_rmid; + + if (c->x86_cache_occ_scale != cqm_l3_scale) { + pr_err("Multiple LLC scale values, disabling\n"); + ret = -EINVAL; + goto out; + } + } + + snprintf(scale, sizeof(scale), "%u", cqm_l3_scale); + str = kstrdup(scale, GFP_KERNEL); + if (!str) { + ret = -ENOMEM; + goto out; + } + + event_attr_intel_cqm_llc_scale.event_str = str; + + ret = intel_cqm_setup_rmid_cache(); + if (ret) + goto out; + + for_each_online_cpu(i) { + intel_cqm_cpu_prepare(i); + cqm_pick_event_reader(i); + } + + __perf_cpu_notifier(intel_cqm_cpu_notifier); + + ret = perf_pmu_register(&intel_cqm_pmu, "intel_cqm", -1); + + if (ret) + pr_err("Intel CQM perf registration failed: %d\n", ret); + else + pr_info("Intel CQM monitoring enabled\n"); + +out: + cpu_notifier_register_done(); + + return ret; +} +device_initcall(intel_cqm_init); diff --git a/include/linux/perf_event.h b/include/linux/perf_event.h index 9fc9b0d31442..ca5504c48f4f 100644 --- a/include/linux/perf_event.h +++ b/include/linux/perf_event.h @@ -123,6 +123,13 @@ struct hw_perf_event { /* for tp_event->class */ struct list_head tp_list; }; + struct { /* intel_cqm */ + int cqm_state; + int cqm_rmid; + struct list_head cqm_events_entry; + struct list_head cqm_groups_entry; + struct list_head cqm_group_entry; + }; #ifdef CONFIG_HAVE_HW_BREAKPOINT struct { /* breakpoint */ /* -- cgit v1.2.3 From bfe1fcd2688f557a6b6a88f59ea7619228728bd7 Mon Sep 17 00:00:00 2001 From: Matt Fleming Date: Fri, 23 Jan 2015 18:45:46 +0000 Subject: perf/x86/intel: Support task events with Intel CQM Add support for task events as well as system-wide events. This change has a big impact on the way that we gather LLC occupancy values in intel_cqm_event_read(). Currently, for system-wide (per-cpu) events we defer processing to userspace which knows how to discard all but one cpu result per package. Things aren't so simple for task events because we need to do the value aggregation ourselves. To do this, we defer updating the LLC occupancy value in event->count from intel_cqm_event_read() and do an SMP cross-call to read values for all packages in intel_cqm_event_count(). We need to ensure that we only do this for one task event per cache group, otherwise we'll report duplicate values. If we're a system-wide event we want to fallback to the default perf_event_count() implementation. Refactor this into a common function so that we don't duplicate the code. Also, introduce PERF_TYPE_INTEL_CQM, since we need a way to track an event's task (if the event isn't per-cpu) inside of the Intel CQM PMU driver. This task information is only availble in the upper layers of the perf infrastructure. Other perf backends stash the target task in event->hw.*target so we need to do something similar. The task is used to determine whether events should share a cache group and an RMID. Signed-off-by: Matt Fleming Signed-off-by: Peter Zijlstra (Intel) Cc: Arnaldo Carvalho de Melo Cc: Arnaldo Carvalho de Melo Cc: H. Peter Anvin Cc: Jiri Olsa Cc: Kanaka Juvva Cc: Linus Torvalds Cc: Vikas Shivappa Cc: linux-api@vger.kernel.org Link: http://lkml.kernel.org/r/1422038748-21397-8-git-send-email-matt@codeblueprint.co.uk Signed-off-by: Ingo Molnar --- arch/x86/kernel/cpu/perf_event_intel_cqm.c | 195 +++++++++++++++++++++++++---- include/linux/perf_event.h | 1 + include/uapi/linux/perf_event.h | 1 + kernel/events/core.c | 2 + 4 files changed, 178 insertions(+), 21 deletions(-) (limited to 'include') diff --git a/arch/x86/kernel/cpu/perf_event_intel_cqm.c b/arch/x86/kernel/cpu/perf_event_intel_cqm.c index b5d9d746dbc0..8003d87afd89 100644 --- a/arch/x86/kernel/cpu/perf_event_intel_cqm.c +++ b/arch/x86/kernel/cpu/perf_event_intel_cqm.c @@ -182,23 +182,124 @@ fail: /* * Determine if @a and @b measure the same set of tasks. + * + * If @a and @b measure the same set of tasks then we want to share a + * single RMID. */ static bool __match_event(struct perf_event *a, struct perf_event *b) { + /* Per-cpu and task events don't mix */ if ((a->attach_state & PERF_ATTACH_TASK) != (b->attach_state & PERF_ATTACH_TASK)) return false; - /* not task */ +#ifdef CONFIG_CGROUP_PERF + if (a->cgrp != b->cgrp) + return false; +#endif + + /* If not task event, we're machine wide */ + if (!(b->attach_state & PERF_ATTACH_TASK)) + return true; + + /* + * Events that target same task are placed into the same cache group. + */ + if (a->hw.cqm_target == b->hw.cqm_target) + return true; + + /* + * Are we an inherited event? + */ + if (b->parent == a) + return true; + + return false; +} + +#ifdef CONFIG_CGROUP_PERF +static inline struct perf_cgroup *event_to_cgroup(struct perf_event *event) +{ + if (event->attach_state & PERF_ATTACH_TASK) + return perf_cgroup_from_task(event->hw.cqm_target); - return true; /* if not task, we're machine wide */ + return event->cgrp; } +#endif /* * Determine if @a's tasks intersect with @b's tasks + * + * There are combinations of events that we explicitly prohibit, + * + * PROHIBITS + * system-wide -> cgroup and task + * cgroup -> system-wide + * -> task in cgroup + * task -> system-wide + * -> task in cgroup + * + * Call this function before allocating an RMID. */ static bool __conflict_event(struct perf_event *a, struct perf_event *b) { +#ifdef CONFIG_CGROUP_PERF + /* + * We can have any number of cgroups but only one system-wide + * event at a time. + */ + if (a->cgrp && b->cgrp) { + struct perf_cgroup *ac = a->cgrp; + struct perf_cgroup *bc = b->cgrp; + + /* + * This condition should have been caught in + * __match_event() and we should be sharing an RMID. + */ + WARN_ON_ONCE(ac == bc); + + if (cgroup_is_descendant(ac->css.cgroup, bc->css.cgroup) || + cgroup_is_descendant(bc->css.cgroup, ac->css.cgroup)) + return true; + + return false; + } + + if (a->cgrp || b->cgrp) { + struct perf_cgroup *ac, *bc; + + /* + * cgroup and system-wide events are mutually exclusive + */ + if ((a->cgrp && !(b->attach_state & PERF_ATTACH_TASK)) || + (b->cgrp && !(a->attach_state & PERF_ATTACH_TASK))) + return true; + + /* + * Ensure neither event is part of the other's cgroup + */ + ac = event_to_cgroup(a); + bc = event_to_cgroup(b); + if (ac == bc) + return true; + + /* + * Must have cgroup and non-intersecting task events. + */ + if (!ac || !bc) + return false; + + /* + * We have cgroup and task events, and the task belongs + * to a cgroup. Check for for overlap. + */ + if (cgroup_is_descendant(ac->css.cgroup, bc->css.cgroup) || + cgroup_is_descendant(bc->css.cgroup, ac->css.cgroup)) + return true; + + return false; + } +#endif /* * If one of them is not a task, same story as above with cgroups. */ @@ -245,9 +346,16 @@ static int intel_cqm_setup_event(struct perf_event *event, static void intel_cqm_event_read(struct perf_event *event) { - unsigned long rmid = event->hw.cqm_rmid; + unsigned long rmid; u64 val; + /* + * Task events are handled by intel_cqm_event_count(). + */ + if (event->cpu == -1) + return; + + rmid = event->hw.cqm_rmid; val = __rmid_read(rmid); /* @@ -259,6 +367,63 @@ static void intel_cqm_event_read(struct perf_event *event) local64_set(&event->count, val); } +struct rmid_read { + unsigned int rmid; + atomic64_t value; +}; + +static void __intel_cqm_event_count(void *info) +{ + struct rmid_read *rr = info; + u64 val; + + val = __rmid_read(rr->rmid); + + if (val & (RMID_VAL_ERROR | RMID_VAL_UNAVAIL)) + return; + + atomic64_add(val, &rr->value); +} + +static inline bool cqm_group_leader(struct perf_event *event) +{ + return !list_empty(&event->hw.cqm_groups_entry); +} + +static u64 intel_cqm_event_count(struct perf_event *event) +{ + struct rmid_read rr = { + .rmid = event->hw.cqm_rmid, + .value = ATOMIC64_INIT(0), + }; + + /* + * We only need to worry about task events. System-wide events + * are handled like usual, i.e. entirely with + * intel_cqm_event_read(). + */ + if (event->cpu != -1) + return __perf_event_count(event); + + /* + * Only the group leader gets to report values. This stops us + * reporting duplicate values to userspace, and gives us a clear + * rule for which task gets to report the values. + * + * Note that it is impossible to attribute these values to + * specific packages - we forfeit that ability when we create + * task events. + */ + if (!cqm_group_leader(event)) + return 0; + + on_each_cpu_mask(&cqm_cpumask, __intel_cqm_event_count, &rr, 1); + + local64_set(&event->count, atomic64_read(&rr.value)); + + return __perf_event_count(event); +} + static void intel_cqm_event_start(struct perf_event *event, int mode) { struct intel_cqm_state *state = this_cpu_ptr(&cqm_state); @@ -344,7 +509,7 @@ static void intel_cqm_event_destroy(struct perf_event *event) /* * And we're the group leader.. */ - if (!list_empty(&event->hw.cqm_groups_entry)) { + if (cqm_group_leader(event)) { /* * If there was a group_other, make that leader, otherwise * destroy the group and return the RMID. @@ -365,17 +530,6 @@ static void intel_cqm_event_destroy(struct perf_event *event) static struct pmu intel_cqm_pmu; -/* - * XXX there's a bit of a problem in that we cannot simply do the one - * event per node as one would want, since that one event would one get - * scheduled on the one cpu. But we want to 'schedule' the RMID on all - * CPUs. - * - * This means we want events for each CPU, however, that generates a lot - * of duplicate values out to userspace -- this is not to be helped - * unless we want to change the core code in some way. Fore more info, - * see intel_cqm_event_read(). - */ static int intel_cqm_event_init(struct perf_event *event) { struct perf_event *group = NULL; @@ -387,9 +541,6 @@ static int intel_cqm_event_init(struct perf_event *event) if (event->attr.config & ~QOS_EVENT_MASK) return -EINVAL; - if (event->cpu == -1) - return -EINVAL; - /* unsupported modes and filters */ if (event->attr.exclude_user || event->attr.exclude_kernel || @@ -407,7 +558,8 @@ static int intel_cqm_event_init(struct perf_event *event) mutex_lock(&cache_mutex); - err = intel_cqm_setup_event(event, &group); /* will also set rmid */ + /* Will also set rmid */ + err = intel_cqm_setup_event(event, &group); if (err) goto out; @@ -470,6 +622,7 @@ static struct pmu intel_cqm_pmu = { .start = intel_cqm_event_start, .stop = intel_cqm_event_stop, .read = intel_cqm_event_read, + .count = intel_cqm_event_count, }; static inline void cqm_pick_event_reader(int cpu) @@ -599,8 +752,8 @@ static int __init intel_cqm_init(void) __perf_cpu_notifier(intel_cqm_cpu_notifier); - ret = perf_pmu_register(&intel_cqm_pmu, "intel_cqm", -1); - + ret = perf_pmu_register(&intel_cqm_pmu, "intel_cqm", + PERF_TYPE_INTEL_CQM); if (ret) pr_err("Intel CQM perf registration failed: %d\n", ret); else diff --git a/include/linux/perf_event.h b/include/linux/perf_event.h index ca5504c48f4f..dac4c2831d82 100644 --- a/include/linux/perf_event.h +++ b/include/linux/perf_event.h @@ -129,6 +129,7 @@ struct hw_perf_event { struct list_head cqm_events_entry; struct list_head cqm_groups_entry; struct list_head cqm_group_entry; + struct task_struct *cqm_target; }; #ifdef CONFIG_HAVE_HW_BREAKPOINT struct { /* breakpoint */ diff --git a/include/uapi/linux/perf_event.h b/include/uapi/linux/perf_event.h index 1e3cd07cf76e..3c8b45de57ec 100644 --- a/include/uapi/linux/perf_event.h +++ b/include/uapi/linux/perf_event.h @@ -32,6 +32,7 @@ enum perf_type_id { PERF_TYPE_HW_CACHE = 3, PERF_TYPE_RAW = 4, PERF_TYPE_BREAKPOINT = 5, + PERF_TYPE_INTEL_CQM = 6, PERF_TYPE_MAX, /* non-ABI */ }; diff --git a/kernel/events/core.c b/kernel/events/core.c index 1fc3bae5904a..71109a045450 100644 --- a/kernel/events/core.c +++ b/kernel/events/core.c @@ -7181,6 +7181,8 @@ perf_event_alloc(struct perf_event_attr *attr, int cpu, else if (attr->type == PERF_TYPE_BREAKPOINT) event->hw.bp_target = task; #endif + else if (attr->type == PERF_TYPE_INTEL_CQM) + event->hw.cqm_target = task; } if (!overflow_handler && parent_event) { -- cgit v1.2.3 From b73adef67765b72f2a0d01ef15aff9d784dc85da Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Tue, 24 Feb 2015 13:15:33 -0800 Subject: net: dsa: integrate with SWITCHDEV for HW bridging In order to support bridging offloads in DSA switch drivers, select NET_SWITCHDEV to get access to the port_stp_update and parent_get_id NDOs that we are required to implement. To facilitate the integratation at the DSA driver level, we implement 3 types of operations: - port_join_bridge - port_leave_bridge - port_stp_update DSA will resolve which switch ports that are currently bridge port members as some Switch hardware/drivers need to know about that to limit the register programming to just the relevant registers (especially for slow MDIO buses). We also take care of setting the correct STP state when slave network devices are brought up/down while being bridge members. Finally, when a port is leaving the bridge, we make sure we set in BR_STATE_FORWARDING state, otherwise the bridge layer would leave it disabled as a result of having left the bridge. Signed-off-by: Florian Fainelli Reviewed-by: Guenter Roeck Tested-by: Guenter Roeck Signed-off-by: David S. Miller --- include/net/dsa.h | 10 ++++ net/dsa/Kconfig | 1 + net/dsa/dsa.c | 7 +++ net/dsa/dsa_priv.h | 4 ++ net/dsa/slave.c | 149 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 171 insertions(+) (limited to 'include') diff --git a/include/net/dsa.h b/include/net/dsa.h index ed3c34bbb67a..92be34791963 100644 --- a/include/net/dsa.h +++ b/include/net/dsa.h @@ -275,6 +275,16 @@ struct dsa_switch_driver { int (*get_regs_len)(struct dsa_switch *ds, int port); void (*get_regs)(struct dsa_switch *ds, int port, struct ethtool_regs *regs, void *p); + + /* + * Bridge integration + */ + int (*port_join_bridge)(struct dsa_switch *ds, int port, + u32 br_port_mask); + int (*port_leave_bridge)(struct dsa_switch *ds, int port, + u32 br_port_mask); + int (*port_stp_update)(struct dsa_switch *ds, int port, + u8 state); }; void register_switch_driver(struct dsa_switch_driver *type); diff --git a/net/dsa/Kconfig b/net/dsa/Kconfig index 5f8ac404535b..b45206e8dd3e 100644 --- a/net/dsa/Kconfig +++ b/net/dsa/Kconfig @@ -8,6 +8,7 @@ config NET_DSA tristate depends on HAVE_NET_DSA select PHYLIB + select NET_SWITCHDEV if NET_DSA diff --git a/net/dsa/dsa.c b/net/dsa/dsa.c index fc1813140be6..9c208f0dab08 100644 --- a/net/dsa/dsa.c +++ b/net/dsa/dsa.c @@ -826,6 +826,10 @@ static struct packet_type dsa_pack_type __read_mostly = { .func = dsa_switch_rcv, }; +static struct notifier_block dsa_netdevice_nb __read_mostly = { + .notifier_call = dsa_slave_netdevice_event, +}; + #ifdef CONFIG_PM_SLEEP static int dsa_suspend(struct device *d) { @@ -884,6 +888,8 @@ static int __init dsa_init_module(void) { int rc; + register_netdevice_notifier(&dsa_netdevice_nb); + rc = platform_driver_register(&dsa_driver); if (rc) return rc; @@ -896,6 +902,7 @@ module_init(dsa_init_module); static void __exit dsa_cleanup_module(void) { + unregister_netdevice_notifier(&dsa_netdevice_nb); dev_remove_pack(&dsa_pack_type); platform_driver_unregister(&dsa_driver); } diff --git a/net/dsa/dsa_priv.h b/net/dsa/dsa_priv.h index 7eb1a6acd46c..d5f1f9b862ea 100644 --- a/net/dsa/dsa_priv.h +++ b/net/dsa/dsa_priv.h @@ -45,6 +45,8 @@ struct dsa_slave_priv { int old_link; int old_pause; int old_duplex; + + struct net_device *bridge_dev; }; /* dsa.c */ @@ -57,6 +59,8 @@ int dsa_slave_create(struct dsa_switch *ds, struct device *parent, int port, char *name); int dsa_slave_suspend(struct net_device *slave_dev); int dsa_slave_resume(struct net_device *slave_dev); +int dsa_slave_netdevice_event(struct notifier_block *unused, + unsigned long event, void *ptr); /* tag_dsa.c */ extern const struct dsa_device_ops dsa_netdev_ops; diff --git a/net/dsa/slave.c b/net/dsa/slave.c index 5be4c928c9c9..b5a4d8974b76 100644 --- a/net/dsa/slave.c +++ b/net/dsa/slave.c @@ -10,10 +10,13 @@ #include #include +#include #include #include #include #include +#include +#include #include "dsa_priv.h" /* slave mii_bus handling ***************************************************/ @@ -60,11 +63,18 @@ static int dsa_slave_init(struct net_device *dev) return 0; } +static inline bool dsa_port_is_bridged(struct dsa_slave_priv *p) +{ + return !!p->bridge_dev; +} + static int dsa_slave_open(struct net_device *dev) { struct dsa_slave_priv *p = netdev_priv(dev); struct net_device *master = p->parent->dst->master_netdev; struct dsa_switch *ds = p->parent; + u8 stp_state = dsa_port_is_bridged(p) ? + BR_STATE_BLOCKING : BR_STATE_FORWARDING; int err; if (!(master->flags & IFF_UP)) @@ -93,6 +103,9 @@ static int dsa_slave_open(struct net_device *dev) goto clear_promisc; } + if (ds->drv->port_stp_update) + ds->drv->port_stp_update(ds, p->port, stp_state); + if (p->phy) phy_start(p->phy); @@ -133,6 +146,9 @@ static int dsa_slave_close(struct net_device *dev) if (ds->drv->port_disable) ds->drv->port_disable(ds, p->port, p->phy); + if (ds->drv->port_stp_update) + ds->drv->port_stp_update(ds, p->port, BR_STATE_DISABLED); + return 0; } @@ -194,6 +210,95 @@ static int dsa_slave_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) return -EOPNOTSUPP; } +/* Return a bitmask of all ports being currently bridged within a given bridge + * device. Note that on leave, the mask will still return the bitmask of ports + * currently bridged, prior to port removal, and this is exactly what we want. + */ +static u32 dsa_slave_br_port_mask(struct dsa_switch *ds, + struct net_device *bridge) +{ + struct dsa_slave_priv *p; + unsigned int port; + u32 mask = 0; + + for (port = 0; port < DSA_MAX_PORTS; port++) { + if (!((1 << port) & ds->phys_port_mask)) + continue; + + if (!ds->ports[port]) + continue; + + p = netdev_priv(ds->ports[port]); + + if (ds->ports[port]->priv_flags & IFF_BRIDGE_PORT && + p->bridge_dev == bridge) + mask |= 1 << port; + } + + return mask; +} + +static int dsa_slave_stp_update(struct net_device *dev, u8 state) +{ + struct dsa_slave_priv *p = netdev_priv(dev); + struct dsa_switch *ds = p->parent; + int ret = -EOPNOTSUPP; + + if (ds->drv->port_stp_update) + ret = ds->drv->port_stp_update(ds, p->port, state); + + return ret; +} + +static int dsa_slave_bridge_port_join(struct net_device *dev, + struct net_device *br) +{ + struct dsa_slave_priv *p = netdev_priv(dev); + struct dsa_switch *ds = p->parent; + int ret = -EOPNOTSUPP; + + p->bridge_dev = br; + + if (ds->drv->port_join_bridge) + ret = ds->drv->port_join_bridge(ds, p->port, + dsa_slave_br_port_mask(ds, br)); + + return ret; +} + +static int dsa_slave_bridge_port_leave(struct net_device *dev) +{ + struct dsa_slave_priv *p = netdev_priv(dev); + struct dsa_switch *ds = p->parent; + int ret = -EOPNOTSUPP; + + + if (ds->drv->port_leave_bridge) + ret = ds->drv->port_leave_bridge(ds, p->port, + dsa_slave_br_port_mask(ds, p->bridge_dev)); + + p->bridge_dev = NULL; + + /* Port left the bridge, put in BR_STATE_DISABLED by the bridge layer, + * so allow it to be in BR_STATE_FORWARDING to be kept functional + */ + dsa_slave_stp_update(dev, BR_STATE_FORWARDING); + + return ret; +} + +static int dsa_slave_parent_id_get(struct net_device *dev, + struct netdev_phys_item_id *psid) +{ + struct dsa_slave_priv *p = netdev_priv(dev); + struct dsa_switch *ds = p->parent; + + psid->id_len = sizeof(ds->index); + memcpy(&psid->id, &ds->index, psid->id_len); + + return 0; +} + static netdev_tx_t dsa_slave_xmit(struct sk_buff *skb, struct net_device *dev) { struct dsa_slave_priv *p = netdev_priv(dev); @@ -470,6 +575,8 @@ static const struct net_device_ops dsa_slave_netdev_ops = { .ndo_set_rx_mode = dsa_slave_set_rx_mode, .ndo_set_mac_address = dsa_slave_set_mac_address, .ndo_do_ioctl = dsa_slave_ioctl, + .ndo_switch_parent_id_get = dsa_slave_parent_id_get, + .ndo_switch_port_stp_update = dsa_slave_stp_update, }; static void dsa_slave_adjust_link(struct net_device *dev) @@ -684,3 +791,45 @@ int dsa_slave_create(struct dsa_switch *ds, struct device *parent, return 0; } + +static bool dsa_slave_dev_check(struct net_device *dev) +{ + return dev->netdev_ops == &dsa_slave_netdev_ops; +} + +static int dsa_slave_master_changed(struct net_device *dev) +{ + struct net_device *master = netdev_master_upper_dev_get(dev); + int err = 0; + + if (master && master->rtnl_link_ops && + !strcmp(master->rtnl_link_ops->kind, "bridge")) + err = dsa_slave_bridge_port_join(dev, master); + else + err = dsa_slave_bridge_port_leave(dev); + + return err; +} + +int dsa_slave_netdevice_event(struct notifier_block *unused, + unsigned long event, void *ptr) +{ + struct net_device *dev; + int err = 0; + + switch (event) { + case NETDEV_CHANGEUPPER: + dev = netdev_notifier_info_to_dev(ptr); + if (!dsa_slave_dev_check(dev)) + goto out; + + err = dsa_slave_master_changed(dev); + if (err) + netdev_warn(dev, "failed to reflect master change\n"); + + break; + } + +out: + return NOTIFY_DONE; +} -- cgit v1.2.3 From d79d21073626cf022943e5c4c10a97cdf7cb8465 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Tue, 24 Feb 2015 23:02:02 -0800 Subject: net: dsa: Introduce dsa_is_port_initialized To avoid race conditions when using the ds->ports[] array, we need to check if the accessed port has been initialized. Introduce and use helper function dsa_is_port_initialized for that purpose and use it where needed. Signed-off-by: Guenter Roeck Signed-off-by: David S. Miller --- include/net/dsa.h | 5 +++++ net/dsa/dsa.c | 4 ++-- net/dsa/slave.c | 5 +---- 3 files changed, 8 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/include/net/dsa.h b/include/net/dsa.h index 92be34791963..c542c131d551 100644 --- a/include/net/dsa.h +++ b/include/net/dsa.h @@ -165,6 +165,11 @@ static inline bool dsa_is_cpu_port(struct dsa_switch *ds, int p) return !!(ds->index == ds->dst->cpu_switch && p == ds->dst->cpu_port); } +static inline bool dsa_is_port_initialized(struct dsa_switch *ds, int p) +{ + return ds->phys_port_mask & (1 << p) && ds->ports[p]; +} + static inline u8 dsa_upstream_port(struct dsa_switch *ds) { struct dsa_switch_tree *dst = ds->dst; diff --git a/net/dsa/dsa.c b/net/dsa/dsa.c index 9c208f0dab08..a1d1f0775bea 100644 --- a/net/dsa/dsa.c +++ b/net/dsa/dsa.c @@ -374,7 +374,7 @@ static int dsa_switch_suspend(struct dsa_switch *ds) /* Suspend slave network devices */ for (i = 0; i < DSA_MAX_PORTS; i++) { - if (!(ds->phys_port_mask & (1 << i))) + if (!dsa_is_port_initialized(ds, i)) continue; ret = dsa_slave_suspend(ds->ports[i]); @@ -400,7 +400,7 @@ static int dsa_switch_resume(struct dsa_switch *ds) /* Resume slave network devices */ for (i = 0; i < DSA_MAX_PORTS; i++) { - if (!(ds->phys_port_mask & (1 << i))) + if (!dsa_is_port_initialized(ds, i)) continue; ret = dsa_slave_resume(ds->ports[i]); diff --git a/net/dsa/slave.c b/net/dsa/slave.c index b5a4d8974b76..a47305c72fcc 100644 --- a/net/dsa/slave.c +++ b/net/dsa/slave.c @@ -222,10 +222,7 @@ static u32 dsa_slave_br_port_mask(struct dsa_switch *ds, u32 mask = 0; for (port = 0; port < DSA_MAX_PORTS; port++) { - if (!((1 << port) & ds->phys_port_mask)) - continue; - - if (!ds->ports[port]) + if (!dsa_is_port_initialized(ds, port)) continue; p = netdev_priv(ds->ports[port]); -- cgit v1.2.3 From ee376dbdf27728a2f3d30e2ba10fa387cc4c645b Mon Sep 17 00:00:00 2001 From: "Paul E. McKenney" Date: Sat, 10 Jan 2015 19:47:10 -0800 Subject: rcu: Consolidate rcu_synchronize and wakeme_after_rcu() There are currently duplicate identical definitions of the rcu_synchronize() structure and the wakeme_after_rcu() function. Thie commit therefore consolidates them. Signed-off-by: Paul E. McKenney --- include/linux/rcupdate.h | 9 +++++++++ kernel/rcu/srcu.c | 17 ----------------- kernel/rcu/update.c | 15 ++++++--------- 3 files changed, 15 insertions(+), 26 deletions(-) (limited to 'include') diff --git a/include/linux/rcupdate.h b/include/linux/rcupdate.h index 78097491cd99..3e6afed51051 100644 --- a/include/linux/rcupdate.h +++ b/include/linux/rcupdate.h @@ -195,6 +195,15 @@ void call_rcu_sched(struct rcu_head *head, void synchronize_sched(void); +/* + * Structure allowing asynchronous waiting on RCU. + */ +struct rcu_synchronize { + struct rcu_head head; + struct completion completion; +}; +void wakeme_after_rcu(struct rcu_head *head); + /** * call_rcu_tasks() - Queue an RCU for invocation task-based grace period * @head: structure to be used for queueing the RCU updates. diff --git a/kernel/rcu/srcu.c b/kernel/rcu/srcu.c index 445bf8ffe3fb..81f53b504c18 100644 --- a/kernel/rcu/srcu.c +++ b/kernel/rcu/srcu.c @@ -402,23 +402,6 @@ void call_srcu(struct srcu_struct *sp, struct rcu_head *head, } EXPORT_SYMBOL_GPL(call_srcu); -struct rcu_synchronize { - struct rcu_head head; - struct completion completion; -}; - -/* - * Awaken the corresponding synchronize_srcu() instance now that a - * grace period has elapsed. - */ -static void wakeme_after_rcu(struct rcu_head *head) -{ - struct rcu_synchronize *rcu; - - rcu = container_of(head, struct rcu_synchronize, head); - complete(&rcu->completion); -} - static void srcu_advance_batches(struct srcu_struct *sp, int trycount); static void srcu_reschedule(struct srcu_struct *sp); diff --git a/kernel/rcu/update.c b/kernel/rcu/update.c index e0d31a345ee6..8864ed90f0d7 100644 --- a/kernel/rcu/update.c +++ b/kernel/rcu/update.c @@ -199,16 +199,13 @@ EXPORT_SYMBOL_GPL(rcu_read_lock_bh_held); #endif /* #ifdef CONFIG_DEBUG_LOCK_ALLOC */ -struct rcu_synchronize { - struct rcu_head head; - struct completion completion; -}; - -/* - * Awaken the corresponding synchronize_rcu() instance now that a - * grace period has elapsed. +/** + * wakeme_after_rcu() - Callback function to awaken a task after grace period + * @head: Pointer to rcu_head member within rcu_synchronize structure + * + * Awaken the corresponding task now that a grace period has elapsed. */ -static void wakeme_after_rcu(struct rcu_head *head) +void wakeme_after_rcu(struct rcu_head *head) { struct rcu_synchronize *rcu; -- cgit v1.2.3 From e5d4ef0d731664b3fe204f4e5e87f5756e848fb1 Mon Sep 17 00:00:00 2001 From: Richard Fitzgerald Date: Sat, 17 Jan 2015 15:21:22 +0000 Subject: mfd: arizona: Add support for WM8280/WM8281 This adds support for the Wolfson Microelectronics WM8280 and WM8281 codecs. Signed-off-by: Richard Fitzgerald Signed-off-by: Charles Keepax [Lee: Minor fixup to remove potentially uninitialised variable. ] Signed-off-by: Lee Jones --- drivers/mfd/Kconfig | 5 +++-- drivers/mfd/arizona-core.c | 15 +++++++++++++-- drivers/mfd/arizona-i2c.c | 2 ++ drivers/mfd/arizona-irq.c | 1 + drivers/mfd/arizona-spi.c | 2 ++ include/linux/mfd/arizona/core.h | 1 + 6 files changed, 22 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 38356e39adba..9b5e60564d97 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -1289,10 +1289,11 @@ config MFD_WM5102 Support for Wolfson Microelectronics WM5102 low power audio SoC config MFD_WM5110 - bool "Wolfson Microelectronics WM5110" + bool "Wolfson Microelectronics WM5110 and WM8280/WM8281" depends on MFD_ARIZONA help - Support for Wolfson Microelectronics WM5110 low power audio SoC + Support for Wolfson Microelectronics WM5110 and WM8280/WM8281 + low power audio SoC config MFD_WM8997 bool "Wolfson Microelectronics WM8997" diff --git a/drivers/mfd/arizona-core.c b/drivers/mfd/arizona-core.c index 09ba8f186e6a..9f819989683b 100644 --- a/drivers/mfd/arizona-core.c +++ b/drivers/mfd/arizona-core.c @@ -567,6 +567,7 @@ static int arizona_of_get_core_pdata(struct arizona *arizona) const struct of_device_id arizona_of_match[] = { { .compatible = "wlf,wm5102", .data = (void *)WM5102 }, { .compatible = "wlf,wm5110", .data = (void *)WM5110 }, + { .compatible = "wlf,wm8280", .data = (void *)WM8280 }, { .compatible = "wlf,wm8997", .data = (void *)WM8997 }, {}, }; @@ -671,6 +672,7 @@ int arizona_dev_init(struct arizona *arizona) switch (arizona->type) { case WM5102: case WM5110: + case WM8280: case WM8997: for (i = 0; i < ARRAY_SIZE(wm5102_core_supplies); i++) arizona->core_supplies[i].supply @@ -834,11 +836,19 @@ int arizona_dev_init(struct arizona *arizona) #endif #ifdef CONFIG_MFD_WM5110 case 0x5110: - type_name = "WM5110"; - if (arizona->type != WM5110) { + switch (arizona->type) { + case WM5110: + type_name = "WM5110"; + break; + case WM8280: + type_name = "WM8280"; + break; + default: + type_name = "WM5110"; dev_err(arizona->dev, "WM5110 registered as %d\n", arizona->type); arizona->type = WM5110; + break; } apply_patch = wm5110_patch; break; @@ -1010,6 +1020,7 @@ int arizona_dev_init(struct arizona *arizona) ARRAY_SIZE(wm5102_devs), NULL, 0, NULL); break; case WM5110: + case WM8280: ret = mfd_add_devices(arizona->dev, -1, wm5110_devs, ARRAY_SIZE(wm5110_devs), NULL, 0, NULL); break; diff --git a/drivers/mfd/arizona-i2c.c b/drivers/mfd/arizona-i2c.c index 9d4156fb082a..ff782a5de235 100644 --- a/drivers/mfd/arizona-i2c.c +++ b/drivers/mfd/arizona-i2c.c @@ -44,6 +44,7 @@ static int arizona_i2c_probe(struct i2c_client *i2c, #endif #ifdef CONFIG_MFD_WM5110 case WM5110: + case WM8280: regmap_config = &wm5110_i2c_regmap; break; #endif @@ -87,6 +88,7 @@ static int arizona_i2c_remove(struct i2c_client *i2c) static const struct i2c_device_id arizona_i2c_id[] = { { "wm5102", WM5102 }, { "wm5110", WM5110 }, + { "wm8280", WM8280 }, { "wm8997", WM8997 }, { } }; diff --git a/drivers/mfd/arizona-irq.c b/drivers/mfd/arizona-irq.c index 3a3fe7cc6d61..d063b94b94b5 100644 --- a/drivers/mfd/arizona-irq.c +++ b/drivers/mfd/arizona-irq.c @@ -211,6 +211,7 @@ int arizona_irq_init(struct arizona *arizona) #endif #ifdef CONFIG_MFD_WM5110 case WM5110: + case WM8280: aod = &wm5110_aod; switch (arizona->rev) { diff --git a/drivers/mfd/arizona-spi.c b/drivers/mfd/arizona-spi.c index 8ef58bcff193..1e845f6d407b 100644 --- a/drivers/mfd/arizona-spi.c +++ b/drivers/mfd/arizona-spi.c @@ -44,6 +44,7 @@ static int arizona_spi_probe(struct spi_device *spi) #endif #ifdef CONFIG_MFD_WM5110 case WM5110: + case WM8280: regmap_config = &wm5110_spi_regmap; break; #endif @@ -84,6 +85,7 @@ static int arizona_spi_remove(struct spi_device *spi) static const struct spi_device_id arizona_spi_ids[] = { { "wm5102", WM5102 }, { "wm5110", WM5110 }, + { "wm8280", WM8280 }, { }, }; MODULE_DEVICE_TABLE(spi, arizona_spi_ids); diff --git a/include/linux/mfd/arizona/core.h b/include/linux/mfd/arizona/core.h index 910e3aa1e965..f97010576f56 100644 --- a/include/linux/mfd/arizona/core.h +++ b/include/linux/mfd/arizona/core.h @@ -24,6 +24,7 @@ enum arizona_type { WM5102 = 1, WM5110 = 2, WM8997 = 3, + WM8280 = 4, }; #define ARIZONA_IRQ_GP1 0 -- cgit v1.2.3 From 7a16360d56f6846da5a7c8cb50b14e3464ad133a Mon Sep 17 00:00:00 2001 From: Tomi Valkeinen Date: Thu, 2 Oct 2014 17:58:48 +0000 Subject: OMAPDSS: DISPC: remove OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES DISPC can drive data lines either on rising or falling pixel clock edge, which can be configured by the user. Sync lines can also be driven on rising or falling pixel clock edge, but additionally the HW can be configured to drive the sync lines on opposite clock edge from the data lines. This opposite edge setting does not make any sense, as the same effect can be achieved by just setting the sync lines to be driven on the other edge compared to the data lines. It feels like some kind of backward compatibility option, even if all DSS versions seem to have the same implementation. To simplify the code and configuration of the signals, and to make the dispc timings more compatible with what is used on other platforms, let's just remove the whole opposite-edge support. The drivers that used OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES setting are changed so that they use the opposite setting from the data edge. Signed-off-by: Tomi Valkeinen --- drivers/gpu/drm/omapdrm/omap_connector.c | 2 +- drivers/video/fbdev/omap2/displays-new/connector-dvi.c | 2 +- .../video/fbdev/omap2/displays-new/panel-lgphilips-lb035q02.c | 2 +- .../video/fbdev/omap2/displays-new/panel-sharp-ls037v7dw01.c | 2 +- drivers/video/fbdev/omap2/displays-new/panel-sony-acx565akm.c | 2 +- drivers/video/fbdev/omap2/displays-new/panel-tpo-td028ttec1.c | 2 +- drivers/video/fbdev/omap2/displays-new/panel-tpo-td043mtea1.c | 2 +- drivers/video/fbdev/omap2/dss/dispc.c | 10 +++------- drivers/video/fbdev/omap2/dss/display.c | 4 +++- drivers/video/fbdev/omap2/dss/dsi.c | 2 +- drivers/video/fbdev/omap2/dss/rfbi.c | 2 +- drivers/video/fbdev/omap2/omapfb/omapfb-main.c | 4 ++-- include/video/omapdss.h | 1 - 13 files changed, 17 insertions(+), 20 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/omapdrm/omap_connector.c b/drivers/gpu/drm/omapdrm/omap_connector.c index a94b11f7859d..2c2173bc3f00 100644 --- a/drivers/gpu/drm/omapdrm/omap_connector.c +++ b/drivers/gpu/drm/omapdrm/omap_connector.c @@ -102,7 +102,7 @@ void copy_timings_drm_to_omap(struct omap_video_timings *timings, timings->data_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE; timings->de_level = OMAPDSS_SIG_ACTIVE_HIGH; - timings->sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES; + timings->sync_pclk_edge = OMAPDSS_DRIVE_SIG_FALLING_EDGE; } static enum drm_connector_status omap_connector_detect( diff --git a/drivers/video/fbdev/omap2/displays-new/connector-dvi.c b/drivers/video/fbdev/omap2/displays-new/connector-dvi.c index 0cdc97413020..a8ce920fa797 100644 --- a/drivers/video/fbdev/omap2/displays-new/connector-dvi.c +++ b/drivers/video/fbdev/omap2/displays-new/connector-dvi.c @@ -37,7 +37,7 @@ static const struct omap_video_timings dvic_default_timings = { .hsync_level = OMAPDSS_SIG_ACTIVE_HIGH, .data_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE, .de_level = OMAPDSS_SIG_ACTIVE_HIGH, - .sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES, + .sync_pclk_edge = OMAPDSS_DRIVE_SIG_FALLING_EDGE, }; struct panel_drv_data { diff --git a/drivers/video/fbdev/omap2/displays-new/panel-lgphilips-lb035q02.c b/drivers/video/fbdev/omap2/displays-new/panel-lgphilips-lb035q02.c index 27d4fcfa1824..9974a37a11af 100644 --- a/drivers/video/fbdev/omap2/displays-new/panel-lgphilips-lb035q02.c +++ b/drivers/video/fbdev/omap2/displays-new/panel-lgphilips-lb035q02.c @@ -37,7 +37,7 @@ static struct omap_video_timings lb035q02_timings = { .hsync_level = OMAPDSS_SIG_ACTIVE_LOW, .data_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE, .de_level = OMAPDSS_SIG_ACTIVE_HIGH, - .sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES, + .sync_pclk_edge = OMAPDSS_DRIVE_SIG_FALLING_EDGE, }; struct panel_drv_data { diff --git a/drivers/video/fbdev/omap2/displays-new/panel-sharp-ls037v7dw01.c b/drivers/video/fbdev/omap2/displays-new/panel-sharp-ls037v7dw01.c index 18b19b6e1ac2..eae263702964 100644 --- a/drivers/video/fbdev/omap2/displays-new/panel-sharp-ls037v7dw01.c +++ b/drivers/video/fbdev/omap2/displays-new/panel-sharp-ls037v7dw01.c @@ -54,7 +54,7 @@ static const struct omap_video_timings sharp_ls_timings = { .hsync_level = OMAPDSS_SIG_ACTIVE_LOW, .data_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE, .de_level = OMAPDSS_SIG_ACTIVE_HIGH, - .sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES, + .sync_pclk_edge = OMAPDSS_DRIVE_SIG_FALLING_EDGE, }; #define to_panel_data(p) container_of(p, struct panel_drv_data, dssdev) diff --git a/drivers/video/fbdev/omap2/displays-new/panel-sony-acx565akm.c b/drivers/video/fbdev/omap2/displays-new/panel-sony-acx565akm.c index 337ccc5c0f5e..90cbc4c3406c 100644 --- a/drivers/video/fbdev/omap2/displays-new/panel-sony-acx565akm.c +++ b/drivers/video/fbdev/omap2/displays-new/panel-sony-acx565akm.c @@ -108,7 +108,7 @@ static const struct omap_video_timings acx565akm_panel_timings = { .data_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE, .de_level = OMAPDSS_SIG_ACTIVE_HIGH, - .sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES, + .sync_pclk_edge = OMAPDSS_DRIVE_SIG_FALLING_EDGE, }; #define to_panel_data(p) container_of(p, struct panel_drv_data, dssdev) diff --git a/drivers/video/fbdev/omap2/displays-new/panel-tpo-td028ttec1.c b/drivers/video/fbdev/omap2/displays-new/panel-tpo-td028ttec1.c index fbba0b8ca871..9edc51133c59 100644 --- a/drivers/video/fbdev/omap2/displays-new/panel-tpo-td028ttec1.c +++ b/drivers/video/fbdev/omap2/displays-new/panel-tpo-td028ttec1.c @@ -58,7 +58,7 @@ static struct omap_video_timings td028ttec1_panel_timings = { .data_pclk_edge = OMAPDSS_DRIVE_SIG_FALLING_EDGE, .de_level = OMAPDSS_SIG_ACTIVE_HIGH, - .sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES, + .sync_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE, }; #define JBT_COMMAND 0x000 diff --git a/drivers/video/fbdev/omap2/displays-new/panel-tpo-td043mtea1.c b/drivers/video/fbdev/omap2/displays-new/panel-tpo-td043mtea1.c index 5aba76bca25a..79e4a029aab9 100644 --- a/drivers/video/fbdev/omap2/displays-new/panel-tpo-td043mtea1.c +++ b/drivers/video/fbdev/omap2/displays-new/panel-tpo-td043mtea1.c @@ -91,7 +91,7 @@ static const struct omap_video_timings tpo_td043_timings = { .hsync_level = OMAPDSS_SIG_ACTIVE_LOW, .data_pclk_edge = OMAPDSS_DRIVE_SIG_FALLING_EDGE, .de_level = OMAPDSS_SIG_ACTIVE_HIGH, - .sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES, + .sync_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE, }; #define to_panel_data(p) container_of(p, struct panel_drv_data, dssdev) diff --git a/drivers/video/fbdev/omap2/dss/dispc.c b/drivers/video/fbdev/omap2/dss/dispc.c index a19a1d4b2d19..11bd780fcdfa 100644 --- a/drivers/video/fbdev/omap2/dss/dispc.c +++ b/drivers/video/fbdev/omap2/dss/dispc.c @@ -2934,22 +2934,18 @@ static void _dispc_mgr_set_lcd_timings(enum omap_channel channel, int hsw, case OMAPDSS_DRIVE_SIG_FALLING_EDGE: ipc = true; break; - case OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES: default: BUG(); } + /* always use the 'rf' setting */ + onoff = true; + switch (sync_pclk_edge) { - case OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES: - onoff = false; - rf = false; - break; case OMAPDSS_DRIVE_SIG_FALLING_EDGE: - onoff = true; rf = false; break; case OMAPDSS_DRIVE_SIG_RISING_EDGE: - onoff = true; rf = true; break; default: diff --git a/drivers/video/fbdev/omap2/dss/display.c b/drivers/video/fbdev/omap2/dss/display.c index 2412a0dd0c13..a6fd2d35ad60 100644 --- a/drivers/video/fbdev/omap2/dss/display.c +++ b/drivers/video/fbdev/omap2/dss/display.c @@ -295,7 +295,9 @@ void videomode_to_omap_video_timings(const struct videomode *vm, OMAPDSS_DRIVE_SIG_RISING_EDGE : OMAPDSS_DRIVE_SIG_FALLING_EDGE; - ovt->sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES; + ovt->sync_pclk_edge = vm->flags & DISPLAY_FLAGS_PIXDATA_POSEDGE ? + OMAPDSS_DRIVE_SIG_FALLING_EDGE : + OMAPDSS_DRIVE_SIG_RISING_EDGE; } EXPORT_SYMBOL(videomode_to_omap_video_timings); diff --git a/drivers/video/fbdev/omap2/dss/dsi.c b/drivers/video/fbdev/omap2/dss/dsi.c index 5081f6fb1737..28b0bc11669d 100644 --- a/drivers/video/fbdev/omap2/dss/dsi.c +++ b/drivers/video/fbdev/omap2/dss/dsi.c @@ -4137,7 +4137,7 @@ static int dsi_display_init_dispc(struct platform_device *dsidev, dsi->timings.vsync_level = OMAPDSS_SIG_ACTIVE_HIGH; dsi->timings.data_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE; dsi->timings.de_level = OMAPDSS_SIG_ACTIVE_HIGH; - dsi->timings.sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES; + dsi->timings.sync_pclk_edge = OMAPDSS_DRIVE_SIG_FALLING_EDGE; dss_mgr_set_timings(mgr, &dsi->timings); diff --git a/drivers/video/fbdev/omap2/dss/rfbi.c b/drivers/video/fbdev/omap2/dss/rfbi.c index 28e694b11ff9..065effca9236 100644 --- a/drivers/video/fbdev/omap2/dss/rfbi.c +++ b/drivers/video/fbdev/omap2/dss/rfbi.c @@ -869,7 +869,7 @@ static void rfbi_config_lcd_manager(struct omap_dss_device *dssdev) rfbi.timings.vsync_level = OMAPDSS_SIG_ACTIVE_HIGH; rfbi.timings.data_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE; rfbi.timings.de_level = OMAPDSS_SIG_ACTIVE_HIGH; - rfbi.timings.sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES; + rfbi.timings.sync_pclk_edge = OMAPDSS_DRIVE_SIG_FALLING_EDGE; dss_mgr_set_timings(mgr, &rfbi.timings); } diff --git a/drivers/video/fbdev/omap2/omapfb/omapfb-main.c b/drivers/video/fbdev/omap2/omapfb/omapfb-main.c index 22f07f88bc40..4f0cbb54d4db 100644 --- a/drivers/video/fbdev/omap2/omapfb/omapfb-main.c +++ b/drivers/video/fbdev/omap2/omapfb/omapfb-main.c @@ -2073,7 +2073,7 @@ static int omapfb_mode_to_timings(const char *mode_str, } else { timings->data_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE; timings->de_level = OMAPDSS_SIG_ACTIVE_HIGH; - timings->sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES; + timings->sync_pclk_edge = OMAPDSS_DRIVE_SIG_FALLING_EDGE; } timings->pixelclock = PICOS2KHZ(var->pixclock) * 1000; @@ -2223,7 +2223,7 @@ static void fb_videomode_to_omap_timings(struct fb_videomode *m, } else { t->data_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE; t->de_level = OMAPDSS_SIG_ACTIVE_HIGH; - t->sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES; + t->sync_pclk_edge = OMAPDSS_DRIVE_SIG_FALLING_EDGE; } t->x_res = m->xres; diff --git a/include/video/omapdss.h b/include/video/omapdss.h index 60de61fea8e3..7414e4a97508 100644 --- a/include/video/omapdss.h +++ b/include/video/omapdss.h @@ -134,7 +134,6 @@ enum omap_dss_signal_level { }; enum omap_dss_signal_edge { - OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES, OMAPDSS_DRIVE_SIG_RISING_EDGE, OMAPDSS_DRIVE_SIG_FALLING_EDGE, }; -- cgit v1.2.3 From 7ad582be12728ea76b11ce4f4c2f1202d11bf13c Mon Sep 17 00:00:00 2001 From: Tomi Valkeinen Date: Thu, 2 Oct 2014 17:58:50 +0000 Subject: OMAPDSS: change signal_level & signal_edge enum values At the moment the enum values for ACTIVE_HIGH and RISING_EDGE are 0, and ACTIVE_LOW and FALLING_EDGE are 1, to match the values programmed to HW. The previous patch removed this dependency. Swap the enum values the other way around. This doesn't change the behavior in any way, but makes it easier to debug as value of '1' means HIGH or RISING. Signed-off-by: Tomi Valkeinen --- include/video/omapdss.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/include/video/omapdss.h b/include/video/omapdss.h index 7414e4a97508..45a230190720 100644 --- a/include/video/omapdss.h +++ b/include/video/omapdss.h @@ -129,13 +129,13 @@ enum omap_rfbi_te_mode { }; enum omap_dss_signal_level { - OMAPDSS_SIG_ACTIVE_HIGH = 0, - OMAPDSS_SIG_ACTIVE_LOW = 1, + OMAPDSS_SIG_ACTIVE_LOW, + OMAPDSS_SIG_ACTIVE_HIGH, }; enum omap_dss_signal_edge { - OMAPDSS_DRIVE_SIG_RISING_EDGE, OMAPDSS_DRIVE_SIG_FALLING_EDGE, + OMAPDSS_DRIVE_SIG_RISING_EDGE, }; enum omap_dss_venc_type { -- cgit v1.2.3 From 7e182f78988404717e27aed5a9d4150631b323f4 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Wed, 25 Feb 2015 12:05:13 -0300 Subject: [media] media.h: mark alsa struct in media_entity_desc as TODO The alsa struct in struct media_entity_desc is now marked as deprecated. However, the alsa struct should remain as it is since it cannot be replaced by a simple major/minor device node description. The alsa struct was designed to be used as an alsa card description so V4L2 drivers could use this to expose the alsa card that they create to carry the captured audio. Such a card is not just a PCM device, but also needs to contain the alsa subdevice information, and it may map to multiple devices, e.g. a PCM and a mixer device, such as the au0828 usb stick creates. This is exactly as intended and this cannot and should not be replaced by a simple major/minor. However, whether this information is in the right form for an ALSA device such that it can handle udev renaming rules as well is another matter. So mark this alsa struct as TODO and document the problems involved. Updated the documentation as well to reflect this and to add the 'major' and 'minor' field documentation. Updated the documentation to clearly state that struct dev is to be used for (sub-)devices that create a single device node. Other devices need their own structure here. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- .../DocBook/media/v4l/media-ioc-enum-entities.xml | 16 ++++++++++++- include/uapi/linux/media.h | 26 +++++++++++++++++----- 2 files changed, 36 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/Documentation/DocBook/media/v4l/media-ioc-enum-entities.xml b/Documentation/DocBook/media/v4l/media-ioc-enum-entities.xml index cbf307f21a63..5872f8bbf774 100644 --- a/Documentation/DocBook/media/v4l/media-ioc-enum-entities.xml +++ b/Documentation/DocBook/media/v4l/media-ioc-enum-entities.xml @@ -145,7 +145,21 @@ struct dev - Valid for (sub-)devices that create devnodes. + Valid for (sub-)devices that create a single device node. + + + + + __u32 + major + Device node major number. + + + + + __u32 + minor + Device node minor number. diff --git a/include/uapi/linux/media.h b/include/uapi/linux/media.h index 52cc2a6b19b7..4e816be3de39 100644 --- a/include/uapi/linux/media.h +++ b/include/uapi/linux/media.h @@ -89,6 +89,27 @@ struct media_entity_desc { __u32 minor; } dev; +#if 1 + /* + * TODO: this shouldn't have been added without + * actual drivers that use this. When the first real driver + * appears that sets this information, special attention + * should be given whether this information is 1) enough, and + * 2) can deal with udev rules that rename devices. The struct + * dev would not be sufficient for this since that does not + * contain the subdevice information. In addition, struct dev + * can only refer to a single device, and not to multiple (e.g. + * pcm and mixer devices). + * + * So for now mark this as a to do. + */ + struct { + __u32 card; + __u32 device; + __u32 subdevice; + } alsa; +#endif + #if 1 /* * DEPRECATED: previous node specifications. Kept just to @@ -106,11 +127,6 @@ struct media_entity_desc { __u32 major; __u32 minor; } fb; - struct { - __u32 card; - __u32 device; - __u32 subdevice; - } alsa; int dvb; #endif -- cgit v1.2.3 From 0d39482c3db13aae1db143d340816108dd53e443 Mon Sep 17 00:00:00 2001 From: "Paul E. McKenney" Date: Wed, 18 Feb 2015 12:24:30 -0800 Subject: rcu: Provide rcu_expedite_gp() and rcu_unexpedite_gp() Currently, expediting of normal synchronous grace-period primitives (synchronize_rcu() and friends) is controlled by the rcu_expedited() boot/sysfs parameter. This works well, but does not handle nesting. This commit therefore provides rcu_expedite_gp() to enable expediting and rcu_unexpedite_gp() to cancel a prior rcu_expedite_gp(), both of which support nesting. Reported-by: Arjan van de Ven Signed-off-by: Paul E. McKenney --- include/linux/rcupdate.h | 20 ++++++++++++++++++++ kernel/rcu/update.c | 48 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 68 insertions(+) (limited to 'include') diff --git a/include/linux/rcupdate.h b/include/linux/rcupdate.h index 78097491cd99..57a4d1f73a00 100644 --- a/include/linux/rcupdate.h +++ b/include/linux/rcupdate.h @@ -48,6 +48,26 @@ extern int rcu_expedited; /* for sysctl */ +#ifdef CONFIG_TINY_RCU +/* Tiny RCU doesn't expedite, as its purpose in life is instead to be tiny. */ +static inline bool rcu_gp_is_expedited(void) /* Internal RCU use. */ +{ + return false; +} + +static inline void rcu_expedite_gp(void) +{ +} + +static inline void rcu_unexpedite_gp(void) +{ +} +#else /* #ifdef CONFIG_TINY_RCU */ +bool rcu_gp_is_expedited(void); /* Internal RCU use. */ +void rcu_expedite_gp(void); +void rcu_unexpedite_gp(void); +#endif /* #else #ifdef CONFIG_TINY_RCU */ + enum rcutorture_type { RCU_FLAVOR, RCU_BH_FLAVOR, diff --git a/kernel/rcu/update.c b/kernel/rcu/update.c index e0d31a345ee6..5f850823c187 100644 --- a/kernel/rcu/update.c +++ b/kernel/rcu/update.c @@ -62,6 +62,54 @@ MODULE_ALIAS("rcupdate"); module_param(rcu_expedited, int, 0); +#ifndef CONFIG_TINY_RCU + +static atomic_t rcu_expedited_nesting; + +/* + * Should normal grace-period primitives be expedited? Intended for + * use within RCU. Note that this function takes the rcu_expedited + * sysfs/boot variable into account as well as the rcu_expedite_gp() + * nesting. So looping on rcu_unexpedite_gp() until rcu_gp_is_expedited() + * returns false is a -really- bad idea. + */ +bool rcu_gp_is_expedited(void) +{ + return rcu_expedited || atomic_read(&rcu_expedited_nesting); +} +EXPORT_SYMBOL_GPL(rcu_gp_is_expedited); + +/** + * rcu_expedite_gp - Expedite future RCU grace periods + * + * After a call to this function, future calls to synchronize_rcu() and + * friends act as the corresponding synchronize_rcu_expedited() function + * had instead been called. + */ +void rcu_expedite_gp(void) +{ + atomic_inc(&rcu_expedited_nesting); +} +EXPORT_SYMBOL_GPL(rcu_expedite_gp); + +/** + * rcu_unexpedite_gp - Cancel prior rcu_expedite_gp() invocation + * + * Undo a prior call to rcu_expedite_gp(). If all prior calls to + * rcu_expedite_gp() are undone by a subsequent call to rcu_unexpedite_gp(), + * and if the rcu_expedited sysfs/boot parameter is not set, then all + * subsequent calls to synchronize_rcu() and friends will return to + * their normal non-expedited behavior. + */ +void rcu_unexpedite_gp(void) +{ + atomic_dec(&rcu_expedited_nesting); +} +EXPORT_SYMBOL_GPL(rcu_unexpedite_gp); + +#endif /* #ifndef CONFIG_TINY_RCU */ + + #ifdef CONFIG_PREEMPT_RCU /* -- cgit v1.2.3 From ee42571f4381f184e2672dd34ab411e5bf5bd5e0 Mon Sep 17 00:00:00 2001 From: "Paul E. McKenney" Date: Thu, 19 Feb 2015 10:51:32 -0800 Subject: rcu: Add Kconfig option to expedite grace periods during boot This commit adds a CONFIG_RCU_EXPEDITE_BOOT Kconfig parameter that emulates a very early boot rcu_expedite_gp(). A late-boot call to rcu_end_inkernel_boot() will provide the corresponding rcu_unexpedite_gp(). The late-boot call to rcu_end_inkernel_boot() should be made just before init is spawned. According to Arjan: > To show the boot time, I'm using the timestamp of the "Write protecting" > line, that's pretty much the last thing we print prior to ring 3 execution. > > A kernel with default RCU behavior (inside KVM, only virtual devices) > looks like this: > > [ 0.038724] Write protecting the kernel read-only data: 10240k > > a kernel with expedited RCU (using the command line option, so that I > don't have to recompile between measurements and thus am completely > oranges-to-oranges) > > [ 0.031768] Write protecting the kernel read-only data: 10240k > > which, in percentage, is an 18% improvement. Reported-by: Arjan van de Ven Signed-off-by: Paul E. McKenney Tested-by: Arjan van de Ven --- include/linux/rcupdate.h | 1 + init/Kconfig | 13 +++++++++++++ kernel/rcu/update.c | 11 ++++++++++- 3 files changed, 24 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/rcupdate.h b/include/linux/rcupdate.h index 57a4d1f73a00..b9f039b11d31 100644 --- a/include/linux/rcupdate.h +++ b/include/linux/rcupdate.h @@ -278,6 +278,7 @@ static inline int rcu_preempt_depth(void) /* Internal to kernel */ void rcu_init(void); +void rcu_end_inkernel_boot(void); void rcu_sched_qs(void); void rcu_bh_qs(void); void rcu_check_callbacks(int user); diff --git a/init/Kconfig b/init/Kconfig index f5dbc6d4261b..9a0592516f48 100644 --- a/init/Kconfig +++ b/init/Kconfig @@ -791,6 +791,19 @@ config RCU_NOCB_CPU_ALL endchoice +config RCU_EXPEDITE_BOOT + bool + default n + help + This option enables expedited grace periods at boot time, + as if rcu_expedite_gp() had been invoked early in boot. + The corresponding rcu_unexpedite_gp() is invoked from + rcu_end_inkernel_boot(), which is intended to be invoked + at the end of the kernel-only boot sequence, just before + init is exec'ed. + + Accept the default if unsure. + endmenu # "RCU Subsystem" config BUILD_BIN2C diff --git a/kernel/rcu/update.c b/kernel/rcu/update.c index 5f850823c187..7b12466f90bc 100644 --- a/kernel/rcu/update.c +++ b/kernel/rcu/update.c @@ -64,7 +64,8 @@ module_param(rcu_expedited, int, 0); #ifndef CONFIG_TINY_RCU -static atomic_t rcu_expedited_nesting; +static atomic_t rcu_expedited_nesting = + ATOMIC_INIT(IS_ENABLED(CONFIG_RCU_EXPEDITE_BOOT) ? 1 : 0); /* * Should normal grace-period primitives be expedited? Intended for @@ -109,6 +110,14 @@ EXPORT_SYMBOL_GPL(rcu_unexpedite_gp); #endif /* #ifndef CONFIG_TINY_RCU */ +/* + * Inform RCU of the end of the in-kernel boot sequence. + */ +void rcu_end_inkernel_boot(void) +{ + if (IS_ENABLED(CONFIG_RCU_EXPEDITE_BOOT)) + rcu_unexpedite_gp(); +} #ifdef CONFIG_PREEMPT_RCU -- cgit v1.2.3 From 1b84f2a4cd4a6f517a313261f6f7c8caae5696c6 Mon Sep 17 00:00:00 2001 From: Javier Martinez Canillas Date: Mon, 2 Feb 2015 12:26:22 +0100 Subject: mfd: cros_ec: Use fixed size arrays to transfer data with the EC The struct cros_ec_command will be used as an ioctl() argument for the API to control the ChromeOS EC from user-space. So the data structure has to be 64-bit safe to make it compatible between 32 and 64 avoiding the need for a compat ioctl interface. Since pointers are self-aligned to different byte boundaries, use fixed size arrays instead of pointers for transferring ingoing and outgoing data with the Embedded Controller. Also, re-arrange struct members by decreasing alignment requirements to reduce the needing padding size. Signed-off-by: Javier Martinez Canillas Acked-by: Lee Jones Tested-by: Gwendal Grignou Reviewed-by: Gwendal Grignou Signed-off-by: Olof Johansson --- drivers/i2c/busses/i2c-cros-ec-tunnel.c | 51 +++++++-------------------------- drivers/input/keyboard/cros_ec_keyb.c | 13 +++++---- drivers/mfd/cros_ec.c | 15 +++++----- include/linux/mfd/cros_ec.h | 8 +++--- 4 files changed, 29 insertions(+), 58 deletions(-) (limited to 'include') diff --git a/drivers/i2c/busses/i2c-cros-ec-tunnel.c b/drivers/i2c/busses/i2c-cros-ec-tunnel.c index 875c22ae5400..fa8dedd8c3a2 100644 --- a/drivers/i2c/busses/i2c-cros-ec-tunnel.c +++ b/drivers/i2c/busses/i2c-cros-ec-tunnel.c @@ -182,72 +182,41 @@ static int ec_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg i2c_msgs[], const u16 bus_num = bus->remote_bus; int request_len; int response_len; - u8 *request = NULL; - u8 *response = NULL; int result; - struct cros_ec_command msg; + struct cros_ec_command msg = { }; request_len = ec_i2c_count_message(i2c_msgs, num); if (request_len < 0) { dev_warn(dev, "Error constructing message %d\n", request_len); - result = request_len; - goto exit; + return request_len; } + response_len = ec_i2c_count_response(i2c_msgs, num); if (response_len < 0) { /* Unexpected; no errors should come when NULL response */ dev_warn(dev, "Error preparing response %d\n", response_len); - result = response_len; - goto exit; - } - - if (request_len <= ARRAY_SIZE(bus->request_buf)) { - request = bus->request_buf; - } else { - request = kzalloc(request_len, GFP_KERNEL); - if (request == NULL) { - result = -ENOMEM; - goto exit; - } - } - if (response_len <= ARRAY_SIZE(bus->response_buf)) { - response = bus->response_buf; - } else { - response = kzalloc(response_len, GFP_KERNEL); - if (response == NULL) { - result = -ENOMEM; - goto exit; - } + return response_len; } - result = ec_i2c_construct_message(request, i2c_msgs, num, bus_num); + result = ec_i2c_construct_message(msg.outdata, i2c_msgs, num, bus_num); if (result) - goto exit; + return result; msg.version = 0; msg.command = EC_CMD_I2C_PASSTHRU; - msg.outdata = request; msg.outsize = request_len; - msg.indata = response; msg.insize = response_len; result = cros_ec_cmd_xfer(bus->ec, &msg); if (result < 0) - goto exit; + return result; - result = ec_i2c_parse_response(response, i2c_msgs, &num); + result = ec_i2c_parse_response(msg.indata, i2c_msgs, &num); if (result < 0) - goto exit; + return result; /* Indicate success by saying how many messages were sent */ - result = num; -exit: - if (request != bus->request_buf) - kfree(request); - if (response != bus->response_buf) - kfree(response); - - return result; + return num; } static u32 ec_i2c_functionality(struct i2c_adapter *adap) diff --git a/drivers/input/keyboard/cros_ec_keyb.c b/drivers/input/keyboard/cros_ec_keyb.c index ffa989f2c785..769f8f7f62b7 100644 --- a/drivers/input/keyboard/cros_ec_keyb.c +++ b/drivers/input/keyboard/cros_ec_keyb.c @@ -148,16 +148,19 @@ static void cros_ec_keyb_process(struct cros_ec_keyb *ckdev, static int cros_ec_keyb_get_state(struct cros_ec_keyb *ckdev, uint8_t *kb_state) { + int ret; struct cros_ec_command msg = { - .version = 0, .command = EC_CMD_MKBP_STATE, - .outdata = NULL, - .outsize = 0, - .indata = kb_state, .insize = ckdev->cols, }; - return cros_ec_cmd_xfer(ckdev->ec, &msg); + ret = cros_ec_cmd_xfer(ckdev->ec, &msg); + if (ret < 0) + return ret; + + memcpy(kb_state, msg.indata, ckdev->cols); + + return 0; } static irqreturn_t cros_ec_keyb_irq(int irq, void *data) diff --git a/drivers/mfd/cros_ec.c b/drivers/mfd/cros_ec.c index fc0c81ef04ff..c872e1b0eaf8 100644 --- a/drivers/mfd/cros_ec.c +++ b/drivers/mfd/cros_ec.c @@ -74,15 +74,11 @@ int cros_ec_cmd_xfer(struct cros_ec_device *ec_dev, ret = ec_dev->cmd_xfer(ec_dev, msg); if (msg->result == EC_RES_IN_PROGRESS) { int i; - struct cros_ec_command status_msg; - struct ec_response_get_comms_status status; + struct cros_ec_command status_msg = { }; + struct ec_response_get_comms_status *status; - status_msg.version = 0; status_msg.command = EC_CMD_GET_COMMS_STATUS; - status_msg.outdata = NULL; - status_msg.outsize = 0; - status_msg.indata = (uint8_t *)&status; - status_msg.insize = sizeof(status); + status_msg.insize = sizeof(*status); /* * Query the EC's status until it's no longer busy or @@ -98,7 +94,10 @@ int cros_ec_cmd_xfer(struct cros_ec_device *ec_dev, msg->result = status_msg.result; if (status_msg.result != EC_RES_SUCCESS) break; - if (!(status.flags & EC_COMMS_STATUS_PROCESSING)) + + status = (struct ec_response_get_comms_status *) + status_msg.indata; + if (!(status->flags & EC_COMMS_STATUS_PROCESSING)) break; } } diff --git a/include/linux/mfd/cros_ec.h b/include/linux/mfd/cros_ec.h index 0e166b92f5b4..71675b14b5ca 100644 --- a/include/linux/mfd/cros_ec.h +++ b/include/linux/mfd/cros_ec.h @@ -38,20 +38,20 @@ enum { /* * @version: Command version number (often 0) * @command: Command to send (EC_CMD_...) - * @outdata: Outgoing data to EC * @outsize: Outgoing length in bytes - * @indata: Where to put the incoming data from EC * @insize: Max number of bytes to accept from EC * @result: EC's response to the command (separate from communication failure) + * @outdata: Outgoing data to EC + * @indata: Where to put the incoming data from EC */ struct cros_ec_command { uint32_t version; uint32_t command; - uint8_t *outdata; uint32_t outsize; - uint8_t *indata; uint32_t insize; uint32_t result; + uint8_t outdata[EC_PROTO2_MAX_PARAM_SIZE]; + uint8_t indata[EC_PROTO2_MAX_PARAM_SIZE]; }; /** -- cgit v1.2.3 From 05c11ac4e0712def44cccbff82ad980d9e1d1b80 Mon Sep 17 00:00:00 2001 From: Javier Martinez Canillas Date: Mon, 2 Feb 2015 12:26:23 +0100 Subject: mfd: cros_ec: Add char dev and virtual dev pointers The ChromeOS Embedded Controller has to be accessed by applications. A virtual character device is used as an interface with user-space. Extend the struct cros_ec_device with the fields needed by the driver of this virtual character device. Signed-off-by: Javier Martinez Canillas Acked-by: Lee Jones Tested-by: Gwendal Grignou Reviewed-by: Gwendal Grignou Signed-off-by: Olof Johansson --- include/linux/mfd/cros_ec.h | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/mfd/cros_ec.h b/include/linux/mfd/cros_ec.h index 71675b14b5ca..324a34683971 100644 --- a/include/linux/mfd/cros_ec.h +++ b/include/linux/mfd/cros_ec.h @@ -16,6 +16,7 @@ #ifndef __LINUX_MFD_CROS_EC_H #define __LINUX_MFD_CROS_EC_H +#include #include #include #include @@ -59,9 +60,17 @@ struct cros_ec_command { * * @ec_name: name of EC device (e.g. 'chromeos-ec') * @phys_name: name of physical comms layer (e.g. 'i2c-4') - * @dev: Device pointer + * @dev: Device pointer for physical comms device + * @vdev: Device pointer for virtual comms device + * @cdev: Character device structure for virtual comms device * @was_wake_device: true if this device was set to wake the system from * sleep at the last suspend + * @cmd_readmem: direct read of the EC memory-mapped region, if supported + * @offset is within EC_LPC_ADDR_MEMMAP region. + * @bytes: number of bytes to read. zero means "read a string" (including + * the trailing '\0'). At most only EC_MEMMAP_SIZE bytes can be read. + * Caller must ensure that the buffer is large enough for the result when + * reading a string. * * @priv: Private data * @irq: Interrupt to use @@ -90,8 +99,12 @@ struct cros_ec_device { const char *ec_name; const char *phys_name; struct device *dev; + struct device *vdev; + struct cdev cdev; bool was_wake_device; struct class *cros_class; + int (*cmd_readmem)(struct cros_ec_device *ec, unsigned int offset, + unsigned int bytes, void *dest); /* These are used to implement the platform-specific interface */ void *priv; -- cgit v1.2.3 From 6232c51cb370919b116e0aea38d12aa33aae2fa9 Mon Sep 17 00:00:00 2001 From: Ulrich Hecht Date: Thu, 26 Feb 2015 17:42:07 +0100 Subject: ARM: shmobile: r8a7778: common clock framework CPG driver Driver for the r8a7778's clocks that depend on the mode bits. Signed-off-by: Ulrich Hecht Acked-by: Laurent Pinchart Acked-by: Michael Turquette Signed-off-by: Simon Horman --- .../bindings/clock/renesas,r8a7778-cpg-clocks.txt | 25 ++++ drivers/clk/shmobile/Makefile | 1 + drivers/clk/shmobile/clk-r8a7778.c | 143 +++++++++++++++++++++ include/linux/clk/shmobile.h | 1 + 4 files changed, 170 insertions(+) create mode 100644 Documentation/devicetree/bindings/clock/renesas,r8a7778-cpg-clocks.txt create mode 100644 drivers/clk/shmobile/clk-r8a7778.c (limited to 'include') diff --git a/Documentation/devicetree/bindings/clock/renesas,r8a7778-cpg-clocks.txt b/Documentation/devicetree/bindings/clock/renesas,r8a7778-cpg-clocks.txt new file mode 100644 index 000000000000..2f3747fdcf1c --- /dev/null +++ b/Documentation/devicetree/bindings/clock/renesas,r8a7778-cpg-clocks.txt @@ -0,0 +1,25 @@ +* Renesas R8A7778 Clock Pulse Generator (CPG) + +The CPG generates core clocks for the R8A7778. It includes two PLLs and +several fixed ratio dividers + +Required Properties: + + - compatible: Must be "renesas,r8a7778-cpg-clocks" + - reg: Base address and length of the memory resource used by the CPG + - #clock-cells: Must be 1 + - clock-output-names: The names of the clocks. Supported clocks are + "plla", "pllb", "b", "out", "p", "s", and "s1". + + +Example +------- + + cpg_clocks: cpg_clocks@ffc80000 { + compatible = "renesas,r8a7778-cpg-clocks"; + reg = <0xffc80000 0x80>; + #clock-cells = <1>; + clocks = <&extal_clk>; + clock-output-names = "plla", "pllb", "b", + "out", "p", "s", "s1"; + }; diff --git a/drivers/clk/shmobile/Makefile b/drivers/clk/shmobile/Makefile index 0689d7fb2666..97c71c885e4f 100644 --- a/drivers/clk/shmobile/Makefile +++ b/drivers/clk/shmobile/Makefile @@ -2,6 +2,7 @@ obj-$(CONFIG_ARCH_EMEV2) += clk-emev2.o obj-$(CONFIG_ARCH_R7S72100) += clk-rz.o obj-$(CONFIG_ARCH_R8A73A4) += clk-r8a73a4.o obj-$(CONFIG_ARCH_R8A7740) += clk-r8a7740.o +obj-$(CONFIG_ARCH_R8A7778) += clk-r8a7778.o obj-$(CONFIG_ARCH_R8A7779) += clk-r8a7779.o obj-$(CONFIG_ARCH_R8A7790) += clk-rcar-gen2.o obj-$(CONFIG_ARCH_R8A7791) += clk-rcar-gen2.o diff --git a/drivers/clk/shmobile/clk-r8a7778.c b/drivers/clk/shmobile/clk-r8a7778.c new file mode 100644 index 000000000000..cb33b57274bf --- /dev/null +++ b/drivers/clk/shmobile/clk-r8a7778.c @@ -0,0 +1,143 @@ +/* + * r8a7778 Core CPG Clocks + * + * Copyright (C) 2014 Ulrich Hecht + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + */ + +#include +#include +#include +#include + +struct r8a7778_cpg { + struct clk_onecell_data data; + spinlock_t lock; + void __iomem *reg; +}; + +/* PLL multipliers per bits 11, 12, and 18 of MODEMR */ +struct { + unsigned long plla_mult; + unsigned long pllb_mult; +} r8a7778_rates[] __initdata = { + [0] = { 21, 21 }, + [1] = { 24, 24 }, + [2] = { 28, 28 }, + [3] = { 32, 32 }, + [5] = { 24, 21 }, + [6] = { 28, 21 }, + [7] = { 32, 24 }, +}; + +/* Clock dividers per bits 1 and 2 of MODEMR */ +struct { + const char *name; + unsigned int div[4]; +} r8a7778_divs[6] __initdata = { + { "b", { 12, 12, 16, 18 } }, + { "out", { 12, 12, 16, 18 } }, + { "p", { 16, 12, 16, 12 } }, + { "s", { 4, 3, 4, 3 } }, + { "s1", { 8, 6, 8, 6 } }, +}; + +static u32 cpg_mode_rates __initdata; +static u32 cpg_mode_divs __initdata; + +static struct clk * __init +r8a7778_cpg_register_clock(struct device_node *np, struct r8a7778_cpg *cpg, + const char *name) +{ + if (!strcmp(name, "plla")) { + return clk_register_fixed_factor(NULL, "plla", + of_clk_get_parent_name(np, 0), 0, + r8a7778_rates[cpg_mode_rates].plla_mult, 1); + } else if (!strcmp(name, "pllb")) { + return clk_register_fixed_factor(NULL, "pllb", + of_clk_get_parent_name(np, 0), 0, + r8a7778_rates[cpg_mode_rates].pllb_mult, 1); + } else { + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(r8a7778_divs); i++) { + if (!strcmp(name, r8a7778_divs[i].name)) { + return clk_register_fixed_factor(NULL, + r8a7778_divs[i].name, + "plla", 0, 1, + r8a7778_divs[i].div[cpg_mode_divs]); + } + } + } + + return ERR_PTR(-EINVAL); +} + + +static void __init r8a7778_cpg_clocks_init(struct device_node *np) +{ + struct r8a7778_cpg *cpg; + struct clk **clks; + unsigned int i; + int num_clks; + + num_clks = of_property_count_strings(np, "clock-output-names"); + if (num_clks < 0) { + pr_err("%s: failed to count clocks\n", __func__); + return; + } + + cpg = kzalloc(sizeof(*cpg), GFP_KERNEL); + clks = kcalloc(num_clks, sizeof(*clks), GFP_KERNEL); + if (cpg == NULL || clks == NULL) { + /* We're leaking memory on purpose, there's no point in cleaning + * up as the system won't boot anyway. + */ + return; + } + + spin_lock_init(&cpg->lock); + + cpg->data.clks = clks; + cpg->data.clk_num = num_clks; + + cpg->reg = of_iomap(np, 0); + if (WARN_ON(cpg->reg == NULL)) + return; + + for (i = 0; i < num_clks; ++i) { + const char *name; + struct clk *clk; + + of_property_read_string_index(np, "clock-output-names", i, + &name); + + clk = r8a7778_cpg_register_clock(np, cpg, name); + if (IS_ERR(clk)) + pr_err("%s: failed to register %s %s clock (%ld)\n", + __func__, np->name, name, PTR_ERR(clk)); + else + cpg->data.clks[i] = clk; + } + + of_clk_add_provider(np, of_clk_src_onecell_get, &cpg->data); +} + +CLK_OF_DECLARE(r8a7778_cpg_clks, "renesas,r8a7778-cpg-clocks", + r8a7778_cpg_clocks_init); + +void __init r8a7778_clocks_init(u32 mode) +{ + BUG_ON(!(mode & BIT(19))); + + cpg_mode_rates = (!!(mode & BIT(18)) << 2) | + (!!(mode & BIT(12)) << 1) | + (!!(mode & BIT(11))); + cpg_mode_divs = (!!(mode & BIT(2)) << 1) | + (!!(mode & BIT(1))); + + of_clk_init(NULL); +} diff --git a/include/linux/clk/shmobile.h b/include/linux/clk/shmobile.h index 9f8a14041dd5..63a8159c4e64 100644 --- a/include/linux/clk/shmobile.h +++ b/include/linux/clk/shmobile.h @@ -16,6 +16,7 @@ #include +void r8a7778_clocks_init(u32 mode); void r8a7779_clocks_init(u32 mode); void rcar_gen2_clocks_init(u32 mode); -- cgit v1.2.3 From b5ff6e1637b683d5996ae11ac29afe406c0bee90 Mon Sep 17 00:00:00 2001 From: Tvrtko Ursulin Date: Fri, 27 Feb 2015 11:15:17 +0000 Subject: drm/i915/skl: Add new displayable tiling formats Starting with SKL display engine can scan out Y, and newly introduced Yf tiling formats so add the latter to the frame buffer modifier space. v2: Definitions moved to drm_fourcc.h. v3: Try to document the format better. Signed-off-by: Tvrtko Ursulin Reviewed-by: Damien Lespiau Signed-off-by: Daniel Vetter --- include/uapi/drm/drm_fourcc.h | 15 +++++++++++++++ 1 file changed, 15 insertions(+) (limited to 'include') diff --git a/include/uapi/drm/drm_fourcc.h b/include/uapi/drm/drm_fourcc.h index 4837c3d2319a..b35418b20dcf 100644 --- a/include/uapi/drm/drm_fourcc.h +++ b/include/uapi/drm/drm_fourcc.h @@ -195,4 +195,19 @@ */ #define I915_FORMAT_MOD_Y_TILED fourcc_mod_code(INTEL, 2) +/* + * Intel Yf-tiling layout + * + * This is a tiled layout using 4Kb tiles in row-major layout. + * Within the tile pixels are laid out in 16 256 byte units / sub-tiles which + * are arranged in four groups (two wide, two high) with column-major layout. + * Each group therefore consits out of four 256 byte units, which are also laid + * out as 2x2 column-major. + * 256 byte units are made out of four 64 byte blocks of pixels, producing + * either a square block or a 2:1 unit. + * 64 byte blocks of pixels contain four pixel rows of 16 bytes, where the width + * in pixel depends on the pixel depth. + */ +#define I915_FORMAT_MOD_Yf_TILED fourcc_mod_code(INTEL, 3) + #endif /* DRM_FOURCC_H */ -- cgit v1.2.3 From 4cd3928a8bee83d86fb3865bb243ab2ff1dd0eb6 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Fri, 27 Feb 2015 10:11:13 +0200 Subject: Bluetooth: Update New CSRK event to match latest specification The 'master' parameter of the New CSRK event was recently renamed to 'type', with the old values kept for backwards compatibility as unauthenticated local/remote keys. This patch updates the code to take into account the two new (authenticated) values and ensures they get used based on the security level of the connection that the respective keys get distributed over. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- include/net/bluetooth/hci_core.h | 2 +- include/net/bluetooth/mgmt.h | 7 ++++++- net/bluetooth/mgmt.c | 2 +- net/bluetooth/smp.c | 10 ++++++++-- 4 files changed, 16 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index a831c8ad10f1..acec9140c3f9 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -108,7 +108,7 @@ struct bt_uuid { struct smp_csrk { bdaddr_t bdaddr; u8 bdaddr_type; - u8 master; + u8 type; u8 val[16]; }; diff --git a/include/net/bluetooth/mgmt.h b/include/net/bluetooth/mgmt.h index e218a30f2061..fe8eef00e9ca 100644 --- a/include/net/bluetooth/mgmt.h +++ b/include/net/bluetooth/mgmt.h @@ -647,9 +647,14 @@ struct mgmt_ev_new_irk { struct mgmt_irk_info irk; } __packed; +#define MGMT_CSRK_LOCAL_UNAUTHENTICATED 0x00 +#define MGMT_CSRK_REMOTE_UNAUTHENTICATED 0x01 +#define MGMT_CSRK_LOCAL_AUTHENTICATED 0x02 +#define MGMT_CSRK_REMOTE_AUTHENTICATED 0x03 + struct mgmt_csrk_info { struct mgmt_addr_info addr; - __u8 master; + __u8 type; __u8 val[16]; } __packed; diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index d5d46e7676f1..1e4635a3374d 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -6664,7 +6664,7 @@ void mgmt_new_csrk(struct hci_dev *hdev, struct smp_csrk *csrk, bacpy(&ev.key.addr.bdaddr, &csrk->bdaddr); ev.key.addr.type = link_to_bdaddr(LE_LINK, csrk->bdaddr_type); - ev.key.master = csrk->master; + ev.key.type = csrk->type; memcpy(ev.key.val, csrk->val, sizeof(csrk->val)); mgmt_event(MGMT_EV_NEW_CSRK, hdev, &ev, sizeof(ev), NULL); diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c index b2803bd6e0d8..c91c19bfc0a8 100644 --- a/net/bluetooth/smp.c +++ b/net/bluetooth/smp.c @@ -1252,7 +1252,10 @@ static void smp_distribute_keys(struct smp_chan *smp) csrk = kzalloc(sizeof(*csrk), GFP_KERNEL); if (csrk) { - csrk->master = 0x00; + if (hcon->sec_level > BT_SECURITY_MEDIUM) + csrk->type = MGMT_CSRK_LOCAL_AUTHENTICATED; + else + csrk->type = MGMT_CSRK_LOCAL_UNAUTHENTICATED; memcpy(csrk->val, sign.csrk, sizeof(csrk->val)); } smp->slave_csrk = csrk; @@ -2352,7 +2355,10 @@ static int smp_cmd_sign_info(struct l2cap_conn *conn, struct sk_buff *skb) csrk = kzalloc(sizeof(*csrk), GFP_KERNEL); if (csrk) { - csrk->master = 0x01; + if (conn->hcon->sec_level > BT_SECURITY_MEDIUM) + csrk->type = MGMT_CSRK_REMOTE_AUTHENTICATED; + else + csrk->type = MGMT_CSRK_REMOTE_UNAUTHENTICATED; memcpy(csrk->val, rp->csrk, sizeof(csrk->val)); } smp->csrk = csrk; -- cgit v1.2.3 From ccdaeb2b176f7db491a6f8e8b1c51f9393525f7d Mon Sep 17 00:00:00 2001 From: Alexander Aring Date: Fri, 27 Feb 2015 09:58:26 +0100 Subject: at86rf230: add support for external xtal trim This patch adds support for setting the xtal trim register. Some at86rf2xx transceiver boards needs fine tuning the xtal capacitor. Signed-off-by: Alexander Aring Signed-off-by: Marcel Holtmann --- .../bindings/net/ieee802154/at86rf230.txt | 3 ++ drivers/net/ieee802154/at86rf230.c | 54 ++++++++++++++++++++-- include/linux/spi/at86rf230.h | 1 + 3 files changed, 54 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/Documentation/devicetree/bindings/net/ieee802154/at86rf230.txt b/Documentation/devicetree/bindings/net/ieee802154/at86rf230.txt index d3bbdded4cbe..1ae5100fea14 100644 --- a/Documentation/devicetree/bindings/net/ieee802154/at86rf230.txt +++ b/Documentation/devicetree/bindings/net/ieee802154/at86rf230.txt @@ -11,6 +11,8 @@ Required properties: Optional properties: - reset-gpio: GPIO spec for the rstn pin - sleep-gpio: GPIO spec for the slp_tr pin + - xtal-trim: u8 value for fine tuning the internal capacitance + arrays of xtal pins: 0 = +0 pF, 0xf = +4.5 pF Example: @@ -20,4 +22,5 @@ Example: reg = <0>; interrupts = <19 1>; interrupt-parent = <&gpio3>; + xtal-trim = /bits/ 8 <0x06>; }; diff --git a/drivers/net/ieee802154/at86rf230.c b/drivers/net/ieee802154/at86rf230.c index 9888b7ff24bc..c1323e5cdd0c 100644 --- a/drivers/net/ieee802154/at86rf230.c +++ b/drivers/net/ieee802154/at86rf230.c @@ -1315,7 +1315,7 @@ static struct at86rf2xx_chip_data at86rf212_data = { .get_desense_steps = at86rf212_get_desens_steps }; -static int at86rf230_hw_init(struct at86rf230_local *lp) +static int at86rf230_hw_init(struct at86rf230_local *lp, u8 xtal_trim) { int rc, irq_type, irq_pol = IRQ_ACTIVE_HIGH; unsigned int dvdd; @@ -1362,6 +1362,45 @@ static int at86rf230_hw_init(struct at86rf230_local *lp) usleep_range(lp->data->t_sleep_cycle, lp->data->t_sleep_cycle + 100); + /* xtal_trim value is calculated by: + * CL = 0.5 * (CX + CTRIM + CPAR) + * + * whereas: + * CL = capacitor of used crystal + * CX = connected capacitors at xtal pins + * CPAR = in all at86rf2xx datasheets this is a constant value 3 pF, + * but this is different on each board setup. You need to fine + * tuning this value via CTRIM. + * CTRIM = variable capacitor setting. Resolution is 0.3 pF range is + * 0 pF upto 4.5 pF. + * + * Examples: + * atben transceiver: + * + * CL = 8 pF + * CX = 12 pF + * CPAR = 3 pF (We assume the magic constant from datasheet) + * CTRIM = 0.9 pF + * + * (12+0.9+3)/2 = 7.95 which is nearly at 8 pF + * + * xtal_trim = 0x3 + * + * openlabs transceiver: + * + * CL = 16 pF + * CX = 22 pF + * CPAR = 3 pF (We assume the magic constant from datasheet) + * CTRIM = 4.5 pF + * + * (22+4.5+3)/2 = 14.75 which is the nearest value to 16 pF + * + * xtal_trim = 0xf + */ + rc = at86rf230_write_subreg(lp, SR_XTAL_TRIM, xtal_trim); + if (rc) + return rc; + rc = at86rf230_read_subreg(lp, SR_DVDD_OK, &dvdd); if (rc) return rc; @@ -1378,9 +1417,11 @@ static int at86rf230_hw_init(struct at86rf230_local *lp) } static int -at86rf230_get_pdata(struct spi_device *spi, int *rstn, int *slp_tr) +at86rf230_get_pdata(struct spi_device *spi, int *rstn, int *slp_tr, + u8 *xtal_trim) { struct at86rf230_platform_data *pdata = spi->dev.platform_data; + int ret; if (!IS_ENABLED(CONFIG_OF) || !spi->dev.of_node) { if (!pdata) @@ -1388,11 +1429,15 @@ at86rf230_get_pdata(struct spi_device *spi, int *rstn, int *slp_tr) *rstn = pdata->rstn; *slp_tr = pdata->slp_tr; + *xtal_trim = pdata->xtal_trim; return 0; } *rstn = of_get_named_gpio(spi->dev.of_node, "reset-gpio", 0); *slp_tr = of_get_named_gpio(spi->dev.of_node, "sleep-gpio", 0); + ret = of_property_read_u8(spi->dev.of_node, "xtal-trim", xtal_trim); + if (ret < 0 && ret != -EINVAL) + return ret; return 0; } @@ -1505,13 +1550,14 @@ static int at86rf230_probe(struct spi_device *spi) struct at86rf230_local *lp; unsigned int status; int rc, irq_type, rstn, slp_tr; + u8 xtal_trim; if (!spi->irq) { dev_err(&spi->dev, "no IRQ specified\n"); return -EINVAL; } - rc = at86rf230_get_pdata(spi, &rstn, &slp_tr); + rc = at86rf230_get_pdata(spi, &rstn, &slp_tr, &xtal_trim); if (rc < 0) { dev_err(&spi->dev, "failed to parse platform_data: %d\n", rc); return rc; @@ -1570,7 +1616,7 @@ static int at86rf230_probe(struct spi_device *spi) spi_set_drvdata(spi, lp); - rc = at86rf230_hw_init(lp); + rc = at86rf230_hw_init(lp, xtal_trim); if (rc) goto free_dev; diff --git a/include/linux/spi/at86rf230.h b/include/linux/spi/at86rf230.h index cd519a11c2c6..b63fe6f5fdc8 100644 --- a/include/linux/spi/at86rf230.h +++ b/include/linux/spi/at86rf230.h @@ -22,6 +22,7 @@ struct at86rf230_platform_data { int rstn; int slp_tr; int dig2; + u8 xtal_trim; }; #endif -- cgit v1.2.3 From 723b8e460d87e957f251dc5764f4ab86af6ab44e Mon Sep 17 00:00:00 2001 From: Tom Herbert Date: Tue, 24 Feb 2015 09:17:31 -0800 Subject: udp: In udp_flow_src_port use random hash value if skb_get_hash fails In the unlikely event that skb_get_hash is unable to deduce a hash in udp_flow_src_port we use a consistent random value instead. This is specified in GRE/UDP draft section 3.2.1: https://tools.ietf.org/html/draft-ietf-tsvwg-gre-in-udp-encap-04 Signed-off-by: Tom Herbert Signed-off-by: David S. Miller --- include/net/udp.h | 21 +++++++++++++++------ net/ipv4/udp.c | 10 ++++++++++ 2 files changed, 25 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/include/net/udp.h b/include/net/udp.h index 07f9b70962f6..32d8d9f07f76 100644 --- a/include/net/udp.h +++ b/include/net/udp.h @@ -194,6 +194,8 @@ int udp_lib_get_port(struct sock *sk, unsigned short snum, int (*)(const struct sock *, const struct sock *), unsigned int hash2_nulladdr); +u32 udp_flow_hashrnd(void); + static inline __be16 udp_flow_src_port(struct net *net, struct sk_buff *skb, int min, int max, bool use_eth) { @@ -205,12 +207,19 @@ static inline __be16 udp_flow_src_port(struct net *net, struct sk_buff *skb, } hash = skb_get_hash(skb); - if (unlikely(!hash) && use_eth) { - /* Can't find a normal hash, caller has indicated an Ethernet - * packet so use that to compute a hash. - */ - hash = jhash(skb->data, 2 * ETH_ALEN, - (__force u32) skb->protocol); + if (unlikely(!hash)) { + if (use_eth) { + /* Can't find a normal hash, caller has indicated an + * Ethernet packet so use that to compute a hash. + */ + hash = jhash(skb->data, 2 * ETH_ALEN, + (__force u32) skb->protocol); + } else { + /* Can't derive any sort of hash for the packet, set + * to some consistent random value. + */ + hash = udp_flow_hashrnd(); + } } /* Since this is being sent on the wire obfuscate hash a bit diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c index 97ef1f8b7be8..0224f930c613 100644 --- a/net/ipv4/udp.c +++ b/net/ipv4/udp.c @@ -2525,6 +2525,16 @@ void __init udp_table_init(struct udp_table *table, const char *name) } } +u32 udp_flow_hashrnd(void) +{ + static u32 hashrnd __read_mostly; + + net_get_random_once(&hashrnd, sizeof(hashrnd)); + + return hashrnd; +} +EXPORT_SYMBOL(udp_flow_hashrnd); + void __init udp_init(void) { unsigned long limit; -- cgit v1.2.3 From 46a4dee074b58c4256dbf6c2dbf199c372f85b04 Mon Sep 17 00:00:00 2001 From: Madhu Challa Date: Wed, 25 Feb 2015 09:58:34 -0800 Subject: igmp v6: add __ipv6_sock_mc_join and __ipv6_sock_mc_drop Based on the igmp v4 changes from Eric Dumazet. 959d10f6bbf6("igmp: add __ip_mc_{join|leave}_group()") These changes are needed to perform igmp v6 join/leave while RTNL is held. Make ipv6_sock_mc_join and ipv6_sock_mc_drop wrappers around __ipv6_sock_mc_join and __ipv6_sock_mc_drop to avoid proliferation of work queues. Signed-off-by: Madhu Challa Acked-by: Eric Dumazet Signed-off-by: David S. Miller --- include/net/ipv6.h | 8 ++++++++ net/ipv6/mcast.c | 40 +++++++++++++++++++++++++++++++--------- 2 files changed, 39 insertions(+), 9 deletions(-) (limited to 'include') diff --git a/include/net/ipv6.h b/include/net/ipv6.h index 4c9fe224d73b..b7673065c074 100644 --- a/include/net/ipv6.h +++ b/include/net/ipv6.h @@ -940,4 +940,12 @@ int ipv6_sysctl_register(void); void ipv6_sysctl_unregister(void); #endif +int ipv6_sock_mc_join(struct sock *sk, int ifindex, + const struct in6_addr *addr); +int __ipv6_sock_mc_join(struct sock *sk, int ifindex, + const struct in6_addr *addr); +int ipv6_sock_mc_drop(struct sock *sk, int ifindex, + const struct in6_addr *addr); +int __ipv6_sock_mc_drop(struct sock *sk, int ifindex, + const struct in6_addr *addr); #endif /* _NET_IPV6_H */ diff --git a/net/ipv6/mcast.c b/net/ipv6/mcast.c index 5ce107c8aab3..e4955d019734 100644 --- a/net/ipv6/mcast.c +++ b/net/ipv6/mcast.c @@ -132,7 +132,7 @@ static int unsolicited_report_interval(struct inet6_dev *idev) return iv > 0 ? iv : 1; } -int ipv6_sock_mc_join(struct sock *sk, int ifindex, const struct in6_addr *addr) +int __ipv6_sock_mc_join(struct sock *sk, int ifindex, const struct in6_addr *addr) { struct net_device *dev = NULL; struct ipv6_mc_socklist *mc_lst; @@ -140,6 +140,8 @@ int ipv6_sock_mc_join(struct sock *sk, int ifindex, const struct in6_addr *addr) struct net *net = sock_net(sk); int err; + ASSERT_RTNL(); + if (!ipv6_addr_is_multicast(addr)) return -EINVAL; @@ -161,7 +163,6 @@ int ipv6_sock_mc_join(struct sock *sk, int ifindex, const struct in6_addr *addr) mc_lst->next = NULL; mc_lst->addr = *addr; - rtnl_lock(); if (ifindex == 0) { struct rt6_info *rt; rt = rt6_lookup(net, addr, NULL, 0, 0); @@ -173,7 +174,6 @@ int ipv6_sock_mc_join(struct sock *sk, int ifindex, const struct in6_addr *addr) dev = __dev_get_by_index(net, ifindex); if (dev == NULL) { - rtnl_unlock(); sock_kfree_s(sk, mc_lst, sizeof(*mc_lst)); return -ENODEV; } @@ -190,7 +190,6 @@ int ipv6_sock_mc_join(struct sock *sk, int ifindex, const struct in6_addr *addr) err = ipv6_dev_mc_inc(dev, addr); if (err) { - rtnl_unlock(); sock_kfree_s(sk, mc_lst, sizeof(*mc_lst)); return err; } @@ -198,25 +197,37 @@ int ipv6_sock_mc_join(struct sock *sk, int ifindex, const struct in6_addr *addr) mc_lst->next = np->ipv6_mc_list; rcu_assign_pointer(np->ipv6_mc_list, mc_lst); + return 0; +} +EXPORT_SYMBOL(__ipv6_sock_mc_join); + +int ipv6_sock_mc_join(struct sock *sk, int ifindex, const struct in6_addr *addr) +{ + int ret; + + rtnl_lock(); + ret = __ipv6_sock_mc_join(sk, ifindex, addr); rtnl_unlock(); - return 0; + return ret; } +EXPORT_SYMBOL(ipv6_sock_mc_join); /* * socket leave on multicast group */ -int ipv6_sock_mc_drop(struct sock *sk, int ifindex, const struct in6_addr *addr) +int __ipv6_sock_mc_drop(struct sock *sk, int ifindex, const struct in6_addr *addr) { struct ipv6_pinfo *np = inet6_sk(sk); struct ipv6_mc_socklist *mc_lst; struct ipv6_mc_socklist __rcu **lnk; struct net *net = sock_net(sk); + ASSERT_RTNL(); + if (!ipv6_addr_is_multicast(addr)) return -EINVAL; - rtnl_lock(); for (lnk = &np->ipv6_mc_list; (mc_lst = rtnl_dereference(*lnk)) != NULL; lnk = &mc_lst->next) { @@ -235,17 +246,28 @@ int ipv6_sock_mc_drop(struct sock *sk, int ifindex, const struct in6_addr *addr) __ipv6_dev_mc_dec(idev, &mc_lst->addr); } else (void) ip6_mc_leave_src(sk, mc_lst, NULL); - rtnl_unlock(); atomic_sub(sizeof(*mc_lst), &sk->sk_omem_alloc); kfree_rcu(mc_lst, rcu); return 0; } } - rtnl_unlock(); return -EADDRNOTAVAIL; } +EXPORT_SYMBOL(__ipv6_sock_mc_drop); + +int ipv6_sock_mc_drop(struct sock *sk, int ifindex, const struct in6_addr *addr) +{ + int ret; + + rtnl_lock(); + ret = __ipv6_sock_mc_drop(sk, ifindex, addr); + rtnl_unlock(); + + return ret; +} +EXPORT_SYMBOL(ipv6_sock_mc_drop); /* called with rcu_read_lock() */ static struct inet6_dev *ip6_mc_find_dev_rcu(struct net *net, -- cgit v1.2.3 From 93a714d6b53d87872e552dbb273544bdeaaf6e12 Mon Sep 17 00:00:00 2001 From: Madhu Challa Date: Wed, 25 Feb 2015 09:58:35 -0800 Subject: multicast: Extend ip address command to enable multicast group join/leave on Joining multicast group on ethernet level via "ip maddr" command would not work if we have an Ethernet switch that does igmp snooping since the switch would not replicate multicast packets on ports that did not have IGMP reports for the multicast addresses. Linux vxlan interfaces created via "ip link add vxlan" have the group option that enables then to do the required join. By extending ip address command with option "autojoin" we can get similar functionality for openvswitch vxlan interfaces as well as other tunneling mechanisms that need to receive multicast traffic. The kernel code is structured similar to how the vxlan driver does a group join / leave. example: ip address add 224.1.1.10/24 dev eth5 autojoin ip address del 224.1.1.10/24 dev eth5 Signed-off-by: Madhu Challa Signed-off-by: David S. Miller --- include/net/netns/ipv4.h | 1 + include/net/netns/ipv6.h | 1 + include/uapi/linux/if_addr.h | 1 + net/ipv4/devinet.c | 31 +++++++++++++++++++++++++++++++ net/ipv4/igmp.c | 13 +++++++++++++ net/ipv6/addrconf.c | 38 +++++++++++++++++++++++++++++++++++--- net/ipv6/mcast.c | 20 ++++++++++++++++---- 7 files changed, 98 insertions(+), 7 deletions(-) (limited to 'include') diff --git a/include/net/netns/ipv4.h b/include/net/netns/ipv4.h index dbe225478adb..1b26c6c3fd7c 100644 --- a/include/net/netns/ipv4.h +++ b/include/net/netns/ipv4.h @@ -49,6 +49,7 @@ struct netns_ipv4 { struct sock *fibnl; struct sock * __percpu *icmp_sk; + struct sock *mc_autojoin_sk; struct inet_peer_base *peers; struct tcpm_hash_bucket *tcp_metrics_hash; diff --git a/include/net/netns/ipv6.h b/include/net/netns/ipv6.h index 69ae41f2098c..ca0db12cd089 100644 --- a/include/net/netns/ipv6.h +++ b/include/net/netns/ipv6.h @@ -67,6 +67,7 @@ struct netns_ipv6 { struct sock *ndisc_sk; struct sock *tcp_sk; struct sock *igmp_sk; + struct sock *mc_autojoin_sk; #ifdef CONFIG_IPV6_MROUTE #ifndef CONFIG_IPV6_MROUTE_MULTIPLE_TABLES struct mr6_table *mrt6; diff --git a/include/uapi/linux/if_addr.h b/include/uapi/linux/if_addr.h index dea10a87dfd1..40fdfea39714 100644 --- a/include/uapi/linux/if_addr.h +++ b/include/uapi/linux/if_addr.h @@ -50,6 +50,7 @@ enum { #define IFA_F_PERMANENT 0x80 #define IFA_F_MANAGETEMPADDR 0x100 #define IFA_F_NOPREFIXROUTE 0x200 +#define IFA_F_MCAUTOJOIN 0x400 struct ifa_cacheinfo { __u32 ifa_prefered; diff --git a/net/ipv4/devinet.c b/net/ipv4/devinet.c index 3a8985c94581..5105759e4e00 100644 --- a/net/ipv4/devinet.c +++ b/net/ipv4/devinet.c @@ -548,6 +548,26 @@ struct in_ifaddr *inet_ifa_byprefix(struct in_device *in_dev, __be32 prefix, return NULL; } +static int ip_mc_config(struct sock *sk, bool join, const struct in_ifaddr *ifa) +{ + struct ip_mreqn mreq = { + .imr_multiaddr.s_addr = ifa->ifa_address, + .imr_ifindex = ifa->ifa_dev->dev->ifindex, + }; + int ret; + + ASSERT_RTNL(); + + lock_sock(sk); + if (join) + ret = __ip_mc_join_group(sk, &mreq); + else + ret = __ip_mc_leave_group(sk, &mreq); + release_sock(sk); + + return ret; +} + static int inet_rtm_deladdr(struct sk_buff *skb, struct nlmsghdr *nlh) { struct net *net = sock_net(skb->sk); @@ -584,6 +604,8 @@ static int inet_rtm_deladdr(struct sk_buff *skb, struct nlmsghdr *nlh) !inet_ifa_match(nla_get_be32(tb[IFA_ADDRESS]), ifa))) continue; + if (ipv4_is_multicast(ifa->ifa_address)) + ip_mc_config(net->ipv4.mc_autojoin_sk, false, ifa); __inet_del_ifa(in_dev, ifap, 1, nlh, NETLINK_CB(skb).portid); return 0; } @@ -838,6 +860,15 @@ static int inet_rtm_newaddr(struct sk_buff *skb, struct nlmsghdr *nlh) * userspace already relies on not having to provide this. */ set_ifa_lifetime(ifa, valid_lft, prefered_lft); + if (ifa->ifa_flags & IFA_F_MCAUTOJOIN) { + int ret = ip_mc_config(net->ipv4.mc_autojoin_sk, + true, ifa); + + if (ret < 0) { + inet_free_ifa(ifa); + return ret; + } + } return __inet_insert_ifa(ifa, nlh, NETLINK_CB(skb).portid); } else { inet_free_ifa(ifa); diff --git a/net/ipv4/igmp.c b/net/ipv4/igmp.c index 4b1172d73e03..5cb1ef4ce292 100644 --- a/net/ipv4/igmp.c +++ b/net/ipv4/igmp.c @@ -97,6 +97,7 @@ #include #include #include +#include #include #ifdef CONFIG_IP_MROUTE #include @@ -2740,6 +2741,7 @@ static const struct file_operations igmp_mcf_seq_fops = { static int __net_init igmp_net_init(struct net *net) { struct proc_dir_entry *pde; + int err; pde = proc_create("igmp", S_IRUGO, net->proc_net, &igmp_mc_seq_fops); if (!pde) @@ -2748,8 +2750,18 @@ static int __net_init igmp_net_init(struct net *net) &igmp_mcf_seq_fops); if (!pde) goto out_mcfilter; + err = inet_ctl_sock_create(&net->ipv4.mc_autojoin_sk, AF_INET, + SOCK_DGRAM, 0, net); + if (err < 0) { + pr_err("Failed to initialize the IGMP autojoin socket (err %d)\n", + err); + goto out_sock; + } + return 0; +out_sock: + remove_proc_entry("mcfilter", net->proc_net); out_mcfilter: remove_proc_entry("igmp", net->proc_net); out_igmp: @@ -2760,6 +2772,7 @@ static void __net_exit igmp_net_exit(struct net *net) { remove_proc_entry("mcfilter", net->proc_net); remove_proc_entry("igmp", net->proc_net); + inet_ctl_sock_destroy(net->ipv4.mc_autojoin_sk); } static struct pernet_operations igmp_net_ops = { diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c index 98e4a63d72bb..783bccfcc060 100644 --- a/net/ipv6/addrconf.c +++ b/net/ipv6/addrconf.c @@ -2464,6 +2464,23 @@ err_exit: return err; } +static int ipv6_mc_config(struct sock *sk, bool join, + const struct in6_addr *addr, int ifindex) +{ + int ret; + + ASSERT_RTNL(); + + lock_sock(sk); + if (join) + ret = __ipv6_sock_mc_join(sk, ifindex, addr); + else + ret = __ipv6_sock_mc_drop(sk, ifindex, addr); + release_sock(sk); + + return ret; +} + /* * Manual configuration of address on an interface */ @@ -2476,10 +2493,10 @@ static int inet6_addr_add(struct net *net, int ifindex, struct inet6_ifaddr *ifp; struct inet6_dev *idev; struct net_device *dev; + unsigned long timeout; + clock_t expires; int scope; u32 flags; - clock_t expires; - unsigned long timeout; ASSERT_RTNL(); @@ -2501,6 +2518,14 @@ static int inet6_addr_add(struct net *net, int ifindex, if (IS_ERR(idev)) return PTR_ERR(idev); + if (ifa_flags & IFA_F_MCAUTOJOIN) { + int ret = ipv6_mc_config(net->ipv6.mc_autojoin_sk, + true, pfx, ifindex); + + if (ret < 0) + return ret; + } + scope = ipv6_addr_scope(pfx); timeout = addrconf_timeout_fixup(valid_lft, HZ); @@ -2542,6 +2567,9 @@ static int inet6_addr_add(struct net *net, int ifindex, in6_ifa_put(ifp); addrconf_verify_rtnl(); return 0; + } else if (ifa_flags & IFA_F_MCAUTOJOIN) { + ipv6_mc_config(net->ipv6.mc_autojoin_sk, + false, pfx, ifindex); } return PTR_ERR(ifp); @@ -2578,6 +2606,10 @@ static int inet6_addr_del(struct net *net, int ifindex, u32 ifa_flags, jiffies); ipv6_del_addr(ifp); addrconf_verify_rtnl(); + if (ipv6_addr_is_multicast(pfx)) { + ipv6_mc_config(net->ipv6.mc_autojoin_sk, + false, pfx, dev->ifindex); + } return 0; } } @@ -3945,7 +3977,7 @@ inet6_rtm_newaddr(struct sk_buff *skb, struct nlmsghdr *nlh) /* We ignore other flags so far. */ ifa_flags &= IFA_F_NODAD | IFA_F_HOMEADDRESS | IFA_F_MANAGETEMPADDR | - IFA_F_NOPREFIXROUTE; + IFA_F_NOPREFIXROUTE | IFA_F_MCAUTOJOIN; ifa = ipv6_get_ifaddr(net, pfx, dev, 1); if (ifa == NULL) { diff --git a/net/ipv6/mcast.c b/net/ipv6/mcast.c index e4955d019734..1dd1fedff9f4 100644 --- a/net/ipv6/mcast.c +++ b/net/ipv6/mcast.c @@ -2929,20 +2929,32 @@ static int __net_init igmp6_net_init(struct net *net) inet6_sk(net->ipv6.igmp_sk)->hop_limit = 1; + err = inet_ctl_sock_create(&net->ipv6.mc_autojoin_sk, PF_INET6, + SOCK_RAW, IPPROTO_ICMPV6, net); + if (err < 0) { + pr_err("Failed to initialize the IGMP6 autojoin socket (err %d)\n", + err); + goto out_sock_create; + } + err = igmp6_proc_init(net); if (err) - goto out_sock_create; -out: - return err; + goto out_sock_create_autojoin; + + return 0; +out_sock_create_autojoin: + inet_ctl_sock_destroy(net->ipv6.mc_autojoin_sk); out_sock_create: inet_ctl_sock_destroy(net->ipv6.igmp_sk); - goto out; +out: + return err; } static void __net_exit igmp6_net_exit(struct net *net) { inet_ctl_sock_destroy(net->ipv6.igmp_sk); + inet_ctl_sock_destroy(net->ipv6.mc_autojoin_sk); igmp6_proc_exit(net); } -- cgit v1.2.3 From 56315f9e6e3a0ba0483c2e1f53333d5275268cb1 Mon Sep 17 00:00:00 2001 From: Alexander Duyck Date: Wed, 25 Feb 2015 15:31:31 -0800 Subject: fib_trie: Convert fib_alias to hlist from list There isn't any advantage to having it as a list and by making it an hlist we make the fib_alias more compatible with the list_info in terms of the type of list used. Signed-off-by: Alexander Duyck Signed-off-by: David S. Miller --- include/net/ip_fib.h | 2 +- net/ipv4/fib_lookup.h | 2 +- net/ipv4/fib_semantics.c | 4 +-- net/ipv4/fib_trie.c | 80 ++++++++++++++++++++++++++---------------------- 4 files changed, 48 insertions(+), 40 deletions(-) (limited to 'include') diff --git a/include/net/ip_fib.h b/include/net/ip_fib.h index 5bd120e4bc0a..cba4b7c32935 100644 --- a/include/net/ip_fib.h +++ b/include/net/ip_fib.h @@ -136,7 +136,7 @@ struct fib_result { u32 tclassid; struct fib_info *fi; struct fib_table *table; - struct list_head *fa_head; + struct hlist_head *fa_head; }; struct fib_result_nl { diff --git a/net/ipv4/fib_lookup.h b/net/ipv4/fib_lookup.h index 825981b1049a..3cd444f4e0f6 100644 --- a/net/ipv4/fib_lookup.h +++ b/net/ipv4/fib_lookup.h @@ -6,7 +6,7 @@ #include struct fib_alias { - struct list_head fa_list; + struct hlist_node fa_list; struct fib_info *fa_info; u8 fa_tos; u8 fa_type; diff --git a/net/ipv4/fib_semantics.c b/net/ipv4/fib_semantics.c index 1e2090ea663e..c6d267442dac 100644 --- a/net/ipv4/fib_semantics.c +++ b/net/ipv4/fib_semantics.c @@ -1163,12 +1163,12 @@ int fib_sync_down_dev(struct net_device *dev, int force) void fib_select_default(struct fib_result *res) { struct fib_info *fi = NULL, *last_resort = NULL; - struct list_head *fa_head = res->fa_head; + struct hlist_head *fa_head = res->fa_head; struct fib_table *tb = res->table; int order = -1, last_idx = -1; struct fib_alias *fa; - list_for_each_entry_rcu(fa, fa_head, fa_list) { + hlist_for_each_entry_rcu(fa, fa_head, fa_list) { struct fib_info *next_fi = fa->fa_info; if (next_fi->fib_scope != res->scope || diff --git a/net/ipv4/fib_trie.c b/net/ipv4/fib_trie.c index 3daf0224ff2e..f17e2239b7b9 100644 --- a/net/ipv4/fib_trie.c +++ b/net/ipv4/fib_trie.c @@ -116,7 +116,7 @@ struct leaf_info { struct hlist_node hlist; int plen; u32 mask_plen; /* ntohl(inet_make_mask(plen)) */ - struct list_head falh; + struct hlist_head falh; struct rcu_head rcu; }; @@ -339,7 +339,7 @@ static struct leaf_info *leaf_info_new(int plen) if (li) { li->plen = plen; li->mask_plen = ntohl(inet_make_mask(plen)); - INIT_LIST_HEAD(&li->falh); + INIT_HLIST_HEAD(&li->falh); } return li; } @@ -881,7 +881,7 @@ static struct leaf_info *find_leaf_info(struct tnode *l, int plen) return NULL; } -static inline struct list_head *get_fa_head(struct tnode *l, int plen) +static inline struct hlist_head *get_fa_head(struct tnode *l, int plen) { struct leaf_info *li = find_leaf_info(l, plen); @@ -994,14 +994,15 @@ static struct tnode *fib_find_node(struct trie *t, u32 key) /* Return the first fib alias matching TOS with * priority less than or equal to PRIO. */ -static struct fib_alias *fib_find_alias(struct list_head *fah, u8 tos, u32 prio) +static struct fib_alias *fib_find_alias(struct hlist_head *fah, u8 tos, + u32 prio) { struct fib_alias *fa; if (!fah) return NULL; - list_for_each_entry(fa, fah, fa_list) { + hlist_for_each_entry(fa, fah, fa_list) { if (fa->fa_tos > tos) continue; if (fa->fa_info->fib_priority >= prio || fa->fa_tos < tos) @@ -1027,9 +1028,9 @@ static void trie_rebalance(struct trie *t, struct tnode *tn) /* only used from updater-side */ -static struct list_head *fib_insert_node(struct trie *t, u32 key, int plen) +static struct hlist_head *fib_insert_node(struct trie *t, u32 key, int plen) { - struct list_head *fa_head = NULL; + struct hlist_head *fa_head = NULL; struct tnode *l, *n, *tp = NULL; struct leaf_info *li; @@ -1130,7 +1131,7 @@ int fib_table_insert(struct fib_table *tb, struct fib_config *cfg) { struct trie *t = (struct trie *) tb->tb_data; struct fib_alias *fa, *new_fa; - struct list_head *fa_head = NULL; + struct hlist_head *fa_head = NULL; struct fib_info *fi; int plen = cfg->fc_dst_len; u8 tos = cfg->fc_tos; @@ -1171,10 +1172,8 @@ int fib_table_insert(struct fib_table *tb, struct fib_config *cfg) * exists or to the node before which we will insert new one. * * If fa is NULL, we will need to allocate a new one and - * insert to the head of f. - * - * If f is NULL, no fib node matched the destination key - * and we need to allocate a new one of those as well. + * insert to the tail of the section matching the suffix length + * of the new alias. */ if (fa && fa->fa_tos == tos && @@ -1192,8 +1191,7 @@ int fib_table_insert(struct fib_table *tb, struct fib_config *cfg) */ fa_match = NULL; fa_first = fa; - fa = list_entry(fa->fa_list.prev, struct fib_alias, fa_list); - list_for_each_entry_continue(fa, fa_head, fa_list) { + hlist_for_each_entry_from(fa, fa_list) { if (fa->fa_tos != tos) break; if (fa->fa_info->fib_priority != fi->fib_priority) @@ -1227,7 +1225,7 @@ int fib_table_insert(struct fib_table *tb, struct fib_config *cfg) state = fa->fa_state; new_fa->fa_state = state & ~FA_S_ACCESSED; - list_replace_rcu(&fa->fa_list, &new_fa->fa_list); + hlist_replace_rcu(&fa->fa_list, &new_fa->fa_list); alias_free_mem_rcu(fa); fib_release_info(fi_drop); @@ -1276,8 +1274,19 @@ int fib_table_insert(struct fib_table *tb, struct fib_config *cfg) if (!plen) tb->tb_num_default++; - list_add_tail_rcu(&new_fa->fa_list, - (fa ? &fa->fa_list : fa_head)); + if (fa) { + hlist_add_before_rcu(&new_fa->fa_list, &fa->fa_list); + } else { + struct fib_alias *last; + + hlist_for_each_entry(last, fa_head, fa_list) + fa = last; + + if (fa) + hlist_add_behind_rcu(&new_fa->fa_list, &fa->fa_list); + else + hlist_add_head_rcu(&new_fa->fa_list, fa_head); + } rt_cache_flush(cfg->fc_nlinfo.nl_net); rtmsg_fib(RTM_NEWROUTE, htonl(key), new_fa, plen, tb->tb_id, @@ -1419,7 +1428,7 @@ found: if ((key ^ n->key) & li->mask_plen) continue; - list_for_each_entry_rcu(fa, &li->falh, fa_list) { + hlist_for_each_entry_rcu(fa, &li->falh, fa_list) { struct fib_info *fi = fa->fa_info; int nhsel, err; @@ -1501,7 +1510,7 @@ int fib_table_delete(struct fib_table *tb, struct fib_config *cfg) int plen = cfg->fc_dst_len; u8 tos = cfg->fc_tos; struct fib_alias *fa, *fa_to_delete; - struct list_head *fa_head; + struct hlist_head *fa_head; struct tnode *l; struct leaf_info *li; @@ -1534,8 +1543,7 @@ int fib_table_delete(struct fib_table *tb, struct fib_config *cfg) pr_debug("Deleting %08x/%d tos=%d t=%p\n", key, plen, tos, t); fa_to_delete = NULL; - fa = list_entry(fa->fa_list.prev, struct fib_alias, fa_list); - list_for_each_entry_continue(fa, fa_head, fa_list) { + hlist_for_each_entry_from(fa, fa_list) { struct fib_info *fi = fa->fa_info; if (fa->fa_tos != tos) @@ -1561,12 +1569,12 @@ int fib_table_delete(struct fib_table *tb, struct fib_config *cfg) rtmsg_fib(RTM_DELROUTE, htonl(key), fa, plen, tb->tb_id, &cfg->fc_nlinfo, 0); - list_del_rcu(&fa->fa_list); + hlist_del_rcu(&fa->fa_list); if (!plen) tb->tb_num_default--; - if (list_empty(fa_head)) { + if (hlist_empty(fa_head)) { remove_leaf_info(l, li); free_leaf_info(li); } @@ -1582,16 +1590,17 @@ int fib_table_delete(struct fib_table *tb, struct fib_config *cfg) return 0; } -static int trie_flush_list(struct list_head *head) +static int trie_flush_list(struct hlist_head *head) { - struct fib_alias *fa, *fa_node; + struct hlist_node *tmp; + struct fib_alias *fa; int found = 0; - list_for_each_entry_safe(fa, fa_node, head, fa_list) { + hlist_for_each_entry_safe(fa, tmp, head, fa_list) { struct fib_info *fi = fa->fa_info; if (fi && (fi->fib_flags & RTNH_F_DEAD)) { - list_del_rcu(&fa->fa_list); + hlist_del_rcu(&fa->fa_list); fib_release_info(fa->fa_info); alias_free_mem_rcu(fa); found++; @@ -1603,15 +1612,14 @@ static int trie_flush_list(struct list_head *head) static int trie_flush_leaf(struct tnode *l) { int found = 0; - struct hlist_head *lih = &l->list; struct hlist_node *tmp; - struct leaf_info *li = NULL; + struct leaf_info *li; unsigned char plen = KEYLENGTH; - hlist_for_each_entry_safe(li, tmp, lih, hlist) { + hlist_for_each_entry_safe(li, tmp, &l->list, hlist) { found += trie_flush_list(&li->falh); - if (list_empty(&li->falh)) { + if (hlist_empty(&li->falh)) { hlist_del_rcu(&li->hlist); free_leaf_info(li); continue; @@ -1731,7 +1739,7 @@ void fib_free_table(struct fib_table *tb) kfree(tb); } -static int fn_trie_dump_fa(t_key key, int plen, struct list_head *fah, +static int fn_trie_dump_fa(t_key key, int plen, struct hlist_head *fah, struct fib_table *tb, struct sk_buff *skb, struct netlink_callback *cb) { @@ -1744,7 +1752,7 @@ static int fn_trie_dump_fa(t_key key, int plen, struct list_head *fah, /* rcu_read_lock is hold by caller */ - list_for_each_entry_rcu(fa, fah, fa_list) { + hlist_for_each_entry_rcu(fa, fah, fa_list) { if (i < s_i) { i++; continue; @@ -1787,7 +1795,7 @@ static int fn_trie_dump_leaf(struct tnode *l, struct fib_table *tb, if (i > s_i) cb->args[5] = 0; - if (list_empty(&li->falh)) + if (hlist_empty(&li->falh)) continue; if (fn_trie_dump_fa(l->key, li->plen, &li->falh, tb, skb, cb) < 0) { @@ -2272,7 +2280,7 @@ static int fib_trie_seq_show(struct seq_file *seq, void *v) hlist_for_each_entry_rcu(li, &n->list, hlist) { struct fib_alias *fa; - list_for_each_entry_rcu(fa, &li->falh, fa_list) { + hlist_for_each_entry_rcu(fa, &li->falh, fa_list) { char buf1[32], buf2[32]; seq_indent(seq, iter->depth+1); @@ -2429,7 +2437,7 @@ static int fib_route_seq_show(struct seq_file *seq, void *v) mask = inet_make_mask(li->plen); prefix = htonl(l->key); - list_for_each_entry_rcu(fa, &li->falh, fa_list) { + hlist_for_each_entry_rcu(fa, &li->falh, fa_list) { const struct fib_info *fi = fa->fa_info; unsigned int flags = fib_flag_trans(fa->fa_type, mask, fi); -- cgit v1.2.3 From 9e39dc1e563e8d390bae42ee80e1e665c18b7de2 Mon Sep 17 00:00:00 2001 From: "Robert P. J. Day" Date: Fri, 13 Feb 2015 03:46:07 -0500 Subject: Documentation: Add "@" in front of private structure members. Even "private" structure members need a leading "@" in their kernel-doc; otherwise, they will be treated as new section names in the resulting manual. Signed-off-by: Robert P. J. Day Acked-by: Randy Dunlap Acked-By: Sebastian Reichel Signed-off-by: Jonathan Corbet --- include/linux/hsi/hsi.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/include/linux/hsi/hsi.h b/include/linux/hsi/hsi.h index 3ec06300d535..5dd60c2e120f 100644 --- a/include/linux/hsi/hsi.h +++ b/include/linux/hsi/hsi.h @@ -135,9 +135,9 @@ static inline int hsi_register_board_info(struct hsi_board_info const *info, * @device: Driver model representation of the device * @tx_cfg: HSI TX configuration * @rx_cfg: HSI RX configuration - * e_handler: Callback for handling port events (RX Wake High/Low) - * pclaimed: Keeps tracks if the clients claimed its associated HSI port - * nb: Notifier block for port events + * @e_handler: Callback for handling port events (RX Wake High/Low) + * @pclaimed: Keeps tracks if the clients claimed its associated HSI port + * @nb: Notifier block for port events */ struct hsi_client { struct device device; -- cgit v1.2.3 From 6588af614e7b79294fbcd4a666a7422c0c854e80 Mon Sep 17 00:00:00 2001 From: Ben Hutchings Date: Thu, 26 Feb 2015 19:34:37 +0000 Subject: usbnet: Fix tx_packets stat for FLAG_MULTI_FRAME drivers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently the usbnet core does not update the tx_packets statistic for drivers with FLAG_MULTI_PACKET and there is no hook in the TX completion path where they could do this. cdc_ncm and dependent drivers are bumping tx_packets stat on the transmit path while asix and sr9800 aren't updating it at all. Add a packet count in struct skb_data so these drivers can fill it in, initialise it to 1 for other drivers, and add the packet count to the tx_packets statistic on completion. Signed-off-by: Ben Hutchings Tested-by: Bjørn Mork Signed-off-by: David S. Miller --- drivers/net/usb/asix_common.c | 2 ++ drivers/net/usb/cdc_ncm.c | 3 ++- drivers/net/usb/sr9800.c | 1 + drivers/net/usb/usbnet.c | 5 +++-- include/linux/usb/usbnet.h | 12 ++++++++++++ 5 files changed, 20 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/drivers/net/usb/asix_common.c b/drivers/net/usb/asix_common.c index 5c55f11572ba..724a9b50df7a 100644 --- a/drivers/net/usb/asix_common.c +++ b/drivers/net/usb/asix_common.c @@ -188,6 +188,8 @@ struct sk_buff *asix_tx_fixup(struct usbnet *dev, struct sk_buff *skb, memcpy(skb_tail_pointer(skb), &padbytes, sizeof(padbytes)); skb_put(skb, sizeof(padbytes)); } + + usbnet_set_skb_tx_stats(skb, 1); return skb; } diff --git a/drivers/net/usb/cdc_ncm.c b/drivers/net/usb/cdc_ncm.c index 80a844e0ae03..70cbea551139 100644 --- a/drivers/net/usb/cdc_ncm.c +++ b/drivers/net/usb/cdc_ncm.c @@ -1172,7 +1172,6 @@ cdc_ncm_fill_tx_frame(struct usbnet *dev, struct sk_buff *skb, __le32 sign) /* return skb */ ctx->tx_curr_skb = NULL; - dev->net->stats.tx_packets += ctx->tx_curr_frame_num; /* keep private stats: framing overhead and number of NTBs */ ctx->tx_overhead += skb_out->len - ctx->tx_curr_frame_payload; @@ -1184,6 +1183,8 @@ cdc_ncm_fill_tx_frame(struct usbnet *dev, struct sk_buff *skb, __le32 sign) */ dev->net->stats.tx_bytes -= skb_out->len - ctx->tx_curr_frame_payload; + usbnet_set_skb_tx_stats(skb_out, n); + return skb_out; exit_no_skb: diff --git a/drivers/net/usb/sr9800.c b/drivers/net/usb/sr9800.c index b94a0fbb8b3b..7650cdc8fe6b 100644 --- a/drivers/net/usb/sr9800.c +++ b/drivers/net/usb/sr9800.c @@ -144,6 +144,7 @@ static struct sk_buff *sr_tx_fixup(struct usbnet *dev, struct sk_buff *skb, skb_put(skb, sizeof(padbytes)); } + usbnet_set_skb_tx_stats(skb, 1); return skb; } diff --git a/drivers/net/usb/usbnet.c b/drivers/net/usb/usbnet.c index 449835f4331e..0f3ff285f6a1 100644 --- a/drivers/net/usb/usbnet.c +++ b/drivers/net/usb/usbnet.c @@ -1188,8 +1188,7 @@ static void tx_complete (struct urb *urb) struct usbnet *dev = entry->dev; if (urb->status == 0) { - if (!(dev->driver_info->flags & FLAG_MULTI_PACKET)) - dev->net->stats.tx_packets++; + dev->net->stats.tx_packets += entry->packets; dev->net->stats.tx_bytes += entry->length; } else { dev->net->stats.tx_errors++; @@ -1348,6 +1347,8 @@ netdev_tx_t usbnet_start_xmit (struct sk_buff *skb, urb->transfer_flags |= URB_ZERO_PACKET; } entry->length = urb->transfer_buffer_length = length; + if (!(info->flags & FLAG_MULTI_PACKET)) + usbnet_set_skb_tx_stats(skb, 1); spin_lock_irqsave(&dev->txq.lock, flags); retval = usb_autopm_get_interface_async(dev->intf); diff --git a/include/linux/usb/usbnet.h b/include/linux/usb/usbnet.h index d9a4905e01d0..ff3fb2bd0e90 100644 --- a/include/linux/usb/usbnet.h +++ b/include/linux/usb/usbnet.h @@ -228,8 +228,20 @@ struct skb_data { /* skb->cb is one of these */ struct usbnet *dev; enum skb_state state; size_t length; + unsigned long packets; }; +/* Drivers that set FLAG_MULTI_PACKET must call this in their + * tx_fixup method before returning an skb. + */ +static inline void +usbnet_set_skb_tx_stats(struct sk_buff *skb, unsigned long packets) +{ + struct skb_data *entry = (struct skb_data *) skb->cb; + + entry->packets = packets; +} + extern int usbnet_open(struct net_device *net); extern int usbnet_stop(struct net_device *net); extern netdev_tx_t usbnet_start_xmit(struct sk_buff *skb, -- cgit v1.2.3 From 5f852eb536ad651b8734559dcf4353514cb0bea3 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Thu, 26 Feb 2015 14:10:18 -0800 Subject: tcp: tso: remove tp->tso_deferred TSO relies on ability to defer sending a small amount of packets. Heuristic is to wait for future ACKS in hope to send more packets at once. Current algorithm uses a per socket tso_deferred field as a pseudo timer. This pseudo timer relies on future ACK, but there is no guarantee we receive them in time. Fix would be to use a real timer, but cost of such timer is probably too expensive for typical cases. This patch changes the logic to test the time of last transmit, because we should not add bursts of more than 1ms for any given flow. We've used this patch for about two years at Google, before FQ/pacing as it would reduce a fair amount of bursts. Signed-off-by: Eric Dumazet Signed-off-by: Yuchung Cheng Signed-off-by: Neal Cardwell Signed-off-by: David S. Miller --- include/linux/tcp.h | 1 - net/ipv4/tcp_output.c | 14 +++++--------- 2 files changed, 5 insertions(+), 10 deletions(-) (limited to 'include') diff --git a/include/linux/tcp.h b/include/linux/tcp.h index 1a7adb411647..97dbf16f7d9d 100644 --- a/include/linux/tcp.h +++ b/include/linux/tcp.h @@ -236,7 +236,6 @@ struct tcp_sock { u32 lost_out; /* Lost packets */ u32 sacked_out; /* SACK'd packets */ u32 fackets_out; /* FACK'd packets */ - u32 tso_deferred; /* from STCP, retrans queue hinting */ struct sk_buff* lost_skb_hint; diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c index a2a796c5536b..cb95c7a9d1e7 100644 --- a/net/ipv4/tcp_output.c +++ b/net/ipv4/tcp_output.c @@ -1763,9 +1763,10 @@ static bool tcp_tso_should_defer(struct sock *sk, struct sk_buff *skb, if (icsk->icsk_ca_state != TCP_CA_Open) goto send_now; - /* Defer for less than two clock ticks. */ - if (tp->tso_deferred && - (((u32)jiffies << 1) >> 1) - (tp->tso_deferred >> 1) > 1) + /* Avoid bursty behavior by allowing defer + * only if the last write was recent. + */ + if ((s32)(tcp_time_stamp - tp->lsndtime) > 0) goto send_now; in_flight = tcp_packets_in_flight(tp); @@ -1807,11 +1808,7 @@ static bool tcp_tso_should_defer(struct sock *sk, struct sk_buff *skb, goto send_now; } - /* Ok, it looks like it is advisable to defer. - * Do not rearm the timer if already set to not break TCP ACK clocking. - */ - if (!tp->tso_deferred) - tp->tso_deferred = 1 | (jiffies << 1); + /* Ok, it looks like it is advisable to defer. */ if (cong_win < send_win && cong_win < skb->len) *is_cwnd_limited = true; @@ -1819,7 +1816,6 @@ static bool tcp_tso_should_defer(struct sock *sk, struct sk_buff *skb, return true; send_now: - tp->tso_deferred = 0; return false; } -- cgit v1.2.3 From 31f909a2c0abfc1a1a76b2981d28ac85d33210e7 Mon Sep 17 00:00:00 2001 From: Masashi Honma Date: Tue, 24 Feb 2015 22:42:16 +0900 Subject: nl/mac80211: allow zero plink timeout to disable STA expiration Both wpa_supplicant and mac80211 have and inactivity timer. By default wpa_supplicant will be timed out in 5 minutes and mac80211's it is 30 minutes. If wpa_supplicant uses a longer timer than mac80211, it will get unexpected disconnection by mac80211. Using 0xffffffff instead as the configured value could solve this w/o changing the code, but due to integer overflow in the expression used this doesn't work. The expression is: (current jiffies) > (frame Rx jiffies + NL80211_MESHCONF_PLINK_TIMEOUT * 250) On 32bit system, the right side would overflow and be a very small value if NL80211_MESHCONF_PLINK_TIMEOUT is sufficiently large, causing unexpectedly early disconnections. Instead allow disabling the inactivity timer to avoid this situation, by passing the (previously invalid and useless) value 0. Signed-off-by: Masashi Honma [reword/rewrap commit log] Signed-off-by: Johannes Berg --- include/uapi/linux/nl80211.h | 3 ++- net/mac80211/mesh.c | 3 ++- net/wireless/nl80211.c | 2 +- 3 files changed, 5 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index 68b294e83944..2dcf9bba317c 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h @@ -3092,7 +3092,8 @@ enum nl80211_mesh_power_mode { * * @NL80211_MESHCONF_PLINK_TIMEOUT: If no tx activity is seen from a STA we've * established peering with for longer than this time (in seconds), then - * remove it from the STA's list of peers. Default is 30 minutes. + * remove it from the STA's list of peers. You may set this to 0 to disable + * the removal of the STA. Default is 30 minutes. * * @__NL80211_MESHCONF_ATTR_AFTER_LAST: internal use */ diff --git a/net/mac80211/mesh.c b/net/mac80211/mesh.c index 0c8b2a77d312..acf441ff9f4a 100644 --- a/net/mac80211/mesh.c +++ b/net/mac80211/mesh.c @@ -574,7 +574,8 @@ static void ieee80211_mesh_housekeeping(struct ieee80211_sub_if_data *sdata) struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; u32 changed; - ieee80211_sta_expire(sdata, ifmsh->mshcfg.plink_timeout * HZ); + if (ifmsh->mshcfg.plink_timeout > 0) + ieee80211_sta_expire(sdata, ifmsh->mshcfg.plink_timeout * HZ); mesh_path_expire(sdata); changed = mesh_accept_plinks_update(sdata); diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index d78fd8b54515..9c6e23ede5b2 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -5265,7 +5265,7 @@ do { \ FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshAwakeWindowDuration, 0, 65535, mask, NL80211_MESHCONF_AWAKE_WINDOW, nla_get_u16); - FILL_IN_MESH_PARAM_IF_SET(tb, cfg, plink_timeout, 1, 0xffffffff, + FILL_IN_MESH_PARAM_IF_SET(tb, cfg, plink_timeout, 0, 0xffffffff, mask, NL80211_MESHCONF_PLINK_TIMEOUT, nla_get_u32); if (mask_out) -- cgit v1.2.3 From 36ef906ee8fefbfac3844206e66d8450e6221c69 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 25 Feb 2015 10:54:33 +0100 Subject: wext: add checked wrappers for adding events/points to streams These checked wrappers are necessary for the next patch, which will use them to avoid sending out partial scan results. Signed-off-by: Johannes Berg --- include/net/iw_handler.h | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) (limited to 'include') diff --git a/include/net/iw_handler.h b/include/net/iw_handler.h index a830b01baba4..8f81bbbc38fc 100644 --- a/include/net/iw_handler.h +++ b/include/net/iw_handler.h @@ -519,6 +519,17 @@ iwe_stream_add_event(struct iw_request_info *info, char *stream, char *ends, return stream; } +static inline char * +iwe_stream_add_event_check(struct iw_request_info *info, char *stream, + char *ends, struct iw_event *iwe, int event_len) +{ + char *res = iwe_stream_add_event(info, stream, ends, iwe, event_len); + + if (res == stream) + return ERR_PTR(-E2BIG); + return res; +} + /*------------------------------------------------------------------*/ /* * Wrapper to add an short Wireless Event containing a pointer to a @@ -545,6 +556,17 @@ iwe_stream_add_point(struct iw_request_info *info, char *stream, char *ends, return stream; } +static inline char * +iwe_stream_add_point_check(struct iw_request_info *info, char *stream, + char *ends, struct iw_event *iwe, char *extra) +{ + char *res = iwe_stream_add_point(info, stream, ends, iwe, extra); + + if (res == stream) + return ERR_PTR(-E2BIG); + return res; +} + /*------------------------------------------------------------------*/ /* * Wrapper to add a value to a Wireless Event in a stream of events. -- cgit v1.2.3 From a2c83fff582ae133d9f5bb187404ea9ce4da1f96 Mon Sep 17 00:00:00 2001 From: Daniel Borkmann Date: Sun, 1 Mar 2015 12:31:42 +0100 Subject: ebpf: constify various function pointer structs We can move bpf_map_ops and bpf_verifier_ops and other structs into ro section, bpf_map_type_list and bpf_prog_type_list into read mostly. Signed-off-by: Daniel Borkmann Acked-by: Alexei Starovoitov Signed-off-by: David S. Miller --- include/linux/bpf.h | 14 +++++++------- kernel/bpf/arraymap.c | 6 +++--- kernel/bpf/hashtab.c | 6 +++--- kernel/bpf/helpers.c | 6 +++--- net/core/filter.c | 6 +++--- 5 files changed, 19 insertions(+), 19 deletions(-) (limited to 'include') diff --git a/include/linux/bpf.h b/include/linux/bpf.h index bbfceb756452..78446860f796 100644 --- a/include/linux/bpf.h +++ b/include/linux/bpf.h @@ -32,13 +32,13 @@ struct bpf_map { u32 key_size; u32 value_size; u32 max_entries; - struct bpf_map_ops *ops; + const struct bpf_map_ops *ops; struct work_struct work; }; struct bpf_map_type_list { struct list_head list_node; - struct bpf_map_ops *ops; + const struct bpf_map_ops *ops; enum bpf_map_type type; }; @@ -109,7 +109,7 @@ struct bpf_verifier_ops { struct bpf_prog_type_list { struct list_head list_node; - struct bpf_verifier_ops *ops; + const struct bpf_verifier_ops *ops; enum bpf_prog_type type; }; @@ -121,7 +121,7 @@ struct bpf_prog_aux { atomic_t refcnt; bool is_gpl_compatible; enum bpf_prog_type prog_type; - struct bpf_verifier_ops *ops; + const struct bpf_verifier_ops *ops; struct bpf_map **used_maps; u32 used_map_cnt; struct bpf_prog *prog; @@ -138,8 +138,8 @@ struct bpf_prog *bpf_prog_get(u32 ufd); int bpf_check(struct bpf_prog *fp, union bpf_attr *attr); /* verifier prototypes for helper functions called from eBPF programs */ -extern struct bpf_func_proto bpf_map_lookup_elem_proto; -extern struct bpf_func_proto bpf_map_update_elem_proto; -extern struct bpf_func_proto bpf_map_delete_elem_proto; +extern const struct bpf_func_proto bpf_map_lookup_elem_proto; +extern const struct bpf_func_proto bpf_map_update_elem_proto; +extern const struct bpf_func_proto bpf_map_delete_elem_proto; #endif /* _LINUX_BPF_H */ diff --git a/kernel/bpf/arraymap.c b/kernel/bpf/arraymap.c index 9eb4d8a7cd87..8a6616583f38 100644 --- a/kernel/bpf/arraymap.c +++ b/kernel/bpf/arraymap.c @@ -134,7 +134,7 @@ static void array_map_free(struct bpf_map *map) kvfree(array); } -static struct bpf_map_ops array_ops = { +static const struct bpf_map_ops array_ops = { .map_alloc = array_map_alloc, .map_free = array_map_free, .map_get_next_key = array_map_get_next_key, @@ -143,14 +143,14 @@ static struct bpf_map_ops array_ops = { .map_delete_elem = array_map_delete_elem, }; -static struct bpf_map_type_list tl = { +static struct bpf_map_type_list array_type __read_mostly = { .ops = &array_ops, .type = BPF_MAP_TYPE_ARRAY, }; static int __init register_array_map(void) { - bpf_register_map_type(&tl); + bpf_register_map_type(&array_type); return 0; } late_initcall(register_array_map); diff --git a/kernel/bpf/hashtab.c b/kernel/bpf/hashtab.c index b3ba43674310..83c209d9b17a 100644 --- a/kernel/bpf/hashtab.c +++ b/kernel/bpf/hashtab.c @@ -345,7 +345,7 @@ static void htab_map_free(struct bpf_map *map) kfree(htab); } -static struct bpf_map_ops htab_ops = { +static const struct bpf_map_ops htab_ops = { .map_alloc = htab_map_alloc, .map_free = htab_map_free, .map_get_next_key = htab_map_get_next_key, @@ -354,14 +354,14 @@ static struct bpf_map_ops htab_ops = { .map_delete_elem = htab_map_delete_elem, }; -static struct bpf_map_type_list tl = { +static struct bpf_map_type_list htab_type __read_mostly = { .ops = &htab_ops, .type = BPF_MAP_TYPE_HASH, }; static int __init register_htab_map(void) { - bpf_register_map_type(&tl); + bpf_register_map_type(&htab_type); return 0; } late_initcall(register_htab_map); diff --git a/kernel/bpf/helpers.c b/kernel/bpf/helpers.c index 9e3414d85459..a3c7701a8b5e 100644 --- a/kernel/bpf/helpers.c +++ b/kernel/bpf/helpers.c @@ -41,7 +41,7 @@ static u64 bpf_map_lookup_elem(u64 r1, u64 r2, u64 r3, u64 r4, u64 r5) return (unsigned long) value; } -struct bpf_func_proto bpf_map_lookup_elem_proto = { +const struct bpf_func_proto bpf_map_lookup_elem_proto = { .func = bpf_map_lookup_elem, .gpl_only = false, .ret_type = RET_PTR_TO_MAP_VALUE_OR_NULL, @@ -60,7 +60,7 @@ static u64 bpf_map_update_elem(u64 r1, u64 r2, u64 r3, u64 r4, u64 r5) return map->ops->map_update_elem(map, key, value, r4); } -struct bpf_func_proto bpf_map_update_elem_proto = { +const struct bpf_func_proto bpf_map_update_elem_proto = { .func = bpf_map_update_elem, .gpl_only = false, .ret_type = RET_INTEGER, @@ -80,7 +80,7 @@ static u64 bpf_map_delete_elem(u64 r1, u64 r2, u64 r3, u64 r4, u64 r5) return map->ops->map_delete_elem(map, key); } -struct bpf_func_proto bpf_map_delete_elem_proto = { +const struct bpf_func_proto bpf_map_delete_elem_proto = { .func = bpf_map_delete_elem, .gpl_only = false, .ret_type = RET_INTEGER, diff --git a/net/core/filter.c b/net/core/filter.c index f6bdc2b1ba01..6fe09e36dad9 100644 --- a/net/core/filter.c +++ b/net/core/filter.c @@ -1159,19 +1159,19 @@ static bool sock_filter_is_valid_access(int off, int size, enum bpf_access_type return false; } -static struct bpf_verifier_ops sock_filter_ops = { +static const struct bpf_verifier_ops sock_filter_ops = { .get_func_proto = sock_filter_func_proto, .is_valid_access = sock_filter_is_valid_access, }; -static struct bpf_prog_type_list tl = { +static struct bpf_prog_type_list sock_filter_type __read_mostly = { .ops = &sock_filter_ops, .type = BPF_PROG_TYPE_SOCKET_FILTER, }; static int __init register_sock_filter_ops(void) { - bpf_register_prog_type(&tl); + bpf_register_prog_type(&sock_filter_type); return 0; } late_initcall(register_sock_filter_ops); -- cgit v1.2.3 From f1a66f85b74c5ef7b503f746ea97742dacd56419 Mon Sep 17 00:00:00 2001 From: Daniel Borkmann Date: Sun, 1 Mar 2015 12:31:43 +0100 Subject: ebpf: export BPF_PSEUDO_MAP_FD to uapi We need to export BPF_PSEUDO_MAP_FD to user space, as it's used in the ELF BPF loader where instructions are being loaded that need map fixups. An initial stage loads all maps into the kernel, and later on replaces related instructions in the eBPF blob with BPF_PSEUDO_MAP_FD as source register and the actual fd as immediate value. The kernel verifier recognizes this keyword and replaces the map fd with a real pointer internally. Signed-off-by: Daniel Borkmann Acked-by: Alexei Starovoitov Signed-off-by: David S. Miller --- include/linux/filter.h | 2 -- include/uapi/linux/bpf.h | 2 ++ samples/bpf/libbpf.h | 4 +++- 3 files changed, 5 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/include/linux/filter.h b/include/linux/filter.h index caac2087a4d5..5e3863d5f666 100644 --- a/include/linux/filter.h +++ b/include/linux/filter.h @@ -145,8 +145,6 @@ struct bpf_prog_aux; .off = 0, \ .imm = ((__u64) (IMM)) >> 32 }) -#define BPF_PSEUDO_MAP_FD 1 - /* pseudo BPF_LD_IMM64 insn used to refer to process-local map_fd */ #define BPF_LD_MAP_FD(DST, MAP_FD) \ BPF_LD_IMM64_RAW(DST, BPF_PSEUDO_MAP_FD, MAP_FD) diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h index 45da7ec7d274..0248180bf2e2 100644 --- a/include/uapi/linux/bpf.h +++ b/include/uapi/linux/bpf.h @@ -120,6 +120,8 @@ enum bpf_prog_type { BPF_PROG_TYPE_SOCKET_FILTER, }; +#define BPF_PSEUDO_MAP_FD 1 + /* flags for BPF_MAP_UPDATE_ELEM command */ #define BPF_ANY 0 /* create new element or update existing */ #define BPF_NOEXIST 1 /* create new element if it didn't exist */ diff --git a/samples/bpf/libbpf.h b/samples/bpf/libbpf.h index 58c5fe1bdba1..a6bb7e9c22c3 100644 --- a/samples/bpf/libbpf.h +++ b/samples/bpf/libbpf.h @@ -92,7 +92,9 @@ extern char bpf_log_buf[LOG_BUF_SIZE]; .off = 0, \ .imm = ((__u64) (IMM)) >> 32 }) -#define BPF_PSEUDO_MAP_FD 1 +#ifndef BPF_PSEUDO_MAP_FD +# define BPF_PSEUDO_MAP_FD 1 +#endif /* pseudo BPF_LD_IMM64 insn used to refer to process-local map_fd */ #define BPF_LD_MAP_FD(DST, MAP_FD) \ -- cgit v1.2.3 From 0fc174dea54546e2b1146e1197da1b6d4bc48107 Mon Sep 17 00:00:00 2001 From: Daniel Borkmann Date: Sun, 1 Mar 2015 12:31:44 +0100 Subject: ebpf: make internal bpf API independent of CONFIG_BPF_SYSCALL ifdefs Socket filter code and other subsystems with upcoming eBPF support should not need to deal with the fact that we have CONFIG_BPF_SYSCALL defined or not. Having the bpf syscall as a config option is a nice thing and I'd expect it to stay that way for expert users (I presume one day the default setting of it might change, though), but code making use of it should not care if it's actually enabled or not. Instead, hide this via header files and let the rest deal with it. Signed-off-by: Daniel Borkmann Signed-off-by: Alexei Starovoitov Signed-off-by: David S. Miller --- include/linux/bpf.h | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/include/linux/bpf.h b/include/linux/bpf.h index 78446860f796..9c458144cdb4 100644 --- a/include/linux/bpf.h +++ b/include/linux/bpf.h @@ -113,8 +113,6 @@ struct bpf_prog_type_list { enum bpf_prog_type type; }; -void bpf_register_prog_type(struct bpf_prog_type_list *tl); - struct bpf_prog; struct bpf_prog_aux { @@ -129,11 +127,25 @@ struct bpf_prog_aux { }; #ifdef CONFIG_BPF_SYSCALL +void bpf_register_prog_type(struct bpf_prog_type_list *tl); + void bpf_prog_put(struct bpf_prog *prog); +struct bpf_prog *bpf_prog_get(u32 ufd); #else -static inline void bpf_prog_put(struct bpf_prog *prog) {} +static inline void bpf_register_prog_type(struct bpf_prog_type_list *tl) +{ +} + +static inline struct bpf_prog *bpf_prog_get(u32 ufd) +{ + return ERR_PTR(-EOPNOTSUPP); +} + +static inline void bpf_prog_put(struct bpf_prog *prog) +{ +} #endif -struct bpf_prog *bpf_prog_get(u32 ufd); + /* verify correctness of eBPF program */ int bpf_check(struct bpf_prog *fp, union bpf_attr *attr); -- cgit v1.2.3 From 96be4325f443dbbfeb37d2a157675ac0736531a1 Mon Sep 17 00:00:00 2001 From: Daniel Borkmann Date: Sun, 1 Mar 2015 12:31:46 +0100 Subject: ebpf: add sched_cls_type and map it to sk_filter's verifier ops As discussed recently and at netconf/netdev01, we want to prevent making bpf_verifier_ops registration available for modules, but have them at a controlled place inside the kernel instead. The reason for this is, that out-of-tree modules can go crazy and define and register any verfifier ops they want, doing all sorts of crap, even bypassing available GPLed eBPF helper functions. We don't want to offer such a shiny playground, of course, but keep strict control to ourselves inside the core kernel. This also encourages us to design eBPF user helpers carefully and generically, so they can be shared among various subsystems using eBPF. For the eBPF traffic classifier (cls_bpf), it's a good start to share the same helper facilities as we currently do in eBPF for socket filters. That way, we have BPF_PROG_TYPE_SCHED_CLS look like it's own type, thus one day if there's a good reason to diverge the set of helper functions from the set available to socket filters, we keep ABI compatibility. In future, we could place all bpf_prog_type_list at a central place, perhaps. Signed-off-by: Daniel Borkmann Signed-off-by: Alexei Starovoitov Signed-off-by: David S. Miller --- include/uapi/linux/bpf.h | 1 + kernel/bpf/verifier.c | 15 +++++++++++++-- net/core/filter.c | 7 +++++++ 3 files changed, 21 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h index 0248180bf2e2..3fa1af8a58d7 100644 --- a/include/uapi/linux/bpf.h +++ b/include/uapi/linux/bpf.h @@ -118,6 +118,7 @@ enum bpf_map_type { enum bpf_prog_type { BPF_PROG_TYPE_UNSPEC, BPF_PROG_TYPE_SOCKET_FILTER, + BPF_PROG_TYPE_SCHED_CLS, }; #define BPF_PSEUDO_MAP_FD 1 diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index a28e09c7825d..594d341f04db 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -1172,6 +1172,17 @@ static int check_ld_imm(struct verifier_env *env, struct bpf_insn *insn) return 0; } +static bool may_access_skb(enum bpf_prog_type type) +{ + switch (type) { + case BPF_PROG_TYPE_SOCKET_FILTER: + case BPF_PROG_TYPE_SCHED_CLS: + return true; + default: + return false; + } +} + /* verify safety of LD_ABS|LD_IND instructions: * - they can only appear in the programs where ctx == skb * - since they are wrappers of function calls, they scratch R1-R5 registers, @@ -1194,8 +1205,8 @@ static int check_ld_abs(struct verifier_env *env, struct bpf_insn *insn) struct reg_state *reg; int i, err; - if (env->prog->aux->prog_type != BPF_PROG_TYPE_SOCKET_FILTER) { - verbose("BPF_LD_ABS|IND instructions are only allowed in socket filters\n"); + if (!may_access_skb(env->prog->aux->prog_type)) { + verbose("BPF_LD_ABS|IND instructions not allowed for this program type\n"); return -EINVAL; } diff --git a/net/core/filter.c b/net/core/filter.c index 741721233166..514d4082f326 100644 --- a/net/core/filter.c +++ b/net/core/filter.c @@ -1166,9 +1166,16 @@ static struct bpf_prog_type_list sk_filter_type __read_mostly = { .type = BPF_PROG_TYPE_SOCKET_FILTER, }; +static struct bpf_prog_type_list sched_cls_type __read_mostly = { + .ops = &sk_filter_ops, + .type = BPF_PROG_TYPE_SCHED_CLS, +}; + static int __init register_sk_filter_ops(void) { bpf_register_prog_type(&sk_filter_type); + bpf_register_prog_type(&sched_cls_type); + return 0; } late_initcall(register_sk_filter_ops); -- cgit v1.2.3 From 24701ecea76b0b93bd9667486934ec310825f558 Mon Sep 17 00:00:00 2001 From: Daniel Borkmann Date: Sun, 1 Mar 2015 12:31:47 +0100 Subject: ebpf: move read-only fields to bpf_prog and shrink bpf_prog_aux is_gpl_compatible and prog_type should be moved directly into bpf_prog as they stay immutable during bpf_prog's lifetime, are core attributes and they can be locked as read-only later on via bpf_prog_select_runtime(). With a bit of rearranging, this also allows us to shrink bpf_prog_aux to exactly 1 cacheline. Signed-off-by: Daniel Borkmann Acked-by: Alexei Starovoitov Signed-off-by: David S. Miller --- include/linux/bpf.h | 4 +--- include/linux/filter.h | 4 +++- kernel/bpf/syscall.c | 7 +++---- kernel/bpf/verifier.c | 4 ++-- net/core/filter.c | 4 ++-- 5 files changed, 11 insertions(+), 12 deletions(-) (limited to 'include') diff --git a/include/linux/bpf.h b/include/linux/bpf.h index 9c458144cdb4..a1a7ff2df328 100644 --- a/include/linux/bpf.h +++ b/include/linux/bpf.h @@ -117,11 +117,9 @@ struct bpf_prog; struct bpf_prog_aux { atomic_t refcnt; - bool is_gpl_compatible; - enum bpf_prog_type prog_type; + u32 used_map_cnt; const struct bpf_verifier_ops *ops; struct bpf_map **used_maps; - u32 used_map_cnt; struct bpf_prog *prog; struct work_struct work; }; diff --git a/include/linux/filter.h b/include/linux/filter.h index 5e3863d5f666..9ee8c67ea249 100644 --- a/include/linux/filter.h +++ b/include/linux/filter.h @@ -308,9 +308,11 @@ struct bpf_binary_header { struct bpf_prog { u16 pages; /* Number of allocated pages */ bool jited; /* Is our filter JIT'ed? */ + bool gpl_compatible; /* Is our filter GPL compatible? */ u32 len; /* Number of filter blocks */ - struct sock_fprog_kern *orig_prog; /* Original BPF program */ + enum bpf_prog_type type; /* Type of BPF program */ struct bpf_prog_aux *aux; /* Auxiliary fields */ + struct sock_fprog_kern *orig_prog; /* Original BPF program */ unsigned int (*bpf_func)(const struct sk_buff *skb, const struct bpf_insn *filter); /* Instructions for interpreter */ diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c index 536edc2be307..0d69449acbd0 100644 --- a/kernel/bpf/syscall.c +++ b/kernel/bpf/syscall.c @@ -354,10 +354,11 @@ static int find_prog_type(enum bpf_prog_type type, struct bpf_prog *prog) list_for_each_entry(tl, &bpf_prog_types, list_node) { if (tl->type == type) { prog->aux->ops = tl->ops; - prog->aux->prog_type = type; + prog->type = type; return 0; } } + return -EINVAL; } @@ -508,7 +509,7 @@ static int bpf_prog_load(union bpf_attr *attr) prog->jited = false; atomic_set(&prog->aux->refcnt, 1); - prog->aux->is_gpl_compatible = is_gpl; + prog->gpl_compatible = is_gpl; /* find program type: socket_filter vs tracing_filter */ err = find_prog_type(type, prog); @@ -517,7 +518,6 @@ static int bpf_prog_load(union bpf_attr *attr) /* run eBPF verifier */ err = bpf_check(prog, attr); - if (err < 0) goto free_used_maps; @@ -528,7 +528,6 @@ static int bpf_prog_load(union bpf_attr *attr) bpf_prog_select_runtime(prog); err = anon_inode_getfd("bpf-prog", &bpf_prog_fops, prog, O_RDWR | O_CLOEXEC); - if (err < 0) /* failed to allocate fd */ goto free_used_maps; diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index 594d341f04db..bdf4192a889b 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -852,7 +852,7 @@ static int check_call(struct verifier_env *env, int func_id) } /* eBPF programs must be GPL compatible to use GPL-ed functions */ - if (!env->prog->aux->is_gpl_compatible && fn->gpl_only) { + if (!env->prog->gpl_compatible && fn->gpl_only) { verbose("cannot call GPL only function from proprietary program\n"); return -EINVAL; } @@ -1205,7 +1205,7 @@ static int check_ld_abs(struct verifier_env *env, struct bpf_insn *insn) struct reg_state *reg; int i, err; - if (!may_access_skb(env->prog->aux->prog_type)) { + if (!may_access_skb(env->prog->type)) { verbose("BPF_LD_ABS|IND instructions not allowed for this program type\n"); return -EINVAL; } diff --git a/net/core/filter.c b/net/core/filter.c index 514d4082f326..ff000cb25e0a 100644 --- a/net/core/filter.c +++ b/net/core/filter.c @@ -814,7 +814,7 @@ static void bpf_release_orig_filter(struct bpf_prog *fp) static void __bpf_prog_release(struct bpf_prog *prog) { - if (prog->aux->prog_type == BPF_PROG_TYPE_SOCKET_FILTER) { + if (prog->type == BPF_PROG_TYPE_SOCKET_FILTER) { bpf_prog_put(prog); } else { bpf_release_orig_filter(prog); @@ -1105,7 +1105,7 @@ int sk_attach_bpf(u32 ufd, struct sock *sk) if (IS_ERR(prog)) return PTR_ERR(prog); - if (prog->aux->prog_type != BPF_PROG_TYPE_SOCKET_FILTER) { + if (prog->type != BPF_PROG_TYPE_SOCKET_FILTER) { bpf_prog_put(prog); return -EINVAL; } -- cgit v1.2.3 From e2e9b6541dd4b31848079da80fe2253daaafb549 Mon Sep 17 00:00:00 2001 From: Daniel Borkmann Date: Sun, 1 Mar 2015 12:31:48 +0100 Subject: cls_bpf: add initial eBPF support for programmable classifiers This work extends the "classic" BPF programmable tc classifier by extending its scope also to native eBPF code! This allows for user space to implement own custom, 'safe' C like classifiers (or whatever other frontend language LLVM et al may provide in future), that can then be compiled with the LLVM eBPF backend to an eBPF elf file. The result of this can be loaded into the kernel via iproute2's tc. In the kernel, they can be JITed on major archs and thus run in native performance. Simple, minimal toy example to demonstrate the workflow: #include #include #include #include "tc_bpf_api.h" __section("classify") int cls_main(struct sk_buff *skb) { return (0x800 << 16) | load_byte(skb, ETH_HLEN + __builtin_offsetof(struct iphdr, tos)); } char __license[] __section("license") = "GPL"; The classifier can then be compiled into eBPF opcodes and loaded via tc, for example: clang -O2 -emit-llvm -c cls.c -o - | llc -march=bpf -filetype=obj -o cls.o tc filter add dev em1 parent 1: bpf cls.o [...] As it has been demonstrated, the scope can even reach up to a fully fledged flow dissector (similarly as in samples/bpf/sockex2_kern.c). For tc, maps are allowed to be used, but from kernel context only, in other words, eBPF code can keep state across filter invocations. In future, we perhaps may reattach from a different application to those maps e.g., to read out collected statistics/state. Similarly as in socket filters, we may extend functionality for eBPF classifiers over time depending on the use cases. For that purpose, cls_bpf programs are using BPF_PROG_TYPE_SCHED_CLS program type, so we can allow additional functions/accessors (e.g. an ABI compatible offset translation to skb fields/metadata). For an initial cls_bpf support, we allow the same set of helper functions as eBPF socket filters, but we could diverge at some point in time w/o problem. I was wondering whether cls_bpf and act_bpf could share C programs, I can imagine that at some point, we introduce i) further common handlers for both (or even beyond their scope), and/or if truly needed ii) some restricted function space for each of them. Both can be abstracted easily through struct bpf_verifier_ops in future. The context of cls_bpf versus act_bpf is slightly different though: a cls_bpf program will return a specific classid whereas act_bpf a drop/non-drop return code, latter may also in future mangle skbs. That said, we can surely have a "classify" and "action" section in a single object file, or considered mentioned constraint add a possibility of a shared section. The workflow for getting native eBPF running from tc [1] is as follows: for f_bpf, I've added a slightly modified ELF parser code from Alexei's kernel sample, which reads out the LLVM compiled object, sets up maps (and dynamically fixes up map fds) if any, and loads the eBPF instructions all centrally through the bpf syscall. The resulting fd from the loaded program itself is being passed down to cls_bpf, which looks up struct bpf_prog from the fd store, and holds reference, so that it stays available also after tc program lifetime. On tc filter destruction, it will then drop its reference. Moreover, I've also added the optional possibility to annotate an eBPF filter with a name (e.g. path to object file, or something else if preferred) so that when tc dumps currently installed filters, some more context can be given to an admin for a given instance (as opposed to just the file descriptor number). Last but not least, bpf_prog_get() and bpf_prog_put() needed to be exported, so that eBPF can be used from cls_bpf built as a module. Thanks to 60a3b2253c41 ("net: bpf: make eBPF interpreter images read-only") I think this is of no concern since anything wanting to alter eBPF opcode after verification stage would crash the kernel. [1] http://git.breakpoint.cc/cgit/dborkman/iproute2.git/log/?h=ebpf Signed-off-by: Daniel Borkmann Cc: Jamal Hadi Salim Cc: Jiri Pirko Acked-by: Alexei Starovoitov Signed-off-by: David S. Miller --- include/uapi/linux/pkt_cls.h | 2 + kernel/bpf/syscall.c | 2 + net/sched/cls_bpf.c | 206 ++++++++++++++++++++++++++++++++----------- 3 files changed, 158 insertions(+), 52 deletions(-) (limited to 'include') diff --git a/include/uapi/linux/pkt_cls.h b/include/uapi/linux/pkt_cls.h index 25731dfb3fcc..bf08e76bf505 100644 --- a/include/uapi/linux/pkt_cls.h +++ b/include/uapi/linux/pkt_cls.h @@ -397,6 +397,8 @@ enum { TCA_BPF_CLASSID, TCA_BPF_OPS_LEN, TCA_BPF_OPS, + TCA_BPF_FD, + TCA_BPF_NAME, __TCA_BPF_MAX, }; diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c index 0d69449acbd0..669719ccc9ee 100644 --- a/kernel/bpf/syscall.c +++ b/kernel/bpf/syscall.c @@ -419,6 +419,7 @@ void bpf_prog_put(struct bpf_prog *prog) bpf_prog_free(prog); } } +EXPORT_SYMBOL_GPL(bpf_prog_put); static int bpf_prog_release(struct inode *inode, struct file *filp) { @@ -466,6 +467,7 @@ struct bpf_prog *bpf_prog_get(u32 ufd) fdput(f); return prog; } +EXPORT_SYMBOL_GPL(bpf_prog_get); /* last field in 'union bpf_attr' used by this command */ #define BPF_PROG_LOAD_LAST_FIELD log_buf diff --git a/net/sched/cls_bpf.c b/net/sched/cls_bpf.c index 5f3ee9e4b5bf..6f7ed8f8e6ee 100644 --- a/net/sched/cls_bpf.c +++ b/net/sched/cls_bpf.c @@ -16,6 +16,8 @@ #include #include #include +#include + #include #include #include @@ -24,6 +26,8 @@ MODULE_LICENSE("GPL"); MODULE_AUTHOR("Daniel Borkmann "); MODULE_DESCRIPTION("TC BPF based classifier"); +#define CLS_BPF_NAME_LEN 256 + struct cls_bpf_head { struct list_head plist; u32 hgen; @@ -32,18 +36,24 @@ struct cls_bpf_head { struct cls_bpf_prog { struct bpf_prog *filter; - struct sock_filter *bpf_ops; - struct tcf_exts exts; - struct tcf_result res; struct list_head link; + struct tcf_result res; + struct tcf_exts exts; u32 handle; - u16 bpf_num_ops; + union { + u32 bpf_fd; + u16 bpf_num_ops; + }; + struct sock_filter *bpf_ops; + const char *bpf_name; struct tcf_proto *tp; struct rcu_head rcu; }; static const struct nla_policy bpf_policy[TCA_BPF_MAX + 1] = { [TCA_BPF_CLASSID] = { .type = NLA_U32 }, + [TCA_BPF_FD] = { .type = NLA_U32 }, + [TCA_BPF_NAME] = { .type = NLA_NUL_STRING, .len = CLS_BPF_NAME_LEN }, [TCA_BPF_OPS_LEN] = { .type = NLA_U16 }, [TCA_BPF_OPS] = { .type = NLA_BINARY, .len = sizeof(struct sock_filter) * BPF_MAXINSNS }, @@ -76,6 +86,11 @@ static int cls_bpf_classify(struct sk_buff *skb, const struct tcf_proto *tp, return -1; } +static bool cls_bpf_is_ebpf(const struct cls_bpf_prog *prog) +{ + return !prog->bpf_ops; +} + static int cls_bpf_init(struct tcf_proto *tp) { struct cls_bpf_head *head; @@ -94,8 +109,12 @@ static void cls_bpf_delete_prog(struct tcf_proto *tp, struct cls_bpf_prog *prog) { tcf_exts_destroy(&prog->exts); - bpf_prog_destroy(prog->filter); + if (cls_bpf_is_ebpf(prog)) + bpf_prog_put(prog->filter); + else + bpf_prog_destroy(prog->filter); + kfree(prog->bpf_name); kfree(prog->bpf_ops); kfree(prog); } @@ -114,6 +133,7 @@ static int cls_bpf_delete(struct tcf_proto *tp, unsigned long arg) list_del_rcu(&prog->link); tcf_unbind_filter(tp, &prog->res); call_rcu(&prog->rcu, __cls_bpf_delete_prog); + return 0; } @@ -151,69 +171,121 @@ static unsigned long cls_bpf_get(struct tcf_proto *tp, u32 handle) return ret; } -static int cls_bpf_modify_existing(struct net *net, struct tcf_proto *tp, - struct cls_bpf_prog *prog, - unsigned long base, struct nlattr **tb, - struct nlattr *est, bool ovr) +static int cls_bpf_prog_from_ops(struct nlattr **tb, + struct cls_bpf_prog *prog, u32 classid) { struct sock_filter *bpf_ops; - struct tcf_exts exts; - struct sock_fprog_kern tmp; + struct sock_fprog_kern fprog_tmp; struct bpf_prog *fp; u16 bpf_size, bpf_num_ops; - u32 classid; int ret; - if (!tb[TCA_BPF_OPS_LEN] || !tb[TCA_BPF_OPS] || !tb[TCA_BPF_CLASSID]) - return -EINVAL; - - tcf_exts_init(&exts, TCA_BPF_ACT, TCA_BPF_POLICE); - ret = tcf_exts_validate(net, tp, tb, est, &exts, ovr); - if (ret < 0) - return ret; - - classid = nla_get_u32(tb[TCA_BPF_CLASSID]); bpf_num_ops = nla_get_u16(tb[TCA_BPF_OPS_LEN]); - if (bpf_num_ops > BPF_MAXINSNS || bpf_num_ops == 0) { - ret = -EINVAL; - goto errout; - } + if (bpf_num_ops > BPF_MAXINSNS || bpf_num_ops == 0) + return -EINVAL; bpf_size = bpf_num_ops * sizeof(*bpf_ops); - if (bpf_size != nla_len(tb[TCA_BPF_OPS])) { - ret = -EINVAL; - goto errout; - } + if (bpf_size != nla_len(tb[TCA_BPF_OPS])) + return -EINVAL; bpf_ops = kzalloc(bpf_size, GFP_KERNEL); - if (bpf_ops == NULL) { - ret = -ENOMEM; - goto errout; - } + if (bpf_ops == NULL) + return -ENOMEM; memcpy(bpf_ops, nla_data(tb[TCA_BPF_OPS]), bpf_size); - tmp.len = bpf_num_ops; - tmp.filter = bpf_ops; + fprog_tmp.len = bpf_num_ops; + fprog_tmp.filter = bpf_ops; - ret = bpf_prog_create(&fp, &tmp); - if (ret) - goto errout_free; + ret = bpf_prog_create(&fp, &fprog_tmp); + if (ret < 0) { + kfree(bpf_ops); + return ret; + } - prog->bpf_num_ops = bpf_num_ops; prog->bpf_ops = bpf_ops; + prog->bpf_num_ops = bpf_num_ops; + prog->bpf_name = NULL; + prog->filter = fp; prog->res.classid = classid; + return 0; +} + +static int cls_bpf_prog_from_efd(struct nlattr **tb, + struct cls_bpf_prog *prog, u32 classid) +{ + struct bpf_prog *fp; + char *name = NULL; + u32 bpf_fd; + + bpf_fd = nla_get_u32(tb[TCA_BPF_FD]); + + fp = bpf_prog_get(bpf_fd); + if (IS_ERR(fp)) + return PTR_ERR(fp); + + if (fp->type != BPF_PROG_TYPE_SCHED_CLS) { + bpf_prog_put(fp); + return -EINVAL; + } + + if (tb[TCA_BPF_NAME]) { + name = kmemdup(nla_data(tb[TCA_BPF_NAME]), + nla_len(tb[TCA_BPF_NAME]), + GFP_KERNEL); + if (!name) { + bpf_prog_put(fp); + return -ENOMEM; + } + } + + prog->bpf_ops = NULL; + prog->bpf_fd = bpf_fd; + prog->bpf_name = name; + + prog->filter = fp; + prog->res.classid = classid; + + return 0; +} + +static int cls_bpf_modify_existing(struct net *net, struct tcf_proto *tp, + struct cls_bpf_prog *prog, + unsigned long base, struct nlattr **tb, + struct nlattr *est, bool ovr) +{ + struct tcf_exts exts; + bool is_bpf, is_ebpf; + u32 classid; + int ret; + + is_bpf = tb[TCA_BPF_OPS_LEN] && tb[TCA_BPF_OPS]; + is_ebpf = tb[TCA_BPF_FD]; + + if ((!is_bpf && !is_ebpf) || (is_bpf && is_ebpf) || + !tb[TCA_BPF_CLASSID]) + return -EINVAL; + + tcf_exts_init(&exts, TCA_BPF_ACT, TCA_BPF_POLICE); + ret = tcf_exts_validate(net, tp, tb, est, &exts, ovr); + if (ret < 0) + return ret; + + classid = nla_get_u32(tb[TCA_BPF_CLASSID]); + + ret = is_bpf ? cls_bpf_prog_from_ops(tb, prog, classid) : + cls_bpf_prog_from_efd(tb, prog, classid); + if (ret < 0) { + tcf_exts_destroy(&exts); + return ret; + } + tcf_bind_filter(tp, &prog->res, base); tcf_exts_change(tp, &prog->exts, &exts); return 0; -errout_free: - kfree(bpf_ops); -errout: - tcf_exts_destroy(&exts); - return ret; } static u32 cls_bpf_grab_new_handle(struct tcf_proto *tp, @@ -297,11 +369,43 @@ errout: return ret; } +static int cls_bpf_dump_bpf_info(const struct cls_bpf_prog *prog, + struct sk_buff *skb) +{ + struct nlattr *nla; + + if (nla_put_u16(skb, TCA_BPF_OPS_LEN, prog->bpf_num_ops)) + return -EMSGSIZE; + + nla = nla_reserve(skb, TCA_BPF_OPS, prog->bpf_num_ops * + sizeof(struct sock_filter)); + if (nla == NULL) + return -EMSGSIZE; + + memcpy(nla_data(nla), prog->bpf_ops, nla_len(nla)); + + return 0; +} + +static int cls_bpf_dump_ebpf_info(const struct cls_bpf_prog *prog, + struct sk_buff *skb) +{ + if (nla_put_u32(skb, TCA_BPF_FD, prog->bpf_fd)) + return -EMSGSIZE; + + if (prog->bpf_name && + nla_put_string(skb, TCA_BPF_NAME, prog->bpf_name)) + return -EMSGSIZE; + + return 0; +} + static int cls_bpf_dump(struct net *net, struct tcf_proto *tp, unsigned long fh, struct sk_buff *skb, struct tcmsg *tm) { struct cls_bpf_prog *prog = (struct cls_bpf_prog *) fh; - struct nlattr *nest, *nla; + struct nlattr *nest; + int ret; if (prog == NULL) return skb->len; @@ -314,16 +418,14 @@ static int cls_bpf_dump(struct net *net, struct tcf_proto *tp, unsigned long fh, if (nla_put_u32(skb, TCA_BPF_CLASSID, prog->res.classid)) goto nla_put_failure; - if (nla_put_u16(skb, TCA_BPF_OPS_LEN, prog->bpf_num_ops)) - goto nla_put_failure; - nla = nla_reserve(skb, TCA_BPF_OPS, prog->bpf_num_ops * - sizeof(struct sock_filter)); - if (nla == NULL) + if (cls_bpf_is_ebpf(prog)) + ret = cls_bpf_dump_ebpf_info(prog, skb); + else + ret = cls_bpf_dump_bpf_info(prog, skb); + if (ret) goto nla_put_failure; - memcpy(nla_data(nla), prog->bpf_ops, nla_len(nla)); - if (tcf_exts_dump(skb, &prog->exts) < 0) goto nla_put_failure; -- cgit v1.2.3 From bc63b6f634d91a0b2a7f3ba4f266e55fec369de3 Mon Sep 17 00:00:00 2001 From: Vitaly Kuznetsov Date: Fri, 27 Feb 2015 11:25:52 -0800 Subject: Drivers: hv: vmbus: rename channel work queues All channel work queues are named 'hv_vmbus_ctl', this makes them indistinguishable in ps output and makes it hard to link to the corresponding vmbus device. Rename them to hv_vmbus_ctl/N and make vmbus device names match, e.g. now vmbus_1 device is served by hv_vmbus_ctl/1 work queue. Signed-off-by: Vitaly Kuznetsov Signed-off-by: K. Y. Srinivasan Signed-off-by: Greg Kroah-Hartman --- drivers/hv/channel_mgmt.c | 5 ++++- drivers/hv/vmbus_drv.c | 6 ++---- include/linux/hyperv.h | 3 +++ 3 files changed, 9 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/drivers/hv/channel_mgmt.c b/drivers/hv/channel_mgmt.c index 3736f71bdec5..ba4b25f0b213 100644 --- a/drivers/hv/channel_mgmt.c +++ b/drivers/hv/channel_mgmt.c @@ -139,19 +139,22 @@ EXPORT_SYMBOL_GPL(vmbus_prep_negotiate_resp); */ static struct vmbus_channel *alloc_channel(void) { + static atomic_t chan_num = ATOMIC_INIT(0); struct vmbus_channel *channel; channel = kzalloc(sizeof(*channel), GFP_ATOMIC); if (!channel) return NULL; + channel->id = atomic_inc_return(&chan_num); spin_lock_init(&channel->inbound_lock); spin_lock_init(&channel->lock); INIT_LIST_HEAD(&channel->sc_list); INIT_LIST_HEAD(&channel->percpu_list); - channel->controlwq = create_workqueue("hv_vmbus_ctl"); + channel->controlwq = alloc_workqueue("hv_vmbus_ctl/%d", WQ_MEM_RECLAIM, + 1, channel->id); if (!channel->controlwq) { kfree(channel); return NULL; diff --git a/drivers/hv/vmbus_drv.c b/drivers/hv/vmbus_drv.c index 3b18a665ef4c..e334ccc70582 100644 --- a/drivers/hv/vmbus_drv.c +++ b/drivers/hv/vmbus_drv.c @@ -875,10 +875,8 @@ int vmbus_device_register(struct hv_device *child_device_obj) { int ret = 0; - static atomic_t device_num = ATOMIC_INIT(0); - - dev_set_name(&child_device_obj->device, "vmbus_0_%d", - atomic_inc_return(&device_num)); + dev_set_name(&child_device_obj->device, "vmbus_%d", + child_device_obj->channel->id); child_device_obj->device.bus = &hv_bus; child_device_obj->device.parent = &hv_acpi_dev->dev; diff --git a/include/linux/hyperv.h b/include/linux/hyperv.h index 5a2ba674795e..26a32b771ee5 100644 --- a/include/linux/hyperv.h +++ b/include/linux/hyperv.h @@ -646,6 +646,9 @@ struct hv_input_signal_event_buffer { }; struct vmbus_channel { + /* Unique channel id */ + int id; + struct list_head listentry; struct hv_device *device_obj; -- cgit v1.2.3 From 04653a009a63d6a91c60a5449ea97e3ed5e1dc29 Mon Sep 17 00:00:00 2001 From: "K. Y. Srinivasan" Date: Fri, 27 Feb 2015 11:26:05 -0800 Subject: Drivers: hv: vmbus: Add support for the NetworkDirect GUID NetworkDirect is a service that supports guest RDMA. Define the GUID for this service. Signed-off-by: K. Y. Srinivasan Signed-off-by: Greg Kroah-Hartman --- drivers/hv/channel_mgmt.c | 2 ++ include/linux/hyperv.h | 10 ++++++++++ 2 files changed, 12 insertions(+) (limited to 'include') diff --git a/drivers/hv/channel_mgmt.c b/drivers/hv/channel_mgmt.c index 6be93f09f976..0ba6b5c303e6 100644 --- a/drivers/hv/channel_mgmt.c +++ b/drivers/hv/channel_mgmt.c @@ -413,6 +413,8 @@ static const struct hv_vmbus_device_id hp_devs[] = { { HV_SCSI_GUID, }, /* Network */ { HV_NIC_GUID, }, + /* NetworkDirect Guest RDMA */ + { HV_ND_GUID, }, }; diff --git a/include/linux/hyperv.h b/include/linux/hyperv.h index 26a32b771ee5..7d976ac01fac 100644 --- a/include/linux/hyperv.h +++ b/include/linux/hyperv.h @@ -1109,6 +1109,16 @@ void vmbus_driver_unregister(struct hv_driver *hv_driver); 0x9A, 0xE7, 0x6B, 0x17, 0x49, 0x77, 0xC1, 0x92 \ } +/* + * NetworkDirect. This is the guest RDMA service. + * {8c2eaf3d-32a7-4b09-ab99-bd1f1c86b501} + */ +#define HV_ND_GUID \ + .guid = { \ + 0x3d, 0xaf, 0x2e, 0x8c, 0xa7, 0x32, 0x09, 0x4b, \ + 0xab, 0x99, 0xbd, 0x1f, 0x1c, 0x86, 0xb5, 0x01 \ + } + /* * Common header for Hyper-V ICs */ -- cgit v1.2.3 From ed6cfcc5fdf2ebca320b6f74c836e555e18216e1 Mon Sep 17 00:00:00 2001 From: "K. Y. Srinivasan" Date: Sat, 28 Feb 2015 11:18:17 -0800 Subject: Drivers: hv: vmbus: Introduce a function to remove a rescinded offer In response to a rescind message, we need to remove the channel and the corresponding device. Cleanup this code path by factoring out the code to remove a channel. Signed-off-by: K. Y. Srinivasan Signed-off-by: Greg Kroah-Hartman --- drivers/hv/channel.c | 9 +++++++++ drivers/hv/channel_mgmt.c | 49 +++++++++++++++++++++++++++++------------------ drivers/hv/vmbus_drv.c | 11 ++++++++++- include/linux/hyperv.h | 1 + 4 files changed, 50 insertions(+), 20 deletions(-) (limited to 'include') diff --git a/drivers/hv/channel.c b/drivers/hv/channel.c index bf0cf8f3bcaf..9b79aca7e565 100644 --- a/drivers/hv/channel.c +++ b/drivers/hv/channel.c @@ -501,6 +501,15 @@ static int vmbus_close_internal(struct vmbus_channel *channel) put_cpu(); } + /* + * If the channel has been rescinded; process device removal. + */ + if (channel->rescind) { + hv_process_channel_removal(channel, + channel->offermsg.child_relid); + return 0; + } + /* Send a closing message */ msg = &channel->close_msg.msg; diff --git a/drivers/hv/channel_mgmt.c b/drivers/hv/channel_mgmt.c index 0ba6b5c303e6..b93389124ec4 100644 --- a/drivers/hv/channel_mgmt.c +++ b/drivers/hv/channel_mgmt.c @@ -207,33 +207,21 @@ static void percpu_channel_deq(void *arg) list_del(&channel->percpu_list); } -/* - * vmbus_process_rescind_offer - - * Rescind the offer by initiating a device removal - */ -static void vmbus_process_rescind_offer(struct work_struct *work) + +void hv_process_channel_removal(struct vmbus_channel *channel, u32 relid) { - struct vmbus_channel *channel = container_of(work, - struct vmbus_channel, - work); + struct vmbus_channel_relid_released msg; unsigned long flags; struct vmbus_channel *primary_channel; - struct vmbus_channel_relid_released msg; - struct device *dev; - - if (channel->device_obj) { - dev = get_device(&channel->device_obj->device); - if (dev) { - vmbus_device_unregister(channel->device_obj); - put_device(dev); - } - } memset(&msg, 0, sizeof(struct vmbus_channel_relid_released)); - msg.child_relid = channel->offermsg.child_relid; + msg.child_relid = relid; msg.header.msgtype = CHANNELMSG_RELID_RELEASED; vmbus_post_msg(&msg, sizeof(struct vmbus_channel_relid_released)); + if (channel == NULL) + return; + if (channel->target_cpu != get_cpu()) { put_cpu(); smp_call_function_single(channel->target_cpu, @@ -256,6 +244,29 @@ static void vmbus_process_rescind_offer(struct work_struct *work) free_channel(channel); } +/* + * vmbus_process_rescind_offer - + * Rescind the offer by initiating a device removal + */ +static void vmbus_process_rescind_offer(struct work_struct *work) +{ + struct vmbus_channel *channel = container_of(work, + struct vmbus_channel, + work); + struct device *dev; + + if (channel->device_obj) { + dev = get_device(&channel->device_obj->device); + if (dev) { + vmbus_device_unregister(channel->device_obj); + put_device(dev); + } + } else { + hv_process_channel_removal(channel, + channel->offermsg.child_relid); + } +} + void vmbus_free_channels(void) { struct vmbus_channel *channel; diff --git a/drivers/hv/vmbus_drv.c b/drivers/hv/vmbus_drv.c index a12666d075ea..2b7b51d264f1 100644 --- a/drivers/hv/vmbus_drv.c +++ b/drivers/hv/vmbus_drv.c @@ -510,14 +510,23 @@ static int vmbus_remove(struct device *child_device) { struct hv_driver *drv; struct hv_device *dev = device_to_hv_device(child_device); + u32 relid = dev->channel->offermsg.child_relid; if (child_device->driver) { drv = drv_to_hv_drv(child_device->driver); if (drv->remove) drv->remove(dev); - else + else { + hv_process_channel_removal(dev->channel, relid); pr_err("remove not set for driver %s\n", dev_name(child_device)); + } + } else { + /* + * We don't have a driver for this device; deal with the + * rescind message by removing the channel. + */ + hv_process_channel_removal(dev->channel, relid); } return 0; diff --git a/include/linux/hyperv.h b/include/linux/hyperv.h index 7d976ac01fac..dd92a854c700 100644 --- a/include/linux/hyperv.h +++ b/include/linux/hyperv.h @@ -1226,6 +1226,7 @@ void hv_kvp_onchannelcallback(void *); int hv_vss_init(struct hv_util_service *); void hv_vss_deinit(void); void hv_vss_onchannelcallback(void *); +void hv_process_channel_removal(struct vmbus_channel *channel, u32 relid); extern struct resource hyperv_mmio; -- cgit v1.2.3 From a13e8bbe851a96a0e78c2bd599bc34082fa697cd Mon Sep 17 00:00:00 2001 From: "K. Y. Srinivasan" Date: Sat, 28 Feb 2015 11:39:02 -0800 Subject: Drivers: hv: vmbus: Use a round-robin algorithm for picking the outgoing channel The current algorithm for picking an outgoing channel was not distributing the load well. Implement a simple round-robin scheme to ensure good distribution of the outgoing traffic. Signed-off-by: K. Y. Srinivasan Reviewed-by: Long Li Signed-off-by: Greg Kroah-Hartman --- drivers/hv/channel_mgmt.c | 30 +++++++++++++++--------------- include/linux/hyperv.h | 3 +++ 2 files changed, 18 insertions(+), 15 deletions(-) (limited to 'include') diff --git a/drivers/hv/channel_mgmt.c b/drivers/hv/channel_mgmt.c index b1e5a5fdaf7f..611789139f9b 100644 --- a/drivers/hv/channel_mgmt.c +++ b/drivers/hv/channel_mgmt.c @@ -350,6 +350,7 @@ static void vmbus_process_offer(struct vmbus_channel *newchannel) } newchannel->state = CHANNEL_OPEN_STATE; + channel->num_sc++; if (channel->sc_creation_callback != NULL) /* * We need to invoke the sub-channel creation @@ -862,9 +863,8 @@ cleanup: /* * Retrieve the (sub) channel on which to send an outgoing request. - * When a primary channel has multiple sub-channels, we choose a - * channel whose VCPU binding is closest to the VCPU on which - * this call is being made. + * When a primary channel has multiple sub-channels, we try to + * distribute the load equally amongst all available channels. */ struct vmbus_channel *vmbus_get_outgoing_channel(struct vmbus_channel *primary) { @@ -872,11 +872,19 @@ struct vmbus_channel *vmbus_get_outgoing_channel(struct vmbus_channel *primary) int cur_cpu; struct vmbus_channel *cur_channel; struct vmbus_channel *outgoing_channel = primary; - int cpu_distance, new_cpu_distance; + int next_channel; + int i = 1; if (list_empty(&primary->sc_list)) return outgoing_channel; + next_channel = primary->next_oc++; + + if (next_channel > (primary->num_sc)) { + primary->next_oc = 0; + return outgoing_channel; + } + cur_cpu = hv_context.vp_index[get_cpu()]; put_cpu(); list_for_each_safe(cur, tmp, &primary->sc_list) { @@ -887,18 +895,10 @@ struct vmbus_channel *vmbus_get_outgoing_channel(struct vmbus_channel *primary) if (cur_channel->target_vp == cur_cpu) return cur_channel; - cpu_distance = ((outgoing_channel->target_vp > cur_cpu) ? - (outgoing_channel->target_vp - cur_cpu) : - (cur_cpu - outgoing_channel->target_vp)); - - new_cpu_distance = ((cur_channel->target_vp > cur_cpu) ? - (cur_channel->target_vp - cur_cpu) : - (cur_cpu - cur_channel->target_vp)); - - if (cpu_distance < new_cpu_distance) - continue; + if (i == next_channel) + return cur_channel; - outgoing_channel = cur_channel; + i++; } return outgoing_channel; diff --git a/include/linux/hyperv.h b/include/linux/hyperv.h index dd92a854c700..1ca582457076 100644 --- a/include/linux/hyperv.h +++ b/include/linux/hyperv.h @@ -761,6 +761,9 @@ struct vmbus_channel { * link up channels based on their CPU affinity. */ struct list_head percpu_list; + + int num_sc; + int next_oc; }; static inline void set_channel_read_state(struct vmbus_channel *c, bool state) -- cgit v1.2.3 From 87e93d61708fe2c44875d1ecdb174aad070dbd08 Mon Sep 17 00:00:00 2001 From: "K. Y. Srinivasan" Date: Sat, 28 Feb 2015 11:39:03 -0800 Subject: Drivers: hv: vmbus: Suport an API to send pagebuffers with additional control Implement an API for sending pagebuffers that gives more control to the client in terms of setting the vmbus flags as well as deciding when to notify the host. This will be useful for enabling batch processing. Signed-off-by: K. Y. Srinivasan Signed-off-by: Greg Kroah-Hartman --- drivers/hv/channel.c | 33 +++++++++++++++++++++++++++------ include/linux/hyperv.h | 9 +++++++++ 2 files changed, 36 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/drivers/hv/channel.c b/drivers/hv/channel.c index 9b79aca7e565..f060d1f7bc99 100644 --- a/drivers/hv/channel.c +++ b/drivers/hv/channel.c @@ -636,13 +636,18 @@ int vmbus_sendpacket(struct vmbus_channel *channel, void *buffer, EXPORT_SYMBOL(vmbus_sendpacket); /* - * vmbus_sendpacket_pagebuffer - Send a range of single-page buffer - * packets using a GPADL Direct packet type. + * vmbus_sendpacket_pagebuffer_ctl - Send a range of single-page buffer + * packets using a GPADL Direct packet type. This interface allows you + * to control notifying the host. This will be useful for sending + * batched data. Also the sender can control the send flags + * explicitly. */ -int vmbus_sendpacket_pagebuffer(struct vmbus_channel *channel, +int vmbus_sendpacket_pagebuffer_ctl(struct vmbus_channel *channel, struct hv_page_buffer pagebuffers[], u32 pagecount, void *buffer, u32 bufferlen, - u64 requestid) + u64 requestid, + u32 flags, + bool kick_q) { int ret; int i; @@ -670,7 +675,7 @@ int vmbus_sendpacket_pagebuffer(struct vmbus_channel *channel, /* Setup the descriptor */ desc.type = VM_PKT_DATA_USING_GPA_DIRECT; - desc.flags = VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED; + desc.flags = flags; desc.dataoffset8 = descsize >> 3; /* in 8-bytes grandularity */ desc.length8 = (u16)(packetlen_aligned >> 3); desc.transactionid = requestid; @@ -691,11 +696,27 @@ int vmbus_sendpacket_pagebuffer(struct vmbus_channel *channel, ret = hv_ringbuffer_write(&channel->outbound, bufferlist, 3, &signal); - if (ret == 0 && signal) + if ((ret == 0) && kick_q && signal) vmbus_setevent(channel); return ret; } + +/* + * vmbus_sendpacket_pagebuffer - Send a range of single-page buffer + * packets using a GPADL Direct packet type. + */ +int vmbus_sendpacket_pagebuffer(struct vmbus_channel *channel, + struct hv_page_buffer pagebuffers[], + u32 pagecount, void *buffer, u32 bufferlen, + u64 requestid) +{ + u32 flags = VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED; + return vmbus_sendpacket_pagebuffer_ctl(channel, pagebuffers, pagecount, + buffer, bufferlen, requestid, + flags, true); + +} EXPORT_SYMBOL_GPL(vmbus_sendpacket_pagebuffer); /* diff --git a/include/linux/hyperv.h b/include/linux/hyperv.h index 1ca582457076..86e1a7a46af3 100644 --- a/include/linux/hyperv.h +++ b/include/linux/hyperv.h @@ -874,6 +874,15 @@ extern int vmbus_sendpacket_pagebuffer(struct vmbus_channel *channel, u32 bufferlen, u64 requestid); +extern int vmbus_sendpacket_pagebuffer_ctl(struct vmbus_channel *channel, + struct hv_page_buffer pagebuffers[], + u32 pagecount, + void *buffer, + u32 bufferlen, + u64 requestid, + u32 flags, + bool kick_q); + extern int vmbus_sendpacket_multipagebuffer(struct vmbus_channel *channel, struct hv_multipage_buffer *mpb, void *buffer, -- cgit v1.2.3 From e9395e3f8952110bda60b54ad03ec52c6e9c7dbd Mon Sep 17 00:00:00 2001 From: "K. Y. Srinivasan" Date: Sat, 28 Feb 2015 11:39:04 -0800 Subject: Drivers: hv: vmbus: Suport an API to send packet with additional control Implement an API that gives additional control on the what VMBUS flags will be set as well as if the host needs to be signalled. This API will be useful for clients that want to batch up requests to the host. Signed-off-by: K. Y. Srinivasan Signed-off-by: Greg Kroah-Hartman --- drivers/hv/channel.c | 43 ++++++++++++++++++++++++++----------------- include/linux/hyperv.h | 8 ++++++++ 2 files changed, 34 insertions(+), 17 deletions(-) (limited to 'include') diff --git a/drivers/hv/channel.c b/drivers/hv/channel.c index f060d1f7bc99..da53180f5eb4 100644 --- a/drivers/hv/channel.c +++ b/drivers/hv/channel.c @@ -584,23 +584,9 @@ void vmbus_close(struct vmbus_channel *channel) } EXPORT_SYMBOL_GPL(vmbus_close); -/** - * vmbus_sendpacket() - Send the specified buffer on the given channel - * @channel: Pointer to vmbus_channel structure. - * @buffer: Pointer to the buffer you want to receive the data into. - * @bufferlen: Maximum size of what the the buffer will hold - * @requestid: Identifier of the request - * @type: Type of packet that is being send e.g. negotiate, time - * packet etc. - * - * Sends data in @buffer directly to hyper-v via the vmbus - * This will send the data unparsed to hyper-v. - * - * Mainly used by Hyper-V drivers. - */ -int vmbus_sendpacket(struct vmbus_channel *channel, void *buffer, +int vmbus_sendpacket_ctl(struct vmbus_channel *channel, void *buffer, u32 bufferlen, u64 requestid, - enum vmbus_packet_type type, u32 flags) + enum vmbus_packet_type type, u32 flags, bool kick_q) { struct vmpacket_descriptor desc; u32 packetlen = sizeof(struct vmpacket_descriptor) + bufferlen; @@ -628,11 +614,34 @@ int vmbus_sendpacket(struct vmbus_channel *channel, void *buffer, ret = hv_ringbuffer_write(&channel->outbound, bufferlist, 3, &signal); - if (ret == 0 && signal) + if ((ret == 0) && kick_q && signal) vmbus_setevent(channel); return ret; } +EXPORT_SYMBOL(vmbus_sendpacket_ctl); + +/** + * vmbus_sendpacket() - Send the specified buffer on the given channel + * @channel: Pointer to vmbus_channel structure. + * @buffer: Pointer to the buffer you want to receive the data into. + * @bufferlen: Maximum size of what the the buffer will hold + * @requestid: Identifier of the request + * @type: Type of packet that is being send e.g. negotiate, time + * packet etc. + * + * Sends data in @buffer directly to hyper-v via the vmbus + * This will send the data unparsed to hyper-v. + * + * Mainly used by Hyper-V drivers. + */ +int vmbus_sendpacket(struct vmbus_channel *channel, void *buffer, + u32 bufferlen, u64 requestid, + enum vmbus_packet_type type, u32 flags) +{ + return vmbus_sendpacket_ctl(channel, buffer, bufferlen, requestid, + type, flags, true); +} EXPORT_SYMBOL(vmbus_sendpacket); /* diff --git a/include/linux/hyperv.h b/include/linux/hyperv.h index 86e1a7a46af3..80e444bfc9dc 100644 --- a/include/linux/hyperv.h +++ b/include/linux/hyperv.h @@ -867,6 +867,14 @@ extern int vmbus_sendpacket(struct vmbus_channel *channel, enum vmbus_packet_type type, u32 flags); +extern int vmbus_sendpacket_ctl(struct vmbus_channel *channel, + void *buffer, + u32 bufferLen, + u64 requestid, + enum vmbus_packet_type type, + u32 flags, + bool kick_q); + extern int vmbus_sendpacket_pagebuffer(struct vmbus_channel *channel, struct hv_page_buffer pagebuffers[], u32 pagecount, -- cgit v1.2.3 From 287f3a943fef58c5c73e42545169443be379222f Mon Sep 17 00:00:00 2001 From: Simon Farnsworth Date: Sun, 1 Mar 2015 10:54:39 +0000 Subject: pppoe: Use workqueue to die properly when a PADT is received When a PADT frame is received, the socket may not be in a good state to close down the PPP interface. The current implementation handles this by simply blocking all further PPP traffic, and hoping that the lack of traffic will trigger the user to investigate. Use schedule_work to get to a process context from which we clear down the PPP interface, in a fashion analogous to hangup on a TTY-based PPP interface. This causes pppd to disconnect immediately, and allows tools to take immediate corrective action. Note that pppd's rp_pppoe.so plugin has code in it to disable the session when it disconnects; however, as a consequence of this patch, the session is already disabled before rp_pppoe.so is asked to disable the session. The result is a harmless error message: Failed to disconnect PPPoE socket: 114 Operation already in progress This message is safe to ignore, as long as the error is 114 Operation already in progress; in that specific case, it means that the PPPoE session has already been disabled before pppd tried to disable it. Signed-off-by: Simon Farnsworth Tested-by: Dan Williams Tested-by: Christoph Schulz Signed-off-by: David S. Miller --- drivers/net/ppp/pppoe.c | 17 ++++++++++++++++- include/linux/if_pppox.h | 2 ++ 2 files changed, 18 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/drivers/net/ppp/pppoe.c b/drivers/net/ppp/pppoe.c index d2408a5e43a6..9c97e9bcf5f5 100644 --- a/drivers/net/ppp/pppoe.c +++ b/drivers/net/ppp/pppoe.c @@ -455,6 +455,18 @@ out: return NET_RX_DROP; } +static void pppoe_unbind_sock_work(struct work_struct *work) +{ + struct pppox_sock *po = container_of(work, struct pppox_sock, + proto.pppoe.padt_work); + struct sock *sk = sk_pppox(po); + + lock_sock(sk); + pppox_unbind_sock(sk); + release_sock(sk); + sock_put(sk); +} + /************************************************************************ * * Receive a PPPoE Discovery frame. @@ -500,7 +512,8 @@ static int pppoe_disc_rcv(struct sk_buff *skb, struct net_device *dev, } bh_unlock_sock(sk); - sock_put(sk); + if (!schedule_work(&po->proto.pppoe.padt_work)) + sock_put(sk); } abort: @@ -613,6 +626,8 @@ static int pppoe_connect(struct socket *sock, struct sockaddr *uservaddr, lock_sock(sk); + INIT_WORK(&po->proto.pppoe.padt_work, pppoe_unbind_sock_work); + error = -EINVAL; if (sp->sa_protocol != PX_PROTO_OE) goto end; diff --git a/include/linux/if_pppox.h b/include/linux/if_pppox.h index aff7ad8a4ea3..66a7d7600f43 100644 --- a/include/linux/if_pppox.h +++ b/include/linux/if_pppox.h @@ -19,6 +19,7 @@ #include #include #include +#include #include static inline struct pppoe_hdr *pppoe_hdr(const struct sk_buff *skb) @@ -32,6 +33,7 @@ struct pppoe_opt { struct pppoe_addr pa; /* what this socket is bound to*/ struct sockaddr_pppox relay; /* what socket data will be relayed to (PPPoE relaying) */ + struct work_struct padt_work;/* Work item for handling PADT */ }; struct pptp_opt { -- cgit v1.2.3 From 49a6fe055739a77ef910c283efb99928423c97a0 Mon Sep 17 00:00:00 2001 From: Eyal Birger Date: Sun, 1 Mar 2015 14:58:25 +0200 Subject: net: bluetooth: compact struct bt_skb_cb by inlining struct hci_req_ctrl struct hci_req_ctrl is never used outside of struct bt_skb_cb; Inlining it frees 8 bytes on a 64 bit system in skb->cb[] allowing the addition of more ancillary data. Signed-off-by: Eyal Birger Reviewed-by: Shmulik Ladkani Signed-off-by: David S. Miller --- include/net/bluetooth/bluetooth.h | 10 +++------- net/bluetooth/hci_core.c | 12 ++++++------ net/bluetooth/hci_event.c | 4 ++-- net/bluetooth/hci_request.c | 6 +++--- net/bluetooth/hci_sock.c | 2 +- 5 files changed, 15 insertions(+), 19 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/bluetooth.h b/include/net/bluetooth/bluetooth.h index e00455aab18c..09893668e6ea 100644 --- a/include/net/bluetooth/bluetooth.h +++ b/include/net/bluetooth/bluetooth.h @@ -275,21 +275,17 @@ struct hci_dev; typedef void (*hci_req_complete_t)(struct hci_dev *hdev, u8 status, u16 opcode); -struct hci_req_ctrl { - bool start; - u8 event; - hci_req_complete_t complete; -}; - struct bt_skb_cb { __u8 pkt_type; __u8 incoming; __u16 opcode; __u16 expect; __u8 force_active; + bool req_start; + u8 req_event; + hci_req_complete_t req_complete; struct l2cap_chan *chan; struct l2cap_ctrl control; - struct hci_req_ctrl req; bdaddr_t bdaddr; __le16 psm; }; diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 3322d3f4c85a..85a0655c4123 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -3517,7 +3517,7 @@ int hci_send_cmd(struct hci_dev *hdev, __u16 opcode, __u32 plen, /* Stand-alone HCI commands must be flagged as * single-command requests. */ - bt_cb(skb)->req.start = true; + bt_cb(skb)->req_start = true; skb_queue_tail(&hdev->cmd_q, skb); queue_work(hdev->workqueue, &hdev->cmd_work); @@ -4195,7 +4195,7 @@ static bool hci_req_is_complete(struct hci_dev *hdev) if (!skb) return true; - return bt_cb(skb)->req.start; + return bt_cb(skb)->req_start; } static void hci_resend_last(struct hci_dev *hdev) @@ -4255,14 +4255,14 @@ void hci_req_cmd_complete(struct hci_dev *hdev, u16 opcode, u8 status) * command queue (hdev->cmd_q). */ if (hdev->sent_cmd) { - req_complete = bt_cb(hdev->sent_cmd)->req.complete; + req_complete = bt_cb(hdev->sent_cmd)->req_complete; if (req_complete) { /* We must set the complete callback to NULL to * avoid calling the callback more than once if * this function gets called again. */ - bt_cb(hdev->sent_cmd)->req.complete = NULL; + bt_cb(hdev->sent_cmd)->req_complete = NULL; goto call_complete; } @@ -4271,12 +4271,12 @@ void hci_req_cmd_complete(struct hci_dev *hdev, u16 opcode, u8 status) /* Remove all pending commands belonging to this request */ spin_lock_irqsave(&hdev->cmd_q.lock, flags); while ((skb = __skb_dequeue(&hdev->cmd_q))) { - if (bt_cb(skb)->req.start) { + if (bt_cb(skb)->req_start) { __skb_queue_head(&hdev->cmd_q, skb); break; } - req_complete = bt_cb(skb)->req.complete; + req_complete = bt_cb(skb)->req_complete; kfree_skb(skb); } spin_unlock_irqrestore(&hdev->cmd_q.lock, flags); diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index a3fb094822b6..8e8c4334c379 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -3106,7 +3106,7 @@ static void hci_cmd_status_evt(struct hci_dev *hdev, struct sk_buff *skb) cancel_delayed_work(&hdev->cmd_timer); if (ev->status || - (hdev->sent_cmd && !bt_cb(hdev->sent_cmd)->req.event)) + (hdev->sent_cmd && !bt_cb(hdev->sent_cmd)->req_event)) hci_req_cmd_complete(hdev, opcode, ev->status); if (ev->ncmd && !test_bit(HCI_RESET, &hdev->flags)) { @@ -5039,7 +5039,7 @@ void hci_event_packet(struct hci_dev *hdev, struct sk_buff *skb) skb_pull(skb, HCI_EVENT_HDR_SIZE); - if (hdev->sent_cmd && bt_cb(hdev->sent_cmd)->req.event == event) { + if (hdev->sent_cmd && bt_cb(hdev->sent_cmd)->req_event == event) { struct hci_command_hdr *cmd_hdr = (void *) hdev->sent_cmd->data; u16 opcode = __le16_to_cpu(cmd_hdr->opcode); diff --git a/net/bluetooth/hci_request.c b/net/bluetooth/hci_request.c index b59f92c6df0c..db2f45a516e9 100644 --- a/net/bluetooth/hci_request.c +++ b/net/bluetooth/hci_request.c @@ -55,7 +55,7 @@ int hci_req_run(struct hci_request *req, hci_req_complete_t complete) return -ENODATA; skb = skb_peek_tail(&req->cmd_q); - bt_cb(skb)->req.complete = complete; + bt_cb(skb)->req_complete = complete; spin_lock_irqsave(&hdev->cmd_q.lock, flags); skb_queue_splice_tail(&req->cmd_q, &hdev->cmd_q); @@ -116,9 +116,9 @@ void hci_req_add_ev(struct hci_request *req, u16 opcode, u32 plen, } if (skb_queue_empty(&req->cmd_q)) - bt_cb(skb)->req.start = true; + bt_cb(skb)->req_start = true; - bt_cb(skb)->req.event = event; + bt_cb(skb)->req_event = event; skb_queue_tail(&req->cmd_q, skb); } diff --git a/net/bluetooth/hci_sock.c b/net/bluetooth/hci_sock.c index 1d65c5be7c82..f0038189a73e 100644 --- a/net/bluetooth/hci_sock.c +++ b/net/bluetooth/hci_sock.c @@ -965,7 +965,7 @@ static int hci_sock_sendmsg(struct kiocb *iocb, struct socket *sock, /* Stand-alone HCI commands must be flagged as * single-command requests. */ - bt_cb(skb)->req.start = true; + bt_cb(skb)->req_start = true; skb_queue_tail(&hdev->cmd_q, skb); queue_work(hdev->workqueue, &hdev->cmd_work); -- cgit v1.2.3 From 6368c235777456ddaeaa61360186d8d03a61cb18 Mon Sep 17 00:00:00 2001 From: Eyal Birger Date: Sun, 1 Mar 2015 14:58:26 +0200 Subject: net: bluetooth: compact struct bt_skb_cb by converting boolean fields to bit fields Convert boolean fields incoming and req_start to bit fields and move force_active in order save space in bt_skb_cb in an effort to use a portion of skb->cb[] for storing skb->dropcount. Signed-off-by: Eyal Birger Signed-off-by: David S. Miller --- include/net/bluetooth/bluetooth.h | 6 +++--- net/bluetooth/hci_core.c | 2 +- net/bluetooth/hci_request.c | 2 +- net/bluetooth/hci_sock.c | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/bluetooth.h b/include/net/bluetooth/bluetooth.h index 09893668e6ea..4500bf88ff55 100644 --- a/include/net/bluetooth/bluetooth.h +++ b/include/net/bluetooth/bluetooth.h @@ -277,11 +277,11 @@ typedef void (*hci_req_complete_t)(struct hci_dev *hdev, u8 status, u16 opcode); struct bt_skb_cb { __u8 pkt_type; - __u8 incoming; + __u8 force_active; __u16 opcode; __u16 expect; - __u8 force_active; - bool req_start; + __u8 incoming:1; + __u8 req_start:1; u8 req_event; hci_req_complete_t req_complete; struct l2cap_chan *chan; diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 85a0655c4123..80f40e859d7d 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -3517,7 +3517,7 @@ int hci_send_cmd(struct hci_dev *hdev, __u16 opcode, __u32 plen, /* Stand-alone HCI commands must be flagged as * single-command requests. */ - bt_cb(skb)->req_start = true; + bt_cb(skb)->req_start = 1; skb_queue_tail(&hdev->cmd_q, skb); queue_work(hdev->workqueue, &hdev->cmd_work); diff --git a/net/bluetooth/hci_request.c b/net/bluetooth/hci_request.c index db2f45a516e9..f857e765e081 100644 --- a/net/bluetooth/hci_request.c +++ b/net/bluetooth/hci_request.c @@ -116,7 +116,7 @@ void hci_req_add_ev(struct hci_request *req, u16 opcode, u32 plen, } if (skb_queue_empty(&req->cmd_q)) - bt_cb(skb)->req_start = true; + bt_cb(skb)->req_start = 1; bt_cb(skb)->req_event = event; diff --git a/net/bluetooth/hci_sock.c b/net/bluetooth/hci_sock.c index f0038189a73e..37198fb99ffe 100644 --- a/net/bluetooth/hci_sock.c +++ b/net/bluetooth/hci_sock.c @@ -965,7 +965,7 @@ static int hci_sock_sendmsg(struct kiocb *iocb, struct socket *sock, /* Stand-alone HCI commands must be flagged as * single-command requests. */ - bt_cb(skb)->req_start = true; + bt_cb(skb)->req_start = 1; skb_queue_tail(&hdev->cmd_q, skb); queue_work(hdev->workqueue, &hdev->cmd_work); -- cgit v1.2.3 From b4772ef879a8f7d8c56118c2ae5a296fcf6f81d2 Mon Sep 17 00:00:00 2001 From: Eyal Birger Date: Sun, 1 Mar 2015 14:58:29 +0200 Subject: net: use common macro for assering skb->cb[] available size in protocol families As part of an effort to move skb->dropcount to skb->cb[] use a common macro in protocol families using skb->cb[] for ancillary data to validate available room in skb->cb[]. Signed-off-by: Eyal Birger Signed-off-by: David S. Miller --- include/net/sock.h | 3 +++ net/bluetooth/af_bluetooth.c | 3 +-- net/can/bcm.c | 2 +- net/can/raw.c | 6 +++--- net/ipv4/af_inet.c | 2 +- net/ipv4/tcp.c | 3 +-- net/ipv6/af_inet6.c | 2 +- net/packet/af_packet.c | 3 +-- net/sctp/protocol.c | 3 +-- 9 files changed, 13 insertions(+), 14 deletions(-) (limited to 'include') diff --git a/include/net/sock.h b/include/net/sock.h index ab186b1d31ff..a2502d248641 100644 --- a/include/net/sock.h +++ b/include/net/sock.h @@ -2078,6 +2078,9 @@ static inline int sock_intr_errno(long timeo) return timeo == MAX_SCHEDULE_TIMEOUT ? -ERESTARTSYS : -EINTR; } +#define sock_skb_cb_check_size(size) \ + BUILD_BUG_ON((size) > FIELD_SIZEOF(struct sk_buff, cb)) + void __sock_recv_timestamp(struct msghdr *msg, struct sock *sk, struct sk_buff *skb); void __sock_recv_wifi_status(struct msghdr *msg, struct sock *sk, diff --git a/net/bluetooth/af_bluetooth.c b/net/bluetooth/af_bluetooth.c index ce22e0cfa923..4b904c97a068 100644 --- a/net/bluetooth/af_bluetooth.c +++ b/net/bluetooth/af_bluetooth.c @@ -711,10 +711,9 @@ EXPORT_SYMBOL_GPL(bt_debugfs); static int __init bt_init(void) { - struct sk_buff *skb; int err; - BUILD_BUG_ON(sizeof(struct bt_skb_cb) > sizeof(skb->cb)); + sock_skb_cb_check_size(sizeof(struct bt_skb_cb)); BT_INFO("Core ver %s", VERSION); diff --git a/net/can/bcm.c b/net/can/bcm.c index ee9ffd956552..d559f922326d 100644 --- a/net/can/bcm.c +++ b/net/can/bcm.c @@ -328,7 +328,7 @@ static void bcm_send_to_user(struct bcm_op *op, struct bcm_msg_head *head, * containing the interface index. */ - BUILD_BUG_ON(sizeof(skb->cb) < sizeof(struct sockaddr_can)); + sock_skb_cb_check_size(sizeof(struct sockaddr_can)); addr = (struct sockaddr_can *)skb->cb; memset(addr, 0, sizeof(*addr)); addr->can_family = AF_CAN; diff --git a/net/can/raw.c b/net/can/raw.c index 00c13ef23661..94601b7ff0a3 100644 --- a/net/can/raw.c +++ b/net/can/raw.c @@ -95,8 +95,8 @@ struct raw_sock { */ static inline unsigned int *raw_flags(struct sk_buff *skb) { - BUILD_BUG_ON(sizeof(skb->cb) <= (sizeof(struct sockaddr_can) + - sizeof(unsigned int))); + sock_skb_cb_check_size(sizeof(struct sockaddr_can) + + sizeof(unsigned int)); /* return pointer after struct sockaddr_can */ return (unsigned int *)(&((struct sockaddr_can *)skb->cb)[1]); @@ -135,7 +135,7 @@ static void raw_rcv(struct sk_buff *oskb, void *data) * containing the interface index. */ - BUILD_BUG_ON(sizeof(skb->cb) < sizeof(struct sockaddr_can)); + sock_skb_cb_check_size(sizeof(struct sockaddr_can)); addr = (struct sockaddr_can *)skb->cb; memset(addr, 0, sizeof(*addr)); addr->can_family = AF_CAN; diff --git a/net/ipv4/af_inet.c b/net/ipv4/af_inet.c index d2e49baaff63..4ce954cc94a4 100644 --- a/net/ipv4/af_inet.c +++ b/net/ipv4/af_inet.c @@ -1675,7 +1675,7 @@ static int __init inet_init(void) struct list_head *r; int rc = -EINVAL; - BUILD_BUG_ON(sizeof(struct inet_skb_parm) > FIELD_SIZEOF(struct sk_buff, cb)); + sock_skb_cb_check_size(sizeof(struct inet_skb_parm)); rc = proto_register(&tcp_prot, 1); if (rc) diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index 9d72a0fcd928..4b57ea8dabc7 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -3005,12 +3005,11 @@ static void __init tcp_init_mem(void) void __init tcp_init(void) { - struct sk_buff *skb = NULL; unsigned long limit; int max_rshare, max_wshare, cnt; unsigned int i; - BUILD_BUG_ON(sizeof(struct tcp_skb_cb) > sizeof(skb->cb)); + sock_skb_cb_check_size(sizeof(struct tcp_skb_cb)); percpu_counter_init(&tcp_sockets_allocated, 0, GFP_KERNEL); percpu_counter_init(&tcp_orphan_count, 0, GFP_KERNEL); diff --git a/net/ipv6/af_inet6.c b/net/ipv6/af_inet6.c index e8c4400f23e9..6bafcc2c79e3 100644 --- a/net/ipv6/af_inet6.c +++ b/net/ipv6/af_inet6.c @@ -824,7 +824,7 @@ static int __init inet6_init(void) struct list_head *r; int err = 0; - BUILD_BUG_ON(sizeof(struct inet6_skb_parm) > FIELD_SIZEOF(struct sk_buff, cb)); + sock_skb_cb_check_size(sizeof(struct inet6_skb_parm)); /* Register the socket-side information for inet6_create. */ for (r = &inetsw6[0]; r < &inetsw6[SOCK_MAX]; ++r) diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c index 7eea30b9c8e5..9cdb414cd231 100644 --- a/net/packet/af_packet.c +++ b/net/packet/af_packet.c @@ -1816,8 +1816,7 @@ static int packet_rcv(struct sk_buff *skb, struct net_device *dev, skb = nskb; } - BUILD_BUG_ON(sizeof(*PACKET_SKB_CB(skb)) + MAX_ADDR_LEN - 8 > - sizeof(skb->cb)); + sock_skb_cb_check_size(sizeof(*PACKET_SKB_CB(skb)) + MAX_ADDR_LEN - 8); sll = &PACKET_SKB_CB(skb)->sa.ll; sll->sll_hatype = dev->type; diff --git a/net/sctp/protocol.c b/net/sctp/protocol.c index 8f34b27d5775..53b7acde9aa3 100644 --- a/net/sctp/protocol.c +++ b/net/sctp/protocol.c @@ -1322,8 +1322,7 @@ static __init int sctp_init(void) int max_share; int order; - BUILD_BUG_ON(sizeof(struct sctp_ulpevent) > - sizeof(((struct sk_buff *) 0)->cb)); + sock_skb_cb_check_size(sizeof(struct sctp_ulpevent)); /* Allocate bind_bucket and chunk caches. */ status = -ENOBUFS; -- cgit v1.2.3 From 3bc3b96f3b455bd14a8ccd83ffffc85625aba641 Mon Sep 17 00:00:00 2001 From: Eyal Birger Date: Sun, 1 Mar 2015 14:58:30 +0200 Subject: net: add common accessor for setting dropcount on packets As part of an effort to move skb->dropcount to skb->cb[], use a common function in order to set dropcount in struct sk_buff. Signed-off-by: Eyal Birger Signed-off-by: David S. Miller --- include/net/sock.h | 6 ++++++ net/core/sock.c | 2 +- net/packet/af_packet.c | 2 +- 3 files changed, 8 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/net/sock.h b/include/net/sock.h index a2502d248641..0996fe451e5f 100644 --- a/include/net/sock.h +++ b/include/net/sock.h @@ -2081,6 +2081,12 @@ static inline int sock_intr_errno(long timeo) #define sock_skb_cb_check_size(size) \ BUILD_BUG_ON((size) > FIELD_SIZEOF(struct sk_buff, cb)) +static inline void +sock_skb_set_dropcount(const struct sock *sk, struct sk_buff *skb) +{ + skb->dropcount = atomic_read(&sk->sk_drops); +} + void __sock_recv_timestamp(struct msghdr *msg, struct sock *sk, struct sk_buff *skb); void __sock_recv_wifi_status(struct msghdr *msg, struct sock *sk, diff --git a/net/core/sock.c b/net/core/sock.c index 93c8b20c91e4..9c74fc8f0e32 100644 --- a/net/core/sock.c +++ b/net/core/sock.c @@ -466,7 +466,7 @@ int sock_queue_rcv_skb(struct sock *sk, struct sk_buff *skb) skb_dst_force(skb); spin_lock_irqsave(&list->lock, flags); - skb->dropcount = atomic_read(&sk->sk_drops); + sock_skb_set_dropcount(sk, skb); __skb_queue_tail(list, skb); spin_unlock_irqrestore(&list->lock, flags); diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c index 9cdb414cd231..9db83693d736 100644 --- a/net/packet/af_packet.c +++ b/net/packet/af_packet.c @@ -1845,7 +1845,7 @@ static int packet_rcv(struct sk_buff *skb, struct net_device *dev, spin_lock(&sk->sk_receive_queue.lock); po->stats.stats1.tp_packets++; - skb->dropcount = atomic_read(&sk->sk_drops); + sock_skb_set_dropcount(sk, skb); __skb_queue_tail(&sk->sk_receive_queue, skb); spin_unlock(&sk->sk_receive_queue.lock); sk->sk_data_ready(sk); -- cgit v1.2.3 From 744d5a3e9fe2690dd85d9991dbb078301694658b Mon Sep 17 00:00:00 2001 From: Eyal Birger Date: Sun, 1 Mar 2015 14:58:31 +0200 Subject: net: move skb->dropcount to skb->cb[] Commit 977750076d98 ("af_packet: add interframe drop cmsg (v6)") unionized skb->mark and skb->dropcount in order to allow recording of the socket drop count while maintaining struct sk_buff size. skb->dropcount was introduced since there was no available room in skb->cb[] in packet sockets. However, its introduction led to the inability to export skb->mark, or any other aliased field to userspace if so desired. Moving the dropcount metric to skb->cb[] eliminates this problem at the expense of 4 bytes less in skb->cb[] for protocol families using it. Signed-off-by: Eyal Birger Signed-off-by: David S. Miller --- include/linux/skbuff.h | 2 -- include/net/sock.h | 18 ++++++++++++++++-- net/socket.c | 4 ++-- 3 files changed, 18 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h index d898b32dedcc..bba1330757c0 100644 --- a/include/linux/skbuff.h +++ b/include/linux/skbuff.h @@ -492,7 +492,6 @@ static inline u32 skb_mstamp_us_delta(const struct skb_mstamp *t1, * @napi_id: id of the NAPI struct this skb came from * @secmark: security marking * @mark: Generic packet mark - * @dropcount: total number of sk_receive_queue overflows * @vlan_proto: vlan encapsulation protocol * @vlan_tci: vlan tag control information * @inner_protocol: Protocol (encapsulation) @@ -641,7 +640,6 @@ struct sk_buff { #endif union { __u32 mark; - __u32 dropcount; __u32 reserved_tailroom; }; diff --git a/include/net/sock.h b/include/net/sock.h index 0996fe451e5f..38369d3580a1 100644 --- a/include/net/sock.h +++ b/include/net/sock.h @@ -2078,13 +2078,27 @@ static inline int sock_intr_errno(long timeo) return timeo == MAX_SCHEDULE_TIMEOUT ? -ERESTARTSYS : -EINTR; } +struct sock_skb_cb { + u32 dropcount; +}; + +/* Store sock_skb_cb at the end of skb->cb[] so protocol families + * using skb->cb[] would keep using it directly and utilize its + * alignement guarantee. + */ +#define SOCK_SKB_CB_OFFSET ((FIELD_SIZEOF(struct sk_buff, cb) - \ + sizeof(struct sock_skb_cb))) + +#define SOCK_SKB_CB(__skb) ((struct sock_skb_cb *)((__skb)->cb + \ + SOCK_SKB_CB_OFFSET)) + #define sock_skb_cb_check_size(size) \ - BUILD_BUG_ON((size) > FIELD_SIZEOF(struct sk_buff, cb)) + BUILD_BUG_ON((size) > SOCK_SKB_CB_OFFSET) static inline void sock_skb_set_dropcount(const struct sock *sk, struct sk_buff *skb) { - skb->dropcount = atomic_read(&sk->sk_drops); + SOCK_SKB_CB(skb)->dropcount = atomic_read(&sk->sk_drops); } void __sock_recv_timestamp(struct msghdr *msg, struct sock *sk, diff --git a/net/socket.c b/net/socket.c index bbedbfcb42c2..b78cf601a021 100644 --- a/net/socket.c +++ b/net/socket.c @@ -731,9 +731,9 @@ EXPORT_SYMBOL_GPL(__sock_recv_wifi_status); static inline void sock_recv_drops(struct msghdr *msg, struct sock *sk, struct sk_buff *skb) { - if (sock_flag(sk, SOCK_RXQ_OVFL) && skb && skb->dropcount) + if (sock_flag(sk, SOCK_RXQ_OVFL) && skb && SOCK_SKB_CB(skb)->dropcount) put_cmsg(msg, SOL_SOCKET, SO_RXQ_OVFL, - sizeof(__u32), &skb->dropcount); + sizeof(__u32), &SOCK_SKB_CB(skb)->dropcount); } void __sock_recv_ts_and_drops(struct msghdr *msg, struct sock *sk, -- cgit v1.2.3 From 5f80e19081e233698c8ea77ed2dd84a66f49fc54 Mon Sep 17 00:00:00 2001 From: Liu Ying Date: Thu, 12 Feb 2015 14:01:25 +0800 Subject: ARM: imx6q: Add GPR3 MIPI muxing control register field shift bits definition This patch adds a macro to define the GPR3 MIPI muxing control register field shift bits. Signed-off-by: Liu Ying Signed-off-by: Shawn Guo --- include/linux/mfd/syscon/imx6q-iomuxc-gpr.h | 1 + 1 file changed, 1 insertion(+) (limited to 'include') diff --git a/include/linux/mfd/syscon/imx6q-iomuxc-gpr.h b/include/linux/mfd/syscon/imx6q-iomuxc-gpr.h index c877cad61a13..d16f4c82c568 100644 --- a/include/linux/mfd/syscon/imx6q-iomuxc-gpr.h +++ b/include/linux/mfd/syscon/imx6q-iomuxc-gpr.h @@ -207,6 +207,7 @@ #define IMX6Q_GPR3_LVDS0_MUX_CTL_IPU1_DI1 (0x1 << 6) #define IMX6Q_GPR3_LVDS0_MUX_CTL_IPU2_DI0 (0x2 << 6) #define IMX6Q_GPR3_LVDS0_MUX_CTL_IPU2_DI1 (0x3 << 6) +#define IMX6Q_GPR3_MIPI_MUX_CTL_SHIFT 4 #define IMX6Q_GPR3_MIPI_MUX_CTL_MASK (0x3 << 4) #define IMX6Q_GPR3_MIPI_MUX_CTL_IPU1_DI0 (0x0 << 4) #define IMX6Q_GPR3_MIPI_MUX_CTL_IPU1_DI1 (0x1 << 4) -- cgit v1.2.3 From 8f21d8d42802a68daef5a34daca8b61df96ff8c6 Mon Sep 17 00:00:00 2001 From: Liu Ying Date: Thu, 12 Feb 2015 14:01:26 +0800 Subject: ARM: imx6q: clk: Add the video_27m clock This patch supports the video_27m clock which is a fixed factor clock of the pll3_pfd1_540m clock. Signed-off-by: Liu Ying Signed-off-by: Shawn Guo --- arch/arm/mach-imx/clk-imx6q.c | 1 + include/dt-bindings/clock/imx6qdl-clock.h | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/arch/arm/mach-imx/clk-imx6q.c b/arch/arm/mach-imx/clk-imx6q.c index d04a430607b8..8335bfc14a58 100644 --- a/arch/arm/mach-imx/clk-imx6q.c +++ b/arch/arm/mach-imx/clk-imx6q.c @@ -246,6 +246,7 @@ static void __init imx6q_clocks_init(struct device_node *ccm_node) clk[IMX6QDL_CLK_PLL3_60M] = imx_clk_fixed_factor("pll3_60m", "pll3_usb_otg", 1, 8); clk[IMX6QDL_CLK_TWD] = imx_clk_fixed_factor("twd", "arm", 1, 2); clk[IMX6QDL_CLK_GPT_3M] = imx_clk_fixed_factor("gpt_3m", "osc", 1, 8); + clk[IMX6QDL_CLK_VIDEO_27M] = imx_clk_fixed_factor("video_27m", "pll3_pfd1_540m", 1, 20); if (cpu_is_imx6dl()) { clk[IMX6QDL_CLK_GPU2D_AXI] = imx_clk_fixed_factor("gpu2d_axi", "mmdc_ch0_axi_podf", 1, 1); clk[IMX6QDL_CLK_GPU3D_AXI] = imx_clk_fixed_factor("gpu3d_axi", "mmdc_ch0_axi_podf", 1, 1); diff --git a/include/dt-bindings/clock/imx6qdl-clock.h b/include/dt-bindings/clock/imx6qdl-clock.h index b690cdba163b..25625bf3927b 100644 --- a/include/dt-bindings/clock/imx6qdl-clock.h +++ b/include/dt-bindings/clock/imx6qdl-clock.h @@ -248,6 +248,7 @@ #define IMX6QDL_PLL6_BYPASS 235 #define IMX6QDL_PLL7_BYPASS 236 #define IMX6QDL_CLK_GPT_3M 237 -#define IMX6QDL_CLK_END 238 +#define IMX6QDL_CLK_VIDEO_27M 238 +#define IMX6QDL_CLK_END 239 #endif /* __DT_BINDINGS_CLOCK_IMX6QDL_H */ -- cgit v1.2.3 From 5ccc248cc53708337a2bfe4ea380c20948e8bbed Mon Sep 17 00:00:00 2001 From: Liu Ying Date: Thu, 12 Feb 2015 14:01:29 +0800 Subject: ARM: imx6q: clk: Add support for mipi_core_cfg clock as a shared clock gate The CG8 field of the CCM CCGR3 register is named as 'mipi_core_cfg' clock, according to the i.MX6q/sdl reference manuals. This clock is actually the gate for several clocks, including the hsi_tx_sel clock's output and the video_27m clock's output. The MIPI DSI host controller embedded in the i.MX6q/sdl SoCs uses the video_27m clock to generate PLL reference clock and MIPI core configuration clock. In order to gate/ungate the two MIPI DSI host controller relevant clocks, this patch adds the mipi_core_cfg clock as a shared clock gate. Suggested-by: Philipp Zabel Signed-off-by: Liu Ying Signed-off-by: Shawn Guo --- arch/arm/mach-imx/clk-imx6q.c | 1 + include/dt-bindings/clock/imx6qdl-clock.h | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/arch/arm/mach-imx/clk-imx6q.c b/arch/arm/mach-imx/clk-imx6q.c index 6843ea9707c1..e59e2c6943fd 100644 --- a/arch/arm/mach-imx/clk-imx6q.c +++ b/arch/arm/mach-imx/clk-imx6q.c @@ -418,6 +418,7 @@ static void __init imx6q_clocks_init(struct device_node *ccm_node) clk[IMX6QDL_CLK_LDB_DI1] = imx_clk_gate2("ldb_di1", "ldb_di1_podf", base + 0x74, 14); clk[IMX6QDL_CLK_IPU2_DI1] = imx_clk_gate2("ipu2_di1", "ipu2_di1_sel", base + 0x74, 10); clk[IMX6QDL_CLK_HSI_TX] = imx_clk_gate2_shared("hsi_tx", "hsi_tx_podf", base + 0x74, 16, &share_count_mipi_core_cfg); + clk[IMX6QDL_CLK_MIPI_CORE_CFG] = imx_clk_gate2_shared("mipi_core_cfg", "video_27m", base + 0x74, 16, &share_count_mipi_core_cfg); if (cpu_is_imx6dl()) /* * The multiplexer and divider of the imx6q clock gpu2d get diff --git a/include/dt-bindings/clock/imx6qdl-clock.h b/include/dt-bindings/clock/imx6qdl-clock.h index 25625bf3927b..dbc828c23a17 100644 --- a/include/dt-bindings/clock/imx6qdl-clock.h +++ b/include/dt-bindings/clock/imx6qdl-clock.h @@ -249,6 +249,7 @@ #define IMX6QDL_PLL7_BYPASS 236 #define IMX6QDL_CLK_GPT_3M 237 #define IMX6QDL_CLK_VIDEO_27M 238 -#define IMX6QDL_CLK_END 239 +#define IMX6QDL_CLK_MIPI_CORE_CFG 239 +#define IMX6QDL_CLK_END 240 #endif /* __DT_BINDINGS_CLOCK_IMX6QDL_H */ -- cgit v1.2.3 From e654df7a1a4843429b5d1d6ee40cac9ecef75304 Mon Sep 17 00:00:00 2001 From: Liu Ying Date: Thu, 12 Feb 2015 14:01:30 +0800 Subject: ARM: imx6q: clk: Add support for mipi_ipg clock as a shared clock gate The CG8 field of the CCM CCGR3 register is the 'mipi_core_cfg' gate clock, according to the i.MX6q/sdl reference manuals. This clock is actually the gate for several clocks, including the ipg clock's output. The MIPI DSI host controller embedded in the i.MX6q/sdl SoCs takes the ipg clock as the pclk - the APB clock signal . In order to gate/ungate the ipg clock, this patch adds a new shared clock gate named as "mipi_ipg". Signed-off-by: Liu Ying Signed-off-by: Shawn Guo --- arch/arm/mach-imx/clk-imx6q.c | 1 + include/dt-bindings/clock/imx6qdl-clock.h | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/arch/arm/mach-imx/clk-imx6q.c b/arch/arm/mach-imx/clk-imx6q.c index e59e2c6943fd..469a150bf98f 100644 --- a/arch/arm/mach-imx/clk-imx6q.c +++ b/arch/arm/mach-imx/clk-imx6q.c @@ -419,6 +419,7 @@ static void __init imx6q_clocks_init(struct device_node *ccm_node) clk[IMX6QDL_CLK_IPU2_DI1] = imx_clk_gate2("ipu2_di1", "ipu2_di1_sel", base + 0x74, 10); clk[IMX6QDL_CLK_HSI_TX] = imx_clk_gate2_shared("hsi_tx", "hsi_tx_podf", base + 0x74, 16, &share_count_mipi_core_cfg); clk[IMX6QDL_CLK_MIPI_CORE_CFG] = imx_clk_gate2_shared("mipi_core_cfg", "video_27m", base + 0x74, 16, &share_count_mipi_core_cfg); + clk[IMX6QDL_CLK_MIPI_IPG] = imx_clk_gate2_shared("mipi_ipg", "ipg", base + 0x74, 16, &share_count_mipi_core_cfg); if (cpu_is_imx6dl()) /* * The multiplexer and divider of the imx6q clock gpu2d get diff --git a/include/dt-bindings/clock/imx6qdl-clock.h b/include/dt-bindings/clock/imx6qdl-clock.h index dbc828c23a17..8780868458a0 100644 --- a/include/dt-bindings/clock/imx6qdl-clock.h +++ b/include/dt-bindings/clock/imx6qdl-clock.h @@ -250,6 +250,7 @@ #define IMX6QDL_CLK_GPT_3M 237 #define IMX6QDL_CLK_VIDEO_27M 238 #define IMX6QDL_CLK_MIPI_CORE_CFG 239 -#define IMX6QDL_CLK_END 240 +#define IMX6QDL_CLK_MIPI_IPG 240 +#define IMX6QDL_CLK_END 241 #endif /* __DT_BINDINGS_CLOCK_IMX6QDL_H */ -- cgit v1.2.3 From 4186721d02b71ae943e60bbf50d3488fd5fd6adb Mon Sep 17 00:00:00 2001 From: Rafał Miłecki Date: Sun, 8 Feb 2015 17:11:47 +0100 Subject: bcma: add helpers bringing PCIe hosted bus up / down MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bringing PCIe hosted bus up requires operating on host-related core. Since we plan to support PCIe Gen 2 devices we should provide a helper picking the correct one (PCIE or PCIE2). Signed-off-by: Rafał Miłecki Signed-off-by: Kalle Valo --- drivers/bcma/bcma_private.h | 2 ++ drivers/bcma/driver_pci.c | 20 ++---------------- drivers/bcma/host_pci.c | 28 ++++++++++++++++++++++++++ drivers/net/wireless/b43/main.c | 4 ++-- drivers/net/wireless/brcm80211/brcmsmac/main.c | 8 ++++---- include/linux/bcma/bcma.h | 3 +++ include/linux/bcma/bcma_driver_pci.h | 2 -- 7 files changed, 41 insertions(+), 26 deletions(-) (limited to 'include') diff --git a/drivers/bcma/bcma_private.h b/drivers/bcma/bcma_private.h index ac6c5fca906d..351f4afdad25 100644 --- a/drivers/bcma/bcma_private.h +++ b/drivers/bcma/bcma_private.h @@ -101,6 +101,8 @@ static inline void __exit bcma_host_soc_unregister_driver(void) /* driver_pci.c */ u32 bcma_pcie_read(struct bcma_drv_pci *pc, u32 address); +void bcma_core_pci_up(struct bcma_drv_pci *pc); +void bcma_core_pci_down(struct bcma_drv_pci *pc); extern int bcma_chipco_watchdog_register(struct bcma_drv_cc *cc); diff --git a/drivers/bcma/driver_pci.c b/drivers/bcma/driver_pci.c index 786666488a2d..cf92bfa7eae0 100644 --- a/drivers/bcma/driver_pci.c +++ b/drivers/bcma/driver_pci.c @@ -328,28 +328,12 @@ static void bcma_core_pci_extend_L1timer(struct bcma_drv_pci *pc, bool extend) bcma_pcie_read(pc, BCMA_CORE_PCI_DLLP_PMTHRESHREG); } -void bcma_core_pci_up(struct bcma_bus *bus) +void bcma_core_pci_up(struct bcma_drv_pci *pc) { - struct bcma_drv_pci *pc; - - if (bus->hosttype != BCMA_HOSTTYPE_PCI) - return; - - pc = &bus->drv_pci[0]; - bcma_core_pci_extend_L1timer(pc, true); } -EXPORT_SYMBOL_GPL(bcma_core_pci_up); -void bcma_core_pci_down(struct bcma_bus *bus) +void bcma_core_pci_down(struct bcma_drv_pci *pc) { - struct bcma_drv_pci *pc; - - if (bus->hosttype != BCMA_HOSTTYPE_PCI) - return; - - pc = &bus->drv_pci[0]; - bcma_core_pci_extend_L1timer(pc, false); } -EXPORT_SYMBOL_GPL(bcma_core_pci_down); diff --git a/drivers/bcma/host_pci.c b/drivers/bcma/host_pci.c index 53c6a8a58859..8dd37dc94cae 100644 --- a/drivers/bcma/host_pci.c +++ b/drivers/bcma/host_pci.c @@ -310,3 +310,31 @@ void __exit bcma_host_pci_exit(void) { pci_unregister_driver(&bcma_pci_bridge_driver); } + +/************************************************** + * Runtime ops for drivers. + **************************************************/ + +/* See also pcicore_up */ +void bcma_host_pci_up(struct bcma_bus *bus) +{ + if (bus->hosttype != BCMA_HOSTTYPE_PCI) + return; + + if (bus->host_is_pcie2) + pr_warn("Bringing up bus with PCIe Gen 2 host is unsupported yet\n"); + else + bcma_core_pci_up(&bus->drv_pci[0]); +} +EXPORT_SYMBOL_GPL(bcma_host_pci_up); + +/* See also pcicore_down */ +void bcma_host_pci_down(struct bcma_bus *bus) +{ + if (bus->hosttype != BCMA_HOSTTYPE_PCI) + return; + + if (!bus->host_is_pcie2) + bcma_core_pci_down(&bus->drv_pci[0]); +} +EXPORT_SYMBOL_GPL(bcma_host_pci_down); diff --git a/drivers/net/wireless/b43/main.c b/drivers/net/wireless/b43/main.c index ccbdb05b28cd..34065a6f7725 100644 --- a/drivers/net/wireless/b43/main.c +++ b/drivers/net/wireless/b43/main.c @@ -4819,7 +4819,7 @@ static void b43_wireless_core_exit(struct b43_wldev *dev) switch (dev->dev->bus_type) { #ifdef CONFIG_B43_BCMA case B43_BUS_BCMA: - bcma_core_pci_down(dev->dev->bdev->bus); + bcma_host_pci_down(dev->dev->bdev->bus); break; #endif #ifdef CONFIG_B43_SSB @@ -4868,7 +4868,7 @@ static int b43_wireless_core_init(struct b43_wldev *dev) case B43_BUS_BCMA: bcma_core_pci_irq_ctl(&dev->dev->bdev->bus->drv_pci[0], dev->dev->bdev, true); - bcma_core_pci_up(dev->dev->bdev->bus); + bcma_host_pci_up(dev->dev->bdev->bus); break; #endif #ifdef CONFIG_B43_SSB diff --git a/drivers/net/wireless/brcm80211/brcmsmac/main.c b/drivers/net/wireless/brcm80211/brcmsmac/main.c index eb8584a9c49a..bcbfc6e467e4 100644 --- a/drivers/net/wireless/brcm80211/brcmsmac/main.c +++ b/drivers/net/wireless/brcm80211/brcmsmac/main.c @@ -4668,7 +4668,7 @@ static int brcms_b_attach(struct brcms_c_info *wlc, struct bcma_device *core, brcms_c_coredisable(wlc_hw); /* Match driver "down" state */ - bcma_core_pci_down(wlc_hw->d11core->bus); + bcma_host_pci_down(wlc_hw->d11core->bus); /* turn off pll and xtal to match driver "down" state */ brcms_b_xtal(wlc_hw, OFF); @@ -4969,12 +4969,12 @@ static int brcms_b_up_prep(struct brcms_hardware *wlc_hw) */ if (brcms_b_radio_read_hwdisabled(wlc_hw)) { /* put SB PCI in down state again */ - bcma_core_pci_down(wlc_hw->d11core->bus); + bcma_host_pci_down(wlc_hw->d11core->bus); brcms_b_xtal(wlc_hw, OFF); return -ENOMEDIUM; } - bcma_core_pci_up(wlc_hw->d11core->bus); + bcma_host_pci_up(wlc_hw->d11core->bus); /* reset the d11 core */ brcms_b_corereset(wlc_hw, BRCMS_USE_COREFLAGS); @@ -5171,7 +5171,7 @@ static int brcms_b_down_finish(struct brcms_hardware *wlc_hw) /* turn off primary xtal and pll */ if (!wlc_hw->noreset) { - bcma_core_pci_down(wlc_hw->d11core->bus); + bcma_host_pci_down(wlc_hw->d11core->bus); brcms_b_xtal(wlc_hw, OFF); } } diff --git a/include/linux/bcma/bcma.h b/include/linux/bcma/bcma.h index 994739da827f..037620b3f113 100644 --- a/include/linux/bcma/bcma.h +++ b/include/linux/bcma/bcma.h @@ -434,6 +434,9 @@ static inline struct bcma_device *bcma_find_core(struct bcma_bus *bus, return bcma_find_core_unit(bus, coreid, 0); } +extern void bcma_host_pci_up(struct bcma_bus *bus); +extern void bcma_host_pci_down(struct bcma_bus *bus); + extern bool bcma_core_is_enabled(struct bcma_device *core); extern void bcma_core_disable(struct bcma_device *core, u32 flags); extern int bcma_core_enable(struct bcma_device *core, u32 flags); diff --git a/include/linux/bcma/bcma_driver_pci.h b/include/linux/bcma/bcma_driver_pci.h index 3f809ae372c4..23af893e9b86 100644 --- a/include/linux/bcma/bcma_driver_pci.h +++ b/include/linux/bcma/bcma_driver_pci.h @@ -242,8 +242,6 @@ extern void bcma_core_pci_early_init(struct bcma_drv_pci *pc); extern void bcma_core_pci_init(struct bcma_drv_pci *pc); extern int bcma_core_pci_irq_ctl(struct bcma_drv_pci *pc, struct bcma_device *core, bool enable); -extern void bcma_core_pci_up(struct bcma_bus *bus); -extern void bcma_core_pci_down(struct bcma_bus *bus); extern void bcma_core_pci_power_save(struct bcma_bus *bus, bool up); extern int bcma_core_pci_pcibios_map_irq(const struct pci_dev *dev); -- cgit v1.2.3 From 5b6ff664c8959d715e785b9465b042407a5d87a0 Mon Sep 17 00:00:00 2001 From: Rafał Miłecki Date: Sun, 8 Feb 2015 17:11:48 +0100 Subject: bcma: change IRQ control function to accept bus as an argument MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It doesn't operate on PCI core, but PCI host device, so there is no point of passing core related struct. Signed-off-by: Rafał Miłecki Signed-off-by: Kalle Valo --- drivers/bcma/driver_pci.c | 6 +++--- drivers/net/wireless/b43/main.c | 2 +- drivers/net/wireless/brcm80211/brcmsmac/main.c | 2 +- include/linux/bcma/bcma_driver_pci.h | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/drivers/bcma/driver_pci.c b/drivers/bcma/driver_pci.c index cf92bfa7eae0..cfd35bc1c5a3 100644 --- a/drivers/bcma/driver_pci.c +++ b/drivers/bcma/driver_pci.c @@ -282,21 +282,21 @@ void bcma_core_pci_power_save(struct bcma_bus *bus, bool up) } EXPORT_SYMBOL_GPL(bcma_core_pci_power_save); -int bcma_core_pci_irq_ctl(struct bcma_drv_pci *pc, struct bcma_device *core, +int bcma_core_pci_irq_ctl(struct bcma_bus *bus, struct bcma_device *core, bool enable) { struct pci_dev *pdev; u32 coremask, tmp; int err = 0; - if (!pc || core->bus->hosttype != BCMA_HOSTTYPE_PCI) { + if (bus->hosttype != BCMA_HOSTTYPE_PCI) { /* This bcma device is not on a PCI host-bus. So the IRQs are * not routed through the PCI core. * So we must not enable routing through the PCI core. */ goto out; } - pdev = pc->core->bus->host_pci; + pdev = bus->host_pci; err = pci_read_config_dword(pdev, BCMA_PCI_IRQMASK, &tmp); if (err) diff --git a/drivers/net/wireless/b43/main.c b/drivers/net/wireless/b43/main.c index 34065a6f7725..f34aa67f3179 100644 --- a/drivers/net/wireless/b43/main.c +++ b/drivers/net/wireless/b43/main.c @@ -4866,7 +4866,7 @@ static int b43_wireless_core_init(struct b43_wldev *dev) switch (dev->dev->bus_type) { #ifdef CONFIG_B43_BCMA case B43_BUS_BCMA: - bcma_core_pci_irq_ctl(&dev->dev->bdev->bus->drv_pci[0], + bcma_core_pci_irq_ctl(dev->dev->bdev->bus, dev->dev->bdev, true); bcma_host_pci_up(dev->dev->bdev->bus); break; diff --git a/drivers/net/wireless/brcm80211/brcmsmac/main.c b/drivers/net/wireless/brcm80211/brcmsmac/main.c index bcbfc6e467e4..c84af1dfc88f 100644 --- a/drivers/net/wireless/brcm80211/brcmsmac/main.c +++ b/drivers/net/wireless/brcm80211/brcmsmac/main.c @@ -4959,7 +4959,7 @@ static int brcms_b_up_prep(struct brcms_hardware *wlc_hw) * Configure pci/pcmcia here instead of in brcms_c_attach() * to allow mfg hotswap: down, hotswap (chip power cycle), up. */ - bcma_core_pci_irq_ctl(&wlc_hw->d11core->bus->drv_pci[0], wlc_hw->d11core, + bcma_core_pci_irq_ctl(wlc_hw->d11core->bus, wlc_hw->d11core, true); /* diff --git a/include/linux/bcma/bcma_driver_pci.h b/include/linux/bcma/bcma_driver_pci.h index 23af893e9b86..6b8bca67851f 100644 --- a/include/linux/bcma/bcma_driver_pci.h +++ b/include/linux/bcma/bcma_driver_pci.h @@ -240,7 +240,7 @@ struct bcma_drv_pci { extern void bcma_core_pci_early_init(struct bcma_drv_pci *pc); extern void bcma_core_pci_init(struct bcma_drv_pci *pc); -extern int bcma_core_pci_irq_ctl(struct bcma_drv_pci *pc, +extern int bcma_core_pci_irq_ctl(struct bcma_bus *bus, struct bcma_device *core, bool enable); extern void bcma_core_pci_power_save(struct bcma_bus *bus, bool up); -- cgit v1.2.3 From 804e27dee49e20c0addd1b7276654220cc3768ae Mon Sep 17 00:00:00 2001 From: Rafał Miłecki Date: Sun, 8 Feb 2015 17:11:49 +0100 Subject: bcma: support bringing up bus hosted on PCIe Gen 2 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Rafał Miłecki Signed-off-by: Kalle Valo --- drivers/bcma/bcma_private.h | 3 +++ drivers/bcma/driver_pcie2.c | 28 ++++++++++++++++++++++++++-- drivers/bcma/host_pci.c | 2 +- include/linux/bcma/bcma_driver_pcie2.h | 2 ++ 4 files changed, 32 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/drivers/bcma/bcma_private.h b/drivers/bcma/bcma_private.h index 351f4afdad25..36929126a206 100644 --- a/drivers/bcma/bcma_private.h +++ b/drivers/bcma/bcma_private.h @@ -104,6 +104,9 @@ u32 bcma_pcie_read(struct bcma_drv_pci *pc, u32 address); void bcma_core_pci_up(struct bcma_drv_pci *pc); void bcma_core_pci_down(struct bcma_drv_pci *pc); +/* driver_pcie2.c */ +void bcma_core_pcie2_up(struct bcma_drv_pcie2 *pcie2); + extern int bcma_chipco_watchdog_register(struct bcma_drv_cc *cc); #ifdef CONFIG_BCMA_DRIVER_PCI_HOSTMODE diff --git a/drivers/bcma/driver_pcie2.c b/drivers/bcma/driver_pcie2.c index e4be537b0c66..4568bc7bd54a 100644 --- a/drivers/bcma/driver_pcie2.c +++ b/drivers/bcma/driver_pcie2.c @@ -156,14 +156,23 @@ static void pciedev_reg_pm_clk_period(struct bcma_drv_pcie2 *pcie2) void bcma_core_pcie2_init(struct bcma_drv_pcie2 *pcie2) { - struct bcma_chipinfo *ci = &pcie2->core->bus->chipinfo; + struct bcma_bus *bus = pcie2->core->bus; + struct bcma_chipinfo *ci = &bus->chipinfo; u32 tmp; tmp = pcie2_read32(pcie2, BCMA_CORE_PCIE2_SPROM(54)); if ((tmp & 0xe) >> 1 == 2) bcma_core_pcie2_cfg_write(pcie2, 0x4e0, 0x17); - /* TODO: Do we need pcie_reqsize? */ + switch (bus->chipinfo.id) { + case BCMA_CHIP_ID_BCM4360: + case BCMA_CHIP_ID_BCM4352: + pcie2->reqsize = 1024; + break; + default: + pcie2->reqsize = 128; + break; + } if (ci->id == BCMA_CHIP_ID_BCM4360 && ci->rev > 3) bcma_core_pcie2_war_delay_perst_enab(pcie2, true); @@ -173,3 +182,18 @@ void bcma_core_pcie2_init(struct bcma_drv_pcie2 *pcie2) pciedev_crwlpciegen2_180(pcie2); pciedev_crwlpciegen2_182(pcie2); } + +/************************************************** + * Runtime ops. + **************************************************/ + +void bcma_core_pcie2_up(struct bcma_drv_pcie2 *pcie2) +{ + struct bcma_bus *bus = pcie2->core->bus; + struct pci_dev *dev = bus->host_pci; + int err; + + err = pcie_set_readrq(dev, pcie2->reqsize); + if (err) + bcma_err(bus, "Error setting PCI_EXP_DEVCTL_READRQ: %d\n", err); +} diff --git a/drivers/bcma/host_pci.c b/drivers/bcma/host_pci.c index 8dd37dc94cae..5fb87a899a24 100644 --- a/drivers/bcma/host_pci.c +++ b/drivers/bcma/host_pci.c @@ -322,7 +322,7 @@ void bcma_host_pci_up(struct bcma_bus *bus) return; if (bus->host_is_pcie2) - pr_warn("Bringing up bus with PCIe Gen 2 host is unsupported yet\n"); + bcma_core_pcie2_up(&bus->drv_pcie2); else bcma_core_pci_up(&bus->drv_pci[0]); } diff --git a/include/linux/bcma/bcma_driver_pcie2.h b/include/linux/bcma/bcma_driver_pcie2.h index 5988b05781c3..d8c43294c527 100644 --- a/include/linux/bcma/bcma_driver_pcie2.h +++ b/include/linux/bcma/bcma_driver_pcie2.h @@ -143,6 +143,8 @@ struct bcma_drv_pcie2 { struct bcma_device *core; + + u16 reqsize; }; #define pcie2_read16(pcie2, offset) bcma_read16((pcie2)->core, offset) -- cgit v1.2.3 From 559addc25b00ff3a40eff03a0b3873c2b6d726f8 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Wed, 4 Feb 2015 06:07:30 -0300 Subject: [media] fixp-arith: replace sin/cos table by a better precision one The cos table used at fixp-arith.h has only 8 bits of precision. That causes problems if it is reused on other drivers. As some media drivers require a higher precision sin/cos implementation, replace the current implementation by one that will provide 32 bits precision. The values generated by the new implementation matches the 32 bit precision of glibc's sin for an angle measured in integer degrees. It also provides support for fractional angles via linear interpolation. On experimental calculus, when used a table with a 0.001 degree angle, the maximum error for sin is 0.000038, which is likely good enough for practical purposes. There are some logic there that seems to be specific to the usage inside ff-memless.c. Move those logic to there, as they're not needed elsewhere. Cc: Hans de Goede Signed-off-by: Mauro Carvalho Chehab Signed-off-by: Prashant Laddha Signed-off-by: Hans Verkuil Acked-by: Dmitry Torokhov Signed-off-by: Mauro Carvalho Chehab --- drivers/input/ff-memless.c | 18 ++++- drivers/media/usb/gspca/ov534.c | 11 +-- include/linux/fixp-arith.h | 145 +++++++++++++++++++++++++++++----------- 3 files changed, 125 insertions(+), 49 deletions(-) (limited to 'include') diff --git a/drivers/input/ff-memless.c b/drivers/input/ff-memless.c index 74c0d8c6002a..fcc6c3368182 100644 --- a/drivers/input/ff-memless.c +++ b/drivers/input/ff-memless.c @@ -237,6 +237,18 @@ static u16 ml_calculate_direction(u16 direction, u16 force, (force + new_force)) << 1; } +#define FRAC_N 8 +static inline s16 fixp_new16(s16 a) +{ + return ((s32)a) >> (16 - FRAC_N); +} + +static inline s16 fixp_mult(s16 a, s16 b) +{ + a = ((s32)a * 0x100) / 0x7fff; + return ((s32)(a * b)) >> FRAC_N; +} + /* * Combine two effects and apply gain. */ @@ -247,7 +259,7 @@ static void ml_combine_effects(struct ff_effect *effect, struct ff_effect *new = state->effect; unsigned int strong, weak, i; int x, y; - fixp_t level; + s16 level; switch (new->type) { case FF_CONSTANT: @@ -255,8 +267,8 @@ static void ml_combine_effects(struct ff_effect *effect, level = fixp_new16(apply_envelope(state, new->u.constant.level, &new->u.constant.envelope)); - x = fixp_mult(fixp_sin(i), level) * gain / 0xffff; - y = fixp_mult(-fixp_cos(i), level) * gain / 0xffff; + x = fixp_mult(fixp_sin16(i), level) * gain / 0xffff; + y = fixp_mult(-fixp_cos16(i), level) * gain / 0xffff; /* * here we abuse ff_ramp to hold x and y of constant force * If in future any driver wants something else than x and y diff --git a/drivers/media/usb/gspca/ov534.c b/drivers/media/usb/gspca/ov534.c index a9c866d6d82d..146071b8e116 100644 --- a/drivers/media/usb/gspca/ov534.c +++ b/drivers/media/usb/gspca/ov534.c @@ -816,21 +816,16 @@ static void sethue(struct gspca_dev *gspca_dev, s32 val) s16 huesin; s16 huecos; - /* fixp_sin and fixp_cos accept only positive values, while - * our val is between -90 and 90 - */ - val += 360; - /* According to the datasheet the registers expect HUESIN and * HUECOS to be the result of the trigonometric functions, * scaled by 0x80. * - * The 0x100 here represents the maximun absolute value + * The 0x7fff here represents the maximum absolute value * returned byt fixp_sin and fixp_cos, so the scaling will * consider the result like in the interval [-1.0, 1.0]. */ - huesin = fixp_sin(val) * 0x80 / 0x100; - huecos = fixp_cos(val) * 0x80 / 0x100; + huesin = fixp_sin16(val) * 0x80 / 0x7fff; + huecos = fixp_cos16(val) * 0x80 / 0x7fff; if (huesin < 0) { sccb_reg_write(gspca_dev, 0xab, diff --git a/include/linux/fixp-arith.h b/include/linux/fixp-arith.h index 3089d7382325..d4686fe1cac7 100644 --- a/include/linux/fixp-arith.h +++ b/include/linux/fixp-arith.h @@ -1,6 +1,8 @@ #ifndef _FIXP_ARITH_H #define _FIXP_ARITH_H +#include + /* * Simplistic fixed-point arithmetics. * Hmm, I'm probably duplicating some code :( @@ -29,59 +31,126 @@ #include -/* The type representing fixed-point values */ -typedef s16 fixp_t; +static const s32 sin_table[] = { + 0x00000000, 0x023be165, 0x04779632, 0x06b2f1d2, 0x08edc7b6, 0x0b27eb5c, + 0x0d61304d, 0x0f996a26, 0x11d06c96, 0x14060b67, 0x163a1a7d, 0x186c6ddd, + 0x1a9cd9ac, 0x1ccb3236, 0x1ef74bf2, 0x2120fb82, 0x234815ba, 0x256c6f9e, + 0x278dde6e, 0x29ac379f, 0x2bc750e8, 0x2ddf003f, 0x2ff31bdd, 0x32037a44, + 0x340ff241, 0x36185aee, 0x381c8bb5, 0x3a1c5c56, 0x3c17a4e7, 0x3e0e3ddb, + 0x3fffffff, 0x41ecc483, 0x43d464fa, 0x45b6bb5d, 0x4793a20f, 0x496af3e1, + 0x4b3c8c11, 0x4d084650, 0x4ecdfec6, 0x508d9210, 0x5246dd48, 0x53f9be04, + 0x55a6125a, 0x574bb8e5, 0x58ea90c2, 0x5a827999, 0x5c135399, 0x5d9cff82, + 0x5f1f5ea0, 0x609a52d1, 0x620dbe8a, 0x637984d3, 0x64dd894f, 0x6639b039, + 0x678dde6d, 0x68d9f963, 0x6a1de735, 0x6b598ea1, 0x6c8cd70a, 0x6db7a879, + 0x6ed9eba0, 0x6ff389de, 0x71046d3c, 0x720c8074, 0x730baeec, 0x7401e4bf, + 0x74ef0ebb, 0x75d31a5f, 0x76adf5e5, 0x777f903b, 0x7847d908, 0x7906c0af, + 0x79bc384c, 0x7a6831b8, 0x7b0a9f8c, 0x7ba3751c, 0x7c32a67c, 0x7cb82884, + 0x7d33f0c8, 0x7da5f5a3, 0x7e0e2e31, 0x7e6c924f, 0x7ec11aa3, 0x7f0bc095, + 0x7f4c7e52, 0x7f834ecf, 0x7fb02dc4, 0x7fd317b3, 0x7fec09e1, 0x7ffb025e, + 0x7fffffff +}; -#define FRAC_N 8 -#define FRAC_MASK ((1< 180) { + negative = true; + degrees -= 180; + } + if (degrees > 90) + degrees = 180 - degrees; + ret = sin_table[degrees]; -/* a: 123 -> 123.0 */ -static inline fixp_t fixp_new(s16 a) -{ - return a< -1.0 - 0x8000 -> 1.0 - 0x0000 -> 0.0 -*/ -static inline fixp_t fixp_new16(s16 a) +/** + * fixp_sin32() returns the sin of an angle in degrees + * + * @degrees: angle, in degrees. The angle can be positive or negative + * + * The returned value ranges from -0x7fffffff to +0x7fffffff. + */ +static inline s32 fixp_sin32(int degrees) { - return ((s32)a)>>(16-FRAC_N); + degrees = (degrees % 360 + 360) % 360; + + return __fixp_sin32(degrees); } -static inline fixp_t fixp_cos(unsigned int degrees) +/* cos(x) = sin(x + 90 degrees) */ +#define fixp_cos32(v) fixp_sin32((v) + 90) + +/* + * 16 bits variants + * + * The returned value ranges from -0x7fff to 0x7fff + */ + +#define fixp_sin16(v) (fixp_sin32(v) >> 16) +#define fixp_cos16(v) (fixp_cos32(v) >> 16) + +/** + * fixp_sin32_rad() - calculates the sin of an angle in radians + * + * @radians: angle, in radians + * @twopi: value to be used for 2*pi + * + * Provides a variant for the cases where just 360 + * values is not enough. This function uses linear + * interpolation to a wider range of values given by + * twopi var. + * + * Experimental tests gave a maximum difference of + * 0.000038 between the value calculated by sin() and + * the one produced by this function, when twopi is + * equal to 360000. That seems to be enough precision + * for practical purposes. + * + * Please notice that two high numbers for twopi could cause + * overflows, so the routine will not allow values of twopi + * bigger than 1^18. + */ +static inline s32 fixp_sin32_rad(u32 radians, u32 twopi) { - int quadrant = (degrees / 90) & 3; - unsigned int i = degrees % 90; + int degrees; + s32 v1, v2, dx, dy; + s64 tmp; - if (quadrant == 1 || quadrant == 3) - i = 90 - i; + /* + * Avoid too large values for twopi, as we don't want overflows. + */ + BUG_ON(twopi > 1 << 18); - i >>= 1; + degrees = (radians * 360) / twopi; + tmp = radians - (degrees * twopi) / 360; - return (quadrant == 1 || quadrant == 2)? -cos_table[i] : cos_table[i]; -} + degrees = (degrees % 360 + 360) % 360; + v1 = __fixp_sin32(degrees); -static inline fixp_t fixp_sin(unsigned int degrees) -{ - return -fixp_cos(degrees + 90); -} + v2 = fixp_sin32(degrees + 1); -static inline fixp_t fixp_mult(fixp_t a, fixp_t b) -{ - return ((s32)(a*b))>>FRAC_N; + dx = twopi / 360; + dy = v2 - v1; + + tmp *= dy; + + return v1 + div_s64(tmp, dx); } +/* cos(x) = sin(x + pi/2 radians) */ + +#define fixp_cos32_rad(rad, twopi) \ + fixp_sin32_rad(rad + twopi / 4, twopi) + #endif -- cgit v1.2.3 From 1b784140474e4fc94281a49e96c67d29df0efbde Mon Sep 17 00:00:00 2001 From: Ying Xue Date: Mon, 2 Mar 2015 15:37:48 +0800 Subject: net: Remove iocb argument from sendmsg and recvmsg After TIPC doesn't depend on iocb argument in its internal implementations of sendmsg() and recvmsg() hooks defined in proto structure, no any user is using iocb argument in them at all now. Then we can drop the redundant iocb argument completely from kinds of implementations of both sendmsg() and recvmsg() in the entire networking stack. Cc: Christoph Hellwig Suggested-by: Al Viro Signed-off-by: Ying Xue Signed-off-by: David S. Miller --- crypto/algif_hash.c | 8 ++-- crypto/algif_rng.c | 4 +- crypto/algif_skcipher.c | 8 ++-- drivers/isdn/mISDN/socket.c | 7 ++-- drivers/net/macvtap.c | 9 ++--- drivers/net/ppp/pppoe.c | 8 ++-- drivers/net/tun.c | 6 +-- drivers/vhost/net.c | 6 +-- include/linux/net.h | 10 ++--- include/net/af_vsock.h | 4 +- include/net/bluetooth/bluetooth.h | 8 ++-- include/net/inet_common.h | 7 ++-- include/net/ping.h | 7 ++-- include/net/sock.h | 16 ++++---- include/net/tcp.h | 7 ++-- include/net/udp.h | 3 +- net/appletalk/ddp.c | 7 ++-- net/atm/common.c | 7 ++-- net/atm/common.h | 7 ++-- net/ax25/af_ax25.c | 7 ++-- net/bluetooth/af_bluetooth.c | 8 ++-- net/bluetooth/hci_sock.c | 8 ++-- net/bluetooth/l2cap_sock.c | 12 +++--- net/bluetooth/rfcomm/sock.c | 10 ++--- net/bluetooth/sco.c | 10 ++--- net/caif/caif_socket.c | 17 ++++----- net/can/bcm.c | 7 ++-- net/can/raw.c | 7 ++-- net/core/sock.c | 13 +++---- net/dccp/dccp.h | 8 ++-- net/dccp/probe.c | 3 +- net/dccp/proto.c | 7 ++-- net/decnet/af_decnet.c | 7 ++-- net/ieee802154/socket.c | 21 +++++------ net/ipv4/af_inet.c | 11 +++--- net/ipv4/ping.c | 7 ++-- net/ipv4/raw.c | 7 ++-- net/ipv4/tcp.c | 7 ++-- net/ipv4/udp.c | 9 ++--- net/ipv4/udp_impl.h | 4 +- net/ipv6/ping.c | 3 +- net/ipv6/raw.c | 8 ++-- net/ipv6/udp.c | 10 ++--- net/ipv6/udp_impl.h | 7 ++-- net/ipx/af_ipx.c | 7 ++-- net/irda/af_irda.c | 29 +++++++-------- net/iucv/af_iucv.c | 8 ++-- net/key/af_key.c | 6 +-- net/l2tp/l2tp_ip.c | 4 +- net/l2tp/l2tp_ip6.c | 8 ++-- net/l2tp/l2tp_ppp.c | 7 ++-- net/llc/af_llc.c | 7 ++-- net/netlink/af_netlink.c | 6 +-- net/netrom/af_netrom.c | 7 ++-- net/nfc/llcp_sock.c | 8 ++-- net/nfc/rawsock.c | 7 ++-- net/packet/af_packet.c | 11 +++--- net/phonet/datagram.c | 8 ++-- net/phonet/pep.c | 8 ++-- net/phonet/socket.c | 6 +-- net/rds/rds.h | 7 ++-- net/rds/recv.c | 4 +- net/rds/send.c | 3 +- net/rose/af_rose.c | 7 ++-- net/rxrpc/af_rxrpc.c | 7 ++-- net/rxrpc/ar-internal.h | 10 ++--- net/rxrpc/ar-output.c | 20 ++++------ net/rxrpc/ar-recvmsg.c | 4 +- net/sctp/socket.c | 8 ++-- net/socket.c | 78 +++++++-------------------------------- net/tipc/socket.c | 23 ++++-------- net/unix/af_unix.c | 50 +++++++++++-------------- net/vmw_vsock/af_vsock.c | 20 +++++----- net/vmw_vsock/vmci_transport.c | 3 +- net/x25/af_x25.c | 6 +-- 75 files changed, 302 insertions(+), 442 deletions(-) (limited to 'include') diff --git a/crypto/algif_hash.c b/crypto/algif_hash.c index 01da360bdb55..0a465e0f3012 100644 --- a/crypto/algif_hash.c +++ b/crypto/algif_hash.c @@ -34,8 +34,8 @@ struct hash_ctx { struct ahash_request req; }; -static int hash_sendmsg(struct kiocb *unused, struct socket *sock, - struct msghdr *msg, size_t ignored) +static int hash_sendmsg(struct socket *sock, struct msghdr *msg, + size_t ignored) { int limit = ALG_MAX_PAGES * PAGE_SIZE; struct sock *sk = sock->sk; @@ -139,8 +139,8 @@ unlock: return err ?: size; } -static int hash_recvmsg(struct kiocb *unused, struct socket *sock, - struct msghdr *msg, size_t len, int flags) +static int hash_recvmsg(struct socket *sock, struct msghdr *msg, size_t len, + int flags) { struct sock *sk = sock->sk; struct alg_sock *ask = alg_sk(sk); diff --git a/crypto/algif_rng.c b/crypto/algif_rng.c index 67f612cfed97..3acba0a7cd55 100644 --- a/crypto/algif_rng.c +++ b/crypto/algif_rng.c @@ -55,8 +55,8 @@ struct rng_ctx { struct crypto_rng *drng; }; -static int rng_recvmsg(struct kiocb *unused, struct socket *sock, - struct msghdr *msg, size_t len, int flags) +static int rng_recvmsg(struct socket *sock, struct msghdr *msg, size_t len, + int flags) { struct sock *sk = sock->sk; struct alg_sock *ask = alg_sk(sk); diff --git a/crypto/algif_skcipher.c b/crypto/algif_skcipher.c index 0c8a1e5ccadf..b9743dc35801 100644 --- a/crypto/algif_skcipher.c +++ b/crypto/algif_skcipher.c @@ -239,8 +239,8 @@ static void skcipher_data_wakeup(struct sock *sk) rcu_read_unlock(); } -static int skcipher_sendmsg(struct kiocb *unused, struct socket *sock, - struct msghdr *msg, size_t size) +static int skcipher_sendmsg(struct socket *sock, struct msghdr *msg, + size_t size) { struct sock *sk = sock->sk; struct alg_sock *ask = alg_sk(sk); @@ -424,8 +424,8 @@ unlock: return err ?: size; } -static int skcipher_recvmsg(struct kiocb *unused, struct socket *sock, - struct msghdr *msg, size_t ignored, int flags) +static int skcipher_recvmsg(struct socket *sock, struct msghdr *msg, + size_t ignored, int flags) { struct sock *sk = sock->sk; struct alg_sock *ask = alg_sk(sk); diff --git a/drivers/isdn/mISDN/socket.c b/drivers/isdn/mISDN/socket.c index 84b35925ee4d..8dc7290089bb 100644 --- a/drivers/isdn/mISDN/socket.c +++ b/drivers/isdn/mISDN/socket.c @@ -112,8 +112,8 @@ mISDN_sock_cmsg(struct sock *sk, struct msghdr *msg, struct sk_buff *skb) } static int -mISDN_sock_recvmsg(struct kiocb *iocb, struct socket *sock, - struct msghdr *msg, size_t len, int flags) +mISDN_sock_recvmsg(struct socket *sock, struct msghdr *msg, size_t len, + int flags) { struct sk_buff *skb; struct sock *sk = sock->sk; @@ -173,8 +173,7 @@ mISDN_sock_recvmsg(struct kiocb *iocb, struct socket *sock, } static int -mISDN_sock_sendmsg(struct kiocb *iocb, struct socket *sock, - struct msghdr *msg, size_t len) +mISDN_sock_sendmsg(struct socket *sock, struct msghdr *msg, size_t len) { struct sock *sk = sock->sk; struct sk_buff *skb; diff --git a/drivers/net/macvtap.c b/drivers/net/macvtap.c index e40fdfccc9c1..1e51c6bf3ae1 100644 --- a/drivers/net/macvtap.c +++ b/drivers/net/macvtap.c @@ -1127,16 +1127,15 @@ static const struct file_operations macvtap_fops = { #endif }; -static int macvtap_sendmsg(struct kiocb *iocb, struct socket *sock, - struct msghdr *m, size_t total_len) +static int macvtap_sendmsg(struct socket *sock, struct msghdr *m, + size_t total_len) { struct macvtap_queue *q = container_of(sock, struct macvtap_queue, sock); return macvtap_get_user(q, m, &m->msg_iter, m->msg_flags & MSG_DONTWAIT); } -static int macvtap_recvmsg(struct kiocb *iocb, struct socket *sock, - struct msghdr *m, size_t total_len, - int flags) +static int macvtap_recvmsg(struct socket *sock, struct msghdr *m, + size_t total_len, int flags) { struct macvtap_queue *q = container_of(sock, struct macvtap_queue, sock); int ret; diff --git a/drivers/net/ppp/pppoe.c b/drivers/net/ppp/pppoe.c index 9c97e9bcf5f5..ff059e1d8ac6 100644 --- a/drivers/net/ppp/pppoe.c +++ b/drivers/net/ppp/pppoe.c @@ -835,8 +835,8 @@ static int pppoe_ioctl(struct socket *sock, unsigned int cmd, return err; } -static int pppoe_sendmsg(struct kiocb *iocb, struct socket *sock, - struct msghdr *m, size_t total_len) +static int pppoe_sendmsg(struct socket *sock, struct msghdr *m, + size_t total_len) { struct sk_buff *skb; struct sock *sk = sock->sk; @@ -977,8 +977,8 @@ static const struct ppp_channel_ops pppoe_chan_ops = { .start_xmit = pppoe_xmit, }; -static int pppoe_recvmsg(struct kiocb *iocb, struct socket *sock, - struct msghdr *m, size_t total_len, int flags) +static int pppoe_recvmsg(struct socket *sock, struct msghdr *m, + size_t total_len, int flags) { struct sock *sk = sock->sk; struct sk_buff *skb; diff --git a/drivers/net/tun.c b/drivers/net/tun.c index 857dca47bf80..b96b94cee760 100644 --- a/drivers/net/tun.c +++ b/drivers/net/tun.c @@ -1448,8 +1448,7 @@ static void tun_sock_write_space(struct sock *sk) kill_fasync(&tfile->fasync, SIGIO, POLL_OUT); } -static int tun_sendmsg(struct kiocb *iocb, struct socket *sock, - struct msghdr *m, size_t total_len) +static int tun_sendmsg(struct socket *sock, struct msghdr *m, size_t total_len) { int ret; struct tun_file *tfile = container_of(sock, struct tun_file, socket); @@ -1464,8 +1463,7 @@ static int tun_sendmsg(struct kiocb *iocb, struct socket *sock, return ret; } -static int tun_recvmsg(struct kiocb *iocb, struct socket *sock, - struct msghdr *m, size_t total_len, +static int tun_recvmsg(struct socket *sock, struct msghdr *m, size_t total_len, int flags) { struct tun_file *tfile = container_of(sock, struct tun_file, socket); diff --git a/drivers/vhost/net.c b/drivers/vhost/net.c index afa06d28725d..633012cc9a57 100644 --- a/drivers/vhost/net.c +++ b/drivers/vhost/net.c @@ -390,7 +390,7 @@ static void handle_tx(struct vhost_net *net) ubufs = NULL; } /* TODO: Check specific error and bomb out unless ENOBUFS? */ - err = sock->ops->sendmsg(NULL, sock, &msg, len); + err = sock->ops->sendmsg(sock, &msg, len); if (unlikely(err < 0)) { if (zcopy_used) { vhost_net_ubuf_put(ubufs); @@ -566,7 +566,7 @@ static void handle_rx(struct vhost_net *net) /* On overrun, truncate and discard */ if (unlikely(headcount > UIO_MAXIOV)) { iov_iter_init(&msg.msg_iter, READ, vq->iov, 1, 1); - err = sock->ops->recvmsg(NULL, sock, &msg, + err = sock->ops->recvmsg(sock, &msg, 1, MSG_DONTWAIT | MSG_TRUNC); pr_debug("Discarded rx packet: len %zd\n", sock_len); continue; @@ -597,7 +597,7 @@ static void handle_rx(struct vhost_net *net) */ iov_iter_advance(&fixup, sizeof(hdr)); } - err = sock->ops->recvmsg(NULL, sock, &msg, + err = sock->ops->recvmsg(sock, &msg, sock_len, MSG_DONTWAIT | MSG_TRUNC); /* Userspace might have consumed the packet meanwhile: * it's not supposed to do this usually, but might be hard diff --git a/include/linux/net.h b/include/linux/net.h index 17d83393afcc..e74114bcca68 100644 --- a/include/linux/net.h +++ b/include/linux/net.h @@ -120,7 +120,6 @@ struct socket { struct vm_area_struct; struct page; -struct kiocb; struct sockaddr; struct msghdr; struct module; @@ -162,8 +161,8 @@ struct proto_ops { int (*compat_getsockopt)(struct socket *sock, int level, int optname, char __user *optval, int __user *optlen); #endif - int (*sendmsg) (struct kiocb *iocb, struct socket *sock, - struct msghdr *m, size_t total_len); + int (*sendmsg) (struct socket *sock, struct msghdr *m, + size_t total_len); /* Notes for implementing recvmsg: * =============================== * msg->msg_namelen should get updated by the recvmsg handlers @@ -172,9 +171,8 @@ struct proto_ops { * handlers can assume that msg.msg_name is either NULL or has * a minimum size of sizeof(struct sockaddr_storage). */ - int (*recvmsg) (struct kiocb *iocb, struct socket *sock, - struct msghdr *m, size_t total_len, - int flags); + int (*recvmsg) (struct socket *sock, struct msghdr *m, + size_t total_len, int flags); int (*mmap) (struct file *file, struct socket *sock, struct vm_area_struct * vma); ssize_t (*sendpage) (struct socket *sock, struct page *page, diff --git a/include/net/af_vsock.h b/include/net/af_vsock.h index 0d87674fb775..172632dd9930 100644 --- a/include/net/af_vsock.h +++ b/include/net/af_vsock.h @@ -100,8 +100,8 @@ struct vsock_transport { /* DGRAM. */ int (*dgram_bind)(struct vsock_sock *, struct sockaddr_vm *); - int (*dgram_dequeue)(struct kiocb *kiocb, struct vsock_sock *vsk, - struct msghdr *msg, size_t len, int flags); + int (*dgram_dequeue)(struct vsock_sock *vsk, struct msghdr *msg, + size_t len, int flags); int (*dgram_enqueue)(struct vsock_sock *, struct sockaddr_vm *, struct msghdr *, size_t len); bool (*dgram_allow)(u32 cid, u32 port); diff --git a/include/net/bluetooth/bluetooth.h b/include/net/bluetooth/bluetooth.h index 4500bf88ff55..6bb97df16d2d 100644 --- a/include/net/bluetooth/bluetooth.h +++ b/include/net/bluetooth/bluetooth.h @@ -245,10 +245,10 @@ int bt_sock_register(int proto, const struct net_proto_family *ops); void bt_sock_unregister(int proto); void bt_sock_link(struct bt_sock_list *l, struct sock *s); void bt_sock_unlink(struct bt_sock_list *l, struct sock *s); -int bt_sock_recvmsg(struct kiocb *iocb, struct socket *sock, - struct msghdr *msg, size_t len, int flags); -int bt_sock_stream_recvmsg(struct kiocb *iocb, struct socket *sock, - struct msghdr *msg, size_t len, int flags); +int bt_sock_recvmsg(struct socket *sock, struct msghdr *msg, size_t len, + int flags); +int bt_sock_stream_recvmsg(struct socket *sock, struct msghdr *msg, + size_t len, int flags); uint bt_sock_poll(struct file *file, struct socket *sock, poll_table *wait); int bt_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg); int bt_sock_wait_state(struct sock *sk, int state, unsigned long timeo); diff --git a/include/net/inet_common.h b/include/net/inet_common.h index b2828a06a5a6..4a92423eefa5 100644 --- a/include/net/inet_common.h +++ b/include/net/inet_common.h @@ -21,12 +21,11 @@ int __inet_stream_connect(struct socket *sock, struct sockaddr *uaddr, int inet_dgram_connect(struct socket *sock, struct sockaddr *uaddr, int addr_len, int flags); int inet_accept(struct socket *sock, struct socket *newsock, int flags); -int inet_sendmsg(struct kiocb *iocb, struct socket *sock, struct msghdr *msg, - size_t size); +int inet_sendmsg(struct socket *sock, struct msghdr *msg, size_t size); ssize_t inet_sendpage(struct socket *sock, struct page *page, int offset, size_t size, int flags); -int inet_recvmsg(struct kiocb *iocb, struct socket *sock, struct msghdr *msg, - size_t size, int flags); +int inet_recvmsg(struct socket *sock, struct msghdr *msg, size_t size, + int flags); int inet_shutdown(struct socket *sock, int how); int inet_listen(struct socket *sock, int backlog); void inet_sock_destruct(struct sock *sk); diff --git a/include/net/ping.h b/include/net/ping.h index cc16d413f681..ac80cb45e630 100644 --- a/include/net/ping.h +++ b/include/net/ping.h @@ -75,12 +75,11 @@ void ping_err(struct sk_buff *skb, int offset, u32 info); int ping_getfrag(void *from, char *to, int offset, int fraglen, int odd, struct sk_buff *); -int ping_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, - size_t len, int noblock, int flags, int *addr_len); +int ping_recvmsg(struct sock *sk, struct msghdr *msg, size_t len, int noblock, + int flags, int *addr_len); int ping_common_sendmsg(int family, struct msghdr *msg, size_t len, void *user_icmph, size_t icmph_len); -int ping_v6_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, - size_t len); +int ping_v6_sendmsg(struct sock *sk, struct msghdr *msg, size_t len); int ping_queue_rcv_skb(struct sock *sk, struct sk_buff *skb); bool ping_rcv(struct sk_buff *skb); diff --git a/include/net/sock.h b/include/net/sock.h index 38369d3580a1..250822cc1e02 100644 --- a/include/net/sock.h +++ b/include/net/sock.h @@ -958,10 +958,9 @@ struct proto { int (*compat_ioctl)(struct sock *sk, unsigned int cmd, unsigned long arg); #endif - int (*sendmsg)(struct kiocb *iocb, struct sock *sk, - struct msghdr *msg, size_t len); - int (*recvmsg)(struct kiocb *iocb, struct sock *sk, - struct msghdr *msg, + int (*sendmsg)(struct sock *sk, struct msghdr *msg, + size_t len); + int (*recvmsg)(struct sock *sk, struct msghdr *msg, size_t len, int noblock, int flags, int *addr_len); int (*sendpage)(struct sock *sk, struct page *page, @@ -1562,9 +1561,8 @@ int sock_no_listen(struct socket *, int); int sock_no_shutdown(struct socket *, int); int sock_no_getsockopt(struct socket *, int , int, char __user *, int __user *); int sock_no_setsockopt(struct socket *, int, int, char __user *, unsigned int); -int sock_no_sendmsg(struct kiocb *, struct socket *, struct msghdr *, size_t); -int sock_no_recvmsg(struct kiocb *, struct socket *, struct msghdr *, size_t, - int); +int sock_no_sendmsg(struct socket *, struct msghdr *, size_t); +int sock_no_recvmsg(struct socket *, struct msghdr *, size_t, int); int sock_no_mmap(struct file *file, struct socket *sock, struct vm_area_struct *vma); ssize_t sock_no_sendpage(struct socket *sock, struct page *page, int offset, @@ -1576,8 +1574,8 @@ ssize_t sock_no_sendpage(struct socket *sock, struct page *page, int offset, */ int sock_common_getsockopt(struct socket *sock, int level, int optname, char __user *optval, int __user *optlen); -int sock_common_recvmsg(struct kiocb *iocb, struct socket *sock, - struct msghdr *msg, size_t size, int flags); +int sock_common_recvmsg(struct socket *sock, struct msghdr *msg, size_t size, + int flags); int sock_common_setsockopt(struct socket *sock, int level, int optname, char __user *optval, unsigned int optlen); int compat_sock_common_getsockopt(struct socket *sock, int level, diff --git a/include/net/tcp.h b/include/net/tcp.h index 8d6b983d5099..f87599d5af82 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -349,8 +349,7 @@ void tcp_v4_early_demux(struct sk_buff *skb); int tcp_v4_rcv(struct sk_buff *skb); int tcp_v4_tw_remember_stamp(struct inet_timewait_sock *tw); -int tcp_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, - size_t size); +int tcp_sendmsg(struct sock *sk, struct msghdr *msg, size_t size); int tcp_sendpage(struct sock *sk, struct page *page, int offset, size_t size, int flags); void tcp_release_cb(struct sock *sk); @@ -430,8 +429,8 @@ int compat_tcp_setsockopt(struct sock *sk, int level, int optname, char __user *optval, unsigned int optlen); void tcp_set_keepalive(struct sock *sk, int val); void tcp_syn_ack_timeout(struct sock *sk, struct request_sock *req); -int tcp_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, - size_t len, int nonblock, int flags, int *addr_len); +int tcp_recvmsg(struct sock *sk, struct msghdr *msg, size_t len, int nonblock, + int flags, int *addr_len); void tcp_parse_options(const struct sk_buff *skb, struct tcp_options_received *opt_rx, int estab, struct tcp_fastopen_cookie *foc); diff --git a/include/net/udp.h b/include/net/udp.h index 32d8d9f07f76..6d4ed18e1427 100644 --- a/include/net/udp.h +++ b/include/net/udp.h @@ -238,8 +238,7 @@ int udp_get_port(struct sock *sk, unsigned short snum, int (*saddr_cmp)(const struct sock *, const struct sock *)); void udp_err(struct sk_buff *, u32); -int udp_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, - size_t len); +int udp_sendmsg(struct sock *sk, struct msghdr *msg, size_t len); int udp_push_pending_frames(struct sock *sk); void udp_flush_pending_frames(struct sock *sk); void udp4_hwcsum(struct sk_buff *skb, __be32 src, __be32 dst); diff --git a/net/appletalk/ddp.c b/net/appletalk/ddp.c index 0d0766ea5ab1..3b7ad43c7dad 100644 --- a/net/appletalk/ddp.c +++ b/net/appletalk/ddp.c @@ -1559,8 +1559,7 @@ freeit: return 0; } -static int atalk_sendmsg(struct kiocb *iocb, struct socket *sock, struct msghdr *msg, - size_t len) +static int atalk_sendmsg(struct socket *sock, struct msghdr *msg, size_t len) { struct sock *sk = sock->sk; struct atalk_sock *at = at_sk(sk); @@ -1728,8 +1727,8 @@ out: return err ? : len; } -static int atalk_recvmsg(struct kiocb *iocb, struct socket *sock, struct msghdr *msg, - size_t size, int flags) +static int atalk_recvmsg(struct socket *sock, struct msghdr *msg, size_t size, + int flags) { struct sock *sk = sock->sk; struct ddpehdr *ddp; diff --git a/net/atm/common.c b/net/atm/common.c index b84057e41bd6..ed0466637e13 100644 --- a/net/atm/common.c +++ b/net/atm/common.c @@ -523,8 +523,8 @@ int vcc_connect(struct socket *sock, int itf, short vpi, int vci) return 0; } -int vcc_recvmsg(struct kiocb *iocb, struct socket *sock, struct msghdr *msg, - size_t size, int flags) +int vcc_recvmsg(struct socket *sock, struct msghdr *msg, size_t size, + int flags) { struct sock *sk = sock->sk; struct atm_vcc *vcc; @@ -569,8 +569,7 @@ int vcc_recvmsg(struct kiocb *iocb, struct socket *sock, struct msghdr *msg, return copied; } -int vcc_sendmsg(struct kiocb *iocb, struct socket *sock, struct msghdr *m, - size_t size) +int vcc_sendmsg(struct socket *sock, struct msghdr *m, size_t size) { struct sock *sk = sock->sk; DEFINE_WAIT(wait); diff --git a/net/atm/common.h b/net/atm/common.h index cc3c2dae4d79..4d6f5b2068ac 100644 --- a/net/atm/common.h +++ b/net/atm/common.h @@ -13,10 +13,9 @@ int vcc_create(struct net *net, struct socket *sock, int protocol, int family); int vcc_release(struct socket *sock); int vcc_connect(struct socket *sock, int itf, short vpi, int vci); -int vcc_recvmsg(struct kiocb *iocb, struct socket *sock, struct msghdr *msg, - size_t size, int flags); -int vcc_sendmsg(struct kiocb *iocb, struct socket *sock, struct msghdr *m, - size_t total_len); +int vcc_recvmsg(struct socket *sock, struct msghdr *msg, size_t size, + int flags); +int vcc_sendmsg(struct socket *sock, struct msghdr *m, size_t total_len); unsigned int vcc_poll(struct file *file, struct socket *sock, poll_table *wait); int vcc_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg); int vcc_compat_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg); diff --git a/net/ax25/af_ax25.c b/net/ax25/af_ax25.c index ca049a7c9287..330c1f4a5a0b 100644 --- a/net/ax25/af_ax25.c +++ b/net/ax25/af_ax25.c @@ -1432,8 +1432,7 @@ out: return err; } -static int ax25_sendmsg(struct kiocb *iocb, struct socket *sock, - struct msghdr *msg, size_t len) +static int ax25_sendmsg(struct socket *sock, struct msghdr *msg, size_t len) { DECLARE_SOCKADDR(struct sockaddr_ax25 *, usax, msg->msg_name); struct sock *sk = sock->sk; @@ -1599,8 +1598,8 @@ out: return err; } -static int ax25_recvmsg(struct kiocb *iocb, struct socket *sock, - struct msghdr *msg, size_t size, int flags) +static int ax25_recvmsg(struct socket *sock, struct msghdr *msg, size_t size, + int flags) { struct sock *sk = sock->sk; struct sk_buff *skb; diff --git a/net/bluetooth/af_bluetooth.c b/net/bluetooth/af_bluetooth.c index 4b904c97a068..20a4698e2255 100644 --- a/net/bluetooth/af_bluetooth.c +++ b/net/bluetooth/af_bluetooth.c @@ -210,8 +210,8 @@ struct sock *bt_accept_dequeue(struct sock *parent, struct socket *newsock) } EXPORT_SYMBOL(bt_accept_dequeue); -int bt_sock_recvmsg(struct kiocb *iocb, struct socket *sock, - struct msghdr *msg, size_t len, int flags) +int bt_sock_recvmsg(struct socket *sock, struct msghdr *msg, size_t len, + int flags) { int noblock = flags & MSG_DONTWAIT; struct sock *sk = sock->sk; @@ -283,8 +283,8 @@ static long bt_sock_data_wait(struct sock *sk, long timeo) return timeo; } -int bt_sock_stream_recvmsg(struct kiocb *iocb, struct socket *sock, - struct msghdr *msg, size_t size, int flags) +int bt_sock_stream_recvmsg(struct socket *sock, struct msghdr *msg, + size_t size, int flags) { struct sock *sk = sock->sk; int err = 0; diff --git a/net/bluetooth/hci_sock.c b/net/bluetooth/hci_sock.c index 37198fb99ffe..aa8be4cb19a1 100644 --- a/net/bluetooth/hci_sock.c +++ b/net/bluetooth/hci_sock.c @@ -826,8 +826,8 @@ static void hci_sock_cmsg(struct sock *sk, struct msghdr *msg, } } -static int hci_sock_recvmsg(struct kiocb *iocb, struct socket *sock, - struct msghdr *msg, size_t len, int flags) +static int hci_sock_recvmsg(struct socket *sock, struct msghdr *msg, size_t len, + int flags) { int noblock = flags & MSG_DONTWAIT; struct sock *sk = sock->sk; @@ -871,8 +871,8 @@ static int hci_sock_recvmsg(struct kiocb *iocb, struct socket *sock, return err ? : copied; } -static int hci_sock_sendmsg(struct kiocb *iocb, struct socket *sock, - struct msghdr *msg, size_t len) +static int hci_sock_sendmsg(struct socket *sock, struct msghdr *msg, + size_t len) { struct sock *sk = sock->sk; struct hci_dev *hdev; diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c index 60694f0f4c73..9070720eedc8 100644 --- a/net/bluetooth/l2cap_sock.c +++ b/net/bluetooth/l2cap_sock.c @@ -944,8 +944,8 @@ static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname, return err; } -static int l2cap_sock_sendmsg(struct kiocb *iocb, struct socket *sock, - struct msghdr *msg, size_t len) +static int l2cap_sock_sendmsg(struct socket *sock, struct msghdr *msg, + size_t len) { struct sock *sk = sock->sk; struct l2cap_chan *chan = l2cap_pi(sk)->chan; @@ -976,8 +976,8 @@ static int l2cap_sock_sendmsg(struct kiocb *iocb, struct socket *sock, return err; } -static int l2cap_sock_recvmsg(struct kiocb *iocb, struct socket *sock, - struct msghdr *msg, size_t len, int flags) +static int l2cap_sock_recvmsg(struct socket *sock, struct msghdr *msg, + size_t len, int flags) { struct sock *sk = sock->sk; struct l2cap_pinfo *pi = l2cap_pi(sk); @@ -1004,9 +1004,9 @@ static int l2cap_sock_recvmsg(struct kiocb *iocb, struct socket *sock, release_sock(sk); if (sock->type == SOCK_STREAM) - err = bt_sock_stream_recvmsg(iocb, sock, msg, len, flags); + err = bt_sock_stream_recvmsg(sock, msg, len, flags); else - err = bt_sock_recvmsg(iocb, sock, msg, len, flags); + err = bt_sock_recvmsg(sock, msg, len, flags); if (pi->chan->mode != L2CAP_MODE_ERTM) return err; diff --git a/net/bluetooth/rfcomm/sock.c b/net/bluetooth/rfcomm/sock.c index 3c6d2c8ac1a4..825e8fb5114b 100644 --- a/net/bluetooth/rfcomm/sock.c +++ b/net/bluetooth/rfcomm/sock.c @@ -549,8 +549,8 @@ static int rfcomm_sock_getname(struct socket *sock, struct sockaddr *addr, int * return 0; } -static int rfcomm_sock_sendmsg(struct kiocb *iocb, struct socket *sock, - struct msghdr *msg, size_t len) +static int rfcomm_sock_sendmsg(struct socket *sock, struct msghdr *msg, + size_t len) { struct sock *sk = sock->sk; struct rfcomm_dlc *d = rfcomm_pi(sk)->dlc; @@ -615,8 +615,8 @@ done: return sent; } -static int rfcomm_sock_recvmsg(struct kiocb *iocb, struct socket *sock, - struct msghdr *msg, size_t size, int flags) +static int rfcomm_sock_recvmsg(struct socket *sock, struct msghdr *msg, + size_t size, int flags) { struct sock *sk = sock->sk; struct rfcomm_dlc *d = rfcomm_pi(sk)->dlc; @@ -627,7 +627,7 @@ static int rfcomm_sock_recvmsg(struct kiocb *iocb, struct socket *sock, return 0; } - len = bt_sock_stream_recvmsg(iocb, sock, msg, size, flags); + len = bt_sock_stream_recvmsg(sock, msg, size, flags); lock_sock(sk); if (!(flags & MSG_PEEK) && len > 0) diff --git a/net/bluetooth/sco.c b/net/bluetooth/sco.c index 76321b546e84..2bb7ef46bb99 100644 --- a/net/bluetooth/sco.c +++ b/net/bluetooth/sco.c @@ -688,8 +688,8 @@ static int sco_sock_getname(struct socket *sock, struct sockaddr *addr, int *len return 0; } -static int sco_sock_sendmsg(struct kiocb *iocb, struct socket *sock, - struct msghdr *msg, size_t len) +static int sco_sock_sendmsg(struct socket *sock, struct msghdr *msg, + size_t len) { struct sock *sk = sock->sk; int err; @@ -758,8 +758,8 @@ static void sco_conn_defer_accept(struct hci_conn *conn, u16 setting) } } -static int sco_sock_recvmsg(struct kiocb *iocb, struct socket *sock, - struct msghdr *msg, size_t len, int flags) +static int sco_sock_recvmsg(struct socket *sock, struct msghdr *msg, + size_t len, int flags) { struct sock *sk = sock->sk; struct sco_pinfo *pi = sco_pi(sk); @@ -777,7 +777,7 @@ static int sco_sock_recvmsg(struct kiocb *iocb, struct socket *sock, release_sock(sk); - return bt_sock_recvmsg(iocb, sock, msg, len, flags); + return bt_sock_recvmsg(sock, msg, len, flags); } static int sco_sock_setsockopt(struct socket *sock, int level, int optname, char __user *optval, unsigned int optlen) diff --git a/net/caif/caif_socket.c b/net/caif/caif_socket.c index 769b185fefbd..b6bf51bb187d 100644 --- a/net/caif/caif_socket.c +++ b/net/caif/caif_socket.c @@ -271,8 +271,8 @@ static void caif_check_flow_release(struct sock *sk) * Copied from unix_dgram_recvmsg, but removed credit checks, * changed locking, address handling and added MSG_TRUNC. */ -static int caif_seqpkt_recvmsg(struct kiocb *iocb, struct socket *sock, - struct msghdr *m, size_t len, int flags) +static int caif_seqpkt_recvmsg(struct socket *sock, struct msghdr *m, + size_t len, int flags) { struct sock *sk = sock->sk; @@ -343,9 +343,8 @@ static long caif_stream_data_wait(struct sock *sk, long timeo) * Copied from unix_stream_recvmsg, but removed credit checks, * changed locking calls, changed address handling. */ -static int caif_stream_recvmsg(struct kiocb *iocb, struct socket *sock, - struct msghdr *msg, size_t size, - int flags) +static int caif_stream_recvmsg(struct socket *sock, struct msghdr *msg, + size_t size, int flags) { struct sock *sk = sock->sk; int copied = 0; @@ -511,8 +510,8 @@ static int transmit_skb(struct sk_buff *skb, struct caifsock *cf_sk, } /* Copied from af_unix:unix_dgram_sendmsg, and adapted to CAIF */ -static int caif_seqpkt_sendmsg(struct kiocb *kiocb, struct socket *sock, - struct msghdr *msg, size_t len) +static int caif_seqpkt_sendmsg(struct socket *sock, struct msghdr *msg, + size_t len) { struct sock *sk = sock->sk; struct caifsock *cf_sk = container_of(sk, struct caifsock, sk); @@ -586,8 +585,8 @@ err: * Changed removed permission handling and added waiting for flow on * and other minor adaptations. */ -static int caif_stream_sendmsg(struct kiocb *kiocb, struct socket *sock, - struct msghdr *msg, size_t len) +static int caif_stream_sendmsg(struct socket *sock, struct msghdr *msg, + size_t len) { struct sock *sk = sock->sk; struct caifsock *cf_sk = container_of(sk, struct caifsock, sk); diff --git a/net/can/bcm.c b/net/can/bcm.c index d559f922326d..b523453585be 100644 --- a/net/can/bcm.c +++ b/net/can/bcm.c @@ -1231,8 +1231,7 @@ static int bcm_tx_send(struct msghdr *msg, int ifindex, struct sock *sk) /* * bcm_sendmsg - process BCM commands (opcodes) from the userspace */ -static int bcm_sendmsg(struct kiocb *iocb, struct socket *sock, - struct msghdr *msg, size_t size) +static int bcm_sendmsg(struct socket *sock, struct msghdr *msg, size_t size) { struct sock *sk = sock->sk; struct bcm_sock *bo = bcm_sk(sk); @@ -1535,8 +1534,8 @@ static int bcm_connect(struct socket *sock, struct sockaddr *uaddr, int len, return 0; } -static int bcm_recvmsg(struct kiocb *iocb, struct socket *sock, - struct msghdr *msg, size_t size, int flags) +static int bcm_recvmsg(struct socket *sock, struct msghdr *msg, size_t size, + int flags) { struct sock *sk = sock->sk; struct sk_buff *skb; diff --git a/net/can/raw.c b/net/can/raw.c index 94601b7ff0a3..63ffdb0f3a23 100644 --- a/net/can/raw.c +++ b/net/can/raw.c @@ -658,8 +658,7 @@ static int raw_getsockopt(struct socket *sock, int level, int optname, return 0; } -static int raw_sendmsg(struct kiocb *iocb, struct socket *sock, - struct msghdr *msg, size_t size) +static int raw_sendmsg(struct socket *sock, struct msghdr *msg, size_t size) { struct sock *sk = sock->sk; struct raw_sock *ro = raw_sk(sk); @@ -728,8 +727,8 @@ send_failed: return err; } -static int raw_recvmsg(struct kiocb *iocb, struct socket *sock, - struct msghdr *msg, size_t size, int flags) +static int raw_recvmsg(struct socket *sock, struct msghdr *msg, size_t size, + int flags) { struct sock *sk = sock->sk; struct sk_buff *skb; diff --git a/net/core/sock.c b/net/core/sock.c index 9c74fc8f0e32..726e1f99aa8d 100644 --- a/net/core/sock.c +++ b/net/core/sock.c @@ -2163,15 +2163,14 @@ int sock_no_getsockopt(struct socket *sock, int level, int optname, } EXPORT_SYMBOL(sock_no_getsockopt); -int sock_no_sendmsg(struct kiocb *iocb, struct socket *sock, struct msghdr *m, - size_t len) +int sock_no_sendmsg(struct socket *sock, struct msghdr *m, size_t len) { return -EOPNOTSUPP; } EXPORT_SYMBOL(sock_no_sendmsg); -int sock_no_recvmsg(struct kiocb *iocb, struct socket *sock, struct msghdr *m, - size_t len, int flags) +int sock_no_recvmsg(struct socket *sock, struct msghdr *m, size_t len, + int flags) { return -EOPNOTSUPP; } @@ -2543,14 +2542,14 @@ int compat_sock_common_getsockopt(struct socket *sock, int level, int optname, EXPORT_SYMBOL(compat_sock_common_getsockopt); #endif -int sock_common_recvmsg(struct kiocb *iocb, struct socket *sock, - struct msghdr *msg, size_t size, int flags) +int sock_common_recvmsg(struct socket *sock, struct msghdr *msg, size_t size, + int flags) { struct sock *sk = sock->sk; int addr_len = 0; int err; - err = sk->sk_prot->recvmsg(iocb, sk, msg, size, flags & MSG_DONTWAIT, + err = sk->sk_prot->recvmsg(sk, msg, size, flags & MSG_DONTWAIT, flags & ~MSG_DONTWAIT, &addr_len); if (err >= 0) msg->msg_namelen = addr_len; diff --git a/net/dccp/dccp.h b/net/dccp/dccp.h index e4c144fa706f..3b1d64d6e093 100644 --- a/net/dccp/dccp.h +++ b/net/dccp/dccp.h @@ -310,11 +310,9 @@ int compat_dccp_setsockopt(struct sock *sk, int level, int optname, char __user *optval, unsigned int optlen); #endif int dccp_ioctl(struct sock *sk, int cmd, unsigned long arg); -int dccp_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, - size_t size); -int dccp_recvmsg(struct kiocb *iocb, struct sock *sk, - struct msghdr *msg, size_t len, int nonblock, int flags, - int *addr_len); +int dccp_sendmsg(struct sock *sk, struct msghdr *msg, size_t size); +int dccp_recvmsg(struct sock *sk, struct msghdr *msg, size_t len, int nonblock, + int flags, int *addr_len); void dccp_shutdown(struct sock *sk, int how); int inet_dccp_listen(struct socket *sock, int backlog); unsigned int dccp_poll(struct file *file, struct socket *sock, diff --git a/net/dccp/probe.c b/net/dccp/probe.c index 595ddf0459db..d8346d0eadeb 100644 --- a/net/dccp/probe.c +++ b/net/dccp/probe.c @@ -72,8 +72,7 @@ static void printl(const char *fmt, ...) wake_up(&dccpw.wait); } -static int jdccp_sendmsg(struct kiocb *iocb, struct sock *sk, - struct msghdr *msg, size_t size) +static int jdccp_sendmsg(struct sock *sk, struct msghdr *msg, size_t size) { const struct inet_sock *inet = inet_sk(sk); struct ccid3_hc_tx_sock *hc = NULL; diff --git a/net/dccp/proto.c b/net/dccp/proto.c index e171b780b499..52a94016526d 100644 --- a/net/dccp/proto.c +++ b/net/dccp/proto.c @@ -741,8 +741,7 @@ static int dccp_msghdr_parse(struct msghdr *msg, struct sk_buff *skb) return 0; } -int dccp_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, - size_t len) +int dccp_sendmsg(struct sock *sk, struct msghdr *msg, size_t len) { const struct dccp_sock *dp = dccp_sk(sk); const int flags = msg->msg_flags; @@ -806,8 +805,8 @@ out_discard: EXPORT_SYMBOL_GPL(dccp_sendmsg); -int dccp_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, - size_t len, int nonblock, int flags, int *addr_len) +int dccp_recvmsg(struct sock *sk, struct msghdr *msg, size_t len, int nonblock, + int flags, int *addr_len) { const struct dccp_hdr *dh; long timeo; diff --git a/net/decnet/af_decnet.c b/net/decnet/af_decnet.c index 810228646de3..754484b3cd0e 100644 --- a/net/decnet/af_decnet.c +++ b/net/decnet/af_decnet.c @@ -1669,8 +1669,8 @@ static int dn_data_ready(struct sock *sk, struct sk_buff_head *q, int flags, int } -static int dn_recvmsg(struct kiocb *iocb, struct socket *sock, - struct msghdr *msg, size_t size, int flags) +static int dn_recvmsg(struct socket *sock, struct msghdr *msg, size_t size, + int flags) { struct sock *sk = sock->sk; struct dn_scp *scp = DN_SK(sk); @@ -1905,8 +1905,7 @@ static inline struct sk_buff *dn_alloc_send_pskb(struct sock *sk, return skb; } -static int dn_sendmsg(struct kiocb *iocb, struct socket *sock, - struct msghdr *msg, size_t size) +static int dn_sendmsg(struct socket *sock, struct msghdr *msg, size_t size) { struct sock *sk = sock->sk; struct dn_scp *scp = DN_SK(sk); diff --git a/net/ieee802154/socket.c b/net/ieee802154/socket.c index 2878d8ca6d3b..b60c65f70346 100644 --- a/net/ieee802154/socket.c +++ b/net/ieee802154/socket.c @@ -98,12 +98,12 @@ static int ieee802154_sock_release(struct socket *sock) return 0; } -static int ieee802154_sock_sendmsg(struct kiocb *iocb, struct socket *sock, - struct msghdr *msg, size_t len) +static int ieee802154_sock_sendmsg(struct socket *sock, struct msghdr *msg, + size_t len) { struct sock *sk = sock->sk; - return sk->sk_prot->sendmsg(iocb, sk, msg, len); + return sk->sk_prot->sendmsg(sk, msg, len); } static int ieee802154_sock_bind(struct socket *sock, struct sockaddr *uaddr, @@ -255,8 +255,7 @@ static int raw_disconnect(struct sock *sk, int flags) return 0; } -static int raw_sendmsg(struct kiocb *iocb, struct sock *sk, - struct msghdr *msg, size_t size) +static int raw_sendmsg(struct sock *sk, struct msghdr *msg, size_t size) { struct net_device *dev; unsigned int mtu; @@ -327,8 +326,8 @@ out: return err; } -static int raw_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, - size_t len, int noblock, int flags, int *addr_len) +static int raw_recvmsg(struct sock *sk, struct msghdr *msg, size_t len, + int noblock, int flags, int *addr_len) { size_t copied = 0; int err = -EOPNOTSUPP; @@ -615,8 +614,7 @@ static int dgram_disconnect(struct sock *sk, int flags) return 0; } -static int dgram_sendmsg(struct kiocb *iocb, struct sock *sk, - struct msghdr *msg, size_t size) +static int dgram_sendmsg(struct sock *sk, struct msghdr *msg, size_t size) { struct net_device *dev; unsigned int mtu; @@ -715,9 +713,8 @@ out: return err; } -static int dgram_recvmsg(struct kiocb *iocb, struct sock *sk, - struct msghdr *msg, size_t len, int noblock, - int flags, int *addr_len) +static int dgram_recvmsg(struct sock *sk, struct msghdr *msg, size_t len, + int noblock, int flags, int *addr_len) { size_t copied = 0; int err = -EOPNOTSUPP; diff --git a/net/ipv4/af_inet.c b/net/ipv4/af_inet.c index 4ce954cc94a4..64a9c0fdc4aa 100644 --- a/net/ipv4/af_inet.c +++ b/net/ipv4/af_inet.c @@ -716,8 +716,7 @@ int inet_getname(struct socket *sock, struct sockaddr *uaddr, } EXPORT_SYMBOL(inet_getname); -int inet_sendmsg(struct kiocb *iocb, struct socket *sock, struct msghdr *msg, - size_t size) +int inet_sendmsg(struct socket *sock, struct msghdr *msg, size_t size) { struct sock *sk = sock->sk; @@ -728,7 +727,7 @@ int inet_sendmsg(struct kiocb *iocb, struct socket *sock, struct msghdr *msg, inet_autobind(sk)) return -EAGAIN; - return sk->sk_prot->sendmsg(iocb, sk, msg, size); + return sk->sk_prot->sendmsg(sk, msg, size); } EXPORT_SYMBOL(inet_sendmsg); @@ -750,8 +749,8 @@ ssize_t inet_sendpage(struct socket *sock, struct page *page, int offset, } EXPORT_SYMBOL(inet_sendpage); -int inet_recvmsg(struct kiocb *iocb, struct socket *sock, struct msghdr *msg, - size_t size, int flags) +int inet_recvmsg(struct socket *sock, struct msghdr *msg, size_t size, + int flags) { struct sock *sk = sock->sk; int addr_len = 0; @@ -759,7 +758,7 @@ int inet_recvmsg(struct kiocb *iocb, struct socket *sock, struct msghdr *msg, sock_rps_record_flow(sk); - err = sk->sk_prot->recvmsg(iocb, sk, msg, size, flags & MSG_DONTWAIT, + err = sk->sk_prot->recvmsg(sk, msg, size, flags & MSG_DONTWAIT, flags & ~MSG_DONTWAIT, &addr_len); if (err >= 0) msg->msg_namelen = addr_len; diff --git a/net/ipv4/ping.c b/net/ipv4/ping.c index e9f66e1cda50..3648e7f32f3d 100644 --- a/net/ipv4/ping.c +++ b/net/ipv4/ping.c @@ -684,8 +684,7 @@ int ping_common_sendmsg(int family, struct msghdr *msg, size_t len, } EXPORT_SYMBOL_GPL(ping_common_sendmsg); -static int ping_v4_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, - size_t len) +static int ping_v4_sendmsg(struct sock *sk, struct msghdr *msg, size_t len) { struct net *net = sock_net(sk); struct flowi4 fl4; @@ -841,8 +840,8 @@ do_confirm: goto out; } -int ping_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, - size_t len, int noblock, int flags, int *addr_len) +int ping_recvmsg(struct sock *sk, struct msghdr *msg, size_t len, int noblock, + int flags, int *addr_len) { struct inet_sock *isk = inet_sk(sk); int family = sk->sk_family; diff --git a/net/ipv4/raw.c b/net/ipv4/raw.c index f027a708b7e0..923cf538fce1 100644 --- a/net/ipv4/raw.c +++ b/net/ipv4/raw.c @@ -481,8 +481,7 @@ static int raw_getfrag(void *from, char *to, int offset, int len, int odd, return ip_generic_getfrag(rfv->msg, to, offset, len, odd, skb); } -static int raw_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, - size_t len) +static int raw_sendmsg(struct sock *sk, struct msghdr *msg, size_t len) { struct inet_sock *inet = inet_sk(sk); struct ipcm_cookie ipc; @@ -709,8 +708,8 @@ out: return ret; * we return it, otherwise we block. */ -static int raw_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, - size_t len, int noblock, int flags, int *addr_len) +static int raw_recvmsg(struct sock *sk, struct msghdr *msg, size_t len, + int noblock, int flags, int *addr_len) { struct inet_sock *inet = inet_sk(sk); size_t copied = 0; diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index 4b57ea8dabc7..d939c35001f9 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -1064,8 +1064,7 @@ static int tcp_sendmsg_fastopen(struct sock *sk, struct msghdr *msg, return err; } -int tcp_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, - size_t size) +int tcp_sendmsg(struct sock *sk, struct msghdr *msg, size_t size) { struct tcp_sock *tp = tcp_sk(sk); struct sk_buff *skb; @@ -1543,8 +1542,8 @@ EXPORT_SYMBOL(tcp_read_sock); * Probably, code can be easily improved even more. */ -int tcp_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, - size_t len, int nonblock, int flags, int *addr_len) +int tcp_recvmsg(struct sock *sk, struct msghdr *msg, size_t len, int nonblock, + int flags, int *addr_len) { struct tcp_sock *tp = tcp_sk(sk); int copied = 0; diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c index 0224f930c613..f27556e2158b 100644 --- a/net/ipv4/udp.c +++ b/net/ipv4/udp.c @@ -873,8 +873,7 @@ out: } EXPORT_SYMBOL(udp_push_pending_frames); -int udp_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, - size_t len) +int udp_sendmsg(struct sock *sk, struct msghdr *msg, size_t len) { struct inet_sock *inet = inet_sk(sk); struct udp_sock *up = udp_sk(sk); @@ -1136,7 +1135,7 @@ int udp_sendpage(struct sock *sk, struct page *page, int offset, * sendpage interface can't pass. * This will succeed only when the socket is connected. */ - ret = udp_sendmsg(NULL, sk, &msg, 0); + ret = udp_sendmsg(sk, &msg, 0); if (ret < 0) return ret; } @@ -1254,8 +1253,8 @@ EXPORT_SYMBOL(udp_ioctl); * return it, otherwise we block. */ -int udp_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, - size_t len, int noblock, int flags, int *addr_len) +int udp_recvmsg(struct sock *sk, struct msghdr *msg, size_t len, int noblock, + int flags, int *addr_len) { struct inet_sock *inet = inet_sk(sk); DECLARE_SOCKADDR(struct sockaddr_in *, sin, msg->msg_name); diff --git a/net/ipv4/udp_impl.h b/net/ipv4/udp_impl.h index f3c27899f62b..7e0fe4bdd967 100644 --- a/net/ipv4/udp_impl.h +++ b/net/ipv4/udp_impl.h @@ -21,8 +21,8 @@ int compat_udp_setsockopt(struct sock *sk, int level, int optname, int compat_udp_getsockopt(struct sock *sk, int level, int optname, char __user *optval, int __user *optlen); #endif -int udp_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, - size_t len, int noblock, int flags, int *addr_len); +int udp_recvmsg(struct sock *sk, struct msghdr *msg, size_t len, int noblock, + int flags, int *addr_len); int udp_sendpage(struct sock *sk, struct page *page, int offset, size_t size, int flags); int udp_queue_rcv_skb(struct sock *sk, struct sk_buff *skb); diff --git a/net/ipv6/ping.c b/net/ipv6/ping.c index bd46f736f61d..fee25c0ed1f5 100644 --- a/net/ipv6/ping.c +++ b/net/ipv6/ping.c @@ -77,8 +77,7 @@ static int dummy_ipv6_chk_addr(struct net *net, const struct in6_addr *addr, return 0; } -int ping_v6_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, - size_t len) +int ping_v6_sendmsg(struct sock *sk, struct msghdr *msg, size_t len) { struct inet_sock *inet = inet_sk(sk); struct ipv6_pinfo *np = inet6_sk(sk); diff --git a/net/ipv6/raw.c b/net/ipv6/raw.c index 0d84b2c7f24e..a5287b3582a4 100644 --- a/net/ipv6/raw.c +++ b/net/ipv6/raw.c @@ -456,9 +456,8 @@ int rawv6_rcv(struct sock *sk, struct sk_buff *skb) * we return it, otherwise we block. */ -static int rawv6_recvmsg(struct kiocb *iocb, struct sock *sk, - struct msghdr *msg, size_t len, - int noblock, int flags, int *addr_len) +static int rawv6_recvmsg(struct sock *sk, struct msghdr *msg, size_t len, + int noblock, int flags, int *addr_len) { struct ipv6_pinfo *np = inet6_sk(sk); DECLARE_SOCKADDR(struct sockaddr_in6 *, sin6, msg->msg_name); @@ -730,8 +729,7 @@ static int raw6_getfrag(void *from, char *to, int offset, int len, int odd, return ip_generic_getfrag(rfv->msg, to, offset, len, odd, skb); } -static int rawv6_sendmsg(struct kiocb *iocb, struct sock *sk, - struct msghdr *msg, size_t len) +static int rawv6_sendmsg(struct sock *sk, struct msghdr *msg, size_t len) { struct ipv6_txoptions opt_space; DECLARE_SOCKADDR(struct sockaddr_in6 *, sin6, msg->msg_name); diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c index d048d46779fc..70568a4548e4 100644 --- a/net/ipv6/udp.c +++ b/net/ipv6/udp.c @@ -391,8 +391,7 @@ EXPORT_SYMBOL_GPL(udp6_lib_lookup); * return it, otherwise we block. */ -int udpv6_recvmsg(struct kiocb *iocb, struct sock *sk, - struct msghdr *msg, size_t len, +int udpv6_recvmsg(struct sock *sk, struct msghdr *msg, size_t len, int noblock, int flags, int *addr_len) { struct ipv6_pinfo *np = inet6_sk(sk); @@ -1101,8 +1100,7 @@ out: return err; } -int udpv6_sendmsg(struct kiocb *iocb, struct sock *sk, - struct msghdr *msg, size_t len) +int udpv6_sendmsg(struct sock *sk, struct msghdr *msg, size_t len) { struct ipv6_txoptions opt_space; struct udp_sock *up = udp_sk(sk); @@ -1164,12 +1162,12 @@ int udpv6_sendmsg(struct kiocb *iocb, struct sock *sk, do_udp_sendmsg: if (__ipv6_only_sock(sk)) return -ENETUNREACH; - return udp_sendmsg(iocb, sk, msg, len); + return udp_sendmsg(sk, msg, len); } } if (up->pending == AF_INET) - return udp_sendmsg(iocb, sk, msg, len); + return udp_sendmsg(sk, msg, len); /* Rough check on arithmetic overflow, better check is made in ip6_append_data(). diff --git a/net/ipv6/udp_impl.h b/net/ipv6/udp_impl.h index c779c3c90b9d..0682c031ccdc 100644 --- a/net/ipv6/udp_impl.h +++ b/net/ipv6/udp_impl.h @@ -23,10 +23,9 @@ int compat_udpv6_setsockopt(struct sock *sk, int level, int optname, int compat_udpv6_getsockopt(struct sock *sk, int level, int optname, char __user *optval, int __user *optlen); #endif -int udpv6_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, - size_t len); -int udpv6_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, - size_t len, int noblock, int flags, int *addr_len); +int udpv6_sendmsg(struct sock *sk, struct msghdr *msg, size_t len); +int udpv6_recvmsg(struct sock *sk, struct msghdr *msg, size_t len, int noblock, + int flags, int *addr_len); int udpv6_queue_rcv_skb(struct sock *sk, struct sk_buff *skb); void udpv6_destroy_sock(struct sock *sk); diff --git a/net/ipx/af_ipx.c b/net/ipx/af_ipx.c index f11ad1d95e0e..4ea5d7497b5f 100644 --- a/net/ipx/af_ipx.c +++ b/net/ipx/af_ipx.c @@ -1688,8 +1688,7 @@ out: return rc; } -static int ipx_sendmsg(struct kiocb *iocb, struct socket *sock, - struct msghdr *msg, size_t len) +static int ipx_sendmsg(struct socket *sock, struct msghdr *msg, size_t len) { struct sock *sk = sock->sk; struct ipx_sock *ipxs = ipx_sk(sk); @@ -1754,8 +1753,8 @@ out: } -static int ipx_recvmsg(struct kiocb *iocb, struct socket *sock, - struct msghdr *msg, size_t size, int flags) +static int ipx_recvmsg(struct socket *sock, struct msghdr *msg, size_t size, + int flags) { struct sock *sk = sock->sk; struct ipx_sock *ipxs = ipx_sk(sk); diff --git a/net/irda/af_irda.c b/net/irda/af_irda.c index 568edc72d737..ee0ea25c8e7a 100644 --- a/net/irda/af_irda.c +++ b/net/irda/af_irda.c @@ -1256,14 +1256,13 @@ static int irda_release(struct socket *sock) } /* - * Function irda_sendmsg (iocb, sock, msg, len) + * Function irda_sendmsg (sock, msg, len) * * Send message down to TinyTP. This function is used for both STREAM and * SEQPACK services. This is possible since it forces the client to * fragment the message if necessary */ -static int irda_sendmsg(struct kiocb *iocb, struct socket *sock, - struct msghdr *msg, size_t len) +static int irda_sendmsg(struct socket *sock, struct msghdr *msg, size_t len) { struct sock *sk = sock->sk; struct irda_sock *self; @@ -1348,13 +1347,13 @@ out: } /* - * Function irda_recvmsg_dgram (iocb, sock, msg, size, flags) + * Function irda_recvmsg_dgram (sock, msg, size, flags) * * Try to receive message and copy it to user. The frame is discarded * after being read, regardless of how much the user actually read */ -static int irda_recvmsg_dgram(struct kiocb *iocb, struct socket *sock, - struct msghdr *msg, size_t size, int flags) +static int irda_recvmsg_dgram(struct socket *sock, struct msghdr *msg, + size_t size, int flags) { struct sock *sk = sock->sk; struct irda_sock *self = irda_sk(sk); @@ -1398,10 +1397,10 @@ static int irda_recvmsg_dgram(struct kiocb *iocb, struct socket *sock, } /* - * Function irda_recvmsg_stream (iocb, sock, msg, size, flags) + * Function irda_recvmsg_stream (sock, msg, size, flags) */ -static int irda_recvmsg_stream(struct kiocb *iocb, struct socket *sock, - struct msghdr *msg, size_t size, int flags) +static int irda_recvmsg_stream(struct socket *sock, struct msghdr *msg, + size_t size, int flags) { struct sock *sk = sock->sk; struct irda_sock *self = irda_sk(sk); @@ -1515,14 +1514,14 @@ static int irda_recvmsg_stream(struct kiocb *iocb, struct socket *sock, } /* - * Function irda_sendmsg_dgram (iocb, sock, msg, len) + * Function irda_sendmsg_dgram (sock, msg, len) * * Send message down to TinyTP for the unreliable sequenced * packet service... * */ -static int irda_sendmsg_dgram(struct kiocb *iocb, struct socket *sock, - struct msghdr *msg, size_t len) +static int irda_sendmsg_dgram(struct socket *sock, struct msghdr *msg, + size_t len) { struct sock *sk = sock->sk; struct irda_sock *self; @@ -1594,14 +1593,14 @@ out: } /* - * Function irda_sendmsg_ultra (iocb, sock, msg, len) + * Function irda_sendmsg_ultra (sock, msg, len) * * Send message down to IrLMP for the unreliable Ultra * packet service... */ #ifdef CONFIG_IRDA_ULTRA -static int irda_sendmsg_ultra(struct kiocb *iocb, struct socket *sock, - struct msghdr *msg, size_t len) +static int irda_sendmsg_ultra(struct socket *sock, struct msghdr *msg, + size_t len) { struct sock *sk = sock->sk; struct irda_sock *self; diff --git a/net/iucv/af_iucv.c b/net/iucv/af_iucv.c index 2e9953b2db84..94b4c898a116 100644 --- a/net/iucv/af_iucv.c +++ b/net/iucv/af_iucv.c @@ -1026,8 +1026,8 @@ static int iucv_send_iprm(struct iucv_path *path, struct iucv_message *msg, (void *) prmdata, 8); } -static int iucv_sock_sendmsg(struct kiocb *iocb, struct socket *sock, - struct msghdr *msg, size_t len) +static int iucv_sock_sendmsg(struct socket *sock, struct msghdr *msg, + size_t len) { struct sock *sk = sock->sk; struct iucv_sock *iucv = iucv_sk(sk); @@ -1317,8 +1317,8 @@ static void iucv_process_message_q(struct sock *sk) } } -static int iucv_sock_recvmsg(struct kiocb *iocb, struct socket *sock, - struct msghdr *msg, size_t len, int flags) +static int iucv_sock_recvmsg(struct socket *sock, struct msghdr *msg, + size_t len, int flags) { int noblock = flags & MSG_DONTWAIT; struct sock *sk = sock->sk; diff --git a/net/key/af_key.c b/net/key/af_key.c index f8ac939d52b4..9255fd9d94bc 100644 --- a/net/key/af_key.c +++ b/net/key/af_key.c @@ -3588,8 +3588,7 @@ static int pfkey_send_migrate(const struct xfrm_selector *sel, u8 dir, u8 type, } #endif -static int pfkey_sendmsg(struct kiocb *kiocb, - struct socket *sock, struct msghdr *msg, size_t len) +static int pfkey_sendmsg(struct socket *sock, struct msghdr *msg, size_t len) { struct sock *sk = sock->sk; struct sk_buff *skb = NULL; @@ -3630,8 +3629,7 @@ out: return err ? : len; } -static int pfkey_recvmsg(struct kiocb *kiocb, - struct socket *sock, struct msghdr *msg, size_t len, +static int pfkey_recvmsg(struct socket *sock, struct msghdr *msg, size_t len, int flags) { struct sock *sk = sock->sk; diff --git a/net/l2tp/l2tp_ip.c b/net/l2tp/l2tp_ip.c index 05dfc8aa36af..79649937ec71 100644 --- a/net/l2tp/l2tp_ip.c +++ b/net/l2tp/l2tp_ip.c @@ -385,7 +385,7 @@ drop: /* Userspace will call sendmsg() on the tunnel socket to send L2TP * control frames. */ -static int l2tp_ip_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, size_t len) +static int l2tp_ip_sendmsg(struct sock *sk, struct msghdr *msg, size_t len) { struct sk_buff *skb; int rc; @@ -506,7 +506,7 @@ no_route: goto out; } -static int l2tp_ip_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, +static int l2tp_ip_recvmsg(struct sock *sk, struct msghdr *msg, size_t len, int noblock, int flags, int *addr_len) { struct inet_sock *inet = inet_sk(sk); diff --git a/net/l2tp/l2tp_ip6.c b/net/l2tp/l2tp_ip6.c index 8611f1b63141..d1ded3777815 100644 --- a/net/l2tp/l2tp_ip6.c +++ b/net/l2tp/l2tp_ip6.c @@ -480,8 +480,7 @@ out: /* Userspace will call sendmsg() on the tunnel socket to send L2TP * control frames. */ -static int l2tp_ip6_sendmsg(struct kiocb *iocb, struct sock *sk, - struct msghdr *msg, size_t len) +static int l2tp_ip6_sendmsg(struct sock *sk, struct msghdr *msg, size_t len) { struct ipv6_txoptions opt_space; DECLARE_SOCKADDR(struct sockaddr_l2tpip6 *, lsa, msg->msg_name); @@ -643,9 +642,8 @@ do_confirm: goto done; } -static int l2tp_ip6_recvmsg(struct kiocb *iocb, struct sock *sk, - struct msghdr *msg, size_t len, int noblock, - int flags, int *addr_len) +static int l2tp_ip6_recvmsg(struct sock *sk, struct msghdr *msg, size_t len, + int noblock, int flags, int *addr_len) { struct ipv6_pinfo *np = inet6_sk(sk); DECLARE_SOCKADDR(struct sockaddr_l2tpip6 *, lsa, msg->msg_name); diff --git a/net/l2tp/l2tp_ppp.c b/net/l2tp/l2tp_ppp.c index cc7a828fc914..e9b0dec56b8e 100644 --- a/net/l2tp/l2tp_ppp.c +++ b/net/l2tp/l2tp_ppp.c @@ -185,9 +185,8 @@ static int pppol2tp_recv_payload_hook(struct sk_buff *skb) /* Receive message. This is the recvmsg for the PPPoL2TP socket. */ -static int pppol2tp_recvmsg(struct kiocb *iocb, struct socket *sock, - struct msghdr *msg, size_t len, - int flags) +static int pppol2tp_recvmsg(struct socket *sock, struct msghdr *msg, + size_t len, int flags) { int err; struct sk_buff *skb; @@ -295,7 +294,7 @@ static void pppol2tp_session_sock_put(struct l2tp_session *session) * when a user application does a sendmsg() on the session socket. L2TP and * PPP headers must be inserted into the user's data. */ -static int pppol2tp_sendmsg(struct kiocb *iocb, struct socket *sock, struct msghdr *m, +static int pppol2tp_sendmsg(struct socket *sock, struct msghdr *m, size_t total_len) { static const unsigned char ppph[2] = { 0xff, 0x03 }; diff --git a/net/llc/af_llc.c b/net/llc/af_llc.c index 2c0b83ce43bd..17a8dff06090 100644 --- a/net/llc/af_llc.c +++ b/net/llc/af_llc.c @@ -704,8 +704,8 @@ out: * Copy received data to the socket user. * Returns non-negative upon success, negative otherwise. */ -static int llc_ui_recvmsg(struct kiocb *iocb, struct socket *sock, - struct msghdr *msg, size_t len, int flags) +static int llc_ui_recvmsg(struct socket *sock, struct msghdr *msg, size_t len, + int flags) { DECLARE_SOCKADDR(struct sockaddr_llc *, uaddr, msg->msg_name); const int nonblock = flags & MSG_DONTWAIT; @@ -878,8 +878,7 @@ copy_uaddr: * Transmit data provided by the socket user. * Returns non-negative upon success, negative otherwise. */ -static int llc_ui_sendmsg(struct kiocb *iocb, struct socket *sock, - struct msghdr *msg, size_t len) +static int llc_ui_sendmsg(struct socket *sock, struct msghdr *msg, size_t len) { struct sock *sk = sock->sk; struct llc_sock *llc = llc_sk(sk); diff --git a/net/netlink/af_netlink.c b/net/netlink/af_netlink.c index 2702673f0f23..a96025c0583f 100644 --- a/net/netlink/af_netlink.c +++ b/net/netlink/af_netlink.c @@ -2256,8 +2256,7 @@ static void netlink_cmsg_recv_pktinfo(struct msghdr *msg, struct sk_buff *skb) put_cmsg(msg, SOL_NETLINK, NETLINK_PKTINFO, sizeof(info), &info); } -static int netlink_sendmsg(struct kiocb *kiocb, struct socket *sock, - struct msghdr *msg, size_t len) +static int netlink_sendmsg(struct socket *sock, struct msghdr *msg, size_t len) { struct sock *sk = sock->sk; struct netlink_sock *nlk = nlk_sk(sk); @@ -2346,8 +2345,7 @@ out: return err; } -static int netlink_recvmsg(struct kiocb *kiocb, struct socket *sock, - struct msghdr *msg, size_t len, +static int netlink_recvmsg(struct socket *sock, struct msghdr *msg, size_t len, int flags) { struct scm_cookie scm; diff --git a/net/netrom/af_netrom.c b/net/netrom/af_netrom.c index 69f1d5e9959f..b987fd56c3c5 100644 --- a/net/netrom/af_netrom.c +++ b/net/netrom/af_netrom.c @@ -1023,8 +1023,7 @@ int nr_rx_frame(struct sk_buff *skb, struct net_device *dev) return 1; } -static int nr_sendmsg(struct kiocb *iocb, struct socket *sock, - struct msghdr *msg, size_t len) +static int nr_sendmsg(struct socket *sock, struct msghdr *msg, size_t len) { struct sock *sk = sock->sk; struct nr_sock *nr = nr_sk(sk); @@ -1133,8 +1132,8 @@ out: return err; } -static int nr_recvmsg(struct kiocb *iocb, struct socket *sock, - struct msghdr *msg, size_t size, int flags) +static int nr_recvmsg(struct socket *sock, struct msghdr *msg, size_t size, + int flags) { struct sock *sk = sock->sk; DECLARE_SOCKADDR(struct sockaddr_ax25 *, sax, msg->msg_name); diff --git a/net/nfc/llcp_sock.c b/net/nfc/llcp_sock.c index e181e290427c..9578bd6a4f3e 100644 --- a/net/nfc/llcp_sock.c +++ b/net/nfc/llcp_sock.c @@ -750,8 +750,8 @@ error: return ret; } -static int llcp_sock_sendmsg(struct kiocb *iocb, struct socket *sock, - struct msghdr *msg, size_t len) +static int llcp_sock_sendmsg(struct socket *sock, struct msghdr *msg, + size_t len) { struct sock *sk = sock->sk; struct nfc_llcp_sock *llcp_sock = nfc_llcp_sock(sk); @@ -793,8 +793,8 @@ static int llcp_sock_sendmsg(struct kiocb *iocb, struct socket *sock, return nfc_llcp_send_i_frame(llcp_sock, msg, len); } -static int llcp_sock_recvmsg(struct kiocb *iocb, struct socket *sock, - struct msghdr *msg, size_t len, int flags) +static int llcp_sock_recvmsg(struct socket *sock, struct msghdr *msg, + size_t len, int flags) { int noblock = flags & MSG_DONTWAIT; struct sock *sk = sock->sk; diff --git a/net/nfc/rawsock.c b/net/nfc/rawsock.c index 373e138c0ab6..82b4e8024778 100644 --- a/net/nfc/rawsock.c +++ b/net/nfc/rawsock.c @@ -211,8 +211,7 @@ static void rawsock_tx_work(struct work_struct *work) } } -static int rawsock_sendmsg(struct kiocb *iocb, struct socket *sock, - struct msghdr *msg, size_t len) +static int rawsock_sendmsg(struct socket *sock, struct msghdr *msg, size_t len) { struct sock *sk = sock->sk; struct nfc_dev *dev = nfc_rawsock(sk)->dev; @@ -248,8 +247,8 @@ static int rawsock_sendmsg(struct kiocb *iocb, struct socket *sock, return len; } -static int rawsock_recvmsg(struct kiocb *iocb, struct socket *sock, - struct msghdr *msg, size_t len, int flags) +static int rawsock_recvmsg(struct socket *sock, struct msghdr *msg, size_t len, + int flags) { int noblock = flags & MSG_DONTWAIT; struct sock *sk = sock->sk; diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c index 9db83693d736..404c9735aee9 100644 --- a/net/packet/af_packet.c +++ b/net/packet/af_packet.c @@ -1606,8 +1606,8 @@ oom: * protocol layers and you must therefore supply it with a complete frame */ -static int packet_sendmsg_spkt(struct kiocb *iocb, struct socket *sock, - struct msghdr *msg, size_t len) +static int packet_sendmsg_spkt(struct socket *sock, struct msghdr *msg, + size_t len) { struct sock *sk = sock->sk; DECLARE_SOCKADDR(struct sockaddr_pkt *, saddr, msg->msg_name); @@ -2601,8 +2601,7 @@ out: return err; } -static int packet_sendmsg(struct kiocb *iocb, struct socket *sock, - struct msghdr *msg, size_t len) +static int packet_sendmsg(struct socket *sock, struct msghdr *msg, size_t len) { struct sock *sk = sock->sk; struct packet_sock *po = pkt_sk(sk); @@ -2882,8 +2881,8 @@ out: * If necessary we block. */ -static int packet_recvmsg(struct kiocb *iocb, struct socket *sock, - struct msghdr *msg, size_t len, int flags) +static int packet_recvmsg(struct socket *sock, struct msghdr *msg, size_t len, + int flags) { struct sock *sk = sock->sk; struct sk_buff *skb; diff --git a/net/phonet/datagram.c b/net/phonet/datagram.c index 26054b4b467c..5e710435ffa9 100644 --- a/net/phonet/datagram.c +++ b/net/phonet/datagram.c @@ -83,8 +83,7 @@ static int pn_init(struct sock *sk) return 0; } -static int pn_sendmsg(struct kiocb *iocb, struct sock *sk, - struct msghdr *msg, size_t len) +static int pn_sendmsg(struct sock *sk, struct msghdr *msg, size_t len) { DECLARE_SOCKADDR(struct sockaddr_pn *, target, msg->msg_name); struct sk_buff *skb; @@ -125,9 +124,8 @@ static int pn_sendmsg(struct kiocb *iocb, struct sock *sk, return (err >= 0) ? len : err; } -static int pn_recvmsg(struct kiocb *iocb, struct sock *sk, - struct msghdr *msg, size_t len, int noblock, - int flags, int *addr_len) +static int pn_recvmsg(struct sock *sk, struct msghdr *msg, size_t len, + int noblock, int flags, int *addr_len) { struct sk_buff *skb = NULL; struct sockaddr_pn sa; diff --git a/net/phonet/pep.c b/net/phonet/pep.c index 5d3f2b7507d4..6de2aeb98a1f 100644 --- a/net/phonet/pep.c +++ b/net/phonet/pep.c @@ -1118,8 +1118,7 @@ static int pipe_skb_send(struct sock *sk, struct sk_buff *skb) } -static int pep_sendmsg(struct kiocb *iocb, struct sock *sk, - struct msghdr *msg, size_t len) +static int pep_sendmsg(struct sock *sk, struct msghdr *msg, size_t len) { struct pep_sock *pn = pep_sk(sk); struct sk_buff *skb; @@ -1246,9 +1245,8 @@ struct sk_buff *pep_read(struct sock *sk) return skb; } -static int pep_recvmsg(struct kiocb *iocb, struct sock *sk, - struct msghdr *msg, size_t len, int noblock, - int flags, int *addr_len) +static int pep_recvmsg(struct sock *sk, struct msghdr *msg, size_t len, + int noblock, int flags, int *addr_len) { struct sk_buff *skb; int err; diff --git a/net/phonet/socket.c b/net/phonet/socket.c index 008214a3d5eb..d575ef4e9aa6 100644 --- a/net/phonet/socket.c +++ b/net/phonet/socket.c @@ -425,15 +425,15 @@ out: return err; } -static int pn_socket_sendmsg(struct kiocb *iocb, struct socket *sock, - struct msghdr *m, size_t total_len) +static int pn_socket_sendmsg(struct socket *sock, struct msghdr *m, + size_t total_len) { struct sock *sk = sock->sk; if (pn_socket_autobind(sock)) return -EAGAIN; - return sk->sk_prot->sendmsg(iocb, sk, m, total_len); + return sk->sk_prot->sendmsg(sk, m, total_len); } const struct proto_ops phonet_dgram_ops = { diff --git a/net/rds/rds.h b/net/rds/rds.h index c2a5eef41343..c3f2855c3d84 100644 --- a/net/rds/rds.h +++ b/net/rds/rds.h @@ -702,8 +702,8 @@ void rds_inc_init(struct rds_incoming *inc, struct rds_connection *conn, void rds_inc_put(struct rds_incoming *inc); void rds_recv_incoming(struct rds_connection *conn, __be32 saddr, __be32 daddr, struct rds_incoming *inc, gfp_t gfp); -int rds_recvmsg(struct kiocb *iocb, struct socket *sock, struct msghdr *msg, - size_t size, int msg_flags); +int rds_recvmsg(struct socket *sock, struct msghdr *msg, size_t size, + int msg_flags); void rds_clear_recv_queue(struct rds_sock *rs); int rds_notify_queue_get(struct rds_sock *rs, struct msghdr *msg); void rds_inc_info_copy(struct rds_incoming *inc, @@ -711,8 +711,7 @@ void rds_inc_info_copy(struct rds_incoming *inc, __be32 saddr, __be32 daddr, int flip); /* send.c */ -int rds_sendmsg(struct kiocb *iocb, struct socket *sock, struct msghdr *msg, - size_t payload_len); +int rds_sendmsg(struct socket *sock, struct msghdr *msg, size_t payload_len); void rds_send_reset(struct rds_connection *conn); int rds_send_xmit(struct rds_connection *conn); struct sockaddr_in; diff --git a/net/rds/recv.c b/net/rds/recv.c index f9ec1acd801c..a00462b0d01d 100644 --- a/net/rds/recv.c +++ b/net/rds/recv.c @@ -395,8 +395,8 @@ static int rds_cmsg_recv(struct rds_incoming *inc, struct msghdr *msg) return 0; } -int rds_recvmsg(struct kiocb *iocb, struct socket *sock, struct msghdr *msg, - size_t size, int msg_flags) +int rds_recvmsg(struct socket *sock, struct msghdr *msg, size_t size, + int msg_flags) { struct sock *sk = sock->sk; struct rds_sock *rs = rds_sk_to_rs(sk); diff --git a/net/rds/send.c b/net/rds/send.c index 42f65d4305c8..44672befc0ee 100644 --- a/net/rds/send.c +++ b/net/rds/send.c @@ -920,8 +920,7 @@ static int rds_cmsg_send(struct rds_sock *rs, struct rds_message *rm, return ret; } -int rds_sendmsg(struct kiocb *iocb, struct socket *sock, struct msghdr *msg, - size_t payload_len) +int rds_sendmsg(struct socket *sock, struct msghdr *msg, size_t payload_len) { struct sock *sk = sock->sk; struct rds_sock *rs = rds_sk_to_rs(sk); diff --git a/net/rose/af_rose.c b/net/rose/af_rose.c index 43bac7c4dd9e..8ae603069a1a 100644 --- a/net/rose/af_rose.c +++ b/net/rose/af_rose.c @@ -1046,8 +1046,7 @@ int rose_rx_call_request(struct sk_buff *skb, struct net_device *dev, struct ros return 1; } -static int rose_sendmsg(struct kiocb *iocb, struct socket *sock, - struct msghdr *msg, size_t len) +static int rose_sendmsg(struct socket *sock, struct msghdr *msg, size_t len) { struct sock *sk = sock->sk; struct rose_sock *rose = rose_sk(sk); @@ -1211,8 +1210,8 @@ static int rose_sendmsg(struct kiocb *iocb, struct socket *sock, } -static int rose_recvmsg(struct kiocb *iocb, struct socket *sock, - struct msghdr *msg, size_t size, int flags) +static int rose_recvmsg(struct socket *sock, struct msghdr *msg, size_t size, + int flags) { struct sock *sk = sock->sk; struct rose_sock *rose = rose_sk(sk); diff --git a/net/rxrpc/af_rxrpc.c b/net/rxrpc/af_rxrpc.c index 7b1670489638..0095b9a0b779 100644 --- a/net/rxrpc/af_rxrpc.c +++ b/net/rxrpc/af_rxrpc.c @@ -441,8 +441,7 @@ static int rxrpc_connect(struct socket *sock, struct sockaddr *addr, * - sends a call data packet * - may send an abort (abort code in control data) */ -static int rxrpc_sendmsg(struct kiocb *iocb, struct socket *sock, - struct msghdr *m, size_t len) +static int rxrpc_sendmsg(struct socket *sock, struct msghdr *m, size_t len) { struct rxrpc_transport *trans; struct rxrpc_sock *rx = rxrpc_sk(sock->sk); @@ -482,7 +481,7 @@ static int rxrpc_sendmsg(struct kiocb *iocb, struct socket *sock, switch (rx->sk.sk_state) { case RXRPC_SERVER_LISTENING: if (!m->msg_name) { - ret = rxrpc_server_sendmsg(iocb, rx, m, len); + ret = rxrpc_server_sendmsg(rx, m, len); break; } case RXRPC_SERVER_BOUND: @@ -492,7 +491,7 @@ static int rxrpc_sendmsg(struct kiocb *iocb, struct socket *sock, break; } case RXRPC_CLIENT_CONNECTED: - ret = rxrpc_client_sendmsg(iocb, rx, trans, m, len); + ret = rxrpc_client_sendmsg(rx, trans, m, len); break; default: ret = -ENOTCONN; diff --git a/net/rxrpc/ar-internal.h b/net/rxrpc/ar-internal.h index ba9fd36d3f15..2fc1e659e5c9 100644 --- a/net/rxrpc/ar-internal.h +++ b/net/rxrpc/ar-internal.h @@ -548,10 +548,9 @@ int rxrpc_get_server_data_key(struct rxrpc_connection *, const void *, time_t, extern unsigned rxrpc_resend_timeout; int rxrpc_send_packet(struct rxrpc_transport *, struct sk_buff *); -int rxrpc_client_sendmsg(struct kiocb *, struct rxrpc_sock *, - struct rxrpc_transport *, struct msghdr *, size_t); -int rxrpc_server_sendmsg(struct kiocb *, struct rxrpc_sock *, struct msghdr *, - size_t); +int rxrpc_client_sendmsg(struct rxrpc_sock *, struct rxrpc_transport *, + struct msghdr *, size_t); +int rxrpc_server_sendmsg(struct rxrpc_sock *, struct msghdr *, size_t); /* * ar-peer.c @@ -572,8 +571,7 @@ extern const struct file_operations rxrpc_connection_seq_fops; * ar-recvmsg.c */ void rxrpc_remove_user_ID(struct rxrpc_sock *, struct rxrpc_call *); -int rxrpc_recvmsg(struct kiocb *, struct socket *, struct msghdr *, size_t, - int); +int rxrpc_recvmsg(struct socket *, struct msghdr *, size_t, int); /* * ar-security.c diff --git a/net/rxrpc/ar-output.c b/net/rxrpc/ar-output.c index 8331c95e1522..09f584566e23 100644 --- a/net/rxrpc/ar-output.c +++ b/net/rxrpc/ar-output.c @@ -23,8 +23,7 @@ */ unsigned rxrpc_resend_timeout = 4 * HZ; -static int rxrpc_send_data(struct kiocb *iocb, - struct rxrpc_sock *rx, +static int rxrpc_send_data(struct rxrpc_sock *rx, struct rxrpc_call *call, struct msghdr *msg, size_t len); @@ -129,9 +128,8 @@ static void rxrpc_send_abort(struct rxrpc_call *call, u32 abort_code) * - caller holds the socket locked * - the socket may be either a client socket or a server socket */ -int rxrpc_client_sendmsg(struct kiocb *iocb, struct rxrpc_sock *rx, - struct rxrpc_transport *trans, struct msghdr *msg, - size_t len) +int rxrpc_client_sendmsg(struct rxrpc_sock *rx, struct rxrpc_transport *trans, + struct msghdr *msg, size_t len) { struct rxrpc_conn_bundle *bundle; enum rxrpc_command cmd; @@ -191,7 +189,7 @@ int rxrpc_client_sendmsg(struct kiocb *iocb, struct rxrpc_sock *rx, /* request phase complete for this client call */ ret = -EPROTO; } else { - ret = rxrpc_send_data(iocb, rx, call, msg, len); + ret = rxrpc_send_data(rx, call, msg, len); } rxrpc_put_call(call); @@ -232,7 +230,7 @@ int rxrpc_kernel_send_data(struct rxrpc_call *call, struct msghdr *msg, call->state != RXRPC_CALL_SERVER_SEND_REPLY) { ret = -EPROTO; /* request phase complete for this client call */ } else { - ret = rxrpc_send_data(NULL, call->socket, call, msg, len); + ret = rxrpc_send_data(call->socket, call, msg, len); } release_sock(&call->socket->sk); @@ -271,8 +269,7 @@ EXPORT_SYMBOL(rxrpc_kernel_abort_call); * send a message through a server socket * - caller holds the socket locked */ -int rxrpc_server_sendmsg(struct kiocb *iocb, struct rxrpc_sock *rx, - struct msghdr *msg, size_t len) +int rxrpc_server_sendmsg(struct rxrpc_sock *rx, struct msghdr *msg, size_t len) { enum rxrpc_command cmd; struct rxrpc_call *call; @@ -313,7 +310,7 @@ int rxrpc_server_sendmsg(struct kiocb *iocb, struct rxrpc_sock *rx, break; } - ret = rxrpc_send_data(iocb, rx, call, msg, len); + ret = rxrpc_send_data(rx, call, msg, len); break; case RXRPC_CMD_SEND_ABORT: @@ -520,8 +517,7 @@ static void rxrpc_queue_packet(struct rxrpc_call *call, struct sk_buff *skb, * - must be called in process context * - caller holds the socket locked */ -static int rxrpc_send_data(struct kiocb *iocb, - struct rxrpc_sock *rx, +static int rxrpc_send_data(struct rxrpc_sock *rx, struct rxrpc_call *call, struct msghdr *msg, size_t len) { diff --git a/net/rxrpc/ar-recvmsg.c b/net/rxrpc/ar-recvmsg.c index d58ba702bd2c..a4f883e2d66f 100644 --- a/net/rxrpc/ar-recvmsg.c +++ b/net/rxrpc/ar-recvmsg.c @@ -43,8 +43,8 @@ void rxrpc_remove_user_ID(struct rxrpc_sock *rx, struct rxrpc_call *call) * - we need to be careful about two or more threads calling recvmsg * simultaneously */ -int rxrpc_recvmsg(struct kiocb *iocb, struct socket *sock, - struct msghdr *msg, size_t len, int flags) +int rxrpc_recvmsg(struct socket *sock, struct msghdr *msg, size_t len, + int flags) { struct rxrpc_skb_priv *sp; struct rxrpc_call *call = NULL, *continue_call = NULL; diff --git a/net/sctp/socket.c b/net/sctp/socket.c index aafe94bf292e..f1a65398f311 100644 --- a/net/sctp/socket.c +++ b/net/sctp/socket.c @@ -1586,8 +1586,7 @@ static int sctp_error(struct sock *sk, int flags, int err) static int sctp_msghdr_parse(const struct msghdr *, sctp_cmsgs_t *); -static int sctp_sendmsg(struct kiocb *iocb, struct sock *sk, - struct msghdr *msg, size_t msg_len) +static int sctp_sendmsg(struct sock *sk, struct msghdr *msg, size_t msg_len) { struct net *net = sock_net(sk); struct sctp_sock *sp; @@ -2066,9 +2065,8 @@ static int sctp_skb_pull(struct sk_buff *skb, int len) * flags - flags sent or received with the user message, see Section * 5 for complete description of the flags. */ -static int sctp_recvmsg(struct kiocb *iocb, struct sock *sk, - struct msghdr *msg, size_t len, int noblock, - int flags, int *addr_len) +static int sctp_recvmsg(struct sock *sk, struct msghdr *msg, size_t len, + int noblock, int flags, int *addr_len) { struct sctp_ulpevent *event = NULL; struct sctp_sock *sp = sctp_sk(sk); diff --git a/net/socket.c b/net/socket.c index b78cf601a021..95d3085cb477 100644 --- a/net/socket.c +++ b/net/socket.c @@ -610,45 +610,20 @@ void __sock_tx_timestamp(const struct sock *sk, __u8 *tx_flags) } EXPORT_SYMBOL(__sock_tx_timestamp); -static inline int __sock_sendmsg_nosec(struct kiocb *iocb, struct socket *sock, - struct msghdr *msg, size_t size) +static inline int sock_sendmsg_nosec(struct socket *sock, struct msghdr *msg, + size_t size) { - return sock->ops->sendmsg(iocb, sock, msg, size); + return sock->ops->sendmsg(sock, msg, size); } -static inline int __sock_sendmsg(struct kiocb *iocb, struct socket *sock, - struct msghdr *msg, size_t size) +int sock_sendmsg(struct socket *sock, struct msghdr *msg, size_t size) { int err = security_socket_sendmsg(sock, msg, size); - return err ?: __sock_sendmsg_nosec(iocb, sock, msg, size); -} - -static int do_sock_sendmsg(struct socket *sock, struct msghdr *msg, - size_t size, bool nosec) -{ - struct kiocb iocb; - int ret; - - init_sync_kiocb(&iocb, NULL); - ret = nosec ? __sock_sendmsg_nosec(&iocb, sock, msg, size) : - __sock_sendmsg(&iocb, sock, msg, size); - if (-EIOCBQUEUED == ret) - ret = wait_on_sync_kiocb(&iocb); - return ret; -} - -int sock_sendmsg(struct socket *sock, struct msghdr *msg, size_t size) -{ - return do_sock_sendmsg(sock, msg, size, false); + return err ?: sock_sendmsg_nosec(sock, msg, size); } EXPORT_SYMBOL(sock_sendmsg); -static int sock_sendmsg_nosec(struct socket *sock, struct msghdr *msg, size_t size) -{ - return do_sock_sendmsg(sock, msg, size, true); -} - int kernel_sendmsg(struct socket *sock, struct msghdr *msg, struct kvec *vec, size_t num, size_t size) { @@ -744,47 +719,21 @@ void __sock_recv_ts_and_drops(struct msghdr *msg, struct sock *sk, } EXPORT_SYMBOL_GPL(__sock_recv_ts_and_drops); -static inline int __sock_recvmsg_nosec(struct kiocb *iocb, struct socket *sock, - struct msghdr *msg, size_t size, int flags) +static inline int sock_recvmsg_nosec(struct socket *sock, struct msghdr *msg, + size_t size, int flags) { - return sock->ops->recvmsg(iocb, sock, msg, size, flags); + return sock->ops->recvmsg(sock, msg, size, flags); } -static inline int __sock_recvmsg(struct kiocb *iocb, struct socket *sock, - struct msghdr *msg, size_t size, int flags) +int sock_recvmsg(struct socket *sock, struct msghdr *msg, size_t size, + int flags) { int err = security_socket_recvmsg(sock, msg, size, flags); - return err ?: __sock_recvmsg_nosec(iocb, sock, msg, size, flags); -} - -int sock_recvmsg(struct socket *sock, struct msghdr *msg, - size_t size, int flags) -{ - struct kiocb iocb; - int ret; - - init_sync_kiocb(&iocb, NULL); - ret = __sock_recvmsg(&iocb, sock, msg, size, flags); - if (-EIOCBQUEUED == ret) - ret = wait_on_sync_kiocb(&iocb); - return ret; + return err ?: sock_recvmsg_nosec(sock, msg, size, flags); } EXPORT_SYMBOL(sock_recvmsg); -static int sock_recvmsg_nosec(struct socket *sock, struct msghdr *msg, - size_t size, int flags) -{ - struct kiocb iocb; - int ret; - - init_sync_kiocb(&iocb, NULL); - ret = __sock_recvmsg_nosec(&iocb, sock, msg, size, flags); - if (-EIOCBQUEUED == ret) - ret = wait_on_sync_kiocb(&iocb); - return ret; -} - /** * kernel_recvmsg - Receive a message from a socket (kernel space) * @sock: The socket to receive the message from @@ -861,8 +810,7 @@ static ssize_t sock_read_iter(struct kiocb *iocb, struct iov_iter *to) if (iocb->ki_nbytes == 0) /* Match SYS5 behaviour */ return 0; - res = __sock_recvmsg(iocb, sock, &msg, - iocb->ki_nbytes, msg.msg_flags); + res = sock_recvmsg(sock, &msg, iocb->ki_nbytes, msg.msg_flags); *to = msg.msg_iter; return res; } @@ -883,7 +831,7 @@ static ssize_t sock_write_iter(struct kiocb *iocb, struct iov_iter *from) if (sock->type == SOCK_SEQPACKET) msg.msg_flags |= MSG_EOR; - res = __sock_sendmsg(iocb, sock, &msg, iocb->ki_nbytes); + res = sock_sendmsg(sock, &msg, iocb->ki_nbytes); *from = msg.msg_iter; return res; } diff --git a/net/tipc/socket.c b/net/tipc/socket.c index c245ec31fa4c..dcb797c60806 100644 --- a/net/tipc/socket.c +++ b/net/tipc/socket.c @@ -895,7 +895,6 @@ static int tipc_wait_for_sndmsg(struct socket *sock, long *timeo_p) /** * tipc_sendmsg - send message in connectionless manner - * @iocb: if NULL, indicates that socket lock is already held * @sock: socket structure * @m: message to send * @dsz: amount of user data to be sent @@ -907,7 +906,7 @@ static int tipc_wait_for_sndmsg(struct socket *sock, long *timeo_p) * * Returns the number of bytes sent on success, or errno otherwise */ -static int tipc_sendmsg(struct kiocb *iocb, struct socket *sock, +static int tipc_sendmsg(struct socket *sock, struct msghdr *m, size_t dsz) { struct sock *sk = sock->sk; @@ -1052,7 +1051,6 @@ static int tipc_wait_for_sndpkt(struct socket *sock, long *timeo_p) /** * tipc_send_stream - send stream-oriented data - * @iocb: (unused) * @sock: socket structure * @m: data to send * @dsz: total length of data to be transmitted @@ -1062,8 +1060,7 @@ static int tipc_wait_for_sndpkt(struct socket *sock, long *timeo_p) * Returns the number of bytes sent on success (or partial success), * or errno if no data sent */ -static int tipc_send_stream(struct kiocb *iocb, struct socket *sock, - struct msghdr *m, size_t dsz) +static int tipc_send_stream(struct socket *sock, struct msghdr *m, size_t dsz) { struct sock *sk = sock->sk; int ret; @@ -1147,7 +1144,6 @@ next: /** * tipc_send_packet - send a connection-oriented message - * @iocb: if NULL, indicates that socket lock is already held * @sock: socket structure * @m: message to send * @dsz: length of data to be transmitted @@ -1156,13 +1152,12 @@ next: * * Returns the number of bytes sent on success, or errno otherwise */ -static int tipc_send_packet(struct kiocb *iocb, struct socket *sock, - struct msghdr *m, size_t dsz) +static int tipc_send_packet(struct socket *sock, struct msghdr *m, size_t dsz) { if (dsz > TIPC_MAX_USER_MSG_SIZE) return -EMSGSIZE; - return tipc_send_stream(iocb, sock, m, dsz); + return tipc_send_stream(sock, m, dsz); } /* tipc_sk_finish_conn - complete the setup of a connection @@ -1337,7 +1332,6 @@ static int tipc_wait_for_rcvmsg(struct socket *sock, long *timeop) /** * tipc_recvmsg - receive packet-oriented message - * @iocb: (unused) * @m: descriptor for message info * @buf_len: total size of user buffer area * @flags: receive flags @@ -1347,8 +1341,8 @@ static int tipc_wait_for_rcvmsg(struct socket *sock, long *timeop) * * Returns size of returned message data, errno otherwise */ -static int tipc_recvmsg(struct kiocb *iocb, struct socket *sock, - struct msghdr *m, size_t buf_len, int flags) +static int tipc_recvmsg(struct socket *sock, struct msghdr *m, size_t buf_len, + int flags) { struct sock *sk = sock->sk; struct tipc_sock *tsk = tipc_sk(sk); @@ -1432,7 +1426,6 @@ exit: /** * tipc_recv_stream - receive stream-oriented data - * @iocb: (unused) * @m: descriptor for message info * @buf_len: total size of user buffer area * @flags: receive flags @@ -1442,8 +1435,8 @@ exit: * * Returns size of returned message data, errno otherwise */ -static int tipc_recv_stream(struct kiocb *iocb, struct socket *sock, - struct msghdr *m, size_t buf_len, int flags) +static int tipc_recv_stream(struct socket *sock, struct msghdr *m, + size_t buf_len, int flags) { struct sock *sk = sock->sk; struct tipc_sock *tsk = tipc_sk(sk); diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c index 526b6edab018..433f287ee548 100644 --- a/net/unix/af_unix.c +++ b/net/unix/af_unix.c @@ -516,20 +516,15 @@ static unsigned int unix_dgram_poll(struct file *, struct socket *, poll_table *); static int unix_ioctl(struct socket *, unsigned int, unsigned long); static int unix_shutdown(struct socket *, int); -static int unix_stream_sendmsg(struct kiocb *, struct socket *, - struct msghdr *, size_t); -static int unix_stream_recvmsg(struct kiocb *, struct socket *, - struct msghdr *, size_t, int); -static int unix_dgram_sendmsg(struct kiocb *, struct socket *, - struct msghdr *, size_t); -static int unix_dgram_recvmsg(struct kiocb *, struct socket *, - struct msghdr *, size_t, int); +static int unix_stream_sendmsg(struct socket *, struct msghdr *, size_t); +static int unix_stream_recvmsg(struct socket *, struct msghdr *, size_t, int); +static int unix_dgram_sendmsg(struct socket *, struct msghdr *, size_t); +static int unix_dgram_recvmsg(struct socket *, struct msghdr *, size_t, int); static int unix_dgram_connect(struct socket *, struct sockaddr *, int, int); -static int unix_seqpacket_sendmsg(struct kiocb *, struct socket *, - struct msghdr *, size_t); -static int unix_seqpacket_recvmsg(struct kiocb *, struct socket *, - struct msghdr *, size_t, int); +static int unix_seqpacket_sendmsg(struct socket *, struct msghdr *, size_t); +static int unix_seqpacket_recvmsg(struct socket *, struct msghdr *, size_t, + int); static int unix_set_peek_off(struct sock *sk, int val) { @@ -1442,8 +1437,8 @@ static void maybe_add_creds(struct sk_buff *skb, const struct socket *sock, * Send AF_UNIX data. */ -static int unix_dgram_sendmsg(struct kiocb *kiocb, struct socket *sock, - struct msghdr *msg, size_t len) +static int unix_dgram_sendmsg(struct socket *sock, struct msghdr *msg, + size_t len) { struct sock *sk = sock->sk; struct net *net = sock_net(sk); @@ -1622,8 +1617,8 @@ out: */ #define UNIX_SKB_FRAGS_SZ (PAGE_SIZE << get_order(32768)) -static int unix_stream_sendmsg(struct kiocb *kiocb, struct socket *sock, - struct msghdr *msg, size_t len) +static int unix_stream_sendmsg(struct socket *sock, struct msghdr *msg, + size_t len) { struct sock *sk = sock->sk; struct sock *other = NULL; @@ -1725,8 +1720,8 @@ out_err: return sent ? : err; } -static int unix_seqpacket_sendmsg(struct kiocb *kiocb, struct socket *sock, - struct msghdr *msg, size_t len) +static int unix_seqpacket_sendmsg(struct socket *sock, struct msghdr *msg, + size_t len) { int err; struct sock *sk = sock->sk; @@ -1741,19 +1736,18 @@ static int unix_seqpacket_sendmsg(struct kiocb *kiocb, struct socket *sock, if (msg->msg_namelen) msg->msg_namelen = 0; - return unix_dgram_sendmsg(kiocb, sock, msg, len); + return unix_dgram_sendmsg(sock, msg, len); } -static int unix_seqpacket_recvmsg(struct kiocb *iocb, struct socket *sock, - struct msghdr *msg, size_t size, - int flags) +static int unix_seqpacket_recvmsg(struct socket *sock, struct msghdr *msg, + size_t size, int flags) { struct sock *sk = sock->sk; if (sk->sk_state != TCP_ESTABLISHED) return -ENOTCONN; - return unix_dgram_recvmsg(iocb, sock, msg, size, flags); + return unix_dgram_recvmsg(sock, msg, size, flags); } static void unix_copy_addr(struct msghdr *msg, struct sock *sk) @@ -1766,9 +1760,8 @@ static void unix_copy_addr(struct msghdr *msg, struct sock *sk) } } -static int unix_dgram_recvmsg(struct kiocb *iocb, struct socket *sock, - struct msghdr *msg, size_t size, - int flags) +static int unix_dgram_recvmsg(struct socket *sock, struct msghdr *msg, + size_t size, int flags) { struct scm_cookie scm; struct sock *sk = sock->sk; @@ -1900,9 +1893,8 @@ static unsigned int unix_skb_len(const struct sk_buff *skb) return skb->len - UNIXCB(skb).consumed; } -static int unix_stream_recvmsg(struct kiocb *iocb, struct socket *sock, - struct msghdr *msg, size_t size, - int flags) +static int unix_stream_recvmsg(struct socket *sock, struct msghdr *msg, + size_t size, int flags) { struct scm_cookie scm; struct sock *sk = sock->sk; diff --git a/net/vmw_vsock/af_vsock.c b/net/vmw_vsock/af_vsock.c index 1d0e39c9a3e2..2ec86e652a19 100644 --- a/net/vmw_vsock/af_vsock.c +++ b/net/vmw_vsock/af_vsock.c @@ -949,8 +949,8 @@ static unsigned int vsock_poll(struct file *file, struct socket *sock, return mask; } -static int vsock_dgram_sendmsg(struct kiocb *kiocb, struct socket *sock, - struct msghdr *msg, size_t len) +static int vsock_dgram_sendmsg(struct socket *sock, struct msghdr *msg, + size_t len) { int err; struct sock *sk; @@ -1062,11 +1062,10 @@ out: return err; } -static int vsock_dgram_recvmsg(struct kiocb *kiocb, struct socket *sock, - struct msghdr *msg, size_t len, int flags) +static int vsock_dgram_recvmsg(struct socket *sock, struct msghdr *msg, + size_t len, int flags) { - return transport->dgram_dequeue(kiocb, vsock_sk(sock->sk), msg, len, - flags); + return transport->dgram_dequeue(vsock_sk(sock->sk), msg, len, flags); } static const struct proto_ops vsock_dgram_ops = { @@ -1505,8 +1504,8 @@ static int vsock_stream_getsockopt(struct socket *sock, return 0; } -static int vsock_stream_sendmsg(struct kiocb *kiocb, struct socket *sock, - struct msghdr *msg, size_t len) +static int vsock_stream_sendmsg(struct socket *sock, struct msghdr *msg, + size_t len) { struct sock *sk; struct vsock_sock *vsk; @@ -1644,9 +1643,8 @@ out: static int -vsock_stream_recvmsg(struct kiocb *kiocb, - struct socket *sock, - struct msghdr *msg, size_t len, int flags) +vsock_stream_recvmsg(struct socket *sock, struct msghdr *msg, size_t len, + int flags) { struct sock *sk; struct vsock_sock *vsk; diff --git a/net/vmw_vsock/vmci_transport.c b/net/vmw_vsock/vmci_transport.c index 7f3255084a6c..c294da095461 100644 --- a/net/vmw_vsock/vmci_transport.c +++ b/net/vmw_vsock/vmci_transport.c @@ -1730,8 +1730,7 @@ static int vmci_transport_dgram_enqueue( return err - sizeof(*dg); } -static int vmci_transport_dgram_dequeue(struct kiocb *kiocb, - struct vsock_sock *vsk, +static int vmci_transport_dgram_dequeue(struct vsock_sock *vsk, struct msghdr *msg, size_t len, int flags) { diff --git a/net/x25/af_x25.c b/net/x25/af_x25.c index d9149b68b9bc..c3ab230e4493 100644 --- a/net/x25/af_x25.c +++ b/net/x25/af_x25.c @@ -1077,8 +1077,7 @@ out_clear_request: goto out; } -static int x25_sendmsg(struct kiocb *iocb, struct socket *sock, - struct msghdr *msg, size_t len) +static int x25_sendmsg(struct socket *sock, struct msghdr *msg, size_t len) { struct sock *sk = sock->sk; struct x25_sock *x25 = x25_sk(sk); @@ -1252,8 +1251,7 @@ out_kfree_skb: } -static int x25_recvmsg(struct kiocb *iocb, struct socket *sock, - struct msghdr *msg, size_t size, +static int x25_recvmsg(struct socket *sock, struct msghdr *msg, size_t size, int flags) { struct sock *sk = sock->sk; -- cgit v1.2.3 From b44b2e06ae463327334235bf160e804632b9b37c Mon Sep 17 00:00:00 2001 From: Pablo Anton Date: Tue, 3 Feb 2015 14:13:18 -0300 Subject: [media] media: i2c: ADV7604: Rename adv7604 prefixes It is confusing which parts of the driver are adv7604 specific, adv7611 specific or common for both. This patch renames any adv7604 prefixes (both for functions and defines) to adv76xx whenever they are common. Signed-off-by: Pablo Anton Signed-off-by: Jean-Michel Hautbois [hans.verkuil@cisco.com: rebased and renamed ADV76xx_fsc to ADV76XX_FSC] [hans.verkuil@cisco.com: kept the existing adv7604 driver name] Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/adv7604.c | 898 ++++++++++++++++++++++---------------------- include/media/adv7604.h | 83 ++-- 2 files changed, 491 insertions(+), 490 deletions(-) (limited to 'include') diff --git a/drivers/media/i2c/adv7604.c b/drivers/media/i2c/adv7604.c index 8d455b766cc2..2def57f4e1e2 100644 --- a/drivers/media/i2c/adv7604.c +++ b/drivers/media/i2c/adv7604.c @@ -53,41 +53,41 @@ MODULE_AUTHOR("Mats Randgaard "); MODULE_LICENSE("GPL"); /* ADV7604 system clock frequency */ -#define ADV7604_fsc (28636360) +#define ADV76XX_FSC (28636360) -#define ADV7604_RGB_OUT (1 << 1) +#define ADV76XX_RGB_OUT (1 << 1) -#define ADV7604_OP_FORMAT_SEL_8BIT (0 << 0) +#define ADV76XX_OP_FORMAT_SEL_8BIT (0 << 0) #define ADV7604_OP_FORMAT_SEL_10BIT (1 << 0) -#define ADV7604_OP_FORMAT_SEL_12BIT (2 << 0) +#define ADV76XX_OP_FORMAT_SEL_12BIT (2 << 0) -#define ADV7604_OP_MODE_SEL_SDR_422 (0 << 5) +#define ADV76XX_OP_MODE_SEL_SDR_422 (0 << 5) #define ADV7604_OP_MODE_SEL_DDR_422 (1 << 5) -#define ADV7604_OP_MODE_SEL_SDR_444 (2 << 5) +#define ADV76XX_OP_MODE_SEL_SDR_444 (2 << 5) #define ADV7604_OP_MODE_SEL_DDR_444 (3 << 5) -#define ADV7604_OP_MODE_SEL_SDR_422_2X (4 << 5) +#define ADV76XX_OP_MODE_SEL_SDR_422_2X (4 << 5) #define ADV7604_OP_MODE_SEL_ADI_CM (5 << 5) -#define ADV7604_OP_CH_SEL_GBR (0 << 5) -#define ADV7604_OP_CH_SEL_GRB (1 << 5) -#define ADV7604_OP_CH_SEL_BGR (2 << 5) -#define ADV7604_OP_CH_SEL_RGB (3 << 5) -#define ADV7604_OP_CH_SEL_BRG (4 << 5) -#define ADV7604_OP_CH_SEL_RBG (5 << 5) +#define ADV76XX_OP_CH_SEL_GBR (0 << 5) +#define ADV76XX_OP_CH_SEL_GRB (1 << 5) +#define ADV76XX_OP_CH_SEL_BGR (2 << 5) +#define ADV76XX_OP_CH_SEL_RGB (3 << 5) +#define ADV76XX_OP_CH_SEL_BRG (4 << 5) +#define ADV76XX_OP_CH_SEL_RBG (5 << 5) -#define ADV7604_OP_SWAP_CB_CR (1 << 0) +#define ADV76XX_OP_SWAP_CB_CR (1 << 0) -enum adv7604_type { +enum adv76xx_type { ADV7604, ADV7611, }; -struct adv7604_reg_seq { +struct adv76xx_reg_seq { unsigned int reg; u8 val; }; -struct adv7604_format_info { +struct adv76xx_format_info { u32 code; u8 op_ch_sel; bool rgb_out; @@ -95,8 +95,8 @@ struct adv7604_format_info { u8 op_format_sel; }; -struct adv7604_chip_info { - enum adv7604_type type; +struct adv76xx_chip_info { + enum adv76xx_type type; bool has_afe; unsigned int max_port; @@ -111,7 +111,7 @@ struct adv7604_chip_info { unsigned int fmt_change_digital_mask; unsigned int cp_csc; - const struct adv7604_format_info *formats; + const struct adv76xx_format_info *formats; unsigned int nformats; void (*set_termination)(struct v4l2_subdev *sd, bool enable); @@ -120,7 +120,7 @@ struct adv7604_chip_info { unsigned int (*read_cable_det)(struct v4l2_subdev *sd); /* 0 = AFE, 1 = HDMI */ - const struct adv7604_reg_seq *recommended_settings[2]; + const struct adv76xx_reg_seq *recommended_settings[2]; unsigned int num_recommended_settings[2]; unsigned long page_mask; @@ -134,22 +134,22 @@ struct adv7604_chip_info { ********************************************************************** */ -struct adv7604_state { - const struct adv7604_chip_info *info; - struct adv7604_platform_data pdata; +struct adv76xx_state { + const struct adv76xx_chip_info *info; + struct adv76xx_platform_data pdata; struct gpio_desc *hpd_gpio[4]; struct v4l2_subdev sd; - struct media_pad pads[ADV7604_PAD_MAX]; + struct media_pad pads[ADV76XX_PAD_MAX]; unsigned int source_pad; struct v4l2_ctrl_handler hdl; - enum adv7604_pad selected_input; + enum adv76xx_pad selected_input; struct v4l2_dv_timings timings; - const struct adv7604_format_info *format; + const struct adv76xx_format_info *format; struct { u8 edid[256]; @@ -164,7 +164,7 @@ struct adv7604_state { bool restart_stdi_once; /* i2c clients */ - struct i2c_client *i2c_clients[ADV7604_PAGE_MAX]; + struct i2c_client *i2c_clients[ADV76XX_PAGE_MAX]; /* controls */ struct v4l2_ctrl *detect_tx_5v_ctrl; @@ -174,13 +174,13 @@ struct adv7604_state { struct v4l2_ctrl *rgb_quantization_range_ctrl; }; -static bool adv7604_has_afe(struct adv7604_state *state) +static bool adv76xx_has_afe(struct adv76xx_state *state) { return state->info->has_afe; } /* Supported CEA and DMT timings */ -static const struct v4l2_dv_timings adv7604_timings[] = { +static const struct v4l2_dv_timings adv76xx_timings[] = { V4L2_DV_BT_CEA_720X480P59_94, V4L2_DV_BT_CEA_720X576P50, V4L2_DV_BT_CEA_1280X720P24, @@ -244,14 +244,14 @@ static const struct v4l2_dv_timings adv7604_timings[] = { { }, }; -struct adv7604_video_standards { +struct adv76xx_video_standards { struct v4l2_dv_timings timings; u8 vid_std; u8 v_freq; }; /* sorted by number of lines */ -static const struct adv7604_video_standards adv7604_prim_mode_comp[] = { +static const struct adv76xx_video_standards adv7604_prim_mode_comp[] = { /* { V4L2_DV_BT_CEA_720X480P59_94, 0x0a, 0x00 }, TODO flickering */ { V4L2_DV_BT_CEA_720X576P50, 0x0b, 0x00 }, { V4L2_DV_BT_CEA_1280X720P50, 0x19, 0x01 }, @@ -266,7 +266,7 @@ static const struct adv7604_video_standards adv7604_prim_mode_comp[] = { }; /* sorted by number of lines */ -static const struct adv7604_video_standards adv7604_prim_mode_gr[] = { +static const struct adv76xx_video_standards adv7604_prim_mode_gr[] = { { V4L2_DV_BT_DMT_640X480P60, 0x08, 0x00 }, { V4L2_DV_BT_DMT_640X480P72, 0x09, 0x00 }, { V4L2_DV_BT_DMT_640X480P75, 0x0a, 0x00 }, @@ -294,7 +294,7 @@ static const struct adv7604_video_standards adv7604_prim_mode_gr[] = { }; /* sorted by number of lines */ -static const struct adv7604_video_standards adv7604_prim_mode_hdmi_comp[] = { +static const struct adv76xx_video_standards adv76xx_prim_mode_hdmi_comp[] = { { V4L2_DV_BT_CEA_720X480P59_94, 0x0a, 0x00 }, { V4L2_DV_BT_CEA_720X576P50, 0x0b, 0x00 }, { V4L2_DV_BT_CEA_1280X720P50, 0x13, 0x01 }, @@ -308,7 +308,7 @@ static const struct adv7604_video_standards adv7604_prim_mode_hdmi_comp[] = { }; /* sorted by number of lines */ -static const struct adv7604_video_standards adv7604_prim_mode_hdmi_gr[] = { +static const struct adv76xx_video_standards adv76xx_prim_mode_hdmi_gr[] = { { V4L2_DV_BT_DMT_640X480P60, 0x08, 0x00 }, { V4L2_DV_BT_DMT_640X480P72, 0x09, 0x00 }, { V4L2_DV_BT_DMT_640X480P75, 0x0a, 0x00 }, @@ -329,9 +329,9 @@ static const struct adv7604_video_standards adv7604_prim_mode_hdmi_gr[] = { /* ----------------------------------------------------------------------- */ -static inline struct adv7604_state *to_state(struct v4l2_subdev *sd) +static inline struct adv76xx_state *to_state(struct v4l2_subdev *sd) { - return container_of(sd, struct adv7604_state, sd); + return container_of(sd, struct adv76xx_state, sd); } static inline unsigned htotal(const struct v4l2_bt_timings *t) @@ -361,15 +361,15 @@ static s32 adv_smbus_read_byte_data_check(struct i2c_client *client, return -EIO; } -static s32 adv_smbus_read_byte_data(struct adv7604_state *state, - enum adv7604_page page, u8 command) +static s32 adv_smbus_read_byte_data(struct adv76xx_state *state, + enum adv76xx_page page, u8 command) { return adv_smbus_read_byte_data_check(state->i2c_clients[page], command, true); } -static s32 adv_smbus_write_byte_data(struct adv7604_state *state, - enum adv7604_page page, u8 command, +static s32 adv_smbus_write_byte_data(struct adv76xx_state *state, + enum adv76xx_page page, u8 command, u8 value) { struct i2c_client *client = state->i2c_clients[page]; @@ -392,8 +392,8 @@ static s32 adv_smbus_write_byte_data(struct adv7604_state *state, return err; } -static s32 adv_smbus_write_i2c_block_data(struct adv7604_state *state, - enum adv7604_page page, u8 command, +static s32 adv_smbus_write_i2c_block_data(struct adv76xx_state *state, + enum adv76xx_page page, u8 command, unsigned length, const u8 *values) { struct i2c_client *client = state->i2c_clients[page]; @@ -412,16 +412,16 @@ static s32 adv_smbus_write_i2c_block_data(struct adv7604_state *state, static inline int io_read(struct v4l2_subdev *sd, u8 reg) { - struct adv7604_state *state = to_state(sd); + struct adv76xx_state *state = to_state(sd); - return adv_smbus_read_byte_data(state, ADV7604_PAGE_IO, reg); + return adv_smbus_read_byte_data(state, ADV76XX_PAGE_IO, reg); } static inline int io_write(struct v4l2_subdev *sd, u8 reg, u8 val) { - struct adv7604_state *state = to_state(sd); + struct adv76xx_state *state = to_state(sd); - return adv_smbus_write_byte_data(state, ADV7604_PAGE_IO, reg, val); + return adv_smbus_write_byte_data(state, ADV76XX_PAGE_IO, reg, val); } static inline int io_write_clr_set(struct v4l2_subdev *sd, u8 reg, u8 mask, u8 val) @@ -431,73 +431,73 @@ static inline int io_write_clr_set(struct v4l2_subdev *sd, u8 reg, u8 mask, u8 v static inline int avlink_read(struct v4l2_subdev *sd, u8 reg) { - struct adv7604_state *state = to_state(sd); + struct adv76xx_state *state = to_state(sd); return adv_smbus_read_byte_data(state, ADV7604_PAGE_AVLINK, reg); } static inline int avlink_write(struct v4l2_subdev *sd, u8 reg, u8 val) { - struct adv7604_state *state = to_state(sd); + struct adv76xx_state *state = to_state(sd); return adv_smbus_write_byte_data(state, ADV7604_PAGE_AVLINK, reg, val); } static inline int cec_read(struct v4l2_subdev *sd, u8 reg) { - struct adv7604_state *state = to_state(sd); + struct adv76xx_state *state = to_state(sd); - return adv_smbus_read_byte_data(state, ADV7604_PAGE_CEC, reg); + return adv_smbus_read_byte_data(state, ADV76XX_PAGE_CEC, reg); } static inline int cec_write(struct v4l2_subdev *sd, u8 reg, u8 val) { - struct adv7604_state *state = to_state(sd); + struct adv76xx_state *state = to_state(sd); - return adv_smbus_write_byte_data(state, ADV7604_PAGE_CEC, reg, val); + return adv_smbus_write_byte_data(state, ADV76XX_PAGE_CEC, reg, val); } static inline int infoframe_read(struct v4l2_subdev *sd, u8 reg) { - struct adv7604_state *state = to_state(sd); + struct adv76xx_state *state = to_state(sd); - return adv_smbus_read_byte_data(state, ADV7604_PAGE_INFOFRAME, reg); + return adv_smbus_read_byte_data(state, ADV76XX_PAGE_INFOFRAME, reg); } static inline int infoframe_write(struct v4l2_subdev *sd, u8 reg, u8 val) { - struct adv7604_state *state = to_state(sd); + struct adv76xx_state *state = to_state(sd); - return adv_smbus_write_byte_data(state, ADV7604_PAGE_INFOFRAME, + return adv_smbus_write_byte_data(state, ADV76XX_PAGE_INFOFRAME, reg, val); } static inline int afe_read(struct v4l2_subdev *sd, u8 reg) { - struct adv7604_state *state = to_state(sd); + struct adv76xx_state *state = to_state(sd); - return adv_smbus_read_byte_data(state, ADV7604_PAGE_AFE, reg); + return adv_smbus_read_byte_data(state, ADV76XX_PAGE_AFE, reg); } static inline int afe_write(struct v4l2_subdev *sd, u8 reg, u8 val) { - struct adv7604_state *state = to_state(sd); + struct adv76xx_state *state = to_state(sd); - return adv_smbus_write_byte_data(state, ADV7604_PAGE_AFE, reg, val); + return adv_smbus_write_byte_data(state, ADV76XX_PAGE_AFE, reg, val); } static inline int rep_read(struct v4l2_subdev *sd, u8 reg) { - struct adv7604_state *state = to_state(sd); + struct adv76xx_state *state = to_state(sd); - return adv_smbus_read_byte_data(state, ADV7604_PAGE_REP, reg); + return adv_smbus_read_byte_data(state, ADV76XX_PAGE_REP, reg); } static inline int rep_write(struct v4l2_subdev *sd, u8 reg, u8 val) { - struct adv7604_state *state = to_state(sd); + struct adv76xx_state *state = to_state(sd); - return adv_smbus_write_byte_data(state, ADV7604_PAGE_REP, reg, val); + return adv_smbus_write_byte_data(state, ADV76XX_PAGE_REP, reg, val); } static inline int rep_write_clr_set(struct v4l2_subdev *sd, u8 reg, u8 mask, u8 val) @@ -507,34 +507,34 @@ static inline int rep_write_clr_set(struct v4l2_subdev *sd, u8 reg, u8 mask, u8 static inline int edid_read(struct v4l2_subdev *sd, u8 reg) { - struct adv7604_state *state = to_state(sd); + struct adv76xx_state *state = to_state(sd); - return adv_smbus_read_byte_data(state, ADV7604_PAGE_EDID, reg); + return adv_smbus_read_byte_data(state, ADV76XX_PAGE_EDID, reg); } static inline int edid_write(struct v4l2_subdev *sd, u8 reg, u8 val) { - struct adv7604_state *state = to_state(sd); + struct adv76xx_state *state = to_state(sd); - return adv_smbus_write_byte_data(state, ADV7604_PAGE_EDID, reg, val); + return adv_smbus_write_byte_data(state, ADV76XX_PAGE_EDID, reg, val); } static inline int edid_write_block(struct v4l2_subdev *sd, unsigned len, const u8 *val) { - struct adv7604_state *state = to_state(sd); + struct adv76xx_state *state = to_state(sd); int err = 0; int i; v4l2_dbg(2, debug, sd, "%s: write EDID block (%d byte)\n", __func__, len); for (i = 0; !err && i < len; i += I2C_SMBUS_BLOCK_MAX) - err = adv_smbus_write_i2c_block_data(state, ADV7604_PAGE_EDID, + err = adv_smbus_write_i2c_block_data(state, ADV76XX_PAGE_EDID, i, I2C_SMBUS_BLOCK_MAX, val + i); return err; } -static void adv7604_set_hpd(struct adv7604_state *state, unsigned int hpd) +static void adv76xx_set_hpd(struct adv76xx_state *state, unsigned int hpd) { unsigned int i; @@ -545,26 +545,26 @@ static void adv7604_set_hpd(struct adv7604_state *state, unsigned int hpd) gpiod_set_value_cansleep(state->hpd_gpio[i], hpd & BIT(i)); } - v4l2_subdev_notify(&state->sd, ADV7604_HOTPLUG, &hpd); + v4l2_subdev_notify(&state->sd, ADV76XX_HOTPLUG, &hpd); } -static void adv7604_delayed_work_enable_hotplug(struct work_struct *work) +static void adv76xx_delayed_work_enable_hotplug(struct work_struct *work) { struct delayed_work *dwork = to_delayed_work(work); - struct adv7604_state *state = container_of(dwork, struct adv7604_state, + struct adv76xx_state *state = container_of(dwork, struct adv76xx_state, delayed_work_enable_hotplug); struct v4l2_subdev *sd = &state->sd; v4l2_dbg(2, debug, sd, "%s: enable hotplug\n", __func__); - adv7604_set_hpd(state, state->edid.present); + adv76xx_set_hpd(state, state->edid.present); } static inline int hdmi_read(struct v4l2_subdev *sd, u8 reg) { - struct adv7604_state *state = to_state(sd); + struct adv76xx_state *state = to_state(sd); - return adv_smbus_read_byte_data(state, ADV7604_PAGE_HDMI, reg); + return adv_smbus_read_byte_data(state, ADV76XX_PAGE_HDMI, reg); } static u16 hdmi_read16(struct v4l2_subdev *sd, u8 reg, u16 mask) @@ -574,9 +574,9 @@ static u16 hdmi_read16(struct v4l2_subdev *sd, u8 reg, u16 mask) static inline int hdmi_write(struct v4l2_subdev *sd, u8 reg, u8 val) { - struct adv7604_state *state = to_state(sd); + struct adv76xx_state *state = to_state(sd); - return adv_smbus_write_byte_data(state, ADV7604_PAGE_HDMI, reg, val); + return adv_smbus_write_byte_data(state, ADV76XX_PAGE_HDMI, reg, val); } static inline int hdmi_write_clr_set(struct v4l2_subdev *sd, u8 reg, u8 mask, u8 val) @@ -586,16 +586,16 @@ static inline int hdmi_write_clr_set(struct v4l2_subdev *sd, u8 reg, u8 mask, u8 static inline int test_write(struct v4l2_subdev *sd, u8 reg, u8 val) { - struct adv7604_state *state = to_state(sd); + struct adv76xx_state *state = to_state(sd); - return adv_smbus_write_byte_data(state, ADV7604_PAGE_TEST, reg, val); + return adv_smbus_write_byte_data(state, ADV76XX_PAGE_TEST, reg, val); } static inline int cp_read(struct v4l2_subdev *sd, u8 reg) { - struct adv7604_state *state = to_state(sd); + struct adv76xx_state *state = to_state(sd); - return adv_smbus_read_byte_data(state, ADV7604_PAGE_CP, reg); + return adv_smbus_read_byte_data(state, ADV76XX_PAGE_CP, reg); } static u16 cp_read16(struct v4l2_subdev *sd, u8 reg, u16 mask) @@ -605,9 +605,9 @@ static u16 cp_read16(struct v4l2_subdev *sd, u8 reg, u16 mask) static inline int cp_write(struct v4l2_subdev *sd, u8 reg, u8 val) { - struct adv7604_state *state = to_state(sd); + struct adv76xx_state *state = to_state(sd); - return adv_smbus_write_byte_data(state, ADV7604_PAGE_CP, reg, val); + return adv_smbus_write_byte_data(state, ADV76XX_PAGE_CP, reg, val); } static inline int cp_write_clr_set(struct v4l2_subdev *sd, u8 reg, u8 mask, u8 val) @@ -617,25 +617,25 @@ static inline int cp_write_clr_set(struct v4l2_subdev *sd, u8 reg, u8 mask, u8 v static inline int vdp_read(struct v4l2_subdev *sd, u8 reg) { - struct adv7604_state *state = to_state(sd); + struct adv76xx_state *state = to_state(sd); return adv_smbus_read_byte_data(state, ADV7604_PAGE_VDP, reg); } static inline int vdp_write(struct v4l2_subdev *sd, u8 reg, u8 val) { - struct adv7604_state *state = to_state(sd); + struct adv76xx_state *state = to_state(sd); return adv_smbus_write_byte_data(state, ADV7604_PAGE_VDP, reg, val); } -#define ADV7604_REG(page, offset) (((page) << 8) | (offset)) -#define ADV7604_REG_SEQ_TERM 0xffff +#define ADV76XX_REG(page, offset) (((page) << 8) | (offset)) +#define ADV76XX_REG_SEQ_TERM 0xffff #ifdef CONFIG_VIDEO_ADV_DEBUG -static int adv7604_read_reg(struct v4l2_subdev *sd, unsigned int reg) +static int adv76xx_read_reg(struct v4l2_subdev *sd, unsigned int reg) { - struct adv7604_state *state = to_state(sd); + struct adv76xx_state *state = to_state(sd); unsigned int page = reg >> 8; if (!(BIT(page) & state->info->page_mask)) @@ -647,9 +647,9 @@ static int adv7604_read_reg(struct v4l2_subdev *sd, unsigned int reg) } #endif -static int adv7604_write_reg(struct v4l2_subdev *sd, unsigned int reg, u8 val) +static int adv76xx_write_reg(struct v4l2_subdev *sd, unsigned int reg, u8 val) { - struct adv7604_state *state = to_state(sd); + struct adv76xx_state *state = to_state(sd); unsigned int page = reg >> 8; if (!(BIT(page) & state->info->page_mask)) @@ -660,91 +660,91 @@ static int adv7604_write_reg(struct v4l2_subdev *sd, unsigned int reg, u8 val) return adv_smbus_write_byte_data(state, page, reg, val); } -static void adv7604_write_reg_seq(struct v4l2_subdev *sd, - const struct adv7604_reg_seq *reg_seq) +static void adv76xx_write_reg_seq(struct v4l2_subdev *sd, + const struct adv76xx_reg_seq *reg_seq) { unsigned int i; - for (i = 0; reg_seq[i].reg != ADV7604_REG_SEQ_TERM; i++) - adv7604_write_reg(sd, reg_seq[i].reg, reg_seq[i].val); + for (i = 0; reg_seq[i].reg != ADV76XX_REG_SEQ_TERM; i++) + adv76xx_write_reg(sd, reg_seq[i].reg, reg_seq[i].val); } /* ----------------------------------------------------------------------------- * Format helpers */ -static const struct adv7604_format_info adv7604_formats[] = { - { MEDIA_BUS_FMT_RGB888_1X24, ADV7604_OP_CH_SEL_RGB, true, false, - ADV7604_OP_MODE_SEL_SDR_444 | ADV7604_OP_FORMAT_SEL_8BIT }, - { MEDIA_BUS_FMT_YUYV8_2X8, ADV7604_OP_CH_SEL_RGB, false, false, - ADV7604_OP_MODE_SEL_SDR_422 | ADV7604_OP_FORMAT_SEL_8BIT }, - { MEDIA_BUS_FMT_YVYU8_2X8, ADV7604_OP_CH_SEL_RGB, false, true, - ADV7604_OP_MODE_SEL_SDR_422 | ADV7604_OP_FORMAT_SEL_8BIT }, - { MEDIA_BUS_FMT_YUYV10_2X10, ADV7604_OP_CH_SEL_RGB, false, false, - ADV7604_OP_MODE_SEL_SDR_422 | ADV7604_OP_FORMAT_SEL_10BIT }, - { MEDIA_BUS_FMT_YVYU10_2X10, ADV7604_OP_CH_SEL_RGB, false, true, - ADV7604_OP_MODE_SEL_SDR_422 | ADV7604_OP_FORMAT_SEL_10BIT }, - { MEDIA_BUS_FMT_YUYV12_2X12, ADV7604_OP_CH_SEL_RGB, false, false, - ADV7604_OP_MODE_SEL_SDR_422 | ADV7604_OP_FORMAT_SEL_12BIT }, - { MEDIA_BUS_FMT_YVYU12_2X12, ADV7604_OP_CH_SEL_RGB, false, true, - ADV7604_OP_MODE_SEL_SDR_422 | ADV7604_OP_FORMAT_SEL_12BIT }, - { MEDIA_BUS_FMT_UYVY8_1X16, ADV7604_OP_CH_SEL_RBG, false, false, - ADV7604_OP_MODE_SEL_SDR_422_2X | ADV7604_OP_FORMAT_SEL_8BIT }, - { MEDIA_BUS_FMT_VYUY8_1X16, ADV7604_OP_CH_SEL_RBG, false, true, - ADV7604_OP_MODE_SEL_SDR_422_2X | ADV7604_OP_FORMAT_SEL_8BIT }, - { MEDIA_BUS_FMT_YUYV8_1X16, ADV7604_OP_CH_SEL_RGB, false, false, - ADV7604_OP_MODE_SEL_SDR_422_2X | ADV7604_OP_FORMAT_SEL_8BIT }, - { MEDIA_BUS_FMT_YVYU8_1X16, ADV7604_OP_CH_SEL_RGB, false, true, - ADV7604_OP_MODE_SEL_SDR_422_2X | ADV7604_OP_FORMAT_SEL_8BIT }, - { MEDIA_BUS_FMT_UYVY10_1X20, ADV7604_OP_CH_SEL_RBG, false, false, - ADV7604_OP_MODE_SEL_SDR_422_2X | ADV7604_OP_FORMAT_SEL_10BIT }, - { MEDIA_BUS_FMT_VYUY10_1X20, ADV7604_OP_CH_SEL_RBG, false, true, - ADV7604_OP_MODE_SEL_SDR_422_2X | ADV7604_OP_FORMAT_SEL_10BIT }, - { MEDIA_BUS_FMT_YUYV10_1X20, ADV7604_OP_CH_SEL_RGB, false, false, - ADV7604_OP_MODE_SEL_SDR_422_2X | ADV7604_OP_FORMAT_SEL_10BIT }, - { MEDIA_BUS_FMT_YVYU10_1X20, ADV7604_OP_CH_SEL_RGB, false, true, - ADV7604_OP_MODE_SEL_SDR_422_2X | ADV7604_OP_FORMAT_SEL_10BIT }, - { MEDIA_BUS_FMT_UYVY12_1X24, ADV7604_OP_CH_SEL_RBG, false, false, - ADV7604_OP_MODE_SEL_SDR_422_2X | ADV7604_OP_FORMAT_SEL_12BIT }, - { MEDIA_BUS_FMT_VYUY12_1X24, ADV7604_OP_CH_SEL_RBG, false, true, - ADV7604_OP_MODE_SEL_SDR_422_2X | ADV7604_OP_FORMAT_SEL_12BIT }, - { MEDIA_BUS_FMT_YUYV12_1X24, ADV7604_OP_CH_SEL_RGB, false, false, - ADV7604_OP_MODE_SEL_SDR_422_2X | ADV7604_OP_FORMAT_SEL_12BIT }, - { MEDIA_BUS_FMT_YVYU12_1X24, ADV7604_OP_CH_SEL_RGB, false, true, - ADV7604_OP_MODE_SEL_SDR_422_2X | ADV7604_OP_FORMAT_SEL_12BIT }, +static const struct adv76xx_format_info adv7604_formats[] = { + { MEDIA_BUS_FMT_RGB888_1X24, ADV76XX_OP_CH_SEL_RGB, true, false, + ADV76XX_OP_MODE_SEL_SDR_444 | ADV76XX_OP_FORMAT_SEL_8BIT }, + { MEDIA_BUS_FMT_YUYV8_2X8, ADV76XX_OP_CH_SEL_RGB, false, false, + ADV76XX_OP_MODE_SEL_SDR_422 | ADV76XX_OP_FORMAT_SEL_8BIT }, + { MEDIA_BUS_FMT_YVYU8_2X8, ADV76XX_OP_CH_SEL_RGB, false, true, + ADV76XX_OP_MODE_SEL_SDR_422 | ADV76XX_OP_FORMAT_SEL_8BIT }, + { MEDIA_BUS_FMT_YUYV10_2X10, ADV76XX_OP_CH_SEL_RGB, false, false, + ADV76XX_OP_MODE_SEL_SDR_422 | ADV7604_OP_FORMAT_SEL_10BIT }, + { MEDIA_BUS_FMT_YVYU10_2X10, ADV76XX_OP_CH_SEL_RGB, false, true, + ADV76XX_OP_MODE_SEL_SDR_422 | ADV7604_OP_FORMAT_SEL_10BIT }, + { MEDIA_BUS_FMT_YUYV12_2X12, ADV76XX_OP_CH_SEL_RGB, false, false, + ADV76XX_OP_MODE_SEL_SDR_422 | ADV76XX_OP_FORMAT_SEL_12BIT }, + { MEDIA_BUS_FMT_YVYU12_2X12, ADV76XX_OP_CH_SEL_RGB, false, true, + ADV76XX_OP_MODE_SEL_SDR_422 | ADV76XX_OP_FORMAT_SEL_12BIT }, + { MEDIA_BUS_FMT_UYVY8_1X16, ADV76XX_OP_CH_SEL_RBG, false, false, + ADV76XX_OP_MODE_SEL_SDR_422_2X | ADV76XX_OP_FORMAT_SEL_8BIT }, + { MEDIA_BUS_FMT_VYUY8_1X16, ADV76XX_OP_CH_SEL_RBG, false, true, + ADV76XX_OP_MODE_SEL_SDR_422_2X | ADV76XX_OP_FORMAT_SEL_8BIT }, + { MEDIA_BUS_FMT_YUYV8_1X16, ADV76XX_OP_CH_SEL_RGB, false, false, + ADV76XX_OP_MODE_SEL_SDR_422_2X | ADV76XX_OP_FORMAT_SEL_8BIT }, + { MEDIA_BUS_FMT_YVYU8_1X16, ADV76XX_OP_CH_SEL_RGB, false, true, + ADV76XX_OP_MODE_SEL_SDR_422_2X | ADV76XX_OP_FORMAT_SEL_8BIT }, + { MEDIA_BUS_FMT_UYVY10_1X20, ADV76XX_OP_CH_SEL_RBG, false, false, + ADV76XX_OP_MODE_SEL_SDR_422_2X | ADV7604_OP_FORMAT_SEL_10BIT }, + { MEDIA_BUS_FMT_VYUY10_1X20, ADV76XX_OP_CH_SEL_RBG, false, true, + ADV76XX_OP_MODE_SEL_SDR_422_2X | ADV7604_OP_FORMAT_SEL_10BIT }, + { MEDIA_BUS_FMT_YUYV10_1X20, ADV76XX_OP_CH_SEL_RGB, false, false, + ADV76XX_OP_MODE_SEL_SDR_422_2X | ADV7604_OP_FORMAT_SEL_10BIT }, + { MEDIA_BUS_FMT_YVYU10_1X20, ADV76XX_OP_CH_SEL_RGB, false, true, + ADV76XX_OP_MODE_SEL_SDR_422_2X | ADV7604_OP_FORMAT_SEL_10BIT }, + { MEDIA_BUS_FMT_UYVY12_1X24, ADV76XX_OP_CH_SEL_RBG, false, false, + ADV76XX_OP_MODE_SEL_SDR_422_2X | ADV76XX_OP_FORMAT_SEL_12BIT }, + { MEDIA_BUS_FMT_VYUY12_1X24, ADV76XX_OP_CH_SEL_RBG, false, true, + ADV76XX_OP_MODE_SEL_SDR_422_2X | ADV76XX_OP_FORMAT_SEL_12BIT }, + { MEDIA_BUS_FMT_YUYV12_1X24, ADV76XX_OP_CH_SEL_RGB, false, false, + ADV76XX_OP_MODE_SEL_SDR_422_2X | ADV76XX_OP_FORMAT_SEL_12BIT }, + { MEDIA_BUS_FMT_YVYU12_1X24, ADV76XX_OP_CH_SEL_RGB, false, true, + ADV76XX_OP_MODE_SEL_SDR_422_2X | ADV76XX_OP_FORMAT_SEL_12BIT }, }; -static const struct adv7604_format_info adv7611_formats[] = { - { MEDIA_BUS_FMT_RGB888_1X24, ADV7604_OP_CH_SEL_RGB, true, false, - ADV7604_OP_MODE_SEL_SDR_444 | ADV7604_OP_FORMAT_SEL_8BIT }, - { MEDIA_BUS_FMT_YUYV8_2X8, ADV7604_OP_CH_SEL_RGB, false, false, - ADV7604_OP_MODE_SEL_SDR_422 | ADV7604_OP_FORMAT_SEL_8BIT }, - { MEDIA_BUS_FMT_YVYU8_2X8, ADV7604_OP_CH_SEL_RGB, false, true, - ADV7604_OP_MODE_SEL_SDR_422 | ADV7604_OP_FORMAT_SEL_8BIT }, - { MEDIA_BUS_FMT_YUYV12_2X12, ADV7604_OP_CH_SEL_RGB, false, false, - ADV7604_OP_MODE_SEL_SDR_422 | ADV7604_OP_FORMAT_SEL_12BIT }, - { MEDIA_BUS_FMT_YVYU12_2X12, ADV7604_OP_CH_SEL_RGB, false, true, - ADV7604_OP_MODE_SEL_SDR_422 | ADV7604_OP_FORMAT_SEL_12BIT }, - { MEDIA_BUS_FMT_UYVY8_1X16, ADV7604_OP_CH_SEL_RBG, false, false, - ADV7604_OP_MODE_SEL_SDR_422_2X | ADV7604_OP_FORMAT_SEL_8BIT }, - { MEDIA_BUS_FMT_VYUY8_1X16, ADV7604_OP_CH_SEL_RBG, false, true, - ADV7604_OP_MODE_SEL_SDR_422_2X | ADV7604_OP_FORMAT_SEL_8BIT }, - { MEDIA_BUS_FMT_YUYV8_1X16, ADV7604_OP_CH_SEL_RGB, false, false, - ADV7604_OP_MODE_SEL_SDR_422_2X | ADV7604_OP_FORMAT_SEL_8BIT }, - { MEDIA_BUS_FMT_YVYU8_1X16, ADV7604_OP_CH_SEL_RGB, false, true, - ADV7604_OP_MODE_SEL_SDR_422_2X | ADV7604_OP_FORMAT_SEL_8BIT }, - { MEDIA_BUS_FMT_UYVY12_1X24, ADV7604_OP_CH_SEL_RBG, false, false, - ADV7604_OP_MODE_SEL_SDR_422_2X | ADV7604_OP_FORMAT_SEL_12BIT }, - { MEDIA_BUS_FMT_VYUY12_1X24, ADV7604_OP_CH_SEL_RBG, false, true, - ADV7604_OP_MODE_SEL_SDR_422_2X | ADV7604_OP_FORMAT_SEL_12BIT }, - { MEDIA_BUS_FMT_YUYV12_1X24, ADV7604_OP_CH_SEL_RGB, false, false, - ADV7604_OP_MODE_SEL_SDR_422_2X | ADV7604_OP_FORMAT_SEL_12BIT }, - { MEDIA_BUS_FMT_YVYU12_1X24, ADV7604_OP_CH_SEL_RGB, false, true, - ADV7604_OP_MODE_SEL_SDR_422_2X | ADV7604_OP_FORMAT_SEL_12BIT }, +static const struct adv76xx_format_info adv7611_formats[] = { + { MEDIA_BUS_FMT_RGB888_1X24, ADV76XX_OP_CH_SEL_RGB, true, false, + ADV76XX_OP_MODE_SEL_SDR_444 | ADV76XX_OP_FORMAT_SEL_8BIT }, + { MEDIA_BUS_FMT_YUYV8_2X8, ADV76XX_OP_CH_SEL_RGB, false, false, + ADV76XX_OP_MODE_SEL_SDR_422 | ADV76XX_OP_FORMAT_SEL_8BIT }, + { MEDIA_BUS_FMT_YVYU8_2X8, ADV76XX_OP_CH_SEL_RGB, false, true, + ADV76XX_OP_MODE_SEL_SDR_422 | ADV76XX_OP_FORMAT_SEL_8BIT }, + { MEDIA_BUS_FMT_YUYV12_2X12, ADV76XX_OP_CH_SEL_RGB, false, false, + ADV76XX_OP_MODE_SEL_SDR_422 | ADV76XX_OP_FORMAT_SEL_12BIT }, + { MEDIA_BUS_FMT_YVYU12_2X12, ADV76XX_OP_CH_SEL_RGB, false, true, + ADV76XX_OP_MODE_SEL_SDR_422 | ADV76XX_OP_FORMAT_SEL_12BIT }, + { MEDIA_BUS_FMT_UYVY8_1X16, ADV76XX_OP_CH_SEL_RBG, false, false, + ADV76XX_OP_MODE_SEL_SDR_422_2X | ADV76XX_OP_FORMAT_SEL_8BIT }, + { MEDIA_BUS_FMT_VYUY8_1X16, ADV76XX_OP_CH_SEL_RBG, false, true, + ADV76XX_OP_MODE_SEL_SDR_422_2X | ADV76XX_OP_FORMAT_SEL_8BIT }, + { MEDIA_BUS_FMT_YUYV8_1X16, ADV76XX_OP_CH_SEL_RGB, false, false, + ADV76XX_OP_MODE_SEL_SDR_422_2X | ADV76XX_OP_FORMAT_SEL_8BIT }, + { MEDIA_BUS_FMT_YVYU8_1X16, ADV76XX_OP_CH_SEL_RGB, false, true, + ADV76XX_OP_MODE_SEL_SDR_422_2X | ADV76XX_OP_FORMAT_SEL_8BIT }, + { MEDIA_BUS_FMT_UYVY12_1X24, ADV76XX_OP_CH_SEL_RBG, false, false, + ADV76XX_OP_MODE_SEL_SDR_422_2X | ADV76XX_OP_FORMAT_SEL_12BIT }, + { MEDIA_BUS_FMT_VYUY12_1X24, ADV76XX_OP_CH_SEL_RBG, false, true, + ADV76XX_OP_MODE_SEL_SDR_422_2X | ADV76XX_OP_FORMAT_SEL_12BIT }, + { MEDIA_BUS_FMT_YUYV12_1X24, ADV76XX_OP_CH_SEL_RGB, false, false, + ADV76XX_OP_MODE_SEL_SDR_422_2X | ADV76XX_OP_FORMAT_SEL_12BIT }, + { MEDIA_BUS_FMT_YVYU12_1X24, ADV76XX_OP_CH_SEL_RGB, false, true, + ADV76XX_OP_MODE_SEL_SDR_422_2X | ADV76XX_OP_FORMAT_SEL_12BIT }, }; -static const struct adv7604_format_info * -adv7604_format_info(struct adv7604_state *state, u32 code) +static const struct adv76xx_format_info * +adv76xx_format_info(struct adv76xx_state *state, u32 code) { unsigned int i; @@ -760,7 +760,7 @@ adv7604_format_info(struct adv7604_state *state, u32 code) static inline bool is_analog_input(struct v4l2_subdev *sd) { - struct adv7604_state *state = to_state(sd); + struct adv76xx_state *state = to_state(sd); return state->selected_input == ADV7604_PAD_VGA_RGB || state->selected_input == ADV7604_PAD_VGA_COMP; @@ -768,9 +768,9 @@ static inline bool is_analog_input(struct v4l2_subdev *sd) static inline bool is_digital_input(struct v4l2_subdev *sd) { - struct adv7604_state *state = to_state(sd); + struct adv76xx_state *state = to_state(sd); - return state->selected_input == ADV7604_PAD_HDMI_PORT_A || + return state->selected_input == ADV76XX_PAD_HDMI_PORT_A || state->selected_input == ADV7604_PAD_HDMI_PORT_B || state->selected_input == ADV7604_PAD_HDMI_PORT_C || state->selected_input == ADV7604_PAD_HDMI_PORT_D; @@ -779,7 +779,7 @@ static inline bool is_digital_input(struct v4l2_subdev *sd) /* ----------------------------------------------------------------------- */ #ifdef CONFIG_VIDEO_ADV_DEBUG -static void adv7604_inv_register(struct v4l2_subdev *sd) +static void adv76xx_inv_register(struct v4l2_subdev *sd) { v4l2_info(sd, "0x000-0x0ff: IO Map\n"); v4l2_info(sd, "0x100-0x1ff: AVLink Map\n"); @@ -796,15 +796,15 @@ static void adv7604_inv_register(struct v4l2_subdev *sd) v4l2_info(sd, "0xc00-0xcff: VDP Map\n"); } -static int adv7604_g_register(struct v4l2_subdev *sd, +static int adv76xx_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) { int ret; - ret = adv7604_read_reg(sd, reg->reg); + ret = adv76xx_read_reg(sd, reg->reg); if (ret < 0) { v4l2_info(sd, "Register %03llx not supported\n", reg->reg); - adv7604_inv_register(sd); + adv76xx_inv_register(sd); return ret; } @@ -814,15 +814,15 @@ static int adv7604_g_register(struct v4l2_subdev *sd, return 0; } -static int adv7604_s_register(struct v4l2_subdev *sd, +static int adv76xx_s_register(struct v4l2_subdev *sd, const struct v4l2_dbg_register *reg) { int ret; - ret = adv7604_write_reg(sd, reg->reg, reg->val); + ret = adv76xx_write_reg(sd, reg->reg, reg->val); if (ret < 0) { v4l2_info(sd, "Register %03llx not supported\n", reg->reg); - adv7604_inv_register(sd); + adv76xx_inv_register(sd); return ret; } @@ -847,10 +847,10 @@ static unsigned int adv7611_read_cable_det(struct v4l2_subdev *sd) return value & 1; } -static int adv7604_s_detect_tx_5v_ctrl(struct v4l2_subdev *sd) +static int adv76xx_s_detect_tx_5v_ctrl(struct v4l2_subdev *sd) { - struct adv7604_state *state = to_state(sd); - const struct adv7604_chip_info *info = state->info; + struct adv76xx_state *state = to_state(sd); + const struct adv76xx_chip_info *info = state->info; return v4l2_ctrl_s_ctrl(state->detect_tx_5v_ctrl, info->read_cable_det(sd)); @@ -858,7 +858,7 @@ static int adv7604_s_detect_tx_5v_ctrl(struct v4l2_subdev *sd) static int find_and_set_predefined_video_timings(struct v4l2_subdev *sd, u8 prim_mode, - const struct adv7604_video_standards *predef_vid_timings, + const struct adv76xx_video_standards *predef_vid_timings, const struct v4l2_dv_timings *timings) { int i; @@ -879,12 +879,12 @@ static int find_and_set_predefined_video_timings(struct v4l2_subdev *sd, static int configure_predefined_video_timings(struct v4l2_subdev *sd, struct v4l2_dv_timings *timings) { - struct adv7604_state *state = to_state(sd); + struct adv76xx_state *state = to_state(sd); int err; v4l2_dbg(1, debug, sd, "%s", __func__); - if (adv7604_has_afe(state)) { + if (adv76xx_has_afe(state)) { /* reset to default values */ io_write(sd, 0x16, 0x43); io_write(sd, 0x17, 0x5a); @@ -910,10 +910,10 @@ static int configure_predefined_video_timings(struct v4l2_subdev *sd, 0x02, adv7604_prim_mode_gr, timings); } else if (is_digital_input(sd)) { err = find_and_set_predefined_video_timings(sd, - 0x05, adv7604_prim_mode_hdmi_comp, timings); + 0x05, adv76xx_prim_mode_hdmi_comp, timings); if (err) err = find_and_set_predefined_video_timings(sd, - 0x06, adv7604_prim_mode_hdmi_gr, timings); + 0x06, adv76xx_prim_mode_hdmi_gr, timings); } else { v4l2_dbg(2, debug, sd, "%s: Unknown port %d selected\n", __func__, state->selected_input); @@ -927,7 +927,7 @@ static int configure_predefined_video_timings(struct v4l2_subdev *sd, static void configure_custom_video_timings(struct v4l2_subdev *sd, const struct v4l2_bt_timings *bt) { - struct adv7604_state *state = to_state(sd); + struct adv76xx_state *state = to_state(sd); u32 width = htotal(bt); u32 height = vtotal(bt); u16 cp_start_sav = bt->hsync + bt->hbackporch - 4; @@ -935,7 +935,7 @@ static void configure_custom_video_timings(struct v4l2_subdev *sd, u16 cp_start_vbi = height - bt->vfrontporch; u16 cp_end_vbi = bt->vsync + bt->vbackporch; u16 ch1_fr_ll = (((u32)bt->pixelclock / 100) > 0) ? - ((width * (ADV7604_fsc / 100)) / ((u32)bt->pixelclock / 100)) : 0; + ((width * (ADV76XX_FSC / 100)) / ((u32)bt->pixelclock / 100)) : 0; const u8 pll[2] = { 0xc0 | ((width >> 8) & 0x1f), width & 0xff @@ -953,7 +953,7 @@ static void configure_custom_video_timings(struct v4l2_subdev *sd, /* Should only be set in auto-graphics mode [REF_02, p. 91-92] */ /* setup PLL_DIV_MAN_EN and PLL_DIV_RATIO */ /* IO-map reg. 0x16 and 0x17 should be written in sequence */ - if (adv_smbus_write_i2c_block_data(state, ADV7604_PAGE_IO, + if (adv_smbus_write_i2c_block_data(state, ADV76XX_PAGE_IO, 0x16, 2, pll)) v4l2_err(sd, "writing to reg 0x16 and 0x17 failed\n"); @@ -984,9 +984,9 @@ static void configure_custom_video_timings(struct v4l2_subdev *sd, cp_write(sd, 0xac, (height & 0x0f) << 4); } -static void adv7604_set_offset(struct v4l2_subdev *sd, bool auto_offset, u16 offset_a, u16 offset_b, u16 offset_c) +static void adv76xx_set_offset(struct v4l2_subdev *sd, bool auto_offset, u16 offset_a, u16 offset_b, u16 offset_c) { - struct adv7604_state *state = to_state(sd); + struct adv76xx_state *state = to_state(sd); u8 offset_buf[4]; if (auto_offset) { @@ -1005,14 +1005,14 @@ static void adv7604_set_offset(struct v4l2_subdev *sd, bool auto_offset, u16 off offset_buf[3] = offset_c & 0x0ff; /* Registers must be written in this order with no i2c access in between */ - if (adv_smbus_write_i2c_block_data(state, ADV7604_PAGE_CP, + if (adv_smbus_write_i2c_block_data(state, ADV76XX_PAGE_CP, 0x77, 4, offset_buf)) v4l2_err(sd, "%s: i2c error writing to CP reg 0x77, 0x78, 0x79, 0x7a\n", __func__); } -static void adv7604_set_gain(struct v4l2_subdev *sd, bool auto_gain, u16 gain_a, u16 gain_b, u16 gain_c) +static void adv76xx_set_gain(struct v4l2_subdev *sd, bool auto_gain, u16 gain_a, u16 gain_b, u16 gain_c) { - struct adv7604_state *state = to_state(sd); + struct adv76xx_state *state = to_state(sd); u8 gain_buf[4]; u8 gain_man = 1; u8 agc_mode_man = 1; @@ -1035,14 +1035,14 @@ static void adv7604_set_gain(struct v4l2_subdev *sd, bool auto_gain, u16 gain_a, gain_buf[3] = ((gain_c & 0x0ff)); /* Registers must be written in this order with no i2c access in between */ - if (adv_smbus_write_i2c_block_data(state, ADV7604_PAGE_CP, + if (adv_smbus_write_i2c_block_data(state, ADV76XX_PAGE_CP, 0x73, 4, gain_buf)) v4l2_err(sd, "%s: i2c error writing to CP reg 0x73, 0x74, 0x75, 0x76\n", __func__); } static void set_rgb_quantization_range(struct v4l2_subdev *sd) { - struct adv7604_state *state = to_state(sd); + struct adv76xx_state *state = to_state(sd); bool rgb_output = io_read(sd, 0x02) & 0x02; bool hdmi_signal = hdmi_read(sd, 0x05) & 0x80; @@ -1050,8 +1050,8 @@ static void set_rgb_quantization_range(struct v4l2_subdev *sd) __func__, state->rgb_quantization_range, rgb_output, hdmi_signal); - adv7604_set_gain(sd, true, 0x0, 0x0, 0x0); - adv7604_set_offset(sd, true, 0x0, 0x0, 0x0); + adv76xx_set_gain(sd, true, 0x0, 0x0, 0x0); + adv76xx_set_offset(sd, true, 0x0, 0x0, 0x0); switch (state->rgb_quantization_range) { case V4L2_DV_RGB_RANGE_AUTO: @@ -1087,10 +1087,10 @@ static void set_rgb_quantization_range(struct v4l2_subdev *sd) io_write_clr_set(sd, 0x02, 0xf0, 0x10); if (is_digital_input(sd) && rgb_output) { - adv7604_set_offset(sd, false, 0x40, 0x40, 0x40); + adv76xx_set_offset(sd, false, 0x40, 0x40, 0x40); } else { - adv7604_set_gain(sd, false, 0xe0, 0xe0, 0xe0); - adv7604_set_offset(sd, false, 0x70, 0x70, 0x70); + adv76xx_set_gain(sd, false, 0xe0, 0xe0, 0xe0); + adv76xx_set_offset(sd, false, 0x70, 0x70, 0x70); } } break; @@ -1120,21 +1120,21 @@ static void set_rgb_quantization_range(struct v4l2_subdev *sd) /* Adjust gain/offset for DVI-D signals only */ if (rgb_output) { - adv7604_set_offset(sd, false, 0x40, 0x40, 0x40); + adv76xx_set_offset(sd, false, 0x40, 0x40, 0x40); } else { - adv7604_set_gain(sd, false, 0xe0, 0xe0, 0xe0); - adv7604_set_offset(sd, false, 0x70, 0x70, 0x70); + adv76xx_set_gain(sd, false, 0xe0, 0xe0, 0xe0); + adv76xx_set_offset(sd, false, 0x70, 0x70, 0x70); } break; } } -static int adv7604_s_ctrl(struct v4l2_ctrl *ctrl) +static int adv76xx_s_ctrl(struct v4l2_ctrl *ctrl) { struct v4l2_subdev *sd = - &container_of(ctrl->handler, struct adv7604_state, hdl)->sd; + &container_of(ctrl->handler, struct adv76xx_state, hdl)->sd; - struct adv7604_state *state = to_state(sd); + struct adv76xx_state *state = to_state(sd); switch (ctrl->id) { case V4L2_CID_BRIGHTNESS: @@ -1154,7 +1154,7 @@ static int adv7604_s_ctrl(struct v4l2_ctrl *ctrl) set_rgb_quantization_range(sd); return 0; case V4L2_CID_ADV_RX_ANALOG_SAMPLING_PHASE: - if (!adv7604_has_afe(state)) + if (!adv76xx_has_afe(state)) return -EINVAL; /* Set the analog sampling phase. This is needed to find the best sampling phase for analog video: an application or @@ -1186,15 +1186,15 @@ static inline bool no_power(struct v4l2_subdev *sd) static inline bool no_signal_tmds(struct v4l2_subdev *sd) { - struct adv7604_state *state = to_state(sd); + struct adv76xx_state *state = to_state(sd); return !(io_read(sd, 0x6a) & (0x10 >> state->selected_input)); } static inline bool no_lock_tmds(struct v4l2_subdev *sd) { - struct adv7604_state *state = to_state(sd); - const struct adv7604_chip_info *info = state->info; + struct adv76xx_state *state = to_state(sd); + const struct adv76xx_chip_info *info = state->info; return (io_read(sd, 0x6a) & info->tdms_lock_mask) != info->tdms_lock_mask; } @@ -1206,13 +1206,13 @@ static inline bool is_hdmi(struct v4l2_subdev *sd) static inline bool no_lock_sspd(struct v4l2_subdev *sd) { - struct adv7604_state *state = to_state(sd); + struct adv76xx_state *state = to_state(sd); /* * Chips without a AFE don't expose registers for the SSPD, so just assume * that we have a lock. */ - if (adv7604_has_afe(state)) + if (adv76xx_has_afe(state)) return false; /* TODO channel 2 */ @@ -1244,9 +1244,9 @@ static inline bool no_signal(struct v4l2_subdev *sd) static inline bool no_lock_cp(struct v4l2_subdev *sd) { - struct adv7604_state *state = to_state(sd); + struct adv76xx_state *state = to_state(sd); - if (!adv7604_has_afe(state)) + if (!adv76xx_has_afe(state)) return false; /* CP has detected a non standard number of lines on the incoming @@ -1259,7 +1259,7 @@ static inline bool in_free_run(struct v4l2_subdev *sd) return cp_read(sd, 0xff) & 0x10; } -static int adv7604_g_input_status(struct v4l2_subdev *sd, u32 *status) +static int adv76xx_g_input_status(struct v4l2_subdev *sd, u32 *status) { *status = 0; *status |= no_power(sd) ? V4L2_IN_ST_NO_POWER : 0; @@ -1285,22 +1285,22 @@ static int stdi2dv_timings(struct v4l2_subdev *sd, struct stdi_readback *stdi, struct v4l2_dv_timings *timings) { - struct adv7604_state *state = to_state(sd); - u32 hfreq = (ADV7604_fsc * 8) / stdi->bl; + struct adv76xx_state *state = to_state(sd); + u32 hfreq = (ADV76XX_FSC * 8) / stdi->bl; u32 pix_clk; int i; - for (i = 0; adv7604_timings[i].bt.height; i++) { - if (vtotal(&adv7604_timings[i].bt) != stdi->lcf + 1) + for (i = 0; adv76xx_timings[i].bt.height; i++) { + if (vtotal(&adv76xx_timings[i].bt) != stdi->lcf + 1) continue; - if (adv7604_timings[i].bt.vsync != stdi->lcvs) + if (adv76xx_timings[i].bt.vsync != stdi->lcvs) continue; - pix_clk = hfreq * htotal(&adv7604_timings[i].bt); + pix_clk = hfreq * htotal(&adv76xx_timings[i].bt); - if ((pix_clk < adv7604_timings[i].bt.pixelclock + 1000000) && - (pix_clk > adv7604_timings[i].bt.pixelclock - 1000000)) { - *timings = adv7604_timings[i]; + if ((pix_clk < adv76xx_timings[i].bt.pixelclock + 1000000) && + (pix_clk > adv76xx_timings[i].bt.pixelclock - 1000000)) { + *timings = adv76xx_timings[i]; return 0; } } @@ -1326,8 +1326,8 @@ static int stdi2dv_timings(struct v4l2_subdev *sd, static int read_stdi(struct v4l2_subdev *sd, struct stdi_readback *stdi) { - struct adv7604_state *state = to_state(sd); - const struct adv7604_chip_info *info = state->info; + struct adv76xx_state *state = to_state(sd); + const struct adv76xx_chip_info *info = state->info; u8 polarity; if (no_lock_stdi(sd) || no_lock_sspd(sd)) { @@ -1341,7 +1341,7 @@ static int read_stdi(struct v4l2_subdev *sd, struct stdi_readback *stdi) stdi->lcvs = cp_read(sd, 0xb3) >> 3; stdi->interlaced = io_read(sd, 0x12) & 0x10; - if (adv7604_has_afe(state)) { + if (adv76xx_has_afe(state)) { /* read SSPD */ polarity = cp_read(sd, 0xb5); if ((polarity & 0x03) == 0x01) { @@ -1380,26 +1380,26 @@ static int read_stdi(struct v4l2_subdev *sd, struct stdi_readback *stdi) return 0; } -static int adv7604_enum_dv_timings(struct v4l2_subdev *sd, +static int adv76xx_enum_dv_timings(struct v4l2_subdev *sd, struct v4l2_enum_dv_timings *timings) { - struct adv7604_state *state = to_state(sd); + struct adv76xx_state *state = to_state(sd); - if (timings->index >= ARRAY_SIZE(adv7604_timings) - 1) + if (timings->index >= ARRAY_SIZE(adv76xx_timings) - 1) return -EINVAL; if (timings->pad >= state->source_pad) return -EINVAL; memset(timings->reserved, 0, sizeof(timings->reserved)); - timings->timings = adv7604_timings[timings->index]; + timings->timings = adv76xx_timings[timings->index]; return 0; } -static int adv7604_dv_timings_cap(struct v4l2_subdev *sd, +static int adv76xx_dv_timings_cap(struct v4l2_subdev *sd, struct v4l2_dv_timings_cap *cap) { - struct adv7604_state *state = to_state(sd); + struct adv76xx_state *state = to_state(sd); if (cap->pad >= state->source_pad) return -EINVAL; @@ -1410,7 +1410,7 @@ static int adv7604_dv_timings_cap(struct v4l2_subdev *sd, cap->bt.min_pixelclock = 25000000; switch (cap->pad) { - case ADV7604_PAD_HDMI_PORT_A: + case ADV76XX_PAD_HDMI_PORT_A: case ADV7604_PAD_HDMI_PORT_B: case ADV7604_PAD_HDMI_PORT_C: case ADV7604_PAD_HDMI_PORT_D: @@ -1431,16 +1431,16 @@ static int adv7604_dv_timings_cap(struct v4l2_subdev *sd, } /* Fill the optional fields .standards and .flags in struct v4l2_dv_timings - if the format is listed in adv7604_timings[] */ -static void adv7604_fill_optional_dv_timings_fields(struct v4l2_subdev *sd, + if the format is listed in adv76xx_timings[] */ +static void adv76xx_fill_optional_dv_timings_fields(struct v4l2_subdev *sd, struct v4l2_dv_timings *timings) { int i; - for (i = 0; adv7604_timings[i].bt.width; i++) { - if (v4l2_match_dv_timings(timings, &adv7604_timings[i], + for (i = 0; adv76xx_timings[i].bt.width; i++) { + if (v4l2_match_dv_timings(timings, &adv76xx_timings[i], is_digital_input(sd) ? 250000 : 1000000)) { - *timings = adv7604_timings[i]; + *timings = adv76xx_timings[i]; break; } } @@ -1478,11 +1478,11 @@ static unsigned int adv7611_read_hdmi_pixelclock(struct v4l2_subdev *sd) return ((a << 1) | (b >> 7)) * 1000000 + (b & 0x7f) * 1000000 / 128; } -static int adv7604_query_dv_timings(struct v4l2_subdev *sd, +static int adv76xx_query_dv_timings(struct v4l2_subdev *sd, struct v4l2_dv_timings *timings) { - struct adv7604_state *state = to_state(sd); - const struct adv7604_chip_info *info = state->info; + struct adv76xx_state *state = to_state(sd); + const struct adv76xx_chip_info *info = state->info; struct v4l2_bt_timings *bt = &timings->bt; struct stdi_readback stdi; @@ -1526,7 +1526,7 @@ static int adv7604_query_dv_timings(struct v4l2_subdev *sd, bt->il_vsync = hdmi_read16(sd, 0x30, 0x1fff) / 2; bt->il_vbackporch = hdmi_read16(sd, 0x34, 0x1fff) / 2; } - adv7604_fill_optional_dv_timings_fields(sd, timings); + adv76xx_fill_optional_dv_timings_fields(sd, timings); } else { /* find format * Since LCVS values are inaccurate [REF_03, p. 275-276], @@ -1583,16 +1583,16 @@ found: } if (debug > 1) - v4l2_print_dv_timings(sd->name, "adv7604_query_dv_timings: ", + v4l2_print_dv_timings(sd->name, "adv76xx_query_dv_timings: ", timings, true); return 0; } -static int adv7604_s_dv_timings(struct v4l2_subdev *sd, +static int adv76xx_s_dv_timings(struct v4l2_subdev *sd, struct v4l2_dv_timings *timings) { - struct adv7604_state *state = to_state(sd); + struct adv76xx_state *state = to_state(sd); struct v4l2_bt_timings *bt; int err; @@ -1613,7 +1613,7 @@ static int adv7604_s_dv_timings(struct v4l2_subdev *sd, return -ERANGE; } - adv7604_fill_optional_dv_timings_fields(sd, timings); + adv76xx_fill_optional_dv_timings_fields(sd, timings); state->timings = *timings; @@ -1630,15 +1630,15 @@ static int adv7604_s_dv_timings(struct v4l2_subdev *sd, set_rgb_quantization_range(sd); if (debug > 1) - v4l2_print_dv_timings(sd->name, "adv7604_s_dv_timings: ", + v4l2_print_dv_timings(sd->name, "adv76xx_s_dv_timings: ", timings, true); return 0; } -static int adv7604_g_dv_timings(struct v4l2_subdev *sd, +static int adv76xx_g_dv_timings(struct v4l2_subdev *sd, struct v4l2_dv_timings *timings) { - struct adv7604_state *state = to_state(sd); + struct adv76xx_state *state = to_state(sd); *timings = state->timings; return 0; @@ -1656,7 +1656,7 @@ static void adv7611_set_termination(struct v4l2_subdev *sd, bool enable) static void enable_input(struct v4l2_subdev *sd) { - struct adv7604_state *state = to_state(sd); + struct adv76xx_state *state = to_state(sd); if (is_analog_input(sd)) { io_write(sd, 0x15, 0xb0); /* Disable Tristate of Pins (no audio) */ @@ -1673,7 +1673,7 @@ static void enable_input(struct v4l2_subdev *sd) static void disable_input(struct v4l2_subdev *sd) { - struct adv7604_state *state = to_state(sd); + struct adv76xx_state *state = to_state(sd); hdmi_write_clr_set(sd, 0x1a, 0x10, 0x10); /* Mute audio */ msleep(16); /* 512 samples with >= 32 kHz sample rate [REF_03, c. 7.16.10] */ @@ -1683,11 +1683,11 @@ static void disable_input(struct v4l2_subdev *sd) static void select_input(struct v4l2_subdev *sd) { - struct adv7604_state *state = to_state(sd); - const struct adv7604_chip_info *info = state->info; + struct adv76xx_state *state = to_state(sd); + const struct adv76xx_chip_info *info = state->info; if (is_analog_input(sd)) { - adv7604_write_reg_seq(sd, info->recommended_settings[0]); + adv76xx_write_reg_seq(sd, info->recommended_settings[0]); afe_write(sd, 0x00, 0x08); /* power up ADC */ afe_write(sd, 0x01, 0x06); /* power up Analog Front End */ @@ -1695,9 +1695,9 @@ static void select_input(struct v4l2_subdev *sd) } else if (is_digital_input(sd)) { hdmi_write(sd, 0x00, state->selected_input & 0x03); - adv7604_write_reg_seq(sd, info->recommended_settings[1]); + adv76xx_write_reg_seq(sd, info->recommended_settings[1]); - if (adv7604_has_afe(state)) { + if (adv76xx_has_afe(state)) { afe_write(sd, 0x00, 0xff); /* power down ADC */ afe_write(sd, 0x01, 0xfe); /* power down Analog Front End */ afe_write(sd, 0xc8, 0x40); /* phase control */ @@ -1712,10 +1712,10 @@ static void select_input(struct v4l2_subdev *sd) } } -static int adv7604_s_routing(struct v4l2_subdev *sd, +static int adv76xx_s_routing(struct v4l2_subdev *sd, u32 input, u32 output, u32 config) { - struct adv7604_state *state = to_state(sd); + struct adv76xx_state *state = to_state(sd); v4l2_dbg(2, debug, sd, "%s: input %d, selected input %d", __func__, input, state->selected_input); @@ -1737,11 +1737,11 @@ static int adv7604_s_routing(struct v4l2_subdev *sd, return 0; } -static int adv7604_enum_mbus_code(struct v4l2_subdev *sd, +static int adv76xx_enum_mbus_code(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, struct v4l2_subdev_mbus_code_enum *code) { - struct adv7604_state *state = to_state(sd); + struct adv76xx_state *state = to_state(sd); if (code->index >= state->info->nformats) return -EINVAL; @@ -1751,7 +1751,7 @@ static int adv7604_enum_mbus_code(struct v4l2_subdev *sd, return 0; } -static void adv7604_fill_format(struct adv7604_state *state, +static void adv76xx_fill_format(struct adv76xx_state *state, struct v4l2_mbus_framefmt *format) { memset(format, 0, sizeof(*format)); @@ -1772,7 +1772,7 @@ static void adv7604_fill_format(struct adv7604_state *state, * * The following table gives the op_ch_value from the format component order * (expressed as op_ch_sel value in column) and the bus reordering (expressed as - * adv7604_bus_order value in row). + * adv76xx_bus_order value in row). * * | GBR(0) GRB(1) BGR(2) RGB(3) BRG(4) RBG(5) * ----------+------------------------------------------------- @@ -1783,11 +1783,11 @@ static void adv7604_fill_format(struct adv7604_state *state, * BRG (ROR) | BRG RBG GRB GBR RGB BGR * GBR (ROL) | RGB BGR RBG BRG GBR GRB */ -static unsigned int adv7604_op_ch_sel(struct adv7604_state *state) +static unsigned int adv76xx_op_ch_sel(struct adv76xx_state *state) { #define _SEL(a,b,c,d,e,f) { \ - ADV7604_OP_CH_SEL_##a, ADV7604_OP_CH_SEL_##b, ADV7604_OP_CH_SEL_##c, \ - ADV7604_OP_CH_SEL_##d, ADV7604_OP_CH_SEL_##e, ADV7604_OP_CH_SEL_##f } + ADV76XX_OP_CH_SEL_##a, ADV76XX_OP_CH_SEL_##b, ADV76XX_OP_CH_SEL_##c, \ + ADV76XX_OP_CH_SEL_##d, ADV76XX_OP_CH_SEL_##e, ADV76XX_OP_CH_SEL_##f } #define _BUS(x) [ADV7604_BUS_ORDER_##x] static const unsigned int op_ch_sel[6][6] = { @@ -1802,28 +1802,28 @@ static unsigned int adv7604_op_ch_sel(struct adv7604_state *state) return op_ch_sel[state->pdata.bus_order][state->format->op_ch_sel >> 5]; } -static void adv7604_setup_format(struct adv7604_state *state) +static void adv76xx_setup_format(struct adv76xx_state *state) { struct v4l2_subdev *sd = &state->sd; io_write_clr_set(sd, 0x02, 0x02, - state->format->rgb_out ? ADV7604_RGB_OUT : 0); + state->format->rgb_out ? ADV76XX_RGB_OUT : 0); io_write(sd, 0x03, state->format->op_format_sel | state->pdata.op_format_mode_sel); - io_write_clr_set(sd, 0x04, 0xe0, adv7604_op_ch_sel(state)); + io_write_clr_set(sd, 0x04, 0xe0, adv76xx_op_ch_sel(state)); io_write_clr_set(sd, 0x05, 0x01, - state->format->swap_cb_cr ? ADV7604_OP_SWAP_CB_CR : 0); + state->format->swap_cb_cr ? ADV76XX_OP_SWAP_CB_CR : 0); } -static int adv7604_get_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, +static int adv76xx_get_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, struct v4l2_subdev_format *format) { - struct adv7604_state *state = to_state(sd); + struct adv76xx_state *state = to_state(sd); if (format->pad != state->source_pad) return -EINVAL; - adv7604_fill_format(state, &format->format); + adv76xx_fill_format(state, &format->format); if (format->which == V4L2_SUBDEV_FORMAT_TRY) { struct v4l2_mbus_framefmt *fmt; @@ -1837,20 +1837,20 @@ static int adv7604_get_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, return 0; } -static int adv7604_set_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, +static int adv76xx_set_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, struct v4l2_subdev_format *format) { - struct adv7604_state *state = to_state(sd); - const struct adv7604_format_info *info; + struct adv76xx_state *state = to_state(sd); + const struct adv76xx_format_info *info; if (format->pad != state->source_pad) return -EINVAL; - info = adv7604_format_info(state, format->format.code); + info = adv76xx_format_info(state, format->format.code); if (info == NULL) - info = adv7604_format_info(state, MEDIA_BUS_FMT_YUYV8_2X8); + info = adv76xx_format_info(state, MEDIA_BUS_FMT_YUYV8_2X8); - adv7604_fill_format(state, &format->format); + adv76xx_fill_format(state, &format->format); format->format.code = info->code; if (format->which == V4L2_SUBDEV_FORMAT_TRY) { @@ -1860,16 +1860,16 @@ static int adv7604_set_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, fmt->code = format->format.code; } else { state->format = info; - adv7604_setup_format(state); + adv76xx_setup_format(state); } return 0; } -static int adv7604_isr(struct v4l2_subdev *sd, u32 status, bool *handled) +static int adv76xx_isr(struct v4l2_subdev *sd, u32 status, bool *handled) { - struct adv7604_state *state = to_state(sd); - const struct adv7604_chip_info *info = state->info; + struct adv76xx_state *state = to_state(sd); + const struct adv76xx_chip_info *info = state->info; const u8 irq_reg_0x43 = io_read(sd, 0x43); const u8 irq_reg_0x6b = io_read(sd, 0x6b); const u8 irq_reg_0x70 = io_read(sd, 0x70); @@ -1897,7 +1897,7 @@ static int adv7604_isr(struct v4l2_subdev *sd, u32 status, bool *handled) "%s: fmt_change = 0x%x, fmt_change_digital = 0x%x\n", __func__, fmt_change, fmt_change_digital); - v4l2_subdev_notify(sd, ADV7604_FMT_CHANGE, NULL); + v4l2_subdev_notify(sd, ADV76XX_FMT_CHANGE, NULL); if (handled) *handled = true; @@ -1916,22 +1916,22 @@ static int adv7604_isr(struct v4l2_subdev *sd, u32 status, bool *handled) if (tx_5v) { v4l2_dbg(1, debug, sd, "%s: tx_5v: 0x%x\n", __func__, tx_5v); io_write(sd, 0x71, tx_5v); - adv7604_s_detect_tx_5v_ctrl(sd); + adv76xx_s_detect_tx_5v_ctrl(sd); if (handled) *handled = true; } return 0; } -static int adv7604_get_edid(struct v4l2_subdev *sd, struct v4l2_edid *edid) +static int adv76xx_get_edid(struct v4l2_subdev *sd, struct v4l2_edid *edid) { - struct adv7604_state *state = to_state(sd); + struct adv76xx_state *state = to_state(sd); u8 *data = NULL; memset(edid->reserved, 0, sizeof(edid->reserved)); switch (edid->pad) { - case ADV7604_PAD_HDMI_PORT_A: + case ADV76XX_PAD_HDMI_PORT_A: case ADV7604_PAD_HDMI_PORT_B: case ADV7604_PAD_HDMI_PORT_C: case ADV7604_PAD_HDMI_PORT_D: @@ -1989,10 +1989,10 @@ static int get_edid_spa_location(const u8 *edid) return -1; } -static int adv7604_set_edid(struct v4l2_subdev *sd, struct v4l2_edid *edid) +static int adv76xx_set_edid(struct v4l2_subdev *sd, struct v4l2_edid *edid) { - struct adv7604_state *state = to_state(sd); - const struct adv7604_chip_info *info = state->info; + struct adv76xx_state *state = to_state(sd); + const struct adv76xx_chip_info *info = state->info; int spa_loc; int err; int i; @@ -2006,7 +2006,7 @@ static int adv7604_set_edid(struct v4l2_subdev *sd, struct v4l2_edid *edid) if (edid->blocks == 0) { /* Disable hotplug and I2C access to EDID RAM from DDC port */ state->edid.present &= ~(1 << edid->pad); - adv7604_set_hpd(state, state->edid.present); + adv76xx_set_hpd(state, state->edid.present); rep_write_clr_set(sd, info->edid_enable_reg, 0x0f, state->edid.present); /* Fall back to a 16:9 aspect ratio */ @@ -2030,7 +2030,7 @@ static int adv7604_set_edid(struct v4l2_subdev *sd, struct v4l2_edid *edid) /* Disable hotplug and I2C access to EDID RAM from DDC port */ cancel_delayed_work_sync(&state->delayed_work_enable_hotplug); - adv7604_set_hpd(state, 0); + adv76xx_set_hpd(state, 0); rep_write_clr_set(sd, info->edid_enable_reg, 0x0f, 0x00); spa_loc = get_edid_spa_location(edid->edid); @@ -2038,7 +2038,7 @@ static int adv7604_set_edid(struct v4l2_subdev *sd, struct v4l2_edid *edid) spa_loc = 0xc0; /* Default value [REF_02, p. 116] */ switch (edid->pad) { - case ADV7604_PAD_HDMI_PORT_A: + case ADV76XX_PAD_HDMI_PORT_A: state->spa_port_a[0] = edid->edid[spa_loc]; state->spa_port_a[1] = edid->edid[spa_loc + 1]; break; @@ -2081,7 +2081,7 @@ static int adv7604_set_edid(struct v4l2_subdev *sd, struct v4l2_edid *edid) return err; } - /* adv7604 calculates the checksums and enables I2C access to internal + /* adv76xx calculates the checksums and enables I2C access to internal EDID RAM from DDC port. */ rep_write_clr_set(sd, info->edid_enable_reg, 0x0f, state->edid.present); @@ -2145,10 +2145,10 @@ static void print_avi_infoframe(struct v4l2_subdev *sd) buf[8], buf[9], buf[10], buf[11], buf[12], buf[13]); } -static int adv7604_log_status(struct v4l2_subdev *sd) +static int adv76xx_log_status(struct v4l2_subdev *sd) { - struct adv7604_state *state = to_state(sd); - const struct adv7604_chip_info *info = state->info; + struct adv76xx_state *state = to_state(sd); + const struct adv76xx_chip_info *info = state->info; struct v4l2_dv_timings timings; struct stdi_readback stdi; u8 reg_io_0x02 = io_read(sd, 0x02); @@ -2220,7 +2220,7 @@ static int adv7604_log_status(struct v4l2_subdev *sd) stdi.lcf, stdi.bl, stdi.lcvs, stdi.interlaced ? "interlaced" : "progressive", stdi.hs_pol, stdi.vs_pol); - if (adv7604_query_dv_timings(sd, &timings)) + if (adv76xx_query_dv_timings(sd, &timings)) v4l2_info(sd, "No video detected\n"); else v4l2_print_dv_timings(sd->name, "Detected format: ", @@ -2286,47 +2286,47 @@ static int adv7604_log_status(struct v4l2_subdev *sd) /* ----------------------------------------------------------------------- */ -static const struct v4l2_ctrl_ops adv7604_ctrl_ops = { - .s_ctrl = adv7604_s_ctrl, +static const struct v4l2_ctrl_ops adv76xx_ctrl_ops = { + .s_ctrl = adv76xx_s_ctrl, }; -static const struct v4l2_subdev_core_ops adv7604_core_ops = { - .log_status = adv7604_log_status, - .interrupt_service_routine = adv7604_isr, +static const struct v4l2_subdev_core_ops adv76xx_core_ops = { + .log_status = adv76xx_log_status, + .interrupt_service_routine = adv76xx_isr, #ifdef CONFIG_VIDEO_ADV_DEBUG - .g_register = adv7604_g_register, - .s_register = adv7604_s_register, + .g_register = adv76xx_g_register, + .s_register = adv76xx_s_register, #endif }; -static const struct v4l2_subdev_video_ops adv7604_video_ops = { - .s_routing = adv7604_s_routing, - .g_input_status = adv7604_g_input_status, - .s_dv_timings = adv7604_s_dv_timings, - .g_dv_timings = adv7604_g_dv_timings, - .query_dv_timings = adv7604_query_dv_timings, +static const struct v4l2_subdev_video_ops adv76xx_video_ops = { + .s_routing = adv76xx_s_routing, + .g_input_status = adv76xx_g_input_status, + .s_dv_timings = adv76xx_s_dv_timings, + .g_dv_timings = adv76xx_g_dv_timings, + .query_dv_timings = adv76xx_query_dv_timings, }; -static const struct v4l2_subdev_pad_ops adv7604_pad_ops = { - .enum_mbus_code = adv7604_enum_mbus_code, - .get_fmt = adv7604_get_format, - .set_fmt = adv7604_set_format, - .get_edid = adv7604_get_edid, - .set_edid = adv7604_set_edid, - .dv_timings_cap = adv7604_dv_timings_cap, - .enum_dv_timings = adv7604_enum_dv_timings, +static const struct v4l2_subdev_pad_ops adv76xx_pad_ops = { + .enum_mbus_code = adv76xx_enum_mbus_code, + .get_fmt = adv76xx_get_format, + .set_fmt = adv76xx_set_format, + .get_edid = adv76xx_get_edid, + .set_edid = adv76xx_set_edid, + .dv_timings_cap = adv76xx_dv_timings_cap, + .enum_dv_timings = adv76xx_enum_dv_timings, }; -static const struct v4l2_subdev_ops adv7604_ops = { - .core = &adv7604_core_ops, - .video = &adv7604_video_ops, - .pad = &adv7604_pad_ops, +static const struct v4l2_subdev_ops adv76xx_ops = { + .core = &adv76xx_core_ops, + .video = &adv76xx_video_ops, + .pad = &adv76xx_pad_ops, }; /* -------------------------- custom ctrls ---------------------------------- */ static const struct v4l2_ctrl_config adv7604_ctrl_analog_sampling_phase = { - .ops = &adv7604_ctrl_ops, + .ops = &adv76xx_ctrl_ops, .id = V4L2_CID_ADV_RX_ANALOG_SAMPLING_PHASE, .name = "Analog Sampling Phase", .type = V4L2_CTRL_TYPE_INTEGER, @@ -2336,8 +2336,8 @@ static const struct v4l2_ctrl_config adv7604_ctrl_analog_sampling_phase = { .def = 0, }; -static const struct v4l2_ctrl_config adv7604_ctrl_free_run_color_manual = { - .ops = &adv7604_ctrl_ops, +static const struct v4l2_ctrl_config adv76xx_ctrl_free_run_color_manual = { + .ops = &adv76xx_ctrl_ops, .id = V4L2_CID_ADV_RX_FREE_RUN_COLOR_MANUAL, .name = "Free Running Color, Manual", .type = V4L2_CTRL_TYPE_BOOLEAN, @@ -2347,8 +2347,8 @@ static const struct v4l2_ctrl_config adv7604_ctrl_free_run_color_manual = { .def = false, }; -static const struct v4l2_ctrl_config adv7604_ctrl_free_run_color = { - .ops = &adv7604_ctrl_ops, +static const struct v4l2_ctrl_config adv76xx_ctrl_free_run_color = { + .ops = &adv76xx_ctrl_ops, .id = V4L2_CID_ADV_RX_FREE_RUN_COLOR, .name = "Free Running Color", .type = V4L2_CTRL_TYPE_INTEGER, @@ -2360,11 +2360,11 @@ static const struct v4l2_ctrl_config adv7604_ctrl_free_run_color = { /* ----------------------------------------------------------------------- */ -static int adv7604_core_init(struct v4l2_subdev *sd) +static int adv76xx_core_init(struct v4l2_subdev *sd) { - struct adv7604_state *state = to_state(sd); - const struct adv7604_chip_info *info = state->info; - struct adv7604_platform_data *pdata = &state->pdata; + struct adv76xx_state *state = to_state(sd); + const struct adv76xx_chip_info *info = state->info; + struct adv76xx_platform_data *pdata = &state->pdata; hdmi_write(sd, 0x48, (pdata->disable_pwrdnb ? 0x80 : 0) | @@ -2392,7 +2392,7 @@ static int adv7604_core_init(struct v4l2_subdev *sd) io_write_clr_set(sd, 0x05, 0x0e, pdata->blank_data << 3 | pdata->insert_av_codes << 2 | pdata->replicate_av_codes << 1); - adv7604_setup_format(state); + adv76xx_setup_format(state); cp_write(sd, 0x69, 0x30); /* Enable CP CSC */ @@ -2422,7 +2422,7 @@ static int adv7604_core_init(struct v4l2_subdev *sd) /* TODO from platform data */ afe_write(sd, 0xb5, 0x01); /* Setting MCLK to 256Fs */ - if (adv7604_has_afe(state)) { + if (adv76xx_has_afe(state)) { afe_write(sd, 0x02, pdata->ain_sel); /* Select analog input muxing mode */ io_write_clr_set(sd, 0x30, 1 << 4, pdata->output_bus_lsb_to_msb << 4); } @@ -2447,7 +2447,7 @@ static void adv7611_setup_irqs(struct v4l2_subdev *sd) io_write(sd, 0x41, 0xd0); /* STDI irq for any change, disable INT2 */ } -static void adv7604_unregister_clients(struct adv7604_state *state) +static void adv76xx_unregister_clients(struct adv76xx_state *state) { unsigned int i; @@ -2457,7 +2457,7 @@ static void adv7604_unregister_clients(struct adv7604_state *state) } } -static struct i2c_client *adv7604_dummy_client(struct v4l2_subdev *sd, +static struct i2c_client *adv76xx_dummy_client(struct v4l2_subdev *sd, u8 addr, u8 io_reg) { struct i2c_client *client = v4l2_get_subdevdata(sd); @@ -2467,74 +2467,74 @@ static struct i2c_client *adv7604_dummy_client(struct v4l2_subdev *sd, return i2c_new_dummy(client->adapter, io_read(sd, io_reg) >> 1); } -static const struct adv7604_reg_seq adv7604_recommended_settings_afe[] = { +static const struct adv76xx_reg_seq adv7604_recommended_settings_afe[] = { /* reset ADI recommended settings for HDMI: */ /* "ADV7604 Register Settings Recommendations (rev. 2.5, June 2010)" p. 4. */ - { ADV7604_REG(ADV7604_PAGE_HDMI, 0x0d), 0x04 }, /* HDMI filter optimization */ - { ADV7604_REG(ADV7604_PAGE_HDMI, 0x0d), 0x04 }, /* HDMI filter optimization */ - { ADV7604_REG(ADV7604_PAGE_HDMI, 0x3d), 0x00 }, /* DDC bus active pull-up control */ - { ADV7604_REG(ADV7604_PAGE_HDMI, 0x3e), 0x74 }, /* TMDS PLL optimization */ - { ADV7604_REG(ADV7604_PAGE_HDMI, 0x4e), 0x3b }, /* TMDS PLL optimization */ - { ADV7604_REG(ADV7604_PAGE_HDMI, 0x57), 0x74 }, /* TMDS PLL optimization */ - { ADV7604_REG(ADV7604_PAGE_HDMI, 0x58), 0x63 }, /* TMDS PLL optimization */ - { ADV7604_REG(ADV7604_PAGE_HDMI, 0x8d), 0x18 }, /* equaliser */ - { ADV7604_REG(ADV7604_PAGE_HDMI, 0x8e), 0x34 }, /* equaliser */ - { ADV7604_REG(ADV7604_PAGE_HDMI, 0x93), 0x88 }, /* equaliser */ - { ADV7604_REG(ADV7604_PAGE_HDMI, 0x94), 0x2e }, /* equaliser */ - { ADV7604_REG(ADV7604_PAGE_HDMI, 0x96), 0x00 }, /* enable automatic EQ changing */ + { ADV76XX_REG(ADV76XX_PAGE_HDMI, 0x0d), 0x04 }, /* HDMI filter optimization */ + { ADV76XX_REG(ADV76XX_PAGE_HDMI, 0x0d), 0x04 }, /* HDMI filter optimization */ + { ADV76XX_REG(ADV76XX_PAGE_HDMI, 0x3d), 0x00 }, /* DDC bus active pull-up control */ + { ADV76XX_REG(ADV76XX_PAGE_HDMI, 0x3e), 0x74 }, /* TMDS PLL optimization */ + { ADV76XX_REG(ADV76XX_PAGE_HDMI, 0x4e), 0x3b }, /* TMDS PLL optimization */ + { ADV76XX_REG(ADV76XX_PAGE_HDMI, 0x57), 0x74 }, /* TMDS PLL optimization */ + { ADV76XX_REG(ADV76XX_PAGE_HDMI, 0x58), 0x63 }, /* TMDS PLL optimization */ + { ADV76XX_REG(ADV76XX_PAGE_HDMI, 0x8d), 0x18 }, /* equaliser */ + { ADV76XX_REG(ADV76XX_PAGE_HDMI, 0x8e), 0x34 }, /* equaliser */ + { ADV76XX_REG(ADV76XX_PAGE_HDMI, 0x93), 0x88 }, /* equaliser */ + { ADV76XX_REG(ADV76XX_PAGE_HDMI, 0x94), 0x2e }, /* equaliser */ + { ADV76XX_REG(ADV76XX_PAGE_HDMI, 0x96), 0x00 }, /* enable automatic EQ changing */ /* set ADI recommended settings for digitizer */ /* "ADV7604 Register Settings Recommendations (rev. 2.5, June 2010)" p. 17. */ - { ADV7604_REG(ADV7604_PAGE_AFE, 0x12), 0x7b }, /* ADC noise shaping filter controls */ - { ADV7604_REG(ADV7604_PAGE_AFE, 0x0c), 0x1f }, /* CP core gain controls */ - { ADV7604_REG(ADV7604_PAGE_CP, 0x3e), 0x04 }, /* CP core pre-gain control */ - { ADV7604_REG(ADV7604_PAGE_CP, 0xc3), 0x39 }, /* CP coast control. Graphics mode */ - { ADV7604_REG(ADV7604_PAGE_CP, 0x40), 0x5c }, /* CP core pre-gain control. Graphics mode */ + { ADV76XX_REG(ADV76XX_PAGE_AFE, 0x12), 0x7b }, /* ADC noise shaping filter controls */ + { ADV76XX_REG(ADV76XX_PAGE_AFE, 0x0c), 0x1f }, /* CP core gain controls */ + { ADV76XX_REG(ADV76XX_PAGE_CP, 0x3e), 0x04 }, /* CP core pre-gain control */ + { ADV76XX_REG(ADV76XX_PAGE_CP, 0xc3), 0x39 }, /* CP coast control. Graphics mode */ + { ADV76XX_REG(ADV76XX_PAGE_CP, 0x40), 0x5c }, /* CP core pre-gain control. Graphics mode */ - { ADV7604_REG_SEQ_TERM, 0 }, + { ADV76XX_REG_SEQ_TERM, 0 }, }; -static const struct adv7604_reg_seq adv7604_recommended_settings_hdmi[] = { +static const struct adv76xx_reg_seq adv7604_recommended_settings_hdmi[] = { /* set ADI recommended settings for HDMI: */ /* "ADV7604 Register Settings Recommendations (rev. 2.5, June 2010)" p. 4. */ - { ADV7604_REG(ADV7604_PAGE_HDMI, 0x0d), 0x84 }, /* HDMI filter optimization */ - { ADV7604_REG(ADV7604_PAGE_HDMI, 0x3d), 0x10 }, /* DDC bus active pull-up control */ - { ADV7604_REG(ADV7604_PAGE_HDMI, 0x3e), 0x39 }, /* TMDS PLL optimization */ - { ADV7604_REG(ADV7604_PAGE_HDMI, 0x4e), 0x3b }, /* TMDS PLL optimization */ - { ADV7604_REG(ADV7604_PAGE_HDMI, 0x57), 0xb6 }, /* TMDS PLL optimization */ - { ADV7604_REG(ADV7604_PAGE_HDMI, 0x58), 0x03 }, /* TMDS PLL optimization */ - { ADV7604_REG(ADV7604_PAGE_HDMI, 0x8d), 0x18 }, /* equaliser */ - { ADV7604_REG(ADV7604_PAGE_HDMI, 0x8e), 0x34 }, /* equaliser */ - { ADV7604_REG(ADV7604_PAGE_HDMI, 0x93), 0x8b }, /* equaliser */ - { ADV7604_REG(ADV7604_PAGE_HDMI, 0x94), 0x2d }, /* equaliser */ - { ADV7604_REG(ADV7604_PAGE_HDMI, 0x96), 0x01 }, /* enable automatic EQ changing */ + { ADV76XX_REG(ADV76XX_PAGE_HDMI, 0x0d), 0x84 }, /* HDMI filter optimization */ + { ADV76XX_REG(ADV76XX_PAGE_HDMI, 0x3d), 0x10 }, /* DDC bus active pull-up control */ + { ADV76XX_REG(ADV76XX_PAGE_HDMI, 0x3e), 0x39 }, /* TMDS PLL optimization */ + { ADV76XX_REG(ADV76XX_PAGE_HDMI, 0x4e), 0x3b }, /* TMDS PLL optimization */ + { ADV76XX_REG(ADV76XX_PAGE_HDMI, 0x57), 0xb6 }, /* TMDS PLL optimization */ + { ADV76XX_REG(ADV76XX_PAGE_HDMI, 0x58), 0x03 }, /* TMDS PLL optimization */ + { ADV76XX_REG(ADV76XX_PAGE_HDMI, 0x8d), 0x18 }, /* equaliser */ + { ADV76XX_REG(ADV76XX_PAGE_HDMI, 0x8e), 0x34 }, /* equaliser */ + { ADV76XX_REG(ADV76XX_PAGE_HDMI, 0x93), 0x8b }, /* equaliser */ + { ADV76XX_REG(ADV76XX_PAGE_HDMI, 0x94), 0x2d }, /* equaliser */ + { ADV76XX_REG(ADV76XX_PAGE_HDMI, 0x96), 0x01 }, /* enable automatic EQ changing */ /* reset ADI recommended settings for digitizer */ /* "ADV7604 Register Settings Recommendations (rev. 2.5, June 2010)" p. 17. */ - { ADV7604_REG(ADV7604_PAGE_AFE, 0x12), 0xfb }, /* ADC noise shaping filter controls */ - { ADV7604_REG(ADV7604_PAGE_AFE, 0x0c), 0x0d }, /* CP core gain controls */ + { ADV76XX_REG(ADV76XX_PAGE_AFE, 0x12), 0xfb }, /* ADC noise shaping filter controls */ + { ADV76XX_REG(ADV76XX_PAGE_AFE, 0x0c), 0x0d }, /* CP core gain controls */ - { ADV7604_REG_SEQ_TERM, 0 }, + { ADV76XX_REG_SEQ_TERM, 0 }, }; -static const struct adv7604_reg_seq adv7611_recommended_settings_hdmi[] = { +static const struct adv76xx_reg_seq adv7611_recommended_settings_hdmi[] = { /* ADV7611 Register Settings Recommendations Rev 1.5, May 2014 */ - { ADV7604_REG(ADV7604_PAGE_CP, 0x6c), 0x00 }, - { ADV7604_REG(ADV7604_PAGE_HDMI, 0x9b), 0x03 }, - { ADV7604_REG(ADV7604_PAGE_HDMI, 0x6f), 0x08 }, - { ADV7604_REG(ADV7604_PAGE_HDMI, 0x85), 0x1f }, - { ADV7604_REG(ADV7604_PAGE_HDMI, 0x87), 0x70 }, - { ADV7604_REG(ADV7604_PAGE_HDMI, 0x57), 0xda }, - { ADV7604_REG(ADV7604_PAGE_HDMI, 0x58), 0x01 }, - { ADV7604_REG(ADV7604_PAGE_HDMI, 0x03), 0x98 }, - { ADV7604_REG(ADV7604_PAGE_HDMI, 0x4c), 0x44 }, - { ADV7604_REG(ADV7604_PAGE_HDMI, 0x8d), 0x04 }, - { ADV7604_REG(ADV7604_PAGE_HDMI, 0x8e), 0x1e }, - - { ADV7604_REG_SEQ_TERM, 0 }, + { ADV76XX_REG(ADV76XX_PAGE_CP, 0x6c), 0x00 }, + { ADV76XX_REG(ADV76XX_PAGE_HDMI, 0x9b), 0x03 }, + { ADV76XX_REG(ADV76XX_PAGE_HDMI, 0x6f), 0x08 }, + { ADV76XX_REG(ADV76XX_PAGE_HDMI, 0x85), 0x1f }, + { ADV76XX_REG(ADV76XX_PAGE_HDMI, 0x87), 0x70 }, + { ADV76XX_REG(ADV76XX_PAGE_HDMI, 0x57), 0xda }, + { ADV76XX_REG(ADV76XX_PAGE_HDMI, 0x58), 0x01 }, + { ADV76XX_REG(ADV76XX_PAGE_HDMI, 0x03), 0x98 }, + { ADV76XX_REG(ADV76XX_PAGE_HDMI, 0x4c), 0x44 }, + { ADV76XX_REG(ADV76XX_PAGE_HDMI, 0x8d), 0x04 }, + { ADV76XX_REG(ADV76XX_PAGE_HDMI, 0x8e), 0x1e }, + + { ADV76XX_REG_SEQ_TERM, 0 }, }; -static const struct adv7604_chip_info adv7604_chip_info[] = { +static const struct adv76xx_chip_info adv76xx_chip_info[] = { [ADV7604] = { .type = ADV7604, .has_afe = true, @@ -2561,18 +2561,18 @@ static const struct adv7604_chip_info adv7604_chip_info[] = { [0] = ARRAY_SIZE(adv7604_recommended_settings_afe), [1] = ARRAY_SIZE(adv7604_recommended_settings_hdmi), }, - .page_mask = BIT(ADV7604_PAGE_IO) | BIT(ADV7604_PAGE_AVLINK) | - BIT(ADV7604_PAGE_CEC) | BIT(ADV7604_PAGE_INFOFRAME) | + .page_mask = BIT(ADV76XX_PAGE_IO) | BIT(ADV7604_PAGE_AVLINK) | + BIT(ADV76XX_PAGE_CEC) | BIT(ADV76XX_PAGE_INFOFRAME) | BIT(ADV7604_PAGE_ESDP) | BIT(ADV7604_PAGE_DPP) | - BIT(ADV7604_PAGE_AFE) | BIT(ADV7604_PAGE_REP) | - BIT(ADV7604_PAGE_EDID) | BIT(ADV7604_PAGE_HDMI) | - BIT(ADV7604_PAGE_TEST) | BIT(ADV7604_PAGE_CP) | + BIT(ADV76XX_PAGE_AFE) | BIT(ADV76XX_PAGE_REP) | + BIT(ADV76XX_PAGE_EDID) | BIT(ADV76XX_PAGE_HDMI) | + BIT(ADV76XX_PAGE_TEST) | BIT(ADV76XX_PAGE_CP) | BIT(ADV7604_PAGE_VDP), }, [ADV7611] = { .type = ADV7611, .has_afe = false, - .max_port = ADV7604_PAD_HDMI_PORT_A, + .max_port = ADV76XX_PAD_HDMI_PORT_A, .num_dv_ports = 1, .edid_enable_reg = 0x74, .edid_status_reg = 0x76, @@ -2593,34 +2593,34 @@ static const struct adv7604_chip_info adv7604_chip_info[] = { .num_recommended_settings = { [1] = ARRAY_SIZE(adv7611_recommended_settings_hdmi), }, - .page_mask = BIT(ADV7604_PAGE_IO) | BIT(ADV7604_PAGE_CEC) | - BIT(ADV7604_PAGE_INFOFRAME) | BIT(ADV7604_PAGE_AFE) | - BIT(ADV7604_PAGE_REP) | BIT(ADV7604_PAGE_EDID) | - BIT(ADV7604_PAGE_HDMI) | BIT(ADV7604_PAGE_CP), + .page_mask = BIT(ADV76XX_PAGE_IO) | BIT(ADV76XX_PAGE_CEC) | + BIT(ADV76XX_PAGE_INFOFRAME) | BIT(ADV76XX_PAGE_AFE) | + BIT(ADV76XX_PAGE_REP) | BIT(ADV76XX_PAGE_EDID) | + BIT(ADV76XX_PAGE_HDMI) | BIT(ADV76XX_PAGE_CP), }, }; -static struct i2c_device_id adv7604_i2c_id[] = { - { "adv7604", (kernel_ulong_t)&adv7604_chip_info[ADV7604] }, - { "adv7611", (kernel_ulong_t)&adv7604_chip_info[ADV7611] }, +static struct i2c_device_id adv76xx_i2c_id[] = { + { "adv7604", (kernel_ulong_t)&adv76xx_chip_info[ADV7604] }, + { "adv7611", (kernel_ulong_t)&adv76xx_chip_info[ADV7611] }, { } }; -MODULE_DEVICE_TABLE(i2c, adv7604_i2c_id); +MODULE_DEVICE_TABLE(i2c, adv76xx_i2c_id); -static struct of_device_id adv7604_of_id[] __maybe_unused = { - { .compatible = "adi,adv7611", .data = &adv7604_chip_info[ADV7611] }, +static struct of_device_id adv76xx_of_id[] __maybe_unused = { + { .compatible = "adi,adv7611", .data = &adv76xx_chip_info[ADV7611] }, { } }; -MODULE_DEVICE_TABLE(of, adv7604_of_id); +MODULE_DEVICE_TABLE(of, adv76xx_of_id); -static int adv7604_parse_dt(struct adv7604_state *state) +static int adv76xx_parse_dt(struct adv76xx_state *state) { struct v4l2_of_endpoint bus_cfg; struct device_node *endpoint; struct device_node *np; unsigned int flags; - np = state->i2c_clients[ADV7604_PAGE_IO]->dev.of_node; + np = state->i2c_clients[ADV76XX_PAGE_IO]->dev.of_node; /* Parse the endpoint. */ endpoint = of_graph_get_next_endpoint(np, NULL); @@ -2647,20 +2647,20 @@ static int adv7604_parse_dt(struct adv7604_state *state) } /* Disable the interrupt for now as no DT-based board uses it. */ - state->pdata.int1_config = ADV7604_INT1_CONFIG_DISABLED; + state->pdata.int1_config = ADV76XX_INT1_CONFIG_DISABLED; /* Use the default I2C addresses. */ state->pdata.i2c_addresses[ADV7604_PAGE_AVLINK] = 0x42; - state->pdata.i2c_addresses[ADV7604_PAGE_CEC] = 0x40; - state->pdata.i2c_addresses[ADV7604_PAGE_INFOFRAME] = 0x3e; + state->pdata.i2c_addresses[ADV76XX_PAGE_CEC] = 0x40; + state->pdata.i2c_addresses[ADV76XX_PAGE_INFOFRAME] = 0x3e; state->pdata.i2c_addresses[ADV7604_PAGE_ESDP] = 0x38; state->pdata.i2c_addresses[ADV7604_PAGE_DPP] = 0x3c; - state->pdata.i2c_addresses[ADV7604_PAGE_AFE] = 0x26; - state->pdata.i2c_addresses[ADV7604_PAGE_REP] = 0x32; - state->pdata.i2c_addresses[ADV7604_PAGE_EDID] = 0x36; - state->pdata.i2c_addresses[ADV7604_PAGE_HDMI] = 0x34; - state->pdata.i2c_addresses[ADV7604_PAGE_TEST] = 0x30; - state->pdata.i2c_addresses[ADV7604_PAGE_CP] = 0x22; + state->pdata.i2c_addresses[ADV76XX_PAGE_AFE] = 0x26; + state->pdata.i2c_addresses[ADV76XX_PAGE_REP] = 0x32; + state->pdata.i2c_addresses[ADV76XX_PAGE_EDID] = 0x36; + state->pdata.i2c_addresses[ADV76XX_PAGE_HDMI] = 0x34; + state->pdata.i2c_addresses[ADV76XX_PAGE_TEST] = 0x30; + state->pdata.i2c_addresses[ADV76XX_PAGE_CP] = 0x22; state->pdata.i2c_addresses[ADV7604_PAGE_VDP] = 0x24; /* Hardcode the remaining platform data fields. */ @@ -2675,12 +2675,12 @@ static int adv7604_parse_dt(struct adv7604_state *state) return 0; } -static int adv7604_probe(struct i2c_client *client, +static int adv76xx_probe(struct i2c_client *client, const struct i2c_device_id *id) { static const struct v4l2_dv_timings cea640x480 = V4L2_DV_BT_CEA_640X480P59_94; - struct adv7604_state *state; + struct adv76xx_state *state; struct v4l2_ctrl_handler *hdl; struct v4l2_subdev *sd; unsigned int i; @@ -2690,16 +2690,16 @@ static int adv7604_probe(struct i2c_client *client, /* Check if the adapter supports the needed features */ if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) return -EIO; - v4l_dbg(1, debug, client, "detecting adv7604 client on address 0x%x\n", + v4l_dbg(1, debug, client, "detecting adv76xx client on address 0x%x\n", client->addr << 1); state = devm_kzalloc(&client->dev, sizeof(*state), GFP_KERNEL); if (!state) { - v4l_err(client, "Could not allocate adv7604_state memory!\n"); + v4l_err(client, "Could not allocate adv76xx_state memory!\n"); return -ENOMEM; } - state->i2c_clients[ADV7604_PAGE_IO] = client; + state->i2c_clients[ADV76XX_PAGE_IO] = client; /* initialize variables */ state->restart_stdi_once = true; @@ -2708,18 +2708,18 @@ static int adv7604_probe(struct i2c_client *client, if (IS_ENABLED(CONFIG_OF) && client->dev.of_node) { const struct of_device_id *oid; - oid = of_match_node(adv7604_of_id, client->dev.of_node); + oid = of_match_node(adv76xx_of_id, client->dev.of_node); state->info = oid->data; - err = adv7604_parse_dt(state); + err = adv76xx_parse_dt(state); if (err < 0) { v4l_err(client, "DT parsing error\n"); return err; } } else if (client->dev.platform_data) { - struct adv7604_platform_data *pdata = client->dev.platform_data; + struct adv76xx_platform_data *pdata = client->dev.platform_data; - state->info = (const struct adv7604_chip_info *)id->driver_data; + state->info = (const struct adv76xx_chip_info *)id->driver_data; state->pdata = *pdata; } else { v4l_err(client, "No platform data!\n"); @@ -2739,10 +2739,10 @@ static int adv7604_probe(struct i2c_client *client, } state->timings = cea640x480; - state->format = adv7604_format_info(state, MEDIA_BUS_FMT_YUYV8_2X8); + state->format = adv76xx_format_info(state, MEDIA_BUS_FMT_YUYV8_2X8); sd = &state->sd; - v4l2_i2c_subdev_init(sd, client, &adv7604_ops); + v4l2_i2c_subdev_init(sd, client, &adv76xx_ops); snprintf(sd->name, sizeof(sd->name), "%s %d-%04x", id->name, i2c_adapter_id(client->adapter), client->addr); @@ -2772,15 +2772,15 @@ static int adv7604_probe(struct i2c_client *client, /* control handlers */ hdl = &state->hdl; - v4l2_ctrl_handler_init(hdl, adv7604_has_afe(state) ? 9 : 8); + v4l2_ctrl_handler_init(hdl, adv76xx_has_afe(state) ? 9 : 8); - v4l2_ctrl_new_std(hdl, &adv7604_ctrl_ops, + v4l2_ctrl_new_std(hdl, &adv76xx_ctrl_ops, V4L2_CID_BRIGHTNESS, -128, 127, 1, 0); - v4l2_ctrl_new_std(hdl, &adv7604_ctrl_ops, + v4l2_ctrl_new_std(hdl, &adv76xx_ctrl_ops, V4L2_CID_CONTRAST, 0, 255, 1, 128); - v4l2_ctrl_new_std(hdl, &adv7604_ctrl_ops, + v4l2_ctrl_new_std(hdl, &adv76xx_ctrl_ops, V4L2_CID_SATURATION, 0, 255, 1, 128); - v4l2_ctrl_new_std(hdl, &adv7604_ctrl_ops, + v4l2_ctrl_new_std(hdl, &adv76xx_ctrl_ops, V4L2_CID_HUE, 0, 128, 1, 0); /* private controls */ @@ -2788,18 +2788,18 @@ static int adv7604_probe(struct i2c_client *client, V4L2_CID_DV_RX_POWER_PRESENT, 0, (1 << state->info->num_dv_ports) - 1, 0, 0); state->rgb_quantization_range_ctrl = - v4l2_ctrl_new_std_menu(hdl, &adv7604_ctrl_ops, + v4l2_ctrl_new_std_menu(hdl, &adv76xx_ctrl_ops, V4L2_CID_DV_RX_RGB_RANGE, V4L2_DV_RGB_RANGE_FULL, 0, V4L2_DV_RGB_RANGE_AUTO); /* custom controls */ - if (adv7604_has_afe(state)) + if (adv76xx_has_afe(state)) state->analog_sampling_phase_ctrl = v4l2_ctrl_new_custom(hdl, &adv7604_ctrl_analog_sampling_phase, NULL); state->free_run_color_manual_ctrl = - v4l2_ctrl_new_custom(hdl, &adv7604_ctrl_free_run_color_manual, NULL); + v4l2_ctrl_new_custom(hdl, &adv76xx_ctrl_free_run_color_manual, NULL); state->free_run_color_ctrl = - v4l2_ctrl_new_custom(hdl, &adv7604_ctrl_free_run_color, NULL); + v4l2_ctrl_new_custom(hdl, &adv76xx_ctrl_free_run_color, NULL); sd->ctrl_handler = hdl; if (hdl->error) { @@ -2808,22 +2808,22 @@ static int adv7604_probe(struct i2c_client *client, } state->detect_tx_5v_ctrl->is_private = true; state->rgb_quantization_range_ctrl->is_private = true; - if (adv7604_has_afe(state)) + if (adv76xx_has_afe(state)) state->analog_sampling_phase_ctrl->is_private = true; state->free_run_color_manual_ctrl->is_private = true; state->free_run_color_ctrl->is_private = true; - if (adv7604_s_detect_tx_5v_ctrl(sd)) { + if (adv76xx_s_detect_tx_5v_ctrl(sd)) { err = -ENODEV; goto err_hdl; } - for (i = 1; i < ADV7604_PAGE_MAX; ++i) { + for (i = 1; i < ADV76XX_PAGE_MAX; ++i) { if (!(BIT(i) & state->info->page_mask)) continue; state->i2c_clients[i] = - adv7604_dummy_client(sd, state->pdata.i2c_addresses[i], + adv76xx_dummy_client(sd, state->pdata.i2c_addresses[i], 0xf2 + i); if (state->i2c_clients[i] == NULL) { err = -ENOMEM; @@ -2841,7 +2841,7 @@ static int adv7604_probe(struct i2c_client *client, } INIT_DELAYED_WORK(&state->delayed_work_enable_hotplug, - adv7604_delayed_work_enable_hotplug); + adv76xx_delayed_work_enable_hotplug); state->source_pad = state->info->num_dv_ports + (state->info->has_afe ? 2 : 0); @@ -2854,7 +2854,7 @@ static int adv7604_probe(struct i2c_client *client, if (err) goto err_work_queues; - err = adv7604_core_init(sd); + err = adv76xx_core_init(sd); if (err) goto err_entity; v4l2_info(sd, "%s found @ 0x%x (%s)\n", client->name, @@ -2872,7 +2872,7 @@ err_work_queues: cancel_delayed_work(&state->delayed_work_enable_hotplug); destroy_workqueue(state->work_queues); err_i2c: - adv7604_unregister_clients(state); + adv76xx_unregister_clients(state); err_hdl: v4l2_ctrl_handler_free(hdl); return err; @@ -2880,32 +2880,32 @@ err_hdl: /* ----------------------------------------------------------------------- */ -static int adv7604_remove(struct i2c_client *client) +static int adv76xx_remove(struct i2c_client *client) { struct v4l2_subdev *sd = i2c_get_clientdata(client); - struct adv7604_state *state = to_state(sd); + struct adv76xx_state *state = to_state(sd); cancel_delayed_work(&state->delayed_work_enable_hotplug); destroy_workqueue(state->work_queues); v4l2_async_unregister_subdev(sd); v4l2_device_unregister_subdev(sd); media_entity_cleanup(&sd->entity); - adv7604_unregister_clients(to_state(sd)); + adv76xx_unregister_clients(to_state(sd)); v4l2_ctrl_handler_free(sd->ctrl_handler); return 0; } /* ----------------------------------------------------------------------- */ -static struct i2c_driver adv7604_driver = { +static struct i2c_driver adv76xx_driver = { .driver = { .owner = THIS_MODULE, .name = "adv7604", - .of_match_table = of_match_ptr(adv7604_of_id), + .of_match_table = of_match_ptr(adv76xx_of_id), }, - .probe = adv7604_probe, - .remove = adv7604_remove, - .id_table = adv7604_i2c_id, + .probe = adv76xx_probe, + .remove = adv76xx_remove, + .id_table = adv76xx_i2c_id, }; -module_i2c_driver(adv7604_driver); +module_i2c_driver(adv76xx_driver); diff --git a/include/media/adv7604.h b/include/media/adv7604.h index aa1c4477722d..9ecf353160c1 100644 --- a/include/media/adv7604.h +++ b/include/media/adv7604.h @@ -47,16 +47,16 @@ enum adv7604_bus_order { }; /* Input Color Space (IO register 0x02, [7:4]) */ -enum adv7604_inp_color_space { - ADV7604_INP_COLOR_SPACE_LIM_RGB = 0, - ADV7604_INP_COLOR_SPACE_FULL_RGB = 1, - ADV7604_INP_COLOR_SPACE_LIM_YCbCr_601 = 2, - ADV7604_INP_COLOR_SPACE_LIM_YCbCr_709 = 3, - ADV7604_INP_COLOR_SPACE_XVYCC_601 = 4, - ADV7604_INP_COLOR_SPACE_XVYCC_709 = 5, - ADV7604_INP_COLOR_SPACE_FULL_YCbCr_601 = 6, - ADV7604_INP_COLOR_SPACE_FULL_YCbCr_709 = 7, - ADV7604_INP_COLOR_SPACE_AUTO = 0xf, +enum adv76xx_inp_color_space { + ADV76XX_INP_COLOR_SPACE_LIM_RGB = 0, + ADV76XX_INP_COLOR_SPACE_FULL_RGB = 1, + ADV76XX_INP_COLOR_SPACE_LIM_YCbCr_601 = 2, + ADV76XX_INP_COLOR_SPACE_LIM_YCbCr_709 = 3, + ADV76XX_INP_COLOR_SPACE_XVYCC_601 = 4, + ADV76XX_INP_COLOR_SPACE_XVYCC_709 = 5, + ADV76XX_INP_COLOR_SPACE_FULL_YCbCr_601 = 6, + ADV76XX_INP_COLOR_SPACE_FULL_YCbCr_709 = 7, + ADV76XX_INP_COLOR_SPACE_AUTO = 0xf, }; /* Select output format (IO register 0x03, [4:2]) */ @@ -66,38 +66,39 @@ enum adv7604_op_format_mode_sel { ADV7604_OP_FORMAT_MODE2 = 0x08, }; -enum adv7604_drive_strength { - ADV7604_DR_STR_MEDIUM_LOW = 1, - ADV7604_DR_STR_MEDIUM_HIGH = 2, - ADV7604_DR_STR_HIGH = 3, +enum adv76xx_drive_strength { + ADV76XX_DR_STR_MEDIUM_LOW = 1, + ADV76XX_DR_STR_MEDIUM_HIGH = 2, + ADV76XX_DR_STR_HIGH = 3, }; -enum adv7604_int1_config { - ADV7604_INT1_CONFIG_OPEN_DRAIN, - ADV7604_INT1_CONFIG_ACTIVE_LOW, - ADV7604_INT1_CONFIG_ACTIVE_HIGH, - ADV7604_INT1_CONFIG_DISABLED, +/* INT1 Configuration (IO register 0x40, [1:0]) */ +enum adv76xx_int1_config { + ADV76XX_INT1_CONFIG_OPEN_DRAIN, + ADV76XX_INT1_CONFIG_ACTIVE_LOW, + ADV76XX_INT1_CONFIG_ACTIVE_HIGH, + ADV76XX_INT1_CONFIG_DISABLED, }; -enum adv7604_page { - ADV7604_PAGE_IO, +enum adv76xx_page { + ADV76XX_PAGE_IO, ADV7604_PAGE_AVLINK, - ADV7604_PAGE_CEC, - ADV7604_PAGE_INFOFRAME, + ADV76XX_PAGE_CEC, + ADV76XX_PAGE_INFOFRAME, ADV7604_PAGE_ESDP, ADV7604_PAGE_DPP, - ADV7604_PAGE_AFE, - ADV7604_PAGE_REP, - ADV7604_PAGE_EDID, - ADV7604_PAGE_HDMI, - ADV7604_PAGE_TEST, - ADV7604_PAGE_CP, + ADV76XX_PAGE_AFE, + ADV76XX_PAGE_REP, + ADV76XX_PAGE_EDID, + ADV76XX_PAGE_HDMI, + ADV76XX_PAGE_TEST, + ADV76XX_PAGE_CP, ADV7604_PAGE_VDP, - ADV7604_PAGE_MAX, + ADV76XX_PAGE_MAX, }; /* Platform dependent definition */ -struct adv7604_platform_data { +struct adv76xx_platform_data { /* DIS_PWRDNB: 1 if the PWRDNB pin is unused and unconnected */ unsigned disable_pwrdnb:1; @@ -116,7 +117,7 @@ struct adv7604_platform_data { enum adv7604_op_format_mode_sel op_format_mode_sel; /* Configuration of the INT1 pin */ - enum adv7604_int1_config int1_config; + enum adv76xx_int1_config int1_config; /* IO register 0x02 */ unsigned alt_gamma:1; @@ -134,9 +135,9 @@ struct adv7604_platform_data { unsigned inv_llc_pol:1; /* IO register 0x14 */ - enum adv7604_drive_strength dr_str_data; - enum adv7604_drive_strength dr_str_clk; - enum adv7604_drive_strength dr_str_sync; + enum adv76xx_drive_strength dr_str_data; + enum adv76xx_drive_strength dr_str_clk; + enum adv76xx_drive_strength dr_str_sync; /* IO register 0x30 */ unsigned output_bus_lsb_to_msb:1; @@ -145,11 +146,11 @@ struct adv7604_platform_data { unsigned hdmi_free_run_mode; /* i2c addresses: 0 == use default */ - u8 i2c_addresses[ADV7604_PAGE_MAX]; + u8 i2c_addresses[ADV76XX_PAGE_MAX]; }; -enum adv7604_pad { - ADV7604_PAD_HDMI_PORT_A = 0, +enum adv76xx_pad { + ADV76XX_PAD_HDMI_PORT_A = 0, ADV7604_PAD_HDMI_PORT_B = 1, ADV7604_PAD_HDMI_PORT_C = 2, ADV7604_PAD_HDMI_PORT_D = 3, @@ -158,7 +159,7 @@ enum adv7604_pad { /* The source pad is either 1 (ADV7611) or 6 (ADV7604) */ ADV7604_PAD_SOURCE = 6, ADV7611_PAD_SOURCE = 1, - ADV7604_PAD_MAX = 7, + ADV76XX_PAD_MAX = 7, }; #define V4L2_CID_ADV_RX_ANALOG_SAMPLING_PHASE (V4L2_CID_DV_CLASS_BASE + 0x1000) @@ -166,7 +167,7 @@ enum adv7604_pad { #define V4L2_CID_ADV_RX_FREE_RUN_COLOR (V4L2_CID_DV_CLASS_BASE + 0x1002) /* notify events */ -#define ADV7604_HOTPLUG 1 -#define ADV7604_FMT_CHANGE 2 +#define ADV76XX_HOTPLUG 1 +#define ADV76XX_FMT_CHANGE 2 #endif -- cgit v1.2.3 From 5cf6f7f327c95f09be859889be39e78950516556 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Tue, 17 Feb 2015 05:44:09 -0300 Subject: [media] v4l2-core: remove the old .ioctl BKL replacement To keep V4L2 drivers that did not yet convert to unlocked_ioctl happy, the v4l2 core had a .ioctl file operation that took a V4L2 lock. The last drivers are now converted to unlocked_ioctl, so all this old code can now be removed. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-dev.c | 28 ---------------------------- drivers/media/v4l2-core/v4l2-device.c | 1 - include/media/v4l2-dev.h | 1 - include/media/v4l2-device.h | 2 -- 4 files changed, 32 deletions(-) (limited to 'include') diff --git a/drivers/media/v4l2-core/v4l2-dev.c b/drivers/media/v4l2-core/v4l2-dev.c index d89d5cb465d9..9f4538ce09c2 100644 --- a/drivers/media/v4l2-core/v4l2-dev.c +++ b/drivers/media/v4l2-core/v4l2-dev.c @@ -357,34 +357,6 @@ static long v4l2_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) ret = vdev->fops->unlocked_ioctl(filp, cmd, arg); if (lock) mutex_unlock(lock); - } else if (vdev->fops->ioctl) { - /* This code path is a replacement for the BKL. It is a major - * hack but it will have to do for those drivers that are not - * yet converted to use unlocked_ioctl. - * - * All drivers implement struct v4l2_device, so we use the - * lock defined there to serialize the ioctls. - * - * However, if the driver sleeps, then it blocks all ioctls - * since the lock is still held. This is very common for - * VIDIOC_DQBUF since that normally waits for a frame to arrive. - * As a result any other ioctl calls will proceed very, very - * slowly since each call will have to wait for the VIDIOC_QBUF - * to finish. Things that should take 0.01s may now take 10-20 - * seconds. - * - * The workaround is to *not* take the lock for VIDIOC_DQBUF. - * This actually works OK for videobuf-based drivers, since - * videobuf will take its own internal lock. - */ - struct mutex *m = &vdev->v4l2_dev->ioctl_lock; - - if (cmd != VIDIOC_DQBUF && mutex_lock_interruptible(m)) - return -ERESTARTSYS; - if (video_is_registered(vdev)) - ret = vdev->fops->ioctl(filp, cmd, arg); - if (cmd != VIDIOC_DQBUF) - mutex_unlock(m); } else ret = -ENOTTY; diff --git a/drivers/media/v4l2-core/v4l2-device.c b/drivers/media/v4l2-core/v4l2-device.c index 204cc67c84e8..5b0a30b9252b 100644 --- a/drivers/media/v4l2-core/v4l2-device.c +++ b/drivers/media/v4l2-core/v4l2-device.c @@ -37,7 +37,6 @@ int v4l2_device_register(struct device *dev, struct v4l2_device *v4l2_dev) INIT_LIST_HEAD(&v4l2_dev->subdevs); spin_lock_init(&v4l2_dev->lock); - mutex_init(&v4l2_dev->ioctl_lock); v4l2_prio_init(&v4l2_dev->prio); kref_init(&v4l2_dev->ref); get_device(dev); diff --git a/include/media/v4l2-dev.h b/include/media/v4l2-dev.h index 3e4fddfc840c..acbcd2f5fe7f 100644 --- a/include/media/v4l2-dev.h +++ b/include/media/v4l2-dev.h @@ -65,7 +65,6 @@ struct v4l2_file_operations { ssize_t (*read) (struct file *, char __user *, size_t, loff_t *); ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *); unsigned int (*poll) (struct file *, struct poll_table_struct *); - long (*ioctl) (struct file *, unsigned int, unsigned long); long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long); #ifdef CONFIG_COMPAT long (*compat_ioctl32) (struct file *, unsigned int, unsigned long); diff --git a/include/media/v4l2-device.h b/include/media/v4l2-device.h index ffb69da3ce9e..9c581578783f 100644 --- a/include/media/v4l2-device.h +++ b/include/media/v4l2-device.h @@ -58,8 +58,6 @@ struct v4l2_device { struct v4l2_ctrl_handler *ctrl_handler; /* Device's priority state */ struct v4l2_prio_state prio; - /* BKL replacement mutex. Temporary solution only. */ - struct mutex ioctl_lock; /* Keep track of the references to this struct. */ struct kref ref; /* Release function that is called when the ref count goes to 0. */ -- cgit v1.2.3 From 61e021f3b86cbbcc04cbe8ac7b7da2b8c94b5e8e Mon Sep 17 00:00:00 2001 From: Daniel Borkmann Date: Mon, 2 Mar 2015 15:21:55 +0100 Subject: ebpf: move CONFIG_BPF_SYSCALL-only function declarations Masami noted that it would be better to hide the remaining CONFIG_BPF_SYSCALL-only function declarations within the BPF header ifdef, w/o else path dummy alternatives since these functions are not supposed to have a user outside of CONFIG_BPF_SYSCALL. Suggested-by: Masami Hiramatsu Reference: http://article.gmane.org/gmane.linux.kernel.api/8658 Signed-off-by: Daniel Borkmann Signed-off-by: David S. Miller --- include/linux/bpf.h | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) (limited to 'include') diff --git a/include/linux/bpf.h b/include/linux/bpf.h index a1a7ff2df328..a884f5a2c503 100644 --- a/include/linux/bpf.h +++ b/include/linux/bpf.h @@ -42,10 +42,6 @@ struct bpf_map_type_list { enum bpf_map_type type; }; -void bpf_register_map_type(struct bpf_map_type_list *tl); -void bpf_map_put(struct bpf_map *map); -struct bpf_map *bpf_map_get(struct fd f); - /* function argument constraints */ enum bpf_arg_type { ARG_ANYTHING = 0, /* any argument is ok */ @@ -126,9 +122,16 @@ struct bpf_prog_aux { #ifdef CONFIG_BPF_SYSCALL void bpf_register_prog_type(struct bpf_prog_type_list *tl); +void bpf_register_map_type(struct bpf_map_type_list *tl); -void bpf_prog_put(struct bpf_prog *prog); struct bpf_prog *bpf_prog_get(u32 ufd); +void bpf_prog_put(struct bpf_prog *prog); + +struct bpf_map *bpf_map_get(struct fd f); +void bpf_map_put(struct bpf_map *map); + +/* verify correctness of eBPF program */ +int bpf_check(struct bpf_prog *fp, union bpf_attr *attr); #else static inline void bpf_register_prog_type(struct bpf_prog_type_list *tl) { @@ -142,10 +145,7 @@ static inline struct bpf_prog *bpf_prog_get(u32 ufd) static inline void bpf_prog_put(struct bpf_prog *prog) { } -#endif - -/* verify correctness of eBPF program */ -int bpf_check(struct bpf_prog *fp, union bpf_attr *attr); +#endif /* CONFIG_BPF_SYSCALL */ /* verifier prototypes for helper functions called from eBPF programs */ extern const struct bpf_func_proto bpf_map_lookup_elem_proto; -- cgit v1.2.3 From 2438e78a48643d41defd4b90da19d17b9c3e65f3 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Tue, 3 Feb 2015 10:46:56 -0300 Subject: [media] v4l2-core: drop g/s_priority ops The handling of VIDIOC_G/S_PRIORITY is now entirely done by the V4L2 core, so we can drop the g/s_priority ioctl ops. We do have to make sure though that when S_PRIORITY is called we check that the driver used struct v4l2_fh. This check can be removed once all drivers are converted to that structure. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-dev.c | 7 +++---- drivers/media/v4l2-core/v4l2-ioctl.c | 6 ++---- include/media/v4l2-ioctl.h | 6 ------ 3 files changed, 5 insertions(+), 14 deletions(-) (limited to 'include') diff --git a/drivers/media/v4l2-core/v4l2-dev.c b/drivers/media/v4l2-core/v4l2-dev.c index 9f4538ce09c2..e2b8b3ed6ef3 100644 --- a/drivers/media/v4l2-core/v4l2-dev.c +++ b/drivers/media/v4l2-core/v4l2-dev.c @@ -532,10 +532,9 @@ static void determine_valid_ioctls(struct video_device *vdev) /* vfl_type and vfl_dir independent ioctls */ SET_VALID_IOCTL(ops, VIDIOC_QUERYCAP, vidioc_querycap); - if (ops->vidioc_g_priority) - set_bit(_IOC_NR(VIDIOC_G_PRIORITY), valid_ioctls); - if (ops->vidioc_s_priority) - set_bit(_IOC_NR(VIDIOC_S_PRIORITY), valid_ioctls); + set_bit(_IOC_NR(VIDIOC_G_PRIORITY), valid_ioctls); + set_bit(_IOC_NR(VIDIOC_S_PRIORITY), valid_ioctls); + /* Note: the control handler can also be passed through the filehandle, and that can't be tested here. If the bit for these control ioctls is set, then the ioctl is valid. But if it is 0, then it can still diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c b/drivers/media/v4l2-core/v4l2-ioctl.c index b08407225db1..09ad8ddfaaf9 100644 --- a/drivers/media/v4l2-core/v4l2-ioctl.c +++ b/drivers/media/v4l2-core/v4l2-ioctl.c @@ -1046,8 +1046,6 @@ static int v4l_g_priority(const struct v4l2_ioctl_ops *ops, struct video_device *vfd; u32 *p = arg; - if (ops->vidioc_g_priority) - return ops->vidioc_g_priority(file, fh, arg); vfd = video_devdata(file); *p = v4l2_prio_max(vfd->prio); return 0; @@ -1060,9 +1058,9 @@ static int v4l_s_priority(const struct v4l2_ioctl_ops *ops, struct v4l2_fh *vfh; u32 *p = arg; - if (ops->vidioc_s_priority) - return ops->vidioc_s_priority(file, fh, *p); vfd = video_devdata(file); + if (!test_bit(V4L2_FL_USES_V4L2_FH, &vfd->flags)) + return -ENOTTY; vfh = file->private_data; return v4l2_prio_change(vfd->prio, &vfh->prio, *p); } diff --git a/include/media/v4l2-ioctl.h b/include/media/v4l2-ioctl.h index 8537983b9b22..8fbbd76d78e8 100644 --- a/include/media/v4l2-ioctl.h +++ b/include/media/v4l2-ioctl.h @@ -23,12 +23,6 @@ struct v4l2_ioctl_ops { /* VIDIOC_QUERYCAP handler */ int (*vidioc_querycap)(struct file *file, void *fh, struct v4l2_capability *cap); - /* Priority handling */ - int (*vidioc_g_priority) (struct file *file, void *fh, - enum v4l2_priority *p); - int (*vidioc_s_priority) (struct file *file, void *fh, - enum v4l2_priority p); - /* VIDIOC_ENUM_FMT handlers */ int (*vidioc_enum_fmt_vid_cap) (struct file *file, void *fh, struct v4l2_fmtdesc *f); -- cgit v1.2.3 From 46d4e47abee04c24055114d50e408078a09c4d36 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Mon, 2 Mar 2015 00:04:31 -0600 Subject: ax25: Make ax25_header and ax25_rebuild_header static The only user is in ax25_ip.c so stop exporting these functions. Cc: Ralf Baechle Cc: linux-hams@vger.kernel.org Signed-off-by: "Eric W. Biederman" Signed-off-by: David S. Miller --- include/net/ax25.h | 3 --- net/ax25/ax25_ip.c | 18 ++++++++---------- 2 files changed, 8 insertions(+), 13 deletions(-) (limited to 'include') diff --git a/include/net/ax25.h b/include/net/ax25.h index bf0396e9a5d3..7385a64b61b8 100644 --- a/include/net/ax25.h +++ b/include/net/ax25.h @@ -366,9 +366,6 @@ int ax25_kiss_rcv(struct sk_buff *, struct net_device *, struct packet_type *, struct net_device *); /* ax25_ip.c */ -int ax25_hard_header(struct sk_buff *, struct net_device *, unsigned short, - const void *, const void *, unsigned int); -int ax25_rebuild_header(struct sk_buff *); extern const struct header_ops ax25_header_ops; /* ax25_out.c */ diff --git a/net/ax25/ax25_ip.c b/net/ax25/ax25_ip.c index db3c283821d1..d93103ba8cec 100644 --- a/net/ax25/ax25_ip.c +++ b/net/ax25/ax25_ip.c @@ -46,9 +46,9 @@ #ifdef CONFIG_INET -int ax25_hard_header(struct sk_buff *skb, struct net_device *dev, - unsigned short type, const void *daddr, - const void *saddr, unsigned int len) +static int ax25_hard_header(struct sk_buff *skb, struct net_device *dev, + unsigned short type, const void *daddr, + const void *saddr, unsigned int len) { unsigned char *buff; @@ -100,7 +100,7 @@ int ax25_hard_header(struct sk_buff *skb, struct net_device *dev, return -AX25_HEADER_LEN; /* Unfinished header */ } -int ax25_rebuild_header(struct sk_buff *skb) +static int ax25_rebuild_header(struct sk_buff *skb) { struct sk_buff *ourskb; unsigned char *bp = skb->data; @@ -218,14 +218,14 @@ put: #else /* INET */ -int ax25_hard_header(struct sk_buff *skb, struct net_device *dev, - unsigned short type, const void *daddr, - const void *saddr, unsigned int len) +static int ax25_hard_header(struct sk_buff *skb, struct net_device *dev, + unsigned short type, const void *daddr, + const void *saddr, unsigned int len) { return -AX25_HEADER_LEN; } -int ax25_rebuild_header(struct sk_buff *skb) +static int ax25_rebuild_header(struct sk_buff *skb) { return 1; } @@ -237,7 +237,5 @@ const struct header_ops ax25_header_ops = { .rebuild = ax25_rebuild_header, }; -EXPORT_SYMBOL(ax25_hard_header); -EXPORT_SYMBOL(ax25_rebuild_header); EXPORT_SYMBOL(ax25_header_ops); -- cgit v1.2.3 From 3b6a94bed0029a6b48055d89b8dea0567abca0ac Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Mon, 2 Mar 2015 00:05:28 -0600 Subject: ax25: Refactor to use private neighbour operations. AX25 already has it's own private arp cache operations to isolate it's abuse of dev_rebuild_header to transmit packets. Add a function ax25_neigh_construct that will allow all of the ax25 devices to force using these operations, so that the generic arp code does not need to. Cc: Ralf Baechle Cc: linux-hams@vger.kernel.org Signed-off-by: "Eric W. Biederman" Signed-off-by: David S. Miller --- drivers/net/hamradio/6pack.c | 2 ++ drivers/net/hamradio/baycom_epp.c | 2 ++ drivers/net/hamradio/bpqether.c | 2 ++ drivers/net/hamradio/dmascc.c | 2 ++ drivers/net/hamradio/hdlcdrv.c | 2 ++ drivers/net/hamradio/mkiss.c | 2 ++ drivers/net/hamradio/scc.c | 2 ++ drivers/net/hamradio/yam.c | 2 ++ include/net/ax25.h | 5 +++++ net/ax25/ax25_ip.c | 21 +++++++++++++++++++++ 10 files changed, 42 insertions(+) (limited to 'include') diff --git a/drivers/net/hamradio/6pack.c b/drivers/net/hamradio/6pack.c index 2533933c79dc..0b8393ca8c80 100644 --- a/drivers/net/hamradio/6pack.c +++ b/drivers/net/hamradio/6pack.c @@ -302,6 +302,7 @@ static const struct net_device_ops sp_netdev_ops = { .ndo_stop = sp_close, .ndo_start_xmit = sp_xmit, .ndo_set_mac_address = sp_set_mac_address, + .ndo_neigh_construct = ax25_neigh_construct, }; static void sp_setup(struct net_device *dev) @@ -315,6 +316,7 @@ static void sp_setup(struct net_device *dev) dev->addr_len = AX25_ADDR_LEN; dev->type = ARPHRD_AX25; + dev->neigh_priv_len = sizeof(struct ax25_neigh_priv); dev->tx_queue_len = 10; /* Only activated in AX.25 mode */ diff --git a/drivers/net/hamradio/baycom_epp.c b/drivers/net/hamradio/baycom_epp.c index a98c153f371e..3539ab392f7d 100644 --- a/drivers/net/hamradio/baycom_epp.c +++ b/drivers/net/hamradio/baycom_epp.c @@ -1109,6 +1109,7 @@ static const struct net_device_ops baycom_netdev_ops = { .ndo_do_ioctl = baycom_ioctl, .ndo_start_xmit = baycom_send_packet, .ndo_set_mac_address = baycom_set_mac_address, + .ndo_neigh_construct = ax25_neigh_construct, }; /* @@ -1146,6 +1147,7 @@ static void baycom_probe(struct net_device *dev) dev->header_ops = &ax25_header_ops; dev->type = ARPHRD_AX25; /* AF_AX25 device */ + dev->neigh_priv_len = sizeof(struct ax25_neigh_priv); dev->hard_header_len = AX25_MAX_HEADER_LEN + AX25_BPQ_HEADER_LEN; dev->mtu = AX25_DEF_PACLEN; /* eth_mtu is the default */ dev->addr_len = AX25_ADDR_LEN; /* sizeof an ax.25 address */ diff --git a/drivers/net/hamradio/bpqether.c b/drivers/net/hamradio/bpqether.c index c2894e43840e..bce105b16ed0 100644 --- a/drivers/net/hamradio/bpqether.c +++ b/drivers/net/hamradio/bpqether.c @@ -469,6 +469,7 @@ static const struct net_device_ops bpq_netdev_ops = { .ndo_start_xmit = bpq_xmit, .ndo_set_mac_address = bpq_set_mac_address, .ndo_do_ioctl = bpq_ioctl, + .ndo_neigh_construct = ax25_neigh_construct, }; static void bpq_setup(struct net_device *dev) @@ -486,6 +487,7 @@ static void bpq_setup(struct net_device *dev) #endif dev->type = ARPHRD_AX25; + dev->neigh_priv_len = sizeof(struct ax25_neigh_priv); dev->hard_header_len = AX25_MAX_HEADER_LEN + AX25_BPQ_HEADER_LEN; dev->mtu = AX25_DEF_PACLEN; dev->addr_len = AX25_ADDR_LEN; diff --git a/drivers/net/hamradio/dmascc.c b/drivers/net/hamradio/dmascc.c index 0fad408f24aa..abab7be77406 100644 --- a/drivers/net/hamradio/dmascc.c +++ b/drivers/net/hamradio/dmascc.c @@ -433,6 +433,7 @@ module_exit(dmascc_exit); static void __init dev_setup(struct net_device *dev) { dev->type = ARPHRD_AX25; + dev->neigh_priv_len = sizeof(struct ax25_neigh_priv); dev->hard_header_len = AX25_MAX_HEADER_LEN; dev->mtu = 1500; dev->addr_len = AX25_ADDR_LEN; @@ -447,6 +448,7 @@ static const struct net_device_ops scc_netdev_ops = { .ndo_start_xmit = scc_send_packet, .ndo_do_ioctl = scc_ioctl, .ndo_set_mac_address = scc_set_mac_address, + .ndo_neigh_construct = ax25_neigh_construct, }; static int __init setup_adapter(int card_base, int type, int n) diff --git a/drivers/net/hamradio/hdlcdrv.c b/drivers/net/hamradio/hdlcdrv.c index c67a27245072..435868a7b69c 100644 --- a/drivers/net/hamradio/hdlcdrv.c +++ b/drivers/net/hamradio/hdlcdrv.c @@ -626,6 +626,7 @@ static const struct net_device_ops hdlcdrv_netdev = { .ndo_start_xmit = hdlcdrv_send_packet, .ndo_do_ioctl = hdlcdrv_ioctl, .ndo_set_mac_address = hdlcdrv_set_mac_address, + .ndo_neigh_construct = ax25_neigh_construct, }; /* @@ -676,6 +677,7 @@ static void hdlcdrv_setup(struct net_device *dev) dev->header_ops = &ax25_header_ops; dev->type = ARPHRD_AX25; /* AF_AX25 device */ + dev->neigh_priv_len = sizeof(struct ax25_neigh_priv); dev->hard_header_len = AX25_MAX_HEADER_LEN + AX25_BPQ_HEADER_LEN; dev->mtu = AX25_DEF_PACLEN; /* eth_mtu is the default */ dev->addr_len = AX25_ADDR_LEN; /* sizeof an ax.25 address */ diff --git a/drivers/net/hamradio/mkiss.c b/drivers/net/hamradio/mkiss.c index e37c8d515ce8..c12ec2c2b594 100644 --- a/drivers/net/hamradio/mkiss.c +++ b/drivers/net/hamradio/mkiss.c @@ -641,6 +641,7 @@ static const struct net_device_ops ax_netdev_ops = { .ndo_stop = ax_close, .ndo_start_xmit = ax_xmit, .ndo_set_mac_address = ax_set_mac_address, + .ndo_neigh_construct = ax25_neigh_construct, }; static void ax_setup(struct net_device *dev) @@ -650,6 +651,7 @@ static void ax_setup(struct net_device *dev) dev->hard_header_len = 0; dev->addr_len = 0; dev->type = ARPHRD_AX25; + dev->neigh_priv_len = sizeof(struct ax25_neigh_priv); dev->tx_queue_len = 10; dev->header_ops = &ax25_header_ops; dev->netdev_ops = &ax_netdev_ops; diff --git a/drivers/net/hamradio/scc.c b/drivers/net/hamradio/scc.c index 57be9e0e98a6..b305f51eb420 100644 --- a/drivers/net/hamradio/scc.c +++ b/drivers/net/hamradio/scc.c @@ -1550,6 +1550,7 @@ static const struct net_device_ops scc_netdev_ops = { .ndo_set_mac_address = scc_net_set_mac_address, .ndo_get_stats = scc_net_get_stats, .ndo_do_ioctl = scc_net_ioctl, + .ndo_neigh_construct = ax25_neigh_construct, }; /* ----> Initialize device <----- */ @@ -1567,6 +1568,7 @@ static void scc_net_setup(struct net_device *dev) dev->flags = 0; dev->type = ARPHRD_AX25; + dev->neigh_priv_len = sizeof(struct ax25_neigh_priv); dev->hard_header_len = AX25_MAX_HEADER_LEN + AX25_BPQ_HEADER_LEN; dev->mtu = AX25_DEF_PACLEN; dev->addr_len = AX25_ADDR_LEN; diff --git a/drivers/net/hamradio/yam.c b/drivers/net/hamradio/yam.c index 717433cfb81d..89d9da7a0c51 100644 --- a/drivers/net/hamradio/yam.c +++ b/drivers/net/hamradio/yam.c @@ -1100,6 +1100,7 @@ static const struct net_device_ops yam_netdev_ops = { .ndo_start_xmit = yam_send_packet, .ndo_do_ioctl = yam_ioctl, .ndo_set_mac_address = yam_set_mac_address, + .ndo_neigh_construct = ax25_neigh_construct, }; static void yam_setup(struct net_device *dev) @@ -1128,6 +1129,7 @@ static void yam_setup(struct net_device *dev) dev->header_ops = &ax25_header_ops; dev->type = ARPHRD_AX25; + dev->neigh_priv_len = sizeof(struct ax25_neigh_priv); dev->hard_header_len = AX25_MAX_HEADER_LEN; dev->mtu = AX25_MTU; dev->addr_len = AX25_ADDR_LEN; diff --git a/include/net/ax25.h b/include/net/ax25.h index 7385a64b61b8..45feeba7a325 100644 --- a/include/net/ax25.h +++ b/include/net/ax25.h @@ -12,6 +12,7 @@ #include #include #include +#include #define AX25_T1CLAMPLO 1 #define AX25_T1CLAMPHI (30 * HZ) @@ -366,7 +367,11 @@ int ax25_kiss_rcv(struct sk_buff *, struct net_device *, struct packet_type *, struct net_device *); /* ax25_ip.c */ +int ax25_neigh_construct(struct neighbour *neigh); extern const struct header_ops ax25_header_ops; +struct ax25_neigh_priv { + struct neigh_ops ops; +}; /* ax25_out.c */ ax25_cb *ax25_send_frame(struct sk_buff *, int, ax25_address *, ax25_address *, diff --git a/net/ax25/ax25_ip.c b/net/ax25/ax25_ip.c index d93103ba8cec..bff12e0c9090 100644 --- a/net/ax25/ax25_ip.c +++ b/net/ax25/ax25_ip.c @@ -216,6 +216,22 @@ put: return 1; } +int ax25_neigh_construct(struct neighbour *neigh) +{ + /* This trouble could be saved if ax25 would right a proper + * dev_queue_xmit function. + */ + struct ax25_neigh_priv *priv = neighbour_priv(neigh); + + if (neigh->tbl->family != AF_INET) + return -EINVAL; + + priv->ops = *neigh->ops; + priv->ops.output = neigh_compat_output; + priv->ops.connected_output = neigh_compat_output; + return 0; +} + #else /* INET */ static int ax25_hard_header(struct sk_buff *skb, struct net_device *dev, @@ -230,6 +246,10 @@ static int ax25_rebuild_header(struct sk_buff *skb) return 1; } +int ax25_neigh_construct(struct neighbour *neigh) +{ + return 0; +} #endif const struct header_ops ax25_header_ops = { @@ -238,4 +258,5 @@ const struct header_ops ax25_header_ops = { }; EXPORT_SYMBOL(ax25_header_ops); +EXPORT_SYMBOL(ax25_neigh_construct); -- cgit v1.2.3 From def6775369fab9d36817b9a6bc58cab67f53f1dc Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Mon, 2 Mar 2015 00:07:37 -0600 Subject: neigh: Move neigh_compat_output into ax25_ip.c The only caller is now is ax25_neigh_construct so move neigh_compat_output into ax25_ip.c make it static and rename it ax25_neigh_output. Cc: Ralf Baechle Cc: linux-hams@vger.kernel.org Signed-off-by: "Eric W. Biederman" Signed-off-by: David S. Miller --- include/net/neighbour.h | 1 - net/ax25/ax25_ip.c | 18 ++++++++++++++++-- net/core/neighbour.c | 20 -------------------- 3 files changed, 16 insertions(+), 23 deletions(-) (limited to 'include') diff --git a/include/net/neighbour.h b/include/net/neighbour.h index 76f708486aae..bc66babb5f27 100644 --- a/include/net/neighbour.h +++ b/include/net/neighbour.h @@ -268,7 +268,6 @@ void neigh_changeaddr(struct neigh_table *tbl, struct net_device *dev); int neigh_ifdown(struct neigh_table *tbl, struct net_device *dev); int neigh_resolve_output(struct neighbour *neigh, struct sk_buff *skb); int neigh_connected_output(struct neighbour *neigh, struct sk_buff *skb); -int neigh_compat_output(struct neighbour *neigh, struct sk_buff *skb); int neigh_direct_output(struct neighbour *neigh, struct sk_buff *skb); struct neighbour *neigh_event_ns(struct neigh_table *tbl, u8 *lladdr, void *saddr, diff --git a/net/ax25/ax25_ip.c b/net/ax25/ax25_ip.c index bff12e0c9090..cc7415b33cfb 100644 --- a/net/ax25/ax25_ip.c +++ b/net/ax25/ax25_ip.c @@ -216,6 +216,20 @@ put: return 1; } +static int ax25_neigh_output(struct neighbour *neigh, struct sk_buff *skb) +{ + struct net_device *dev = skb->dev; + + __skb_pull(skb, skb_network_offset(skb)); + + if (dev_hard_header(skb, dev, ntohs(skb->protocol), NULL, NULL, + skb->len) < 0 && + dev_rebuild_header(skb)) + return 0; + + return dev_queue_xmit(skb); +} + int ax25_neigh_construct(struct neighbour *neigh) { /* This trouble could be saved if ax25 would right a proper @@ -227,8 +241,8 @@ int ax25_neigh_construct(struct neighbour *neigh) return -EINVAL; priv->ops = *neigh->ops; - priv->ops.output = neigh_compat_output; - priv->ops.connected_output = neigh_compat_output; + priv->ops.output = ax25_neigh_output; + priv->ops.connected_output = ax25_neigh_output; return 0; } diff --git a/net/core/neighbour.c b/net/core/neighbour.c index 70fe9e10ac86..8a319ff3e8d1 100644 --- a/net/core/neighbour.c +++ b/net/core/neighbour.c @@ -1280,26 +1280,6 @@ static void neigh_hh_init(struct neighbour *n, struct dst_entry *dst) write_unlock_bh(&n->lock); } -/* This function can be used in contexts, where only old dev_queue_xmit - * worked, f.e. if you want to override normal output path (eql, shaper), - * but resolution is not made yet. - */ - -int neigh_compat_output(struct neighbour *neigh, struct sk_buff *skb) -{ - struct net_device *dev = skb->dev; - - __skb_pull(skb, skb_network_offset(skb)); - - if (dev_hard_header(skb, dev, ntohs(skb->protocol), NULL, NULL, - skb->len) < 0 && - dev_rebuild_header(skb)) - return 0; - - return dev_queue_xmit(skb); -} -EXPORT_SYMBOL(neigh_compat_output); - /* Slow and careful. */ int neigh_resolve_output(struct neighbour *neigh, struct sk_buff *skb) -- cgit v1.2.3 From d476059e77d1af48453a58f9de1e36f2eaff6450 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Mon, 2 Mar 2015 00:11:09 -0600 Subject: net: Kill dev_rebuild_header Now that there are no more users kill dev_rebuild_header and all of it's implementations. This is long overdue. Signed-off-by: "Eric W. Biederman" Signed-off-by: David S. Miller --- drivers/firewire/net.c | 13 -------- drivers/isdn/i4l/isdn_net.c | 33 ------------------- drivers/media/dvb-core/dvb_net.c | 1 - drivers/net/arcnet/arcnet.c | 55 ------------------------------- drivers/net/ipvlan/ipvlan_main.c | 1 - drivers/net/macvlan.c | 1 - drivers/net/wireless/hostap/hostap_main.c | 1 - include/linux/etherdevice.h | 1 - include/linux/netdevice.h | 12 +------ net/802/fc.c | 21 ------------ net/802/fddi.c | 26 --------------- net/802/hippi.c | 28 ---------------- net/8021q/vlan_dev.c | 35 -------------------- net/ethernet/eth.c | 34 ------------------- net/netrom/nr_dev.c | 31 ----------------- net/rose/rose_dev.c | 14 -------- 16 files changed, 1 insertion(+), 306 deletions(-) (limited to 'include') diff --git a/drivers/firewire/net.c b/drivers/firewire/net.c index 2c68da1ceeee..f4ea80d602f7 100644 --- a/drivers/firewire/net.c +++ b/drivers/firewire/net.c @@ -237,18 +237,6 @@ static int fwnet_header_create(struct sk_buff *skb, struct net_device *net, return -net->hard_header_len; } -static int fwnet_header_rebuild(struct sk_buff *skb) -{ - struct fwnet_header *h = (struct fwnet_header *)skb->data; - - if (get_unaligned_be16(&h->h_proto) == ETH_P_IP) - return arp_find((unsigned char *)&h->h_dest, skb); - - dev_notice(&skb->dev->dev, "unable to resolve type %04x addresses\n", - be16_to_cpu(h->h_proto)); - return 0; -} - static int fwnet_header_cache(const struct neighbour *neigh, struct hh_cache *hh, __be16 type) { @@ -282,7 +270,6 @@ static int fwnet_header_parse(const struct sk_buff *skb, unsigned char *haddr) static const struct header_ops fwnet_header_ops = { .create = fwnet_header_create, - .rebuild = fwnet_header_rebuild, .cache = fwnet_header_cache, .cache_update = fwnet_header_cache_update, .parse = fwnet_header_parse, diff --git a/drivers/isdn/i4l/isdn_net.c b/drivers/isdn/i4l/isdn_net.c index 94affa5e6f28..546b7e81161d 100644 --- a/drivers/isdn/i4l/isdn_net.c +++ b/drivers/isdn/i4l/isdn_net.c @@ -1951,38 +1951,6 @@ static int isdn_net_header(struct sk_buff *skb, struct net_device *dev, return len; } -/* We don't need to send arp, because we have point-to-point connections. */ -static int -isdn_net_rebuild_header(struct sk_buff *skb) -{ - struct net_device *dev = skb->dev; - isdn_net_local *lp = netdev_priv(dev); - int ret = 0; - - if (lp->p_encap == ISDN_NET_ENCAP_ETHER) { - struct ethhdr *eth = (struct ethhdr *) skb->data; - - /* - * Only ARP/IP is currently supported - */ - - if (eth->h_proto != htons(ETH_P_IP)) { - printk(KERN_WARNING - "isdn_net: %s don't know how to resolve type %d addresses?\n", - dev->name, (int) eth->h_proto); - memcpy(eth->h_source, dev->dev_addr, dev->addr_len); - return 0; - } - /* - * Try to get ARP to resolve the header. - */ -#ifdef CONFIG_INET - ret = arp_find(eth->h_dest, skb); -#endif - } - return ret; -} - static int isdn_header_cache(const struct neighbour *neigh, struct hh_cache *hh, __be16 type) { @@ -2005,7 +1973,6 @@ static void isdn_header_cache_update(struct hh_cache *hh, static const struct header_ops isdn_header_ops = { .create = isdn_net_header, - .rebuild = isdn_net_rebuild_header, .cache = isdn_header_cache, .cache_update = isdn_header_cache_update, }; diff --git a/drivers/media/dvb-core/dvb_net.c b/drivers/media/dvb-core/dvb_net.c index 686d3277dad1..4a77cb02dffc 100644 --- a/drivers/media/dvb-core/dvb_net.c +++ b/drivers/media/dvb-core/dvb_net.c @@ -1190,7 +1190,6 @@ static int dvb_net_stop(struct net_device *dev) static const struct header_ops dvb_header_ops = { .create = eth_header, .parse = eth_header_parse, - .rebuild = eth_rebuild_header, }; diff --git a/drivers/net/arcnet/arcnet.c b/drivers/net/arcnet/arcnet.c index 09de683c167e..10f71c732b59 100644 --- a/drivers/net/arcnet/arcnet.c +++ b/drivers/net/arcnet/arcnet.c @@ -104,7 +104,6 @@ EXPORT_SYMBOL(arcnet_timeout); static int arcnet_header(struct sk_buff *skb, struct net_device *dev, unsigned short type, const void *daddr, const void *saddr, unsigned len); -static int arcnet_rebuild_header(struct sk_buff *skb); static int go_tx(struct net_device *dev); static int debug = ARCNET_DEBUG; @@ -312,7 +311,6 @@ static int choose_mtu(void) static const struct header_ops arcnet_header_ops = { .create = arcnet_header, - .rebuild = arcnet_rebuild_header, }; static const struct net_device_ops arcnet_netdev_ops = { @@ -538,59 +536,6 @@ static int arcnet_header(struct sk_buff *skb, struct net_device *dev, return proto->build_header(skb, dev, type, _daddr); } - -/* - * Rebuild the ARCnet hard header. This is called after an ARP (or in the - * future other address resolution) has completed on this sk_buff. We now - * let ARP fill in the destination field. - */ -static int arcnet_rebuild_header(struct sk_buff *skb) -{ - struct net_device *dev = skb->dev; - struct arcnet_local *lp = netdev_priv(dev); - int status = 0; /* default is failure */ - unsigned short type; - uint8_t daddr=0; - struct ArcProto *proto; - /* - * XXX: Why not use skb->mac_len? - */ - if (skb->network_header - skb->mac_header != 2) { - BUGMSG(D_NORMAL, - "rebuild_header: shouldn't be here! (hdrsize=%d)\n", - (int)(skb->network_header - skb->mac_header)); - return 0; - } - type = *(uint16_t *) skb_pull(skb, 2); - BUGMSG(D_DURING, "rebuild header for protocol %Xh\n", type); - - if (type == ETH_P_IP) { -#ifdef CONFIG_INET - BUGMSG(D_DURING, "rebuild header for ethernet protocol %Xh\n", type); - status = arp_find(&daddr, skb) ? 1 : 0; - BUGMSG(D_DURING, " rebuilt: dest is %d; protocol %Xh\n", - daddr, type); -#endif - } else { - BUGMSG(D_NORMAL, - "I don't understand ethernet protocol %Xh addresses!\n", type); - dev->stats.tx_errors++; - dev->stats.tx_aborted_errors++; - } - - /* if we couldn't resolve the address... give up. */ - if (!status) - return 0; - - /* add the _real_ header this time! */ - proto = arc_proto_map[lp->default_proto[daddr]]; - proto->build_header(skb, dev, type, daddr); - - return 1; /* success */ -} - - - /* Called by the kernel in order to transmit a packet. */ netdev_tx_t arcnet_send_packet(struct sk_buff *skb, struct net_device *dev) diff --git a/drivers/net/ipvlan/ipvlan_main.c b/drivers/net/ipvlan/ipvlan_main.c index 4f4099d5603d..2950c3780230 100644 --- a/drivers/net/ipvlan/ipvlan_main.c +++ b/drivers/net/ipvlan/ipvlan_main.c @@ -336,7 +336,6 @@ static int ipvlan_hard_header(struct sk_buff *skb, struct net_device *dev, static const struct header_ops ipvlan_header_ops = { .create = ipvlan_hard_header, - .rebuild = eth_rebuild_header, .parse = eth_header_parse, .cache = eth_header_cache, .cache_update = eth_header_cache_update, diff --git a/drivers/net/macvlan.c b/drivers/net/macvlan.c index 1df38bdae2ee..b5e3320ca506 100644 --- a/drivers/net/macvlan.c +++ b/drivers/net/macvlan.c @@ -550,7 +550,6 @@ static int macvlan_hard_header(struct sk_buff *skb, struct net_device *dev, static const struct header_ops macvlan_hard_header_ops = { .create = macvlan_hard_header, - .rebuild = eth_rebuild_header, .parse = eth_header_parse, .cache = eth_header_cache, .cache_update = eth_header_cache_update, diff --git a/drivers/net/wireless/hostap/hostap_main.c b/drivers/net/wireless/hostap/hostap_main.c index 52919ad42726..8f9f3e9fbfce 100644 --- a/drivers/net/wireless/hostap/hostap_main.c +++ b/drivers/net/wireless/hostap/hostap_main.c @@ -798,7 +798,6 @@ static void prism2_tx_timeout(struct net_device *dev) const struct header_ops hostap_80211_ops = { .create = eth_header, - .rebuild = eth_rebuild_header, .cache = eth_header_cache, .cache_update = eth_header_cache_update, .parse = hostap_80211_header_parse, diff --git a/include/linux/etherdevice.h b/include/linux/etherdevice.h index 1d869d185a0d..606563ef8a72 100644 --- a/include/linux/etherdevice.h +++ b/include/linux/etherdevice.h @@ -35,7 +35,6 @@ extern const struct header_ops eth_header_ops; int eth_header(struct sk_buff *skb, struct net_device *dev, unsigned short type, const void *daddr, const void *saddr, unsigned len); -int eth_rebuild_header(struct sk_buff *skb); int eth_header_parse(const struct sk_buff *skb, unsigned char *haddr); int eth_header_cache(const struct neighbour *neigh, struct hh_cache *hh, __be16 type); diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 5897b4ea5a3f..2007f3b44d05 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -261,7 +261,6 @@ struct header_ops { unsigned short type, const void *daddr, const void *saddr, unsigned int len); int (*parse)(const struct sk_buff *skb, unsigned char *haddr); - int (*rebuild)(struct sk_buff *skb); int (*cache)(const struct neighbour *neigh, struct hh_cache *hh, __be16 type); void (*cache_update)(struct hh_cache *hh, const struct net_device *dev, @@ -1346,7 +1345,7 @@ enum netdev_priv_flags { * if one wants to override the ndo_*() functions * @ethtool_ops: Management operations * @fwd_ops: Management operations - * @header_ops: Includes callbacks for creating,parsing,rebuilding,etc + * @header_ops: Includes callbacks for creating,parsing,caching,etc * of Layer 2 headers. * * @flags: Interface flags (a la BSD) @@ -2399,15 +2398,6 @@ static inline int dev_parse_header(const struct sk_buff *skb, return dev->header_ops->parse(skb, haddr); } -static inline int dev_rebuild_header(struct sk_buff *skb) -{ - const struct net_device *dev = skb->dev; - - if (!dev->header_ops || !dev->header_ops->rebuild) - return 0; - return dev->header_ops->rebuild(skb); -} - typedef int gifconf_func_t(struct net_device * dev, char __user * bufptr, int len); int register_gifconf(unsigned int family, gifconf_func_t *gifconf); static inline int unregister_gifconf(unsigned int family) diff --git a/net/802/fc.c b/net/802/fc.c index 7c174b6750cd..7b9219022418 100644 --- a/net/802/fc.c +++ b/net/802/fc.c @@ -75,29 +75,8 @@ static int fc_header(struct sk_buff *skb, struct net_device *dev, return -hdr_len; } -/* - * A neighbour discovery of some species (eg arp) has completed. We - * can now send the packet. - */ - -static int fc_rebuild_header(struct sk_buff *skb) -{ -#ifdef CONFIG_INET - struct fch_hdr *fch=(struct fch_hdr *)skb->data; - struct fcllc *fcllc=(struct fcllc *)(skb->data+sizeof(struct fch_hdr)); - if(fcllc->ethertype != htons(ETH_P_IP)) { - printk("fc_rebuild_header: Don't know how to resolve type %04X addresses ?\n", ntohs(fcllc->ethertype)); - return 0; - } - return arp_find(fch->daddr, skb); -#else - return 0; -#endif -} - static const struct header_ops fc_header_ops = { .create = fc_header, - .rebuild = fc_rebuild_header, }; static void fc_setup(struct net_device *dev) diff --git a/net/802/fddi.c b/net/802/fddi.c index 59e7346f1193..7d3a0af954e8 100644 --- a/net/802/fddi.c +++ b/net/802/fddi.c @@ -87,31 +87,6 @@ static int fddi_header(struct sk_buff *skb, struct net_device *dev, return -hl; } - -/* - * Rebuild the FDDI MAC header. This is called after an ARP - * (or in future other address resolution) has completed on - * this sk_buff. We now let ARP fill in the other fields. - */ - -static int fddi_rebuild_header(struct sk_buff *skb) -{ - struct fddihdr *fddi = (struct fddihdr *)skb->data; - -#ifdef CONFIG_INET - if (fddi->hdr.llc_snap.ethertype == htons(ETH_P_IP)) - /* Try to get ARP to resolve the header and fill destination address */ - return arp_find(fddi->daddr, skb); - else -#endif - { - printk("%s: Don't know how to resolve type %04X addresses.\n", - skb->dev->name, ntohs(fddi->hdr.llc_snap.ethertype)); - return 0; - } -} - - /* * Determine the packet's protocol ID and fill in skb fields. * This routine is called before an incoming packet is passed @@ -177,7 +152,6 @@ EXPORT_SYMBOL(fddi_change_mtu); static const struct header_ops fddi_header_ops = { .create = fddi_header, - .rebuild = fddi_rebuild_header, }; diff --git a/net/802/hippi.c b/net/802/hippi.c index 2e03f8259dd5..ade1a52cdcff 100644 --- a/net/802/hippi.c +++ b/net/802/hippi.c @@ -90,33 +90,6 @@ static int hippi_header(struct sk_buff *skb, struct net_device *dev, } -/* - * Rebuild the HIPPI MAC header. This is called after an ARP has - * completed on this sk_buff. We now let ARP fill in the other fields. - */ - -static int hippi_rebuild_header(struct sk_buff *skb) -{ - struct hippi_hdr *hip = (struct hippi_hdr *)skb->data; - - /* - * Only IP is currently supported - */ - - if(hip->snap.ethertype != htons(ETH_P_IP)) - { - printk(KERN_DEBUG "%s: unable to resolve type %X addresses.\n",skb->dev->name,ntohs(hip->snap.ethertype)); - return 0; - } - - /* - * We don't support dynamic ARP on HIPPI, but we use the ARP - * static ARP tables to hold the I-FIELDs. - */ - return arp_find(hip->le.daddr, skb); -} - - /* * Determine the packet's protocol ID. */ @@ -186,7 +159,6 @@ EXPORT_SYMBOL(hippi_neigh_setup_dev); static const struct header_ops hippi_header_ops = { .create = hippi_header, - .rebuild = hippi_rebuild_header, }; diff --git a/net/8021q/vlan_dev.c b/net/8021q/vlan_dev.c index 118956448cf6..1dcfec8b49f3 100644 --- a/net/8021q/vlan_dev.c +++ b/net/8021q/vlan_dev.c @@ -36,39 +36,6 @@ #include #include -/* - * Rebuild the Ethernet MAC header. This is called after an ARP - * (or in future other address resolution) has completed on this - * sk_buff. We now let ARP fill in the other fields. - * - * This routine CANNOT use cached dst->neigh! - * Really, it is used only when dst->neigh is wrong. - * - * TODO: This needs a checkup, I'm ignorant here. --BLG - */ -static int vlan_dev_rebuild_header(struct sk_buff *skb) -{ - struct net_device *dev = skb->dev; - struct vlan_ethhdr *veth = (struct vlan_ethhdr *)(skb->data); - - switch (veth->h_vlan_encapsulated_proto) { -#ifdef CONFIG_INET - case htons(ETH_P_IP): - - /* TODO: Confirm this will work with VLAN headers... */ - return arp_find(veth->h_dest, skb); -#endif - default: - pr_debug("%s: unable to resolve type %X addresses\n", - dev->name, ntohs(veth->h_vlan_encapsulated_proto)); - - ether_addr_copy(veth->h_source, dev->dev_addr); - break; - } - - return 0; -} - /* * Create the VLAN header for an arbitrary protocol layer * @@ -534,7 +501,6 @@ static int vlan_dev_get_lock_subclass(struct net_device *dev) static const struct header_ops vlan_header_ops = { .create = vlan_dev_hard_header, - .rebuild = vlan_dev_rebuild_header, .parse = eth_header_parse, }; @@ -554,7 +520,6 @@ static int vlan_passthru_hard_header(struct sk_buff *skb, struct net_device *dev static const struct header_ops vlan_passthru_header_ops = { .create = vlan_passthru_hard_header, - .rebuild = dev_rebuild_header, .parse = eth_header_parse, }; diff --git a/net/ethernet/eth.c b/net/ethernet/eth.c index 238f38d21641..8dbdf6c910b7 100644 --- a/net/ethernet/eth.c +++ b/net/ethernet/eth.c @@ -112,39 +112,6 @@ int eth_header(struct sk_buff *skb, struct net_device *dev, } EXPORT_SYMBOL(eth_header); -/** - * eth_rebuild_header- rebuild the Ethernet MAC header. - * @skb: socket buffer to update - * - * This is called after an ARP or IPV6 ndisc it's resolution on this - * sk_buff. We now let protocol (ARP) fill in the other fields. - * - * This routine CANNOT use cached dst->neigh! - * Really, it is used only when dst->neigh is wrong. - */ -int eth_rebuild_header(struct sk_buff *skb) -{ - struct ethhdr *eth = (struct ethhdr *)skb->data; - struct net_device *dev = skb->dev; - - switch (eth->h_proto) { -#ifdef CONFIG_INET - case htons(ETH_P_IP): - return arp_find(eth->h_dest, skb); -#endif - default: - netdev_dbg(dev, - "%s: unable to resolve type %X addresses.\n", - dev->name, ntohs(eth->h_proto)); - - memcpy(eth->h_source, dev->dev_addr, ETH_ALEN); - break; - } - - return 0; -} -EXPORT_SYMBOL(eth_rebuild_header); - /** * eth_get_headlen - determine the the length of header for an ethernet frame * @data: pointer to start of frame @@ -369,7 +336,6 @@ EXPORT_SYMBOL(eth_validate_addr); const struct header_ops eth_header_ops ____cacheline_aligned = { .create = eth_header, .parse = eth_header_parse, - .rebuild = eth_rebuild_header, .cache = eth_header_cache, .cache_update = eth_header_cache_update, }; diff --git a/net/netrom/nr_dev.c b/net/netrom/nr_dev.c index 6ae063cebf7d..988f542481a8 100644 --- a/net/netrom/nr_dev.c +++ b/net/netrom/nr_dev.c @@ -65,36 +65,6 @@ int nr_rx_ip(struct sk_buff *skb, struct net_device *dev) return 1; } -#ifdef CONFIG_INET - -static int nr_rebuild_header(struct sk_buff *skb) -{ - unsigned char *bp = skb->data; - - if (arp_find(bp + 7, skb)) - return 1; - - bp[6] &= ~AX25_CBIT; - bp[6] &= ~AX25_EBIT; - bp[6] |= AX25_SSSID_SPARE; - bp += AX25_ADDR_LEN; - - bp[6] &= ~AX25_CBIT; - bp[6] |= AX25_EBIT; - bp[6] |= AX25_SSSID_SPARE; - - return 0; -} - -#else - -static int nr_rebuild_header(struct sk_buff *skb) -{ - return 1; -} - -#endif - static int nr_header(struct sk_buff *skb, struct net_device *dev, unsigned short type, const void *daddr, const void *saddr, unsigned int len) @@ -188,7 +158,6 @@ static netdev_tx_t nr_xmit(struct sk_buff *skb, struct net_device *dev) static const struct header_ops nr_header_ops = { .create = nr_header, - .rebuild= nr_rebuild_header, }; static const struct net_device_ops nr_netdev_ops = { diff --git a/net/rose/rose_dev.c b/net/rose/rose_dev.c index 90209c1fa49b..369ca81a8c5d 100644 --- a/net/rose/rose_dev.c +++ b/net/rose/rose_dev.c @@ -56,19 +56,6 @@ static int rose_header(struct sk_buff *skb, struct net_device *dev, return -37; } -static int rose_rebuild_header(struct sk_buff *skb) -{ -#ifdef CONFIG_INET - unsigned char *bp = (unsigned char *)skb->data; - - if (arp_find(bp + 7, skb)) { - return 1; - } - -#endif - return 0; -} - static int rose_set_mac_address(struct net_device *dev, void *addr) { struct sockaddr *sa = addr; @@ -133,7 +120,6 @@ static netdev_tx_t rose_xmit(struct sk_buff *skb, struct net_device *dev) static const struct header_ops rose_header_ops = { .create = rose_header, - .rebuild = rose_rebuild_header, }; static const struct net_device_ops rose_netdev_ops = { -- cgit v1.2.3 From 59b2af26b9f674749216fd3bfba19d842de3671c Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Mon, 2 Mar 2015 00:12:05 -0600 Subject: arp: Kill arp_find There are no more callers so kill this function. Signed-off-by: "Eric W. Biederman" Signed-off-by: David S. Miller --- include/net/arp.h | 1 - net/ipv4/arp.c | 65 ------------------------------------------------------- 2 files changed, 66 deletions(-) (limited to 'include') diff --git a/include/net/arp.h b/include/net/arp.h index 73c49864076b..21ee1860abbc 100644 --- a/include/net/arp.h +++ b/include/net/arp.h @@ -47,7 +47,6 @@ static inline struct neighbour *__ipv4_neigh_lookup(struct net_device *dev, u32 } void arp_init(void); -int arp_find(unsigned char *haddr, struct sk_buff *skb); int arp_ioctl(struct net *net, unsigned int cmd, void __user *arg); void arp_send(int type, int ptype, __be32 dest_ip, struct net_device *dev, __be32 src_ip, diff --git a/net/ipv4/arp.c b/net/ipv4/arp.c index 2557cf9a4648..bca5b9d9b442 100644 --- a/net/ipv4/arp.c +++ b/net/ipv4/arp.c @@ -396,71 +396,6 @@ static int arp_filter(__be32 sip, __be32 tip, struct net_device *dev) return flag; } -/* OBSOLETE FUNCTIONS */ - -/* - * Find an arp mapping in the cache. If not found, post a request. - * - * It is very UGLY routine: it DOES NOT use skb->dst->neighbour, - * even if it exists. It is supposed that skb->dev was mangled - * by a virtual device (eql, shaper). Nobody but broken devices - * is allowed to use this function, it is scheduled to be removed. --ANK - */ - -static int arp_set_predefined(int addr_hint, unsigned char *haddr, - __be32 paddr, struct net_device *dev) -{ - switch (addr_hint) { - case RTN_LOCAL: - pr_debug("arp called for own IP address\n"); - memcpy(haddr, dev->dev_addr, dev->addr_len); - return 1; - case RTN_MULTICAST: - arp_mc_map(paddr, haddr, dev, 1); - return 1; - case RTN_BROADCAST: - memcpy(haddr, dev->broadcast, dev->addr_len); - return 1; - } - return 0; -} - - -int arp_find(unsigned char *haddr, struct sk_buff *skb) -{ - struct net_device *dev = skb->dev; - __be32 paddr; - struct neighbour *n; - - if (!skb_dst(skb)) { - pr_debug("arp_find is called with dst==NULL\n"); - kfree_skb(skb); - return 1; - } - - paddr = rt_nexthop(skb_rtable(skb), ip_hdr(skb)->daddr); - if (arp_set_predefined(inet_addr_type(dev_net(dev), paddr), haddr, - paddr, dev)) - return 0; - - n = __neigh_lookup(&arp_tbl, &paddr, dev, 1); - - if (n) { - n->used = jiffies; - if (n->nud_state & NUD_VALID || neigh_event_send(n, skb) == 0) { - neigh_ha_snapshot(haddr, n, dev); - neigh_release(n); - return 0; - } - neigh_release(n); - } else - kfree_skb(skb); - return 1; -} -EXPORT_SYMBOL(arp_find); - -/* END OF OBSOLETE FUNCTIONS */ - /* * Check if we can use proxy ARP for this path */ -- cgit v1.2.3 From bdf53c58490bb52e17636eca8ad18d2c38ec3cb8 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Mon, 2 Mar 2015 00:13:22 -0600 Subject: neigh: Don't require dst in neigh_hh_init - Add protocol to neigh_tbl so that dst->ops->protocol is not needed - Acquire the device from neigh->dev This results in a neigh_hh_init that will cache the samve values regardless of the packets flowing through it. Signed-off-by: "Eric W. Biederman" Signed-off-by: David S. Miller --- include/net/neighbour.h | 1 + net/core/neighbour.c | 8 ++++---- net/decnet/dn_neigh.c | 1 + net/ipv4/arp.c | 1 + net/ipv6/ndisc.c | 1 + 5 files changed, 8 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/include/net/neighbour.h b/include/net/neighbour.h index bc66babb5f27..9f912e4d4232 100644 --- a/include/net/neighbour.h +++ b/include/net/neighbour.h @@ -193,6 +193,7 @@ struct neigh_table { int family; int entry_size; int key_len; + __be16 protocol; __u32 (*hash)(const void *pkey, const struct net_device *dev, __u32 *hash_rnd); diff --git a/net/core/neighbour.c b/net/core/neighbour.c index 8a319ff3e8d1..af72b863e968 100644 --- a/net/core/neighbour.c +++ b/net/core/neighbour.c @@ -1263,10 +1263,10 @@ struct neighbour *neigh_event_ns(struct neigh_table *tbl, EXPORT_SYMBOL(neigh_event_ns); /* called with read_lock_bh(&n->lock); */ -static void neigh_hh_init(struct neighbour *n, struct dst_entry *dst) +static void neigh_hh_init(struct neighbour *n) { - struct net_device *dev = dst->dev; - __be16 prot = dst->ops->protocol; + struct net_device *dev = n->dev; + __be16 prot = n->tbl->protocol; struct hh_cache *hh = &n->hh; write_lock_bh(&n->lock); @@ -1296,7 +1296,7 @@ int neigh_resolve_output(struct neighbour *neigh, struct sk_buff *skb) unsigned int seq; if (dev->header_ops->cache && !neigh->hh.hh_len) - neigh_hh_init(neigh, dst); + neigh_hh_init(neigh); do { __skb_pull(skb, skb_network_offset(skb)); diff --git a/net/decnet/dn_neigh.c b/net/decnet/dn_neigh.c index 7ca7c3143da3..f123c6c6748c 100644 --- a/net/decnet/dn_neigh.c +++ b/net/decnet/dn_neigh.c @@ -97,6 +97,7 @@ struct neigh_table dn_neigh_table = { .family = PF_DECnet, .entry_size = NEIGH_ENTRY_SIZE(sizeof(struct dn_neigh)), .key_len = sizeof(__le16), + .protocol = cpu_to_be16(ETH_P_DNA_RT), .hash = dn_neigh_hash, .constructor = dn_neigh_construct, .id = "dn_neigh_cache", diff --git a/net/ipv4/arp.c b/net/ipv4/arp.c index bca5b9d9b442..6b8aad6a0d7d 100644 --- a/net/ipv4/arp.c +++ b/net/ipv4/arp.c @@ -152,6 +152,7 @@ static const struct neigh_ops arp_direct_ops = { struct neigh_table arp_tbl = { .family = AF_INET, .key_len = 4, + .protocol = cpu_to_be16(ETH_P_IP), .hash = arp_hash, .constructor = arp_constructor, .proxy_redo = parp_redo, diff --git a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c index 471ed24aabae..e363bbc2420d 100644 --- a/net/ipv6/ndisc.c +++ b/net/ipv6/ndisc.c @@ -117,6 +117,7 @@ static const struct neigh_ops ndisc_direct_ops = { struct neigh_table nd_tbl = { .family = AF_INET6, .key_len = sizeof(struct in6_addr), + .protocol = cpu_to_be16(ETH_P_IPV6), .hash = ndisc_hash, .constructor = ndisc_constructor, .pconstructor = pndisc_constructor, -- cgit v1.2.3 From ee586bbc28fb7128133457cf711880d13a3b7ce4 Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Mon, 16 Feb 2015 18:54:04 +0100 Subject: netfilter: reject: don't send icmp error if csum is invalid tcp resets are never emitted if the packet that triggers the reject/reset has an invalid checksum. For icmp error responses there was no such check. It allows to distinguish icmp response generated via iptables -I INPUT -p udp --dport 42 -j REJECT and those emitted by network stack (won't respond if csum is invalid, REJECT does). Arguably its possible to avoid this by using conntrack and only using REJECT with -m conntrack NEW/RELATED. However, this doesn't work when connection tracking is not in use or when using nf_conntrack_checksum=0. Furthermore, sending errors in response to invalid csums doesn't make much sense so just add similar test as in nf_send_reset. Validate csum if needed and only send the response if it is ok. Reference: http://bugzilla.redhat.com/show_bug.cgi?id=1169829 Signed-off-by: Florian Westphal Signed-off-by: Pablo Neira Ayuso --- include/net/netfilter/ipv4/nf_reject.h | 6 +----- include/net/netfilter/ipv6/nf_reject.h | 11 ++--------- net/ipv4/netfilter/ipt_REJECT.c | 17 +++++++++-------- net/ipv4/netfilter/nf_reject_ipv4.c | 23 ++++++++++++++++++++++ net/ipv4/netfilter/nft_reject_ipv4.c | 3 ++- net/ipv6/netfilter/nf_reject_ipv6.c | 35 ++++++++++++++++++++++++++++++++++ net/netfilter/nft_reject_inet.c | 6 ++++-- 7 files changed, 76 insertions(+), 25 deletions(-) (limited to 'include') diff --git a/include/net/netfilter/ipv4/nf_reject.h b/include/net/netfilter/ipv4/nf_reject.h index 03e928a55229..864127573c32 100644 --- a/include/net/netfilter/ipv4/nf_reject.h +++ b/include/net/netfilter/ipv4/nf_reject.h @@ -5,11 +5,7 @@ #include #include -static inline void nf_send_unreach(struct sk_buff *skb_in, int code) -{ - icmp_send(skb_in, ICMP_DEST_UNREACH, code, 0); -} - +void nf_send_unreach(struct sk_buff *skb_in, int code, int hook); void nf_send_reset(struct sk_buff *oldskb, int hook); const struct tcphdr *nf_reject_ip_tcphdr_get(struct sk_buff *oldskb, diff --git a/include/net/netfilter/ipv6/nf_reject.h b/include/net/netfilter/ipv6/nf_reject.h index 23216d48abf9..0ae445d3f217 100644 --- a/include/net/netfilter/ipv6/nf_reject.h +++ b/include/net/netfilter/ipv6/nf_reject.h @@ -3,15 +3,8 @@ #include -static inline void -nf_send_unreach6(struct net *net, struct sk_buff *skb_in, unsigned char code, - unsigned int hooknum) -{ - if (hooknum == NF_INET_LOCAL_OUT && skb_in->dev == NULL) - skb_in->dev = net->loopback_dev; - - icmpv6_send(skb_in, ICMPV6_DEST_UNREACH, code, 0); -} +void nf_send_unreach6(struct net *net, struct sk_buff *skb_in, unsigned char code, + unsigned int hooknum); void nf_send_reset6(struct net *net, struct sk_buff *oldskb, int hook); diff --git a/net/ipv4/netfilter/ipt_REJECT.c b/net/ipv4/netfilter/ipt_REJECT.c index 8f48f5517e33..87907d4bd259 100644 --- a/net/ipv4/netfilter/ipt_REJECT.c +++ b/net/ipv4/netfilter/ipt_REJECT.c @@ -34,31 +34,32 @@ static unsigned int reject_tg(struct sk_buff *skb, const struct xt_action_param *par) { const struct ipt_reject_info *reject = par->targinfo; + int hook = par->hooknum; switch (reject->with) { case IPT_ICMP_NET_UNREACHABLE: - nf_send_unreach(skb, ICMP_NET_UNREACH); + nf_send_unreach(skb, ICMP_NET_UNREACH, hook); break; case IPT_ICMP_HOST_UNREACHABLE: - nf_send_unreach(skb, ICMP_HOST_UNREACH); + nf_send_unreach(skb, ICMP_HOST_UNREACH, hook); break; case IPT_ICMP_PROT_UNREACHABLE: - nf_send_unreach(skb, ICMP_PROT_UNREACH); + nf_send_unreach(skb, ICMP_PROT_UNREACH, hook); break; case IPT_ICMP_PORT_UNREACHABLE: - nf_send_unreach(skb, ICMP_PORT_UNREACH); + nf_send_unreach(skb, ICMP_PORT_UNREACH, hook); break; case IPT_ICMP_NET_PROHIBITED: - nf_send_unreach(skb, ICMP_NET_ANO); + nf_send_unreach(skb, ICMP_NET_ANO, hook); break; case IPT_ICMP_HOST_PROHIBITED: - nf_send_unreach(skb, ICMP_HOST_ANO); + nf_send_unreach(skb, ICMP_HOST_ANO, hook); break; case IPT_ICMP_ADMIN_PROHIBITED: - nf_send_unreach(skb, ICMP_PKT_FILTERED); + nf_send_unreach(skb, ICMP_PKT_FILTERED, hook); break; case IPT_TCP_RESET: - nf_send_reset(skb, par->hooknum); + nf_send_reset(skb, hook); case IPT_ICMP_ECHOREPLY: /* Doesn't happen. */ break; diff --git a/net/ipv4/netfilter/nf_reject_ipv4.c b/net/ipv4/netfilter/nf_reject_ipv4.c index 536da7bc598a..b7405eb7f1ef 100644 --- a/net/ipv4/netfilter/nf_reject_ipv4.c +++ b/net/ipv4/netfilter/nf_reject_ipv4.c @@ -164,4 +164,27 @@ void nf_send_reset(struct sk_buff *oldskb, int hook) } EXPORT_SYMBOL_GPL(nf_send_reset); +void nf_send_unreach(struct sk_buff *skb_in, int code, int hook) +{ + struct iphdr *iph = ip_hdr(skb_in); + u8 proto; + + if (skb_in->csum_bad || iph->frag_off & htons(IP_OFFSET)) + return; + + if (skb_csum_unnecessary(skb_in)) { + icmp_send(skb_in, ICMP_DEST_UNREACH, code, 0); + return; + } + + if (iph->protocol == IPPROTO_TCP || iph->protocol == IPPROTO_UDP) + proto = iph->protocol; + else + proto = 0; + + if (nf_ip_checksum(skb_in, hook, ip_hdrlen(skb_in), proto) == 0) + icmp_send(skb_in, ICMP_DEST_UNREACH, code, 0); +} +EXPORT_SYMBOL_GPL(nf_send_unreach); + MODULE_LICENSE("GPL"); diff --git a/net/ipv4/netfilter/nft_reject_ipv4.c b/net/ipv4/netfilter/nft_reject_ipv4.c index d729542bd1b7..16a5d4d73d75 100644 --- a/net/ipv4/netfilter/nft_reject_ipv4.c +++ b/net/ipv4/netfilter/nft_reject_ipv4.c @@ -27,7 +27,8 @@ static void nft_reject_ipv4_eval(const struct nft_expr *expr, switch (priv->type) { case NFT_REJECT_ICMP_UNREACH: - nf_send_unreach(pkt->skb, priv->icmp_code); + nf_send_unreach(pkt->skb, priv->icmp_code, + pkt->ops->hooknum); break; case NFT_REJECT_TCP_RST: nf_send_reset(pkt->skb, pkt->ops->hooknum); diff --git a/net/ipv6/netfilter/nf_reject_ipv6.c b/net/ipv6/netfilter/nf_reject_ipv6.c index d05b36440e8b..68e0bb4db1bf 100644 --- a/net/ipv6/netfilter/nf_reject_ipv6.c +++ b/net/ipv6/netfilter/nf_reject_ipv6.c @@ -208,4 +208,39 @@ void nf_send_reset6(struct net *net, struct sk_buff *oldskb, int hook) } EXPORT_SYMBOL_GPL(nf_send_reset6); +static bool reject6_csum_ok(struct sk_buff *skb, int hook) +{ + const struct ipv6hdr *ip6h = ipv6_hdr(skb); + int thoff; + __be16 fo; + u8 proto; + + if (skb->csum_bad) + return false; + + if (skb_csum_unnecessary(skb)) + return true; + + proto = ip6h->nexthdr; + thoff = ipv6_skip_exthdr(skb, ((u8*)(ip6h+1) - skb->data), &proto, &fo); + + if (thoff < 0 || thoff >= skb->len || (fo & htons(~0x7)) != 0) + return false; + + return nf_ip6_checksum(skb, hook, thoff, proto) == 0; +} + +void nf_send_unreach6(struct net *net, struct sk_buff *skb_in, + unsigned char code, unsigned int hooknum) +{ + if (!reject6_csum_ok(skb_in, hooknum)) + return; + + if (hooknum == NF_INET_LOCAL_OUT && skb_in->dev == NULL) + skb_in->dev = net->loopback_dev; + + icmpv6_send(skb_in, ICMPV6_DEST_UNREACH, code, 0); +} +EXPORT_SYMBOL_GPL(nf_send_unreach6); + MODULE_LICENSE("GPL"); diff --git a/net/netfilter/nft_reject_inet.c b/net/netfilter/nft_reject_inet.c index 7b5f9d58680a..92877114aff4 100644 --- a/net/netfilter/nft_reject_inet.c +++ b/net/netfilter/nft_reject_inet.c @@ -28,14 +28,16 @@ static void nft_reject_inet_eval(const struct nft_expr *expr, case NFPROTO_IPV4: switch (priv->type) { case NFT_REJECT_ICMP_UNREACH: - nf_send_unreach(pkt->skb, priv->icmp_code); + nf_send_unreach(pkt->skb, priv->icmp_code, + pkt->ops->hooknum); break; case NFT_REJECT_TCP_RST: nf_send_reset(pkt->skb, pkt->ops->hooknum); break; case NFT_REJECT_ICMPX_UNREACH: nf_send_unreach(pkt->skb, - nft_reject_icmp_code(priv->icmp_code)); + nft_reject_icmp_code(priv->icmp_code), + pkt->ops->hooknum); break; } break; -- cgit v1.2.3 From e086e3035e0691b362755d1b5e24df631eee335a Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 27 Feb 2015 18:01:22 +0100 Subject: ALSA: core: Re-add snd_device_disconnect() Revive snd_device_disconnect() again so that it can be called from the individual driver. This time, HD-audio will need it. Signed-off-by: Takashi Iwai --- include/sound/core.h | 3 ++- sound/core/device.c | 43 +++++++++++++++++++++++++++++++++---------- sound/core/init.c | 5 +---- 3 files changed, 36 insertions(+), 15 deletions(-) (limited to 'include') diff --git a/include/sound/core.h b/include/sound/core.h index da5748289968..b12931f513f4 100644 --- a/include/sound/core.h +++ b/include/sound/core.h @@ -278,7 +278,8 @@ int snd_device_new(struct snd_card *card, enum snd_device_type type, void *device_data, struct snd_device_ops *ops); int snd_device_register(struct snd_card *card, void *device_data); int snd_device_register_all(struct snd_card *card); -int snd_device_disconnect_all(struct snd_card *card); +void snd_device_disconnect(struct snd_card *card, void *device_data); +void snd_device_disconnect_all(struct snd_card *card); void snd_device_free(struct snd_card *card, void *device_data); void snd_device_free_all(struct snd_card *card); diff --git a/sound/core/device.c b/sound/core/device.c index 41bec3075ae5..446dc452654e 100644 --- a/sound/core/device.c +++ b/sound/core/device.c @@ -73,7 +73,7 @@ int snd_device_new(struct snd_card *card, enum snd_device_type type, } EXPORT_SYMBOL(snd_device_new); -static int __snd_device_disconnect(struct snd_device *dev) +static void __snd_device_disconnect(struct snd_device *dev) { if (dev->state == SNDRV_DEV_REGISTERED) { if (dev->ops->dev_disconnect && @@ -81,7 +81,6 @@ static int __snd_device_disconnect(struct snd_device *dev) dev_err(dev->card->dev, "device disconnect failure\n"); dev->state = SNDRV_DEV_DISCONNECTED; } - return 0; } static void __snd_device_free(struct snd_device *dev) @@ -108,6 +107,34 @@ static struct snd_device *look_for_dev(struct snd_card *card, void *device_data) return NULL; } +/** + * snd_device_disconnect - disconnect the device + * @card: the card instance + * @device_data: the data pointer to disconnect + * + * Turns the device into the disconnection state, invoking + * dev_disconnect callback, if the device was already registered. + * + * Usually called from snd_card_disconnect(). + * + * Return: Zero if successful, or a negative error code on failure or if the + * device not found. + */ +void snd_device_disconnect(struct snd_card *card, void *device_data) +{ + struct snd_device *dev; + + if (snd_BUG_ON(!card || !device_data)) + return; + dev = look_for_dev(card, device_data); + if (dev) + __snd_device_disconnect(dev); + else + dev_dbg(card->dev, "device disconnect %p (from %pF), not found\n", + device_data, __builtin_return_address(0)); +} +EXPORT_SYMBOL_GPL(snd_device_disconnect); + /** * snd_device_free - release the device from the card * @card: the card instance @@ -195,18 +222,14 @@ int snd_device_register_all(struct snd_card *card) * disconnect all the devices on the card. * called from init.c */ -int snd_device_disconnect_all(struct snd_card *card) +void snd_device_disconnect_all(struct snd_card *card) { struct snd_device *dev; - int err = 0; if (snd_BUG_ON(!card)) - return -ENXIO; - list_for_each_entry_reverse(dev, &card->devices, list) { - if (__snd_device_disconnect(dev) < 0) - err = -ENXIO; - } - return err; + return; + list_for_each_entry_reverse(dev, &card->devices, list) + __snd_device_disconnect(dev); } /* diff --git a/sound/core/init.c b/sound/core/init.c index 35419054821c..04734e047bfe 100644 --- a/sound/core/init.c +++ b/sound/core/init.c @@ -400,7 +400,6 @@ static const struct file_operations snd_shutdown_f_ops = int snd_card_disconnect(struct snd_card *card) { struct snd_monitor_file *mfile; - int err; if (!card) return -EINVAL; @@ -445,9 +444,7 @@ int snd_card_disconnect(struct snd_card *card) #endif /* notify all devices that we are disconnected */ - err = snd_device_disconnect_all(card); - if (err < 0) - dev_err(card->dev, "not all devices for card %i can be disconnected\n", card->number); + snd_device_disconnect_all(card); snd_info_card_disconnect(card); if (card->registered) { -- cgit v1.2.3 From 6eb18137643fee5f182d85c818062b4feddfb76b Mon Sep 17 00:00:00 2001 From: Dedy Lansky Date: Sun, 8 Feb 2015 15:52:03 +0200 Subject: cfg80211: add bss_type and privacy arguments in cfg80211_get_bss() 802.11ad adds new a network type (PBSS) and changes the capability field interpretation for the DMG (60G) band. The same 2 bits that were interpreted as "ESS" and "IBSS" before are re-used as a 2-bit field with 3 valid values (and 1 reserved). Valid values are: "IBSS", "PBSS" (new) and "AP". In order to get the BSS struct for the new PBSS networks, change the cfg80211_get_bss() function to take a new enum ieee80211_bss_type argument with the valid network types, as "capa_mask" and "capa_val" no longer work correctly (the search must be band-aware now.) The remaining bits in "capa_mask" and "capa_val" are used only for privacy matching so replace those two with a privacy enum as well. Signed-off-by: Dedy Lansky [rewrite commit log, tiny fixes] Signed-off-by: Johannes Berg --- drivers/net/wireless/ath/ath10k/mac.c | 3 +- drivers/net/wireless/ath/ath6kl/cfg80211.c | 9 +-- drivers/net/wireless/ath/wil6210/cfg80211.c | 2 +- drivers/net/wireless/cw1200/sta.c | 4 +- drivers/net/wireless/libertas/cfg.c | 6 +- drivers/net/wireless/mwifiex/cfg80211.c | 8 +-- include/net/cfg80211.h | 39 ++++++++++++- net/mac80211/ibss.c | 43 ++++----------- net/wireless/ibss.c | 2 +- net/wireless/mlme.c | 6 +- net/wireless/scan.c | 86 ++++++++++++++++++++++++++--- net/wireless/sme.c | 16 ++---- net/wireless/trace.h | 24 ++++---- 13 files changed, 168 insertions(+), 80 deletions(-) (limited to 'include') diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c index d6d2f0f00caa..d372ebfd933d 100644 --- a/drivers/net/wireless/ath/ath10k/mac.c +++ b/drivers/net/wireless/ath/ath10k/mac.c @@ -1386,7 +1386,8 @@ static void ath10k_peer_assoc_h_crypto(struct ath10k *ar, lockdep_assert_held(&ar->conf_mutex); bss = cfg80211_get_bss(ar->hw->wiphy, ar->hw->conf.chandef.chan, - info->bssid, NULL, 0, 0, 0); + info->bssid, NULL, 0, IEEE80211_BSS_TYPE_ANY, + IEEE80211_PRIVACY_ANY); if (bss) { const struct cfg80211_bss_ies *ies; diff --git a/drivers/net/wireless/ath/ath6kl/cfg80211.c b/drivers/net/wireless/ath/ath6kl/cfg80211.c index 85da63a67faf..ff7ba5c195c6 100644 --- a/drivers/net/wireless/ath/ath6kl/cfg80211.c +++ b/drivers/net/wireless/ath/ath6kl/cfg80211.c @@ -686,20 +686,21 @@ ath6kl_add_bss_if_needed(struct ath6kl_vif *vif, { struct ath6kl *ar = vif->ar; struct cfg80211_bss *bss; - u16 cap_mask, cap_val; + u16 cap_val; + enum ieee80211_bss_type bss_type; u8 *ie; if (nw_type & ADHOC_NETWORK) { - cap_mask = WLAN_CAPABILITY_IBSS; cap_val = WLAN_CAPABILITY_IBSS; + bss_type = IEEE80211_BSS_TYPE_IBSS; } else { - cap_mask = WLAN_CAPABILITY_ESS; cap_val = WLAN_CAPABILITY_ESS; + bss_type = IEEE80211_BSS_TYPE_ESS; } bss = cfg80211_get_bss(ar->wiphy, chan, bssid, vif->ssid, vif->ssid_len, - cap_mask, cap_val); + bss_type, IEEE80211_PRIVACY_ANY); if (bss == NULL) { /* * Since cfg80211 may not yet know about the BSS, diff --git a/drivers/net/wireless/ath/wil6210/cfg80211.c b/drivers/net/wireless/ath/wil6210/cfg80211.c index 2d5ea21be47e..adfd815e3f7d 100644 --- a/drivers/net/wireless/ath/wil6210/cfg80211.c +++ b/drivers/net/wireless/ath/wil6210/cfg80211.c @@ -395,7 +395,7 @@ static int wil_cfg80211_connect(struct wiphy *wiphy, bss = cfg80211_get_bss(wiphy, sme->channel, sme->bssid, sme->ssid, sme->ssid_len, - WLAN_CAPABILITY_ESS, WLAN_CAPABILITY_ESS); + IEEE80211_BSS_TYPE_ESS, IEEE80211_PRIVACY_ANY); if (!bss) { wil_err(wil, "Unable to find BSS\n"); return -ENOENT; diff --git a/drivers/net/wireless/cw1200/sta.c b/drivers/net/wireless/cw1200/sta.c index 4a47c7f8a246..1b58b2e2a538 100644 --- a/drivers/net/wireless/cw1200/sta.c +++ b/drivers/net/wireless/cw1200/sta.c @@ -1240,8 +1240,8 @@ static void cw1200_do_join(struct cw1200_common *priv) bssid = priv->vif->bss_conf.bssid; - bss = cfg80211_get_bss(priv->hw->wiphy, priv->channel, - bssid, NULL, 0, 0, 0); + bss = cfg80211_get_bss(priv->hw->wiphy, priv->channel, bssid, NULL, 0, + IEEE80211_BSS_TYPE_ANY, IEEE80211_PRIVACY_ANY); if (!bss && !conf->ibss_joined) { wsm_unlock_tx(priv); diff --git a/drivers/net/wireless/libertas/cfg.c b/drivers/net/wireless/libertas/cfg.c index a92985a6ea21..1a4d558022d8 100644 --- a/drivers/net/wireless/libertas/cfg.c +++ b/drivers/net/wireless/libertas/cfg.c @@ -1356,8 +1356,8 @@ static int lbs_cfg_connect(struct wiphy *wiphy, struct net_device *dev, /* Find the BSS we want using available scan results */ bss = cfg80211_get_bss(wiphy, sme->channel, sme->bssid, - sme->ssid, sme->ssid_len, - WLAN_CAPABILITY_ESS, WLAN_CAPABILITY_ESS); + sme->ssid, sme->ssid_len, IEEE80211_BSS_TYPE_ESS, + IEEE80211_PRIVACY_ANY); if (!bss) { wiphy_err(wiphy, "assoc: bss %pM not in scan results\n", sme->bssid); @@ -2000,7 +2000,7 @@ static int lbs_join_ibss(struct wiphy *wiphy, struct net_device *dev, * bss list is populated already */ bss = cfg80211_get_bss(wiphy, params->chandef.chan, params->bssid, params->ssid, params->ssid_len, - WLAN_CAPABILITY_IBSS, WLAN_CAPABILITY_IBSS); + IEEE80211_BSS_TYPE_IBSS, IEEE80211_PRIVACY_ANY); if (bss) { ret = lbs_ibss_join_existing(priv, params, bss); diff --git a/drivers/net/wireless/mwifiex/cfg80211.c b/drivers/net/wireless/mwifiex/cfg80211.c index 41c8e25df954..a47eb55bb6da 100644 --- a/drivers/net/wireless/mwifiex/cfg80211.c +++ b/drivers/net/wireless/mwifiex/cfg80211.c @@ -1954,13 +1954,13 @@ done: if (mode == NL80211_IFTYPE_ADHOC) bss = cfg80211_get_bss(priv->wdev.wiphy, channel, bssid, ssid, ssid_len, - WLAN_CAPABILITY_IBSS, - WLAN_CAPABILITY_IBSS); + IEEE80211_BSS_TYPE_IBSS, + IEEE80211_PRIVACY_ANY); else bss = cfg80211_get_bss(priv->wdev.wiphy, channel, bssid, ssid, ssid_len, - WLAN_CAPABILITY_ESS, - WLAN_CAPABILITY_ESS); + IEEE80211_BSS_TYPE_ESS, + IEEE80211_PRIVACY_ANY); if (!bss) { if (is_scanning_required) { diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 64e09e1e8099..28fff56f5606 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -214,6 +214,39 @@ enum ieee80211_rate_flags { IEEE80211_RATE_SUPPORTS_10MHZ = 1<<6, }; +/** + * enum ieee80211_bss_type - BSS type filter + * + * @IEEE80211_BSS_TYPE_ESS: Infrastructure BSS + * @IEEE80211_BSS_TYPE_PBSS: Personal BSS + * @IEEE80211_BSS_TYPE_IBSS: Independent BSS + * @IEEE80211_BSS_TYPE_MBSS: Mesh BSS + * @IEEE80211_BSS_TYPE_ANY: Wildcard value for matching any BSS type + */ +enum ieee80211_bss_type { + IEEE80211_BSS_TYPE_ESS, + IEEE80211_BSS_TYPE_PBSS, + IEEE80211_BSS_TYPE_IBSS, + IEEE80211_BSS_TYPE_MBSS, + IEEE80211_BSS_TYPE_ANY +}; + +/** + * enum ieee80211_privacy - BSS privacy filter + * + * @IEEE80211_PRIVACY_ON: privacy bit set + * @IEEE80211_PRIVACY_OFF: privacy bit clear + * @IEEE80211_PRIVACY_ANY: Wildcard value for matching any privacy setting + */ +enum ieee80211_privacy { + IEEE80211_PRIVACY_ON, + IEEE80211_PRIVACY_OFF, + IEEE80211_PRIVACY_ANY +}; + +#define IEEE80211_PRIVACY(x) \ + ((x) ? IEEE80211_PRIVACY_ON : IEEE80211_PRIVACY_OFF) + /** * struct ieee80211_rate - bitrate definition * @@ -4012,14 +4045,16 @@ struct cfg80211_bss *cfg80211_get_bss(struct wiphy *wiphy, struct ieee80211_channel *channel, const u8 *bssid, const u8 *ssid, size_t ssid_len, - u16 capa_mask, u16 capa_val); + enum ieee80211_bss_type bss_type, + enum ieee80211_privacy); static inline struct cfg80211_bss * cfg80211_get_ibss(struct wiphy *wiphy, struct ieee80211_channel *channel, const u8 *ssid, size_t ssid_len) { return cfg80211_get_bss(wiphy, channel, NULL, ssid, ssid_len, - WLAN_CAPABILITY_IBSS, WLAN_CAPABILITY_IBSS); + IEEE80211_BSS_TYPE_IBSS, + IEEE80211_PRIVACY_ANY); } /** diff --git a/net/mac80211/ibss.c b/net/mac80211/ibss.c index b606b53a49a7..ee93d7d9aa4b 100644 --- a/net/mac80211/ibss.c +++ b/net/mac80211/ibss.c @@ -470,22 +470,19 @@ int ieee80211_ibss_csa_beacon(struct ieee80211_sub_if_data *sdata, struct beacon_data *presp, *old_presp; struct cfg80211_bss *cbss; const struct cfg80211_bss_ies *ies; - u16 capability; + u16 capability = 0; u64 tsf; int ret = 0; sdata_assert_lock(sdata); - capability = WLAN_CAPABILITY_IBSS; - if (ifibss->privacy) - capability |= WLAN_CAPABILITY_PRIVACY; + capability = WLAN_CAPABILITY_PRIVACY; cbss = cfg80211_get_bss(sdata->local->hw.wiphy, ifibss->chandef.chan, ifibss->bssid, ifibss->ssid, - ifibss->ssid_len, WLAN_CAPABILITY_IBSS | - WLAN_CAPABILITY_PRIVACY, - capability); + ifibss->ssid_len, IEEE80211_BSS_TYPE_IBSS, + IEEE80211_PRIVACY(ifibss->privacy)); if (WARN_ON(!cbss)) { ret = -EINVAL; @@ -525,23 +522,17 @@ int ieee80211_ibss_finish_csa(struct ieee80211_sub_if_data *sdata) struct ieee80211_if_ibss *ifibss = &sdata->u.ibss; struct cfg80211_bss *cbss; int err, changed = 0; - u16 capability; sdata_assert_lock(sdata); /* update cfg80211 bss information with the new channel */ if (!is_zero_ether_addr(ifibss->bssid)) { - capability = WLAN_CAPABILITY_IBSS; - - if (ifibss->privacy) - capability |= WLAN_CAPABILITY_PRIVACY; - cbss = cfg80211_get_bss(sdata->local->hw.wiphy, ifibss->chandef.chan, ifibss->bssid, ifibss->ssid, - ifibss->ssid_len, WLAN_CAPABILITY_IBSS | - WLAN_CAPABILITY_PRIVACY, - capability); + ifibss->ssid_len, + IEEE80211_BSS_TYPE_IBSS, + IEEE80211_PRIVACY(ifibss->privacy)); /* XXX: should not really modify cfg80211 data */ if (cbss) { cbss->channel = sdata->csa_chandef.chan; @@ -682,19 +673,13 @@ static void ieee80211_ibss_disconnect(struct ieee80211_sub_if_data *sdata) struct cfg80211_bss *cbss; struct beacon_data *presp; struct sta_info *sta; - u16 capability; if (!is_zero_ether_addr(ifibss->bssid)) { - capability = WLAN_CAPABILITY_IBSS; - - if (ifibss->privacy) - capability |= WLAN_CAPABILITY_PRIVACY; - cbss = cfg80211_get_bss(local->hw.wiphy, ifibss->chandef.chan, ifibss->bssid, ifibss->ssid, - ifibss->ssid_len, WLAN_CAPABILITY_IBSS | - WLAN_CAPABILITY_PRIVACY, - capability); + ifibss->ssid_len, + IEEE80211_BSS_TYPE_IBSS, + IEEE80211_PRIVACY(ifibss->privacy)); if (cbss) { cfg80211_unlink_bss(local->hw.wiphy, cbss); @@ -1325,7 +1310,6 @@ static void ieee80211_sta_find_ibss(struct ieee80211_sub_if_data *sdata) const u8 *bssid = NULL; enum nl80211_bss_scan_width scan_width; int active_ibss; - u16 capability; sdata_assert_lock(sdata); @@ -1335,9 +1319,6 @@ static void ieee80211_sta_find_ibss(struct ieee80211_sub_if_data *sdata) if (active_ibss) return; - capability = WLAN_CAPABILITY_IBSS; - if (ifibss->privacy) - capability |= WLAN_CAPABILITY_PRIVACY; if (ifibss->fixed_bssid) bssid = ifibss->bssid; if (ifibss->fixed_channel) @@ -1346,8 +1327,8 @@ static void ieee80211_sta_find_ibss(struct ieee80211_sub_if_data *sdata) bssid = ifibss->bssid; cbss = cfg80211_get_bss(local->hw.wiphy, chan, bssid, ifibss->ssid, ifibss->ssid_len, - WLAN_CAPABILITY_IBSS | WLAN_CAPABILITY_PRIVACY, - capability); + IEEE80211_BSS_TYPE_IBSS, + IEEE80211_PRIVACY(ifibss->privacy)); if (cbss) { struct ieee80211_bss *bss; diff --git a/net/wireless/ibss.c b/net/wireless/ibss.c index e24fc585c883..1a65662a5d73 100644 --- a/net/wireless/ibss.c +++ b/net/wireless/ibss.c @@ -30,7 +30,7 @@ void __cfg80211_ibss_joined(struct net_device *dev, const u8 *bssid, return; bss = cfg80211_get_bss(wdev->wiphy, channel, bssid, NULL, 0, - WLAN_CAPABILITY_IBSS, WLAN_CAPABILITY_IBSS); + IEEE80211_BSS_TYPE_IBSS, IEEE80211_PRIVACY_ANY); if (WARN_ON(!bss)) return; diff --git a/net/wireless/mlme.c b/net/wireless/mlme.c index 2c52b59e43f3..7aae329e2b4e 100644 --- a/net/wireless/mlme.c +++ b/net/wireless/mlme.c @@ -229,7 +229,8 @@ int cfg80211_mlme_auth(struct cfg80211_registered_device *rdev, return -EALREADY; req.bss = cfg80211_get_bss(&rdev->wiphy, chan, bssid, ssid, ssid_len, - WLAN_CAPABILITY_ESS, WLAN_CAPABILITY_ESS); + IEEE80211_BSS_TYPE_ESS, + IEEE80211_PRIVACY_ANY); if (!req.bss) return -ENOENT; @@ -296,7 +297,8 @@ int cfg80211_mlme_assoc(struct cfg80211_registered_device *rdev, rdev->wiphy.vht_capa_mod_mask); req->bss = cfg80211_get_bss(&rdev->wiphy, chan, bssid, ssid, ssid_len, - WLAN_CAPABILITY_ESS, WLAN_CAPABILITY_ESS); + IEEE80211_BSS_TYPE_ESS, + IEEE80211_PRIVACY_ANY); if (!req->bss) return -ENOENT; diff --git a/net/wireless/scan.c b/net/wireless/scan.c index ceb8f0040dae..3a50aa2553bf 100644 --- a/net/wireless/scan.c +++ b/net/wireless/scan.c @@ -531,24 +531,78 @@ static int cmp_bss(struct cfg80211_bss *a, } } +static bool cfg80211_bss_type_match(u16 capability, + enum ieee80211_band band, + enum ieee80211_bss_type bss_type) +{ + bool ret = true; + u16 mask, val; + + if (bss_type == IEEE80211_BSS_TYPE_ANY) + return ret; + + if (band == IEEE80211_BAND_60GHZ) { + mask = WLAN_CAPABILITY_DMG_TYPE_MASK; + switch (bss_type) { + case IEEE80211_BSS_TYPE_ESS: + val = WLAN_CAPABILITY_DMG_TYPE_AP; + break; + case IEEE80211_BSS_TYPE_PBSS: + val = WLAN_CAPABILITY_DMG_TYPE_PBSS; + break; + case IEEE80211_BSS_TYPE_IBSS: + val = WLAN_CAPABILITY_DMG_TYPE_IBSS; + break; + default: + return false; + } + } else { + mask = WLAN_CAPABILITY_ESS | WLAN_CAPABILITY_IBSS; + switch (bss_type) { + case IEEE80211_BSS_TYPE_ESS: + val = WLAN_CAPABILITY_ESS; + break; + case IEEE80211_BSS_TYPE_IBSS: + val = WLAN_CAPABILITY_IBSS; + break; + case IEEE80211_BSS_TYPE_MBSS: + val = 0; + break; + default: + return false; + } + } + + ret = ((capability & mask) == val); + return ret; +} + /* Returned bss is reference counted and must be cleaned up appropriately. */ struct cfg80211_bss *cfg80211_get_bss(struct wiphy *wiphy, struct ieee80211_channel *channel, const u8 *bssid, const u8 *ssid, size_t ssid_len, - u16 capa_mask, u16 capa_val) + enum ieee80211_bss_type bss_type, + enum ieee80211_privacy privacy) { struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy); struct cfg80211_internal_bss *bss, *res = NULL; unsigned long now = jiffies; + int bss_privacy; - trace_cfg80211_get_bss(wiphy, channel, bssid, ssid, ssid_len, capa_mask, - capa_val); + trace_cfg80211_get_bss(wiphy, channel, bssid, ssid, ssid_len, bss_type, + privacy); spin_lock_bh(&rdev->bss_lock); list_for_each_entry(bss, &rdev->bss_list, list) { - if ((bss->pub.capability & capa_mask) != capa_val) + if (!cfg80211_bss_type_match(bss->pub.capability, + bss->pub.channel->band, bss_type)) + continue; + + bss_privacy = (bss->pub.capability & WLAN_CAPABILITY_PRIVACY); + if ((privacy == IEEE80211_PRIVACY_ON && !bss_privacy) || + (privacy == IEEE80211_PRIVACY_OFF && bss_privacy)) continue; if (channel && bss->pub.channel != channel) continue; @@ -896,6 +950,7 @@ cfg80211_inform_bss_width(struct wiphy *wiphy, struct cfg80211_bss_ies *ies; struct ieee80211_channel *channel; struct cfg80211_internal_bss tmp = {}, *res; + int bss_type; bool signal_valid; if (WARN_ON(!wiphy)) @@ -950,8 +1005,15 @@ cfg80211_inform_bss_width(struct wiphy *wiphy, if (!res) return NULL; - if (res->pub.capability & WLAN_CAPABILITY_ESS) - regulatory_hint_found_beacon(wiphy, channel, gfp); + if (channel->band == IEEE80211_BAND_60GHZ) { + bss_type = res->pub.capability & WLAN_CAPABILITY_DMG_TYPE_MASK; + if (bss_type == WLAN_CAPABILITY_DMG_TYPE_AP || + bss_type == WLAN_CAPABILITY_DMG_TYPE_PBSS) + regulatory_hint_found_beacon(wiphy, channel, gfp); + } else { + if (res->pub.capability & WLAN_CAPABILITY_ESS) + regulatory_hint_found_beacon(wiphy, channel, gfp); + } trace_cfg80211_return_bss(&res->pub); /* cfg80211_bss_update gives us a referenced result */ @@ -973,6 +1035,7 @@ cfg80211_inform_bss_width_frame(struct wiphy *wiphy, bool signal_valid; size_t ielen = len - offsetof(struct ieee80211_mgmt, u.probe_resp.variable); + int bss_type; BUILD_BUG_ON(offsetof(struct ieee80211_mgmt, u.probe_resp.variable) != offsetof(struct ieee80211_mgmt, u.beacon.variable)); @@ -1025,8 +1088,15 @@ cfg80211_inform_bss_width_frame(struct wiphy *wiphy, if (!res) return NULL; - if (res->pub.capability & WLAN_CAPABILITY_ESS) - regulatory_hint_found_beacon(wiphy, channel, gfp); + if (channel->band == IEEE80211_BAND_60GHZ) { + bss_type = res->pub.capability & WLAN_CAPABILITY_DMG_TYPE_MASK; + if (bss_type == WLAN_CAPABILITY_DMG_TYPE_AP || + bss_type == WLAN_CAPABILITY_DMG_TYPE_PBSS) + regulatory_hint_found_beacon(wiphy, channel, gfp); + } else { + if (res->pub.capability & WLAN_CAPABILITY_ESS) + regulatory_hint_found_beacon(wiphy, channel, gfp); + } trace_cfg80211_return_bss(&res->pub); /* cfg80211_bss_update gives us a referenced result */ diff --git a/net/wireless/sme.c b/net/wireless/sme.c index 0ab3711c79a0..ea1da6621ff0 100644 --- a/net/wireless/sme.c +++ b/net/wireless/sme.c @@ -257,19 +257,15 @@ static struct cfg80211_bss *cfg80211_get_conn_bss(struct wireless_dev *wdev) { struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy); struct cfg80211_bss *bss; - u16 capa = WLAN_CAPABILITY_ESS; ASSERT_WDEV_LOCK(wdev); - if (wdev->conn->params.privacy) - capa |= WLAN_CAPABILITY_PRIVACY; - bss = cfg80211_get_bss(wdev->wiphy, wdev->conn->params.channel, wdev->conn->params.bssid, wdev->conn->params.ssid, wdev->conn->params.ssid_len, - WLAN_CAPABILITY_ESS | WLAN_CAPABILITY_PRIVACY, - capa); + IEEE80211_BSS_TYPE_ESS, + IEEE80211_PRIVACY(wdev->conn->params.privacy)); if (!bss) return NULL; @@ -637,8 +633,8 @@ void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid, WARN_ON_ONCE(!wiphy_to_rdev(wdev->wiphy)->ops->connect); bss = cfg80211_get_bss(wdev->wiphy, NULL, bssid, wdev->ssid, wdev->ssid_len, - WLAN_CAPABILITY_ESS, - WLAN_CAPABILITY_ESS); + IEEE80211_BSS_TYPE_ESS, + IEEE80211_PRIVACY_ANY); if (bss) cfg80211_hold_bss(bss_from_pub(bss)); } @@ -795,8 +791,8 @@ void cfg80211_roamed(struct net_device *dev, struct cfg80211_bss *bss; bss = cfg80211_get_bss(wdev->wiphy, channel, bssid, wdev->ssid, - wdev->ssid_len, WLAN_CAPABILITY_ESS, - WLAN_CAPABILITY_ESS); + wdev->ssid_len, + IEEE80211_BSS_TYPE_ESS, IEEE80211_PRIVACY_ANY); if (WARN_ON(!bss)) return; diff --git a/net/wireless/trace.h b/net/wireless/trace.h index b17b3692f8c2..b19773c9c81b 100644 --- a/net/wireless/trace.h +++ b/net/wireless/trace.h @@ -2636,28 +2636,30 @@ DEFINE_EVENT(wiphy_only_evt, cfg80211_sched_scan_stopped, TRACE_EVENT(cfg80211_get_bss, TP_PROTO(struct wiphy *wiphy, struct ieee80211_channel *channel, const u8 *bssid, const u8 *ssid, size_t ssid_len, - u16 capa_mask, u16 capa_val), - TP_ARGS(wiphy, channel, bssid, ssid, ssid_len, capa_mask, capa_val), + enum ieee80211_bss_type bss_type, + enum ieee80211_privacy privacy), + TP_ARGS(wiphy, channel, bssid, ssid, ssid_len, bss_type, privacy), TP_STRUCT__entry( WIPHY_ENTRY CHAN_ENTRY MAC_ENTRY(bssid) __dynamic_array(u8, ssid, ssid_len) - __field(u16, capa_mask) - __field(u16, capa_val) + __field(enum ieee80211_bss_type, bss_type) + __field(enum ieee80211_privacy, privacy) ), TP_fast_assign( WIPHY_ASSIGN; CHAN_ASSIGN(channel); MAC_ASSIGN(bssid, bssid); memcpy(__get_dynamic_array(ssid), ssid, ssid_len); - __entry->capa_mask = capa_mask; - __entry->capa_val = capa_val; - ), - TP_printk(WIPHY_PR_FMT ", " CHAN_PR_FMT ", " MAC_PR_FMT ", buf: %#.2x, " - "capa_mask: %d, capa_val: %u", WIPHY_PR_ARG, CHAN_PR_ARG, - MAC_PR_ARG(bssid), ((u8 *)__get_dynamic_array(ssid))[0], - __entry->capa_mask, __entry->capa_val) + __entry->bss_type = bss_type; + __entry->privacy = privacy; + ), + TP_printk(WIPHY_PR_FMT ", " CHAN_PR_FMT ", " MAC_PR_FMT + ", buf: %#.2x, bss_type: %d, privacy: %d", + WIPHY_PR_ARG, CHAN_PR_ARG, MAC_PR_ARG(bssid), + ((u8 *)__get_dynamic_array(ssid))[0], __entry->bss_type, + __entry->privacy) ); TRACE_EVENT(cfg80211_inform_bss_width_frame, -- cgit v1.2.3 From ffc1199122d83d60ad99f9c55df32feb650b7bff Mon Sep 17 00:00:00 2001 From: "Janusz.Dziedzic@tieto.com" Date: Sat, 21 Feb 2015 16:52:39 +0100 Subject: cfg80211: add VHT support for IBSS Add NL80211_EXT_FEATURE_VHT_IBSS flag and VHT support for IBSS. Signed-off-by: Janusz Dziedzic Signed-off-by: Johannes Berg --- include/uapi/linux/nl80211.h | 2 ++ net/wireless/nl80211.c | 14 ++++++++++++-- 2 files changed, 14 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index 2dcf9bba317c..8ee31f108407 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h @@ -4328,11 +4328,13 @@ enum nl80211_feature_flags { /** * enum nl80211_ext_feature_index - bit index of extended features. + * @NL80211_EXT_FEATURE_VHT_IBSS: This driver supports IBSS with VHT datarates. * * @NUM_NL80211_EXT_FEATURES: number of extended features. * @MAX_NL80211_EXT_FEATURES: highest extended feature index. */ enum nl80211_ext_feature_index { + NL80211_EXT_FEATURE_VHT_IBSS, /* add new features before the definition below */ NUM_NL80211_EXT_FEATURES, diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 9c6e23ede5b2..66666fdf1c8d 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -7265,8 +7265,18 @@ static int nl80211_join_ibss(struct sk_buff *skb, struct genl_info *info) break; case NL80211_CHAN_WIDTH_20: case NL80211_CHAN_WIDTH_40: - if (rdev->wiphy.features & NL80211_FEATURE_HT_IBSS) - break; + if (!(rdev->wiphy.features & NL80211_FEATURE_HT_IBSS)) + return -EINVAL; + break; + case NL80211_CHAN_WIDTH_80: + case NL80211_CHAN_WIDTH_80P80: + case NL80211_CHAN_WIDTH_160: + if (!(rdev->wiphy.features & NL80211_FEATURE_HT_IBSS)) + return -EINVAL; + if (!wiphy_ext_feature_isset(&rdev->wiphy, + NL80211_EXT_FEATURE_VHT_IBSS)) + return -EINVAL; + break; default: return -EINVAL; } -- cgit v1.2.3 From 6c09e791b21309a1ad71f9702b766dae12a3cb0a Mon Sep 17 00:00:00 2001 From: Ahmad Kholaif Date: Thu, 26 Feb 2015 15:26:53 +0200 Subject: cfg80211: Allow NL80211_ATTR_IFINDEX to be added to vendor events This modifies cfg80211_vendor_event_alloc() with an additional argument struct wireless_dev *wdev. __cfg80211_alloc_event_skb() is modified to take in *wdev argument, if wdev != NULL, both the NL80211_ATTR_IFINDEX and wdev identifier are added to the vendor event. These changes make it easier for drivers to add ifindex indication in vendor events cleanly. This also updates all existing users of cfg80211_vendor_event_alloc() and __cfg80211_alloc_event_skb() in the kernel tree. Signed-off-by: Ahmad Kholaif Signed-off-by: Jouni Malinen Signed-off-by: Johannes Berg --- drivers/net/wireless/ti/wl18xx/event.c | 4 ++-- include/net/cfg80211.h | 14 ++++++++++---- net/wireless/nl80211.c | 19 +++++++++++++++---- 3 files changed, 27 insertions(+), 10 deletions(-) (limited to 'include') diff --git a/drivers/net/wireless/ti/wl18xx/event.c b/drivers/net/wireless/ti/wl18xx/event.c index c28f06854195..548bb9e7e91e 100644 --- a/drivers/net/wireless/ti/wl18xx/event.c +++ b/drivers/net/wireless/ti/wl18xx/event.c @@ -77,7 +77,7 @@ static int wlcore_smart_config_sync_event(struct wl1271 *wl, u8 sync_channel, wl1271_debug(DEBUG_EVENT, "SMART_CONFIG_SYNC_EVENT_ID, freq: %d (chan: %d band %d)", freq, sync_channel, sync_band); - skb = cfg80211_vendor_event_alloc(wl->hw->wiphy, 20, + skb = cfg80211_vendor_event_alloc(wl->hw->wiphy, NULL, 20, WLCORE_VENDOR_EVENT_SC_SYNC, GFP_KERNEL); @@ -98,7 +98,7 @@ static int wlcore_smart_config_decode_event(struct wl1271 *wl, wl1271_debug(DEBUG_EVENT, "SMART_CONFIG_DECODE_EVENT_ID"); wl1271_dump_ascii(DEBUG_EVENT, "SSID:", ssid, ssid_len); - skb = cfg80211_vendor_event_alloc(wl->hw->wiphy, + skb = cfg80211_vendor_event_alloc(wl->hw->wiphy, NULL, ssid_len + pwd_len + 20, WLCORE_VENDOR_EVENT_SC_DECODE, GFP_KERNEL); diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 28fff56f5606..12a6121ea76e 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -4295,6 +4295,7 @@ struct sk_buff *__cfg80211_alloc_reply_skb(struct wiphy *wiphy, int approxlen); struct sk_buff *__cfg80211_alloc_event_skb(struct wiphy *wiphy, + struct wireless_dev *wdev, enum nl80211_commands cmd, enum nl80211_attrs attr, int vendor_event_idx, @@ -4349,6 +4350,7 @@ int cfg80211_vendor_cmd_reply(struct sk_buff *skb); /** * cfg80211_vendor_event_alloc - allocate vendor-specific event skb * @wiphy: the wiphy + * @wdev: the wireless device * @event_idx: index of the vendor event in the wiphy's vendor_events * @approxlen: an upper bound of the length of the data that will * be put into the skb @@ -4357,16 +4359,20 @@ int cfg80211_vendor_cmd_reply(struct sk_buff *skb); * This function allocates and pre-fills an skb for an event on the * vendor-specific multicast group. * + * If wdev != NULL, both the ifindex and identifier of the specified + * wireless device are added to the event message before the vendor data + * attribute. + * * When done filling the skb, call cfg80211_vendor_event() with the * skb to send the event. * * Return: An allocated and pre-filled skb. %NULL if any errors happen. */ static inline struct sk_buff * -cfg80211_vendor_event_alloc(struct wiphy *wiphy, int approxlen, - int event_idx, gfp_t gfp) +cfg80211_vendor_event_alloc(struct wiphy *wiphy, struct wireless_dev *wdev, + int approxlen, int event_idx, gfp_t gfp) { - return __cfg80211_alloc_event_skb(wiphy, NL80211_CMD_VENDOR, + return __cfg80211_alloc_event_skb(wiphy, wdev, NL80211_CMD_VENDOR, NL80211_ATTR_VENDOR_DATA, event_idx, approxlen, gfp); } @@ -4467,7 +4473,7 @@ static inline int cfg80211_testmode_reply(struct sk_buff *skb) static inline struct sk_buff * cfg80211_testmode_alloc_event_skb(struct wiphy *wiphy, int approxlen, gfp_t gfp) { - return __cfg80211_alloc_event_skb(wiphy, NL80211_CMD_TESTMODE, + return __cfg80211_alloc_event_skb(wiphy, NULL, NL80211_CMD_TESTMODE, NL80211_ATTR_TESTDATA, -1, approxlen, gfp); } diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 66666fdf1c8d..01874628ae00 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -7389,8 +7389,8 @@ static int nl80211_set_mcast_rate(struct sk_buff *skb, struct genl_info *info) static struct sk_buff * __cfg80211_alloc_vendor_skb(struct cfg80211_registered_device *rdev, - int approxlen, u32 portid, u32 seq, - enum nl80211_commands cmd, + struct wireless_dev *wdev, int approxlen, + u32 portid, u32 seq, enum nl80211_commands cmd, enum nl80211_attrs attr, const struct nl80211_vendor_cmd_info *info, gfp_t gfp) @@ -7421,6 +7421,16 @@ __cfg80211_alloc_vendor_skb(struct cfg80211_registered_device *rdev, goto nla_put_failure; } + if (wdev) { + if (nla_put_u64(skb, NL80211_ATTR_WDEV, + wdev_id(wdev))) + goto nla_put_failure; + if (wdev->netdev && + nla_put_u32(skb, NL80211_ATTR_IFINDEX, + wdev->netdev->ifindex)) + goto nla_put_failure; + } + data = nla_nest_start(skb, attr); ((void **)skb->cb)[0] = rdev; @@ -7435,6 +7445,7 @@ __cfg80211_alloc_vendor_skb(struct cfg80211_registered_device *rdev, } struct sk_buff *__cfg80211_alloc_event_skb(struct wiphy *wiphy, + struct wireless_dev *wdev, enum nl80211_commands cmd, enum nl80211_attrs attr, int vendor_event_idx, @@ -7460,7 +7471,7 @@ struct sk_buff *__cfg80211_alloc_event_skb(struct wiphy *wiphy, return NULL; } - return __cfg80211_alloc_vendor_skb(rdev, approxlen, 0, 0, + return __cfg80211_alloc_vendor_skb(rdev, wdev, approxlen, 0, 0, cmd, attr, info, gfp); } EXPORT_SYMBOL(__cfg80211_alloc_event_skb); @@ -9906,7 +9917,7 @@ struct sk_buff *__cfg80211_alloc_reply_skb(struct wiphy *wiphy, if (WARN_ON(!rdev->cur_cmd_info)) return NULL; - return __cfg80211_alloc_vendor_skb(rdev, approxlen, + return __cfg80211_alloc_vendor_skb(rdev, NULL, approxlen, rdev->cur_cmd_info->snd_portid, rdev->cur_cmd_info->snd_seq, cmd, attr, NULL, GFP_KERNEL); -- cgit v1.2.3 From 5fc7432991a86678b38a2d700edbe8bcd29cc579 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 27 Feb 2015 15:32:43 +0100 Subject: nl80211: add notes about userspace API/ABI modifications Add notes about userspace ABI/API modifications, including the fact that we decided that API submissions should come with a driver implementation. Signed-off-by: Johannes Berg --- include/uapi/linux/nl80211.h | 13 +++++++++++++ 1 file changed, 13 insertions(+) (limited to 'include') diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index 8ee31f108407..90c5aeb3cca7 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h @@ -25,6 +25,19 @@ * */ +/* + * This header file defines the userspace API to the wireless stack. Please + * be careful not to break things - i.e. don't move anything around or so + * unless you can demonstrate that it breaks neither API nor ABI. + * + * Additions to the API should be accompanied by actual implementations in + * an upstream driver, so that example implementations exist in case there + * are ever concerns about the precise semantics of the API or changes are + * needed, and to ensure that code for dead (no longer implemented) API + * can actually be identified and removed. + * Nonetheless, semantics should also be documented carefully in this file. + */ + #include #define NL80211_GENL_NAME "nl80211" -- cgit v1.2.3 From 2ecc3905e6c51f545a44cc621216b5dfd7f94c50 Mon Sep 17 00:00:00 2001 From: Alexander Bondar Date: Sun, 1 Mar 2015 09:10:00 +0200 Subject: mac80211: Update beacon's timing and DTIM count on every beacon Beacon's timestamp, device system time associated with this beacon and DTIM count parameters are not updated in the associated vif context if the latest beacon's content is identical to the previously received. It make sense to update these changing parameters on every beacon so the driver can get most updated values. This may be necessary, for example, to avoid either beacons' drift effect or device time stamp overrun. IMPORTANT: Three sync_* parameters - sync_ts, sync_device_ts and sync_dtim_count would possibly be out of sync by the time the driver will use them. The synchronized view is currently guaranteed only in certain callbacks. Signed-off-by: Alexander Bondar Signed-off-by: Emmanuel Grumbach Signed-off-by: Johannes Berg --- include/net/mac80211.h | 7 +++++-- net/mac80211/mlme.c | 32 ++++++++++++++++++++------------ 2 files changed, 25 insertions(+), 14 deletions(-) (limited to 'include') diff --git a/include/net/mac80211.h b/include/net/mac80211.h index d52914b75331..3a029f0e303d 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -337,12 +337,15 @@ enum ieee80211_rssi_event { * HW flag %IEEE80211_HW_TIMING_BEACON_ONLY is set, then this can * only come from a beacon, but might not become valid until after * association when a beacon is received (which is notified with the - * %BSS_CHANGED_DTIM flag.) + * %BSS_CHANGED_DTIM flag.). See also sync_dtim_count important notice. * @sync_device_ts: the device timestamp corresponding to the sync_tsf, * the driver/device can use this to calculate synchronisation - * (see @sync_tsf) + * (see @sync_tsf). See also sync_dtim_count important notice. * @sync_dtim_count: Only valid when %IEEE80211_HW_TIMING_BEACON_ONLY * is requested, see @sync_tsf/@sync_device_ts. + * IMPORTANT: These three sync_* parameters would possibly be out of sync + * by the time the driver will use them. The synchronized view is currently + * guaranteed only in certain callbacks. * @beacon_int: beacon interval * @assoc_capability: capabilities taken from assoc resp * @basic_rates: bitmap of basic rates, each bit stands for an diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 10ac6324c1d0..cf3ae9348a9d 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -3419,6 +3419,26 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata, if (ifmgd->csa_waiting_bcn) ieee80211_chswitch_post_beacon(sdata); + /* + * Update beacon timing and dtim count on every beacon appearance. This + * will allow the driver to use the most updated values. Do it before + * comparing this one with last received beacon. + * IMPORTANT: These parameters would possibly be out of sync by the time + * the driver will use them. The synchronized view is currently + * guaranteed only in certain callbacks. + */ + if (local->hw.flags & IEEE80211_HW_TIMING_BEACON_ONLY) { + sdata->vif.bss_conf.sync_tsf = + le64_to_cpu(mgmt->u.beacon.timestamp); + sdata->vif.bss_conf.sync_device_ts = + rx_status->device_timestamp; + if (elems.tim) + sdata->vif.bss_conf.sync_dtim_count = + elems.tim->dtim_count; + else + sdata->vif.bss_conf.sync_dtim_count = 0; + } + if (ncrc == ifmgd->beacon_crc && ifmgd->beacon_crc_valid) return; ifmgd->beacon_crc = ncrc; @@ -3446,18 +3466,6 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata, else bss_conf->dtim_period = 1; - if (local->hw.flags & IEEE80211_HW_TIMING_BEACON_ONLY) { - sdata->vif.bss_conf.sync_tsf = - le64_to_cpu(mgmt->u.beacon.timestamp); - sdata->vif.bss_conf.sync_device_ts = - rx_status->device_timestamp; - if (elems.tim) - sdata->vif.bss_conf.sync_dtim_count = - elems.tim->dtim_count; - else - sdata->vif.bss_conf.sync_dtim_count = 0; - } - changed |= BSS_CHANGED_BEACON_INFO; ifmgd->have_beacon = true; -- cgit v1.2.3 From b10848e6f9fa7638fc0713695a12c0735ffb52b7 Mon Sep 17 00:00:00 2001 From: Vignesh R Date: Wed, 7 Jan 2015 11:19:36 +0530 Subject: mfd: ti_am335x_tscadc: Remove unwanted reg_se_cache save In one shot mode, sequencer automatically disables all enabled steps at the end of each cycle. (both ADC steps and TSC steps) Hence these steps need not be saved in reg_se_cache for clearing these steps at a later stage. Also, when ADC wakes up Sequencer should not be busy executing any of the config steps except for the charge step. Previously charge step was 1 ADC clock cycle and hence it was ignored. TSC steps are always disabled at the end of each conversion cycle, hence there is no need to explicitly disable TSC steps by writing 0 to REG_SE. Signed-off-by: Vignesh R Signed-off-by: Lee Jones --- drivers/mfd/ti_am335x_tscadc.c | 13 +++++-------- include/linux/mfd/ti_am335x_tscadc.h | 1 + 2 files changed, 6 insertions(+), 8 deletions(-) (limited to 'include') diff --git a/drivers/mfd/ti_am335x_tscadc.c b/drivers/mfd/ti_am335x_tscadc.c index 467c80e1c4ae..e4e4b22eebc9 100644 --- a/drivers/mfd/ti_am335x_tscadc.c +++ b/drivers/mfd/ti_am335x_tscadc.c @@ -68,12 +68,6 @@ static void am335x_tscadc_need_adc(struct ti_tscadc_dev *tsadc) DEFINE_WAIT(wait); u32 reg; - /* - * disable TSC steps so it does not run while the ADC is using it. If - * write 0 while it is running (it just started or was already running) - * then it completes all steps that were enabled and stops then. - */ - tscadc_writel(tsadc, REG_SE, 0); reg = tscadc_readl(tsadc, REG_ADCFSM); if (reg & SEQ_STATUS) { tsadc->adc_waiting = true; @@ -86,8 +80,12 @@ static void am335x_tscadc_need_adc(struct ti_tscadc_dev *tsadc) spin_lock_irq(&tsadc->reg_lock); finish_wait(&tsadc->reg_se_wait, &wait); + /* + * Sequencer should either be idle or + * busy applying the charge step. + */ reg = tscadc_readl(tsadc, REG_ADCFSM); - WARN_ON(reg & SEQ_STATUS); + WARN_ON((reg & SEQ_STATUS) && !(reg & CHARGE_STEP)); tsadc->adc_waiting = false; } tsadc->adc_in_use = true; @@ -96,7 +94,6 @@ static void am335x_tscadc_need_adc(struct ti_tscadc_dev *tsadc) void am335x_tsc_se_set_once(struct ti_tscadc_dev *tsadc, u32 val) { spin_lock_irq(&tsadc->reg_lock); - tsadc->reg_se_cache |= val; am335x_tscadc_need_adc(tsadc); tscadc_writel(tsadc, REG_SE, val); diff --git a/include/linux/mfd/ti_am335x_tscadc.h b/include/linux/mfd/ti_am335x_tscadc.h index 3f4e994ace2b..1fd50dcfe47c 100644 --- a/include/linux/mfd/ti_am335x_tscadc.h +++ b/include/linux/mfd/ti_am335x_tscadc.h @@ -128,6 +128,7 @@ /* Sequencer Status */ #define SEQ_STATUS BIT(5) +#define CHARGE_STEP 0x11 #define ADC_CLK 3000000 #define TOTAL_STEPS 16 -- cgit v1.2.3 From 84f00b1b9631319361f6c36e2f5a8e833d09af5b Mon Sep 17 00:00:00 2001 From: Micky Ching Date: Wed, 25 Feb 2015 13:50:08 +0800 Subject: mfd: rtsx: Replace TAB by SPC after #define Re-format coding-style, using uniform SPC after "#define" keyword instead of mixing using TAB and SPC. Signed-off-by: Micky Ching Signed-off-by: Lee Jones --- include/linux/mfd/rtsx_pci.h | 254 +++++++++++++++++++++---------------------- 1 file changed, 127 insertions(+), 127 deletions(-) (limited to 'include') diff --git a/include/linux/mfd/rtsx_pci.h b/include/linux/mfd/rtsx_pci.h index 0c12628e91c6..a9c2a14fd521 100644 --- a/include/linux/mfd/rtsx_pci.h +++ b/include/linux/mfd/rtsx_pci.h @@ -175,9 +175,9 @@ /* CARD_SHARE_MODE */ #define CARD_SHARE_MASK 0x0F #define CARD_SHARE_MULTI_LUN 0x00 -#define CARD_SHARE_NORMAL 0x00 -#define CARD_SHARE_48_SD 0x04 -#define CARD_SHARE_48_MS 0x08 +#define CARD_SHARE_NORMAL 0x00 +#define CARD_SHARE_48_SD 0x04 +#define CARD_SHARE_48_MS 0x08 /* CARD_SHARE_MODE for barossa */ #define CARD_SHARE_BAROSSA_SD 0x01 #define CARD_SHARE_BAROSSA_MS 0x02 @@ -249,76 +249,76 @@ #define CD_AUTO_DISABLE 0x40 /* SD_STAT1 */ -#define SD_CRC7_ERR 0x80 -#define SD_CRC16_ERR 0x40 -#define SD_CRC_WRITE_ERR 0x20 -#define SD_CRC_WRITE_ERR_MASK 0x1C -#define GET_CRC_TIME_OUT 0x02 -#define SD_TUNING_COMPARE_ERR 0x01 +#define SD_CRC7_ERR 0x80 +#define SD_CRC16_ERR 0x40 +#define SD_CRC_WRITE_ERR 0x20 +#define SD_CRC_WRITE_ERR_MASK 0x1C +#define GET_CRC_TIME_OUT 0x02 +#define SD_TUNING_COMPARE_ERR 0x01 /* SD_STAT2 */ -#define SD_RSP_80CLK_TIMEOUT 0x01 +#define SD_RSP_80CLK_TIMEOUT 0x01 /* SD_BUS_STAT */ -#define SD_CLK_TOGGLE_EN 0x80 -#define SD_CLK_FORCE_STOP 0x40 -#define SD_DAT3_STATUS 0x10 -#define SD_DAT2_STATUS 0x08 -#define SD_DAT1_STATUS 0x04 -#define SD_DAT0_STATUS 0x02 -#define SD_CMD_STATUS 0x01 +#define SD_CLK_TOGGLE_EN 0x80 +#define SD_CLK_FORCE_STOP 0x40 +#define SD_DAT3_STATUS 0x10 +#define SD_DAT2_STATUS 0x08 +#define SD_DAT1_STATUS 0x04 +#define SD_DAT0_STATUS 0x02 +#define SD_CMD_STATUS 0x01 /* SD_PAD_CTL */ -#define SD_IO_USING_1V8 0x80 -#define SD_IO_USING_3V3 0x7F -#define TYPE_A_DRIVING 0x00 -#define TYPE_B_DRIVING 0x01 -#define TYPE_C_DRIVING 0x02 -#define TYPE_D_DRIVING 0x03 +#define SD_IO_USING_1V8 0x80 +#define SD_IO_USING_3V3 0x7F +#define TYPE_A_DRIVING 0x00 +#define TYPE_B_DRIVING 0x01 +#define TYPE_C_DRIVING 0x02 +#define TYPE_D_DRIVING 0x03 /* SD_SAMPLE_POINT_CTL */ -#define DDR_FIX_RX_DAT 0x00 -#define DDR_VAR_RX_DAT 0x80 -#define DDR_FIX_RX_DAT_EDGE 0x00 -#define DDR_FIX_RX_DAT_14_DELAY 0x40 -#define DDR_FIX_RX_CMD 0x00 -#define DDR_VAR_RX_CMD 0x20 -#define DDR_FIX_RX_CMD_POS_EDGE 0x00 -#define DDR_FIX_RX_CMD_14_DELAY 0x10 -#define SD20_RX_POS_EDGE 0x00 -#define SD20_RX_14_DELAY 0x08 +#define DDR_FIX_RX_DAT 0x00 +#define DDR_VAR_RX_DAT 0x80 +#define DDR_FIX_RX_DAT_EDGE 0x00 +#define DDR_FIX_RX_DAT_14_DELAY 0x40 +#define DDR_FIX_RX_CMD 0x00 +#define DDR_VAR_RX_CMD 0x20 +#define DDR_FIX_RX_CMD_POS_EDGE 0x00 +#define DDR_FIX_RX_CMD_14_DELAY 0x10 +#define SD20_RX_POS_EDGE 0x00 +#define SD20_RX_14_DELAY 0x08 #define SD20_RX_SEL_MASK 0x08 /* SD_PUSH_POINT_CTL */ -#define DDR_FIX_TX_CMD_DAT 0x00 -#define DDR_VAR_TX_CMD_DAT 0x80 -#define DDR_FIX_TX_DAT_14_TSU 0x00 -#define DDR_FIX_TX_DAT_12_TSU 0x40 -#define DDR_FIX_TX_CMD_NEG_EDGE 0x00 -#define DDR_FIX_TX_CMD_14_AHEAD 0x20 -#define SD20_TX_NEG_EDGE 0x00 -#define SD20_TX_14_AHEAD 0x10 +#define DDR_FIX_TX_CMD_DAT 0x00 +#define DDR_VAR_TX_CMD_DAT 0x80 +#define DDR_FIX_TX_DAT_14_TSU 0x00 +#define DDR_FIX_TX_DAT_12_TSU 0x40 +#define DDR_FIX_TX_CMD_NEG_EDGE 0x00 +#define DDR_FIX_TX_CMD_14_AHEAD 0x20 +#define SD20_TX_NEG_EDGE 0x00 +#define SD20_TX_14_AHEAD 0x10 #define SD20_TX_SEL_MASK 0x10 -#define DDR_VAR_SDCLK_POL_SWAP 0x01 +#define DDR_VAR_SDCLK_POL_SWAP 0x01 /* SD_TRANSFER */ -#define SD_TRANSFER_START 0x80 -#define SD_TRANSFER_END 0x40 +#define SD_TRANSFER_START 0x80 +#define SD_TRANSFER_END 0x40 #define SD_STAT_IDLE 0x20 -#define SD_TRANSFER_ERR 0x10 +#define SD_TRANSFER_ERR 0x10 /* SD Transfer Mode definition */ -#define SD_TM_NORMAL_WRITE 0x00 -#define SD_TM_AUTO_WRITE_3 0x01 -#define SD_TM_AUTO_WRITE_4 0x02 -#define SD_TM_AUTO_READ_3 0x05 -#define SD_TM_AUTO_READ_4 0x06 -#define SD_TM_CMD_RSP 0x08 -#define SD_TM_AUTO_WRITE_1 0x09 -#define SD_TM_AUTO_WRITE_2 0x0A -#define SD_TM_NORMAL_READ 0x0C -#define SD_TM_AUTO_READ_1 0x0D -#define SD_TM_AUTO_READ_2 0x0E -#define SD_TM_AUTO_TUNING 0x0F +#define SD_TM_NORMAL_WRITE 0x00 +#define SD_TM_AUTO_WRITE_3 0x01 +#define SD_TM_AUTO_WRITE_4 0x02 +#define SD_TM_AUTO_READ_3 0x05 +#define SD_TM_AUTO_READ_4 0x06 +#define SD_TM_CMD_RSP 0x08 +#define SD_TM_AUTO_WRITE_1 0x09 +#define SD_TM_AUTO_WRITE_2 0x0A +#define SD_TM_NORMAL_READ 0x0C +#define SD_TM_AUTO_READ_1 0x0D +#define SD_TM_AUTO_READ_2 0x0E +#define SD_TM_AUTO_TUNING 0x0F /* SD_VPTX_CTL / SD_VPRX_CTL */ #define PHASE_CHANGE 0x80 @@ -332,15 +332,15 @@ /* SD Configure 1 Register */ #define SD_CLK_DIVIDE_0 0x00 -#define SD_CLK_DIVIDE_256 0xC0 -#define SD_CLK_DIVIDE_128 0x80 -#define SD_BUS_WIDTH_1BIT 0x00 -#define SD_BUS_WIDTH_4BIT 0x01 -#define SD_BUS_WIDTH_8BIT 0x02 -#define SD_ASYNC_FIFO_NOT_RST 0x10 -#define SD_20_MODE 0x00 -#define SD_DDR_MODE 0x04 -#define SD_30_MODE 0x08 +#define SD_CLK_DIVIDE_256 0xC0 +#define SD_CLK_DIVIDE_128 0x80 +#define SD_BUS_WIDTH_1BIT 0x00 +#define SD_BUS_WIDTH_4BIT 0x01 +#define SD_BUS_WIDTH_8BIT 0x02 +#define SD_ASYNC_FIFO_NOT_RST 0x10 +#define SD_20_MODE 0x00 +#define SD_DDR_MODE 0x04 +#define SD_30_MODE 0x08 #define SD_CLK_DIVIDE_MASK 0xC0 @@ -415,71 +415,71 @@ #define CLK_DIV_8 0x04 /* MS_CFG */ -#define SAMPLE_TIME_RISING 0x00 -#define SAMPLE_TIME_FALLING 0x80 -#define PUSH_TIME_DEFAULT 0x00 -#define PUSH_TIME_ODD 0x40 -#define NO_EXTEND_TOGGLE 0x00 -#define EXTEND_TOGGLE_CHK 0x20 -#define MS_BUS_WIDTH_1 0x00 -#define MS_BUS_WIDTH_4 0x10 -#define MS_BUS_WIDTH_8 0x18 -#define MS_2K_SECTOR_MODE 0x04 -#define MS_512_SECTOR_MODE 0x00 -#define MS_TOGGLE_TIMEOUT_EN 0x00 -#define MS_TOGGLE_TIMEOUT_DISEN 0x01 +#define SAMPLE_TIME_RISING 0x00 +#define SAMPLE_TIME_FALLING 0x80 +#define PUSH_TIME_DEFAULT 0x00 +#define PUSH_TIME_ODD 0x40 +#define NO_EXTEND_TOGGLE 0x00 +#define EXTEND_TOGGLE_CHK 0x20 +#define MS_BUS_WIDTH_1 0x00 +#define MS_BUS_WIDTH_4 0x10 +#define MS_BUS_WIDTH_8 0x18 +#define MS_2K_SECTOR_MODE 0x04 +#define MS_512_SECTOR_MODE 0x00 +#define MS_TOGGLE_TIMEOUT_EN 0x00 +#define MS_TOGGLE_TIMEOUT_DISEN 0x01 #define MS_NO_CHECK_INT 0x02 /* MS_TRANS_CFG */ -#define WAIT_INT 0x80 -#define NO_WAIT_INT 0x00 -#define NO_AUTO_READ_INT_REG 0x00 -#define AUTO_READ_INT_REG 0x40 -#define MS_CRC16_ERR 0x20 -#define MS_RDY_TIMEOUT 0x10 -#define MS_INT_CMDNK 0x08 -#define MS_INT_BREQ 0x04 -#define MS_INT_ERR 0x02 -#define MS_INT_CED 0x01 +#define WAIT_INT 0x80 +#define NO_WAIT_INT 0x00 +#define NO_AUTO_READ_INT_REG 0x00 +#define AUTO_READ_INT_REG 0x40 +#define MS_CRC16_ERR 0x20 +#define MS_RDY_TIMEOUT 0x10 +#define MS_INT_CMDNK 0x08 +#define MS_INT_BREQ 0x04 +#define MS_INT_ERR 0x02 +#define MS_INT_CED 0x01 /* MS_TRANSFER */ -#define MS_TRANSFER_START 0x80 -#define MS_TRANSFER_END 0x40 -#define MS_TRANSFER_ERR 0x20 -#define MS_BS_STATE 0x10 -#define MS_TM_READ_BYTES 0x00 -#define MS_TM_NORMAL_READ 0x01 -#define MS_TM_WRITE_BYTES 0x04 -#define MS_TM_NORMAL_WRITE 0x05 -#define MS_TM_AUTO_READ 0x08 -#define MS_TM_AUTO_WRITE 0x0C +#define MS_TRANSFER_START 0x80 +#define MS_TRANSFER_END 0x40 +#define MS_TRANSFER_ERR 0x20 +#define MS_BS_STATE 0x10 +#define MS_TM_READ_BYTES 0x00 +#define MS_TM_NORMAL_READ 0x01 +#define MS_TM_WRITE_BYTES 0x04 +#define MS_TM_NORMAL_WRITE 0x05 +#define MS_TM_AUTO_READ 0x08 +#define MS_TM_AUTO_WRITE 0x0C /* SD Configure 2 Register */ -#define SD_CALCULATE_CRC7 0x00 -#define SD_NO_CALCULATE_CRC7 0x80 -#define SD_CHECK_CRC16 0x00 -#define SD_NO_CHECK_CRC16 0x40 +#define SD_CALCULATE_CRC7 0x00 +#define SD_NO_CALCULATE_CRC7 0x80 +#define SD_CHECK_CRC16 0x00 +#define SD_NO_CHECK_CRC16 0x40 #define SD_NO_CHECK_WAIT_CRC_TO 0x20 -#define SD_WAIT_BUSY_END 0x08 -#define SD_NO_WAIT_BUSY_END 0x00 -#define SD_CHECK_CRC7 0x00 -#define SD_NO_CHECK_CRC7 0x04 -#define SD_RSP_LEN_0 0x00 -#define SD_RSP_LEN_6 0x01 -#define SD_RSP_LEN_17 0x02 +#define SD_WAIT_BUSY_END 0x08 +#define SD_NO_WAIT_BUSY_END 0x00 +#define SD_CHECK_CRC7 0x00 +#define SD_NO_CHECK_CRC7 0x04 +#define SD_RSP_LEN_0 0x00 +#define SD_RSP_LEN_6 0x01 +#define SD_RSP_LEN_17 0x02 /* SD/MMC Response Type Definition */ -#define SD_RSP_TYPE_R0 0x04 -#define SD_RSP_TYPE_R1 0x01 -#define SD_RSP_TYPE_R1b 0x09 -#define SD_RSP_TYPE_R2 0x02 -#define SD_RSP_TYPE_R3 0x05 -#define SD_RSP_TYPE_R4 0x05 -#define SD_RSP_TYPE_R5 0x01 -#define SD_RSP_TYPE_R6 0x01 -#define SD_RSP_TYPE_R7 0x01 +#define SD_RSP_TYPE_R0 0x04 +#define SD_RSP_TYPE_R1 0x01 +#define SD_RSP_TYPE_R1b 0x09 +#define SD_RSP_TYPE_R2 0x02 +#define SD_RSP_TYPE_R3 0x05 +#define SD_RSP_TYPE_R4 0x05 +#define SD_RSP_TYPE_R5 0x01 +#define SD_RSP_TYPE_R6 0x01 +#define SD_RSP_TYPE_R7 0x01 /* SD_CONFIGURE3 */ -#define SD_RSP_80CLK_TIMEOUT_EN 0x01 +#define SD_RSP_80CLK_TIMEOUT_EN 0x01 /* Card Transfer Reset Register */ #define SPI_STOP 0x01 @@ -574,13 +574,13 @@ #define SRCTL 0xFC13 -#define DCM_DRP_CTL 0xFC23 -#define DCM_DRP_TRIG 0xFC24 -#define DCM_DRP_CFG 0xFC25 -#define DCM_DRP_WR_DATA_L 0xFC26 -#define DCM_DRP_WR_DATA_H 0xFC27 -#define DCM_DRP_RD_DATA_L 0xFC28 -#define DCM_DRP_RD_DATA_H 0xFC29 +#define DCM_DRP_CTL 0xFC23 +#define DCM_DRP_TRIG 0xFC24 +#define DCM_DRP_CFG 0xFC25 +#define DCM_DRP_WR_DATA_L 0xFC26 +#define DCM_DRP_WR_DATA_H 0xFC27 +#define DCM_DRP_RD_DATA_L 0xFC28 +#define DCM_DRP_RD_DATA_H 0xFC29 #define SD_VPCLK0_CTL 0xFC2A #define SD_VPCLK1_CTL 0xFC2B #define SD_DCMPS0_CTL 0xFC2C -- cgit v1.2.3 From ada71f5588320e7a5c7166cb7c1c8c40cb866ac4 Mon Sep 17 00:00:00 2001 From: Micky Ching Date: Wed, 25 Feb 2015 13:50:09 +0800 Subject: mfd: rtsx: Place register address and values togather It is more readable to place register address and values define togather. The values define add two leading space indicate belong to the register address defined above. Signed-off-by: Micky Ching Signed-off-by: Lee Jones --- include/linux/mfd/rtsx_pci.h | 836 +++++++++++++++++++------------------------ 1 file changed, 369 insertions(+), 467 deletions(-) (limited to 'include') diff --git a/include/linux/mfd/rtsx_pci.h b/include/linux/mfd/rtsx_pci.h index a9c2a14fd521..e81f2bbfcda0 100644 --- a/include/linux/mfd/rtsx_pci.h +++ b/include/linux/mfd/rtsx_pci.h @@ -28,74 +28,72 @@ #define MAX_RW_REG_CNT 1024 -/* PCI Operation Register Address */ #define RTSX_HCBAR 0x00 #define RTSX_HCBCTLR 0x04 +#define STOP_CMD (0x01 << 28) +#define READ_REG_CMD 0 +#define WRITE_REG_CMD 1 +#define CHECK_REG_CMD 2 + #define RTSX_HDBAR 0x08 +#define SG_INT 0x04 +#define SG_END 0x02 +#define SG_VALID 0x01 +#define SG_NO_OP 0x00 +#define SG_TRANS_DATA (0x02 << 4) +#define SG_LINK_DESC (0x03 << 4) #define RTSX_HDBCTLR 0x0C +#define SDMA_MODE 0x00 +#define ADMA_MODE (0x02 << 26) +#define STOP_DMA (0x01 << 28) +#define TRIG_DMA (0x01 << 31) + #define RTSX_HAIMR 0x10 -#define RTSX_BIPR 0x14 -#define RTSX_BIER 0x18 +#define HAIMR_TRANS_START (0x01 << 31) +#define HAIMR_READ 0x00 +#define HAIMR_WRITE (0x01 << 30) +#define HAIMR_READ_START (HAIMR_TRANS_START | HAIMR_READ) +#define HAIMR_WRITE_START (HAIMR_TRANS_START | HAIMR_WRITE) +#define HAIMR_TRANS_END (HAIMR_TRANS_START) -/* Host command buffer control register */ -#define STOP_CMD (0x01 << 28) - -/* Host data buffer control register */ -#define SDMA_MODE 0x00 -#define ADMA_MODE (0x02 << 26) -#define STOP_DMA (0x01 << 28) -#define TRIG_DMA (0x01 << 31) - -/* Host access internal memory register */ -#define HAIMR_TRANS_START (0x01 << 31) -#define HAIMR_READ 0x00 -#define HAIMR_WRITE (0x01 << 30) -#define HAIMR_READ_START (HAIMR_TRANS_START | HAIMR_READ) -#define HAIMR_WRITE_START (HAIMR_TRANS_START | HAIMR_WRITE) -#define HAIMR_TRANS_END (HAIMR_TRANS_START) - -/* Bus interrupt pending register */ -#define CMD_DONE_INT (1 << 31) -#define DATA_DONE_INT (1 << 30) -#define TRANS_OK_INT (1 << 29) -#define TRANS_FAIL_INT (1 << 28) -#define XD_INT (1 << 27) -#define MS_INT (1 << 26) -#define SD_INT (1 << 25) -#define GPIO0_INT (1 << 24) -#define OC_INT (1 << 23) -#define SD_WRITE_PROTECT (1 << 19) -#define XD_EXIST (1 << 18) -#define MS_EXIST (1 << 17) -#define SD_EXIST (1 << 16) -#define DELINK_INT GPIO0_INT -#define MS_OC_INT (1 << 23) -#define SD_OC_INT (1 << 22) +#define RTSX_BIPR 0x14 +#define CMD_DONE_INT (1 << 31) +#define DATA_DONE_INT (1 << 30) +#define TRANS_OK_INT (1 << 29) +#define TRANS_FAIL_INT (1 << 28) +#define XD_INT (1 << 27) +#define MS_INT (1 << 26) +#define SD_INT (1 << 25) +#define GPIO0_INT (1 << 24) +#define OC_INT (1 << 23) +#define SD_WRITE_PROTECT (1 << 19) +#define XD_EXIST (1 << 18) +#define MS_EXIST (1 << 17) +#define SD_EXIST (1 << 16) +#define DELINK_INT GPIO0_INT +#define MS_OC_INT (1 << 23) +#define SD_OC_INT (1 << 22) #define CARD_INT (XD_INT | MS_INT | SD_INT) #define NEED_COMPLETE_INT (DATA_DONE_INT | TRANS_OK_INT | TRANS_FAIL_INT) #define RTSX_INT (CMD_DONE_INT | NEED_COMPLETE_INT | \ CARD_INT | GPIO0_INT | OC_INT) - #define CARD_EXIST (XD_EXIST | MS_EXIST | SD_EXIST) -/* Bus interrupt enable register */ -#define CMD_DONE_INT_EN (1 << 31) -#define DATA_DONE_INT_EN (1 << 30) -#define TRANS_OK_INT_EN (1 << 29) -#define TRANS_FAIL_INT_EN (1 << 28) -#define XD_INT_EN (1 << 27) -#define MS_INT_EN (1 << 26) -#define SD_INT_EN (1 << 25) -#define GPIO0_INT_EN (1 << 24) -#define OC_INT_EN (1 << 23) -#define DELINK_INT_EN GPIO0_INT_EN -#define MS_OC_INT_EN (1 << 23) -#define SD_OC_INT_EN (1 << 22) - -#define READ_REG_CMD 0 -#define WRITE_REG_CMD 1 -#define CHECK_REG_CMD 2 +#define RTSX_BIER 0x18 +#define CMD_DONE_INT_EN (1 << 31) +#define DATA_DONE_INT_EN (1 << 30) +#define TRANS_OK_INT_EN (1 << 29) +#define TRANS_FAIL_INT_EN (1 << 28) +#define XD_INT_EN (1 << 27) +#define MS_INT_EN (1 << 26) +#define SD_INT_EN (1 << 25) +#define GPIO0_INT_EN (1 << 24) +#define OC_INT_EN (1 << 23) +#define DELINK_INT_EN GPIO0_INT_EN +#define MS_OC_INT_EN (1 << 23) +#define SD_OC_INT_EN (1 << 22) + /* * macros for easy use @@ -125,423 +123,68 @@ #define rtsx_pci_write_config_dword(pcr, where, val) \ pci_write_config_dword((pcr)->pci, where, val) -#define STATE_TRANS_NONE 0 -#define STATE_TRANS_CMD 1 -#define STATE_TRANS_BUF 2 -#define STATE_TRANS_SG 3 - -#define TRANS_NOT_READY 0 -#define TRANS_RESULT_OK 1 -#define TRANS_RESULT_FAIL 2 -#define TRANS_NO_DEVICE 3 - -#define RTSX_RESV_BUF_LEN 4096 -#define HOST_CMDS_BUF_LEN 1024 -#define HOST_SG_TBL_BUF_LEN (RTSX_RESV_BUF_LEN - HOST_CMDS_BUF_LEN) -#define HOST_SG_TBL_ITEMS (HOST_SG_TBL_BUF_LEN / 8) -#define MAX_SG_ITEM_LEN 0x80000 - -#define HOST_TO_DEVICE 0 -#define DEVICE_TO_HOST 1 - -#define RTSX_PHASE_MAX 32 -#define RX_TUNING_CNT 3 - -/* SG descriptor */ -#define SG_INT 0x04 -#define SG_END 0x02 -#define SG_VALID 0x01 - -#define SG_NO_OP 0x00 -#define SG_TRANS_DATA (0x02 << 4) -#define SG_LINK_DESC (0x03 << 4) - -/* Output voltage */ -#define OUTPUT_3V3 0 -#define OUTPUT_1V8 1 - -/* Card Clock Enable Register */ -#define SD_CLK_EN 0x04 -#define MS_CLK_EN 0x08 - -/* Card Select Register */ -#define SD_MOD_SEL 2 -#define MS_MOD_SEL 3 - -/* Card Output Enable Register */ -#define SD_OUTPUT_EN 0x04 -#define MS_OUTPUT_EN 0x08 - -/* CARD_SHARE_MODE */ -#define CARD_SHARE_MASK 0x0F -#define CARD_SHARE_MULTI_LUN 0x00 -#define CARD_SHARE_NORMAL 0x00 -#define CARD_SHARE_48_SD 0x04 -#define CARD_SHARE_48_MS 0x08 -/* CARD_SHARE_MODE for barossa */ -#define CARD_SHARE_BAROSSA_SD 0x01 -#define CARD_SHARE_BAROSSA_MS 0x02 - -/* CARD_DRIVE_SEL */ -#define MS_DRIVE_8mA (0x01 << 6) -#define MMC_DRIVE_8mA (0x01 << 4) -#define XD_DRIVE_8mA (0x01 << 2) -#define GPIO_DRIVE_8mA 0x01 -#define RTS5209_CARD_DRIVE_DEFAULT (MS_DRIVE_8mA | MMC_DRIVE_8mA |\ - XD_DRIVE_8mA | GPIO_DRIVE_8mA) -#define RTL8411_CARD_DRIVE_DEFAULT (MS_DRIVE_8mA | MMC_DRIVE_8mA |\ - XD_DRIVE_8mA) -#define RTSX_CARD_DRIVE_DEFAULT (MS_DRIVE_8mA | GPIO_DRIVE_8mA) +#define STATE_TRANS_NONE 0 +#define STATE_TRANS_CMD 1 +#define STATE_TRANS_BUF 2 +#define STATE_TRANS_SG 3 -/* SD30_DRIVE_SEL */ -#define DRIVER_TYPE_A 0x05 -#define DRIVER_TYPE_B 0x03 -#define DRIVER_TYPE_C 0x02 -#define DRIVER_TYPE_D 0x01 -#define CFG_DRIVER_TYPE_A 0x02 -#define CFG_DRIVER_TYPE_B 0x03 -#define CFG_DRIVER_TYPE_C 0x01 -#define CFG_DRIVER_TYPE_D 0x00 - -/* FPDCTL */ -#define SSC_POWER_DOWN 0x01 -#define SD_OC_POWER_DOWN 0x02 -#define ALL_POWER_DOWN 0x07 -#define OC_POWER_DOWN 0x06 - -/* CLK_CTL */ -#define CHANGE_CLK 0x01 - -/* LDO_CTL */ -#define BPP_ASIC_1V7 0x00 -#define BPP_ASIC_1V8 0x01 -#define BPP_ASIC_1V9 0x02 -#define BPP_ASIC_2V0 0x03 -#define BPP_ASIC_2V7 0x04 -#define BPP_ASIC_2V8 0x05 -#define BPP_ASIC_3V2 0x06 -#define BPP_ASIC_3V3 0x07 -#define BPP_REG_TUNED18 0x07 -#define BPP_TUNED18_SHIFT_8402 5 -#define BPP_TUNED18_SHIFT_8411 4 -#define BPP_PAD_MASK 0x04 -#define BPP_PAD_3V3 0x04 -#define BPP_PAD_1V8 0x00 -#define BPP_LDO_POWB 0x03 -#define BPP_LDO_ON 0x00 -#define BPP_LDO_SUSPEND 0x02 -#define BPP_LDO_OFF 0x03 - -/* CD_PAD_CTL */ -#define CD_DISABLE_MASK 0x07 -#define MS_CD_DISABLE 0x04 -#define SD_CD_DISABLE 0x02 -#define XD_CD_DISABLE 0x01 -#define CD_DISABLE 0x07 -#define CD_ENABLE 0x00 -#define MS_CD_EN_ONLY 0x03 -#define SD_CD_EN_ONLY 0x05 -#define XD_CD_EN_ONLY 0x06 -#define FORCE_CD_LOW_MASK 0x38 -#define FORCE_CD_XD_LOW 0x08 -#define FORCE_CD_SD_LOW 0x10 -#define FORCE_CD_MS_LOW 0x20 -#define CD_AUTO_DISABLE 0x40 - -/* SD_STAT1 */ -#define SD_CRC7_ERR 0x80 -#define SD_CRC16_ERR 0x40 -#define SD_CRC_WRITE_ERR 0x20 -#define SD_CRC_WRITE_ERR_MASK 0x1C -#define GET_CRC_TIME_OUT 0x02 -#define SD_TUNING_COMPARE_ERR 0x01 - -/* SD_STAT2 */ -#define SD_RSP_80CLK_TIMEOUT 0x01 - -/* SD_BUS_STAT */ -#define SD_CLK_TOGGLE_EN 0x80 -#define SD_CLK_FORCE_STOP 0x40 -#define SD_DAT3_STATUS 0x10 -#define SD_DAT2_STATUS 0x08 -#define SD_DAT1_STATUS 0x04 -#define SD_DAT0_STATUS 0x02 -#define SD_CMD_STATUS 0x01 - -/* SD_PAD_CTL */ -#define SD_IO_USING_1V8 0x80 -#define SD_IO_USING_3V3 0x7F -#define TYPE_A_DRIVING 0x00 -#define TYPE_B_DRIVING 0x01 -#define TYPE_C_DRIVING 0x02 -#define TYPE_D_DRIVING 0x03 - -/* SD_SAMPLE_POINT_CTL */ -#define DDR_FIX_RX_DAT 0x00 -#define DDR_VAR_RX_DAT 0x80 -#define DDR_FIX_RX_DAT_EDGE 0x00 -#define DDR_FIX_RX_DAT_14_DELAY 0x40 -#define DDR_FIX_RX_CMD 0x00 -#define DDR_VAR_RX_CMD 0x20 -#define DDR_FIX_RX_CMD_POS_EDGE 0x00 -#define DDR_FIX_RX_CMD_14_DELAY 0x10 -#define SD20_RX_POS_EDGE 0x00 -#define SD20_RX_14_DELAY 0x08 -#define SD20_RX_SEL_MASK 0x08 +#define TRANS_NOT_READY 0 +#define TRANS_RESULT_OK 1 +#define TRANS_RESULT_FAIL 2 +#define TRANS_NO_DEVICE 3 -/* SD_PUSH_POINT_CTL */ -#define DDR_FIX_TX_CMD_DAT 0x00 -#define DDR_VAR_TX_CMD_DAT 0x80 -#define DDR_FIX_TX_DAT_14_TSU 0x00 -#define DDR_FIX_TX_DAT_12_TSU 0x40 -#define DDR_FIX_TX_CMD_NEG_EDGE 0x00 -#define DDR_FIX_TX_CMD_14_AHEAD 0x20 -#define SD20_TX_NEG_EDGE 0x00 -#define SD20_TX_14_AHEAD 0x10 -#define SD20_TX_SEL_MASK 0x10 -#define DDR_VAR_SDCLK_POL_SWAP 0x01 - -/* SD_TRANSFER */ -#define SD_TRANSFER_START 0x80 -#define SD_TRANSFER_END 0x40 -#define SD_STAT_IDLE 0x20 -#define SD_TRANSFER_ERR 0x10 -/* SD Transfer Mode definition */ -#define SD_TM_NORMAL_WRITE 0x00 -#define SD_TM_AUTO_WRITE_3 0x01 -#define SD_TM_AUTO_WRITE_4 0x02 -#define SD_TM_AUTO_READ_3 0x05 -#define SD_TM_AUTO_READ_4 0x06 -#define SD_TM_CMD_RSP 0x08 -#define SD_TM_AUTO_WRITE_1 0x09 -#define SD_TM_AUTO_WRITE_2 0x0A -#define SD_TM_NORMAL_READ 0x0C -#define SD_TM_AUTO_READ_1 0x0D -#define SD_TM_AUTO_READ_2 0x0E -#define SD_TM_AUTO_TUNING 0x0F - -/* SD_VPTX_CTL / SD_VPRX_CTL */ -#define PHASE_CHANGE 0x80 -#define PHASE_NOT_RESET 0x40 - -/* SD_DCMPS_TX_CTL / SD_DCMPS_RX_CTL */ -#define DCMPS_CHANGE 0x80 -#define DCMPS_CHANGE_DONE 0x40 -#define DCMPS_ERROR 0x20 -#define DCMPS_CURRENT_PHASE 0x1F - -/* SD Configure 1 Register */ -#define SD_CLK_DIVIDE_0 0x00 -#define SD_CLK_DIVIDE_256 0xC0 -#define SD_CLK_DIVIDE_128 0x80 -#define SD_BUS_WIDTH_1BIT 0x00 -#define SD_BUS_WIDTH_4BIT 0x01 -#define SD_BUS_WIDTH_8BIT 0x02 -#define SD_ASYNC_FIFO_NOT_RST 0x10 -#define SD_20_MODE 0x00 -#define SD_DDR_MODE 0x04 -#define SD_30_MODE 0x08 - -#define SD_CLK_DIVIDE_MASK 0xC0 - -/* SD_CMD_STATE */ -#define SD_CMD_IDLE 0x80 - -/* SD_DATA_STATE */ -#define SD_DATA_IDLE 0x80 - -/* DCM_DRP_CTL */ -#define DCM_RESET 0x08 -#define DCM_LOCKED 0x04 -#define DCM_208M 0x00 -#define DCM_TX 0x01 -#define DCM_RX 0x02 - -/* DCM_DRP_TRIG */ -#define DRP_START 0x80 -#define DRP_DONE 0x40 - -/* DCM_DRP_CFG */ -#define DRP_WRITE 0x80 -#define DRP_READ 0x00 -#define DCM_WRITE_ADDRESS_50 0x50 -#define DCM_WRITE_ADDRESS_51 0x51 -#define DCM_READ_ADDRESS_00 0x00 -#define DCM_READ_ADDRESS_51 0x51 - -/* IRQSTAT0 */ -#define DMA_DONE_INT 0x80 -#define SUSPEND_INT 0x40 -#define LINK_RDY_INT 0x20 -#define LINK_DOWN_INT 0x10 - -/* DMACTL */ -#define DMA_RST 0x80 -#define DMA_BUSY 0x04 -#define DMA_DIR_TO_CARD 0x00 -#define DMA_DIR_FROM_CARD 0x02 -#define DMA_EN 0x01 -#define DMA_128 (0 << 4) -#define DMA_256 (1 << 4) -#define DMA_512 (2 << 4) -#define DMA_1024 (3 << 4) -#define DMA_PACK_SIZE_MASK 0x30 - -/* SSC_CTL1 */ -#define SSC_RSTB 0x80 -#define SSC_8X_EN 0x40 -#define SSC_FIX_FRAC 0x20 -#define SSC_SEL_1M 0x00 -#define SSC_SEL_2M 0x08 -#define SSC_SEL_4M 0x10 -#define SSC_SEL_8M 0x18 - -/* SSC_CTL2 */ -#define SSC_DEPTH_MASK 0x07 -#define SSC_DEPTH_DISALBE 0x00 -#define SSC_DEPTH_4M 0x01 -#define SSC_DEPTH_2M 0x02 -#define SSC_DEPTH_1M 0x03 -#define SSC_DEPTH_500K 0x04 -#define SSC_DEPTH_250K 0x05 - -/* System Clock Control Register */ -#define CLK_LOW_FREQ 0x01 - -/* System Clock Divider Register */ -#define CLK_DIV_1 0x01 -#define CLK_DIV_2 0x02 -#define CLK_DIV_4 0x03 -#define CLK_DIV_8 0x04 - -/* MS_CFG */ -#define SAMPLE_TIME_RISING 0x00 -#define SAMPLE_TIME_FALLING 0x80 -#define PUSH_TIME_DEFAULT 0x00 -#define PUSH_TIME_ODD 0x40 -#define NO_EXTEND_TOGGLE 0x00 -#define EXTEND_TOGGLE_CHK 0x20 -#define MS_BUS_WIDTH_1 0x00 -#define MS_BUS_WIDTH_4 0x10 -#define MS_BUS_WIDTH_8 0x18 -#define MS_2K_SECTOR_MODE 0x04 -#define MS_512_SECTOR_MODE 0x00 -#define MS_TOGGLE_TIMEOUT_EN 0x00 -#define MS_TOGGLE_TIMEOUT_DISEN 0x01 -#define MS_NO_CHECK_INT 0x02 +#define RTSX_RESV_BUF_LEN 4096 +#define HOST_CMDS_BUF_LEN 1024 +#define HOST_SG_TBL_BUF_LEN (RTSX_RESV_BUF_LEN - HOST_CMDS_BUF_LEN) +#define HOST_SG_TBL_ITEMS (HOST_SG_TBL_BUF_LEN / 8) +#define MAX_SG_ITEM_LEN 0x80000 +#define HOST_TO_DEVICE 0 +#define DEVICE_TO_HOST 1 + +#define OUTPUT_3V3 0 +#define OUTPUT_1V8 1 -/* MS_TRANS_CFG */ -#define WAIT_INT 0x80 -#define NO_WAIT_INT 0x00 -#define NO_AUTO_READ_INT_REG 0x00 -#define AUTO_READ_INT_REG 0x40 -#define MS_CRC16_ERR 0x20 -#define MS_RDY_TIMEOUT 0x10 -#define MS_INT_CMDNK 0x08 -#define MS_INT_BREQ 0x04 -#define MS_INT_ERR 0x02 -#define MS_INT_CED 0x01 - -/* MS_TRANSFER */ -#define MS_TRANSFER_START 0x80 -#define MS_TRANSFER_END 0x40 -#define MS_TRANSFER_ERR 0x20 -#define MS_BS_STATE 0x10 -#define MS_TM_READ_BYTES 0x00 -#define MS_TM_NORMAL_READ 0x01 -#define MS_TM_WRITE_BYTES 0x04 -#define MS_TM_NORMAL_WRITE 0x05 -#define MS_TM_AUTO_READ 0x08 -#define MS_TM_AUTO_WRITE 0x0C - -/* SD Configure 2 Register */ -#define SD_CALCULATE_CRC7 0x00 -#define SD_NO_CALCULATE_CRC7 0x80 -#define SD_CHECK_CRC16 0x00 -#define SD_NO_CHECK_CRC16 0x40 -#define SD_NO_CHECK_WAIT_CRC_TO 0x20 -#define SD_WAIT_BUSY_END 0x08 -#define SD_NO_WAIT_BUSY_END 0x00 -#define SD_CHECK_CRC7 0x00 -#define SD_NO_CHECK_CRC7 0x04 -#define SD_RSP_LEN_0 0x00 -#define SD_RSP_LEN_6 0x01 -#define SD_RSP_LEN_17 0x02 -/* SD/MMC Response Type Definition */ -#define SD_RSP_TYPE_R0 0x04 -#define SD_RSP_TYPE_R1 0x01 -#define SD_RSP_TYPE_R1b 0x09 -#define SD_RSP_TYPE_R2 0x02 -#define SD_RSP_TYPE_R3 0x05 -#define SD_RSP_TYPE_R4 0x05 -#define SD_RSP_TYPE_R5 0x01 -#define SD_RSP_TYPE_R6 0x01 -#define SD_RSP_TYPE_R7 0x01 - -/* SD_CONFIGURE3 */ -#define SD_RSP_80CLK_TIMEOUT_EN 0x01 - -/* Card Transfer Reset Register */ -#define SPI_STOP 0x01 -#define XD_STOP 0x02 -#define SD_STOP 0x04 -#define MS_STOP 0x08 -#define SPI_CLR_ERR 0x10 -#define XD_CLR_ERR 0x20 -#define SD_CLR_ERR 0x40 -#define MS_CLR_ERR 0x80 - -/* Card Data Source Register */ -#define PINGPONG_BUFFER 0x01 -#define RING_BUFFER 0x00 - -/* Card Power Control Register */ -#define PMOS_STRG_MASK 0x10 -#define PMOS_STRG_800mA 0x10 -#define PMOS_STRG_400mA 0x00 -#define SD_POWER_OFF 0x03 -#define SD_PARTIAL_POWER_ON 0x01 -#define SD_POWER_ON 0x00 -#define SD_POWER_MASK 0x03 -#define MS_POWER_OFF 0x0C -#define MS_PARTIAL_POWER_ON 0x04 -#define MS_POWER_ON 0x00 -#define MS_POWER_MASK 0x0C -#define BPP_POWER_OFF 0x0F -#define BPP_POWER_5_PERCENT_ON 0x0E -#define BPP_POWER_10_PERCENT_ON 0x0C -#define BPP_POWER_15_PERCENT_ON 0x08 -#define BPP_POWER_ON 0x00 -#define BPP_POWER_MASK 0x0F -#define SD_VCC_PARTIAL_POWER_ON 0x02 -#define SD_VCC_POWER_ON 0x00 - -/* PWR_GATE_CTRL */ -#define PWR_GATE_EN 0x01 -#define LDO3318_PWR_MASK 0x06 -#define LDO_ON 0x00 -#define LDO_SUSPEND 0x04 -#define LDO_OFF 0x06 - -/* CARD_CLK_SOURCE */ -#define CRC_FIX_CLK (0x00 << 0) -#define CRC_VAR_CLK0 (0x01 << 0) -#define CRC_VAR_CLK1 (0x02 << 0) -#define SD30_FIX_CLK (0x00 << 2) -#define SD30_VAR_CLK0 (0x01 << 2) -#define SD30_VAR_CLK1 (0x02 << 2) -#define SAMPLE_FIX_CLK (0x00 << 4) -#define SAMPLE_VAR_CLK0 (0x01 << 4) -#define SAMPLE_VAR_CLK1 (0x02 << 4) - -/* HOST_SLEEP_STATE */ -#define HOST_ENTER_S1 1 -#define HOST_ENTER_S3 2 +#define RTSX_PHASE_MAX 32 +#define RX_TUNING_CNT 3 #define MS_CFG 0xFD40 +#define SAMPLE_TIME_RISING 0x00 +#define SAMPLE_TIME_FALLING 0x80 +#define PUSH_TIME_DEFAULT 0x00 +#define PUSH_TIME_ODD 0x40 +#define NO_EXTEND_TOGGLE 0x00 +#define EXTEND_TOGGLE_CHK 0x20 +#define MS_BUS_WIDTH_1 0x00 +#define MS_BUS_WIDTH_4 0x10 +#define MS_BUS_WIDTH_8 0x18 +#define MS_2K_SECTOR_MODE 0x04 +#define MS_512_SECTOR_MODE 0x00 +#define MS_TOGGLE_TIMEOUT_EN 0x00 +#define MS_TOGGLE_TIMEOUT_DISEN 0x01 +#define MS_NO_CHECK_INT 0x02 #define MS_TPC 0xFD41 #define MS_TRANS_CFG 0xFD42 +#define WAIT_INT 0x80 +#define NO_WAIT_INT 0x00 +#define NO_AUTO_READ_INT_REG 0x00 +#define AUTO_READ_INT_REG 0x40 +#define MS_CRC16_ERR 0x20 +#define MS_RDY_TIMEOUT 0x10 +#define MS_INT_CMDNK 0x08 +#define MS_INT_BREQ 0x04 +#define MS_INT_ERR 0x02 +#define MS_INT_CED 0x01 #define MS_TRANSFER 0xFD43 +#define MS_TRANSFER_START 0x80 +#define MS_TRANSFER_END 0x40 +#define MS_TRANSFER_ERR 0x20 +#define MS_BS_STATE 0x10 +#define MS_TM_READ_BYTES 0x00 +#define MS_TM_NORMAL_READ 0x01 +#define MS_TM_WRITE_BYTES 0x04 +#define MS_TM_NORMAL_WRITE 0x05 +#define MS_TM_AUTO_READ 0x08 +#define MS_TM_AUTO_WRITE 0x0C #define MS_INT_REG 0xFD44 #define MS_BYTE_CNT 0xFD45 #define MS_SECTOR_CNT_L 0xFD46 @@ -549,14 +192,90 @@ #define MS_DBUS_H 0xFD48 #define SD_CFG1 0xFDA0 +#define SD_CLK_DIVIDE_0 0x00 +#define SD_CLK_DIVIDE_256 0xC0 +#define SD_CLK_DIVIDE_128 0x80 +#define SD_BUS_WIDTH_1BIT 0x00 +#define SD_BUS_WIDTH_4BIT 0x01 +#define SD_BUS_WIDTH_8BIT 0x02 +#define SD_ASYNC_FIFO_NOT_RST 0x10 +#define SD_20_MODE 0x00 +#define SD_DDR_MODE 0x04 +#define SD_30_MODE 0x08 +#define SD_CLK_DIVIDE_MASK 0xC0 #define SD_CFG2 0xFDA1 +#define SD_CALCULATE_CRC7 0x00 +#define SD_NO_CALCULATE_CRC7 0x80 +#define SD_CHECK_CRC16 0x00 +#define SD_NO_CHECK_CRC16 0x40 +#define SD_NO_CHECK_WAIT_CRC_TO 0x20 +#define SD_WAIT_BUSY_END 0x08 +#define SD_NO_WAIT_BUSY_END 0x00 +#define SD_CHECK_CRC7 0x00 +#define SD_NO_CHECK_CRC7 0x04 +#define SD_RSP_LEN_0 0x00 +#define SD_RSP_LEN_6 0x01 +#define SD_RSP_LEN_17 0x02 +#define SD_RSP_TYPE_R0 0x04 +#define SD_RSP_TYPE_R1 0x01 +#define SD_RSP_TYPE_R1b 0x09 +#define SD_RSP_TYPE_R2 0x02 +#define SD_RSP_TYPE_R3 0x05 +#define SD_RSP_TYPE_R4 0x05 +#define SD_RSP_TYPE_R5 0x01 +#define SD_RSP_TYPE_R6 0x01 +#define SD_RSP_TYPE_R7 0x01 #define SD_CFG3 0xFDA2 +#define SD_RSP_80CLK_TIMEOUT_EN 0x01 + #define SD_STAT1 0xFDA3 +#define SD_CRC7_ERR 0x80 +#define SD_CRC16_ERR 0x40 +#define SD_CRC_WRITE_ERR 0x20 +#define SD_CRC_WRITE_ERR_MASK 0x1C +#define GET_CRC_TIME_OUT 0x02 +#define SD_TUNING_COMPARE_ERR 0x01 #define SD_STAT2 0xFDA4 +#define SD_RSP_80CLK_TIMEOUT 0x01 + #define SD_BUS_STAT 0xFDA5 +#define SD_CLK_TOGGLE_EN 0x80 +#define SD_CLK_FORCE_STOP 0x40 +#define SD_DAT3_STATUS 0x10 +#define SD_DAT2_STATUS 0x08 +#define SD_DAT1_STATUS 0x04 +#define SD_DAT0_STATUS 0x02 +#define SD_CMD_STATUS 0x01 #define SD_PAD_CTL 0xFDA6 +#define SD_IO_USING_1V8 0x80 +#define SD_IO_USING_3V3 0x7F +#define TYPE_A_DRIVING 0x00 +#define TYPE_B_DRIVING 0x01 +#define TYPE_C_DRIVING 0x02 +#define TYPE_D_DRIVING 0x03 #define SD_SAMPLE_POINT_CTL 0xFDA7 +#define DDR_FIX_RX_DAT 0x00 +#define DDR_VAR_RX_DAT 0x80 +#define DDR_FIX_RX_DAT_EDGE 0x00 +#define DDR_FIX_RX_DAT_14_DELAY 0x40 +#define DDR_FIX_RX_CMD 0x00 +#define DDR_VAR_RX_CMD 0x20 +#define DDR_FIX_RX_CMD_POS_EDGE 0x00 +#define DDR_FIX_RX_CMD_14_DELAY 0x10 +#define SD20_RX_POS_EDGE 0x00 +#define SD20_RX_14_DELAY 0x08 +#define SD20_RX_SEL_MASK 0x08 #define SD_PUSH_POINT_CTL 0xFDA8 +#define DDR_FIX_TX_CMD_DAT 0x00 +#define DDR_VAR_TX_CMD_DAT 0x80 +#define DDR_FIX_TX_DAT_14_TSU 0x00 +#define DDR_FIX_TX_DAT_12_TSU 0x40 +#define DDR_FIX_TX_CMD_NEG_EDGE 0x00 +#define DDR_FIX_TX_CMD_14_AHEAD 0x20 +#define SD20_TX_NEG_EDGE 0x00 +#define SD20_TX_14_AHEAD 0x10 +#define SD20_TX_SEL_MASK 0x10 +#define DDR_VAR_SDCLK_POL_SWAP 0x01 #define SD_CMD0 0xFDA9 #define SD_CMD_START 0x40 #define SD_CMD1 0xFDAA @@ -569,14 +288,46 @@ #define SD_BLOCK_CNT_L 0xFDB1 #define SD_BLOCK_CNT_H 0xFDB2 #define SD_TRANSFER 0xFDB3 +#define SD_TRANSFER_START 0x80 +#define SD_TRANSFER_END 0x40 +#define SD_STAT_IDLE 0x20 +#define SD_TRANSFER_ERR 0x10 +#define SD_TM_NORMAL_WRITE 0x00 +#define SD_TM_AUTO_WRITE_3 0x01 +#define SD_TM_AUTO_WRITE_4 0x02 +#define SD_TM_AUTO_READ_3 0x05 +#define SD_TM_AUTO_READ_4 0x06 +#define SD_TM_CMD_RSP 0x08 +#define SD_TM_AUTO_WRITE_1 0x09 +#define SD_TM_AUTO_WRITE_2 0x0A +#define SD_TM_NORMAL_READ 0x0C +#define SD_TM_AUTO_READ_1 0x0D +#define SD_TM_AUTO_READ_2 0x0E +#define SD_TM_AUTO_TUNING 0x0F #define SD_CMD_STATE 0xFDB5 +#define SD_CMD_IDLE 0x80 + #define SD_DATA_STATE 0xFDB6 +#define SD_DATA_IDLE 0x80 #define SRCTL 0xFC13 #define DCM_DRP_CTL 0xFC23 +#define DCM_RESET 0x08 +#define DCM_LOCKED 0x04 +#define DCM_208M 0x00 +#define DCM_TX 0x01 +#define DCM_RX 0x02 #define DCM_DRP_TRIG 0xFC24 +#define DRP_START 0x80 +#define DRP_DONE 0x40 #define DCM_DRP_CFG 0xFC25 +#define DRP_WRITE 0x80 +#define DRP_READ 0x00 +#define DCM_WRITE_ADDRESS_50 0x50 +#define DCM_WRITE_ADDRESS_51 0x51 +#define DCM_READ_ADDRESS_00 0x00 +#define DCM_READ_ADDRESS_51 0x51 #define DCM_DRP_WR_DATA_L 0xFC26 #define DCM_DRP_WR_DATA_H 0xFC27 #define DCM_DRP_RD_DATA_L 0xFC28 @@ -587,42 +338,153 @@ #define SD_DCMPS1_CTL 0xFC2D #define SD_VPTX_CTL SD_VPCLK0_CTL #define SD_VPRX_CTL SD_VPCLK1_CTL +#define PHASE_CHANGE 0x80 +#define PHASE_NOT_RESET 0x40 #define SD_DCMPS_TX_CTL SD_DCMPS0_CTL #define SD_DCMPS_RX_CTL SD_DCMPS1_CTL +#define DCMPS_CHANGE 0x80 +#define DCMPS_CHANGE_DONE 0x40 +#define DCMPS_ERROR 0x20 +#define DCMPS_CURRENT_PHASE 0x1F #define CARD_CLK_SOURCE 0xFC2E - +#define CRC_FIX_CLK (0x00 << 0) +#define CRC_VAR_CLK0 (0x01 << 0) +#define CRC_VAR_CLK1 (0x02 << 0) +#define SD30_FIX_CLK (0x00 << 2) +#define SD30_VAR_CLK0 (0x01 << 2) +#define SD30_VAR_CLK1 (0x02 << 2) +#define SAMPLE_FIX_CLK (0x00 << 4) +#define SAMPLE_VAR_CLK0 (0x01 << 4) +#define SAMPLE_VAR_CLK1 (0x02 << 4) #define CARD_PWR_CTL 0xFD50 +#define PMOS_STRG_MASK 0x10 +#define PMOS_STRG_800mA 0x10 +#define PMOS_STRG_400mA 0x00 +#define SD_POWER_OFF 0x03 +#define SD_PARTIAL_POWER_ON 0x01 +#define SD_POWER_ON 0x00 +#define SD_POWER_MASK 0x03 +#define MS_POWER_OFF 0x0C +#define MS_PARTIAL_POWER_ON 0x04 +#define MS_POWER_ON 0x00 +#define MS_POWER_MASK 0x0C +#define BPP_POWER_OFF 0x0F +#define BPP_POWER_5_PERCENT_ON 0x0E +#define BPP_POWER_10_PERCENT_ON 0x0C +#define BPP_POWER_15_PERCENT_ON 0x08 +#define BPP_POWER_ON 0x00 +#define BPP_POWER_MASK 0x0F +#define SD_VCC_PARTIAL_POWER_ON 0x02 +#define SD_VCC_POWER_ON 0x00 #define CARD_CLK_SWITCH 0xFD51 #define RTL8411B_PACKAGE_MODE 0xFD51 #define CARD_SHARE_MODE 0xFD52 +#define CARD_SHARE_MASK 0x0F +#define CARD_SHARE_MULTI_LUN 0x00 +#define CARD_SHARE_NORMAL 0x00 +#define CARD_SHARE_48_SD 0x04 +#define CARD_SHARE_48_MS 0x08 +#define CARD_SHARE_BAROSSA_SD 0x01 +#define CARD_SHARE_BAROSSA_MS 0x02 #define CARD_DRIVE_SEL 0xFD53 +#define MS_DRIVE_8mA (0x01 << 6) +#define MMC_DRIVE_8mA (0x01 << 4) +#define XD_DRIVE_8mA (0x01 << 2) +#define GPIO_DRIVE_8mA 0x01 +#define RTS5209_CARD_DRIVE_DEFAULT (MS_DRIVE_8mA | MMC_DRIVE_8mA |\ + XD_DRIVE_8mA | GPIO_DRIVE_8mA) +#define RTL8411_CARD_DRIVE_DEFAULT (MS_DRIVE_8mA | MMC_DRIVE_8mA |\ + XD_DRIVE_8mA) +#define RTSX_CARD_DRIVE_DEFAULT (MS_DRIVE_8mA | GPIO_DRIVE_8mA) + #define CARD_STOP 0xFD54 +#define SPI_STOP 0x01 +#define XD_STOP 0x02 +#define SD_STOP 0x04 +#define MS_STOP 0x08 +#define SPI_CLR_ERR 0x10 +#define XD_CLR_ERR 0x20 +#define SD_CLR_ERR 0x40 +#define MS_CLR_ERR 0x80 #define CARD_OE 0xFD55 +#define SD_OUTPUT_EN 0x04 +#define MS_OUTPUT_EN 0x08 #define CARD_AUTO_BLINK 0xFD56 #define CARD_GPIO_DIR 0xFD57 #define CARD_GPIO 0xFD58 #define CARD_DATA_SOURCE 0xFD5B +#define PINGPONG_BUFFER 0x01 +#define RING_BUFFER 0x00 #define SD30_CLK_DRIVE_SEL 0xFD5A +#define DRIVER_TYPE_A 0x05 +#define DRIVER_TYPE_B 0x03 +#define DRIVER_TYPE_C 0x02 +#define DRIVER_TYPE_D 0x01 #define CARD_SELECT 0xFD5C +#define SD_MOD_SEL 2 +#define MS_MOD_SEL 3 #define SD30_DRIVE_SEL 0xFD5E +#define CFG_DRIVER_TYPE_A 0x02 +#define CFG_DRIVER_TYPE_B 0x03 +#define CFG_DRIVER_TYPE_C 0x01 +#define CFG_DRIVER_TYPE_D 0x00 #define SD30_CMD_DRIVE_SEL 0xFD5E #define SD30_DAT_DRIVE_SEL 0xFD5F #define CARD_CLK_EN 0xFD69 +#define SD_CLK_EN 0x04 +#define MS_CLK_EN 0x08 #define SDIO_CTRL 0xFD6B #define CD_PAD_CTL 0xFD73 - +#define CD_DISABLE_MASK 0x07 +#define MS_CD_DISABLE 0x04 +#define SD_CD_DISABLE 0x02 +#define XD_CD_DISABLE 0x01 +#define CD_DISABLE 0x07 +#define CD_ENABLE 0x00 +#define MS_CD_EN_ONLY 0x03 +#define SD_CD_EN_ONLY 0x05 +#define XD_CD_EN_ONLY 0x06 +#define FORCE_CD_LOW_MASK 0x38 +#define FORCE_CD_XD_LOW 0x08 +#define FORCE_CD_SD_LOW 0x10 +#define FORCE_CD_MS_LOW 0x20 +#define CD_AUTO_DISABLE 0x40 #define FPDCTL 0xFC00 +#define SSC_POWER_DOWN 0x01 +#define SD_OC_POWER_DOWN 0x02 +#define ALL_POWER_DOWN 0x07 +#define OC_POWER_DOWN 0x06 #define PDINFO 0xFC01 #define CLK_CTL 0xFC02 +#define CHANGE_CLK 0x01 +#define CLK_LOW_FREQ 0x01 + #define CLK_DIV 0xFC03 +#define CLK_DIV_1 0x01 +#define CLK_DIV_2 0x02 +#define CLK_DIV_4 0x03 +#define CLK_DIV_8 0x04 #define CLK_SEL 0xFC04 #define SSC_DIV_N_0 0xFC0F #define SSC_DIV_N_1 0xFC10 #define SSC_CTL1 0xFC11 +#define SSC_RSTB 0x80 +#define SSC_8X_EN 0x40 +#define SSC_FIX_FRAC 0x20 +#define SSC_SEL_1M 0x00 +#define SSC_SEL_2M 0x08 +#define SSC_SEL_4M 0x10 +#define SSC_SEL_8M 0x18 #define SSC_CTL2 0xFC12 - +#define SSC_DEPTH_MASK 0x07 +#define SSC_DEPTH_DISALBE 0x00 +#define SSC_DEPTH_4M 0x01 +#define SSC_DEPTH_2M 0x02 +#define SSC_DEPTH_1M 0x03 +#define SSC_DEPTH_500K 0x04 +#define SSC_DEPTH_250K 0x05 #define RCCTL 0xFC14 #define FPGA_PULL_CTL 0xFC1D @@ -630,6 +492,24 @@ #define GPIO_CTL 0xFC1F #define LDO_CTL 0xFC1E +#define BPP_ASIC_1V7 0x00 +#define BPP_ASIC_1V8 0x01 +#define BPP_ASIC_1V9 0x02 +#define BPP_ASIC_2V0 0x03 +#define BPP_ASIC_2V7 0x04 +#define BPP_ASIC_2V8 0x05 +#define BPP_ASIC_3V2 0x06 +#define BPP_ASIC_3V3 0x07 +#define BPP_REG_TUNED18 0x07 +#define BPP_TUNED18_SHIFT_8402 5 +#define BPP_TUNED18_SHIFT_8411 4 +#define BPP_PAD_MASK 0x04 +#define BPP_PAD_3V3 0x04 +#define BPP_PAD_1V8 0x00 +#define BPP_LDO_POWB 0x03 +#define BPP_LDO_ON 0x00 +#define BPP_LDO_SUSPEND 0x02 +#define BPP_LDO_OFF 0x03 #define SYS_VER 0xFC32 #define CARD_PULL_CTL1 0xFD60 @@ -642,6 +522,10 @@ /* PCI Express Related Registers */ #define IRQEN0 0xFE20 #define IRQSTAT0 0xFE21 +#define DMA_DONE_INT 0x80 +#define SUSPEND_INT 0x40 +#define LINK_RDY_INT 0x20 +#define LINK_DOWN_INT 0x10 #define IRQEN1 0xFE22 #define IRQSTAT1 0xFE23 #define TLPRIEN 0xFE24 @@ -653,6 +537,16 @@ #define DMATC2 0xFE2A #define DMATC3 0xFE2B #define DMACTL 0xFE2C +#define DMA_RST 0x80 +#define DMA_BUSY 0x04 +#define DMA_DIR_TO_CARD 0x00 +#define DMA_DIR_FROM_CARD 0x02 +#define DMA_EN 0x01 +#define DMA_128 (0 << 4) +#define DMA_256 (1 << 4) +#define DMA_512 (2 << 4) +#define DMA_1024 (3 << 4) +#define DMA_PACK_SIZE_MASK 0x30 #define BCTL 0xFE2D #define RBBC0 0xFE2E #define RBBC1 0xFE2F @@ -693,11 +587,19 @@ #define RESET_LOAD_REG 0xFE5E #define EFUSE_CONTENT 0xFE5F #define HOST_SLEEP_STATE 0xFE60 +#define HOST_ENTER_S1 1 +#define HOST_ENTER_S3 2 + #define SDIO_CFG 0xFE70 #define NFTS_TX_CTRL 0xFE72 #define PWR_GATE_CTRL 0xFE75 +#define PWR_GATE_EN 0x01 +#define LDO3318_PWR_MASK 0x06 +#define LDO_ON 0x00 +#define LDO_SUSPEND 0x04 +#define LDO_OFF 0x06 #define PWD_SUSPEND_EN 0xFE76 #define LDO_PWR_SEL 0xFE78 -- cgit v1.2.3 From 9e33ce79f828eb5a1bb9dd4830c7fa719d4279dc Mon Sep 17 00:00:00 2001 From: Micky Ching Date: Wed, 25 Feb 2015 13:50:10 +0800 Subject: mfd: rtsx: Update PETXCFG address PETXCFG is defined at 0xFF03, the old 0xFE49 not used any more. Signed-off-by: Micky Ching Signed-off-by: Lee Jones --- drivers/mfd/rts5227.c | 6 ++---- drivers/mfd/rts5249.c | 6 ++---- include/linux/mfd/rtsx_pci.h | 2 +- 3 files changed, 5 insertions(+), 9 deletions(-) (limited to 'include') diff --git a/drivers/mfd/rts5227.c b/drivers/mfd/rts5227.c index 32407404d838..1f387d4cec6c 100644 --- a/drivers/mfd/rts5227.c +++ b/drivers/mfd/rts5227.c @@ -118,11 +118,9 @@ static int rts5227_extra_init_hw(struct rtsx_pcr *pcr) rts5227_fill_driving(pcr, OUTPUT_3V3); /* Configure force_clock_req */ if (pcr->flags & PCR_REVERSE_SOCKET) - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, - AUTOLOAD_CFG_BASE + 3, 0xB8, 0xB8); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, PETXCFG, 0xB8, 0xB8); else - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, - AUTOLOAD_CFG_BASE + 3, 0xB8, 0x88); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, PETXCFG, 0xB8, 0x88); rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, PM_CTRL3, 0x10, 0x00); return rtsx_pci_send_cmd(pcr, 100); diff --git a/drivers/mfd/rts5249.c b/drivers/mfd/rts5249.c index cf425cc959d5..225ad5527d3e 100644 --- a/drivers/mfd/rts5249.c +++ b/drivers/mfd/rts5249.c @@ -116,11 +116,9 @@ static int rts5249_extra_init_hw(struct rtsx_pcr *pcr) /* Configure driving */ rts5249_fill_driving(pcr, OUTPUT_3V3); if (pcr->flags & PCR_REVERSE_SOCKET) - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, - AUTOLOAD_CFG_BASE + 3, 0xB0, 0xB0); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, PETXCFG, 0xB0, 0xB0); else - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, - AUTOLOAD_CFG_BASE + 3, 0xB0, 0x80); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, PETXCFG, 0xB0, 0x80); rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, PM_CTRL3, 0x10, 0x00); return rtsx_pci_send_cmd(pcr, 100); diff --git a/include/linux/mfd/rtsx_pci.h b/include/linux/mfd/rtsx_pci.h index e81f2bbfcda0..87cff60953f6 100644 --- a/include/linux/mfd/rtsx_pci.h +++ b/include/linux/mfd/rtsx_pci.h @@ -572,7 +572,6 @@ #define MSGTXDATA2 0xFE46 #define MSGTXDATA3 0xFE47 #define MSGTXCTL 0xFE48 -#define PETXCFG 0xFE49 #define LTR_CTL 0xFE4A #define OBFF_CFG 0xFE4C @@ -606,6 +605,7 @@ #define DUMMY_REG_RESET_0 0xFE90 #define AUTOLOAD_CFG_BASE 0xFF00 +#define PETXCFG 0xFF03 #define PM_CTRL1 0xFF44 #define PM_CTRL2 0xFF45 -- cgit v1.2.3 From b038538104d5b033d3beaffba6d6efe01246930b Mon Sep 17 00:00:00 2001 From: Micky Ching Date: Wed, 25 Feb 2015 13:50:12 +0800 Subject: mfd: rtsx: Update phy register Update some phy register name and value for rts5249, the updated value makes chip more stable on some platform. Signed-off-by: Micky Ching Signed-off-by: Lee Jones --- drivers/mfd/rts5249.c | 29 +++++++----- include/linux/mfd/rtsx_pci.h | 109 ++++++++++++++++++++++--------------------- 2 files changed, 72 insertions(+), 66 deletions(-) (limited to 'include') diff --git a/drivers/mfd/rts5249.c b/drivers/mfd/rts5249.c index 2fe2854fb450..8de822048ff2 100644 --- a/drivers/mfd/rts5249.c +++ b/drivers/mfd/rts5249.c @@ -132,11 +132,12 @@ static int rts5249_optimize_phy(struct rtsx_pcr *pcr) if (err < 0) return err; - err = rtsx_pci_write_phy_register(pcr, PHY_REG_REV, - PHY_REG_REV_RESV | PHY_REG_REV_RXIDLE_LATCHED | - PHY_REG_REV_P1_EN | PHY_REG_REV_RXIDLE_EN | - PHY_REG_REV_RX_PWST | PHY_REG_REV_CLKREQ_DLY_TIMER_1_0 | - PHY_REG_REV_STOP_CLKRD | PHY_REG_REV_STOP_CLKWR); + err = rtsx_pci_write_phy_register(pcr, PHY_REV, + PHY_REV_RESV | PHY_REV_RXIDLE_LATCHED | + PHY_REV_P1_EN | PHY_REV_RXIDLE_EN | + PHY_REV_CLKREQ_TX_EN | PHY_REV_RX_PWST | + PHY_REV_CLKREQ_DT_1_0 | PHY_REV_STOP_CLKRD | + PHY_REV_STOP_CLKWR); if (err < 0) return err; @@ -147,19 +148,21 @@ static int rts5249_optimize_phy(struct rtsx_pcr *pcr) PHY_BPCR_IB_FILTER | PHY_BPCR_CMIRROR_EN); if (err < 0) return err; + err = rtsx_pci_write_phy_register(pcr, PHY_PCR, PHY_PCR_FORCE_CODE | PHY_PCR_OOBS_CALI_50 | PHY_PCR_OOBS_VCM_08 | PHY_PCR_OOBS_SEN_90 | - PHY_PCR_RSSI_EN); + PHY_PCR_RSSI_EN | PHY_PCR_RX10K); if (err < 0) return err; + err = rtsx_pci_write_phy_register(pcr, PHY_RCR2, PHY_RCR2_EMPHASE_EN | PHY_RCR2_NADJR | - PHY_RCR2_CDR_CP_10 | PHY_RCR2_CDR_SR_2 | - PHY_RCR2_FREQSEL_12 | PHY_RCR2_CPADJEN | - PHY_RCR2_CDR_SC_8 | PHY_RCR2_CALIB_LATE); + PHY_RCR2_CDR_SR_2 | PHY_RCR2_FREQSEL_12 | + PHY_RCR2_CDR_SC_12P | PHY_RCR2_CALIB_LATE); if (err < 0) return err; + err = rtsx_pci_write_phy_register(pcr, PHY_FLD4, PHY_FLD4_FLDEN_SEL | PHY_FLD4_REQ_REF | PHY_FLD4_RXAMP_OFF | PHY_FLD4_REQ_ADDA | @@ -167,11 +170,12 @@ static int rts5249_optimize_phy(struct rtsx_pcr *pcr) PHY_FLD4_BER_CHK_EN); if (err < 0) return err; - err = rtsx_pci_write_phy_register(pcr, PHY_RDR, PHY_RDR_RXDSEL_1_9); + err = rtsx_pci_write_phy_register(pcr, PHY_RDR, + PHY_RDR_RXDSEL_1_9 | PHY_SSC_AUTO_PWD); if (err < 0) return err; err = rtsx_pci_write_phy_register(pcr, PHY_RCR1, - PHY_RCR1_ADP_TIME | PHY_RCR1_VCO_COARSE); + PHY_RCR1_ADP_TIME_4 | PHY_RCR1_VCO_COARSE); if (err < 0) return err; err = rtsx_pci_write_phy_register(pcr, PHY_FLD3, @@ -179,10 +183,11 @@ static int rts5249_optimize_phy(struct rtsx_pcr *pcr) PHY_FLD3_RXDELINK); if (err < 0) return err; + return rtsx_pci_write_phy_register(pcr, PHY_TUNE, PHY_TUNE_TUNEREF_1_0 | PHY_TUNE_VBGSEL_1252 | PHY_TUNE_SDBUS_33 | PHY_TUNE_TUNED18 | - PHY_TUNE_TUNED12); + PHY_TUNE_TUNED12 | PHY_TUNE_TUNEA12); } static int rts5249_turn_on_led(struct rtsx_pcr *pcr) diff --git a/include/linux/mfd/rtsx_pci.h b/include/linux/mfd/rtsx_pci.h index 87cff60953f6..0103210ec3e1 100644 --- a/include/linux/mfd/rtsx_pci.h +++ b/include/linux/mfd/rtsx_pci.h @@ -630,16 +630,47 @@ /* Phy register */ #define PHY_PCR 0x00 +#define PHY_PCR_FORCE_CODE 0xB000 +#define PHY_PCR_OOBS_CALI_50 0x0800 +#define PHY_PCR_OOBS_VCM_08 0x0200 +#define PHY_PCR_OOBS_SEN_90 0x0040 +#define PHY_PCR_RSSI_EN 0x0002 +#define PHY_PCR_RX10K 0x0001 + #define PHY_RCR0 0x01 #define PHY_RCR1 0x02 +#define PHY_RCR1_ADP_TIME_4 0x0400 +#define PHY_RCR1_VCO_COARSE 0x001F + #define PHY_RCR2 0x03 +#define PHY_RCR2_EMPHASE_EN 0x8000 +#define PHY_RCR2_NADJR 0x4000 +#define PHY_RCR2_CDR_SR_2 0x0100 +#define PHY_RCR2_FREQSEL_12 0x0040 +#define PHY_RCR2_CDR_SC_12P 0x0010 +#define PHY_RCR2_CALIB_LATE 0x0002 + #define PHY_RTCR 0x04 #define PHY_RDR 0x05 +#define PHY_RDR_RXDSEL_1_9 0x4000 +#define PHY_SSC_AUTO_PWD 0x0600 #define PHY_TCR0 0x06 #define PHY_TCR1 0x07 #define PHY_TUNE 0x08 +#define PHY_TUNE_TUNEREF_1_0 0x4000 +#define PHY_TUNE_VBGSEL_1252 0x0C00 +#define PHY_TUNE_SDBUS_33 0x0200 +#define PHY_TUNE_TUNED18 0x01C0 +#define PHY_TUNE_TUNED12 0X0020 +#define PHY_TUNE_TUNEA12 0x0004 + #define PHY_IMR 0x09 #define PHY_BPCR 0x0A +#define PHY_BPCR_IBRXSEL 0x0400 +#define PHY_BPCR_IBTXSEL 0x0100 +#define PHY_BPCR_IB_FILTER 0x0080 +#define PHY_BPCR_CMIRROR_EN 0x0040 + #define PHY_BIST 0x0B #define PHY_RAW_L 0x0C #define PHY_RAW_H 0x0D @@ -654,12 +685,35 @@ #define PHY_BPNR 0x16 #define PHY_BRNR2 0x17 #define PHY_BENR 0x18 -#define PHY_REG_REV 0x19 +#define PHY_REV 0x19 +#define PHY_REV_RESV 0xE000 +#define PHY_REV_RXIDLE_LATCHED 0x1000 +#define PHY_REV_P1_EN 0x0800 +#define PHY_REV_RXIDLE_EN 0x0400 +#define PHY_REV_CLKREQ_TX_EN 0x0200 +#define PHY_REV_CLKREQ_RX_EN 0x0100 +#define PHY_REV_CLKREQ_DT_1_0 0x0040 +#define PHY_REV_STOP_CLKRD 0x0020 +#define PHY_REV_RX_PWST 0x0008 +#define PHY_REV_STOP_CLKWR 0x0004 + #define PHY_FLD0 0x1A #define PHY_FLD1 0x1B #define PHY_FLD2 0x1C #define PHY_FLD3 0x1D +#define PHY_FLD3_TIMER_4 0x0800 +#define PHY_FLD3_TIMER_6 0x0020 +#define PHY_FLD3_RXDELINK 0x0004 + #define PHY_FLD4 0x1E +#define PHY_FLD4_FLDEN_SEL 0x4000 +#define PHY_FLD4_REQ_REF 0x2000 +#define PHY_FLD4_RXAMP_OFF 0x1000 +#define PHY_FLD4_REQ_ADDA 0x0800 +#define PHY_FLD4_BER_COUNT 0x00E0 +#define PHY_FLD4_BER_TIMER 0x000A +#define PHY_FLD4_BER_CHK_EN 0x0001 + #define PHY_DUM_REG 0x1F #define LCTLR 0x80 @@ -675,59 +729,6 @@ #define PCR_SETTING_REG2 0x814 #define PCR_SETTING_REG3 0x747 -/* Phy bits */ -#define PHY_PCR_FORCE_CODE 0xB000 -#define PHY_PCR_OOBS_CALI_50 0x0800 -#define PHY_PCR_OOBS_VCM_08 0x0200 -#define PHY_PCR_OOBS_SEN_90 0x0040 -#define PHY_PCR_RSSI_EN 0x0002 - -#define PHY_RCR1_ADP_TIME 0x0100 -#define PHY_RCR1_VCO_COARSE 0x001F - -#define PHY_RCR2_EMPHASE_EN 0x8000 -#define PHY_RCR2_NADJR 0x4000 -#define PHY_RCR2_CDR_CP_10 0x0400 -#define PHY_RCR2_CDR_SR_2 0x0100 -#define PHY_RCR2_FREQSEL_12 0x0040 -#define PHY_RCR2_CPADJEN 0x0020 -#define PHY_RCR2_CDR_SC_8 0x0008 -#define PHY_RCR2_CALIB_LATE 0x0002 - -#define PHY_RDR_RXDSEL_1_9 0x4000 - -#define PHY_TUNE_TUNEREF_1_0 0x4000 -#define PHY_TUNE_VBGSEL_1252 0x0C00 -#define PHY_TUNE_SDBUS_33 0x0200 -#define PHY_TUNE_TUNED18 0x01C0 -#define PHY_TUNE_TUNED12 0X0020 - -#define PHY_BPCR_IBRXSEL 0x0400 -#define PHY_BPCR_IBTXSEL 0x0100 -#define PHY_BPCR_IB_FILTER 0x0080 -#define PHY_BPCR_CMIRROR_EN 0x0040 - -#define PHY_REG_REV_RESV 0xE000 -#define PHY_REG_REV_RXIDLE_LATCHED 0x1000 -#define PHY_REG_REV_P1_EN 0x0800 -#define PHY_REG_REV_RXIDLE_EN 0x0400 -#define PHY_REG_REV_CLKREQ_DLY_TIMER_1_0 0x0040 -#define PHY_REG_REV_STOP_CLKRD 0x0020 -#define PHY_REG_REV_RX_PWST 0x0008 -#define PHY_REG_REV_STOP_CLKWR 0x0004 - -#define PHY_FLD3_TIMER_4 0x7800 -#define PHY_FLD3_TIMER_6 0x00E0 -#define PHY_FLD3_RXDELINK 0x0004 - -#define PHY_FLD4_FLDEN_SEL 0x4000 -#define PHY_FLD4_REQ_REF 0x2000 -#define PHY_FLD4_RXAMP_OFF 0x1000 -#define PHY_FLD4_REQ_ADDA 0x0800 -#define PHY_FLD4_BER_COUNT 0x00E0 -#define PHY_FLD4_BER_TIMER 0x000A -#define PHY_FLD4_BER_CHK_EN 0x0001 - #define rtsx_pci_init_cmd(pcr) ((pcr)->ci = 0) struct rtsx_pcr; -- cgit v1.2.3 From 19f3bd548f2750a8a7e4e6d2f25fdc5f8e2c3ee9 Mon Sep 17 00:00:00 2001 From: Micky Ching Date: Wed, 25 Feb 2015 13:50:13 +0800 Subject: mfd: rtsx: Remove LCTLR defination To enable/disable ASPM we should find LINK CONTROL register in PCI config space. All old chip use 0x80 address, but new chip may use another address, so we using pci_find_capability() to get LINK CONTROL address. rtsx_gops.c was removed, we consider to put some common operations to this file, but the actual thing is, only a group of chips are in common ops1, and another group of chips in common ops2, it is hard to decide put which ops into generic ops file. Signed-off-by: Micky Ching Signed-off-by: Lee Jones --- drivers/mfd/Makefile | 2 +- drivers/mfd/rts5227.c | 2 +- drivers/mfd/rts5249.c | 3 +-- drivers/mfd/rtsx_gops.c | 37 ------------------------------------- drivers/mfd/rtsx_pcr.c | 22 +++++++++++++++++----- include/linux/mfd/rtsx_pci.h | 10 +--------- 6 files changed, 21 insertions(+), 55 deletions(-) delete mode 100644 drivers/mfd/rtsx_gops.c (limited to 'include') diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index 19f3d744e3bd..c8bb34929903 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -13,7 +13,7 @@ obj-$(CONFIG_MFD_CROS_EC) += cros_ec.o obj-$(CONFIG_MFD_CROS_EC_I2C) += cros_ec_i2c.o obj-$(CONFIG_MFD_CROS_EC_SPI) += cros_ec_spi.o -rtsx_pci-objs := rtsx_pcr.o rtsx_gops.o rts5209.o rts5229.o rtl8411.o rts5227.o rts5249.o +rtsx_pci-objs := rtsx_pcr.o rts5209.o rts5229.o rtl8411.o rts5227.o rts5249.o obj-$(CONFIG_MFD_RTSX_PCI) += rtsx_pci.o obj-$(CONFIG_MFD_RTSX_USB) += rtsx_usb.o diff --git a/drivers/mfd/rts5227.c b/drivers/mfd/rts5227.c index 1f387d4cec6c..0c0283154ab5 100644 --- a/drivers/mfd/rts5227.c +++ b/drivers/mfd/rts5227.c @@ -130,7 +130,7 @@ static int rts5227_optimize_phy(struct rtsx_pcr *pcr) { int err; - err = rtsx_gops_pm_reset(pcr); + err = rtsx_pci_write_register(pcr, PM_CTRL3, D3_DELINK_MODE_EN, 0x00); if (err < 0) return err; diff --git a/drivers/mfd/rts5249.c b/drivers/mfd/rts5249.c index 8de822048ff2..3c77058a70d2 100644 --- a/drivers/mfd/rts5249.c +++ b/drivers/mfd/rts5249.c @@ -119,7 +119,6 @@ static int rts5249_extra_init_hw(struct rtsx_pcr *pcr) rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, PETXCFG, 0xB0, 0xB0); else rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, PETXCFG, 0xB0, 0x80); - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, PM_CTRL3, 0x10, 0x00); return rtsx_pci_send_cmd(pcr, 100); } @@ -128,7 +127,7 @@ static int rts5249_optimize_phy(struct rtsx_pcr *pcr) { int err; - err = rtsx_gops_pm_reset(pcr); + err = rtsx_pci_write_register(pcr, PM_CTRL3, D3_DELINK_MODE_EN, 0x00); if (err < 0) return err; diff --git a/drivers/mfd/rtsx_gops.c b/drivers/mfd/rtsx_gops.c deleted file mode 100644 index b1a98c678593..000000000000 --- a/drivers/mfd/rtsx_gops.c +++ /dev/null @@ -1,37 +0,0 @@ -/* Driver for Realtek PCI-Express card reader - * - * Copyright(c) 2009-2013 Realtek Semiconductor Corp. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2, or (at your option) any - * later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, see . - * - * Author: - * Micky Ching - */ - -#include -#include "rtsx_pcr.h" - -int rtsx_gops_pm_reset(struct rtsx_pcr *pcr) -{ - int err; - - /* init aspm */ - rtsx_pci_write_register(pcr, ASPM_FORCE_CTL, 0xFF, 0x00); - err = rtsx_pci_update_cfg_byte(pcr, LCTLR, ~LCTLR_ASPM_CTL_MASK, 0x00); - if (err < 0) - return err; - - /* reset PM_CTRL3 before send buffer cmd */ - return rtsx_pci_write_register(pcr, PM_CTRL3, D3_DELINK_MODE_EN, 0x00); -} diff --git a/drivers/mfd/rtsx_pcr.c b/drivers/mfd/rtsx_pcr.c index 30f7ca89a0e6..81b9c2c2e0f1 100644 --- a/drivers/mfd/rtsx_pcr.c +++ b/drivers/mfd/rtsx_pcr.c @@ -63,6 +63,18 @@ static const struct pci_device_id rtsx_pci_ids[] = { MODULE_DEVICE_TABLE(pci, rtsx_pci_ids); +static inline void rtsx_pci_enable_aspm(struct rtsx_pcr *pcr) +{ + rtsx_pci_update_cfg_byte(pcr, pcr->pcie_cap + PCI_EXP_LNKCTL, + 0xFC, pcr->aspm_en); +} + +static inline void rtsx_pci_disable_aspm(struct rtsx_pcr *pcr) +{ + rtsx_pci_update_cfg_byte(pcr, pcr->pcie_cap + PCI_EXP_LNKCTL, + 0xFC, 0); +} + void rtsx_pci_start_run(struct rtsx_pcr *pcr) { /* If pci device removed, don't queue idle work any more */ @@ -75,7 +87,7 @@ void rtsx_pci_start_run(struct rtsx_pcr *pcr) pcr->ops->enable_auto_blink(pcr); if (pcr->aspm_en) - rtsx_pci_write_config_byte(pcr, LCTLR, 0); + rtsx_pci_disable_aspm(pcr); } mod_delayed_work(system_wq, &pcr->idle_work, msecs_to_jiffies(200)); @@ -942,7 +954,7 @@ static void rtsx_pci_idle_work(struct work_struct *work) pcr->ops->turn_off_led(pcr); if (pcr->aspm_en) - rtsx_pci_write_config_byte(pcr, LCTLR, pcr->aspm_en); + rtsx_pci_enable_aspm(pcr); mutex_unlock(&pcr->pcr_mutex); } @@ -968,6 +980,7 @@ static int rtsx_pci_init_hw(struct rtsx_pcr *pcr) { int err; + pcr->pcie_cap = pci_find_capability(pcr->pci, PCI_CAP_ID_EXP); rtsx_pci_writel(pcr, RTSX_HCBAR, pcr->host_cmds_addr); rtsx_pci_enable_bus_int(pcr); @@ -980,6 +993,7 @@ static int rtsx_pci_init_hw(struct rtsx_pcr *pcr) /* Wait SSC power stable */ udelay(200); + rtsx_pci_disable_aspm(pcr); if (pcr->ops->optimize_phy) { err = pcr->ops->optimize_phy(pcr); if (err < 0) @@ -1028,10 +1042,8 @@ static int rtsx_pci_init_hw(struct rtsx_pcr *pcr) if (err < 0) return err; - rtsx_pci_write_config_byte(pcr, LCTLR, 0); - /* Enable clk_request_n to enable clock power management */ - rtsx_pci_write_config_byte(pcr, 0x81, 1); + rtsx_pci_write_config_byte(pcr, pcr->pcie_cap + PCI_EXP_LNKCTL + 1, 1); /* Enter L1 when host tx idle */ rtsx_pci_write_config_byte(pcr, 0x70F, 0x5B); diff --git a/include/linux/mfd/rtsx_pci.h b/include/linux/mfd/rtsx_pci.h index 0103210ec3e1..33cc63ced99e 100644 --- a/include/linux/mfd/rtsx_pci.h +++ b/include/linux/mfd/rtsx_pci.h @@ -716,15 +716,6 @@ #define PHY_DUM_REG 0x1F -#define LCTLR 0x80 -#define LCTLR_EXT_SYNC 0x80 -#define LCTLR_COMMON_CLOCK_CFG 0x40 -#define LCTLR_RETRAIN_LINK 0x20 -#define LCTLR_LINK_DISABLE 0x10 -#define LCTLR_RCB 0x08 -#define LCTLR_RESERVED 0x04 -#define LCTLR_ASPM_CTL_MASK 0x03 - #define PCR_SETTING_REG1 0x724 #define PCR_SETTING_REG2 0x814 #define PCR_SETTING_REG3 0x747 @@ -759,6 +750,7 @@ enum PDEV_STAT {PDEV_STAT_IDLE, PDEV_STAT_RUN}; struct rtsx_pcr { struct pci_dev *pci; unsigned int id; + int pcie_cap; /* pci resources */ unsigned long addr; -- cgit v1.2.3 From 663c425f2c8d87a433629f09c5afd0af7e7e550c Mon Sep 17 00:00:00 2001 From: Micky Ching Date: Wed, 25 Feb 2015 13:50:14 +0800 Subject: mfd: rtsx: Add support for rts524A add support for new chip rts524A. Signed-off-by: Micky Ching Signed-off-by: Lee Jones --- drivers/mfd/rts5249.c | 186 ++++++++++++++++++++++++++++++++++++------- drivers/mfd/rtsx_pcr.c | 25 +++++- drivers/mfd/rtsx_pcr.h | 7 ++ include/linux/mfd/rtsx_pci.h | 132 +++++++++++++++++++++++++++++- 4 files changed, 318 insertions(+), 32 deletions(-) (limited to 'include') diff --git a/drivers/mfd/rts5249.c b/drivers/mfd/rts5249.c index 3c77058a70d2..32be803b5327 100644 --- a/drivers/mfd/rts5249.c +++ b/drivers/mfd/rts5249.c @@ -65,15 +65,17 @@ static void rts5249_fill_driving(struct rtsx_pcr *pcr, u8 voltage) 0xFF, driving[drive_sel][2]); } -static void rts5249_fetch_vendor_settings(struct rtsx_pcr *pcr) +static void rtsx_base_fetch_vendor_settings(struct rtsx_pcr *pcr) { u32 reg; rtsx_pci_read_config_dword(pcr, PCR_SETTING_REG1, ®); dev_dbg(&(pcr->pci->dev), "Cfg 0x%x: 0x%x\n", PCR_SETTING_REG1, reg); - if (!rtsx_vendor_setting_valid(reg)) + if (!rtsx_vendor_setting_valid(reg)) { + pcr_dbg(pcr, "skip fetch vendor setting\n"); return; + } pcr->aspm_en = rtsx_reg_to_aspm(reg); pcr->sd30_drive_sel_1v8 = rtsx_reg_to_sd30_drive_sel_1v8(reg); @@ -87,7 +89,7 @@ static void rts5249_fetch_vendor_settings(struct rtsx_pcr *pcr) pcr->flags |= PCR_REVERSE_SOCKET; } -static void rts5249_force_power_down(struct rtsx_pcr *pcr, u8 pm_state) +static void rtsx_base_force_power_down(struct rtsx_pcr *pcr, u8 pm_state) { /* Set relink_time to 0 */ rtsx_pci_write_register(pcr, AUTOLOAD_CFG_BASE + 1, 0xFF, 0); @@ -95,7 +97,8 @@ static void rts5249_force_power_down(struct rtsx_pcr *pcr, u8 pm_state) rtsx_pci_write_register(pcr, AUTOLOAD_CFG_BASE + 3, 0x01, 0); if (pm_state == HOST_ENTER_S3) - rtsx_pci_write_register(pcr, PM_CTRL3, 0x10, 0x10); + rtsx_pci_write_register(pcr, pcr->reg_pm_ctrl3, + D3_DELINK_MODE_EN, D3_DELINK_MODE_EN); rtsx_pci_write_register(pcr, FPDCTL, 0x03, 0x03); } @@ -104,6 +107,8 @@ static int rts5249_extra_init_hw(struct rtsx_pcr *pcr) { rtsx_pci_init_cmd(pcr); + /* Rest L1SUB Config */ + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, L1SUB_CONFIG3, 0xFF, 0x00); /* Configure GPIO as output */ rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, GPIO_CTL, 0x02, 0x02); /* Reset ASPM state to default value */ @@ -189,27 +194,27 @@ static int rts5249_optimize_phy(struct rtsx_pcr *pcr) PHY_TUNE_TUNED12 | PHY_TUNE_TUNEA12); } -static int rts5249_turn_on_led(struct rtsx_pcr *pcr) +static int rtsx_base_turn_on_led(struct rtsx_pcr *pcr) { return rtsx_pci_write_register(pcr, GPIO_CTL, 0x02, 0x02); } -static int rts5249_turn_off_led(struct rtsx_pcr *pcr) +static int rtsx_base_turn_off_led(struct rtsx_pcr *pcr) { return rtsx_pci_write_register(pcr, GPIO_CTL, 0x02, 0x00); } -static int rts5249_enable_auto_blink(struct rtsx_pcr *pcr) +static int rtsx_base_enable_auto_blink(struct rtsx_pcr *pcr) { return rtsx_pci_write_register(pcr, OLT_LED_CTL, 0x08, 0x08); } -static int rts5249_disable_auto_blink(struct rtsx_pcr *pcr) +static int rtsx_base_disable_auto_blink(struct rtsx_pcr *pcr) { return rtsx_pci_write_register(pcr, OLT_LED_CTL, 0x08, 0x00); } -static int rts5249_card_power_on(struct rtsx_pcr *pcr, int card) +static int rtsx_base_card_power_on(struct rtsx_pcr *pcr, int card) { int err; @@ -236,7 +241,7 @@ static int rts5249_card_power_on(struct rtsx_pcr *pcr, int card) return 0; } -static int rts5249_card_power_off(struct rtsx_pcr *pcr, int card) +static int rtsx_base_card_power_off(struct rtsx_pcr *pcr, int card) { rtsx_pci_init_cmd(pcr); rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CARD_PWR_CTL, @@ -246,22 +251,35 @@ static int rts5249_card_power_off(struct rtsx_pcr *pcr, int card) return rtsx_pci_send_cmd(pcr, 100); } -static int rts5249_switch_output_voltage(struct rtsx_pcr *pcr, u8 voltage) +static int rtsx_base_switch_output_voltage(struct rtsx_pcr *pcr, u8 voltage) { int err; + u16 append; - if (voltage == OUTPUT_3V3) { - err = rtsx_pci_write_phy_register(pcr, PHY_TUNE, 0x4FC0 | 0x24); + switch (voltage) { + case OUTPUT_3V3: + err = rtsx_pci_update_phy(pcr, PHY_TUNE, PHY_TUNE_VOLTAGE_MASK, + PHY_TUNE_VOLTAGE_3V3); if (err < 0) return err; - } else if (voltage == OUTPUT_1V8) { - err = rtsx_pci_write_phy_register(pcr, PHY_BACR, 0x3C02); + break; + case OUTPUT_1V8: + append = PHY_TUNE_D18_1V8; + if (CHK_PCI_PID(pcr, 0x5249)) { + err = rtsx_pci_update_phy(pcr, PHY_BACR, + PHY_BACR_BASIC_MASK, 0); + if (err < 0) + return err; + append = PHY_TUNE_D18_1V7; + } + + err = rtsx_pci_update_phy(pcr, PHY_TUNE, PHY_TUNE_VOLTAGE_MASK, + append); if (err < 0) return err; - err = rtsx_pci_write_phy_register(pcr, PHY_TUNE, 0x4C40 | 0x24); - if (err < 0) - return err; - } else { + break; + default: + pcr_dbg(pcr, "unknown output voltage %d\n", voltage); return -EINVAL; } @@ -272,17 +290,17 @@ static int rts5249_switch_output_voltage(struct rtsx_pcr *pcr, u8 voltage) } static const struct pcr_ops rts5249_pcr_ops = { - .fetch_vendor_settings = rts5249_fetch_vendor_settings, + .fetch_vendor_settings = rtsx_base_fetch_vendor_settings, .extra_init_hw = rts5249_extra_init_hw, .optimize_phy = rts5249_optimize_phy, - .turn_on_led = rts5249_turn_on_led, - .turn_off_led = rts5249_turn_off_led, - .enable_auto_blink = rts5249_enable_auto_blink, - .disable_auto_blink = rts5249_disable_auto_blink, - .card_power_on = rts5249_card_power_on, - .card_power_off = rts5249_card_power_off, - .switch_output_voltage = rts5249_switch_output_voltage, - .force_power_down = rts5249_force_power_down, + .turn_on_led = rtsx_base_turn_on_led, + .turn_off_led = rtsx_base_turn_off_led, + .enable_auto_blink = rtsx_base_enable_auto_blink, + .disable_auto_blink = rtsx_base_disable_auto_blink, + .card_power_on = rtsx_base_card_power_on, + .card_power_off = rtsx_base_card_power_off, + .switch_output_voltage = rtsx_base_switch_output_voltage, + .force_power_down = rtsx_base_force_power_down, }; /* SD Pull Control Enable: @@ -356,4 +374,116 @@ void rts5249_init_params(struct rtsx_pcr *pcr) pcr->sd_pull_ctl_disable_tbl = rts5249_sd_pull_ctl_disable_tbl; pcr->ms_pull_ctl_enable_tbl = rts5249_ms_pull_ctl_enable_tbl; pcr->ms_pull_ctl_disable_tbl = rts5249_ms_pull_ctl_disable_tbl; + + pcr->reg_pm_ctrl3 = PM_CTRL3; +} + +static int rts524a_write_phy(struct rtsx_pcr *pcr, u8 addr, u16 val) +{ + addr = addr & 0x80 ? (addr & 0x7F) | 0x40 : addr; + + return __rtsx_pci_write_phy_register(pcr, addr, val); } + +static int rts524a_read_phy(struct rtsx_pcr *pcr, u8 addr, u16 *val) +{ + addr = addr & 0x80 ? (addr & 0x7F) | 0x40 : addr; + + return __rtsx_pci_read_phy_register(pcr, addr, val); +} + +static int rts524a_optimize_phy(struct rtsx_pcr *pcr) +{ + int err; + + err = rtsx_pci_write_register(pcr, RTS524A_PM_CTRL3, + D3_DELINK_MODE_EN, 0x00); + if (err < 0) + return err; + + rtsx_pci_write_phy_register(pcr, PHY_PCR, + PHY_PCR_FORCE_CODE | PHY_PCR_OOBS_CALI_50 | + PHY_PCR_OOBS_VCM_08 | PHY_PCR_OOBS_SEN_90 | PHY_PCR_RSSI_EN); + rtsx_pci_write_phy_register(pcr, PHY_SSCCR3, + PHY_SSCCR3_STEP_IN | PHY_SSCCR3_CHECK_DELAY); + + if (is_version(pcr, 0x524A, IC_VER_A)) { + rtsx_pci_write_phy_register(pcr, PHY_SSCCR3, + PHY_SSCCR3_STEP_IN | PHY_SSCCR3_CHECK_DELAY); + rtsx_pci_write_phy_register(pcr, PHY_SSCCR2, + PHY_SSCCR2_PLL_NCODE | PHY_SSCCR2_TIME0 | + PHY_SSCCR2_TIME2_WIDTH); + rtsx_pci_write_phy_register(pcr, PHY_ANA1A, + PHY_ANA1A_TXR_LOOPBACK | PHY_ANA1A_RXT_BIST | + PHY_ANA1A_TXR_BIST | PHY_ANA1A_REV); + rtsx_pci_write_phy_register(pcr, PHY_ANA1D, + PHY_ANA1D_DEBUG_ADDR); + rtsx_pci_write_phy_register(pcr, PHY_DIG1E, + PHY_DIG1E_REV | PHY_DIG1E_D0_X_D1 | + PHY_DIG1E_RX_ON_HOST | PHY_DIG1E_RCLK_REF_HOST | + PHY_DIG1E_RCLK_TX_EN_KEEP | + PHY_DIG1E_RCLK_TX_TERM_KEEP | + PHY_DIG1E_RCLK_RX_EIDLE_ON | PHY_DIG1E_TX_TERM_KEEP | + PHY_DIG1E_RX_TERM_KEEP | PHY_DIG1E_TX_EN_KEEP | + PHY_DIG1E_RX_EN_KEEP); + } + + rtsx_pci_write_phy_register(pcr, PHY_ANA08, + PHY_ANA08_RX_EQ_DCGAIN | PHY_ANA08_SEL_RX_EN | + PHY_ANA08_RX_EQ_VAL | PHY_ANA08_SCP | PHY_ANA08_SEL_IPI); + + return 0; +} + +static int rts524a_extra_init_hw(struct rtsx_pcr *pcr) +{ + rts5249_extra_init_hw(pcr); + + rtsx_pci_write_register(pcr, FUNC_FORCE_CTL, + FORCE_ASPM_L1_EN, FORCE_ASPM_L1_EN); + rtsx_pci_write_register(pcr, PM_EVENT_DEBUG, PME_DEBUG_0, PME_DEBUG_0); + rtsx_pci_write_register(pcr, LDO_VCC_CFG1, LDO_VCC_LMT_EN, + LDO_VCC_LMT_EN); + rtsx_pci_write_register(pcr, PCLK_CTL, PCLK_MODE_SEL, PCLK_MODE_SEL); + if (is_version(pcr, 0x524A, IC_VER_A)) { + rtsx_pci_write_register(pcr, LDO_DV18_CFG, + LDO_DV18_SR_MASK, LDO_DV18_SR_DF); + rtsx_pci_write_register(pcr, LDO_VCC_CFG1, + LDO_VCC_REF_TUNE_MASK, LDO_VCC_REF_1V2); + rtsx_pci_write_register(pcr, LDO_VIO_CFG, + LDO_VIO_REF_TUNE_MASK, LDO_VIO_REF_1V2); + rtsx_pci_write_register(pcr, LDO_VIO_CFG, + LDO_VIO_SR_MASK, LDO_VIO_SR_DF); + rtsx_pci_write_register(pcr, LDO_DV12S_CFG, + LDO_REF12_TUNE_MASK, LDO_REF12_TUNE_DF); + rtsx_pci_write_register(pcr, SD40_LDO_CTL1, + SD40_VIO_TUNE_MASK, SD40_VIO_TUNE_1V7); + } + + return 0; +} + +static const struct pcr_ops rts524a_pcr_ops = { + .write_phy = rts524a_write_phy, + .read_phy = rts524a_read_phy, + .fetch_vendor_settings = rtsx_base_fetch_vendor_settings, + .extra_init_hw = rts524a_extra_init_hw, + .optimize_phy = rts524a_optimize_phy, + .turn_on_led = rtsx_base_turn_on_led, + .turn_off_led = rtsx_base_turn_off_led, + .enable_auto_blink = rtsx_base_enable_auto_blink, + .disable_auto_blink = rtsx_base_disable_auto_blink, + .card_power_on = rtsx_base_card_power_on, + .card_power_off = rtsx_base_card_power_off, + .switch_output_voltage = rtsx_base_switch_output_voltage, + .force_power_down = rtsx_base_force_power_down, +}; + +void rts524a_init_params(struct rtsx_pcr *pcr) +{ + rts5249_init_params(pcr); + + pcr->reg_pm_ctrl3 = RTS524A_PM_CTRL3; + pcr->ops = &rts524a_pcr_ops; +} + diff --git a/drivers/mfd/rtsx_pcr.c b/drivers/mfd/rtsx_pcr.c index 81b9c2c2e0f1..e6d97adcc825 100644 --- a/drivers/mfd/rtsx_pcr.c +++ b/drivers/mfd/rtsx_pcr.c @@ -58,6 +58,7 @@ static const struct pci_device_id rtsx_pci_ids[] = { { PCI_DEVICE(0x10EC, 0x5249), PCI_CLASS_OTHERS << 16, 0xFF0000 }, { PCI_DEVICE(0x10EC, 0x5287), PCI_CLASS_OTHERS << 16, 0xFF0000 }, { PCI_DEVICE(0x10EC, 0x5286), PCI_CLASS_OTHERS << 16, 0xFF0000 }, + { PCI_DEVICE(0x10EC, 0x524A), PCI_CLASS_OTHERS << 16, 0xFF0000 }, { 0, } }; @@ -142,7 +143,7 @@ int rtsx_pci_read_register(struct rtsx_pcr *pcr, u16 addr, u8 *data) } EXPORT_SYMBOL_GPL(rtsx_pci_read_register); -int rtsx_pci_write_phy_register(struct rtsx_pcr *pcr, u8 addr, u16 val) +int __rtsx_pci_write_phy_register(struct rtsx_pcr *pcr, u8 addr, u16 val) { int err, i, finished = 0; u8 tmp; @@ -174,9 +175,17 @@ int rtsx_pci_write_phy_register(struct rtsx_pcr *pcr, u8 addr, u16 val) return 0; } + +int rtsx_pci_write_phy_register(struct rtsx_pcr *pcr, u8 addr, u16 val) +{ + if (pcr->ops->write_phy) + return pcr->ops->write_phy(pcr, addr, val); + + return __rtsx_pci_write_phy_register(pcr, addr, val); +} EXPORT_SYMBOL_GPL(rtsx_pci_write_phy_register); -int rtsx_pci_read_phy_register(struct rtsx_pcr *pcr, u8 addr, u16 *val) +int __rtsx_pci_read_phy_register(struct rtsx_pcr *pcr, u8 addr, u16 *val) { int err, i, finished = 0; u16 data; @@ -222,6 +231,14 @@ int rtsx_pci_read_phy_register(struct rtsx_pcr *pcr, u8 addr, u16 *val) return 0; } + +int rtsx_pci_read_phy_register(struct rtsx_pcr *pcr, u8 addr, u16 *val) +{ + if (pcr->ops->read_phy) + return pcr->ops->read_phy(pcr, addr, val); + + return __rtsx_pci_read_phy_register(pcr, addr, val); +} EXPORT_SYMBOL_GPL(rtsx_pci_read_phy_register); void rtsx_pci_stop_cmd(struct rtsx_pcr *pcr) @@ -1093,6 +1110,10 @@ static int rtsx_pci_init_chip(struct rtsx_pcr *pcr) rts5249_init_params(pcr); break; + case 0x524A: + rts524a_init_params(pcr); + break; + case 0x5287: rtl8411b_init_params(pcr); break; diff --git a/drivers/mfd/rtsx_pcr.h b/drivers/mfd/rtsx_pcr.h index fe2bbb67defc..e7daf6f54b83 100644 --- a/drivers/mfd/rtsx_pcr.h +++ b/drivers/mfd/rtsx_pcr.h @@ -27,12 +27,19 @@ #define MIN_DIV_N_PCR 80 #define MAX_DIV_N_PCR 208 +#define RTS524A_PME_FORCE_CTL 0xFF78 +#define RTS524A_PM_CTRL3 0xFF7E + +int __rtsx_pci_write_phy_register(struct rtsx_pcr *pcr, u8 addr, u16 val); +int __rtsx_pci_read_phy_register(struct rtsx_pcr *pcr, u8 addr, u16 *val); + void rts5209_init_params(struct rtsx_pcr *pcr); void rts5229_init_params(struct rtsx_pcr *pcr); void rtl8411_init_params(struct rtsx_pcr *pcr); void rtl8402_init_params(struct rtsx_pcr *pcr); void rts5227_init_params(struct rtsx_pcr *pcr); void rts5249_init_params(struct rtsx_pcr *pcr); +void rts524a_init_params(struct rtsx_pcr *pcr); void rtl8411b_init_params(struct rtsx_pcr *pcr); static inline u8 map_sd_drive(int idx) diff --git a/include/linux/mfd/rtsx_pci.h b/include/linux/mfd/rtsx_pci.h index 33cc63ced99e..754a18d4203a 100644 --- a/include/linux/mfd/rtsx_pci.h +++ b/include/linux/mfd/rtsx_pci.h @@ -577,8 +577,16 @@ #define CDRESUMECTL 0xFE52 #define WAKE_SEL_CTL 0xFE54 +#define PCLK_CTL 0xFE55 +#define PCLK_MODE_SEL 0x20 #define PME_FORCE_CTL 0xFE56 + #define ASPM_FORCE_CTL 0xFE57 +#define FORCE_ASPM_CTL0 0x10 +#define FORCE_ASPM_VAL_MASK 0x03 +#define FORCE_ASPM_L1_EN 0x02 +#define FORCE_ASPM_L0_EN 0x01 +#define FORCE_ASPM_NO_ASPM 0x00 #define PM_CLK_FORCE_CTL 0xFE58 #define FUNC_FORCE_CTL 0xFE59 #define PERST_GLITCH_WIDTH 0xFE5C @@ -590,7 +598,8 @@ #define HOST_ENTER_S3 2 #define SDIO_CFG 0xFE70 - +#define PM_EVENT_DEBUG 0xFE71 +#define PME_DEBUG_0 0x08 #define NFTS_TX_CTRL 0xFE72 #define PWR_GATE_CTRL 0xFE75 @@ -602,12 +611,19 @@ #define PWD_SUSPEND_EN 0xFE76 #define LDO_PWR_SEL 0xFE78 +#define L1SUB_CONFIG1 0xFE8D +#define L1SUB_CONFIG2 0xFE8E +#define L1SUB_AUTO_CFG 0x02 +#define L1SUB_CONFIG3 0xFE8F + #define DUMMY_REG_RESET_0 0xFE90 #define AUTOLOAD_CFG_BASE 0xFF00 #define PETXCFG 0xFF03 #define PM_CTRL1 0xFF44 +#define CD_RESUME_EN_MASK 0xF0 + #define PM_CTRL2 0xFF45 #define PM_CTRL3 0xFF46 #define SDIO_SEND_PME_EN 0x80 @@ -628,6 +644,61 @@ #define IMAGE_FLAG_ADDR0 0xCE80 #define IMAGE_FLAG_ADDR1 0xCE81 +#define RREF_CFG 0xFF6C +#define RREF_VBGSEL_MASK 0x38 +#define RREF_VBGSEL_1V25 0x28 + +#define OOBS_CONFIG 0xFF6E +#define OOBS_AUTOK_DIS 0x80 +#define OOBS_VAL_MASK 0x1F + +#define LDO_DV18_CFG 0xFF70 +#define LDO_DV18_SR_MASK 0xC0 +#define LDO_DV18_SR_DF 0x40 + +#define LDO_CONFIG2 0xFF71 +#define LDO_D3318_MASK 0x07 +#define LDO_D3318_33V 0x07 +#define LDO_D3318_18V 0x02 + +#define LDO_VCC_CFG0 0xFF72 +#define LDO_VCC_LMTVTH_MASK 0x30 +#define LDO_VCC_LMTVTH_2A 0x10 + +#define LDO_VCC_CFG1 0xFF73 +#define LDO_VCC_REF_TUNE_MASK 0x30 +#define LDO_VCC_REF_1V2 0x20 +#define LDO_VCC_TUNE_MASK 0x07 +#define LDO_VCC_1V8 0x04 +#define LDO_VCC_3V3 0x07 +#define LDO_VCC_LMT_EN 0x08 + +#define LDO_VIO_CFG 0xFF75 +#define LDO_VIO_SR_MASK 0xC0 +#define LDO_VIO_SR_DF 0x40 +#define LDO_VIO_REF_TUNE_MASK 0x30 +#define LDO_VIO_REF_1V2 0x20 +#define LDO_VIO_TUNE_MASK 0x07 +#define LDO_VIO_1V7 0x03 +#define LDO_VIO_1V8 0x04 +#define LDO_VIO_3V3 0x07 + +#define LDO_DV12S_CFG 0xFF76 +#define LDO_REF12_TUNE_MASK 0x18 +#define LDO_REF12_TUNE_DF 0x10 +#define LDO_D12_TUNE_MASK 0x07 +#define LDO_D12_TUNE_DF 0x04 + +#define LDO_AV12S_CFG 0xFF77 +#define LDO_AV12S_TUNE_MASK 0x07 +#define LDO_AV12S_TUNE_DF 0x04 + +#define SD40_LDO_CTL1 0xFE7D +#define SD40_VIO_TUNE_MASK 0x70 +#define SD40_VIO_TUNE_1V7 0x30 +#define SD_VIO_LDO_1V8 0x40 +#define SD_VIO_LDO_3V3 0x70 + /* Phy register */ #define PHY_PCR 0x00 #define PHY_PCR_FORCE_CODE 0xB000 @@ -641,6 +712,10 @@ #define PHY_RCR1 0x02 #define PHY_RCR1_ADP_TIME_4 0x0400 #define PHY_RCR1_VCO_COARSE 0x001F +#define PHY_SSCCR2 0x02 +#define PHY_SSCCR2_PLL_NCODE 0x0A00 +#define PHY_SSCCR2_TIME0 0x001C +#define PHY_SSCCR2_TIME2_WIDTH 0x0003 #define PHY_RCR2 0x03 #define PHY_RCR2_EMPHASE_EN 0x8000 @@ -649,6 +724,9 @@ #define PHY_RCR2_FREQSEL_12 0x0040 #define PHY_RCR2_CDR_SC_12P 0x0010 #define PHY_RCR2_CALIB_LATE 0x0002 +#define PHY_SSCCR3 0x03 +#define PHY_SSCCR3_STEP_IN 0x2740 +#define PHY_SSCCR3_CHECK_DELAY 0x0008 #define PHY_RTCR 0x04 #define PHY_RDR 0x05 @@ -663,6 +741,16 @@ #define PHY_TUNE_TUNED18 0x01C0 #define PHY_TUNE_TUNED12 0X0020 #define PHY_TUNE_TUNEA12 0x0004 +#define PHY_TUNE_VOLTAGE_MASK 0xFC3F +#define PHY_TUNE_VOLTAGE_3V3 0x03C0 +#define PHY_TUNE_D18_1V8 0x0100 +#define PHY_TUNE_D18_1V7 0x0080 +#define PHY_ANA08 0x08 +#define PHY_ANA08_RX_EQ_DCGAIN 0x5000 +#define PHY_ANA08_SEL_RX_EN 0x0400 +#define PHY_ANA08_RX_EQ_VAL 0x03C0 +#define PHY_ANA08_SCP 0x0020 +#define PHY_ANA08_SEL_IPI 0x0004 #define PHY_IMR 0x09 #define PHY_BPCR 0x0A @@ -678,6 +766,7 @@ #define PHY_HOST_CLK_CTRL 0x0F #define PHY_DMR 0x10 #define PHY_BACR 0x11 +#define PHY_BACR_BASIC_MASK 0xFFF3 #define PHY_IER 0x12 #define PHY_BCSR 0x13 #define PHY_BPR 0x14 @@ -698,12 +787,19 @@ #define PHY_REV_STOP_CLKWR 0x0004 #define PHY_FLD0 0x1A +#define PHY_ANA1A 0x1A +#define PHY_ANA1A_TXR_LOOPBACK 0x2000 +#define PHY_ANA1A_RXT_BIST 0x0500 +#define PHY_ANA1A_TXR_BIST 0x0040 +#define PHY_ANA1A_REV 0x0006 #define PHY_FLD1 0x1B #define PHY_FLD2 0x1C #define PHY_FLD3 0x1D #define PHY_FLD3_TIMER_4 0x0800 #define PHY_FLD3_TIMER_6 0x0020 #define PHY_FLD3_RXDELINK 0x0004 +#define PHY_ANA1D 0x1D +#define PHY_ANA1D_DEBUG_ADDR 0x0004 #define PHY_FLD4 0x1E #define PHY_FLD4_FLDEN_SEL 0x4000 @@ -713,7 +809,18 @@ #define PHY_FLD4_BER_COUNT 0x00E0 #define PHY_FLD4_BER_TIMER 0x000A #define PHY_FLD4_BER_CHK_EN 0x0001 - +#define PHY_DIG1E 0x1E +#define PHY_DIG1E_REV 0x4000 +#define PHY_DIG1E_D0_X_D1 0x1000 +#define PHY_DIG1E_RX_ON_HOST 0x0800 +#define PHY_DIG1E_RCLK_REF_HOST 0x0400 +#define PHY_DIG1E_RCLK_TX_EN_KEEP 0x0040 +#define PHY_DIG1E_RCLK_TX_TERM_KEEP 0x0020 +#define PHY_DIG1E_RCLK_RX_EIDLE_ON 0x0010 +#define PHY_DIG1E_TX_TERM_KEEP 0x0008 +#define PHY_DIG1E_RX_TERM_KEEP 0x0004 +#define PHY_DIG1E_TX_EN_KEEP 0x0002 +#define PHY_DIG1E_RX_EN_KEEP 0x0001 #define PHY_DUM_REG 0x1F #define PCR_SETTING_REG1 0x724 @@ -729,6 +836,8 @@ struct pcr_handle { }; struct pcr_ops { + int (*write_phy)(struct rtsx_pcr *pcr, u8 addr, u16 val); + int (*read_phy)(struct rtsx_pcr *pcr, u8 addr, u16 *val); int (*extra_init_hw)(struct rtsx_pcr *pcr); int (*optimize_phy)(struct rtsx_pcr *pcr); int (*turn_on_led)(struct rtsx_pcr *pcr); @@ -823,6 +932,8 @@ struct rtsx_pcr { const struct pcr_ops *ops; enum PDEV_STAT state; + u16 reg_pm_ctrl3; + int num_slots; struct rtsx_slot *slots; }; @@ -830,6 +941,10 @@ struct rtsx_pcr { #define CHK_PCI_PID(pcr, pid) ((pcr)->pci->device == (pid)) #define PCI_VID(pcr) ((pcr)->pci->vendor) #define PCI_PID(pcr) ((pcr)->pci->device) +#define is_version(pcr, pid, ver) \ + (CHK_PCI_PID(pcr, pid) && (pcr)->ic_version == (ver)) +#define pcr_dbg(pcr, fmt, arg...) \ + dev_dbg(&(pcr)->pci->dev, fmt, ##arg) #define SDR104_PHASE(val) ((val) & 0xFF) #define SDR50_PHASE(val) (((val) >> 8) & 0xFF) @@ -899,4 +1014,17 @@ static inline void rtsx_pci_write_be32(struct rtsx_pcr *pcr, u16 reg, u32 val) rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, reg + 3, 0xFF, val); } +static inline int rtsx_pci_update_phy(struct rtsx_pcr *pcr, u8 addr, + u16 mask, u16 append) +{ + int err; + u16 val; + + err = rtsx_pci_read_phy_register(pcr, addr, &val); + if (err < 0) + return err; + + return rtsx_pci_write_phy_register(pcr, addr, (val & mask) | append); +} + #endif -- cgit v1.2.3 From 41bc2334737a32d3062a318dde5964590d0e24c9 Mon Sep 17 00:00:00 2001 From: Micky Ching Date: Wed, 25 Feb 2015 13:50:15 +0800 Subject: mfd: rtsx: Add support for rts525A Add support for new chip rts525A. Signed-off-by: Micky Ching Signed-off-by: Lee Jones --- drivers/mfd/rts5249.c | 103 +++++++++++++++++++++++++++++++++++++++++++ drivers/mfd/rtsx_pcr.c | 13 ++++-- drivers/mfd/rtsx_pcr.h | 1 + include/linux/mfd/rtsx_pci.h | 15 +++++++ 4 files changed, 129 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/drivers/mfd/rts5249.c b/drivers/mfd/rts5249.c index 32be803b5327..d1ff32f742ba 100644 --- a/drivers/mfd/rts5249.c +++ b/drivers/mfd/rts5249.c @@ -487,3 +487,106 @@ void rts524a_init_params(struct rtsx_pcr *pcr) pcr->ops = &rts524a_pcr_ops; } +static int rts525a_card_power_on(struct rtsx_pcr *pcr, int card) +{ + rtsx_pci_write_register(pcr, LDO_VCC_CFG1, + LDO_VCC_TUNE_MASK, LDO_VCC_3V3); + return rtsx_base_card_power_on(pcr, card); +} + +static int rts525a_switch_output_voltage(struct rtsx_pcr *pcr, u8 voltage) +{ + switch (voltage) { + case OUTPUT_3V3: + rtsx_pci_write_register(pcr, LDO_CONFIG2, + LDO_D3318_MASK, LDO_D3318_33V); + rtsx_pci_write_register(pcr, SD_PAD_CTL, SD_IO_USING_1V8, 0); + break; + case OUTPUT_1V8: + rtsx_pci_write_register(pcr, LDO_CONFIG2, + LDO_D3318_MASK, LDO_D3318_18V); + rtsx_pci_write_register(pcr, SD_PAD_CTL, SD_IO_USING_1V8, + SD_IO_USING_1V8); + break; + default: + return -EINVAL; + } + + rtsx_pci_init_cmd(pcr); + rts5249_fill_driving(pcr, voltage); + return rtsx_pci_send_cmd(pcr, 100); +} + +static int rts525a_optimize_phy(struct rtsx_pcr *pcr) +{ + int err; + + err = rtsx_pci_write_register(pcr, RTS524A_PM_CTRL3, + D3_DELINK_MODE_EN, 0x00); + if (err < 0) + return err; + + rtsx_pci_write_phy_register(pcr, _PHY_FLD0, + _PHY_FLD0_CLK_REQ_20C | _PHY_FLD0_RX_IDLE_EN | + _PHY_FLD0_BIT_ERR_RSTN | _PHY_FLD0_BER_COUNT | + _PHY_FLD0_BER_TIMER | _PHY_FLD0_CHECK_EN); + + rtsx_pci_write_phy_register(pcr, _PHY_ANA03, + _PHY_ANA03_TIMER_MAX | _PHY_ANA03_OOBS_DEB_EN | + _PHY_CMU_DEBUG_EN); + + if (is_version(pcr, 0x525A, IC_VER_A)) + rtsx_pci_write_phy_register(pcr, _PHY_REV0, + _PHY_REV0_FILTER_OUT | _PHY_REV0_CDR_BYPASS_PFD | + _PHY_REV0_CDR_RX_IDLE_BYPASS); + + return 0; +} + +static int rts525a_extra_init_hw(struct rtsx_pcr *pcr) +{ + rts5249_extra_init_hw(pcr); + + rtsx_pci_write_register(pcr, PCLK_CTL, PCLK_MODE_SEL, PCLK_MODE_SEL); + if (is_version(pcr, 0x525A, IC_VER_A)) { + rtsx_pci_write_register(pcr, L1SUB_CONFIG2, + L1SUB_AUTO_CFG, L1SUB_AUTO_CFG); + rtsx_pci_write_register(pcr, RREF_CFG, + RREF_VBGSEL_MASK, RREF_VBGSEL_1V25); + rtsx_pci_write_register(pcr, LDO_VIO_CFG, + LDO_VIO_TUNE_MASK, LDO_VIO_1V7); + rtsx_pci_write_register(pcr, LDO_DV12S_CFG, + LDO_D12_TUNE_MASK, LDO_D12_TUNE_DF); + rtsx_pci_write_register(pcr, LDO_AV12S_CFG, + LDO_AV12S_TUNE_MASK, LDO_AV12S_TUNE_DF); + rtsx_pci_write_register(pcr, LDO_VCC_CFG0, + LDO_VCC_LMTVTH_MASK, LDO_VCC_LMTVTH_2A); + rtsx_pci_write_register(pcr, OOBS_CONFIG, + OOBS_AUTOK_DIS | OOBS_VAL_MASK, 0x89); + } + + return 0; +} + +static const struct pcr_ops rts525a_pcr_ops = { + .fetch_vendor_settings = rtsx_base_fetch_vendor_settings, + .extra_init_hw = rts525a_extra_init_hw, + .optimize_phy = rts525a_optimize_phy, + .turn_on_led = rtsx_base_turn_on_led, + .turn_off_led = rtsx_base_turn_off_led, + .enable_auto_blink = rtsx_base_enable_auto_blink, + .disable_auto_blink = rtsx_base_disable_auto_blink, + .card_power_on = rts525a_card_power_on, + .card_power_off = rtsx_base_card_power_off, + .switch_output_voltage = rts525a_switch_output_voltage, + .force_power_down = rtsx_base_force_power_down, +}; + +void rts525a_init_params(struct rtsx_pcr *pcr) +{ + rts5249_init_params(pcr); + + pcr->reg_pm_ctrl3 = RTS524A_PM_CTRL3; + pcr->ops = &rts525a_pcr_ops; +} + diff --git a/drivers/mfd/rtsx_pcr.c b/drivers/mfd/rtsx_pcr.c index e6d97adcc825..433cb41cf556 100644 --- a/drivers/mfd/rtsx_pcr.c +++ b/drivers/mfd/rtsx_pcr.c @@ -59,6 +59,7 @@ static const struct pci_device_id rtsx_pci_ids[] = { { PCI_DEVICE(0x10EC, 0x5287), PCI_CLASS_OTHERS << 16, 0xFF0000 }, { PCI_DEVICE(0x10EC, 0x5286), PCI_CLASS_OTHERS << 16, 0xFF0000 }, { PCI_DEVICE(0x10EC, 0x524A), PCI_CLASS_OTHERS << 16, 0xFF0000 }, + { PCI_DEVICE(0x10EC, 0x525A), PCI_CLASS_OTHERS << 16, 0xFF0000 }, { 0, } }; @@ -1114,6 +1115,10 @@ static int rtsx_pci_init_chip(struct rtsx_pcr *pcr) rts524a_init_params(pcr); break; + case 0x525A: + rts525a_init_params(pcr); + break; + case 0x5287: rtl8411b_init_params(pcr); break; @@ -1159,7 +1164,7 @@ static int rtsx_pci_probe(struct pci_dev *pcidev, struct rtsx_pcr *pcr; struct pcr_handle *handle; u32 base, len; - int ret, i; + int ret, i, bar = 0; dev_dbg(&(pcidev->dev), ": Realtek PCI-E Card Reader found at %s [%04x:%04x] (rev %x)\n", @@ -1204,8 +1209,10 @@ static int rtsx_pci_probe(struct pci_dev *pcidev, pcr->pci = pcidev; dev_set_drvdata(&pcidev->dev, handle); - len = pci_resource_len(pcidev, 0); - base = pci_resource_start(pcidev, 0); + if (CHK_PCI_PID(pcr, 0x525A)) + bar = 1; + len = pci_resource_len(pcidev, bar); + base = pci_resource_start(pcidev, bar); pcr->remap_addr = ioremap_nocache(base, len); if (!pcr->remap_addr) { ret = -ENOMEM; diff --git a/drivers/mfd/rtsx_pcr.h b/drivers/mfd/rtsx_pcr.h index e7daf6f54b83..ce48842570d7 100644 --- a/drivers/mfd/rtsx_pcr.h +++ b/drivers/mfd/rtsx_pcr.h @@ -40,6 +40,7 @@ void rtl8402_init_params(struct rtsx_pcr *pcr); void rts5227_init_params(struct rtsx_pcr *pcr); void rts5249_init_params(struct rtsx_pcr *pcr); void rts524a_init_params(struct rtsx_pcr *pcr); +void rts525a_init_params(struct rtsx_pcr *pcr); void rtl8411b_init_params(struct rtsx_pcr *pcr); static inline u8 map_sd_drive(int idx) diff --git a/include/linux/mfd/rtsx_pci.h b/include/linux/mfd/rtsx_pci.h index 754a18d4203a..ff843e7ca23d 100644 --- a/include/linux/mfd/rtsx_pci.h +++ b/include/linux/mfd/rtsx_pci.h @@ -727,6 +727,10 @@ #define PHY_SSCCR3 0x03 #define PHY_SSCCR3_STEP_IN 0x2740 #define PHY_SSCCR3_CHECK_DELAY 0x0008 +#define _PHY_ANA03 0x03 +#define _PHY_ANA03_TIMER_MAX 0x2700 +#define _PHY_ANA03_OOBS_DEB_EN 0x0040 +#define _PHY_CMU_DEBUG_EN 0x0008 #define PHY_RTCR 0x04 #define PHY_RDR 0x05 @@ -785,6 +789,10 @@ #define PHY_REV_STOP_CLKRD 0x0020 #define PHY_REV_RX_PWST 0x0008 #define PHY_REV_STOP_CLKWR 0x0004 +#define _PHY_REV0 0x19 +#define _PHY_REV0_FILTER_OUT 0x3800 +#define _PHY_REV0_CDR_BYPASS_PFD 0x0100 +#define _PHY_REV0_CDR_RX_IDLE_BYPASS 0x0002 #define PHY_FLD0 0x1A #define PHY_ANA1A 0x1A @@ -800,6 +808,13 @@ #define PHY_FLD3_RXDELINK 0x0004 #define PHY_ANA1D 0x1D #define PHY_ANA1D_DEBUG_ADDR 0x0004 +#define _PHY_FLD0 0x1D +#define _PHY_FLD0_CLK_REQ_20C 0x8000 +#define _PHY_FLD0_RX_IDLE_EN 0x1000 +#define _PHY_FLD0_BIT_ERR_RSTN 0x0800 +#define _PHY_FLD0_BER_COUNT 0x01E0 +#define _PHY_FLD0_BER_TIMER 0x001E +#define _PHY_FLD0_CHECK_EN 0x0001 #define PHY_FLD4 0x1E #define PHY_FLD4_FLDEN_SEL 0x4000 -- cgit v1.2.3 From 49010336290f4e3e4249c43bff1a1ff065c8f94e Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Wed, 25 Feb 2015 15:37:13 +0000 Subject: mfd: arizona: Move useful defines into a dt-binding include Move parts of linux/mfd/arizona/pdata.h and gpio.h into a new file in the dt-binding directory for use by device tree bindings. This also makes gpio.h redundant so remove it in the process. Signed-off-by: Charles Keepax Acked-by: Mark Brown Acked-by: Rob Herring Signed-off-by: Lee Jones --- Documentation/devicetree/bindings/mfd/arizona.txt | 20 ++--- include/dt-bindings/mfd/arizona.h | 93 ++++++++++++++++++++++ include/linux/mfd/arizona/gpio.h | 96 ----------------------- include/linux/mfd/arizona/pdata.h | 22 +----- sound/soc/codecs/arizona.c | 1 - 5 files changed, 105 insertions(+), 127 deletions(-) create mode 100644 include/dt-bindings/mfd/arizona.h delete mode 100644 include/linux/mfd/arizona/gpio.h (limited to 'include') diff --git a/Documentation/devicetree/bindings/mfd/arizona.txt b/Documentation/devicetree/bindings/mfd/arizona.txt index 87c878b1a17b..af8646ffc336 100644 --- a/Documentation/devicetree/bindings/mfd/arizona.txt +++ b/Documentation/devicetree/bindings/mfd/arizona.txt @@ -43,11 +43,11 @@ Optional properties: - wlf,reset : GPIO specifier for the GPIO controlling /RESET - wlf,ldoena : GPIO specifier for the GPIO controlling LDOENA - - wlf,gpio-defaults : A list of GPIO configuration register values. If - absent, no configuration of these registers is performed. If any - entry has a value that is out of range for a 16 bit register then - the chip default will be used. If present exactly five values must - be specified. + - wlf,gpio-defaults : A list of GPIO configuration register values. Defines + for the appropriate values can found in . If + absent, no configuration of these registers is performed. If any entry has + a value that is out of range for a 16 bit register then the chip default + will be used. If present exactly five values must be specified. - wlf,inmode : A list of INn_MODE register values, where n is the number of input signals. Valid values are 0 (Differential), 1 (Single-ended) and @@ -80,10 +80,10 @@ codec: wm5102@1a { #gpio-cells = <2>; wlf,gpio-defaults = < - 0x00000000 /* AIF1TXLRCLK */ - 0xffffffff - 0xffffffff - 0xffffffff - 0xffffffff + ARIZONA_GP_FN_TXLRCLK + ARIZONA_GP_DEFAULT + ARIZONA_GP_DEFAULT + ARIZONA_GP_DEFAULT + ARIZONA_GP_DEFAULT >; }; diff --git a/include/dt-bindings/mfd/arizona.h b/include/dt-bindings/mfd/arizona.h new file mode 100644 index 000000000000..c7af7c7ef793 --- /dev/null +++ b/include/dt-bindings/mfd/arizona.h @@ -0,0 +1,93 @@ +/* + * Device Tree defines for Arizona devices + * + * Copyright 2015 Cirrus Logic Inc. + * + * Author: Charles Keepax + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef _DT_BINDINGS_MFD_ARIZONA_H +#define _DT_BINDINGS_MFD_ARIZONA_H + +/* GPIO Function Definitions */ +#define ARIZONA_GP_FN_TXLRCLK 0x00 +#define ARIZONA_GP_FN_GPIO 0x01 +#define ARIZONA_GP_FN_IRQ1 0x02 +#define ARIZONA_GP_FN_IRQ2 0x03 +#define ARIZONA_GP_FN_OPCLK 0x04 +#define ARIZONA_GP_FN_FLL1_OUT 0x05 +#define ARIZONA_GP_FN_FLL2_OUT 0x06 +#define ARIZONA_GP_FN_PWM1 0x08 +#define ARIZONA_GP_FN_PWM2 0x09 +#define ARIZONA_GP_FN_SYSCLK_UNDERCLOCKED 0x0A +#define ARIZONA_GP_FN_ASYNCCLK_UNDERCLOCKED 0x0B +#define ARIZONA_GP_FN_FLL1_LOCK 0x0C +#define ARIZONA_GP_FN_FLL2_LOCK 0x0D +#define ARIZONA_GP_FN_FLL1_CLOCK_OK 0x0F +#define ARIZONA_GP_FN_FLL2_CLOCK_OK 0x10 +#define ARIZONA_GP_FN_HEADPHONE_DET 0x12 +#define ARIZONA_GP_FN_MIC_DET 0x13 +#define ARIZONA_GP_FN_WSEQ_STATUS 0x15 +#define ARIZONA_GP_FN_CIF_ADDRESS_ERROR 0x16 +#define ARIZONA_GP_FN_ASRC1_LOCK 0x1A +#define ARIZONA_GP_FN_ASRC2_LOCK 0x1B +#define ARIZONA_GP_FN_ASRC_CONFIG_ERROR 0x1C +#define ARIZONA_GP_FN_DRC1_SIGNAL_DETECT 0x1D +#define ARIZONA_GP_FN_DRC1_ANTICLIP 0x1E +#define ARIZONA_GP_FN_DRC1_DECAY 0x1F +#define ARIZONA_GP_FN_DRC1_NOISE 0x20 +#define ARIZONA_GP_FN_DRC1_QUICK_RELEASE 0x21 +#define ARIZONA_GP_FN_DRC2_SIGNAL_DETECT 0x22 +#define ARIZONA_GP_FN_DRC2_ANTICLIP 0x23 +#define ARIZONA_GP_FN_DRC2_DECAY 0x24 +#define ARIZONA_GP_FN_DRC2_NOISE 0x25 +#define ARIZONA_GP_FN_DRC2_QUICK_RELEASE 0x26 +#define ARIZONA_GP_FN_MIXER_DROPPED_SAMPLE 0x27 +#define ARIZONA_GP_FN_AIF1_CONFIG_ERROR 0x28 +#define ARIZONA_GP_FN_AIF2_CONFIG_ERROR 0x29 +#define ARIZONA_GP_FN_AIF3_CONFIG_ERROR 0x2A +#define ARIZONA_GP_FN_SPK_TEMP_SHUTDOWN 0x2B +#define ARIZONA_GP_FN_SPK_TEMP_WARNING 0x2C +#define ARIZONA_GP_FN_UNDERCLOCKED 0x2D +#define ARIZONA_GP_FN_OVERCLOCKED 0x2E +#define ARIZONA_GP_FN_DSP_IRQ1 0x35 +#define ARIZONA_GP_FN_DSP_IRQ2 0x36 +#define ARIZONA_GP_FN_ASYNC_OPCLK 0x3D +#define ARIZONA_GP_FN_BOOT_DONE 0x44 +#define ARIZONA_GP_FN_DSP1_RAM_READY 0x45 +#define ARIZONA_GP_FN_SYSCLK_ENA_STATUS 0x4B +#define ARIZONA_GP_FN_ASYNCCLK_ENA_STATUS 0x4C + +/* GPIO Configuration Bits */ +#define ARIZONA_GPN_DIR 0x8000 +#define ARIZONA_GPN_PU 0x4000 +#define ARIZONA_GPN_PD 0x2000 +#define ARIZONA_GPN_LVL 0x0800 +#define ARIZONA_GPN_POL 0x0400 +#define ARIZONA_GPN_OP_CFG 0x0200 +#define ARIZONA_GPN_DB 0x0100 + +/* Provide some defines for the most common configs */ +#define ARIZONA_GP_DEFAULT 0xffffffff +#define ARIZONA_GP_OUTPUT (ARIZONA_GP_FN_GPIO) +#define ARIZONA_GP_INPUT (ARIZONA_GP_FN_GPIO | \ + ARIZONA_GPN_DIR) + +#define ARIZONA_32KZ_MCLK1 1 +#define ARIZONA_32KZ_MCLK2 2 +#define ARIZONA_32KZ_NONE 3 + +#define ARIZONA_DMIC_MICVDD 0 +#define ARIZONA_DMIC_MICBIAS1 1 +#define ARIZONA_DMIC_MICBIAS2 2 +#define ARIZONA_DMIC_MICBIAS3 3 + +#define ARIZONA_INMODE_DIFF 0 +#define ARIZONA_INMODE_SE 1 +#define ARIZONA_INMODE_DMIC 2 + +#endif diff --git a/include/linux/mfd/arizona/gpio.h b/include/linux/mfd/arizona/gpio.h deleted file mode 100644 index d2146bb74f89..000000000000 --- a/include/linux/mfd/arizona/gpio.h +++ /dev/null @@ -1,96 +0,0 @@ -/* - * GPIO configuration for Arizona devices - * - * Copyright 2013 Wolfson Microelectronics. PLC. - * - * Author: Charles Keepax - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#ifndef _ARIZONA_GPIO_H -#define _ARIZONA_GPIO_H - -#define ARIZONA_GP_FN_TXLRCLK 0x00 -#define ARIZONA_GP_FN_GPIO 0x01 -#define ARIZONA_GP_FN_IRQ1 0x02 -#define ARIZONA_GP_FN_IRQ2 0x03 -#define ARIZONA_GP_FN_OPCLK 0x04 -#define ARIZONA_GP_FN_FLL1_OUT 0x05 -#define ARIZONA_GP_FN_FLL2_OUT 0x06 -#define ARIZONA_GP_FN_PWM1 0x08 -#define ARIZONA_GP_FN_PWM2 0x09 -#define ARIZONA_GP_FN_SYSCLK_UNDERCLOCKED 0x0A -#define ARIZONA_GP_FN_ASYNCCLK_UNDERCLOCKED 0x0B -#define ARIZONA_GP_FN_FLL1_LOCK 0x0C -#define ARIZONA_GP_FN_FLL2_LOCK 0x0D -#define ARIZONA_GP_FN_FLL1_CLOCK_OK 0x0F -#define ARIZONA_GP_FN_FLL2_CLOCK_OK 0x10 -#define ARIZONA_GP_FN_HEADPHONE_DET 0x12 -#define ARIZONA_GP_FN_MIC_DET 0x13 -#define ARIZONA_GP_FN_WSEQ_STATUS 0x15 -#define ARIZONA_GP_FN_CIF_ADDRESS_ERROR 0x16 -#define ARIZONA_GP_FN_ASRC1_LOCK 0x1A -#define ARIZONA_GP_FN_ASRC2_LOCK 0x1B -#define ARIZONA_GP_FN_ASRC_CONFIG_ERROR 0x1C -#define ARIZONA_GP_FN_DRC1_SIGNAL_DETECT 0x1D -#define ARIZONA_GP_FN_DRC1_ANTICLIP 0x1E -#define ARIZONA_GP_FN_DRC1_DECAY 0x1F -#define ARIZONA_GP_FN_DRC1_NOISE 0x20 -#define ARIZONA_GP_FN_DRC1_QUICK_RELEASE 0x21 -#define ARIZONA_GP_FN_DRC2_SIGNAL_DETECT 0x22 -#define ARIZONA_GP_FN_DRC2_ANTICLIP 0x23 -#define ARIZONA_GP_FN_DRC2_DECAY 0x24 -#define ARIZONA_GP_FN_DRC2_NOISE 0x25 -#define ARIZONA_GP_FN_DRC2_QUICK_RELEASE 0x26 -#define ARIZONA_GP_FN_MIXER_DROPPED_SAMPLE 0x27 -#define ARIZONA_GP_FN_AIF1_CONFIG_ERROR 0x28 -#define ARIZONA_GP_FN_AIF2_CONFIG_ERROR 0x29 -#define ARIZONA_GP_FN_AIF3_CONFIG_ERROR 0x2A -#define ARIZONA_GP_FN_SPK_TEMP_SHUTDOWN 0x2B -#define ARIZONA_GP_FN_SPK_TEMP_WARNING 0x2C -#define ARIZONA_GP_FN_UNDERCLOCKED 0x2D -#define ARIZONA_GP_FN_OVERCLOCKED 0x2E -#define ARIZONA_GP_FN_DSP_IRQ1 0x35 -#define ARIZONA_GP_FN_DSP_IRQ2 0x36 -#define ARIZONA_GP_FN_ASYNC_OPCLK 0x3D -#define ARIZONA_GP_FN_BOOT_DONE 0x44 -#define ARIZONA_GP_FN_DSP1_RAM_READY 0x45 -#define ARIZONA_GP_FN_SYSCLK_ENA_STATUS 0x4B -#define ARIZONA_GP_FN_ASYNCCLK_ENA_STATUS 0x4C - -#define ARIZONA_GPN_DIR 0x8000 /* GPN_DIR */ -#define ARIZONA_GPN_DIR_MASK 0x8000 /* GPN_DIR */ -#define ARIZONA_GPN_DIR_SHIFT 15 /* GPN_DIR */ -#define ARIZONA_GPN_DIR_WIDTH 1 /* GPN_DIR */ -#define ARIZONA_GPN_PU 0x4000 /* GPN_PU */ -#define ARIZONA_GPN_PU_MASK 0x4000 /* GPN_PU */ -#define ARIZONA_GPN_PU_SHIFT 14 /* GPN_PU */ -#define ARIZONA_GPN_PU_WIDTH 1 /* GPN_PU */ -#define ARIZONA_GPN_PD 0x2000 /* GPN_PD */ -#define ARIZONA_GPN_PD_MASK 0x2000 /* GPN_PD */ -#define ARIZONA_GPN_PD_SHIFT 13 /* GPN_PD */ -#define ARIZONA_GPN_PD_WIDTH 1 /* GPN_PD */ -#define ARIZONA_GPN_LVL 0x0800 /* GPN_LVL */ -#define ARIZONA_GPN_LVL_MASK 0x0800 /* GPN_LVL */ -#define ARIZONA_GPN_LVL_SHIFT 11 /* GPN_LVL */ -#define ARIZONA_GPN_LVL_WIDTH 1 /* GPN_LVL */ -#define ARIZONA_GPN_POL 0x0400 /* GPN_POL */ -#define ARIZONA_GPN_POL_MASK 0x0400 /* GPN_POL */ -#define ARIZONA_GPN_POL_SHIFT 10 /* GPN_POL */ -#define ARIZONA_GPN_POL_WIDTH 1 /* GPN_POL */ -#define ARIZONA_GPN_OP_CFG 0x0200 /* GPN_OP_CFG */ -#define ARIZONA_GPN_OP_CFG_MASK 0x0200 /* GPN_OP_CFG */ -#define ARIZONA_GPN_OP_CFG_SHIFT 9 /* GPN_OP_CFG */ -#define ARIZONA_GPN_OP_CFG_WIDTH 1 /* GPN_OP_CFG */ -#define ARIZONA_GPN_DB 0x0100 /* GPN_DB */ -#define ARIZONA_GPN_DB_MASK 0x0100 /* GPN_DB */ -#define ARIZONA_GPN_DB_SHIFT 8 /* GPN_DB */ -#define ARIZONA_GPN_DB_WIDTH 1 /* GPN_DB */ -#define ARIZONA_GPN_FN_MASK 0x007F /* GPN_DB */ -#define ARIZONA_GPN_FN_SHIFT 0 /* GPN_DB */ -#define ARIZONA_GPN_FN_WIDTH 7 /* GPN_DB */ - -#endif diff --git a/include/linux/mfd/arizona/pdata.h b/include/linux/mfd/arizona/pdata.h index 4578c72c9b86..48fe31356605 100644 --- a/include/linux/mfd/arizona/pdata.h +++ b/include/linux/mfd/arizona/pdata.h @@ -11,31 +11,26 @@ #ifndef _ARIZONA_PDATA_H #define _ARIZONA_PDATA_H -#define ARIZONA_GPN_DIR 0x8000 /* GPN_DIR */ +#include + #define ARIZONA_GPN_DIR_MASK 0x8000 /* GPN_DIR */ #define ARIZONA_GPN_DIR_SHIFT 15 /* GPN_DIR */ #define ARIZONA_GPN_DIR_WIDTH 1 /* GPN_DIR */ -#define ARIZONA_GPN_PU 0x4000 /* GPN_PU */ #define ARIZONA_GPN_PU_MASK 0x4000 /* GPN_PU */ #define ARIZONA_GPN_PU_SHIFT 14 /* GPN_PU */ #define ARIZONA_GPN_PU_WIDTH 1 /* GPN_PU */ -#define ARIZONA_GPN_PD 0x2000 /* GPN_PD */ #define ARIZONA_GPN_PD_MASK 0x2000 /* GPN_PD */ #define ARIZONA_GPN_PD_SHIFT 13 /* GPN_PD */ #define ARIZONA_GPN_PD_WIDTH 1 /* GPN_PD */ -#define ARIZONA_GPN_LVL 0x0800 /* GPN_LVL */ #define ARIZONA_GPN_LVL_MASK 0x0800 /* GPN_LVL */ #define ARIZONA_GPN_LVL_SHIFT 11 /* GPN_LVL */ #define ARIZONA_GPN_LVL_WIDTH 1 /* GPN_LVL */ -#define ARIZONA_GPN_POL 0x0400 /* GPN_POL */ #define ARIZONA_GPN_POL_MASK 0x0400 /* GPN_POL */ #define ARIZONA_GPN_POL_SHIFT 10 /* GPN_POL */ #define ARIZONA_GPN_POL_WIDTH 1 /* GPN_POL */ -#define ARIZONA_GPN_OP_CFG 0x0200 /* GPN_OP_CFG */ #define ARIZONA_GPN_OP_CFG_MASK 0x0200 /* GPN_OP_CFG */ #define ARIZONA_GPN_OP_CFG_SHIFT 9 /* GPN_OP_CFG */ #define ARIZONA_GPN_OP_CFG_WIDTH 1 /* GPN_OP_CFG */ -#define ARIZONA_GPN_DB 0x0100 /* GPN_DB */ #define ARIZONA_GPN_DB_MASK 0x0100 /* GPN_DB */ #define ARIZONA_GPN_DB_SHIFT 8 /* GPN_DB */ #define ARIZONA_GPN_DB_WIDTH 1 /* GPN_DB */ @@ -45,23 +40,10 @@ #define ARIZONA_MAX_GPIO 5 -#define ARIZONA_32KZ_MCLK1 1 -#define ARIZONA_32KZ_MCLK2 2 -#define ARIZONA_32KZ_NONE 3 - #define ARIZONA_MAX_INPUT 4 -#define ARIZONA_DMIC_MICVDD 0 -#define ARIZONA_DMIC_MICBIAS1 1 -#define ARIZONA_DMIC_MICBIAS2 2 -#define ARIZONA_DMIC_MICBIAS3 3 - #define ARIZONA_MAX_MICBIAS 3 -#define ARIZONA_INMODE_DIFF 0 -#define ARIZONA_INMODE_SE 1 -#define ARIZONA_INMODE_DMIC 2 - #define ARIZONA_MAX_OUTPUT 6 #define ARIZONA_MAX_AIF 3 diff --git a/sound/soc/codecs/arizona.c b/sound/soc/codecs/arizona.c index baf7b36aaaa4..95d31d6291ac 100644 --- a/sound/soc/codecs/arizona.c +++ b/sound/soc/codecs/arizona.c @@ -19,7 +19,6 @@ #include #include -#include #include #include "arizona.h" -- cgit v1.2.3 From ef9c80b37ad3062d335d4e37846a94d862b5579f Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Thu, 26 Feb 2015 10:13:50 +0100 Subject: mfd: sec: Cleanup unused RTC fields: ono, WTSR and SMPL The WTSR (Watchdog Timer Software Reset) and SMPL (Sudden Momentary Power Loss) were removed from rtc-s5m driver because they were not used. Remove them (and on/off interrupt) from main MFD driver and header. Signed-off-by: Krzysztof Kozlowski Signed-off-by: Lee Jones --- drivers/mfd/sec-core.c | 1 - include/linux/mfd/samsung/core.h | 9 --------- 2 files changed, 10 deletions(-) (limited to 'include') diff --git a/drivers/mfd/sec-core.c b/drivers/mfd/sec-core.c index 0a7bc43db4e4..423a2d3da4b2 100644 --- a/drivers/mfd/sec-core.c +++ b/drivers/mfd/sec-core.c @@ -333,7 +333,6 @@ static int sec_pmic_probe(struct i2c_client *i2c, } if (pdata) { sec_pmic->device_type = pdata->device_type; - sec_pmic->ono = pdata->ono; sec_pmic->irq_base = pdata->irq_base; sec_pmic->wakeup = pdata->wakeup; sec_pmic->pdata = pdata; diff --git a/include/linux/mfd/samsung/core.h b/include/linux/mfd/samsung/core.h index 3fdb7cfbffb3..75115384f3fc 100644 --- a/include/linux/mfd/samsung/core.h +++ b/include/linux/mfd/samsung/core.h @@ -58,13 +58,7 @@ enum sec_device_type { * @irq_base: Base IRQ number for device, required for IRQs * @irq: Generic IRQ number for device * @irq_data: Runtime data structure for IRQ controller - * @ono: Power onoff IRQ number for s5m87xx * @wakeup: Whether or not this is a wakeup device - * @wtsr_smpl: Whether or not to enable in RTC driver the Watchdog - * Timer Software Reset (registers set to default value - * after PWRHOLD falling) and Sudden Momentary Power Loss - * (PMIC will enter power on sequence after short drop in - * VBATT voltage). */ struct sec_pmic_dev { struct device *dev; @@ -77,9 +71,7 @@ struct sec_pmic_dev { int irq; struct regmap_irq_chip_data *irq_data; - int ono; bool wakeup; - bool wtsr_smpl; }; int sec_irq_init(struct sec_pmic_dev *sec_pmic); @@ -95,7 +87,6 @@ struct sec_platform_data { int irq_base; int (*cfg_pmic_irq)(void); - int ono; bool wakeup; bool buck_voltage_lock; -- cgit v1.2.3 From 4d0b0bd4385f0ce8d3b430f9667c5e2ca1de10af Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Thu, 5 Feb 2015 17:47:25 +0800 Subject: f2fs: simplfy a field name in struct f2fs_extent,extent_info Rename a filed name from 'blk_addr' to 'blk' in struct {f2fs_extent,extent_info} as annotation of this field descripts its meaning well to us. By this way, we can avoid long statement in code of following patches. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/data.c | 13 ++++++------- fs/f2fs/f2fs.h | 6 +++--- include/linux/f2fs_fs.h | 2 +- 3 files changed, 10 insertions(+), 11 deletions(-) (limited to 'include') diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index 0811d6509fce..64f9049a9f25 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -268,7 +268,7 @@ static int check_extent_cache(struct inode *inode, pgoff_t pgofs, start_fofs = fi->ext.fofs; end_fofs = fi->ext.fofs + fi->ext.len - 1; - start_blkaddr = fi->ext.blk_addr; + start_blkaddr = fi->ext.blk; if (pgofs >= start_fofs && pgofs <= end_fofs) { unsigned int blkbits = inode->i_sb->s_blocksize_bits; @@ -313,8 +313,8 @@ void update_extent_cache(struct dnode_of_data *dn) start_fofs = fi->ext.fofs; end_fofs = fi->ext.fofs + fi->ext.len - 1; - start_blkaddr = fi->ext.blk_addr; - end_blkaddr = fi->ext.blk_addr + fi->ext.len - 1; + start_blkaddr = fi->ext.blk; + end_blkaddr = fi->ext.blk + fi->ext.len - 1; /* Drop and initialize the matched extent */ if (fi->ext.len == 1 && fofs == start_fofs) @@ -324,7 +324,7 @@ void update_extent_cache(struct dnode_of_data *dn) if (fi->ext.len == 0) { if (dn->data_blkaddr != NULL_ADDR) { fi->ext.fofs = fofs; - fi->ext.blk_addr = dn->data_blkaddr; + fi->ext.blk = dn->data_blkaddr; fi->ext.len = 1; } goto end_update; @@ -333,7 +333,7 @@ void update_extent_cache(struct dnode_of_data *dn) /* Front merge */ if (fofs == start_fofs - 1 && dn->data_blkaddr == start_blkaddr - 1) { fi->ext.fofs--; - fi->ext.blk_addr--; + fi->ext.blk--; fi->ext.len++; goto end_update; } @@ -351,8 +351,7 @@ void update_extent_cache(struct dnode_of_data *dn) fi->ext.len = fofs - start_fofs; } else { fi->ext.fofs = fofs + 1; - fi->ext.blk_addr = start_blkaddr + - fofs - start_fofs + 1; + fi->ext.blk = start_blkaddr + fofs - start_fofs + 1; fi->ext.len -= fofs - start_fofs + 1; } } else { diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index c3caa3cb5beb..5d2e52e31fac 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -278,7 +278,7 @@ enum { struct extent_info { unsigned int fofs; /* start offset in a file */ - u32 blk_addr; /* start block address of the extent */ + u32 blk; /* start block address of the extent */ unsigned int len; /* length of the extent */ }; @@ -320,7 +320,7 @@ static inline void get_extent_info(struct extent_info *ext, struct f2fs_extent i_ext) { ext->fofs = le32_to_cpu(i_ext.fofs); - ext->blk_addr = le32_to_cpu(i_ext.blk_addr); + ext->blk = le32_to_cpu(i_ext.blk); ext->len = le32_to_cpu(i_ext.len); } @@ -328,7 +328,7 @@ static inline void set_raw_extent(struct extent_info *ext, struct f2fs_extent *i_ext) { i_ext->fofs = cpu_to_le32(ext->fofs); - i_ext->blk_addr = cpu_to_le32(ext->blk_addr); + i_ext->blk = cpu_to_le32(ext->blk); i_ext->len = cpu_to_le32(ext->len); } diff --git a/include/linux/f2fs_fs.h b/include/linux/f2fs_fs.h index a23556c32703..502f28cfb78e 100644 --- a/include/linux/f2fs_fs.h +++ b/include/linux/f2fs_fs.h @@ -153,7 +153,7 @@ struct f2fs_orphan_block { */ struct f2fs_extent { __le32 fofs; /* start file offset of the extent */ - __le32 blk_addr; /* start block address of the extent */ + __le32 blk; /* start block address of the extent */ __le32 len; /* lengh of the extent */ } __packed; -- cgit v1.2.3 From 1ec4610c522cc51219cc022ef120a928828fa934 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Thu, 5 Feb 2015 17:59:59 +0800 Subject: f2fs: add trace for rb-tree extent cache ops This patch adds trace for lookup/update/shrink/destroy ops in rb-tree extent cache. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/data.c | 16 +++++- include/trace/events/f2fs.h | 134 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 148 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index acdc0767f77c..d7ff4ca5be18 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -533,6 +533,8 @@ static bool f2fs_lookup_extent_tree(struct inode *inode, pgoff_t pgofs, if (is_inode_flag_set(F2FS_I(inode), FI_NO_EXTENT)) return false; + trace_f2fs_lookup_extent_tree_start(inode, pgofs); + down_read(&sbi->extent_tree_lock); et = radix_tree_lookup(&sbi->extent_tree_root, inode->i_ino); if (!et) { @@ -555,6 +557,8 @@ static bool f2fs_lookup_extent_tree(struct inode *inode, pgoff_t pgofs, stat_inc_total_hit(sbi->sb); read_unlock(&et->lock); + trace_f2fs_lookup_extent_tree_end(inode, pgofs, en); + atomic_dec(&et->refcount); return en ? true : false; } @@ -573,6 +577,8 @@ static void f2fs_update_extent_tree(struct inode *inode, pgoff_t fofs, if (is_inode_flag_set(F2FS_I(inode), FI_NO_EXTENT)) return; + trace_f2fs_update_extent_tree(inode, fofs, blkaddr); + down_write(&sbi->extent_tree_lock); et = radix_tree_lookup(&sbi->extent_tree_root, ino); if (!et) { @@ -665,6 +671,7 @@ void f2fs_shrink_extent_tree(struct f2fs_sb_info *sbi, int nr_shrink) struct radix_tree_iter iter; void **slot; unsigned int found; + unsigned int node_cnt = 0, tree_cnt = 0; if (!test_opt(sbi, EXTENT_CACHE)) return; @@ -691,7 +698,7 @@ void f2fs_shrink_extent_tree(struct f2fs_sb_info *sbi, int nr_shrink) atomic_inc(&et->refcount); write_lock(&et->lock); - __free_extent_tree(sbi, et, false); + node_cnt += __free_extent_tree(sbi, et, false); write_unlock(&et->lock); atomic_dec(&et->refcount); } @@ -707,15 +714,19 @@ void f2fs_shrink_extent_tree(struct f2fs_sb_info *sbi, int nr_shrink) radix_tree_delete(&sbi->extent_tree_root, et->ino); kmem_cache_free(extent_tree_slab, et); sbi->total_ext_tree--; + tree_cnt++; } } up_write(&sbi->extent_tree_lock); + + trace_f2fs_shrink_extent_tree(sbi, node_cnt, tree_cnt); } void f2fs_destroy_extent_tree(struct inode *inode) { struct f2fs_sb_info *sbi = F2FS_I_SB(inode); struct extent_tree *et; + unsigned int node_cnt = 0; if (!test_opt(sbi, EXTENT_CACHE)) return; @@ -731,7 +742,7 @@ void f2fs_destroy_extent_tree(struct inode *inode) /* free all extent info belong to this extent tree */ write_lock(&et->lock); - __free_extent_tree(sbi, et, true); + node_cnt = __free_extent_tree(sbi, et, true); write_unlock(&et->lock); atomic_dec(&et->refcount); @@ -749,6 +760,7 @@ void f2fs_destroy_extent_tree(struct inode *inode) sbi->total_ext_tree--; up_write(&sbi->extent_tree_lock); out: + trace_f2fs_destroy_extent_tree(inode, node_cnt); return; } diff --git a/include/trace/events/f2fs.h b/include/trace/events/f2fs.h index 5422dbfaf97d..09a56dc75f8d 100644 --- a/include/trace/events/f2fs.h +++ b/include/trace/events/f2fs.h @@ -1011,6 +1011,140 @@ TRACE_EVENT(f2fs_issue_flush, __entry->nobarrier ? "skip (nobarrier)" : "issue", __entry->flush_merge ? " with flush_merge" : "") ); + +TRACE_EVENT(f2fs_lookup_extent_tree_start, + + TP_PROTO(struct inode *inode, unsigned int pgofs), + + TP_ARGS(inode, pgofs), + + TP_STRUCT__entry( + __field(dev_t, dev) + __field(ino_t, ino) + __field(unsigned int, pgofs) + ), + + TP_fast_assign( + __entry->dev = inode->i_sb->s_dev; + __entry->ino = inode->i_ino; + __entry->pgofs = pgofs; + ), + + TP_printk("dev = (%d,%d), ino = %lu, pgofs = %u", + show_dev_ino(__entry), + __entry->pgofs) +); + +TRACE_EVENT_CONDITION(f2fs_lookup_extent_tree_end, + + TP_PROTO(struct inode *inode, unsigned int pgofs, + struct extent_node *en), + + TP_ARGS(inode, pgofs, en), + + TP_CONDITION(en), + + TP_STRUCT__entry( + __field(dev_t, dev) + __field(ino_t, ino) + __field(unsigned int, pgofs) + __field(unsigned int, fofs) + __field(u32, blk) + __field(unsigned int, len) + ), + + TP_fast_assign( + __entry->dev = inode->i_sb->s_dev; + __entry->ino = inode->i_ino; + __entry->pgofs = pgofs; + __entry->fofs = en->ei.fofs; + __entry->blk = en->ei.blk; + __entry->len = en->ei.len; + ), + + TP_printk("dev = (%d,%d), ino = %lu, pgofs = %u, " + "ext_info(fofs: %u, blk: %u, len: %u)", + show_dev_ino(__entry), + __entry->pgofs, + __entry->fofs, + __entry->blk, + __entry->len) +); + +TRACE_EVENT(f2fs_update_extent_tree, + + TP_PROTO(struct inode *inode, unsigned int pgofs, block_t blkaddr), + + TP_ARGS(inode, pgofs, blkaddr), + + TP_STRUCT__entry( + __field(dev_t, dev) + __field(ino_t, ino) + __field(unsigned int, pgofs) + __field(u32, blk) + ), + + TP_fast_assign( + __entry->dev = inode->i_sb->s_dev; + __entry->ino = inode->i_ino; + __entry->pgofs = pgofs; + __entry->blk = blkaddr; + ), + + TP_printk("dev = (%d,%d), ino = %lu, pgofs = %u, blkaddr = %u", + show_dev_ino(__entry), + __entry->pgofs, + __entry->blk) +); + +TRACE_EVENT(f2fs_shrink_extent_tree, + + TP_PROTO(struct f2fs_sb_info *sbi, unsigned int node_cnt, + unsigned int tree_cnt), + + TP_ARGS(sbi, node_cnt, tree_cnt), + + TP_STRUCT__entry( + __field(dev_t, dev) + __field(unsigned int, node_cnt) + __field(unsigned int, tree_cnt) + ), + + TP_fast_assign( + __entry->dev = sbi->sb->s_dev; + __entry->node_cnt = node_cnt; + __entry->tree_cnt = tree_cnt; + ), + + TP_printk("dev = (%d,%d), shrunk: node_cnt = %u, tree_cnt = %u", + show_dev(__entry), + __entry->node_cnt, + __entry->tree_cnt) +); + +TRACE_EVENT(f2fs_destroy_extent_tree, + + TP_PROTO(struct inode *inode, unsigned int node_cnt), + + TP_ARGS(inode, node_cnt), + + TP_STRUCT__entry( + __field(dev_t, dev) + __field(ino_t, ino) + __field(unsigned int, node_cnt) + ), + + TP_fast_assign( + __entry->dev = inode->i_sb->s_dev; + __entry->ino = inode->i_ino; + __entry->node_cnt = node_cnt; + ), + + TP_printk("dev = (%d,%d), ino = %lu, destroyed: node_cnt = %u", + show_dev_ino(__entry), + __entry->node_cnt) +); + #endif /* _TRACE_F2FS_H */ /* This part must be outside protection */ -- cgit v1.2.3 From d24209bb689e2c7f7418faec9b4a948e922d24da Mon Sep 17 00:00:00 2001 From: "Paul E. McKenney" Date: Wed, 21 Jan 2015 15:26:03 -0800 Subject: rcu: Improve diagnostics for blocked critical sections in irq If an RCU read-side critical section occurs within an interrupt handler or a softirq handler, it cannot have been preempted. Therefore, there is a check in rcu_read_unlock_special() checking for this error. However, when this check triggers, it lacks diagnostic information. This commit therefore moves rcu_read_unlock()'s lockdep annotation to follow the call to __rcu_read_unlock() and changes rcu_read_unlock_special()'s WARN_ON_ONCE() to an lockdep_rcu_suspicious() in order to locate where the offending RCU read-side critical section began. In addition, the value of the ->rcu_read_unlock_special field is printed. Signed-off-by: Paul E. McKenney --- include/linux/lockdep.h | 7 ++++++- include/linux/rcupdate.h | 2 +- kernel/rcu/tree_plugin.h | 8 +++++++- 3 files changed, 14 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/include/linux/lockdep.h b/include/linux/lockdep.h index 74ab23176e9b..066ba4157541 100644 --- a/include/linux/lockdep.h +++ b/include/linux/lockdep.h @@ -531,8 +531,13 @@ do { \ # define might_lock_read(lock) do { } while (0) #endif -#ifdef CONFIG_PROVE_RCU +#ifdef CONFIG_LOCKDEP void lockdep_rcu_suspicious(const char *file, const int line, const char *s); +#else +static inline void +lockdep_rcu_suspicious(const char *file, const int line, const char *s) +{ +} #endif #endif /* __LINUX_LOCKDEP_H */ diff --git a/include/linux/rcupdate.h b/include/linux/rcupdate.h index 3e6afed51051..70b896e16f19 100644 --- a/include/linux/rcupdate.h +++ b/include/linux/rcupdate.h @@ -942,9 +942,9 @@ static inline void rcu_read_unlock(void) { rcu_lockdep_assert(rcu_is_watching(), "rcu_read_unlock() used illegally while idle"); - rcu_lock_release(&rcu_lock_map); __release(RCU); __rcu_read_unlock(); + rcu_lock_release(&rcu_lock_map); /* Keep acq info for rls diags. */ } /** diff --git a/kernel/rcu/tree_plugin.h b/kernel/rcu/tree_plugin.h index 0a571e9a0f1d..8a33920b8845 100644 --- a/kernel/rcu/tree_plugin.h +++ b/kernel/rcu/tree_plugin.h @@ -334,7 +334,13 @@ void rcu_read_unlock_special(struct task_struct *t) } /* Hardware IRQ handlers cannot block, complain if they get here. */ - if (WARN_ON_ONCE(in_irq() || in_serving_softirq())) { + if (in_irq() || in_serving_softirq()) { + lockdep_rcu_suspicious(__FILE__, __LINE__, + "rcu_read_unlock() from irq or softirq with blocking in critical section!!!\n"); + pr_alert("->rcu_read_unlock_special: %#x (b: %d, nq: %d)\n", + t->rcu_read_unlock_special.s, + t->rcu_read_unlock_special.b.blocked, + t->rcu_read_unlock_special.b.need_qs); local_irq_restore(flags); return; } -- cgit v1.2.3 From b826565aaf8809df146666c03d1acbb7febbd13e Mon Sep 17 00:00:00 2001 From: "Paul E. McKenney" Date: Mon, 2 Feb 2015 11:46:33 -0800 Subject: rcu: Reverse rcu_dereference_check() conditions The rcu_dereference_check() family of primitives evaluates the RCU lockdep expression first, and only then evaluates the expression passed in. This works fine normally, but can potentially fail in environments (such as NMI handlers) where lockdep cannot be invoked. The problem is that even if the expression passed in is "1", the compiler would need to prove that the RCU lockdep expression (rcu_read_lock_held(), for example) is free of side effects in order to be able to elide it. Given that rcu_read_lock_held() is sometimes separately compiled, the compiler cannot always use this optimization. This commit therefore reverse the order of evaluation, so that the expression passed in is evaluated first, and the RCU lockdep expression is evaluated only if the passed-in expression evaluated to false, courtesy of the C-language short-circuit boolean evaluation rules. This compells the compiler to forego executing the RCU lockdep expression in cases where the passed-in expression evaluates to "1" at compile time, so that (for example) rcu_dereference_raw() can be guaranteed to execute safely within an NMI handler. Signed-off-by: Paul E. McKenney Acked-by: Peter Zijlstra (Intel) --- include/linux/rcupdate.h | 6 +++--- include/linux/srcu.h | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/include/linux/rcupdate.h b/include/linux/rcupdate.h index 70b896e16f19..416ae2848c6c 100644 --- a/include/linux/rcupdate.h +++ b/include/linux/rcupdate.h @@ -729,7 +729,7 @@ static inline void rcu_preempt_sleep_check(void) * annotated as __rcu. */ #define rcu_dereference_check(p, c) \ - __rcu_dereference_check((p), rcu_read_lock_held() || (c), __rcu) + __rcu_dereference_check((p), (c) || rcu_read_lock_held(), __rcu) /** * rcu_dereference_bh_check() - rcu_dereference_bh with debug checking @@ -739,7 +739,7 @@ static inline void rcu_preempt_sleep_check(void) * This is the RCU-bh counterpart to rcu_dereference_check(). */ #define rcu_dereference_bh_check(p, c) \ - __rcu_dereference_check((p), rcu_read_lock_bh_held() || (c), __rcu) + __rcu_dereference_check((p), (c) || rcu_read_lock_bh_held(), __rcu) /** * rcu_dereference_sched_check() - rcu_dereference_sched with debug checking @@ -749,7 +749,7 @@ static inline void rcu_preempt_sleep_check(void) * This is the RCU-sched counterpart to rcu_dereference_check(). */ #define rcu_dereference_sched_check(p, c) \ - __rcu_dereference_check((p), rcu_read_lock_sched_held() || (c), \ + __rcu_dereference_check((p), (c) || rcu_read_lock_sched_held(), \ __rcu) #define rcu_dereference_raw(p) rcu_dereference_check(p, 1) /*@@@ needed? @@@*/ diff --git a/include/linux/srcu.h b/include/linux/srcu.h index 9cfd9623fb03..bdeb4567b71e 100644 --- a/include/linux/srcu.h +++ b/include/linux/srcu.h @@ -182,7 +182,7 @@ static inline int srcu_read_lock_held(struct srcu_struct *sp) * lockdep_is_held() calls. */ #define srcu_dereference_check(p, sp, c) \ - __rcu_dereference_check((p), srcu_read_lock_held(sp) || (c), __rcu) + __rcu_dereference_check((p), (c) || srcu_read_lock_held(sp), __rcu) /** * srcu_dereference - fetch SRCU-protected pointer for later dereferencing -- cgit v1.2.3 From 1d5da757da860a6916adbf68b09e868062b4b3b8 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Tue, 3 Mar 2015 09:41:47 -0600 Subject: ax25: Stop using magic neighbour cache operations. Before the ax25 stack calls dev_queue_xmit it always calls ax25_type_trans which sets skb->protocol to ETH_P_AX25. Which means that by looking at the protocol type it is possible to detect IP packets that have not been munged by the ax25 stack in ndo_start_xmit and call a function to munge them. Rename ax25_neigh_xmit to ax25_ip_xmit and tweak the return type and value to be appropriate for an ndo_start_xmit function. Update all of the ax25 devices to test the protocol type for ETH_P_IP and return ax25_ip_xmit as the first thing they do. This preserves the existing semantics of IP packet processing, but the timing will be a little different as the IP packets now pass through the qdisc layer before reaching the ax25 ip packet processing. Remove the now unnecessary ax25 neighbour table operations. Signed-off-by: "Eric W. Biederman" Signed-off-by: David S. Miller --- drivers/net/hamradio/6pack.c | 5 ++-- drivers/net/hamradio/baycom_epp.c | 5 ++-- drivers/net/hamradio/bpqether.c | 5 ++-- drivers/net/hamradio/dmascc.c | 5 ++-- drivers/net/hamradio/hdlcdrv.c | 5 ++-- drivers/net/hamradio/mkiss.c | 5 ++-- drivers/net/hamradio/scc.c | 5 ++-- drivers/net/hamradio/yam.c | 5 ++-- include/net/ax25.h | 5 +--- net/ax25/ax25_ip.c | 60 ++++----------------------------------- 10 files changed, 31 insertions(+), 74 deletions(-) (limited to 'include') diff --git a/drivers/net/hamradio/6pack.c b/drivers/net/hamradio/6pack.c index 0b8393ca8c80..7c4a4151ef0f 100644 --- a/drivers/net/hamradio/6pack.c +++ b/drivers/net/hamradio/6pack.c @@ -247,6 +247,9 @@ static netdev_tx_t sp_xmit(struct sk_buff *skb, struct net_device *dev) { struct sixpack *sp = netdev_priv(dev); + if (skb->protocol == htons(ETH_P_IP)) + return ax25_ip_xmit(skb); + spin_lock_bh(&sp->lock); /* We were not busy, so we are now... :-) */ netif_stop_queue(dev); @@ -302,7 +305,6 @@ static const struct net_device_ops sp_netdev_ops = { .ndo_stop = sp_close, .ndo_start_xmit = sp_xmit, .ndo_set_mac_address = sp_set_mac_address, - .ndo_neigh_construct = ax25_neigh_construct, }; static void sp_setup(struct net_device *dev) @@ -316,7 +318,6 @@ static void sp_setup(struct net_device *dev) dev->addr_len = AX25_ADDR_LEN; dev->type = ARPHRD_AX25; - dev->neigh_priv_len = sizeof(struct ax25_neigh_priv); dev->tx_queue_len = 10; /* Only activated in AX.25 mode */ diff --git a/drivers/net/hamradio/baycom_epp.c b/drivers/net/hamradio/baycom_epp.c index 3539ab392f7d..83c7cce0d172 100644 --- a/drivers/net/hamradio/baycom_epp.c +++ b/drivers/net/hamradio/baycom_epp.c @@ -772,6 +772,9 @@ static int baycom_send_packet(struct sk_buff *skb, struct net_device *dev) { struct baycom_state *bc = netdev_priv(dev); + if (skb->protocol == htons(ETH_P_IP)) + return ax25_ip_xmit(skb); + if (skb->data[0] != 0) { do_kiss_params(bc, skb->data, skb->len); dev_kfree_skb(skb); @@ -1109,7 +1112,6 @@ static const struct net_device_ops baycom_netdev_ops = { .ndo_do_ioctl = baycom_ioctl, .ndo_start_xmit = baycom_send_packet, .ndo_set_mac_address = baycom_set_mac_address, - .ndo_neigh_construct = ax25_neigh_construct, }; /* @@ -1147,7 +1149,6 @@ static void baycom_probe(struct net_device *dev) dev->header_ops = &ax25_header_ops; dev->type = ARPHRD_AX25; /* AF_AX25 device */ - dev->neigh_priv_len = sizeof(struct ax25_neigh_priv); dev->hard_header_len = AX25_MAX_HEADER_LEN + AX25_BPQ_HEADER_LEN; dev->mtu = AX25_DEF_PACLEN; /* eth_mtu is the default */ dev->addr_len = AX25_ADDR_LEN; /* sizeof an ax.25 address */ diff --git a/drivers/net/hamradio/bpqether.c b/drivers/net/hamradio/bpqether.c index bce105b16ed0..63ff08a26da8 100644 --- a/drivers/net/hamradio/bpqether.c +++ b/drivers/net/hamradio/bpqether.c @@ -251,6 +251,9 @@ static netdev_tx_t bpq_xmit(struct sk_buff *skb, struct net_device *dev) struct net_device *orig_dev; int size; + if (skb->protocol == htons(ETH_P_IP)) + return ax25_ip_xmit(skb); + /* * Just to be *really* sure not to send anything if the interface * is down, the ethernet device may have gone. @@ -469,7 +472,6 @@ static const struct net_device_ops bpq_netdev_ops = { .ndo_start_xmit = bpq_xmit, .ndo_set_mac_address = bpq_set_mac_address, .ndo_do_ioctl = bpq_ioctl, - .ndo_neigh_construct = ax25_neigh_construct, }; static void bpq_setup(struct net_device *dev) @@ -487,7 +489,6 @@ static void bpq_setup(struct net_device *dev) #endif dev->type = ARPHRD_AX25; - dev->neigh_priv_len = sizeof(struct ax25_neigh_priv); dev->hard_header_len = AX25_MAX_HEADER_LEN + AX25_BPQ_HEADER_LEN; dev->mtu = AX25_DEF_PACLEN; dev->addr_len = AX25_ADDR_LEN; diff --git a/drivers/net/hamradio/dmascc.c b/drivers/net/hamradio/dmascc.c index abab7be77406..c3d377770616 100644 --- a/drivers/net/hamradio/dmascc.c +++ b/drivers/net/hamradio/dmascc.c @@ -433,7 +433,6 @@ module_exit(dmascc_exit); static void __init dev_setup(struct net_device *dev) { dev->type = ARPHRD_AX25; - dev->neigh_priv_len = sizeof(struct ax25_neigh_priv); dev->hard_header_len = AX25_MAX_HEADER_LEN; dev->mtu = 1500; dev->addr_len = AX25_ADDR_LEN; @@ -448,7 +447,6 @@ static const struct net_device_ops scc_netdev_ops = { .ndo_start_xmit = scc_send_packet, .ndo_do_ioctl = scc_ioctl, .ndo_set_mac_address = scc_set_mac_address, - .ndo_neigh_construct = ax25_neigh_construct, }; static int __init setup_adapter(int card_base, int type, int n) @@ -922,6 +920,9 @@ static int scc_send_packet(struct sk_buff *skb, struct net_device *dev) unsigned long flags; int i; + if (skb->protocol == htons(ETH_P_IP)) + return ax25_ip_xmit(skb); + /* Temporarily stop the scheduler feeding us packets */ netif_stop_queue(dev); diff --git a/drivers/net/hamradio/hdlcdrv.c b/drivers/net/hamradio/hdlcdrv.c index 435868a7b69c..49fe59b180a8 100644 --- a/drivers/net/hamradio/hdlcdrv.c +++ b/drivers/net/hamradio/hdlcdrv.c @@ -404,6 +404,9 @@ static netdev_tx_t hdlcdrv_send_packet(struct sk_buff *skb, { struct hdlcdrv_state *sm = netdev_priv(dev); + if (skb->protocol == htons(ETH_P_IP)) + return ax25_ip_xmit(skb); + if (skb->data[0] != 0) { do_kiss_params(sm, skb->data, skb->len); dev_kfree_skb(skb); @@ -626,7 +629,6 @@ static const struct net_device_ops hdlcdrv_netdev = { .ndo_start_xmit = hdlcdrv_send_packet, .ndo_do_ioctl = hdlcdrv_ioctl, .ndo_set_mac_address = hdlcdrv_set_mac_address, - .ndo_neigh_construct = ax25_neigh_construct, }; /* @@ -677,7 +679,6 @@ static void hdlcdrv_setup(struct net_device *dev) dev->header_ops = &ax25_header_ops; dev->type = ARPHRD_AX25; /* AF_AX25 device */ - dev->neigh_priv_len = sizeof(struct ax25_neigh_priv); dev->hard_header_len = AX25_MAX_HEADER_LEN + AX25_BPQ_HEADER_LEN; dev->mtu = AX25_DEF_PACLEN; /* eth_mtu is the default */ dev->addr_len = AX25_ADDR_LEN; /* sizeof an ax.25 address */ diff --git a/drivers/net/hamradio/mkiss.c b/drivers/net/hamradio/mkiss.c index c12ec2c2b594..17058c490b79 100644 --- a/drivers/net/hamradio/mkiss.c +++ b/drivers/net/hamradio/mkiss.c @@ -529,6 +529,9 @@ static netdev_tx_t ax_xmit(struct sk_buff *skb, struct net_device *dev) { struct mkiss *ax = netdev_priv(dev); + if (skb->protocol == htons(ETH_P_IP)) + return ax25_ip_xmit(skb); + if (!netif_running(dev)) { printk(KERN_ERR "mkiss: %s: xmit call when iface is down\n", dev->name); return NETDEV_TX_BUSY; @@ -641,7 +644,6 @@ static const struct net_device_ops ax_netdev_ops = { .ndo_stop = ax_close, .ndo_start_xmit = ax_xmit, .ndo_set_mac_address = ax_set_mac_address, - .ndo_neigh_construct = ax25_neigh_construct, }; static void ax_setup(struct net_device *dev) @@ -651,7 +653,6 @@ static void ax_setup(struct net_device *dev) dev->hard_header_len = 0; dev->addr_len = 0; dev->type = ARPHRD_AX25; - dev->neigh_priv_len = sizeof(struct ax25_neigh_priv); dev->tx_queue_len = 10; dev->header_ops = &ax25_header_ops; dev->netdev_ops = &ax_netdev_ops; diff --git a/drivers/net/hamradio/scc.c b/drivers/net/hamradio/scc.c index b305f51eb420..ce88df33fe17 100644 --- a/drivers/net/hamradio/scc.c +++ b/drivers/net/hamradio/scc.c @@ -1550,7 +1550,6 @@ static const struct net_device_ops scc_netdev_ops = { .ndo_set_mac_address = scc_net_set_mac_address, .ndo_get_stats = scc_net_get_stats, .ndo_do_ioctl = scc_net_ioctl, - .ndo_neigh_construct = ax25_neigh_construct, }; /* ----> Initialize device <----- */ @@ -1568,7 +1567,6 @@ static void scc_net_setup(struct net_device *dev) dev->flags = 0; dev->type = ARPHRD_AX25; - dev->neigh_priv_len = sizeof(struct ax25_neigh_priv); dev->hard_header_len = AX25_MAX_HEADER_LEN + AX25_BPQ_HEADER_LEN; dev->mtu = AX25_DEF_PACLEN; dev->addr_len = AX25_ADDR_LEN; @@ -1641,6 +1639,9 @@ static netdev_tx_t scc_net_tx(struct sk_buff *skb, struct net_device *dev) unsigned long flags; char kisscmd; + if (skb->protocol == htons(ETH_P_IP)) + return ax25_ip_xmit(skb); + if (skb->len > scc->stat.bufsize || skb->len < 2) { scc->dev_stat.tx_dropped++; /* bogus frame */ dev_kfree_skb(skb); diff --git a/drivers/net/hamradio/yam.c b/drivers/net/hamradio/yam.c index 89d9da7a0c51..1a4729c36aa4 100644 --- a/drivers/net/hamradio/yam.c +++ b/drivers/net/hamradio/yam.c @@ -597,6 +597,9 @@ static netdev_tx_t yam_send_packet(struct sk_buff *skb, { struct yam_port *yp = netdev_priv(dev); + if (skb->protocol == htons(ETH_P_IP)) + return ax25_ip_xmit(skb); + skb_queue_tail(&yp->send_queue, skb); dev->trans_start = jiffies; return NETDEV_TX_OK; @@ -1100,7 +1103,6 @@ static const struct net_device_ops yam_netdev_ops = { .ndo_start_xmit = yam_send_packet, .ndo_do_ioctl = yam_ioctl, .ndo_set_mac_address = yam_set_mac_address, - .ndo_neigh_construct = ax25_neigh_construct, }; static void yam_setup(struct net_device *dev) @@ -1129,7 +1131,6 @@ static void yam_setup(struct net_device *dev) dev->header_ops = &ax25_header_ops; dev->type = ARPHRD_AX25; - dev->neigh_priv_len = sizeof(struct ax25_neigh_priv); dev->hard_header_len = AX25_MAX_HEADER_LEN; dev->mtu = AX25_MTU; dev->addr_len = AX25_ADDR_LEN; diff --git a/include/net/ax25.h b/include/net/ax25.h index 45feeba7a325..16a923a3a43a 100644 --- a/include/net/ax25.h +++ b/include/net/ax25.h @@ -367,11 +367,8 @@ int ax25_kiss_rcv(struct sk_buff *, struct net_device *, struct packet_type *, struct net_device *); /* ax25_ip.c */ -int ax25_neigh_construct(struct neighbour *neigh); +netdev_tx_t ax25_ip_xmit(struct sk_buff *skb); extern const struct header_ops ax25_header_ops; -struct ax25_neigh_priv { - struct neigh_ops ops; -}; /* ax25_out.c */ ax25_cb *ax25_send_frame(struct sk_buff *, int, ax25_address *, ax25_address *, diff --git a/net/ax25/ax25_ip.c b/net/ax25/ax25_ip.c index e030c64ebfb7..8b35af4ef93e 100644 --- a/net/ax25/ax25_ip.c +++ b/net/ax25/ax25_ip.c @@ -100,7 +100,7 @@ static int ax25_hard_header(struct sk_buff *skb, struct net_device *dev, return -AX25_HEADER_LEN; /* Unfinished header */ } -static int ax25_neigh_xmit(struct sk_buff *skb) +netdev_tx_t ax25_ip_xmit(struct sk_buff *skb) { struct sk_buff *ourskb; unsigned char *bp = skb->data; @@ -210,56 +210,7 @@ put: if (route) ax25_put_route(route); - return 1; -} - -static int ax25_neigh_output(struct neighbour *neigh, struct sk_buff *skb) -{ - /* Except for calling ax25_neigh_xmit instead of - * dev_queue_xmit this is neigh_resolve_output. - */ - int rc = 0; - - if (!neigh_event_send(neigh, skb)) { - int err; - struct net_device *dev = neigh->dev; - unsigned int seq; - - do { - __skb_pull(skb, skb_network_offset(skb)); - seq = read_seqbegin(&neigh->ha_lock); - err = dev_hard_header(skb, dev, ntohs(skb->protocol), - neigh->ha, NULL, skb->len); - } while (read_seqretry(&neigh->ha_lock, seq)); - - if (err >= 0) { - ax25_neigh_xmit(skb); - } else - goto out_kfree_skb; - } -out: - return rc; - -out_kfree_skb: - rc = -EINVAL; - kfree_skb(skb); - goto out; -} - -int ax25_neigh_construct(struct neighbour *neigh) -{ - /* This trouble could be saved if ax25 would right a proper - * dev_queue_xmit function. - */ - struct ax25_neigh_priv *priv = neighbour_priv(neigh); - - if (neigh->tbl->family != AF_INET) - return -EINVAL; - - priv->ops = *neigh->ops; - priv->ops.output = ax25_neigh_output; - priv->ops.connected_output = ax25_neigh_output; - return 0; + return NETDEV_TX_OK; } #else /* INET */ @@ -271,9 +222,10 @@ static int ax25_hard_header(struct sk_buff *skb, struct net_device *dev, return -AX25_HEADER_LEN; } -int ax25_neigh_construct(struct neighbour *neigh) +netdev_tx_t ax25_ip_xmit(sturct sk_buff *skb) { - return 0; + kfree_skb(skb); + return NETDEV_TX_OK; } #endif @@ -282,5 +234,5 @@ const struct header_ops ax25_header_ops = { }; EXPORT_SYMBOL(ax25_header_ops); -EXPORT_SYMBOL(ax25_neigh_construct); +EXPORT_SYMBOL(ax25_ip_xmit); -- cgit v1.2.3 From 112bdfaa525fd5993e17885861342893f15290b0 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Mon, 16 Feb 2015 15:41:02 +0000 Subject: extcon: arizona: Deobfuscate arizona_extcon_do_magic arizona_extcon_do_magic does not lend a lot of clarity to the purpose of the function, and as all the registers used are described in the datasheet there is no need to obfuscate the code. This patch renames the function to arizona_extcon_hp_clamp, as it controls clamping on the headphone output. Signed-off-by: Charles Keepax Acked-by: Lee Jones Signed-off-by: Chanwoo Choi --- drivers/extcon/extcon-arizona.c | 36 ++++++++++++++++++++---------------- include/linux/mfd/arizona/core.h | 2 +- sound/soc/codecs/arizona.c | 4 ++-- 3 files changed, 23 insertions(+), 19 deletions(-) (limited to 'include') diff --git a/drivers/extcon/extcon-arizona.c b/drivers/extcon/extcon-arizona.c index 63f01c42aed4..95cf7f875bb3 100644 --- a/drivers/extcon/extcon-arizona.c +++ b/drivers/extcon/extcon-arizona.c @@ -136,18 +136,22 @@ static const char *arizona_cable[] = { static void arizona_start_hpdet_acc_id(struct arizona_extcon_info *info); -static void arizona_extcon_do_magic(struct arizona_extcon_info *info, - unsigned int magic) +static void arizona_extcon_hp_clamp(struct arizona_extcon_info *info, + bool clamp) { struct arizona *arizona = info->arizona; + unsigned int val = 0; int ret; + if (clamp) + val = ARIZONA_RMV_SHRT_HP1L; + mutex_lock(&arizona->dapm->card->dapm_mutex); - arizona->hpdet_magic = magic; + arizona->hpdet_clamp = clamp; - /* Keep the HP output stages disabled while doing the magic */ - if (magic) { + /* Keep the HP output stages disabled while doing the clamp */ + if (clamp) { ret = regmap_update_bits(arizona->regmap, ARIZONA_OUTPUT_ENABLES_1, ARIZONA_OUT1L_ENA | @@ -158,20 +162,20 @@ static void arizona_extcon_do_magic(struct arizona_extcon_info *info, ret); } - ret = regmap_update_bits(arizona->regmap, 0x225, 0x4000, - magic); + ret = regmap_update_bits(arizona->regmap, ARIZONA_HP_CTRL_1L, + ARIZONA_RMV_SHRT_HP1L, val); if (ret != 0) - dev_warn(arizona->dev, "Failed to do magic: %d\n", + dev_warn(arizona->dev, "Failed to do clamp: %d\n", ret); - ret = regmap_update_bits(arizona->regmap, 0x226, 0x4000, - magic); + ret = regmap_update_bits(arizona->regmap, ARIZONA_HP_CTRL_1R, + ARIZONA_RMV_SHRT_HP1R, val); if (ret != 0) - dev_warn(arizona->dev, "Failed to do magic: %d\n", + dev_warn(arizona->dev, "Failed to do clamp: %d\n", ret); - /* Restore the desired state while not doing the magic */ - if (!magic) { + /* Restore the desired state while not doing the clamp */ + if (!clamp) { ret = regmap_update_bits(arizona->regmap, ARIZONA_OUTPUT_ENABLES_1, ARIZONA_OUT1L_ENA | @@ -603,7 +607,7 @@ done: ARIZONA_HP_IMPEDANCE_RANGE_MASK | ARIZONA_HP_POLL, 0); - arizona_extcon_do_magic(info, 0); + arizona_extcon_hp_clamp(info, false); if (id_gpio) gpio_set_value_cansleep(id_gpio, 0); @@ -648,7 +652,7 @@ static void arizona_identify_headphone(struct arizona_extcon_info *info) if (info->mic) arizona_stop_mic(info); - arizona_extcon_do_magic(info, 0x4000); + arizona_extcon_hp_clamp(info, true); ret = regmap_update_bits(arizona->regmap, ARIZONA_ACCESSORY_DETECT_MODE_1, @@ -699,7 +703,7 @@ static void arizona_start_hpdet_acc_id(struct arizona_extcon_info *info) info->hpdet_active = true; - arizona_extcon_do_magic(info, 0x4000); + arizona_extcon_hp_clamp(info, true); ret = regmap_update_bits(arizona->regmap, ARIZONA_ACCESSORY_DETECT_MODE_1, diff --git a/include/linux/mfd/arizona/core.h b/include/linux/mfd/arizona/core.h index 910e3aa1e965..4863548faff7 100644 --- a/include/linux/mfd/arizona/core.h +++ b/include/linux/mfd/arizona/core.h @@ -126,7 +126,7 @@ struct arizona { struct regmap_irq_chip_data *aod_irq_chip; struct regmap_irq_chip_data *irq_chip; - bool hpdet_magic; + bool hpdet_clamp; unsigned int hp_ena; struct mutex clk_lock; diff --git a/sound/soc/codecs/arizona.c b/sound/soc/codecs/arizona.c index 29202610dd0d..fb58c7ee3780 100644 --- a/sound/soc/codecs/arizona.c +++ b/sound/soc/codecs/arizona.c @@ -840,8 +840,8 @@ int arizona_hp_ev(struct snd_soc_dapm_widget *w, priv->arizona->hp_ena &= ~mask; priv->arizona->hp_ena |= val; - /* Force off if HPDET magic is active */ - if (priv->arizona->hpdet_magic) + /* Force off if HPDET clamp is active */ + if (priv->arizona->hpdet_clamp) val = 0; regmap_update_bits_async(arizona->regmap, ARIZONA_OUTPUT_ENABLES_1, -- cgit v1.2.3 From 60395a20ffd74166ea373ea91418d6f98fa7fdfb Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Tue, 3 Mar 2015 17:10:44 -0600 Subject: neigh: Factor out ___neigh_lookup_noref While looking at the mpls code I found myself writing yet another version of neigh_lookup_noref. We currently have __ipv4_lookup_noref and __ipv6_lookup_noref. So to make my work a little easier and to make it a smidge easier to verify/maintain the mpls code in the future I stopped and wrote ___neigh_lookup_noref. Then I rewote __ipv4_lookup_noref and __ipv6_lookup_noref in terms of this new function. I tested my new version by verifying that the same code is generated in ip_finish_output2 and ip6_finish_output2 where these functions are inlined. To get to ___neigh_lookup_noref I added a new neighbour cache table function key_eq. So that the static size of the key would be available. I also added __neigh_lookup_noref for people who want to to lookup a neighbour table entry quickly but don't know which neibhgour table they are going to look up. Signed-off-by: "Eric W. Biederman" Signed-off-by: David S. Miller --- include/net/arp.h | 19 ++++-------------- include/net/ndisc.h | 19 +----------------- include/net/neighbour.h | 52 +++++++++++++++++++++++++++++++++++++++++++++++++ net/core/neighbour.c | 20 +++++-------------- net/decnet/dn_neigh.c | 6 ++++++ net/ipv4/arp.c | 9 ++++++++- net/ipv6/ndisc.c | 7 +++++++ 7 files changed, 83 insertions(+), 49 deletions(-) (limited to 'include') diff --git a/include/net/arp.h b/include/net/arp.h index 21ee1860abbc..5e0f891d476c 100644 --- a/include/net/arp.h +++ b/include/net/arp.h @@ -9,28 +9,17 @@ extern struct neigh_table arp_tbl; -static inline u32 arp_hashfn(u32 key, const struct net_device *dev, u32 hash_rnd) +static inline u32 arp_hashfn(const void *pkey, const struct net_device *dev, u32 *hash_rnd) { + u32 key = *(const u32 *)pkey; u32 val = key ^ hash32_ptr(dev); - return val * hash_rnd; + return val * hash_rnd[0]; } static inline struct neighbour *__ipv4_neigh_lookup_noref(struct net_device *dev, u32 key) { - struct neigh_hash_table *nht = rcu_dereference_bh(arp_tbl.nht); - struct neighbour *n; - u32 hash_val; - - hash_val = arp_hashfn(key, dev, nht->hash_rnd[0]) >> (32 - nht->hash_shift); - for (n = rcu_dereference_bh(nht->hash_buckets[hash_val]); - n != NULL; - n = rcu_dereference_bh(n->next)) { - if (n->dev == dev && *(u32 *)n->primary_key == key) - return n; - } - - return NULL; + return ___neigh_lookup_noref(&arp_tbl, neigh_key_eq32, arp_hashfn, &key, dev); } static inline struct neighbour *__ipv4_neigh_lookup(struct net_device *dev, u32 key) diff --git a/include/net/ndisc.h b/include/net/ndisc.h index 6bbda34d5e59..b3a7751251b4 100644 --- a/include/net/ndisc.h +++ b/include/net/ndisc.h @@ -156,24 +156,7 @@ static inline u32 ndisc_hashfn(const void *pkey, const struct net_device *dev, _ static inline struct neighbour *__ipv6_neigh_lookup_noref(struct net_device *dev, const void *pkey) { - struct neigh_hash_table *nht; - const u32 *p32 = pkey; - struct neighbour *n; - u32 hash_val; - - nht = rcu_dereference_bh(nd_tbl.nht); - hash_val = ndisc_hashfn(pkey, dev, nht->hash_rnd) >> (32 - nht->hash_shift); - for (n = rcu_dereference_bh(nht->hash_buckets[hash_val]); - n != NULL; - n = rcu_dereference_bh(n->next)) { - u32 *n32 = (u32 *) n->primary_key; - if (n->dev == dev && - ((n32[0] ^ p32[0]) | (n32[1] ^ p32[1]) | - (n32[2] ^ p32[2]) | (n32[3] ^ p32[3])) == 0) - return n; - } - - return NULL; + return ___neigh_lookup_noref(&nd_tbl, neigh_key_eq128, ndisc_hashfn, pkey, dev); } static inline struct neighbour *__ipv6_neigh_lookup(struct net_device *dev, const void *pkey) diff --git a/include/net/neighbour.h b/include/net/neighbour.h index 9f912e4d4232..14e3f017966b 100644 --- a/include/net/neighbour.h +++ b/include/net/neighbour.h @@ -197,6 +197,7 @@ struct neigh_table { __u32 (*hash)(const void *pkey, const struct net_device *dev, __u32 *hash_rnd); + bool (*key_eq)(const struct neighbour *, const void *pkey); int (*constructor)(struct neighbour *); int (*pconstructor)(struct pneigh_entry *); void (*pdestructor)(struct pneigh_entry *); @@ -247,6 +248,57 @@ static inline void *neighbour_priv(const struct neighbour *n) #define NEIGH_UPDATE_F_ISROUTER 0x40000000 #define NEIGH_UPDATE_F_ADMIN 0x80000000 + +static inline bool neigh_key_eq16(const struct neighbour *n, const void *pkey) +{ + return *(const u16 *)n->primary_key == *(const u16 *)pkey; +} + +static inline bool neigh_key_eq32(const struct neighbour *n, const void *pkey) +{ + return *(const u32 *)n->primary_key == *(const u32 *)pkey; +} + +static inline bool neigh_key_eq128(const struct neighbour *n, const void *pkey) +{ + const u32 *n32 = (const u32 *)n->primary_key; + const u32 *p32 = pkey; + + return ((n32[0] ^ p32[0]) | (n32[1] ^ p32[1]) | + (n32[2] ^ p32[2]) | (n32[3] ^ p32[3])) == 0; +} + +static inline struct neighbour *___neigh_lookup_noref( + struct neigh_table *tbl, + bool (*key_eq)(const struct neighbour *n, const void *pkey), + __u32 (*hash)(const void *pkey, + const struct net_device *dev, + __u32 *hash_rnd), + const void *pkey, + struct net_device *dev) +{ + struct neigh_hash_table *nht = rcu_dereference_bh(tbl->nht); + struct neighbour *n; + u32 hash_val; + + hash_val = hash(pkey, dev, nht->hash_rnd) >> (32 - nht->hash_shift); + for (n = rcu_dereference_bh(nht->hash_buckets[hash_val]); + n != NULL; + n = rcu_dereference_bh(n->next)) { + if (n->dev == dev && key_eq(n, pkey)) + return n; + } + + return NULL; +} + +static inline struct neighbour *__neigh_lookup_noref(struct neigh_table *tbl, + const void *pkey, + struct net_device *dev) +{ + return ___neigh_lookup_noref(tbl, tbl->key_eq, tbl->hash, pkey, dev); +} + void neigh_table_init(int index, struct neigh_table *tbl); int neigh_table_clear(int index, struct neigh_table *tbl); struct neighbour *neigh_lookup(struct neigh_table *tbl, const void *pkey, diff --git a/net/core/neighbour.c b/net/core/neighbour.c index 0f48ea3affed..fe3c6eac5805 100644 --- a/net/core/neighbour.c +++ b/net/core/neighbour.c @@ -397,25 +397,15 @@ struct neighbour *neigh_lookup(struct neigh_table *tbl, const void *pkey, struct net_device *dev) { struct neighbour *n; - int key_len = tbl->key_len; - u32 hash_val; - struct neigh_hash_table *nht; NEIGH_CACHE_STAT_INC(tbl, lookups); rcu_read_lock_bh(); - nht = rcu_dereference_bh(tbl->nht); - hash_val = tbl->hash(pkey, dev, nht->hash_rnd) >> (32 - nht->hash_shift); - - for (n = rcu_dereference_bh(nht->hash_buckets[hash_val]); - n != NULL; - n = rcu_dereference_bh(n->next)) { - if (dev == n->dev && !memcmp(n->primary_key, pkey, key_len)) { - if (!atomic_inc_not_zero(&n->refcnt)) - n = NULL; - NEIGH_CACHE_STAT_INC(tbl, hits); - break; - } + n = __neigh_lookup_noref(tbl, pkey, dev); + if (n) { + if (!atomic_inc_not_zero(&n->refcnt)) + n = NULL; + NEIGH_CACHE_STAT_INC(tbl, hits); } rcu_read_unlock_bh(); diff --git a/net/decnet/dn_neigh.c b/net/decnet/dn_neigh.c index f123c6c6748c..ee7d1cef0027 100644 --- a/net/decnet/dn_neigh.c +++ b/net/decnet/dn_neigh.c @@ -93,12 +93,18 @@ static u32 dn_neigh_hash(const void *pkey, return jhash_2words(*(__u16 *)pkey, 0, hash_rnd[0]); } +static bool dn_key_eq(const struct neighbour *neigh, const void *pkey) +{ + return neigh_key_eq16(neigh, pkey); +} + struct neigh_table dn_neigh_table = { .family = PF_DECnet, .entry_size = NEIGH_ENTRY_SIZE(sizeof(struct dn_neigh)), .key_len = sizeof(__le16), .protocol = cpu_to_be16(ETH_P_DNA_RT), .hash = dn_neigh_hash, + .key_eq = dn_key_eq, .constructor = dn_neigh_construct, .id = "dn_neigh_cache", .parms ={ diff --git a/net/ipv4/arp.c b/net/ipv4/arp.c index 6b8aad6a0d7d..5f5c674e130a 100644 --- a/net/ipv4/arp.c +++ b/net/ipv4/arp.c @@ -122,6 +122,7 @@ * Interface to generic neighbour cache. */ static u32 arp_hash(const void *pkey, const struct net_device *dev, __u32 *hash_rnd); +static bool arp_key_eq(const struct neighbour *n, const void *pkey); static int arp_constructor(struct neighbour *neigh); static void arp_solicit(struct neighbour *neigh, struct sk_buff *skb); static void arp_error_report(struct neighbour *neigh, struct sk_buff *skb); @@ -154,6 +155,7 @@ struct neigh_table arp_tbl = { .key_len = 4, .protocol = cpu_to_be16(ETH_P_IP), .hash = arp_hash, + .key_eq = arp_key_eq, .constructor = arp_constructor, .proxy_redo = parp_redo, .id = "arp_cache", @@ -209,7 +211,12 @@ static u32 arp_hash(const void *pkey, const struct net_device *dev, __u32 *hash_rnd) { - return arp_hashfn(*(u32 *)pkey, dev, *hash_rnd); + return arp_hashfn(pkey, dev, hash_rnd); +} + +static bool arp_key_eq(const struct neighbour *neigh, const void *pkey) +{ + return neigh_key_eq32(neigh, pkey); } static int arp_constructor(struct neighbour *neigh) diff --git a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c index e363bbc2420d..247ad7c298f7 100644 --- a/net/ipv6/ndisc.c +++ b/net/ipv6/ndisc.c @@ -84,6 +84,7 @@ do { \ static u32 ndisc_hash(const void *pkey, const struct net_device *dev, __u32 *hash_rnd); +static bool ndisc_key_eq(const struct neighbour *neigh, const void *pkey); static int ndisc_constructor(struct neighbour *neigh); static void ndisc_solicit(struct neighbour *neigh, struct sk_buff *skb); static void ndisc_error_report(struct neighbour *neigh, struct sk_buff *skb); @@ -119,6 +120,7 @@ struct neigh_table nd_tbl = { .key_len = sizeof(struct in6_addr), .protocol = cpu_to_be16(ETH_P_IPV6), .hash = ndisc_hash, + .key_eq = ndisc_key_eq, .constructor = ndisc_constructor, .pconstructor = pndisc_constructor, .pdestructor = pndisc_destructor, @@ -295,6 +297,11 @@ static u32 ndisc_hash(const void *pkey, return ndisc_hashfn(pkey, dev, hash_rnd); } +static bool ndisc_key_eq(const struct neighbour *n, const void *pkey) +{ + return neigh_key_eq128(n, pkey); +} + static int ndisc_constructor(struct neighbour *neigh) { struct in6_addr *addr = (struct in6_addr *)&neigh->primary_key; -- cgit v1.2.3 From 4fd3d7d9e868ffbdb0e7a67c5c8e9dfdcd846a62 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Tue, 3 Mar 2015 17:11:16 -0600 Subject: neigh: Add helper function neigh_xmit For MPLS I am building the code so that either the neighbour mac address can be specified or we can have a next hop in ipv4 or ipv6. The kind of next hop we have is indicated by the neighbour table pointer. A neighbour table pointer of NULL is a link layer address. A non-NULL neighbour table pointer indicates which neighbour table and thus which address family the next hop address is in that we need to look up. The code either sends a packet directly or looks up the appropriate neighbour table entry and sends the packet. Signed-off-by: "Eric W. Biederman" Signed-off-by: David S. Miller --- include/net/neighbour.h | 3 +++ net/core/neighbour.c | 34 ++++++++++++++++++++++++++++++++++ 2 files changed, 37 insertions(+) (limited to 'include') diff --git a/include/net/neighbour.h b/include/net/neighbour.h index 14e3f017966b..afb8237b0a8c 100644 --- a/include/net/neighbour.h +++ b/include/net/neighbour.h @@ -358,6 +358,7 @@ void neigh_for_each(struct neigh_table *tbl, void (*cb)(struct neighbour *, void *), void *cookie); void __neigh_for_each_release(struct neigh_table *tbl, int (*cb)(struct neighbour *)); +int neigh_xmit(int fam, struct net_device *, const void *, struct sk_buff *); void pneigh_for_each(struct neigh_table *tbl, void (*cb)(struct pneigh_entry *)); @@ -511,4 +512,6 @@ static inline void neigh_ha_snapshot(char *dst, const struct neighbour *n, memcpy(dst, n->ha, dev->addr_len); } while (read_seqretry(&n->ha_lock, seq)); } + + #endif diff --git a/net/core/neighbour.c b/net/core/neighbour.c index fe3c6eac5805..cffaf00561e7 100644 --- a/net/core/neighbour.c +++ b/net/core/neighbour.c @@ -2391,6 +2391,40 @@ void __neigh_for_each_release(struct neigh_table *tbl, } EXPORT_SYMBOL(__neigh_for_each_release); +int neigh_xmit(int family, struct net_device *dev, + const void *addr, struct sk_buff *skb) +{ + int err; + if (family == AF_PACKET) { + err = dev_hard_header(skb, dev, ntohs(skb->protocol), + addr, NULL, skb->len); + if (err < 0) + goto out_kfree_skb; + err = dev_queue_xmit(skb); + } else { + struct neigh_table *tbl; + struct neighbour *neigh; + + err = -ENETDOWN; + tbl = neigh_find_table(family); + if (!tbl) + goto out; + neigh = __neigh_lookup_noref(tbl, addr, dev); + if (!neigh) + neigh = __neigh_create(tbl, addr, dev, false); + err = PTR_ERR(neigh); + if (IS_ERR(neigh)) + goto out_kfree_skb; + err = neigh->output(neigh, skb); + } +out: + return err; +out_kfree_skb: + kfree_skb(skb); + goto out; +} +EXPORT_SYMBOL(neigh_xmit); + #ifdef CONFIG_PROC_FS static struct neighbour *neigh_get_first(struct seq_file *seq) -- cgit v1.2.3 From 0189197f441602acdca3f97750d392a895b778fd Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Tue, 3 Mar 2015 19:10:47 -0600 Subject: mpls: Basic routing support This change adds a new Kconfig option MPLS_ROUTING. The core of this change is the code to look at an mpls packet received from another machine. Look that packet up in a routing table and forward the packet on. Support of MPLS over ATM is not considered or attempted here. This implemntation follows RFC3032 and implements the MPLS shim header that can pass over essentially any network. What RFC3021 refers to as the as the Incoming Label Map (ILM) I call net->mpls.platform_label[]. What RFC3031 refers to as the Next Label Hop Forwarding Entry (NHLFE) I call mpls_route. Though calling it the label fordwarding information base (lfib) might also be valid. Further the implemntation forwards packets as described in RFC3032. There is no need and given the original motivation for MPLS a strong discincentive to have a flexible label forwarding path. In essence the logic is the topmost label is read, looked up, removed, and replaced by 0 or more new lables and the sent out the specified interface to it's next hop. Quite a few optional features are not implemented here. Among them are generation of ICMP errors when the TTL is exceeded or the packet is larger than the next hop MTU (those conditions are detected and the packets are dropped instead of generating an icmp error). The traffic class field is always set to 0. The implementation focuses on IP over MPLS and does not handle egress of other kinds of protocols. Instead of implementing coordination with the neighbour table and sorting out how to input next hops in a different address family (for which there is value). I was lazy and implemented a next hop mac address instead. The code is simpler and there are flavor of MPLS such as MPLS-TP where neither an IPv4 nor an IPv6 next hop is appropriate so a next hop by mac address would need to be implemented at some point. Two new definitions AF_MPLS and PF_MPLS are exposed to userspace. Decoding the mpls header must be done by first byeswapping a 32bit bit endian word into the local cpu endian and then bit shifting to extract the pieces. There is no C bit-field that can represent a wire format mpls header on a little endian machine as the low bits of the 20bit label wind up in the wrong half of third byte. Therefore internally everything is deal with in cpu native byte order except when writing to and reading from a packet. For management simplicity if a label is configured to forward out an interface that is down the packet is dropped early. Similarly if an network interface is removed rt_dev is updated to NULL (so no reference is preserved) and any packets for that label are dropped. Keeping the label entries in the kernel allows the kernel label table to function as the definitive source of which labels are allocated and which are not. Signed-off-by: "Eric W. Biederman" Signed-off-by: David S. Miller --- include/linux/socket.h | 2 + include/net/net_namespace.h | 4 + include/net/netns/mpls.h | 15 ++ net/mpls/Kconfig | 5 + net/mpls/Makefile | 1 + net/mpls/af_mpls.c | 349 ++++++++++++++++++++++++++++++++++++++++++++ net/mpls/internal.h | 56 +++++++ 7 files changed, 432 insertions(+) create mode 100644 include/net/netns/mpls.h create mode 100644 net/mpls/af_mpls.c create mode 100644 net/mpls/internal.h (limited to 'include') diff --git a/include/linux/socket.h b/include/linux/socket.h index 5c19cba34dce..fab4d0ddf4ed 100644 --- a/include/linux/socket.h +++ b/include/linux/socket.h @@ -181,6 +181,7 @@ struct ucred { #define AF_WANPIPE 25 /* Wanpipe API Sockets */ #define AF_LLC 26 /* Linux LLC */ #define AF_IB 27 /* Native InfiniBand address */ +#define AF_MPLS 28 /* MPLS */ #define AF_CAN 29 /* Controller Area Network */ #define AF_TIPC 30 /* TIPC sockets */ #define AF_BLUETOOTH 31 /* Bluetooth sockets */ @@ -226,6 +227,7 @@ struct ucred { #define PF_WANPIPE AF_WANPIPE #define PF_LLC AF_LLC #define PF_IB AF_IB +#define PF_MPLS AF_MPLS #define PF_CAN AF_CAN #define PF_TIPC AF_TIPC #define PF_BLUETOOTH AF_BLUETOOTH diff --git a/include/net/net_namespace.h b/include/net/net_namespace.h index 36faf4990c4b..2cb9acb618e9 100644 --- a/include/net/net_namespace.h +++ b/include/net/net_namespace.h @@ -26,6 +26,7 @@ #endif #include #include +#include #include struct user_namespace; @@ -129,6 +130,9 @@ struct net { #endif #if IS_ENABLED(CONFIG_IP_VS) struct netns_ipvs *ipvs; +#endif +#if IS_ENABLED(CONFIG_MPLS) + struct netns_mpls mpls; #endif struct sock *diag_nlsk; atomic_t fnhe_genid; diff --git a/include/net/netns/mpls.h b/include/net/netns/mpls.h new file mode 100644 index 000000000000..f90aaf8d4f89 --- /dev/null +++ b/include/net/netns/mpls.h @@ -0,0 +1,15 @@ +/* + * mpls in net namespaces + */ + +#ifndef __NETNS_MPLS_H__ +#define __NETNS_MPLS_H__ + +struct mpls_route; + +struct netns_mpls { + size_t platform_labels; + struct mpls_route __rcu * __rcu *platform_label; +}; + +#endif /* __NETNS_MPLS_H__ */ diff --git a/net/mpls/Kconfig b/net/mpls/Kconfig index a77fbcdd04ee..f4286ee7e2b0 100644 --- a/net/mpls/Kconfig +++ b/net/mpls/Kconfig @@ -22,4 +22,9 @@ config NET_MPLS_GSO that have had MPLS stack entries pushed onto them and thus become MPLS GSO packets. +config MPLS_ROUTING + bool "MPLS: routing support" + help + Add support for forwarding of mpls packets. + endif # MPLS diff --git a/net/mpls/Makefile b/net/mpls/Makefile index 6dec088c2d0f..60af15f1960e 100644 --- a/net/mpls/Makefile +++ b/net/mpls/Makefile @@ -2,3 +2,4 @@ # Makefile for MPLS. # obj-$(CONFIG_NET_MPLS_GSO) += mpls_gso.o +obj-$(CONFIG_MPLS_ROUTING) += af_mpls.o diff --git a/net/mpls/af_mpls.c b/net/mpls/af_mpls.c new file mode 100644 index 000000000000..924377736b2a --- /dev/null +++ b/net/mpls/af_mpls.c @@ -0,0 +1,349 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "internal.h" + +#define MAX_NEW_LABELS 2 + +/* This maximum ha length copied from the definition of struct neighbour */ +#define MAX_VIA_ALEN (ALIGN(MAX_ADDR_LEN, sizeof(unsigned long))) + +struct mpls_route { /* next hop label forwarding entry */ + struct net_device *rt_dev; + struct rcu_head rt_rcu; + u32 rt_label[MAX_NEW_LABELS]; + u8 rt_protocol; /* routing protocol that set this entry */ + u8 rt_labels:2, + rt_via_alen:6; + unsigned short rt_via_family; + u8 rt_via[0]; +}; + +static struct mpls_route *mpls_route_input_rcu(struct net *net, unsigned index) +{ + struct mpls_route *rt = NULL; + + if (index < net->mpls.platform_labels) { + struct mpls_route __rcu **platform_label = + rcu_dereference(net->mpls.platform_label); + rt = rcu_dereference(platform_label[index]); + } + return rt; +} + +static bool mpls_output_possible(const struct net_device *dev) +{ + return dev && (dev->flags & IFF_UP) && netif_carrier_ok(dev); +} + +static unsigned int mpls_rt_header_size(const struct mpls_route *rt) +{ + /* The size of the layer 2.5 labels to be added for this route */ + return rt->rt_labels * sizeof(struct mpls_shim_hdr); +} + +static unsigned int mpls_dev_mtu(const struct net_device *dev) +{ + /* The amount of data the layer 2 frame can hold */ + return dev->mtu; +} + +static bool mpls_pkt_too_big(const struct sk_buff *skb, unsigned int mtu) +{ + if (skb->len <= mtu) + return false; + + if (skb_is_gso(skb) && skb_gso_network_seglen(skb) <= mtu) + return false; + + return true; +} + +static bool mpls_egress(struct mpls_route *rt, struct sk_buff *skb, + struct mpls_entry_decoded dec) +{ + /* RFC4385 and RFC5586 encode other packets in mpls such that + * they don't conflict with the ip version number, making + * decoding by examining the ip version correct in everything + * except for the strangest cases. + * + * The strange cases if we choose to support them will require + * manual configuration. + */ + struct iphdr *hdr4 = ip_hdr(skb); + bool success = true; + + if (hdr4->version == 4) { + skb->protocol = htons(ETH_P_IP); + csum_replace2(&hdr4->check, + htons(hdr4->ttl << 8), + htons(dec.ttl << 8)); + hdr4->ttl = dec.ttl; + } + else if (hdr4->version == 6) { + struct ipv6hdr *hdr6 = ipv6_hdr(skb); + skb->protocol = htons(ETH_P_IPV6); + hdr6->hop_limit = dec.ttl; + } + else + /* version 0 and version 1 are used by pseudo wires */ + success = false; + return success; +} + +static int mpls_forward(struct sk_buff *skb, struct net_device *dev, + struct packet_type *pt, struct net_device *orig_dev) +{ + struct net *net = dev_net(dev); + struct mpls_shim_hdr *hdr; + struct mpls_route *rt; + struct mpls_entry_decoded dec; + struct net_device *out_dev; + unsigned int hh_len; + unsigned int new_header_size; + unsigned int mtu; + int err; + + /* Careful this entire function runs inside of an rcu critical section */ + + if (skb->pkt_type != PACKET_HOST) + goto drop; + + if ((skb = skb_share_check(skb, GFP_ATOMIC)) == NULL) + goto drop; + + if (!pskb_may_pull(skb, sizeof(*hdr))) + goto drop; + + /* Read and decode the label */ + hdr = mpls_hdr(skb); + dec = mpls_entry_decode(hdr); + + /* Pop the label */ + skb_pull(skb, sizeof(*hdr)); + skb_reset_network_header(skb); + + skb_orphan(skb); + + rt = mpls_route_input_rcu(net, dec.label); + if (!rt) + goto drop; + + /* Find the output device */ + out_dev = rt->rt_dev; + if (!mpls_output_possible(out_dev)) + goto drop; + + if (skb_warn_if_lro(skb)) + goto drop; + + skb_forward_csum(skb); + + /* Verify ttl is valid */ + if (dec.ttl <= 2) + goto drop; + dec.ttl -= 1; + + /* Verify the destination can hold the packet */ + new_header_size = mpls_rt_header_size(rt); + mtu = mpls_dev_mtu(out_dev); + if (mpls_pkt_too_big(skb, mtu - new_header_size)) + goto drop; + + hh_len = LL_RESERVED_SPACE(out_dev); + if (!out_dev->header_ops) + hh_len = 0; + + /* Ensure there is enough space for the headers in the skb */ + if (skb_cow(skb, hh_len + new_header_size)) + goto drop; + + skb->dev = out_dev; + skb->protocol = htons(ETH_P_MPLS_UC); + + if (unlikely(!new_header_size && dec.bos)) { + /* Penultimate hop popping */ + if (!mpls_egress(rt, skb, dec)) + goto drop; + } else { + bool bos; + int i; + skb_push(skb, new_header_size); + skb_reset_network_header(skb); + /* Push the new labels */ + hdr = mpls_hdr(skb); + bos = dec.bos; + for (i = rt->rt_labels - 1; i >= 0; i--) { + hdr[i] = mpls_entry_encode(rt->rt_label[i], dec.ttl, 0, bos); + bos = false; + } + } + + err = neigh_xmit(rt->rt_via_family, out_dev, rt->rt_via, skb); + if (err) + net_dbg_ratelimited("%s: packet transmission failed: %d\n", + __func__, err); + return 0; + +drop: + kfree_skb(skb); + return NET_RX_DROP; +} + +static struct packet_type mpls_packet_type __read_mostly = { + .type = cpu_to_be16(ETH_P_MPLS_UC), + .func = mpls_forward, +}; + +static struct mpls_route *mpls_rt_alloc(size_t alen) +{ + struct mpls_route *rt; + + rt = kzalloc(GFP_KERNEL, sizeof(*rt) + alen); + if (rt) + rt->rt_via_alen = alen; + return rt; +} + +static void mpls_rt_free(struct mpls_route *rt) +{ + if (rt) + kfree_rcu(rt, rt_rcu); +} + +static void mpls_route_update(struct net *net, unsigned index, + struct net_device *dev, struct mpls_route *new, + const struct nl_info *info) +{ + struct mpls_route *rt, *old = NULL; + + ASSERT_RTNL(); + + rt = net->mpls.platform_label[index]; + if (!dev || (rt && (rt->rt_dev == dev))) { + rcu_assign_pointer(net->mpls.platform_label[index], new); + old = rt; + } + + /* If we removed a route free it now */ + mpls_rt_free(old); +} + +static void mpls_ifdown(struct net_device *dev) +{ + struct net *net = dev_net(dev); + unsigned index; + + for (index = 0; index < net->mpls.platform_labels; index++) { + struct mpls_route *rt = net->mpls.platform_label[index]; + if (!rt) + continue; + if (rt->rt_dev != dev) + continue; + rt->rt_dev = NULL; + } +} + +static int mpls_dev_notify(struct notifier_block *this, unsigned long event, + void *ptr) +{ + struct net_device *dev = netdev_notifier_info_to_dev(ptr); + + switch(event) { + case NETDEV_UNREGISTER: + mpls_ifdown(dev); + break; + } + return NOTIFY_OK; +} + +static struct notifier_block mpls_dev_notifier = { + .notifier_call = mpls_dev_notify, +}; + +static int mpls_net_init(struct net *net) +{ + net->mpls.platform_labels = 0; + net->mpls.platform_label = NULL; + + return 0; +} + +static void mpls_net_exit(struct net *net) +{ + unsigned int index; + + /* An rcu grace period haselapsed since there was a device in + * the network namespace (and thus the last in fqlight packet) + * left this network namespace. This is because + * unregister_netdevice_many and netdev_run_todo has completed + * for each network device that was in this network namespace. + * + * As such no additional rcu synchronization is necessary when + * freeing the platform_label table. + */ + rtnl_lock(); + for (index = 0; index < net->mpls.platform_labels; index++) { + struct mpls_route *rt = net->mpls.platform_label[index]; + rcu_assign_pointer(net->mpls.platform_label[index], NULL); + mpls_rt_free(rt); + } + rtnl_unlock(); + + kvfree(net->mpls.platform_label); +} + +static struct pernet_operations mpls_net_ops = { + .init = mpls_net_init, + .exit = mpls_net_exit, +}; + +static int __init mpls_init(void) +{ + int err; + + BUILD_BUG_ON(sizeof(struct mpls_shim_hdr) != 4); + + err = register_pernet_subsys(&mpls_net_ops); + if (err) + goto out; + + err = register_netdevice_notifier(&mpls_dev_notifier); + if (err) + goto out_unregister_pernet; + + dev_add_pack(&mpls_packet_type); + + err = 0; +out: + return err; + +out_unregister_pernet: + unregister_pernet_subsys(&mpls_net_ops); + goto out; +} +module_init(mpls_init); + +static void __exit mpls_exit(void) +{ + dev_remove_pack(&mpls_packet_type); + unregister_netdevice_notifier(&mpls_dev_notifier); + unregister_pernet_subsys(&mpls_net_ops); +} +module_exit(mpls_exit); + +MODULE_DESCRIPTION("MultiProtocol Label Switching"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS_NETPROTO(PF_MPLS); diff --git a/net/mpls/internal.h b/net/mpls/internal.h new file mode 100644 index 000000000000..c2944cb84d48 --- /dev/null +++ b/net/mpls/internal.h @@ -0,0 +1,56 @@ +#ifndef MPLS_INTERNAL_H +#define MPLS_INTERNAL_H + +#define LABEL_IPV4_EXPLICIT_NULL 0 /* RFC3032 */ +#define LABEL_ROUTER_ALERT_LABEL 1 /* RFC3032 */ +#define LABEL_IPV6_EXPLICIT_NULL 2 /* RFC3032 */ +#define LABEL_IMPLICIT_NULL 3 /* RFC3032 */ +#define LABEL_ENTROPY_INDICATOR 7 /* RFC6790 */ +#define LABEL_GAL 13 /* RFC5586 */ +#define LABEL_OAM_ALERT 14 /* RFC3429 */ +#define LABEL_EXTENSION 15 /* RFC7274 */ + + +struct mpls_shim_hdr { + __be32 label_stack_entry; +}; + +struct mpls_entry_decoded { + u32 label; + u8 ttl; + u8 tc; + u8 bos; +}; + +struct sk_buff; + +static inline struct mpls_shim_hdr *mpls_hdr(const struct sk_buff *skb) +{ + return (struct mpls_shim_hdr *)skb_network_header(skb); +} + +static inline struct mpls_shim_hdr mpls_entry_encode(u32 label, unsigned ttl, unsigned tc, bool bos) +{ + struct mpls_shim_hdr result; + result.label_stack_entry = + cpu_to_be32((label << MPLS_LS_LABEL_SHIFT) | + (tc << MPLS_LS_TC_SHIFT) | + (bos ? (1 << MPLS_LS_S_SHIFT) : 0) | + (ttl << MPLS_LS_TTL_SHIFT)); + return result; +} + +static inline struct mpls_entry_decoded mpls_entry_decode(struct mpls_shim_hdr *hdr) +{ + struct mpls_entry_decoded result; + unsigned entry = be32_to_cpu(hdr->label_stack_entry); + + result.label = (entry & MPLS_LS_LABEL_MASK) >> MPLS_LS_LABEL_SHIFT; + result.ttl = (entry & MPLS_LS_TTL_MASK) >> MPLS_LS_TTL_SHIFT; + result.tc = (entry & MPLS_LS_TC_MASK) >> MPLS_LS_TC_SHIFT; + result.bos = (entry & MPLS_LS_S_MASK) >> MPLS_LS_S_SHIFT; + + return result; +} + +#endif /* MPLS_INTERNAL_H */ -- cgit v1.2.3 From 7720c01f3f590116882e251f13c7e1d5602f8643 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Tue, 3 Mar 2015 19:11:20 -0600 Subject: mpls: Add a sysctl to control the size of the mpls label table This sysctl gives two benefits. By defaulting the table size to 0 mpls even when compiled in and enabled defaults to not forwarding any packets. This prevents unpleasant surprises for users. The other benefit is that as mpls labels are allocated locally a dense table a small dense label table may be used which saves memory and is extremely simple and efficient to implement. This sysctl allows userspace to choose the restrictions on the label table size userspace applications need to cope with. Signed-off-by: "Eric W. Biederman" Signed-off-by: David S. Miller --- Documentation/networking/mpls-sysctl.txt | 20 +++++ include/net/netns/mpls.h | 2 + net/mpls/af_mpls.c | 146 +++++++++++++++++++++++++++++++ 3 files changed, 168 insertions(+) create mode 100644 Documentation/networking/mpls-sysctl.txt (limited to 'include') diff --git a/Documentation/networking/mpls-sysctl.txt b/Documentation/networking/mpls-sysctl.txt new file mode 100644 index 000000000000..639ddf0ece9b --- /dev/null +++ b/Documentation/networking/mpls-sysctl.txt @@ -0,0 +1,20 @@ +/proc/sys/net/mpls/* Variables: + +platform_labels - INTEGER + Number of entries in the platform label table. It is not + possible to configure forwarding for label values equal to or + greater than the number of platform labels. + + A dense utliziation of the entries in the platform label table + is possible and expected aas the platform labels are locally + allocated. + + If the number of platform label table entries is set to 0 no + label will be recognized by the kernel and mpls forwarding + will be disabled. + + Reducing this value will remove all label routing entries that + no longer fit in the table. + + Possible values: 0 - 1048575 + Default: 0 diff --git a/include/net/netns/mpls.h b/include/net/netns/mpls.h index f90aaf8d4f89..d29203651c01 100644 --- a/include/net/netns/mpls.h +++ b/include/net/netns/mpls.h @@ -6,10 +6,12 @@ #define __NETNS_MPLS_H__ struct mpls_route; +struct ctl_table_header; struct netns_mpls { size_t platform_labels; struct mpls_route __rcu * __rcu *platform_label; + struct ctl_table_header *ctl; }; #endif /* __NETNS_MPLS_H__ */ diff --git a/net/mpls/af_mpls.c b/net/mpls/af_mpls.c index 924377736b2a..b097125dfa33 100644 --- a/net/mpls/af_mpls.c +++ b/net/mpls/af_mpls.c @@ -1,6 +1,7 @@ #include #include #include +#include #include #include #include @@ -31,6 +32,9 @@ struct mpls_route { /* next hop label forwarding entry */ u8 rt_via[0]; }; +static int zero = 0; +static int label_limit = (1 << 20) - 1; + static struct mpls_route *mpls_route_input_rcu(struct net *net, unsigned index) { struct mpls_route *rt = NULL; @@ -273,18 +277,160 @@ static struct notifier_block mpls_dev_notifier = { .notifier_call = mpls_dev_notify, }; +static int resize_platform_label_table(struct net *net, size_t limit) +{ + size_t size = sizeof(struct mpls_route *) * limit; + size_t old_limit; + size_t cp_size; + struct mpls_route __rcu **labels = NULL, **old; + struct mpls_route *rt0 = NULL, *rt2 = NULL; + unsigned index; + + if (size) { + labels = kzalloc(size, GFP_KERNEL | __GFP_NOWARN | __GFP_NORETRY); + if (!labels) + labels = vzalloc(size); + + if (!labels) + goto nolabels; + } + + /* In case the predefined labels need to be populated */ + if (limit > LABEL_IPV4_EXPLICIT_NULL) { + struct net_device *lo = net->loopback_dev; + rt0 = mpls_rt_alloc(lo->addr_len); + if (!rt0) + goto nort0; + rt0->rt_dev = lo; + rt0->rt_protocol = RTPROT_KERNEL; + rt0->rt_via_family = AF_PACKET; + memcpy(rt0->rt_via, lo->dev_addr, lo->addr_len); + } + if (limit > LABEL_IPV6_EXPLICIT_NULL) { + struct net_device *lo = net->loopback_dev; + rt2 = mpls_rt_alloc(lo->addr_len); + if (!rt2) + goto nort2; + rt2->rt_dev = lo; + rt2->rt_protocol = RTPROT_KERNEL; + rt2->rt_via_family = AF_PACKET; + memcpy(rt2->rt_via, lo->dev_addr, lo->addr_len); + } + + rtnl_lock(); + /* Remember the original table */ + old = net->mpls.platform_label; + old_limit = net->mpls.platform_labels; + + /* Free any labels beyond the new table */ + for (index = limit; index < old_limit; index++) + mpls_route_update(net, index, NULL, NULL, NULL); + + /* Copy over the old labels */ + cp_size = size; + if (old_limit < limit) + cp_size = old_limit * sizeof(struct mpls_route *); + + memcpy(labels, old, cp_size); + + /* If needed set the predefined labels */ + if ((old_limit <= LABEL_IPV6_EXPLICIT_NULL) && + (limit > LABEL_IPV6_EXPLICIT_NULL)) { + labels[LABEL_IPV6_EXPLICIT_NULL] = rt2; + rt2 = NULL; + } + + if ((old_limit <= LABEL_IPV4_EXPLICIT_NULL) && + (limit > LABEL_IPV4_EXPLICIT_NULL)) { + labels[LABEL_IPV4_EXPLICIT_NULL] = rt0; + rt0 = NULL; + } + + /* Update the global pointers */ + net->mpls.platform_labels = limit; + net->mpls.platform_label = labels; + + rtnl_unlock(); + + mpls_rt_free(rt2); + mpls_rt_free(rt0); + + if (old) { + synchronize_rcu(); + kvfree(old); + } + return 0; + +nort2: + mpls_rt_free(rt0); +nort0: + kvfree(labels); +nolabels: + return -ENOMEM; +} + +static int mpls_platform_labels(struct ctl_table *table, int write, + void __user *buffer, size_t *lenp, loff_t *ppos) +{ + struct net *net = table->data; + int platform_labels = net->mpls.platform_labels; + int ret; + struct ctl_table tmp = { + .procname = table->procname, + .data = &platform_labels, + .maxlen = sizeof(int), + .mode = table->mode, + .extra1 = &zero, + .extra2 = &label_limit, + }; + + ret = proc_dointvec_minmax(&tmp, write, buffer, lenp, ppos); + + if (write && ret == 0) + ret = resize_platform_label_table(net, platform_labels); + + return ret; +} + +static struct ctl_table mpls_table[] = { + { + .procname = "platform_labels", + .data = NULL, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = mpls_platform_labels, + }, + { } +}; + static int mpls_net_init(struct net *net) { + struct ctl_table *table; + net->mpls.platform_labels = 0; net->mpls.platform_label = NULL; + table = kmemdup(mpls_table, sizeof(mpls_table), GFP_KERNEL); + if (table == NULL) + return -ENOMEM; + + table[0].data = net; + net->mpls.ctl = register_net_sysctl(net, "net/mpls", table); + if (net->mpls.ctl == NULL) + return -ENOMEM; + return 0; } static void mpls_net_exit(struct net *net) { + struct ctl_table *table; unsigned int index; + table = net->mpls.ctl->ctl_table_arg; + unregister_net_sysctl_table(net->mpls.ctl); + kfree(table); + /* An rcu grace period haselapsed since there was a device in * the network namespace (and thus the last in fqlight packet) * left this network namespace. This is because -- cgit v1.2.3 From 03c0566542f4c7a45ce3193f27cbf5700b506c18 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Tue, 3 Mar 2015 19:13:56 -0600 Subject: mpls: Netlink commands to add, remove, and dump routes This change adds two new netlink routing attributes: RTA_VIA and RTA_NEWDST. RTA_VIA specifies the specifies the next machine to send a packet to like RTA_GATEWAY. RTA_VIA differs from RTA_GATEWAY in that it includes the address family of the address of the next machine to send a packet to. Currently the MPLS code supports addresses in AF_INET, AF_INET6 and AF_PACKET. For AF_INET and AF_INET6 the destination mac address is acquired from the neighbour table. For AF_PACKET the destination mac_address is specified in the netlink configuration. I think raw destination mac address support with the family AF_PACKET will prove useful. There is MPLS-TP which is defined to operate on machines that do not support internet packets of any flavor. Further seem to be corner cases where it can be useful. At this point I don't care much either way. RTA_NEWDST specifies the destination address to forward the packet with. MPLS typically changes it's destination address at every hop. For a swap operation RTA_NEWDST is specified with a length of one label. For a push operation RTA_NEWDST is specified with two or more labels. For a pop operation RTA_NEWDST is not specified or equivalently an emtpy RTAN_NEWDST is specified. Those new netlink attributes are used to implement handling of rt-netlink RTM_NEWROUTE, RTM_DELROUTE, and RTM_GETROUTE messages, to maintain the MPLS label table. rtm_to_route_config parses a netlink RTM_NEWROUTE or RTM_DELROUTE message, verify no unhandled attributes or unhandled values are present and sets up the data structures for mpls_route_add and mpls_route_del. I did my best to match up with the existing conventions with the caveats that MPLS addresses are all destination-specific-addresses, and so don't properly have a scope. Signed-off-by: "Eric W. Biederman" Signed-off-by: David S. Miller --- include/uapi/linux/rtnetlink.h | 8 ++ net/mpls/af_mpls.c | 229 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 237 insertions(+) (limited to 'include') diff --git a/include/uapi/linux/rtnetlink.h b/include/uapi/linux/rtnetlink.h index 5cc5d66bf519..bad65550ae3e 100644 --- a/include/uapi/linux/rtnetlink.h +++ b/include/uapi/linux/rtnetlink.h @@ -303,6 +303,8 @@ enum rtattr_type_t { RTA_TABLE, RTA_MARK, RTA_MFC_STATS, + RTA_VIA, + RTA_NEWDST, __RTA_MAX }; @@ -344,6 +346,12 @@ struct rtnexthop { #define RTNH_SPACE(len) RTNH_ALIGN(RTNH_LENGTH(len)) #define RTNH_DATA(rtnh) ((struct rtattr*)(((char*)(rtnh)) + RTNH_LENGTH(0))) +/* RTA_VIA */ +struct rtvia { + __kernel_sa_family_t rtvia_family; + __u8 rtvia_addr[0]; +}; + /* RTM_CACHEINFO */ struct rta_cacheinfo { diff --git a/net/mpls/af_mpls.c b/net/mpls/af_mpls.c index 2d6612a10e30..b4d7cec398d2 100644 --- a/net/mpls/af_mpls.c +++ b/net/mpls/af_mpls.c @@ -212,6 +212,11 @@ static struct packet_type mpls_packet_type __read_mostly = { .func = mpls_forward, }; +const struct nla_policy rtm_mpls_policy[RTA_MAX+1] = { + [RTA_DST] = { .type = NLA_U32 }, + [RTA_OIF] = { .type = NLA_U32 }, +}; + struct mpls_route_config { u32 rc_protocol; u32 rc_ifindex; @@ -410,6 +415,22 @@ static struct notifier_block mpls_dev_notifier = { .notifier_call = mpls_dev_notify, }; +static int nla_put_via(struct sk_buff *skb, + u16 family, const void *addr, int alen) +{ + struct nlattr *nla; + struct rtvia *via; + + nla = nla_reserve(skb, RTA_VIA, alen + 2); + if (!nla) + return -EMSGSIZE; + + via = nla_data(nla); + via->rtvia_family = family; + memcpy(via->rtvia_addr, addr, alen); + return 0; +} + int nla_put_labels(struct sk_buff *skb, int attrtype, u8 labels, const u32 label[]) { @@ -467,6 +488,210 @@ int nla_get_labels(const struct nlattr *nla, return 0; } +static int rtm_to_route_config(struct sk_buff *skb, struct nlmsghdr *nlh, + struct mpls_route_config *cfg) +{ + struct rtmsg *rtm; + struct nlattr *tb[RTA_MAX+1]; + int index; + int err; + + err = nlmsg_parse(nlh, sizeof(*rtm), tb, RTA_MAX, rtm_mpls_policy); + if (err < 0) + goto errout; + + err = -EINVAL; + rtm = nlmsg_data(nlh); + memset(cfg, 0, sizeof(*cfg)); + + if (rtm->rtm_family != AF_MPLS) + goto errout; + if (rtm->rtm_dst_len != 20) + goto errout; + if (rtm->rtm_src_len != 0) + goto errout; + if (rtm->rtm_tos != 0) + goto errout; + if (rtm->rtm_table != RT_TABLE_MAIN) + goto errout; + /* Any value is acceptable for rtm_protocol */ + + /* As mpls uses destination specific addresses + * (or source specific address in the case of multicast) + * all addresses have universal scope. + */ + if (rtm->rtm_scope != RT_SCOPE_UNIVERSE) + goto errout; + if (rtm->rtm_type != RTN_UNICAST) + goto errout; + if (rtm->rtm_flags != 0) + goto errout; + + cfg->rc_label = LABEL_NOT_SPECIFIED; + cfg->rc_protocol = rtm->rtm_protocol; + cfg->rc_nlflags = nlh->nlmsg_flags; + cfg->rc_nlinfo.portid = NETLINK_CB(skb).portid; + cfg->rc_nlinfo.nlh = nlh; + cfg->rc_nlinfo.nl_net = sock_net(skb->sk); + + for (index = 0; index <= RTA_MAX; index++) { + struct nlattr *nla = tb[index]; + if (!nla) + continue; + + switch(index) { + case RTA_OIF: + cfg->rc_ifindex = nla_get_u32(nla); + break; + case RTA_NEWDST: + if (nla_get_labels(nla, MAX_NEW_LABELS, + &cfg->rc_output_labels, + cfg->rc_output_label)) + goto errout; + break; + case RTA_DST: + { + u32 label_count; + if (nla_get_labels(nla, 1, &label_count, + &cfg->rc_label)) + goto errout; + + /* The first 16 labels are reserved, and may not be set */ + if (cfg->rc_label < 16) + goto errout; + + break; + } + case RTA_VIA: + { + struct rtvia *via = nla_data(nla); + cfg->rc_via_family = via->rtvia_family; + cfg->rc_via_alen = nla_len(nla) - 2; + if (cfg->rc_via_alen > MAX_VIA_ALEN) + goto errout; + + /* Validate the address family */ + switch(cfg->rc_via_family) { + case AF_PACKET: + break; + case AF_INET: + if (cfg->rc_via_alen != 4) + goto errout; + break; + case AF_INET6: + if (cfg->rc_via_alen != 16) + goto errout; + break; + default: + /* Unsupported address family */ + goto errout; + } + + memcpy(cfg->rc_via, via->rtvia_addr, cfg->rc_via_alen); + break; + } + default: + /* Unsupported attribute */ + goto errout; + } + } + + err = 0; +errout: + return err; +} + +static int mpls_rtm_delroute(struct sk_buff *skb, struct nlmsghdr *nlh) +{ + struct mpls_route_config cfg; + int err; + + err = rtm_to_route_config(skb, nlh, &cfg); + if (err < 0) + return err; + + return mpls_route_del(&cfg); +} + + +static int mpls_rtm_newroute(struct sk_buff *skb, struct nlmsghdr *nlh) +{ + struct mpls_route_config cfg; + int err; + + err = rtm_to_route_config(skb, nlh, &cfg); + if (err < 0) + return err; + + return mpls_route_add(&cfg); +} + +static int mpls_dump_route(struct sk_buff *skb, u32 portid, u32 seq, int event, + u32 label, struct mpls_route *rt, int flags) +{ + struct nlmsghdr *nlh; + struct rtmsg *rtm; + + nlh = nlmsg_put(skb, portid, seq, event, sizeof(*rtm), flags); + if (nlh == NULL) + return -EMSGSIZE; + + rtm = nlmsg_data(nlh); + rtm->rtm_family = AF_MPLS; + rtm->rtm_dst_len = 20; + rtm->rtm_src_len = 0; + rtm->rtm_tos = 0; + rtm->rtm_table = RT_TABLE_MAIN; + rtm->rtm_protocol = rt->rt_protocol; + rtm->rtm_scope = RT_SCOPE_UNIVERSE; + rtm->rtm_type = RTN_UNICAST; + rtm->rtm_flags = 0; + + if (rt->rt_labels && + nla_put_labels(skb, RTA_NEWDST, rt->rt_labels, rt->rt_label)) + goto nla_put_failure; + if (nla_put_via(skb, rt->rt_via_family, rt->rt_via, rt->rt_via_alen)) + goto nla_put_failure; + if (rt->rt_dev && nla_put_u32(skb, RTA_OIF, rt->rt_dev->ifindex)) + goto nla_put_failure; + if (nla_put_labels(skb, RTA_DST, 1, &label)) + goto nla_put_failure; + + nlmsg_end(skb, nlh); + return 0; + +nla_put_failure: + nlmsg_cancel(skb, nlh); + return -EMSGSIZE; +} + +static int mpls_dump_routes(struct sk_buff *skb, struct netlink_callback *cb) +{ + struct net *net = sock_net(skb->sk); + unsigned int index; + + ASSERT_RTNL(); + + index = cb->args[0]; + if (index < 16) + index = 16; + + for (; index < net->mpls.platform_labels; index++) { + struct mpls_route *rt; + rt = net->mpls.platform_label[index]; + if (!rt) + continue; + + if (mpls_dump_route(skb, NETLINK_CB(cb->skb).portid, + cb->nlh->nlmsg_seq, RTM_NEWROUTE, + index, rt, NLM_F_MULTI) < 0) + break; + } + cb->args[0] = index; + + return skb->len; +} + static int resize_platform_label_table(struct net *net, size_t limit) { size_t size = sizeof(struct mpls_route *) * limit; @@ -662,6 +887,9 @@ static int __init mpls_init(void) dev_add_pack(&mpls_packet_type); + rtnl_register(PF_MPLS, RTM_NEWROUTE, mpls_rtm_newroute, NULL, NULL); + rtnl_register(PF_MPLS, RTM_DELROUTE, mpls_rtm_delroute, NULL, NULL); + rtnl_register(PF_MPLS, RTM_GETROUTE, NULL, mpls_dump_routes, NULL); err = 0; out: return err; @@ -674,6 +902,7 @@ module_init(mpls_init); static void __exit mpls_exit(void) { + rtnl_unregister_all(PF_MPLS); dev_remove_pack(&mpls_packet_type); unregister_netdevice_notifier(&mpls_dev_notifier); unregister_pernet_subsys(&mpls_net_ops); -- cgit v1.2.3 From 8de147dc8e2adea82b8a1a2a08fcc983330f6770 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Tue, 3 Mar 2015 19:14:31 -0600 Subject: mpls: Multicast route table change notifications Unlike IPv4 this code notifies on all cases where mpls routes are added or removed and it never automatically removes routes. Avoiding both the userspace confusion that is caused by omitting route updates and the possibility of a flood of netlink traffic when an interface goes doew. For now reserved labels are handled automatically and userspace is not notified. Signed-off-by: "Eric W. Biederman" Signed-off-by: David S. Miller --- include/uapi/linux/rtnetlink.h | 2 ++ net/mpls/af_mpls.c | 60 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 62 insertions(+) (limited to 'include') diff --git a/include/uapi/linux/rtnetlink.h b/include/uapi/linux/rtnetlink.h index bad65550ae3e..06f75a407f74 100644 --- a/include/uapi/linux/rtnetlink.h +++ b/include/uapi/linux/rtnetlink.h @@ -631,6 +631,8 @@ enum rtnetlink_groups { #define RTNLGRP_IPV6_NETCONF RTNLGRP_IPV6_NETCONF RTNLGRP_MDB, #define RTNLGRP_MDB RTNLGRP_MDB + RTNLGRP_MPLS_ROUTE, +#define RTNLGRP_MPLS_ROUTE RTNLGRP_MPLS_ROUTE __RTNLGRP_MAX }; #define RTNLGRP_MAX (__RTNLGRP_MAX - 1) diff --git a/net/mpls/af_mpls.c b/net/mpls/af_mpls.c index b4d7cec398d2..75a994a50381 100644 --- a/net/mpls/af_mpls.c +++ b/net/mpls/af_mpls.c @@ -36,6 +36,10 @@ struct mpls_route { /* next hop label forwarding entry */ static int zero = 0; static int label_limit = (1 << 20) - 1; +static void rtmsg_lfib(int event, u32 label, struct mpls_route *rt, + struct nlmsghdr *nlh, struct net *net, u32 portid, + unsigned int nlm_flags); + static struct mpls_route *mpls_route_input_rcu(struct net *net, unsigned index) { struct mpls_route *rt = NULL; @@ -246,6 +250,20 @@ static void mpls_rt_free(struct mpls_route *rt) kfree_rcu(rt, rt_rcu); } +static void mpls_notify_route(struct net *net, unsigned index, + struct mpls_route *old, struct mpls_route *new, + const struct nl_info *info) +{ + struct nlmsghdr *nlh = info ? info->nlh : NULL; + unsigned portid = info ? info->portid : 0; + int event = new ? RTM_NEWROUTE : RTM_DELROUTE; + struct mpls_route *rt = new ? new : old; + unsigned nlm_flags = (old && new) ? NLM_F_REPLACE : 0; + /* Ignore reserved labels for now */ + if (rt && (index >= 16)) + rtmsg_lfib(event, index, rt, nlh, net, portid, nlm_flags); +} + static void mpls_route_update(struct net *net, unsigned index, struct net_device *dev, struct mpls_route *new, const struct nl_info *info) @@ -260,6 +278,8 @@ static void mpls_route_update(struct net *net, unsigned index, old = rt; } + mpls_notify_route(net, index, old, new, info); + /* If we removed a route free it now */ mpls_rt_free(old); } @@ -692,6 +712,46 @@ static int mpls_dump_routes(struct sk_buff *skb, struct netlink_callback *cb) return skb->len; } +static inline size_t lfib_nlmsg_size(struct mpls_route *rt) +{ + size_t payload = + NLMSG_ALIGN(sizeof(struct rtmsg)) + + nla_total_size(2 + rt->rt_via_alen) /* RTA_VIA */ + + nla_total_size(4); /* RTA_DST */ + if (rt->rt_labels) /* RTA_NEWDST */ + payload += nla_total_size(rt->rt_labels * 4); + if (rt->rt_dev) /* RTA_OIF */ + payload += nla_total_size(4); + return payload; +} + +static void rtmsg_lfib(int event, u32 label, struct mpls_route *rt, + struct nlmsghdr *nlh, struct net *net, u32 portid, + unsigned int nlm_flags) +{ + struct sk_buff *skb; + u32 seq = nlh ? nlh->nlmsg_seq : 0; + int err = -ENOBUFS; + + skb = nlmsg_new(lfib_nlmsg_size(rt), GFP_KERNEL); + if (skb == NULL) + goto errout; + + err = mpls_dump_route(skb, portid, seq, event, label, rt, nlm_flags); + if (err < 0) { + /* -EMSGSIZE implies BUG in lfib_nlmsg_size */ + WARN_ON(err == -EMSGSIZE); + kfree_skb(skb); + goto errout; + } + rtnl_notify(skb, net, portid, RTNLGRP_MPLS_ROUTE, nlh, GFP_KERNEL); + + return; +errout: + if (err < 0) + rtnl_set_sk_err(net, RTNLGRP_MPLS_ROUTE, err); +} + static int resize_platform_label_table(struct net *net, size_t limit) { size_t size = sizeof(struct mpls_route *) * limit; -- cgit v1.2.3 From 6df8dd5c185b212d3d7364402df58bff0e67ace4 Mon Sep 17 00:00:00 2001 From: Flora Fu Date: Sun, 22 Feb 2015 13:15:29 +0100 Subject: mfd: Add support for the MediaTek MT6397 PMIC This adds support for the MediaTek MT6397 PMIC. This is a multifunction device with the following sub modules: - Regulator - RTC - Audio codec - GPIO - Clock It is interfaced to the host controller using SPI interface by a proprietary hardware called PMIC wrapper or pwrap. MT6397 MFD is a child device of the pwrap. Signed-off-by: Flora Fu, MediaTek Signed-off-by: Sascha Hauer Signed-off-by: Lee Jones --- drivers/mfd/Kconfig | 10 + drivers/mfd/Makefile | 1 + drivers/mfd/mt6397-core.c | 227 ++++++++++++++++++++++ include/linux/mfd/mt6397/core.h | 64 +++++++ include/linux/mfd/mt6397/registers.h | 362 +++++++++++++++++++++++++++++++++++ 5 files changed, 664 insertions(+) create mode 100644 drivers/mfd/mt6397-core.c create mode 100644 include/linux/mfd/mt6397/core.h create mode 100644 include/linux/mfd/mt6397/registers.h (limited to 'include') diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index b5fb03c109bc..c602f7440e00 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -503,6 +503,16 @@ config MFD_MAX8998 additional drivers must be enabled in order to use the functionality of the device. +config MFD_MT6397 + tristate "MediaTek MT6397 PMIC Support" + select MFD_CORE + select IRQ_DOMAIN + help + Say yes here to add support for MediaTek MT6397 PMIC. This is + a Power Management IC. This driver provides common support for + accessing the device; additional drivers must be enabled in order + to use the functionality of the device. + config MFD_MENF21BMC tristate "MEN 14F021P00 Board Management Controller Support" depends on I2C diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index c8bb34929903..26303f712afb 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -181,3 +181,4 @@ obj-$(CONFIG_MFD_RT5033) += rt5033.o intel-soc-pmic-objs := intel_soc_pmic_core.o intel_soc_pmic_crc.o obj-$(CONFIG_INTEL_SOC_PMIC) += intel-soc-pmic.o +obj-$(CONFIG_MFD_MT6397) += mt6397-core.o diff --git a/drivers/mfd/mt6397-core.c b/drivers/mfd/mt6397-core.c new file mode 100644 index 000000000000..09bc7804952a --- /dev/null +++ b/drivers/mfd/mt6397-core.c @@ -0,0 +1,227 @@ +/* + * Copyright (c) 2014 MediaTek Inc. + * Author: Flora Fu, MediaTek + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +static const struct mfd_cell mt6397_devs[] = { + { + .name = "mt6397-rtc", + .of_compatible = "mediatek,mt6397-rtc", + }, { + .name = "mt6397-regulator", + .of_compatible = "mediatek,mt6397-regulator", + }, { + .name = "mt6397-codec", + .of_compatible = "mediatek,mt6397-codec", + }, { + .name = "mt6397-clk", + .of_compatible = "mediatek,mt6397-clk", + }, +}; + +static void mt6397_irq_lock(struct irq_data *data) +{ + struct mt6397_chip *mt6397 = irq_get_chip_data(data->irq); + + mutex_lock(&mt6397->irqlock); +} + +static void mt6397_irq_sync_unlock(struct irq_data *data) +{ + struct mt6397_chip *mt6397 = irq_get_chip_data(data->irq); + + regmap_write(mt6397->regmap, MT6397_INT_CON0, mt6397->irq_masks_cur[0]); + regmap_write(mt6397->regmap, MT6397_INT_CON1, mt6397->irq_masks_cur[1]); + + mutex_unlock(&mt6397->irqlock); +} + +static void mt6397_irq_disable(struct irq_data *data) +{ + struct mt6397_chip *mt6397 = irq_get_chip_data(data->irq); + int shift = data->hwirq & 0xf; + int reg = data->hwirq >> 4; + + mt6397->irq_masks_cur[reg] &= ~BIT(shift); +} + +static void mt6397_irq_enable(struct irq_data *data) +{ + struct mt6397_chip *mt6397 = irq_get_chip_data(data->irq); + int shift = data->hwirq & 0xf; + int reg = data->hwirq >> 4; + + mt6397->irq_masks_cur[reg] |= BIT(shift); +} + +static struct irq_chip mt6397_irq_chip = { + .name = "mt6397-irq", + .irq_bus_lock = mt6397_irq_lock, + .irq_bus_sync_unlock = mt6397_irq_sync_unlock, + .irq_enable = mt6397_irq_enable, + .irq_disable = mt6397_irq_disable, +}; + +static void mt6397_irq_handle_reg(struct mt6397_chip *mt6397, int reg, + int irqbase) +{ + unsigned int status; + int i, irq, ret; + + ret = regmap_read(mt6397->regmap, reg, &status); + if (ret) { + dev_err(mt6397->dev, "Failed to read irq status: %d\n", ret); + return; + } + + for (i = 0; i < 16; i++) { + if (status & BIT(i)) { + irq = irq_find_mapping(mt6397->irq_domain, irqbase + i); + if (irq) + handle_nested_irq(irq); + } + } + + regmap_write(mt6397->regmap, reg, status); +} + +static irqreturn_t mt6397_irq_thread(int irq, void *data) +{ + struct mt6397_chip *mt6397 = data; + + mt6397_irq_handle_reg(mt6397, MT6397_INT_STATUS0, 0); + mt6397_irq_handle_reg(mt6397, MT6397_INT_STATUS1, 16); + + return IRQ_HANDLED; +} + +static int mt6397_irq_domain_map(struct irq_domain *d, unsigned int irq, + irq_hw_number_t hw) +{ + struct mt6397_chip *mt6397 = d->host_data; + + irq_set_chip_data(irq, mt6397); + irq_set_chip_and_handler(irq, &mt6397_irq_chip, handle_level_irq); + irq_set_nested_thread(irq, 1); +#ifdef CONFIG_ARM + set_irq_flags(irq, IRQF_VALID); +#else + irq_set_noprobe(irq); +#endif + + return 0; +} + +static struct irq_domain_ops mt6397_irq_domain_ops = { + .map = mt6397_irq_domain_map, +}; + +static int mt6397_irq_init(struct mt6397_chip *mt6397) +{ + int ret; + + mutex_init(&mt6397->irqlock); + + /* Mask all interrupt sources */ + regmap_write(mt6397->regmap, MT6397_INT_CON0, 0x0); + regmap_write(mt6397->regmap, MT6397_INT_CON1, 0x0); + + mt6397->irq_domain = irq_domain_add_linear(mt6397->dev->of_node, + MT6397_IRQ_NR, &mt6397_irq_domain_ops, mt6397); + if (!mt6397->irq_domain) { + dev_err(mt6397->dev, "could not create irq domain\n"); + return -ENOMEM; + } + + ret = devm_request_threaded_irq(mt6397->dev, mt6397->irq, NULL, + mt6397_irq_thread, IRQF_ONESHOT, "mt6397-pmic", mt6397); + if (ret) { + dev_err(mt6397->dev, "failed to register irq=%d; err: %d\n", + mt6397->irq, ret); + return ret; + } + + return 0; +} + +static int mt6397_probe(struct platform_device *pdev) +{ + int ret; + struct mt6397_chip *mt6397; + + mt6397 = devm_kzalloc(&pdev->dev, sizeof(*mt6397), GFP_KERNEL); + if (!mt6397) + return -ENOMEM; + + mt6397->dev = &pdev->dev; + /* + * mt6397 MFD is child device of soc pmic wrapper. + * Regmap is set from its parent. + */ + mt6397->regmap = dev_get_regmap(pdev->dev.parent, NULL); + if (!mt6397->regmap) + return -ENODEV; + + platform_set_drvdata(pdev, mt6397); + + mt6397->irq = platform_get_irq(pdev, 0); + if (mt6397->irq > 0) { + ret = mt6397_irq_init(mt6397); + if (ret) + return ret; + } + + ret = mfd_add_devices(&pdev->dev, -1, mt6397_devs, + ARRAY_SIZE(mt6397_devs), NULL, 0, NULL); + if (ret) + dev_err(&pdev->dev, "failed to add child devices: %d\n", ret); + + return ret; +} + +static int mt6397_remove(struct platform_device *pdev) +{ + mfd_remove_devices(&pdev->dev); + + return 0; +} + +static const struct of_device_id mt6397_of_match[] = { + { .compatible = "mediatek,mt6397" }, + { } +}; +MODULE_DEVICE_TABLE(of, mt6397_of_match); + +static struct platform_driver mt6397_driver = { + .probe = mt6397_probe, + .remove = mt6397_remove, + .driver = { + .name = "mt6397", + .of_match_table = of_match_ptr(mt6397_of_match), + }, +}; + +module_platform_driver(mt6397_driver); + +MODULE_AUTHOR("Flora Fu, MediaTek"); +MODULE_DESCRIPTION("Driver for MediaTek MT6397 PMIC"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:mt6397"); diff --git a/include/linux/mfd/mt6397/core.h b/include/linux/mfd/mt6397/core.h new file mode 100644 index 000000000000..cf5265b0d1c1 --- /dev/null +++ b/include/linux/mfd/mt6397/core.h @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2014 MediaTek Inc. + * Author: Flora Fu, MediaTek + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef __MFD_MT6397_CORE_H__ +#define __MFD_MT6397_CORE_H__ + +enum mt6397_irq_numbers { + MT6397_IRQ_SPKL_AB = 0, + MT6397_IRQ_SPKR_AB, + MT6397_IRQ_SPKL, + MT6397_IRQ_SPKR, + MT6397_IRQ_BAT_L, + MT6397_IRQ_BAT_H, + MT6397_IRQ_FG_BAT_L, + MT6397_IRQ_FG_BAT_H, + MT6397_IRQ_WATCHDOG, + MT6397_IRQ_PWRKEY, + MT6397_IRQ_THR_L, + MT6397_IRQ_THR_H, + MT6397_IRQ_VBATON_UNDET, + MT6397_IRQ_BVALID_DET, + MT6397_IRQ_CHRDET, + MT6397_IRQ_OV, + MT6397_IRQ_LDO, + MT6397_IRQ_HOMEKEY, + MT6397_IRQ_ACCDET, + MT6397_IRQ_AUDIO, + MT6397_IRQ_RTC, + MT6397_IRQ_PWRKEY_RSTB, + MT6397_IRQ_HDMI_SIFM, + MT6397_IRQ_HDMI_CEC, + MT6397_IRQ_VCA15, + MT6397_IRQ_VSRMCA15, + MT6397_IRQ_VCORE, + MT6397_IRQ_VGPU, + MT6397_IRQ_VIO18, + MT6397_IRQ_VPCA7, + MT6397_IRQ_VSRMCA7, + MT6397_IRQ_VDRM, + MT6397_IRQ_NR, +}; + +struct mt6397_chip { + struct device *dev; + struct regmap *regmap; + int irq; + struct irq_domain *irq_domain; + struct mutex irqlock; + u16 irq_masks_cur[2]; + u16 irq_masks_cache[2]; +}; + +#endif /* __MFD_MT6397_CORE_H__ */ diff --git a/include/linux/mfd/mt6397/registers.h b/include/linux/mfd/mt6397/registers.h new file mode 100644 index 000000000000..f23a0a60a877 --- /dev/null +++ b/include/linux/mfd/mt6397/registers.h @@ -0,0 +1,362 @@ +/* + * Copyright (c) 2014 MediaTek Inc. + * Author: Flora Fu, MediaTek + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef __MFD_MT6397_REGISTERS_H__ +#define __MFD_MT6397_REGISTERS_H__ + +/* PMIC Registers */ +#define MT6397_CID 0x0100 +#define MT6397_TOP_CKPDN 0x0102 +#define MT6397_TOP_CKPDN_SET 0x0104 +#define MT6397_TOP_CKPDN_CLR 0x0106 +#define MT6397_TOP_CKPDN2 0x0108 +#define MT6397_TOP_CKPDN2_SET 0x010A +#define MT6397_TOP_CKPDN2_CLR 0x010C +#define MT6397_TOP_GPIO_CKPDN 0x010E +#define MT6397_TOP_RST_CON 0x0114 +#define MT6397_WRP_CKPDN 0x011A +#define MT6397_WRP_RST_CON 0x0120 +#define MT6397_TOP_RST_MISC 0x0126 +#define MT6397_TOP_CKCON1 0x0128 +#define MT6397_TOP_CKCON2 0x012A +#define MT6397_TOP_CKTST1 0x012C +#define MT6397_TOP_CKTST2 0x012E +#define MT6397_OC_DEG_EN 0x0130 +#define MT6397_OC_CTL0 0x0132 +#define MT6397_OC_CTL1 0x0134 +#define MT6397_OC_CTL2 0x0136 +#define MT6397_INT_RSV 0x0138 +#define MT6397_TEST_CON0 0x013A +#define MT6397_TEST_CON1 0x013C +#define MT6397_STATUS0 0x013E +#define MT6397_STATUS1 0x0140 +#define MT6397_PGSTATUS 0x0142 +#define MT6397_CHRSTATUS 0x0144 +#define MT6397_OCSTATUS0 0x0146 +#define MT6397_OCSTATUS1 0x0148 +#define MT6397_OCSTATUS2 0x014A +#define MT6397_HDMI_PAD_IE 0x014C +#define MT6397_TEST_OUT_L 0x014E +#define MT6397_TEST_OUT_H 0x0150 +#define MT6397_TDSEL_CON 0x0152 +#define MT6397_RDSEL_CON 0x0154 +#define MT6397_GPIO_SMT_CON0 0x0156 +#define MT6397_GPIO_SMT_CON1 0x0158 +#define MT6397_GPIO_SMT_CON2 0x015A +#define MT6397_GPIO_SMT_CON3 0x015C +#define MT6397_DRV_CON0 0x015E +#define MT6397_DRV_CON1 0x0160 +#define MT6397_DRV_CON2 0x0162 +#define MT6397_DRV_CON3 0x0164 +#define MT6397_DRV_CON4 0x0166 +#define MT6397_DRV_CON5 0x0168 +#define MT6397_DRV_CON6 0x016A +#define MT6397_DRV_CON7 0x016C +#define MT6397_DRV_CON8 0x016E +#define MT6397_DRV_CON9 0x0170 +#define MT6397_DRV_CON10 0x0172 +#define MT6397_DRV_CON11 0x0174 +#define MT6397_DRV_CON12 0x0176 +#define MT6397_INT_CON0 0x0178 +#define MT6397_INT_CON1 0x017E +#define MT6397_INT_STATUS0 0x0184 +#define MT6397_INT_STATUS1 0x0186 +#define MT6397_FQMTR_CON0 0x0188 +#define MT6397_FQMTR_CON1 0x018A +#define MT6397_FQMTR_CON2 0x018C +#define MT6397_EFUSE_DOUT_0_15 0x01C4 +#define MT6397_EFUSE_DOUT_16_31 0x01C6 +#define MT6397_EFUSE_DOUT_32_47 0x01C8 +#define MT6397_EFUSE_DOUT_48_63 0x01CA +#define MT6397_SPI_CON 0x01CC +#define MT6397_TOP_CKPDN3 0x01CE +#define MT6397_TOP_CKCON3 0x01D4 +#define MT6397_EFUSE_DOUT_64_79 0x01D6 +#define MT6397_EFUSE_DOUT_80_95 0x01D8 +#define MT6397_EFUSE_DOUT_96_111 0x01DA +#define MT6397_EFUSE_DOUT_112_127 0x01DC +#define MT6397_EFUSE_DOUT_128_143 0x01DE +#define MT6397_EFUSE_DOUT_144_159 0x01E0 +#define MT6397_EFUSE_DOUT_160_175 0x01E2 +#define MT6397_EFUSE_DOUT_176_191 0x01E4 +#define MT6397_EFUSE_DOUT_192_207 0x01E6 +#define MT6397_EFUSE_DOUT_208_223 0x01E8 +#define MT6397_EFUSE_DOUT_224_239 0x01EA +#define MT6397_EFUSE_DOUT_240_255 0x01EC +#define MT6397_EFUSE_DOUT_256_271 0x01EE +#define MT6397_EFUSE_DOUT_272_287 0x01F0 +#define MT6397_EFUSE_DOUT_288_300 0x01F2 +#define MT6397_EFUSE_DOUT_304_319 0x01F4 +#define MT6397_BUCK_CON0 0x0200 +#define MT6397_BUCK_CON1 0x0202 +#define MT6397_BUCK_CON2 0x0204 +#define MT6397_BUCK_CON3 0x0206 +#define MT6397_BUCK_CON4 0x0208 +#define MT6397_BUCK_CON5 0x020A +#define MT6397_BUCK_CON6 0x020C +#define MT6397_BUCK_CON7 0x020E +#define MT6397_BUCK_CON8 0x0210 +#define MT6397_BUCK_CON9 0x0212 +#define MT6397_VCA15_CON0 0x0214 +#define MT6397_VCA15_CON1 0x0216 +#define MT6397_VCA15_CON2 0x0218 +#define MT6397_VCA15_CON3 0x021A +#define MT6397_VCA15_CON4 0x021C +#define MT6397_VCA15_CON5 0x021E +#define MT6397_VCA15_CON6 0x0220 +#define MT6397_VCA15_CON7 0x0222 +#define MT6397_VCA15_CON8 0x0224 +#define MT6397_VCA15_CON9 0x0226 +#define MT6397_VCA15_CON10 0x0228 +#define MT6397_VCA15_CON11 0x022A +#define MT6397_VCA15_CON12 0x022C +#define MT6397_VCA15_CON13 0x022E +#define MT6397_VCA15_CON14 0x0230 +#define MT6397_VCA15_CON15 0x0232 +#define MT6397_VCA15_CON16 0x0234 +#define MT6397_VCA15_CON17 0x0236 +#define MT6397_VCA15_CON18 0x0238 +#define MT6397_VSRMCA15_CON0 0x023A +#define MT6397_VSRMCA15_CON1 0x023C +#define MT6397_VSRMCA15_CON2 0x023E +#define MT6397_VSRMCA15_CON3 0x0240 +#define MT6397_VSRMCA15_CON4 0x0242 +#define MT6397_VSRMCA15_CON5 0x0244 +#define MT6397_VSRMCA15_CON6 0x0246 +#define MT6397_VSRMCA15_CON7 0x0248 +#define MT6397_VSRMCA15_CON8 0x024A +#define MT6397_VSRMCA15_CON9 0x024C +#define MT6397_VSRMCA15_CON10 0x024E +#define MT6397_VSRMCA15_CON11 0x0250 +#define MT6397_VSRMCA15_CON12 0x0252 +#define MT6397_VSRMCA15_CON13 0x0254 +#define MT6397_VSRMCA15_CON14 0x0256 +#define MT6397_VSRMCA15_CON15 0x0258 +#define MT6397_VSRMCA15_CON16 0x025A +#define MT6397_VSRMCA15_CON17 0x025C +#define MT6397_VSRMCA15_CON18 0x025E +#define MT6397_VSRMCA15_CON19 0x0260 +#define MT6397_VSRMCA15_CON20 0x0262 +#define MT6397_VSRMCA15_CON21 0x0264 +#define MT6397_VCORE_CON0 0x0266 +#define MT6397_VCORE_CON1 0x0268 +#define MT6397_VCORE_CON2 0x026A +#define MT6397_VCORE_CON3 0x026C +#define MT6397_VCORE_CON4 0x026E +#define MT6397_VCORE_CON5 0x0270 +#define MT6397_VCORE_CON6 0x0272 +#define MT6397_VCORE_CON7 0x0274 +#define MT6397_VCORE_CON8 0x0276 +#define MT6397_VCORE_CON9 0x0278 +#define MT6397_VCORE_CON10 0x027A +#define MT6397_VCORE_CON11 0x027C +#define MT6397_VCORE_CON12 0x027E +#define MT6397_VCORE_CON13 0x0280 +#define MT6397_VCORE_CON14 0x0282 +#define MT6397_VCORE_CON15 0x0284 +#define MT6397_VCORE_CON16 0x0286 +#define MT6397_VCORE_CON17 0x0288 +#define MT6397_VCORE_CON18 0x028A +#define MT6397_VGPU_CON0 0x028C +#define MT6397_VGPU_CON1 0x028E +#define MT6397_VGPU_CON2 0x0290 +#define MT6397_VGPU_CON3 0x0292 +#define MT6397_VGPU_CON4 0x0294 +#define MT6397_VGPU_CON5 0x0296 +#define MT6397_VGPU_CON6 0x0298 +#define MT6397_VGPU_CON7 0x029A +#define MT6397_VGPU_CON8 0x029C +#define MT6397_VGPU_CON9 0x029E +#define MT6397_VGPU_CON10 0x02A0 +#define MT6397_VGPU_CON11 0x02A2 +#define MT6397_VGPU_CON12 0x02A4 +#define MT6397_VGPU_CON13 0x02A6 +#define MT6397_VGPU_CON14 0x02A8 +#define MT6397_VGPU_CON15 0x02AA +#define MT6397_VGPU_CON16 0x02AC +#define MT6397_VGPU_CON17 0x02AE +#define MT6397_VGPU_CON18 0x02B0 +#define MT6397_VIO18_CON0 0x0300 +#define MT6397_VIO18_CON1 0x0302 +#define MT6397_VIO18_CON2 0x0304 +#define MT6397_VIO18_CON3 0x0306 +#define MT6397_VIO18_CON4 0x0308 +#define MT6397_VIO18_CON5 0x030A +#define MT6397_VIO18_CON6 0x030C +#define MT6397_VIO18_CON7 0x030E +#define MT6397_VIO18_CON8 0x0310 +#define MT6397_VIO18_CON9 0x0312 +#define MT6397_VIO18_CON10 0x0314 +#define MT6397_VIO18_CON11 0x0316 +#define MT6397_VIO18_CON12 0x0318 +#define MT6397_VIO18_CON13 0x031A +#define MT6397_VIO18_CON14 0x031C +#define MT6397_VIO18_CON15 0x031E +#define MT6397_VIO18_CON16 0x0320 +#define MT6397_VIO18_CON17 0x0322 +#define MT6397_VIO18_CON18 0x0324 +#define MT6397_VPCA7_CON0 0x0326 +#define MT6397_VPCA7_CON1 0x0328 +#define MT6397_VPCA7_CON2 0x032A +#define MT6397_VPCA7_CON3 0x032C +#define MT6397_VPCA7_CON4 0x032E +#define MT6397_VPCA7_CON5 0x0330 +#define MT6397_VPCA7_CON6 0x0332 +#define MT6397_VPCA7_CON7 0x0334 +#define MT6397_VPCA7_CON8 0x0336 +#define MT6397_VPCA7_CON9 0x0338 +#define MT6397_VPCA7_CON10 0x033A +#define MT6397_VPCA7_CON11 0x033C +#define MT6397_VPCA7_CON12 0x033E +#define MT6397_VPCA7_CON13 0x0340 +#define MT6397_VPCA7_CON14 0x0342 +#define MT6397_VPCA7_CON15 0x0344 +#define MT6397_VPCA7_CON16 0x0346 +#define MT6397_VPCA7_CON17 0x0348 +#define MT6397_VPCA7_CON18 0x034A +#define MT6397_VSRMCA7_CON0 0x034C +#define MT6397_VSRMCA7_CON1 0x034E +#define MT6397_VSRMCA7_CON2 0x0350 +#define MT6397_VSRMCA7_CON3 0x0352 +#define MT6397_VSRMCA7_CON4 0x0354 +#define MT6397_VSRMCA7_CON5 0x0356 +#define MT6397_VSRMCA7_CON6 0x0358 +#define MT6397_VSRMCA7_CON7 0x035A +#define MT6397_VSRMCA7_CON8 0x035C +#define MT6397_VSRMCA7_CON9 0x035E +#define MT6397_VSRMCA7_CON10 0x0360 +#define MT6397_VSRMCA7_CON11 0x0362 +#define MT6397_VSRMCA7_CON12 0x0364 +#define MT6397_VSRMCA7_CON13 0x0366 +#define MT6397_VSRMCA7_CON14 0x0368 +#define MT6397_VSRMCA7_CON15 0x036A +#define MT6397_VSRMCA7_CON16 0x036C +#define MT6397_VSRMCA7_CON17 0x036E +#define MT6397_VSRMCA7_CON18 0x0370 +#define MT6397_VSRMCA7_CON19 0x0372 +#define MT6397_VSRMCA7_CON20 0x0374 +#define MT6397_VSRMCA7_CON21 0x0376 +#define MT6397_VDRM_CON0 0x0378 +#define MT6397_VDRM_CON1 0x037A +#define MT6397_VDRM_CON2 0x037C +#define MT6397_VDRM_CON3 0x037E +#define MT6397_VDRM_CON4 0x0380 +#define MT6397_VDRM_CON5 0x0382 +#define MT6397_VDRM_CON6 0x0384 +#define MT6397_VDRM_CON7 0x0386 +#define MT6397_VDRM_CON8 0x0388 +#define MT6397_VDRM_CON9 0x038A +#define MT6397_VDRM_CON10 0x038C +#define MT6397_VDRM_CON11 0x038E +#define MT6397_VDRM_CON12 0x0390 +#define MT6397_VDRM_CON13 0x0392 +#define MT6397_VDRM_CON14 0x0394 +#define MT6397_VDRM_CON15 0x0396 +#define MT6397_VDRM_CON16 0x0398 +#define MT6397_VDRM_CON17 0x039A +#define MT6397_VDRM_CON18 0x039C +#define MT6397_BUCK_K_CON0 0x039E +#define MT6397_BUCK_K_CON1 0x03A0 +#define MT6397_ANALDO_CON0 0x0400 +#define MT6397_ANALDO_CON1 0x0402 +#define MT6397_ANALDO_CON2 0x0404 +#define MT6397_ANALDO_CON3 0x0406 +#define MT6397_ANALDO_CON4 0x0408 +#define MT6397_ANALDO_CON5 0x040A +#define MT6397_ANALDO_CON6 0x040C +#define MT6397_ANALDO_CON7 0x040E +#define MT6397_DIGLDO_CON0 0x0410 +#define MT6397_DIGLDO_CON1 0x0412 +#define MT6397_DIGLDO_CON2 0x0414 +#define MT6397_DIGLDO_CON3 0x0416 +#define MT6397_DIGLDO_CON4 0x0418 +#define MT6397_DIGLDO_CON5 0x041A +#define MT6397_DIGLDO_CON6 0x041C +#define MT6397_DIGLDO_CON7 0x041E +#define MT6397_DIGLDO_CON8 0x0420 +#define MT6397_DIGLDO_CON9 0x0422 +#define MT6397_DIGLDO_CON10 0x0424 +#define MT6397_DIGLDO_CON11 0x0426 +#define MT6397_DIGLDO_CON12 0x0428 +#define MT6397_DIGLDO_CON13 0x042A +#define MT6397_DIGLDO_CON14 0x042C +#define MT6397_DIGLDO_CON15 0x042E +#define MT6397_DIGLDO_CON16 0x0430 +#define MT6397_DIGLDO_CON17 0x0432 +#define MT6397_DIGLDO_CON18 0x0434 +#define MT6397_DIGLDO_CON19 0x0436 +#define MT6397_DIGLDO_CON20 0x0438 +#define MT6397_DIGLDO_CON21 0x043A +#define MT6397_DIGLDO_CON22 0x043C +#define MT6397_DIGLDO_CON23 0x043E +#define MT6397_DIGLDO_CON24 0x0440 +#define MT6397_DIGLDO_CON25 0x0442 +#define MT6397_DIGLDO_CON26 0x0444 +#define MT6397_DIGLDO_CON27 0x0446 +#define MT6397_DIGLDO_CON28 0x0448 +#define MT6397_DIGLDO_CON29 0x044A +#define MT6397_DIGLDO_CON30 0x044C +#define MT6397_DIGLDO_CON31 0x044E +#define MT6397_DIGLDO_CON32 0x0450 +#define MT6397_DIGLDO_CON33 0x045A +#define MT6397_SPK_CON0 0x0600 +#define MT6397_SPK_CON1 0x0602 +#define MT6397_SPK_CON2 0x0604 +#define MT6397_SPK_CON3 0x0606 +#define MT6397_SPK_CON4 0x0608 +#define MT6397_SPK_CON5 0x060A +#define MT6397_SPK_CON6 0x060C +#define MT6397_SPK_CON7 0x060E +#define MT6397_SPK_CON8 0x0610 +#define MT6397_SPK_CON9 0x0612 +#define MT6397_SPK_CON10 0x0614 +#define MT6397_SPK_CON11 0x0616 +#define MT6397_AUDDAC_CON0 0x0700 +#define MT6397_AUDBUF_CFG0 0x0702 +#define MT6397_AUDBUF_CFG1 0x0704 +#define MT6397_AUDBUF_CFG2 0x0706 +#define MT6397_AUDBUF_CFG3 0x0708 +#define MT6397_AUDBUF_CFG4 0x070A +#define MT6397_IBIASDIST_CFG0 0x070C +#define MT6397_AUDACCDEPOP_CFG0 0x070E +#define MT6397_AUD_IV_CFG0 0x0710 +#define MT6397_AUDCLKGEN_CFG0 0x0712 +#define MT6397_AUDLDO_CFG0 0x0714 +#define MT6397_AUDLDO_CFG1 0x0716 +#define MT6397_AUDNVREGGLB_CFG0 0x0718 +#define MT6397_AUD_NCP0 0x071A +#define MT6397_AUDPREAMP_CON0 0x071C +#define MT6397_AUDADC_CON0 0x071E +#define MT6397_AUDADC_CON1 0x0720 +#define MT6397_AUDADC_CON2 0x0722 +#define MT6397_AUDADC_CON3 0x0724 +#define MT6397_AUDADC_CON4 0x0726 +#define MT6397_AUDADC_CON5 0x0728 +#define MT6397_AUDADC_CON6 0x072A +#define MT6397_AUDDIGMI_CON0 0x072C +#define MT6397_AUDLSBUF_CON0 0x072E +#define MT6397_AUDLSBUF_CON1 0x0730 +#define MT6397_AUDENCSPARE_CON0 0x0732 +#define MT6397_AUDENCCLKSQ_CON0 0x0734 +#define MT6397_AUDPREAMPGAIN_CON0 0x0736 +#define MT6397_ZCD_CON0 0x0738 +#define MT6397_ZCD_CON1 0x073A +#define MT6397_ZCD_CON2 0x073C +#define MT6397_ZCD_CON3 0x073E +#define MT6397_ZCD_CON4 0x0740 +#define MT6397_ZCD_CON5 0x0742 +#define MT6397_NCP_CLKDIV_CON0 0x0744 +#define MT6397_NCP_CLKDIV_CON1 0x0746 + +#endif /* __MFD_MT6397_REGISTERS_H__ */ -- cgit v1.2.3 From c7f585fe46d834d5837db7fbe205c46b94f81dc2 Mon Sep 17 00:00:00 2001 From: Jaewon Kim Date: Mon, 2 Mar 2015 19:10:34 +0900 Subject: mfd: max77843: Add max77843 MFD driver core driver This patch adds MAX77843 core/irq driver to support PMIC, MUIC(Micro USB Interface Controller), Charger, Fuel Gauge, LED and Haptic device. Signed-off-by: Jaewon Kim Signed-off-by: Beomho Seo Signed-off-by: Lee Jones --- drivers/mfd/Kconfig | 14 ++ drivers/mfd/Makefile | 1 + drivers/mfd/max77843.c | 243 +++++++++++++++++++ include/linux/mfd/max77843-private.h | 454 +++++++++++++++++++++++++++++++++++ 4 files changed, 712 insertions(+) create mode 100644 drivers/mfd/max77843.c create mode 100644 include/linux/mfd/max77843-private.h (limited to 'include') diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index c602f7440e00..f8ef77d9ac1f 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -456,6 +456,20 @@ config MFD_MAX77693 additional drivers must be enabled in order to use the functionality of the device. +config MFD_MAX77843 + bool "Maxim Semiconductor MAX77843 PMIC Support" + depends on I2C=y + select MFD_CORE + select REGMAP_I2C + select REGMAP_IRQ + help + Say yes here to add support for Maxim Semiconductor MAX77843. + This is companion Power Management IC with LEDs, Haptic, Charger, + Fuel Gauge, MUIC(Micro USB Interface Controller) controls on chip. + This driver provides common support for accessing the device; + additional drivers must be enabled in order to use the functionality + of the device. + config MFD_MAX8907 tristate "Maxim Semiconductor MAX8907 PMIC Support" select MFD_CORE diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index 26303f712afb..234998d77d43 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -117,6 +117,7 @@ obj-$(CONFIG_MFD_DA9150) += da9150-core.o obj-$(CONFIG_MFD_MAX14577) += max14577.o obj-$(CONFIG_MFD_MAX77686) += max77686.o obj-$(CONFIG_MFD_MAX77693) += max77693.o +obj-$(CONFIG_MFD_MAX77843) += max77843.o obj-$(CONFIG_MFD_MAX8907) += max8907.o max8925-objs := max8925-core.o max8925-i2c.o obj-$(CONFIG_MFD_MAX8925) += max8925.o diff --git a/drivers/mfd/max77843.c b/drivers/mfd/max77843.c new file mode 100644 index 000000000000..a354ac677ec7 --- /dev/null +++ b/drivers/mfd/max77843.c @@ -0,0 +1,243 @@ +/* + * MFD core driver for the Maxim MAX77843 + * + * Copyright (C) 2015 Samsung Electronics + * Author: Jaewon Kim + * Author: Beomho Seo + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static const struct mfd_cell max77843_devs[] = { + { + .name = "max77843-muic", + .of_compatible = "maxim,max77843-muic", + }, { + .name = "max77843-regulator", + .of_compatible = "maxim,max77843-regulator", + }, { + .name = "max77843-charger", + .of_compatible = "maxim,max77843-charger" + }, { + .name = "max77843-fuelgauge", + .of_compatible = "maxim,max77843-fuelgauge", + }, { + .name = "max77843-haptic", + .of_compatible = "maxim,max77843-haptic", + }, +}; + +static const struct regmap_config max77843_charger_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .max_register = MAX77843_CHG_REG_END, +}; + +static const struct regmap_config max77843_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .max_register = MAX77843_SYS_REG_END, +}; + +static const struct regmap_irq max77843_irqs[] = { + /* TOPSYS interrupts */ + { .reg_offset = 0, .mask = MAX77843_SYS_IRQ_SYSUVLO_INT, }, + { .reg_offset = 0, .mask = MAX77843_SYS_IRQ_SYSOVLO_INT, }, + { .reg_offset = 0, .mask = MAX77843_SYS_IRQ_TSHDN_INT, }, + { .reg_offset = 0, .mask = MAX77843_SYS_IRQ_TM_INT, }, +}; + +static const struct regmap_irq_chip max77843_irq_chip = { + .name = "max77843", + .status_base = MAX77843_SYS_REG_SYSINTSRC, + .mask_base = MAX77843_SYS_REG_SYSINTMASK, + .mask_invert = false, + .num_regs = 1, + .irqs = max77843_irqs, + .num_irqs = ARRAY_SIZE(max77843_irqs), +}; + +/* Charger and Charger regulator use same regmap. */ +static int max77843_chg_init(struct max77843 *max77843) +{ + int ret; + + max77843->i2c_chg = i2c_new_dummy(max77843->i2c->adapter, I2C_ADDR_CHG); + if (!max77843->i2c_chg) { + dev_err(&max77843->i2c->dev, + "Cannot allocate I2C device for Charger\n"); + return PTR_ERR(max77843->i2c_chg); + } + i2c_set_clientdata(max77843->i2c_chg, max77843); + + max77843->regmap_chg = devm_regmap_init_i2c(max77843->i2c_chg, + &max77843_charger_regmap_config); + if (IS_ERR(max77843->regmap_chg)) { + ret = PTR_ERR(max77843->regmap_chg); + goto err_chg_i2c; + } + + return 0; + +err_chg_i2c: + i2c_unregister_device(max77843->i2c_chg); + + return ret; +} + +static int max77843_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + struct max77843 *max77843; + unsigned int reg_data; + int ret; + + max77843 = devm_kzalloc(&i2c->dev, sizeof(*max77843), GFP_KERNEL); + if (!max77843) + return -ENOMEM; + + i2c_set_clientdata(i2c, max77843); + max77843->dev = &i2c->dev; + max77843->i2c = i2c; + max77843->irq = i2c->irq; + + max77843->regmap = devm_regmap_init_i2c(i2c, + &max77843_regmap_config); + if (IS_ERR(max77843->regmap)) { + dev_err(&i2c->dev, "Failed to allocate topsys register map\n"); + return PTR_ERR(max77843->regmap); + } + + ret = regmap_add_irq_chip(max77843->regmap, max77843->irq, + IRQF_TRIGGER_LOW | IRQF_ONESHOT | IRQF_SHARED, + 0, &max77843_irq_chip, &max77843->irq_data); + if (ret) { + dev_err(&i2c->dev, "Failed to add TOPSYS IRQ chip\n"); + return ret; + } + + ret = regmap_read(max77843->regmap, + MAX77843_SYS_REG_PMICID, ®_data); + if (ret < 0) { + dev_err(&i2c->dev, "Failed to read PMIC ID\n"); + goto err_pmic_id; + } + dev_info(&i2c->dev, "device ID: 0x%x\n", reg_data); + + ret = max77843_chg_init(max77843); + if (ret) { + dev_err(&i2c->dev, "Failed to init Charger\n"); + goto err_pmic_id; + } + + ret = regmap_update_bits(max77843->regmap, + MAX77843_SYS_REG_INTSRCMASK, + MAX77843_INTSRC_MASK_MASK, + (unsigned int)~MAX77843_INTSRC_MASK_MASK); + if (ret < 0) { + dev_err(&i2c->dev, "Failed to unmask interrupt source\n"); + goto err_pmic_id; + } + + ret = mfd_add_devices(max77843->dev, -1, max77843_devs, + ARRAY_SIZE(max77843_devs), NULL, 0, NULL); + if (ret < 0) { + dev_err(&i2c->dev, "Failed to add mfd device\n"); + goto err_pmic_id; + } + + device_init_wakeup(max77843->dev, true); + + return 0; + +err_pmic_id: + regmap_del_irq_chip(max77843->irq, max77843->irq_data); + + return ret; +} + +static int max77843_remove(struct i2c_client *i2c) +{ + struct max77843 *max77843 = i2c_get_clientdata(i2c); + + mfd_remove_devices(max77843->dev); + + regmap_del_irq_chip(max77843->irq, max77843->irq_data); + + i2c_unregister_device(max77843->i2c_chg); + + return 0; +} + +static const struct of_device_id max77843_dt_match[] = { + { .compatible = "maxim,max77843", }, + { }, +}; + +static const struct i2c_device_id max77843_id[] = { + { "max77843", }, + { }, +}; +MODULE_DEVICE_TABLE(i2c, max77843_id); + +static int __maybe_unused max77843_suspend(struct device *dev) +{ + struct i2c_client *i2c = container_of(dev, struct i2c_client, dev); + struct max77843 *max77843 = i2c_get_clientdata(i2c); + + disable_irq(max77843->irq); + if (device_may_wakeup(dev)) + enable_irq_wake(max77843->irq); + + return 0; +} + +static int __maybe_unused max77843_resume(struct device *dev) +{ + struct i2c_client *i2c = container_of(dev, struct i2c_client, dev); + struct max77843 *max77843 = i2c_get_clientdata(i2c); + + if (device_may_wakeup(dev)) + disable_irq_wake(max77843->irq); + enable_irq(max77843->irq); + + return 0; +} + +static SIMPLE_DEV_PM_OPS(max77843_pm, max77843_suspend, max77843_resume); + +static struct i2c_driver max77843_i2c_driver = { + .driver = { + .name = "max77843", + .pm = &max77843_pm, + .of_match_table = max77843_dt_match, + }, + .probe = max77843_probe, + .remove = max77843_remove, + .id_table = max77843_id, +}; + +static int __init max77843_i2c_init(void) +{ + return i2c_add_driver(&max77843_i2c_driver); +} +subsys_initcall(max77843_i2c_init); + +static void __exit max77843_i2c_exit(void) +{ + i2c_del_driver(&max77843_i2c_driver); +} +module_exit(max77843_i2c_exit); diff --git a/include/linux/mfd/max77843-private.h b/include/linux/mfd/max77843-private.h new file mode 100644 index 000000000000..7178ace8379e --- /dev/null +++ b/include/linux/mfd/max77843-private.h @@ -0,0 +1,454 @@ +/* + * Common variables for the Maxim MAX77843 driver + * + * Copyright (C) 2015 Samsung Electronics + * Author: Jaewon Kim + * Author: Beomho Seo + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#ifndef __MAX77843_PRIVATE_H_ +#define __MAX77843_PRIVATE_H_ + +#include +#include + +#define I2C_ADDR_TOPSYS (0xCC >> 1) +#define I2C_ADDR_CHG (0xD2 >> 1) +#define I2C_ADDR_FG (0x6C >> 1) +#define I2C_ADDR_MUIC (0x4A >> 1) + +/* Topsys, Haptic and LED registers */ +enum max77843_sys_reg { + MAX77843_SYS_REG_PMICID = 0x00, + MAX77843_SYS_REG_PMICREV = 0x01, + MAX77843_SYS_REG_MAINCTRL1 = 0x02, + MAX77843_SYS_REG_INTSRC = 0x22, + MAX77843_SYS_REG_INTSRCMASK = 0x23, + MAX77843_SYS_REG_SYSINTSRC = 0x24, + MAX77843_SYS_REG_SYSINTMASK = 0x26, + MAX77843_SYS_REG_TOPSYS_STAT = 0x28, + MAX77843_SYS_REG_SAFEOUTCTRL = 0xC6, + + MAX77843_SYS_REG_END, +}; + +enum max77843_haptic_reg { + MAX77843_HAP_REG_MCONFIG = 0x10, + + MAX77843_HAP_REG_END, +}; + +enum max77843_led_reg { + MAX77843_LED_REG_LEDEN = 0x30, + MAX77843_LED_REG_LED0BRT = 0x31, + MAX77843_LED_REG_LED1BRT = 0x32, + MAX77843_LED_REG_LED2BRT = 0x33, + MAX77843_LED_REG_LED3BRT = 0x34, + MAX77843_LED_REG_LEDBLNK = 0x38, + MAX77843_LED_REG_LEDRAMP = 0x36, + + MAX77843_LED_REG_END, +}; + +/* Charger registers */ +enum max77843_charger_reg { + MAX77843_CHG_REG_CHG_INT = 0xB0, + MAX77843_CHG_REG_CHG_INT_MASK = 0xB1, + MAX77843_CHG_REG_CHG_INT_OK = 0xB2, + MAX77843_CHG_REG_CHG_DTLS_00 = 0xB3, + MAX77843_CHG_REG_CHG_DTLS_01 = 0xB4, + MAX77843_CHG_REG_CHG_DTLS_02 = 0xB5, + MAX77843_CHG_REG_CHG_CNFG_00 = 0xB7, + MAX77843_CHG_REG_CHG_CNFG_01 = 0xB8, + MAX77843_CHG_REG_CHG_CNFG_02 = 0xB9, + MAX77843_CHG_REG_CHG_CNFG_03 = 0xBA, + MAX77843_CHG_REG_CHG_CNFG_04 = 0xBB, + MAX77843_CHG_REG_CHG_CNFG_06 = 0xBD, + MAX77843_CHG_REG_CHG_CNFG_07 = 0xBE, + MAX77843_CHG_REG_CHG_CNFG_09 = 0xC0, + MAX77843_CHG_REG_CHG_CNFG_10 = 0xC1, + MAX77843_CHG_REG_CHG_CNFG_11 = 0xC2, + MAX77843_CHG_REG_CHG_CNFG_12 = 0xC3, + + MAX77843_CHG_REG_END, +}; + +/* Fuel gauge registers */ +enum max77843_fuelgauge { + MAX77843_FG_REG_STATUS = 0x00, + MAX77843_FG_REG_VALRT_TH = 0x01, + MAX77843_FG_REG_TALRT_TH = 0x02, + MAX77843_FG_REG_SALRT_TH = 0x03, + MAX77843_FG_RATE_AT_RATE = 0x04, + MAX77843_FG_REG_REMCAP_REP = 0x05, + MAX77843_FG_REG_SOCREP = 0x06, + MAX77843_FG_REG_AGE = 0x07, + MAX77843_FG_REG_TEMP = 0x08, + MAX77843_FG_REG_VCELL = 0x09, + MAX77843_FG_REG_CURRENT = 0x0A, + MAX77843_FG_REG_AVG_CURRENT = 0x0B, + MAX77843_FG_REG_SOCMIX = 0x0D, + MAX77843_FG_REG_SOCAV = 0x0E, + MAX77843_FG_REG_REMCAP_MIX = 0x0F, + MAX77843_FG_REG_FULLCAP = 0x10, + MAX77843_FG_REG_AVG_TEMP = 0x16, + MAX77843_FG_REG_CYCLES = 0x17, + MAX77843_FG_REG_AVG_VCELL = 0x19, + MAX77843_FG_REG_CONFIG = 0x1D, + MAX77843_FG_REG_REMCAP_AV = 0x1F, + MAX77843_FG_REG_FULLCAP_NOM = 0x23, + MAX77843_FG_REG_MISCCFG = 0x2B, + MAX77843_FG_REG_RCOMP = 0x38, + MAX77843_FG_REG_FSTAT = 0x3D, + MAX77843_FG_REG_DQACC = 0x45, + MAX77843_FG_REG_DPACC = 0x46, + MAX77843_FG_REG_OCV = 0xEE, + MAX77843_FG_REG_VFOCV = 0xFB, + MAX77843_FG_SOCVF = 0xFF, + + MAX77843_FG_END, +}; + +/* MUIC registers */ +enum max77843_muic_reg { + MAX77843_MUIC_REG_ID = 0x00, + MAX77843_MUIC_REG_INT1 = 0x01, + MAX77843_MUIC_REG_INT2 = 0x02, + MAX77843_MUIC_REG_INT3 = 0x03, + MAX77843_MUIC_REG_STATUS1 = 0x04, + MAX77843_MUIC_REG_STATUS2 = 0x05, + MAX77843_MUIC_REG_STATUS3 = 0x06, + MAX77843_MUIC_REG_INTMASK1 = 0x07, + MAX77843_MUIC_REG_INTMASK2 = 0x08, + MAX77843_MUIC_REG_INTMASK3 = 0x09, + MAX77843_MUIC_REG_CDETCTRL1 = 0x0A, + MAX77843_MUIC_REG_CDETCTRL2 = 0x0B, + MAX77843_MUIC_REG_CONTROL1 = 0x0C, + MAX77843_MUIC_REG_CONTROL2 = 0x0D, + MAX77843_MUIC_REG_CONTROL3 = 0x0E, + MAX77843_MUIC_REG_CONTROL4 = 0x16, + MAX77843_MUIC_REG_HVCONTROL1 = 0x17, + MAX77843_MUIC_REG_HVCONTROL2 = 0x18, + + MAX77843_MUIC_REG_END, +}; + +enum max77843_irq { + /* Topsys: SYSTEM */ + MAX77843_SYS_IRQ_SYSINTSRC_SYSUVLO_INT, + MAX77843_SYS_IRQ_SYSINTSRC_SYSOVLO_INT, + MAX77843_SYS_IRQ_SYSINTSRC_TSHDN_INT, + MAX77843_SYS_IRQ_SYSINTSRC_TM_INT, + + /* Charger: CHG_INT */ + MAX77843_CHG_IRQ_CHG_INT_BYP_I, + MAX77843_CHG_IRQ_CHG_INT_BATP_I, + MAX77843_CHG_IRQ_CHG_INT_BAT_I, + MAX77843_CHG_IRQ_CHG_INT_CHG_I, + MAX77843_CHG_IRQ_CHG_INT_WCIN_I, + MAX77843_CHG_IRQ_CHG_INT_CHGIN_I, + MAX77843_CHG_IRQ_CHG_INT_AICL_I, + + MAX77843_IRQ_NUM, +}; + +enum max77843_irq_muic { + /* MUIC: INT1 */ + MAX77843_MUIC_IRQ_INT1_ADC, + MAX77843_MUIC_IRQ_INT1_ADCERROR, + MAX77843_MUIC_IRQ_INT1_ADC1K, + + /* MUIC: INT2 */ + MAX77843_MUIC_IRQ_INT2_CHGTYP, + MAX77843_MUIC_IRQ_INT2_CHGDETRUN, + MAX77843_MUIC_IRQ_INT2_DCDTMR, + MAX77843_MUIC_IRQ_INT2_DXOVP, + MAX77843_MUIC_IRQ_INT2_VBVOLT, + + /* MUIC: INT3 */ + MAX77843_MUIC_IRQ_INT3_VBADC, + MAX77843_MUIC_IRQ_INT3_VDNMON, + MAX77843_MUIC_IRQ_INT3_DNRES, + MAX77843_MUIC_IRQ_INT3_MPNACK, + MAX77843_MUIC_IRQ_INT3_MRXBUFOW, + MAX77843_MUIC_IRQ_INT3_MRXTRF, + MAX77843_MUIC_IRQ_INT3_MRXPERR, + MAX77843_MUIC_IRQ_INT3_MRXRDY, + + MAX77843_MUIC_IRQ_NUM, +}; + +/* MAX77843 interrupts */ +#define MAX77843_SYS_IRQ_SYSUVLO_INT BIT(0) +#define MAX77843_SYS_IRQ_SYSOVLO_INT BIT(1) +#define MAX77843_SYS_IRQ_TSHDN_INT BIT(2) +#define MAX77843_SYS_IRQ_TM_INT BIT(3) + +/* MAX77843 MAINCTRL1 register */ +#define MAINCTRL1_BIASEN_SHIFT 7 +#define MAX77843_MAINCTRL1_BIASEN_MASK BIT(MAINCTRL1_BIASEN_SHIFT) + +/* MAX77843 MCONFIG register */ +#define MCONFIG_MODE_SHIFT 7 +#define MCONFIG_MEN_SHIFT 6 +#define MCONFIG_PDIV_SHIFT 0 + +#define MAX77843_MCONFIG_MODE_MASK BIT(MCONFIG_MODE_SHIFT) +#define MAX77843_MCONFIG_MEN_MASK BIT(MCONFIG_MEN_SHIFT) +#define MAX77843_MCONFIG_PDIV_MASK (0x3 << MCONFIG_PDIV_SHIFT) + +/* Max77843 charger insterrupts */ +#define MAX77843_CHG_BYP_I BIT(0) +#define MAX77843_CHG_BATP_I BIT(2) +#define MAX77843_CHG_BAT_I BIT(3) +#define MAX77843_CHG_CHG_I BIT(4) +#define MAX77843_CHG_WCIN_I BIT(5) +#define MAX77843_CHG_CHGIN_I BIT(6) +#define MAX77843_CHG_AICL_I BIT(7) + +/* MAX77843 CHG_INT_OK register */ +#define MAX77843_CHG_BYP_OK BIT(0) +#define MAX77843_CHG_BATP_OK BIT(2) +#define MAX77843_CHG_BAT_OK BIT(3) +#define MAX77843_CHG_CHG_OK BIT(4) +#define MAX77843_CHG_WCIN_OK BIT(5) +#define MAX77843_CHG_CHGIN_OK BIT(6) +#define MAX77843_CHG_AICL_OK BIT(7) + +/* MAX77843 CHG_DETAILS_00 register */ +#define MAX77843_CHG_BAT_DTLS BIT(0) + +/* MAX77843 CHG_DETAILS_01 register */ +#define MAX77843_CHG_DTLS_MASK 0x0f +#define MAX77843_CHG_PQ_MODE 0x00 +#define MAX77843_CHG_CC_MODE 0x01 +#define MAX77843_CHG_CV_MODE 0x02 +#define MAX77843_CHG_TO_MODE 0x03 +#define MAX77843_CHG_DO_MODE 0x04 +#define MAX77843_CHG_HT_MODE 0x05 +#define MAX77843_CHG_TF_MODE 0x06 +#define MAX77843_CHG_TS_MODE 0x07 +#define MAX77843_CHG_OFF_MODE 0x08 + +#define MAX77843_CHG_BAT_DTLS_MASK 0xf0 +#define MAX77843_CHG_NO_BAT (0x00 << 4) +#define MAX77843_CHG_LOW_VOLT_BAT (0x01 << 4) +#define MAX77843_CHG_LONG_BAT_TIME (0x02 << 4) +#define MAX77843_CHG_OK_BAT (0x03 << 4) +#define MAX77843_CHG_OK_LOW_VOLT_BAT (0x04 << 4) +#define MAX77843_CHG_OVER_VOLT_BAT (0x05 << 4) +#define MAX77843_CHG_OVER_CURRENT_BAT (0x06 << 4) + +/* MAX77843 CHG_CNFG_00 register */ +#define MAX77843_CHG_DISABLE 0x00 +#define MAX77843_CHG_ENABLE 0x05 +#define MAX77843_CHG_MASK 0x01 +#define MAX77843_CHG_BUCK_MASK 0x04 + +/* MAX77843 CHG_CNFG_01 register */ +#define MAX77843_CHG_RESTART_THRESHOLD_100 0x00 +#define MAX77843_CHG_RESTART_THRESHOLD_150 0x10 +#define MAX77843_CHG_RESTART_THRESHOLD_200 0x20 +#define MAX77843_CHG_RESTART_THRESHOLD_DISABLE 0x30 + +/* MAX77843 CHG_CNFG_02 register */ +#define MAX77843_CHG_FAST_CHG_CURRENT_MIN 100000 +#define MAX77843_CHG_FAST_CHG_CURRENT_MAX 3150000 +#define MAX77843_CHG_FAST_CHG_CURRENT_STEP 50000 +#define MAX77843_CHG_FAST_CHG_CURRENT_MASK 0x3f +#define MAX77843_CHG_OTG_ILIMIT_500 (0x00 << 6) +#define MAX77843_CHG_OTG_ILIMIT_900 (0x01 << 6) +#define MAX77843_CHG_OTG_ILIMIT_1200 (0x02 << 6) +#define MAX77843_CHG_OTG_ILIMIT_1500 (0x03 << 6) +#define MAX77843_CHG_OTG_ILIMIT_MASK 0xc0 + +/* MAX77843 CHG_CNFG_03 register */ +#define MAX77843_CHG_TOP_OFF_CURRENT_MIN 125000 +#define MAX77843_CHG_TOP_OFF_CURRENT_MAX 650000 +#define MAX77843_CHG_TOP_OFF_CURRENT_STEP 75000 +#define MAX77843_CHG_TOP_OFF_CURRENT_MASK 0x07 + +/* MAX77843 CHG_CNFG_06 register */ +#define MAX77843_CHG_WRITE_CAP_BLOCK 0x10 +#define MAX77843_CHG_WRITE_CAP_UNBLOCK 0x0C + +/* MAX77843_CHG_CNFG_09_register */ +#define MAX77843_CHG_INPUT_CURRENT_LIMIT_MIN 100000 +#define MAX77843_CHG_INPUT_CURRENT_LIMIT_MAX 4000000 +#define MAX77843_CHG_INPUT_CURRENT_LIMIT_REF 3367000 +#define MAX77843_CHG_INPUT_CURRENT_LIMIT_STEP 33000 + +#define MAX77843_MUIC_ADC BIT(0) +#define MAX77843_MUIC_ADCERROR BIT(2) +#define MAX77843_MUIC_ADC1K BIT(3) + +#define MAX77843_MUIC_CHGTYP BIT(0) +#define MAX77843_MUIC_CHGDETRUN BIT(1) +#define MAX77843_MUIC_DCDTMR BIT(2) +#define MAX77843_MUIC_DXOVP BIT(3) +#define MAX77843_MUIC_VBVOLT BIT(4) + +#define MAX77843_MUIC_VBADC BIT(0) +#define MAX77843_MUIC_VDNMON BIT(1) +#define MAX77843_MUIC_DNRES BIT(2) +#define MAX77843_MUIC_MPNACK BIT(3) +#define MAX77843_MUIC_MRXBUFOW BIT(4) +#define MAX77843_MUIC_MRXTRF BIT(5) +#define MAX77843_MUIC_MRXPERR BIT(6) +#define MAX77843_MUIC_MRXRDY BIT(7) + +/* MAX77843 INTSRCMASK register */ +#define MAX77843_INTSRCMASK_CHGR 0 +#define MAX77843_INTSRCMASK_SYS 1 +#define MAX77843_INTSRCMASK_FG 2 +#define MAX77843_INTSRCMASK_MUIC 3 + +#define MAX77843_INTSRCMASK_CHGR_MASK BIT(MAX77843_INTSRCMASK_CHGR) +#define MAX77843_INTSRCMASK_SYS_MASK BIT(MAX77843_INTSRCMASK_SYS) +#define MAX77843_INTSRCMASK_FG_MASK BIT(MAX77843_INTSRCMASK_FG) +#define MAX77843_INTSRCMASK_MUIC_MASK BIT(MAX77843_INTSRCMASK_MUIC) + +#define MAX77843_INTSRC_MASK_MASK \ + (MAX77843_INTSRCMASK_MUIC_MASK | MAX77843_INTSRCMASK_FG_MASK | \ + MAX77843_INTSRCMASK_SYS_MASK | MAX77843_INTSRCMASK_CHGR_MASK) + +/* MAX77843 STATUS register*/ +#define STATUS1_ADC_SHIFT 0 +#define STATUS1_ADCERROR_SHIFT 6 +#define STATUS1_ADC1K_SHIFT 7 +#define STATUS2_CHGTYP_SHIFT 0 +#define STATUS2_CHGDETRUN_SHIFT 3 +#define STATUS2_DCDTMR_SHIFT 4 +#define STATUS2_DXOVP_SHIFT 5 +#define STATUS2_VBVOLT_SHIFT 6 +#define STATUS3_VBADC_SHIFT 0 +#define STATUS3_VDNMON_SHIFT 4 +#define STATUS3_DNRES_SHIFT 5 +#define STATUS3_MPNACK_SHIFT 6 + +#define MAX77843_MUIC_STATUS1_ADC_MASK (0x1f << STATUS1_ADC_SHIFT) +#define MAX77843_MUIC_STATUS1_ADCERROR_MASK BIT(STATUS1_ADCERROR_SHIFT) +#define MAX77843_MUIC_STATUS1_ADC1K_MASK BIT(STATUS1_ADC1K_SHIFT) +#define MAX77843_MUIC_STATUS2_CHGTYP_MASK (0x7 << STATUS2_CHGTYP_SHIFT) +#define MAX77843_MUIC_STATUS2_CHGDETRUN_MASK BIT(STATUS2_CHGDETRUN_SHIFT) +#define MAX77843_MUIC_STATUS2_DCDTMR_MASK BIT(STATUS2_DCDTMR_SHIFT) +#define MAX77843_MUIC_STATUS2_DXOVP_MASK BIT(STATUS2_DXOVP_SHIFT) +#define MAX77843_MUIC_STATUS2_VBVOLT_MASK BIT(STATUS2_VBVOLT_SHIFT) +#define MAX77843_MUIC_STATUS3_VBADC_MASK (0xf << STATUS3_VBADC_SHIFT) +#define MAX77843_MUIC_STATUS3_VDNMON_MASK BIT(STATUS3_VDNMON_SHIFT) +#define MAX77843_MUIC_STATUS3_DNRES_MASK BIT(STATUS3_DNRES_SHIFT) +#define MAX77843_MUIC_STATUS3_MPNACK_MASK BIT(STATUS3_MPNACK_SHIFT) + +/* MAX77843 CONTROL register */ +#define CONTROL1_COMP1SW_SHIFT 0 +#define CONTROL1_COMP2SW_SHIFT 3 +#define CONTROL1_IDBEN_SHIFT 7 +#define CONTROL2_LOWPWR_SHIFT 0 +#define CONTROL2_ADCEN_SHIFT 1 +#define CONTROL2_CPEN_SHIFT 2 +#define CONTROL2_ACC_DET_SHIFT 5 +#define CONTROL2_USBCPINT_SHIFT 6 +#define CONTROL2_RCPS_SHIFT 7 +#define CONTROL3_JIGSET_SHIFT 0 +#define CONTROL4_ADCDBSET_SHIFT 0 +#define CONTROL4_USBAUTO_SHIFT 4 +#define CONTROL4_FCTAUTO_SHIFT 5 +#define CONTROL4_ADCMODE_SHIFT 6 + +#define MAX77843_MUIC_CONTROL1_COMP1SW_MASK (0x7 << CONTROL1_COMP1SW_SHIFT) +#define MAX77843_MUIC_CONTROL1_COMP2SW_MASK (0x7 << CONTROL1_COMP2SW_SHIFT) +#define MAX77843_MUIC_CONTROL1_IDBEN_MASK BIT(CONTROL1_IDBEN_SHIFT) +#define MAX77843_MUIC_CONTROL2_LOWPWR_MASK BIT(CONTROL2_LOWPWR_SHIFT) +#define MAX77843_MUIC_CONTROL2_ADCEN_MASK BIT(CONTROL2_ADCEN_SHIFT) +#define MAX77843_MUIC_CONTROL2_CPEN_MASK BIT(CONTROL2_CPEN_SHIFT) +#define MAX77843_MUIC_CONTROL2_ACC_DET_MASK BIT(CONTROL2_ACC_DET_SHIFT) +#define MAX77843_MUIC_CONTROL2_USBCPINT_MASK BIT(CONTROL2_USBCPINT_SHIFT) +#define MAX77843_MUIC_CONTROL2_RCPS_MASK BIT(CONTROL2_RCPS_SHIFT) +#define MAX77843_MUIC_CONTROL3_JIGSET_MASK (0x3 << CONTROL3_JIGSET_SHIFT) +#define MAX77843_MUIC_CONTROL4_ADCDBSET_MASK (0x3 << CONTROL4_ADCDBSET_SHIFT) +#define MAX77843_MUIC_CONTROL4_USBAUTO_MASK BIT(CONTROL4_USBAUTO_SHIFT) +#define MAX77843_MUIC_CONTROL4_FCTAUTO_MASK BIT(CONTROL4_FCTAUTO_SHIFT) +#define MAX77843_MUIC_CONTROL4_ADCMODE_MASK (0x3 << CONTROL4_ADCMODE_SHIFT) + +/* MAX77843 switch port */ +#define COM_OPEN 0 +#define COM_USB 1 +#define COM_AUDIO 2 +#define COM_UART 3 +#define COM_AUX_USB 4 +#define COM_AUX_UART 5 + +#define CONTROL1_COM_SW \ + ((MAX77843_MUIC_CONTROL1_COMP1SW_MASK | \ + MAX77843_MUIC_CONTROL1_COMP2SW_MASK)) + +#define CONTROL1_SW_OPEN \ + ((COM_OPEN << CONTROL1_COMP1SW_SHIFT | \ + COM_OPEN << CONTROL1_COMP2SW_SHIFT)) +#define CONTROL1_SW_USB \ + ((COM_USB << CONTROL1_COMP1SW_SHIFT | \ + COM_USB << CONTROL1_COMP2SW_SHIFT)) +#define CONTROL1_SW_AUDIO \ + ((COM_AUDIO << CONTROL1_COMP1SW_SHIFT | \ + COM_AUDIO << CONTROL1_COMP2SW_SHIFT)) +#define CONTROL1_SW_UART \ + ((COM_UART << CONTROL1_COMP1SW_SHIFT | \ + COM_UART << CONTROL1_COMP2SW_SHIFT)) +#define CONTROL1_SW_AUX_USB \ + ((COM_AUX_USB << CONTROL1_COMP1SW_SHIFT | \ + COM_AUX_USB << CONTROL1_COMP2SW_SHIFT)) +#define CONTROL1_SW_AUX_UART \ + ((COM_AUX_UART << CONTROL1_COMP1SW_SHIFT | \ + COM_AUX_UART << CONTROL1_COMP2SW_SHIFT)) + +#define MAX77843_DISABLE 0 +#define MAX77843_ENABLE 1 + +#define CONTROL4_AUTO_DISABLE \ + ((MAX77843_DISABLE << CONTROL4_USBAUTO_SHIFT) | \ + (MAX77843_DISABLE << CONTROL4_FCTAUTO_SHIFT)) +#define CONTROL4_AUTO_ENABLE \ + ((MAX77843_ENABLE << CONTROL4_USBAUTO_SHIFT) | \ + (MAX77843_ENABLE << CONTROL4_FCTAUTO_SHIFT)) + +/* MAX77843 SAFEOUT LDO Control register */ +#define SAFEOUTCTRL_SAFEOUT1_SHIFT 0 +#define SAFEOUTCTRL_SAFEOUT2_SHIFT 2 +#define SAFEOUTCTRL_ENSAFEOUT1_SHIFT 6 +#define SAFEOUTCTRL_ENSAFEOUT2_SHIFT 7 + +#define MAX77843_REG_SAFEOUTCTRL_ENSAFEOUT1 \ + BIT(SAFEOUTCTRL_ENSAFEOUT1_SHIFT) +#define MAX77843_REG_SAFEOUTCTRL_ENSAFEOUT2 \ + BIT(SAFEOUTCTRL_ENSAFEOUT2_SHIFT) +#define MAX77843_REG_SAFEOUTCTRL_SAFEOUT1_MASK \ + (0x3 << SAFEOUTCTRL_SAFEOUT1_SHIFT) +#define MAX77843_REG_SAFEOUTCTRL_SAFEOUT2_MASK \ + (0x3 << SAFEOUTCTRL_SAFEOUT2_SHIFT) + +struct max77843 { + struct device *dev; + + struct i2c_client *i2c; + struct i2c_client *i2c_chg; + struct i2c_client *i2c_fuel; + struct i2c_client *i2c_muic; + + struct regmap *regmap; + struct regmap *regmap_chg; + struct regmap *regmap_fuel; + struct regmap *regmap_muic; + + struct regmap_irq_chip_data *irq_data; + struct regmap_irq_chip_data *irq_data_chg; + struct regmap_irq_chip_data *irq_data_fuel; + struct regmap_irq_chip_data *irq_data_muic; + + int irq; +}; +#endif /* __MAX77843_H__ */ -- cgit v1.2.3 From 98fc43864af9e74116eec81c290db048cded15d8 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Sun, 1 Mar 2015 09:10:13 +0200 Subject: nl80211: prohibit mixing 'any' and regular wowlan triggers If the device supports waking up on 'any' signal - i.e. it continues operating as usual and wakes up the host on pretty much anything that happens, then it makes no sense to also configure the more restricted WoWLAN mode where the device operates more autonomously but also in a more restricted fashion. Currently only cw2100 supports both 'any' and other triggers, but it seems to be broken as it doesn't configure anything to the device, so we can't currently get into a situation where both even can correctly be configured. This is about to change (Intel devices are going to support both and have different behaviour depending on configuration) so make sure the conflicting modes cannot be configured. (It seems that cw2100 advertises 'any' and 'disconnect' as a means of saying that's what it will always do, but that isn't really the way this API was meant to be used nor does it actually mean anything as 'any' always implies 'disconnect' already, and the driver doesn't change device configuration in any way depending on the settings.) Signed-off-by: Johannes Berg --- include/uapi/linux/nl80211.h | 2 ++ net/wireless/nl80211.c | 22 ++++++++++++++++++++++ 2 files changed, 24 insertions(+) (limited to 'include') diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index 90c5aeb3cca7..37e7f39441e5 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h @@ -3708,6 +3708,8 @@ struct nl80211_pattern_support { * @NL80211_WOWLAN_TRIG_ANY: wake up on any activity, do not really put * the chip into a special state -- works best with chips that have * support for low-power operation already (flag) + * Note that this mode is incompatible with all of the others, if + * any others are even supported by the device. * @NL80211_WOWLAN_TRIG_DISCONNECT: wake up on disconnect, the way disconnect * is detected is implementation-specific (flag) * @NL80211_WOWLAN_TRIG_MAGIC_PKT: wake up on magic packet (6x 0xff, followed diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 01874628ae00..07cef3d7653e 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -9105,6 +9105,7 @@ static int nl80211_set_wowlan(struct sk_buff *skb, struct genl_info *info) const struct wiphy_wowlan_support *wowlan = rdev->wiphy.wowlan; int err, i; bool prev_enabled = rdev->wiphy.wowlan_config; + bool regular = false; if (!wowlan) return -EOPNOTSUPP; @@ -9132,12 +9133,14 @@ static int nl80211_set_wowlan(struct sk_buff *skb, struct genl_info *info) if (!(wowlan->flags & WIPHY_WOWLAN_DISCONNECT)) return -EINVAL; new_triggers.disconnect = true; + regular = true; } if (tb[NL80211_WOWLAN_TRIG_MAGIC_PKT]) { if (!(wowlan->flags & WIPHY_WOWLAN_MAGIC_PKT)) return -EINVAL; new_triggers.magic_pkt = true; + regular = true; } if (tb[NL80211_WOWLAN_TRIG_GTK_REKEY_SUPPORTED]) @@ -9147,24 +9150,28 @@ static int nl80211_set_wowlan(struct sk_buff *skb, struct genl_info *info) if (!(wowlan->flags & WIPHY_WOWLAN_GTK_REKEY_FAILURE)) return -EINVAL; new_triggers.gtk_rekey_failure = true; + regular = true; } if (tb[NL80211_WOWLAN_TRIG_EAP_IDENT_REQUEST]) { if (!(wowlan->flags & WIPHY_WOWLAN_EAP_IDENTITY_REQ)) return -EINVAL; new_triggers.eap_identity_req = true; + regular = true; } if (tb[NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE]) { if (!(wowlan->flags & WIPHY_WOWLAN_4WAY_HANDSHAKE)) return -EINVAL; new_triggers.four_way_handshake = true; + regular = true; } if (tb[NL80211_WOWLAN_TRIG_RFKILL_RELEASE]) { if (!(wowlan->flags & WIPHY_WOWLAN_RFKILL_RELEASE)) return -EINVAL; new_triggers.rfkill_release = true; + regular = true; } if (tb[NL80211_WOWLAN_TRIG_PKT_PATTERN]) { @@ -9173,6 +9180,8 @@ static int nl80211_set_wowlan(struct sk_buff *skb, struct genl_info *info) int rem, pat_len, mask_len, pkt_offset; struct nlattr *pat_tb[NUM_NL80211_PKTPAT]; + regular = true; + nla_for_each_nested(pat, tb[NL80211_WOWLAN_TRIG_PKT_PATTERN], rem) n_patterns++; @@ -9234,6 +9243,7 @@ static int nl80211_set_wowlan(struct sk_buff *skb, struct genl_info *info) } if (tb[NL80211_WOWLAN_TRIG_TCP_CONNECTION]) { + regular = true; err = nl80211_parse_wowlan_tcp( rdev, tb[NL80211_WOWLAN_TRIG_TCP_CONNECTION], &new_triggers); @@ -9242,6 +9252,7 @@ static int nl80211_set_wowlan(struct sk_buff *skb, struct genl_info *info) } if (tb[NL80211_WOWLAN_TRIG_NET_DETECT]) { + regular = true; err = nl80211_parse_wowlan_nd( rdev, wowlan, tb[NL80211_WOWLAN_TRIG_NET_DETECT], &new_triggers); @@ -9249,6 +9260,17 @@ static int nl80211_set_wowlan(struct sk_buff *skb, struct genl_info *info) goto error; } + /* The 'any' trigger means the device continues operating more or less + * as in its normal operation mode and wakes up the host on most of the + * normal interrupts (like packet RX, ...) + * It therefore makes little sense to combine with the more constrained + * wakeup trigger modes. + */ + if (new_triggers.any && regular) { + err = -EINVAL; + goto error; + } + ntrig = kmemdup(&new_triggers, sizeof(new_triggers), GFP_KERNEL); if (!ntrig) { err = -ENOMEM; -- cgit v1.2.3 From 3384d757d41521a3dee274ed2802bcd285ed8e62 Mon Sep 17 00:00:00 2001 From: Arik Nemtsov Date: Sun, 1 Mar 2015 09:10:15 +0200 Subject: mac80211: allow iterating inactive interfaces Sometimes the driver might want to modify private data in interfaces that are down. One possible use-case is cleaning up interface state after HW recovery. Some interfaces that were up before the recovery took place might be down now, but they might still be "dirty". Introduce a new iterate_interfaces() API and a new ACTIVE iterator flag. This way the internal implementation of the both active and inactive APIs remains the same. Signed-off-by: Arik Nemtsov Signed-off-by: Emmanuel Grumbach Signed-off-by: Johannes Berg --- include/net/mac80211.h | 35 ++++++++++++++++++++++++++++++----- net/mac80211/util.c | 29 ++++++++++++++++------------- 2 files changed, 46 insertions(+), 18 deletions(-) (limited to 'include') diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 3a029f0e303d..d1d6fbc13b1c 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -4346,12 +4346,32 @@ void ieee80211_sched_scan_stopped(struct ieee80211_hw *hw); * haven't been re-added to the driver yet. * @IEEE80211_IFACE_ITER_RESUME_ALL: During resume, iterate over all * interfaces, even if they haven't been re-added to the driver yet. + * @IEEE80211_IFACE_ITER_ACTIVE: Iterate only active interfaces (netdev is up). */ enum ieee80211_interface_iteration_flags { IEEE80211_IFACE_ITER_NORMAL = 0, IEEE80211_IFACE_ITER_RESUME_ALL = BIT(0), + IEEE80211_IFACE_ITER_ACTIVE = BIT(1), }; +/** + * ieee80211_iterate_interfaces - iterate interfaces + * + * This function iterates over the interfaces associated with a given + * hardware and calls the callback for them. This includes active as well as + * inactive interfaces. This function allows the iterator function to sleep. + * Will iterate over a new interface during add_interface(). + * + * @hw: the hardware struct of which the interfaces should be iterated over + * @iter_flags: iteration flags, see &enum ieee80211_interface_iteration_flags + * @iterator: the iterator function to call + * @data: first argument of the iterator function + */ +void ieee80211_iterate_interfaces(struct ieee80211_hw *hw, u32 iter_flags, + void (*iterator)(void *data, u8 *mac, + struct ieee80211_vif *vif), + void *data); + /** * ieee80211_iterate_active_interfaces - iterate active interfaces * @@ -4367,11 +4387,16 @@ enum ieee80211_interface_iteration_flags { * @iterator: the iterator function to call * @data: first argument of the iterator function */ -void ieee80211_iterate_active_interfaces(struct ieee80211_hw *hw, - u32 iter_flags, - void (*iterator)(void *data, u8 *mac, - struct ieee80211_vif *vif), - void *data); +static inline void +ieee80211_iterate_active_interfaces(struct ieee80211_hw *hw, u32 iter_flags, + void (*iterator)(void *data, u8 *mac, + struct ieee80211_vif *vif), + void *data) +{ + ieee80211_iterate_interfaces(hw, + iter_flags | IEEE80211_IFACE_ITER_ACTIVE, + iterator, data); +} /** * ieee80211_iterate_active_interfaces_atomic - iterate active interfaces diff --git a/net/mac80211/util.c b/net/mac80211/util.c index 327886748a1d..37d85d36dd2c 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -625,13 +625,14 @@ void ieee80211_wake_vif_queues(struct ieee80211_local *local, reason, true); } -static void __iterate_active_interfaces(struct ieee80211_local *local, - u32 iter_flags, - void (*iterator)(void *data, u8 *mac, - struct ieee80211_vif *vif), - void *data) +static void __iterate_interfaces(struct ieee80211_local *local, + u32 iter_flags, + void (*iterator)(void *data, u8 *mac, + struct ieee80211_vif *vif), + void *data) { struct ieee80211_sub_if_data *sdata; + bool active_only = iter_flags & IEEE80211_IFACE_ITER_ACTIVE; list_for_each_entry_rcu(sdata, &local->interfaces, list) { switch (sdata->vif.type) { @@ -645,9 +646,9 @@ static void __iterate_active_interfaces(struct ieee80211_local *local, break; } if (!(iter_flags & IEEE80211_IFACE_ITER_RESUME_ALL) && - !(sdata->flags & IEEE80211_SDATA_IN_DRIVER)) + active_only && !(sdata->flags & IEEE80211_SDATA_IN_DRIVER)) continue; - if (ieee80211_sdata_running(sdata)) + if (ieee80211_sdata_running(sdata) || !active_only) iterator(data, sdata->vif.addr, &sdata->vif); } @@ -656,12 +657,12 @@ static void __iterate_active_interfaces(struct ieee80211_local *local, lockdep_is_held(&local->iflist_mtx) || lockdep_rtnl_is_held()); if (sdata && - (iter_flags & IEEE80211_IFACE_ITER_RESUME_ALL || + (iter_flags & IEEE80211_IFACE_ITER_RESUME_ALL || !active_only || sdata->flags & IEEE80211_SDATA_IN_DRIVER)) iterator(data, sdata->vif.addr, &sdata->vif); } -void ieee80211_iterate_active_interfaces( +void ieee80211_iterate_interfaces( struct ieee80211_hw *hw, u32 iter_flags, void (*iterator)(void *data, u8 *mac, struct ieee80211_vif *vif), @@ -670,10 +671,10 @@ void ieee80211_iterate_active_interfaces( struct ieee80211_local *local = hw_to_local(hw); mutex_lock(&local->iflist_mtx); - __iterate_active_interfaces(local, iter_flags, iterator, data); + __iterate_interfaces(local, iter_flags, iterator, data); mutex_unlock(&local->iflist_mtx); } -EXPORT_SYMBOL_GPL(ieee80211_iterate_active_interfaces); +EXPORT_SYMBOL_GPL(ieee80211_iterate_interfaces); void ieee80211_iterate_active_interfaces_atomic( struct ieee80211_hw *hw, u32 iter_flags, @@ -684,7 +685,8 @@ void ieee80211_iterate_active_interfaces_atomic( struct ieee80211_local *local = hw_to_local(hw); rcu_read_lock(); - __iterate_active_interfaces(local, iter_flags, iterator, data); + __iterate_interfaces(local, iter_flags | IEEE80211_IFACE_ITER_ACTIVE, + iterator, data); rcu_read_unlock(); } EXPORT_SYMBOL_GPL(ieee80211_iterate_active_interfaces_atomic); @@ -699,7 +701,8 @@ void ieee80211_iterate_active_interfaces_rtnl( ASSERT_RTNL(); - __iterate_active_interfaces(local, iter_flags, iterator, data); + __iterate_interfaces(local, iter_flags | IEEE80211_IFACE_ITER_ACTIVE, + iterator, data); } EXPORT_SYMBOL_GPL(ieee80211_iterate_active_interfaces_rtnl); -- cgit v1.2.3 From 64a8cef41a8ce694b59ec75ae52688f58925693c Mon Sep 17 00:00:00 2001 From: SenthilKumar Jegadeesan Date: Mon, 2 Mar 2015 13:29:40 +0530 Subject: mac80211: provide station PMF configuration to driver Some device drivers offload part of aggregation including AddBA/DelBA negotiations to firmware. In such scenario, the PMF configuration of the station needs to be provided to driver to enable encryption of AddBA/DelBA action frames. Signed-off-by: SenthilKumar Jegadeesan [fix commit log, documentation] Signed-off-by: Johannes Berg --- include/net/mac80211.h | 2 ++ net/mac80211/cfg.c | 1 + net/mac80211/mlme.c | 6 +++++- 3 files changed, 8 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/net/mac80211.h b/include/net/mac80211.h index d1d6fbc13b1c..a7756e45465e 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -1491,6 +1491,7 @@ struct ieee80211_sta_rates { * @tdls: indicates whether the STA is a TDLS peer * @tdls_initiator: indicates the STA is an initiator of the TDLS link. Only * valid if the STA is a TDLS peer in the first place. + * @mfp: indicates whether the STA uses management frame protection or not. */ struct ieee80211_sta { u32 supp_rates[IEEE80211_NUM_BANDS]; @@ -1507,6 +1508,7 @@ struct ieee80211_sta { struct ieee80211_sta_rates __rcu *rates; bool tdls; bool tdls_initiator; + bool mfp; /* must be last */ u8 drv_priv[0] __aligned(sizeof(void *)); diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 06557e4f9588..94889def2ef5 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -1068,6 +1068,7 @@ static int sta_apply_parameters(struct ieee80211_local *local, sta->sta.wme = set & BIT(NL80211_STA_FLAG_WME); if (mask & BIT(NL80211_STA_FLAG_MFP)) { + sta->sta.mfp = !!(set & BIT(NL80211_STA_FLAG_MFP)); if (set & BIT(NL80211_STA_FLAG_MFP)) set_sta_flag(sta, WLAN_STA_MFP); else diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index bc2975e91272..539d6a976cbf 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -2966,8 +2966,12 @@ static bool ieee80211_assoc_success(struct ieee80211_sub_if_data *sdata, rate_control_rate_init(sta); - if (ifmgd->flags & IEEE80211_STA_MFP_ENABLED) + if (ifmgd->flags & IEEE80211_STA_MFP_ENABLED) { set_sta_flag(sta, WLAN_STA_MFP); + sta->sta.mfp = true; + } else { + sta->sta.mfp = false; + } sta->sta.wme = elems.wmm_param; -- cgit v1.2.3 From 1a6ab46fa9c2bc9399694b4856ab7ea19c036485 Mon Sep 17 00:00:00 2001 From: Masanari Iida Date: Wed, 4 Mar 2015 10:56:13 +0900 Subject: ALSA: Fix spelling typo in Documentation/DocBook/alsa-driver-api.xml This patch fix spelling typo found in alsa-driver-api.xml. It is because this file is generated from comments in source files, I have to fix source files. Signed-off-by: Masanari Iida Signed-off-by: Takashi Iwai --- include/sound/compress_driver.h | 4 ++-- include/sound/control.h | 2 +- include/sound/soc.h | 2 +- include/uapi/sound/compress_offload.h | 2 +- sound/core/pcm_dmaengine.c | 4 ++-- 5 files changed, 7 insertions(+), 7 deletions(-) (limited to 'include') diff --git a/include/sound/compress_driver.h b/include/sound/compress_driver.h index f48089d364c5..fa1d05512c09 100644 --- a/include/sound/compress_driver.h +++ b/include/sound/compress_driver.h @@ -70,7 +70,7 @@ struct snd_compr_runtime { * @device: device pointer * @direction: stream direction, playback/recording * @metadata_set: metadata set flag, true when set - * @next_track: has userspace signall next track transistion, true when set + * @next_track: has userspace signal next track transition, true when set * @private_data: pointer to DSP private data */ struct snd_compr_stream { @@ -95,7 +95,7 @@ struct snd_compr_stream { * and the stream properties * @get_params: retrieve the codec parameters, mandatory * @set_metadata: Set the metadata values for a stream - * @get_metadata: retreives the requested metadata values from stream + * @get_metadata: retrieves the requested metadata values from stream * @trigger: Trigger operations like start, pause, resume, drain, stop. * This callback is mandatory * @pointer: Retrieve current h/w pointer information. Mandatory diff --git a/include/sound/control.h b/include/sound/control.h index 75f3054023f7..95aad6d3fd1a 100644 --- a/include/sound/control.h +++ b/include/sound/control.h @@ -227,7 +227,7 @@ snd_ctl_add_slave(struct snd_kcontrol *master, struct snd_kcontrol *slave) * Add a virtual slave control to the given master. * Unlike snd_ctl_add_slave(), the element added via this function * is supposed to have volatile values, and get callback is called - * at each time quried from the master. + * at each time queried from the master. * * When the control peeks the hardware values directly and the value * can be changed by other means than the put callback of the element, diff --git a/include/sound/soc.h b/include/sound/soc.h index 0d1ade195628..cf0bb156d6da 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -1469,7 +1469,7 @@ static inline struct snd_soc_codec *snd_soc_kcontrol_codec( } /** - * snd_soc_kcontrol_platform() - Returns the platform that registerd the control + * snd_soc_kcontrol_platform() - Returns the platform that registered the control * @kcontrol: The control for which to get the platform * * Note: This function will only work correctly if the control has been diff --git a/include/uapi/sound/compress_offload.h b/include/uapi/sound/compress_offload.h index 22ed8cb7800b..e00d8cbfc628 100644 --- a/include/uapi/sound/compress_offload.h +++ b/include/uapi/sound/compress_offload.h @@ -75,7 +75,7 @@ struct snd_compr_tstamp { /** * struct snd_compr_avail - avail descriptor * @avail: Number of bytes available in ring buffer for writing/reading - * @tstamp: timestamp infomation + * @tstamp: timestamp information */ struct snd_compr_avail { __u64 avail; diff --git a/sound/core/pcm_dmaengine.c b/sound/core/pcm_dmaengine.c index 6542c4083594..fba365a78390 100644 --- a/sound/core/pcm_dmaengine.c +++ b/sound/core/pcm_dmaengine.c @@ -289,7 +289,7 @@ EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_request_channel); * * The function should usually be called from the pcm open callback. Note that * this function will use private_data field of the substream's runtime. So it - * is not availabe to your pcm driver implementation. + * is not available to your pcm driver implementation. */ int snd_dmaengine_pcm_open(struct snd_pcm_substream *substream, struct dma_chan *chan) @@ -328,7 +328,7 @@ EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_open); * This function will request a DMA channel using the passed filter function and * data. The function should usually be called from the pcm open callback. Note * that this function will use private_data field of the substream's runtime. So - * it is not availabe to your pcm driver implementation. + * it is not available to your pcm driver implementation. */ int snd_dmaengine_pcm_open_request_chan(struct snd_pcm_substream *substream, dma_filter_fn filter_fn, void *filter_data) -- cgit v1.2.3 From e921eea8e7d4457f424bc3f821cb836e35b91f88 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Tue, 3 Mar 2015 12:05:00 +0100 Subject: dmaengine: Remove memset leftovers Commit 48a9db462d99 ("drivers/dma: remove unused support for MEMSET operations") removed support for the memset operation in dmaengine, but left the fill_aligned field that was supposed to set the buffer alignment for the memset operations. Remove that field too. Signed-off-by: Maxime Ripard Signed-off-by: Vinod Koul --- include/linux/dmaengine.h | 8 -------- 1 file changed, 8 deletions(-) (limited to 'include') diff --git a/include/linux/dmaengine.h b/include/linux/dmaengine.h index b6997a0cb528..db0104b0da4d 100644 --- a/include/linux/dmaengine.h +++ b/include/linux/dmaengine.h @@ -574,7 +574,6 @@ struct dma_tx_state { * @copy_align: alignment shift for memcpy operations * @xor_align: alignment shift for xor operations * @pq_align: alignment shift for pq operations - * @fill_align: alignment shift for memset operations * @dev_id: unique device ID * @dev: struct device reference for dma mapping api * @src_addr_widths: bit mask of src addr widths the device supports @@ -625,7 +624,6 @@ struct dma_device { u8 copy_align; u8 xor_align; u8 pq_align; - u8 fill_align; #define DMA_HAS_PQ_CONTINUE (1 << 15) int dev_id; @@ -826,12 +824,6 @@ static inline bool is_dma_pq_aligned(struct dma_device *dev, size_t off1, return dmaengine_check_align(dev->pq_align, off1, off2, len); } -static inline bool is_dma_fill_aligned(struct dma_device *dev, size_t off1, - size_t off2, size_t len) -{ - return dmaengine_check_align(dev->fill_align, off1, off2, len); -} - static inline void dma_set_maxpq(struct dma_device *dma, int maxpq, int has_pq_continue) { -- cgit v1.2.3 From 1feb57a245a4910b03202a814ffc51a900bd4aca Mon Sep 17 00:00:00 2001 From: Olliver Schinagl Date: Wed, 21 Jan 2015 22:33:46 +0100 Subject: gpio: add parameter to allow the use named gpios The gpio binding document says that new code should always use named gpios. Patch 40b73183 added support to parse a list of gpios from child nodes, but does not make it possible to use named gpios. This patch adds the con_id property and implements it is done in gpiolib.c, where the old-style of using unnamed gpios still works. Signed-off-by: Olliver Schinagl Acked-by: Bryan Wu Acked-by: Dmitry Torokhov Reviewed-by: Alexandre Courbot Signed-off-by: Linus Walleij --- drivers/gpio/devres.c | 18 +++++++++++++++++- drivers/input/keyboard/gpio_keys_polled.c | 2 +- drivers/leds/leds-gpio.c | 2 +- include/linux/gpio/consumer.h | 1 + 4 files changed, 20 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/drivers/gpio/devres.c b/drivers/gpio/devres.c index 13dbd3dfc33a..12c2082f968e 100644 --- a/drivers/gpio/devres.c +++ b/drivers/gpio/devres.c @@ -111,23 +111,39 @@ EXPORT_SYMBOL(__devm_gpiod_get_index); /** * devm_get_gpiod_from_child - get a GPIO descriptor from a device's child node * @dev: GPIO consumer + * @con_id: function within the GPIO consumer * @child: firmware node (child of @dev) * * GPIO descriptors returned from this function are automatically disposed on * driver detach. */ struct gpio_desc *devm_get_gpiod_from_child(struct device *dev, + const char *con_id, struct fwnode_handle *child) { + static const char const *suffixes[] = { "gpios", "gpio" }; + char prop_name[32]; /* 32 is max size of property name */ struct gpio_desc **dr; struct gpio_desc *desc; + unsigned int i; dr = devres_alloc(devm_gpiod_release, sizeof(struct gpio_desc *), GFP_KERNEL); if (!dr) return ERR_PTR(-ENOMEM); - desc = fwnode_get_named_gpiod(child, "gpios"); + for (i = 0; i < ARRAY_SIZE(suffixes); i++) { + if (con_id) + snprintf(prop_name, sizeof(prop_name), "%s-%s", + con_id, suffixes[i]); + else + snprintf(prop_name, sizeof(prop_name), "%s", + suffixes[i]); + + desc = fwnode_get_named_gpiod(child, prop_name); + if (!IS_ERR(desc) || (PTR_ERR(desc) == -EPROBE_DEFER)) + break; + } if (IS_ERR(desc)) { devres_free(dr); return desc; diff --git a/drivers/input/keyboard/gpio_keys_polled.c b/drivers/input/keyboard/gpio_keys_polled.c index 90df4df58b07..097d7216d98e 100644 --- a/drivers/input/keyboard/gpio_keys_polled.c +++ b/drivers/input/keyboard/gpio_keys_polled.c @@ -125,7 +125,7 @@ static struct gpio_keys_platform_data *gpio_keys_polled_get_devtree_pdata(struct device_for_each_child_node(dev, child) { struct gpio_desc *desc; - desc = devm_get_gpiod_from_child(dev, child); + desc = devm_get_gpiod_from_child(dev, NULL, child); if (IS_ERR(desc)) { error = PTR_ERR(desc); if (error != -EPROBE_DEFER) diff --git a/drivers/leds/leds-gpio.c b/drivers/leds/leds-gpio.c index d26af0a79a90..15eb3f86f670 100644 --- a/drivers/leds/leds-gpio.c +++ b/drivers/leds/leds-gpio.c @@ -184,7 +184,7 @@ static struct gpio_leds_priv *gpio_leds_create(struct platform_device *pdev) struct gpio_led led = {}; const char *state = NULL; - led.gpiod = devm_get_gpiod_from_child(dev, child); + led.gpiod = devm_get_gpiod_from_child(dev, NULL, child); if (IS_ERR(led.gpiod)) { fwnode_handle_put(child); ret = PTR_ERR(led.gpiod); diff --git a/include/linux/gpio/consumer.h b/include/linux/gpio/consumer.h index 45afc2dee560..ed20759229eb 100644 --- a/include/linux/gpio/consumer.h +++ b/include/linux/gpio/consumer.h @@ -110,6 +110,7 @@ struct fwnode_handle; struct gpio_desc *fwnode_get_named_gpiod(struct fwnode_handle *fwnode, const char *propname); struct gpio_desc *devm_get_gpiod_from_child(struct device *dev, + const char *con_id, struct fwnode_handle *child); #else /* CONFIG_GPIOLIB */ -- cgit v1.2.3 From 0a240339a8deeb13a19043389bba4285a6c0592e Mon Sep 17 00:00:00 2001 From: Jan Kara Date: Wed, 19 Nov 2014 00:42:09 +0100 Subject: quota: Make VFS quotas use new interface for getting quota info Create new internal interface for getting information about quota which contains everything needed for both VFS quotas and XFS quotas. Make VFS use this and hook it up to Q_GETINFO. Reviewed-by: Christoph Hellwig Signed-off-by: Jan Kara --- fs/ext3/super.c | 2 +- fs/ext4/super.c | 2 +- fs/quota/dquot.c | 41 +++++++++++++++++++++++++++-------------- fs/quota/quota.c | 25 +++++++++++++++++++++---- fs/reiserfs/super.c | 2 +- include/linux/quota.h | 33 ++++++++++++++++++++++++++++++++- include/linux/quotaops.h | 2 +- 7 files changed, 84 insertions(+), 23 deletions(-) (limited to 'include') diff --git a/fs/ext3/super.c b/fs/ext3/super.c index d4dbf3c259b3..f037b4b27300 100644 --- a/fs/ext3/super.c +++ b/fs/ext3/super.c @@ -789,7 +789,7 @@ static const struct quotactl_ops ext3_qctl_operations = { .quota_on = ext3_quota_on, .quota_off = dquot_quota_off, .quota_sync = dquot_quota_sync, - .get_info = dquot_get_dqinfo, + .get_state = dquot_get_state, .set_info = dquot_set_dqinfo, .get_dqblk = dquot_get_dqblk, .set_dqblk = dquot_set_dqblk diff --git a/fs/ext4/super.c b/fs/ext4/super.c index e061e66c8280..d348c7d29d80 100644 --- a/fs/ext4/super.c +++ b/fs/ext4/super.c @@ -1076,7 +1076,7 @@ static const struct quotactl_ops ext4_qctl_operations = { .quota_on = ext4_quota_on, .quota_off = ext4_quota_off, .quota_sync = dquot_quota_sync, - .get_info = dquot_get_dqinfo, + .get_state = dquot_get_state, .set_info = dquot_set_dqinfo, .get_dqblk = dquot_get_dqblk, .set_dqblk = dquot_set_dqblk diff --git a/fs/quota/dquot.c b/fs/quota/dquot.c index 0ccd4ba3a246..cf4edd87e854 100644 --- a/fs/quota/dquot.c +++ b/fs/quota/dquot.c @@ -2614,26 +2614,39 @@ out: EXPORT_SYMBOL(dquot_set_dqblk); /* Generic routine for getting common part of quota file information */ -int dquot_get_dqinfo(struct super_block *sb, int type, struct if_dqinfo *ii) +int dquot_get_state(struct super_block *sb, struct qc_state *state) { struct mem_dqinfo *mi; + struct qc_type_state *tstate; + struct quota_info *dqopt = sb_dqopt(sb); + int type; mutex_lock(&sb_dqopt(sb)->dqonoff_mutex); - if (!sb_has_quota_active(sb, type)) { - mutex_unlock(&sb_dqopt(sb)->dqonoff_mutex); - return -ESRCH; + memset(state, 0, sizeof(*state)); + for (type = 0; type < MAXQUOTAS; type++) { + if (!sb_has_quota_active(sb, type)) + continue; + tstate = state->s_state + type; + mi = sb_dqopt(sb)->info + type; + tstate->flags = QCI_ACCT_ENABLED; + spin_lock(&dq_data_lock); + if (mi->dqi_flags & DQF_SYS_FILE) + tstate->flags |= QCI_SYSFILE; + if (mi->dqi_flags & DQF_ROOT_SQUASH) + tstate->flags |= QCI_ROOT_SQUASH; + if (sb_has_quota_limits_enabled(sb, type)) + tstate->flags |= QCI_LIMITS_ENFORCED; + tstate->spc_timelimit = mi->dqi_bgrace; + tstate->ino_timelimit = mi->dqi_igrace; + tstate->ino = dqopt->files[type]->i_ino; + tstate->blocks = dqopt->files[type]->i_blocks; + tstate->nextents = 1; /* We don't know... */ + spin_unlock(&dq_data_lock); } - mi = sb_dqopt(sb)->info + type; - spin_lock(&dq_data_lock); - ii->dqi_bgrace = mi->dqi_bgrace; - ii->dqi_igrace = mi->dqi_igrace; - ii->dqi_flags = mi->dqi_flags & DQF_GETINFO_MASK; - ii->dqi_valid = IIF_ALL; - spin_unlock(&dq_data_lock); mutex_unlock(&sb_dqopt(sb)->dqonoff_mutex); return 0; } -EXPORT_SYMBOL(dquot_get_dqinfo); +EXPORT_SYMBOL(dquot_get_state); /* Generic routine for setting common part of quota file information */ int dquot_set_dqinfo(struct super_block *sb, int type, struct if_dqinfo *ii) @@ -2677,7 +2690,7 @@ const struct quotactl_ops dquot_quotactl_ops = { .quota_on = dquot_quota_on, .quota_off = dquot_quota_off, .quota_sync = dquot_quota_sync, - .get_info = dquot_get_dqinfo, + .get_state = dquot_get_state, .set_info = dquot_set_dqinfo, .get_dqblk = dquot_get_dqblk, .set_dqblk = dquot_set_dqblk @@ -2688,7 +2701,7 @@ const struct quotactl_ops dquot_quotactl_sysfile_ops = { .quota_enable = dquot_quota_enable, .quota_disable = dquot_quota_disable, .quota_sync = dquot_quota_sync, - .get_info = dquot_get_dqinfo, + .get_state = dquot_get_state, .set_info = dquot_set_dqinfo, .get_dqblk = dquot_get_dqblk, .set_dqblk = dquot_set_dqblk diff --git a/fs/quota/quota.c b/fs/quota/quota.c index d14a799c7785..00d50fca1005 100644 --- a/fs/quota/quota.c +++ b/fs/quota/quota.c @@ -118,13 +118,30 @@ static int quota_getfmt(struct super_block *sb, int type, void __user *addr) static int quota_getinfo(struct super_block *sb, int type, void __user *addr) { - struct if_dqinfo info; + struct qc_state state; + struct qc_type_state *tstate; + struct if_dqinfo uinfo; int ret; - if (!sb->s_qcop->get_info) + /* This checks whether qc_state has enough entries... */ + BUILD_BUG_ON(MAXQUOTAS > XQM_MAXQUOTAS); + if (!sb->s_qcop->get_state) return -ENOSYS; - ret = sb->s_qcop->get_info(sb, type, &info); - if (!ret && copy_to_user(addr, &info, sizeof(info))) + ret = sb->s_qcop->get_state(sb, &state); + if (ret) + return ret; + tstate = state.s_state + type; + if (!(tstate->flags & QCI_ACCT_ENABLED)) + return -ESRCH; + memset(&uinfo, 0, sizeof(uinfo)); + uinfo.dqi_bgrace = tstate->spc_timelimit; + uinfo.dqi_igrace = tstate->ino_timelimit; + if (tstate->flags & QCI_SYSFILE) + uinfo.dqi_flags |= DQF_SYS_FILE; + if (tstate->flags & QCI_ROOT_SQUASH) + uinfo.dqi_flags |= DQF_ROOT_SQUASH; + uinfo.dqi_valid = IIF_ALL; + if (!ret && copy_to_user(addr, &uinfo, sizeof(uinfo))) return -EFAULT; return ret; } diff --git a/fs/reiserfs/super.c b/fs/reiserfs/super.c index 71fbbe3e2dab..68b5f182984e 100644 --- a/fs/reiserfs/super.c +++ b/fs/reiserfs/super.c @@ -805,7 +805,7 @@ static const struct quotactl_ops reiserfs_qctl_operations = { .quota_on = reiserfs_quota_on, .quota_off = dquot_quota_off, .quota_sync = dquot_quota_sync, - .get_info = dquot_get_dqinfo, + .get_state = dquot_get_state, .set_info = dquot_set_dqinfo, .get_dqblk = dquot_get_dqblk, .set_dqblk = dquot_set_dqblk, diff --git a/include/linux/quota.h b/include/linux/quota.h index d534e8ed308a..6ecac0f3b2ca 100644 --- a/include/linux/quota.h +++ b/include/linux/quota.h @@ -366,6 +366,37 @@ struct qc_dqblk { #define QC_RT_SPACE (1<<14) #define QC_ACCT_MASK (QC_SPACE | QC_INO_COUNT | QC_RT_SPACE) +#define QCI_SYSFILE (1 << 0) /* Quota file is hidden from userspace */ +#define QCI_ROOT_SQUASH (1 << 1) /* Root squash turned on */ +#define QCI_ACCT_ENABLED (1 << 2) /* Quota accounting enabled */ +#define QCI_LIMITS_ENFORCED (1 << 3) /* Quota limits enforced */ + +/* Structures for communicating via ->get_state */ +struct qc_type_state { + unsigned int flags; /* Flags QCI_* */ + unsigned int spc_timelimit; /* Time after which space softlimit is + * enforced */ + unsigned int ino_timelimit; /* Ditto for inode softlimit */ + unsigned int rt_spc_timelimit; /* Ditto for real-time space */ + unsigned int spc_warnlimit; /* Limit for number of space warnings */ + unsigned int ino_warnlimit; /* Ditto for inodes */ + unsigned int rt_spc_warnlimit; /* Ditto for real-time space */ + unsigned long long ino; /* Inode number of quota file */ + blkcnt_t blocks; /* Number of 512-byte blocks in the file */ + blkcnt_t nextents; /* Number of extents in the file */ +}; + +struct qc_state { + unsigned int s_incoredqs; /* Number of dquots in core */ + /* + * Per quota type information. The array should really have + * max(MAXQUOTAS, XQM_MAXQUOTAS) entries. BUILD_BUG_ON in + * quota_getinfo() makes sure XQM_MAXQUOTAS is large enough. Once VFS + * supports project quotas, this can be changed to MAXQUOTAS + */ + struct qc_type_state s_state[XQM_MAXQUOTAS]; +}; + /* Operations handling requests from userspace */ struct quotactl_ops { int (*quota_on)(struct super_block *, int, int, struct path *); @@ -373,10 +404,10 @@ struct quotactl_ops { int (*quota_enable)(struct super_block *, unsigned int); int (*quota_disable)(struct super_block *, unsigned int); int (*quota_sync)(struct super_block *, int); - int (*get_info)(struct super_block *, int, struct if_dqinfo *); int (*set_info)(struct super_block *, int, struct if_dqinfo *); int (*get_dqblk)(struct super_block *, struct kqid, struct qc_dqblk *); int (*set_dqblk)(struct super_block *, struct kqid, struct qc_dqblk *); + int (*get_state)(struct super_block *, struct qc_state *); int (*get_xstate)(struct super_block *, struct fs_quota_stat *); int (*get_xstatev)(struct super_block *, struct fs_quota_statv *); int (*rm_xquota)(struct super_block *, unsigned int); diff --git a/include/linux/quotaops.h b/include/linux/quotaops.h index df73258cca47..6509a29523e2 100644 --- a/include/linux/quotaops.h +++ b/include/linux/quotaops.h @@ -95,7 +95,7 @@ int dquot_quota_on_mount(struct super_block *sb, char *qf_name, int dquot_quota_off(struct super_block *sb, int type); int dquot_writeback_dquots(struct super_block *sb, int type); int dquot_quota_sync(struct super_block *sb, int type); -int dquot_get_dqinfo(struct super_block *sb, int type, struct if_dqinfo *ii); +int dquot_get_state(struct super_block *sb, struct qc_state *state); int dquot_set_dqinfo(struct super_block *sb, int type, struct if_dqinfo *ii); int dquot_get_dqblk(struct super_block *sb, struct kqid id, struct qc_dqblk *di); -- cgit v1.2.3 From 59b6ba699043e0f55d4057cf2ae79d9c1171bc58 Mon Sep 17 00:00:00 2001 From: Jan Kara Date: Wed, 19 Nov 2014 16:44:58 +0100 Subject: quota: Remove ->get_xstate and ->get_xstatev callbacks These callbacks are now unused. Remove them. Reviewed-by: Christoph Hellwig Signed-off-by: Jan Kara --- fs/quota/quota.c | 14 ++++---------- include/linux/quota.h | 2 -- 2 files changed, 4 insertions(+), 12 deletions(-) (limited to 'include') diff --git a/fs/quota/quota.c b/fs/quota/quota.c index 83939ff4c444..20d11cd21247 100644 --- a/fs/quota/quota.c +++ b/fs/quota/quota.c @@ -350,12 +350,9 @@ static int quota_getxstate(struct super_block *sb, void __user *addr) struct fs_quota_stat fqs; int ret; - if (!sb->s_qcop->get_xstate && !sb->s_qcop->get_state) + if (!sb->s_qcop->get_state) return -ENOSYS; - if (sb->s_qcop->get_state) - ret = quota_getstate(sb, &fqs); - else - ret = sb->s_qcop->get_xstate(sb, &fqs); + ret = quota_getstate(sb, &fqs); if (!ret && copy_to_user(addr, &fqs, sizeof(fqs))) return -EFAULT; return ret; @@ -414,7 +411,7 @@ static int quota_getxstatev(struct super_block *sb, void __user *addr) struct fs_quota_statv fqs; int ret; - if (!sb->s_qcop->get_xstatev && !sb->s_qcop->get_state) + if (!sb->s_qcop->get_state) return -ENOSYS; memset(&fqs, 0, sizeof(fqs)); @@ -428,10 +425,7 @@ static int quota_getxstatev(struct super_block *sb, void __user *addr) default: return -EINVAL; } - if (sb->s_qcop->get_state) - ret = quota_getstatev(sb, &fqs); - else - ret = sb->s_qcop->get_xstatev(sb, &fqs); + ret = quota_getstatev(sb, &fqs); if (!ret && copy_to_user(addr, &fqs, sizeof(fqs))) return -EFAULT; return ret; diff --git a/include/linux/quota.h b/include/linux/quota.h index 6ecac0f3b2ca..a07f2ed25284 100644 --- a/include/linux/quota.h +++ b/include/linux/quota.h @@ -408,8 +408,6 @@ struct quotactl_ops { int (*get_dqblk)(struct super_block *, struct kqid, struct qc_dqblk *); int (*set_dqblk)(struct super_block *, struct kqid, struct qc_dqblk *); int (*get_state)(struct super_block *, struct qc_state *); - int (*get_xstate)(struct super_block *, struct fs_quota_stat *); - int (*get_xstatev)(struct super_block *, struct fs_quota_statv *); int (*rm_xquota)(struct super_block *, unsigned int); }; -- cgit v1.2.3 From 5eacb2ac029161d94969a511e0adf7dca28cda1f Mon Sep 17 00:00:00 2001 From: Jan Kara Date: Tue, 16 Dec 2014 12:03:51 +0100 Subject: quota: Make ->set_info use structure with neccesary info to VFS and XFS Change ->set_info to take new qc_info structure which contains all the necessary information both for XFS and VFS. Convert Q_SETINFO handler to use this structure. Signed-off-by: Jan Kara --- fs/quota/dquot.c | 27 ++++++++++++++++----------- fs/quota/quota.c | 21 ++++++++++++++++++++- include/linux/quota.h | 21 +++++++++++++++++++-- include/linux/quotaops.h | 2 +- 4 files changed, 56 insertions(+), 15 deletions(-) (limited to 'include') diff --git a/fs/quota/dquot.c b/fs/quota/dquot.c index cf4edd87e854..f37b74eab807 100644 --- a/fs/quota/dquot.c +++ b/fs/quota/dquot.c @@ -2649,33 +2649,38 @@ int dquot_get_state(struct super_block *sb, struct qc_state *state) EXPORT_SYMBOL(dquot_get_state); /* Generic routine for setting common part of quota file information */ -int dquot_set_dqinfo(struct super_block *sb, int type, struct if_dqinfo *ii) +int dquot_set_dqinfo(struct super_block *sb, int type, struct qc_info *ii) { struct mem_dqinfo *mi; int err = 0; + if ((ii->i_fieldmask & QC_WARNS_MASK) || + (ii->i_fieldmask & QC_RT_SPC_TIMER)) + return -EINVAL; mutex_lock(&sb_dqopt(sb)->dqonoff_mutex); if (!sb_has_quota_active(sb, type)) { err = -ESRCH; goto out; } mi = sb_dqopt(sb)->info + type; - if (ii->dqi_valid & IIF_FLAGS) { - if (ii->dqi_flags & ~DQF_SETINFO_MASK || - (ii->dqi_flags & DQF_ROOT_SQUASH && + if (ii->i_fieldmask & QC_FLAGS) { + if ((ii->i_flags & QCI_ROOT_SQUASH && mi->dqi_format->qf_fmt_id != QFMT_VFS_OLD)) { err = -EINVAL; goto out; } } spin_lock(&dq_data_lock); - if (ii->dqi_valid & IIF_BGRACE) - mi->dqi_bgrace = ii->dqi_bgrace; - if (ii->dqi_valid & IIF_IGRACE) - mi->dqi_igrace = ii->dqi_igrace; - if (ii->dqi_valid & IIF_FLAGS) - mi->dqi_flags = (mi->dqi_flags & ~DQF_SETINFO_MASK) | - (ii->dqi_flags & DQF_SETINFO_MASK); + if (ii->i_fieldmask & QC_SPC_TIMER) + mi->dqi_bgrace = ii->i_spc_timelimit; + if (ii->i_fieldmask & QC_INO_TIMER) + mi->dqi_igrace = ii->i_ino_timelimit; + if (ii->i_fieldmask & QC_FLAGS) { + if (ii->i_flags & QCI_ROOT_SQUASH) + mi->dqi_flags |= DQF_ROOT_SQUASH; + else + mi->dqi_flags &= ~DQF_ROOT_SQUASH; + } spin_unlock(&dq_data_lock); mark_info_dirty(sb, type); /* Force write to disk */ diff --git a/fs/quota/quota.c b/fs/quota/quota.c index 20d11cd21247..741d5a178268 100644 --- a/fs/quota/quota.c +++ b/fs/quota/quota.c @@ -149,12 +149,31 @@ static int quota_getinfo(struct super_block *sb, int type, void __user *addr) static int quota_setinfo(struct super_block *sb, int type, void __user *addr) { struct if_dqinfo info; + struct qc_info qinfo; if (copy_from_user(&info, addr, sizeof(info))) return -EFAULT; if (!sb->s_qcop->set_info) return -ENOSYS; - return sb->s_qcop->set_info(sb, type, &info); + if (info.dqi_valid & ~(IIF_FLAGS | IIF_BGRACE | IIF_IGRACE)) + return -EINVAL; + memset(&qinfo, 0, sizeof(qinfo)); + if (info.dqi_valid & IIF_FLAGS) { + if (info.dqi_flags & ~DQF_SETINFO_MASK) + return -EINVAL; + if (info.dqi_flags & DQF_ROOT_SQUASH) + qinfo.i_flags |= QCI_ROOT_SQUASH; + qinfo.i_fieldmask |= QC_FLAGS; + } + if (info.dqi_valid & IIF_BGRACE) { + qinfo.i_spc_timelimit = info.dqi_bgrace; + qinfo.i_fieldmask |= QC_SPC_TIMER; + } + if (info.dqi_valid & IIF_IGRACE) { + qinfo.i_ino_timelimit = info.dqi_igrace; + qinfo.i_fieldmask |= QC_INO_TIMER; + } + return sb->s_qcop->set_info(sb, type, &qinfo); } static inline qsize_t qbtos(qsize_t blocks) diff --git a/include/linux/quota.h b/include/linux/quota.h index a07f2ed25284..3d521199a0bd 100644 --- a/include/linux/quota.h +++ b/include/linux/quota.h @@ -344,7 +344,10 @@ struct qc_dqblk { int d_rt_spc_warns; /* # warnings issued wrt RT space */ }; -/* Field specifiers for ->set_dqblk() in struct qc_dqblk */ +/* + * Field specifiers for ->set_dqblk() in struct qc_dqblk and also for + * ->set_info() in struct qc_info + */ #define QC_INO_SOFT (1<<0) #define QC_INO_HARD (1<<1) #define QC_SPC_SOFT (1<<2) @@ -365,6 +368,7 @@ struct qc_dqblk { #define QC_INO_COUNT (1<<13) #define QC_RT_SPACE (1<<14) #define QC_ACCT_MASK (QC_SPACE | QC_INO_COUNT | QC_RT_SPACE) +#define QC_FLAGS (1<<15) #define QCI_SYSFILE (1 << 0) /* Quota file is hidden from userspace */ #define QCI_ROOT_SQUASH (1 << 1) /* Root squash turned on */ @@ -397,6 +401,19 @@ struct qc_state { struct qc_type_state s_state[XQM_MAXQUOTAS]; }; +/* Structure for communicating via ->set_info */ +struct qc_info { + int i_fieldmask; /* mask of fields to change in ->set_info() */ + unsigned int i_flags; /* Flags QCI_* */ + unsigned int i_spc_timelimit; /* Time after which space softlimit is + * enforced */ + unsigned int i_ino_timelimit; /* Ditto for inode softlimit */ + unsigned int i_rt_spc_timelimit;/* Ditto for real-time space */ + unsigned int i_spc_warnlimit; /* Limit for number of space warnings */ + unsigned int i_ino_warnlimit; /* Limit for number of inode warnings */ + unsigned int i_rt_spc_warnlimit; /* Ditto for real-time space */ +}; + /* Operations handling requests from userspace */ struct quotactl_ops { int (*quota_on)(struct super_block *, int, int, struct path *); @@ -404,7 +421,7 @@ struct quotactl_ops { int (*quota_enable)(struct super_block *, unsigned int); int (*quota_disable)(struct super_block *, unsigned int); int (*quota_sync)(struct super_block *, int); - int (*set_info)(struct super_block *, int, struct if_dqinfo *); + int (*set_info)(struct super_block *, int, struct qc_info *); int (*get_dqblk)(struct super_block *, struct kqid, struct qc_dqblk *); int (*set_dqblk)(struct super_block *, struct kqid, struct qc_dqblk *); int (*get_state)(struct super_block *, struct qc_state *); diff --git a/include/linux/quotaops.h b/include/linux/quotaops.h index 6509a29523e2..9f4b07ba9e8c 100644 --- a/include/linux/quotaops.h +++ b/include/linux/quotaops.h @@ -96,7 +96,7 @@ int dquot_quota_off(struct super_block *sb, int type); int dquot_writeback_dquots(struct super_block *sb, int type); int dquot_quota_sync(struct super_block *sb, int type); int dquot_get_state(struct super_block *sb, struct qc_state *state); -int dquot_set_dqinfo(struct super_block *sb, int type, struct if_dqinfo *ii); +int dquot_set_dqinfo(struct super_block *sb, int type, struct qc_info *ii); int dquot_get_dqblk(struct super_block *sb, struct kqid id, struct qc_dqblk *di); int dquot_set_dqblk(struct super_block *sb, struct kqid id, -- cgit v1.2.3 From 781970240a56d1c15a9b8ee37d28987b8182f060 Mon Sep 17 00:00:00 2001 From: Konstantin Khlebnikov Date: Thu, 12 Feb 2015 19:08:16 +0300 Subject: quota: reorder flags in quota state Flags in struct quota_state keep flags for each quota type and some common flags. This patch reorders typed flags: Before: 0 USRQUOTA DQUOT_USAGE_ENABLED 1 USRQUOTA DQUOT_LIMITS_ENABLED 2 USRQUOTA DQUOT_SUSPENDED 3 GRPQUOTA DQUOT_USAGE_ENABLED 4 GRPQUOTA DQUOT_LIMITS_ENABLED 5 GRPQUOTA DQUOT_SUSPENDED 6 DQUOT_QUOTA_SYS_FILE 7 DQUOT_NEGATIVE_USAGE After: 0 USRQUOTA DQUOT_USAGE_ENABLED 1 GRPQUOTA DQUOT_USAGE_ENABLED 2 USRQUOTA DQUOT_LIMITS_ENABLED 3 GRPQUOTA DQUOT_LIMITS_ENABLED 4 USRQUOTA DQUOT_SUSPENDED 5 GRPQUOTA DQUOT_SUSPENDED 6 DQUOT_QUOTA_SYS_FILE 7 DQUOT_NEGATIVE_USAGE Now we can get bitmap of all enabled/suspended quota types without loop. For example suspended: (flags / DQUOT_SUSPENDED) & ((1 << MAXQUOTAS) - 1). add/remove: 0/1 grow/shrink: 3/11 up/down: 56/-215 (-159) function old new delta __dquot_initialize 423 447 +24 dquot_transfer 181 197 +16 dquot_alloc_inode 286 302 +16 dquot_reclaim_space_nodirty 316 313 -3 dquot_claim_space_nodirty 314 311 -3 dquot_resume 286 281 -5 dquot_free_inode 332 324 -8 __dquot_alloc_space 500 492 -8 dquot_disable 1944 1929 -15 dquot_quota_enable 252 236 -16 __dquot_free_space 750 734 -16 dquot_writeback_dquots 625 608 -17 __dquot_transfer 1186 1154 -32 dquot_quota_sync 299 261 -38 dquot_active.isra 54 - -54 Signed-off-by: Konstantin Khlebnikov Signed-off-by: Jan Kara --- include/linux/quota.h | 32 +++++++++++++++++++++++++------- include/linux/quotaops.h | 10 ++-------- 2 files changed, 27 insertions(+), 15 deletions(-) (limited to 'include') diff --git a/include/linux/quota.h b/include/linux/quota.h index d534e8ed308a..a3374dc3a91b 100644 --- a/include/linux/quota.h +++ b/include/linux/quota.h @@ -389,7 +389,19 @@ struct quota_format_type { struct quota_format_type *qf_next; }; -/* Quota state flags - they actually come in two flavors - for users and groups */ +/** + * Quota state flags - they actually come in two flavors - for users and groups. + * + * Actual typed flags layout: + * USRQUOTA GRPQUOTA + * DQUOT_USAGE_ENABLED 0x0001 0x0002 + * DQUOT_LIMITS_ENABLED 0x0004 0x0008 + * DQUOT_SUSPENDED 0x0010 0x0020 + * + * Following bits are used for non-typed flags: + * DQUOT_QUOTA_SYS_FILE 0x0040 + * DQUOT_NEGATIVE_USAGE 0x0080 + */ enum { _DQUOT_USAGE_ENABLED = 0, /* Track disk usage for users */ _DQUOT_LIMITS_ENABLED, /* Enforce quota limits for users */ @@ -398,9 +410,9 @@ enum { * memory to turn them on */ _DQUOT_STATE_FLAGS }; -#define DQUOT_USAGE_ENABLED (1 << _DQUOT_USAGE_ENABLED) -#define DQUOT_LIMITS_ENABLED (1 << _DQUOT_LIMITS_ENABLED) -#define DQUOT_SUSPENDED (1 << _DQUOT_SUSPENDED) +#define DQUOT_USAGE_ENABLED (1 << _DQUOT_USAGE_ENABLED * MAXQUOTAS) +#define DQUOT_LIMITS_ENABLED (1 << _DQUOT_LIMITS_ENABLED * MAXQUOTAS) +#define DQUOT_SUSPENDED (1 << _DQUOT_SUSPENDED * MAXQUOTAS) #define DQUOT_STATE_FLAGS (DQUOT_USAGE_ENABLED | DQUOT_LIMITS_ENABLED | \ DQUOT_SUSPENDED) /* Other quota flags */ @@ -414,15 +426,21 @@ enum { */ #define DQUOT_NEGATIVE_USAGE (1 << (DQUOT_STATE_LAST + 1)) /* Allow negative quota usage */ - static inline unsigned int dquot_state_flag(unsigned int flags, int type) { - return flags << _DQUOT_STATE_FLAGS * type; + return flags << type; } static inline unsigned int dquot_generic_flag(unsigned int flags, int type) { - return (flags >> _DQUOT_STATE_FLAGS * type) & DQUOT_STATE_FLAGS; + return (flags >> type) & DQUOT_STATE_FLAGS; +} + +/* Bitmap of quota types where flag is set in flags */ +static __always_inline unsigned dquot_state_types(unsigned flags, unsigned flag) +{ + BUILD_BUG_ON_NOT_POWER_OF_2(flag); + return (flags / flag) & ((1 << MAXQUOTAS) - 1); } #ifdef CONFIG_QUOTA_NETLINK_INTERFACE diff --git a/include/linux/quotaops.h b/include/linux/quotaops.h index df73258cca47..8778ec4775eb 100644 --- a/include/linux/quotaops.h +++ b/include/linux/quotaops.h @@ -134,10 +134,7 @@ static inline bool sb_has_quota_suspended(struct super_block *sb, int type) static inline unsigned sb_any_quota_suspended(struct super_block *sb) { - unsigned type, tmsk = 0; - for (type = 0; type < MAXQUOTAS; type++) - tmsk |= sb_has_quota_suspended(sb, type) << type; - return tmsk; + return dquot_state_types(sb_dqopt(sb)->flags, DQUOT_SUSPENDED); } /* Does kernel know about any quota information for given sb + type? */ @@ -149,10 +146,7 @@ static inline bool sb_has_quota_loaded(struct super_block *sb, int type) static inline unsigned sb_any_quota_loaded(struct super_block *sb) { - unsigned type, tmsk = 0; - for (type = 0; type < MAXQUOTAS; type++) - tmsk |= sb_has_quota_loaded(sb, type) << type; - return tmsk; + return dquot_state_types(sb_dqopt(sb)->flags, DQUOT_USAGE_ENABLED); } static inline bool sb_has_quota_active(struct super_block *sb, int type) -- cgit v1.2.3 From 970939964c26db4643f58d4e84487821962e0b65 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Wed, 4 Mar 2015 10:33:17 +0100 Subject: ASoC: Allow to register jacks at the card level Jacks are typically card level elements, but are currently registered with a CODEC. When it was originally introduced snd_soc_jack_new() took a snd_soc_card as its parameter, but at that time DAPM was only implemented at the CODEC level and there was only one CODEC per card. This made it clear which CODEC to use for the jack DAPM operations. But the multi-component patchset added support for having multiple CODECs per card and with it the API was updated to register jacks with a specific CODEC instance instead. Subsequently DAPM support at the card level has been introduced, but the snd_soc_jack_new() API has so remained unchanged. This leaves us with the issue that the DAPM pins that are managed by the jack detection logic usually are part of the card DAPM context but are accessed through a CODEC DAPM context. Currently this works fine, but might break in the future if we take a more hierarchical approach to DAPM contexts. Furthermore with componentization progressing systems that do not register a snd_soc_codec might appear, while these system may still want to able to register a jack. This patch addresses these issues by adding a new function called snd_soc_card_jack_new() that can be used to register jacks with the card rather than a CODEC. This new function is mostly identical to snd_soc_jack_new() except that it additionally allows to directly specify the DAPM pins associated with the jack. This was done since most users of snd_soc_jack_new() typically call snd_soc_jack_add_pins() right after it, which is not necessary with the new API and allows to reduce the amount of boiler plate code. The old snd_soc_jack_new() is re-implemented as a wrapper around snd_soc_card_jack_new(). Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown --- include/sound/soc.h | 28 +++++++++++++++++++++++++--- sound/soc/soc-jack.c | 42 ++++++++++++++++++++++++++---------------- 2 files changed, 51 insertions(+), 19 deletions(-) (limited to 'include') diff --git a/include/sound/soc.h b/include/sound/soc.h index 0d1ade195628..99d9ce272209 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -450,8 +450,10 @@ int soc_dai_hw_params(struct snd_pcm_substream *substream, struct snd_soc_dai *dai); /* Jack reporting */ -int snd_soc_jack_new(struct snd_soc_codec *codec, const char *id, int type, - struct snd_soc_jack *jack); +int snd_soc_card_jack_new(struct snd_soc_card *card, const char *id, int type, + struct snd_soc_jack *jack, struct snd_soc_jack_pin *pins, + unsigned int num_pins); + void snd_soc_jack_report(struct snd_soc_jack *jack, int status, int mask); int snd_soc_jack_add_pins(struct snd_soc_jack *jack, int count, struct snd_soc_jack_pin *pins); @@ -659,7 +661,7 @@ struct snd_soc_jack_gpio { struct snd_soc_jack { struct mutex mutex; struct snd_jack *jack; - struct snd_soc_codec *codec; + struct snd_soc_card *card; struct list_head pins; int status; struct blocking_notifier_head notifier; @@ -1482,6 +1484,26 @@ static inline struct snd_soc_platform *snd_soc_kcontrol_platform( return snd_soc_component_to_platform(snd_soc_kcontrol_component(kcontrol)); } +/** + * snd_soc_jack_new - Create a new jack + * @codec: ASoC CODEC + * @id: an identifying string for this jack + * @type: a bitmask of enum snd_jack_type values that can be detected by + * this jack + * @jack: structure to use for the jack + * + * Creates a new jack object. + * + * Returns zero if successful, or a negative error code on failure. + * On success jack will be initialised. + */ +static inline int snd_soc_jack_new(struct snd_soc_codec *codec, const char *id, + int type, struct snd_soc_jack *jack) +{ + return snd_soc_card_jack_new(codec->component.card, id, type, jack, + NULL, 0); +} + int snd_soc_util_init(void); void snd_soc_util_exit(void); diff --git a/sound/soc/soc-jack.c b/sound/soc/soc-jack.c index 4380dcc064a5..9f60c25c4568 100644 --- a/sound/soc/soc-jack.c +++ b/sound/soc/soc-jack.c @@ -22,30 +22,42 @@ #include /** - * snd_soc_jack_new - Create a new jack - * @codec: ASoC codec + * snd_soc_card_jack_new - Create a new jack + * @card: ASoC card * @id: an identifying string for this jack * @type: a bitmask of enum snd_jack_type values that can be detected by * this jack * @jack: structure to use for the jack + * @pins: Array of jack pins to be added to the jack or NULL + * @num_pins: Number of elements in the @pins array * * Creates a new jack object. * * Returns zero if successful, or a negative error code on failure. * On success jack will be initialised. */ -int snd_soc_jack_new(struct snd_soc_codec *codec, const char *id, int type, - struct snd_soc_jack *jack) +int snd_soc_card_jack_new(struct snd_soc_card *card, const char *id, int type, + struct snd_soc_jack *jack, struct snd_soc_jack_pin *pins, + unsigned int num_pins) { + int ret; + mutex_init(&jack->mutex); - jack->codec = codec; + jack->card = card; INIT_LIST_HEAD(&jack->pins); INIT_LIST_HEAD(&jack->jack_zones); BLOCKING_INIT_NOTIFIER_HEAD(&jack->notifier); - return snd_jack_new(codec->component.card->snd_card, id, type, &jack->jack); + ret = snd_jack_new(card->snd_card, id, type, &jack->jack); + if (ret) + return ret; + + if (num_pins) + return snd_soc_jack_add_pins(jack, num_pins, pins); + + return 0; } -EXPORT_SYMBOL_GPL(snd_soc_jack_new); +EXPORT_SYMBOL_GPL(snd_soc_card_jack_new); /** * snd_soc_jack_report - Report the current status for a jack @@ -63,7 +75,6 @@ EXPORT_SYMBOL_GPL(snd_soc_jack_new); */ void snd_soc_jack_report(struct snd_soc_jack *jack, int status, int mask) { - struct snd_soc_codec *codec; struct snd_soc_dapm_context *dapm; struct snd_soc_jack_pin *pin; unsigned int sync = 0; @@ -74,8 +85,7 @@ void snd_soc_jack_report(struct snd_soc_jack *jack, int status, int mask) if (!jack) return; - codec = jack->codec; - dapm = &codec->dapm; + dapm = &jack->card->dapm; mutex_lock(&jack->mutex); @@ -175,12 +185,12 @@ int snd_soc_jack_add_pins(struct snd_soc_jack *jack, int count, for (i = 0; i < count; i++) { if (!pins[i].pin) { - dev_err(jack->codec->dev, "ASoC: No name for pin %d\n", + dev_err(jack->card->dev, "ASoC: No name for pin %d\n", i); return -EINVAL; } if (!pins[i].mask) { - dev_err(jack->codec->dev, "ASoC: No mask for pin %d" + dev_err(jack->card->dev, "ASoC: No mask for pin %d" " (%s)\n", i, pins[i].pin); return -EINVAL; } @@ -260,7 +270,7 @@ static void snd_soc_jack_gpio_detect(struct snd_soc_jack_gpio *gpio) static irqreturn_t gpio_handler(int irq, void *data) { struct snd_soc_jack_gpio *gpio = data; - struct device *dev = gpio->jack->codec->component.card->dev; + struct device *dev = gpio->jack->card->dev; trace_snd_soc_jack_irq(gpio->name); @@ -299,7 +309,7 @@ int snd_soc_jack_add_gpios(struct snd_soc_jack *jack, int count, for (i = 0; i < count; i++) { if (!gpios[i].name) { - dev_err(jack->codec->dev, + dev_err(jack->card->dev, "ASoC: No name for gpio at index %d\n", i); ret = -EINVAL; goto undo; @@ -320,7 +330,7 @@ int snd_soc_jack_add_gpios(struct snd_soc_jack *jack, int count, } else { /* legacy GPIO number */ if (!gpio_is_valid(gpios[i].gpio)) { - dev_err(jack->codec->dev, + dev_err(jack->card->dev, "ASoC: Invalid gpio %d\n", gpios[i].gpio); ret = -EINVAL; @@ -350,7 +360,7 @@ int snd_soc_jack_add_gpios(struct snd_soc_jack *jack, int count, if (gpios[i].wake) { ret = irq_set_irq_wake(gpiod_to_irq(gpios[i].desc), 1); if (ret != 0) - dev_err(jack->codec->dev, + dev_err(jack->card->dev, "ASoC: Failed to mark GPIO at index %d as wake source: %d\n", i, ret); } -- cgit v1.2.3 From 77c71765ef78f87dd901fcb4c751908e835a3b2e Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Wed, 4 Mar 2015 10:33:45 +0100 Subject: ASoC: Remove snd_soc_jack_new() There are no users of snd_soc_jack_new() left and new users should use snd_soc_card_jack_new() instead. So remove the function. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown --- include/sound/soc.h | 20 -------------------- 1 file changed, 20 deletions(-) (limited to 'include') diff --git a/include/sound/soc.h b/include/sound/soc.h index 99d9ce272209..40b3ee96f317 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -1484,26 +1484,6 @@ static inline struct snd_soc_platform *snd_soc_kcontrol_platform( return snd_soc_component_to_platform(snd_soc_kcontrol_component(kcontrol)); } -/** - * snd_soc_jack_new - Create a new jack - * @codec: ASoC CODEC - * @id: an identifying string for this jack - * @type: a bitmask of enum snd_jack_type values that can be detected by - * this jack - * @jack: structure to use for the jack - * - * Creates a new jack object. - * - * Returns zero if successful, or a negative error code on failure. - * On success jack will be initialised. - */ -static inline int snd_soc_jack_new(struct snd_soc_codec *codec, const char *id, - int type, struct snd_soc_jack *jack) -{ - return snd_soc_card_jack_new(codec->component.card, id, type, jack, - NULL, 0); -} - int snd_soc_util_init(void); void snd_soc_util_exit(void); -- cgit v1.2.3 From d136dfeec84bfe3e4238bacd23f21e161268deac Mon Sep 17 00:00:00 2001 From: Tvrtko Ursulin Date: Tue, 3 Mar 2015 14:22:31 +0000 Subject: drm: Pass in new and old plane state to prepare_fb and cleanup_fb Use cases like rotation require these hooks to have some context so they know how to prepare and cleanup the frame buffer correctly. For i915 specifically, object backing pages need to be mapped differently for different rotation modes and the driver needs to know which mapping to instantiate and which to tear down when transitioning between them. v2: Made passed in states const. (Daniel Vetter) [airlied: add mdp5 and atmel fixups] Signed-off-by: Tvrtko Ursulin Cc: Daniel Vetter Cc: dri-devel@lists.freedesktop.org Reviewed-by: Rob Clark Signed-off-by: Dave Airlie --- drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_plane.c | 3 ++- drivers/gpu/drm/drm_atomic_helper.c | 13 ++++++++----- drivers/gpu/drm/drm_plane_helper.c | 5 +++-- drivers/gpu/drm/i915/intel_display.c | 6 ++++-- drivers/gpu/drm/i915/intel_drv.h | 6 ++++-- drivers/gpu/drm/msm/mdp/mdp4/mdp4_plane.c | 6 ++++-- drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c | 6 ++++-- drivers/gpu/drm/tegra/dc.c | 6 ++++-- include/drm/drm_plane_helper.h | 6 ++++-- 9 files changed, 37 insertions(+), 20 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_plane.c b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_plane.c index dbf97d999d40..be9fa8220499 100644 --- a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_plane.c +++ b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_plane.c @@ -712,7 +712,8 @@ static int atmel_hlcdc_plane_atomic_check(struct drm_plane *p, } static int atmel_hlcdc_plane_prepare_fb(struct drm_plane *p, - struct drm_framebuffer *fb) + struct drm_framebuffer *fb, + const struct drm_plane_state *new_state) { struct atmel_hlcdc_plane *plane = drm_plane_to_atmel_hlcdc_plane(p); diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c index 28aa87510551..7715c40d4e74 100644 --- a/drivers/gpu/drm/drm_atomic_helper.c +++ b/drivers/gpu/drm/drm_atomic_helper.c @@ -1116,6 +1116,7 @@ int drm_atomic_helper_prepare_planes(struct drm_device *dev, for (i = 0; i < nplanes; i++) { struct drm_plane_helper_funcs *funcs; struct drm_plane *plane = state->planes[i]; + struct drm_plane_state *plane_state = state->plane_states[i]; struct drm_framebuffer *fb; if (!plane) @@ -1123,10 +1124,10 @@ int drm_atomic_helper_prepare_planes(struct drm_device *dev, funcs = plane->helper_private; - fb = state->plane_states[i]->fb; + fb = plane_state->fb; if (fb && funcs->prepare_fb) { - ret = funcs->prepare_fb(plane, fb); + ret = funcs->prepare_fb(plane, fb, plane_state); if (ret) goto fail; } @@ -1138,6 +1139,7 @@ fail: for (i--; i >= 0; i--) { struct drm_plane_helper_funcs *funcs; struct drm_plane *plane = state->planes[i]; + struct drm_plane_state *plane_state = state->plane_states[i]; struct drm_framebuffer *fb; if (!plane) @@ -1148,7 +1150,7 @@ fail: fb = state->plane_states[i]->fb; if (fb && funcs->cleanup_fb) - funcs->cleanup_fb(plane, fb); + funcs->cleanup_fb(plane, fb, plane_state); } @@ -1254,6 +1256,7 @@ void drm_atomic_helper_cleanup_planes(struct drm_device *dev, for (i = 0; i < nplanes; i++) { struct drm_plane_helper_funcs *funcs; struct drm_plane *plane = old_state->planes[i]; + struct drm_plane_state *plane_state = old_state->plane_states[i]; struct drm_framebuffer *old_fb; if (!plane) @@ -1261,10 +1264,10 @@ void drm_atomic_helper_cleanup_planes(struct drm_device *dev, funcs = plane->helper_private; - old_fb = old_state->plane_states[i]->fb; + old_fb = plane_state->fb; if (old_fb && funcs->cleanup_fb) - funcs->cleanup_fb(plane, old_fb); + funcs->cleanup_fb(plane, old_fb, plane_state); } } EXPORT_SYMBOL(drm_atomic_helper_cleanup_planes); diff --git a/drivers/gpu/drm/drm_plane_helper.c b/drivers/gpu/drm/drm_plane_helper.c index 5ba5792bfdba..813a06627eb3 100644 --- a/drivers/gpu/drm/drm_plane_helper.c +++ b/drivers/gpu/drm/drm_plane_helper.c @@ -437,7 +437,8 @@ int drm_plane_helper_commit(struct drm_plane *plane, if (plane_funcs->prepare_fb && plane_state->fb && plane_state->fb != old_fb) { - ret = plane_funcs->prepare_fb(plane, plane_state->fb); + ret = plane_funcs->prepare_fb(plane, plane_state->fb, + plane_state); if (ret) goto out; } @@ -487,7 +488,7 @@ int drm_plane_helper_commit(struct drm_plane *plane, } if (plane_funcs->cleanup_fb && old_fb) - plane_funcs->cleanup_fb(plane, old_fb); + plane_funcs->cleanup_fb(plane, old_fb, plane_state); out: if (plane_state) { if (plane->funcs->atomic_destroy_state) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 3b0fe9f1f3c9..c234af0379fc 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -11776,7 +11776,8 @@ static void intel_shared_dpll_init(struct drm_device *dev) */ int intel_prepare_plane_fb(struct drm_plane *plane, - struct drm_framebuffer *fb) + struct drm_framebuffer *fb, + const struct drm_plane_state *new_state) { struct drm_device *dev = plane->dev; struct intel_plane *intel_plane = to_intel_plane(plane); @@ -11830,7 +11831,8 @@ intel_prepare_plane_fb(struct drm_plane *plane, */ void intel_cleanup_plane_fb(struct drm_plane *plane, - struct drm_framebuffer *fb) + struct drm_framebuffer *fb, + const struct drm_plane_state *old_state) { struct drm_device *dev = plane->dev; struct drm_i915_gem_object *obj = intel_fb_obj(fb); diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index 1de8e20474d7..58d11a8066d4 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -943,9 +943,11 @@ void intel_finish_page_flip(struct drm_device *dev, int pipe); void intel_finish_page_flip_plane(struct drm_device *dev, int plane); void intel_check_page_flip(struct drm_device *dev, int pipe); int intel_prepare_plane_fb(struct drm_plane *plane, - struct drm_framebuffer *fb); + struct drm_framebuffer *fb, + const struct drm_plane_state *new_state); void intel_cleanup_plane_fb(struct drm_plane *plane, - struct drm_framebuffer *fb); + struct drm_framebuffer *fb, + const struct drm_plane_state *old_state); int intel_plane_atomic_get_property(struct drm_plane *plane, const struct drm_plane_state *state, struct drm_property *property, diff --git a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_plane.c b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_plane.c index cde25009203a..dbc068988377 100644 --- a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_plane.c +++ b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_plane.c @@ -83,7 +83,8 @@ static const struct drm_plane_funcs mdp4_plane_funcs = { }; static int mdp4_plane_prepare_fb(struct drm_plane *plane, - struct drm_framebuffer *fb) + struct drm_framebuffer *fb, + const struct drm_plane_state *new_state) { struct mdp4_plane *mdp4_plane = to_mdp4_plane(plane); struct mdp4_kms *mdp4_kms = get_kms(plane); @@ -93,7 +94,8 @@ static int mdp4_plane_prepare_fb(struct drm_plane *plane, } static void mdp4_plane_cleanup_fb(struct drm_plane *plane, - struct drm_framebuffer *fb) + struct drm_framebuffer *fb, + const struct drm_plane_state *old_state) { struct mdp4_plane *mdp4_plane = to_mdp4_plane(plane); struct mdp4_kms *mdp4_kms = get_kms(plane); diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c index 05cf9ab2a876..6bd48e246283 100644 --- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c +++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c @@ -156,7 +156,8 @@ static const struct drm_plane_funcs mdp5_plane_funcs = { }; static int mdp5_plane_prepare_fb(struct drm_plane *plane, - struct drm_framebuffer *fb) + struct drm_framebuffer *fb, + const struct drm_plane_state *new_state) { struct mdp5_plane *mdp5_plane = to_mdp5_plane(plane); struct mdp5_kms *mdp5_kms = get_kms(plane); @@ -166,7 +167,8 @@ static int mdp5_plane_prepare_fb(struct drm_plane *plane, } static void mdp5_plane_cleanup_fb(struct drm_plane *plane, - struct drm_framebuffer *fb) + struct drm_framebuffer *fb, + const struct drm_plane_state *old_state) { struct mdp5_plane *mdp5_plane = to_mdp5_plane(plane); struct mdp5_kms *mdp5_kms = get_kms(plane); diff --git a/drivers/gpu/drm/tegra/dc.c b/drivers/gpu/drm/tegra/dc.c index 3aaa84ae2681..bc3e85186b88 100644 --- a/drivers/gpu/drm/tegra/dc.c +++ b/drivers/gpu/drm/tegra/dc.c @@ -472,13 +472,15 @@ static const struct drm_plane_funcs tegra_primary_plane_funcs = { }; static int tegra_plane_prepare_fb(struct drm_plane *plane, - struct drm_framebuffer *fb) + struct drm_framebuffer *fb, + const struct drm_plane_state *new_state) { return 0; } static void tegra_plane_cleanup_fb(struct drm_plane *plane, - struct drm_framebuffer *fb) + struct drm_framebuffer *fb, + const struct drm_plane_state *old_fb) { } diff --git a/include/drm/drm_plane_helper.h b/include/drm/drm_plane_helper.h index 31c11d36fae6..72ddab02ebd9 100644 --- a/include/drm/drm_plane_helper.h +++ b/include/drm/drm_plane_helper.h @@ -59,9 +59,11 @@ extern int drm_crtc_init(struct drm_device *dev, */ struct drm_plane_helper_funcs { int (*prepare_fb)(struct drm_plane *plane, - struct drm_framebuffer *fb); + struct drm_framebuffer *fb, + const struct drm_plane_state *new_state); void (*cleanup_fb)(struct drm_plane *plane, - struct drm_framebuffer *fb); + struct drm_framebuffer *fb, + const struct drm_plane_state *old_state); int (*atomic_check)(struct drm_plane *plane, struct drm_plane_state *state); -- cgit v1.2.3 From a7e53531234dc206bb75abb5305a72665dd4d75d Mon Sep 17 00:00:00 2001 From: Alexander Duyck Date: Wed, 4 Mar 2015 15:02:44 -0800 Subject: fib_trie: Make fib_table rcu safe The fib_table was wrapped in several places with an rcu_read_lock/rcu_read_unlock however after looking over the code I found several spots where the tables were being accessed as just standard pointers without any protections. This change fixes that so that all of the proper protections are in place when accessing the table to take RCU replacement or removal of the table into account. Signed-off-by: Alexander Duyck Signed-off-by: David S. Miller --- include/net/ip_fib.h | 70 +++++++++++++++++++++++++++++------------------- include/net/netns/ipv4.h | 7 ++--- net/ipv4/fib_frontend.c | 52 ++++++++++++++++++++++++----------- net/ipv4/fib_trie.c | 21 ++++++++++----- 4 files changed, 98 insertions(+), 52 deletions(-) (limited to 'include') diff --git a/include/net/ip_fib.h b/include/net/ip_fib.h index cba4b7c32935..825cb2800908 100644 --- a/include/net/ip_fib.h +++ b/include/net/ip_fib.h @@ -185,6 +185,7 @@ struct fib_table { u32 tb_id; int tb_default; int tb_num_default; + struct rcu_head rcu; unsigned long tb_data[0]; }; @@ -206,12 +207,16 @@ void fib_free_table(struct fib_table *tb); static inline struct fib_table *fib_get_table(struct net *net, u32 id) { + struct hlist_node *tb_hlist; struct hlist_head *ptr; ptr = id == RT_TABLE_LOCAL ? &net->ipv4.fib_table_hash[TABLE_LOCAL_INDEX] : &net->ipv4.fib_table_hash[TABLE_MAIN_INDEX]; - return hlist_entry(ptr->first, struct fib_table, tb_hlist); + + tb_hlist = rcu_dereference_rtnl(hlist_first_rcu(ptr)); + + return hlist_entry(tb_hlist, struct fib_table, tb_hlist); } static inline struct fib_table *fib_new_table(struct net *net, u32 id) @@ -222,15 +227,19 @@ static inline struct fib_table *fib_new_table(struct net *net, u32 id) static inline int fib_lookup(struct net *net, const struct flowi4 *flp, struct fib_result *res) { - int err = -ENETUNREACH; + struct fib_table *tb; + int err; rcu_read_lock(); - if (!fib_table_lookup(fib_get_table(net, RT_TABLE_LOCAL), flp, res, - FIB_LOOKUP_NOREF) || - !fib_table_lookup(fib_get_table(net, RT_TABLE_MAIN), flp, res, - FIB_LOOKUP_NOREF)) - err = 0; + for (err = 0; !err; err = -ENETUNREACH) { + tb = fib_get_table(net, RT_TABLE_LOCAL); + if (tb && !fib_table_lookup(tb, flp, res, FIB_LOOKUP_NOREF)) + break; + tb = fib_get_table(net, RT_TABLE_MAIN); + if (tb && !fib_table_lookup(tb, flp, res, FIB_LOOKUP_NOREF)) + break; + } rcu_read_unlock(); @@ -249,28 +258,33 @@ int __fib_lookup(struct net *net, struct flowi4 *flp, struct fib_result *res); static inline int fib_lookup(struct net *net, struct flowi4 *flp, struct fib_result *res) { - if (!net->ipv4.fib_has_custom_rules) { - int err = -ENETUNREACH; - - rcu_read_lock(); - - res->tclassid = 0; - if ((net->ipv4.fib_local && - !fib_table_lookup(net->ipv4.fib_local, flp, res, - FIB_LOOKUP_NOREF)) || - (net->ipv4.fib_main && - !fib_table_lookup(net->ipv4.fib_main, flp, res, - FIB_LOOKUP_NOREF)) || - (net->ipv4.fib_default && - !fib_table_lookup(net->ipv4.fib_default, flp, res, - FIB_LOOKUP_NOREF))) - err = 0; - - rcu_read_unlock(); - - return err; + struct fib_table *tb; + int err; + + if (net->ipv4.fib_has_custom_rules) + return __fib_lookup(net, flp, res); + + rcu_read_lock(); + + res->tclassid = 0; + + for (err = 0; !err; err = -ENETUNREACH) { + tb = rcu_dereference_rtnl(net->ipv4.fib_local); + if (tb && !fib_table_lookup(tb, flp, res, FIB_LOOKUP_NOREF)) + break; + + tb = rcu_dereference_rtnl(net->ipv4.fib_main); + if (tb && !fib_table_lookup(tb, flp, res, FIB_LOOKUP_NOREF)) + break; + + tb = rcu_dereference_rtnl(net->ipv4.fib_default); + if (tb && !fib_table_lookup(tb, flp, res, FIB_LOOKUP_NOREF)) + break; } - return __fib_lookup(net, flp, res); + + rcu_read_unlock(); + + return err; } #endif /* CONFIG_IP_MULTIPLE_TABLES */ diff --git a/include/net/netns/ipv4.h b/include/net/netns/ipv4.h index 1b26c6c3fd7c..db1db158a00e 100644 --- a/include/net/netns/ipv4.h +++ b/include/net/netns/ipv4.h @@ -7,6 +7,7 @@ #include #include +#include struct tcpm_hash_bucket; struct ctl_table_header; @@ -38,9 +39,9 @@ struct netns_ipv4 { #ifdef CONFIG_IP_MULTIPLE_TABLES struct fib_rules_ops *rules_ops; bool fib_has_custom_rules; - struct fib_table *fib_local; - struct fib_table *fib_main; - struct fib_table *fib_default; + struct fib_table __rcu *fib_local; + struct fib_table __rcu *fib_main; + struct fib_table __rcu *fib_default; #endif #ifdef CONFIG_IP_ROUTE_CLASSID int fib_num_tclassid_users; diff --git a/net/ipv4/fib_frontend.c b/net/ipv4/fib_frontend.c index 57be71dd6a9e..220c4b4af4cf 100644 --- a/net/ipv4/fib_frontend.c +++ b/net/ipv4/fib_frontend.c @@ -89,17 +89,14 @@ struct fib_table *fib_new_table(struct net *net, u32 id) switch (id) { case RT_TABLE_LOCAL: - net->ipv4.fib_local = tb; + rcu_assign_pointer(net->ipv4.fib_local, tb); break; - case RT_TABLE_MAIN: - net->ipv4.fib_main = tb; + rcu_assign_pointer(net->ipv4.fib_main, tb); break; - case RT_TABLE_DEFAULT: - net->ipv4.fib_default = tb; + rcu_assign_pointer(net->ipv4.fib_default, tb); break; - default: break; } @@ -132,13 +129,14 @@ struct fib_table *fib_get_table(struct net *net, u32 id) static void fib_flush(struct net *net) { int flushed = 0; - struct fib_table *tb; - struct hlist_head *head; unsigned int h; for (h = 0; h < FIB_TABLE_HASHSZ; h++) { - head = &net->ipv4.fib_table_hash[h]; - hlist_for_each_entry(tb, head, tb_hlist) + struct hlist_head *head = &net->ipv4.fib_table_hash[h]; + struct hlist_node *tmp; + struct fib_table *tb; + + hlist_for_each_entry_safe(tb, tmp, head, tb_hlist) flushed += fib_table_flush(tb); } @@ -665,10 +663,12 @@ static int inet_dump_fib(struct sk_buff *skb, struct netlink_callback *cb) s_h = cb->args[0]; s_e = cb->args[1]; + rcu_read_lock(); + for (h = s_h; h < FIB_TABLE_HASHSZ; h++, s_e = 0) { e = 0; head = &net->ipv4.fib_table_hash[h]; - hlist_for_each_entry(tb, head, tb_hlist) { + hlist_for_each_entry_rcu(tb, head, tb_hlist) { if (e < s_e) goto next; if (dumped) @@ -682,6 +682,8 @@ next: } } out: + rcu_read_unlock(); + cb->args[1] = e; cb->args[0] = h; @@ -1117,14 +1119,34 @@ static void ip_fib_net_exit(struct net *net) rtnl_lock(); for (i = 0; i < FIB_TABLE_HASHSZ; i++) { - struct fib_table *tb; - struct hlist_head *head; + struct hlist_head *head = &net->ipv4.fib_table_hash[i]; struct hlist_node *tmp; + struct fib_table *tb; + + /* this is done in two passes as flushing the table could + * cause it to be reallocated in order to accommodate new + * tnodes at the root as the table shrinks. + */ + hlist_for_each_entry_safe(tb, tmp, head, tb_hlist) + fib_table_flush(tb); - head = &net->ipv4.fib_table_hash[i]; hlist_for_each_entry_safe(tb, tmp, head, tb_hlist) { +#ifdef CONFIG_IP_MULTIPLE_TABLES + switch (tb->tb_id) { + case RT_TABLE_LOCAL: + RCU_INIT_POINTER(net->ipv4.fib_local, NULL); + break; + case RT_TABLE_MAIN: + RCU_INIT_POINTER(net->ipv4.fib_main, NULL); + break; + case RT_TABLE_DEFAULT: + RCU_INIT_POINTER(net->ipv4.fib_default, NULL); + break; + default: + break; + } +#endif hlist_del(&tb->tb_hlist); - fib_table_flush(tb); fib_free_table(tb); } } diff --git a/net/ipv4/fib_trie.c b/net/ipv4/fib_trie.c index 2233ebf2aae8..3642b17c8726 100644 --- a/net/ipv4/fib_trie.c +++ b/net/ipv4/fib_trie.c @@ -193,6 +193,13 @@ static inline struct tnode *tnode_get_child_rcu(const struct tnode *tn, return rcu_dereference_rtnl(tn->tnode[i]); } +static inline struct fib_table *trie_get_table(struct trie *t) +{ + unsigned long *tb_data = (unsigned long *)t; + + return container_of(tb_data, struct fib_table, tb_data[0]); +} + /* To understand this stuff, an understanding of keys and all their bits is * necessary. Every node in the trie has a key associated with it, but not * all of the bits in that key are significant. @@ -1593,8 +1600,9 @@ flush_complete: return found; } -void fib_free_table(struct fib_table *tb) +static void __trie_free_rcu(struct rcu_head *head) { + struct fib_table *tb = container_of(head, struct fib_table, rcu); #ifdef CONFIG_IP_FIB_TRIE_STATS struct trie *t = (struct trie *)tb->tb_data; @@ -1603,6 +1611,11 @@ void fib_free_table(struct fib_table *tb) kfree(tb); } +void fib_free_table(struct fib_table *tb) +{ + call_rcu(&tb->rcu, __trie_free_rcu); +} + static int fn_trie_dump_leaf(struct tnode *l, struct fib_table *tb, struct sk_buff *skb, struct netlink_callback *cb) { @@ -1639,6 +1652,7 @@ static int fn_trie_dump_leaf(struct tnode *l, struct fib_table *tb, return skb->len; } +/* rcu_read_lock needs to be hold by caller from readside */ int fib_table_dump(struct fib_table *tb, struct sk_buff *skb, struct netlink_callback *cb) { @@ -1650,15 +1664,12 @@ int fib_table_dump(struct fib_table *tb, struct sk_buff *skb, int count = cb->args[2]; t_key key = cb->args[3]; - rcu_read_lock(); - tp = rcu_dereference_rtnl(t->trie); while ((l = leaf_walk_rcu(&tp, key)) != NULL) { if (fn_trie_dump_leaf(l, tb, skb, cb) < 0) { cb->args[3] = key; cb->args[2] = count; - rcu_read_unlock(); return -1; } @@ -1673,8 +1684,6 @@ int fib_table_dump(struct fib_table *tb, struct sk_buff *skb, break; } - rcu_read_unlock(); - cb->args[3] = key; cb->args[2] = count; -- cgit v1.2.3 From 9941a383df98193aa9a9b3662af7c1fcbc3070ee Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Thu, 5 Mar 2015 10:49:19 +1030 Subject: CPU_MASK_ALL/CPU_MASK_NONE: remove from deprecated region. They're used to initialize various static fields, though static cpumasks should generally be avoided. Signed-off-by: Rusty Russell --- include/linux/cpumask.h | 31 ++++++++++++------------------- 1 file changed, 12 insertions(+), 19 deletions(-) (limited to 'include') diff --git a/include/linux/cpumask.h b/include/linux/cpumask.h index 086549a665e2..dc037ae6f4f2 100644 --- a/include/linux/cpumask.h +++ b/include/linux/cpumask.h @@ -768,7 +768,7 @@ static inline const struct cpumask *get_cpu_mask(unsigned int cpu) #if NR_CPUS <= BITS_PER_LONG #define CPU_BITS_ALL \ { \ - [BITS_TO_LONGS(NR_CPUS)-1] = CPU_MASK_LAST_WORD \ + [BITS_TO_LONGS(NR_CPUS)-1] = BITMAP_LAST_WORD_MASK(NR_CPUS) \ } #else /* NR_CPUS > BITS_PER_LONG */ @@ -776,7 +776,7 @@ static inline const struct cpumask *get_cpu_mask(unsigned int cpu) #define CPU_BITS_ALL \ { \ [0 ... BITS_TO_LONGS(NR_CPUS)-2] = ~0UL, \ - [BITS_TO_LONGS(NR_CPUS)-1] = CPU_MASK_LAST_WORD \ + [BITS_TO_LONGS(NR_CPUS)-1] = BITMAP_LAST_WORD_MASK(NR_CPUS) \ } #endif /* NR_CPUS > BITS_PER_LONG */ @@ -797,38 +797,31 @@ cpumap_print_to_pagebuf(bool list, char *buf, const struct cpumask *mask) nr_cpu_ids); } -/* - * - * From here down, all obsolete. Use cpumask_ variants! - * - */ -#ifndef CONFIG_DISABLE_OBSOLETE_CPUMASK_FUNCTIONS -#define cpumask_of_cpu(cpu) (*get_cpu_mask(cpu)) - -#define CPU_MASK_LAST_WORD BITMAP_LAST_WORD_MASK(NR_CPUS) - #if NR_CPUS <= BITS_PER_LONG - #define CPU_MASK_ALL \ (cpumask_t) { { \ - [BITS_TO_LONGS(NR_CPUS)-1] = CPU_MASK_LAST_WORD \ + [BITS_TO_LONGS(NR_CPUS)-1] = BITMAP_LAST_WORD_MASK(NR_CPUS) \ } } - #else - #define CPU_MASK_ALL \ (cpumask_t) { { \ [0 ... BITS_TO_LONGS(NR_CPUS)-2] = ~0UL, \ - [BITS_TO_LONGS(NR_CPUS)-1] = CPU_MASK_LAST_WORD \ + [BITS_TO_LONGS(NR_CPUS)-1] = BITMAP_LAST_WORD_MASK(NR_CPUS) \ } } - -#endif +#endif /* NR_CPUS > BITS_PER_LONG */ #define CPU_MASK_NONE \ (cpumask_t) { { \ [0 ... BITS_TO_LONGS(NR_CPUS)-1] = 0UL \ } } +/* + * + * From here down, all obsolete. Use cpumask_ variants! + * + */ +#ifndef CONFIG_DISABLE_OBSOLETE_CPUMASK_FUNCTIONS + #define CPU_MASK_CPU0 \ (cpumask_t) { { \ [0] = 1UL \ -- cgit v1.2.3 From 82f8b651a94d5c7090563fe55cfdb286c461a16c Mon Sep 17 00:00:00 2001 From: Jakub Pawlowski Date: Wed, 4 Mar 2015 16:24:26 -0800 Subject: Bluetooth: fix service discovery behaviour for empty uuids filter This patch fixes service discovery behaviour, when provided uuid filter is empty and HCI_QUIRK_STRICT_DUPLICATE_FILTER is set. Before this patch, empty uuid filter was unable to trigger scan restart, and that caused inconsistent behaviour in applications. Example: two DBus clients call BlueZ, one to find all devices with service abcd, second to find all devices with rssi smaller than -90. Sum of those filters, that is passed to mgmt_service_scan is empty filter, with no rssi or uuids set. That caused kernel not to restart scan when quirk was set. That was inconsistent with what happen when there's only one of those two filters set (scan is restarted and reports devices). To fix that, new variable hdev->discovery.result_filtering was introduced. It can indicate that filtered scan is running, no matter what uuid or rssi filter is set. Signed-off-by: Jakub Pawlowski Signed-off-by: Johan Hedberg --- include/net/bluetooth/hci_core.h | 2 ++ net/bluetooth/mgmt.c | 7 +++---- 2 files changed, 5 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index acec9140c3f9..15c761c1f82a 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -76,6 +76,7 @@ struct discovery_state { u8 last_adv_data[HCI_MAX_AD_LENGTH]; u8 last_adv_data_len; bool report_invalid_rssi; + bool result_filtering; s8 rssi; u16 uuid_count; u8 (*uuids)[16]; @@ -525,6 +526,7 @@ static inline void discovery_init(struct hci_dev *hdev) static inline void hci_discovery_filter_clear(struct hci_dev *hdev) { + hdev->discovery.result_filtering = false; hdev->discovery.report_invalid_rssi = true; hdev->discovery.rssi = HCI_RSSI_INVALID; hdev->discovery.uuid_count = 0; diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index bc09c5a37032..967f07fdbbbe 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -3933,8 +3933,7 @@ static void start_discovery_complete(struct hci_dev *hdev, u8 status, */ if (test_bit(HCI_QUIRK_STRICT_DUPLICATE_FILTER, &hdev->quirks) && - (hdev->discovery.uuid_count > 0 || - hdev->discovery.rssi != HCI_RSSI_INVALID)) { + hdev->discovery.result_filtering) { hdev->discovery.scan_start = jiffies; hdev->discovery.scan_duration = timeout; } @@ -4087,6 +4086,7 @@ static int start_service_discovery(struct sock *sk, struct hci_dev *hdev, */ hci_discovery_filter_clear(hdev); + hdev->discovery.result_filtering = true; hdev->discovery.type = cp->type; hdev->discovery.rssi = cp->rssi; hdev->discovery.uuid_count = uuid_count; @@ -7344,8 +7344,7 @@ void mgmt_device_found(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type, return; } - if (hdev->discovery.rssi != HCI_RSSI_INVALID || - hdev->discovery.uuid_count > 0) { + if (hdev->discovery.result_filtering) { /* We are using service discovery */ if (!is_filter_match(hdev, rssi, eir, eir_len, scan_rsp, scan_rsp_len)) -- cgit v1.2.3 From 668585273246f67b0cdafa30dd2da2047a2e1290 Mon Sep 17 00:00:00 2001 From: Rojhalat Ibrahim Date: Wed, 11 Feb 2015 17:27:58 +0100 Subject: gpiolib: add gpiod_get_array and gpiod_put_array functions Introduce new functions for conveniently obtaining and disposing of an entire array of GPIOs with one function call. ACPI parts tested by Mika Westerberg, DT parts tested by Rojhalat Ibrahim. Change log: v5: move the ACPI functions to gpiolib-acpi.c v4: - use shorter names for members of struct gpio_descs - rename lut_gpio_count to platform_gpio_count for clarity - add check for successful memory allocation - use ERR_CAST() v3: - rebase on current linux-gpio devel branch - fix ACPI GPIO counting - allow for zero-sized arrays - make the flags argument mandatory for the new functions - clarify documentation v2: change interface Suggested-by: Alexandre Courbot Signed-off-by: Rojhalat Ibrahim Reviewed-by: Alexandre Courbot Reviewed-by: Mika Westerberg Tested-by: Mika Westerberg Tested-by: Rojhalat Ibrahim Signed-off-by: Linus Walleij --- Documentation/gpio/consumer.txt | 33 ++++++++- drivers/gpio/gpiolib-acpi.c | 84 +++++++++++++++++++++++ drivers/gpio/gpiolib.c | 145 ++++++++++++++++++++++++++++++++++++++++ drivers/gpio/gpiolib.h | 7 ++ include/linux/gpio/consumer.h | 46 +++++++++++++ 5 files changed, 312 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/Documentation/gpio/consumer.txt b/Documentation/gpio/consumer.txt index d85fbae451ea..2924f2ffcd91 100644 --- a/Documentation/gpio/consumer.txt +++ b/Documentation/gpio/consumer.txt @@ -58,7 +58,6 @@ pattern where a GPIO is optional, the gpiod_get_optional() and gpiod_get_index_optional() functions can be used. These functions return NULL instead of -ENOENT if no GPIO has been assigned to the requested function: - struct gpio_desc *gpiod_get_optional(struct device *dev, const char *con_id, enum gpiod_flags flags) @@ -68,6 +67,27 @@ instead of -ENOENT if no GPIO has been assigned to the requested function: unsigned int index, enum gpiod_flags flags) +For a function using multiple GPIOs all of those can be obtained with one call: + + struct gpio_descs *gpiod_get_array(struct device *dev, + const char *con_id, + enum gpiod_flags flags) + +This function returns a struct gpio_descs which contains an array of +descriptors: + + struct gpio_descs { + unsigned int ndescs; + struct gpio_desc *desc[]; + } + +The following function returns NULL instead of -ENOENT if no GPIOs have been +assigned to the requested function: + + struct gpio_descs *gpiod_get_array_optional(struct device *dev, + const char *con_id, + enum gpiod_flags flags) + Device-managed variants of these functions are also defined: struct gpio_desc *devm_gpiod_get(struct device *dev, const char *con_id, @@ -91,8 +111,15 @@ A GPIO descriptor can be disposed of using the gpiod_put() function: void gpiod_put(struct gpio_desc *desc) -It is strictly forbidden to use a descriptor after calling this function. The -device-managed variant is, unsurprisingly: +For an array of GPIOs this function can be used: + + void gpiod_put_array(struct gpio_descs *descs) + +It is strictly forbidden to use a descriptor after calling these functions. +It is also not allowed to individually release descriptors (using gpiod_put()) +from an array acquired with gpiod_get_array(). + +The device-managed variant is, unsurprisingly: void devm_gpiod_put(struct device *dev, struct gpio_desc *desc) diff --git a/drivers/gpio/gpiolib-acpi.c b/drivers/gpio/gpiolib-acpi.c index c0929d938ced..c4919966453d 100644 --- a/drivers/gpio/gpiolib-acpi.c +++ b/drivers/gpio/gpiolib-acpi.c @@ -712,3 +712,87 @@ void acpi_gpiochip_remove(struct gpio_chip *chip) acpi_detach_data(handle, acpi_gpio_chip_dh); kfree(acpi_gpio); } + +static unsigned int acpi_gpio_package_count(const union acpi_object *obj) +{ + const union acpi_object *element = obj->package.elements; + const union acpi_object *end = element + obj->package.count; + unsigned int count = 0; + + while (element < end) { + if (element->type == ACPI_TYPE_LOCAL_REFERENCE) + count++; + + element++; + } + return count; +} + +static int acpi_find_gpio_count(struct acpi_resource *ares, void *data) +{ + unsigned int *count = data; + + if (ares->type == ACPI_RESOURCE_TYPE_GPIO) + *count += ares->data.gpio.pin_table_length; + + return 1; +} + +/** + * acpi_gpio_count - return the number of GPIOs associated with a + * device / function or -ENOENT if no GPIO has been + * assigned to the requested function. + * @dev: GPIO consumer, can be NULL for system-global GPIOs + * @con_id: function within the GPIO consumer + */ +int acpi_gpio_count(struct device *dev, const char *con_id) +{ + struct acpi_device *adev = ACPI_COMPANION(dev); + const union acpi_object *obj; + const struct acpi_gpio_mapping *gm; + int count = -ENOENT; + int ret; + char propname[32]; + unsigned int i; + + /* Try first from _DSD */ + for (i = 0; i < ARRAY_SIZE(gpio_suffixes); i++) { + if (con_id && strcmp(con_id, "gpios")) + snprintf(propname, sizeof(propname), "%s-%s", + con_id, gpio_suffixes[i]); + else + snprintf(propname, sizeof(propname), "%s", + gpio_suffixes[i]); + + ret = acpi_dev_get_property(adev, propname, ACPI_TYPE_ANY, + &obj); + if (ret == 0) { + if (obj->type == ACPI_TYPE_LOCAL_REFERENCE) + count = 1; + else if (obj->type == ACPI_TYPE_PACKAGE) + count = acpi_gpio_package_count(obj); + } else if (adev->driver_gpios) { + for (gm = adev->driver_gpios; gm->name; gm++) + if (strcmp(propname, gm->name) == 0) { + count = gm->size; + break; + } + } + if (count >= 0) + break; + } + + /* Then from plain _CRS GPIOs */ + if (count < 0) { + struct list_head resource_list; + unsigned int crs_count = 0; + + INIT_LIST_HEAD(&resource_list); + acpi_dev_get_resources(adev, &resource_list, + acpi_find_gpio_count, &crs_count); + acpi_dev_free_resource_list(&resource_list); + if (crs_count > 0) + count = crs_count; + } + return count; +} diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 2f8296c021e5..3d5b85a7dcdf 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -1806,6 +1806,70 @@ static struct gpio_desc *gpiod_find(struct device *dev, const char *con_id, return desc; } +static int dt_gpio_count(struct device *dev, const char *con_id) +{ + int ret; + char propname[32]; + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(gpio_suffixes); i++) { + if (con_id) + snprintf(propname, sizeof(propname), "%s-%s", + con_id, gpio_suffixes[i]); + else + snprintf(propname, sizeof(propname), "%s", + gpio_suffixes[i]); + + ret = of_gpio_named_count(dev->of_node, propname); + if (ret >= 0) + break; + } + return ret; +} + +static int platform_gpio_count(struct device *dev, const char *con_id) +{ + struct gpiod_lookup_table *table; + struct gpiod_lookup *p; + unsigned int count = 0; + + table = gpiod_find_lookup_table(dev); + if (!table) + return -ENOENT; + + for (p = &table->table[0]; p->chip_label; p++) { + if ((con_id && p->con_id && !strcmp(con_id, p->con_id)) || + (!con_id && !p->con_id)) + count++; + } + if (!count) + return -ENOENT; + + return count; +} + +/** + * gpiod_count - return the number of GPIOs associated with a device / function + * or -ENOENT if no GPIO has been assigned to the requested function + * @dev: GPIO consumer, can be NULL for system-global GPIOs + * @con_id: function within the GPIO consumer + */ +int gpiod_count(struct device *dev, const char *con_id) +{ + int count = -ENOENT; + + if (IS_ENABLED(CONFIG_OF) && dev && dev->of_node) + count = dt_gpio_count(dev, con_id); + else if (IS_ENABLED(CONFIG_ACPI) && dev && ACPI_HANDLE(dev)) + count = acpi_gpio_count(dev, con_id); + + if (count < 0) + count = platform_gpio_count(dev, con_id); + + return count; +} +EXPORT_SYMBOL_GPL(gpiod_count); + /** * gpiod_get - obtain a GPIO for a given GPIO function * @dev: GPIO consumer, can be NULL for system-global GPIOs @@ -2089,6 +2153,72 @@ static void gpiochip_free_hogs(struct gpio_chip *chip) } } +/** + * gpiod_get_array - obtain multiple GPIOs from a multi-index GPIO function + * @dev: GPIO consumer, can be NULL for system-global GPIOs + * @con_id: function within the GPIO consumer + * @flags: optional GPIO initialization flags + * + * This function acquires all the GPIOs defined under a given function. + * + * Return a struct gpio_descs containing an array of descriptors, -ENOENT if + * no GPIO has been assigned to the requested function, or another IS_ERR() + * code if an error occurred while trying to acquire the GPIOs. + */ +struct gpio_descs *__must_check gpiod_get_array(struct device *dev, + const char *con_id, + enum gpiod_flags flags) +{ + struct gpio_desc *desc; + struct gpio_descs *descs; + int count; + + count = gpiod_count(dev, con_id); + if (count < 0) + return ERR_PTR(count); + + descs = kzalloc(sizeof(*descs) + sizeof(descs->desc[0]) * count, + GFP_KERNEL); + if (!descs) + return ERR_PTR(-ENOMEM); + + for (descs->ndescs = 0; descs->ndescs < count; ) { + desc = gpiod_get_index(dev, con_id, descs->ndescs, flags); + if (IS_ERR(desc)) { + gpiod_put_array(descs); + return ERR_CAST(desc); + } + descs->desc[descs->ndescs] = desc; + descs->ndescs++; + } + return descs; +} +EXPORT_SYMBOL_GPL(gpiod_get_array); + +/** + * gpiod_get_array_optional - obtain multiple GPIOs from a multi-index GPIO + * function + * @dev: GPIO consumer, can be NULL for system-global GPIOs + * @con_id: function within the GPIO consumer + * @flags: optional GPIO initialization flags + * + * This is equivalent to gpiod_get_array(), except that when no GPIO was + * assigned to the requested function it will return NULL. + */ +struct gpio_descs *__must_check gpiod_get_array_optional(struct device *dev, + const char *con_id, + enum gpiod_flags flags) +{ + struct gpio_descs *descs; + + descs = gpiod_get_array(dev, con_id, flags); + if (IS_ERR(descs) && (PTR_ERR(descs) == -ENOENT)) + return NULL; + + return descs; +} +EXPORT_SYMBOL_GPL(gpiod_get_array_optional); + /** * gpiod_put - dispose of a GPIO descriptor * @desc: GPIO descriptor to dispose of @@ -2101,6 +2231,21 @@ void gpiod_put(struct gpio_desc *desc) } EXPORT_SYMBOL_GPL(gpiod_put); +/** + * gpiod_put_array - dispose of multiple GPIO descriptors + * @descs: struct gpio_descs containing an array of descriptors + */ +void gpiod_put_array(struct gpio_descs *descs) +{ + unsigned int i; + + for (i = 0; i < descs->ndescs; i++) + gpiod_put(descs->desc[i]); + + kfree(descs); +} +EXPORT_SYMBOL_GPL(gpiod_put_array); + #ifdef CONFIG_DEBUG_FS static void gpiolib_dbg_show(struct seq_file *s, struct gpio_chip *chip) diff --git a/drivers/gpio/gpiolib.h b/drivers/gpio/gpiolib.h index 205dd12659c0..54bc5ec91398 100644 --- a/drivers/gpio/gpiolib.h +++ b/drivers/gpio/gpiolib.h @@ -40,6 +40,8 @@ void acpi_gpiochip_free_interrupts(struct gpio_chip *chip); struct gpio_desc *acpi_get_gpiod_by_index(struct acpi_device *adev, const char *propname, int index, struct acpi_gpio_info *info); + +int acpi_gpio_count(struct device *dev, const char *con_id); #else static inline void acpi_gpiochip_add(struct gpio_chip *chip) { } static inline void acpi_gpiochip_remove(struct gpio_chip *chip) { } @@ -56,6 +58,11 @@ acpi_get_gpiod_by_index(struct acpi_device *adev, const char *propname, { return ERR_PTR(-ENOSYS); } + +static inline int acpi_gpio_count(struct device *dev, const char *con_id) +{ + return -ENODEV; +} #endif struct gpio_desc *of_get_named_gpiod_flags(struct device_node *np, diff --git a/include/linux/gpio/consumer.h b/include/linux/gpio/consumer.h index ed20759229eb..33eb52fd0932 100644 --- a/include/linux/gpio/consumer.h +++ b/include/linux/gpio/consumer.h @@ -16,6 +16,15 @@ struct device; */ struct gpio_desc; +/** + * Struct containing an array of descriptors that can be obtained using + * gpiod_get_array(). + */ +struct gpio_descs { + unsigned int ndescs; + struct gpio_desc *desc[]; +}; + #define GPIOD_FLAGS_BIT_DIR_SET BIT(0) #define GPIOD_FLAGS_BIT_DIR_OUT BIT(1) #define GPIOD_FLAGS_BIT_DIR_VAL BIT(2) @@ -34,6 +43,9 @@ enum gpiod_flags { #ifdef CONFIG_GPIOLIB +/* Return the number of GPIOs associated with a device / function */ +int gpiod_count(struct device *dev, const char *con_id); + /* Acquire and dispose GPIOs */ struct gpio_desc *__must_check __gpiod_get(struct device *dev, const char *con_id, @@ -49,7 +61,14 @@ struct gpio_desc *__must_check __gpiod_get_index_optional(struct device *dev, const char *con_id, unsigned int index, enum gpiod_flags flags); +struct gpio_descs *__must_check gpiod_get_array(struct device *dev, + const char *con_id, + enum gpiod_flags flags); +struct gpio_descs *__must_check gpiod_get_array_optional(struct device *dev, + const char *con_id, + enum gpiod_flags flags); void gpiod_put(struct gpio_desc *desc); +void gpiod_put_array(struct gpio_descs *descs); struct gpio_desc *__must_check __devm_gpiod_get(struct device *dev, const char *con_id, @@ -114,6 +133,11 @@ struct gpio_desc *devm_get_gpiod_from_child(struct device *dev, struct fwnode_handle *child); #else /* CONFIG_GPIOLIB */ +static inline int gpiod_count(struct device *dev, const char *con_id) +{ + return 0; +} + static inline struct gpio_desc *__must_check __gpiod_get(struct device *dev, const char *con_id, enum gpiod_flags flags) @@ -143,6 +167,20 @@ __gpiod_get_index_optional(struct device *dev, const char *con_id, return ERR_PTR(-ENOSYS); } +static inline struct gpio_descs *__must_check +gpiod_get_array(struct device *dev, const char *con_id, + enum gpiod_flags flags) +{ + return ERR_PTR(-ENOSYS); +} + +static inline struct gpio_descs *__must_check +gpiod_get_array_optional(struct device *dev, const char *con_id, + enum gpiod_flags flags) +{ + return ERR_PTR(-ENOSYS); +} + static inline void gpiod_put(struct gpio_desc *desc) { might_sleep(); @@ -151,6 +189,14 @@ static inline void gpiod_put(struct gpio_desc *desc) WARN_ON(1); } +static inline void gpiod_put_array(struct gpio_descs *descs) +{ + might_sleep(); + + /* GPIO can never have been requested */ + WARN_ON(1); +} + static inline struct gpio_desc *__must_check __devm_gpiod_get(struct device *dev, const char *con_id, -- cgit v1.2.3 From 331758eef83620eef3f21289f0f44aba094d0503 Mon Sep 17 00:00:00 2001 From: Rojhalat Ibrahim Date: Wed, 11 Feb 2015 17:28:02 +0100 Subject: gpiolib: add devm_gpiod_get_array and devm_gpiod_put_array functions Add device managed variants of gpiod_get_array() / gpiod_put_array() functions for conveniently obtaining and disposing of an entire array of GPIOs with one function call. Signed-off-by: Rojhalat Ibrahim Reviewed-by: Alexandre Courbot Signed-off-by: Linus Walleij --- Documentation/gpio/consumer.txt | 14 ++++++- drivers/gpio/devres.c | 89 +++++++++++++++++++++++++++++++++++++++++ include/linux/gpio/consumer.h | 30 ++++++++++++++ 3 files changed, 131 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/Documentation/gpio/consumer.txt b/Documentation/gpio/consumer.txt index 2924f2ffcd91..d29a9725c9e5 100644 --- a/Documentation/gpio/consumer.txt +++ b/Documentation/gpio/consumer.txt @@ -102,11 +102,19 @@ Device-managed variants of these functions are also defined: const char *con_id, enum gpiod_flags flags) - struct gpio_desc * devm_gpiod_get_index_optional(struct device *dev, + struct gpio_desc *devm_gpiod_get_index_optional(struct device *dev, const char *con_id, unsigned int index, enum gpiod_flags flags) + struct gpio_descs *devm_gpiod_get_array(struct device *dev, + const char *con_id, + enum gpiod_flags flags) + + struct gpio_descs *devm_gpiod_get_array_optional(struct device *dev, + const char *con_id, + enum gpiod_flags flags) + A GPIO descriptor can be disposed of using the gpiod_put() function: void gpiod_put(struct gpio_desc *desc) @@ -119,10 +127,12 @@ It is strictly forbidden to use a descriptor after calling these functions. It is also not allowed to individually release descriptors (using gpiod_put()) from an array acquired with gpiod_get_array(). -The device-managed variant is, unsurprisingly: +The device-managed variants are, unsurprisingly: void devm_gpiod_put(struct device *dev, struct gpio_desc *desc) + void devm_gpiod_put_array(struct device *dev, struct gpio_descs *descs) + Using GPIOs =========== diff --git a/drivers/gpio/devres.c b/drivers/gpio/devres.c index 12c2082f968e..ec24da2418b3 100644 --- a/drivers/gpio/devres.c +++ b/drivers/gpio/devres.c @@ -35,6 +35,20 @@ static int devm_gpiod_match(struct device *dev, void *res, void *data) return *this == *gpio; } +static void devm_gpiod_release_array(struct device *dev, void *res) +{ + struct gpio_descs **descs = res; + + gpiod_put_array(*descs); +} + +static int devm_gpiod_match_array(struct device *dev, void *res, void *data) +{ + struct gpio_descs **this = res, **gpios = data; + + return *this == *gpios; +} + /** * devm_gpiod_get - Resource-managed gpiod_get() * @dev: GPIO consumer @@ -185,6 +199,66 @@ struct gpio_desc *__must_check __devm_gpiod_get_index_optional(struct device *de } EXPORT_SYMBOL(__devm_gpiod_get_index_optional); +/** + * devm_gpiod_get_array - Resource-managed gpiod_get_array() + * @dev: GPIO consumer + * @con_id: function within the GPIO consumer + * @flags: optional GPIO initialization flags + * + * Managed gpiod_get_array(). GPIO descriptors returned from this function are + * automatically disposed on driver detach. See gpiod_get_array() for detailed + * information about behavior and return values. + */ +struct gpio_descs *__must_check devm_gpiod_get_array(struct device *dev, + const char *con_id, + enum gpiod_flags flags) +{ + struct gpio_descs **dr; + struct gpio_descs *descs; + + dr = devres_alloc(devm_gpiod_release_array, + sizeof(struct gpio_descs *), GFP_KERNEL); + if (!dr) + return ERR_PTR(-ENOMEM); + + descs = gpiod_get_array(dev, con_id, flags); + if (IS_ERR(descs)) { + devres_free(dr); + return descs; + } + + *dr = descs; + devres_add(dev, dr); + + return descs; +} +EXPORT_SYMBOL(devm_gpiod_get_array); + +/** + * devm_gpiod_get_array_optional - Resource-managed gpiod_get_array_optional() + * @dev: GPIO consumer + * @con_id: function within the GPIO consumer + * @flags: optional GPIO initialization flags + * + * Managed gpiod_get_array_optional(). GPIO descriptors returned from this + * function are automatically disposed on driver detach. + * See gpiod_get_array_optional() for detailed information about behavior and + * return values. + */ +struct gpio_descs *__must_check +devm_gpiod_get_array_optional(struct device *dev, const char *con_id, + enum gpiod_flags flags) +{ + struct gpio_descs *descs; + + descs = devm_gpiod_get_array(dev, con_id, flags); + if (IS_ERR(descs) && (PTR_ERR(descs) == -ENOENT)) + return NULL; + + return descs; +} +EXPORT_SYMBOL(devm_gpiod_get_array_optional); + /** * devm_gpiod_put - Resource-managed gpiod_put() * @desc: GPIO descriptor to dispose of @@ -200,6 +274,21 @@ void devm_gpiod_put(struct device *dev, struct gpio_desc *desc) } EXPORT_SYMBOL(devm_gpiod_put); +/** + * devm_gpiod_put_array - Resource-managed gpiod_put_array() + * @descs: GPIO descriptor array to dispose of + * + * Dispose of an array of GPIO descriptors obtained with devm_gpiod_get_array(). + * Normally this function will not be called as the GPIOs will be disposed of + * by the resource management code. + */ +void devm_gpiod_put_array(struct device *dev, struct gpio_descs *descs) +{ + WARN_ON(devres_release(dev, devm_gpiod_release_array, + devm_gpiod_match_array, &descs)); +} +EXPORT_SYMBOL(devm_gpiod_put_array); + diff --git a/include/linux/gpio/consumer.h b/include/linux/gpio/consumer.h index 33eb52fd0932..3a7c9ffd5ab9 100644 --- a/include/linux/gpio/consumer.h +++ b/include/linux/gpio/consumer.h @@ -83,7 +83,14 @@ struct gpio_desc *__must_check __devm_gpiod_get_optional(struct device *dev, struct gpio_desc *__must_check __devm_gpiod_get_index_optional(struct device *dev, const char *con_id, unsigned int index, enum gpiod_flags flags); +struct gpio_descs *__must_check devm_gpiod_get_array(struct device *dev, + const char *con_id, + enum gpiod_flags flags); +struct gpio_descs *__must_check +devm_gpiod_get_array_optional(struct device *dev, const char *con_id, + enum gpiod_flags flags); void devm_gpiod_put(struct device *dev, struct gpio_desc *desc); +void devm_gpiod_put_array(struct device *dev, struct gpio_descs *descs); int gpiod_get_direction(struct gpio_desc *desc); int gpiod_direction_input(struct gpio_desc *desc); @@ -228,6 +235,20 @@ __devm_gpiod_get_index_optional(struct device *dev, const char *con_id, return ERR_PTR(-ENOSYS); } +static inline struct gpio_descs *__must_check +devm_gpiod_get_array(struct device *dev, const char *con_id, + enum gpiod_flags flags) +{ + return ERR_PTR(-ENOSYS); +} + +static inline struct gpio_descs *__must_check +devm_gpiod_get_array_optional(struct device *dev, const char *con_id, + enum gpiod_flags flags) +{ + return ERR_PTR(-ENOSYS); +} + static inline void devm_gpiod_put(struct device *dev, struct gpio_desc *desc) { might_sleep(); @@ -236,6 +257,15 @@ static inline void devm_gpiod_put(struct device *dev, struct gpio_desc *desc) WARN_ON(1); } +static inline void devm_gpiod_put_array(struct device *dev, + struct gpio_descs *descs) +{ + might_sleep(); + + /* GPIO can never have been requested */ + WARN_ON(1); +} + static inline int gpiod_get_direction(const struct gpio_desc *desc) { -- cgit v1.2.3 From c32ec2a11321978c34296d9a6bd5b0c31a2eb182 Mon Sep 17 00:00:00 2001 From: Rafał Miłecki Date: Wed, 4 Mar 2015 12:14:41 +0100 Subject: bcma: make bcma_host_pci_(up|down) calls safe for every config MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We were providing declarations but actual code was compiled only with CONFIG_BCMA_HOST_PCI set. This could result in: ERROR: "bcma_host_pci_down" [drivers/net/wireless/brcm80211/brcmsmac/brcmsmac.ko] undefined! ERROR: "bcma_host_pci_up" [drivers/net/wireless/brcm80211/brcmsmac/brcmsmac.ko] undefined! ERROR: "bcma_host_pci_down" [drivers/net/wireless/b43/b43.ko] undefined! ERROR: "bcma_host_pci_up" [drivers/net/wireless/b43/b43.ko] undefined! Reported-by: Arnd Bergmann Signed-off-by: Rafał Miłecki Signed-off-by: Kalle Valo --- include/linux/bcma/bcma.h | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'include') diff --git a/include/linux/bcma/bcma.h b/include/linux/bcma/bcma.h index 037620b3f113..44057b45ed32 100644 --- a/include/linux/bcma/bcma.h +++ b/include/linux/bcma/bcma.h @@ -434,8 +434,17 @@ static inline struct bcma_device *bcma_find_core(struct bcma_bus *bus, return bcma_find_core_unit(bus, coreid, 0); } +#ifdef CONFIG_BCMA_HOST_PCI extern void bcma_host_pci_up(struct bcma_bus *bus); extern void bcma_host_pci_down(struct bcma_bus *bus); +#else +static inline void bcma_host_pci_up(struct bcma_bus *bus) +{ +} +static inline void bcma_host_pci_down(struct bcma_bus *bus) +{ +} +#endif extern bool bcma_core_is_enabled(struct bcma_device *core); extern void bcma_core_disable(struct bcma_device *core, u32 flags); -- cgit v1.2.3 From 0a4e699a41f767dff76ca7dc1019b9ca6de3eb42 Mon Sep 17 00:00:00 2001 From: Rafał Miłecki Date: Wed, 4 Mar 2015 14:24:52 +0100 Subject: bcma: move internal function declarations to private header MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit These functions are not exported nor used anywhere, so there is no reason to put them in public headers. Also drop unused bcma_chipco_(suspend|resume). Signed-off-by: Rafał Miłecki Signed-off-by: Kalle Valo --- drivers/bcma/bcma_private.h | 41 +++++++++++++++++++++++++++++ include/linux/bcma/bcma_driver_chipcommon.h | 11 -------- include/linux/bcma/bcma_driver_gmac_cmn.h | 6 ----- include/linux/bcma/bcma_driver_mips.h | 15 ----------- include/linux/bcma/bcma_driver_pci.h | 2 -- include/linux/bcma/bcma_driver_pcie2.h | 2 -- 6 files changed, 41 insertions(+), 36 deletions(-) (limited to 'include') diff --git a/drivers/bcma/bcma_private.h b/drivers/bcma/bcma_private.h index 29565e30700a..5a1d22489afc 100644 --- a/drivers/bcma/bcma_private.h +++ b/drivers/bcma/bcma_private.h @@ -43,6 +43,9 @@ int bcma_bus_scan(struct bcma_bus *bus); int bcma_sprom_get(struct bcma_bus *bus); /* driver_chipcommon.c */ +void bcma_core_chipcommon_early_init(struct bcma_drv_cc *cc); +void bcma_core_chipcommon_init(struct bcma_drv_cc *cc); +void bcma_chipco_bcm4331_ext_pa_lines_ctl(struct bcma_drv_cc *cc, bool enable); #ifdef CONFIG_BCMA_DRIVER_MIPS void bcma_chipco_serial_init(struct bcma_drv_cc *cc); extern struct platform_device bcma_pflash_dev; @@ -53,6 +56,8 @@ int bcma_core_chipcommon_b_init(struct bcma_drv_cc_b *ccb); void bcma_core_chipcommon_b_free(struct bcma_drv_cc_b *ccb); /* driver_chipcommon_pmu.c */ +void bcma_pmu_early_init(struct bcma_drv_cc *cc); +void bcma_pmu_init(struct bcma_drv_cc *cc); u32 bcma_pmu_get_alp_clock(struct bcma_drv_cc *cc); u32 bcma_pmu_get_cpu_clock(struct bcma_drv_cc *cc); @@ -102,10 +107,13 @@ static inline void __exit bcma_host_soc_unregister_driver(void) /* driver_pci.c */ u32 bcma_pcie_read(struct bcma_drv_pci *pc, u32 address); +void bcma_core_pci_early_init(struct bcma_drv_pci *pc); +void bcma_core_pci_init(struct bcma_drv_pci *pc); void bcma_core_pci_up(struct bcma_drv_pci *pc); void bcma_core_pci_down(struct bcma_drv_pci *pc); /* driver_pcie2.c */ +void bcma_core_pcie2_init(struct bcma_drv_pcie2 *pcie2); void bcma_core_pcie2_up(struct bcma_drv_pcie2 *pcie2); extern int bcma_chipco_watchdog_register(struct bcma_drv_cc *cc); @@ -123,6 +131,39 @@ static inline void bcma_core_pci_hostmode_init(struct bcma_drv_pci *pc) } #endif /* CONFIG_BCMA_DRIVER_PCI_HOSTMODE */ +/************************************************** + * driver_mips.c + **************************************************/ + +#ifdef CONFIG_BCMA_DRIVER_MIPS +unsigned int bcma_core_mips_irq(struct bcma_device *dev); +void bcma_core_mips_early_init(struct bcma_drv_mips *mcore); +void bcma_core_mips_init(struct bcma_drv_mips *mcore); +#else +static inline unsigned int bcma_core_mips_irq(struct bcma_device *dev) +{ + return 0; +} +static inline void bcma_core_mips_early_init(struct bcma_drv_mips *mcore) +{ +} +static inline void bcma_core_mips_init(struct bcma_drv_mips *mcore) +{ +} +#endif + +/************************************************** + * driver_gmac_cmn.c + **************************************************/ + +#ifdef CONFIG_BCMA_DRIVER_GMAC_CMN +void bcma_core_gmac_cmn_init(struct bcma_drv_gmac_cmn *gc); +#else +static inline void bcma_core_gmac_cmn_init(struct bcma_drv_gmac_cmn *gc) +{ +} +#endif + #ifdef CONFIG_BCMA_DRIVER_GPIO /* driver_gpio.c */ int bcma_gpio_init(struct bcma_drv_cc *cc); diff --git a/include/linux/bcma/bcma_driver_chipcommon.h b/include/linux/bcma/bcma_driver_chipcommon.h index db6fa217f98b..6cceedf65ca2 100644 --- a/include/linux/bcma/bcma_driver_chipcommon.h +++ b/include/linux/bcma/bcma_driver_chipcommon.h @@ -663,14 +663,6 @@ struct bcma_drv_cc_b { #define bcma_cc_maskset32(cc, offset, mask, set) \ bcma_cc_write32(cc, offset, (bcma_cc_read32(cc, offset) & (mask)) | (set)) -extern void bcma_core_chipcommon_init(struct bcma_drv_cc *cc); -extern void bcma_core_chipcommon_early_init(struct bcma_drv_cc *cc); - -extern void bcma_chipco_suspend(struct bcma_drv_cc *cc); -extern void bcma_chipco_resume(struct bcma_drv_cc *cc); - -void bcma_chipco_bcm4331_ext_pa_lines_ctl(struct bcma_drv_cc *cc, bool enable); - extern u32 bcma_chipco_watchdog_timer_set(struct bcma_drv_cc *cc, u32 ticks); extern u32 bcma_chipco_get_alp_clock(struct bcma_drv_cc *cc); @@ -690,9 +682,6 @@ u32 bcma_chipco_gpio_pullup(struct bcma_drv_cc *cc, u32 mask, u32 value); u32 bcma_chipco_gpio_pulldown(struct bcma_drv_cc *cc, u32 mask, u32 value); /* PMU support */ -extern void bcma_pmu_init(struct bcma_drv_cc *cc); -extern void bcma_pmu_early_init(struct bcma_drv_cc *cc); - extern void bcma_chipco_pll_write(struct bcma_drv_cc *cc, u32 offset, u32 value); extern void bcma_chipco_pll_maskset(struct bcma_drv_cc *cc, u32 offset, diff --git a/include/linux/bcma/bcma_driver_gmac_cmn.h b/include/linux/bcma/bcma_driver_gmac_cmn.h index 4dd1f33e36a2..4354d4ea6713 100644 --- a/include/linux/bcma/bcma_driver_gmac_cmn.h +++ b/include/linux/bcma/bcma_driver_gmac_cmn.h @@ -91,10 +91,4 @@ struct bcma_drv_gmac_cmn { #define gmac_cmn_write16(gc, offset, val) bcma_write16((gc)->core, offset, val) #define gmac_cmn_write32(gc, offset, val) bcma_write32((gc)->core, offset, val) -#ifdef CONFIG_BCMA_DRIVER_GMAC_CMN -extern void bcma_core_gmac_cmn_init(struct bcma_drv_gmac_cmn *gc); -#else -static inline void bcma_core_gmac_cmn_init(struct bcma_drv_gmac_cmn *gc) { } -#endif - #endif /* LINUX_BCMA_DRIVER_GMAC_CMN_H_ */ diff --git a/include/linux/bcma/bcma_driver_mips.h b/include/linux/bcma/bcma_driver_mips.h index 0b3b32aeeb8a..8eea7f9e33b4 100644 --- a/include/linux/bcma/bcma_driver_mips.h +++ b/include/linux/bcma/bcma_driver_mips.h @@ -39,21 +39,6 @@ struct bcma_drv_mips { u8 early_setup_done:1; }; -#ifdef CONFIG_BCMA_DRIVER_MIPS -extern void bcma_core_mips_init(struct bcma_drv_mips *mcore); -extern void bcma_core_mips_early_init(struct bcma_drv_mips *mcore); - -extern unsigned int bcma_core_mips_irq(struct bcma_device *dev); -#else -static inline void bcma_core_mips_init(struct bcma_drv_mips *mcore) { } -static inline void bcma_core_mips_early_init(struct bcma_drv_mips *mcore) { } - -static inline unsigned int bcma_core_mips_irq(struct bcma_device *dev) -{ - return 0; -} -#endif - extern u32 bcma_cpu_clock(struct bcma_drv_mips *mcore); #endif /* LINUX_BCMA_DRIVER_MIPS_H_ */ diff --git a/include/linux/bcma/bcma_driver_pci.h b/include/linux/bcma/bcma_driver_pci.h index 6b8bca67851f..8e90004fdfd7 100644 --- a/include/linux/bcma/bcma_driver_pci.h +++ b/include/linux/bcma/bcma_driver_pci.h @@ -238,8 +238,6 @@ struct bcma_drv_pci { #define pcicore_write16(pc, offset, val) bcma_write16((pc)->core, offset, val) #define pcicore_write32(pc, offset, val) bcma_write32((pc)->core, offset, val) -extern void bcma_core_pci_early_init(struct bcma_drv_pci *pc); -extern void bcma_core_pci_init(struct bcma_drv_pci *pc); extern int bcma_core_pci_irq_ctl(struct bcma_bus *bus, struct bcma_device *core, bool enable); extern void bcma_core_pci_power_save(struct bcma_bus *bus, bool up); diff --git a/include/linux/bcma/bcma_driver_pcie2.h b/include/linux/bcma/bcma_driver_pcie2.h index d8c43294c527..31e6d17ab798 100644 --- a/include/linux/bcma/bcma_driver_pcie2.h +++ b/include/linux/bcma/bcma_driver_pcie2.h @@ -155,6 +155,4 @@ struct bcma_drv_pcie2 { #define pcie2_set32(pcie2, offset, set) bcma_set32((pcie2)->core, offset, set) #define pcie2_mask32(pcie2, offset, mask) bcma_mask32((pcie2)->core, offset, mask) -void bcma_core_pcie2_init(struct bcma_drv_pcie2 *pcie2); - #endif /* LINUX_BCMA_DRIVER_PCIE2_H_ */ -- cgit v1.2.3 From 68c062eaa87b7b85b65f20f25c54524437715a95 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Thu, 5 Mar 2015 11:42:43 +0100 Subject: dmaengine: Remove net_dma leftovers Commit 7bce d397 510a ("net_dma: simple removal") removed the net_dma support entirely but left some functions and prototypes in the dmaengine header. Remove them. Signed-off-by: Maxime Ripard Signed-off-by: Vinod Koul --- include/linux/dmaengine.h | 23 ----------------------- 1 file changed, 23 deletions(-) (limited to 'include') diff --git a/include/linux/dmaengine.h b/include/linux/dmaengine.h index db0104b0da4d..f5cc5d4f1ad5 100644 --- a/include/linux/dmaengine.h +++ b/include/linux/dmaengine.h @@ -1108,27 +1108,4 @@ static inline struct dma_chan return __dma_request_channel(mask, fn, fn_param); } - -/* --- Helper iov-locking functions --- */ - -struct dma_page_list { - char __user *base_address; - int nr_pages; - struct page **pages; -}; - -struct dma_pinned_list { - int nr_iovecs; - struct dma_page_list page_list[0]; -}; - -struct dma_pinned_list *dma_pin_iovec_pages(struct iovec *iov, size_t len); -void dma_unpin_iovec_pages(struct dma_pinned_list* pinned_list); - -dma_cookie_t dma_memcpy_to_iovec(struct dma_chan *chan, struct iovec *iov, - struct dma_pinned_list *pinned_list, unsigned char *kdata, size_t len); -dma_cookie_t dma_memcpy_pg_to_iovec(struct dma_chan *chan, struct iovec *iov, - struct dma_pinned_list *pinned_list, struct page *page, - unsigned int offset, size_t len); - #endif /* DMAENGINE_H */ -- cgit v1.2.3 From bfde98bd762346639f0a5a557e02c4828dd6273b Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Thu, 5 Mar 2015 11:48:50 +0100 Subject: dmaengine: Remove net_dma_find_channel Since commit 7bced397510a ("net_dma: simple removal") removed the net_dma support entirely, net_dma_find_channel has no users left. Remove the function entirely. Signed-off-by: Maxime Ripard Signed-off-by: Vinod Koul --- drivers/dma/dmaengine.c | 14 -------------- include/linux/dmaengine.h | 1 - 2 files changed, 15 deletions(-) (limited to 'include') diff --git a/drivers/dma/dmaengine.c b/drivers/dma/dmaengine.c index f15712f2fec6..344b0ac6d985 100644 --- a/drivers/dma/dmaengine.c +++ b/drivers/dma/dmaengine.c @@ -355,20 +355,6 @@ struct dma_chan *dma_find_channel(enum dma_transaction_type tx_type) } EXPORT_SYMBOL(dma_find_channel); -/* - * net_dma_find_channel - find a channel for net_dma - * net_dma has alignment requirements - */ -struct dma_chan *net_dma_find_channel(void) -{ - struct dma_chan *chan = dma_find_channel(DMA_MEMCPY); - if (chan && !is_dma_copy_aligned(chan->device, 1, 1, 1)) - return NULL; - - return chan; -} -EXPORT_SYMBOL(net_dma_find_channel); - /** * dma_issue_pending_all - flush all pending operations across all channels */ diff --git a/include/linux/dmaengine.h b/include/linux/dmaengine.h index f5cc5d4f1ad5..2bff9abc162a 100644 --- a/include/linux/dmaengine.h +++ b/include/linux/dmaengine.h @@ -1090,7 +1090,6 @@ void dma_async_device_unregister(struct dma_device *device); void dma_run_dependencies(struct dma_async_tx_descriptor *tx); struct dma_chan *dma_get_slave_channel(struct dma_chan *chan); struct dma_chan *dma_get_any_slave_channel(struct dma_device *device); -struct dma_chan *net_dma_find_channel(void); #define dma_request_channel(mask, x, y) __dma_request_channel(&(mask), x, y) #define dma_request_slave_channel_compat(mask, x, y, dev, name) \ __dma_request_slave_channel_compat(&(mask), x, y, dev, name) -- cgit v1.2.3 From f33c9d655893d8632460696bbbdee737cb315711 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Tue, 24 Feb 2015 02:06:43 +0000 Subject: mmc: tmio: mmc: tmio: tmio_mmc_data has .chan_priv_?x dma_request_slave_channel_compat() in tmio_mmc_dma needs .chan_priv_tx/.chan_priv_rx. But these are copied from sh_mobile_sdhi only, and sh_mobile_sdhi_info is now almost same as tmio_mmc_data except .chan_priv_?x. sh_mobile_sdhi_info can be replaced to tmio_mmc_data, but it is used from ${LINUX}/arch/arm/mach-shmobile, ${LINUX}/arch/sh. So, this patch adds .chan_priv_?x into tmio_mmc_data as 1st step, and sh_mobile_sdhi driver has dummy operation for now. It will be replaced/removed together with platform data replace. Signed-off-by: Kuninori Morimoto Acked-by: Arnd Bergmann Acked-by: Ulf Hansson Acked-by: Lee Jones Signed-off-by: Vinod Koul --- drivers/mmc/host/sh_mobile_sdhi.c | 44 ++++++++++++++++++++++++--------------- drivers/mmc/host/tmio_mmc.h | 2 -- drivers/mmc/host/tmio_mmc_dma.c | 6 +++--- include/linux/mfd/tmio.h | 2 ++ 4 files changed, 32 insertions(+), 22 deletions(-) (limited to 'include') diff --git a/drivers/mmc/host/sh_mobile_sdhi.c b/drivers/mmc/host/sh_mobile_sdhi.c index 11991f5f3fef..3137e292270e 100644 --- a/drivers/mmc/host/sh_mobile_sdhi.c +++ b/drivers/mmc/host/sh_mobile_sdhi.c @@ -201,6 +201,7 @@ static int sh_mobile_sdhi_probe(struct platform_device *pdev) of_match_device(sh_mobile_sdhi_of_match, &pdev->dev); struct sh_mobile_sdhi *priv; struct tmio_mmc_data *mmc_data; + struct tmio_mmc_data *mmd = pdev->dev.platform_data; struct sh_mobile_sdhi_info *p = pdev->dev.platform_data; struct tmio_mmc_host *host; struct resource *res; @@ -245,28 +246,37 @@ static int sh_mobile_sdhi_probe(struct platform_device *pdev) else host->bus_shift = 0; - mmc_data->capabilities = MMC_CAP_MMC_HIGHSPEED; - if (p) { - mmc_data->flags = p->tmio_flags; - mmc_data->ocr_mask = p->tmio_ocr_mask; - mmc_data->capabilities |= p->tmio_caps; - mmc_data->capabilities2 |= p->tmio_caps2; - mmc_data->cd_gpio = p->cd_gpio; - - if (p->dma_slave_tx > 0 && p->dma_slave_rx > 0) { - /* - * Yes, we have to provide slave IDs twice to TMIO: - * once as a filter parameter and once for channel - * configuration as an explicit slave ID - */ - dma_priv->chan_priv_tx = (void *)p->dma_slave_tx; - dma_priv->chan_priv_rx = (void *)p->dma_slave_rx; - } + if (mmd) { + /* + * FIXME + * + * sh_mobile_sdhi_info will be replaced to tmio_mmc_data soon. + * But, sh_mobile_sdhi_info is used under + * ${LINUX}/arch/arm/mach-shmobile/ + * ${LINUX}/arch/sh/ + * To separate large patch into "tmio_mmc_data has .chan_priv_?x" + * and "replace sh_mobile_sdhi_info in tmio_mmc_data", + * here has dummy method. + * These should be removed. + */ + struct tmio_mmc_data m; + + mmd = &m; + m.flags = p->tmio_flags; + m.ocr_mask = p->tmio_ocr_mask; + m.capabilities = p->tmio_caps; + m.capabilities2 = p->tmio_caps2; + m.cd_gpio = p->cd_gpio; + m.chan_priv_tx = (void *)p->dma_slave_tx; + m.chan_priv_rx = (void *)p->dma_slave_rx; + + *mmc_data = *mmd; } dma_priv->filter = shdma_chan_filter; dma_priv->enable = sh_mobile_sdhi_enable_dma; mmc_data->alignment_shift = 1; /* 2-byte alignment */ + mmc_data->capabilities |= MMC_CAP_MMC_HIGHSPEED; /* * All SDHI blocks support 2-byte and larger block sizes in 4-bit diff --git a/drivers/mmc/host/tmio_mmc.h b/drivers/mmc/host/tmio_mmc.h index 1aea5c67af94..4a597f5a53e2 100644 --- a/drivers/mmc/host/tmio_mmc.h +++ b/drivers/mmc/host/tmio_mmc.h @@ -43,8 +43,6 @@ struct tmio_mmc_data; struct tmio_mmc_host; struct tmio_mmc_dma { - void *chan_priv_tx; - void *chan_priv_rx; enum dma_slave_buswidth dma_buswidth; bool (*filter)(struct dma_chan *chan, void *arg); void (*enable)(struct tmio_mmc_host *host, bool enable); diff --git a/drivers/mmc/host/tmio_mmc_dma.c b/drivers/mmc/host/tmio_mmc_dma.c index 8dbd785366a6..e4b05dbb9ca8 100644 --- a/drivers/mmc/host/tmio_mmc_dma.c +++ b/drivers/mmc/host/tmio_mmc_dma.c @@ -261,7 +261,7 @@ void tmio_mmc_request_dma(struct tmio_mmc_host *host, struct tmio_mmc_data *pdat { /* We can only either use DMA for both Tx and Rx or not use it at all */ if (!host->dma || (!host->pdev->dev.of_node && - (!host->dma->chan_priv_tx || !host->dma->chan_priv_rx))) + (!pdata->chan_priv_tx || !pdata->chan_priv_rx))) return; if (!host->chan_tx && !host->chan_rx) { @@ -278,7 +278,7 @@ void tmio_mmc_request_dma(struct tmio_mmc_host *host, struct tmio_mmc_data *pdat dma_cap_set(DMA_SLAVE, mask); host->chan_tx = dma_request_slave_channel_compat(mask, - host->dma->filter, host->dma->chan_priv_tx, + host->dma->filter, pdata->chan_priv_tx, &host->pdev->dev, "tx"); dev_dbg(&host->pdev->dev, "%s: TX: got channel %p\n", __func__, host->chan_tx); @@ -297,7 +297,7 @@ void tmio_mmc_request_dma(struct tmio_mmc_host *host, struct tmio_mmc_data *pdat goto ecfgtx; host->chan_rx = dma_request_slave_channel_compat(mask, - host->dma->filter, host->dma->chan_priv_rx, + host->dma->filter, pdata->chan_priv_rx, &host->pdev->dev, "rx"); dev_dbg(&host->pdev->dev, "%s: RX: got channel %p\n", __func__, host->chan_rx); diff --git a/include/linux/mfd/tmio.h b/include/linux/mfd/tmio.h index 605812820e48..24b86d538e88 100644 --- a/include/linux/mfd/tmio.h +++ b/include/linux/mfd/tmio.h @@ -111,6 +111,8 @@ struct dma_chan; * data for the MMC controller */ struct tmio_mmc_data { + void *chan_priv_tx; + void *chan_priv_rx; unsigned int hclk; unsigned long capabilities; unsigned long capabilities2; -- cgit v1.2.3 From 84f11d5b1f2abc0e22895b7e12e037f0ec03caeb Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Tue, 24 Feb 2015 02:07:07 +0000 Subject: mmc: sh_mobile_sdhi: remove sh_mobile_sdhi_info Current sh_mobile_sdhi's platform data is set via sh_mobile_sdhi_info and it is just copied to tmio_mmc_data. Now, tmio mmc platform data is specified via tmio_mmc_data. This patch replace sh_mobile_sdhi_info to tmio_mmc_data struct sh_mobile_sdhi_info { -> struct tmio_mmc_data { int dma_slave_tx; -> void *chan_priv_tx; int dma_slave_rx; -> void *chan_priv_rx; unsigned long tmio_flags; -> unsigned long flags; unsigned long tmio_caps; -> unsigned long capabilities; unsigned long tmio_caps2; -> unsigned long capabilities2; u32 tmio_ocr_mask; -> u32 ocr_mask; unsigned int cd_gpio; -> unsigned int cd_gpio; }; unsigned int hclk; void (*set_pwr)(...); void (*set_clk_div)(...); }; Signed-off-by: Kuninori Morimoto Acked-by: Arnd Bergmann Acked-by: Ulf Hansson Signed-off-by: Vinod Koul --- arch/arm/mach-shmobile/board-armadillo800eva.c | 20 +++++++++--------- arch/arm/mach-shmobile/board-bockw.c | 14 ++++++------- arch/arm/mach-shmobile/board-kzm9g.c | 20 +++++++++--------- arch/arm/mach-shmobile/board-marzen.c | 10 ++++----- arch/sh/boards/board-sh7757lcr.c | 9 +++++---- arch/sh/boards/mach-ap325rxa/setup.c | 9 +++++---- arch/sh/boards/mach-ecovec24/setup.c | 20 +++++++++--------- arch/sh/boards/mach-kfr2r09/setup.c | 10 ++++----- arch/sh/boards/mach-migor/setup.c | 9 +++++---- arch/sh/boards/mach-se/7724/setup.c | 17 ++++++++-------- drivers/mmc/host/sh_mobile_sdhi.c | 28 ++------------------------ include/linux/mmc/sh_mobile_sdhi.h | 10 --------- 12 files changed, 73 insertions(+), 103 deletions(-) (limited to 'include') diff --git a/arch/arm/mach-shmobile/board-armadillo800eva.c b/arch/arm/mach-shmobile/board-armadillo800eva.c index 6d949f1c850b..7eac84687cb4 100644 --- a/arch/arm/mach-shmobile/board-armadillo800eva.c +++ b/arch/arm/mach-shmobile/board-armadillo800eva.c @@ -754,12 +754,12 @@ static struct platform_device vcc_sdhi1 = { }; /* SDHI0 */ -static struct sh_mobile_sdhi_info sdhi0_info = { - .dma_slave_tx = SHDMA_SLAVE_SDHI0_TX, - .dma_slave_rx = SHDMA_SLAVE_SDHI0_RX, - .tmio_caps = MMC_CAP_SD_HIGHSPEED | MMC_CAP_SDIO_IRQ | +static struct tmio_mmc_data sdhi0_info = { + .chan_priv_tx = (void *)SHDMA_SLAVE_SDHI0_TX, + .chan_priv_rx = (void *)SHDMA_SLAVE_SDHI0_RX, + .capabilities = MMC_CAP_SD_HIGHSPEED | MMC_CAP_SDIO_IRQ | MMC_CAP_POWER_OFF_CARD, - .tmio_flags = TMIO_MMC_HAS_IDLE_WAIT | TMIO_MMC_USE_GPIO_CD, + .flags = TMIO_MMC_HAS_IDLE_WAIT | TMIO_MMC_USE_GPIO_CD, .cd_gpio = 167, }; @@ -796,12 +796,12 @@ static struct platform_device sdhi0_device = { }; /* SDHI1 */ -static struct sh_mobile_sdhi_info sdhi1_info = { - .dma_slave_tx = SHDMA_SLAVE_SDHI1_TX, - .dma_slave_rx = SHDMA_SLAVE_SDHI1_RX, - .tmio_caps = MMC_CAP_SD_HIGHSPEED | MMC_CAP_SDIO_IRQ | +static struct tmio_mmc_data sdhi1_info = { + .chan_priv_tx = (void *)SHDMA_SLAVE_SDHI1_TX, + .chan_priv_rx = (void *)SHDMA_SLAVE_SDHI1_RX, + .capabilities = MMC_CAP_SD_HIGHSPEED | MMC_CAP_SDIO_IRQ | MMC_CAP_POWER_OFF_CARD, - .tmio_flags = TMIO_MMC_HAS_IDLE_WAIT | TMIO_MMC_USE_GPIO_CD, + .flags = TMIO_MMC_HAS_IDLE_WAIT | TMIO_MMC_USE_GPIO_CD, /* Port72 cannot generate IRQs, will be used in polling mode. */ .cd_gpio = 72, }; diff --git a/arch/arm/mach-shmobile/board-bockw.c b/arch/arm/mach-shmobile/board-bockw.c index f27b5a833bf0..25558d1f417f 100644 --- a/arch/arm/mach-shmobile/board-bockw.c +++ b/arch/arm/mach-shmobile/board-bockw.c @@ -201,12 +201,12 @@ static struct rcar_phy_platform_data usb_phy_platform_data __initdata = /* SDHI */ -static struct sh_mobile_sdhi_info sdhi0_info __initdata = { - .dma_slave_tx = HPBDMA_SLAVE_SDHI0_TX, - .dma_slave_rx = HPBDMA_SLAVE_SDHI0_RX, - .tmio_caps = MMC_CAP_SD_HIGHSPEED, - .tmio_ocr_mask = MMC_VDD_165_195 | MMC_VDD_32_33 | MMC_VDD_33_34, - .tmio_flags = TMIO_MMC_HAS_IDLE_WAIT, +static struct tmio_mmc_data sdhi0_info __initdata = { + .chan_priv_tx = (void *)HPBDMA_SLAVE_SDHI0_TX, + .chan_priv_rx = (void *)HPBDMA_SLAVE_SDHI0_RX, + .capabilities = MMC_CAP_SD_HIGHSPEED, + .ocr_mask = MMC_VDD_165_195 | MMC_VDD_32_33 | MMC_VDD_33_34, + .flags = TMIO_MMC_HAS_IDLE_WAIT, }; static struct resource sdhi0_resources[] __initdata = { @@ -683,7 +683,7 @@ static void __init bockw_init(void) platform_device_register_resndata( NULL, "sh_mobile_sdhi", 0, sdhi0_resources, ARRAY_SIZE(sdhi0_resources), - &sdhi0_info, sizeof(struct sh_mobile_sdhi_info)); + &sdhi0_info, sizeof(struct tmio_mmc_data)); } /* for Audio */ diff --git a/arch/arm/mach-shmobile/board-kzm9g.c b/arch/arm/mach-shmobile/board-kzm9g.c index 7c9b63bdde9f..260d8319fd82 100644 --- a/arch/arm/mach-shmobile/board-kzm9g.c +++ b/arch/arm/mach-shmobile/board-kzm9g.c @@ -442,11 +442,11 @@ static struct platform_device vcc_sdhi2 = { }; /* SDHI */ -static struct sh_mobile_sdhi_info sdhi0_info = { - .dma_slave_tx = SHDMA_SLAVE_SDHI0_TX, - .dma_slave_rx = SHDMA_SLAVE_SDHI0_RX, - .tmio_flags = TMIO_MMC_HAS_IDLE_WAIT, - .tmio_caps = MMC_CAP_SD_HIGHSPEED | MMC_CAP_SDIO_IRQ | +static struct tmio_mmc_data sdhi0_info = { + .chan_priv_tx = (void *)SHDMA_SLAVE_SDHI0_TX, + .chan_priv_rx = (void *)SHDMA_SLAVE_SDHI0_RX, + .flags = TMIO_MMC_HAS_IDLE_WAIT, + .capabilities = MMC_CAP_SD_HIGHSPEED | MMC_CAP_SDIO_IRQ | MMC_CAP_POWER_OFF_CARD, }; @@ -484,13 +484,13 @@ static struct platform_device sdhi0_device = { }; /* Micro SD */ -static struct sh_mobile_sdhi_info sdhi2_info = { - .dma_slave_tx = SHDMA_SLAVE_SDHI2_TX, - .dma_slave_rx = SHDMA_SLAVE_SDHI2_RX, - .tmio_flags = TMIO_MMC_HAS_IDLE_WAIT | +static struct tmio_mmc_data sdhi2_info = { + .chan_priv_tx = (void *)SHDMA_SLAVE_SDHI2_TX, + .chan_priv_rx = (void *)SHDMA_SLAVE_SDHI2_RX, + .flags = TMIO_MMC_HAS_IDLE_WAIT | TMIO_MMC_USE_GPIO_CD | TMIO_MMC_WRPROTECT_DISABLE, - .tmio_caps = MMC_CAP_SD_HIGHSPEED | MMC_CAP_POWER_OFF_CARD, + .capabilities = MMC_CAP_SD_HIGHSPEED | MMC_CAP_POWER_OFF_CARD, .cd_gpio = 13, }; diff --git a/arch/arm/mach-shmobile/board-marzen.c b/arch/arm/mach-shmobile/board-marzen.c index 598f704f76ae..51db288f192a 100644 --- a/arch/arm/mach-shmobile/board-marzen.c +++ b/arch/arm/mach-shmobile/board-marzen.c @@ -122,11 +122,11 @@ static struct resource sdhi0_resources[] = { }, }; -static struct sh_mobile_sdhi_info sdhi0_platform_data = { - .dma_slave_tx = HPBDMA_SLAVE_SDHI0_TX, - .dma_slave_rx = HPBDMA_SLAVE_SDHI0_RX, - .tmio_flags = TMIO_MMC_WRPROTECT_DISABLE | TMIO_MMC_HAS_IDLE_WAIT, - .tmio_caps = MMC_CAP_SD_HIGHSPEED, +static struct tmio_mmc_data sdhi0_platform_data = { + .chan_priv_tx = (void *)HPBDMA_SLAVE_SDHI0_TX, + .chan_priv_rx = (void *)HPBDMA_SLAVE_SDHI0_RX, + .flags = TMIO_MMC_WRPROTECT_DISABLE | TMIO_MMC_HAS_IDLE_WAIT, + .capabilities = MMC_CAP_SD_HIGHSPEED, }; static struct platform_device sdhi0_device = { diff --git a/arch/sh/boards/board-sh7757lcr.c b/arch/sh/boards/board-sh7757lcr.c index 669df51a82e3..324599bfad14 100644 --- a/arch/sh/boards/board-sh7757lcr.c +++ b/arch/sh/boards/board-sh7757lcr.c @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -243,10 +244,10 @@ static struct platform_device sh_mmcif_device = { }; /* SDHI0 */ -static struct sh_mobile_sdhi_info sdhi_info = { - .dma_slave_tx = SHDMA_SLAVE_SDHI_TX, - .dma_slave_rx = SHDMA_SLAVE_SDHI_RX, - .tmio_caps = MMC_CAP_SD_HIGHSPEED, +static struct tmio_mmc_data sdhi_info = { + .chan_priv_tx = (void *)SHDMA_SLAVE_SDHI_TX, + .chan_priv_rx = (void *)SHDMA_SLAVE_SDHI_RX, + .capabilities = MMC_CAP_SD_HIGHSPEED, }; static struct resource sdhi_resources[] = { diff --git a/arch/sh/boards/mach-ap325rxa/setup.c b/arch/sh/boards/mach-ap325rxa/setup.c index d4b01d4cc102..cbd2a9f02a91 100644 --- a/arch/sh/boards/mach-ap325rxa/setup.c +++ b/arch/sh/boards/mach-ap325rxa/setup.c @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -447,8 +448,8 @@ static struct resource sdhi0_cn3_resources[] = { }, }; -static struct sh_mobile_sdhi_info sdhi0_cn3_data = { - .tmio_caps = MMC_CAP_SDIO_IRQ, +static struct tmio_mmc_data sdhi0_cn3_data = { + .capabilities = MMC_CAP_SDIO_IRQ, }; static struct platform_device sdhi0_cn3_device = { @@ -474,8 +475,8 @@ static struct resource sdhi1_cn7_resources[] = { }, }; -static struct sh_mobile_sdhi_info sdhi1_cn7_data = { - .tmio_caps = MMC_CAP_SDIO_IRQ, +static struct tmio_mmc_data sdhi1_cn7_data = { + .capabilities = MMC_CAP_SDIO_IRQ, }; static struct platform_device sdhi1_cn7_device = { diff --git a/arch/sh/boards/mach-ecovec24/setup.c b/arch/sh/boards/mach-ecovec24/setup.c index 0d3049244cd3..d531791f06ff 100644 --- a/arch/sh/boards/mach-ecovec24/setup.c +++ b/arch/sh/boards/mach-ecovec24/setup.c @@ -601,12 +601,12 @@ static struct platform_device sdhi0_power = { }, }; -static struct sh_mobile_sdhi_info sdhi0_info = { - .dma_slave_tx = SHDMA_SLAVE_SDHI0_TX, - .dma_slave_rx = SHDMA_SLAVE_SDHI0_RX, - .tmio_caps = MMC_CAP_SDIO_IRQ | MMC_CAP_POWER_OFF_CARD | +static struct tmio_mmc_data sdhi0_info = { + .chan_priv_tx = (void *)SHDMA_SLAVE_SDHI0_TX, + .chan_priv_rx = (void *)SHDMA_SLAVE_SDHI0_RX, + .capabilities = MMC_CAP_SDIO_IRQ | MMC_CAP_POWER_OFF_CARD | MMC_CAP_NEEDS_POLL, - .tmio_flags = TMIO_MMC_USE_GPIO_CD, + .flags = TMIO_MMC_USE_GPIO_CD, .cd_gpio = GPIO_PTY7, }; @@ -635,12 +635,12 @@ static struct platform_device sdhi0_device = { #if !defined(CONFIG_MMC_SH_MMCIF) && !defined(CONFIG_MMC_SH_MMCIF_MODULE) /* SDHI1 */ -static struct sh_mobile_sdhi_info sdhi1_info = { - .dma_slave_tx = SHDMA_SLAVE_SDHI1_TX, - .dma_slave_rx = SHDMA_SLAVE_SDHI1_RX, - .tmio_caps = MMC_CAP_SDIO_IRQ | MMC_CAP_POWER_OFF_CARD | +static struct tmio_mmc_data sdhi1_info = { + .chan_priv_tx = (void *)SHDMA_SLAVE_SDHI1_TX, + .chan_priv_rx = (void *)SHDMA_SLAVE_SDHI1_RX, + .capabilities = MMC_CAP_SDIO_IRQ | MMC_CAP_POWER_OFF_CARD | MMC_CAP_NEEDS_POLL, - .tmio_flags = TMIO_MMC_USE_GPIO_CD, + .flags = TMIO_MMC_USE_GPIO_CD, .cd_gpio = GPIO_PTW7, }; diff --git a/arch/sh/boards/mach-kfr2r09/setup.c b/arch/sh/boards/mach-kfr2r09/setup.c index 1df4398f8375..7d997cec09c5 100644 --- a/arch/sh/boards/mach-kfr2r09/setup.c +++ b/arch/sh/boards/mach-kfr2r09/setup.c @@ -373,11 +373,11 @@ static struct resource kfr2r09_sh_sdhi0_resources[] = { }, }; -static struct sh_mobile_sdhi_info sh7724_sdhi0_data = { - .dma_slave_tx = SHDMA_SLAVE_SDHI0_TX, - .dma_slave_rx = SHDMA_SLAVE_SDHI0_RX, - .tmio_flags = TMIO_MMC_WRPROTECT_DISABLE, - .tmio_caps = MMC_CAP_SDIO_IRQ, +static struct tmio_mmc_data sh7724_sdhi0_data = { + .chan_priv_tx = (void *)SHDMA_SLAVE_SDHI0_TX, + .chan_priv_rx = (void *)SHDMA_SLAVE_SDHI0_RX, + .flags = TMIO_MMC_WRPROTECT_DISABLE, + .capabilities = MMC_CAP_SDIO_IRQ, }; static struct platform_device kfr2r09_sh_sdhi0_device = { diff --git a/arch/sh/boards/mach-migor/setup.c b/arch/sh/boards/mach-migor/setup.c index 8b73194ed2ce..29b7c0dcfc51 100644 --- a/arch/sh/boards/mach-migor/setup.c +++ b/arch/sh/boards/mach-migor/setup.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -408,10 +409,10 @@ static struct resource sdhi_cn9_resources[] = { }, }; -static struct sh_mobile_sdhi_info sh7724_sdhi_data = { - .dma_slave_tx = SHDMA_SLAVE_SDHI0_TX, - .dma_slave_rx = SHDMA_SLAVE_SDHI0_RX, - .tmio_caps = MMC_CAP_SDIO_IRQ, +static struct tmio_mmc_data sh7724_sdhi_data = { + .chan_priv_tx = (void *)SHDMA_SLAVE_SDHI0_TX, + .chan_priv_rx = (void *)SHDMA_SLAVE_SDHI0_RX, + .capabilities = MMC_CAP_SDIO_IRQ, }; static struct platform_device sdhi_cn9_device = { diff --git a/arch/sh/boards/mach-se/7724/setup.c b/arch/sh/boards/mach-se/7724/setup.c index 1162bc6945a3..4f6635a075f2 100644 --- a/arch/sh/boards/mach-se/7724/setup.c +++ b/arch/sh/boards/mach-se/7724/setup.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -468,10 +469,10 @@ static struct resource sdhi0_cn7_resources[] = { }, }; -static struct sh_mobile_sdhi_info sh7724_sdhi0_data = { - .dma_slave_tx = SHDMA_SLAVE_SDHI0_TX, - .dma_slave_rx = SHDMA_SLAVE_SDHI0_RX, - .tmio_caps = MMC_CAP_SDIO_IRQ, +static struct tmio_mmc_data sh7724_sdhi0_data = { + .chan_priv_tx = (void *)SHDMA_SLAVE_SDHI0_TX, + .chan_priv_rx = (void *)SHDMA_SLAVE_SDHI0_RX, + .capabilities = MMC_CAP_SDIO_IRQ, }; static struct platform_device sdhi0_cn7_device = { @@ -497,10 +498,10 @@ static struct resource sdhi1_cn8_resources[] = { }, }; -static struct sh_mobile_sdhi_info sh7724_sdhi1_data = { - .dma_slave_tx = SHDMA_SLAVE_SDHI1_TX, - .dma_slave_rx = SHDMA_SLAVE_SDHI1_RX, - .tmio_caps = MMC_CAP_SDIO_IRQ, +static struct tmio_mmc_data sh7724_sdhi1_data = { + .chan_priv_tx = (void *)SHDMA_SLAVE_SDHI1_TX, + .chan_priv_rx = (void *)SHDMA_SLAVE_SDHI1_RX, + .capabilities = MMC_CAP_SDIO_IRQ, }; static struct platform_device sdhi1_cn8_device = { diff --git a/drivers/mmc/host/sh_mobile_sdhi.c b/drivers/mmc/host/sh_mobile_sdhi.c index 3137e292270e..354f4f335ed5 100644 --- a/drivers/mmc/host/sh_mobile_sdhi.c +++ b/drivers/mmc/host/sh_mobile_sdhi.c @@ -202,7 +202,6 @@ static int sh_mobile_sdhi_probe(struct platform_device *pdev) struct sh_mobile_sdhi *priv; struct tmio_mmc_data *mmc_data; struct tmio_mmc_data *mmd = pdev->dev.platform_data; - struct sh_mobile_sdhi_info *p = pdev->dev.platform_data; struct tmio_mmc_host *host; struct resource *res; int irq, ret, i = 0; @@ -246,32 +245,9 @@ static int sh_mobile_sdhi_probe(struct platform_device *pdev) else host->bus_shift = 0; - if (mmd) { - /* - * FIXME - * - * sh_mobile_sdhi_info will be replaced to tmio_mmc_data soon. - * But, sh_mobile_sdhi_info is used under - * ${LINUX}/arch/arm/mach-shmobile/ - * ${LINUX}/arch/sh/ - * To separate large patch into "tmio_mmc_data has .chan_priv_?x" - * and "replace sh_mobile_sdhi_info in tmio_mmc_data", - * here has dummy method. - * These should be removed. - */ - struct tmio_mmc_data m; - - mmd = &m; - m.flags = p->tmio_flags; - m.ocr_mask = p->tmio_ocr_mask; - m.capabilities = p->tmio_caps; - m.capabilities2 = p->tmio_caps2; - m.cd_gpio = p->cd_gpio; - m.chan_priv_tx = (void *)p->dma_slave_tx; - m.chan_priv_rx = (void *)p->dma_slave_rx; - + if (mmd) *mmc_data = *mmd; - } + dma_priv->filter = shdma_chan_filter; dma_priv->enable = sh_mobile_sdhi_enable_dma; diff --git a/include/linux/mmc/sh_mobile_sdhi.h b/include/linux/mmc/sh_mobile_sdhi.h index da77e5e2041d..95d6f0314a7d 100644 --- a/include/linux/mmc/sh_mobile_sdhi.h +++ b/include/linux/mmc/sh_mobile_sdhi.h @@ -7,14 +7,4 @@ #define SH_MOBILE_SDHI_IRQ_SDCARD "sdcard" #define SH_MOBILE_SDHI_IRQ_SDIO "sdio" -struct sh_mobile_sdhi_info { - int dma_slave_tx; - int dma_slave_rx; - unsigned long tmio_flags; - unsigned long tmio_caps; - unsigned long tmio_caps2; - u32 tmio_ocr_mask; /* available MMC voltages */ - unsigned int cd_gpio; -}; - #endif /* LINUX_MMC_SH_MOBILE_SDHI_H */ -- cgit v1.2.3 From 842a9ae08a25671db3d4f689eed68b4d64be15b5 Mon Sep 17 00:00:00 2001 From: Jouni Malinen Date: Wed, 4 Mar 2015 12:54:21 +0200 Subject: bridge: Extend Proxy ARP design to allow optional rules for Wi-Fi This extends the design in commit 958501163ddd ("bridge: Add support for IEEE 802.11 Proxy ARP") with optional set of rules that are needed to meet the IEEE 802.11 and Hotspot 2.0 requirements for ProxyARP. The previously added BR_PROXYARP behavior is left as-is and a new BR_PROXYARP_WIFI alternative is added so that this behavior can be configured from user space when required. In addition, this enables proxyarp functionality for unicast ARP requests for both BR_PROXYARP and BR_PROXYARP_WIFI since it is possible to use unicast as well as broadcast for these frames. The key differences in functionality: BR_PROXYARP: - uses the flag on the bridge port on which the request frame was received to determine whether to reply - block bridge port flooding completely on ports that enable proxy ARP BR_PROXYARP_WIFI: - uses the flag on the bridge port to which the target device of the request belongs - block bridge port flooding selectively based on whether the proxyarp functionality replied Signed-off-by: Jouni Malinen Signed-off-by: David S. Miller --- include/linux/if_bridge.h | 1 + include/uapi/linux/if_link.h | 1 + net/bridge/br_forward.c | 3 +++ net/bridge/br_input.c | 17 ++++++++++------- net/bridge/br_netlink.c | 5 ++++- net/bridge/br_private.h | 1 + net/bridge/br_sysfs_if.c | 2 ++ 7 files changed, 22 insertions(+), 8 deletions(-) (limited to 'include') diff --git a/include/linux/if_bridge.h b/include/linux/if_bridge.h index a57bca2ea97e..dad8b00beed2 100644 --- a/include/linux/if_bridge.h +++ b/include/linux/if_bridge.h @@ -44,6 +44,7 @@ struct br_ip_list { #define BR_PROMISC BIT(7) #define BR_PROXYARP BIT(8) #define BR_LEARNING_SYNC BIT(9) +#define BR_PROXYARP_WIFI BIT(10) extern void brioctl_set(int (*ioctl_hook)(struct net *, unsigned int, void __user *)); diff --git a/include/uapi/linux/if_link.h b/include/uapi/linux/if_link.h index dfd0bb22e554..756436e1ce89 100644 --- a/include/uapi/linux/if_link.h +++ b/include/uapi/linux/if_link.h @@ -247,6 +247,7 @@ enum { IFLA_BRPORT_UNICAST_FLOOD, /* flood unicast traffic */ IFLA_BRPORT_PROXYARP, /* proxy ARP */ IFLA_BRPORT_LEARNING_SYNC, /* mac learning sync from device */ + IFLA_BRPORT_PROXYARP_WIFI, /* proxy ARP for Wi-Fi */ __IFLA_BRPORT_MAX }; #define IFLA_BRPORT_MAX (__IFLA_BRPORT_MAX - 1) diff --git a/net/bridge/br_forward.c b/net/bridge/br_forward.c index f96933a823e3..1238fabff874 100644 --- a/net/bridge/br_forward.c +++ b/net/bridge/br_forward.c @@ -188,6 +188,9 @@ static void br_flood(struct net_bridge *br, struct sk_buff *skb, /* Do not flood to ports that enable proxy ARP */ if (p->flags & BR_PROXYARP) continue; + if ((p->flags & BR_PROXYARP_WIFI) && + BR_INPUT_SKB_CB(skb)->proxyarp_replied) + continue; prev = maybe_deliver(prev, p, skb, __packet_hook); if (IS_ERR(prev)) diff --git a/net/bridge/br_input.c b/net/bridge/br_input.c index e2aa7be3a847..052c5ebbc947 100644 --- a/net/bridge/br_input.c +++ b/net/bridge/br_input.c @@ -60,7 +60,7 @@ static int br_pass_frame_up(struct sk_buff *skb) } static void br_do_proxy_arp(struct sk_buff *skb, struct net_bridge *br, - u16 vid) + u16 vid, struct net_bridge_port *p) { struct net_device *dev = br->dev; struct neighbour *n; @@ -68,6 +68,8 @@ static void br_do_proxy_arp(struct sk_buff *skb, struct net_bridge *br, u8 *arpptr, *sha; __be32 sip, tip; + BR_INPUT_SKB_CB(skb)->proxyarp_replied = false; + if (dev->flags & IFF_NOARP) return; @@ -105,9 +107,12 @@ static void br_do_proxy_arp(struct sk_buff *skb, struct net_bridge *br, } f = __br_fdb_get(br, n->ha, vid); - if (f) + if (f && ((p->flags & BR_PROXYARP) || + (f->dst && (f->dst->flags & BR_PROXYARP_WIFI)))) { arp_send(ARPOP_REPLY, ETH_P_ARP, sip, skb->dev, tip, sha, n->ha, sha); + BR_INPUT_SKB_CB(skb)->proxyarp_replied = true; + } neigh_release(n); } @@ -153,12 +158,10 @@ int br_handle_frame_finish(struct sk_buff *skb) dst = NULL; - if (is_broadcast_ether_addr(dest)) { - if (IS_ENABLED(CONFIG_INET) && - p->flags & BR_PROXYARP && - skb->protocol == htons(ETH_P_ARP)) - br_do_proxy_arp(skb, br, vid); + if (IS_ENABLED(CONFIG_INET) && skb->protocol == htons(ETH_P_ARP)) + br_do_proxy_arp(skb, br, vid, p); + if (is_broadcast_ether_addr(dest)) { skb2 = skb; unicast = false; } else if (is_multicast_ether_addr(dest)) { diff --git a/net/bridge/br_netlink.c b/net/bridge/br_netlink.c index c72083968768..8bc6b67457dc 100644 --- a/net/bridge/br_netlink.c +++ b/net/bridge/br_netlink.c @@ -143,7 +143,9 @@ static int br_port_fill_attrs(struct sk_buff *skb, nla_put_u8(skb, IFLA_BRPORT_FAST_LEAVE, !!(p->flags & BR_MULTICAST_FAST_LEAVE)) || nla_put_u8(skb, IFLA_BRPORT_LEARNING, !!(p->flags & BR_LEARNING)) || nla_put_u8(skb, IFLA_BRPORT_UNICAST_FLOOD, !!(p->flags & BR_FLOOD)) || - nla_put_u8(skb, IFLA_BRPORT_PROXYARP, !!(p->flags & BR_PROXYARP))) + nla_put_u8(skb, IFLA_BRPORT_PROXYARP, !!(p->flags & BR_PROXYARP)) || + nla_put_u8(skb, IFLA_BRPORT_PROXYARP_WIFI, + !!(p->flags & BR_PROXYARP_WIFI))) return -EMSGSIZE; return 0; @@ -553,6 +555,7 @@ static int br_setport(struct net_bridge_port *p, struct nlattr *tb[]) br_set_port_flag(p, tb, IFLA_BRPORT_LEARNING, BR_LEARNING); br_set_port_flag(p, tb, IFLA_BRPORT_UNICAST_FLOOD, BR_FLOOD); br_set_port_flag(p, tb, IFLA_BRPORT_PROXYARP, BR_PROXYARP); + br_set_port_flag(p, tb, IFLA_BRPORT_PROXYARP_WIFI, BR_PROXYARP_WIFI); if (tb[IFLA_BRPORT_COST]) { err = br_stp_set_path_cost(p, nla_get_u32(tb[IFLA_BRPORT_COST])); diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h index de0919975a25..c32e279c62f8 100644 --- a/net/bridge/br_private.h +++ b/net/bridge/br_private.h @@ -305,6 +305,7 @@ struct br_input_skb_cb { #endif u16 frag_max_size; + bool proxyarp_replied; #ifdef CONFIG_BRIDGE_VLAN_FILTERING bool vlan_filtered; diff --git a/net/bridge/br_sysfs_if.c b/net/bridge/br_sysfs_if.c index 2de5d91199e8..4905845a94e9 100644 --- a/net/bridge/br_sysfs_if.c +++ b/net/bridge/br_sysfs_if.c @@ -171,6 +171,7 @@ BRPORT_ATTR_FLAG(root_block, BR_ROOT_BLOCK); BRPORT_ATTR_FLAG(learning, BR_LEARNING); BRPORT_ATTR_FLAG(unicast_flood, BR_FLOOD); BRPORT_ATTR_FLAG(proxyarp, BR_PROXYARP); +BRPORT_ATTR_FLAG(proxyarp_wifi, BR_PROXYARP_WIFI); #ifdef CONFIG_BRIDGE_IGMP_SNOOPING static ssize_t show_multicast_router(struct net_bridge_port *p, char *buf) @@ -215,6 +216,7 @@ static const struct brport_attribute *brport_attrs[] = { &brport_attr_multicast_fast_leave, #endif &brport_attr_proxyarp, + &brport_attr_proxyarp_wifi, NULL }; -- cgit v1.2.3 From 43270b1bc5f1e33522dacf3d3b9175c29404c36c Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Mon, 2 Mar 2015 14:40:39 +0100 Subject: netfilter: ipt_CLUSTERIP: deprecate it in favour of xt_cluster xt_cluster supersedes ipt_CLUSTERIP since it can be also used in gateway configurations (not only from the backend side). ipt_CLUSTER is also known to leak the netdev that it uses on device removal, which requires a rather large fix to workaround the problem: http://patchwork.ozlabs.org/patch/358629/ So let's deprecate this so we can probably kill code this in the future. Signed-off-by: Pablo Neira Ayuso --- include/net/netns/x_tables.h | 1 + net/ipv4/netfilter/ipt_CLUSTERIP.c | 7 +++++++ 2 files changed, 8 insertions(+) (limited to 'include') diff --git a/include/net/netns/x_tables.h b/include/net/netns/x_tables.h index c24060ee411e..4d6597ad6067 100644 --- a/include/net/netns/x_tables.h +++ b/include/net/netns/x_tables.h @@ -9,6 +9,7 @@ struct ebt_table; struct netns_xt { struct list_head tables[NFPROTO_NUMPROTO]; bool notrack_deprecated_warning; + bool clusterip_deprecated_warning; #if defined(CONFIG_BRIDGE_NF_EBTABLES) || \ defined(CONFIG_BRIDGE_NF_EBTABLES_MODULE) struct ebt_table *broute_table; diff --git a/net/ipv4/netfilter/ipt_CLUSTERIP.c b/net/ipv4/netfilter/ipt_CLUSTERIP.c index e90f83a3415b..f75e9df5e017 100644 --- a/net/ipv4/netfilter/ipt_CLUSTERIP.c +++ b/net/ipv4/netfilter/ipt_CLUSTERIP.c @@ -418,6 +418,13 @@ static int clusterip_tg_check(const struct xt_tgchk_param *par) if (ret < 0) pr_info("cannot load conntrack support for proto=%u\n", par->family); + + if (!par->net->xt.clusterip_deprecated_warning) { + pr_info("ipt_CLUSTERIP is deprecated and it will removed soon, " + "use xt_cluster instead\n"); + par->net->xt.clusterip_deprecated_warning = true; + } + return ret; } -- cgit v1.2.3 From 1a1e1a12199ca27cebe87677b4c2153a3d2bacf2 Mon Sep 17 00:00:00 2001 From: Patrick McHardy Date: Tue, 3 Mar 2015 20:10:06 +0000 Subject: netfilter: nf_tables: cleanup nf_tables.h The transaction related definitions are squeezed in between the rule and expression definitions, which are closely related and should be next to each other. The transaction definitions actually don't belong into that file at all since it defines the global objects and API and transactions are internal to nf_tables_api, but for now simply move them to a seperate section. Similar, the chain types are in between a set of registration functions, they belong to the chain section. Signed-off-by: Patrick McHardy Signed-off-by: Pablo Neira Ayuso --- include/net/netfilter/nf_tables.h | 174 +++++++++++++++++++------------------- 1 file changed, 87 insertions(+), 87 deletions(-) (limited to 'include') diff --git a/include/net/netfilter/nf_tables.h b/include/net/netfilter/nf_tables.h index 9eaaa7884586..04188b47629d 100644 --- a/include/net/netfilter/nf_tables.h +++ b/include/net/netfilter/nf_tables.h @@ -393,74 +393,6 @@ struct nft_rule { __attribute__((aligned(__alignof__(struct nft_expr)))); }; -/** - * struct nft_trans - nf_tables object update in transaction - * - * @list: used internally - * @msg_type: message type - * @ctx: transaction context - * @data: internal information related to the transaction - */ -struct nft_trans { - struct list_head list; - int msg_type; - struct nft_ctx ctx; - char data[0]; -}; - -struct nft_trans_rule { - struct nft_rule *rule; -}; - -#define nft_trans_rule(trans) \ - (((struct nft_trans_rule *)trans->data)->rule) - -struct nft_trans_set { - struct nft_set *set; - u32 set_id; -}; - -#define nft_trans_set(trans) \ - (((struct nft_trans_set *)trans->data)->set) -#define nft_trans_set_id(trans) \ - (((struct nft_trans_set *)trans->data)->set_id) - -struct nft_trans_chain { - bool update; - char name[NFT_CHAIN_MAXNAMELEN]; - struct nft_stats __percpu *stats; - u8 policy; -}; - -#define nft_trans_chain_update(trans) \ - (((struct nft_trans_chain *)trans->data)->update) -#define nft_trans_chain_name(trans) \ - (((struct nft_trans_chain *)trans->data)->name) -#define nft_trans_chain_stats(trans) \ - (((struct nft_trans_chain *)trans->data)->stats) -#define nft_trans_chain_policy(trans) \ - (((struct nft_trans_chain *)trans->data)->policy) - -struct nft_trans_table { - bool update; - bool enable; -}; - -#define nft_trans_table_update(trans) \ - (((struct nft_trans_table *)trans->data)->update) -#define nft_trans_table_enable(trans) \ - (((struct nft_trans_table *)trans->data)->enable) - -struct nft_trans_elem { - struct nft_set *set; - struct nft_set_elem elem; -}; - -#define nft_trans_elem_set(trans) \ - (((struct nft_trans_elem *)trans->data)->set) -#define nft_trans_elem(trans) \ - (((struct nft_trans_elem *)trans->data)->elem) - static inline struct nft_expr *nft_expr_first(const struct nft_rule *rule) { return (struct nft_expr *)&rule->data[0]; @@ -528,6 +460,25 @@ enum nft_chain_type { NFT_CHAIN_T_MAX }; +/** + * struct nf_chain_type - nf_tables chain type info + * + * @name: name of the type + * @type: numeric identifier + * @family: address family + * @owner: module owner + * @hook_mask: mask of valid hooks + * @hooks: hookfn overrides + */ +struct nf_chain_type { + const char *name; + enum nft_chain_type type; + int family; + struct module *owner; + unsigned int hook_mask; + nf_hookfn *hooks[NF_MAX_HOOKS]; +}; + int nft_chain_validate_dependency(const struct nft_chain *chain, enum nft_chain_type type); int nft_chain_validate_hooks(const struct nft_chain *chain, @@ -614,25 +565,6 @@ struct nft_af_info { int nft_register_afinfo(struct net *, struct nft_af_info *); void nft_unregister_afinfo(struct nft_af_info *); -/** - * struct nf_chain_type - nf_tables chain type info - * - * @name: name of the type - * @type: numeric identifier - * @family: address family - * @owner: module owner - * @hook_mask: mask of valid hooks - * @hooks: hookfn overrides - */ -struct nf_chain_type { - const char *name; - enum nft_chain_type type; - int family; - struct module *owner; - unsigned int hook_mask; - nf_hookfn *hooks[NF_MAX_HOOKS]; -}; - int nft_register_chain_type(const struct nf_chain_type *); void nft_unregister_chain_type(const struct nf_chain_type *); @@ -657,4 +589,72 @@ void nft_unregister_expr(struct nft_expr_type *); #define MODULE_ALIAS_NFT_SET() \ MODULE_ALIAS("nft-set") +/** + * struct nft_trans - nf_tables object update in transaction + * + * @list: used internally + * @msg_type: message type + * @ctx: transaction context + * @data: internal information related to the transaction + */ +struct nft_trans { + struct list_head list; + int msg_type; + struct nft_ctx ctx; + char data[0]; +}; + +struct nft_trans_rule { + struct nft_rule *rule; +}; + +#define nft_trans_rule(trans) \ + (((struct nft_trans_rule *)trans->data)->rule) + +struct nft_trans_set { + struct nft_set *set; + u32 set_id; +}; + +#define nft_trans_set(trans) \ + (((struct nft_trans_set *)trans->data)->set) +#define nft_trans_set_id(trans) \ + (((struct nft_trans_set *)trans->data)->set_id) + +struct nft_trans_chain { + bool update; + char name[NFT_CHAIN_MAXNAMELEN]; + struct nft_stats __percpu *stats; + u8 policy; +}; + +#define nft_trans_chain_update(trans) \ + (((struct nft_trans_chain *)trans->data)->update) +#define nft_trans_chain_name(trans) \ + (((struct nft_trans_chain *)trans->data)->name) +#define nft_trans_chain_stats(trans) \ + (((struct nft_trans_chain *)trans->data)->stats) +#define nft_trans_chain_policy(trans) \ + (((struct nft_trans_chain *)trans->data)->policy) + +struct nft_trans_table { + bool update; + bool enable; +}; + +#define nft_trans_table_update(trans) \ + (((struct nft_trans_table *)trans->data)->update) +#define nft_trans_table_enable(trans) \ + (((struct nft_trans_table *)trans->data)->enable) + +struct nft_trans_elem { + struct nft_set *set; + struct nft_set_elem elem; +}; + +#define nft_trans_elem_set(trans) \ + (((struct nft_trans_elem *)trans->data)->set) +#define nft_trans_elem(trans) \ + (((struct nft_trans_elem *)trans->data)->elem) + #endif /* _NET_NF_TABLES_H */ -- cgit v1.2.3 From 1cae565e8b746f484f1ff1b71d2a1c89d7cf0668 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Thu, 5 Mar 2015 15:05:36 +0100 Subject: netfilter: nf_tables: limit maximum table name length to 32 bytes Set the same as we use for chain names, it should be enough. Signed-off-by: Pablo Neira Ayuso --- include/net/netfilter/nf_tables.h | 2 +- include/uapi/linux/netfilter/nf_tables.h | 1 + net/netfilter/nf_tables_api.c | 7 ++++--- 3 files changed, 6 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/include/net/netfilter/nf_tables.h b/include/net/netfilter/nf_tables.h index 04188b47629d..a143acafa5d9 100644 --- a/include/net/netfilter/nf_tables.h +++ b/include/net/netfilter/nf_tables.h @@ -535,7 +535,7 @@ struct nft_table { u64 hgenerator; u32 use; u16 flags; - char name[]; + char name[NFT_TABLE_MAXNAMELEN]; }; /** diff --git a/include/uapi/linux/netfilter/nf_tables.h b/include/uapi/linux/netfilter/nf_tables.h index 832bc46db78b..b9783931503b 100644 --- a/include/uapi/linux/netfilter/nf_tables.h +++ b/include/uapi/linux/netfilter/nf_tables.h @@ -1,6 +1,7 @@ #ifndef _LINUX_NF_TABLES_H #define _LINUX_NF_TABLES_H +#define NFT_TABLE_MAXNAMELEN 32 #define NFT_CHAIN_MAXNAMELEN 32 #define NFT_USERDATA_MAXLEN 256 diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c index 199fd0f27b0e..284b20ce566b 100644 --- a/net/netfilter/nf_tables_api.c +++ b/net/netfilter/nf_tables_api.c @@ -401,7 +401,8 @@ nf_tables_chain_type_lookup(const struct nft_af_info *afi, } static const struct nla_policy nft_table_policy[NFTA_TABLE_MAX + 1] = { - [NFTA_TABLE_NAME] = { .type = NLA_STRING }, + [NFTA_TABLE_NAME] = { .type = NLA_STRING, + .len = NFT_TABLE_MAXNAMELEN - 1 }, [NFTA_TABLE_FLAGS] = { .type = NLA_U32 }, }; @@ -686,13 +687,13 @@ static int nf_tables_newtable(struct sock *nlsk, struct sk_buff *skb, if (!try_module_get(afi->owner)) return -EAFNOSUPPORT; - table = kzalloc(sizeof(*table) + nla_len(name), GFP_KERNEL); + table = kzalloc(sizeof(*table), GFP_KERNEL); if (table == NULL) { module_put(afi->owner); return -ENOMEM; } - nla_strlcpy(table->name, name, nla_len(name)); + nla_strlcpy(table->name, name, NFT_TABLE_MAXNAMELEN); INIT_LIST_HEAD(&table->chains); INIT_LIST_HEAD(&table->sets); table->flags = flags; -- cgit v1.2.3 From d0f91938bede204a343473792529e0db7d599836 Mon Sep 17 00:00:00 2001 From: Erik Hugne Date: Thu, 5 Mar 2015 10:23:49 +0100 Subject: tipc: add ip/udp media type The ip/udp bearer can be configured in a point-to-point mode by specifying both local and remote ip/hostname, or it can be enabled in multicast mode, where links are established to all tipc nodes that have joined the same multicast group. The multicast IP address is generated based on the TIPC network ID, but can be overridden by using another multicast address as remote ip. Signed-off-by: Erik Hugne Reviewed-by: Jon Maloy Reviewed-by: Ying Xue Signed-off-by: David S. Miller --- include/uapi/linux/tipc_netlink.h | 9 + net/tipc/Kconfig | 8 + net/tipc/Makefile | 1 + net/tipc/bearer.c | 13 +- net/tipc/bearer.h | 12 +- net/tipc/msg.h | 2 +- net/tipc/udp_media.c | 442 ++++++++++++++++++++++++++++++++++++++ 7 files changed, 479 insertions(+), 8 deletions(-) create mode 100644 net/tipc/udp_media.c (limited to 'include') diff --git a/include/uapi/linux/tipc_netlink.h b/include/uapi/linux/tipc_netlink.h index 8d723824ad69..d4c8f142ba63 100644 --- a/include/uapi/linux/tipc_netlink.h +++ b/include/uapi/linux/tipc_netlink.h @@ -83,11 +83,20 @@ enum { TIPC_NLA_BEARER_NAME, /* string */ TIPC_NLA_BEARER_PROP, /* nest */ TIPC_NLA_BEARER_DOMAIN, /* u32 */ + TIPC_NLA_BEARER_UDP_OPTS, /* nest */ __TIPC_NLA_BEARER_MAX, TIPC_NLA_BEARER_MAX = __TIPC_NLA_BEARER_MAX - 1 }; +enum { + TIPC_NLA_UDP_UNSPEC, + TIPC_NLA_UDP_LOCAL, /* sockaddr_storage */ + TIPC_NLA_UDP_REMOTE, /* sockaddr_storage */ + + __TIPC_NLA_UDP_MAX, + TIPC_NLA_UDP_MAX = __TIPC_NLA_UDP_MAX - 1 +}; /* Socket info */ enum { TIPC_NLA_SOCK_UNSPEC, diff --git a/net/tipc/Kconfig b/net/tipc/Kconfig index 91c8a8e031db..c25a3a149dc4 100644 --- a/net/tipc/Kconfig +++ b/net/tipc/Kconfig @@ -26,3 +26,11 @@ config TIPC_MEDIA_IB help Saying Y here will enable support for running TIPC on IP-over-InfiniBand devices. +config TIPC_MEDIA_UDP + bool "IP/UDP media type support" + depends on TIPC + select NET_UDP_TUNNEL + help + Saying Y here will enable support for running TIPC over IP/UDP + bool + default y diff --git a/net/tipc/Makefile b/net/tipc/Makefile index 599b1a540d2b..57e460be4692 100644 --- a/net/tipc/Makefile +++ b/net/tipc/Makefile @@ -10,5 +10,6 @@ tipc-y += addr.o bcast.o bearer.o \ netlink.o netlink_compat.o node.o socket.o eth_media.o \ server.o socket.o +tipc-$(CONFIG_TIPC_MEDIA_UDP) += udp_media.o tipc-$(CONFIG_TIPC_MEDIA_IB) += ib_media.o tipc-$(CONFIG_SYSCTL) += sysctl.o diff --git a/net/tipc/bearer.c b/net/tipc/bearer.c index af6deeb397a8..840db89e4283 100644 --- a/net/tipc/bearer.c +++ b/net/tipc/bearer.c @@ -47,6 +47,9 @@ static struct tipc_media * const media_info_array[] = { ð_media_info, #ifdef CONFIG_TIPC_MEDIA_IB &ib_media_info, +#endif +#ifdef CONFIG_TIPC_MEDIA_UDP + &udp_media_info, #endif NULL }; @@ -216,7 +219,8 @@ void tipc_bearer_remove_dest(struct net *net, u32 bearer_id, u32 dest) * tipc_enable_bearer - enable bearer with the given name */ static int tipc_enable_bearer(struct net *net, const char *name, - u32 disc_domain, u32 priority) + u32 disc_domain, u32 priority, + struct nlattr *attr[]) { struct tipc_net *tn = net_generic(net, tipc_net_id); struct tipc_bearer *b_ptr; @@ -304,7 +308,7 @@ restart: strcpy(b_ptr->name, name); b_ptr->media = m_ptr; - res = m_ptr->enable_media(net, b_ptr); + res = m_ptr->enable_media(net, b_ptr, attr); if (res) { pr_warn("Bearer <%s> rejected, enable failure (%d)\n", name, -res); @@ -372,7 +376,8 @@ static void bearer_disable(struct net *net, struct tipc_bearer *b_ptr, kfree_rcu(b_ptr, rcu); } -int tipc_enable_l2_media(struct net *net, struct tipc_bearer *b) +int tipc_enable_l2_media(struct net *net, struct tipc_bearer *b, + struct nlattr *attr[]) { struct net_device *dev; char *driver_name = strchr((const char *)b->name, ':') + 1; @@ -791,7 +796,7 @@ int tipc_nl_bearer_enable(struct sk_buff *skb, struct genl_info *info) } rtnl_lock(); - err = tipc_enable_bearer(net, bearer, domain, prio); + err = tipc_enable_bearer(net, bearer, domain, prio, attrs); if (err) { rtnl_unlock(); return err; diff --git a/net/tipc/bearer.h b/net/tipc/bearer.h index 097aff08ad5b..5cad243ee8fc 100644 --- a/net/tipc/bearer.h +++ b/net/tipc/bearer.h @@ -41,7 +41,7 @@ #include #define MAX_BEARERS 2 -#define MAX_MEDIA 2 +#define MAX_MEDIA 3 #define MAX_NODES 4096 #define WSIZE 32 @@ -59,6 +59,7 @@ */ #define TIPC_MEDIA_TYPE_ETH 1 #define TIPC_MEDIA_TYPE_IB 2 +#define TIPC_MEDIA_TYPE_UDP 3 /** * struct tipc_node_map - set of node identifiers @@ -104,7 +105,8 @@ struct tipc_media { int (*send_msg)(struct net *net, struct sk_buff *buf, struct tipc_bearer *b_ptr, struct tipc_media_addr *dest); - int (*enable_media)(struct net *net, struct tipc_bearer *b_ptr); + int (*enable_media)(struct net *net, struct tipc_bearer *b_ptr, + struct nlattr *attr[]); void (*disable_media)(struct tipc_bearer *b_ptr); int (*addr2str)(struct tipc_media_addr *addr, char *strbuf, @@ -183,6 +185,9 @@ extern struct tipc_media eth_media_info; #ifdef CONFIG_TIPC_MEDIA_IB extern struct tipc_media ib_media_info; #endif +#ifdef CONFIG_TIPC_MEDIA_UDP +extern struct tipc_media udp_media_info; +#endif int tipc_nl_bearer_disable(struct sk_buff *skb, struct genl_info *info); int tipc_nl_bearer_enable(struct sk_buff *skb, struct genl_info *info); @@ -197,7 +202,8 @@ int tipc_nl_media_set(struct sk_buff *skb, struct genl_info *info); int tipc_media_set_priority(const char *name, u32 new_value); int tipc_media_set_window(const char *name, u32 new_value); void tipc_media_addr_printf(char *buf, int len, struct tipc_media_addr *a); -int tipc_enable_l2_media(struct net *net, struct tipc_bearer *b); +int tipc_enable_l2_media(struct net *net, struct tipc_bearer *b, + struct nlattr *attrs[]); void tipc_disable_l2_media(struct tipc_bearer *b); int tipc_l2_send_msg(struct net *net, struct sk_buff *buf, struct tipc_bearer *b, struct tipc_media_addr *dest); diff --git a/net/tipc/msg.h b/net/tipc/msg.h index c1cc8d7a5d52..fa167846d1ab 100644 --- a/net/tipc/msg.h +++ b/net/tipc/msg.h @@ -87,7 +87,7 @@ struct plist; * Note: Headroom should be a multiple of 4 to ensure the TIPC header fields * are word aligned for quicker access */ -#define BUF_HEADROOM LL_MAX_HEADER +#define BUF_HEADROOM (LL_MAX_HEADER + 48) struct tipc_skb_cb { void *handle; diff --git a/net/tipc/udp_media.c b/net/tipc/udp_media.c new file mode 100644 index 000000000000..0d10001db40d --- /dev/null +++ b/net/tipc/udp_media.c @@ -0,0 +1,442 @@ +/* net/tipc/udp_media.c: IP bearer support for TIPC + * + * Copyright (c) 2015, Ericsson AB + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the names of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "core.h" +#include "bearer.h" + +/* IANA assigned UDP port */ +#define UDP_PORT_DEFAULT 6118 + +static const struct nla_policy tipc_nl_udp_policy[TIPC_NLA_UDP_MAX + 1] = { + [TIPC_NLA_UDP_UNSPEC] = {.type = NLA_UNSPEC}, + [TIPC_NLA_UDP_LOCAL] = {.type = NLA_BINARY, + .len = sizeof(struct sockaddr_storage)}, + [TIPC_NLA_UDP_REMOTE] = {.type = NLA_BINARY, + .len = sizeof(struct sockaddr_storage)}, +}; + +/** + * struct udp_media_addr - IP/UDP addressing information + * + * This is the bearer level originating address used in neighbor discovery + * messages, and all fields should be in network byte order + */ +struct udp_media_addr { + __be16 proto; + __be16 udp_port; + union { + struct in_addr ipv4; + struct in6_addr ipv6; + }; +}; + +/** + * struct udp_bearer - ip/udp bearer data structure + * @bearer: associated generic tipc bearer + * @ubsock: bearer associated socket + * @ifindex: local address scope + * @work: used to schedule deferred work on a bearer + */ +struct udp_bearer { + struct tipc_bearer __rcu *bearer; + struct socket *ubsock; + u32 ifindex; + struct work_struct work; +}; + +/* udp_media_addr_set - convert a ip/udp address to a TIPC media address */ +static void tipc_udp_media_addr_set(struct tipc_media_addr *addr, + struct udp_media_addr *ua) +{ + memset(addr, 0, sizeof(struct tipc_media_addr)); + addr->media_id = TIPC_MEDIA_TYPE_UDP; + memcpy(addr->value, ua, sizeof(struct udp_media_addr)); + if (ntohs(ua->proto) == ETH_P_IP) { + if (ipv4_is_multicast(ua->ipv4.s_addr)) + addr->broadcast = 1; + } else if (ntohs(ua->proto) == ETH_P_IPV6) { + if (ipv6_addr_type(&ua->ipv6) & IPV6_ADDR_MULTICAST) + addr->broadcast = 1; + } else { + pr_err("Invalid UDP media address\n"); + } +} + +/* tipc_udp_addr2str - convert ip/udp address to string */ +static int tipc_udp_addr2str(struct tipc_media_addr *a, char *buf, int size) +{ + struct udp_media_addr *ua = (struct udp_media_addr *)&a->value; + + if (ntohs(ua->proto) == ETH_P_IP) + snprintf(buf, size, "%pI4:%u", &ua->ipv4, ntohs(ua->udp_port)); + else if (ntohs(ua->proto) == ETH_P_IPV6) + snprintf(buf, size, "%pI6:%u", &ua->ipv6, ntohs(ua->udp_port)); + else + pr_err("Invalid UDP media address\n"); + return 0; +} + +/* tipc_udp_msg2addr - extract an ip/udp address from a TIPC ndisc message */ +static int tipc_udp_msg2addr(struct tipc_bearer *b, struct tipc_media_addr *a, + char *msg) +{ + struct udp_media_addr *ua; + + ua = (struct udp_media_addr *) (msg + TIPC_MEDIA_ADDR_OFFSET); + if (msg[TIPC_MEDIA_TYPE_OFFSET] != TIPC_MEDIA_TYPE_UDP) + return -EINVAL; + tipc_udp_media_addr_set(a, ua); + return 0; +} + +/* tipc_udp_addr2msg - write an ip/udp address to a TIPC ndisc message */ +static int tipc_udp_addr2msg(char *msg, struct tipc_media_addr *a) +{ + memset(msg, 0, TIPC_MEDIA_INFO_SIZE); + msg[TIPC_MEDIA_TYPE_OFFSET] = TIPC_MEDIA_TYPE_UDP; + memcpy(msg + TIPC_MEDIA_ADDR_OFFSET, a->value, + sizeof(struct udp_media_addr)); + return 0; +} + +/* tipc_send_msg - enqueue a send request */ +static int tipc_udp_send_msg(struct net *net, struct sk_buff *skb, + struct tipc_bearer *b, + struct tipc_media_addr *dest) +{ + int ttl, err = 0; + struct udp_bearer *ub; + struct udp_media_addr *dst = (struct udp_media_addr *)&dest->value; + struct udp_media_addr *src = (struct udp_media_addr *)&b->addr.value; + struct sk_buff *clone; + struct rtable *rt; + + clone = skb_clone(skb, GFP_ATOMIC); + skb_set_inner_protocol(clone, htons(ETH_P_TIPC)); + ub = rcu_dereference_rtnl(b->media_ptr); + if (!ub) { + err = -ENODEV; + goto tx_error; + } + if (htons(dst->proto) == ETH_P_IP) { + struct flowi4 fl = { + .daddr = dst->ipv4.s_addr, + .saddr = src->ipv4.s_addr, + .flowi4_mark = clone->mark, + .flowi4_proto = IPPROTO_UDP + }; + rt = ip_route_output_key(net, &fl); + if (IS_ERR(rt)) { + err = PTR_ERR(rt); + goto tx_error; + } + ttl = ip4_dst_hoplimit(&rt->dst); + err = udp_tunnel_xmit_skb(rt, clone, src->ipv4.s_addr, + dst->ipv4.s_addr, 0, ttl, 0, + src->udp_port, dst->udp_port, + false, true); + if (err < 0) { + ip_rt_put(rt); + goto tx_error; + } +#if IS_ENABLED(CONFIG_IPV6) + } else { + struct dst_entry *ndst; + struct flowi6 fl6 = { + .flowi6_oif = ub->ifindex, + .daddr = dst->ipv6, + .saddr = src->ipv6, + .flowi6_proto = IPPROTO_UDP + }; + err = ip6_dst_lookup(ub->ubsock->sk, &ndst, &fl6); + if (err) + goto tx_error; + ttl = ip6_dst_hoplimit(ndst); + err = udp_tunnel6_xmit_skb(ndst, clone, ndst->dev, &src->ipv6, + &dst->ipv6, 0, ttl, src->udp_port, + dst->udp_port, false); +#endif + } + return err; + +tx_error: + kfree_skb(clone); + return err; +} + +/* tipc_udp_recv - read data from bearer socket */ +static int tipc_udp_recv(struct sock *sk, struct sk_buff *skb) +{ + struct udp_bearer *ub; + struct tipc_bearer *b; + + ub = rcu_dereference_sk_user_data(sk); + if (!ub) { + pr_err_ratelimited("Failed to get UDP bearer reference"); + kfree_skb(skb); + return 0; + } + + skb_pull(skb, sizeof(struct udphdr)); + rcu_read_lock(); + b = rcu_dereference_rtnl(ub->bearer); + + if (b) { + tipc_rcv(sock_net(sk), skb, b); + rcu_read_unlock(); + return 0; + } + rcu_read_unlock(); + kfree_skb(skb); + return 0; +} + +static int enable_mcast(struct udp_bearer *ub, struct udp_media_addr *remote) +{ + int err = 0; + struct ip_mreqn mreqn; + struct sock *sk = ub->ubsock->sk; + + if (ntohs(remote->proto) == ETH_P_IP) { + if (!ipv4_is_multicast(remote->ipv4.s_addr)) + return 0; + mreqn.imr_multiaddr = remote->ipv4; + mreqn.imr_ifindex = ub->ifindex; + err = __ip_mc_join_group(sk, &mreqn); + } else { + if (!ipv6_addr_is_multicast(&remote->ipv6)) + return 0; + err = __ipv6_sock_mc_join(sk, ub->ifindex, &remote->ipv6); + } + return err; +} + +/** + * parse_options - build local/remote addresses from configuration + * @attrs: netlink config data + * @ub: UDP bearer instance + * @local: local bearer IP address/port + * @remote: peer or multicast IP/port + */ +static int parse_options(struct nlattr *attrs[], struct udp_bearer *ub, + struct udp_media_addr *local, + struct udp_media_addr *remote) +{ + struct nlattr *opts[TIPC_NLA_UDP_MAX + 1]; + struct sockaddr_storage *sa_local, *sa_remote; + + if (!attrs[TIPC_NLA_BEARER_UDP_OPTS]) + goto err; + if (nla_parse_nested(opts, TIPC_NLA_UDP_MAX, + attrs[TIPC_NLA_BEARER_UDP_OPTS], + tipc_nl_udp_policy)) + goto err; + if (opts[TIPC_NLA_UDP_LOCAL] && opts[TIPC_NLA_UDP_REMOTE]) { + sa_local = nla_data(opts[TIPC_NLA_UDP_LOCAL]); + sa_remote = nla_data(opts[TIPC_NLA_UDP_REMOTE]); + } else { +err: + pr_err("Invalid UDP bearer configuration"); + return -EINVAL; + } + if ((sa_local->ss_family & sa_remote->ss_family) == AF_INET) { + struct sockaddr_in *ip4; + + ip4 = (struct sockaddr_in *)sa_local; + local->proto = htons(ETH_P_IP); + local->udp_port = ip4->sin_port; + local->ipv4.s_addr = ip4->sin_addr.s_addr; + + ip4 = (struct sockaddr_in *)sa_remote; + remote->proto = htons(ETH_P_IP); + remote->udp_port = ip4->sin_port; + remote->ipv4.s_addr = ip4->sin_addr.s_addr; + return 0; + +#if IS_ENABLED(CONFIG_IPV6) + } else if ((sa_local->ss_family & sa_remote->ss_family) == AF_INET6) { + struct sockaddr_in6 *ip6; + + ip6 = (struct sockaddr_in6 *)sa_local; + local->proto = htons(ETH_P_IPV6); + local->udp_port = ip6->sin6_port; + local->ipv6 = ip6->sin6_addr; + ub->ifindex = ip6->sin6_scope_id; + + ip6 = (struct sockaddr_in6 *)sa_remote; + remote->proto = htons(ETH_P_IPV6); + remote->udp_port = ip6->sin6_port; + remote->ipv6 = ip6->sin6_addr; + return 0; +#endif + } + return -EADDRNOTAVAIL; +} + +/** + * tipc_udp_enable - callback to create a new udp bearer instance + * @net: network namespace + * @b: pointer to generic tipc_bearer + * @attrs: netlink bearer configuration + * + * validate the bearer parameters and initialize the udp bearer + * rtnl_lock should be held + */ +static int tipc_udp_enable(struct net *net, struct tipc_bearer *b, + struct nlattr *attrs[]) +{ + int err = -EINVAL; + struct udp_bearer *ub; + struct udp_media_addr *remote; + struct udp_media_addr local = {0}; + struct udp_port_cfg udp_conf = {0}; + struct udp_tunnel_sock_cfg tuncfg = {0}; + + ub = kzalloc(sizeof(*ub), GFP_ATOMIC); + if (!ub) + return -ENOMEM; + + remote = (struct udp_media_addr *)&b->bcast_addr.value; + memset(remote, 0, sizeof(struct udp_media_addr)); + err = parse_options(attrs, ub, &local, remote); + if (err) + goto err; + + b->bcast_addr.media_id = TIPC_MEDIA_TYPE_UDP; + b->bcast_addr.broadcast = 1; + rcu_assign_pointer(b->media_ptr, ub); + rcu_assign_pointer(ub->bearer, b); + tipc_udp_media_addr_set(&b->addr, &local); + if (htons(local.proto) == ETH_P_IP) { + struct net_device *dev; + + dev = __ip_dev_find(net, local.ipv4.s_addr, false); + if (!dev) { + err = -ENODEV; + goto err; + } + udp_conf.family = AF_INET; + udp_conf.local_ip.s_addr = htonl(INADDR_ANY); + udp_conf.use_udp_checksums = false; + ub->ifindex = dev->ifindex; + b->mtu = dev->mtu - sizeof(struct iphdr) + - sizeof(struct udphdr); +#if IS_ENABLED(CONFIG_IPV6) + } else if (htons(local.proto) == ETH_P_IPV6) { + udp_conf.family = AF_INET6; + udp_conf.use_udp6_tx_checksums = true; + udp_conf.use_udp6_rx_checksums = true; + udp_conf.local_ip6 = in6addr_any; + b->mtu = 1280; +#endif + } else { + err = -EAFNOSUPPORT; + goto err; + } + udp_conf.local_udp_port = local.udp_port; + err = udp_sock_create(net, &udp_conf, &ub->ubsock); + if (err) + goto err; + tuncfg.sk_user_data = ub; + tuncfg.encap_type = 1; + tuncfg.encap_rcv = tipc_udp_recv; + tuncfg.encap_destroy = NULL; + setup_udp_tunnel_sock(net, ub->ubsock, &tuncfg); + + if (enable_mcast(ub, remote)) + goto err; + return 0; +err: + kfree(ub); + return err; +} + +/* cleanup_bearer - break the socket/bearer association */ +static void cleanup_bearer(struct work_struct *work) +{ + struct udp_bearer *ub = container_of(work, struct udp_bearer, work); + + if (ub->ubsock) + udp_tunnel_sock_release(ub->ubsock); + synchronize_net(); + kfree(ub); +} + +/* tipc_udp_disable - detach bearer from socket */ +static void tipc_udp_disable(struct tipc_bearer *b) +{ + struct udp_bearer *ub; + + ub = rcu_dereference_rtnl(b->media_ptr); + if (!ub) { + pr_err("UDP bearer instance not found\n"); + return; + } + if (ub->ubsock) + sock_set_flag(ub->ubsock->sk, SOCK_DEAD); + RCU_INIT_POINTER(b->media_ptr, NULL); + RCU_INIT_POINTER(ub->bearer, NULL); + + /* sock_release need to be done outside of rtnl lock */ + INIT_WORK(&ub->work, cleanup_bearer); + schedule_work(&ub->work); +} + +struct tipc_media udp_media_info = { + .send_msg = tipc_udp_send_msg, + .enable_media = tipc_udp_enable, + .disable_media = tipc_udp_disable, + .addr2str = tipc_udp_addr2str, + .addr2msg = tipc_udp_addr2msg, + .msg2addr = tipc_udp_msg2addr, + .priority = TIPC_DEF_LINK_PRI, + .tolerance = TIPC_DEF_LINK_TOL, + .window = TIPC_DEF_LINK_WIN, + .type_id = TIPC_MEDIA_TYPE_UDP, + .hwaddr_len = 0, + .name = "udp" +}; -- cgit v1.2.3 From 59299031038f3ea92cf484bc4a68d16ad4bb3050 Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Thu, 5 Mar 2015 12:35:07 -0800 Subject: net: dsa: let switches specify their tagging protocol In order to support the new DSA device driver model, a dsa_switch should be able to advertise the type of tagging protocol supported by the underlying switch device. This also removes constraints on how tagging can be stacked to each other. Signed-off-by: Florian Fainelli Signed-off-by: David S. Miller --- include/net/dsa.h | 5 +++++ net/dsa/dsa.c | 5 +++-- 2 files changed, 8 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/net/dsa.h b/include/net/dsa.h index c542c131d551..b525ac516559 100644 --- a/include/net/dsa.h +++ b/include/net/dsa.h @@ -127,6 +127,11 @@ struct dsa_switch { struct dsa_switch_tree *dst; int index; + /* + * Tagging protocol understood by this switch + */ + enum dsa_tag_protocol tag_protocol; + /* * Configuration data for this switch. */ diff --git a/net/dsa/dsa.c b/net/dsa/dsa.c index 6f02ccc57593..4cc995664fdf 100644 --- a/net/dsa/dsa.c +++ b/net/dsa/dsa.c @@ -227,7 +227,7 @@ static int dsa_switch_setup_one(struct dsa_switch *ds, struct device *parent) * switch. */ if (dst->cpu_switch == index) { - switch (drv->tag_protocol) { + switch (ds->tag_protocol) { #ifdef CONFIG_NET_DSA_TAG_DSA case DSA_TAG_PROTO_DSA: dst->rcv = dsa_netdev_ops.rcv; @@ -255,7 +255,7 @@ static int dsa_switch_setup_one(struct dsa_switch *ds, struct device *parent) goto out; } - dst->tag_protocol = drv->tag_protocol; + dst->tag_protocol = ds->tag_protocol; } /* @@ -364,6 +364,7 @@ dsa_switch_setup(struct dsa_switch_tree *dst, int index, ds->index = index; ds->pd = pd; ds->drv = drv; + ds->tag_protocol = drv->tag_protocol; ds->master_dev = host_dev; ret = dsa_switch_setup_one(ds, parent); -- cgit v1.2.3 From 37ed9493699cc5dafe1b8725858ef73176fdc9d2 Mon Sep 17 00:00:00 2001 From: Scott Feldman Date: Thu, 5 Mar 2015 21:21:13 -0800 Subject: rtnetlink: add RTNH_F_EXTERNAL flag for fib offload Add new RTNH_F_EXTERNAL flag to mark fib entries offloaded externally, for example to a switchdev switch device. Signed-off-by: Scott Feldman Signed-off-by: David S. Miller --- include/uapi/linux/rtnetlink.h | 1 + 1 file changed, 1 insertion(+) (limited to 'include') diff --git a/include/uapi/linux/rtnetlink.h b/include/uapi/linux/rtnetlink.h index 06f75a407f74..c3722b024e73 100644 --- a/include/uapi/linux/rtnetlink.h +++ b/include/uapi/linux/rtnetlink.h @@ -334,6 +334,7 @@ struct rtnexthop { #define RTNH_F_DEAD 1 /* Nexthop is dead (used by multipath) */ #define RTNH_F_PERVASIVE 2 /* Do recursive gateway lookup */ #define RTNH_F_ONLINK 4 /* Gateway is forced on link */ +#define RTNH_F_EXTERNAL 8 /* Route installed externally */ /* Macros to handle hexthops */ -- cgit v1.2.3 From 4586f1bb911ce219a4bc1c2a9d6eee2e058b2b51 Mon Sep 17 00:00:00 2001 From: Scott Feldman Date: Thu, 5 Mar 2015 21:21:14 -0800 Subject: netdevice: add IPv4 fib add/del ops Add two new ndo ops for IPv4 fib offload support, add and del. Add uses modifiy semantics if fib entry already offloaded. Drivers implementing the new ndo ops will return err<0 if programming device fails, for example if device's tables are full. Signed-off-by: Scott Feldman Signed-off-by: David S. Miller --- include/linux/netdevice.h | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) (limited to 'include') diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 625c8d71511b..45413784a3b1 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -768,6 +768,8 @@ struct netdev_phys_item_id { typedef u16 (*select_queue_fallback_t)(struct net_device *dev, struct sk_buff *skb); +struct fib_info; + /* * This structure defines the management hooks for network devices. * The following hooks can be defined; unless noted otherwise, they are @@ -1031,6 +1033,14 @@ typedef u16 (*select_queue_fallback_t)(struct net_device *dev, * int (*ndo_switch_port_stp_update)(struct net_device *dev, u8 state); * Called to notify switch device port of bridge port STP * state change. + * int (*ndo_sw_parent_fib_ipv4_add)(struct net_device *dev, __be32 dst, + * int dst_len, struct fib_info *fi, + * u8 tos, u8 type, u32 tb_id); + * Called to add/modify IPv4 route to switch device. + * int (*ndo_sw_parent_fib_ipv4_del)(struct net_device *dev, __be32 dst, + * int dst_len, struct fib_info *fi, + * u8 tos, u8 type, u32 tb_id); + * Called to delete IPv4 route from switch device. */ struct net_device_ops { int (*ndo_init)(struct net_device *dev); @@ -1192,6 +1202,18 @@ struct net_device_ops { struct netdev_phys_item_id *psid); int (*ndo_switch_port_stp_update)(struct net_device *dev, u8 state); + int (*ndo_switch_fib_ipv4_add)(struct net_device *dev, + __be32 dst, + int dst_len, + struct fib_info *fi, + u8 tos, u8 type, + u32 tb_id); + int (*ndo_switch_fib_ipv4_del)(struct net_device *dev, + __be32 dst, + int dst_len, + struct fib_info *fi, + u8 tos, u8 type, + u32 tb_id); #endif }; -- cgit v1.2.3 From 5e8d90497d65f528c54015644095ace6e330fd8e Mon Sep 17 00:00:00 2001 From: Scott Feldman Date: Thu, 5 Mar 2015 21:21:15 -0800 Subject: switchdev: add IPv4 fib ndo ops wrappers Add IPv4 fib ndo wrapper funcs and stub them out for now. Signed-off-by: Scott Feldman Signed-off-by: David S. Miller --- include/net/switchdev.h | 19 +++++++++++++++++++ net/switchdev/switchdev.c | 39 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 58 insertions(+) (limited to 'include') diff --git a/include/net/switchdev.h b/include/net/switchdev.h index cfcdac2e5d25..8d2ac663325a 100644 --- a/include/net/switchdev.h +++ b/include/net/switchdev.h @@ -51,6 +51,11 @@ int ndo_dflt_netdev_switch_port_bridge_dellink(struct net_device *dev, struct nlmsghdr *nlh, u16 flags); int ndo_dflt_netdev_switch_port_bridge_setlink(struct net_device *dev, struct nlmsghdr *nlh, u16 flags); +int netdev_switch_fib_ipv4_add(u32 dst, int dst_len, struct fib_info *fi, + u8 tos, u8 type, u32 tb_id); +int netdev_switch_fib_ipv4_del(u32 dst, int dst_len, struct fib_info *fi, + u8 tos, u8 type, u32 tb_id); + #else static inline int netdev_switch_parent_id_get(struct net_device *dev, @@ -109,6 +114,20 @@ static inline int ndo_dflt_netdev_switch_port_bridge_setlink(struct net_device * return 0; } +static inline int netdev_switch_fib_ipv4_add(u32 dst, int dst_len, + struct fib_info *fi, + u8 tos, u8 type, u32 tb_id) +{ + return 0; +} + +static inline int netdev_switch_fib_ipv4_del(u32 dst, int dst_len, + struct fib_info *fi, + u8 tos, u8 type, u32 tb_id) +{ + return 0; +} + #endif #endif /* _LINUX_SWITCHDEV_H_ */ diff --git a/net/switchdev/switchdev.c b/net/switchdev/switchdev.c index 8c1e558db118..3c090f8d071b 100644 --- a/net/switchdev/switchdev.c +++ b/net/switchdev/switchdev.c @@ -14,6 +14,7 @@ #include #include #include +#include #include /** @@ -225,3 +226,41 @@ int ndo_dflt_netdev_switch_port_bridge_dellink(struct net_device *dev, return ret; } EXPORT_SYMBOL(ndo_dflt_netdev_switch_port_bridge_dellink); + +/** + * netdev_switch_fib_ipv4_add - Add IPv4 route entry to switch + * + * @dst: route's IPv4 destination address + * @dst_len: destination address length (prefix length) + * @fi: route FIB info structure + * @tos: route TOS + * @type: route type + * @tb_id: route table ID + * + * Add IPv4 route entry to switch device. + */ +int netdev_switch_fib_ipv4_add(u32 dst, int dst_len, struct fib_info *fi, + u8 tos, u8 type, u32 tb_id) +{ + return 0; +} +EXPORT_SYMBOL(netdev_switch_fib_ipv4_add); + +/** + * netdev_switch_fib_ipv4_del - Delete IPv4 route entry from switch + * + * @dst: route's IPv4 destination address + * @dst_len: destination address length (prefix length) + * @fi: route FIB info structure + * @tos: route TOS + * @type: route type + * @tb_id: route table ID + * + * Delete IPv4 route entry from switch device. + */ +int netdev_switch_fib_ipv4_del(u32 dst, int dst_len, struct fib_info *fi, + u8 tos, u8 type, u32 tb_id) +{ + return 0; +} +EXPORT_SYMBOL(netdev_switch_fib_ipv4_del); -- cgit v1.2.3 From 104616e74e0b464d449fdd2ee2f547d2fad71610 Mon Sep 17 00:00:00 2001 From: Scott Feldman Date: Thu, 5 Mar 2015 21:21:16 -0800 Subject: switchdev: don't support custom ip rules, for now Keep switchdev FIB offload model simple for now and don't allow custom ip rules. Signed-off-by: Scott Feldman Signed-off-by: David S. Miller --- include/net/ip_fib.h | 2 ++ net/ipv4/fib_frontend.c | 13 ++++++++++ net/ipv4/fib_rules.c | 3 +++ net/ipv4/fib_trie.c | 61 +++++++++++++++++++++++++++++++++++++++++++++++ net/switchdev/switchdev.c | 4 ++++ 5 files changed, 83 insertions(+) (limited to 'include') diff --git a/include/net/ip_fib.h b/include/net/ip_fib.h index 825cb2800908..1657604c5dd3 100644 --- a/include/net/ip_fib.h +++ b/include/net/ip_fib.h @@ -196,6 +196,7 @@ int fib_table_delete(struct fib_table *, struct fib_config *); int fib_table_dump(struct fib_table *table, struct sk_buff *skb, struct netlink_callback *cb); int fib_table_flush(struct fib_table *table); +void fib_table_flush_external(struct fib_table *table); void fib_free_table(struct fib_table *tb); @@ -308,6 +309,7 @@ static inline int fib_num_tclassid_users(struct net *net) return 0; } #endif +void fib_flush_external(struct net *net); /* Exported by fib_semantics.c */ int ip_fib_check_default(__be32 gw, struct net_device *dev); diff --git a/net/ipv4/fib_frontend.c b/net/ipv4/fib_frontend.c index 220c4b4af4cf..e067770235bf 100644 --- a/net/ipv4/fib_frontend.c +++ b/net/ipv4/fib_frontend.c @@ -144,6 +144,19 @@ static void fib_flush(struct net *net) rt_cache_flush(net); } +void fib_flush_external(struct net *net) +{ + struct fib_table *tb; + struct hlist_head *head; + unsigned int h; + + for (h = 0; h < FIB_TABLE_HASHSZ; h++) { + head = &net->ipv4.fib_table_hash[h]; + hlist_for_each_entry(tb, head, tb_hlist) + fib_table_flush_external(tb); + } +} + /* * Find address type as if only "dev" was present in the system. If * on_dev is NULL then all interfaces are taken into consideration. diff --git a/net/ipv4/fib_rules.c b/net/ipv4/fib_rules.c index d3db718be51d..190d0d00d744 100644 --- a/net/ipv4/fib_rules.c +++ b/net/ipv4/fib_rules.c @@ -209,6 +209,8 @@ static int fib4_rule_configure(struct fib_rule *rule, struct sk_buff *skb, rule4->tos = frh->tos; net->ipv4.fib_has_custom_rules = true; + fib_flush_external(rule->fr_net); + err = 0; errout: return err; @@ -224,6 +226,7 @@ static void fib4_rule_delete(struct fib_rule *rule) net->ipv4.fib_num_tclassid_users--; #endif net->ipv4.fib_has_custom_rules = true; + fib_flush_external(rule->fr_net); } static int fib4_rule_compare(struct fib_rule *rule, struct fib_rule_hdr *frh, diff --git a/net/ipv4/fib_trie.c b/net/ipv4/fib_trie.c index fae34ad4bb1a..2de43956c9d0 100644 --- a/net/ipv4/fib_trie.c +++ b/net/ipv4/fib_trie.c @@ -1536,6 +1536,67 @@ found: return n; } +/* Caller must hold RTNL */ +void fib_table_flush_external(struct fib_table *tb) +{ + struct trie *t = (struct trie *)tb->tb_data; + struct fib_alias *fa; + struct tnode *n, *pn; + unsigned long cindex; + unsigned char slen; + int found = 0; + + n = rcu_dereference(t->trie); + if (!n) + return; + + pn = NULL; + cindex = 0; + + while (IS_TNODE(n)) { + /* record pn and cindex for leaf walking */ + pn = n; + cindex = 1ul << n->bits; +backtrace: + /* walk trie in reverse order */ + do { + while (!(cindex--)) { + t_key pkey = pn->key; + + n = pn; + pn = node_parent(n); + + /* resize completed node */ + resize(t, n); + + /* if we got the root we are done */ + if (!pn) + return; + + cindex = get_index(pkey, pn); + } + + /* grab the next available node */ + n = tnode_get_child(pn, cindex); + } while (!n); + } + + hlist_for_each_entry(fa, &n->leaf, fa_list) { + struct fib_info *fi = fa->fa_info; + + if (fi && (fi->fib_flags & RTNH_F_EXTERNAL)) { + netdev_switch_fib_ipv4_del(n->key, + KEYLENGTH - fa->fa_slen, + fi, fa->fa_tos, + fa->fa_type, tb->tb_id); + } + } + + /* if trie is leaf only loop is completed */ + if (pn) + goto backtrace; +} + /* Caller must hold RTNL. */ int fib_table_flush(struct fib_table *tb) { diff --git a/net/switchdev/switchdev.c b/net/switchdev/switchdev.c index 3c090f8d071b..81c4c0274b9b 100644 --- a/net/switchdev/switchdev.c +++ b/net/switchdev/switchdev.c @@ -242,6 +242,10 @@ EXPORT_SYMBOL(ndo_dflt_netdev_switch_port_bridge_dellink); int netdev_switch_fib_ipv4_add(u32 dst, int dst_len, struct fib_info *fi, u8 tos, u8 type, u32 tb_id) { + /* Don't offload route if using custom ip rules */ + if (fi->fib_net->ipv4.fib_has_custom_rules) + return 0; + return 0; } EXPORT_SYMBOL(netdev_switch_fib_ipv4_add); -- cgit v1.2.3 From 448b128a14501543748514a4f9adedd3c0da2e85 Mon Sep 17 00:00:00 2001 From: Scott Feldman Date: Thu, 5 Mar 2015 21:21:18 -0800 Subject: ipv4: add net bool fib_offload_disabled If something goes wrong with IPv4 FIB offload, mark entire net offload disabled. This is brute force policy to basically shut down IPv4 FIB offload permanently if there is a problem offloading any route to an external device. We can refine the policy in the future, to handle failures on a per-device or per-route basis, but for now, this policy is per-net. What we're trying to avoid is an inconsistent split between the kernel's FIB and the offload device's FIB. We don't want the device to fwd a pkt inconsitent with what the kernel would do. An example of a split is if device has 10.0.0.0/16 and kernel has 10.0.0.0/16 and 10.0.0.0/24, the device wouldn't see the longest prefix 10.0.0.0/24 and potentially forward pkts incorrectly. Limited capacity or limited capability are two ways a route may fail to install to the offload device. We'll not differentiate between failures at this time, and treat any failure as fatal and mark the net as fib_offload_disabled. Signed-off-by: Scott Feldman Signed-off-by: David S. Miller --- include/net/netns/ipv4.h | 1 + 1 file changed, 1 insertion(+) (limited to 'include') diff --git a/include/net/netns/ipv4.h b/include/net/netns/ipv4.h index db1db158a00e..1085e12f940f 100644 --- a/include/net/netns/ipv4.h +++ b/include/net/netns/ipv4.h @@ -47,6 +47,7 @@ struct netns_ipv4 { int fib_num_tclassid_users; #endif struct hlist_head *fib_table_hash; + bool fib_offload_disabled; struct sock *fibnl; struct sock * __percpu *icmp_sk; -- cgit v1.2.3 From 8e05fd7166c6123334b7a739a697d677747aa462 Mon Sep 17 00:00:00 2001 From: Scott Feldman Date: Thu, 5 Mar 2015 21:21:19 -0800 Subject: fib: hook IPv4 fib for hardware offload Call into the switchdev driver any time an IPv4 fib entry is added/modified/deleted from the kernel's FIB. The switchdev driver may or may not install the route to the offload device. In the case where the driver tries to install the route and something goes wrong (device's routing table is full, etc), then all of the offloaded routes will be flushed from the device, route forwarding falls back to the kernel, and no more routes are offloading. We can refine this logic later. For now, use the simplist model of offloading routes up to the point of failure, and then on failure, undo everything and mark IPv4 offloading disabled. Signed-off-by: Scott Feldman Signed-off-by: David S. Miller --- include/net/switchdev.h | 5 +++++ net/ipv4/fib_trie.c | 31 ++++++++++++++++++++++++++++++- net/switchdev/switchdev.c | 28 ++++++++++++++++++++++++++-- 3 files changed, 61 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/include/net/switchdev.h b/include/net/switchdev.h index 8d2ac663325a..dc0a5cc7c2c5 100644 --- a/include/net/switchdev.h +++ b/include/net/switchdev.h @@ -55,6 +55,7 @@ int netdev_switch_fib_ipv4_add(u32 dst, int dst_len, struct fib_info *fi, u8 tos, u8 type, u32 tb_id); int netdev_switch_fib_ipv4_del(u32 dst, int dst_len, struct fib_info *fi, u8 tos, u8 type, u32 tb_id); +void netdev_switch_fib_ipv4_abort(struct fib_info *fi); #else @@ -128,6 +129,10 @@ static inline int netdev_switch_fib_ipv4_del(u32 dst, int dst_len, return 0; } +void netdev_switch_fib_ipv4_abort(struct fib_info *fi) +{ +} + #endif #endif /* _LINUX_SWITCHDEV_H_ */ diff --git a/net/ipv4/fib_trie.c b/net/ipv4/fib_trie.c index 2de43956c9d0..6544f1a0cfa1 100644 --- a/net/ipv4/fib_trie.c +++ b/net/ipv4/fib_trie.c @@ -79,6 +79,7 @@ #include #include #include +#include #include "fib_lookup.h" #define MAX_STAT_DEPTH 32 @@ -1135,7 +1136,18 @@ int fib_table_insert(struct fib_table *tb, struct fib_config *cfg) new_fa->fa_state = state & ~FA_S_ACCESSED; new_fa->fa_slen = fa->fa_slen; + err = netdev_switch_fib_ipv4_add(key, plen, fi, + new_fa->fa_tos, + cfg->fc_type, + tb->tb_id); + if (err) { + netdev_switch_fib_ipv4_abort(fi); + kmem_cache_free(fn_alias_kmem, new_fa); + goto out; + } + hlist_replace_rcu(&fa->fa_list, &new_fa->fa_list); + alias_free_mem_rcu(fa); fib_release_info(fi_drop); @@ -1171,10 +1183,18 @@ int fib_table_insert(struct fib_table *tb, struct fib_config *cfg) new_fa->fa_state = 0; new_fa->fa_slen = slen; + /* (Optionally) offload fib entry to switch hardware. */ + err = netdev_switch_fib_ipv4_add(key, plen, fi, tos, + cfg->fc_type, tb->tb_id); + if (err) { + netdev_switch_fib_ipv4_abort(fi); + goto out_free_new_fa; + } + /* Insert new entry to the list. */ err = fib_insert_alias(t, tp, l, new_fa, fa, key); if (err) - goto out_free_new_fa; + goto out_sw_fib_del; if (!plen) tb->tb_num_default++; @@ -1185,6 +1205,8 @@ int fib_table_insert(struct fib_table *tb, struct fib_config *cfg) succeeded: return 0; +out_sw_fib_del: + netdev_switch_fib_ipv4_del(key, plen, fi, tos, cfg->fc_type, tb->tb_id); out_free_new_fa: kmem_cache_free(fn_alias_kmem, new_fa); out: @@ -1456,6 +1478,9 @@ int fib_table_delete(struct fib_table *tb, struct fib_config *cfg) if (!fa_to_delete) return -ESRCH; + netdev_switch_fib_ipv4_del(key, plen, fa_to_delete->fa_info, tos, + cfg->fc_type, tb->tb_id); + rtmsg_fib(RTM_DELROUTE, htonl(key), fa_to_delete, plen, tb->tb_id, &cfg->fc_nlinfo, 0); @@ -1650,6 +1675,10 @@ backtrace: struct fib_info *fi = fa->fa_info; if (fi && (fi->fib_flags & RTNH_F_DEAD)) { + netdev_switch_fib_ipv4_del(n->key, + KEYLENGTH - fa->fa_slen, + fi, fa->fa_tos, + fa->fa_type, tb->tb_id); hlist_del_rcu(&fa->fa_list); fib_release_info(fa->fa_info); alias_free_mem_rcu(fa); diff --git a/net/switchdev/switchdev.c b/net/switchdev/switchdev.c index 99907d829419..f4fd575aa2a3 100644 --- a/net/switchdev/switchdev.c +++ b/net/switchdev/switchdev.c @@ -305,8 +305,12 @@ int netdev_switch_fib_ipv4_add(u32 dst, int dst_len, struct fib_info *fi, const struct net_device_ops *ops; int err = 0; - /* Don't offload route if using custom ip rules */ - if (fi->fib_net->ipv4.fib_has_custom_rules) + /* Don't offload route if using custom ip rules or if + * IPv4 FIB offloading has been disabled completely. + */ + + if (fi->fib_net->ipv4.fib_has_custom_rules | + fi->fib_net->ipv4.fib_offload_disabled) return 0; dev = netdev_switch_get_dev_by_nhs(fi); @@ -362,3 +366,23 @@ int netdev_switch_fib_ipv4_del(u32 dst, int dst_len, struct fib_info *fi, return err; } EXPORT_SYMBOL(netdev_switch_fib_ipv4_del); + +/** + * netdev_switch_fib_ipv4_abort - Abort an IPv4 FIB operation + * + * @fi: route FIB info structure + */ +void netdev_switch_fib_ipv4_abort(struct fib_info *fi) +{ + /* There was a problem installing this route to the offload + * device. For now, until we come up with more refined + * policy handling, abruptly end IPv4 fib offloading for + * for entire net by flushing offload device(s) of all + * IPv4 routes, and mark IPv4 fib offloading broken from + * this point forward. + */ + + fib_flush_external(fi->fib_net); + fi->fib_net->ipv4.fib_offload_disabled = true; +} +EXPORT_SYMBOL(netdev_switch_fib_ipv4_abort); -- cgit v1.2.3 From 05050753602626ed4c46271c689929b625f409e7 Mon Sep 17 00:00:00 2001 From: Ilan peer Date: Wed, 4 Mar 2015 00:32:06 -0500 Subject: cfg80211: Add API to change the indoor regulatory setting Previously, the indoor setting configuration assumed that as long as a station interface is connected, the indoor environment setting does not change. However, this assumption is problematic as: - It is possible that a station interface is connected to a mobile AP, e.g., softAP or a P2P GO, where it is possible that both the station and the mobile AP move out of the indoor environment making the indoor setting invalid. In such a case, user space has no way to invalidate the setting. - A station interface disconnection does not necessarily imply that the device is no longer operating in an indoor environment, e.g., it is possible that the station interface is roaming but is still stays indoor. To handle the above, extend the indoor configuration API to allow user space to indicate a change of indoor settings, and allow it to indicate weather it controls the indoor setting, such that: 1. If the user space process explicitly indicates that it is going to control the indoor setting, do not clear the indoor setting internally, unless the socket is released. The user space process should use the NL80211_ATTR_SOCKET_OWNER attribute in the command to state that it is going to control the indoor setting. 2. Reset the indoor setting when restoring the regulatory settings in case it is not owned by a user space process. Based on the above, a user space tool that continuously monitors the indoor settings, i.e., tracking power setting, location etc., can indicate environment changes to the regulatory core. It should be noted that currently user space is the only provided mechanism used to hint to the regulatory core over the indoor/outdoor environment -- while the country IEs do have an environment setting this has been completely ignored by the regulatory core by design for a while now since country IEs typically can contain bogus data. Acked-by: Luis R. Rodriguez Signed-off-by: ArikX Nemtsov Signed-off-by: Ilan Peer Signed-off-by: Johannes Berg --- include/uapi/linux/nl80211.h | 9 +++++++ net/wireless/nl80211.c | 19 ++++++++++++++- net/wireless/reg.c | 57 ++++++++++++++++++++++++++++++++++++++++---- net/wireless/reg.h | 15 +++++++++++- 4 files changed, 94 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index 37e7f39441e5..ae16ba9cb1e3 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h @@ -1697,6 +1697,10 @@ enum nl80211_commands { * If set during scheduled scan start then the new scan req will be * owned by the netlink socket that created it and the scheduled scan will * be stopped when the socket is closed. + * If set during configuration of regulatory indoor operation then the + * regulatory indoor configuration would be owned by the netlink socket + * that configured the indoor setting, and the indoor operation would be + * cleared when the socket is closed. * * @NL80211_ATTR_TDLS_INITIATOR: flag attribute indicating the current end is * the TDLS link initiator. @@ -1752,6 +1756,9 @@ enum nl80211_commands { * * @NL80211_ATTR_SCHED_SCAN_DELAY: delay before a scheduled scan (or a * WoWLAN net-detect scan) is started, u32 in seconds. + + * @NL80211_ATTR_REG_INDOOR: flag attribute, if set indicates that the device + * is operating in an indoor environment. * * @NUM_NL80211_ATTR: total number of nl80211_attrs available * @NL80211_ATTR_MAX: highest attribute number currently defined @@ -2120,6 +2127,8 @@ enum nl80211_attrs { NL80211_ATTR_SCHED_SCAN_DELAY, + NL80211_ATTR_REG_INDOOR, + /* add attributes here, update the policy in nl80211.c */ __NL80211_ATTR_AFTER_LAST, diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 07cef3d7653e..b02085301785 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -399,6 +399,7 @@ static const struct nla_policy nl80211_policy[NUM_NL80211_ATTR] = { [NL80211_ATTR_WIPHY_SELF_MANAGED_REG] = { .type = NLA_FLAG }, [NL80211_ATTR_NETNS_FD] = { .type = NLA_U32 }, [NL80211_ATTR_SCHED_SCAN_DELAY] = { .type = NLA_U32 }, + [NL80211_ATTR_REG_INDOOR] = { .type = NLA_FLAG }, }; /* policy for the key attributes */ @@ -4958,7 +4959,10 @@ static int parse_reg_rule(struct nlattr *tb[], static int nl80211_req_set_reg(struct sk_buff *skb, struct genl_info *info) { char *data = NULL; + bool is_indoor; enum nl80211_user_reg_hint_type user_reg_hint_type; + u32 owner_nlportid; + /* * You should only get this when cfg80211 hasn't yet initialized @@ -4984,7 +4988,15 @@ static int nl80211_req_set_reg(struct sk_buff *skb, struct genl_info *info) data = nla_data(info->attrs[NL80211_ATTR_REG_ALPHA2]); return regulatory_hint_user(data, user_reg_hint_type); case NL80211_USER_REG_HINT_INDOOR: - return regulatory_hint_indoor_user(); + if (info->attrs[NL80211_ATTR_SOCKET_OWNER]) { + owner_nlportid = info->snd_portid; + is_indoor = !!info->attrs[NL80211_ATTR_REG_INDOOR]; + } else { + owner_nlportid = 0; + is_indoor = true; + } + + return regulatory_hint_indoor(is_indoor, owner_nlportid); default: return -EINVAL; } @@ -12810,6 +12822,11 @@ static int nl80211_netlink_notify(struct notifier_block * nb, rcu_read_unlock(); + /* + * It is possible that the user space process that is controlling the + * indoor setting disappeared, so notify the regulatory core. + */ + regulatory_netlink_notify(notify->portid); return NOTIFY_OK; } diff --git a/net/wireless/reg.c b/net/wireless/reg.c index c24c8bf3c988..4239dd408137 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c @@ -128,9 +128,12 @@ static int reg_num_devs_support_basehint; * State variable indicating if the platform on which the devices * are attached is operating in an indoor environment. The state variable * is relevant for all registered devices. - * (protected by RTNL) */ static bool reg_is_indoor; +static spinlock_t reg_indoor_lock; + +/* Used to track the userspace process controlling the indoor setting */ +static u32 reg_is_indoor_portid; static const struct ieee80211_regdomain *get_cfg80211_regdom(void) { @@ -2288,15 +2291,50 @@ int regulatory_hint_user(const char *alpha2, return 0; } -int regulatory_hint_indoor_user(void) +int regulatory_hint_indoor(bool is_indoor, u32 portid) { + spin_lock(®_indoor_lock); + + /* It is possible that more than one user space process is trying to + * configure the indoor setting. To handle such cases, clear the indoor + * setting in case that some process does not think that the device + * is operating in an indoor environment. In addition, if a user space + * process indicates that it is controlling the indoor setting, save its + * portid, i.e., make it the owner. + */ + reg_is_indoor = is_indoor; + if (reg_is_indoor) { + if (!reg_is_indoor_portid) + reg_is_indoor_portid = portid; + } else { + reg_is_indoor_portid = 0; + } + spin_unlock(®_indoor_lock); - reg_is_indoor = true; + if (!is_indoor) + reg_check_channels(); return 0; } +void regulatory_netlink_notify(u32 portid) +{ + spin_lock(®_indoor_lock); + + if (reg_is_indoor_portid != portid) { + spin_unlock(®_indoor_lock); + return; + } + + reg_is_indoor = false; + reg_is_indoor_portid = 0; + + spin_unlock(®_indoor_lock); + + reg_check_channels(); +} + /* Driver hints */ int regulatory_hint(struct wiphy *wiphy, const char *alpha2) { @@ -2464,7 +2502,17 @@ static void restore_regulatory_settings(bool reset_user) ASSERT_RTNL(); - reg_is_indoor = false; + /* + * Clear the indoor setting in case that it is not controlled by user + * space, as otherwise there is no guarantee that the device is still + * operating in an indoor environment. + */ + spin_lock(®_indoor_lock); + if (reg_is_indoor && !reg_is_indoor_portid) { + reg_is_indoor = false; + reg_check_channels(); + } + spin_unlock(®_indoor_lock); reset_regdomains(true, &world_regdom); restore_alpha2(alpha2, reset_user); @@ -3061,6 +3109,7 @@ int __init regulatory_init(void) spin_lock_init(®_requests_lock); spin_lock_init(®_pending_beacons_lock); + spin_lock_init(®_indoor_lock); reg_regdb_size_check(); diff --git a/net/wireless/reg.h b/net/wireless/reg.h index 4b45d6e61d24..a2c4e16459da 100644 --- a/net/wireless/reg.h +++ b/net/wireless/reg.h @@ -25,7 +25,20 @@ enum nl80211_dfs_regions reg_get_dfs_region(struct wiphy *wiphy); int regulatory_hint_user(const char *alpha2, enum nl80211_user_reg_hint_type user_reg_hint_type); -int regulatory_hint_indoor_user(void); + +/** + * regulatory_hint_indoor - hint operation in indoor env. or not + * @is_indoor: if true indicates that user space thinks that the + * device is operating in an indoor environment. + * @portid: the netlink port ID on which the hint was given. + */ +int regulatory_hint_indoor(bool is_indoor, u32 portid); + +/** + * regulatory_netlink_notify - notify on released netlink socket + * @portid: the netlink socket port ID + */ +void regulatory_netlink_notify(u32 portid); void wiphy_regulatory_register(struct wiphy *wiphy); void wiphy_regulatory_deregister(struct wiphy *wiphy); -- cgit v1.2.3 From 2e67690137f3a7bac660edd548f8846709c55381 Mon Sep 17 00:00:00 2001 From: Robert ABEL Date: Fri, 27 Feb 2015 16:56:53 +0100 Subject: ARM OMAP2+ GPMC: calculate GPMCFCLKDIVIDER based on WAITMONITORINGTIME The WAITMONITORINGTIME is expressed as a number of GPMC_CLK clock cycles, even though the access is defined as asynchronous, and no GPMC_CLK clock is provided to the external device. Still, GPMCFCLKDIVIDER is used as a divider for the GPMC clock, so it must be programmed to define the correct WAITMONITORINGTIME delay. Calculate GPMCFCLKDIVIDER independent of gpmc,sync-clk-ps in DT for pure asynchronous accesses, i.e. both read and write asynchronous. Signed-off-by: Robert ABEL Acked-by: Tony Lindgren Signed-off-by: Roger Quadros --- arch/arm/mach-omap2/gpmc-nand.c | 18 ++++---- arch/arm/mach-omap2/gpmc-onenand.c | 4 +- arch/arm/mach-omap2/usb-tusb6010.c | 4 +- drivers/memory/omap-gpmc.c | 86 ++++++++++++++++++++++++++++++++++---- include/linux/omap-gpmc.h | 3 +- 5 files changed, 95 insertions(+), 20 deletions(-) (limited to 'include') diff --git a/arch/arm/mach-omap2/gpmc-nand.c b/arch/arm/mach-omap2/gpmc-nand.c index d5951b17b736..72918c4973ea 100644 --- a/arch/arm/mach-omap2/gpmc-nand.c +++ b/arch/arm/mach-omap2/gpmc-nand.c @@ -96,14 +96,6 @@ int gpmc_nand_init(struct omap_nand_platform_data *gpmc_nand_data, gpmc_nand_res[1].start = gpmc_get_client_irq(GPMC_IRQ_FIFOEVENTENABLE); gpmc_nand_res[2].start = gpmc_get_client_irq(GPMC_IRQ_COUNT_EVENT); - if (gpmc_t) { - err = gpmc_cs_set_timings(gpmc_nand_data->cs, gpmc_t); - if (err < 0) { - pr_err("omap2-gpmc: Unable to set gpmc timings: %d\n", err); - return err; - } - } - memset(&s, 0, sizeof(struct gpmc_settings)); if (gpmc_nand_data->of_node) gpmc_read_settings_dt(gpmc_nand_data->of_node, &s); @@ -111,6 +103,16 @@ int gpmc_nand_init(struct omap_nand_platform_data *gpmc_nand_data, gpmc_set_legacy(gpmc_nand_data, &s); s.device_nand = true; + + if (gpmc_t) { + err = gpmc_cs_set_timings(gpmc_nand_data->cs, gpmc_t, &s); + if (err < 0) { + pr_err("omap2-gpmc: Unable to set gpmc timings: %d\n", + err); + return err; + } + } + err = gpmc_cs_program_settings(gpmc_nand_data->cs, &s); if (err < 0) goto out_free_cs; diff --git a/arch/arm/mach-omap2/gpmc-onenand.c b/arch/arm/mach-omap2/gpmc-onenand.c index 53d197e0c1f3..f899e77ff5e6 100644 --- a/arch/arm/mach-omap2/gpmc-onenand.c +++ b/arch/arm/mach-omap2/gpmc-onenand.c @@ -293,7 +293,7 @@ static int omap2_onenand_setup_async(void __iomem *onenand_base) if (ret < 0) return ret; - ret = gpmc_cs_set_timings(gpmc_onenand_data->cs, &t); + ret = gpmc_cs_set_timings(gpmc_onenand_data->cs, &t, &onenand_async); if (ret < 0) return ret; @@ -331,7 +331,7 @@ static int omap2_onenand_setup_sync(void __iomem *onenand_base, int *freq_ptr) if (ret < 0) return ret; - ret = gpmc_cs_set_timings(gpmc_onenand_data->cs, &t); + ret = gpmc_cs_set_timings(gpmc_onenand_data->cs, &t, &onenand_sync); if (ret < 0) return ret; diff --git a/arch/arm/mach-omap2/usb-tusb6010.c b/arch/arm/mach-omap2/usb-tusb6010.c index 8333400898fb..e554d9e66a1c 100644 --- a/arch/arm/mach-omap2/usb-tusb6010.c +++ b/arch/arm/mach-omap2/usb-tusb6010.c @@ -71,7 +71,7 @@ static int tusb_set_async_mode(unsigned sysclk_ps) gpmc_calc_timings(&t, &tusb_async, &dev_t); - return gpmc_cs_set_timings(async_cs, &t); + return gpmc_cs_set_timings(async_cs, &t, &tusb_async); } static int tusb_set_sync_mode(unsigned sysclk_ps) @@ -98,7 +98,7 @@ static int tusb_set_sync_mode(unsigned sysclk_ps) gpmc_calc_timings(&t, &tusb_sync, &dev_t); - return gpmc_cs_set_timings(sync_cs, &t); + return gpmc_cs_set_timings(sync_cs, &t, &tusb_sync); } /* tusb driver calls this when it changes the chip's clocking */ diff --git a/drivers/memory/omap-gpmc.c b/drivers/memory/omap-gpmc.c index 5c36ff397b73..768bab233196 100644 --- a/drivers/memory/omap-gpmc.c +++ b/drivers/memory/omap-gpmc.c @@ -138,7 +138,9 @@ #define GPMC_CONFIG1_PAGE_LEN(val) ((val & 3) << 23) #define GPMC_CONFIG1_WAIT_READ_MON (1 << 22) #define GPMC_CONFIG1_WAIT_WRITE_MON (1 << 21) -#define GPMC_CONFIG1_WAIT_MON_IIME(val) ((val & 3) << 18) +#define GPMC_CONFIG1_WAIT_MON_TIME(val) ((val & 3) << 18) +/** WAITMONITORINGTIME Max Ticks */ +#define GPMC_CONFIG1_WAITMONITORINGTIME_MAX 2 #define GPMC_CONFIG1_WAIT_PIN_SEL(val) ((val & 3) << 16) #define GPMC_CONFIG1_DEVICESIZE(val) ((val & 3) << 12) #define GPMC_CONFIG1_DEVICESIZE_16 GPMC_CONFIG1_DEVICESIZE(1) @@ -525,13 +527,48 @@ static int set_gpmc_timing_reg(int cs, int reg, int st_bit, int end_bit, t->field, #field) < 0) \ return -1 +/** + * gpmc_calc_waitmonitoring_divider - calculate proper GPMCFCLKDIVIDER based on WAITMONITORINGTIME + * WAITMONITORINGTIME will be _at least_ as long as desired, i.e. + * read --> don't sample bus too early + * write --> data is longer on bus + * + * Formula: + * gpmc_clk_div + 1 = ceil(ceil(waitmonitoringtime_ns / gpmc_fclk_ns) + * / waitmonitoring_ticks) + * WAITMONITORINGTIME resulting in 0 or 1 tick with div = 1 are caught by + * div <= 0 check. + * + * @wait_monitoring: WAITMONITORINGTIME in ns. + * @return: -1 on failure to scale, else proper divider > 0. + */ +static int gpmc_calc_waitmonitoring_divider(unsigned int wait_monitoring) +{ + + int div = gpmc_ns_to_ticks(wait_monitoring); + + div += GPMC_CONFIG1_WAITMONITORINGTIME_MAX - 1; + div /= GPMC_CONFIG1_WAITMONITORINGTIME_MAX; + + if (div > 4) + return -1; + if (div <= 0) + div = 1; + + return div; + +} + +/** + * gpmc_calc_divider - calculate GPMC_FCLK divider for sync_clk GPMC_CLK period. + * @sync_clk: GPMC_CLK period in ps. + * @return: Returns at least 1 if GPMC_FCLK can be divided to GPMC_CLK. + * Else, returns -1. + */ int gpmc_calc_divider(unsigned int sync_clk) { - int div; - u32 l; + int div = gpmc_ps_to_ticks(sync_clk); - l = sync_clk + (gpmc_get_fclk_period() - 1); - div = l / gpmc_get_fclk_period(); if (div > 4) return -1; if (div <= 0) @@ -540,7 +577,15 @@ int gpmc_calc_divider(unsigned int sync_clk) return div; } -int gpmc_cs_set_timings(int cs, const struct gpmc_timings *t) +/** + * gpmc_cs_set_timings - program timing parameters for Chip Select Region. + * @cs: Chip Select Region. + * @t: GPMC timing parameters. + * @s: GPMC timing settings. + * @return: 0 on success, -1 on error. + */ +int gpmc_cs_set_timings(int cs, const struct gpmc_timings *t, + const struct gpmc_settings *s) { int div; u32 l; @@ -550,6 +595,33 @@ int gpmc_cs_set_timings(int cs, const struct gpmc_timings *t) if (div < 0) return div; + /* + * See if we need to change the divider for waitmonitoringtime. + * + * Calculate GPMCFCLKDIVIDER independent of gpmc,sync-clk-ps in DT for + * pure asynchronous accesses, i.e. both read and write asynchronous. + * However, only do so if WAITMONITORINGTIME is actually used, i.e. + * either WAITREADMONITORING or WAITWRITEMONITORING is set. + * + * This statement must not change div to scale async WAITMONITORINGTIME + * to protect mixed synchronous and asynchronous accesses. + * + * We raise an error later if WAITMONITORINGTIME does not fit. + */ + if (!s->sync_read && !s->sync_write && + (s->wait_on_read || s->wait_on_write) + ) { + + div = gpmc_calc_waitmonitoring_divider(t->wait_monitoring); + if (div < 0) { + pr_err("%s: waitmonitoringtime %3d ns too large for greatest gpmcfclkdivider.\n", + __func__, + t->wait_monitoring + ); + return -1; + } + } + GPMC_SET_ONE(GPMC_CS_CONFIG2, 0, 3, cs_on); GPMC_SET_ONE(GPMC_CS_CONFIG2, 8, 12, cs_rd_off); GPMC_SET_ONE(GPMC_CS_CONFIG2, 16, 20, cs_wr_off); @@ -1810,7 +1882,7 @@ static int gpmc_probe_generic_child(struct platform_device *pdev, if (ret < 0) goto err; - ret = gpmc_cs_set_timings(cs, &gpmc_t); + ret = gpmc_cs_set_timings(cs, &gpmc_t, &gpmc_s); if (ret) { dev_err(&pdev->dev, "failed to set gpmc timings for: %s\n", child->name); diff --git a/include/linux/omap-gpmc.h b/include/linux/omap-gpmc.h index c2080eebbb47..7dee00143afd 100644 --- a/include/linux/omap-gpmc.h +++ b/include/linux/omap-gpmc.h @@ -163,7 +163,8 @@ extern unsigned int gpmc_ticks_to_ns(unsigned int ticks); extern void gpmc_cs_write_reg(int cs, int idx, u32 val); extern int gpmc_calc_divider(unsigned int sync_clk); -extern int gpmc_cs_set_timings(int cs, const struct gpmc_timings *t); +extern int gpmc_cs_set_timings(int cs, const struct gpmc_timings *t, + const struct gpmc_settings *s); extern int gpmc_cs_program_settings(int cs, struct gpmc_settings *p); extern int gpmc_cs_request(int cs, unsigned long size, unsigned long *base); extern void gpmc_cs_free(int cs); -- cgit v1.2.3 From 93690c227acf08a2a19cbaf9acbcd2210fbb8ded Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Fri, 6 Mar 2015 10:11:21 -0800 Subject: Bluetooth: Introduce controller setting information for static address Currently it is not possible to determine if the static address is used by the controller. It is also not possible to determine if using a static on a dual-mode controller with disabled BR/EDR is possible or not. To address this issue, introduce a new setting called static-address. If support for this setting is signaled that means that the kernel supports using static addresses. And if used on dual-mode controllers with BR/EDR disabled it means that a configured static address can be used. In addition utilize the same setting for the list of current active settings that indicates if a static address is configured and if that address will be actually used. With this in mind the existing Set Static Address management command has been extended to return the current settings. That way the caller of that command can easily determine if the programmed address will be used or if extra steps are required. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- include/net/bluetooth/mgmt.h | 1 + net/bluetooth/mgmt.c | 28 ++++++++++++++++++++++++++-- 2 files changed, 27 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/mgmt.h b/include/net/bluetooth/mgmt.h index fe8eef00e9ca..0c737e4b8f57 100644 --- a/include/net/bluetooth/mgmt.h +++ b/include/net/bluetooth/mgmt.h @@ -98,6 +98,7 @@ struct mgmt_rp_read_index_list { #define MGMT_SETTING_DEBUG_KEYS 0x00001000 #define MGMT_SETTING_PRIVACY 0x00002000 #define MGMT_SETTING_CONFIGURATION 0x00004000 +#define MGMT_SETTING_STATIC_ADDRESS 0x00008000 #define MGMT_OP_READ_INFO 0x0004 #define MGMT_READ_INFO_SIZE 0 diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 967f07fdbbbe..d185a9800983 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -583,6 +583,7 @@ static u32 get_supported_settings(struct hci_dev *hdev) settings |= MGMT_SETTING_ADVERTISING; settings |= MGMT_SETTING_SECURE_CONN; settings |= MGMT_SETTING_PRIVACY; + settings |= MGMT_SETTING_STATIC_ADDRESS; } if (test_bit(HCI_QUIRK_EXTERNAL_CONFIG, &hdev->quirks) || @@ -638,6 +639,25 @@ static u32 get_current_settings(struct hci_dev *hdev) if (test_bit(HCI_PRIVACY, &hdev->dev_flags)) settings |= MGMT_SETTING_PRIVACY; + /* The current setting for static address has two purposes. The + * first is to indicate if the static address will be used and + * the second is to indicate if it is actually set. + * + * This means if the static address is not configured, this flag + * will never bet set. If the address is configured, then if the + * address is actually used decides if the flag is set or not. + * + * For single mode LE only controllers and dual-mode controllers + * with BR/EDR disabled, the existence of the static address will + * be evaluated. + */ + if (test_bit(HCI_FORCE_STATIC_ADDR, &hdev->dbg_flags) || + !test_bit(HCI_BREDR_ENABLED, &hdev->dev_flags) || + !bacmp(&hdev->bdaddr, BDADDR_ANY)) { + if (bacmp(&hdev->static_addr, BDADDR_ANY)) + settings |= MGMT_SETTING_STATIC_ADDRESS; + } + return settings; } @@ -4498,10 +4518,14 @@ static int set_static_address(struct sock *sk, struct hci_dev *hdev, bacpy(&hdev->static_addr, &cp->bdaddr); - err = cmd_complete(sk, hdev->id, MGMT_OP_SET_STATIC_ADDRESS, 0, NULL, 0); + err = send_settings_rsp(sk, MGMT_OP_SET_STATIC_ADDRESS, hdev); + if (err < 0) + goto unlock; - hci_dev_unlock(hdev); + err = new_settings(hdev, sk); +unlock: + hci_dev_unlock(hdev); return err; } -- cgit v1.2.3 From 801c1e8da57499a9922223ee1882b2b59debd47c Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Fri, 6 Mar 2015 21:08:50 +0200 Subject: Bluetooth: Add mgmt HCI channel registration API This patch adds an API for registering HCI channels with mgmt-like semantics. For now the only user will be HCI_CHANNEL_CONTROL, but e.g. 6lowpan is intended to use this as well in the future. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- include/net/bluetooth/hci_core.h | 17 ++++++++ net/bluetooth/hci_sock.c | 85 ++++++++++++++++++++++++++++++++++++++-- 2 files changed, 99 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 15c761c1f82a..0c84d48e5517 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -1273,6 +1273,23 @@ void hci_send_to_monitor(struct hci_dev *hdev, struct sk_buff *skb); void hci_sock_dev_event(struct hci_dev *hdev, int event); +struct hci_mgmt_handler { + int (*func) (struct sock *sk, struct hci_dev *hdev, void *data, + u16 data_len); + bool var_len; + size_t data_len; +}; + +struct hci_mgmt_chan { + struct list_head list; + unsigned short channel; + size_t handler_count; + const struct hci_mgmt_handler *handlers; +}; + +int hci_mgmt_chan_register(struct hci_mgmt_chan *c); +void hci_mgmt_chan_unregister(struct hci_mgmt_chan *c); + /* Management interface */ #define DISCOV_TYPE_BREDR (BIT(BDADDR_BREDR)) #define DISCOV_TYPE_LE (BIT(BDADDR_LE_PUBLIC) | \ diff --git a/net/bluetooth/hci_sock.c b/net/bluetooth/hci_sock.c index cb4bc4883350..0d5ace8922b1 100644 --- a/net/bluetooth/hci_sock.c +++ b/net/bluetooth/hci_sock.c @@ -31,6 +31,9 @@ #include #include +static LIST_HEAD(mgmt_chan_list); +static DEFINE_MUTEX(mgmt_chan_list_lock); + static atomic_t monitor_promisc = ATOMIC_INIT(0); /* ----- HCI socket interface ----- */ @@ -401,6 +404,56 @@ void hci_sock_dev_event(struct hci_dev *hdev, int event) } } +static struct hci_mgmt_chan *__hci_mgmt_chan_find(unsigned short channel) +{ + struct hci_mgmt_chan *c; + + list_for_each_entry(c, &mgmt_chan_list, list) { + if (c->channel == channel) + return c; + } + + return NULL; +} + +static struct hci_mgmt_chan *hci_mgmt_chan_find(unsigned short channel) +{ + struct hci_mgmt_chan *c; + + mutex_lock(&mgmt_chan_list_lock); + c = __hci_mgmt_chan_find(channel); + mutex_unlock(&mgmt_chan_list_lock); + + return c; +} + +int hci_mgmt_chan_register(struct hci_mgmt_chan *c) +{ + if (c->channel < HCI_CHANNEL_CONTROL) + return -EINVAL; + + mutex_lock(&mgmt_chan_list_lock); + if (__hci_mgmt_chan_find(c->channel)) { + mutex_unlock(&mgmt_chan_list_lock); + return -EALREADY; + } + + list_add_tail(&c->list, &mgmt_chan_list); + + mutex_unlock(&mgmt_chan_list_lock); + + return 0; +} +EXPORT_SYMBOL(hci_mgmt_chan_register); + +void hci_mgmt_chan_unregister(struct hci_mgmt_chan *c) +{ + mutex_lock(&mgmt_chan_list_lock); + list_del(&c->list); + mutex_unlock(&mgmt_chan_list_lock); +} +EXPORT_SYMBOL(hci_mgmt_chan_unregister); + static int hci_sock_release(struct socket *sock) { struct sock *sk = sock->sk; @@ -718,8 +771,22 @@ static int hci_sock_bind(struct socket *sock, struct sockaddr *addr, break; default: - err = -EINVAL; - goto done; + if (!hci_mgmt_chan_find(haddr.hci_channel)) { + err = -EINVAL; + goto done; + } + + if (haddr.hci_dev != HCI_DEV_NONE) { + err = -EINVAL; + goto done; + } + + if (!capable(CAP_NET_ADMIN)) { + err = -EPERM; + goto done; + } + + break; } @@ -837,6 +904,10 @@ static int hci_sock_recvmsg(struct socket *sock, struct msghdr *msg, size_t len, case HCI_CHANNEL_MONITOR: sock_recv_timestamp(msg, sk, skb); break; + default: + if (hci_mgmt_chan_find(hci_pi(sk)->channel)) + sock_recv_timestamp(msg, sk, skb); + break; } skb_free_datagram(sk, skb); @@ -848,6 +919,7 @@ static int hci_sock_sendmsg(struct socket *sock, struct msghdr *msg, size_t len) { struct sock *sk = sock->sk; + struct hci_mgmt_chan *chan; struct hci_dev *hdev; struct sk_buff *skb; int err; @@ -876,7 +948,14 @@ static int hci_sock_sendmsg(struct socket *sock, struct msghdr *msg, err = -EOPNOTSUPP; goto done; default: - err = -EINVAL; + mutex_lock(&mgmt_chan_list_lock); + chan = __hci_mgmt_chan_find(hci_pi(sk)->channel); + if (chan) + err = -ENOSYS; /* FIXME: call handler */ + else + err = -EINVAL; + + mutex_unlock(&mgmt_chan_list_lock); goto done; } -- cgit v1.2.3 From 6d785aa345f525e1fdf098b7c590168f0b00f3f1 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Fri, 6 Mar 2015 21:08:51 +0200 Subject: Bluetooth: Convert mgmt to use HCI chan registration API This patch converts the existing mgmt code to use the newly introduced generic API for registering HCI channels with mgmt-like semantics. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- include/net/bluetooth/bluetooth.h | 3 +++ include/net/bluetooth/hci_core.h | 4 +++- net/bluetooth/af_bluetooth.c | 9 +++++++++ net/bluetooth/hci_sock.c | 19 +------------------ net/bluetooth/mgmt.c | 34 +++++++++++++++++++++++----------- 5 files changed, 39 insertions(+), 30 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/bluetooth.h b/include/net/bluetooth/bluetooth.h index 6bb97df16d2d..e598ca096ec9 100644 --- a/include/net/bluetooth/bluetooth.h +++ b/include/net/bluetooth/bluetooth.h @@ -354,6 +354,9 @@ void l2cap_exit(void); int sco_init(void); void sco_exit(void); +int mgmt_init(void); +void mgmt_exit(void); + void bt_sock_reclassify_lock(struct sock *sk, int proto); #endif /* __BLUETOOTH_H */ diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 0c84d48e5517..b2a183d201b7 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -1309,7 +1309,9 @@ void hci_mgmt_chan_unregister(struct hci_mgmt_chan *c); #define DISCOV_BREDR_INQUIRY_LEN 0x08 #define DISCOV_LE_RESTART_DELAY msecs_to_jiffies(200) /* msec */ -int mgmt_control(struct sock *sk, struct msghdr *msg, size_t len); +int mgmt_control(struct hci_mgmt_chan *chan, struct sock *sk, + struct msghdr *msg, size_t msglen); + int mgmt_new_settings(struct hci_dev *hdev); void mgmt_index_added(struct hci_dev *hdev); void mgmt_index_removed(struct hci_dev *hdev); diff --git a/net/bluetooth/af_bluetooth.c b/net/bluetooth/af_bluetooth.c index 20a4698e2255..70f9d945faf7 100644 --- a/net/bluetooth/af_bluetooth.c +++ b/net/bluetooth/af_bluetooth.c @@ -749,6 +749,13 @@ static int __init bt_init(void) goto sock_err; } + err = mgmt_init(); + if (err < 0) { + sco_exit(); + l2cap_exit(); + goto sock_err; + } + return 0; sock_err: @@ -763,6 +770,8 @@ error: static void __exit bt_exit(void) { + mgmt_exit(); + sco_exit(); l2cap_exit(); diff --git a/net/bluetooth/hci_sock.c b/net/bluetooth/hci_sock.c index 0d5ace8922b1..aa9ffcb9481f 100644 --- a/net/bluetooth/hci_sock.c +++ b/net/bluetooth/hci_sock.c @@ -741,19 +741,6 @@ static int hci_sock_bind(struct socket *sock, struct sockaddr *addr, hci_pi(sk)->hdev = hdev; break; - case HCI_CHANNEL_CONTROL: - if (haddr.hci_dev != HCI_DEV_NONE) { - err = -EINVAL; - goto done; - } - - if (!capable(CAP_NET_ADMIN)) { - err = -EPERM; - goto done; - } - - break; - case HCI_CHANNEL_MONITOR: if (haddr.hci_dev != HCI_DEV_NONE) { err = -EINVAL; @@ -900,7 +887,6 @@ static int hci_sock_recvmsg(struct socket *sock, struct msghdr *msg, size_t len, hci_sock_cmsg(sk, msg, skb); break; case HCI_CHANNEL_USER: - case HCI_CHANNEL_CONTROL: case HCI_CHANNEL_MONITOR: sock_recv_timestamp(msg, sk, skb); break; @@ -941,9 +927,6 @@ static int hci_sock_sendmsg(struct socket *sock, struct msghdr *msg, case HCI_CHANNEL_RAW: case HCI_CHANNEL_USER: break; - case HCI_CHANNEL_CONTROL: - err = mgmt_control(sk, msg, len); - goto done; case HCI_CHANNEL_MONITOR: err = -EOPNOTSUPP; goto done; @@ -951,7 +934,7 @@ static int hci_sock_sendmsg(struct socket *sock, struct msghdr *msg, mutex_lock(&mgmt_chan_list_lock); chan = __hci_mgmt_chan_find(hci_pi(sk)->channel); if (chan) - err = -ENOSYS; /* FIXME: call handler */ + err = mgmt_control(chan, sk, msg, len); else err = -EINVAL; diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index d185a9800983..bb02dd1b82bf 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -6130,12 +6130,7 @@ unlock: return err; } -static const struct mgmt_handler { - int (*func) (struct sock *sk, struct hci_dev *hdev, void *data, - u16 data_len); - bool var_len; - size_t data_len; -} mgmt_handlers[] = { +static const struct hci_mgmt_handler mgmt_handlers[] = { { NULL }, /* 0x0000 (no command) */ { read_version, false, MGMT_READ_VERSION_SIZE }, { read_commands, false, MGMT_READ_COMMANDS_SIZE }, @@ -6197,14 +6192,15 @@ static const struct mgmt_handler { { start_service_discovery,true, MGMT_START_SERVICE_DISCOVERY_SIZE }, }; -int mgmt_control(struct sock *sk, struct msghdr *msg, size_t msglen) +int mgmt_control(struct hci_mgmt_chan *chan, struct sock *sk, + struct msghdr *msg, size_t msglen) { void *buf; u8 *cp; struct mgmt_hdr *hdr; u16 opcode, index, len; struct hci_dev *hdev = NULL; - const struct mgmt_handler *handler; + const struct hci_mgmt_handler *handler; int err; BT_DBG("got %zu bytes", msglen); @@ -6257,8 +6253,8 @@ int mgmt_control(struct sock *sk, struct msghdr *msg, size_t msglen) } } - if (opcode >= ARRAY_SIZE(mgmt_handlers) || - mgmt_handlers[opcode].func == NULL) { + if (opcode >= chan->handler_count || + chan->handlers[opcode].func == NULL) { BT_DBG("Unknown op %u", opcode); err = cmd_status(sk, index, opcode, MGMT_STATUS_UNKNOWN_COMMAND); @@ -6279,7 +6275,7 @@ int mgmt_control(struct sock *sk, struct msghdr *msg, size_t msglen) goto done; } - handler = &mgmt_handlers[opcode]; + handler = &chan->handlers[opcode]; if ((handler->var_len && len < handler->data_len) || (!handler->var_len && len != handler->data_len)) { @@ -7470,3 +7466,19 @@ void mgmt_reenable_advertising(struct hci_dev *hdev) enable_advertising(&req); hci_req_run(&req, adv_enable_complete); } + +static struct hci_mgmt_chan chan = { + .channel = HCI_CHANNEL_CONTROL, + .handler_count = ARRAY_SIZE(mgmt_handlers), + .handlers = mgmt_handlers, +}; + +int mgmt_init(void) +{ + return hci_mgmt_chan_register(&chan); +} + +void mgmt_exit(void) +{ + hci_mgmt_chan_unregister(&chan); +} -- cgit v1.2.3 From b9a245fb12315f8c6528b29a991a004859c982d5 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Fri, 6 Mar 2015 21:08:52 +0200 Subject: Bluetooth: Move all mgmt command quirks to handler table In order to completely generalize the mgmt command handling we need to move away command-specific information from mgmt_control() into the actual command table. This patch adds a new 'flags' field to the handler entries which can now contain the following command specific information: - Command takes variable length parameters - Command doesn't target any specific HCI device - Command can be sent when the HCI device is unconfigured After this the mgmt_control() function is completely generic and can potentially be reused by new HCI channels. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- include/net/bluetooth/hci_core.h | 6 +- net/bluetooth/mgmt.c | 170 ++++++++++++++++++++------------------- 2 files changed, 93 insertions(+), 83 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index b2a183d201b7..afc641c5e55c 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -1273,11 +1273,15 @@ void hci_send_to_monitor(struct hci_dev *hdev, struct sk_buff *skb); void hci_sock_dev_event(struct hci_dev *hdev, int event); +#define HCI_MGMT_VAR_LEN (1 << 0) +#define HCI_MGMT_NO_HDEV (1 << 1) +#define HCI_MGMT_UNCONFIGURED (1 << 2) + struct hci_mgmt_handler { int (*func) (struct sock *sk, struct hci_dev *hdev, void *data, u16 data_len); - bool var_len; size_t data_len; + unsigned long flags; }; struct hci_mgmt_chan { diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index bb02dd1b82bf..f65516420a31 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -6132,64 +6132,77 @@ unlock: static const struct hci_mgmt_handler mgmt_handlers[] = { { NULL }, /* 0x0000 (no command) */ - { read_version, false, MGMT_READ_VERSION_SIZE }, - { read_commands, false, MGMT_READ_COMMANDS_SIZE }, - { read_index_list, false, MGMT_READ_INDEX_LIST_SIZE }, - { read_controller_info, false, MGMT_READ_INFO_SIZE }, - { set_powered, false, MGMT_SETTING_SIZE }, - { set_discoverable, false, MGMT_SET_DISCOVERABLE_SIZE }, - { set_connectable, false, MGMT_SETTING_SIZE }, - { set_fast_connectable, false, MGMT_SETTING_SIZE }, - { set_bondable, false, MGMT_SETTING_SIZE }, - { set_link_security, false, MGMT_SETTING_SIZE }, - { set_ssp, false, MGMT_SETTING_SIZE }, - { set_hs, false, MGMT_SETTING_SIZE }, - { set_le, false, MGMT_SETTING_SIZE }, - { set_dev_class, false, MGMT_SET_DEV_CLASS_SIZE }, - { set_local_name, false, MGMT_SET_LOCAL_NAME_SIZE }, - { add_uuid, false, MGMT_ADD_UUID_SIZE }, - { remove_uuid, false, MGMT_REMOVE_UUID_SIZE }, - { load_link_keys, true, MGMT_LOAD_LINK_KEYS_SIZE }, - { load_long_term_keys, true, MGMT_LOAD_LONG_TERM_KEYS_SIZE }, - { disconnect, false, MGMT_DISCONNECT_SIZE }, - { get_connections, false, MGMT_GET_CONNECTIONS_SIZE }, - { pin_code_reply, false, MGMT_PIN_CODE_REPLY_SIZE }, - { pin_code_neg_reply, false, MGMT_PIN_CODE_NEG_REPLY_SIZE }, - { set_io_capability, false, MGMT_SET_IO_CAPABILITY_SIZE }, - { pair_device, false, MGMT_PAIR_DEVICE_SIZE }, - { cancel_pair_device, false, MGMT_CANCEL_PAIR_DEVICE_SIZE }, - { unpair_device, false, MGMT_UNPAIR_DEVICE_SIZE }, - { user_confirm_reply, false, MGMT_USER_CONFIRM_REPLY_SIZE }, - { user_confirm_neg_reply, false, MGMT_USER_CONFIRM_NEG_REPLY_SIZE }, - { user_passkey_reply, false, MGMT_USER_PASSKEY_REPLY_SIZE }, - { user_passkey_neg_reply, false, MGMT_USER_PASSKEY_NEG_REPLY_SIZE }, - { read_local_oob_data, false, MGMT_READ_LOCAL_OOB_DATA_SIZE }, - { add_remote_oob_data, true, MGMT_ADD_REMOTE_OOB_DATA_SIZE }, - { remove_remote_oob_data, false, MGMT_REMOVE_REMOTE_OOB_DATA_SIZE }, - { start_discovery, false, MGMT_START_DISCOVERY_SIZE }, - { stop_discovery, false, MGMT_STOP_DISCOVERY_SIZE }, - { confirm_name, false, MGMT_CONFIRM_NAME_SIZE }, - { block_device, false, MGMT_BLOCK_DEVICE_SIZE }, - { unblock_device, false, MGMT_UNBLOCK_DEVICE_SIZE }, - { set_device_id, false, MGMT_SET_DEVICE_ID_SIZE }, - { set_advertising, false, MGMT_SETTING_SIZE }, - { set_bredr, false, MGMT_SETTING_SIZE }, - { set_static_address, false, MGMT_SET_STATIC_ADDRESS_SIZE }, - { set_scan_params, false, MGMT_SET_SCAN_PARAMS_SIZE }, - { set_secure_conn, false, MGMT_SETTING_SIZE }, - { set_debug_keys, false, MGMT_SETTING_SIZE }, - { set_privacy, false, MGMT_SET_PRIVACY_SIZE }, - { load_irks, true, MGMT_LOAD_IRKS_SIZE }, - { get_conn_info, false, MGMT_GET_CONN_INFO_SIZE }, - { get_clock_info, false, MGMT_GET_CLOCK_INFO_SIZE }, - { add_device, false, MGMT_ADD_DEVICE_SIZE }, - { remove_device, false, MGMT_REMOVE_DEVICE_SIZE }, - { load_conn_param, true, MGMT_LOAD_CONN_PARAM_SIZE }, - { read_unconf_index_list, false, MGMT_READ_UNCONF_INDEX_LIST_SIZE }, - { read_config_info, false, MGMT_READ_CONFIG_INFO_SIZE }, - { set_external_config, false, MGMT_SET_EXTERNAL_CONFIG_SIZE }, - { set_public_address, false, MGMT_SET_PUBLIC_ADDRESS_SIZE }, - { start_service_discovery,true, MGMT_START_SERVICE_DISCOVERY_SIZE }, + { read_version, MGMT_READ_VERSION_SIZE, + HCI_MGMT_NO_HDEV }, + { read_commands, MGMT_READ_COMMANDS_SIZE, + HCI_MGMT_NO_HDEV }, + { read_index_list, MGMT_READ_INDEX_LIST_SIZE, + HCI_MGMT_NO_HDEV }, + { read_controller_info, MGMT_READ_INFO_SIZE, 0 }, + { set_powered, MGMT_SETTING_SIZE, 0 }, + { set_discoverable, MGMT_SET_DISCOVERABLE_SIZE, 0 }, + { set_connectable, MGMT_SETTING_SIZE, 0 }, + { set_fast_connectable, MGMT_SETTING_SIZE, 0 }, + { set_bondable, MGMT_SETTING_SIZE, 0 }, + { set_link_security, MGMT_SETTING_SIZE, 0 }, + { set_ssp, MGMT_SETTING_SIZE, 0 }, + { set_hs, MGMT_SETTING_SIZE, 0 }, + { set_le, MGMT_SETTING_SIZE, 0 }, + { set_dev_class, MGMT_SET_DEV_CLASS_SIZE, 0 }, + { set_local_name, MGMT_SET_LOCAL_NAME_SIZE, 0 }, + { add_uuid, MGMT_ADD_UUID_SIZE, 0 }, + { remove_uuid, MGMT_REMOVE_UUID_SIZE, 0 }, + { load_link_keys, MGMT_LOAD_LINK_KEYS_SIZE, + HCI_MGMT_VAR_LEN }, + { load_long_term_keys, MGMT_LOAD_LONG_TERM_KEYS_SIZE, + HCI_MGMT_VAR_LEN }, + { disconnect, MGMT_DISCONNECT_SIZE, 0 }, + { get_connections, MGMT_GET_CONNECTIONS_SIZE, 0 }, + { pin_code_reply, MGMT_PIN_CODE_REPLY_SIZE, 0 }, + { pin_code_neg_reply, MGMT_PIN_CODE_NEG_REPLY_SIZE, 0 }, + { set_io_capability, MGMT_SET_IO_CAPABILITY_SIZE, 0 }, + { pair_device, MGMT_PAIR_DEVICE_SIZE, 0 }, + { cancel_pair_device, MGMT_CANCEL_PAIR_DEVICE_SIZE, 0 }, + { unpair_device, MGMT_UNPAIR_DEVICE_SIZE, 0 }, + { user_confirm_reply, MGMT_USER_CONFIRM_REPLY_SIZE, 0 }, + { user_confirm_neg_reply, MGMT_USER_CONFIRM_NEG_REPLY_SIZE, 0 }, + { user_passkey_reply, MGMT_USER_PASSKEY_REPLY_SIZE, 0 }, + { user_passkey_neg_reply, MGMT_USER_PASSKEY_NEG_REPLY_SIZE, 0 }, + { read_local_oob_data, MGMT_READ_LOCAL_OOB_DATA_SIZE }, + { add_remote_oob_data, MGMT_ADD_REMOTE_OOB_DATA_SIZE, + HCI_MGMT_VAR_LEN }, + { remove_remote_oob_data, MGMT_REMOVE_REMOTE_OOB_DATA_SIZE, 0 }, + { start_discovery, MGMT_START_DISCOVERY_SIZE, 0 }, + { stop_discovery, MGMT_STOP_DISCOVERY_SIZE, 0 }, + { confirm_name, MGMT_CONFIRM_NAME_SIZE, 0 }, + { block_device, MGMT_BLOCK_DEVICE_SIZE, 0 }, + { unblock_device, MGMT_UNBLOCK_DEVICE_SIZE, 0 }, + { set_device_id, MGMT_SET_DEVICE_ID_SIZE, 0 }, + { set_advertising, MGMT_SETTING_SIZE, 0 }, + { set_bredr, MGMT_SETTING_SIZE, 0 }, + { set_static_address, MGMT_SET_STATIC_ADDRESS_SIZE, 0 }, + { set_scan_params, MGMT_SET_SCAN_PARAMS_SIZE, 0 }, + { set_secure_conn, MGMT_SETTING_SIZE, 0 }, + { set_debug_keys, MGMT_SETTING_SIZE, 0 }, + { set_privacy, MGMT_SET_PRIVACY_SIZE, 0 }, + { load_irks, MGMT_LOAD_IRKS_SIZE, + HCI_MGMT_VAR_LEN }, + { get_conn_info, MGMT_GET_CONN_INFO_SIZE, 0 }, + { get_clock_info, MGMT_GET_CLOCK_INFO_SIZE, 0 }, + { add_device, MGMT_ADD_DEVICE_SIZE, 0 }, + { remove_device, MGMT_REMOVE_DEVICE_SIZE, 0 }, + { load_conn_param, MGMT_LOAD_CONN_PARAM_SIZE, + HCI_MGMT_VAR_LEN }, + { read_unconf_index_list, MGMT_READ_UNCONF_INDEX_LIST_SIZE, + HCI_MGMT_NO_HDEV }, + { read_config_info, MGMT_READ_CONFIG_INFO_SIZE, + HCI_MGMT_UNCONFIGURED }, + { set_external_config, MGMT_SET_EXTERNAL_CONFIG_SIZE, + HCI_MGMT_UNCONFIGURED }, + { set_public_address, MGMT_SET_PUBLIC_ADDRESS_SIZE, + HCI_MGMT_UNCONFIGURED }, + { start_service_discovery, MGMT_START_SERVICE_DISCOVERY_SIZE, + HCI_MGMT_VAR_LEN }, }; int mgmt_control(struct hci_mgmt_chan *chan, struct sock *sk, @@ -6201,6 +6214,7 @@ int mgmt_control(struct hci_mgmt_chan *chan, struct sock *sk, u16 opcode, index, len; struct hci_dev *hdev = NULL; const struct hci_mgmt_handler *handler; + bool var_len, no_hdev; int err; BT_DBG("got %zu bytes", msglen); @@ -6227,6 +6241,16 @@ int mgmt_control(struct hci_mgmt_chan *chan, struct sock *sk, goto done; } + if (opcode >= chan->handler_count || + chan->handlers[opcode].func == NULL) { + BT_DBG("Unknown op %u", opcode); + err = cmd_status(sk, index, opcode, + MGMT_STATUS_UNKNOWN_COMMAND); + goto done; + } + + handler = &chan->handlers[opcode]; + if (index != MGMT_INDEX_NONE) { hdev = hci_dev_get(index); if (!hdev) { @@ -6244,41 +6268,23 @@ int mgmt_control(struct hci_mgmt_chan *chan, struct sock *sk, } if (test_bit(HCI_UNCONFIGURED, &hdev->dev_flags) && - opcode != MGMT_OP_READ_CONFIG_INFO && - opcode != MGMT_OP_SET_EXTERNAL_CONFIG && - opcode != MGMT_OP_SET_PUBLIC_ADDRESS) { + !(handler->flags & HCI_MGMT_UNCONFIGURED)) { err = cmd_status(sk, index, opcode, MGMT_STATUS_INVALID_INDEX); goto done; } } - if (opcode >= chan->handler_count || - chan->handlers[opcode].func == NULL) { - BT_DBG("Unknown op %u", opcode); - err = cmd_status(sk, index, opcode, - MGMT_STATUS_UNKNOWN_COMMAND); - goto done; - } - - if (hdev && (opcode <= MGMT_OP_READ_INDEX_LIST || - opcode == MGMT_OP_READ_UNCONF_INDEX_LIST)) { - err = cmd_status(sk, index, opcode, - MGMT_STATUS_INVALID_INDEX); - goto done; - } - - if (!hdev && (opcode > MGMT_OP_READ_INDEX_LIST && - opcode != MGMT_OP_READ_UNCONF_INDEX_LIST)) { + no_hdev = (handler->flags & HCI_MGMT_NO_HDEV); + if (no_hdev != !hdev) { err = cmd_status(sk, index, opcode, MGMT_STATUS_INVALID_INDEX); goto done; } - handler = &chan->handlers[opcode]; - - if ((handler->var_len && len < handler->data_len) || - (!handler->var_len && len != handler->data_len)) { + var_len = (handler->flags & HCI_MGMT_VAR_LEN); + if ((var_len && len < handler->data_len) || + (!var_len && len != handler->data_len)) { err = cmd_status(sk, index, opcode, MGMT_STATUS_INVALID_PARAMS); goto done; -- cgit v1.2.3 From aaa4e70404c7b38a8792dc69af54afd7218b2ec0 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Wed, 4 Mar 2015 10:16:43 -0600 Subject: DECnet: Only use neigh_ops for adding the link layer header Other users users of the neighbour table use neigh->output as the method to decided when and which link-layer header to place on a packet. DECnet has been using neigh->output to decide which DECnet headers to place on a packet depending which neighbour the packet is destined for. The DECnet usage isn't totally wrong but it can run into problems if the neighbour output function is run for a second time as the teql driver and the bridge netfilter code can do. Therefore to avoid pathologic problems later down the line and make the neighbour code easier to understand by refactoring the decnet output code to only use a neighbour method to add a link layer header to a packet. This is done by moving the neigbhour operations lookup from dn_to_neigh_output to dn_neigh_output_packet. Signed-off-by: "Eric W. Biederman" Signed-off-by: David S. Miller --- include/net/dn_neigh.h | 1 + net/decnet/dn_neigh.c | 105 +++++++++++++++++++++++++------------------------ net/decnet/dn_route.c | 9 ----- 3 files changed, 55 insertions(+), 60 deletions(-) (limited to 'include') diff --git a/include/net/dn_neigh.h b/include/net/dn_neigh.h index fac4e3f4a6d3..0f26aa707e62 100644 --- a/include/net/dn_neigh.h +++ b/include/net/dn_neigh.h @@ -22,6 +22,7 @@ int dn_neigh_router_hello(struct sk_buff *skb); int dn_neigh_endnode_hello(struct sk_buff *skb); void dn_neigh_pointopoint_hello(struct sk_buff *skb); int dn_neigh_elist(struct net_device *dev, unsigned char *ptr, int n); +int dn_to_neigh_output(struct sk_buff *skb); extern struct neigh_table dn_neigh_table; diff --git a/net/decnet/dn_neigh.c b/net/decnet/dn_neigh.c index ee7d1cef0027..be1f08cdad29 100644 --- a/net/decnet/dn_neigh.c +++ b/net/decnet/dn_neigh.c @@ -49,41 +49,17 @@ #include static int dn_neigh_construct(struct neighbour *); -static void dn_long_error_report(struct neighbour *, struct sk_buff *); -static void dn_short_error_report(struct neighbour *, struct sk_buff *); -static int dn_long_output(struct neighbour *, struct sk_buff *); -static int dn_short_output(struct neighbour *, struct sk_buff *); -static int dn_phase3_output(struct neighbour *, struct sk_buff *); - - -/* - * For talking to broadcast devices: Ethernet & PPP - */ -static const struct neigh_ops dn_long_ops = { - .family = AF_DECnet, - .error_report = dn_long_error_report, - .output = dn_long_output, - .connected_output = dn_long_output, -}; +static void dn_neigh_error_report(struct neighbour *, struct sk_buff *); +static int dn_neigh_output(struct neighbour *neigh, struct sk_buff *skb); /* - * For talking to pointopoint and multidrop devices: DDCMP and X.25 + * Operations for adding the link layer header. */ -static const struct neigh_ops dn_short_ops = { +static const struct neigh_ops dn_neigh_ops = { .family = AF_DECnet, - .error_report = dn_short_error_report, - .output = dn_short_output, - .connected_output = dn_short_output, -}; - -/* - * For talking to DECnet phase III nodes - */ -static const struct neigh_ops dn_phase3_ops = { - .family = AF_DECnet, - .error_report = dn_short_error_report, /* Can use short version here */ - .output = dn_phase3_output, - .connected_output = dn_phase3_output, + .error_report = dn_neigh_error_report, + .output = dn_neigh_output, + .connected_output = dn_neigh_output, }; static u32 dn_neigh_hash(const void *pkey, @@ -153,16 +129,9 @@ static int dn_neigh_construct(struct neighbour *neigh) __neigh_parms_put(neigh->parms); neigh->parms = neigh_parms_clone(parms); - - if (dn_db->use_long) - neigh->ops = &dn_long_ops; - else - neigh->ops = &dn_short_ops; rcu_read_unlock(); - if (dn->flags & DN_NDFLAG_P3) - neigh->ops = &dn_phase3_ops; - + neigh->ops = &dn_neigh_ops; neigh->nud_state = NUD_NOARP; neigh->output = neigh->ops->connected_output; @@ -194,24 +163,16 @@ static int dn_neigh_construct(struct neighbour *neigh) return 0; } -static void dn_long_error_report(struct neighbour *neigh, struct sk_buff *skb) -{ - printk(KERN_DEBUG "dn_long_error_report: called\n"); - kfree_skb(skb); -} - - -static void dn_short_error_report(struct neighbour *neigh, struct sk_buff *skb) +static void dn_neigh_error_report(struct neighbour *neigh, struct sk_buff *skb) { - printk(KERN_DEBUG "dn_short_error_report: called\n"); + printk(KERN_DEBUG "dn_neigh_error_report: called\n"); kfree_skb(skb); } -static int dn_neigh_output_packet(struct sk_buff *skb) +static int dn_neigh_output(struct neighbour *neigh, struct sk_buff *skb) { struct dst_entry *dst = skb_dst(skb); struct dn_route *rt = (struct dn_route *)dst; - struct neighbour *neigh = rt->n; struct net_device *dev = neigh->dev; char mac_addr[ETH_ALEN]; unsigned int seq; @@ -233,6 +194,18 @@ static int dn_neigh_output_packet(struct sk_buff *skb) return err; } +static int dn_neigh_output_packet(struct sk_buff *skb) +{ + struct dst_entry *dst = skb_dst(skb); + struct dn_route *rt = (struct dn_route *)dst; + struct neighbour *neigh = rt->n; + + return neigh->output(neigh, skb); +} + +/* + * For talking to broadcast devices: Ethernet & PPP + */ static int dn_long_output(struct neighbour *neigh, struct sk_buff *skb) { struct net_device *dev = neigh->dev; @@ -276,6 +249,9 @@ static int dn_long_output(struct neighbour *neigh, struct sk_buff *skb) neigh->dev, dn_neigh_output_packet); } +/* + * For talking to pointopoint and multidrop devices: DDCMP and X.25 + */ static int dn_short_output(struct neighbour *neigh, struct sk_buff *skb) { struct net_device *dev = neigh->dev; @@ -313,7 +289,8 @@ static int dn_short_output(struct neighbour *neigh, struct sk_buff *skb) } /* - * Phase 3 output is the same is short output, execpt that + * For talking to DECnet phase III nodes + * Phase 3 output is the same as short output, execpt that * it clears the area bits before transmission. */ static int dn_phase3_output(struct neighbour *neigh, struct sk_buff *skb) @@ -351,6 +328,32 @@ static int dn_phase3_output(struct neighbour *neigh, struct sk_buff *skb) neigh->dev, dn_neigh_output_packet); } +int dn_to_neigh_output(struct sk_buff *skb) +{ + struct dst_entry *dst = skb_dst(skb); + struct dn_route *rt = (struct dn_route *) dst; + struct neighbour *neigh = rt->n; + struct dn_neigh *dn = (struct dn_neigh *)neigh; + struct dn_dev *dn_db; + bool use_long; + + rcu_read_lock(); + dn_db = rcu_dereference(neigh->dev->dn_ptr); + if (dn_db == NULL) { + rcu_read_unlock(); + return -EINVAL; + } + use_long = dn_db->use_long; + rcu_read_unlock(); + + if (dn->flags & DN_NDFLAG_P3) + return dn_phase3_output(neigh, skb); + if (use_long) + return dn_long_output(neigh, skb); + else + return dn_short_output(neigh, skb); +} + /* * Unfortunately, the neighbour code uses the device in its hash * function, so we don't get any advantage from it. This function diff --git a/net/decnet/dn_route.c b/net/decnet/dn_route.c index 3b81092771f8..771815575dbd 100644 --- a/net/decnet/dn_route.c +++ b/net/decnet/dn_route.c @@ -743,15 +743,6 @@ out: return NET_RX_DROP; } -static int dn_to_neigh_output(struct sk_buff *skb) -{ - struct dst_entry *dst = skb_dst(skb); - struct dn_route *rt = (struct dn_route *) dst; - struct neighbour *n = rt->n; - - return n->output(n, skb); -} - static int dn_output(struct sock *sk, struct sk_buff *skb) { struct dst_entry *dst = skb_dst(skb); -- cgit v1.2.3 From dcd8fb8533ceb493146ce030d15f7965b82d0c27 Mon Sep 17 00:00:00 2001 From: Fan Du Date: Fri, 6 Mar 2015 11:18:22 +0800 Subject: ipv4: Raise tcp PMTU probe mss base size Quotes from RFC4821 7.2. Selecting Initial Values It is RECOMMENDED that search_low be initially set to an MTU size that is likely to work over a very wide range of environments. Given today's technologies, a value of 1024 bytes is probably safe enough. The initial value for search_low SHOULD be configurable. Moreover, set a small value will introduce extra time for the search to converge. So set the initial probe base mss size to 1024 Bytes. Signed-off-by: Fan Du Acked-by: John Heffner Signed-off-by: David S. Miller --- include/net/tcp.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/net/tcp.h b/include/net/tcp.h index f87599d5af82..834089b0cffc 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -65,7 +65,7 @@ void tcp_time_wait(struct sock *sk, int state, int timeo); #define TCP_MIN_MSS 88U /* The least MTU to use for probing */ -#define TCP_BASE_MSS 512 +#define TCP_BASE_MSS 1024 /* After receiving this amount of duplicate ACKs fast retransmit starts. */ #define TCP_FASTRETRANS_THRESH 3 -- cgit v1.2.3 From 6b58e0a5f32dedb609438bb9c9c82aa6e23381f2 Mon Sep 17 00:00:00 2001 From: Fan Du Date: Fri, 6 Mar 2015 11:18:23 +0800 Subject: ipv4: Use binary search to choose tcp PMTU probe_size Current probe_size is chosen by doubling mss_cache, the probing process will end shortly with a sub-optimal mss size, and the link mtu will not be taken full advantage of, in return, this will make user to tweak tcp_base_mss with care. Use binary search to choose probe_size in a fine granularity manner, an optimal mss will be found to boost performance as its maxmium. In addition, introduce a sysctl_tcp_probe_threshold to control when probing will stop in respect to the width of search range. Test env: Docker instance with vxlan encapuslation(82599EB) iperf -c 10.0.0.24 -t 60 before this patch: 1.26 Gbits/sec After this patch: increase 26% 1.59 Gbits/sec Signed-off-by: Fan Du Acked-by: John Heffner Signed-off-by: David S. Miller --- include/net/netns/ipv4.h | 1 + include/net/tcp.h | 3 +++ net/ipv4/sysctl_net_ipv4.c | 7 +++++++ net/ipv4/tcp_ipv4.c | 1 + net/ipv4/tcp_output.c | 14 +++++++++++--- 5 files changed, 23 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/include/net/netns/ipv4.h b/include/net/netns/ipv4.h index 1085e12f940f..e051d399fa17 100644 --- a/include/net/netns/ipv4.h +++ b/include/net/netns/ipv4.h @@ -87,6 +87,7 @@ struct netns_ipv4 { int sysctl_tcp_fwmark_accept; int sysctl_tcp_mtu_probing; int sysctl_tcp_base_mss; + int sysctl_tcp_probe_threshold; struct ping_group_range ping_group_range; diff --git a/include/net/tcp.h b/include/net/tcp.h index 834089b0cffc..1ad82e334e27 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -67,6 +67,9 @@ void tcp_time_wait(struct sock *sk, int state, int timeo); /* The least MTU to use for probing */ #define TCP_BASE_MSS 1024 +/* Specify interval when tcp mtu probing will stop */ +#define TCP_PROBE_THRESHOLD 8 + /* After receiving this amount of duplicate ACKs fast retransmit starts. */ #define TCP_FASTRETRANS_THRESH 3 diff --git a/net/ipv4/sysctl_net_ipv4.c b/net/ipv4/sysctl_net_ipv4.c index d151539da8e6..d3c09c12ee81 100644 --- a/net/ipv4/sysctl_net_ipv4.c +++ b/net/ipv4/sysctl_net_ipv4.c @@ -883,6 +883,13 @@ static struct ctl_table ipv4_net_table[] = { .mode = 0644, .proc_handler = proc_dointvec, }, + { + .procname = "tcp_probe_threshold", + .data = &init_net.ipv4.sysctl_tcp_probe_threshold, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = proc_dointvec, + }, { } }; diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index 5a2dfed4783b..35790d977a2b 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -2460,6 +2460,7 @@ static int __net_init tcp_sk_init(struct net *net) } net->ipv4.sysctl_tcp_ecn = 2; net->ipv4.sysctl_tcp_base_mss = TCP_BASE_MSS; + net->ipv4.sysctl_tcp_probe_threshold = TCP_PROBE_THRESHOLD; return 0; fail: diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c index 8bbd86cd81c8..ed024cbb097f 100644 --- a/net/ipv4/tcp_output.c +++ b/net/ipv4/tcp_output.c @@ -1842,11 +1842,13 @@ static int tcp_mtu_probe(struct sock *sk) struct tcp_sock *tp = tcp_sk(sk); struct inet_connection_sock *icsk = inet_csk(sk); struct sk_buff *skb, *nskb, *next; + struct net *net = sock_net(sk); int len; int probe_size; int size_needed; int copy; int mss_now; + int interval; /* Not currently probing/verifying, * not in recovery, @@ -1859,11 +1861,17 @@ static int tcp_mtu_probe(struct sock *sk) tp->rx_opt.num_sacks || tp->rx_opt.dsack) return -1; - /* Very simple search strategy: just double the MSS. */ + /* Use binary search for probe_size between tcp_mss_base, + * and current mss_clamp. if (search_high - search_low) + * smaller than a threshold, backoff from probing. + */ mss_now = tcp_current_mss(sk); - probe_size = 2 * tp->mss_cache; + probe_size = tcp_mtu_to_mss(sk, (icsk->icsk_mtup.search_high + + icsk->icsk_mtup.search_low) >> 1); size_needed = probe_size + (tp->reordering + 1) * tp->mss_cache; - if (probe_size > tcp_mtu_to_mss(sk, icsk->icsk_mtup.search_high)) { + interval = icsk->icsk_mtup.search_high - icsk->icsk_mtup.search_low; + if (probe_size > tcp_mtu_to_mss(sk, icsk->icsk_mtup.search_high) || + interval < max(1, net->ipv4.sysctl_tcp_probe_threshold)) { /* TODO: set timer for probe_converge_event */ return -1; } -- cgit v1.2.3 From 05cbc0db03e82128f2e7e353d4194dd24a1627fe Mon Sep 17 00:00:00 2001 From: Fan Du Date: Fri, 6 Mar 2015 11:18:24 +0800 Subject: ipv4: Create probe timer for tcp PMTU as per RFC4821 As per RFC4821 7.3. Selecting Probe Size, a probe timer should be armed once probing has converged. Once this timer expired, probing again to take advantage of any path PMTU change. The recommended probing interval is 10 minutes per RFC1981. Probing interval could be sysctled by sysctl_tcp_probe_interval. Eric Dumazet suggested to implement pseudo timer based on 32bits jiffies tcp_time_stamp instead of using classic timer for such rare event. Signed-off-by: Fan Du Signed-off-by: David S. Miller --- include/net/inet_connection_sock.h | 2 ++ include/net/netns/ipv4.h | 1 + include/net/tcp.h | 3 +++ net/ipv4/sysctl_net_ipv4.c | 7 +++++++ net/ipv4/tcp_ipv4.c | 1 + net/ipv4/tcp_output.c | 38 ++++++++++++++++++++++++++++++++++++-- net/ipv4/tcp_timer.c | 1 + 7 files changed, 51 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/net/inet_connection_sock.h b/include/net/inet_connection_sock.h index 5976bdecf58b..b9a6b0a94cc6 100644 --- a/include/net/inet_connection_sock.h +++ b/include/net/inet_connection_sock.h @@ -126,6 +126,8 @@ struct inet_connection_sock { /* Information on the current probe. */ int probe_size; + + u32 probe_timestamp; } icsk_mtup; u32 icsk_ca_priv[16]; u32 icsk_user_timeout; diff --git a/include/net/netns/ipv4.h b/include/net/netns/ipv4.h index e051d399fa17..8f3a1a1a5a94 100644 --- a/include/net/netns/ipv4.h +++ b/include/net/netns/ipv4.h @@ -88,6 +88,7 @@ struct netns_ipv4 { int sysctl_tcp_mtu_probing; int sysctl_tcp_base_mss; int sysctl_tcp_probe_threshold; + u32 sysctl_tcp_probe_interval; struct ping_group_range ping_group_range; diff --git a/include/net/tcp.h b/include/net/tcp.h index 1ad82e334e27..2e11e38205c2 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -67,6 +67,9 @@ void tcp_time_wait(struct sock *sk, int state, int timeo); /* The least MTU to use for probing */ #define TCP_BASE_MSS 1024 +/* probing interval, default to 10 minutes as per RFC4821 */ +#define TCP_PROBE_INTERVAL 600 + /* Specify interval when tcp mtu probing will stop */ #define TCP_PROBE_THRESHOLD 8 diff --git a/net/ipv4/sysctl_net_ipv4.c b/net/ipv4/sysctl_net_ipv4.c index d3c09c12ee81..fdf899163d44 100644 --- a/net/ipv4/sysctl_net_ipv4.c +++ b/net/ipv4/sysctl_net_ipv4.c @@ -890,6 +890,13 @@ static struct ctl_table ipv4_net_table[] = { .mode = 0644, .proc_handler = proc_dointvec, }, + { + .procname = "tcp_probe_interval", + .data = &init_net.ipv4.sysctl_tcp_probe_interval, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = proc_dointvec, + }, { } }; diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index 35790d977a2b..f0c6fc32bfa8 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -2461,6 +2461,7 @@ static int __net_init tcp_sk_init(struct net *net) net->ipv4.sysctl_tcp_ecn = 2; net->ipv4.sysctl_tcp_base_mss = TCP_BASE_MSS; net->ipv4.sysctl_tcp_probe_threshold = TCP_PROBE_THRESHOLD; + net->ipv4.sysctl_tcp_probe_interval = TCP_PROBE_INTERVAL; return 0; fail: diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c index ed024cbb097f..5a73ad5afaf7 100644 --- a/net/ipv4/tcp_output.c +++ b/net/ipv4/tcp_output.c @@ -1354,6 +1354,8 @@ void tcp_mtup_init(struct sock *sk) icsk->icsk_af_ops->net_header_len; icsk->icsk_mtup.search_low = tcp_mss_to_mtu(sk, net->ipv4.sysctl_tcp_base_mss); icsk->icsk_mtup.probe_size = 0; + if (icsk->icsk_mtup.enabled) + icsk->icsk_mtup.probe_timestamp = tcp_time_stamp; } EXPORT_SYMBOL(tcp_mtup_init); @@ -1828,6 +1830,31 @@ send_now: return false; } +static inline void tcp_mtu_check_reprobe(struct sock *sk) +{ + struct inet_connection_sock *icsk = inet_csk(sk); + struct tcp_sock *tp = tcp_sk(sk); + struct net *net = sock_net(sk); + u32 interval; + s32 delta; + + interval = net->ipv4.sysctl_tcp_probe_interval; + delta = tcp_time_stamp - icsk->icsk_mtup.probe_timestamp; + if (unlikely(delta >= interval * HZ)) { + int mss = tcp_current_mss(sk); + + /* Update current search range */ + icsk->icsk_mtup.probe_size = 0; + icsk->icsk_mtup.search_high = tp->rx_opt.mss_clamp + + sizeof(struct tcphdr) + + icsk->icsk_af_ops->net_header_len; + icsk->icsk_mtup.search_low = tcp_mss_to_mtu(sk, mss); + + /* Update probe time stamp */ + icsk->icsk_mtup.probe_timestamp = tcp_time_stamp; + } +} + /* Create a new MTU probe if we are ready. * MTU probe is regularly attempting to increase the path MTU by * deliberately sending larger packets. This discovers routing @@ -1870,9 +1897,16 @@ static int tcp_mtu_probe(struct sock *sk) icsk->icsk_mtup.search_low) >> 1); size_needed = probe_size + (tp->reordering + 1) * tp->mss_cache; interval = icsk->icsk_mtup.search_high - icsk->icsk_mtup.search_low; + /* When misfortune happens, we are reprobing actively, + * and then reprobe timer has expired. We stick with current + * probing process by not resetting search range to its orignal. + */ if (probe_size > tcp_mtu_to_mss(sk, icsk->icsk_mtup.search_high) || - interval < max(1, net->ipv4.sysctl_tcp_probe_threshold)) { - /* TODO: set timer for probe_converge_event */ + interval < net->ipv4.sysctl_tcp_probe_threshold) { + /* Check whether enough time has elaplased for + * another round of probing. + */ + tcp_mtu_check_reprobe(sk); return -1; } diff --git a/net/ipv4/tcp_timer.c b/net/ipv4/tcp_timer.c index 0732b787904e..15505936511d 100644 --- a/net/ipv4/tcp_timer.c +++ b/net/ipv4/tcp_timer.c @@ -107,6 +107,7 @@ static void tcp_mtu_probing(struct inet_connection_sock *icsk, struct sock *sk) if (net->ipv4.sysctl_tcp_mtu_probing) { if (!icsk->icsk_mtup.enabled) { icsk->icsk_mtup.enabled = 1; + icsk->icsk_mtup.probe_timestamp = tcp_time_stamp; tcp_sync_mss(sk, icsk->icsk_pmtu_cookie); } else { struct net *net = sock_net(sk); -- cgit v1.2.3 From 89650ad0047f039b3c3bc0f6a5823bb9c9738152 Mon Sep 17 00:00:00 2001 From: Willem de Bruijn Date: Fri, 6 Mar 2015 11:44:28 -0500 Subject: fib: make netdev_switch_fib_ipv4_abort in header file static inline When building without CONFIG_NET_SWITCHDEV, netdev_switch_fib_ipv4_abort is defined in the header file. It must be static inline to avoid build failure at link time. Fixes: 8e05fd7166c6 ("fib: hook IPv4 fib for hardware offload") Signed-off-by: Willem de Bruijn Acked-by: Scott Feldman Signed-off-by: David S. Miller --- include/net/switchdev.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/net/switchdev.h b/include/net/switchdev.h index dc0a5cc7c2c5..933fac410a7a 100644 --- a/include/net/switchdev.h +++ b/include/net/switchdev.h @@ -129,7 +129,7 @@ static inline int netdev_switch_fib_ipv4_del(u32 dst, int dst_len, return 0; } -void netdev_switch_fib_ipv4_abort(struct fib_info *fi) +static inline void netdev_switch_fib_ipv4_abort(struct fib_info *fi) { } -- cgit v1.2.3 From 637473cf006fe4cd85aed0fb69b6c917d868ada2 Mon Sep 17 00:00:00 2001 From: Sharon Dvir Date: Thu, 22 Jan 2015 12:15:25 +0200 Subject: mod_devicetable: fix comment for match_flags Signed-off-by: Sharon Dvir Signed-off-by: Jiri Kosina --- include/linux/mod_devicetable.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/include/linux/mod_devicetable.h b/include/linux/mod_devicetable.h index 745def862580..470a240f66a1 100644 --- a/include/linux/mod_devicetable.h +++ b/include/linux/mod_devicetable.h @@ -53,9 +53,9 @@ struct ieee1394_device_id { /** * struct usb_device_id - identifies USB devices for probing and hotplugging - * @match_flags: Bit mask controlling of the other fields are used to match - * against new devices. Any field except for driver_info may be used, - * although some only make sense in conjunction with other fields. + * @match_flags: Bit mask controlling which of the other fields are used to + * match against new devices. Any field except for driver_info may be + * used, although some only make sense in conjunction with other fields. * This is usually set by a USB_DEVICE_*() macro, which sets all * other fields in this structure except for driver_info. * @idVendor: USB vendor ID for a device; numbers are assigned -- cgit v1.2.3 From c5b66e47251d797e38f3ee8ec8d613780506c245 Mon Sep 17 00:00:00 2001 From: Jiang Liu Date: Fri, 23 Jan 2015 13:36:55 +0800 Subject: smpboot.h: Remove unused function prototype Function smpboot_thread_schedule() is neither used nor defined, so kill it. Signed-off-by: Jiang Liu Signed-off-by: Jiri Kosina --- include/linux/smpboot.h | 1 - 1 file changed, 1 deletion(-) (limited to 'include') diff --git a/include/linux/smpboot.h b/include/linux/smpboot.h index 13e929679550..d600afb21926 100644 --- a/include/linux/smpboot.h +++ b/include/linux/smpboot.h @@ -47,6 +47,5 @@ struct smp_hotplug_thread { int smpboot_register_percpu_thread(struct smp_hotplug_thread *plug_thread); void smpboot_unregister_percpu_thread(struct smp_hotplug_thread *plug_thread); -int smpboot_thread_schedule(void); #endif -- cgit v1.2.3 From f38bacb3ebdd30ae4695735a82497659ba05d9fa Mon Sep 17 00:00:00 2001 From: Himanshu Maithani Date: Fri, 30 Jan 2015 01:31:14 +0530 Subject: stacktrace.h: remove duplicate declaration task_struct There is duplicate declaration for struct task_struct. One can be removed safely. Signed-off-by: Himanshu Maithani Signed-off-by: Jiri Kosina --- include/linux/stacktrace.h | 2 -- 1 file changed, 2 deletions(-) (limited to 'include') diff --git a/include/linux/stacktrace.h b/include/linux/stacktrace.h index 115b570e3bff..1fea0380e97f 100644 --- a/include/linux/stacktrace.h +++ b/include/linux/stacktrace.h @@ -5,8 +5,6 @@ struct task_struct; struct pt_regs; #ifdef CONFIG_STACKTRACE -struct task_struct; - struct stack_trace { unsigned int nr_entries, max_entries; unsigned long *entries; -- cgit v1.2.3 From bfd30caebaba7f00c13eec3c538799202d36d12a Mon Sep 17 00:00:00 2001 From: Stefan Brüns Date: Thu, 5 Mar 2015 23:15:07 -0800 Subject: Input: rename KEY_DIRECTION to KEY_ROTATE_DISPLAY MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The new name better reflects intended usage (but we are keeping the old name as an alias for compatibility). Signed-off-by: Stefan Brüns Signed-off-by: Dmitry Torokhov --- include/uapi/linux/input.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/uapi/linux/input.h b/include/uapi/linux/input.h index a1d7e931ab72..eff1cf18031e 100644 --- a/include/uapi/linux/input.h +++ b/include/uapi/linux/input.h @@ -368,7 +368,8 @@ struct input_keymap_entry { #define KEY_MSDOS 151 #define KEY_COFFEE 152 /* AL Terminal Lock/Screensaver */ #define KEY_SCREENLOCK KEY_COFFEE -#define KEY_DIRECTION 153 +#define KEY_ROTATE_DISPLAY 153 /* Display orientation for e.g. tablets */ +#define KEY_DIRECTION KEY_ROTATE_DISPLAY #define KEY_CYCLEWINDOWS 154 #define KEY_MAIL 155 #define KEY_BOOKMARKS 156 /* AC Bookmarks */ -- cgit v1.2.3 From 2b49e0c56741fca538176f66ed3c8d16ce4fccd8 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Mon, 23 Feb 2015 16:24:42 +0200 Subject: dmaengine: append hsu DMA driver The HSU DMA is developed to support High Speed UART controllers found in particular on Intel MID platforms such as Intel Medfield. The existing implementation is tighten to the drivers/tty/serial/mfd.c driver and has a lot of disadvantages. Besides that we would like to get rid of the old HS UART driver in regarding to extending the 8250 which supports generic DMAEngine API. That's why the current driver has been developed. Signed-off-by: Andy Shevchenko Signed-off-by: Greg Kroah-Hartman --- drivers/dma/Kconfig | 2 + drivers/dma/Makefile | 1 + drivers/dma/hsu/Kconfig | 14 + drivers/dma/hsu/Makefile | 5 + drivers/dma/hsu/hsu.c | 504 ++++++++++++++++++++++++++++++++++ drivers/dma/hsu/hsu.h | 118 ++++++++ drivers/dma/hsu/pci.c | 123 +++++++++ include/linux/dma/hsu.h | 48 ++++ include/linux/platform_data/dma-hsu.h | 25 ++ 9 files changed, 840 insertions(+) create mode 100644 drivers/dma/hsu/Kconfig create mode 100644 drivers/dma/hsu/Makefile create mode 100644 drivers/dma/hsu/hsu.c create mode 100644 drivers/dma/hsu/hsu.h create mode 100644 drivers/dma/hsu/pci.c create mode 100644 include/linux/dma/hsu.h create mode 100644 include/linux/platform_data/dma-hsu.h (limited to 'include') diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig index a874b6ec6650..074ffad334a7 100644 --- a/drivers/dma/Kconfig +++ b/drivers/dma/Kconfig @@ -125,6 +125,8 @@ config FSL_DMA EloPlus is on mpc85xx and mpc86xx and Pxxx parts, and the Elo3 is on some Txxx and Bxxx parts. +source "drivers/dma/hsu/Kconfig" + config MPC512X_DMA tristate "Freescale MPC512x built-in DMA engine support" depends on PPC_MPC512x || PPC_MPC831x diff --git a/drivers/dma/Makefile b/drivers/dma/Makefile index f915f61ec574..bf4485800c60 100644 --- a/drivers/dma/Makefile +++ b/drivers/dma/Makefile @@ -11,6 +11,7 @@ obj-$(CONFIG_DMATEST) += dmatest.o obj-$(CONFIG_INTEL_IOATDMA) += ioat/ obj-$(CONFIG_INTEL_IOP_ADMA) += iop-adma.o obj-$(CONFIG_FSL_DMA) += fsldma.o +obj-$(CONFIG_HSU_DMA) += hsu/ obj-$(CONFIG_MPC512X_DMA) += mpc512x_dma.o obj-$(CONFIG_PPC_BESTCOMM) += bestcomm/ obj-$(CONFIG_MV_XOR) += mv_xor.o diff --git a/drivers/dma/hsu/Kconfig b/drivers/dma/hsu/Kconfig new file mode 100644 index 000000000000..7e98eff7440e --- /dev/null +++ b/drivers/dma/hsu/Kconfig @@ -0,0 +1,14 @@ +# DMA engine configuration for hsu +config HSU_DMA + tristate "High Speed UART DMA support" + select DMA_ENGINE + select DMA_VIRTUAL_CHANNELS + +config HSU_DMA_PCI + tristate "High Speed UART DMA PCI driver" + depends on PCI + select HSU_DMA + help + Support the High Speed UART DMA on the platfroms that + enumerate it as a PCI device. For example, Intel Medfield + has integrated this HSU DMA controller. diff --git a/drivers/dma/hsu/Makefile b/drivers/dma/hsu/Makefile new file mode 100644 index 000000000000..b8f9af032ef1 --- /dev/null +++ b/drivers/dma/hsu/Makefile @@ -0,0 +1,5 @@ +obj-$(CONFIG_HSU_DMA) += hsu_dma.o +hsu_dma-objs := hsu.o + +obj-$(CONFIG_HSU_DMA_PCI) += hsu_dma_pci.o +hsu_dma_pci-objs := pci.o diff --git a/drivers/dma/hsu/hsu.c b/drivers/dma/hsu/hsu.c new file mode 100644 index 000000000000..683ba9b62795 --- /dev/null +++ b/drivers/dma/hsu/hsu.c @@ -0,0 +1,504 @@ +/* + * Core driver for the High Speed UART DMA + * + * Copyright (C) 2015 Intel Corporation + * Author: Andy Shevchenko + * + * Partially based on the bits found in drivers/tty/serial/mfd.c. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +/* + * DMA channel allocation: + * 1. Even number chans are used for DMA Read (UART TX), odd chans for DMA + * Write (UART RX). + * 2. 0/1 channel are assigned to port 0, 2/3 chan to port 1, 4/5 chan to + * port 3, and so on. + */ + +#include +#include +#include +#include +#include +#include + +#include "hsu.h" + +#define HSU_DMA_BUSWIDTHS \ + BIT(DMA_SLAVE_BUSWIDTH_UNDEFINED) | \ + BIT(DMA_SLAVE_BUSWIDTH_1_BYTE) | \ + BIT(DMA_SLAVE_BUSWIDTH_2_BYTES) | \ + BIT(DMA_SLAVE_BUSWIDTH_3_BYTES) | \ + BIT(DMA_SLAVE_BUSWIDTH_4_BYTES) | \ + BIT(DMA_SLAVE_BUSWIDTH_8_BYTES) | \ + BIT(DMA_SLAVE_BUSWIDTH_16_BYTES) + +static inline void hsu_chan_disable(struct hsu_dma_chan *hsuc) +{ + hsu_chan_writel(hsuc, HSU_CH_CR, 0); +} + +static inline void hsu_chan_enable(struct hsu_dma_chan *hsuc) +{ + u32 cr = HSU_CH_CR_CHA; + + if (hsuc->direction == DMA_MEM_TO_DEV) + cr &= ~HSU_CH_CR_CHD; + else if (hsuc->direction == DMA_DEV_TO_MEM) + cr |= HSU_CH_CR_CHD; + + hsu_chan_writel(hsuc, HSU_CH_CR, cr); +} + +static void hsu_dma_chan_start(struct hsu_dma_chan *hsuc) +{ + struct dma_slave_config *config = &hsuc->config; + struct hsu_dma_desc *desc = hsuc->desc; + u32 bsr, mtsr; + u32 dcr = HSU_CH_DCR_CHSOE | HSU_CH_DCR_CHEI; + unsigned int i, count; + + if (hsuc->direction == DMA_MEM_TO_DEV) { + bsr = config->dst_maxburst; + mtsr = config->dst_addr_width; + } else if (hsuc->direction == DMA_DEV_TO_MEM) { + bsr = config->src_maxburst; + mtsr = config->src_addr_width; + } else { + /* Not supported direction */ + return; + } + + hsu_chan_disable(hsuc); + + hsu_chan_writel(hsuc, HSU_CH_DCR, 0); + hsu_chan_writel(hsuc, HSU_CH_BSR, bsr); + hsu_chan_writel(hsuc, HSU_CH_MTSR, mtsr); + + /* Set descriptors */ + count = (desc->nents - desc->active) % HSU_DMA_CHAN_NR_DESC; + for (i = 0; i < count; i++) { + hsu_chan_writel(hsuc, HSU_CH_DxSAR(i), desc->sg[i].addr); + hsu_chan_writel(hsuc, HSU_CH_DxTSR(i), desc->sg[i].len); + + /* Prepare value for DCR */ + dcr |= HSU_CH_DCR_DESCA(i); + dcr |= HSU_CH_DCR_CHTOI(i); /* timeout bit, see HSU Errata 1 */ + + desc->active++; + } + /* Only for the last descriptor in the chain */ + dcr |= HSU_CH_DCR_CHSOD(count - 1); + dcr |= HSU_CH_DCR_CHDI(count - 1); + + hsu_chan_writel(hsuc, HSU_CH_DCR, dcr); + + hsu_chan_enable(hsuc); +} + +static void hsu_dma_stop_channel(struct hsu_dma_chan *hsuc) +{ + unsigned long flags; + + spin_lock_irqsave(&hsuc->lock, flags); + hsu_chan_disable(hsuc); + hsu_chan_writel(hsuc, HSU_CH_DCR, 0); + spin_unlock_irqrestore(&hsuc->lock, flags); +} + +static void hsu_dma_start_channel(struct hsu_dma_chan *hsuc) +{ + unsigned long flags; + + spin_lock_irqsave(&hsuc->lock, flags); + hsu_dma_chan_start(hsuc); + spin_unlock_irqrestore(&hsuc->lock, flags); +} + +static void hsu_dma_start_transfer(struct hsu_dma_chan *hsuc) +{ + struct virt_dma_desc *vdesc; + + /* Get the next descriptor */ + vdesc = vchan_next_desc(&hsuc->vchan); + if (!vdesc) { + hsuc->desc = NULL; + return; + } + + list_del(&vdesc->node); + hsuc->desc = to_hsu_dma_desc(vdesc); + + /* Start the channel with a new descriptor */ + hsu_dma_start_channel(hsuc); +} + +static u32 hsu_dma_chan_get_sr(struct hsu_dma_chan *hsuc) +{ + unsigned long flags; + u32 sr; + + spin_lock_irqsave(&hsuc->lock, flags); + sr = hsu_chan_readl(hsuc, HSU_CH_SR); + spin_unlock_irqrestore(&hsuc->lock, flags); + + return sr; +} + +irqreturn_t hsu_dma_irq(struct hsu_dma_chip *chip, unsigned short nr) +{ + struct hsu_dma_chan *hsuc; + struct hsu_dma_desc *desc; + unsigned long flags; + u32 sr; + + /* Sanity check */ + if (nr >= chip->pdata->nr_channels) + return IRQ_NONE; + + hsuc = &chip->hsu->chan[nr]; + + /* + * No matter what situation, need read clear the IRQ status + * There is a bug, see Errata 5, HSD 2900918 + */ + sr = hsu_dma_chan_get_sr(hsuc); + if (!sr) + return IRQ_NONE; + + /* Timeout IRQ, need wait some time, see Errata 2 */ + if (hsuc->direction == DMA_DEV_TO_MEM && (sr & HSU_CH_SR_DESCTO_ANY)) + udelay(2); + + sr &= ~HSU_CH_SR_DESCTO_ANY; + if (!sr) + return IRQ_HANDLED; + + spin_lock_irqsave(&hsuc->vchan.lock, flags); + desc = hsuc->desc; + if (desc) { + if (sr & HSU_CH_SR_CHE) { + desc->status = DMA_ERROR; + } else if (desc->active < desc->nents) { + hsu_dma_start_channel(hsuc); + } else { + vchan_cookie_complete(&desc->vdesc); + desc->status = DMA_COMPLETE; + hsu_dma_start_transfer(hsuc); + } + } + spin_unlock_irqrestore(&hsuc->vchan.lock, flags); + + return IRQ_HANDLED; +} +EXPORT_SYMBOL_GPL(hsu_dma_irq); + +static struct hsu_dma_desc *hsu_dma_alloc_desc(unsigned int nents) +{ + struct hsu_dma_desc *desc; + + desc = kzalloc(sizeof(*desc), GFP_ATOMIC); + if (!desc) + return NULL; + + desc->sg = kcalloc(nents, sizeof(*desc->sg), GFP_ATOMIC); + if (!desc->sg) { + kfree(desc); + return NULL; + } + + return desc; +} + +static void hsu_dma_desc_free(struct virt_dma_desc *vdesc) +{ + struct hsu_dma_desc *desc = to_hsu_dma_desc(vdesc); + + kfree(desc->sg); + kfree(desc); +} + +static struct dma_async_tx_descriptor *hsu_dma_prep_slave_sg( + struct dma_chan *chan, struct scatterlist *sgl, + unsigned int sg_len, enum dma_transfer_direction direction, + unsigned long flags, void *context) +{ + struct hsu_dma_chan *hsuc = to_hsu_dma_chan(chan); + struct hsu_dma_desc *desc; + struct scatterlist *sg; + unsigned int i; + + desc = hsu_dma_alloc_desc(sg_len); + if (!desc) + return NULL; + + for_each_sg(sgl, sg, sg_len, i) { + desc->sg[i].addr = sg_dma_address(sg); + desc->sg[i].len = sg_dma_len(sg); + } + + desc->nents = sg_len; + desc->direction = direction; + desc->active = 0; + desc->status = DMA_IN_PROGRESS; + + return vchan_tx_prep(&hsuc->vchan, &desc->vdesc, flags); +} + +static void hsu_dma_issue_pending(struct dma_chan *chan) +{ + struct hsu_dma_chan *hsuc = to_hsu_dma_chan(chan); + unsigned long flags; + + spin_lock_irqsave(&hsuc->vchan.lock, flags); + if (vchan_issue_pending(&hsuc->vchan) && !hsuc->desc) + hsu_dma_start_transfer(hsuc); + spin_unlock_irqrestore(&hsuc->vchan.lock, flags); +} + +static size_t hsu_dma_desc_size(struct hsu_dma_desc *desc) +{ + size_t bytes = 0; + unsigned int i; + + for (i = desc->active; i < desc->nents; i++) + bytes += desc->sg[i].len; + + return bytes; +} + +static size_t hsu_dma_active_desc_size(struct hsu_dma_chan *hsuc) +{ + struct hsu_dma_desc *desc = hsuc->desc; + size_t bytes = hsu_dma_desc_size(desc); + int i; + unsigned long flags; + + spin_lock_irqsave(&hsuc->lock, flags); + i = desc->active % HSU_DMA_CHAN_NR_DESC; + do { + bytes += hsu_chan_readl(hsuc, HSU_CH_DxTSR(i)); + } while (--i >= 0); + spin_unlock_irqrestore(&hsuc->lock, flags); + + return bytes; +} + +static enum dma_status hsu_dma_tx_status(struct dma_chan *chan, + dma_cookie_t cookie, struct dma_tx_state *state) +{ + struct hsu_dma_chan *hsuc = to_hsu_dma_chan(chan); + struct virt_dma_desc *vdesc; + enum dma_status status; + size_t bytes; + unsigned long flags; + + status = dma_cookie_status(chan, cookie, state); + if (status == DMA_COMPLETE) + return status; + + spin_lock_irqsave(&hsuc->vchan.lock, flags); + vdesc = vchan_find_desc(&hsuc->vchan, cookie); + if (hsuc->desc && cookie == hsuc->desc->vdesc.tx.cookie) { + bytes = hsu_dma_active_desc_size(hsuc); + dma_set_residue(state, bytes); + status = hsuc->desc->status; + } else if (vdesc) { + bytes = hsu_dma_desc_size(to_hsu_dma_desc(vdesc)); + dma_set_residue(state, bytes); + } + spin_unlock_irqrestore(&hsuc->vchan.lock, flags); + + return status; +} + +static int hsu_dma_slave_config(struct dma_chan *chan, + struct dma_slave_config *config) +{ + struct hsu_dma_chan *hsuc = to_hsu_dma_chan(chan); + + /* Check if chan will be configured for slave transfers */ + if (!is_slave_direction(config->direction)) + return -EINVAL; + + memcpy(&hsuc->config, config, sizeof(hsuc->config)); + + return 0; +} + +static void hsu_dma_chan_deactivate(struct hsu_dma_chan *hsuc) +{ + unsigned long flags; + + spin_lock_irqsave(&hsuc->lock, flags); + hsu_chan_disable(hsuc); + spin_unlock_irqrestore(&hsuc->lock, flags); +} + +static void hsu_dma_chan_activate(struct hsu_dma_chan *hsuc) +{ + unsigned long flags; + + spin_lock_irqsave(&hsuc->lock, flags); + hsu_chan_enable(hsuc); + spin_unlock_irqrestore(&hsuc->lock, flags); +} + +static int hsu_dma_pause(struct dma_chan *chan) +{ + struct hsu_dma_chan *hsuc = to_hsu_dma_chan(chan); + unsigned long flags; + + spin_lock_irqsave(&hsuc->vchan.lock, flags); + if (hsuc->desc && hsuc->desc->status == DMA_IN_PROGRESS) { + hsu_dma_chan_deactivate(hsuc); + hsuc->desc->status = DMA_PAUSED; + } + spin_unlock_irqrestore(&hsuc->vchan.lock, flags); + + return 0; +} + +static int hsu_dma_resume(struct dma_chan *chan) +{ + struct hsu_dma_chan *hsuc = to_hsu_dma_chan(chan); + unsigned long flags; + + spin_lock_irqsave(&hsuc->vchan.lock, flags); + if (hsuc->desc && hsuc->desc->status == DMA_PAUSED) { + hsuc->desc->status = DMA_IN_PROGRESS; + hsu_dma_chan_activate(hsuc); + } + spin_unlock_irqrestore(&hsuc->vchan.lock, flags); + + return 0; +} + +static int hsu_dma_terminate_all(struct dma_chan *chan) +{ + struct hsu_dma_chan *hsuc = to_hsu_dma_chan(chan); + unsigned long flags; + LIST_HEAD(head); + + spin_lock_irqsave(&hsuc->vchan.lock, flags); + + hsu_dma_stop_channel(hsuc); + hsuc->desc = NULL; + + vchan_get_all_descriptors(&hsuc->vchan, &head); + spin_unlock_irqrestore(&hsuc->vchan.lock, flags); + vchan_dma_desc_free_list(&hsuc->vchan, &head); + + return 0; +} + +static int hsu_dma_alloc_chan_resources(struct dma_chan *chan) +{ + return 0; +} + +static void hsu_dma_free_chan_resources(struct dma_chan *chan) +{ + vchan_free_chan_resources(to_virt_chan(chan)); +} + +int hsu_dma_probe(struct hsu_dma_chip *chip) +{ + struct hsu_dma *hsu; + struct hsu_dma_platform_data *pdata = chip->pdata; + void __iomem *addr = chip->regs + chip->offset; + unsigned short i; + int ret; + + hsu = devm_kzalloc(chip->dev, sizeof(*hsu), GFP_KERNEL); + if (!hsu) + return -ENOMEM; + + chip->hsu = hsu; + + if (!pdata) { + pdata = devm_kzalloc(chip->dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) + return -ENOMEM; + + chip->pdata = pdata; + + /* Guess nr_channels from the IO space length */ + pdata->nr_channels = (chip->length - chip->offset) / + HSU_DMA_CHAN_LENGTH; + } + + hsu->chan = devm_kcalloc(chip->dev, pdata->nr_channels, + sizeof(*hsu->chan), GFP_KERNEL); + if (!hsu->chan) + return -ENOMEM; + + INIT_LIST_HEAD(&hsu->dma.channels); + for (i = 0; i < pdata->nr_channels; i++) { + struct hsu_dma_chan *hsuc = &hsu->chan[i]; + + hsuc->vchan.desc_free = hsu_dma_desc_free; + vchan_init(&hsuc->vchan, &hsu->dma); + + hsuc->direction = (i & 0x1) ? DMA_DEV_TO_MEM : DMA_MEM_TO_DEV; + hsuc->reg = addr + i * HSU_DMA_CHAN_LENGTH; + + spin_lock_init(&hsuc->lock); + } + + dma_cap_set(DMA_SLAVE, hsu->dma.cap_mask); + dma_cap_set(DMA_PRIVATE, hsu->dma.cap_mask); + + hsu->dma.device_alloc_chan_resources = hsu_dma_alloc_chan_resources; + hsu->dma.device_free_chan_resources = hsu_dma_free_chan_resources; + + hsu->dma.device_prep_slave_sg = hsu_dma_prep_slave_sg; + + hsu->dma.device_issue_pending = hsu_dma_issue_pending; + hsu->dma.device_tx_status = hsu_dma_tx_status; + + hsu->dma.device_config = hsu_dma_slave_config; + hsu->dma.device_pause = hsu_dma_pause; + hsu->dma.device_resume = hsu_dma_resume; + hsu->dma.device_terminate_all = hsu_dma_terminate_all; + + hsu->dma.src_addr_widths = HSU_DMA_BUSWIDTHS; + hsu->dma.dst_addr_widths = HSU_DMA_BUSWIDTHS; + hsu->dma.directions = BIT(DMA_DEV_TO_MEM) | BIT(DMA_MEM_TO_DEV); + hsu->dma.residue_granularity = DMA_RESIDUE_GRANULARITY_BURST; + + hsu->dma.dev = chip->dev; + + ret = dma_async_device_register(&hsu->dma); + if (ret) + return ret; + + dev_info(chip->dev, "Found HSU DMA, %d channels\n", pdata->nr_channels); + return 0; +} +EXPORT_SYMBOL_GPL(hsu_dma_probe); + +int hsu_dma_remove(struct hsu_dma_chip *chip) +{ + struct hsu_dma *hsu = chip->hsu; + unsigned short i; + + dma_async_device_unregister(&hsu->dma); + + for (i = 0; i < chip->pdata->nr_channels; i++) { + struct hsu_dma_chan *hsuc = &hsu->chan[i]; + + tasklet_kill(&hsuc->vchan.task); + } + + return 0; +} +EXPORT_SYMBOL_GPL(hsu_dma_remove); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("High Speed UART DMA core driver"); +MODULE_AUTHOR("Andy Shevchenko "); diff --git a/drivers/dma/hsu/hsu.h b/drivers/dma/hsu/hsu.h new file mode 100644 index 000000000000..0275233cf550 --- /dev/null +++ b/drivers/dma/hsu/hsu.h @@ -0,0 +1,118 @@ +/* + * Driver for the High Speed UART DMA + * + * Copyright (C) 2015 Intel Corporation + * + * Partially based on the bits found in drivers/tty/serial/mfd.c. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef __DMA_HSU_H__ +#define __DMA_HSU_H__ + +#include +#include + +#include "../virt-dma.h" + +#define HSU_CH_SR 0x00 /* channel status */ +#define HSU_CH_CR 0x04 /* channel control */ +#define HSU_CH_DCR 0x08 /* descriptor control */ +#define HSU_CH_BSR 0x10 /* FIFO buffer size */ +#define HSU_CH_MTSR 0x14 /* minimum transfer size */ +#define HSU_CH_DxSAR(x) (0x20 + 8 * (x)) /* desc start addr */ +#define HSU_CH_DxTSR(x) (0x24 + 8 * (x)) /* desc transfer size */ +#define HSU_CH_D0SAR 0x20 /* desc 0 start addr */ +#define HSU_CH_D0TSR 0x24 /* desc 0 transfer size */ +#define HSU_CH_D1SAR 0x28 +#define HSU_CH_D1TSR 0x2c +#define HSU_CH_D2SAR 0x30 +#define HSU_CH_D2TSR 0x34 +#define HSU_CH_D3SAR 0x38 +#define HSU_CH_D3TSR 0x3c + +#define HSU_DMA_CHAN_NR_DESC 4 +#define HSU_DMA_CHAN_LENGTH 0x40 + +/* Bits in HSU_CH_SR */ +#define HSU_CH_SR_DESCTO(x) BIT(8 + (x)) +#define HSU_CH_SR_DESCTO_ANY (BIT(11) | BIT(10) | BIT(9) | BIT(8)) +#define HSU_CH_SR_CHE BIT(15) + +/* Bits in HSU_CH_CR */ +#define HSU_CH_CR_CHA BIT(0) +#define HSU_CH_CR_CHD BIT(1) + +/* Bits in HSU_CH_DCR */ +#define HSU_CH_DCR_DESCA(x) BIT(0 + (x)) +#define HSU_CH_DCR_CHSOD(x) BIT(8 + (x)) +#define HSU_CH_DCR_CHSOTO BIT(14) +#define HSU_CH_DCR_CHSOE BIT(15) +#define HSU_CH_DCR_CHDI(x) BIT(16 + (x)) +#define HSU_CH_DCR_CHEI BIT(23) +#define HSU_CH_DCR_CHTOI(x) BIT(24 + (x)) + +struct hsu_dma_sg { + dma_addr_t addr; + unsigned int len; +}; + +struct hsu_dma_desc { + struct virt_dma_desc vdesc; + enum dma_transfer_direction direction; + struct hsu_dma_sg *sg; + unsigned int nents; + unsigned int active; + enum dma_status status; +}; + +static inline struct hsu_dma_desc *to_hsu_dma_desc(struct virt_dma_desc *vdesc) +{ + return container_of(vdesc, struct hsu_dma_desc, vdesc); +} + +struct hsu_dma_chan { + struct virt_dma_chan vchan; + + void __iomem *reg; + spinlock_t lock; + + /* hardware configuration */ + enum dma_transfer_direction direction; + struct dma_slave_config config; + + struct hsu_dma_desc *desc; +}; + +static inline struct hsu_dma_chan *to_hsu_dma_chan(struct dma_chan *chan) +{ + return container_of(chan, struct hsu_dma_chan, vchan.chan); +} + +static inline u32 hsu_chan_readl(struct hsu_dma_chan *hsuc, int offset) +{ + return readl(hsuc->reg + offset); +} + +static inline void hsu_chan_writel(struct hsu_dma_chan *hsuc, int offset, + u32 value) +{ + writel(value, hsuc->reg + offset); +} + +struct hsu_dma { + struct dma_device dma; + + /* channels */ + struct hsu_dma_chan *chan; +}; + +static inline struct hsu_dma *to_hsu_dma(struct dma_device *ddev) +{ + return container_of(ddev, struct hsu_dma, dma); +} + +#endif /* __DMA_HSU_H__ */ diff --git a/drivers/dma/hsu/pci.c b/drivers/dma/hsu/pci.c new file mode 100644 index 000000000000..563b4685d766 --- /dev/null +++ b/drivers/dma/hsu/pci.c @@ -0,0 +1,123 @@ +/* + * PCI driver for the High Speed UART DMA + * + * Copyright (C) 2015 Intel Corporation + * Author: Andy Shevchenko + * + * Partially based on the bits found in drivers/tty/serial/mfd.c. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include + +#include "hsu.h" + +#define HSU_PCI_DMASR 0x00 +#define HSU_PCI_DMAISR 0x04 + +#define HSU_PCI_CHAN_OFFSET 0x100 + +static irqreturn_t hsu_pci_irq(int irq, void *dev) +{ + struct hsu_dma_chip *chip = dev; + u32 dmaisr; + unsigned short i; + irqreturn_t ret = IRQ_NONE; + + dmaisr = readl(chip->regs + HSU_PCI_DMAISR); + for (i = 0; i < chip->pdata->nr_channels; i++) { + if (dmaisr & 0x1) + ret |= hsu_dma_irq(chip, i); + dmaisr >>= 1; + } + + return ret; +} + +static int hsu_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) +{ + struct hsu_dma_chip *chip; + int ret; + + ret = pcim_enable_device(pdev); + if (ret) + return ret; + + ret = pcim_iomap_regions(pdev, BIT(0), pci_name(pdev)); + if (ret) { + dev_err(&pdev->dev, "I/O memory remapping failed\n"); + return ret; + } + + pci_set_master(pdev); + pci_try_set_mwi(pdev); + + ret = pci_set_dma_mask(pdev, DMA_BIT_MASK(32)); + if (ret) + return ret; + + ret = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32)); + if (ret) + return ret; + + chip = devm_kzalloc(&pdev->dev, sizeof(*chip), GFP_KERNEL); + if (!chip) + return -ENOMEM; + + chip->dev = &pdev->dev; + chip->regs = pcim_iomap_table(pdev)[0]; + chip->length = pci_resource_len(pdev, 0); + chip->offset = HSU_PCI_CHAN_OFFSET; + chip->irq = pdev->irq; + + pci_enable_msi(pdev); + + ret = hsu_dma_probe(chip); + if (ret) + return ret; + + ret = request_irq(chip->irq, hsu_pci_irq, 0, "hsu_dma_pci", chip); + if (ret) + goto err_register_irq; + + pci_set_drvdata(pdev, chip); + + return 0; + +err_register_irq: + hsu_dma_remove(chip); + return ret; +} + +static void hsu_pci_remove(struct pci_dev *pdev) +{ + struct hsu_dma_chip *chip = pci_get_drvdata(pdev); + + free_irq(chip->irq, chip); + hsu_dma_remove(chip); +} + +static const struct pci_device_id hsu_pci_id_table[] = { + { PCI_VDEVICE(INTEL, 0x081e), 0 }, + { } +}; +MODULE_DEVICE_TABLE(pci, hsu_pci_id_table); + +static struct pci_driver hsu_pci_driver = { + .name = "hsu_dma_pci", + .id_table = hsu_pci_id_table, + .probe = hsu_pci_probe, + .remove = hsu_pci_remove, +}; + +module_pci_driver(hsu_pci_driver); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("High Speed UART DMA PCI driver"); +MODULE_AUTHOR("Andy Shevchenko "); diff --git a/include/linux/dma/hsu.h b/include/linux/dma/hsu.h new file mode 100644 index 000000000000..234393a6997b --- /dev/null +++ b/include/linux/dma/hsu.h @@ -0,0 +1,48 @@ +/* + * Driver for the High Speed UART DMA + * + * Copyright (C) 2015 Intel Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef _DMA_HSU_H +#define _DMA_HSU_H + +#include +#include + +#include + +struct hsu_dma; + +/** + * struct hsu_dma_chip - representation of HSU DMA hardware + * @dev: struct device of the DMA controller + * @irq: irq line + * @regs: memory mapped I/O space + * @length: I/O space length + * @offset: offset of the I/O space where registers are located + * @hsu: struct hsu_dma that is filed by ->probe() + * @pdata: platform data for the DMA controller if provided + */ +struct hsu_dma_chip { + struct device *dev; + int irq; + void __iomem *regs; + unsigned int length; + unsigned int offset; + struct hsu_dma *hsu; + struct hsu_dma_platform_data *pdata; +}; + +/* Export to the internal users */ +irqreturn_t hsu_dma_irq(struct hsu_dma_chip *chip, unsigned short nr); + +/* Export to the platform drivers */ +int hsu_dma_probe(struct hsu_dma_chip *chip); +int hsu_dma_remove(struct hsu_dma_chip *chip); + +#endif /* _DMA_HSU_H */ diff --git a/include/linux/platform_data/dma-hsu.h b/include/linux/platform_data/dma-hsu.h new file mode 100644 index 000000000000..8a1f6a4920b2 --- /dev/null +++ b/include/linux/platform_data/dma-hsu.h @@ -0,0 +1,25 @@ +/* + * Driver for the High Speed UART DMA + * + * Copyright (C) 2015 Intel Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef _PLATFORM_DATA_DMA_HSU_H +#define _PLATFORM_DATA_DMA_HSU_H + +#include + +struct hsu_dma_slave { + struct device *dma_dev; + int chan_id; +}; + +struct hsu_dma_platform_data { + unsigned short nr_channels; +}; + +#endif /* _PLATFORM_DATA_DMA_HSU_H */ -- cgit v1.2.3 From 1bd187de536494a27925902b9653e9ef0ace4774 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Mon, 23 Feb 2015 16:24:44 +0200 Subject: x86, intel-mid: remove Intel MID specific serial support Since we have a native 8250 driver carrying the Intel MID serial devices the specific support is not needed anymore. This patch removes it for Intel MID. Note that the console device name is changed from ttyMFDx to ttySx. Signed-off-by: Andy Shevchenko Acked-by: Ingo Molnar Signed-off-by: Greg Kroah-Hartman --- arch/x86/Kconfig.debug | 4 - arch/x86/include/asm/intel-mid.h | 3 - arch/x86/kernel/early_printk.c | 6 - arch/x86/platform/intel-mid/Makefile | 1 - .../platform/intel-mid/early_printk_intel_mid.c | 112 -- drivers/tty/serial/Kconfig | 10 - drivers/tty/serial/Makefile | 1 - drivers/tty/serial/mfd.c | 1505 -------------------- include/linux/serial_mfd.h | 47 - include/uapi/linux/serial_reg.h | 19 - 10 files changed, 1708 deletions(-) delete mode 100644 arch/x86/platform/intel-mid/early_printk_intel_mid.c delete mode 100644 drivers/tty/serial/mfd.c delete mode 100644 include/linux/serial_mfd.h (limited to 'include') diff --git a/arch/x86/Kconfig.debug b/arch/x86/Kconfig.debug index 20028da8ae18..72484a645f05 100644 --- a/arch/x86/Kconfig.debug +++ b/arch/x86/Kconfig.debug @@ -43,10 +43,6 @@ config EARLY_PRINTK with klogd/syslogd or the X server. You should normally N here, unless you want to debug such a crash. -config EARLY_PRINTK_INTEL_MID - bool "Early printk for Intel MID platform support" - depends on EARLY_PRINTK && X86_INTEL_MID - config EARLY_PRINTK_DBGP bool "Early printk via EHCI debug port" depends on EARLY_PRINTK && PCI diff --git a/arch/x86/include/asm/intel-mid.h b/arch/x86/include/asm/intel-mid.h index 705d35708a50..7c5af123bdbd 100644 --- a/arch/x86/include/asm/intel-mid.h +++ b/arch/x86/include/asm/intel-mid.h @@ -136,9 +136,6 @@ extern enum intel_mid_timer_options intel_mid_timer_options; #define SFI_MTMR_MAX_NUM 8 #define SFI_MRTC_MAX 8 -extern struct console early_hsu_console; -extern void hsu_early_console_init(const char *); - extern void intel_scu_devices_create(void); extern void intel_scu_devices_destroy(void); diff --git a/arch/x86/kernel/early_printk.c b/arch/x86/kernel/early_printk.c index a62536a1be88..f85e3fb50f28 100644 --- a/arch/x86/kernel/early_printk.c +++ b/arch/x86/kernel/early_printk.c @@ -375,12 +375,6 @@ static int __init setup_early_printk(char *buf) if (!strncmp(buf, "xen", 3)) early_console_register(&xenboot_console, keep); #endif -#ifdef CONFIG_EARLY_PRINTK_INTEL_MID - if (!strncmp(buf, "hsu", 3)) { - hsu_early_console_init(buf + 3); - early_console_register(&early_hsu_console, keep); - } -#endif #ifdef CONFIG_EARLY_PRINTK_EFI if (!strncmp(buf, "efi", 3)) early_console_register(&early_efi_console, keep); diff --git a/arch/x86/platform/intel-mid/Makefile b/arch/x86/platform/intel-mid/Makefile index 0a8ee703b9fa..0ce1b1913673 100644 --- a/arch/x86/platform/intel-mid/Makefile +++ b/arch/x86/platform/intel-mid/Makefile @@ -1,5 +1,4 @@ obj-$(CONFIG_X86_INTEL_MID) += intel-mid.o intel_mid_vrtc.o mfld.o mrfl.o -obj-$(CONFIG_EARLY_PRINTK_INTEL_MID) += early_printk_intel_mid.o # SFI specific code ifdef CONFIG_X86_INTEL_MID diff --git a/arch/x86/platform/intel-mid/early_printk_intel_mid.c b/arch/x86/platform/intel-mid/early_printk_intel_mid.c deleted file mode 100644 index 4e720829ab90..000000000000 --- a/arch/x86/platform/intel-mid/early_printk_intel_mid.c +++ /dev/null @@ -1,112 +0,0 @@ -/* - * early_printk_intel_mid.c - early consoles for Intel MID platforms - * - * Copyright (c) 2008-2010, Intel Corporation - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; version 2 - * of the License. - */ - -/* - * This file implements early console named hsu. - * hsu is based on a High Speed UART device which only exists in the Medfield - * platform - */ - -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -/* - * Following is the early console based on Medfield HSU (High - * Speed UART) device. - */ -#define HSU_PORT_BASE 0xffa28080 - -static void __iomem *phsu; - -void hsu_early_console_init(const char *s) -{ - unsigned long paddr, port = 0; - u8 lcr; - - /* - * Select the early HSU console port if specified by user in the - * kernel command line. - */ - if (*s && !kstrtoul(s, 10, &port)) - port = clamp_val(port, 0, 2); - - paddr = HSU_PORT_BASE + port * 0x80; - phsu = (void __iomem *)set_fixmap_offset_nocache(FIX_EARLYCON_MEM_BASE, paddr); - - /* Disable FIFO */ - writeb(0x0, phsu + UART_FCR); - - /* Set to default 115200 bps, 8n1 */ - lcr = readb(phsu + UART_LCR); - writeb((0x80 | lcr), phsu + UART_LCR); - writeb(0x18, phsu + UART_DLL); - writeb(lcr, phsu + UART_LCR); - writel(0x3600, phsu + UART_MUL*4); - - writeb(0x8, phsu + UART_MCR); - writeb(0x7, phsu + UART_FCR); - writeb(0x3, phsu + UART_LCR); - - /* Clear IRQ status */ - readb(phsu + UART_LSR); - readb(phsu + UART_RX); - readb(phsu + UART_IIR); - readb(phsu + UART_MSR); - - /* Enable FIFO */ - writeb(0x7, phsu + UART_FCR); -} - -#define BOTH_EMPTY (UART_LSR_TEMT | UART_LSR_THRE) - -static void early_hsu_putc(char ch) -{ - unsigned int timeout = 10000; /* 10ms */ - u8 status; - - while (--timeout) { - status = readb(phsu + UART_LSR); - if (status & BOTH_EMPTY) - break; - udelay(1); - } - - /* Only write the char when there was no timeout */ - if (timeout) - writeb(ch, phsu + UART_TX); -} - -static void early_hsu_write(struct console *con, const char *str, unsigned n) -{ - int i; - - for (i = 0; i < n && *str; i++) { - if (*str == '\n') - early_hsu_putc('\r'); - early_hsu_putc(*str); - str++; - } -} - -struct console early_hsu_console = { - .name = "earlyhsu", - .write = early_hsu_write, - .flags = CON_PRINTBUFFER, - .index = -1, -}; diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig index 60c368a5dbf1..c9dbc626038d 100644 --- a/drivers/tty/serial/Kconfig +++ b/drivers/tty/serial/Kconfig @@ -483,16 +483,6 @@ config SERIAL_SA1100_CONSOLE your boot loader (lilo or loadlin) about how to pass options to the kernel at boot time.) -config SERIAL_MFD_HSU - tristate "Medfield High Speed UART support" - depends on PCI - select SERIAL_CORE - -config SERIAL_MFD_HSU_CONSOLE - bool "Medfield HSU serial console support" - depends on SERIAL_MFD_HSU=y - select SERIAL_CORE_CONSOLE - config SERIAL_BFIN tristate "Blackfin serial port support" depends on BLACKFIN diff --git a/drivers/tty/serial/Makefile b/drivers/tty/serial/Makefile index 599be4b05a26..f42b4f9845df 100644 --- a/drivers/tty/serial/Makefile +++ b/drivers/tty/serial/Makefile @@ -78,7 +78,6 @@ obj-$(CONFIG_SERIAL_TIMBERDALE) += timbuart.o obj-$(CONFIG_SERIAL_GRLIB_GAISLER_APBUART) += apbuart.o obj-$(CONFIG_SERIAL_ALTERA_JTAGUART) += altera_jtaguart.o obj-$(CONFIG_SERIAL_VT8500) += vt8500_serial.o -obj-$(CONFIG_SERIAL_MFD_HSU) += mfd.o obj-$(CONFIG_SERIAL_IFX6X60) += ifx6x60.o obj-$(CONFIG_SERIAL_PCH_UART) += pch_uart.o obj-$(CONFIG_SERIAL_MSM_SMD) += msm_smd_tty.o diff --git a/drivers/tty/serial/mfd.c b/drivers/tty/serial/mfd.c deleted file mode 100644 index 8fe4501d7565..000000000000 --- a/drivers/tty/serial/mfd.c +++ /dev/null @@ -1,1505 +0,0 @@ -/* - * mfd.c: driver for High Speed UART device of Intel Medfield platform - * - * Refer pxa.c, 8250.c and some other drivers in drivers/serial/ - * - * (C) Copyright 2010 Intel Corporation - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; version 2 - * of the License. - */ - -/* Notes: - * 1. DMA channel allocation: 0/1 channel are assigned to port 0, - * 2/3 chan to port 1, 4/5 chan to port 3. Even number chans - * are used for RX, odd chans for TX - * - * 2. The RI/DSR/DCD/DTR are not pinned out, DCD & DSR are always - * asserted, only when the HW is reset the DDCD and DDSR will - * be triggered - */ - -#if defined(CONFIG_SERIAL_MFD_HSU_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) -#define SUPPORT_SYSRQ -#endif - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define HSU_DMA_BUF_SIZE 2048 - -#define chan_readl(chan, offset) readl(chan->reg + offset) -#define chan_writel(chan, offset, val) writel(val, chan->reg + offset) - -#define mfd_readl(obj, offset) readl(obj->reg + offset) -#define mfd_writel(obj, offset, val) writel(val, obj->reg + offset) - -static int hsu_dma_enable; -module_param(hsu_dma_enable, int, 0); -MODULE_PARM_DESC(hsu_dma_enable, - "It is a bitmap to set working mode, if bit[x] is 1, then port[x] will work in DMA mode, otherwise in PIO mode."); - -struct hsu_dma_buffer { - u8 *buf; - dma_addr_t dma_addr; - u32 dma_size; - u32 ofs; -}; - -struct hsu_dma_chan { - u32 id; - enum dma_data_direction dirt; - struct uart_hsu_port *uport; - void __iomem *reg; -}; - -struct uart_hsu_port { - struct uart_port port; - unsigned char ier; - unsigned char lcr; - unsigned char mcr; - unsigned int lsr_break_flag; - char name[12]; - int index; - struct device *dev; - - struct hsu_dma_chan *txc; - struct hsu_dma_chan *rxc; - struct hsu_dma_buffer txbuf; - struct hsu_dma_buffer rxbuf; - int use_dma; /* flag for DMA/PIO */ - int running; - int dma_tx_on; -}; - -/* Top level data structure of HSU */ -struct hsu_port { - void __iomem *reg; - unsigned long paddr; - unsigned long iolen; - u32 irq; - - struct uart_hsu_port port[3]; - struct hsu_dma_chan chans[10]; - - struct dentry *debugfs; -}; - -static inline unsigned int serial_in(struct uart_hsu_port *up, int offset) -{ - unsigned int val; - - if (offset > UART_MSR) { - offset <<= 2; - val = readl(up->port.membase + offset); - } else - val = (unsigned int)readb(up->port.membase + offset); - - return val; -} - -static inline void serial_out(struct uart_hsu_port *up, int offset, int value) -{ - if (offset > UART_MSR) { - offset <<= 2; - writel(value, up->port.membase + offset); - } else { - unsigned char val = value & 0xff; - writeb(val, up->port.membase + offset); - } -} - -#ifdef CONFIG_DEBUG_FS - -#define HSU_REGS_BUFSIZE 1024 - - -static ssize_t port_show_regs(struct file *file, char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct uart_hsu_port *up = file->private_data; - char *buf; - u32 len = 0; - ssize_t ret; - - buf = kzalloc(HSU_REGS_BUFSIZE, GFP_KERNEL); - if (!buf) - return 0; - - len += snprintf(buf + len, HSU_REGS_BUFSIZE - len, - "MFD HSU port[%d] regs:\n", up->index); - - len += snprintf(buf + len, HSU_REGS_BUFSIZE - len, - "=================================\n"); - len += snprintf(buf + len, HSU_REGS_BUFSIZE - len, - "IER: \t\t0x%08x\n", serial_in(up, UART_IER)); - len += snprintf(buf + len, HSU_REGS_BUFSIZE - len, - "IIR: \t\t0x%08x\n", serial_in(up, UART_IIR)); - len += snprintf(buf + len, HSU_REGS_BUFSIZE - len, - "LCR: \t\t0x%08x\n", serial_in(up, UART_LCR)); - len += snprintf(buf + len, HSU_REGS_BUFSIZE - len, - "MCR: \t\t0x%08x\n", serial_in(up, UART_MCR)); - len += snprintf(buf + len, HSU_REGS_BUFSIZE - len, - "LSR: \t\t0x%08x\n", serial_in(up, UART_LSR)); - len += snprintf(buf + len, HSU_REGS_BUFSIZE - len, - "MSR: \t\t0x%08x\n", serial_in(up, UART_MSR)); - len += snprintf(buf + len, HSU_REGS_BUFSIZE - len, - "FOR: \t\t0x%08x\n", serial_in(up, UART_FOR)); - len += snprintf(buf + len, HSU_REGS_BUFSIZE - len, - "PS: \t\t0x%08x\n", serial_in(up, UART_PS)); - len += snprintf(buf + len, HSU_REGS_BUFSIZE - len, - "MUL: \t\t0x%08x\n", serial_in(up, UART_MUL)); - len += snprintf(buf + len, HSU_REGS_BUFSIZE - len, - "DIV: \t\t0x%08x\n", serial_in(up, UART_DIV)); - - if (len > HSU_REGS_BUFSIZE) - len = HSU_REGS_BUFSIZE; - - ret = simple_read_from_buffer(user_buf, count, ppos, buf, len); - kfree(buf); - return ret; -} - -static ssize_t dma_show_regs(struct file *file, char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct hsu_dma_chan *chan = file->private_data; - char *buf; - u32 len = 0; - ssize_t ret; - - buf = kzalloc(HSU_REGS_BUFSIZE, GFP_KERNEL); - if (!buf) - return 0; - - len += snprintf(buf + len, HSU_REGS_BUFSIZE - len, - "MFD HSU DMA channel [%d] regs:\n", chan->id); - - len += snprintf(buf + len, HSU_REGS_BUFSIZE - len, - "=================================\n"); - len += snprintf(buf + len, HSU_REGS_BUFSIZE - len, - "CR: \t\t0x%08x\n", chan_readl(chan, HSU_CH_CR)); - len += snprintf(buf + len, HSU_REGS_BUFSIZE - len, - "DCR: \t\t0x%08x\n", chan_readl(chan, HSU_CH_DCR)); - len += snprintf(buf + len, HSU_REGS_BUFSIZE - len, - "BSR: \t\t0x%08x\n", chan_readl(chan, HSU_CH_BSR)); - len += snprintf(buf + len, HSU_REGS_BUFSIZE - len, - "MOTSR: \t\t0x%08x\n", chan_readl(chan, HSU_CH_MOTSR)); - len += snprintf(buf + len, HSU_REGS_BUFSIZE - len, - "D0SAR: \t\t0x%08x\n", chan_readl(chan, HSU_CH_D0SAR)); - len += snprintf(buf + len, HSU_REGS_BUFSIZE - len, - "D0TSR: \t\t0x%08x\n", chan_readl(chan, HSU_CH_D0TSR)); - len += snprintf(buf + len, HSU_REGS_BUFSIZE - len, - "D0SAR: \t\t0x%08x\n", chan_readl(chan, HSU_CH_D1SAR)); - len += snprintf(buf + len, HSU_REGS_BUFSIZE - len, - "D0TSR: \t\t0x%08x\n", chan_readl(chan, HSU_CH_D1TSR)); - len += snprintf(buf + len, HSU_REGS_BUFSIZE - len, - "D0SAR: \t\t0x%08x\n", chan_readl(chan, HSU_CH_D2SAR)); - len += snprintf(buf + len, HSU_REGS_BUFSIZE - len, - "D0TSR: \t\t0x%08x\n", chan_readl(chan, HSU_CH_D2TSR)); - len += snprintf(buf + len, HSU_REGS_BUFSIZE - len, - "D0SAR: \t\t0x%08x\n", chan_readl(chan, HSU_CH_D3SAR)); - len += snprintf(buf + len, HSU_REGS_BUFSIZE - len, - "D0TSR: \t\t0x%08x\n", chan_readl(chan, HSU_CH_D3TSR)); - - if (len > HSU_REGS_BUFSIZE) - len = HSU_REGS_BUFSIZE; - - ret = simple_read_from_buffer(user_buf, count, ppos, buf, len); - kfree(buf); - return ret; -} - -static const struct file_operations port_regs_ops = { - .owner = THIS_MODULE, - .open = simple_open, - .read = port_show_regs, - .llseek = default_llseek, -}; - -static const struct file_operations dma_regs_ops = { - .owner = THIS_MODULE, - .open = simple_open, - .read = dma_show_regs, - .llseek = default_llseek, -}; - -static int hsu_debugfs_init(struct hsu_port *hsu) -{ - int i; - char name[32]; - - hsu->debugfs = debugfs_create_dir("hsu", NULL); - if (!hsu->debugfs) - return -ENOMEM; - - for (i = 0; i < 3; i++) { - snprintf(name, sizeof(name), "port_%d_regs", i); - debugfs_create_file(name, S_IFREG | S_IRUGO, - hsu->debugfs, (void *)(&hsu->port[i]), &port_regs_ops); - } - - for (i = 0; i < 6; i++) { - snprintf(name, sizeof(name), "dma_chan_%d_regs", i); - debugfs_create_file(name, S_IFREG | S_IRUGO, - hsu->debugfs, (void *)&hsu->chans[i], &dma_regs_ops); - } - - return 0; -} - -static void hsu_debugfs_remove(struct hsu_port *hsu) -{ - if (hsu->debugfs) - debugfs_remove_recursive(hsu->debugfs); -} - -#else -static inline int hsu_debugfs_init(struct hsu_port *hsu) -{ - return 0; -} - -static inline void hsu_debugfs_remove(struct hsu_port *hsu) -{ -} -#endif /* CONFIG_DEBUG_FS */ - -static void serial_hsu_enable_ms(struct uart_port *port) -{ - struct uart_hsu_port *up = - container_of(port, struct uart_hsu_port, port); - - up->ier |= UART_IER_MSI; - serial_out(up, UART_IER, up->ier); -} - -static void hsu_dma_tx(struct uart_hsu_port *up) -{ - struct circ_buf *xmit = &up->port.state->xmit; - struct hsu_dma_buffer *dbuf = &up->txbuf; - int count; - - /* test_and_set_bit may be better, but anyway it's in lock protected mode */ - if (up->dma_tx_on) - return; - - /* Update the circ buf info */ - xmit->tail += dbuf->ofs; - xmit->tail &= UART_XMIT_SIZE - 1; - - up->port.icount.tx += dbuf->ofs; - dbuf->ofs = 0; - - /* Disable the channel */ - chan_writel(up->txc, HSU_CH_CR, 0x0); - - if (!uart_circ_empty(xmit) && !uart_tx_stopped(&up->port)) { - dma_sync_single_for_device(up->port.dev, - dbuf->dma_addr, - dbuf->dma_size, - DMA_TO_DEVICE); - - count = CIRC_CNT_TO_END(xmit->head, xmit->tail, UART_XMIT_SIZE); - dbuf->ofs = count; - - /* Reprogram the channel */ - chan_writel(up->txc, HSU_CH_D0SAR, dbuf->dma_addr + xmit->tail); - chan_writel(up->txc, HSU_CH_D0TSR, count); - - /* Reenable the channel */ - chan_writel(up->txc, HSU_CH_DCR, 0x1 - | (0x1 << 8) - | (0x1 << 16) - | (0x1 << 24)); - up->dma_tx_on = 1; - chan_writel(up->txc, HSU_CH_CR, 0x1); - } - - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) - uart_write_wakeup(&up->port); -} - -/* The buffer is already cache coherent */ -static void hsu_dma_start_rx_chan(struct hsu_dma_chan *rxc, - struct hsu_dma_buffer *dbuf) -{ - dbuf->ofs = 0; - - chan_writel(rxc, HSU_CH_BSR, 32); - chan_writel(rxc, HSU_CH_MOTSR, 4); - - chan_writel(rxc, HSU_CH_D0SAR, dbuf->dma_addr); - chan_writel(rxc, HSU_CH_D0TSR, dbuf->dma_size); - chan_writel(rxc, HSU_CH_DCR, 0x1 | (0x1 << 8) - | (0x1 << 16) - | (0x1 << 24) /* timeout bit, see HSU Errata 1 */ - ); - chan_writel(rxc, HSU_CH_CR, 0x3); -} - -/* Protected by spin_lock_irqsave(port->lock) */ -static void serial_hsu_start_tx(struct uart_port *port) -{ - struct uart_hsu_port *up = - container_of(port, struct uart_hsu_port, port); - - if (up->use_dma) { - hsu_dma_tx(up); - } else if (!(up->ier & UART_IER_THRI)) { - up->ier |= UART_IER_THRI; - serial_out(up, UART_IER, up->ier); - } -} - -static void serial_hsu_stop_tx(struct uart_port *port) -{ - struct uart_hsu_port *up = - container_of(port, struct uart_hsu_port, port); - struct hsu_dma_chan *txc = up->txc; - - if (up->use_dma) - chan_writel(txc, HSU_CH_CR, 0x0); - else if (up->ier & UART_IER_THRI) { - up->ier &= ~UART_IER_THRI; - serial_out(up, UART_IER, up->ier); - } -} - -/* This is always called in spinlock protected mode, so - * modify timeout timer is safe here */ -static void hsu_dma_rx(struct uart_hsu_port *up, u32 int_sts, - unsigned long *flags) -{ - struct hsu_dma_buffer *dbuf = &up->rxbuf; - struct hsu_dma_chan *chan = up->rxc; - struct uart_port *port = &up->port; - struct tty_port *tport = &port->state->port; - int count; - - /* - * First need to know how many is already transferred, - * then check if its a timeout DMA irq, and return - * the trail bytes out, push them up and reenable the - * channel - */ - - /* Timeout IRQ, need wait some time, see Errata 2 */ - if (int_sts & 0xf00) - udelay(2); - - /* Stop the channel */ - chan_writel(chan, HSU_CH_CR, 0x0); - - count = chan_readl(chan, HSU_CH_D0SAR) - dbuf->dma_addr; - if (!count) { - /* Restart the channel before we leave */ - chan_writel(chan, HSU_CH_CR, 0x3); - return; - } - - dma_sync_single_for_cpu(port->dev, dbuf->dma_addr, - dbuf->dma_size, DMA_FROM_DEVICE); - - /* - * Head will only wrap around when we recycle - * the DMA buffer, and when that happens, we - * explicitly set tail to 0. So head will - * always be greater than tail. - */ - tty_insert_flip_string(tport, dbuf->buf, count); - port->icount.rx += count; - - dma_sync_single_for_device(up->port.dev, dbuf->dma_addr, - dbuf->dma_size, DMA_FROM_DEVICE); - - /* Reprogram the channel */ - chan_writel(chan, HSU_CH_D0SAR, dbuf->dma_addr); - chan_writel(chan, HSU_CH_D0TSR, dbuf->dma_size); - chan_writel(chan, HSU_CH_DCR, 0x1 - | (0x1 << 8) - | (0x1 << 16) - | (0x1 << 24) /* timeout bit, see HSU Errata 1 */ - ); - spin_unlock_irqrestore(&up->port.lock, *flags); - tty_flip_buffer_push(tport); - spin_lock_irqsave(&up->port.lock, *flags); - - chan_writel(chan, HSU_CH_CR, 0x3); - -} - -static void serial_hsu_stop_rx(struct uart_port *port) -{ - struct uart_hsu_port *up = - container_of(port, struct uart_hsu_port, port); - struct hsu_dma_chan *chan = up->rxc; - - if (up->use_dma) - chan_writel(chan, HSU_CH_CR, 0x2); - else { - up->ier &= ~UART_IER_RLSI; - up->port.read_status_mask &= ~UART_LSR_DR; - serial_out(up, UART_IER, up->ier); - } -} - -static inline void receive_chars(struct uart_hsu_port *up, int *status, - unsigned long *flags) -{ - unsigned int ch, flag; - unsigned int max_count = 256; - - do { - ch = serial_in(up, UART_RX); - flag = TTY_NORMAL; - up->port.icount.rx++; - - if (unlikely(*status & (UART_LSR_BI | UART_LSR_PE | - UART_LSR_FE | UART_LSR_OE))) { - - dev_warn(up->dev, "We really rush into ERR/BI case" - "status = 0x%02x", *status); - /* For statistics only */ - if (*status & UART_LSR_BI) { - *status &= ~(UART_LSR_FE | UART_LSR_PE); - up->port.icount.brk++; - /* - * We do the SysRQ and SAK checking - * here because otherwise the break - * may get masked by ignore_status_mask - * or read_status_mask. - */ - if (uart_handle_break(&up->port)) - goto ignore_char; - } else if (*status & UART_LSR_PE) - up->port.icount.parity++; - else if (*status & UART_LSR_FE) - up->port.icount.frame++; - if (*status & UART_LSR_OE) - up->port.icount.overrun++; - - /* Mask off conditions which should be ignored. */ - *status &= up->port.read_status_mask; - -#ifdef CONFIG_SERIAL_MFD_HSU_CONSOLE - if (up->port.cons && - up->port.cons->index == up->port.line) { - /* Recover the break flag from console xmit */ - *status |= up->lsr_break_flag; - up->lsr_break_flag = 0; - } -#endif - if (*status & UART_LSR_BI) { - flag = TTY_BREAK; - } else if (*status & UART_LSR_PE) - flag = TTY_PARITY; - else if (*status & UART_LSR_FE) - flag = TTY_FRAME; - } - - if (uart_handle_sysrq_char(&up->port, ch)) - goto ignore_char; - - uart_insert_char(&up->port, *status, UART_LSR_OE, ch, flag); - ignore_char: - *status = serial_in(up, UART_LSR); - } while ((*status & UART_LSR_DR) && max_count--); - - spin_unlock_irqrestore(&up->port.lock, *flags); - tty_flip_buffer_push(&up->port.state->port); - spin_lock_irqsave(&up->port.lock, *flags); -} - -static void transmit_chars(struct uart_hsu_port *up) -{ - struct circ_buf *xmit = &up->port.state->xmit; - int count; - - if (up->port.x_char) { - serial_out(up, UART_TX, up->port.x_char); - up->port.icount.tx++; - up->port.x_char = 0; - return; - } - if (uart_circ_empty(xmit) || uart_tx_stopped(&up->port)) { - serial_hsu_stop_tx(&up->port); - return; - } - - /* The IRQ is for TX FIFO half-empty */ - count = up->port.fifosize / 2; - - do { - serial_out(up, UART_TX, xmit->buf[xmit->tail]); - xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); - - up->port.icount.tx++; - if (uart_circ_empty(xmit)) - break; - } while (--count > 0); - - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) - uart_write_wakeup(&up->port); - - if (uart_circ_empty(xmit)) - serial_hsu_stop_tx(&up->port); -} - -static inline void check_modem_status(struct uart_hsu_port *up) -{ - int status; - - status = serial_in(up, UART_MSR); - - if ((status & UART_MSR_ANY_DELTA) == 0) - return; - - if (status & UART_MSR_TERI) - up->port.icount.rng++; - if (status & UART_MSR_DDSR) - up->port.icount.dsr++; - /* We may only get DDCD when HW init and reset */ - if (status & UART_MSR_DDCD) - uart_handle_dcd_change(&up->port, status & UART_MSR_DCD); - /* Will start/stop_tx accordingly */ - if (status & UART_MSR_DCTS) - uart_handle_cts_change(&up->port, status & UART_MSR_CTS); - - wake_up_interruptible(&up->port.state->port.delta_msr_wait); -} - -/* - * This handles the interrupt from one port. - */ -static irqreturn_t port_irq(int irq, void *dev_id) -{ - struct uart_hsu_port *up = dev_id; - unsigned int iir, lsr; - unsigned long flags; - - if (unlikely(!up->running)) - return IRQ_NONE; - - spin_lock_irqsave(&up->port.lock, flags); - if (up->use_dma) { - lsr = serial_in(up, UART_LSR); - if (unlikely(lsr & (UART_LSR_BI | UART_LSR_PE | - UART_LSR_FE | UART_LSR_OE))) - dev_warn(up->dev, - "Got lsr irq while using DMA, lsr = 0x%2x\n", - lsr); - check_modem_status(up); - spin_unlock_irqrestore(&up->port.lock, flags); - return IRQ_HANDLED; - } - - iir = serial_in(up, UART_IIR); - if (iir & UART_IIR_NO_INT) { - spin_unlock_irqrestore(&up->port.lock, flags); - return IRQ_NONE; - } - - lsr = serial_in(up, UART_LSR); - if (lsr & UART_LSR_DR) - receive_chars(up, &lsr, &flags); - check_modem_status(up); - - /* lsr will be renewed during the receive_chars */ - if (lsr & UART_LSR_THRE) - transmit_chars(up); - - spin_unlock_irqrestore(&up->port.lock, flags); - return IRQ_HANDLED; -} - -static inline void dma_chan_irq(struct hsu_dma_chan *chan) -{ - struct uart_hsu_port *up = chan->uport; - unsigned long flags; - u32 int_sts; - - spin_lock_irqsave(&up->port.lock, flags); - - if (!up->use_dma || !up->running) - goto exit; - - /* - * No matter what situation, need read clear the IRQ status - * There is a bug, see Errata 5, HSD 2900918 - */ - int_sts = chan_readl(chan, HSU_CH_SR); - - /* Rx channel */ - if (chan->dirt == DMA_FROM_DEVICE) - hsu_dma_rx(up, int_sts, &flags); - - /* Tx channel */ - if (chan->dirt == DMA_TO_DEVICE) { - chan_writel(chan, HSU_CH_CR, 0x0); - up->dma_tx_on = 0; - hsu_dma_tx(up); - } - -exit: - spin_unlock_irqrestore(&up->port.lock, flags); - return; -} - -static irqreturn_t dma_irq(int irq, void *dev_id) -{ - struct hsu_port *hsu = dev_id; - u32 int_sts, i; - - int_sts = mfd_readl(hsu, HSU_GBL_DMAISR); - - /* Currently we only have 6 channels may be used */ - for (i = 0; i < 6; i++) { - if (int_sts & 0x1) - dma_chan_irq(&hsu->chans[i]); - int_sts >>= 1; - } - - return IRQ_HANDLED; -} - -static unsigned int serial_hsu_tx_empty(struct uart_port *port) -{ - struct uart_hsu_port *up = - container_of(port, struct uart_hsu_port, port); - unsigned long flags; - unsigned int ret; - - spin_lock_irqsave(&up->port.lock, flags); - ret = serial_in(up, UART_LSR) & UART_LSR_TEMT ? TIOCSER_TEMT : 0; - spin_unlock_irqrestore(&up->port.lock, flags); - - return ret; -} - -static unsigned int serial_hsu_get_mctrl(struct uart_port *port) -{ - struct uart_hsu_port *up = - container_of(port, struct uart_hsu_port, port); - unsigned char status; - unsigned int ret; - - status = serial_in(up, UART_MSR); - - ret = 0; - if (status & UART_MSR_DCD) - ret |= TIOCM_CAR; - if (status & UART_MSR_RI) - ret |= TIOCM_RNG; - if (status & UART_MSR_DSR) - ret |= TIOCM_DSR; - if (status & UART_MSR_CTS) - ret |= TIOCM_CTS; - return ret; -} - -static void serial_hsu_set_mctrl(struct uart_port *port, unsigned int mctrl) -{ - struct uart_hsu_port *up = - container_of(port, struct uart_hsu_port, port); - unsigned char mcr = 0; - - if (mctrl & TIOCM_RTS) - mcr |= UART_MCR_RTS; - if (mctrl & TIOCM_DTR) - mcr |= UART_MCR_DTR; - if (mctrl & TIOCM_OUT1) - mcr |= UART_MCR_OUT1; - if (mctrl & TIOCM_OUT2) - mcr |= UART_MCR_OUT2; - if (mctrl & TIOCM_LOOP) - mcr |= UART_MCR_LOOP; - - mcr |= up->mcr; - - serial_out(up, UART_MCR, mcr); -} - -static void serial_hsu_break_ctl(struct uart_port *port, int break_state) -{ - struct uart_hsu_port *up = - container_of(port, struct uart_hsu_port, port); - unsigned long flags; - - spin_lock_irqsave(&up->port.lock, flags); - if (break_state == -1) - up->lcr |= UART_LCR_SBC; - else - up->lcr &= ~UART_LCR_SBC; - serial_out(up, UART_LCR, up->lcr); - spin_unlock_irqrestore(&up->port.lock, flags); -} - -/* - * What special to do: - * 1. chose the 64B fifo mode - * 2. start dma or pio depends on configuration - * 3. we only allocate dma memory when needed - */ -static int serial_hsu_startup(struct uart_port *port) -{ - struct uart_hsu_port *up = - container_of(port, struct uart_hsu_port, port); - unsigned long flags; - - pm_runtime_get_sync(up->dev); - - /* - * Clear the FIFO buffers and disable them. - * (they will be reenabled in set_termios()) - */ - serial_out(up, UART_FCR, UART_FCR_ENABLE_FIFO); - serial_out(up, UART_FCR, UART_FCR_ENABLE_FIFO | - UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT); - serial_out(up, UART_FCR, 0); - - /* Clear the interrupt registers. */ - (void) serial_in(up, UART_LSR); - (void) serial_in(up, UART_RX); - (void) serial_in(up, UART_IIR); - (void) serial_in(up, UART_MSR); - - /* Now, initialize the UART, default is 8n1 */ - serial_out(up, UART_LCR, UART_LCR_WLEN8); - - spin_lock_irqsave(&up->port.lock, flags); - - up->port.mctrl |= TIOCM_OUT2; - serial_hsu_set_mctrl(&up->port, up->port.mctrl); - - /* - * Finally, enable interrupts. Note: Modem status interrupts - * are set via set_termios(), which will be occurring imminently - * anyway, so we don't enable them here. - */ - if (!up->use_dma) - up->ier = UART_IER_RLSI | UART_IER_RDI | UART_IER_RTOIE; - else - up->ier = 0; - serial_out(up, UART_IER, up->ier); - - spin_unlock_irqrestore(&up->port.lock, flags); - - /* DMA init */ - if (up->use_dma) { - struct hsu_dma_buffer *dbuf; - struct circ_buf *xmit = &port->state->xmit; - - up->dma_tx_on = 0; - - /* First allocate the RX buffer */ - dbuf = &up->rxbuf; - dbuf->buf = kzalloc(HSU_DMA_BUF_SIZE, GFP_KERNEL); - if (!dbuf->buf) { - up->use_dma = 0; - goto exit; - } - dbuf->dma_addr = dma_map_single(port->dev, - dbuf->buf, - HSU_DMA_BUF_SIZE, - DMA_FROM_DEVICE); - dbuf->dma_size = HSU_DMA_BUF_SIZE; - - /* Start the RX channel right now */ - hsu_dma_start_rx_chan(up->rxc, dbuf); - - /* Next init the TX DMA */ - dbuf = &up->txbuf; - dbuf->buf = xmit->buf; - dbuf->dma_addr = dma_map_single(port->dev, - dbuf->buf, - UART_XMIT_SIZE, - DMA_TO_DEVICE); - dbuf->dma_size = UART_XMIT_SIZE; - - /* This should not be changed all around */ - chan_writel(up->txc, HSU_CH_BSR, 32); - chan_writel(up->txc, HSU_CH_MOTSR, 4); - dbuf->ofs = 0; - } - -exit: - /* And clear the interrupt registers again for luck. */ - (void) serial_in(up, UART_LSR); - (void) serial_in(up, UART_RX); - (void) serial_in(up, UART_IIR); - (void) serial_in(up, UART_MSR); - - up->running = 1; - return 0; -} - -static void serial_hsu_shutdown(struct uart_port *port) -{ - struct uart_hsu_port *up = - container_of(port, struct uart_hsu_port, port); - unsigned long flags; - - /* Disable interrupts from this port */ - up->ier = 0; - serial_out(up, UART_IER, 0); - up->running = 0; - - spin_lock_irqsave(&up->port.lock, flags); - up->port.mctrl &= ~TIOCM_OUT2; - serial_hsu_set_mctrl(&up->port, up->port.mctrl); - spin_unlock_irqrestore(&up->port.lock, flags); - - /* Disable break condition and FIFOs */ - serial_out(up, UART_LCR, serial_in(up, UART_LCR) & ~UART_LCR_SBC); - serial_out(up, UART_FCR, UART_FCR_ENABLE_FIFO | - UART_FCR_CLEAR_RCVR | - UART_FCR_CLEAR_XMIT); - serial_out(up, UART_FCR, 0); - - pm_runtime_put(up->dev); -} - -static void -serial_hsu_set_termios(struct uart_port *port, struct ktermios *termios, - struct ktermios *old) -{ - struct uart_hsu_port *up = - container_of(port, struct uart_hsu_port, port); - unsigned char cval, fcr = 0; - unsigned long flags; - unsigned int baud, quot; - u32 ps, mul; - - switch (termios->c_cflag & CSIZE) { - case CS5: - cval = UART_LCR_WLEN5; - break; - case CS6: - cval = UART_LCR_WLEN6; - break; - case CS7: - cval = UART_LCR_WLEN7; - break; - default: - case CS8: - cval = UART_LCR_WLEN8; - break; - } - - /* CMSPAR isn't supported by this driver */ - termios->c_cflag &= ~CMSPAR; - - if (termios->c_cflag & CSTOPB) - cval |= UART_LCR_STOP; - if (termios->c_cflag & PARENB) - cval |= UART_LCR_PARITY; - if (!(termios->c_cflag & PARODD)) - cval |= UART_LCR_EPAR; - - /* - * The base clk is 50Mhz, and the baud rate come from: - * baud = 50M * MUL / (DIV * PS * DLAB) - * - * For those basic low baud rate we can get the direct - * scalar from 2746800, like 115200 = 2746800/24. For those - * higher baud rate, we handle them case by case, mainly by - * adjusting the MUL/PS registers, and DIV register is kept - * as default value 0x3d09 to make things simple - */ - baud = uart_get_baud_rate(port, termios, old, 0, 4000000); - - quot = 1; - ps = 0x10; - mul = 0x3600; - switch (baud) { - case 3500000: - mul = 0x3345; - ps = 0xC; - break; - case 1843200: - mul = 0x2400; - break; - case 3000000: - case 2500000: - case 2000000: - case 1500000: - case 1000000: - case 500000: - /* mul/ps/quot = 0x9C4/0x10/0x1 will make a 500000 bps */ - mul = baud / 500000 * 0x9C4; - break; - default: - /* Use uart_get_divisor to get quot for other baud rates */ - quot = 0; - } - - if (!quot) - quot = uart_get_divisor(port, baud); - - if ((up->port.uartclk / quot) < (2400 * 16)) - fcr = UART_FCR_ENABLE_FIFO | UART_FCR_HSU_64_1B; - else if ((up->port.uartclk / quot) < (230400 * 16)) - fcr = UART_FCR_ENABLE_FIFO | UART_FCR_HSU_64_16B; - else - fcr = UART_FCR_ENABLE_FIFO | UART_FCR_HSU_64_32B; - - fcr |= UART_FCR_HSU_64B_FIFO; - - /* - * Ok, we're now changing the port state. Do it with - * interrupts disabled. - */ - spin_lock_irqsave(&up->port.lock, flags); - - /* Update the per-port timeout */ - uart_update_timeout(port, termios->c_cflag, baud); - - up->port.read_status_mask = UART_LSR_OE | UART_LSR_THRE | UART_LSR_DR; - if (termios->c_iflag & INPCK) - up->port.read_status_mask |= UART_LSR_FE | UART_LSR_PE; - if (termios->c_iflag & (IGNBRK | BRKINT | PARMRK)) - up->port.read_status_mask |= UART_LSR_BI; - - /* Characters to ignore */ - up->port.ignore_status_mask = 0; - if (termios->c_iflag & IGNPAR) - up->port.ignore_status_mask |= UART_LSR_PE | UART_LSR_FE; - if (termios->c_iflag & IGNBRK) { - up->port.ignore_status_mask |= UART_LSR_BI; - /* - * If we're ignoring parity and break indicators, - * ignore overruns too (for real raw support). - */ - if (termios->c_iflag & IGNPAR) - up->port.ignore_status_mask |= UART_LSR_OE; - } - - /* Ignore all characters if CREAD is not set */ - if ((termios->c_cflag & CREAD) == 0) - up->port.ignore_status_mask |= UART_LSR_DR; - - /* - * CTS flow control flag and modem status interrupts, disable - * MSI by default - */ - up->ier &= ~UART_IER_MSI; - if (UART_ENABLE_MS(&up->port, termios->c_cflag)) - up->ier |= UART_IER_MSI; - - serial_out(up, UART_IER, up->ier); - - if (termios->c_cflag & CRTSCTS) - up->mcr |= UART_MCR_AFE | UART_MCR_RTS; - else - up->mcr &= ~UART_MCR_AFE; - - serial_out(up, UART_LCR, cval | UART_LCR_DLAB); /* set DLAB */ - serial_out(up, UART_DLL, quot & 0xff); /* LS of divisor */ - serial_out(up, UART_DLM, quot >> 8); /* MS of divisor */ - serial_out(up, UART_LCR, cval); /* reset DLAB */ - serial_out(up, UART_MUL, mul); /* set MUL */ - serial_out(up, UART_PS, ps); /* set PS */ - up->lcr = cval; /* Save LCR */ - serial_hsu_set_mctrl(&up->port, up->port.mctrl); - serial_out(up, UART_FCR, fcr); - spin_unlock_irqrestore(&up->port.lock, flags); -} - -static void -serial_hsu_pm(struct uart_port *port, unsigned int state, - unsigned int oldstate) -{ -} - -static void serial_hsu_release_port(struct uart_port *port) -{ -} - -static int serial_hsu_request_port(struct uart_port *port) -{ - return 0; -} - -static void serial_hsu_config_port(struct uart_port *port, int flags) -{ - struct uart_hsu_port *up = - container_of(port, struct uart_hsu_port, port); - up->port.type = PORT_MFD; -} - -static int -serial_hsu_verify_port(struct uart_port *port, struct serial_struct *ser) -{ - /* We don't want the core code to modify any port params */ - return -EINVAL; -} - -static const char * -serial_hsu_type(struct uart_port *port) -{ - struct uart_hsu_port *up = - container_of(port, struct uart_hsu_port, port); - return up->name; -} - -/* Mainly for uart console use */ -static struct uart_hsu_port *serial_hsu_ports[3]; -static struct uart_driver serial_hsu_reg; - -#ifdef CONFIG_SERIAL_MFD_HSU_CONSOLE - -#define BOTH_EMPTY (UART_LSR_TEMT | UART_LSR_THRE) - -/* Wait for transmitter & holding register to empty */ -static inline void wait_for_xmitr(struct uart_hsu_port *up) -{ - unsigned int status, tmout = 1000; - - /* Wait up to 1ms for the character to be sent. */ - do { - status = serial_in(up, UART_LSR); - - if (status & UART_LSR_BI) - up->lsr_break_flag = UART_LSR_BI; - - if (--tmout == 0) - break; - udelay(1); - } while (!(status & BOTH_EMPTY)); - - /* Wait up to 1s for flow control if necessary */ - if (up->port.flags & UPF_CONS_FLOW) { - tmout = 1000000; - while (--tmout && - ((serial_in(up, UART_MSR) & UART_MSR_CTS) == 0)) - udelay(1); - } -} - -static void serial_hsu_console_putchar(struct uart_port *port, int ch) -{ - struct uart_hsu_port *up = - container_of(port, struct uart_hsu_port, port); - - wait_for_xmitr(up); - serial_out(up, UART_TX, ch); -} - -/* - * Print a string to the serial port trying not to disturb - * any possible real use of the port... - * - * The console_lock must be held when we get here. - */ -static void -serial_hsu_console_write(struct console *co, const char *s, unsigned int count) -{ - struct uart_hsu_port *up = serial_hsu_ports[co->index]; - unsigned long flags; - unsigned int ier; - int locked = 1; - - touch_nmi_watchdog(); - - local_irq_save(flags); - if (up->port.sysrq) - locked = 0; - else if (oops_in_progress) { - locked = spin_trylock(&up->port.lock); - } else - spin_lock(&up->port.lock); - - /* First save the IER then disable the interrupts */ - ier = serial_in(up, UART_IER); - serial_out(up, UART_IER, 0); - - uart_console_write(&up->port, s, count, serial_hsu_console_putchar); - - /* - * Finally, wait for transmitter to become empty - * and restore the IER - */ - wait_for_xmitr(up); - serial_out(up, UART_IER, ier); - - if (locked) - spin_unlock(&up->port.lock); - local_irq_restore(flags); -} - -static struct console serial_hsu_console; - -static int __init -serial_hsu_console_setup(struct console *co, char *options) -{ - struct uart_hsu_port *up; - int baud = 115200; - int bits = 8; - int parity = 'n'; - int flow = 'n'; - - if (co->index == -1 || co->index >= serial_hsu_reg.nr) - co->index = 0; - up = serial_hsu_ports[co->index]; - if (!up) - return -ENODEV; - - if (options) - uart_parse_options(options, &baud, &parity, &bits, &flow); - - return uart_set_options(&up->port, co, baud, parity, bits, flow); -} - -static struct console serial_hsu_console = { - .name = "ttyMFD", - .write = serial_hsu_console_write, - .device = uart_console_device, - .setup = serial_hsu_console_setup, - .flags = CON_PRINTBUFFER, - .index = -1, - .data = &serial_hsu_reg, -}; - -#define SERIAL_HSU_CONSOLE (&serial_hsu_console) -#else -#define SERIAL_HSU_CONSOLE NULL -#endif - -static struct uart_ops serial_hsu_pops = { - .tx_empty = serial_hsu_tx_empty, - .set_mctrl = serial_hsu_set_mctrl, - .get_mctrl = serial_hsu_get_mctrl, - .stop_tx = serial_hsu_stop_tx, - .start_tx = serial_hsu_start_tx, - .stop_rx = serial_hsu_stop_rx, - .enable_ms = serial_hsu_enable_ms, - .break_ctl = serial_hsu_break_ctl, - .startup = serial_hsu_startup, - .shutdown = serial_hsu_shutdown, - .set_termios = serial_hsu_set_termios, - .pm = serial_hsu_pm, - .type = serial_hsu_type, - .release_port = serial_hsu_release_port, - .request_port = serial_hsu_request_port, - .config_port = serial_hsu_config_port, - .verify_port = serial_hsu_verify_port, -}; - -static struct uart_driver serial_hsu_reg = { - .owner = THIS_MODULE, - .driver_name = "MFD serial", - .dev_name = "ttyMFD", - .major = TTY_MAJOR, - .minor = 128, - .nr = 3, - .cons = SERIAL_HSU_CONSOLE, -}; - -#ifdef CONFIG_PM -static int serial_hsu_suspend(struct pci_dev *pdev, pm_message_t state) -{ - void *priv = pci_get_drvdata(pdev); - struct uart_hsu_port *up; - - /* Make sure this is not the internal dma controller */ - if (priv && (pdev->device != 0x081E)) { - up = priv; - uart_suspend_port(&serial_hsu_reg, &up->port); - } - - pci_save_state(pdev); - pci_set_power_state(pdev, pci_choose_state(pdev, state)); - return 0; -} - -static int serial_hsu_resume(struct pci_dev *pdev) -{ - void *priv = pci_get_drvdata(pdev); - struct uart_hsu_port *up; - int ret; - - pci_set_power_state(pdev, PCI_D0); - pci_restore_state(pdev); - - ret = pci_enable_device(pdev); - if (ret) - dev_warn(&pdev->dev, - "HSU: can't re-enable device, try to continue\n"); - - if (priv && (pdev->device != 0x081E)) { - up = priv; - uart_resume_port(&serial_hsu_reg, &up->port); - } - return 0; -} - -static int serial_hsu_runtime_idle(struct device *dev) -{ - pm_schedule_suspend(dev, 500); - return -EBUSY; -} - -static int serial_hsu_runtime_suspend(struct device *dev) -{ - return 0; -} - -static int serial_hsu_runtime_resume(struct device *dev) -{ - return 0; -} -#else -#define serial_hsu_suspend NULL -#define serial_hsu_resume NULL -#define serial_hsu_runtime_idle NULL -#define serial_hsu_runtime_suspend NULL -#define serial_hsu_runtime_resume NULL -#endif - -static const struct dev_pm_ops serial_hsu_pm_ops = { - .runtime_suspend = serial_hsu_runtime_suspend, - .runtime_resume = serial_hsu_runtime_resume, - .runtime_idle = serial_hsu_runtime_idle, -}; - -/* temp global pointer before we settle down on using one or four PCI dev */ -static struct hsu_port *phsu; - -static int serial_hsu_probe(struct pci_dev *pdev, - const struct pci_device_id *ent) -{ - struct uart_hsu_port *uport; - int index, ret; - - printk(KERN_INFO "HSU: found PCI Serial controller(ID: %04x:%04x)\n", - pdev->vendor, pdev->device); - - switch (pdev->device) { - case 0x081B: - index = 0; - break; - case 0x081C: - index = 1; - break; - case 0x081D: - index = 2; - break; - case 0x081E: - /* internal DMA controller */ - index = 3; - break; - default: - dev_err(&pdev->dev, "HSU: out of index!"); - return -ENODEV; - } - - ret = pci_enable_device(pdev); - if (ret) - return ret; - - if (index == 3) { - /* DMA controller */ - ret = request_irq(pdev->irq, dma_irq, 0, "hsu_dma", phsu); - if (ret) { - dev_err(&pdev->dev, "can not get IRQ\n"); - goto err_disable; - } - pci_set_drvdata(pdev, phsu); - } else { - /* UART port 0~2 */ - uport = &phsu->port[index]; - uport->port.irq = pdev->irq; - uport->port.dev = &pdev->dev; - uport->dev = &pdev->dev; - - ret = request_irq(pdev->irq, port_irq, 0, uport->name, uport); - if (ret) { - dev_err(&pdev->dev, "can not get IRQ\n"); - goto err_disable; - } - uart_add_one_port(&serial_hsu_reg, &uport->port); - - pci_set_drvdata(pdev, uport); - } - - pm_runtime_put_noidle(&pdev->dev); - pm_runtime_allow(&pdev->dev); - - return 0; - -err_disable: - pci_disable_device(pdev); - return ret; -} - -static void hsu_global_init(void) -{ - struct hsu_port *hsu; - struct uart_hsu_port *uport; - struct hsu_dma_chan *dchan; - int i, ret; - - hsu = kzalloc(sizeof(struct hsu_port), GFP_KERNEL); - if (!hsu) - return; - - /* Get basic io resource and map it */ - hsu->paddr = 0xffa28000; - hsu->iolen = 0x1000; - - if (!(request_mem_region(hsu->paddr, hsu->iolen, "HSU global"))) - pr_warn("HSU: error in request mem region\n"); - - hsu->reg = ioremap_nocache((unsigned long)hsu->paddr, hsu->iolen); - if (!hsu->reg) { - pr_err("HSU: error in ioremap\n"); - ret = -ENOMEM; - goto err_free_region; - } - - /* Initialise the 3 UART ports */ - uport = hsu->port; - for (i = 0; i < 3; i++) { - uport->port.type = PORT_MFD; - uport->port.iotype = UPIO_MEM; - uport->port.mapbase = (resource_size_t)hsu->paddr - + HSU_PORT_REG_OFFSET - + i * HSU_PORT_REG_LENGTH; - uport->port.membase = hsu->reg + HSU_PORT_REG_OFFSET - + i * HSU_PORT_REG_LENGTH; - - sprintf(uport->name, "hsu_port%d", i); - uport->port.fifosize = 64; - uport->port.ops = &serial_hsu_pops; - uport->port.line = i; - uport->port.flags = UPF_IOREMAP; - /* set the scalable maxim support rate to 2746800 bps */ - uport->port.uartclk = 115200 * 24 * 16; - - uport->running = 0; - uport->txc = &hsu->chans[i * 2]; - uport->rxc = &hsu->chans[i * 2 + 1]; - - serial_hsu_ports[i] = uport; - uport->index = i; - - if (hsu_dma_enable & (1<use_dma = 1; - else - uport->use_dma = 0; - - uport++; - } - - /* Initialise 6 dma channels */ - dchan = hsu->chans; - for (i = 0; i < 6; i++) { - dchan->id = i; - dchan->dirt = (i & 0x1) ? DMA_FROM_DEVICE : DMA_TO_DEVICE; - dchan->uport = &hsu->port[i/2]; - dchan->reg = hsu->reg + HSU_DMA_CHANS_REG_OFFSET + - i * HSU_DMA_CHANS_REG_LENGTH; - - dchan++; - } - - phsu = hsu; - hsu_debugfs_init(hsu); - return; - -err_free_region: - release_mem_region(hsu->paddr, hsu->iolen); - kfree(hsu); - return; -} - -static void serial_hsu_remove(struct pci_dev *pdev) -{ - void *priv = pci_get_drvdata(pdev); - struct uart_hsu_port *up; - - if (!priv) - return; - - pm_runtime_forbid(&pdev->dev); - pm_runtime_get_noresume(&pdev->dev); - - /* For port 0/1/2, priv is the address of uart_hsu_port */ - if (pdev->device != 0x081E) { - up = priv; - uart_remove_one_port(&serial_hsu_reg, &up->port); - } - - free_irq(pdev->irq, priv); - pci_disable_device(pdev); -} - -/* First 3 are UART ports, and the 4th is the DMA */ -static const struct pci_device_id pci_ids[] = { - { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x081B) }, - { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x081C) }, - { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x081D) }, - { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x081E) }, - {}, -}; - -static struct pci_driver hsu_pci_driver = { - .name = "HSU serial", - .id_table = pci_ids, - .probe = serial_hsu_probe, - .remove = serial_hsu_remove, - .suspend = serial_hsu_suspend, - .resume = serial_hsu_resume, - .driver = { - .pm = &serial_hsu_pm_ops, - }, -}; - -static int __init hsu_pci_init(void) -{ - int ret; - - hsu_global_init(); - - ret = uart_register_driver(&serial_hsu_reg); - if (ret) - return ret; - - return pci_register_driver(&hsu_pci_driver); -} - -static void __exit hsu_pci_exit(void) -{ - pci_unregister_driver(&hsu_pci_driver); - uart_unregister_driver(&serial_hsu_reg); - - hsu_debugfs_remove(phsu); - - kfree(phsu); -} - -module_init(hsu_pci_init); -module_exit(hsu_pci_exit); - -MODULE_LICENSE("GPL v2"); -MODULE_DEVICE_TABLE(pci, pci_ids); diff --git a/include/linux/serial_mfd.h b/include/linux/serial_mfd.h deleted file mode 100644 index 2b071e0b034d..000000000000 --- a/include/linux/serial_mfd.h +++ /dev/null @@ -1,47 +0,0 @@ -#ifndef _SERIAL_MFD_H_ -#define _SERIAL_MFD_H_ - -/* HW register offset definition */ -#define UART_FOR 0x08 -#define UART_PS 0x0C -#define UART_MUL 0x0D -#define UART_DIV 0x0E - -#define HSU_GBL_IEN 0x0 -#define HSU_GBL_IST 0x4 - -#define HSU_GBL_INT_BIT_PORT0 0x0 -#define HSU_GBL_INT_BIT_PORT1 0x1 -#define HSU_GBL_INT_BIT_PORT2 0x2 -#define HSU_GBL_INT_BIT_IRI 0x3 -#define HSU_GBL_INT_BIT_HDLC 0x4 -#define HSU_GBL_INT_BIT_DMA 0x5 - -#define HSU_GBL_ISR 0x8 -#define HSU_GBL_DMASR 0x400 -#define HSU_GBL_DMAISR 0x404 - -#define HSU_PORT_REG_OFFSET 0x80 -#define HSU_PORT0_REG_OFFSET 0x80 -#define HSU_PORT1_REG_OFFSET 0x100 -#define HSU_PORT2_REG_OFFSET 0x180 -#define HSU_PORT_REG_LENGTH 0x80 - -#define HSU_DMA_CHANS_REG_OFFSET 0x500 -#define HSU_DMA_CHANS_REG_LENGTH 0x40 - -#define HSU_CH_SR 0x0 /* channel status reg */ -#define HSU_CH_CR 0x4 /* control reg */ -#define HSU_CH_DCR 0x8 /* descriptor control reg */ -#define HSU_CH_BSR 0x10 /* max fifo buffer size reg */ -#define HSU_CH_MOTSR 0x14 /* minimum ocp transfer size */ -#define HSU_CH_D0SAR 0x20 /* desc 0 start addr */ -#define HSU_CH_D0TSR 0x24 /* desc 0 transfer size */ -#define HSU_CH_D1SAR 0x28 -#define HSU_CH_D1TSR 0x2C -#define HSU_CH_D2SAR 0x30 -#define HSU_CH_D2TSR 0x34 -#define HSU_CH_D3SAR 0x38 -#define HSU_CH_D3TSR 0x3C - -#endif diff --git a/include/uapi/linux/serial_reg.h b/include/uapi/linux/serial_reg.h index 00adb01fa5f3..e9b4cb0cd7ed 100644 --- a/include/uapi/linux/serial_reg.h +++ b/include/uapi/linux/serial_reg.h @@ -241,25 +241,6 @@ #define UART_FCR_PXAR16 0x80 /* receive FIFO threshold = 16 */ #define UART_FCR_PXAR32 0xc0 /* receive FIFO threshold = 32 */ -/* - * Intel MID on-chip HSU (High Speed UART) defined bits - */ -#define UART_FCR_HSU_64_1B 0x00 /* receive FIFO treshold = 1 */ -#define UART_FCR_HSU_64_16B 0x40 /* receive FIFO treshold = 16 */ -#define UART_FCR_HSU_64_32B 0x80 /* receive FIFO treshold = 32 */ -#define UART_FCR_HSU_64_56B 0xc0 /* receive FIFO treshold = 56 */ - -#define UART_FCR_HSU_16_1B 0x00 /* receive FIFO treshold = 1 */ -#define UART_FCR_HSU_16_4B 0x40 /* receive FIFO treshold = 4 */ -#define UART_FCR_HSU_16_8B 0x80 /* receive FIFO treshold = 8 */ -#define UART_FCR_HSU_16_14B 0xc0 /* receive FIFO treshold = 14 */ - -#define UART_FCR_HSU_64B_FIFO 0x20 /* chose 64 bytes FIFO */ -#define UART_FCR_HSU_16B_FIFO 0x00 /* chose 16 bytes FIFO */ - -#define UART_FCR_HALF_EMPT_TXI 0x00 /* trigger TX_EMPT IRQ for half empty */ -#define UART_FCR_FULL_EMPT_TXI 0x08 /* trigger TX_EMPT IRQ for full empty */ - /* * These register definitions are for the 16C950 */ -- cgit v1.2.3 From afe9cbb1a6adf6da5fa6d4747d102b95b4bb52c1 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Tue, 24 Feb 2015 11:17:10 +0100 Subject: serial: imx: drop support for IRDA MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Support for IRDA was added in 2009 in commit v2.6.31-rc1~399^2~2. There are no in-tree users. Signed-off-by: Uwe Kleine-König Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/imx.c | 157 ++----------------------------- include/linux/platform_data/serial-imx.h | 5 - 2 files changed, 9 insertions(+), 153 deletions(-) (limited to 'include') diff --git a/drivers/tty/serial/imx.c b/drivers/tty/serial/imx.c index cbbb47385877..6dc1d2781b86 100644 --- a/drivers/tty/serial/imx.c +++ b/drivers/tty/serial/imx.c @@ -6,9 +6,6 @@ * Author: Sascha Hauer * Copyright (C) 2004 Pengutronix * - * Author: Fabian Godehardt (added IrDA support for iMX) - * Copyright (C) 2009 emlix GmbH - * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or @@ -201,7 +198,6 @@ struct imx_port { unsigned int old_status; unsigned int have_rtscts:1; unsigned int dte_mode:1; - unsigned int use_irda:1; unsigned int irda_inv_rx:1; unsigned int irda_inv_tx:1; unsigned short trcv_delay; /* transceiver delay */ @@ -228,12 +224,6 @@ struct imx_port_ucrs { unsigned int ucr3; }; -#ifdef CONFIG_IRDA -#define USE_IRDA(sport) ((sport)->use_irda) -#else -#define USE_IRDA(sport) (0) -#endif - static struct imx_uart_data imx_uart_devdata[] = { [IMX1_UART] = { .uts_reg = IMX1_UTS, @@ -368,48 +358,6 @@ static void imx_stop_tx(struct uart_port *port) struct imx_port *sport = (struct imx_port *)port; unsigned long temp; - if (USE_IRDA(sport)) { - /* half duplex - wait for end of transmission */ - int n = 256; - while ((--n > 0) && - !(readl(sport->port.membase + USR2) & USR2_TXDC)) { - udelay(5); - barrier(); - } - /* - * irda transceiver - wait a bit more to avoid - * cutoff, hardware dependent - */ - udelay(sport->trcv_delay); - - /* - * half duplex - reactivate receive mode, - * flush receive pipe echo crap - */ - if (readl(sport->port.membase + USR2) & USR2_TXDC) { - temp = readl(sport->port.membase + UCR1); - temp &= ~(UCR1_TXMPTYEN | UCR1_TRDYEN); - writel(temp, sport->port.membase + UCR1); - - temp = readl(sport->port.membase + UCR4); - temp &= ~(UCR4_TCEN); - writel(temp, sport->port.membase + UCR4); - - while (readl(sport->port.membase + URXD0) & - URXD_CHARRDY) - barrier(); - - temp = readl(sport->port.membase + UCR1); - temp |= UCR1_RRDYEN; - writel(temp, sport->port.membase + UCR1); - - temp = readl(sport->port.membase + UCR4); - temp |= UCR4_DREN; - writel(temp, sport->port.membase + UCR4); - } - return; - } - /* * We are maybe in the SMP context, so if the DMA TX thread is running * on other cpu, we have to wait for it to finish. @@ -612,32 +560,11 @@ static void imx_start_tx(struct uart_port *port) struct imx_port *sport = (struct imx_port *)port; unsigned long temp; - if (USE_IRDA(sport)) { - /* half duplex in IrDA mode; have to disable receive mode */ - temp = readl(sport->port.membase + UCR4); - temp &= ~(UCR4_DREN); - writel(temp, sport->port.membase + UCR4); - - temp = readl(sport->port.membase + UCR1); - temp &= ~(UCR1_RRDYEN); - writel(temp, sport->port.membase + UCR1); - } - if (!sport->dma_is_enabled) { temp = readl(sport->port.membase + UCR1); writel(temp | UCR1_TXMPTYEN, sport->port.membase + UCR1); } - if (USE_IRDA(sport)) { - temp = readl(sport->port.membase + UCR1); - temp |= UCR1_TRDYEN; - writel(temp, sport->port.membase + UCR1); - - temp = readl(sport->port.membase + UCR4); - temp |= UCR4_TCEN; - writel(temp, sport->port.membase + UCR4); - } - if (sport->dma_is_enabled) { if (sport->port.x_char) { /* We have X-char to send, so enable TX IRQ and @@ -1148,9 +1075,6 @@ static int imx_startup(struct uart_port *port) */ temp = readl(sport->port.membase + UCR4); - if (USE_IRDA(sport)) - temp |= UCR4_IRSC; - /* set the trigger level for CTS */ temp &= ~(UCR4_CTSTL_MASK << UCR4_CTSTL_SHF); temp |= CTSTL << UCR4_CTSTL_SHF; @@ -1186,11 +1110,6 @@ static int imx_startup(struct uart_port *port) temp = readl(sport->port.membase + UCR1); temp |= UCR1_RRDYEN | UCR1_RTSDEN | UCR1_UARTEN; - if (USE_IRDA(sport)) { - temp |= UCR1_IREN; - temp &= ~(UCR1_RTSDEN); - } - writel(temp, sport->port.membase + UCR1); temp = readl(sport->port.membase + UCR4); @@ -1209,38 +1128,12 @@ static int imx_startup(struct uart_port *port) writel(temp, sport->port.membase + UCR3); } - if (USE_IRDA(sport)) { - temp = readl(sport->port.membase + UCR4); - if (sport->irda_inv_rx) - temp |= UCR4_INVR; - else - temp &= ~(UCR4_INVR); - writel(temp | UCR4_DREN, sport->port.membase + UCR4); - - temp = readl(sport->port.membase + UCR3); - if (sport->irda_inv_tx) - temp |= UCR3_INVT; - else - temp &= ~(UCR3_INVT); - writel(temp, sport->port.membase + UCR3); - } - /* * Enable modem status interrupts */ imx_enable_ms(&sport->port); spin_unlock_irqrestore(&sport->port.lock, flags); - if (USE_IRDA(sport)) { - struct imxuart_platform_data *pdata; - pdata = dev_get_platdata(sport->port.dev); - sport->irda_inv_rx = pdata->irda_inv_rx; - sport->irda_inv_tx = pdata->irda_inv_tx; - sport->trcv_delay = pdata->transceiver_delay; - if (pdata->irda_enable) - pdata->irda_enable(1); - } - return 0; } @@ -1276,13 +1169,6 @@ static void imx_shutdown(struct uart_port *port) writel(temp, sport->port.membase + UCR2); spin_unlock_irqrestore(&sport->port.lock, flags); - if (USE_IRDA(sport)) { - struct imxuart_platform_data *pdata; - pdata = dev_get_platdata(sport->port.dev); - if (pdata->irda_enable) - pdata->irda_enable(0); - } - /* * Stop our timer. */ @@ -1295,8 +1181,6 @@ static void imx_shutdown(struct uart_port *port) spin_lock_irqsave(&sport->port.lock, flags); temp = readl(sport->port.membase + UCR1); temp &= ~(UCR1_TXMPTYEN | UCR1_RRDYEN | UCR1_RTSDEN | UCR1_UARTEN); - if (USE_IRDA(sport)) - temp &= ~(UCR1_IREN); writel(temp, sport->port.membase + UCR1); spin_unlock_irqrestore(&sport->port.lock, flags); @@ -1450,24 +1334,16 @@ imx_set_termios(struct uart_port *port, struct ktermios *termios, sport->port.membase + UCR2); old_txrxen &= (UCR2_TXEN | UCR2_RXEN); - if (USE_IRDA(sport)) { - /* - * use maximum available submodule frequency to - * avoid missing short pulses due to low sampling rate - */ + /* custom-baudrate handling */ + div = sport->port.uartclk / (baud * 16); + if (baud == 38400 && quot != div) + baud = sport->port.uartclk / (quot * 16); + + div = sport->port.uartclk / (baud * 16); + if (div > 7) + div = 7; + if (!div) div = 1; - } else { - /* custom-baudrate handling */ - div = sport->port.uartclk / (baud * 16); - if (baud == 38400 && quot != div) - baud = sport->port.uartclk / (quot * 16); - - div = sport->port.uartclk / (baud * 16); - if (div > 7) - div = 7; - if (!div) - div = 1; - } rational_best_approximation(16 * div * baud, sport->port.uartclk, 1 << 16, 1 << 16, &num, &denom); @@ -1906,9 +1782,6 @@ static int serial_imx_probe_dt(struct imx_port *sport, if (of_get_property(np, "fsl,uart-has-rtscts", NULL)) sport->have_rtscts = 1; - if (of_get_property(np, "fsl,irda-mode", NULL)) - sport->use_irda = 1; - if (of_get_property(np, "fsl,dte-mode", NULL)) sport->dte_mode = 1; @@ -1937,9 +1810,6 @@ static void serial_imx_probe_pdata(struct imx_port *sport, if (pdata->flags & IMXUART_HAVE_RTSCTS) sport->have_rtscts = 1; - - if (pdata->flags & IMXUART_IRDA) - sport->use_irda = 1; } static int serial_imx_probe(struct platform_device *pdev) @@ -2012,15 +1882,6 @@ static int serial_imx_probe(struct platform_device *pdev) dev_name(&pdev->dev), sport); if (ret) return ret; - - /* do not use RTS IRQ on IrDA */ - if (!USE_IRDA(sport)) { - ret = devm_request_irq(&pdev->dev, rtsirq, - imx_rtsint, 0, - dev_name(&pdev->dev), sport); - if (ret) - return ret; - } } else { ret = devm_request_irq(&pdev->dev, rxirq, imx_int, 0, dev_name(&pdev->dev), sport); diff --git a/include/linux/platform_data/serial-imx.h b/include/linux/platform_data/serial-imx.h index 3cc2e3c40914..a938eba2f18e 100644 --- a/include/linux/platform_data/serial-imx.h +++ b/include/linux/platform_data/serial-imx.h @@ -20,14 +20,9 @@ #define ASMARM_ARCH_UART_H #define IMXUART_HAVE_RTSCTS (1<<0) -#define IMXUART_IRDA (1<<1) struct imxuart_platform_data { unsigned int flags; - void (*irda_enable)(int enable); - unsigned int irda_inv_rx:1; - unsigned int irda_inv_tx:1; - unsigned short transceiver_delay; }; #endif -- cgit v1.2.3 From c93682477bd861744589215515a63b81fdbd8948 Mon Sep 17 00:00:00 2001 From: Shani Michaeli Date: Thu, 5 Mar 2015 20:16:11 +0200 Subject: net/dcb: Add IEEE QCN attribute As specified in 802.1Qau spec. Add this optional attribute to the DCB netlink layer. To allow for application to use the new attribute, NIC drivers should implement and register the callbacks ieee_getqcn, ieee_setqcn and ieee_getqcnstats. The QCN attribute holds a set of parameters for management, and a set of statistics to provide informative data on Congestion-Control defined by this spec. Signed-off-by: Shani Michaeli Signed-off-by: Shachar Raindel Signed-off-by: Or Gerlitz Acked-by: John Fastabend Signed-off-by: David S. Miller --- include/net/dcbnl.h | 3 +++ include/uapi/linux/dcbnl.h | 66 ++++++++++++++++++++++++++++++++++++++++++++++ net/dcb/dcbnl.c | 44 ++++++++++++++++++++++++++++--- 3 files changed, 110 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/include/net/dcbnl.h b/include/net/dcbnl.h index 597b88a94332..207d9ba1f92c 100644 --- a/include/net/dcbnl.h +++ b/include/net/dcbnl.h @@ -49,6 +49,9 @@ struct dcbnl_rtnl_ops { int (*ieee_setets) (struct net_device *, struct ieee_ets *); int (*ieee_getmaxrate) (struct net_device *, struct ieee_maxrate *); int (*ieee_setmaxrate) (struct net_device *, struct ieee_maxrate *); + int (*ieee_getqcn) (struct net_device *, struct ieee_qcn *); + int (*ieee_setqcn) (struct net_device *, struct ieee_qcn *); + int (*ieee_getqcnstats) (struct net_device *, struct ieee_qcn_stats *); int (*ieee_getpfc) (struct net_device *, struct ieee_pfc *); int (*ieee_setpfc) (struct net_device *, struct ieee_pfc *); int (*ieee_getapp) (struct net_device *, struct dcb_app *); diff --git a/include/uapi/linux/dcbnl.h b/include/uapi/linux/dcbnl.h index e711f20dc522..6497d7933d5b 100644 --- a/include/uapi/linux/dcbnl.h +++ b/include/uapi/linux/dcbnl.h @@ -78,6 +78,70 @@ struct ieee_maxrate { __u64 tc_maxrate[IEEE_8021QAZ_MAX_TCS]; }; +enum dcbnl_cndd_states { + DCB_CNDD_RESET = 0, + DCB_CNDD_EDGE, + DCB_CNDD_INTERIOR, + DCB_CNDD_INTERIOR_READY, +}; + +/* This structure contains the IEEE 802.1Qau QCN managed object. + * + *@rpg_enable: enable QCN RP + *@rppp_max_rps: maximum number of RPs allowed for this CNPV on this port + *@rpg_time_reset: time between rate increases if no CNMs received. + * given in u-seconds + *@rpg_byte_reset: transmitted data between rate increases if no CNMs received. + * given in Bytes + *@rpg_threshold: The number of times rpByteStage or rpTimeStage can count + * before RP rate control state machine advances states + *@rpg_max_rate: the maxinun rate, in Mbits per second, + * at which an RP can transmit + *@rpg_ai_rate: The rate, in Mbits per second, + * used to increase rpTargetRate in the RPR_ACTIVE_INCREASE + *@rpg_hai_rate: The rate, in Mbits per second, + * used to increase rpTargetRate in the RPR_HYPER_INCREASE state + *@rpg_gd: Upon CNM receive, flow rate is limited to (Fb/Gd)*CurrentRate. + * rpgGd is given as log2(Gd), where Gd may only be powers of 2 + *@rpg_min_dec_fac: The minimum factor by which the current transmit rate + * can be changed by reception of a CNM. + * value is given as percentage (1-100) + *@rpg_min_rate: The minimum value, in bits per second, for rate to limit + *@cndd_state_machine: The state of the congestion notification domain + * defense state machine, as defined by IEEE 802.3Qau + * section 32.1.1. In the interior ready state, + * the QCN capable hardware may add CN-TAG TLV to the + * outgoing traffic, to specifically identify outgoing + * flows. + */ + +struct ieee_qcn { + __u8 rpg_enable[IEEE_8021QAZ_MAX_TCS]; + __u32 rppp_max_rps[IEEE_8021QAZ_MAX_TCS]; + __u32 rpg_time_reset[IEEE_8021QAZ_MAX_TCS]; + __u32 rpg_byte_reset[IEEE_8021QAZ_MAX_TCS]; + __u32 rpg_threshold[IEEE_8021QAZ_MAX_TCS]; + __u32 rpg_max_rate[IEEE_8021QAZ_MAX_TCS]; + __u32 rpg_ai_rate[IEEE_8021QAZ_MAX_TCS]; + __u32 rpg_hai_rate[IEEE_8021QAZ_MAX_TCS]; + __u32 rpg_gd[IEEE_8021QAZ_MAX_TCS]; + __u32 rpg_min_dec_fac[IEEE_8021QAZ_MAX_TCS]; + __u32 rpg_min_rate[IEEE_8021QAZ_MAX_TCS]; + __u32 cndd_state_machine[IEEE_8021QAZ_MAX_TCS]; +}; + +/* This structure contains the IEEE 802.1Qau QCN statistics. + * + *@rppp_rp_centiseconds: the number of RP-centiseconds accumulated + * by RPs at this priority level on this Port + *@rppp_created_rps: number of active RPs(flows) that react to CNMs + */ + +struct ieee_qcn_stats { + __u64 rppp_rp_centiseconds[IEEE_8021QAZ_MAX_TCS]; + __u32 rppp_created_rps[IEEE_8021QAZ_MAX_TCS]; +}; + /* This structure contains the IEEE 802.1Qaz PFC managed object * * @pfc_cap: Indicates the number of traffic classes on the local device @@ -334,6 +398,8 @@ enum ieee_attrs { DCB_ATTR_IEEE_PEER_PFC, DCB_ATTR_IEEE_PEER_APP, DCB_ATTR_IEEE_MAXRATE, + DCB_ATTR_IEEE_QCN, + DCB_ATTR_IEEE_QCN_STATS, __DCB_ATTR_IEEE_MAX }; #define DCB_ATTR_IEEE_MAX (__DCB_ATTR_IEEE_MAX - 1) diff --git a/net/dcb/dcbnl.c b/net/dcb/dcbnl.c index 93ea80196f0e..5b21f6f88e97 100644 --- a/net/dcb/dcbnl.c +++ b/net/dcb/dcbnl.c @@ -177,6 +177,8 @@ static const struct nla_policy dcbnl_ieee_policy[DCB_ATTR_IEEE_MAX + 1] = { [DCB_ATTR_IEEE_PFC] = {.len = sizeof(struct ieee_pfc)}, [DCB_ATTR_IEEE_APP_TABLE] = {.type = NLA_NESTED}, [DCB_ATTR_IEEE_MAXRATE] = {.len = sizeof(struct ieee_maxrate)}, + [DCB_ATTR_IEEE_QCN] = {.len = sizeof(struct ieee_qcn)}, + [DCB_ATTR_IEEE_QCN_STATS] = {.len = sizeof(struct ieee_qcn_stats)}, }; static const struct nla_policy dcbnl_ieee_app[DCB_ATTR_IEEE_APP_MAX + 1] = { @@ -1030,7 +1032,7 @@ nla_put_failure: return err; } -/* Handle IEEE 802.1Qaz GET commands. */ +/* Handle IEEE 802.1Qaz/802.1Qau/802.1Qbb GET commands. */ static int dcbnl_ieee_fill(struct sk_buff *skb, struct net_device *netdev) { struct nlattr *ieee, *app; @@ -1067,6 +1069,32 @@ static int dcbnl_ieee_fill(struct sk_buff *skb, struct net_device *netdev) } } + if (ops->ieee_getqcn) { + struct ieee_qcn qcn; + + memset(&qcn, 0, sizeof(qcn)); + err = ops->ieee_getqcn(netdev, &qcn); + if (!err) { + err = nla_put(skb, DCB_ATTR_IEEE_QCN, + sizeof(qcn), &qcn); + if (err) + return -EMSGSIZE; + } + } + + if (ops->ieee_getqcnstats) { + struct ieee_qcn_stats qcn_stats; + + memset(&qcn_stats, 0, sizeof(qcn_stats)); + err = ops->ieee_getqcnstats(netdev, &qcn_stats); + if (!err) { + err = nla_put(skb, DCB_ATTR_IEEE_QCN_STATS, + sizeof(qcn_stats), &qcn_stats); + if (err) + return -EMSGSIZE; + } + } + if (ops->ieee_getpfc) { struct ieee_pfc pfc; memset(&pfc, 0, sizeof(pfc)); @@ -1379,8 +1407,9 @@ int dcbnl_cee_notify(struct net_device *dev, int event, int cmd, } EXPORT_SYMBOL(dcbnl_cee_notify); -/* Handle IEEE 802.1Qaz SET commands. If any requested operation can not - * be completed the entire msg is aborted and error value is returned. +/* Handle IEEE 802.1Qaz/802.1Qau/802.1Qbb SET commands. + * If any requested operation can not be completed + * the entire msg is aborted and error value is returned. * No attempt is made to reconcile the case where only part of the * cmd can be completed. */ @@ -1417,6 +1446,15 @@ static int dcbnl_ieee_set(struct net_device *netdev, struct nlmsghdr *nlh, goto err; } + if (ieee[DCB_ATTR_IEEE_QCN] && ops->ieee_setqcn) { + struct ieee_qcn *qcn = + nla_data(ieee[DCB_ATTR_IEEE_QCN]); + + err = ops->ieee_setqcn(netdev, qcn); + if (err) + goto err; + } + if (ieee[DCB_ATTR_IEEE_PFC] && ops->ieee_setpfc) { struct ieee_pfc *pfc = nla_data(ieee[DCB_ATTR_IEEE_PFC]); err = ops->ieee_setpfc(netdev, pfc); -- cgit v1.2.3 From d237baa1cbb3a2335357484c1d63a810a01947e2 Mon Sep 17 00:00:00 2001 From: Shani Michaeli Date: Thu, 5 Mar 2015 20:16:12 +0200 Subject: net/mlx4_core: Add basic elements for QCN Add device capability, firmware command opcode and etc prior elements needed for QCN suppprt. Disable SRIOV VF view/access for QCN is disabled. While here, remove a redundant offset definition into the QUERY_DEV_CAP mailbox. Signed-off-by: Shani Michaeli Signed-off-by: Or Gerlitz Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlx4/cmd.c | 9 +++++++++ drivers/net/ethernet/mellanox/mlx4/fw.c | 13 +++++++++++-- include/linux/mlx4/cmd.h | 13 +++++++++++++ include/linux/mlx4/device.h | 3 ++- 4 files changed, 35 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/drivers/net/ethernet/mellanox/mlx4/cmd.c b/drivers/net/ethernet/mellanox/mlx4/cmd.c index a681d7c0bb9f..20b3c7b21e63 100644 --- a/drivers/net/ethernet/mellanox/mlx4/cmd.c +++ b/drivers/net/ethernet/mellanox/mlx4/cmd.c @@ -1499,6 +1499,15 @@ static struct mlx4_cmd_info cmd_info[] = { .verify = NULL, .wrapper = mlx4_ACCESS_REG_wrapper, }, + { + .opcode = MLX4_CMD_CONGESTION_CTRL_OPCODE, + .has_inbox = false, + .has_outbox = false, + .out_is_imm = false, + .encode_slave_id = false, + .verify = NULL, + .wrapper = mlx4_CMD_EPERM_wrapper, + }, /* Native multicast commands are not available for guests */ { .opcode = MLX4_CMD_QP_ATTACH, diff --git a/drivers/net/ethernet/mellanox/mlx4/fw.c b/drivers/net/ethernet/mellanox/mlx4/fw.c index 5a21e5dc94cb..242bcee5d774 100644 --- a/drivers/net/ethernet/mellanox/mlx4/fw.c +++ b/drivers/net/ethernet/mellanox/mlx4/fw.c @@ -143,7 +143,8 @@ static void dump_dev_cap_flags2(struct mlx4_dev *dev, u64 flags) [18] = "More than 80 VFs support", [19] = "Performance optimized for limited rule configuration flow steering support", [20] = "Recoverable error events support", - [21] = "Port Remap support" + [21] = "Port Remap support", + [22] = "QCN support" }; int i; @@ -675,7 +676,7 @@ int mlx4_QUERY_DEV_CAP(struct mlx4_dev *dev, struct mlx4_dev_cap *dev_cap) #define QUERY_DEV_CAP_FLOW_STEERING_RANGE_EN_OFFSET 0x76 #define QUERY_DEV_CAP_FLOW_STEERING_MAX_QP_OFFSET 0x77 #define QUERY_DEV_CAP_CQ_EQ_CACHE_LINE_STRIDE 0x7a -#define QUERY_DEV_CAP_ETH_PROT_CTRL_OFFSET 0x7a +#define QUERY_DEV_CAP_ECN_QCN_VER_OFFSET 0x7b #define QUERY_DEV_CAP_RDMARC_ENTRY_SZ_OFFSET 0x80 #define QUERY_DEV_CAP_QPC_ENTRY_SZ_OFFSET 0x82 #define QUERY_DEV_CAP_AUX_ENTRY_SZ_OFFSET 0x84 @@ -777,6 +778,9 @@ int mlx4_QUERY_DEV_CAP(struct mlx4_dev *dev, struct mlx4_dev_cap *dev_cap) dev_cap->flags2 |= MLX4_DEV_CAP_FLAG2_DMFS_IPOIB; MLX4_GET(field, outbox, QUERY_DEV_CAP_FLOW_STEERING_MAX_QP_OFFSET); dev_cap->fs_max_num_qp_per_entry = field; + MLX4_GET(field, outbox, QUERY_DEV_CAP_ECN_QCN_VER_OFFSET); + if (field & 0x1) + dev_cap->flags2 |= MLX4_DEV_CAP_FLAG2_QCN; MLX4_GET(stat_rate, outbox, QUERY_DEV_CAP_RATE_SUPPORT_OFFSET); dev_cap->stat_rate_support = stat_rate; MLX4_GET(field, outbox, QUERY_DEV_CAP_CQ_TS_SUPPORT_OFFSET); @@ -1149,6 +1153,11 @@ int mlx4_QUERY_DEV_CAP_wrapper(struct mlx4_dev *dev, int slave, DEV_CAP_EXT_2_FLAG_FSM); MLX4_PUT(outbox->buf, field32, QUERY_DEV_CAP_EXT_2_FLAGS_OFFSET); + /* turn off QCN for guests */ + MLX4_GET(field, outbox->buf, QUERY_DEV_CAP_ECN_QCN_VER_OFFSET); + field &= 0xfe; + MLX4_PUT(outbox->buf, field, QUERY_DEV_CAP_ECN_QCN_VER_OFFSET); + return 0; } diff --git a/include/linux/mlx4/cmd.h b/include/linux/mlx4/cmd.h index 7b6d4e9ff603..7299e9548906 100644 --- a/include/linux/mlx4/cmd.h +++ b/include/linux/mlx4/cmd.h @@ -163,6 +163,9 @@ enum { MLX4_QP_FLOW_STEERING_ATTACH = 0x65, MLX4_QP_FLOW_STEERING_DETACH = 0x66, MLX4_FLOW_STEERING_IB_UC_QP_RANGE = 0x64, + + /* Update and read QCN parameters */ + MLX4_CMD_CONGESTION_CTRL_OPCODE = 0x68, }; enum { @@ -233,6 +236,16 @@ struct mlx4_config_dev_params { u8 rx_csum_flags_port_2; }; +enum mlx4_en_congestion_control_algorithm { + MLX4_CTRL_ALGO_802_1_QAU_REACTION_POINT = 0, +}; + +enum mlx4_en_congestion_control_opmod { + MLX4_CONGESTION_CONTROL_GET_PARAMS, + MLX4_CONGESTION_CONTROL_GET_STATISTICS, + MLX4_CONGESTION_CONTROL_SET_PARAMS = 4, +}; + struct mlx4_dev; struct mlx4_cmd_mailbox { diff --git a/include/linux/mlx4/device.h b/include/linux/mlx4/device.h index e4ebff7e9d02..1cc54822b931 100644 --- a/include/linux/mlx4/device.h +++ b/include/linux/mlx4/device.h @@ -203,7 +203,8 @@ enum { MLX4_DEV_CAP_FLAG2_80_VFS = 1LL << 18, MLX4_DEV_CAP_FLAG2_FS_A0 = 1LL << 19, MLX4_DEV_CAP_FLAG2_RECOVERABLE_ERROR_EVENT = 1LL << 20, - MLX4_DEV_CAP_FLAG2_PORT_REMAP = 1LL << 21 + MLX4_DEV_CAP_FLAG2_PORT_REMAP = 1LL << 21, + MLX4_DEV_CAP_FLAG2_QCN = 1LL << 22, }; enum { -- cgit v1.2.3 From 73abaf87f01be6fa6da3c0aa9c138a1b6b281068 Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Sun, 1 Mar 2015 11:05:46 -0500 Subject: serial: earlycon: Refactor parse_options into serial core Prepare to support console-defined matching; refactor the command line parameter string processing from parse_options() into a new core function, uart_parse_earlycon(), which decodes command line parameters of the form: earlycon=,io|mmio|mmio32,, console=,io|mmio|mmio32,, earlycon=,0x, console=,0x, Signed-off-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/earlycon.c | 39 ++++++++++++---------------------- drivers/tty/serial/serial_core.c | 46 ++++++++++++++++++++++++++++++++++++++++ include/linux/serial_core.h | 2 ++ 3 files changed, 61 insertions(+), 26 deletions(-) (limited to 'include') diff --git a/drivers/tty/serial/earlycon.c b/drivers/tty/serial/earlycon.c index 64fe25a4285c..58d6bcdaf31c 100644 --- a/drivers/tty/serial/earlycon.c +++ b/drivers/tty/serial/earlycon.c @@ -54,44 +54,31 @@ static void __iomem * __init earlycon_map(unsigned long paddr, size_t size) return base; } -static int __init parse_options(struct earlycon_device *device, - char *options) +static int __init parse_options(struct earlycon_device *device, char *options) { struct uart_port *port = &device->port; - int mmio, mmio32, length; + int length; unsigned long addr; - if (!options) - return -ENODEV; + if (uart_parse_earlycon(options, &port->iotype, &addr, &options)) + return -EINVAL; - mmio = !strncmp(options, "mmio,", 5); - mmio32 = !strncmp(options, "mmio32,", 7); - if (mmio || mmio32) { - port->iotype = (mmio ? UPIO_MEM : UPIO_MEM32); - options += mmio ? 5 : 7; - addr = simple_strtoul(options, NULL, 0); + switch (port->iotype) { + case UPIO_MEM32: + port->regshift = 2; /* fall-through */ + case UPIO_MEM: port->mapbase = addr; - if (mmio32) - port->regshift = 2; - } else if (!strncmp(options, "io,", 3)) { - port->iotype = UPIO_PORT; - options += 3; - addr = simple_strtoul(options, NULL, 0); + break; + case UPIO_PORT: port->iobase = addr; - mmio = 0; - } else if (!strncmp(options, "0x", 2)) { - port->iotype = UPIO_MEM; - addr = simple_strtoul(options, NULL, 0); - port->mapbase = addr; - } else { + break; + default: return -EINVAL; } port->uartclk = BASE_BAUD * 16; - options = strchr(options, ','); if (options) { - options++; device->baud = simple_strtoul(options, NULL, 0); length = min(strcspn(options, " ") + 1, (size_t)(sizeof(device->options))); @@ -100,7 +87,7 @@ static int __init parse_options(struct earlycon_device *device, if (port->iotype == UPIO_MEM || port->iotype == UPIO_MEM32) pr_info("Early serial console at MMIO%s 0x%llx (options '%s')\n", - mmio32 ? "32" : "", + (port->iotype == UPIO_MEM32) ? "32" : "", (unsigned long long)port->mapbase, device->options); else diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c index 63d29473c703..8379e3fa0162 100644 --- a/drivers/tty/serial/serial_core.c +++ b/drivers/tty/serial/serial_core.c @@ -1808,6 +1808,52 @@ uart_get_console(struct uart_port *ports, int nr, struct console *co) return ports + idx; } +/** + * uart_parse_earlycon - Parse earlycon options + * @p: ptr to 2nd field (ie., just beyond ',') + * @iotype: ptr for decoded iotype (out) + * @addr: ptr for decoded mapbase/iobase (out) + * @options: ptr for field; NULL if not present (out) + * + * Decodes earlycon kernel command line parameters of the form + * earlycon=,io|mmio|mmio32,, + * console=,io|mmio|mmio32,, + * + * The optional form + * earlycon=,0x, + * console=,0x, + * is also accepted; the returned @iotype will be UPIO_MEM. + * + * Returns 0 on success or -EINVAL on failure + */ +int uart_parse_earlycon(char *p, unsigned char *iotype, unsigned long *addr, + char **options) +{ + if (strncmp(p, "mmio,", 5) == 0) { + *iotype = UPIO_MEM; + p += 5; + } else if (strncmp(p, "mmio32,", 7) == 0) { + *iotype = UPIO_MEM32; + p += 7; + } else if (strncmp(p, "io,", 3) == 0) { + *iotype = UPIO_PORT; + p += 3; + } else if (strncmp(p, "0x", 2) == 0) { + *iotype = UPIO_MEM; + } else { + return -EINVAL; + } + + *addr = simple_strtoul(p, NULL, 0); + p = strchr(p, ','); + if (p) + p++; + + *options = p; + return 0; +} +EXPORT_SYMBOL_GPL(uart_parse_earlycon); + /** * uart_parse_options - Parse serial port baud/parity/bits/flow control. * @options: pointer to option string diff --git a/include/linux/serial_core.h b/include/linux/serial_core.h index baf3e1d08416..cc5c506f07dd 100644 --- a/include/linux/serial_core.h +++ b/include/linux/serial_core.h @@ -354,6 +354,8 @@ early_param("earlycon", name ## _setup_earlycon); struct uart_port *uart_get_console(struct uart_port *ports, int nr, struct console *c); +int uart_parse_earlycon(char *p, unsigned char *iotype, unsigned long *addr, + char **options); void uart_parse_options(char *options, int *baud, int *parity, int *bits, int *flow); int uart_set_options(struct uart_port *port, struct console *co, int baud, -- cgit v1.2.3 From f8e471f9eb9068bf5ac8c6a04da74329a442f75a Mon Sep 17 00:00:00 2001 From: Nicholas Bellinger Date: Fri, 6 Mar 2015 20:34:32 -0800 Subject: target: Add target_show_dynamic_sessions attribute helper This patch adds a new helper function that can be used by fabric driver TPG attributes for dumping the list of active sessions with a dynamically generated se_node_acl. (generate_node_acl=1). It prints one se_node_acl->initiatorname per line, up to PAGE_SIZE which is due to the current limitiation of single page attribute output within sysfs and configfs code. Note that if a session is referencing a explicit NodeACL, the InitiatorName will not appear within dynamic_sessions output. Reported-by: Andy Grover Signed-off-by: Nicholas Bellinger --- drivers/target/target_core_transport.c | 24 ++++++++++++++++++++++++ include/target/target_core_fabric.h | 1 + include/target/target_core_fabric_configfs.h | 5 +++++ 3 files changed, 30 insertions(+) (limited to 'include') diff --git a/drivers/target/target_core_transport.c b/drivers/target/target_core_transport.c index 0adc0f650213..e06c136ff839 100644 --- a/drivers/target/target_core_transport.c +++ b/drivers/target/target_core_transport.c @@ -404,6 +404,30 @@ void target_put_session(struct se_session *se_sess) } EXPORT_SYMBOL(target_put_session); +ssize_t target_show_dynamic_sessions(struct se_portal_group *se_tpg, char *page) +{ + struct se_session *se_sess; + ssize_t len = 0; + + spin_lock_bh(&se_tpg->session_lock); + list_for_each_entry(se_sess, &se_tpg->tpg_sess_list, sess_list) { + if (!se_sess->se_node_acl) + continue; + if (!se_sess->se_node_acl->dynamic_node_acl) + continue; + if (strlen(se_sess->se_node_acl->initiatorname) + 1 + len > PAGE_SIZE) + break; + + len += snprintf(page + len, PAGE_SIZE - len, "%s\n", + se_sess->se_node_acl->initiatorname); + len += 1; /* Include NULL terminator */ + } + spin_unlock_bh(&se_tpg->session_lock); + + return len; +} +EXPORT_SYMBOL(target_show_dynamic_sessions); + static void target_complete_nacl(struct kref *kref) { struct se_node_acl *nacl = container_of(kref, diff --git a/include/target/target_core_fabric.h b/include/target/target_core_fabric.h index 22a4e98eec80..2f4a2505db4c 100644 --- a/include/target/target_core_fabric.h +++ b/include/target/target_core_fabric.h @@ -95,6 +95,7 @@ void transport_register_session(struct se_portal_group *, struct se_node_acl *, struct se_session *, void *); void target_get_session(struct se_session *); void target_put_session(struct se_session *); +ssize_t target_show_dynamic_sessions(struct se_portal_group *, char *); void transport_free_session(struct se_session *); void target_put_nacl(struct se_node_acl *); void transport_deregister_session_configfs(struct se_session *); diff --git a/include/target/target_core_fabric_configfs.h b/include/target/target_core_fabric_configfs.h index b32a14905cfa..7a0649c09e79 100644 --- a/include/target/target_core_fabric_configfs.h +++ b/include/target/target_core_fabric_configfs.h @@ -90,6 +90,11 @@ static struct target_fabric_tpg_attribute _fabric##_tpg_##_name = \ _fabric##_tpg_store_##_name); +#define TF_TPG_BASE_ATTR_RO(_fabric, _name) \ +static struct target_fabric_tpg_attribute _fabric##_tpg_##_name = \ + __CONFIGFS_EATTR_RO(_name, \ + _fabric##_tpg_show_##_name); + CONFIGFS_EATTR_STRUCT(target_fabric_wwn, target_fabric_configfs); #define TF_WWN_ATTR(_fabric, _name, _mode) \ static struct target_fabric_wwn_attribute _fabric##_wwn_##_name = \ -- cgit v1.2.3 From b79bda3d38ae67940f1740f7e015f284eb551680 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Sat, 7 Mar 2015 16:25:56 -0600 Subject: neigh: Use neigh table index for neigh_packet_xmit Remove a little bit of unnecessary work when transmitting a packet with neigh_packet_xmit. Use the neighbour table index not the address family as a parameter. Signed-off-by: "Eric W. Biederman" Signed-off-by: David S. Miller --- include/net/neighbour.h | 1 + net/core/neighbour.c | 22 +++++++++++----------- net/mpls/af_mpls.c | 35 ++++++++++++++++++++++------------- 3 files changed, 34 insertions(+), 24 deletions(-) (limited to 'include') diff --git a/include/net/neighbour.h b/include/net/neighbour.h index afb8237b0a8c..d48b8ec8b5f4 100644 --- a/include/net/neighbour.h +++ b/include/net/neighbour.h @@ -226,6 +226,7 @@ enum { NEIGH_ND_TABLE = 1, NEIGH_DN_TABLE = 2, NEIGH_NR_TABLES, + NEIGH_LINK_TABLE = NEIGH_NR_TABLES /* Pseudo table for neigh_xmit */ }; static inline int neigh_parms_family(struct neigh_parms *p) diff --git a/net/core/neighbour.c b/net/core/neighbour.c index cffaf00561e7..ad07990e943d 100644 --- a/net/core/neighbour.c +++ b/net/core/neighbour.c @@ -2391,22 +2391,15 @@ void __neigh_for_each_release(struct neigh_table *tbl, } EXPORT_SYMBOL(__neigh_for_each_release); -int neigh_xmit(int family, struct net_device *dev, +int neigh_xmit(int index, struct net_device *dev, const void *addr, struct sk_buff *skb) { - int err; - if (family == AF_PACKET) { - err = dev_hard_header(skb, dev, ntohs(skb->protocol), - addr, NULL, skb->len); - if (err < 0) - goto out_kfree_skb; - err = dev_queue_xmit(skb); - } else { + int err = -EAFNOSUPPORT; + if (likely(index < NEIGH_NR_TABLES)) { struct neigh_table *tbl; struct neighbour *neigh; - err = -ENETDOWN; - tbl = neigh_find_table(family); + tbl = neigh_tables[index]; if (!tbl) goto out; neigh = __neigh_lookup_noref(tbl, addr, dev); @@ -2417,6 +2410,13 @@ int neigh_xmit(int family, struct net_device *dev, goto out_kfree_skb; err = neigh->output(neigh, skb); } + else if (index == NEIGH_LINK_TABLE) { + err = dev_hard_header(skb, dev, ntohs(skb->protocol), + addr, NULL, skb->len); + if (err < 0) + goto out_kfree_skb; + err = dev_queue_xmit(skb); + } out: return err; out_kfree_skb: diff --git a/net/mpls/af_mpls.c b/net/mpls/af_mpls.c index e120074157de..0ad8f7141be2 100644 --- a/net/mpls/af_mpls.c +++ b/net/mpls/af_mpls.c @@ -28,9 +28,9 @@ struct mpls_route { /* next hop label forwarding entry */ struct rcu_head rt_rcu; u32 rt_label[MAX_NEW_LABELS]; u8 rt_protocol; /* routing protocol that set this entry */ - u8 rt_labels:2, - rt_via_alen:6; - unsigned short rt_via_family; + u8 rt_labels; + u8 rt_via_alen; + u8 rt_via_table; u8 rt_via[0]; }; @@ -201,7 +201,7 @@ static int mpls_forward(struct sk_buff *skb, struct net_device *dev, } } - err = neigh_xmit(rt->rt_via_family, out_dev, rt->rt_via, skb); + err = neigh_xmit(rt->rt_via_table, out_dev, rt->rt_via, skb); if (err) net_dbg_ratelimited("%s: packet transmission failed: %d\n", __func__, err); @@ -225,7 +225,7 @@ static const struct nla_policy rtm_mpls_policy[RTA_MAX+1] = { struct mpls_route_config { u32 rc_protocol; u32 rc_ifindex; - u16 rc_via_family; + u16 rc_via_table; u16 rc_via_alen; u8 rc_via[MAX_VIA_ALEN]; u32 rc_label; @@ -343,7 +343,7 @@ static int mpls_route_add(struct mpls_route_config *cfg) goto errout; err = -EINVAL; - if ((cfg->rc_via_family == AF_PACKET) && + if ((cfg->rc_via_table == NEIGH_LINK_TABLE) && (dev->addr_len != cfg->rc_via_alen)) goto errout; @@ -376,7 +376,7 @@ static int mpls_route_add(struct mpls_route_config *cfg) rt->rt_label[i] = cfg->rc_output_label[i]; rt->rt_protocol = cfg->rc_protocol; RCU_INIT_POINTER(rt->rt_dev, dev); - rt->rt_via_family = cfg->rc_via_family; + rt->rt_via_table = cfg->rc_via_table; memcpy(rt->rt_via, cfg->rc_via, cfg->rc_via_alen); mpls_route_update(net, index, NULL, rt, &cfg->rc_nlinfo); @@ -448,15 +448,22 @@ static struct notifier_block mpls_dev_notifier = { }; static int nla_put_via(struct sk_buff *skb, - u16 family, const void *addr, int alen) + u8 table, const void *addr, int alen) { + static const int table_to_family[NEIGH_NR_TABLES + 1] = { + AF_INET, AF_INET6, AF_DECnet, AF_PACKET, + }; struct nlattr *nla; struct rtvia *via; + int family = AF_UNSPEC; nla = nla_reserve(skb, RTA_VIA, alen + 2); if (!nla) return -EMSGSIZE; + if (table <= NEIGH_NR_TABLES) + family = table_to_family[table]; + via = nla_data(nla); via->rtvia_family = family; memcpy(via->rtvia_addr, addr, alen); @@ -599,21 +606,23 @@ static int rtm_to_route_config(struct sk_buff *skb, struct nlmsghdr *nlh, struct rtvia *via = nla_data(nla); if (nla_len(nla) < offsetof(struct rtvia, rtvia_addr)) goto errout; - cfg->rc_via_family = via->rtvia_family; cfg->rc_via_alen = nla_len(nla) - offsetof(struct rtvia, rtvia_addr); if (cfg->rc_via_alen > MAX_VIA_ALEN) goto errout; /* Validate the address family */ - switch(cfg->rc_via_family) { + switch(via->rtvia_family) { case AF_PACKET: + cfg->rc_via_table = NEIGH_LINK_TABLE; break; case AF_INET: + cfg->rc_via_table = NEIGH_ARP_TABLE; if (cfg->rc_via_alen != 4) goto errout; break; case AF_INET6: + cfg->rc_via_table = NEIGH_ND_TABLE; if (cfg->rc_via_alen != 16) goto errout; break; @@ -686,7 +695,7 @@ static int mpls_dump_route(struct sk_buff *skb, u32 portid, u32 seq, int event, if (rt->rt_labels && nla_put_labels(skb, RTA_NEWDST, rt->rt_labels, rt->rt_label)) goto nla_put_failure; - if (nla_put_via(skb, rt->rt_via_family, rt->rt_via, rt->rt_via_alen)) + if (nla_put_via(skb, rt->rt_via_table, rt->rt_via, rt->rt_via_alen)) goto nla_put_failure; dev = rtnl_dereference(rt->rt_dev); if (dev && nla_put_u32(skb, RTA_OIF, dev->ifindex)) @@ -799,7 +808,7 @@ static int resize_platform_label_table(struct net *net, size_t limit) goto nort0; RCU_INIT_POINTER(rt0->rt_dev, lo); rt0->rt_protocol = RTPROT_KERNEL; - rt0->rt_via_family = AF_PACKET; + rt0->rt_via_table = NEIGH_LINK_TABLE; memcpy(rt0->rt_via, lo->dev_addr, lo->addr_len); } if (limit > LABEL_IPV6_EXPLICIT_NULL) { @@ -809,7 +818,7 @@ static int resize_platform_label_table(struct net *net, size_t limit) goto nort2; RCU_INIT_POINTER(rt2->rt_dev, lo); rt2->rt_protocol = RTPROT_KERNEL; - rt2->rt_via_family = AF_PACKET; + rt2->rt_via_table = NEIGH_LINK_TABLE; memcpy(rt2->rt_via, lo->dev_addr, lo->addr_len); } -- cgit v1.2.3 From cde001e4c3c3625c60b68a83eb1f1c2572dee07a Mon Sep 17 00:00:00 2001 From: Stephan Mueller Date: Fri, 6 Mar 2015 08:26:31 +0100 Subject: crypto: rng - RNGs must return 0 in success case Change the RNGs to always return 0 in success case. This patch ensures that seqiv.c works with RNGs other than krng. seqiv expects that any return code other than 0 is an error. Without the patch, rfc4106(gcm(aes)) will not work when using a DRBG or an ANSI X9.31 RNG. Signed-off-by: Stephan Mueller Signed-off-by: Herbert Xu --- crypto/ansi_cprng.c | 6 +++++- crypto/drbg.c | 7 ++++++- include/crypto/rng.h | 3 +-- 3 files changed, 12 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/crypto/ansi_cprng.c b/crypto/ansi_cprng.c index 6f5bebc9bf01..765fe7609348 100644 --- a/crypto/ansi_cprng.c +++ b/crypto/ansi_cprng.c @@ -210,7 +210,11 @@ static int get_prng_bytes(char *buf, size_t nbytes, struct prng_context *ctx, byte_count = DEFAULT_BLK_SZ; } - err = byte_count; + /* + * Return 0 in case of success as mandated by the kernel + * crypto API interface definition. + */ + err = 0; dbgprint(KERN_CRIT "getting %d random bytes for context %p\n", byte_count, ctx); diff --git a/crypto/drbg.c b/crypto/drbg.c index 56c1d7ec3d9e..b69409cb7e6a 100644 --- a/crypto/drbg.c +++ b/crypto/drbg.c @@ -1280,7 +1280,7 @@ static void drbg_restore_shadow(struct drbg_state *drbg, * as defined in SP800-90A. The additional input is mixed into * the state in addition to the pulled entropy. * - * return: generated number of bytes + * return: 0 when all bytes are generated; < 0 in case of an error */ static int drbg_generate(struct drbg_state *drbg, unsigned char *buf, unsigned int buflen, @@ -1419,6 +1419,11 @@ static int drbg_generate(struct drbg_state *drbg, } #endif + /* + * All operations were successful, return 0 as mandated by + * the kernel crypto API interface. + */ + len = 0; err: shadow->d_ops->crypto_fini(shadow); drbg_restore_shadow(drbg, &shadow); diff --git a/include/crypto/rng.h b/include/crypto/rng.h index a16fb10142bf..6e28ea5be9f1 100644 --- a/include/crypto/rng.h +++ b/include/crypto/rng.h @@ -103,8 +103,7 @@ static inline void crypto_free_rng(struct crypto_rng *tfm) * This function fills the caller-allocated buffer with random numbers using the * random number generator referenced by the cipher handle. * - * Return: > 0 function was successful and returns the number of generated - * bytes; < 0 if an error occurred + * Return: 0 function was successful; < 0 if an error occurred */ static inline int crypto_rng_get_bytes(struct crypto_rng *tfm, u8 *rdata, unsigned int dlen) -- cgit v1.2.3 From 8582e267e9a29ccfd2151c5d74bc2b1440dfb827 Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Delgado Date: Wed, 11 Feb 2015 13:53:14 +0100 Subject: asm/dma-mapping-common: Clarify output of dma_map_sg_attrs Although dma_map_sg_attrs returns 0 on error and it cannot return a value < 0, the function returns a signed integer. Most of the time, this function is used with a scatterlist structure. This structure uses an unsigned integer for the number of memory. A dma developer that has not read in detail DMA-API.txt, can wrongly return a value < 0 on error. The comment will help the driver developer, and the WARN_ON the dma developer. Signed-off-by: Ricardo Ribalda Delgado Signed-off-by: Marek Szyprowski --- include/asm-generic/dma-mapping-common.h | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'include') diff --git a/include/asm-generic/dma-mapping-common.h b/include/asm-generic/dma-mapping-common.h index 3378dcf4c31e..940d5ec122c9 100644 --- a/include/asm-generic/dma-mapping-common.h +++ b/include/asm-generic/dma-mapping-common.h @@ -39,6 +39,10 @@ static inline void dma_unmap_single_attrs(struct device *dev, dma_addr_t addr, debug_dma_unmap_page(dev, addr, size, dir, true); } +/* + * dma_maps_sg_attrs returns 0 on error and > 0 on success. + * It should never return a value < 0. + */ static inline int dma_map_sg_attrs(struct device *dev, struct scatterlist *sg, int nents, enum dma_data_direction dir, struct dma_attrs *attrs) @@ -51,6 +55,7 @@ static inline int dma_map_sg_attrs(struct device *dev, struct scatterlist *sg, kmemcheck_mark_initialized(sg_virt(s), s->length); BUG_ON(!valid_dma_direction(dir)); ents = ops->map_sg(dev, sg, nents, dir, attrs); + BUG_ON(ents < 0); debug_dma_map_sg(dev, sg, nents, ents, dir); return ents; -- cgit v1.2.3 From 04abab698285297115e5096b3100df1064045529 Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Delgado Date: Wed, 11 Feb 2015 13:53:15 +0100 Subject: include/dma-mapping: Clarify output of dma_map_sg Although dma_map_sg returns 0 on error and it cannot return a value < 0, the function returns a signed integer. Most of the time, this function is used with a scatterlist structure. This structure uses an unsigned integer for the number of memory. A dma developer that has not read in detail DMA-API.txt, can wrongly return a value < 0 on error. Signed-off-by: Ricardo Ribalda Delgado Signed-off-by: Marek Szyprowski --- include/linux/dma-mapping.h | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'include') diff --git a/include/linux/dma-mapping.h b/include/linux/dma-mapping.h index c3007cb4bfa6..ac07ff090919 100644 --- a/include/linux/dma-mapping.h +++ b/include/linux/dma-mapping.h @@ -34,6 +34,10 @@ struct dma_map_ops { void (*unmap_page)(struct device *dev, dma_addr_t dma_handle, size_t size, enum dma_data_direction dir, struct dma_attrs *attrs); + /* + * map_sg returns 0 on error and a value > 0 on success. + * It should never return a value < 0. + */ int (*map_sg)(struct device *dev, struct scatterlist *sg, int nents, enum dma_data_direction dir, struct dma_attrs *attrs); -- cgit v1.2.3 From 8bd63cf1a426e69bf4f611b08978f721e46c194f Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Thu, 5 Mar 2015 00:52:33 +0100 Subject: bridge: move mac header copying into br_netfilter The mac header only has to be copied back into the skb for fragments generated by ip_fragment(), which only happens for bridge forwarded packets with nf-call-iptables=1 && active nf_defrag. Signed-off-by: Florian Westphal Signed-off-by: Pablo Neira Ayuso --- include/linux/netfilter_bridge.h | 31 ------------------------------- net/bridge/br_forward.c | 4 +--- net/bridge/br_netfilter.c | 29 ++++++++++++++++++++++++++++- 3 files changed, 29 insertions(+), 35 deletions(-) (limited to 'include') diff --git a/include/linux/netfilter_bridge.h b/include/linux/netfilter_bridge.h index c755e4971fa3..332ef8ab37e9 100644 --- a/include/linux/netfilter_bridge.h +++ b/include/linux/netfilter_bridge.h @@ -44,36 +44,6 @@ static inline void nf_bridge_update_protocol(struct sk_buff *skb) skb->protocol = htons(ETH_P_PPP_SES); } -/* Fill in the header for fragmented IP packets handled by - * the IPv4 connection tracking code. - * - * Only used in br_forward.c - */ -static inline int nf_bridge_copy_header(struct sk_buff *skb) -{ - int err; - unsigned int header_size; - - nf_bridge_update_protocol(skb); - header_size = ETH_HLEN + nf_bridge_encap_header_len(skb); - err = skb_cow_head(skb, header_size); - if (err) - return err; - - skb_copy_to_linear_data_offset(skb, -header_size, - skb->nf_bridge->data, header_size); - __skb_push(skb, nf_bridge_encap_header_len(skb)); - return 0; -} - -static inline int nf_bridge_maybe_copy_header(struct sk_buff *skb) -{ - if (skb->nf_bridge && - skb->nf_bridge->mask & (BRNF_BRIDGED | BRNF_BRIDGED_DNAT)) - return nf_bridge_copy_header(skb); - return 0; -} - static inline unsigned int nf_bridge_mtu_reduction(const struct sk_buff *skb) { if (unlikely(skb->nf_bridge->mask & BRNF_PPPoE)) @@ -119,7 +89,6 @@ static inline void br_drop_fake_rtable(struct sk_buff *skb) } #else -#define nf_bridge_maybe_copy_header(skb) (0) #define nf_bridge_pad(skb) (0) #define br_drop_fake_rtable(skb) do { } while (0) #endif /* CONFIG_BRIDGE_NETFILTER */ diff --git a/net/bridge/br_forward.c b/net/bridge/br_forward.c index f96933a823e3..32541d4f72e8 100644 --- a/net/bridge/br_forward.c +++ b/net/bridge/br_forward.c @@ -37,9 +37,7 @@ static inline int should_deliver(const struct net_bridge_port *p, int br_dev_queue_push_xmit(struct sk_buff *skb) { - /* ip_fragment doesn't copy the MAC header */ - if (nf_bridge_maybe_copy_header(skb) || - !is_skb_forwardable(skb->dev, skb)) { + if (!is_skb_forwardable(skb->dev, skb)) { kfree_skb(skb); } else { skb_push(skb, ETH_HLEN); diff --git a/net/bridge/br_netfilter.c b/net/bridge/br_netfilter.c index 0ee453fad3de..e5479112c4a3 100644 --- a/net/bridge/br_netfilter.c +++ b/net/bridge/br_netfilter.c @@ -764,6 +764,33 @@ static unsigned int br_nf_forward_arp(const struct nf_hook_ops *ops, } #if IS_ENABLED(CONFIG_NF_DEFRAG_IPV4) +static bool nf_bridge_copy_header(struct sk_buff *skb) +{ + int err; + unsigned int header_size; + + nf_bridge_update_protocol(skb); + header_size = ETH_HLEN + nf_bridge_encap_header_len(skb); + err = skb_cow_head(skb, header_size); + if (err) + return false; + + skb_copy_to_linear_data_offset(skb, -header_size, + skb->nf_bridge->data, header_size); + __skb_push(skb, nf_bridge_encap_header_len(skb)); + return true; +} + +static int br_nf_push_frag_xmit(struct sk_buff *skb) +{ + if (!nf_bridge_copy_header(skb)) { + kfree_skb(skb); + return 0; + } + + return br_dev_queue_push_xmit(skb); +} + static int br_nf_dev_queue_xmit(struct sk_buff *skb) { int ret; @@ -780,7 +807,7 @@ static int br_nf_dev_queue_xmit(struct sk_buff *skb) /* Drop invalid packet */ return NF_DROP; IPCB(skb)->frag_max_size = frag_max_size; - ret = ip_fragment(skb, br_dev_queue_push_xmit); + ret = ip_fragment(skb, br_nf_push_frag_xmit); } else ret = br_dev_queue_push_xmit(skb); -- cgit v1.2.3 From 4a9d2f200862683d6680d5565f30c126625afe65 Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Thu, 5 Mar 2015 00:52:34 +0100 Subject: netfilter: bridge: move nf_bridge_update_protocol to where its used no need to keep it in a header file. Signed-off-by: Florian Westphal Signed-off-by: Pablo Neira Ayuso --- include/linux/netfilter_bridge.h | 8 -------- net/bridge/br_netfilter.c | 8 ++++++++ 2 files changed, 8 insertions(+), 8 deletions(-) (limited to 'include') diff --git a/include/linux/netfilter_bridge.h b/include/linux/netfilter_bridge.h index 332ef8ab37e9..dd580a9a1add 100644 --- a/include/linux/netfilter_bridge.h +++ b/include/linux/netfilter_bridge.h @@ -36,14 +36,6 @@ static inline unsigned int nf_bridge_encap_header_len(const struct sk_buff *skb) } } -static inline void nf_bridge_update_protocol(struct sk_buff *skb) -{ - if (skb->nf_bridge->mask & BRNF_8021Q) - skb->protocol = htons(ETH_P_8021Q); - else if (skb->nf_bridge->mask & BRNF_PPPoE) - skb->protocol = htons(ETH_P_PPP_SES); -} - static inline unsigned int nf_bridge_mtu_reduction(const struct sk_buff *skb) { if (unlikely(skb->nf_bridge->mask & BRNF_PPPoE)) diff --git a/net/bridge/br_netfilter.c b/net/bridge/br_netfilter.c index e5479112c4a3..5b3bceb3ee62 100644 --- a/net/bridge/br_netfilter.c +++ b/net/bridge/br_netfilter.c @@ -239,6 +239,14 @@ drop: return -1; } +static void nf_bridge_update_protocol(struct sk_buff *skb) +{ + if (skb->nf_bridge->mask & BRNF_8021Q) + skb->protocol = htons(ETH_P_8021Q); + else if (skb->nf_bridge->mask & BRNF_PPPoE) + skb->protocol = htons(ETH_P_PPP_SES); +} + /* PF_BRIDGE/PRE_ROUTING *********************************************/ /* Undo the changes made for ip6tables PREROUTING and continue the * bridge PRE_ROUTING hook. */ -- cgit v1.2.3 From c467ea763fd5d8795b7d1b5a78eb94b6ad8f66ad Mon Sep 17 00:00:00 2001 From: Frederic Weisbecker Date: Wed, 4 Mar 2015 18:06:33 +0100 Subject: context_tracking: Rename context symbols to prepare for transition state Current context tracking symbols are designed to express living state. As such they are prefixed with "IN_": IN_USER, IN_KERNEL. Now we are going to use these symbols to also express state transitions such as context_tracking_enter(IN_USER) or context_tracking_exit(IN_USER). But while the "IN_" prefix works well to express entering a context, it's confusing to depict a context exit: context_tracking_exit(IN_USER) could mean two things: 1) We are exiting the current context to enter user context. 2) We are exiting the user context We want 2) but the reviewer may be confused and understand 1) So lets disambiguate these symbols and rename them to CONTEXT_USER and CONTEXT_KERNEL. Acked-by: Rik van Riel Cc: Paul E. McKenney Cc: Andy Lutomirski Cc: Will deacon Cc: Marcelo Tosatti Cc: Christian Borntraeger Cc: Luiz Capitulino Cc: Paolo Bonzini Signed-off-by: Frederic Weisbecker --- arch/x86/kernel/traps.c | 2 +- include/linux/context_tracking.h | 2 +- include/linux/context_tracking_state.h | 6 +++--- kernel/context_tracking.c | 8 ++++---- kernel/sched/core.c | 2 +- 5 files changed, 10 insertions(+), 10 deletions(-) (limited to 'include') diff --git a/arch/x86/kernel/traps.c b/arch/x86/kernel/traps.c index 9d2073e2ecc9..756f74eed35d 100644 --- a/arch/x86/kernel/traps.c +++ b/arch/x86/kernel/traps.c @@ -123,7 +123,7 @@ enum ctx_state ist_enter(struct pt_regs *regs) * but we need to notify RCU. */ rcu_nmi_enter(); - prev_state = IN_KERNEL; /* the value is irrelevant. */ + prev_state = CONTEXT_KERNEL; /* the value is irrelevant. */ } /* diff --git a/include/linux/context_tracking.h b/include/linux/context_tracking.h index 37b81bd51ec0..427b056dfd3d 100644 --- a/include/linux/context_tracking.h +++ b/include/linux/context_tracking.h @@ -43,7 +43,7 @@ static inline enum ctx_state exception_enter(void) static inline void exception_exit(enum ctx_state prev_ctx) { if (context_tracking_is_enabled()) { - if (prev_ctx == IN_USER) + if (prev_ctx == CONTEXT_USER) context_tracking_user_enter(); } } diff --git a/include/linux/context_tracking_state.h b/include/linux/context_tracking_state.h index 97a81225d037..8076f875c324 100644 --- a/include/linux/context_tracking_state.h +++ b/include/linux/context_tracking_state.h @@ -13,8 +13,8 @@ struct context_tracking { */ bool active; enum ctx_state { - IN_KERNEL = 0, - IN_USER, + CONTEXT_KERNEL = 0, + CONTEXT_USER, } state; }; @@ -34,7 +34,7 @@ static inline bool context_tracking_cpu_is_enabled(void) static inline bool context_tracking_in_user(void) { - return __this_cpu_read(context_tracking.state) == IN_USER; + return __this_cpu_read(context_tracking.state) == CONTEXT_USER; } #else static inline bool context_tracking_in_user(void) { return false; } diff --git a/kernel/context_tracking.c b/kernel/context_tracking.c index 937ecdfdf258..8ad53c9d38b6 100644 --- a/kernel/context_tracking.c +++ b/kernel/context_tracking.c @@ -75,7 +75,7 @@ void context_tracking_user_enter(void) WARN_ON_ONCE(!current->mm); local_irq_save(flags); - if ( __this_cpu_read(context_tracking.state) != IN_USER) { + if ( __this_cpu_read(context_tracking.state) != CONTEXT_USER) { if (__this_cpu_read(context_tracking.active)) { trace_user_enter(0); /* @@ -101,7 +101,7 @@ void context_tracking_user_enter(void) * OTOH we can spare the calls to vtime and RCU when context_tracking.active * is false because we know that CPU is not tickless. */ - __this_cpu_write(context_tracking.state, IN_USER); + __this_cpu_write(context_tracking.state, CONTEXT_USER); } local_irq_restore(flags); } @@ -129,7 +129,7 @@ void context_tracking_user_exit(void) return; local_irq_save(flags); - if (__this_cpu_read(context_tracking.state) == IN_USER) { + if (__this_cpu_read(context_tracking.state) == CONTEXT_USER) { if (__this_cpu_read(context_tracking.active)) { /* * We are going to run code that may use RCU. Inform @@ -139,7 +139,7 @@ void context_tracking_user_exit(void) vtime_user_exit(current); trace_user_exit(0); } - __this_cpu_write(context_tracking.state, IN_KERNEL); + __this_cpu_write(context_tracking.state, CONTEXT_KERNEL); } local_irq_restore(flags); } diff --git a/kernel/sched/core.c b/kernel/sched/core.c index f0f831e8a345..06b9a00871e0 100644 --- a/kernel/sched/core.c +++ b/kernel/sched/core.c @@ -2818,7 +2818,7 @@ asmlinkage __visible void __sched schedule_user(void) * we find a better solution. * * NB: There are buggy callers of this function. Ideally we - * should warn if prev_state != IN_USER, but that will trigger + * should warn if prev_state != CONTEXT_USER, but that will trigger * too frequently to make sense yet. */ enum ctx_state prev_state = exception_enter(); -- cgit v1.2.3 From 3aab4f50bff89bdea5066a05d4f3c5fa25bc37c7 Mon Sep 17 00:00:00 2001 From: Rik van Riel Date: Tue, 10 Feb 2015 15:27:50 -0500 Subject: context_tracking: Generalize context tracking APIs to support user and guest Generalize the context tracking APIs to support various nature of contexts. This is performed by splitting out the mechanism from context_tracking_user_enter and context_tracking_user_exit into context_tracking_enter and context_tracking_exit. The nature of the context we track is now detailed in a ctx_state parameter pushed to these APIs, allowing the same functions to not just track kernel <> user space switching, but also kernel <> guest transitions. But leave the old functions in order to avoid breaking ARM, which calls these functions from assembler code, and cannot easily use C enum parameters. Reviewed-by: Paul E. McKenney Signed-off-by: Rik van Riel Cc: Paul E. McKenney Cc: Andy Lutomirski Cc: Will deacon Cc: Marcelo Tosatti Cc: Christian Borntraeger Cc: Luiz Capitulino Cc: Paolo Bonzini Signed-off-by: Frederic Weisbecker --- include/linux/context_tracking.h | 9 ++++++--- kernel/context_tracking.c | 43 ++++++++++++++++++++++++++-------------- 2 files changed, 34 insertions(+), 18 deletions(-) (limited to 'include') diff --git a/include/linux/context_tracking.h b/include/linux/context_tracking.h index 427b056dfd3d..7f1810a3b5a4 100644 --- a/include/linux/context_tracking.h +++ b/include/linux/context_tracking.h @@ -10,6 +10,8 @@ #ifdef CONFIG_CONTEXT_TRACKING extern void context_tracking_cpu_set(int cpu); +extern void context_tracking_enter(enum ctx_state state); +extern void context_tracking_exit(enum ctx_state state); extern void context_tracking_user_enter(void); extern void context_tracking_user_exit(void); extern void __context_tracking_task_switch(struct task_struct *prev, @@ -35,7 +37,8 @@ static inline enum ctx_state exception_enter(void) return 0; prev_ctx = this_cpu_read(context_tracking.state); - context_tracking_user_exit(); + if (prev_ctx != CONTEXT_KERNEL) + context_tracking_exit(prev_ctx); return prev_ctx; } @@ -43,8 +46,8 @@ static inline enum ctx_state exception_enter(void) static inline void exception_exit(enum ctx_state prev_ctx) { if (context_tracking_is_enabled()) { - if (prev_ctx == CONTEXT_USER) - context_tracking_user_enter(); + if (prev_ctx != CONTEXT_KERNEL) + context_tracking_enter(prev_ctx); } } diff --git a/kernel/context_tracking.c b/kernel/context_tracking.c index 8ad53c9d38b6..17715d811b71 100644 --- a/kernel/context_tracking.c +++ b/kernel/context_tracking.c @@ -39,15 +39,15 @@ void context_tracking_cpu_set(int cpu) } /** - * context_tracking_user_enter - Inform the context tracking that the CPU is going to - * enter userspace mode. + * context_tracking_enter - Inform the context tracking that the CPU is going + * enter user or guest space mode. * * This function must be called right before we switch from the kernel - * to userspace, when it's guaranteed the remaining kernel instructions - * to execute won't use any RCU read side critical section because this - * function sets RCU in extended quiescent state. + * to user or guest space, when it's guaranteed the remaining kernel + * instructions to execute won't use any RCU read side critical section + * because this function sets RCU in extended quiescent state. */ -void context_tracking_user_enter(void) +void context_tracking_enter(enum ctx_state state) { unsigned long flags; @@ -75,7 +75,7 @@ void context_tracking_user_enter(void) WARN_ON_ONCE(!current->mm); local_irq_save(flags); - if ( __this_cpu_read(context_tracking.state) != CONTEXT_USER) { + if ( __this_cpu_read(context_tracking.state) != state) { if (__this_cpu_read(context_tracking.active)) { trace_user_enter(0); /* @@ -101,24 +101,31 @@ void context_tracking_user_enter(void) * OTOH we can spare the calls to vtime and RCU when context_tracking.active * is false because we know that CPU is not tickless. */ - __this_cpu_write(context_tracking.state, CONTEXT_USER); + __this_cpu_write(context_tracking.state, state); } local_irq_restore(flags); } +NOKPROBE_SYMBOL(context_tracking_enter); + +void context_tracking_user_enter(void) +{ + context_tracking_enter(CONTEXT_USER); +} NOKPROBE_SYMBOL(context_tracking_user_enter); /** - * context_tracking_user_exit - Inform the context tracking that the CPU is - * exiting userspace mode and entering the kernel. + * context_tracking_exit - Inform the context tracking that the CPU is + * exiting user or guest mode and entering the kernel. * - * This function must be called after we entered the kernel from userspace - * before any use of RCU read side critical section. This potentially include - * any high level kernel code like syscalls, exceptions, signal handling, etc... + * This function must be called after we entered the kernel from user or + * guest space before any use of RCU read side critical section. This + * potentially include any high level kernel code like syscalls, exceptions, + * signal handling, etc... * * This call supports re-entrancy. This way it can be called from any exception * handler without needing to know if we came from userspace or not. */ -void context_tracking_user_exit(void) +void context_tracking_exit(enum ctx_state state) { unsigned long flags; @@ -129,7 +136,7 @@ void context_tracking_user_exit(void) return; local_irq_save(flags); - if (__this_cpu_read(context_tracking.state) == CONTEXT_USER) { + if (__this_cpu_read(context_tracking.state) == state) { if (__this_cpu_read(context_tracking.active)) { /* * We are going to run code that may use RCU. Inform @@ -143,6 +150,12 @@ void context_tracking_user_exit(void) } local_irq_restore(flags); } +NOKPROBE_SYMBOL(context_tracking_exit); + +void context_tracking_user_exit(void) +{ + context_tracking_exit(CONTEXT_USER); +} NOKPROBE_SYMBOL(context_tracking_user_exit); /** -- cgit v1.2.3 From a3a9c7dff25791f3a1041aa96e770af662d99a35 Mon Sep 17 00:00:00 2001 From: Rik van Riel Date: Tue, 10 Feb 2015 15:27:51 -0500 Subject: context_tracking: Add stub context_tracking_is_enabled With code elsewhere doing something conditional on whether or not context tracking is enabled, we want a stub function that tells us context tracking is not enabled, when CONFIG_CONTEXT_TRACKING is not set. Reviewed-by: Paul E. McKenney Signed-off-by: Rik van Riel Cc: Paul E. McKenney Cc: Andy Lutomirski Cc: Will deacon Cc: Marcelo Tosatti Cc: Christian Borntraeger Cc: Luiz Capitulino Cc: Paolo Bonzini Signed-off-by: Frederic Weisbecker --- include/linux/context_tracking_state.h | 2 ++ 1 file changed, 2 insertions(+) (limited to 'include') diff --git a/include/linux/context_tracking_state.h b/include/linux/context_tracking_state.h index 8076f875c324..ad4458588b47 100644 --- a/include/linux/context_tracking_state.h +++ b/include/linux/context_tracking_state.h @@ -39,6 +39,8 @@ static inline bool context_tracking_in_user(void) #else static inline bool context_tracking_in_user(void) { return false; } static inline bool context_tracking_active(void) { return false; } +static inline bool context_tracking_is_enabled(void) { return false; } +static inline bool context_tracking_cpu_is_enabled(void) { return false; } #endif /* CONFIG_CONTEXT_TRACKING */ #endif -- cgit v1.2.3 From 126a6a542446f1a49b9f3c69237c87df3eb4e6e1 Mon Sep 17 00:00:00 2001 From: Rik van Riel Date: Tue, 10 Feb 2015 15:27:54 -0500 Subject: kvm,rcu,nohz: use RCU extended quiescent state when running KVM guest The host kernel is not doing anything while the CPU is executing a KVM guest VCPU, so it can be marked as being in an extended quiescent state, identical to that used when running user space code. The only exception to that rule is when the host handles an interrupt, which is already handled by the irq code, which calls rcu_irq_enter and rcu_irq_exit. The guest_enter and guest_exit functions already switch vtime accounting independent of context tracking. Leave those calls where they are, instead of moving them into the context tracking code. Reviewed-by: Paul E. McKenney Signed-off-by: Rik van Riel Cc: Paul E. McKenney Cc: Andy Lutomirski Cc: Will deacon Cc: Marcelo Tosatti Cc: Christian Borntraeger Cc: Luiz Capitulino Cc: Paolo Bonzini Signed-off-by: Frederic Weisbecker --- include/linux/context_tracking.h | 6 ++++++ include/linux/context_tracking_state.h | 1 + include/linux/kvm_host.h | 3 ++- 3 files changed, 9 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/context_tracking.h b/include/linux/context_tracking.h index 7f1810a3b5a4..2821838256b4 100644 --- a/include/linux/context_tracking.h +++ b/include/linux/context_tracking.h @@ -81,10 +81,16 @@ static inline void guest_enter(void) vtime_guest_enter(current); else current->flags |= PF_VCPU; + + if (context_tracking_is_enabled()) + context_tracking_enter(CONTEXT_GUEST); } static inline void guest_exit(void) { + if (context_tracking_is_enabled()) + context_tracking_exit(CONTEXT_GUEST); + if (vtime_accounting_enabled()) vtime_guest_exit(current); else diff --git a/include/linux/context_tracking_state.h b/include/linux/context_tracking_state.h index ad4458588b47..6b7b96a32b75 100644 --- a/include/linux/context_tracking_state.h +++ b/include/linux/context_tracking_state.h @@ -15,6 +15,7 @@ struct context_tracking { enum ctx_state { CONTEXT_KERNEL = 0, CONTEXT_USER, + CONTEXT_GUEST, } state; }; diff --git a/include/linux/kvm_host.h b/include/linux/kvm_host.h index d12b2104d19b..cc8c61c5459c 100644 --- a/include/linux/kvm_host.h +++ b/include/linux/kvm_host.h @@ -766,7 +766,8 @@ static inline void kvm_guest_enter(void) * one time slice). Lets treat guest mode as quiescent state, just like * we do with user-mode execution. */ - rcu_virt_note_context_switch(smp_processor_id()); + if (!context_tracking_cpu_is_enabled()) + rcu_virt_note_context_switch(smp_processor_id()); } static inline void kvm_guest_exit(void) -- cgit v1.2.3 From e5de75bf88858f5b3ab11e2504b86ec059f03102 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Mon, 9 Mar 2015 12:30:12 +0100 Subject: netfilter: bridge: move DNAT helper to br_netfilter Only one caller, there is no need to keep this in a header. Move it to br_netfilter.c where this belongs to. Based on patch from Florian Westphal. Signed-off-by: Pablo Neira Ayuso --- include/linux/netfilter_bridge.h | 12 ------------ net/bridge/br_device.c | 5 +---- net/bridge/br_netfilter.c | 32 ++++++++++++++++++++++++++++++++ net/bridge/br_private.h | 5 +++++ 4 files changed, 38 insertions(+), 16 deletions(-) (limited to 'include') diff --git a/include/linux/netfilter_bridge.h b/include/linux/netfilter_bridge.h index dd580a9a1add..bb39113ea596 100644 --- a/include/linux/netfilter_bridge.h +++ b/include/linux/netfilter_bridge.h @@ -44,18 +44,6 @@ static inline unsigned int nf_bridge_mtu_reduction(const struct sk_buff *skb) } int br_handle_frame_finish(struct sk_buff *skb); -/* Only used in br_device.c */ -static inline int br_nf_pre_routing_finish_bridge_slow(struct sk_buff *skb) -{ - struct nf_bridge_info *nf_bridge = skb->nf_bridge; - - skb_pull(skb, ETH_HLEN); - nf_bridge->mask ^= BRNF_BRIDGED_DNAT; - skb_copy_to_linear_data_offset(skb, -(ETH_HLEN-ETH_ALEN), - skb->nf_bridge->data, ETH_HLEN-ETH_ALEN); - skb->dev = nf_bridge->physindev; - return br_handle_frame_finish(skb); -} /* This is called by the IP fragmenting code and it ensures there is * enough room for the encapsulating header (if there is one). */ diff --git a/net/bridge/br_device.c b/net/bridge/br_device.c index ffd379db5938..294cbcc49263 100644 --- a/net/bridge/br_device.c +++ b/net/bridge/br_device.c @@ -36,13 +36,10 @@ netdev_tx_t br_dev_xmit(struct sk_buff *skb, struct net_device *dev) u16 vid = 0; rcu_read_lock(); -#if IS_ENABLED(CONFIG_BRIDGE_NETFILTER) - if (skb->nf_bridge && (skb->nf_bridge->mask & BRNF_BRIDGED_DNAT)) { - br_nf_pre_routing_finish_bridge_slow(skb); + if (br_nf_prerouting_finish_bridge(skb)) { rcu_read_unlock(); return NETDEV_TX_OK; } -#endif u64_stats_update_begin(&brstats->syncp); brstats->tx_packets++; diff --git a/net/bridge/br_netfilter.c b/net/bridge/br_netfilter.c index ef1fe281ca11..a8361c7cdf81 100644 --- a/net/bridge/br_netfilter.c +++ b/net/bridge/br_netfilter.c @@ -892,6 +892,38 @@ static unsigned int ip_sabotage_in(const struct nf_hook_ops *ops, return NF_ACCEPT; } +/* This is called when br_netfilter has called into iptables/netfilter, + * and DNAT has taken place on a bridge-forwarded packet. + * + * neigh->output has created a new MAC header, with local br0 MAC + * as saddr. + * + * This restores the original MAC saddr of the bridged packet + * before invoking bridge forward logic to transmit the packet. + */ +static void br_nf_pre_routing_finish_bridge_slow(struct sk_buff *skb) +{ + struct nf_bridge_info *nf_bridge = skb->nf_bridge; + + skb_pull(skb, ETH_HLEN); + nf_bridge->mask &= ~BRNF_BRIDGED_DNAT; + + skb_copy_to_linear_data_offset(skb, -(ETH_HLEN-ETH_ALEN), + skb->nf_bridge->data, ETH_HLEN-ETH_ALEN); + skb->dev = nf_bridge->physindev; + br_handle_frame_finish(skb); +} + +int br_nf_prerouting_finish_bridge(struct sk_buff *skb) +{ + if (skb->nf_bridge && (skb->nf_bridge->mask & BRNF_BRIDGED_DNAT)) { + br_nf_pre_routing_finish_bridge_slow(skb); + return 1; + } + return 0; +} +EXPORT_SYMBOL_GPL(br_nf_prerouting_finish_bridge); + void br_netfilter_enable(void) { } diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h index de0919975a25..d63fc17fe4f4 100644 --- a/net/bridge/br_private.h +++ b/net/bridge/br_private.h @@ -764,10 +764,15 @@ static inline int br_vlan_enabled(struct net_bridge *br) /* br_netfilter.c */ #if IS_ENABLED(CONFIG_BRIDGE_NETFILTER) +int br_nf_prerouting_finish_bridge(struct sk_buff *skb); int br_nf_core_init(void); void br_nf_core_fini(void); void br_netfilter_rtable_init(struct net_bridge *); #else +static inline int br_nf_prerouting_finish_bridge(struct sk_buff *skb) +{ + return 0; +} static inline int br_nf_core_init(void) { return 0; } static inline void br_nf_core_fini(void) {} #define br_netfilter_rtable_init(x) -- cgit v1.2.3 From 1e052be69d045c8d0f82ff1116fd3e5a79661745 Mon Sep 17 00:00:00 2001 From: Cong Wang Date: Fri, 6 Mar 2015 11:47:59 -0800 Subject: net_sched: destroy proto tp when all filters are gone Kernel automatically creates a tp for each (kind, protocol, priority) tuple, which has handle 0, when we add a new filter, but it still is left there after we remove our own, unless we don't specify the handle (literally means all the filters under the tuple). For example this one is left: # tc filter show dev eth0 filter parent 8001: protocol arp pref 49152 basic The user-space is hard to clean up these for kernel because filters like u32 are organized in a complex way. So kernel is responsible to remove it after all filters are gone. Each type of filter has its own way to store the filters, so each type has to provide its way to check if all filters are gone. Cc: Jamal Hadi Salim Signed-off-by: Cong Wang Signed-off-by: Cong Wang Acked-by: Jamal Hadi Salim Signed-off-by: David S. Miller --- include/net/sch_generic.h | 4 ++-- net/sched/cls_api.c | 14 ++++++++++---- net/sched/cls_basic.c | 6 +++++- net/sched/cls_bpf.c | 6 +++++- net/sched/cls_cgroup.c | 6 +++++- net/sched/cls_flow.c | 6 +++++- net/sched/cls_fw.c | 11 +++++++++-- net/sched/cls_route.c | 12 ++++++++++-- net/sched/cls_rsvp.h | 12 ++++++++++-- net/sched/cls_tcindex.c | 6 +++++- net/sched/cls_u32.c | 25 ++++++++++++++++++++++++- net/sched/sch_api.c | 14 +++++++++----- 12 files changed, 99 insertions(+), 23 deletions(-) (limited to 'include') diff --git a/include/net/sch_generic.h b/include/net/sch_generic.h index c605d305c577..6d778efcfdfd 100644 --- a/include/net/sch_generic.h +++ b/include/net/sch_generic.h @@ -213,7 +213,7 @@ struct tcf_proto_ops { const struct tcf_proto *, struct tcf_result *); int (*init)(struct tcf_proto*); - void (*destroy)(struct tcf_proto*); + bool (*destroy)(struct tcf_proto*, bool); unsigned long (*get)(struct tcf_proto*, u32 handle); int (*change)(struct net *net, struct sk_buff *, @@ -399,7 +399,7 @@ struct Qdisc *qdisc_create_dflt(struct netdev_queue *dev_queue, const struct Qdisc_ops *ops, u32 parentid); void __qdisc_calculate_pkt_len(struct sk_buff *skb, const struct qdisc_size_table *stab); -void tcf_destroy(struct tcf_proto *tp); +bool tcf_destroy(struct tcf_proto *tp, bool force); void tcf_destroy_chain(struct tcf_proto __rcu **fl); /* Reset all TX qdiscs greater then index of a device. */ diff --git a/net/sched/cls_api.c b/net/sched/cls_api.c index baef987fe2c0..8b0470e418dc 100644 --- a/net/sched/cls_api.c +++ b/net/sched/cls_api.c @@ -286,7 +286,7 @@ replay: RCU_INIT_POINTER(*back, next); tfilter_notify(net, skb, n, tp, fh, RTM_DELTFILTER); - tcf_destroy(tp); + tcf_destroy(tp, true); err = 0; goto errout; } @@ -301,14 +301,20 @@ replay: err = -EEXIST; if (n->nlmsg_flags & NLM_F_EXCL) { if (tp_created) - tcf_destroy(tp); + tcf_destroy(tp, true); goto errout; } break; case RTM_DELTFILTER: err = tp->ops->delete(tp, fh); - if (err == 0) + if (err == 0) { tfilter_notify(net, skb, n, tp, fh, RTM_DELTFILTER); + if (tcf_destroy(tp, false)) { + struct tcf_proto *next = rtnl_dereference(tp->next); + + RCU_INIT_POINTER(*back, next); + } + } goto errout; case RTM_GETTFILTER: err = tfilter_notify(net, skb, n, tp, fh, RTM_NEWTFILTER); @@ -329,7 +335,7 @@ replay: tfilter_notify(net, skb, n, tp, fh, RTM_NEWTFILTER); } else { if (tp_created) - tcf_destroy(tp); + tcf_destroy(tp, true); } errout: diff --git a/net/sched/cls_basic.c b/net/sched/cls_basic.c index fc399db86f11..0b8c3ace671f 100644 --- a/net/sched/cls_basic.c +++ b/net/sched/cls_basic.c @@ -96,11 +96,14 @@ static void basic_delete_filter(struct rcu_head *head) kfree(f); } -static void basic_destroy(struct tcf_proto *tp) +static bool basic_destroy(struct tcf_proto *tp, bool force) { struct basic_head *head = rtnl_dereference(tp->root); struct basic_filter *f, *n; + if (!force && !list_empty(&head->flist)) + return false; + list_for_each_entry_safe(f, n, &head->flist, link) { list_del_rcu(&f->link); tcf_unbind_filter(tp, &f->res); @@ -108,6 +111,7 @@ static void basic_destroy(struct tcf_proto *tp) } RCU_INIT_POINTER(tp->root, NULL); kfree_rcu(head, rcu); + return true; } static int basic_delete(struct tcf_proto *tp, unsigned long arg) diff --git a/net/sched/cls_bpf.c b/net/sched/cls_bpf.c index 6f7ed8f8e6ee..243c9f225a73 100644 --- a/net/sched/cls_bpf.c +++ b/net/sched/cls_bpf.c @@ -137,11 +137,14 @@ static int cls_bpf_delete(struct tcf_proto *tp, unsigned long arg) return 0; } -static void cls_bpf_destroy(struct tcf_proto *tp) +static bool cls_bpf_destroy(struct tcf_proto *tp, bool force) { struct cls_bpf_head *head = rtnl_dereference(tp->root); struct cls_bpf_prog *prog, *tmp; + if (!force && !list_empty(&head->plist)) + return false; + list_for_each_entry_safe(prog, tmp, &head->plist, link) { list_del_rcu(&prog->link); tcf_unbind_filter(tp, &prog->res); @@ -150,6 +153,7 @@ static void cls_bpf_destroy(struct tcf_proto *tp) RCU_INIT_POINTER(tp->root, NULL); kfree_rcu(head, rcu); + return true; } static unsigned long cls_bpf_get(struct tcf_proto *tp, u32 handle) diff --git a/net/sched/cls_cgroup.c b/net/sched/cls_cgroup.c index 221697ab0247..ea611b216412 100644 --- a/net/sched/cls_cgroup.c +++ b/net/sched/cls_cgroup.c @@ -143,14 +143,18 @@ errout: return err; } -static void cls_cgroup_destroy(struct tcf_proto *tp) +static bool cls_cgroup_destroy(struct tcf_proto *tp, bool force) { struct cls_cgroup_head *head = rtnl_dereference(tp->root); + if (!force) + return false; + if (head) { RCU_INIT_POINTER(tp->root, NULL); call_rcu(&head->rcu, cls_cgroup_destroy_rcu); } + return true; } static int cls_cgroup_delete(struct tcf_proto *tp, unsigned long arg) diff --git a/net/sched/cls_flow.c b/net/sched/cls_flow.c index 461410394d08..a620c4e288a5 100644 --- a/net/sched/cls_flow.c +++ b/net/sched/cls_flow.c @@ -557,17 +557,21 @@ static int flow_init(struct tcf_proto *tp) return 0; } -static void flow_destroy(struct tcf_proto *tp) +static bool flow_destroy(struct tcf_proto *tp, bool force) { struct flow_head *head = rtnl_dereference(tp->root); struct flow_filter *f, *next; + if (!force && !list_empty(&head->filters)) + return false; + list_for_each_entry_safe(f, next, &head->filters, list) { list_del_rcu(&f->list); call_rcu(&f->rcu, flow_destroy_filter); } RCU_INIT_POINTER(tp->root, NULL); kfree_rcu(head, rcu); + return true; } static unsigned long flow_get(struct tcf_proto *tp, u32 handle) diff --git a/net/sched/cls_fw.c b/net/sched/cls_fw.c index 9d9aa3e82b10..715e01e5910a 100644 --- a/net/sched/cls_fw.c +++ b/net/sched/cls_fw.c @@ -133,14 +133,20 @@ static void fw_delete_filter(struct rcu_head *head) kfree(f); } -static void fw_destroy(struct tcf_proto *tp) +static bool fw_destroy(struct tcf_proto *tp, bool force) { struct fw_head *head = rtnl_dereference(tp->root); struct fw_filter *f; int h; if (head == NULL) - return; + return true; + + if (!force) { + for (h = 0; h < HTSIZE; h++) + if (rcu_access_pointer(head->ht[h])) + return false; + } for (h = 0; h < HTSIZE; h++) { while ((f = rtnl_dereference(head->ht[h])) != NULL) { @@ -152,6 +158,7 @@ static void fw_destroy(struct tcf_proto *tp) } RCU_INIT_POINTER(tp->root, NULL); kfree_rcu(head, rcu); + return true; } static int fw_delete(struct tcf_proto *tp, unsigned long arg) diff --git a/net/sched/cls_route.c b/net/sched/cls_route.c index bb8a60235d01..08a3b0a6f5ab 100644 --- a/net/sched/cls_route.c +++ b/net/sched/cls_route.c @@ -277,13 +277,20 @@ route4_delete_filter(struct rcu_head *head) kfree(f); } -static void route4_destroy(struct tcf_proto *tp) +static bool route4_destroy(struct tcf_proto *tp, bool force) { struct route4_head *head = rtnl_dereference(tp->root); int h1, h2; if (head == NULL) - return; + return true; + + if (!force) { + for (h1 = 0; h1 <= 256; h1++) { + if (rcu_access_pointer(head->table[h1])) + return false; + } + } for (h1 = 0; h1 <= 256; h1++) { struct route4_bucket *b; @@ -308,6 +315,7 @@ static void route4_destroy(struct tcf_proto *tp) } RCU_INIT_POINTER(tp->root, NULL); kfree_rcu(head, rcu); + return true; } static int route4_delete(struct tcf_proto *tp, unsigned long arg) diff --git a/net/sched/cls_rsvp.h b/net/sched/cls_rsvp.h index edd8ade3fbc1..02fa82792dab 100644 --- a/net/sched/cls_rsvp.h +++ b/net/sched/cls_rsvp.h @@ -291,13 +291,20 @@ rsvp_delete_filter(struct tcf_proto *tp, struct rsvp_filter *f) kfree_rcu(f, rcu); } -static void rsvp_destroy(struct tcf_proto *tp) +static bool rsvp_destroy(struct tcf_proto *tp, bool force) { struct rsvp_head *data = rtnl_dereference(tp->root); int h1, h2; if (data == NULL) - return; + return true; + + if (!force) { + for (h1 = 0; h1 < 256; h1++) { + if (rcu_access_pointer(data->ht[h1])) + return false; + } + } RCU_INIT_POINTER(tp->root, NULL); @@ -319,6 +326,7 @@ static void rsvp_destroy(struct tcf_proto *tp) } } kfree_rcu(data, rcu); + return true; } static int rsvp_delete(struct tcf_proto *tp, unsigned long arg) diff --git a/net/sched/cls_tcindex.c b/net/sched/cls_tcindex.c index bd49bf547a47..a557dbaf5afe 100644 --- a/net/sched/cls_tcindex.c +++ b/net/sched/cls_tcindex.c @@ -468,11 +468,14 @@ static void tcindex_walk(struct tcf_proto *tp, struct tcf_walker *walker) } } -static void tcindex_destroy(struct tcf_proto *tp) +static bool tcindex_destroy(struct tcf_proto *tp, bool force) { struct tcindex_data *p = rtnl_dereference(tp->root); struct tcf_walker walker; + if (!force) + return false; + pr_debug("tcindex_destroy(tp %p),p %p\n", tp, p); walker.count = 0; walker.skip = 0; @@ -481,6 +484,7 @@ static void tcindex_destroy(struct tcf_proto *tp) RCU_INIT_POINTER(tp->root, NULL); call_rcu(&p->rcu, __tcindex_destroy); + return true; } diff --git a/net/sched/cls_u32.c b/net/sched/cls_u32.c index 09487afbfd51..375e51b71c80 100644 --- a/net/sched/cls_u32.c +++ b/net/sched/cls_u32.c @@ -460,13 +460,35 @@ static int u32_destroy_hnode(struct tcf_proto *tp, struct tc_u_hnode *ht) return -ENOENT; } -static void u32_destroy(struct tcf_proto *tp) +static bool ht_empty(struct tc_u_hnode *ht) +{ + unsigned int h; + + for (h = 0; h <= ht->divisor; h++) + if (rcu_access_pointer(ht->ht[h])) + return false; + + return true; +} + +static bool u32_destroy(struct tcf_proto *tp, bool force) { struct tc_u_common *tp_c = tp->data; struct tc_u_hnode *root_ht = rtnl_dereference(tp->root); WARN_ON(root_ht == NULL); + if (!force) { + if (root_ht) { + if (root_ht->refcnt > 1) + return false; + if (root_ht->refcnt == 1) { + if (!ht_empty(root_ht)) + return false; + } + } + } + if (root_ht && --root_ht->refcnt == 0) u32_destroy_hnode(tp, root_ht); @@ -491,6 +513,7 @@ static void u32_destroy(struct tcf_proto *tp) } tp->data = NULL; + return true; } static int u32_delete(struct tcf_proto *tp, unsigned long arg) diff --git a/net/sched/sch_api.c b/net/sched/sch_api.c index 243b7d169d61..ad9eed70bc8f 100644 --- a/net/sched/sch_api.c +++ b/net/sched/sch_api.c @@ -1858,11 +1858,15 @@ reclassify: } EXPORT_SYMBOL(tc_classify); -void tcf_destroy(struct tcf_proto *tp) +bool tcf_destroy(struct tcf_proto *tp, bool force) { - tp->ops->destroy(tp); - module_put(tp->ops->owner); - kfree_rcu(tp, rcu); + if (tp->ops->destroy(tp, force)) { + module_put(tp->ops->owner); + kfree_rcu(tp, rcu); + return true; + } + + return false; } void tcf_destroy_chain(struct tcf_proto __rcu **fl) @@ -1871,7 +1875,7 @@ void tcf_destroy_chain(struct tcf_proto __rcu **fl) while ((tp = rtnl_dereference(*fl)) != NULL) { RCU_INIT_POINTER(*fl, tp->next); - tcf_destroy(tp); + tcf_destroy(tp, true); } } EXPORT_SYMBOL(tcf_destroy_chain); -- cgit v1.2.3 From ddb3b6033ca68d71a5f0611b58b2642729342245 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Mon, 9 Mar 2015 13:14:37 -0500 Subject: net: Remove protocol from struct dst_ops After my change to neigh_hh_init to obtain the protocol from the neigh_table there are no more users of protocol in struct dst_ops. Remove the protocol field from dst_ops and all of it's initializers. Signed-off-by: "Eric W. Biederman" Signed-off-by: David S. Miller --- include/net/dst_ops.h | 1 - net/bridge/br_nf_core.c | 1 - net/decnet/dn_route.c | 1 - net/ipv4/route.c | 2 -- net/ipv4/xfrm4_policy.c | 1 - net/ipv6/route.c | 2 -- net/ipv6/xfrm6_policy.c | 1 - 7 files changed, 9 deletions(-) (limited to 'include') diff --git a/include/net/dst_ops.h b/include/net/dst_ops.h index 1f99a1de0e4f..d64253914a6a 100644 --- a/include/net/dst_ops.h +++ b/include/net/dst_ops.h @@ -12,7 +12,6 @@ struct sock; struct dst_ops { unsigned short family; - __be16 protocol; unsigned int gc_thresh; int (*gc)(struct dst_ops *ops); diff --git a/net/bridge/br_nf_core.c b/net/bridge/br_nf_core.c index 387cb3bd017c..20cbb727df4d 100644 --- a/net/bridge/br_nf_core.c +++ b/net/bridge/br_nf_core.c @@ -54,7 +54,6 @@ static unsigned int fake_mtu(const struct dst_entry *dst) static struct dst_ops fake_dst_ops = { .family = AF_INET, - .protocol = cpu_to_be16(ETH_P_IP), .update_pmtu = fake_update_pmtu, .redirect = fake_redirect, .cow_metrics = fake_cow_metrics, diff --git a/net/decnet/dn_route.c b/net/decnet/dn_route.c index 771815575dbd..9ab0c4ba297f 100644 --- a/net/decnet/dn_route.c +++ b/net/decnet/dn_route.c @@ -136,7 +136,6 @@ int decnet_dst_gc_interval = 2; static struct dst_ops dn_dst_ops = { .family = PF_DECnet, - .protocol = cpu_to_be16(ETH_P_DNA_RT), .gc_thresh = 128, .gc = dn_dst_gc, .check = dn_dst_check, diff --git a/net/ipv4/route.c b/net/ipv4/route.c index ad5064362c5c..649c8a3f0189 100644 --- a/net/ipv4/route.c +++ b/net/ipv4/route.c @@ -152,7 +152,6 @@ static struct neighbour *ipv4_neigh_lookup(const struct dst_entry *dst, static struct dst_ops ipv4_dst_ops = { .family = AF_INET, - .protocol = cpu_to_be16(ETH_P_IP), .check = ipv4_dst_check, .default_advmss = ipv4_default_advmss, .mtu = ipv4_mtu, @@ -2225,7 +2224,6 @@ static u32 *ipv4_rt_blackhole_cow_metrics(struct dst_entry *dst, static struct dst_ops ipv4_dst_blackhole_ops = { .family = AF_INET, - .protocol = cpu_to_be16(ETH_P_IP), .check = ipv4_blackhole_dst_check, .mtu = ipv4_blackhole_mtu, .default_advmss = ipv4_default_advmss, diff --git a/net/ipv4/xfrm4_policy.c b/net/ipv4/xfrm4_policy.c index 6156f68a1e90..c224c856247b 100644 --- a/net/ipv4/xfrm4_policy.c +++ b/net/ipv4/xfrm4_policy.c @@ -232,7 +232,6 @@ static void xfrm4_dst_ifdown(struct dst_entry *dst, struct net_device *dev, static struct dst_ops xfrm4_dst_ops = { .family = AF_INET, - .protocol = cpu_to_be16(ETH_P_IP), .gc = xfrm4_garbage_collect, .update_pmtu = xfrm4_update_pmtu, .redirect = xfrm4_redirect, diff --git a/net/ipv6/route.c b/net/ipv6/route.c index 4688bd4d7f59..06fa819c43c9 100644 --- a/net/ipv6/route.c +++ b/net/ipv6/route.c @@ -194,7 +194,6 @@ static struct neighbour *ip6_neigh_lookup(const struct dst_entry *dst, static struct dst_ops ip6_dst_ops_template = { .family = AF_INET6, - .protocol = cpu_to_be16(ETH_P_IPV6), .gc = ip6_dst_gc, .gc_thresh = 1024, .check = ip6_dst_check, @@ -236,7 +235,6 @@ static u32 *ip6_rt_blackhole_cow_metrics(struct dst_entry *dst, static struct dst_ops ip6_dst_blackhole_ops = { .family = AF_INET6, - .protocol = cpu_to_be16(ETH_P_IPV6), .destroy = ip6_dst_destroy, .check = ip6_dst_check, .mtu = ip6_blackhole_mtu, diff --git a/net/ipv6/xfrm6_policy.c b/net/ipv6/xfrm6_policy.c index 48bf5a06847b..8ddf2b545151 100644 --- a/net/ipv6/xfrm6_policy.c +++ b/net/ipv6/xfrm6_policy.c @@ -292,7 +292,6 @@ static void xfrm6_dst_ifdown(struct dst_entry *dst, struct net_device *dev, static struct dst_ops xfrm6_dst_ops = { .family = AF_INET6, - .protocol = cpu_to_be16(ETH_P_IPV6), .gc = xfrm6_garbage_collect, .update_pmtu = xfrm6_update_pmtu, .redirect = xfrm6_redirect, -- cgit v1.2.3 From ca1bb4ee4c3a017bb66840d11d5efdf4e8f3f66d Mon Sep 17 00:00:00 2001 From: Bjorn Andersson Date: Mon, 23 Feb 2015 16:11:41 -0800 Subject: leds: Introduce devres helper for led_classdev_register (cooloney@gmail.com: add _unregister function into the document) Suggested-by: Stephen Boyd Signed-off-by: Bjorn Andersson Signed-off-by: Bryan Wu --- Documentation/driver-model/devres.txt | 4 +++ drivers/leds/led-class.c | 57 +++++++++++++++++++++++++++++++++++ include/linux/leds.h | 4 +++ 3 files changed, 65 insertions(+) (limited to 'include') diff --git a/Documentation/driver-model/devres.txt b/Documentation/driver-model/devres.txt index 6d1e8eeb5990..e1e2bbd7a404 100644 --- a/Documentation/driver-model/devres.txt +++ b/Documentation/driver-model/devres.txt @@ -289,6 +289,10 @@ IRQ devm_request_irq() devm_request_threaded_irq() +LED + devm_led_classdev_register() + devm_led_classdev_unregister() + MDIO devm_mdiobus_alloc() devm_mdiobus_alloc_size() diff --git a/drivers/leds/led-class.c b/drivers/leds/led-class.c index 795ec994c663..768d33a79881 100644 --- a/drivers/leds/led-class.c +++ b/drivers/leds/led-class.c @@ -288,6 +288,63 @@ void led_classdev_unregister(struct led_classdev *led_cdev) } EXPORT_SYMBOL_GPL(led_classdev_unregister); +static void devm_led_classdev_release(struct device *dev, void *res) +{ + led_classdev_unregister(*(struct led_classdev **)res); +} + +/** + * devm_led_classdev_register - resource managed led_classdev_register() + * @parent: The device to register. + * @led_cdev: the led_classdev structure for this device. + */ +int devm_led_classdev_register(struct device *parent, + struct led_classdev *led_cdev) +{ + struct led_classdev **dr; + int rc; + + dr = devres_alloc(devm_led_classdev_release, sizeof(*dr), GFP_KERNEL); + if (!dr) + return -ENOMEM; + + rc = led_classdev_register(parent, led_cdev); + if (rc) { + devres_free(dr); + return rc; + } + + *dr = led_cdev; + devres_add(parent, dr); + + return 0; +} +EXPORT_SYMBOL_GPL(devm_led_classdev_register); + +static int devm_led_classdev_match(struct device *dev, void *res, void *data) +{ + struct led_cdev **p = res; + + if (WARN_ON(!p || !*p)) + return 0; + + return *p == data; +} + +/** + * devm_led_classdev_unregister() - resource managed led_classdev_unregister() + * @parent: The device to unregister. + * @led_cdev: the led_classdev structure for this device. + */ +void devm_led_classdev_unregister(struct device *dev, + struct led_classdev *led_cdev) +{ + WARN_ON(devres_release(dev, + devm_led_classdev_release, + devm_led_classdev_match, led_cdev)); +} +EXPORT_SYMBOL_GPL(devm_led_classdev_unregister); + static int __init leds_init(void) { leds_class = class_create(THIS_MODULE, "leds"); diff --git a/include/linux/leds.h b/include/linux/leds.h index f70f84f35674..ed634279062e 100644 --- a/include/linux/leds.h +++ b/include/linux/leds.h @@ -105,7 +105,11 @@ struct led_classdev { extern int led_classdev_register(struct device *parent, struct led_classdev *led_cdev); +extern int devm_led_classdev_register(struct device *parent, + struct led_classdev *led_cdev); extern void led_classdev_unregister(struct led_classdev *led_cdev); +extern void devm_led_classdev_unregister(struct device *parent, + struct led_classdev *led_cdev); extern void led_classdev_suspend(struct led_classdev *led_cdev); extern void led_classdev_resume(struct led_classdev *led_cdev); -- cgit v1.2.3 From 94fdec768dc72a6993479f59a17daa413f30029e Mon Sep 17 00:00:00 2001 From: Jacek Anaszewski Date: Wed, 4 Mar 2015 08:14:22 -0800 Subject: leds: flash: Remove synchronized flash strobe feature Synchronized flash strobe feature has been considered not fitting for LED subsystem sysfs interface and thus is being removed. Signed-off-by: Jacek Anaszewski Acked-by: Kyungmin Park Cc: Richard Purdie Signed-off-by: Bryan Wu --- drivers/leds/led-class-flash.c | 82 ----------------------------------------- include/linux/led-class-flash.h | 14 ------- include/linux/leds.h | 1 - 3 files changed, 97 deletions(-) (limited to 'include') diff --git a/drivers/leds/led-class-flash.c b/drivers/leds/led-class-flash.c index 4a19fd44f93f..3b2573411a37 100644 --- a/drivers/leds/led-class-flash.c +++ b/drivers/leds/led-class-flash.c @@ -216,75 +216,6 @@ static ssize_t flash_fault_show(struct device *dev, } static DEVICE_ATTR_RO(flash_fault); -static ssize_t available_sync_leds_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct led_classdev *led_cdev = dev_get_drvdata(dev); - struct led_classdev_flash *fled_cdev = lcdev_to_flcdev(led_cdev); - char *pbuf = buf; - int i, buf_len; - - buf_len = sprintf(pbuf, "[0: none] "); - pbuf += buf_len; - - for (i = 0; i < fled_cdev->num_sync_leds; ++i) { - buf_len = sprintf(pbuf, "[%d: %s] ", i + 1, - fled_cdev->sync_leds[i]->led_cdev.name); - pbuf += buf_len; - } - - return sprintf(buf, "%s\n", buf); -} -static DEVICE_ATTR_RO(available_sync_leds); - -static ssize_t flash_sync_strobe_store(struct device *dev, - struct device_attribute *attr, const char *buf, size_t size) -{ - struct led_classdev *led_cdev = dev_get_drvdata(dev); - struct led_classdev_flash *fled_cdev = lcdev_to_flcdev(led_cdev); - unsigned long led_id; - ssize_t ret; - - mutex_lock(&led_cdev->led_access); - - if (led_sysfs_is_disabled(led_cdev)) { - ret = -EBUSY; - goto unlock; - } - - ret = kstrtoul(buf, 10, &led_id); - if (ret) - goto unlock; - - if (led_id > fled_cdev->num_sync_leds) { - ret = -ERANGE; - goto unlock; - } - - fled_cdev->sync_led_id = led_id; - - ret = size; -unlock: - mutex_unlock(&led_cdev->led_access); - return ret; -} - -static ssize_t flash_sync_strobe_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct led_classdev *led_cdev = dev_get_drvdata(dev); - struct led_classdev_flash *fled_cdev = lcdev_to_flcdev(led_cdev); - int sled_id = fled_cdev->sync_led_id; - char *sync_led_name = "none"; - - if (fled_cdev->sync_led_id > 0) - sync_led_name = (char *) - fled_cdev->sync_leds[sled_id - 1]->led_cdev.name; - - return sprintf(buf, "[%d: %s]\n", sled_id, sync_led_name); -} -static DEVICE_ATTR_RW(flash_sync_strobe); - static struct attribute *led_flash_strobe_attrs[] = { &dev_attr_flash_strobe.attr, NULL, @@ -307,12 +238,6 @@ static struct attribute *led_flash_fault_attrs[] = { NULL, }; -static struct attribute *led_flash_sync_strobe_attrs[] = { - &dev_attr_available_sync_leds.attr, - &dev_attr_flash_sync_strobe.attr, - NULL, -}; - static const struct attribute_group led_flash_strobe_group = { .attrs = led_flash_strobe_attrs, }; @@ -329,10 +254,6 @@ static const struct attribute_group led_flash_fault_group = { .attrs = led_flash_fault_attrs, }; -static const struct attribute_group led_flash_sync_strobe_group = { - .attrs = led_flash_sync_strobe_attrs, -}; - static void led_flash_resume(struct led_classdev *led_cdev) { struct led_classdev_flash *fled_cdev = lcdev_to_flcdev(led_cdev); @@ -361,9 +282,6 @@ static void led_flash_init_sysfs_groups(struct led_classdev_flash *fled_cdev) if (ops->fault_get) flash_groups[num_sysfs_groups++] = &led_flash_fault_group; - if (led_cdev->flags & LED_DEV_CAP_SYNC_STROBE) - flash_groups[num_sysfs_groups++] = &led_flash_sync_strobe_group; - led_cdev->groups = flash_groups; } diff --git a/include/linux/led-class-flash.h b/include/linux/led-class-flash.h index 3b5b9643cea1..21ec91e13c47 100644 --- a/include/linux/led-class-flash.h +++ b/include/linux/led-class-flash.h @@ -81,20 +81,6 @@ struct led_classdev_flash { /* LED Flash class sysfs groups */ const struct attribute_group *sysfs_groups[LED_FLASH_MAX_SYSFS_GROUPS]; - - /* LEDs available for flash strobe synchronization */ - struct led_classdev_flash **sync_leds; - - /* Number of LEDs available for flash strobe synchronization */ - int num_sync_leds; - - /* - * The identifier of the sub-led to synchronize the flash strobe with. - * Identifiers start from 1, which reflects the first element from the - * sync_leds array. 0 means that the flash strobe should not be - * synchronized. - */ - u32 sync_led_id; }; static inline struct led_classdev_flash *lcdev_to_flcdev( diff --git a/include/linux/leds.h b/include/linux/leds.h index ed634279062e..9a2b000094cf 100644 --- a/include/linux/leds.h +++ b/include/linux/leds.h @@ -47,7 +47,6 @@ struct led_classdev { #define SET_BRIGHTNESS_ASYNC (1 << 21) #define SET_BRIGHTNESS_SYNC (1 << 22) #define LED_DEV_CAP_FLASH (1 << 23) -#define LED_DEV_CAP_SYNC_STROBE (1 << 24) /* Set LED brightness level */ /* Must not sleep, use a workqueue if needed */ -- cgit v1.2.3 From 2f0f267ea0720ec6adbe9cf7386450425fac8258 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Thu, 5 Mar 2015 10:49:19 +1030 Subject: cpumask: remove deprecated functions. Using these functions with offstack cpus is unsafe. They use all NR_CPUS bits, unstead of nr_cpumask_bits. In particular, lustre (in staging) used cpus_ and that caused a bug. Reported-by: Oleg Drokin Signed-off-by: Rusty Russell --- include/linux/cpumask.h | 151 ------------------------------------------------ lib/Kconfig | 4 -- 2 files changed, 155 deletions(-) (limited to 'include') diff --git a/include/linux/cpumask.h b/include/linux/cpumask.h index dc037ae6f4f2..646fadee5caf 100644 --- a/include/linux/cpumask.h +++ b/include/linux/cpumask.h @@ -815,155 +815,4 @@ cpumap_print_to_pagebuf(bool list, char *buf, const struct cpumask *mask) [0 ... BITS_TO_LONGS(NR_CPUS)-1] = 0UL \ } } -/* - * - * From here down, all obsolete. Use cpumask_ variants! - * - */ -#ifndef CONFIG_DISABLE_OBSOLETE_CPUMASK_FUNCTIONS - -#define CPU_MASK_CPU0 \ -(cpumask_t) { { \ - [0] = 1UL \ -} } - -#if NR_CPUS == 1 -#define first_cpu(src) ({ (void)(src); 0; }) -#define next_cpu(n, src) ({ (void)(src); 1; }) -#define any_online_cpu(mask) 0 -#define for_each_cpu_mask(cpu, mask) \ - for ((cpu) = 0; (cpu) < 1; (cpu)++, (void)mask) -#else /* NR_CPUS > 1 */ -int __first_cpu(const cpumask_t *srcp); -int __next_cpu(int n, const cpumask_t *srcp); - -#define first_cpu(src) __first_cpu(&(src)) -#define next_cpu(n, src) __next_cpu((n), &(src)) -#define any_online_cpu(mask) cpumask_any_and(&mask, cpu_online_mask) -#define for_each_cpu_mask(cpu, mask) \ - for ((cpu) = -1; \ - (cpu) = next_cpu((cpu), (mask)), \ - (cpu) < NR_CPUS; ) -#endif /* SMP */ - -#if NR_CPUS <= 64 - -#define for_each_cpu_mask_nr(cpu, mask) for_each_cpu_mask(cpu, mask) - -#else /* NR_CPUS > 64 */ - -int __next_cpu_nr(int n, const cpumask_t *srcp); -#define for_each_cpu_mask_nr(cpu, mask) \ - for ((cpu) = -1; \ - (cpu) = __next_cpu_nr((cpu), &(mask)), \ - (cpu) < nr_cpu_ids; ) - -#endif /* NR_CPUS > 64 */ - -#define cpus_addr(src) ((src).bits) - -#define cpu_set(cpu, dst) __cpu_set((cpu), &(dst)) -static inline void __cpu_set(int cpu, volatile cpumask_t *dstp) -{ - set_bit(cpu, dstp->bits); -} - -#define cpu_clear(cpu, dst) __cpu_clear((cpu), &(dst)) -static inline void __cpu_clear(int cpu, volatile cpumask_t *dstp) -{ - clear_bit(cpu, dstp->bits); -} - -#define cpus_setall(dst) __cpus_setall(&(dst), NR_CPUS) -static inline void __cpus_setall(cpumask_t *dstp, unsigned int nbits) -{ - bitmap_fill(dstp->bits, nbits); -} - -#define cpus_clear(dst) __cpus_clear(&(dst), NR_CPUS) -static inline void __cpus_clear(cpumask_t *dstp, unsigned int nbits) -{ - bitmap_zero(dstp->bits, nbits); -} - -/* No static inline type checking - see Subtlety (1) above. */ -#define cpu_isset(cpu, cpumask) test_bit((cpu), (cpumask).bits) - -#define cpu_test_and_set(cpu, cpumask) __cpu_test_and_set((cpu), &(cpumask)) -static inline int __cpu_test_and_set(int cpu, cpumask_t *addr) -{ - return test_and_set_bit(cpu, addr->bits); -} - -#define cpus_and(dst, src1, src2) __cpus_and(&(dst), &(src1), &(src2), NR_CPUS) -static inline int __cpus_and(cpumask_t *dstp, const cpumask_t *src1p, - const cpumask_t *src2p, unsigned int nbits) -{ - return bitmap_and(dstp->bits, src1p->bits, src2p->bits, nbits); -} - -#define cpus_or(dst, src1, src2) __cpus_or(&(dst), &(src1), &(src2), NR_CPUS) -static inline void __cpus_or(cpumask_t *dstp, const cpumask_t *src1p, - const cpumask_t *src2p, unsigned int nbits) -{ - bitmap_or(dstp->bits, src1p->bits, src2p->bits, nbits); -} - -#define cpus_xor(dst, src1, src2) __cpus_xor(&(dst), &(src1), &(src2), NR_CPUS) -static inline void __cpus_xor(cpumask_t *dstp, const cpumask_t *src1p, - const cpumask_t *src2p, unsigned int nbits) -{ - bitmap_xor(dstp->bits, src1p->bits, src2p->bits, nbits); -} - -#define cpus_andnot(dst, src1, src2) \ - __cpus_andnot(&(dst), &(src1), &(src2), NR_CPUS) -static inline int __cpus_andnot(cpumask_t *dstp, const cpumask_t *src1p, - const cpumask_t *src2p, unsigned int nbits) -{ - return bitmap_andnot(dstp->bits, src1p->bits, src2p->bits, nbits); -} - -#define cpus_equal(src1, src2) __cpus_equal(&(src1), &(src2), NR_CPUS) -static inline int __cpus_equal(const cpumask_t *src1p, - const cpumask_t *src2p, unsigned int nbits) -{ - return bitmap_equal(src1p->bits, src2p->bits, nbits); -} - -#define cpus_intersects(src1, src2) __cpus_intersects(&(src1), &(src2), NR_CPUS) -static inline int __cpus_intersects(const cpumask_t *src1p, - const cpumask_t *src2p, unsigned int nbits) -{ - return bitmap_intersects(src1p->bits, src2p->bits, nbits); -} - -#define cpus_subset(src1, src2) __cpus_subset(&(src1), &(src2), NR_CPUS) -static inline int __cpus_subset(const cpumask_t *src1p, - const cpumask_t *src2p, unsigned int nbits) -{ - return bitmap_subset(src1p->bits, src2p->bits, nbits); -} - -#define cpus_empty(src) __cpus_empty(&(src), NR_CPUS) -static inline int __cpus_empty(const cpumask_t *srcp, unsigned int nbits) -{ - return bitmap_empty(srcp->bits, nbits); -} - -#define cpus_weight(cpumask) __cpus_weight(&(cpumask), NR_CPUS) -static inline int __cpus_weight(const cpumask_t *srcp, unsigned int nbits) -{ - return bitmap_weight(srcp->bits, nbits); -} - -#define cpus_shift_left(dst, src, n) \ - __cpus_shift_left(&(dst), &(src), (n), NR_CPUS) -static inline void __cpus_shift_left(cpumask_t *dstp, - const cpumask_t *srcp, int n, int nbits) -{ - bitmap_shift_left(dstp->bits, srcp->bits, n, nbits); -} -#endif /* !CONFIG_DISABLE_OBSOLETE_CPUMASK_FUNCTIONS */ - #endif /* __LINUX_CPUMASK_H */ diff --git a/lib/Kconfig b/lib/Kconfig index 87da53bb1fef..47d262b3251e 100644 --- a/lib/Kconfig +++ b/lib/Kconfig @@ -397,10 +397,6 @@ config CPUMASK_OFFSTACK them on the stack. This is a bit more expensive, but avoids stack overflow. -config DISABLE_OBSOLETE_CPUMASK_FUNCTIONS - bool "Disable obsolete cpumask functions" if DEBUG_PER_CPU_MAPS - depends on BROKEN - config CPU_RMAP bool depends on SMP -- cgit v1.2.3 From cdfdef75e795fb5ab76c66f3329e509f3ab8b9b5 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Thu, 5 Mar 2015 10:49:19 +1030 Subject: cpumask: only allocate nr_cpumask_bits. Now we'll find out the hard way if anyone has CPUMASK_OFFSTACK and is returning these or assigning them. Signed-off-by: Rusty Russell --- include/linux/cpumask.h | 5 ++--- lib/cpumask.c | 7 ------- 2 files changed, 2 insertions(+), 10 deletions(-) (limited to 'include') diff --git a/include/linux/cpumask.h b/include/linux/cpumask.h index 646fadee5caf..4ad2d3c8e21f 100644 --- a/include/linux/cpumask.h +++ b/include/linux/cpumask.h @@ -11,6 +11,7 @@ #include #include +/* Don't assign or return these: may not be this big! */ typedef struct cpumask { DECLARE_BITMAP(bits, NR_CPUS); } cpumask_t; /** @@ -609,9 +610,7 @@ static inline int cpulist_parse(const char *buf, struct cpumask *dstp) */ static inline size_t cpumask_size(void) { - /* FIXME: Once all cpumask assignments are eliminated, this - * can be nr_cpumask_bits */ - return BITS_TO_LONGS(NR_CPUS) * sizeof(long); + return BITS_TO_LONGS(nr_cpumask_bits) * sizeof(long); } /* diff --git a/lib/cpumask.c b/lib/cpumask.c index b6513a9f2892..ba379d12bb57 100644 --- a/lib/cpumask.c +++ b/lib/cpumask.c @@ -89,13 +89,6 @@ bool alloc_cpumask_var_node(cpumask_var_t *mask, gfp_t flags, int node) dump_stack(); } #endif - /* FIXME: Bandaid to save us from old primitives which go to NR_CPUS. */ - if (*mask) { - unsigned char *ptr = (unsigned char *)cpumask_bits(*mask); - unsigned int tail; - tail = BITS_TO_LONGS(NR_CPUS - nr_cpumask_bits) * sizeof(long); - memset(ptr + cpumask_size() - tail, 0, tail); - } return *mask != NULL; } -- cgit v1.2.3 From aa836df958886e57ff0d43fb3d79d1af4aec0cc8 Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Mon, 9 Mar 2015 14:31:20 -0700 Subject: net: core: add of_find_net_device_by_node() Add a helper function which allows getting the struct net_device pointer associated with a given struct device_node pointer. This is useful for instance for DSA Ethernet devices not backed by a platform_device, but a PCI device. Since we need to access net_class which is not accessible outside of net/core/net-sysfs.c, this helper function is also added here and gated with CONFIG_OF_NET. Network devices initialized with SET_NETDEV_DEV() are also taken into account by checking for dev->parent first and then falling back to checking the device pointer within struct net_device. Signed-off-by: Florian Fainelli Signed-off-by: David S. Miller --- include/linux/of_net.h | 8 ++++++++ net/core/net-sysfs.c | 25 +++++++++++++++++++++++++ 2 files changed, 33 insertions(+) (limited to 'include') diff --git a/include/linux/of_net.h b/include/linux/of_net.h index 34597c8c1a4c..9cd72aab76fe 100644 --- a/include/linux/of_net.h +++ b/include/linux/of_net.h @@ -9,8 +9,11 @@ #ifdef CONFIG_OF_NET #include + +struct net_device; extern int of_get_phy_mode(struct device_node *np); extern const void *of_get_mac_address(struct device_node *np); +extern struct net_device *of_find_net_device_by_node(struct device_node *np); #else static inline int of_get_phy_mode(struct device_node *np) { @@ -21,6 +24,11 @@ static inline const void *of_get_mac_address(struct device_node *np) { return NULL; } + +static inline struct net_device *of_find_net_device_by_node(struct device_node *np) +{ + return NULL; +} #endif #endif /* __LINUX_OF_NET_H */ diff --git a/net/core/net-sysfs.c b/net/core/net-sysfs.c index f2aa73bfb0e4..cf30620a88e1 100644 --- a/net/core/net-sysfs.c +++ b/net/core/net-sysfs.c @@ -23,6 +23,7 @@ #include #include #include +#include #include "net-sysfs.h" @@ -1374,6 +1375,30 @@ static struct class net_class = { .namespace = net_namespace, }; +#ifdef CONFIG_OF_NET +static int of_dev_node_match(struct device *dev, const void *data) +{ + int ret = 0; + + if (dev->parent) + ret = dev->parent->of_node == data; + + return ret == 0 ? dev->of_node == data : ret; +} + +struct net_device *of_find_net_device_by_node(struct device_node *np) +{ + struct device *dev; + + dev = class_find_device(&net_class, NULL, np, of_dev_node_match); + if (!dev) + return NULL; + + return to_net_dev(dev); +} +EXPORT_SYMBOL(of_find_net_device_by_node); +#endif + /* Delete sysfs entries but hold kobject reference until after all * netdev references are gone. */ -- cgit v1.2.3 From 769a020289bc8f68b7e48faf8fee970346d71a3b Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Mon, 9 Mar 2015 14:31:21 -0700 Subject: net: dsa: utilize of_find_net_device_by_node Using of_find_device_by_node() restricts the search to platform_device that match the specified device_node pointer. This is not even remotely true for network devices backed by a pci_device for instance. of_find_net_device_by_node() allows us to do a more thorough lookup to find the struct net_device corresponding to a particular device_node pointer. For symetry with the non-OF code path, we hold the net_device pointer in dsa_probe() just like what dev_to_net_dev() does when we call this function. Signed-off-by: Florian Fainelli Signed-off-by: David S. Miller --- include/net/dsa.h | 1 + net/dsa/dsa.c | 16 +++++++++++----- 2 files changed, 12 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/include/net/dsa.h b/include/net/dsa.h index b525ac516559..47917e5e1e12 100644 --- a/include/net/dsa.h +++ b/include/net/dsa.h @@ -72,6 +72,7 @@ struct dsa_platform_data { * to the root switch chip of the tree. */ struct device *netdev; + struct net_device *of_netdev; /* * Info structs describing each of the switch chips diff --git a/net/dsa/dsa.c b/net/dsa/dsa.c index b40f11bb419c..899772108ee3 100644 --- a/net/dsa/dsa.c +++ b/net/dsa/dsa.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #include "dsa_priv.h" @@ -583,7 +584,7 @@ static int dsa_of_probe(struct device *dev) struct device_node *np = dev->of_node; struct device_node *child, *mdio, *ethernet, *port, *link; struct mii_bus *mdio_bus; - struct platform_device *ethernet_dev; + struct net_device *ethernet_dev; struct dsa_platform_data *pd; struct dsa_chip_data *cd; const char *port_name; @@ -604,7 +605,7 @@ static int dsa_of_probe(struct device *dev) if (!ethernet) return -EINVAL; - ethernet_dev = of_find_device_by_node(ethernet); + ethernet_dev = of_find_net_device_by_node(ethernet); if (!ethernet_dev) return -EPROBE_DEFER; @@ -613,7 +614,7 @@ static int dsa_of_probe(struct device *dev) return -ENOMEM; dev->platform_data = pd; - pd->netdev = ðernet_dev->dev; + pd->of_netdev = ethernet_dev; pd->nr_chips = of_get_available_child_count(np); if (pd->nr_chips > DSA_MAX_SWITCHES) pd->nr_chips = DSA_MAX_SWITCHES; @@ -771,10 +772,15 @@ static int dsa_probe(struct platform_device *pdev) pd = pdev->dev.platform_data; } - if (pd == NULL || pd->netdev == NULL) + if (pd == NULL || (pd->netdev == NULL && pd->of_netdev == NULL)) return -EINVAL; - dev = dev_to_net_device(pd->netdev); + if (pd->of_netdev) { + dev = pd->of_netdev; + dev_hold(dev); + } else { + dev = dev_to_net_device(pd->netdev); + } if (dev == NULL) { ret = -EPROBE_DEFER; goto out; -- cgit v1.2.3 From f8f2147150de303e814c0452075d467734d3544b Mon Sep 17 00:00:00 2001 From: Scott Feldman Date: Mon, 9 Mar 2015 13:59:09 -0700 Subject: switchdev: add netlink flags to IPv4 FIB add op Pass in the netlink flags (NLM_F_*) into switchdev driver for IPv4 FIB add op to allow driver to 1) optimize hardware updates, 2) handle ip route prepend and append commands correctly. Suggested-by: Jamal Hadi Salim Suggested-by: Roopa Prabhu Signed-off-by: Scott Feldman Reviewed-by: Simon Horman Acked-by: Roopa Prabhu Signed-off-by: David S. Miller --- drivers/net/ethernet/rocker/rocker.c | 3 ++- include/linux/netdevice.h | 3 ++- include/net/switchdev.h | 6 ++++-- net/ipv4/fib_trie.c | 5 ++++- net/switchdev/switchdev.c | 7 +++++-- 5 files changed, 17 insertions(+), 7 deletions(-) (limited to 'include') diff --git a/drivers/net/ethernet/rocker/rocker.c b/drivers/net/ethernet/rocker/rocker.c index 65e140315a58..223348d8cc07 100644 --- a/drivers/net/ethernet/rocker/rocker.c +++ b/drivers/net/ethernet/rocker/rocker.c @@ -4152,7 +4152,8 @@ static int rocker_port_switch_port_stp_update(struct net_device *dev, u8 state) static int rocker_port_switch_fib_ipv4_add(struct net_device *dev, __be32 dst, int dst_len, struct fib_info *fi, - u8 tos, u8 type, u32 tb_id) + u8 tos, u8 type, + u32 nlflags, u32 tb_id) { struct rocker_port *rocker_port = netdev_priv(dev); int flags = 0; diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 45413784a3b1..1354ae83efc8 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -1035,7 +1035,7 @@ struct fib_info; * state change. * int (*ndo_sw_parent_fib_ipv4_add)(struct net_device *dev, __be32 dst, * int dst_len, struct fib_info *fi, - * u8 tos, u8 type, u32 tb_id); + * u8 tos, u8 type, u32 nlflags, u32 tb_id); * Called to add/modify IPv4 route to switch device. * int (*ndo_sw_parent_fib_ipv4_del)(struct net_device *dev, __be32 dst, * int dst_len, struct fib_info *fi, @@ -1207,6 +1207,7 @@ struct net_device_ops { int dst_len, struct fib_info *fi, u8 tos, u8 type, + u32 nlflags, u32 tb_id); int (*ndo_switch_fib_ipv4_del)(struct net_device *dev, __be32 dst, diff --git a/include/net/switchdev.h b/include/net/switchdev.h index 933fac410a7a..1a9382febcc3 100644 --- a/include/net/switchdev.h +++ b/include/net/switchdev.h @@ -1,6 +1,7 @@ /* * include/net/switchdev.h - Switch device API * Copyright (c) 2014 Jiri Pirko + * Copyright (c) 2014-2015 Scott Feldman * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -52,7 +53,7 @@ int ndo_dflt_netdev_switch_port_bridge_dellink(struct net_device *dev, int ndo_dflt_netdev_switch_port_bridge_setlink(struct net_device *dev, struct nlmsghdr *nlh, u16 flags); int netdev_switch_fib_ipv4_add(u32 dst, int dst_len, struct fib_info *fi, - u8 tos, u8 type, u32 tb_id); + u8 tos, u8 type, u32 nlflags, u32 tb_id); int netdev_switch_fib_ipv4_del(u32 dst, int dst_len, struct fib_info *fi, u8 tos, u8 type, u32 tb_id); void netdev_switch_fib_ipv4_abort(struct fib_info *fi); @@ -117,7 +118,8 @@ static inline int ndo_dflt_netdev_switch_port_bridge_setlink(struct net_device * static inline int netdev_switch_fib_ipv4_add(u32 dst, int dst_len, struct fib_info *fi, - u8 tos, u8 type, u32 tb_id) + u8 tos, u8 type, + u32 nlflags, u32 tb_id) { return 0; } diff --git a/net/ipv4/fib_trie.c b/net/ipv4/fib_trie.c index 90955455884e..fcfa9825a816 100644 --- a/net/ipv4/fib_trie.c +++ b/net/ipv4/fib_trie.c @@ -1155,6 +1155,7 @@ int fib_table_insert(struct fib_table *tb, struct fib_config *cfg) err = netdev_switch_fib_ipv4_add(key, plen, fi, new_fa->fa_tos, cfg->fc_type, + cfg->fc_nlflags, tb->tb_id); if (err) { netdev_switch_fib_ipv4_abort(fi); @@ -1201,7 +1202,9 @@ int fib_table_insert(struct fib_table *tb, struct fib_config *cfg) /* (Optionally) offload fib entry to switch hardware. */ err = netdev_switch_fib_ipv4_add(key, plen, fi, tos, - cfg->fc_type, tb->tb_id); + cfg->fc_type, + cfg->fc_nlflags, + tb->tb_id); if (err) { netdev_switch_fib_ipv4_abort(fi); goto out_free_new_fa; diff --git a/net/switchdev/switchdev.c b/net/switchdev/switchdev.c index aba6aa2656d8..8cf42a69baf4 100644 --- a/net/switchdev/switchdev.c +++ b/net/switchdev/switchdev.c @@ -1,6 +1,7 @@ /* * net/switchdev/switchdev.c - Switch device API * Copyright (c) 2014 Jiri Pirko + * Copyright (c) 2014-2015 Scott Feldman * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -294,12 +295,13 @@ static struct net_device *netdev_switch_get_dev_by_nhs(struct fib_info *fi) * @fi: route FIB info structure * @tos: route TOS * @type: route type + * @nlflags: netlink flags passed in (NLM_F_*) * @tb_id: route table ID * * Add IPv4 route entry to switch device. */ int netdev_switch_fib_ipv4_add(u32 dst, int dst_len, struct fib_info *fi, - u8 tos, u8 type, u32 tb_id) + u8 tos, u8 type, u32 nlflags, u32 tb_id) { struct net_device *dev; const struct net_device_ops *ops; @@ -324,7 +326,8 @@ int netdev_switch_fib_ipv4_add(u32 dst, int dst_len, struct fib_info *fi, if (ops->ndo_switch_fib_ipv4_add) { err = ops->ndo_switch_fib_ipv4_add(dev, htonl(dst), dst_len, - fi, tos, type, tb_id); + fi, tos, type, nlflags, + tb_id); if (!err) fi->fib_flags |= RTNH_F_EXTERNAL; } -- cgit v1.2.3 From 59e33c2b021322db92ca27c4d9958e57630443b6 Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Mon, 9 Mar 2015 15:44:13 -0700 Subject: net: phy: bcm7xxx: add alternate id for 7439 BCM7439 has an alternate PHY OUI: 0xae025080 which is to be found in some variants of this chip. Signed-off-by: Florian Fainelli Signed-off-by: David S. Miller --- drivers/net/phy/bcm7xxx.c | 1 + include/linux/brcmphy.h | 1 + 2 files changed, 2 insertions(+) (limited to 'include') diff --git a/drivers/net/phy/bcm7xxx.c b/drivers/net/phy/bcm7xxx.c index 974ec4515269..64c74c6a4828 100644 --- a/drivers/net/phy/bcm7xxx.c +++ b/drivers/net/phy/bcm7xxx.c @@ -396,6 +396,7 @@ static struct phy_driver bcm7xxx_driver[] = { BCM7XXX_28NM_GPHY(PHY_ID_BCM7364, "Broadcom BCM7364"), BCM7XXX_28NM_GPHY(PHY_ID_BCM7366, "Broadcom BCM7366"), BCM7XXX_28NM_GPHY(PHY_ID_BCM7439, "Broadcom BCM7439"), + BCM7XXX_28NM_GPHY(PHY_ID_BCM7439_2, "Broadcom BCM7439 (2)"), BCM7XXX_28NM_GPHY(PHY_ID_BCM7445, "Broadcom BCM7445"), { .phy_id = PHY_ID_BCM7425, diff --git a/include/linux/brcmphy.h b/include/linux/brcmphy.h index 7ccd928cc1f2..cab606617522 100644 --- a/include/linux/brcmphy.h +++ b/include/linux/brcmphy.h @@ -19,6 +19,7 @@ #define PHY_ID_BCM7425 0x03625e60 #define PHY_ID_BCM7429 0x600d8730 #define PHY_ID_BCM7439 0x600d8480 +#define PHY_ID_BCM7439_2 0xae025080 #define PHY_ID_BCM7445 0x600d8510 #define PHY_BCM_OUI_MASK 0xfffffc00 -- cgit v1.2.3 From bd5da992b96bc018c3e64ffc5ac15cbe301f6440 Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Wed, 25 Feb 2015 14:46:51 +0200 Subject: drm/dp: indentation and ordering cleanups Keep the DPCD macros ordered by address, and make indentation conform to the rest of the file. commit e045d20bef41707dbba676e58624b54f9f39e172 Author: Sonika Jindal Date: Thu Feb 19 13:16:44 2015 +0530 drm: Adding edp1.4 specific dpcd macros Signed-off-by: Jani Nikula Reviewed-by: Sonika Jindal Signed-off-by: Daniel Vetter --- include/drm/drm_dp_helper.h | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/include/drm/drm_dp_helper.h b/include/drm/drm_dp_helper.h index d4803224028f..98fefe45d158 100644 --- a/include/drm/drm_dp_helper.h +++ b/include/drm/drm_dp_helper.h @@ -92,9 +92,6 @@ # define DP_MSA_TIMING_PAR_IGNORED (1 << 6) /* eDP */ # define DP_OUI_SUPPORT (1 << 7) -#define DP_SUPPORTED_LINK_RATES 0x010 /*eDP 1.4*/ -#define DP_MAX_SUPPORTED_RATES 0x8 - #define DP_I2C_SPEED_CAP 0x00c /* DPI */ # define DP_I2C_SPEED_1K 0x01 # define DP_I2C_SPEED_5K 0x02 @@ -105,8 +102,12 @@ #define DP_EDP_CONFIGURATION_CAP 0x00d /* XXX 1.2? */ # define DP_DPCD_DISPLAY_CONTROL_CAPABLE (1 << 3) /* edp v1.2 or higher */ + #define DP_TRAINING_AUX_RD_INTERVAL 0x00e /* XXX 1.2? */ +#define DP_SUPPORTED_LINK_RATES 0x010 /* eDP 1.4 */ +# define DP_MAX_SUPPORTED_RATES 8 /* 16-bit little-endian */ + /* Multiple stream transport */ #define DP_FAUX_CAP 0x020 /* 1.2 */ # define DP_FAUX_CAP_1 (1 << 0) @@ -225,7 +226,7 @@ # define DP_UP_REQ_EN (1 << 1) # define DP_UPSTREAM_IS_SRC (1 << 2) -#define DP_LINK_RATE_SET 0x115 +#define DP_LINK_RATE_SET 0x115 /* eDP 1.4 */ #define DP_PSR_EN_CFG 0x170 /* XXX 1.2? */ # define DP_PSR_ENABLE (1 << 0) @@ -338,7 +339,7 @@ # define DP_SET_POWER_D3 0x2 # define DP_SET_POWER_MASK 0x3 -#define DP_EDP_DPCD_REV 0x700 +#define DP_EDP_DPCD_REV 0x700 /* eDP 1.2 */ #define DP_SIDEBAND_MSG_DOWN_REQ_BASE 0x1000 /* 1.2 MST */ #define DP_SIDEBAND_MSG_UP_REP_BASE 0x1200 /* 1.2 MST */ -- cgit v1.2.3 From 0e71244c1bb873f0e3be3b79d3436d7ef2601029 Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Wed, 25 Feb 2015 14:46:53 +0200 Subject: drm/dp: add DPCD definitions from eDP 1.2 Mostly display control related DPCD addresses. Signed-off-by: Jani Nikula Reviewed-by: Sonika Jindal Signed-off-by: Daniel Vetter --- include/drm/drm_dp_helper.h | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) (limited to 'include') diff --git a/include/drm/drm_dp_helper.h b/include/drm/drm_dp_helper.h index 98fefe45d158..a3ecaa06c9db 100644 --- a/include/drm/drm_dp_helper.h +++ b/include/drm/drm_dp_helper.h @@ -341,6 +341,38 @@ #define DP_EDP_DPCD_REV 0x700 /* eDP 1.2 */ +#define DP_EDP_GENERAL_CAP_1 0x701 + +#define DP_EDP_BACKLIGHT_ADJUSTMENT_CAP 0x702 + +#define DP_EDP_GENERAL_CAP_2 0x703 + +#define DP_EDP_DISPLAY_CONTROL_REGISTER 0x720 + +#define DP_EDP_BACKLIGHT_MODE_SET_REGISTER 0x721 + +#define DP_EDP_BACKLIGHT_BRIGHTNESS_MSB 0x722 +#define DP_EDP_BACKLIGHT_BRIGHTNESS_LSB 0x723 + +#define DP_EDP_PWMGEN_BIT_COUNT 0x724 +#define DP_EDP_PWMGEN_BIT_COUNT_CAP_MIN 0x725 +#define DP_EDP_PWMGEN_BIT_COUNT_CAP_MAX 0x726 + +#define DP_EDP_BACKLIGHT_CONTROL_STATUS 0x727 + +#define DP_EDP_BACKLIGHT_FREQ_SET 0x728 + +#define DP_EDP_BACKLIGHT_FREQ_CAP_MIN_MSB 0x72a +#define DP_EDP_BACKLIGHT_FREQ_CAP_MIN_MID 0x72b +#define DP_EDP_BACKLIGHT_FREQ_CAP_MIN_LSB 0x72c + +#define DP_EDP_BACKLIGHT_FREQ_CAP_MAX_MSB 0x72d +#define DP_EDP_BACKLIGHT_FREQ_CAP_MAX_MID 0x72e +#define DP_EDP_BACKLIGHT_FREQ_CAP_MAX_LSB 0x72f + +#define DP_EDP_DBC_MINIMUM_BRIGHTNESS_SET 0x732 +#define DP_EDP_DBC_MAXIMUM_BRIGHTNESS_SET 0x733 + #define DP_SIDEBAND_MSG_DOWN_REQ_BASE 0x1000 /* 1.2 MST */ #define DP_SIDEBAND_MSG_UP_REP_BASE 0x1200 /* 1.2 MST */ #define DP_SIDEBAND_MSG_DOWN_REP_BASE 0x1400 /* 1.2 MST */ -- cgit v1.2.3 From 9474675afa9fe9d1145df0acb9fc15b6ad56a9f9 Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Fri, 27 Feb 2015 13:10:38 +0200 Subject: drm/dp: add DPCD definitions from DP 1.1 and 1.2a Add a number of DPCD definitions from DP 1.1 and 1.2a. v2: drop wrong DP version reference, rename DP training set macros (Sonika). Reviewed-by: Sonika Jindal Signed-off-by: Jani Nikula Signed-off-by: Daniel Vetter --- include/drm/drm_dp_helper.h | 93 ++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 88 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/include/drm/drm_dp_helper.h b/include/drm/drm_dp_helper.h index a3ecaa06c9db..319d5edfb3b5 100644 --- a/include/drm/drm_dp_helper.h +++ b/include/drm/drm_dp_helper.h @@ -92,6 +92,15 @@ # define DP_MSA_TIMING_PAR_IGNORED (1 << 6) /* eDP */ # define DP_OUI_SUPPORT (1 << 7) +#define DP_RECEIVE_PORT_0_CAP_0 0x008 +# define DP_LOCAL_EDID_PRESENT (1 << 1) +# define DP_ASSOCIATED_TO_PRECEDING_PORT (1 << 2) + +#define DP_RECEIVE_PORT_0_BUFFER_SIZE 0x009 + +#define DP_RECEIVE_PORT_1_CAP_0 0x00a +#define DP_RECEIVE_PORT_1_BUFFER_SIZE 0x00b + #define DP_I2C_SPEED_CAP 0x00c /* DPI */ # define DP_I2C_SPEED_1K 0x01 # define DP_I2C_SPEED_5K 0x02 @@ -101,10 +110,16 @@ # define DP_I2C_SPEED_1M 0x20 #define DP_EDP_CONFIGURATION_CAP 0x00d /* XXX 1.2? */ +# define DP_ALTERNATE_SCRAMBLER_RESET_CAP (1 << 0) +# define DP_FRAMING_CHANGE_CAP (1 << 1) # define DP_DPCD_DISPLAY_CONTROL_CAPABLE (1 << 3) /* edp v1.2 or higher */ #define DP_TRAINING_AUX_RD_INTERVAL 0x00e /* XXX 1.2? */ +#define DP_ADAPTER_CAP 0x00f /* 1.2 */ +# define DP_FORCE_LOAD_SENSE_CAP (1 << 0) +# define DP_ALTERNATE_I2C_PATTERN_CAP (1 << 1) + #define DP_SUPPORTED_LINK_RATES 0x010 /* eDP 1.4 */ # define DP_MAX_SUPPORTED_RATES 8 /* 16-bit little-endian */ @@ -115,6 +130,44 @@ #define DP_MSTM_CAP 0x021 /* 1.2 */ # define DP_MST_CAP (1 << 0) +#define DP_NUMBER_OF_AUDIO_ENDPOINTS 0x022 /* 1.2 */ + +/* AV_SYNC_DATA_BLOCK 1.2 */ +#define DP_AV_GRANULARITY 0x023 +# define DP_AG_FACTOR_MASK (0xf << 0) +# define DP_AG_FACTOR_3MS (0 << 0) +# define DP_AG_FACTOR_2MS (1 << 0) +# define DP_AG_FACTOR_1MS (2 << 0) +# define DP_AG_FACTOR_500US (3 << 0) +# define DP_AG_FACTOR_200US (4 << 0) +# define DP_AG_FACTOR_100US (5 << 0) +# define DP_AG_FACTOR_10US (6 << 0) +# define DP_AG_FACTOR_1US (7 << 0) +# define DP_VG_FACTOR_MASK (0xf << 4) +# define DP_VG_FACTOR_3MS (0 << 4) +# define DP_VG_FACTOR_2MS (1 << 4) +# define DP_VG_FACTOR_1MS (2 << 4) +# define DP_VG_FACTOR_500US (3 << 4) +# define DP_VG_FACTOR_200US (4 << 4) +# define DP_VG_FACTOR_100US (5 << 4) + +#define DP_AUD_DEC_LAT0 0x024 +#define DP_AUD_DEC_LAT1 0x025 + +#define DP_AUD_PP_LAT0 0x026 +#define DP_AUD_PP_LAT1 0x027 + +#define DP_VID_INTER_LAT 0x028 + +#define DP_VID_PROG_LAT 0x029 + +#define DP_REP_LAT 0x02a + +#define DP_AUD_DEL_INS0 0x02b +#define DP_AUD_DEL_INS1 0x02c +#define DP_AUD_DEL_INS2 0x02d +/* End of AV_SYNC_DATA_BLOCK */ + #define DP_GUID 0x030 /* 1.2 */ #define DP_PSR_SUPPORT 0x070 /* XXX 1.2? */ @@ -173,11 +226,12 @@ # define DP_TRAINING_PATTERN_3 3 /* 1.2 */ # define DP_TRAINING_PATTERN_MASK 0x3 -# define DP_LINK_QUAL_PATTERN_DISABLE (0 << 2) -# define DP_LINK_QUAL_PATTERN_D10_2 (1 << 2) -# define DP_LINK_QUAL_PATTERN_ERROR_RATE (2 << 2) -# define DP_LINK_QUAL_PATTERN_PRBS7 (3 << 2) -# define DP_LINK_QUAL_PATTERN_MASK (3 << 2) +/* DPCD 1.1 only. For DPCD >= 1.2 see per-lane DP_LINK_QUAL_LANEn_SET */ +# define DP_LINK_QUAL_PATTERN_11_DISABLE (0 << 2) +# define DP_LINK_QUAL_PATTERN_11_D10_2 (1 << 2) +# define DP_LINK_QUAL_PATTERN_11_ERROR_RATE (2 << 2) +# define DP_LINK_QUAL_PATTERN_11_PRBS7 (3 << 2) +# define DP_LINK_QUAL_PATTERN_11_MASK (3 << 2) # define DP_RECOVERED_CLOCK_OUT_EN (1 << 4) # define DP_LINK_SCRAMBLING_DISABLE (1 << 5) @@ -220,14 +274,43 @@ /* bitmask as for DP_I2C_SPEED_CAP */ #define DP_EDP_CONFIGURATION_SET 0x10a /* XXX 1.2? */ +# define DP_ALTERNATE_SCRAMBLER_RESET_ENABLE (1 << 0) +# define DP_FRAMING_CHANGE_ENABLE (1 << 1) +# define DP_PANEL_SELF_TEST_ENABLE (1 << 7) + +#define DP_LINK_QUAL_LANE0_SET 0x10b /* DPCD >= 1.2 */ +#define DP_LINK_QUAL_LANE1_SET 0x10c +#define DP_LINK_QUAL_LANE2_SET 0x10d +#define DP_LINK_QUAL_LANE3_SET 0x10e +# define DP_LINK_QUAL_PATTERN_DISABLE 0 +# define DP_LINK_QUAL_PATTERN_D10_2 1 +# define DP_LINK_QUAL_PATTERN_ERROR_RATE 2 +# define DP_LINK_QUAL_PATTERN_PRBS7 3 +# define DP_LINK_QUAL_PATTERN_80BIT_CUSTOM 4 +# define DP_LINK_QUAL_PATTERN_HBR2_EYE 5 +# define DP_LINK_QUAL_PATTERN_MASK 7 + +#define DP_TRAINING_LANE0_1_SET2 0x10f +#define DP_TRAINING_LANE2_3_SET2 0x110 +# define DP_LANE02_POST_CURSOR2_SET_MASK (3 << 0) +# define DP_LANE02_MAX_POST_CURSOR2_REACHED (1 << 2) +# define DP_LANE13_POST_CURSOR2_SET_MASK (3 << 4) +# define DP_LANE13_MAX_POST_CURSOR2_REACHED (1 << 6) #define DP_MSTM_CTRL 0x111 /* 1.2 */ # define DP_MST_EN (1 << 0) # define DP_UP_REQ_EN (1 << 1) # define DP_UPSTREAM_IS_SRC (1 << 2) +#define DP_AUDIO_DELAY0 0x112 /* 1.2 */ +#define DP_AUDIO_DELAY1 0x113 +#define DP_AUDIO_DELAY2 0x114 + #define DP_LINK_RATE_SET 0x115 /* eDP 1.4 */ +#define DP_UPSTREAM_DEVICE_DP_PWR_NEED 0x118 /* 1.2 */ +# define DP_PWR_NOT_NEEDED (1 << 0) + #define DP_PSR_EN_CFG 0x170 /* XXX 1.2? */ # define DP_PSR_ENABLE (1 << 0) # define DP_PSR_MAIN_LINK_ACTIVE (1 << 1) -- cgit v1.2.3 From 6b1e3f615482f308ae74af13fae6c1d9191d2906 Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Fri, 27 Feb 2015 13:11:14 +0200 Subject: drm/dp: add DPCD definitions from eDP 1.4 Add a number of DPCD definitions from eDP 1.4. v2: s/DP_ALPM_LOCK_TIMEOUT_ERROR_STATUS/DP_ALPM_LOCK_TIMEOUT_ERROR/ (Sonika) Signed-off-by: Jani Nikula Reviewed-by: Sonika Jindal Signed-off-by: Daniel Vetter --- include/drm/drm_dp_helper.h | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) (limited to 'include') diff --git a/include/drm/drm_dp_helper.h b/include/drm/drm_dp_helper.h index 319d5edfb3b5..c5fdc2d3ca97 100644 --- a/include/drm/drm_dp_helper.h +++ b/include/drm/drm_dp_helper.h @@ -168,10 +168,18 @@ #define DP_AUD_DEL_INS2 0x02d /* End of AV_SYNC_DATA_BLOCK */ +#define DP_RECEIVER_ALPM_CAP 0x02e /* eDP 1.4 */ +# define DP_ALPM_CAP (1 << 0) + +#define DP_SINK_DEVICE_AUX_FRAME_SYNC_CAP 0x02f /* eDP 1.4 */ +# define DP_AUX_FRAME_SYNC_CAP (1 << 0) + #define DP_GUID 0x030 /* 1.2 */ #define DP_PSR_SUPPORT 0x070 /* XXX 1.2? */ # define DP_PSR_IS_SUPPORTED 1 +# define DP_PSR2_IS_SUPPORTED 2 /* eDP 1.4 */ + #define DP_PSR_CAPS 0x071 /* XXX 1.2? */ # define DP_PSR_NO_TRAIN_ON_EXIT 1 # define DP_PSR_SETUP_TIME_330 (0 << 1) @@ -211,6 +219,7 @@ /* link configuration */ #define DP_LINK_BW_SET 0x100 +# define DP_LINK_RATE_TABLE 0x00 /* eDP 1.4 */ # define DP_LINK_BW_1_62 0x06 # define DP_LINK_BW_2_7 0x0a # define DP_LINK_BW_5_4 0x14 /* 1.2 */ @@ -307,15 +316,30 @@ #define DP_AUDIO_DELAY2 0x114 #define DP_LINK_RATE_SET 0x115 /* eDP 1.4 */ +# define DP_LINK_RATE_SET_SHIFT 0 +# define DP_LINK_RATE_SET_MASK (7 << 0) + +#define DP_RECEIVER_ALPM_CONFIG 0x116 /* eDP 1.4 */ +# define DP_ALPM_ENABLE (1 << 0) +# define DP_ALPM_LOCK_ERROR_IRQ_HPD_ENABLE (1 << 1) + +#define DP_SINK_DEVICE_AUX_FRAME_SYNC_CONF 0x117 /* eDP 1.4 */ +# define DP_AUX_FRAME_SYNC_ENABLE (1 << 0) +# define DP_IRQ_HPD_ENABLE (1 << 1) #define DP_UPSTREAM_DEVICE_DP_PWR_NEED 0x118 /* 1.2 */ # define DP_PWR_NOT_NEEDED (1 << 0) +#define DP_AUX_FRAME_SYNC_VALUE 0x15c /* eDP 1.4 */ +# define DP_AUX_FRAME_SYNC_VALID (1 << 0) + #define DP_PSR_EN_CFG 0x170 /* XXX 1.2? */ # define DP_PSR_ENABLE (1 << 0) # define DP_PSR_MAIN_LINK_ACTIVE (1 << 1) # define DP_PSR_CRC_VERIFICATION (1 << 2) # define DP_PSR_FRAME_CAPTURE (1 << 3) +# define DP_PSR_SELECTIVE_UPDATE (1 << 4) +# define DP_PSR_IRQ_HPD_WITH_CRC_ERRORS (1 << 5) #define DP_ADAPTER_CTRL 0x1a0 # define DP_ADAPTER_CTRL_FORCE_LOAD_SENSE (1 << 0) @@ -423,6 +447,10 @@ # define DP_SET_POWER_MASK 0x3 #define DP_EDP_DPCD_REV 0x700 /* eDP 1.2 */ +# define DP_EDP_11 0x00 +# define DP_EDP_12 0x01 +# define DP_EDP_13 0x02 +# define DP_EDP_14 0x03 #define DP_EDP_GENERAL_CAP_1 0x701 @@ -430,6 +458,8 @@ #define DP_EDP_GENERAL_CAP_2 0x703 +#define DP_EDP_GENERAL_CAP_3 0x704 /* eDP 1.4 */ + #define DP_EDP_DISPLAY_CONTROL_REGISTER 0x720 #define DP_EDP_BACKLIGHT_MODE_SET_REGISTER 0x721 @@ -456,6 +486,9 @@ #define DP_EDP_DBC_MINIMUM_BRIGHTNESS_SET 0x732 #define DP_EDP_DBC_MAXIMUM_BRIGHTNESS_SET 0x733 +#define DP_EDP_REGIONAL_BACKLIGHT_BASE 0x740 /* eDP 1.4 */ +#define DP_EDP_REGIONAL_BACKLIGHT_0 0x741 /* eDP 1.4 */ + #define DP_SIDEBAND_MSG_DOWN_REQ_BASE 0x1000 /* 1.2 MST */ #define DP_SIDEBAND_MSG_UP_REP_BASE 0x1200 /* 1.2 MST */ #define DP_SIDEBAND_MSG_DOWN_REP_BASE 0x1400 /* 1.2 MST */ @@ -474,6 +507,7 @@ #define DP_PSR_ERROR_STATUS 0x2006 /* XXX 1.2? */ # define DP_PSR_LINK_CRC_ERROR (1 << 0) # define DP_PSR_RFB_STORAGE_ERROR (1 << 1) +# define DP_PSR_VSC_SDP_UNCORRECTABLE_ERROR (1 << 2) /* eDP 1.4 */ #define DP_PSR_ESI 0x2007 /* XXX 1.2? */ # define DP_PSR_CAPS_CHANGE (1 << 0) @@ -487,6 +521,9 @@ # define DP_PSR_SINK_INTERNAL_ERROR 7 # define DP_PSR_SINK_STATE_MASK 0x07 +#define DP_RECEIVER_ALPM_STATUS 0x200b /* eDP 1.4 */ +# define DP_ALPM_LOCK_TIMEOUT_ERROR (1 << 0) + /* DP 1.2 Sideband message defines */ /* peer device type - DP 1.2a Table 2-92 */ #define DP_PEER_DEVICE_NONE 0x0 -- cgit v1.2.3 From ead8610d42105a3d01f755522f11b96c60dc648f Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Thu, 5 Mar 2015 02:25:43 +0200 Subject: drm: Share plane pixel format check code between legacy and atomic Both the legacy and atomic helpers need to check whether a plane supports a given pixel format. The code is currently duplicated, share it. Signed-off-by: Laurent Pinchart [danvet: Slightly extend the docbook.] Signed-off-by: Daniel Vetter --- drivers/gpu/drm/drm_atomic.c | 10 ++++------ drivers/gpu/drm/drm_crtc.c | 29 +++++++++++++++++++++++------ include/drm/drm_crtc.h | 2 ++ 3 files changed, 29 insertions(+), 12 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c index 7ca54cb6b15b..a6caaae40b9e 100644 --- a/drivers/gpu/drm/drm_atomic.c +++ b/drivers/gpu/drm/drm_atomic.c @@ -475,7 +475,7 @@ static int drm_atomic_plane_check(struct drm_plane *plane, struct drm_plane_state *state) { unsigned int fb_width, fb_height; - unsigned int i; + int ret; /* either *both* CRTC and FB must be set, or neither */ if (WARN_ON(state->crtc && !state->fb)) { @@ -497,13 +497,11 @@ static int drm_atomic_plane_check(struct drm_plane *plane, } /* Check whether this plane supports the fb pixel format. */ - for (i = 0; i < plane->format_count; i++) - if (state->fb->pixel_format == plane->format_types[i]) - break; - if (i == plane->format_count) { + ret = drm_plane_check_pixel_format(plane, state->fb->pixel_format); + if (ret) { DRM_DEBUG_ATOMIC("Invalid pixel format %s\n", drm_get_format_name(state->fb->pixel_format)); - return -EINVAL; + return ret; } /* Give drivers some help against integer overflows */ diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index c83e4db0adf7..447db50e6838 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -2408,6 +2408,27 @@ int drm_mode_getplane(struct drm_device *dev, void *data, return 0; } +/** + * drm_plane_check_pixel_format - Check if the plane supports the pixel format + * @plane: plane to check for format support + * @format: the pixel format + * + * Returns: + * Zero of @plane has @format in its list of supported pixel formats, -EINVAL + * otherwise. + */ +int drm_plane_check_pixel_format(const struct drm_plane *plane, u32 format) +{ + unsigned int i; + + for (i = 0; i < plane->format_count; i++) { + if (format == plane->format_types[i]) + return 0; + } + + return -EINVAL; +} + /* * setplane_internal - setplane handler for internal callers * @@ -2428,7 +2449,6 @@ static int __setplane_internal(struct drm_plane *plane, { int ret = 0; unsigned int fb_width, fb_height; - unsigned int i; /* No fb means shut it down */ if (!fb) { @@ -2451,13 +2471,10 @@ static int __setplane_internal(struct drm_plane *plane, } /* Check whether this plane supports the fb pixel format. */ - for (i = 0; i < plane->format_count; i++) - if (fb->pixel_format == plane->format_types[i]) - break; - if (i == plane->format_count) { + ret = drm_plane_check_pixel_format(plane, fb->pixel_format); + if (ret) { DRM_DEBUG_KMS("Invalid pixel format %s\n", drm_get_format_name(fb->pixel_format)); - ret = -EINVAL; goto out; } diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index b1465d6fbe94..da83d39e37d4 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h @@ -1263,6 +1263,8 @@ extern int drm_plane_init(struct drm_device *dev, extern void drm_plane_cleanup(struct drm_plane *plane); extern unsigned int drm_plane_index(struct drm_plane *plane); extern void drm_plane_force_disable(struct drm_plane *plane); +extern int drm_plane_check_pixel_format(const struct drm_plane *plane, + u32 format); extern void drm_crtc_get_hv_timing(const struct drm_display_mode *mode, int *hdisplay, int *vdisplay); extern int drm_crtc_check_viewport(const struct drm_crtc *crtc, -- cgit v1.2.3 From 3461b30b3e171e16498f3d7bc59ab703aec475c8 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Thu, 5 Mar 2015 10:32:44 +0100 Subject: drm/plane-helper: unexport drm_primary_helper_create_plane We shouldn't tempt driver writers into using this since it uses a default format list which is likely wrong. And when that's done we can simplify the code a bit, too. Noticed while reviewing a patch from Laurent. Cc: Laurent Pinchart Cc: Matt Roper Reviewed-by: Matt Roper Signed-off-by: Daniel Vetter --- drivers/gpu/drm/drm_plane_helper.c | 26 ++++---------------------- include/drm/drm_plane_helper.h | 4 ---- 2 files changed, 4 insertions(+), 26 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/drm_plane_helper.c b/drivers/gpu/drm/drm_plane_helper.c index 813a06627eb3..8e56783265e1 100644 --- a/drivers/gpu/drm/drm_plane_helper.c +++ b/drivers/gpu/drm/drm_plane_helper.c @@ -344,20 +344,7 @@ const struct drm_plane_funcs drm_primary_helper_funcs = { }; EXPORT_SYMBOL(drm_primary_helper_funcs); -/** - * drm_primary_helper_create_plane() - Create a generic primary plane - * @dev: drm device - * @formats: pixel formats supported, or NULL for a default safe list - * @num_formats: size of @formats; ignored if @formats is NULL - * - * Allocates and initializes a primary plane that can be used with the primary - * plane helpers. Drivers that wish to use driver-specific plane structures or - * provide custom handler functions may perform their own allocation and - * initialization rather than calling this function. - */ -struct drm_plane *drm_primary_helper_create_plane(struct drm_device *dev, - const uint32_t *formats, - int num_formats) +static struct drm_plane *create_primary_plane(struct drm_device *dev) { struct drm_plane *primary; int ret; @@ -368,15 +355,11 @@ struct drm_plane *drm_primary_helper_create_plane(struct drm_device *dev, return NULL; } - if (formats == NULL) { - formats = safe_modeset_formats; - num_formats = ARRAY_SIZE(safe_modeset_formats); - } - /* possible_crtc's will be filled in later by crtc_init */ ret = drm_universal_plane_init(dev, primary, 0, &drm_primary_helper_funcs, - formats, num_formats, + safe_modeset_formats, + ARRAY_SIZE(safe_modeset_formats), DRM_PLANE_TYPE_PRIMARY); if (ret) { kfree(primary); @@ -385,7 +368,6 @@ struct drm_plane *drm_primary_helper_create_plane(struct drm_device *dev, return primary; } -EXPORT_SYMBOL(drm_primary_helper_create_plane); /** * drm_crtc_init - Legacy CRTC initialization function @@ -404,7 +386,7 @@ int drm_crtc_init(struct drm_device *dev, struct drm_crtc *crtc, { struct drm_plane *primary; - primary = drm_primary_helper_create_plane(dev, NULL, 0); + primary = create_primary_plane(dev); return drm_crtc_init_with_planes(dev, crtc, primary, NULL, funcs); } EXPORT_SYMBOL(drm_crtc_init); diff --git a/include/drm/drm_plane_helper.h b/include/drm/drm_plane_helper.h index 72ddab02ebd9..e48157a5a59c 100644 --- a/include/drm/drm_plane_helper.h +++ b/include/drm/drm_plane_helper.h @@ -100,10 +100,6 @@ extern int drm_primary_helper_update(struct drm_plane *plane, extern int drm_primary_helper_disable(struct drm_plane *plane); extern void drm_primary_helper_destroy(struct drm_plane *plane); extern const struct drm_plane_funcs drm_primary_helper_funcs; -extern struct drm_plane *drm_primary_helper_create_plane(struct drm_device *dev, - const uint32_t *formats, - int num_formats); - int drm_plane_helper_update(struct drm_plane *plane, struct drm_crtc *crtc, struct drm_framebuffer *fb, -- cgit v1.2.3 From 7eb5f302bbe78b88da8b2008c502c1975e75db05 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 9 Mar 2015 10:41:07 +0200 Subject: drm: Check in setcrtc if the primary plane supports the fb pixel format Drivers implementing the universal planes API report the list of supported pixel formats for the primary plane. Make sure the fb passed to the setcrtc ioctl is compatible. Drivers not implementing the universal planes API will have no format reported for the primary plane, skip the check in that case. Signed-off-by: Laurent Pinchart Signed-off-by: Daniel Vetter --- drivers/gpu/drm/drm_crtc.c | 17 +++++++++++++++++ drivers/gpu/drm/drm_plane_helper.c | 5 +++++ include/drm/drm_crtc.h | 2 ++ 3 files changed, 24 insertions(+) (limited to 'include') diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index 447db50e6838..5785336695ca 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -2798,6 +2798,23 @@ int drm_mode_setcrtc(struct drm_device *dev, void *data, drm_mode_set_crtcinfo(mode, CRTC_INTERLACE_HALVE_V); + /* + * Check whether the primary plane supports the fb pixel format. + * Drivers not implementing the universal planes API use a + * default formats list provided by the DRM core which doesn't + * match real hardware capabilities. Skip the check in that + * case. + */ + if (!crtc->primary->format_default) { + ret = drm_plane_check_pixel_format(crtc->primary, + fb->pixel_format); + if (ret) { + DRM_DEBUG_KMS("Invalid pixel format %s\n", + drm_get_format_name(fb->pixel_format)); + goto out; + } + } + ret = drm_crtc_check_viewport(crtc, crtc_req->x, crtc_req->y, mode, fb); if (ret) diff --git a/drivers/gpu/drm/drm_plane_helper.c b/drivers/gpu/drm/drm_plane_helper.c index 8e56783265e1..b62b03635050 100644 --- a/drivers/gpu/drm/drm_plane_helper.c +++ b/drivers/gpu/drm/drm_plane_helper.c @@ -353,6 +353,11 @@ static struct drm_plane *create_primary_plane(struct drm_device *dev) if (primary == NULL) { DRM_DEBUG_KMS("Failed to allocate primary plane\n"); return NULL; + /* + * Remove the format_default field from drm_plane when dropping + * this helper. + */ + primary->format_default = true; } /* possible_crtc's will be filled in later by crtc_init */ diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index da83d39e37d4..adc9ea5acf02 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h @@ -830,6 +830,7 @@ enum drm_plane_type { * @possible_crtcs: pipes this plane can be bound to * @format_types: array of formats supported by this plane * @format_count: number of formats supported + * @format_default: driver hasn't supplied supported formats for the plane * @crtc: currently bound CRTC * @fb: currently bound fb * @old_fb: Temporary tracking of the old fb while a modeset is ongoing. Used by @@ -850,6 +851,7 @@ struct drm_plane { uint32_t possible_crtcs; uint32_t *format_types; uint32_t format_count; + bool format_default; struct drm_crtc *crtc; struct drm_framebuffer *fb; -- cgit v1.2.3 From a03a8dbe20eff6d57aae3147577bf84b52aba4e6 Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Mon, 9 Mar 2015 23:04:15 +0100 Subject: netfilter: fix sparse warnings in reject handling make C=1 CF=-D__CHECK_ENDIAN__ shows following: net/bridge/netfilter/nft_reject_bridge.c:65:50: warning: incorrect type in argument 3 (different base types) net/bridge/netfilter/nft_reject_bridge.c:65:50: expected restricted __be16 [usertype] protocol [..] net/bridge/netfilter/nft_reject_bridge.c:102:37: warning: cast from restricted __be16 net/bridge/netfilter/nft_reject_bridge.c:102:37: warning: incorrect type in argument 1 (different base types) [..] net/bridge/netfilter/nft_reject_bridge.c:121:50: warning: incorrect type in argument 3 (different base types) [..] net/bridge/netfilter/nft_reject_bridge.c:168:52: warning: incorrect type in argument 3 (different base types) [..] net/bridge/netfilter/nft_reject_bridge.c:233:52: warning: incorrect type in argument 3 (different base types) [..] Caused by two (harmless) errors: 1. htons() instead of ntohs() 2. __be16 for protocol in nf_reject_ipXhdr_put API, use u8 instead. Reported-by: kbuild test robot Signed-off-by: Florian Westphal Signed-off-by: Pablo Neira Ayuso --- include/net/netfilter/ipv4/nf_reject.h | 2 +- include/net/netfilter/ipv6/nf_reject.h | 2 +- net/bridge/netfilter/nft_reject_bridge.c | 2 +- net/ipv4/netfilter/nf_reject_ipv4.c | 2 +- net/ipv6/netfilter/nf_reject_ipv6.c | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/include/net/netfilter/ipv4/nf_reject.h b/include/net/netfilter/ipv4/nf_reject.h index 864127573c32..77862c3645f0 100644 --- a/include/net/netfilter/ipv4/nf_reject.h +++ b/include/net/netfilter/ipv4/nf_reject.h @@ -12,7 +12,7 @@ const struct tcphdr *nf_reject_ip_tcphdr_get(struct sk_buff *oldskb, struct tcphdr *_oth, int hook); struct iphdr *nf_reject_iphdr_put(struct sk_buff *nskb, const struct sk_buff *oldskb, - __be16 protocol, int ttl); + __u8 protocol, int ttl); void nf_reject_ip_tcphdr_put(struct sk_buff *nskb, const struct sk_buff *oldskb, const struct tcphdr *oth); diff --git a/include/net/netfilter/ipv6/nf_reject.h b/include/net/netfilter/ipv6/nf_reject.h index 0ae445d3f217..0ea4fa37db16 100644 --- a/include/net/netfilter/ipv6/nf_reject.h +++ b/include/net/netfilter/ipv6/nf_reject.h @@ -13,7 +13,7 @@ const struct tcphdr *nf_reject_ip6_tcphdr_get(struct sk_buff *oldskb, unsigned int *otcplen, int hook); struct ipv6hdr *nf_reject_ip6hdr_put(struct sk_buff *nskb, const struct sk_buff *oldskb, - __be16 protocol, int hoplimit); + __u8 protocol, int hoplimit); void nf_reject_ip6_tcphdr_put(struct sk_buff *nskb, const struct sk_buff *oldskb, const struct tcphdr *oth, unsigned int otcplen); diff --git a/net/bridge/netfilter/nft_reject_bridge.c b/net/bridge/netfilter/nft_reject_bridge.c index 5c6c96585acd..54a2fdf0f457 100644 --- a/net/bridge/netfilter/nft_reject_bridge.c +++ b/net/bridge/netfilter/nft_reject_bridge.c @@ -99,7 +99,7 @@ static void nft_reject_br_send_v4_unreach(struct sk_buff *oldskb, if (!pskb_may_pull(oldskb, len)) return; - if (pskb_trim_rcsum(oldskb, htons(ip_hdr(oldskb)->tot_len))) + if (pskb_trim_rcsum(oldskb, ntohs(ip_hdr(oldskb)->tot_len))) return; if (ip_hdr(oldskb)->protocol == IPPROTO_TCP || diff --git a/net/ipv4/netfilter/nf_reject_ipv4.c b/net/ipv4/netfilter/nf_reject_ipv4.c index b7405eb7f1ef..c5b794da51a9 100644 --- a/net/ipv4/netfilter/nf_reject_ipv4.c +++ b/net/ipv4/netfilter/nf_reject_ipv4.c @@ -43,7 +43,7 @@ EXPORT_SYMBOL_GPL(nf_reject_ip_tcphdr_get); struct iphdr *nf_reject_iphdr_put(struct sk_buff *nskb, const struct sk_buff *oldskb, - __be16 protocol, int ttl) + __u8 protocol, int ttl) { struct iphdr *niph, *oiph = ip_hdr(oldskb); diff --git a/net/ipv6/netfilter/nf_reject_ipv6.c b/net/ipv6/netfilter/nf_reject_ipv6.c index 68e0bb4db1bf..3afdce03d94e 100644 --- a/net/ipv6/netfilter/nf_reject_ipv6.c +++ b/net/ipv6/netfilter/nf_reject_ipv6.c @@ -65,7 +65,7 @@ EXPORT_SYMBOL_GPL(nf_reject_ip6_tcphdr_get); struct ipv6hdr *nf_reject_ip6hdr_put(struct sk_buff *nskb, const struct sk_buff *oldskb, - __be16 protocol, int hoplimit) + __u8 protocol, int hoplimit) { struct ipv6hdr *ip6h; const struct ipv6hdr *oip6h = ipv6_hdr(oldskb); -- cgit v1.2.3 From 491da2a477077357c8206a601559e2ea58f224db Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Tue, 10 Mar 2015 07:15:52 -0700 Subject: net: constify sock_diag_check_cookie() sock_diag_check_cookie() second parameter is constant Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- include/linux/sock_diag.h | 2 +- net/core/sock_diag.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/linux/sock_diag.h b/include/linux/sock_diag.h index 46cca4c06848..b5ad7d35a636 100644 --- a/include/linux/sock_diag.h +++ b/include/linux/sock_diag.h @@ -19,7 +19,7 @@ void sock_diag_unregister(const struct sock_diag_handler *h); void sock_diag_register_inet_compat(int (*fn)(struct sk_buff *skb, struct nlmsghdr *nlh)); void sock_diag_unregister_inet_compat(int (*fn)(struct sk_buff *skb, struct nlmsghdr *nlh)); -int sock_diag_check_cookie(void *sk, __u32 *cookie); +int sock_diag_check_cookie(void *sk, const __u32 *cookie); void sock_diag_save_cookie(void *sk, __u32 *cookie); int sock_diag_put_meminfo(struct sock *sk, struct sk_buff *skb, int attr); diff --git a/net/core/sock_diag.c b/net/core/sock_diag.c index ad704c757bb4..96e70ee05a8d 100644 --- a/net/core/sock_diag.c +++ b/net/core/sock_diag.c @@ -13,7 +13,7 @@ static const struct sock_diag_handler *sock_diag_handlers[AF_MAX]; static int (*inet_rcv_compat)(struct sk_buff *skb, struct nlmsghdr *nlh); static DEFINE_MUTEX(sock_diag_table_mutex); -int sock_diag_check_cookie(void *sk, __u32 *cookie) +int sock_diag_check_cookie(void *sk, const __u32 *cookie) { if ((cookie[0] != INET_DIAG_NOCOOKIE || cookie[1] != INET_DIAG_NOCOOKIE) && -- cgit v1.2.3 From 34160ea3f9c96b5ae71a11459f9b9f6c298b8930 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Tue, 10 Mar 2015 07:15:54 -0700 Subject: inet_diag: add const to inet_diag_req_v2 diag dumpers should not modify the request. Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- include/linux/inet_diag.h | 43 ++++++++++++++++++++++--------------------- net/dccp/diag.c | 7 ++++--- net/ipv4/inet_diag.c | 22 +++++++++++----------- net/ipv4/tcp_diag.c | 4 ++-- net/ipv4/udp_diag.c | 22 +++++++++++++--------- 5 files changed, 52 insertions(+), 46 deletions(-) (limited to 'include') diff --git a/include/linux/inet_diag.h b/include/linux/inet_diag.h index 46da02410a09..ac48b10c9395 100644 --- a/include/linux/inet_diag.h +++ b/include/linux/inet_diag.h @@ -11,33 +11,34 @@ struct sk_buff; struct netlink_callback; struct inet_diag_handler { - void (*dump)(struct sk_buff *skb, - struct netlink_callback *cb, - struct inet_diag_req_v2 *r, - struct nlattr *bc); - - int (*dump_one)(struct sk_buff *in_skb, - const struct nlmsghdr *nlh, - struct inet_diag_req_v2 *req); - - void (*idiag_get_info)(struct sock *sk, - struct inet_diag_msg *r, - void *info); - __u16 idiag_type; + void (*dump)(struct sk_buff *skb, + struct netlink_callback *cb, + const struct inet_diag_req_v2 *r, + struct nlattr *bc); + + int (*dump_one)(struct sk_buff *in_skb, + const struct nlmsghdr *nlh, + const struct inet_diag_req_v2 *req); + + void (*idiag_get_info)(struct sock *sk, + struct inet_diag_msg *r, + void *info); + __u16 idiag_type; }; struct inet_connection_sock; int inet_sk_diag_fill(struct sock *sk, struct inet_connection_sock *icsk, - struct sk_buff *skb, struct inet_diag_req_v2 *req, - struct user_namespace *user_ns, - u32 pid, u32 seq, u16 nlmsg_flags, - const struct nlmsghdr *unlh); + struct sk_buff *skb, const struct inet_diag_req_v2 *req, + struct user_namespace *user_ns, + u32 pid, u32 seq, u16 nlmsg_flags, + const struct nlmsghdr *unlh); void inet_diag_dump_icsk(struct inet_hashinfo *h, struct sk_buff *skb, - struct netlink_callback *cb, struct inet_diag_req_v2 *r, - struct nlattr *bc); + struct netlink_callback *cb, + const struct inet_diag_req_v2 *r, + struct nlattr *bc); int inet_diag_dump_one_icsk(struct inet_hashinfo *hashinfo, - struct sk_buff *in_skb, const struct nlmsghdr *nlh, - struct inet_diag_req_v2 *req); + struct sk_buff *in_skb, const struct nlmsghdr *nlh, + const struct inet_diag_req_v2 *req); int inet_diag_bc_sk(const struct nlattr *_bc, struct sock *sk); diff --git a/net/dccp/diag.c b/net/dccp/diag.c index 028fc43aacbd..5a45f8de5d99 100644 --- a/net/dccp/diag.c +++ b/net/dccp/diag.c @@ -49,13 +49,14 @@ static void dccp_diag_get_info(struct sock *sk, struct inet_diag_msg *r, } static void dccp_diag_dump(struct sk_buff *skb, struct netlink_callback *cb, - struct inet_diag_req_v2 *r, struct nlattr *bc) + const struct inet_diag_req_v2 *r, struct nlattr *bc) { inet_diag_dump_icsk(&dccp_hashinfo, skb, cb, r, bc); } -static int dccp_diag_dump_one(struct sk_buff *in_skb, const struct nlmsghdr *nlh, - struct inet_diag_req_v2 *req) +static int dccp_diag_dump_one(struct sk_buff *in_skb, + const struct nlmsghdr *nlh, + const struct inet_diag_req_v2 *req) { return inet_diag_dump_one_icsk(&dccp_hashinfo, in_skb, nlh, req); } diff --git a/net/ipv4/inet_diag.c b/net/ipv4/inet_diag.c index cd261f6e3abb..ac3bfb458afd 100644 --- a/net/ipv4/inet_diag.c +++ b/net/ipv4/inet_diag.c @@ -71,7 +71,7 @@ static void inet_diag_unlock_handler(const struct inet_diag_handler *handler) } int inet_sk_diag_fill(struct sock *sk, struct inet_connection_sock *icsk, - struct sk_buff *skb, struct inet_diag_req_v2 *req, + struct sk_buff *skb, const struct inet_diag_req_v2 *req, struct user_namespace *user_ns, u32 portid, u32 seq, u16 nlmsg_flags, const struct nlmsghdr *unlh) @@ -212,7 +212,7 @@ EXPORT_SYMBOL_GPL(inet_sk_diag_fill); static int inet_csk_diag_fill(struct sock *sk, struct sk_buff *skb, - struct inet_diag_req_v2 *req, + const struct inet_diag_req_v2 *req, struct user_namespace *user_ns, u32 portid, u32 seq, u16 nlmsg_flags, const struct nlmsghdr *unlh) @@ -223,7 +223,7 @@ static int inet_csk_diag_fill(struct sock *sk, static int inet_twsk_diag_fill(struct inet_timewait_sock *tw, struct sk_buff *skb, - struct inet_diag_req_v2 *req, + const struct inet_diag_req_v2 *req, u32 portid, u32 seq, u16 nlmsg_flags, const struct nlmsghdr *unlh) { @@ -277,7 +277,7 @@ static int inet_twsk_diag_fill(struct inet_timewait_sock *tw, } static int sk_diag_fill(struct sock *sk, struct sk_buff *skb, - struct inet_diag_req_v2 *r, + const struct inet_diag_req_v2 *r, struct user_namespace *user_ns, u32 portid, u32 seq, u16 nlmsg_flags, const struct nlmsghdr *unlh) @@ -293,7 +293,7 @@ static int sk_diag_fill(struct sock *sk, struct sk_buff *skb, int inet_diag_dump_one_icsk(struct inet_hashinfo *hashinfo, struct sk_buff *in_skb, const struct nlmsghdr *nlh, - struct inet_diag_req_v2 *req) + const struct inet_diag_req_v2 *req) { struct net *net = sock_net(in_skb->sk); struct sk_buff *rep; @@ -358,7 +358,7 @@ EXPORT_SYMBOL_GPL(inet_diag_dump_one_icsk); static int inet_diag_get_exact(struct sk_buff *in_skb, const struct nlmsghdr *nlh, - struct inet_diag_req_v2 *req) + const struct inet_diag_req_v2 *req) { const struct inet_diag_handler *handler; int err; @@ -626,7 +626,7 @@ static int inet_diag_bc_audit(const void *bytecode, int bytecode_len) static int inet_csk_diag_dump(struct sock *sk, struct sk_buff *skb, struct netlink_callback *cb, - struct inet_diag_req_v2 *r, + const struct inet_diag_req_v2 *r, const struct nlattr *bc) { if (!inet_diag_bc_sk(bc, sk)) @@ -667,7 +667,7 @@ static void twsk_build_assert(void) static int inet_twsk_diag_dump(struct sock *sk, struct sk_buff *skb, struct netlink_callback *cb, - struct inet_diag_req_v2 *r, + const struct inet_diag_req_v2 *r, const struct nlattr *bc) { twsk_build_assert(); @@ -770,7 +770,7 @@ static int inet_diag_fill_req(struct sk_buff *skb, struct sock *sk, static int inet_diag_dump_reqs(struct sk_buff *skb, struct sock *sk, struct netlink_callback *cb, - struct inet_diag_req_v2 *r, + const struct inet_diag_req_v2 *r, const struct nlattr *bc) { struct inet_connection_sock *icsk = inet_csk(sk); @@ -842,7 +842,7 @@ out: void inet_diag_dump_icsk(struct inet_hashinfo *hashinfo, struct sk_buff *skb, struct netlink_callback *cb, - struct inet_diag_req_v2 *r, struct nlattr *bc) + const struct inet_diag_req_v2 *r, struct nlattr *bc) { struct net *net = sock_net(skb->sk); int i, num, s_i, s_num; @@ -978,7 +978,7 @@ out: EXPORT_SYMBOL_GPL(inet_diag_dump_icsk); static int __inet_diag_dump(struct sk_buff *skb, struct netlink_callback *cb, - struct inet_diag_req_v2 *r, + const struct inet_diag_req_v2 *r, struct nlattr *bc) { const struct inet_diag_handler *handler; diff --git a/net/ipv4/tcp_diag.c b/net/ipv4/tcp_diag.c index 0d73f9ddb55b..86dc119a3815 100644 --- a/net/ipv4/tcp_diag.c +++ b/net/ipv4/tcp_diag.c @@ -34,13 +34,13 @@ static void tcp_diag_get_info(struct sock *sk, struct inet_diag_msg *r, } static void tcp_diag_dump(struct sk_buff *skb, struct netlink_callback *cb, - struct inet_diag_req_v2 *r, struct nlattr *bc) + const struct inet_diag_req_v2 *r, struct nlattr *bc) { inet_diag_dump_icsk(&tcp_hashinfo, skb, cb, r, bc); } static int tcp_diag_dump_one(struct sk_buff *in_skb, const struct nlmsghdr *nlh, - struct inet_diag_req_v2 *req) + const struct inet_diag_req_v2 *req) { return inet_diag_dump_one_icsk(&tcp_hashinfo, in_skb, nlh, req); } diff --git a/net/ipv4/udp_diag.c b/net/ipv4/udp_diag.c index 4a000f1dd757..2dbfc1f1f7b3 100644 --- a/net/ipv4/udp_diag.c +++ b/net/ipv4/udp_diag.c @@ -18,8 +18,9 @@ #include static int sk_diag_dump(struct sock *sk, struct sk_buff *skb, - struct netlink_callback *cb, struct inet_diag_req_v2 *req, - struct nlattr *bc) + struct netlink_callback *cb, + const struct inet_diag_req_v2 *req, + struct nlattr *bc) { if (!inet_diag_bc_sk(bc, sk)) return 0; @@ -31,7 +32,8 @@ static int sk_diag_dump(struct sock *sk, struct sk_buff *skb, } static int udp_dump_one(struct udp_table *tbl, struct sk_buff *in_skb, - const struct nlmsghdr *nlh, struct inet_diag_req_v2 *req) + const struct nlmsghdr *nlh, + const struct inet_diag_req_v2 *req) { int err = -EINVAL; struct sock *sk; @@ -90,8 +92,9 @@ out_nosk: return err; } -static void udp_dump(struct udp_table *table, struct sk_buff *skb, struct netlink_callback *cb, - struct inet_diag_req_v2 *r, struct nlattr *bc) +static void udp_dump(struct udp_table *table, struct sk_buff *skb, + struct netlink_callback *cb, + const struct inet_diag_req_v2 *r, struct nlattr *bc) { int num, s_num, slot, s_slot; struct net *net = sock_net(skb->sk); @@ -144,13 +147,13 @@ done: } static void udp_diag_dump(struct sk_buff *skb, struct netlink_callback *cb, - struct inet_diag_req_v2 *r, struct nlattr *bc) + const struct inet_diag_req_v2 *r, struct nlattr *bc) { udp_dump(&udp_table, skb, cb, r, bc); } static int udp_diag_dump_one(struct sk_buff *in_skb, const struct nlmsghdr *nlh, - struct inet_diag_req_v2 *req) + const struct inet_diag_req_v2 *req) { return udp_dump_one(&udp_table, in_skb, nlh, req); } @@ -170,13 +173,14 @@ static const struct inet_diag_handler udp_diag_handler = { }; static void udplite_diag_dump(struct sk_buff *skb, struct netlink_callback *cb, - struct inet_diag_req_v2 *r, struct nlattr *bc) + const struct inet_diag_req_v2 *r, + struct nlattr *bc) { udp_dump(&udplite_table, skb, cb, r, bc); } static int udplite_diag_dump_one(struct sk_buff *in_skb, const struct nlmsghdr *nlh, - struct inet_diag_req_v2 *req) + const struct inet_diag_req_v2 *req) { return udp_dump_one(&udplite_table, in_skb, nlh, req); } -- cgit v1.2.3 From 406ef2a67bd0bb13d77d5e5d700e36a2caea09ae Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Tue, 10 Mar 2015 20:14:27 +0200 Subject: Bluetooth: Make Fast Connectable available while powered off To maximize the usability of the Fast Connectable feature we should make it possible to set (or unset) it at any given moment. This means removing the dependency on the 'connectable' setting as well as the 'powered' setting. The former makes also sense since page scan may get enabled through add_device even if 'connectable' is false. To keep the setting available over power cycles its flag also needs to be removed from the flags that are cleared upon HCI_Reset. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- include/net/bluetooth/hci.h | 2 +- net/bluetooth/mgmt.c | 30 ++++++++++++------------------ 2 files changed, 13 insertions(+), 19 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index 8e54f825153c..f76f45ae76c3 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -231,7 +231,7 @@ enum { * or the HCI device is closed. */ #define HCI_PERSISTENT_MASK (BIT(HCI_LE_SCAN) | BIT(HCI_PERIODIC_INQ) | \ - BIT(HCI_FAST_CONNECTABLE) | BIT(HCI_LE_ADV)) + BIT(HCI_LE_ADV)) /* HCI timeouts */ #define HCI_DISCONN_TIMEOUT msecs_to_jiffies(2000) /* 2 seconds */ diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index d769b428b630..49b8e09ffe67 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -1968,15 +1968,6 @@ static int set_connectable(struct sock *sk, struct hci_dev *hdev, void *data, } no_scan_update: - /* If we're going from non-connectable to connectable or - * vice-versa when fast connectable is enabled ensure that fast - * connectable gets disabled. write_fast_connectable won't do - * anything if the page scan parameters are already what they - * should be. - */ - if (cp->val || test_bit(HCI_FAST_CONNECTABLE, &hdev->dev_flags)) - write_fast_connectable(&req, false); - /* Update the advertising parameters if necessary */ if (test_bit(HCI_ADVERTISING, &hdev->dev_flags)) enable_advertising(&req); @@ -4660,14 +4651,6 @@ static int set_fast_connectable(struct sock *sk, struct hci_dev *hdev, return mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_FAST_CONNECTABLE, MGMT_STATUS_INVALID_PARAMS); - if (!hdev_is_powered(hdev)) - return mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_FAST_CONNECTABLE, - MGMT_STATUS_NOT_POWERED); - - if (!test_bit(HCI_CONNECTABLE, &hdev->dev_flags)) - return mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_FAST_CONNECTABLE, - MGMT_STATUS_REJECTED); - hci_dev_lock(hdev); if (mgmt_pending_find(MGMT_OP_SET_FAST_CONNECTABLE, hdev)) { @@ -4682,6 +4665,14 @@ static int set_fast_connectable(struct sock *sk, struct hci_dev *hdev, goto unlock; } + if (!hdev_is_powered(hdev)) { + change_bit(HCI_FAST_CONNECTABLE, &hdev->dev_flags); + err = send_settings_rsp(sk, MGMT_OP_SET_FAST_CONNECTABLE, + hdev); + new_settings(hdev, sk); + goto unlock; + } + cmd = mgmt_pending_add(sk, MGMT_OP_SET_FAST_CONNECTABLE, hdev, data, len); if (!cmd) { @@ -6481,7 +6472,10 @@ static int powered_update_hci(struct hci_dev *hdev) sizeof(link_sec), &link_sec); if (lmp_bredr_capable(hdev)) { - write_fast_connectable(&req, false); + if (test_bit(HCI_FAST_CONNECTABLE, &hdev->dev_flags)) + write_fast_connectable(&req, true); + else + write_fast_connectable(&req, false); __hci_update_page_scan(&req); update_class(&req); update_name(&req); -- cgit v1.2.3 From 55e76b38986a61259f3079afd0f9a865651a34fe Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Tue, 10 Mar 2015 22:34:40 +0200 Subject: Bluetooth: Add 'Already Paired' error for Pair Device command To make the behavior predictable when attempting to pair with a device for which we already have a Link Key or Long Term Key, this patch adds a new 'Already Paired' error which gets sent in such a scenario. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- include/net/bluetooth/hci_core.h | 2 ++ include/net/bluetooth/mgmt.h | 1 + net/bluetooth/hci_core.c | 27 +++++++++++++++++++++++++++ net/bluetooth/mgmt.c | 7 +++++++ 4 files changed, 37 insertions(+) (limited to 'include') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index afc641c5e55c..5cc5a192359d 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -967,6 +967,8 @@ struct smp_irk *hci_add_irk(struct hci_dev *hdev, bdaddr_t *bdaddr, void hci_remove_irk(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 addr_type); void hci_smp_irks_clear(struct hci_dev *hdev); +bool hci_bdaddr_is_paired(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type); + void hci_remote_oob_data_clear(struct hci_dev *hdev); struct oob_data *hci_find_remote_oob_data(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 bdaddr_type); diff --git a/include/net/bluetooth/mgmt.h b/include/net/bluetooth/mgmt.h index 0c737e4b8f57..5bf6af9cee78 100644 --- a/include/net/bluetooth/mgmt.h +++ b/include/net/bluetooth/mgmt.h @@ -43,6 +43,7 @@ #define MGMT_STATUS_CANCELLED 0x10 #define MGMT_STATUS_INVALID_INDEX 0x11 #define MGMT_STATUS_RFKILLED 0x12 +#define MGMT_STATUS_ALREADY_PAIRED 0x13 struct mgmt_hdr { __le16 opcode; diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index bba4c344c6e0..a35d8441187a 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -2516,6 +2516,33 @@ void hci_remove_irk(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 addr_type) } } +bool hci_bdaddr_is_paired(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type) +{ + struct smp_ltk *k; + u8 addr_type; + + if (type == BDADDR_BREDR) { + if (hci_find_link_key(hdev, bdaddr)) + return true; + return false; + } + + /* Convert to HCI addr type which struct smp_ltk uses */ + if (type == BDADDR_LE_PUBLIC) + addr_type = ADDR_LE_DEV_PUBLIC; + else + addr_type = ADDR_LE_DEV_RANDOM; + + rcu_read_lock(); + list_for_each_entry_rcu(k, &hdev->long_term_keys, list) { + if (k->bdaddr_type == addr_type && !bacmp(bdaddr, &k->bdaddr)) + return true; + } + rcu_read_unlock(); + + return false; +} + /* HCI command timer function */ static void hci_cmd_timeout(struct work_struct *work) { diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 49b8e09ffe67..600636c00d34 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -3245,6 +3245,13 @@ static int pair_device(struct sock *sk, struct hci_dev *hdev, void *data, goto unlock; } + if (hci_bdaddr_is_paired(hdev, &cp->addr.bdaddr, cp->addr.type)) { + err = mgmt_cmd_complete(sk, hdev->id, MGMT_OP_PAIR_DEVICE, + MGMT_STATUS_ALREADY_PAIRED, &rp, + sizeof(rp)); + goto unlock; + } + sec_level = BT_SECURITY_MEDIUM; auth_type = HCI_AT_DEDICATED_BONDING; -- cgit v1.2.3 From d3ef70543429b754dacc87fc68c30c2c34502337 Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Wed, 11 Mar 2015 11:42:44 +0800 Subject: ASoC: rt5670: Add IRQ function This patch adds the IRQ function support of rt5670. We use a flag named dev_gpio in platform data to inform codec driver if the IRQ function is used or not. Also, we export rt5670_set_jack_detect for machine driver to pass the jack point. Signed-off-by: Bard Liao Signed-off-by: Mark Brown --- include/sound/rt5670.h | 1 + sound/soc/codecs/rt5670.c | 176 +++++++++++++++++++++++++++++++++++++++++++++- sound/soc/codecs/rt5670.h | 4 ++ 3 files changed, 179 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/sound/rt5670.h b/include/sound/rt5670.h index bd311197a3b5..b7d60510819b 100644 --- a/include/sound/rt5670.h +++ b/include/sound/rt5670.h @@ -14,6 +14,7 @@ struct rt5670_platform_data { int jd_mode; bool in2_diff; + bool dev_gpio; bool dmic_en; unsigned int dmic1_data_pin; diff --git a/sound/soc/codecs/rt5670.c b/sound/soc/codecs/rt5670.c index 1a6a9c4dc879..a900db5fb1d9 100644 --- a/sound/soc/codecs/rt5670.c +++ b/sound/soc/codecs/rt5670.c @@ -403,6 +403,171 @@ static bool rt5670_readable_register(struct device *dev, unsigned int reg) } } +/** + * rt5670_headset_detect - Detect headset. + * @codec: SoC audio codec device. + * @jack_insert: Jack insert or not. + * + * Detect whether is headset or not when jack inserted. + * + * Returns detect status. + */ + +static int rt5670_headset_detect(struct snd_soc_codec *codec, int jack_insert) +{ + int val; + struct rt5670_priv *rt5670 = snd_soc_codec_get_drvdata(codec); + + if (jack_insert) { + snd_soc_dapm_force_enable_pin(&codec->dapm, + "Mic Det Power"); + snd_soc_dapm_sync(&codec->dapm); + snd_soc_update_bits(codec, RT5670_GEN_CTRL3, 0x4, 0x0); + snd_soc_update_bits(codec, RT5670_CJ_CTRL2, + RT5670_CBJ_DET_MODE | RT5670_CBJ_MN_JD, + RT5670_CBJ_MN_JD); + snd_soc_write(codec, RT5670_GPIO_CTRL2, 0x0004); + snd_soc_update_bits(codec, RT5670_GPIO_CTRL1, + RT5670_GP1_PIN_MASK, RT5670_GP1_PIN_IRQ); + snd_soc_update_bits(codec, RT5670_CJ_CTRL1, + RT5670_CBJ_BST1_EN, RT5670_CBJ_BST1_EN); + snd_soc_write(codec, RT5670_JD_CTRL3, 0x00f0); + snd_soc_update_bits(codec, RT5670_CJ_CTRL2, + RT5670_CBJ_MN_JD, RT5670_CBJ_MN_JD); + snd_soc_update_bits(codec, RT5670_CJ_CTRL2, + RT5670_CBJ_MN_JD, 0); + msleep(300); + val = snd_soc_read(codec, RT5670_CJ_CTRL3) & 0x7; + if (val == 0x1 || val == 0x2) { + rt5670->jack_type = SND_JACK_HEADSET; + /* for push button */ + snd_soc_update_bits(codec, RT5670_INT_IRQ_ST, 0x8, 0x8); + snd_soc_update_bits(codec, RT5670_IL_CMD, 0x40, 0x40); + snd_soc_read(codec, RT5670_IL_CMD); + } else { + snd_soc_update_bits(codec, RT5670_GEN_CTRL3, 0x4, 0x4); + rt5670->jack_type = SND_JACK_HEADPHONE; + snd_soc_dapm_disable_pin(&codec->dapm, "Mic Det Power"); + snd_soc_dapm_sync(&codec->dapm); + } + } else { + snd_soc_update_bits(codec, RT5670_INT_IRQ_ST, 0x8, 0x0); + snd_soc_update_bits(codec, RT5670_GEN_CTRL3, 0x4, 0x4); + rt5670->jack_type = 0; + snd_soc_dapm_disable_pin(&codec->dapm, "Mic Det Power"); + snd_soc_dapm_sync(&codec->dapm); + } + + return rt5670->jack_type; +} + +static int rt5670_button_detect(struct snd_soc_codec *codec) +{ + int btn_type, val; + + val = snd_soc_read(codec, RT5670_IL_CMD); + btn_type = val & 0xff80; + snd_soc_write(codec, RT5670_IL_CMD, val); + if (btn_type != 0) { + msleep(20); + val = snd_soc_read(codec, RT5670_IL_CMD); + snd_soc_write(codec, RT5670_IL_CMD, val); + } + + return btn_type; +} + +static int rt5670_irq_detection(void *data) +{ + struct rt5670_priv *rt5670 = (struct rt5670_priv *)data; + struct snd_soc_jack_gpio *gpio = &rt5670->hp_gpio; + struct snd_soc_jack *jack = rt5670->jack; + int val, btn_type, report = jack->status; + + if (rt5670->pdata.jd_mode == 1) /* 2 port */ + val = snd_soc_read(rt5670->codec, RT5670_A_JD_CTRL1) & 0x0070; + else + val = snd_soc_read(rt5670->codec, RT5670_A_JD_CTRL1) & 0x0020; + + switch (val) { + /* jack in */ + case 0x30: /* 2 port */ + case 0x0: /* 1 port or 2 port */ + if (rt5670->jack_type == 0) { + report = rt5670_headset_detect(rt5670->codec, 1); + /* for push button and jack out */ + gpio->debounce_time = 25; + break; + } + btn_type = 0; + if (snd_soc_read(rt5670->codec, RT5670_INT_IRQ_ST) & 0x4) { + /* button pressed */ + report = SND_JACK_HEADSET; + btn_type = rt5670_button_detect(rt5670->codec); + switch (btn_type) { + case 0x2000: /* up */ + report |= SND_JACK_BTN_1; + break; + case 0x0400: /* center */ + report |= SND_JACK_BTN_0; + break; + case 0x0080: /* down */ + report |= SND_JACK_BTN_2; + break; + default: + dev_err(rt5670->codec->dev, + "Unexpected button code 0x%04x\n", + btn_type); + break; + } + } + if (btn_type == 0)/* button release */ + report = rt5670->jack_type; + + break; + /* jack out */ + case 0x70: /* 2 port */ + case 0x10: /* 2 port */ + case 0x20: /* 1 port */ + report = 0; + snd_soc_update_bits(rt5670->codec, RT5670_INT_IRQ_ST, 0x1, 0x0); + rt5670_headset_detect(rt5670->codec, 0); + gpio->debounce_time = 150; /* for jack in */ + break; + default: + break; + } + + return report; +} + +int rt5670_set_jack_detect(struct snd_soc_codec *codec, + struct snd_soc_jack *jack) +{ + struct rt5670_priv *rt5670 = snd_soc_codec_get_drvdata(codec); + int ret; + + rt5670->jack = jack; + rt5670->hp_gpio.gpiod_dev = codec->dev; + rt5670->hp_gpio.name = "headphone detect"; + rt5670->hp_gpio.report = SND_JACK_HEADSET | + SND_JACK_BTN_0 | SND_JACK_BTN_1 | SND_JACK_BTN_2; + rt5670->hp_gpio.debounce_time = 150; + rt5670->hp_gpio.wake = true; + rt5670->hp_gpio.data = (struct rt5670_priv *)rt5670; + rt5670->hp_gpio.jack_status_check = rt5670_irq_detection; + + ret = snd_soc_jack_add_gpios(rt5670->jack, 1, + &rt5670->hp_gpio); + if (ret) { + dev_err(codec->dev, "Adding jack GPIO failed\n"); + return ret; + } + + return 0; +} +EXPORT_SYMBOL_GPL(rt5670_set_jack_detect); + static const DECLARE_TLV_DB_SCALE(out_vol_tlv, -4650, 150, 0); static const DECLARE_TLV_DB_SCALE(dac_vol_tlv, -65625, 375, 0); static const DECLARE_TLV_DB_SCALE(in_vol_tlv, -3450, 150, 0); @@ -2506,6 +2671,7 @@ static int rt5670_remove(struct snd_soc_codec *codec) struct rt5670_priv *rt5670 = snd_soc_codec_get_drvdata(codec); regmap_write(rt5670->regmap, RT5670_RESET, 0); + snd_soc_jack_free_gpios(rt5670->jack, 1, &rt5670->hp_gpio); return 0; } @@ -2665,6 +2831,7 @@ static int rt5670_i2c_probe(struct i2c_client *i2c, if (dmi_check_system(dmi_platform_intel_braswell)) { rt5670->pdata.dmic_en = true; rt5670->pdata.dmic1_data_pin = RT5670_DMIC_DATA_IN2P; + rt5670->pdata.dev_gpio = true; rt5670->pdata.jd_mode = 1; } @@ -2706,12 +2873,17 @@ static int rt5670_i2c_probe(struct i2c_client *i2c, regmap_update_bits(rt5670->regmap, RT5670_IN2, RT5670_IN_DF2, RT5670_IN_DF2); - if (i2c->irq) { + if (rt5670->pdata.dev_gpio) { + /* for push button */ + regmap_write(rt5670->regmap, RT5670_IL_CMD, 0x0000); + regmap_write(rt5670->regmap, RT5670_IL_CMD2, 0x0010); + regmap_write(rt5670->regmap, RT5670_IL_CMD3, 0x0014); + /* for irq */ regmap_update_bits(rt5670->regmap, RT5670_GPIO_CTRL1, RT5670_GP1_PIN_MASK, RT5670_GP1_PIN_IRQ); regmap_update_bits(rt5670->regmap, RT5670_GPIO_CTRL2, RT5670_GP1_PF_MASK, RT5670_GP1_PF_OUT); - + regmap_update_bits(rt5670->regmap, RT5670_DIG_MISC, 0x8, 0x8); } if (rt5670->pdata.jd_mode) { diff --git a/sound/soc/codecs/rt5670.h b/sound/soc/codecs/rt5670.h index 0a67adbcfbc3..0f3255aeeb9b 100644 --- a/sound/soc/codecs/rt5670.h +++ b/sound/soc/codecs/rt5670.h @@ -1988,6 +1988,8 @@ struct rt5670_priv { struct snd_soc_codec *codec; struct rt5670_platform_data pdata; struct regmap *regmap; + struct snd_soc_jack *jack; + struct snd_soc_jack_gpio hp_gpio; int sysclk; int sysclk_src; @@ -2004,4 +2006,6 @@ struct rt5670_priv { int jack_type; }; +int rt5670_set_jack_detect(struct snd_soc_codec *codec, + struct snd_soc_jack *jack); #endif /* __RT5670_H__ */ -- cgit v1.2.3 From 916f743da3546c28a2f350d197e3bea95d97ba15 Mon Sep 17 00:00:00 2001 From: Kumar Gala Date: Thu, 26 Feb 2015 15:49:09 -0600 Subject: firmware: qcom: scm: Move the scm driver to drivers/firmware Architectural changes in the ARM Linux kernel tree mandate the eventual removal of the mach-* directories. Move the scm driver to drivers/firmware and the scm header to include/linux to support that removal. Signed-off-by: Kumar Gala --- MAINTAINERS | 1 + arch/arm/Kconfig | 2 + arch/arm/mach-qcom/Kconfig | 3 - arch/arm/mach-qcom/Makefile | 3 - arch/arm/mach-qcom/platsmp.c | 2 +- arch/arm/mach-qcom/scm.c | 344 ------------------------------------------- arch/arm/mach-qcom/scm.h | 29 ---- drivers/firmware/Kconfig | 4 + drivers/firmware/Makefile | 2 + drivers/firmware/qcom_scm.c | 344 +++++++++++++++++++++++++++++++++++++++++++ include/linux/qcom_scm.h | 29 ++++ 11 files changed, 383 insertions(+), 380 deletions(-) delete mode 100644 arch/arm/mach-qcom/scm.c delete mode 100644 arch/arm/mach-qcom/scm.h create mode 100644 drivers/firmware/qcom_scm.c create mode 100644 include/linux/qcom_scm.h (limited to 'include') diff --git a/MAINTAINERS b/MAINTAINERS index ddc5a8cf9a8a..beb8aa4840e4 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1317,6 +1317,7 @@ L: linux-soc@vger.kernel.org S: Maintained F: arch/arm/mach-qcom/ F: drivers/soc/qcom/ +F: drivers/firmware/qcom_scm.c T: git git://git.kernel.org/pub/scm/linux/kernel/git/galak/linux-qcom.git ARM/RADISYS ENP2611 MACHINE SUPPORT diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index 9f1f09a2bc9b..7ffd1518d2aa 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -2160,6 +2160,8 @@ source "net/Kconfig" source "drivers/Kconfig" +source "drivers/firmware/Kconfig" + source "fs/Kconfig" source "arch/arm/Kconfig.debug" diff --git a/arch/arm/mach-qcom/Kconfig b/arch/arm/mach-qcom/Kconfig index 48003ea652b9..2256cd1e25d1 100644 --- a/arch/arm/mach-qcom/Kconfig +++ b/arch/arm/mach-qcom/Kconfig @@ -22,7 +22,4 @@ config ARCH_MSM8974 bool "Enable support for MSM8974" select HAVE_ARM_ARCH_TIMER -config QCOM_SCM - bool - endif diff --git a/arch/arm/mach-qcom/Makefile b/arch/arm/mach-qcom/Makefile index 10b684140aa1..e324375fa919 100644 --- a/arch/arm/mach-qcom/Makefile +++ b/arch/arm/mach-qcom/Makefile @@ -1,5 +1,2 @@ obj-y := board.o obj-$(CONFIG_SMP) += platsmp.o -obj-$(CONFIG_QCOM_SCM) += scm.o - -CFLAGS_scm.o :=$(call as-instr,.arch_extension sec,-DREQUIRES_SEC=1) diff --git a/arch/arm/mach-qcom/platsmp.c b/arch/arm/mach-qcom/platsmp.c index 596e6237dc7e..4b67e56911d3 100644 --- a/arch/arm/mach-qcom/platsmp.c +++ b/arch/arm/mach-qcom/platsmp.c @@ -17,10 +17,10 @@ #include #include #include +#include #include -#include "scm.h" #define VDD_SC1_ARRAY_CLAMP_GFS_CTL 0x35a0 #define SCSS_CPU1CORE_RESET 0x2d80 diff --git a/arch/arm/mach-qcom/scm.c b/arch/arm/mach-qcom/scm.c deleted file mode 100644 index 3e0e334374de..000000000000 --- a/arch/arm/mach-qcom/scm.c +++ /dev/null @@ -1,344 +0,0 @@ -/* Copyright (c) 2010, Code Aurora Forum. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 and - * only version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA - * 02110-1301, USA. - */ - -#include -#include -#include -#include -#include -#include - -#include -#include - -#include "scm.h" - -#define QCOM_SCM_ENOMEM -5 -#define QCOM_SCM_EOPNOTSUPP -4 -#define QCOM_SCM_EINVAL_ADDR -3 -#define QCOM_SCM_EINVAL_ARG -2 -#define QCOM_SCM_ERROR -1 -#define QCOM_SCM_INTERRUPTED 1 - -static DEFINE_MUTEX(qcom_scm_lock); - -/** - * struct qcom_scm_command - one SCM command buffer - * @len: total available memory for command and response - * @buf_offset: start of command buffer - * @resp_hdr_offset: start of response buffer - * @id: command to be executed - * @buf: buffer returned from qcom_scm_get_command_buffer() - * - * An SCM command is laid out in memory as follows: - * - * ------------------- <--- struct qcom_scm_command - * | command header | - * ------------------- <--- qcom_scm_get_command_buffer() - * | command buffer | - * ------------------- <--- struct qcom_scm_response and - * | response header | qcom_scm_command_to_response() - * ------------------- <--- qcom_scm_get_response_buffer() - * | response buffer | - * ------------------- - * - * There can be arbitrary padding between the headers and buffers so - * you should always use the appropriate qcom_scm_get_*_buffer() routines - * to access the buffers in a safe manner. - */ -struct qcom_scm_command { - __le32 len; - __le32 buf_offset; - __le32 resp_hdr_offset; - __le32 id; - __le32 buf[0]; -}; - -/** - * struct qcom_scm_response - one SCM response buffer - * @len: total available memory for response - * @buf_offset: start of response data relative to start of qcom_scm_response - * @is_complete: indicates if the command has finished processing - */ -struct qcom_scm_response { - __le32 len; - __le32 buf_offset; - __le32 is_complete; -}; - -/** - * alloc_qcom_scm_command() - Allocate an SCM command - * @cmd_size: size of the command buffer - * @resp_size: size of the response buffer - * - * Allocate an SCM command, including enough room for the command - * and response headers as well as the command and response buffers. - * - * Returns a valid &qcom_scm_command on success or %NULL if the allocation fails. - */ -static struct qcom_scm_command *alloc_qcom_scm_command(size_t cmd_size, size_t resp_size) -{ - struct qcom_scm_command *cmd; - size_t len = sizeof(*cmd) + sizeof(struct qcom_scm_response) + cmd_size + - resp_size; - u32 offset; - - cmd = kzalloc(PAGE_ALIGN(len), GFP_KERNEL); - if (cmd) { - cmd->len = cpu_to_le32(len); - offset = offsetof(struct qcom_scm_command, buf); - cmd->buf_offset = cpu_to_le32(offset); - cmd->resp_hdr_offset = cpu_to_le32(offset + cmd_size); - } - return cmd; -} - -/** - * free_qcom_scm_command() - Free an SCM command - * @cmd: command to free - * - * Free an SCM command. - */ -static inline void free_qcom_scm_command(struct qcom_scm_command *cmd) -{ - kfree(cmd); -} - -/** - * qcom_scm_command_to_response() - Get a pointer to a qcom_scm_response - * @cmd: command - * - * Returns a pointer to a response for a command. - */ -static inline struct qcom_scm_response *qcom_scm_command_to_response( - const struct qcom_scm_command *cmd) -{ - return (void *)cmd + le32_to_cpu(cmd->resp_hdr_offset); -} - -/** - * qcom_scm_get_command_buffer() - Get a pointer to a command buffer - * @cmd: command - * - * Returns a pointer to the command buffer of a command. - */ -static inline void *qcom_scm_get_command_buffer(const struct qcom_scm_command *cmd) -{ - return (void *)cmd->buf; -} - -/** - * qcom_scm_get_response_buffer() - Get a pointer to a response buffer - * @rsp: response - * - * Returns a pointer to a response buffer of a response. - */ -static inline void *qcom_scm_get_response_buffer(const struct qcom_scm_response *rsp) -{ - return (void *)rsp + le32_to_cpu(rsp->buf_offset); -} - -static int qcom_scm_remap_error(int err) -{ - pr_err("qcom_scm_call failed with error code %d\n", err); - switch (err) { - case QCOM_SCM_ERROR: - return -EIO; - case QCOM_SCM_EINVAL_ADDR: - case QCOM_SCM_EINVAL_ARG: - return -EINVAL; - case QCOM_SCM_EOPNOTSUPP: - return -EOPNOTSUPP; - case QCOM_SCM_ENOMEM: - return -ENOMEM; - } - return -EINVAL; -} - -static u32 smc(u32 cmd_addr) -{ - int context_id; - register u32 r0 asm("r0") = 1; - register u32 r1 asm("r1") = (u32)&context_id; - register u32 r2 asm("r2") = cmd_addr; - do { - asm volatile( - __asmeq("%0", "r0") - __asmeq("%1", "r0") - __asmeq("%2", "r1") - __asmeq("%3", "r2") -#ifdef REQUIRES_SEC - ".arch_extension sec\n" -#endif - "smc #0 @ switch to secure world\n" - : "=r" (r0) - : "r" (r0), "r" (r1), "r" (r2) - : "r3"); - } while (r0 == QCOM_SCM_INTERRUPTED); - - return r0; -} - -static int __qcom_scm_call(const struct qcom_scm_command *cmd) -{ - int ret; - u32 cmd_addr = virt_to_phys(cmd); - - /* - * Flush the command buffer so that the secure world sees - * the correct data. - */ - __cpuc_flush_dcache_area((void *)cmd, cmd->len); - outer_flush_range(cmd_addr, cmd_addr + cmd->len); - - ret = smc(cmd_addr); - if (ret < 0) - ret = qcom_scm_remap_error(ret); - - return ret; -} - -static void qcom_scm_inv_range(unsigned long start, unsigned long end) -{ - u32 cacheline_size, ctr; - - asm volatile("mrc p15, 0, %0, c0, c0, 1" : "=r" (ctr)); - cacheline_size = 4 << ((ctr >> 16) & 0xf); - - start = round_down(start, cacheline_size); - end = round_up(end, cacheline_size); - outer_inv_range(start, end); - while (start < end) { - asm ("mcr p15, 0, %0, c7, c6, 1" : : "r" (start) - : "memory"); - start += cacheline_size; - } - dsb(); - isb(); -} - -/** - * qcom_scm_call() - Send an SCM command - * @svc_id: service identifier - * @cmd_id: command identifier - * @cmd_buf: command buffer - * @cmd_len: length of the command buffer - * @resp_buf: response buffer - * @resp_len: length of the response buffer - * - * Sends a command to the SCM and waits for the command to finish processing. - * - * A note on cache maintenance: - * Note that any buffers that are expected to be accessed by the secure world - * must be flushed before invoking qcom_scm_call and invalidated in the cache - * immediately after qcom_scm_call returns. Cache maintenance on the command - * and response buffers is taken care of by qcom_scm_call; however, callers are - * responsible for any other cached buffers passed over to the secure world. - */ -static int qcom_scm_call(u32 svc_id, u32 cmd_id, const void *cmd_buf, - size_t cmd_len, void *resp_buf, size_t resp_len) -{ - int ret; - struct qcom_scm_command *cmd; - struct qcom_scm_response *rsp; - unsigned long start, end; - - cmd = alloc_qcom_scm_command(cmd_len, resp_len); - if (!cmd) - return -ENOMEM; - - cmd->id = cpu_to_le32((svc_id << 10) | cmd_id); - if (cmd_buf) - memcpy(qcom_scm_get_command_buffer(cmd), cmd_buf, cmd_len); - - mutex_lock(&qcom_scm_lock); - ret = __qcom_scm_call(cmd); - mutex_unlock(&qcom_scm_lock); - if (ret) - goto out; - - rsp = qcom_scm_command_to_response(cmd); - start = (unsigned long)rsp; - - do { - qcom_scm_inv_range(start, start + sizeof(*rsp)); - } while (!rsp->is_complete); - - end = (unsigned long)qcom_scm_get_response_buffer(rsp) + resp_len; - qcom_scm_inv_range(start, end); - - if (resp_buf) - memcpy(resp_buf, qcom_scm_get_response_buffer(rsp), resp_len); -out: - free_qcom_scm_command(cmd); - return ret; -} - -u32 qcom_scm_get_version(void) -{ - int context_id; - static u32 version = -1; - register u32 r0 asm("r0"); - register u32 r1 asm("r1"); - - if (version != -1) - return version; - - mutex_lock(&qcom_scm_lock); - - r0 = 0x1 << 8; - r1 = (u32)&context_id; - do { - asm volatile( - __asmeq("%0", "r0") - __asmeq("%1", "r1") - __asmeq("%2", "r0") - __asmeq("%3", "r1") -#ifdef REQUIRES_SEC - ".arch_extension sec\n" -#endif - "smc #0 @ switch to secure world\n" - : "=r" (r0), "=r" (r1) - : "r" (r0), "r" (r1) - : "r2", "r3"); - } while (r0 == QCOM_SCM_INTERRUPTED); - - version = r1; - mutex_unlock(&qcom_scm_lock); - - return version; -} -EXPORT_SYMBOL(qcom_scm_get_version); - -#define QCOM_SCM_SVC_BOOT 0x1 -#define QCOM_SCM_BOOT_ADDR 0x1 -/* - * Set the cold/warm boot address for one of the CPU cores. - */ -int qcom_scm_set_boot_addr(u32 addr, int flags) -{ - struct { - __le32 flags; - __le32 addr; - } cmd; - - cmd.addr = cpu_to_le32(addr); - cmd.flags = cpu_to_le32(flags); - return qcom_scm_call(QCOM_SCM_SVC_BOOT, QCOM_SCM_BOOT_ADDR, - &cmd, sizeof(cmd), NULL, 0); -} -EXPORT_SYMBOL(qcom_scm_set_boot_addr); diff --git a/arch/arm/mach-qcom/scm.h b/arch/arm/mach-qcom/scm.h deleted file mode 100644 index 6bb84cffb396..000000000000 --- a/arch/arm/mach-qcom/scm.h +++ /dev/null @@ -1,29 +0,0 @@ -/* Copyright (c) 2010, Code Aurora Forum. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 and - * only version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ -#ifndef __QCOM_SCM_H -#define __QCOM_SCM_H - -#define QCOM_SCM_FLAG_COLDBOOT_CPU1 0x01 -#define QCOM_SCM_FLAG_COLDBOOT_CPU2 0x08 -#define QCOM_SCM_FLAG_COLDBOOT_CPU3 0x20 -#define QCOM_SCM_FLAG_WARMBOOT_CPU0 0x04 -#define QCOM_SCM_FLAG_WARMBOOT_CPU1 0x02 -#define QCOM_SCM_FLAG_WARMBOOT_CPU2 0x10 -#define QCOM_SCM_FLAG_WARMBOOT_CPU3 0x40 - -extern int qcom_scm_set_boot_addr(u32 addr, int flags); - -#define QCOM_SCM_VERSION(major, minor) (((major) << 16) | ((minor) & 0xFF)) - -extern u32 qcom_scm_get_version(void); - -#endif diff --git a/drivers/firmware/Kconfig b/drivers/firmware/Kconfig index 41983883cef4..6517132e5d8b 100644 --- a/drivers/firmware/Kconfig +++ b/drivers/firmware/Kconfig @@ -132,6 +132,10 @@ config ISCSI_IBFT detect iSCSI boot parameters dynamically during system boot, say Y. Otherwise, say N. +config QCOM_SCM + bool + depends on ARM || ARM64 + source "drivers/firmware/google/Kconfig" source "drivers/firmware/efi/Kconfig" diff --git a/drivers/firmware/Makefile b/drivers/firmware/Makefile index 5373dc5b6011..3fdd3912709a 100644 --- a/drivers/firmware/Makefile +++ b/drivers/firmware/Makefile @@ -11,6 +11,8 @@ obj-$(CONFIG_DMIID) += dmi-id.o obj-$(CONFIG_ISCSI_IBFT_FIND) += iscsi_ibft_find.o obj-$(CONFIG_ISCSI_IBFT) += iscsi_ibft.o obj-$(CONFIG_FIRMWARE_MEMMAP) += memmap.o +obj-$(CONFIG_QCOM_SCM) += qcom_scm.o +CFLAGS_qcom_scm.o :=$(call as-instr,.arch_extension sec,-DREQUIRES_SEC=1) obj-$(CONFIG_GOOGLE_FIRMWARE) += google/ obj-$(CONFIG_EFI) += efi/ diff --git a/drivers/firmware/qcom_scm.c b/drivers/firmware/qcom_scm.c new file mode 100644 index 000000000000..6e7a72bdb176 --- /dev/null +++ b/drivers/firmware/qcom_scm.c @@ -0,0 +1,344 @@ +/* Copyright (c) 2010, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + + +#define QCOM_SCM_ENOMEM -5 +#define QCOM_SCM_EOPNOTSUPP -4 +#define QCOM_SCM_EINVAL_ADDR -3 +#define QCOM_SCM_EINVAL_ARG -2 +#define QCOM_SCM_ERROR -1 +#define QCOM_SCM_INTERRUPTED 1 + +static DEFINE_MUTEX(qcom_scm_lock); + +/** + * struct qcom_scm_command - one SCM command buffer + * @len: total available memory for command and response + * @buf_offset: start of command buffer + * @resp_hdr_offset: start of response buffer + * @id: command to be executed + * @buf: buffer returned from qcom_scm_get_command_buffer() + * + * An SCM command is laid out in memory as follows: + * + * ------------------- <--- struct qcom_scm_command + * | command header | + * ------------------- <--- qcom_scm_get_command_buffer() + * | command buffer | + * ------------------- <--- struct qcom_scm_response and + * | response header | qcom_scm_command_to_response() + * ------------------- <--- qcom_scm_get_response_buffer() + * | response buffer | + * ------------------- + * + * There can be arbitrary padding between the headers and buffers so + * you should always use the appropriate qcom_scm_get_*_buffer() routines + * to access the buffers in a safe manner. + */ +struct qcom_scm_command { + __le32 len; + __le32 buf_offset; + __le32 resp_hdr_offset; + __le32 id; + __le32 buf[0]; +}; + +/** + * struct qcom_scm_response - one SCM response buffer + * @len: total available memory for response + * @buf_offset: start of response data relative to start of qcom_scm_response + * @is_complete: indicates if the command has finished processing + */ +struct qcom_scm_response { + __le32 len; + __le32 buf_offset; + __le32 is_complete; +}; + +/** + * alloc_qcom_scm_command() - Allocate an SCM command + * @cmd_size: size of the command buffer + * @resp_size: size of the response buffer + * + * Allocate an SCM command, including enough room for the command + * and response headers as well as the command and response buffers. + * + * Returns a valid &qcom_scm_command on success or %NULL if the allocation fails. + */ +static struct qcom_scm_command *alloc_qcom_scm_command(size_t cmd_size, size_t resp_size) +{ + struct qcom_scm_command *cmd; + size_t len = sizeof(*cmd) + sizeof(struct qcom_scm_response) + cmd_size + + resp_size; + u32 offset; + + cmd = kzalloc(PAGE_ALIGN(len), GFP_KERNEL); + if (cmd) { + cmd->len = cpu_to_le32(len); + offset = offsetof(struct qcom_scm_command, buf); + cmd->buf_offset = cpu_to_le32(offset); + cmd->resp_hdr_offset = cpu_to_le32(offset + cmd_size); + } + return cmd; +} + +/** + * free_qcom_scm_command() - Free an SCM command + * @cmd: command to free + * + * Free an SCM command. + */ +static inline void free_qcom_scm_command(struct qcom_scm_command *cmd) +{ + kfree(cmd); +} + +/** + * qcom_scm_command_to_response() - Get a pointer to a qcom_scm_response + * @cmd: command + * + * Returns a pointer to a response for a command. + */ +static inline struct qcom_scm_response *qcom_scm_command_to_response( + const struct qcom_scm_command *cmd) +{ + return (void *)cmd + le32_to_cpu(cmd->resp_hdr_offset); +} + +/** + * qcom_scm_get_command_buffer() - Get a pointer to a command buffer + * @cmd: command + * + * Returns a pointer to the command buffer of a command. + */ +static inline void *qcom_scm_get_command_buffer(const struct qcom_scm_command *cmd) +{ + return (void *)cmd->buf; +} + +/** + * qcom_scm_get_response_buffer() - Get a pointer to a response buffer + * @rsp: response + * + * Returns a pointer to a response buffer of a response. + */ +static inline void *qcom_scm_get_response_buffer(const struct qcom_scm_response *rsp) +{ + return (void *)rsp + le32_to_cpu(rsp->buf_offset); +} + +static int qcom_scm_remap_error(int err) +{ + pr_err("qcom_scm_call failed with error code %d\n", err); + switch (err) { + case QCOM_SCM_ERROR: + return -EIO; + case QCOM_SCM_EINVAL_ADDR: + case QCOM_SCM_EINVAL_ARG: + return -EINVAL; + case QCOM_SCM_EOPNOTSUPP: + return -EOPNOTSUPP; + case QCOM_SCM_ENOMEM: + return -ENOMEM; + } + return -EINVAL; +} + +static u32 smc(u32 cmd_addr) +{ + int context_id; + register u32 r0 asm("r0") = 1; + register u32 r1 asm("r1") = (u32)&context_id; + register u32 r2 asm("r2") = cmd_addr; + do { + asm volatile( + __asmeq("%0", "r0") + __asmeq("%1", "r0") + __asmeq("%2", "r1") + __asmeq("%3", "r2") +#ifdef REQUIRES_SEC + ".arch_extension sec\n" +#endif + "smc #0 @ switch to secure world\n" + : "=r" (r0) + : "r" (r0), "r" (r1), "r" (r2) + : "r3"); + } while (r0 == QCOM_SCM_INTERRUPTED); + + return r0; +} + +static int __qcom_scm_call(const struct qcom_scm_command *cmd) +{ + int ret; + u32 cmd_addr = virt_to_phys(cmd); + + /* + * Flush the command buffer so that the secure world sees + * the correct data. + */ + __cpuc_flush_dcache_area((void *)cmd, cmd->len); + outer_flush_range(cmd_addr, cmd_addr + cmd->len); + + ret = smc(cmd_addr); + if (ret < 0) + ret = qcom_scm_remap_error(ret); + + return ret; +} + +static void qcom_scm_inv_range(unsigned long start, unsigned long end) +{ + u32 cacheline_size, ctr; + + asm volatile("mrc p15, 0, %0, c0, c0, 1" : "=r" (ctr)); + cacheline_size = 4 << ((ctr >> 16) & 0xf); + + start = round_down(start, cacheline_size); + end = round_up(end, cacheline_size); + outer_inv_range(start, end); + while (start < end) { + asm ("mcr p15, 0, %0, c7, c6, 1" : : "r" (start) + : "memory"); + start += cacheline_size; + } + dsb(); + isb(); +} + +/** + * qcom_scm_call() - Send an SCM command + * @svc_id: service identifier + * @cmd_id: command identifier + * @cmd_buf: command buffer + * @cmd_len: length of the command buffer + * @resp_buf: response buffer + * @resp_len: length of the response buffer + * + * Sends a command to the SCM and waits for the command to finish processing. + * + * A note on cache maintenance: + * Note that any buffers that are expected to be accessed by the secure world + * must be flushed before invoking qcom_scm_call and invalidated in the cache + * immediately after qcom_scm_call returns. Cache maintenance on the command + * and response buffers is taken care of by qcom_scm_call; however, callers are + * responsible for any other cached buffers passed over to the secure world. + */ +static int qcom_scm_call(u32 svc_id, u32 cmd_id, const void *cmd_buf, + size_t cmd_len, void *resp_buf, size_t resp_len) +{ + int ret; + struct qcom_scm_command *cmd; + struct qcom_scm_response *rsp; + unsigned long start, end; + + cmd = alloc_qcom_scm_command(cmd_len, resp_len); + if (!cmd) + return -ENOMEM; + + cmd->id = cpu_to_le32((svc_id << 10) | cmd_id); + if (cmd_buf) + memcpy(qcom_scm_get_command_buffer(cmd), cmd_buf, cmd_len); + + mutex_lock(&qcom_scm_lock); + ret = __qcom_scm_call(cmd); + mutex_unlock(&qcom_scm_lock); + if (ret) + goto out; + + rsp = qcom_scm_command_to_response(cmd); + start = (unsigned long)rsp; + + do { + qcom_scm_inv_range(start, start + sizeof(*rsp)); + } while (!rsp->is_complete); + + end = (unsigned long)qcom_scm_get_response_buffer(rsp) + resp_len; + qcom_scm_inv_range(start, end); + + if (resp_buf) + memcpy(resp_buf, qcom_scm_get_response_buffer(rsp), resp_len); +out: + free_qcom_scm_command(cmd); + return ret; +} + +u32 qcom_scm_get_version(void) +{ + int context_id; + static u32 version = -1; + register u32 r0 asm("r0"); + register u32 r1 asm("r1"); + + if (version != -1) + return version; + + mutex_lock(&qcom_scm_lock); + + r0 = 0x1 << 8; + r1 = (u32)&context_id; + do { + asm volatile( + __asmeq("%0", "r0") + __asmeq("%1", "r1") + __asmeq("%2", "r0") + __asmeq("%3", "r1") +#ifdef REQUIRES_SEC + ".arch_extension sec\n" +#endif + "smc #0 @ switch to secure world\n" + : "=r" (r0), "=r" (r1) + : "r" (r0), "r" (r1) + : "r2", "r3"); + } while (r0 == QCOM_SCM_INTERRUPTED); + + version = r1; + mutex_unlock(&qcom_scm_lock); + + return version; +} +EXPORT_SYMBOL(qcom_scm_get_version); + +#define QCOM_SCM_SVC_BOOT 0x1 +#define QCOM_SCM_BOOT_ADDR 0x1 +/* + * Set the cold/warm boot address for one of the CPU cores. + */ +int qcom_scm_set_boot_addr(u32 addr, int flags) +{ + struct { + __le32 flags; + __le32 addr; + } cmd; + + cmd.addr = cpu_to_le32(addr); + cmd.flags = cpu_to_le32(flags); + return qcom_scm_call(QCOM_SCM_SVC_BOOT, QCOM_SCM_BOOT_ADDR, + &cmd, sizeof(cmd), NULL, 0); +} +EXPORT_SYMBOL(qcom_scm_set_boot_addr); diff --git a/include/linux/qcom_scm.h b/include/linux/qcom_scm.h new file mode 100644 index 000000000000..6bb84cffb396 --- /dev/null +++ b/include/linux/qcom_scm.h @@ -0,0 +1,29 @@ +/* Copyright (c) 2010, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#ifndef __QCOM_SCM_H +#define __QCOM_SCM_H + +#define QCOM_SCM_FLAG_COLDBOOT_CPU1 0x01 +#define QCOM_SCM_FLAG_COLDBOOT_CPU2 0x08 +#define QCOM_SCM_FLAG_COLDBOOT_CPU3 0x20 +#define QCOM_SCM_FLAG_WARMBOOT_CPU0 0x04 +#define QCOM_SCM_FLAG_WARMBOOT_CPU1 0x02 +#define QCOM_SCM_FLAG_WARMBOOT_CPU2 0x10 +#define QCOM_SCM_FLAG_WARMBOOT_CPU3 0x40 + +extern int qcom_scm_set_boot_addr(u32 addr, int flags); + +#define QCOM_SCM_VERSION(major, minor) (((major) << 16) | ((minor) & 0xFF)) + +extern u32 qcom_scm_get_version(void); + +#endif -- cgit v1.2.3 From a353e4a06f24235138d483a2625726a5fc472949 Mon Sep 17 00:00:00 2001 From: Lina Iyer Date: Mon, 2 Mar 2015 16:30:28 -0700 Subject: firmware: qcom: scm: Clean cold boot entry to export only the API We dont need to export the SCM specific cold boot flags to the platform code. Export only a function to set the cold boot address. Signed-off-by: Lina Iyer Signed-off-by: Kumar Gala --- arch/arm/mach-qcom/platsmp.c | 21 +++------------------ drivers/firmware/qcom_scm.c | 41 +++++++++++++++++++++++++++++++++++++++-- include/linux/qcom_scm.h | 5 +---- 3 files changed, 43 insertions(+), 24 deletions(-) (limited to 'include') diff --git a/arch/arm/mach-qcom/platsmp.c b/arch/arm/mach-qcom/platsmp.c index 4b67e56911d3..5cde63a64b34 100644 --- a/arch/arm/mach-qcom/platsmp.c +++ b/arch/arm/mach-qcom/platsmp.c @@ -319,25 +319,10 @@ static int kpssv2_boot_secondary(unsigned int cpu, struct task_struct *idle) static void __init qcom_smp_prepare_cpus(unsigned int max_cpus) { - int cpu, map; - unsigned int flags = 0; - static const int cold_boot_flags[] = { - 0, - QCOM_SCM_FLAG_COLDBOOT_CPU1, - QCOM_SCM_FLAG_COLDBOOT_CPU2, - QCOM_SCM_FLAG_COLDBOOT_CPU3, - }; - - for_each_present_cpu(cpu) { - map = cpu_logical_map(cpu); - if (WARN_ON(map >= ARRAY_SIZE(cold_boot_flags))) { - set_cpu_present(cpu, false); - continue; - } - flags |= cold_boot_flags[map]; - } + int cpu; - if (qcom_scm_set_boot_addr(virt_to_phys(secondary_startup_arm), flags)) { + if (qcom_scm_set_cold_boot_addr(secondary_startup_arm, + cpu_present_mask)) { for_each_present_cpu(cpu) { if (cpu == smp_processor_id()) continue; diff --git a/drivers/firmware/qcom_scm.c b/drivers/firmware/qcom_scm.c index 6e7a72bdb176..c953cc38918f 100644 --- a/drivers/firmware/qcom_scm.c +++ b/drivers/firmware/qcom_scm.c @@ -34,6 +34,11 @@ #define QCOM_SCM_ERROR -1 #define QCOM_SCM_INTERRUPTED 1 +#define QCOM_SCM_FLAG_COLDBOOT_CPU0 0x00 +#define QCOM_SCM_FLAG_COLDBOOT_CPU1 0x01 +#define QCOM_SCM_FLAG_COLDBOOT_CPU2 0x08 +#define QCOM_SCM_FLAG_COLDBOOT_CPU3 0x20 + static DEFINE_MUTEX(qcom_scm_lock); /** @@ -329,7 +334,7 @@ EXPORT_SYMBOL(qcom_scm_get_version); /* * Set the cold/warm boot address for one of the CPU cores. */ -int qcom_scm_set_boot_addr(u32 addr, int flags) +static int qcom_scm_set_boot_addr(u32 addr, int flags) { struct { __le32 flags; @@ -341,4 +346,36 @@ int qcom_scm_set_boot_addr(u32 addr, int flags) return qcom_scm_call(QCOM_SCM_SVC_BOOT, QCOM_SCM_BOOT_ADDR, &cmd, sizeof(cmd), NULL, 0); } -EXPORT_SYMBOL(qcom_scm_set_boot_addr); + +/** + * qcom_scm_set_cold_boot_addr() - Set the cold boot address for cpus + * @entry: Entry point function for the cpus + * @cpus: The cpumask of cpus that will use the entry point + * + * Set the cold boot address of the cpus. Any cpu outside the supported + * range would be removed from the cpu present mask. + */ +int qcom_scm_set_cold_boot_addr(void *entry, const cpumask_t *cpus) +{ + int flags = 0; + int cpu; + int scm_cb_flags[] = { + QCOM_SCM_FLAG_COLDBOOT_CPU0, + QCOM_SCM_FLAG_COLDBOOT_CPU1, + QCOM_SCM_FLAG_COLDBOOT_CPU2, + QCOM_SCM_FLAG_COLDBOOT_CPU3, + }; + + if (!cpus || (cpus && cpumask_empty(cpus))) + return -EINVAL; + + for_each_cpu(cpu, cpus) { + if (cpu < ARRAY_SIZE(scm_cb_flags)) + flags |= scm_cb_flags[cpu]; + else + set_cpu_present(cpu, false); + } + + return qcom_scm_set_boot_addr(virt_to_phys(entry), flags); +} +EXPORT_SYMBOL(qcom_scm_set_cold_boot_addr); diff --git a/include/linux/qcom_scm.h b/include/linux/qcom_scm.h index 6bb84cffb396..68a1d8801c6f 100644 --- a/include/linux/qcom_scm.h +++ b/include/linux/qcom_scm.h @@ -12,15 +12,12 @@ #ifndef __QCOM_SCM_H #define __QCOM_SCM_H -#define QCOM_SCM_FLAG_COLDBOOT_CPU1 0x01 -#define QCOM_SCM_FLAG_COLDBOOT_CPU2 0x08 -#define QCOM_SCM_FLAG_COLDBOOT_CPU3 0x20 #define QCOM_SCM_FLAG_WARMBOOT_CPU0 0x04 #define QCOM_SCM_FLAG_WARMBOOT_CPU1 0x02 #define QCOM_SCM_FLAG_WARMBOOT_CPU2 0x10 #define QCOM_SCM_FLAG_WARMBOOT_CPU3 0x40 -extern int qcom_scm_set_boot_addr(u32 addr, int flags); +extern int qcom_scm_set_cold_boot_addr(void *entry, const cpumask_t *cpus); #define QCOM_SCM_VERSION(major, minor) (((major) << 16) | ((minor) & 0xFF)) -- cgit v1.2.3 From 2ce76a6ad32fa076a2bb5561e859c97fceec8bb1 Mon Sep 17 00:00:00 2001 From: Lina Iyer Date: Mon, 2 Mar 2015 16:30:29 -0700 Subject: firmware: qcom: scm: Add qcom_scm_set_warm_boot_addr function A core can be powered down for cpuidle or when it is hotplugged off. In either case, the warmboot return address would be different. Allow setting the warmboot address for a specific cpu, optimize and write to the firmware, if the address is different than the previously set address. Export qcom_scm_set_warm_boot_addr function move the warm boot flags to implementation. Signed-off-by: Lina Iyer Signed-off-by: Kumar Gala --- drivers/firmware/qcom_scm.c | 56 +++++++++++++++++++++++++++++++++++++++++++++ include/linux/qcom_scm.h | 7 ++---- 2 files changed, 58 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/drivers/firmware/qcom_scm.c b/drivers/firmware/qcom_scm.c index c953cc38918f..4d8ede4807ac 100644 --- a/drivers/firmware/qcom_scm.c +++ b/drivers/firmware/qcom_scm.c @@ -1,4 +1,5 @@ /* Copyright (c) 2010, Code Aurora Forum. All rights reserved. + * Copyright (C) 2015 Linaro Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -39,6 +40,23 @@ #define QCOM_SCM_FLAG_COLDBOOT_CPU2 0x08 #define QCOM_SCM_FLAG_COLDBOOT_CPU3 0x20 +#define QCOM_SCM_FLAG_WARMBOOT_CPU0 0x04 +#define QCOM_SCM_FLAG_WARMBOOT_CPU1 0x02 +#define QCOM_SCM_FLAG_WARMBOOT_CPU2 0x10 +#define QCOM_SCM_FLAG_WARMBOOT_CPU3 0x40 + +struct qcom_scm_entry { + int flag; + void *entry; +}; + +static struct qcom_scm_entry qcom_scm_wb[] = { + { .flag = QCOM_SCM_FLAG_WARMBOOT_CPU0 }, + { .flag = QCOM_SCM_FLAG_WARMBOOT_CPU1 }, + { .flag = QCOM_SCM_FLAG_WARMBOOT_CPU2 }, + { .flag = QCOM_SCM_FLAG_WARMBOOT_CPU3 }, +}; + static DEFINE_MUTEX(qcom_scm_lock); /** @@ -379,3 +397,41 @@ int qcom_scm_set_cold_boot_addr(void *entry, const cpumask_t *cpus) return qcom_scm_set_boot_addr(virt_to_phys(entry), flags); } EXPORT_SYMBOL(qcom_scm_set_cold_boot_addr); + +/** + * qcom_scm_set_warm_boot_addr() - Set the warm boot address for cpus + * @entry: Entry point function for the cpus + * @cpus: The cpumask of cpus that will use the entry point + * + * Set the Linux entry point for the SCM to transfer control to when coming + * out of a power down. CPU power down may be executed on cpuidle or hotplug. + */ +int qcom_scm_set_warm_boot_addr(void *entry, const cpumask_t *cpus) +{ + int ret; + int flags = 0; + int cpu; + + /* + * Reassign only if we are switching from hotplug entry point + * to cpuidle entry point or vice versa. + */ + for_each_cpu(cpu, cpus) { + if (entry == qcom_scm_wb[cpu].entry) + continue; + flags |= qcom_scm_wb[cpu].flag; + } + + /* No change in entry function */ + if (!flags) + return 0; + + ret = qcom_scm_set_boot_addr(virt_to_phys(entry), flags); + if (!ret) { + for_each_cpu(cpu, cpus) + qcom_scm_wb[cpu].entry = entry; + } + + return ret; +} +EXPORT_SYMBOL(qcom_scm_set_warm_boot_addr); diff --git a/include/linux/qcom_scm.h b/include/linux/qcom_scm.h index 68a1d8801c6f..95ef72a47b0f 100644 --- a/include/linux/qcom_scm.h +++ b/include/linux/qcom_scm.h @@ -1,4 +1,5 @@ /* Copyright (c) 2010, Code Aurora Forum. All rights reserved. + * Copyright (C) 2015 Linaro Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -12,12 +13,8 @@ #ifndef __QCOM_SCM_H #define __QCOM_SCM_H -#define QCOM_SCM_FLAG_WARMBOOT_CPU0 0x04 -#define QCOM_SCM_FLAG_WARMBOOT_CPU1 0x02 -#define QCOM_SCM_FLAG_WARMBOOT_CPU2 0x10 -#define QCOM_SCM_FLAG_WARMBOOT_CPU3 0x40 - extern int qcom_scm_set_cold_boot_addr(void *entry, const cpumask_t *cpus); +extern int qcom_scm_set_warm_boot_addr(void *entry, const cpumask_t *cpus); #define QCOM_SCM_VERSION(major, minor) (((major) << 16) | ((minor) & 0xFF)) -- cgit v1.2.3 From 767b0235dd476596c0d4154839ae6880bec71b3c Mon Sep 17 00:00:00 2001 From: Lina Iyer Date: Mon, 2 Mar 2015 16:30:30 -0700 Subject: firmware: qcom: scm: Support cpu power down through SCM Support powering down the calling cpu, by trapping into SCM. This termination function triggers the ARM cpu to execute WFI instruction, causing the power controller to safely power the cpu down. Caches may be flushed before powering down the cpu. If cache controller is set to turn off when the cpu is powered down, then the flags argument indicates to the secure mode to flush its cache lines before executing WFI.The warm boot reset address for the cpu should be set before the calling into this function for the cpu to resume. The original code for the qcom_scm_call_atomic1() comes from a patch by Stephen Boyd [1]. The function scm_call_atomic1() has been cherry picked and renamed to match the convention used in this file. Since there are no users of scm_call_atomic2(), the function is not included. [1]. https://lkml.org/lkml/2014/8/4/765 Signed-off-by: Stephen Boyd Signed-off-by: Lina Iyer Signed-off-by: Kumar Gala --- drivers/firmware/qcom_scm.c | 57 +++++++++++++++++++++++++++++++++++++++++++++ include/linux/qcom_scm.h | 7 +++++- 2 files changed, 63 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/drivers/firmware/qcom_scm.c b/drivers/firmware/qcom_scm.c index 4d8ede4807ac..994b50fd997c 100644 --- a/drivers/firmware/qcom_scm.c +++ b/drivers/firmware/qcom_scm.c @@ -311,6 +311,45 @@ out: return ret; } +#define SCM_CLASS_REGISTER (0x2 << 8) +#define SCM_MASK_IRQS BIT(5) +#define SCM_ATOMIC(svc, cmd, n) (((((svc) << 10)|((cmd) & 0x3ff)) << 12) | \ + SCM_CLASS_REGISTER | \ + SCM_MASK_IRQS | \ + (n & 0xf)) + +/** + * qcom_scm_call_atomic1() - Send an atomic SCM command with one argument + * @svc_id: service identifier + * @cmd_id: command identifier + * @arg1: first argument + * + * This shall only be used with commands that are guaranteed to be + * uninterruptable, atomic and SMP safe. + */ +static s32 qcom_scm_call_atomic1(u32 svc, u32 cmd, u32 arg1) +{ + int context_id; + + register u32 r0 asm("r0") = SCM_ATOMIC(svc, cmd, 1); + register u32 r1 asm("r1") = (u32)&context_id; + register u32 r2 asm("r2") = arg1; + + asm volatile( + __asmeq("%0", "r0") + __asmeq("%1", "r0") + __asmeq("%2", "r1") + __asmeq("%3", "r2") +#ifdef REQUIRES_SEC + ".arch_extension sec\n" +#endif + "smc #0 @ switch to secure world\n" + : "=r" (r0) + : "r" (r0), "r" (r1), "r" (r2) + : "r3"); + return r0; +} + u32 qcom_scm_get_version(void) { int context_id; @@ -435,3 +474,21 @@ int qcom_scm_set_warm_boot_addr(void *entry, const cpumask_t *cpus) return ret; } EXPORT_SYMBOL(qcom_scm_set_warm_boot_addr); + +#define QCOM_SCM_CMD_TERMINATE_PC 0x2 +#define QCOM_SCM_FLUSH_FLAG_MASK 0x3 + +/** + * qcom_scm_cpu_power_down() - Power down the cpu + * @flags - Flags to flush cache + * + * This is an end point to power down cpu. If there was a pending interrupt, + * the control would return from this function, otherwise, the cpu jumps to the + * warm boot entry point set for this cpu upon reset. + */ +void qcom_scm_cpu_power_down(u32 flags) +{ + qcom_scm_call_atomic1(QCOM_SCM_SVC_BOOT, QCOM_SCM_CMD_TERMINATE_PC, + flags & QCOM_SCM_FLUSH_FLAG_MASK); +} +EXPORT_SYMBOL(qcom_scm_cpu_power_down); diff --git a/include/linux/qcom_scm.h b/include/linux/qcom_scm.h index 95ef72a47b0f..d7a974d5f57c 100644 --- a/include/linux/qcom_scm.h +++ b/include/linux/qcom_scm.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2010, Code Aurora Forum. All rights reserved. +/* Copyright (c) 2010-2014, The Linux Foundation. All rights reserved. * Copyright (C) 2015 Linaro Ltd. * * This program is free software; you can redistribute it and/or modify @@ -16,6 +16,11 @@ extern int qcom_scm_set_cold_boot_addr(void *entry, const cpumask_t *cpus); extern int qcom_scm_set_warm_boot_addr(void *entry, const cpumask_t *cpus); +#define QCOM_SCM_CPU_PWR_DOWN_L2_ON 0x0 +#define QCOM_SCM_CPU_PWR_DOWN_L2_OFF 0x1 + +extern void qcom_scm_cpu_power_down(u32 flags); + #define QCOM_SCM_VERSION(major, minor) (((major) << 16) | ((minor) & 0xFF)) extern u32 qcom_scm_get_version(void); -- cgit v1.2.3 From 8038dad7e888581266c76df15d70ca457a3c5910 Mon Sep 17 00:00:00 2001 From: "Paul E. McKenney" Date: Wed, 25 Feb 2015 10:34:39 -0800 Subject: smpboot: Add common code for notification from dying CPU RCU ignores offlined CPUs, so they cannot safely run RCU read-side code. (They -can- use SRCU, but not RCU.) This means that any use of RCU during or after the call to arch_cpu_idle_dead(). Unfortunately, commit 2ed53c0d6cc99 added a complete() call, which will contain RCU read-side critical sections if there is a task waiting to be awakened. Which, as it turns out, there almost never is. In my qemu/KVM testing, the to-be-awakened task is not yet asleep more than 99.5% of the time. In current mainline, failure is even harder to reproduce, requiring a virtualized environment that delays the outgoing CPU by at least three jiffies between the time it exits its stop_machine() task at CPU_DYING time and the time it calls arch_cpu_idle_dead() from the idle loop. However, this problem really can occur, especially in virtualized environments, and therefore really does need to be fixed This suggests moving back to the polling loop, but using a much shorter wait, with gentle exponential backoff instead of the old 100-millisecond wait. Most of the time, the loop will exit without waiting at all, and almost all of the remaining uses will wait only five microseconds. If the outgoing CPU is preempted, a loop will wait one jiffy, then increase the wait by a factor of 11/10ths, rounding up. As before, there is a five-second timeout. This commit therefore provides common-code infrastructure to do the dying-to-surviving CPU handoff in a safe manner. This code also provides an indication at CPU-online of whether the CPU to be onlined previously timed out on offline. The new cpu_check_up_prepare() function returns -EBUSY if this CPU previously took more than five seconds to go offline, or -EAGAIN if it has not yet managed to go offline. The rationale for -EAGAIN is that it might still be preempted, so an additional wait might well find it correctly offlined. Architecture-specific code can decide how to handle these conditions. Systems in which CPUs take themselves completely offline might respond to an -EBUSY return as if it was a zero (success) return. Systems in which the surviving CPU must take some action might take it at this time, or might simply mark the other CPU as unusable. Note that architectures that take the easy way out and simply pass the -EBUSY and -EAGAIN upwards will change the sysfs API. Signed-off-by: Paul E. McKenney Cc: Cc: [ paulmck: Fixed state machine for architectures that don't check earlier CPU-hotplug results as suggested by James Hogan. ] --- include/linux/cpu.h | 12 ++++ kernel/smpboot.c | 156 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 168 insertions(+) (limited to 'include') diff --git a/include/linux/cpu.h b/include/linux/cpu.h index 4260e8594bd7..4744ef915acd 100644 --- a/include/linux/cpu.h +++ b/include/linux/cpu.h @@ -95,6 +95,8 @@ enum { * Called on the new cpu, just before * enabling interrupts. Must not sleep, * must not fail */ +#define CPU_BROKEN 0x000C /* CPU (unsigned)v did not die properly, + * perhaps due to preemption. */ /* Used for CPU hotplug events occurring while tasks are frozen due to a suspend * operation in progress @@ -271,4 +273,14 @@ void arch_cpu_idle_enter(void); void arch_cpu_idle_exit(void); void arch_cpu_idle_dead(void); +DECLARE_PER_CPU(bool, cpu_dead_idle); + +int cpu_report_state(int cpu); +int cpu_check_up_prepare(int cpu); +void cpu_set_state_online(int cpu); +#ifdef CONFIG_HOTPLUG_CPU +bool cpu_wait_death(unsigned int cpu, int seconds); +bool cpu_report_death(void); +#endif /* #ifdef CONFIG_HOTPLUG_CPU */ + #endif /* _LINUX_CPU_H_ */ diff --git a/kernel/smpboot.c b/kernel/smpboot.c index 40190f28db35..c697f73d82d6 100644 --- a/kernel/smpboot.c +++ b/kernel/smpboot.c @@ -4,6 +4,7 @@ #include #include #include +#include #include #include #include @@ -314,3 +315,158 @@ void smpboot_unregister_percpu_thread(struct smp_hotplug_thread *plug_thread) put_online_cpus(); } EXPORT_SYMBOL_GPL(smpboot_unregister_percpu_thread); + +static DEFINE_PER_CPU(atomic_t, cpu_hotplug_state) = ATOMIC_INIT(CPU_POST_DEAD); + +/* + * Called to poll specified CPU's state, for example, when waiting for + * a CPU to come online. + */ +int cpu_report_state(int cpu) +{ + return atomic_read(&per_cpu(cpu_hotplug_state, cpu)); +} + +/* + * If CPU has died properly, set its state to CPU_UP_PREPARE and + * return success. Otherwise, return -EBUSY if the CPU died after + * cpu_wait_death() timed out. And yet otherwise again, return -EAGAIN + * if cpu_wait_death() timed out and the CPU still hasn't gotten around + * to dying. In the latter two cases, the CPU might not be set up + * properly, but it is up to the arch-specific code to decide. + * Finally, -EIO indicates an unanticipated problem. + * + * Note that it is permissible to omit this call entirely, as is + * done in architectures that do no CPU-hotplug error checking. + */ +int cpu_check_up_prepare(int cpu) +{ + if (!IS_ENABLED(CONFIG_HOTPLUG_CPU)) { + atomic_set(&per_cpu(cpu_hotplug_state, cpu), CPU_UP_PREPARE); + return 0; + } + + switch (atomic_read(&per_cpu(cpu_hotplug_state, cpu))) { + + case CPU_POST_DEAD: + + /* The CPU died properly, so just start it up again. */ + atomic_set(&per_cpu(cpu_hotplug_state, cpu), CPU_UP_PREPARE); + return 0; + + case CPU_DEAD_FROZEN: + + /* + * Timeout during CPU death, so let caller know. + * The outgoing CPU completed its processing, but after + * cpu_wait_death() timed out and reported the error. The + * caller is free to proceed, in which case the state + * will be reset properly by cpu_set_state_online(). + * Proceeding despite this -EBUSY return makes sense + * for systems where the outgoing CPUs take themselves + * offline, with no post-death manipulation required from + * a surviving CPU. + */ + return -EBUSY; + + case CPU_BROKEN: + + /* + * The most likely reason we got here is that there was + * a timeout during CPU death, and the outgoing CPU never + * did complete its processing. This could happen on + * a virtualized system if the outgoing VCPU gets preempted + * for more than five seconds, and the user attempts to + * immediately online that same CPU. Trying again later + * might return -EBUSY above, hence -EAGAIN. + */ + return -EAGAIN; + + default: + + /* Should not happen. Famous last words. */ + return -EIO; + } +} + +/* + * Mark the specified CPU online. + * + * Note that it is permissible to omit this call entirely, as is + * done in architectures that do no CPU-hotplug error checking. + */ +void cpu_set_state_online(int cpu) +{ + (void)atomic_xchg(&per_cpu(cpu_hotplug_state, cpu), CPU_ONLINE); +} + +#ifdef CONFIG_HOTPLUG_CPU + +/* + * Wait for the specified CPU to exit the idle loop and die. + */ +bool cpu_wait_death(unsigned int cpu, int seconds) +{ + int jf_left = seconds * HZ; + int oldstate; + bool ret = true; + int sleep_jf = 1; + + might_sleep(); + + /* The outgoing CPU will normally get done quite quickly. */ + if (atomic_read(&per_cpu(cpu_hotplug_state, cpu)) == CPU_DEAD) + goto update_state; + udelay(5); + + /* But if the outgoing CPU dawdles, wait increasingly long times. */ + while (atomic_read(&per_cpu(cpu_hotplug_state, cpu)) != CPU_DEAD) { + schedule_timeout_uninterruptible(sleep_jf); + jf_left -= sleep_jf; + if (jf_left <= 0) + break; + sleep_jf = DIV_ROUND_UP(sleep_jf * 11, 10); + } +update_state: + oldstate = atomic_read(&per_cpu(cpu_hotplug_state, cpu)); + if (oldstate == CPU_DEAD) { + /* Outgoing CPU died normally, update state. */ + smp_mb(); /* atomic_read() before update. */ + atomic_set(&per_cpu(cpu_hotplug_state, cpu), CPU_POST_DEAD); + } else { + /* Outgoing CPU still hasn't died, set state accordingly. */ + if (atomic_cmpxchg(&per_cpu(cpu_hotplug_state, cpu), + oldstate, CPU_BROKEN) != oldstate) + goto update_state; + ret = false; + } + return ret; +} + +/* + * Called by the outgoing CPU to report its successful death. Return + * false if this report follows the surviving CPU's timing out. + * + * A separate "CPU_DEAD_FROZEN" is used when the surviving CPU + * timed out. This approach allows architectures to omit calls to + * cpu_check_up_prepare() and cpu_set_state_online() without defeating + * the next cpu_wait_death()'s polling loop. + */ +bool cpu_report_death(void) +{ + int oldstate; + int newstate; + int cpu = smp_processor_id(); + + do { + oldstate = atomic_read(&per_cpu(cpu_hotplug_state, cpu)); + if (oldstate != CPU_BROKEN) + newstate = CPU_DEAD; + else + newstate = CPU_DEAD_FROZEN; + } while (atomic_cmpxchg(&per_cpu(cpu_hotplug_state, cpu), + oldstate, newstate) != oldstate); + return newstate == CPU_DEAD; +} + +#endif /* #ifdef CONFIG_HOTPLUG_CPU */ -- cgit v1.2.3 From 0ddcf43d5d4a03ded1ee3f6b3b72a0cbed4e90b1 Mon Sep 17 00:00:00 2001 From: Alexander Duyck Date: Fri, 6 Mar 2015 13:47:00 -0800 Subject: ipv4: FIB Local/MAIN table collapse This patch is meant to collapse local and main into one by converting tb_data from an array to a pointer. Doing this allows us to point the local table into the main while maintaining the same variables in the table. As such the tb_data was converted from an array to a pointer, and a new array called data is added in order to still provide an object for tb_data to point to. In order to track the origin of the fib aliases a tb_id value was added in a hole that existed on 64b systems. Using this we can also reverse the merge in the event that custom FIB rules are enabled. With this patch I am seeing an improvement of 20ns to 30ns for routing lookups as long as custom rules are not enabled, with custom rules enabled we fall back to split tables and the original behavior. Signed-off-by: Alexander Duyck Signed-off-by: David S. Miller --- include/net/fib_rules.h | 2 +- include/net/ip_fib.h | 26 +++----- net/core/fib_rules.c | 8 ++- net/ipv4/fib_frontend.c | 59 +++++++++++++++-- net/ipv4/fib_lookup.h | 1 + net/ipv4/fib_rules.c | 20 ++++-- net/ipv4/fib_trie.c | 172 ++++++++++++++++++++++++++++++++++++++++++++++-- 7 files changed, 250 insertions(+), 38 deletions(-) (limited to 'include') diff --git a/include/net/fib_rules.h b/include/net/fib_rules.h index e584de16e4c3..88d2ae526961 100644 --- a/include/net/fib_rules.h +++ b/include/net/fib_rules.h @@ -58,7 +58,7 @@ struct fib_rules_ops { struct sk_buff *, struct fib_rule_hdr *, struct nlattr **); - void (*delete)(struct fib_rule *); + int (*delete)(struct fib_rule *); int (*compare)(struct fib_rule *, struct fib_rule_hdr *, struct nlattr **); diff --git a/include/net/ip_fib.h b/include/net/ip_fib.h index 1657604c5dd3..54271ed0ed45 100644 --- a/include/net/ip_fib.h +++ b/include/net/ip_fib.h @@ -186,7 +186,8 @@ struct fib_table { int tb_default; int tb_num_default; struct rcu_head rcu; - unsigned long tb_data[0]; + unsigned long *tb_data; + unsigned long __data[0]; }; int fib_table_lookup(struct fib_table *tb, const struct flowi4 *flp, @@ -196,11 +197,10 @@ int fib_table_delete(struct fib_table *, struct fib_config *); int fib_table_dump(struct fib_table *table, struct sk_buff *skb, struct netlink_callback *cb); int fib_table_flush(struct fib_table *table); +struct fib_table *fib_trie_unmerge(struct fib_table *main_tb); void fib_table_flush_external(struct fib_table *table); void fib_free_table(struct fib_table *tb); - - #ifndef CONFIG_IP_MULTIPLE_TABLES #define TABLE_LOCAL_INDEX (RT_TABLE_LOCAL & (FIB_TABLE_HASHSZ - 1)) @@ -229,18 +229,13 @@ static inline int fib_lookup(struct net *net, const struct flowi4 *flp, struct fib_result *res) { struct fib_table *tb; - int err; + int err = -ENETUNREACH; rcu_read_lock(); - for (err = 0; !err; err = -ENETUNREACH) { - tb = fib_get_table(net, RT_TABLE_LOCAL); - if (tb && !fib_table_lookup(tb, flp, res, FIB_LOOKUP_NOREF)) - break; - tb = fib_get_table(net, RT_TABLE_MAIN); - if (tb && !fib_table_lookup(tb, flp, res, FIB_LOOKUP_NOREF)) - break; - } + tb = fib_get_table(net, RT_TABLE_MAIN); + if (tb && !fib_table_lookup(tb, flp, res, FIB_LOOKUP_NOREF)) + err = 0; rcu_read_unlock(); @@ -270,10 +265,6 @@ static inline int fib_lookup(struct net *net, struct flowi4 *flp, res->tclassid = 0; for (err = 0; !err; err = -ENETUNREACH) { - tb = rcu_dereference_rtnl(net->ipv4.fib_local); - if (tb && !fib_table_lookup(tb, flp, res, FIB_LOOKUP_NOREF)) - break; - tb = rcu_dereference_rtnl(net->ipv4.fib_main); if (tb && !fib_table_lookup(tb, flp, res, FIB_LOOKUP_NOREF)) break; @@ -309,6 +300,7 @@ static inline int fib_num_tclassid_users(struct net *net) return 0; } #endif +int fib_unmerge(struct net *net); void fib_flush_external(struct net *net); /* Exported by fib_semantics.c */ @@ -320,7 +312,7 @@ void fib_select_multipath(struct fib_result *res); /* Exported by fib_trie.c */ void fib_trie_init(void); -struct fib_table *fib_trie_table(u32 id); +struct fib_table *fib_trie_table(u32 id, struct fib_table *alias); static inline void fib_combine_itag(u32 *itag, const struct fib_result *res) { diff --git a/net/core/fib_rules.c b/net/core/fib_rules.c index 44706e81b2e0..b55677fed1c8 100644 --- a/net/core/fib_rules.c +++ b/net/core/fib_rules.c @@ -492,6 +492,12 @@ static int fib_nl_delrule(struct sk_buff *skb, struct nlmsghdr* nlh) goto errout; } + if (ops->delete) { + err = ops->delete(rule); + if (err) + goto errout; + } + list_del_rcu(&rule->list); if (rule->action == FR_ACT_GOTO) { @@ -517,8 +523,6 @@ static int fib_nl_delrule(struct sk_buff *skb, struct nlmsghdr* nlh) notify_rule_change(RTM_DELRULE, rule, ops, nlh, NETLINK_CB(skb).portid); - if (ops->delete) - ops->delete(rule); fib_rule_put(rule); flush_route_cache(ops); rules_ops_put(ops); diff --git a/net/ipv4/fib_frontend.c b/net/ipv4/fib_frontend.c index e067770235bf..7cda3b0521d8 100644 --- a/net/ipv4/fib_frontend.c +++ b/net/ipv4/fib_frontend.c @@ -52,14 +52,14 @@ static int __net_init fib4_rules_init(struct net *net) { struct fib_table *local_table, *main_table; - local_table = fib_trie_table(RT_TABLE_LOCAL); - if (local_table == NULL) - return -ENOMEM; - - main_table = fib_trie_table(RT_TABLE_MAIN); + main_table = fib_trie_table(RT_TABLE_MAIN, NULL); if (main_table == NULL) goto fail; + local_table = fib_trie_table(RT_TABLE_LOCAL, main_table); + if (local_table == NULL) + return -ENOMEM; + hlist_add_head_rcu(&local_table->tb_hlist, &net->ipv4.fib_table_hash[TABLE_LOCAL_INDEX]); hlist_add_head_rcu(&main_table->tb_hlist, @@ -74,7 +74,7 @@ fail: struct fib_table *fib_new_table(struct net *net, u32 id) { - struct fib_table *tb; + struct fib_table *tb, *alias = NULL; unsigned int h; if (id == 0) @@ -83,7 +83,10 @@ struct fib_table *fib_new_table(struct net *net, u32 id) if (tb) return tb; - tb = fib_trie_table(id); + if (id == RT_TABLE_LOCAL) + alias = fib_new_table(net, RT_TABLE_MAIN); + + tb = fib_trie_table(id, alias); if (!tb) return NULL; @@ -126,6 +129,48 @@ struct fib_table *fib_get_table(struct net *net, u32 id) } #endif /* CONFIG_IP_MULTIPLE_TABLES */ +static void fib_replace_table(struct net *net, struct fib_table *old, + struct fib_table *new) +{ +#ifdef CONFIG_IP_MULTIPLE_TABLES + switch (new->tb_id) { + case RT_TABLE_LOCAL: + rcu_assign_pointer(net->ipv4.fib_local, new); + break; + case RT_TABLE_MAIN: + rcu_assign_pointer(net->ipv4.fib_main, new); + break; + case RT_TABLE_DEFAULT: + rcu_assign_pointer(net->ipv4.fib_default, new); + break; + default: + break; + } + +#endif + /* replace the old table in the hlist */ + hlist_replace_rcu(&old->tb_hlist, &new->tb_hlist); +} + +int fib_unmerge(struct net *net) +{ + struct fib_table *old, *new; + + old = fib_get_table(net, RT_TABLE_LOCAL); + new = fib_trie_unmerge(old); + + if (!new) + return -ENOMEM; + + /* replace merged table with clean table */ + if (new != old) { + fib_replace_table(net, old, new); + fib_free_table(old); + } + + return 0; +} + static void fib_flush(struct net *net) { int flushed = 0; diff --git a/net/ipv4/fib_lookup.h b/net/ipv4/fib_lookup.h index ae2e6eede46e..c6211ed60b03 100644 --- a/net/ipv4/fib_lookup.h +++ b/net/ipv4/fib_lookup.h @@ -12,6 +12,7 @@ struct fib_alias { u8 fa_type; u8 fa_state; u8 fa_slen; + u32 tb_id; struct rcu_head rcu; }; diff --git a/net/ipv4/fib_rules.c b/net/ipv4/fib_rules.c index 190d0d00d744..e9bc5e42cf43 100644 --- a/net/ipv4/fib_rules.c +++ b/net/ipv4/fib_rules.c @@ -174,6 +174,11 @@ static int fib4_rule_configure(struct fib_rule *rule, struct sk_buff *skb, if (frh->tos & ~IPTOS_TOS_MASK) goto errout; + /* split local/main if they are not already split */ + err = fib_unmerge(net); + if (err) + goto errout; + if (rule->table == RT_TABLE_UNSPEC) { if (rule->action == FR_ACT_TO_TBL) { struct fib_table *table; @@ -216,17 +221,24 @@ errout: return err; } -static void fib4_rule_delete(struct fib_rule *rule) +static int fib4_rule_delete(struct fib_rule *rule) { struct net *net = rule->fr_net; -#ifdef CONFIG_IP_ROUTE_CLASSID - struct fib4_rule *rule4 = (struct fib4_rule *) rule; + int err; - if (rule4->tclassid) + /* split local/main if they are not already split */ + err = fib_unmerge(net); + if (err) + goto errout; + +#ifdef CONFIG_IP_ROUTE_CLASSID + if (((struct fib4_rule *)rule)->tclassid) net->ipv4.fib_num_tclassid_users--; #endif net->ipv4.fib_has_custom_rules = true; fib_flush_external(rule->fr_net); +errout: + return err; } static int fib4_rule_compare(struct fib_rule *rule, struct fib_rule_hdr *frh, diff --git a/net/ipv4/fib_trie.c b/net/ipv4/fib_trie.c index 83290beaf7cf..7b2badd74ad8 100644 --- a/net/ipv4/fib_trie.c +++ b/net/ipv4/fib_trie.c @@ -1120,6 +1120,9 @@ int fib_table_insert(struct fib_table *tb, struct fib_config *cfg) break; if (fa->fa_info->fib_priority != fi->fib_priority) break; + /* duplicate entry from another table */ + if (WARN_ON(fa->tb_id != tb->tb_id)) + continue; if (fa->fa_type == cfg->fc_type && fa->fa_info == fi) { fa_match = fa; @@ -1197,6 +1200,7 @@ int fib_table_insert(struct fib_table *tb, struct fib_config *cfg) new_fa->fa_type = cfg->fc_type; new_fa->fa_state = 0; new_fa->fa_slen = slen; + new_fa->tb_id = tb->tb_id; /* (Optionally) offload fib entry to switch hardware. */ err = netdev_switch_fib_ipv4_add(key, plen, fi, tos, @@ -1217,7 +1221,7 @@ int fib_table_insert(struct fib_table *tb, struct fib_config *cfg) tb->tb_num_default++; rt_cache_flush(cfg->fc_nlinfo.nl_net); - rtmsg_fib(RTM_NEWROUTE, htonl(key), new_fa, plen, tb->tb_id, + rtmsg_fib(RTM_NEWROUTE, htonl(key), new_fa, plen, new_fa->tb_id, &cfg->fc_nlinfo, 0); succeeded: return 0; @@ -1243,7 +1247,7 @@ static inline t_key prefix_mismatch(t_key key, struct key_vector *n) int fib_table_lookup(struct fib_table *tb, const struct flowi4 *flp, struct fib_result *res, int fib_flags) { - struct trie *t = (struct trie *)tb->tb_data; + struct trie *t = (struct trie *) tb->tb_data; #ifdef CONFIG_IP_FIB_TRIE_STATS struct trie_use_stats __percpu *stats = t->stats; #endif @@ -1483,6 +1487,9 @@ int fib_table_delete(struct fib_table *tb, struct fib_config *cfg) if ((fa->fa_slen != slen) || (fa->fa_tos != tos)) break; + if (fa->tb_id != tb->tb_id) + continue; + if ((!cfg->fc_type || fa->fa_type == cfg->fc_type) && (cfg->fc_scope == RT_SCOPE_NOWHERE || fa->fa_info->fib_scope == cfg->fc_scope) && @@ -1576,6 +1583,120 @@ found: return n; } +static void fib_trie_free(struct fib_table *tb) +{ + struct trie *t = (struct trie *)tb->tb_data; + struct key_vector *pn = t->kv; + unsigned long cindex = 1; + struct hlist_node *tmp; + struct fib_alias *fa; + + /* walk trie in reverse order and free everything */ + for (;;) { + struct key_vector *n; + + if (!(cindex--)) { + t_key pkey = pn->key; + + if (IS_TRIE(pn)) + break; + + n = pn; + pn = node_parent(pn); + + /* drop emptied tnode */ + put_child_root(pn, n->key, NULL); + node_free(n); + + cindex = get_index(pkey, pn); + + continue; + } + + /* grab the next available node */ + n = get_child(pn, cindex); + if (!n) + continue; + + if (IS_TNODE(n)) { + /* record pn and cindex for leaf walking */ + pn = n; + cindex = 1ul << n->bits; + + continue; + } + + hlist_for_each_entry_safe(fa, tmp, &n->leaf, fa_list) { + hlist_del_rcu(&fa->fa_list); + alias_free_mem_rcu(fa); + } + + put_child_root(pn, n->key, NULL); + node_free(n); + } + +#ifdef CONFIG_IP_FIB_TRIE_STATS + free_percpu(t->stats); +#endif + kfree(tb); +} + +struct fib_table *fib_trie_unmerge(struct fib_table *oldtb) +{ + struct trie *ot = (struct trie *)oldtb->tb_data; + struct key_vector *l, *tp = ot->kv; + struct fib_table *local_tb; + struct fib_alias *fa; + struct trie *lt; + t_key key = 0; + + if (oldtb->tb_data == oldtb->__data) + return oldtb; + + local_tb = fib_trie_table(RT_TABLE_LOCAL, NULL); + if (!local_tb) + return NULL; + + lt = (struct trie *)local_tb->tb_data; + + while ((l = leaf_walk_rcu(&tp, key)) != NULL) { + struct key_vector *local_l = NULL, *local_tp; + + hlist_for_each_entry_rcu(fa, &l->leaf, fa_list) { + struct fib_alias *new_fa; + + if (local_tb->tb_id != fa->tb_id) + continue; + + /* clone fa for new local table */ + new_fa = kmem_cache_alloc(fn_alias_kmem, GFP_KERNEL); + if (!new_fa) + goto out; + + memcpy(new_fa, fa, sizeof(*fa)); + + /* insert clone into table */ + if (!local_l) + local_l = fib_find_node(lt, &local_tp, l->key); + + if (fib_insert_alias(lt, local_tp, local_l, new_fa, + NULL, l->key)) + goto out; + } + + /* stop loop if key wrapped back to 0 */ + key = l->key + 1; + if (key < l->key) + break; + } + + return local_tb; +out: + fib_trie_free(local_tb); + + return NULL; +} + /* Caller must hold RTNL */ void fib_table_flush_external(struct fib_table *tb) { @@ -1587,6 +1708,7 @@ void fib_table_flush_external(struct fib_table *tb) /* walk trie in reverse order */ for (;;) { + unsigned char slen = 0; struct key_vector *n; if (!(cindex--)) { @@ -1596,8 +1718,8 @@ void fib_table_flush_external(struct fib_table *tb) if (IS_TRIE(pn)) break; - /* no need to resize like in flush below */ - pn = node_parent(pn); + /* resize completed node */ + pn = resize(t, pn); cindex = get_index(pkey, pn); continue; @@ -1619,6 +1741,18 @@ void fib_table_flush_external(struct fib_table *tb) hlist_for_each_entry_safe(fa, tmp, &n->leaf, fa_list) { struct fib_info *fi = fa->fa_info; + /* if alias was cloned to local then we just + * need to remove the local copy from main + */ + if (tb->tb_id != fa->tb_id) { + hlist_del_rcu(&fa->fa_list); + alias_free_mem_rcu(fa); + continue; + } + + /* record local slen */ + slen = fa->fa_slen; + if (!fi || !(fi->fib_flags & RTNH_F_EXTERNAL)) continue; @@ -1627,6 +1761,16 @@ void fib_table_flush_external(struct fib_table *tb) fi, fa->fa_tos, fa->fa_type, tb->tb_id); } + + /* update leaf slen */ + n->slen = slen; + + if (hlist_empty(&n->leaf)) { + put_child_root(pn, n->key, NULL); + node_free(n); + } else { + leaf_pull_suffix(pn, n); + } } } @@ -1711,7 +1855,8 @@ static void __trie_free_rcu(struct rcu_head *head) #ifdef CONFIG_IP_FIB_TRIE_STATS struct trie *t = (struct trie *)tb->tb_data; - free_percpu(t->stats); + if (tb->tb_data == tb->__data) + free_percpu(t->stats); #endif /* CONFIG_IP_FIB_TRIE_STATS */ kfree(tb); } @@ -1738,6 +1883,11 @@ static int fn_trie_dump_leaf(struct key_vector *l, struct fib_table *tb, continue; } + if (tb->tb_id != fa->tb_id) { + i++; + continue; + } + if (fib_dump_info(skb, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq, RTM_NEWROUTE, @@ -1804,18 +1954,26 @@ void __init fib_trie_init(void) 0, SLAB_PANIC, NULL); } -struct fib_table *fib_trie_table(u32 id) +struct fib_table *fib_trie_table(u32 id, struct fib_table *alias) { struct fib_table *tb; struct trie *t; + size_t sz = sizeof(*tb); + + if (!alias) + sz += sizeof(struct trie); - tb = kzalloc(sizeof(*tb) + sizeof(struct trie), GFP_KERNEL); + tb = kzalloc(sz, GFP_KERNEL); if (tb == NULL) return NULL; tb->tb_id = id; tb->tb_default = -1; tb->tb_num_default = 0; + tb->tb_data = (alias ? alias->__data : tb->__data); + + if (alias) + return tb; t = (struct trie *) tb->tb_data; t->kv[0].pos = KEYLENGTH; -- cgit v1.2.3 From 988dfbd795cf08b00576c1ced4210281b2bccffc Mon Sep 17 00:00:00 2001 From: Herbert Xu Date: Tue, 10 Mar 2015 09:27:55 +1100 Subject: rhashtable: Move hash_rnd into bucket_table Currently hash_rnd is a parameter that users can set. However, no existing users set this parameter. It is also something that people are unlikely to want to set directly since it's just a random number. In preparation for allowing the reseeding/rehashing of rhashtable, this patch moves hash_rnd into bucket_table so that it's now an internal state rather than a parameter. Signed-off-by: Herbert Xu Acked-by: Thomas Graf Signed-off-by: David S. Miller --- include/linux/rhashtable.h | 4 ++-- lib/rhashtable.c | 24 +++++++++++++++--------- 2 files changed, 17 insertions(+), 11 deletions(-) (limited to 'include') diff --git a/include/linux/rhashtable.h b/include/linux/rhashtable.h index d438eeb08bff..5ef8ea551556 100644 --- a/include/linux/rhashtable.h +++ b/include/linux/rhashtable.h @@ -49,12 +49,14 @@ struct rhash_head { /** * struct bucket_table - Table of hash buckets * @size: Number of hash buckets + * @hash_rnd: Random seed to fold into hash * @locks_mask: Mask to apply before accessing locks[] * @locks: Array of spinlocks protecting individual buckets * @buckets: size * hash buckets */ struct bucket_table { size_t size; + u32 hash_rnd; unsigned int locks_mask; spinlock_t *locks; @@ -72,7 +74,6 @@ struct rhashtable; * @key_len: Length of key * @key_offset: Offset of key in struct to be hashed * @head_offset: Offset of rhash_head in struct to be hashed - * @hash_rnd: Seed to use while hashing * @max_shift: Maximum number of shifts while expanding * @min_shift: Minimum number of shifts while shrinking * @nulls_base: Base value to generate nulls marker @@ -85,7 +86,6 @@ struct rhashtable_params { size_t key_len; size_t key_offset; size_t head_offset; - u32 hash_rnd; size_t max_shift; size_t min_shift; u32 nulls_base; diff --git a/lib/rhashtable.c b/lib/rhashtable.c index b5344ef4c684..ba15dceee27f 100644 --- a/lib/rhashtable.c +++ b/lib/rhashtable.c @@ -66,25 +66,28 @@ static u32 rht_bucket_index(const struct bucket_table *tbl, u32 hash) return hash & (tbl->size - 1); } -static u32 obj_raw_hashfn(const struct rhashtable *ht, const void *ptr) +static u32 obj_raw_hashfn(struct rhashtable *ht, const void *ptr) { + struct bucket_table *tbl = rht_dereference_rcu(ht->tbl, ht); u32 hash; if (unlikely(!ht->p.key_len)) - hash = ht->p.obj_hashfn(ptr, ht->p.hash_rnd); + hash = ht->p.obj_hashfn(ptr, tbl->hash_rnd); else hash = ht->p.hashfn(ptr + ht->p.key_offset, ht->p.key_len, - ht->p.hash_rnd); + tbl->hash_rnd); return hash >> HASH_RESERVED_SPACE; } static u32 key_hashfn(struct rhashtable *ht, const void *key, u32 len) { - return ht->p.hashfn(key, len, ht->p.hash_rnd) >> HASH_RESERVED_SPACE; + struct bucket_table *tbl = rht_dereference_rcu(ht->tbl, ht); + + return ht->p.hashfn(key, len, tbl->hash_rnd) >> HASH_RESERVED_SPACE; } -static u32 head_hashfn(const struct rhashtable *ht, +static u32 head_hashfn(struct rhashtable *ht, const struct bucket_table *tbl, const struct rhash_head *he) { @@ -92,7 +95,7 @@ static u32 head_hashfn(const struct rhashtable *ht, } #ifdef CONFIG_PROVE_LOCKING -static void debug_dump_buckets(const struct rhashtable *ht, +static void debug_dump_buckets(struct rhashtable *ht, const struct bucket_table *tbl) { struct rhash_head *he; @@ -385,6 +388,8 @@ int rhashtable_expand(struct rhashtable *ht) if (new_tbl == NULL) return -ENOMEM; + new_tbl->hash_rnd = old_tbl->hash_rnd; + atomic_inc(&ht->shift); /* Make insertions go into the new, empty table right away. Deletions @@ -476,6 +481,8 @@ int rhashtable_shrink(struct rhashtable *ht) if (new_tbl == NULL) return -ENOMEM; + new_tbl->hash_rnd = tbl->hash_rnd; + rcu_assign_pointer(ht->future_tbl, new_tbl); synchronize_rcu(); @@ -1099,14 +1106,13 @@ int rhashtable_init(struct rhashtable *ht, struct rhashtable_params *params) if (tbl == NULL) return -ENOMEM; + get_random_bytes(&tbl->hash_rnd, sizeof(tbl->hash_rnd)); + atomic_set(&ht->nelems, 0); atomic_set(&ht->shift, ilog2(tbl->size)); RCU_INIT_POINTER(ht->tbl, tbl); RCU_INIT_POINTER(ht->future_tbl, tbl); - if (!ht->p.hash_rnd) - get_random_bytes(&ht->p.hash_rnd, sizeof(ht->p.hash_rnd)); - INIT_WORK(&ht->run_work, rht_deferred_worker); return 0; -- cgit v1.2.3 From 1d002fa720738bcd0bddb9178e9ea0773288e1dd Mon Sep 17 00:00:00 2001 From: Simon Farnsworth Date: Tue, 10 Feb 2015 18:38:08 +0000 Subject: drm/dp: Use large transactions for I2C over AUX MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Older DisplayPort to DVI-D Dual Link adapters designed by Bizlink have bugs in their I2C over AUX implementation (fixed in newer revisions). They work fine with Windows, but fail with Linux. It turns out that they cannot keep an I2C transaction open unless the previous read was 16 bytes; shorter reads can only be followed by a zero byte transfer ending the I2C transaction. Copy Windows's behaviour, and read 16 bytes at a time. If we get a short reply, assume that there's a hardware bottleneck, and shrink our read size to match. For this purpose, use the algorithm in the DisplayPort 1.2 spec, in the hopes that it'll be closest to what Windows does. Also provide an unsafe module parameter for testing smaller transfer sizes, in case there are sinks out there that cannot work with Windows. Note also that despite the previous comment in drm_dp_i2c_xfer, this speeds up native DP EDID reads; Ville Syrjälä found the following changes in his testing: Device under test: old -> with this patch DP->DVI (OUI 001cf8): 40ms -> 35ms DP->VGA (OUI 0022b9): 45ms -> 38ms Zotac DP->2xHDMI: 25ms -> 4ms Asus PB278 monitor: 22ms -> 3ms A back of the envelope calculation shows that peak theoretical transfer rate for 1 byte reads is around 60 kbit/s; with 16 byte reads, this increases to around 500 kbit/s, which explains the increase in speed. Bugzilla: https://bugs.freedesktop.org/show_bug.cgi?id=55228 Tested-by: Aidan Marks (v3) Signed-off-by: Simon Farnsworth Reviewed-by: Ville Syrjälä Signed-off-by: Daniel Vetter --- drivers/gpu/drm/drm_dp_helper.c | 76 +++++++++++++++++++++++++++++++---------- include/drm/drm_dp_helper.h | 5 +++ 2 files changed, 63 insertions(+), 18 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/drm_dp_helper.c b/drivers/gpu/drm/drm_dp_helper.c index f1283878ff6d..d5368ea56a0f 100644 --- a/drivers/gpu/drm/drm_dp_helper.c +++ b/drivers/gpu/drm/drm_dp_helper.c @@ -427,11 +427,13 @@ static u32 drm_dp_i2c_functionality(struct i2c_adapter *adapter) * retrying the transaction as appropriate. It is assumed that the * aux->transfer function does not modify anything in the msg other than the * reply field. + * + * Returns bytes transferred on success, or a negative error code on failure. */ static int drm_dp_i2c_do_msg(struct drm_dp_aux *aux, struct drm_dp_aux_msg *msg) { unsigned int retry; - int err; + int ret; /* * DP1.2 sections 2.7.7.1.5.6.1 and 2.7.7.1.6.6.1: A DP Source device @@ -440,14 +442,14 @@ static int drm_dp_i2c_do_msg(struct drm_dp_aux *aux, struct drm_dp_aux_msg *msg) */ for (retry = 0; retry < 7; retry++) { mutex_lock(&aux->hw_mutex); - err = aux->transfer(aux, msg); + ret = aux->transfer(aux, msg); mutex_unlock(&aux->hw_mutex); - if (err < 0) { - if (err == -EBUSY) + if (ret < 0) { + if (ret == -EBUSY) continue; - DRM_DEBUG_KMS("transaction failed: %d\n", err); - return err; + DRM_DEBUG_KMS("transaction failed: %d\n", ret); + return ret; } @@ -488,9 +490,7 @@ static int drm_dp_i2c_do_msg(struct drm_dp_aux *aux, struct drm_dp_aux_msg *msg) * Both native ACK and I2C ACK replies received. We * can assume the transfer was successful. */ - if (err < msg->size) - return -EPROTO; - return 0; + return ret; case DP_AUX_I2C_REPLY_NACK: DRM_DEBUG_KMS("I2C nack\n"); @@ -513,14 +513,55 @@ static int drm_dp_i2c_do_msg(struct drm_dp_aux *aux, struct drm_dp_aux_msg *msg) return -EREMOTEIO; } +/* + * Keep retrying drm_dp_i2c_do_msg until all data has been transferred. + * + * Returns an error code on failure, or a recommended transfer size on success. + */ +static int drm_dp_i2c_drain_msg(struct drm_dp_aux *aux, struct drm_dp_aux_msg *orig_msg) +{ + int err, ret = orig_msg->size; + struct drm_dp_aux_msg msg = *orig_msg; + + while (msg.size > 0) { + err = drm_dp_i2c_do_msg(aux, &msg); + if (err <= 0) + return err == 0 ? -EPROTO : err; + + if (err < msg.size && err < ret) { + DRM_DEBUG_KMS("Partial I2C reply: requested %zu bytes got %d bytes\n", + msg.size, err); + ret = err; + } + + msg.size -= err; + msg.buffer += err; + } + + return ret; +} + +/* + * Bizlink designed DP->DVI-D Dual Link adapters require the I2C over AUX + * packets to be as large as possible. If not, the I2C transactions never + * succeed. Hence the default is maximum. + */ +static int dp_aux_i2c_transfer_size __read_mostly = DP_AUX_MAX_PAYLOAD_BYTES; +module_param_unsafe(dp_aux_i2c_transfer_size, int, 0644); +MODULE_PARM_DESC(dp_aux_i2c_transfer_size, + "Number of bytes to transfer in a single I2C over DP AUX CH message, (1-16, default 16)"); + static int drm_dp_i2c_xfer(struct i2c_adapter *adapter, struct i2c_msg *msgs, int num) { struct drm_dp_aux *aux = adapter->algo_data; unsigned int i, j; + unsigned transfer_size; struct drm_dp_aux_msg msg; int err = 0; + dp_aux_i2c_transfer_size = clamp(dp_aux_i2c_transfer_size, 1, DP_AUX_MAX_PAYLOAD_BYTES); + memset(&msg, 0, sizeof(msg)); for (i = 0; i < num; i++) { @@ -538,20 +579,19 @@ static int drm_dp_i2c_xfer(struct i2c_adapter *adapter, struct i2c_msg *msgs, err = drm_dp_i2c_do_msg(aux, &msg); if (err < 0) break; - /* - * Many hardware implementations support FIFOs larger than a - * single byte, but it has been empirically determined that - * transferring data in larger chunks can actually lead to - * decreased performance. Therefore each message is simply - * transferred byte-by-byte. + /* We want each transaction to be as large as possible, but + * we'll go to smaller sizes if the hardware gives us a + * short reply. */ - for (j = 0; j < msgs[i].len; j++) { + transfer_size = dp_aux_i2c_transfer_size; + for (j = 0; j < msgs[i].len; j += msg.size) { msg.buffer = msgs[i].buf + j; - msg.size = 1; + msg.size = min(transfer_size, msgs[i].len - j); - err = drm_dp_i2c_do_msg(aux, &msg); + err = drm_dp_i2c_drain_msg(aux, &msg); if (err < 0) break; + transfer_size = err; } if (err < 0) break; diff --git a/include/drm/drm_dp_helper.h b/include/drm/drm_dp_helper.h index c5fdc2d3ca97..523f04c90dea 100644 --- a/include/drm/drm_dp_helper.h +++ b/include/drm/drm_dp_helper.h @@ -42,6 +42,8 @@ * 1.2 formally includes both eDP and DPI definitions. */ +#define DP_AUX_MAX_PAYLOAD_BYTES 16 + #define DP_AUX_I2C_WRITE 0x0 #define DP_AUX_I2C_READ 0x1 #define DP_AUX_I2C_STATUS 0x2 @@ -680,6 +682,9 @@ struct drm_dp_aux_msg { * transactions. The drm_dp_aux_register_i2c_bus() function registers an * I2C adapter that can be passed to drm_probe_ddc(). Upon removal, drivers * should call drm_dp_aux_unregister_i2c_bus() to remove the I2C adapter. + * The I2C adapter uses long transfers by default; if a partial response is + * received, the adapter will drop down to the size given by the partial + * response for this transaction only. * * Note that the aux helper code assumes that the .transfer() function * only modifies the reply field of the drm_dp_aux_msg structure. The -- cgit v1.2.3 From b7b5ee593118f9dc884fc21237f51b9f599cc432 Mon Sep 17 00:00:00 2001 From: Rob Clark Date: Wed, 11 Mar 2015 10:23:08 -0400 Subject: drm/fb: document drm_fb_helper_surface_size There has been some confusion about this struct. Lack of documentation probably didn't help. Signed-off-by: Rob Clark Reviewed-by: Alex Deucher Acked-by: Laurent Pinchart Signed-off-by: Daniel Vetter --- include/drm/drm_fb_helper.h | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) (limited to 'include') diff --git a/include/drm/drm_fb_helper.h b/include/drm/drm_fb_helper.h index 21b944c456f6..0dfd94def593 100644 --- a/include/drm/drm_fb_helper.h +++ b/include/drm/drm_fb_helper.h @@ -44,6 +44,25 @@ struct drm_fb_helper_crtc { int x, y; }; +/** + * struct drm_fb_helper_surface_size - describes fbdev size and scanout surface size + * @fb_width: fbdev width + * @fb_height: fbdev height + * @surface_width: scanout buffer width + * @surface_height: scanout buffer height + * @surface_bpp: scanout buffer bpp + * @surface_depth: scanout buffer depth + * + * Note that the scanout surface width/height may be larger than the fbdev + * width/height. In case of multiple displays, the scanout surface is sized + * according to the largest width/height (so it is large enough for all CRTCs + * to scanout). But the fbdev width/height is sized to the minimum width/ + * height of all the displays. This ensures that fbcon fits on the smallest + * of the attached displays. + * + * So what is passed to drm_fb_helper_fill_var() should be fb_width/fb_height, + * rather than the surface size. + */ struct drm_fb_helper_surface_size { u32 fb_width; u32 fb_height; -- cgit v1.2.3 From 08855fae10f088d83527bdf108199d3e35be503b Mon Sep 17 00:00:00 2001 From: Rob Clark Date: Wed, 11 Mar 2015 10:23:09 -0400 Subject: drm/atomic: minor kerneldoc typo fix Signed-off-by: Rob Clark Reviewed-by: Alex Deucher Acked-by: Laurent Pinchart Signed-off-by: Daniel Vetter --- include/drm/drm_crtc.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index adc9ea5acf02..7b5c661b37d8 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h @@ -915,7 +915,7 @@ struct drm_bridge { }; /** - * struct struct drm_atomic_state - the global state object for atomic updates + * struct drm_atomic_state - the global state object for atomic updates * @dev: parent DRM device * @allow_modeset: allow full modeset * @legacy_cursor_update: hint to enforce legacy cursor ioctl semantics -- cgit v1.2.3 From 33d6737761ea425d43f3b6e4561573a4020982f2 Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Tue, 10 Mar 2015 16:57:11 -0700 Subject: of: mdio: export of_mdio_parse_addr Export of_mdio_parse_addr() which allows parsing a given Ethernet PHY node MDIO address, verify it is within the allowed range, and return its value. This is going to be useful for the DSA code which needs to deal with multiple layers of MDIO buses. Signed-off-by: Florian Fainelli Acked-by: Rob Herring Signed-off-by: David S. Miller --- drivers/of/of_mdio.c | 3 ++- include/linux/of_mdio.h | 7 +++++++ 2 files changed, 9 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/drivers/of/of_mdio.c b/drivers/of/of_mdio.c index 1bd43053b8c7..0c064485d1c2 100644 --- a/drivers/of/of_mdio.c +++ b/drivers/of/of_mdio.c @@ -88,7 +88,7 @@ static int of_mdiobus_register_phy(struct mii_bus *mdio, struct device_node *chi return 0; } -static int of_mdio_parse_addr(struct device *dev, const struct device_node *np) +int of_mdio_parse_addr(struct device *dev, const struct device_node *np) { u32 addr; int ret; @@ -108,6 +108,7 @@ static int of_mdio_parse_addr(struct device *dev, const struct device_node *np) return addr; } +EXPORT_SYMBOL(of_mdio_parse_addr); /** * of_mdiobus_register - Register mii_bus and create PHYs from the device tree diff --git a/include/linux/of_mdio.h b/include/linux/of_mdio.h index d449018d0726..8f2237eb3485 100644 --- a/include/linux/of_mdio.h +++ b/include/linux/of_mdio.h @@ -24,6 +24,7 @@ struct phy_device *of_phy_attach(struct net_device *dev, phy_interface_t iface); extern struct mii_bus *of_mdio_find_bus(struct device_node *mdio_np); +extern int of_mdio_parse_addr(struct device *dev, const struct device_node *np); #else /* CONFIG_OF */ static inline int of_mdiobus_register(struct mii_bus *mdio, struct device_node *np) @@ -60,6 +61,12 @@ static inline struct mii_bus *of_mdio_find_bus(struct device_node *mdio_np) { return NULL; } + +static inline int of_mdio_parse_addr(struct device *dev, + const struct device_node *np) +{ + return -ENOSYS; +} #endif /* CONFIG_OF */ #if defined(CONFIG_OF) && defined(CONFIG_FIXED_PHY) -- cgit v1.2.3 From d09957fbb4d0b059b3176b510540df69048ad170 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Tue, 10 Mar 2015 17:48:37 +0100 Subject: mtd: cfi: reduce stack size The cfi_staa_write_buffers function uses a large amount of kernel stack whenever CONFIG_MTD_MAP_BANK_WIDTH_32 is set, and that results in a warning on ARM allmodconfig builds: drivers/mtd/chips/cfi_cmdset_0020.c: In function 'cfi_staa_write_buffers': drivers/mtd/chips/cfi_cmdset_0020.c:651:1: warning: the frame size of 1208 bytes is larger than 1024 bytes [-Wframe-larger-than=] It turns out that this is largely a result of a suboptimal implementation of map_word_andequal(). Replacing this function with a straightforward one reduces the stack size in this function by exactly 200 bytes, shrinks the .text segment for this file from 27648 bytes to 26608 bytes, and makes the warning go away. Signed-off-by: Arnd Bergmann Signed-off-by: Brian Norris --- include/linux/mtd/map.h | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/mtd/map.h b/include/linux/mtd/map.h index 5f487d776411..a872157a0700 100644 --- a/include/linux/mtd/map.h +++ b/include/linux/mtd/map.h @@ -314,7 +314,17 @@ static inline map_word map_word_or(struct map_info *map, map_word val1, map_word return r; } -#define map_word_andequal(m, a, b, z) map_word_equal(m, z, map_word_and(m, a, b)) +static inline int map_word_andequal(struct map_info *map, map_word val1, map_word val2, map_word val3) +{ + int i; + + for (i = 0; i < map_words(map); i++) { + if ((val1.x[i] & val2.x[i]) != val3.x[i]) + return 0; + } + + return 1; +} static inline int map_word_bitsset(struct map_info *map, map_word val1, map_word val2) { -- cgit v1.2.3 From 7234bea69de200e2060d099685c4c674b556edc0 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Tue, 10 Mar 2015 17:51:55 +0100 Subject: mtd: clean up whitespace in linux/mtd/map.h As the only comments I got for the "mtd: cfi: reduce stack size" patch were about whitespace changes, it appears necessary to fix up the rest of the file as well, which contains the exact same mistakes. Signed-off-by: Arnd Bergmann Signed-off-by: Brian Norris --- include/linux/mtd/map.h | 42 ++++++++++++++++++++++++------------------ 1 file changed, 24 insertions(+), 18 deletions(-) (limited to 'include') diff --git a/include/linux/mtd/map.h b/include/linux/mtd/map.h index a872157a0700..29975c73a953 100644 --- a/include/linux/mtd/map.h +++ b/include/linux/mtd/map.h @@ -77,7 +77,7 @@ /* ensure we never evaluate anything shorted than an unsigned long * to zero, and ensure we'll never miss the end of an comparison (bjd) */ -#define map_calc_words(map) ((map_bankwidth(map) + (sizeof(unsigned long)-1))/ sizeof(unsigned long)) +#define map_calc_words(map) ((map_bankwidth(map) + (sizeof(unsigned long)-1)) / sizeof(unsigned long)) #ifdef CONFIG_MTD_MAP_BANK_WIDTH_8 # ifdef map_bankwidth @@ -181,7 +181,7 @@ static inline int map_bankwidth_supported(int w) } } -#define MAX_MAP_LONGS ( ((MAX_MAP_BANKWIDTH*8) + BITS_PER_LONG - 1) / BITS_PER_LONG ) +#define MAX_MAP_LONGS (((MAX_MAP_BANKWIDTH * 8) + BITS_PER_LONG - 1) / BITS_PER_LONG) typedef union { unsigned long x[MAX_MAP_LONGS]; @@ -264,20 +264,22 @@ void unregister_mtd_chip_driver(struct mtd_chip_driver *); struct mtd_info *do_map_probe(const char *name, struct map_info *map); void map_destroy(struct mtd_info *mtd); -#define ENABLE_VPP(map) do { if(map->set_vpp) map->set_vpp(map, 1); } while(0) -#define DISABLE_VPP(map) do { if(map->set_vpp) map->set_vpp(map, 0); } while(0) +#define ENABLE_VPP(map) do { if (map->set_vpp) map->set_vpp(map, 1); } while (0) +#define DISABLE_VPP(map) do { if (map->set_vpp) map->set_vpp(map, 0); } while (0) #define INVALIDATE_CACHED_RANGE(map, from, size) \ - do { if(map->inval_cache) map->inval_cache(map, from, size); } while(0) + do { if (map->inval_cache) map->inval_cache(map, from, size); } while (0) static inline int map_word_equal(struct map_info *map, map_word val1, map_word val2) { int i; - for (i=0; ivirt + ofs); #endif else if (map_bankwidth_is_large(map)) - memcpy_fromio(r.x, map->virt+ofs, map->bankwidth); + memcpy_fromio(r.x, map->virt + ofs, map->bankwidth); else BUG(); -- cgit v1.2.3 From 33cf7c90fe2f97afb1cadaa0cfb782cb9d1b9ee2 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Wed, 11 Mar 2015 18:53:14 -0700 Subject: net: add real socket cookies A long standing problem in netlink socket dumps is the use of kernel socket addresses as cookies. 1) It is a security concern. 2) Sockets can be reused quite quickly, so there is no guarantee a cookie is used once and identify a flow. 3) request sock, establish sock, and timewait socks for a given flow have different cookies. Part of our effort to bring better TCP statistics requires to switch to a different allocator. In this patch, I chose to use a per network namespace 64bit generator, and to use it only in the case a socket needs to be dumped to netlink. (This might be refined later if needed) Note that I tried to carry cookies from request sock, to establish sock, then timewait sockets. Signed-off-by: Eric Dumazet Cc: Eric Salo Signed-off-by: David S. Miller --- include/linux/sock_diag.h | 4 ++-- include/net/inet_sock.h | 2 ++ include/net/inet_timewait_sock.h | 1 + include/net/net_namespace.h | 2 ++ include/net/sock.h | 3 +++ net/core/sock.c | 1 + net/core/sock_diag.c | 37 +++++++++++++++++++++++++++---------- net/dccp/ipv4.c | 2 ++ net/ipv4/inet_connection_sock.c | 2 ++ net/ipv4/inet_diag.c | 14 +++++++++----- net/ipv4/inet_timewait_sock.c | 1 + net/ipv4/syncookies.c | 1 + net/ipv4/tcp_input.c | 2 ++ 13 files changed, 55 insertions(+), 17 deletions(-) (limited to 'include') diff --git a/include/linux/sock_diag.h b/include/linux/sock_diag.h index b5ad7d35a636..083ac388098e 100644 --- a/include/linux/sock_diag.h +++ b/include/linux/sock_diag.h @@ -19,8 +19,8 @@ void sock_diag_unregister(const struct sock_diag_handler *h); void sock_diag_register_inet_compat(int (*fn)(struct sk_buff *skb, struct nlmsghdr *nlh)); void sock_diag_unregister_inet_compat(int (*fn)(struct sk_buff *skb, struct nlmsghdr *nlh)); -int sock_diag_check_cookie(void *sk, const __u32 *cookie); -void sock_diag_save_cookie(void *sk, __u32 *cookie); +int sock_diag_check_cookie(struct sock *sk, const __u32 *cookie); +void sock_diag_save_cookie(struct sock *sk, __u32 *cookie); int sock_diag_put_meminfo(struct sock *sk, struct sk_buff *skb, int attr); int sock_diag_put_filterinfo(bool may_report_filterinfo, struct sock *sk, diff --git a/include/net/inet_sock.h b/include/net/inet_sock.h index eb16c7beed1e..e565afdc14ad 100644 --- a/include/net/inet_sock.h +++ b/include/net/inet_sock.h @@ -77,6 +77,8 @@ struct inet_request_sock { #define ir_v6_rmt_addr req.__req_common.skc_v6_daddr #define ir_v6_loc_addr req.__req_common.skc_v6_rcv_saddr #define ir_iif req.__req_common.skc_bound_dev_if +#define ir_cookie req.__req_common.skc_cookie +#define ireq_net req.__req_common.skc_net kmemcheck_bitfield_begin(flags); u16 snd_wscale : 4, diff --git a/include/net/inet_timewait_sock.h b/include/net/inet_timewait_sock.h index 6c566034e26d..b7ce1003c429 100644 --- a/include/net/inet_timewait_sock.h +++ b/include/net/inet_timewait_sock.h @@ -122,6 +122,7 @@ struct inet_timewait_sock { #define tw_v6_rcv_saddr __tw_common.skc_v6_rcv_saddr #define tw_dport __tw_common.skc_dport #define tw_num __tw_common.skc_num +#define tw_cookie __tw_common.skc_cookie int tw_timeout; volatile unsigned char tw_substate; diff --git a/include/net/net_namespace.h b/include/net/net_namespace.h index 2cb9acb618e9..e086f4030dd2 100644 --- a/include/net/net_namespace.h +++ b/include/net/net_namespace.h @@ -56,6 +56,8 @@ struct net { #endif spinlock_t rules_mod_lock; + atomic64_t cookie_gen; + struct list_head list; /* list of network namespaces */ struct list_head cleanup_list; /* namespaces on death row */ struct list_head exit_list; /* Use only net_mutex */ diff --git a/include/net/sock.h b/include/net/sock.h index 250822cc1e02..d996c633bec2 100644 --- a/include/net/sock.h +++ b/include/net/sock.h @@ -199,6 +199,8 @@ struct sock_common { struct in6_addr skc_v6_rcv_saddr; #endif + atomic64_t skc_cookie; + /* * fields between dontcopy_begin/dontcopy_end * are not copied in sock_copy() @@ -329,6 +331,7 @@ struct sock { #define sk_net __sk_common.skc_net #define sk_v6_daddr __sk_common.skc_v6_daddr #define sk_v6_rcv_saddr __sk_common.skc_v6_rcv_saddr +#define sk_cookie __sk_common.skc_cookie socket_lock_t sk_lock; struct sk_buff_head sk_receive_queue; diff --git a/net/core/sock.c b/net/core/sock.c index 726e1f99aa8d..a9a9c2ff9260 100644 --- a/net/core/sock.c +++ b/net/core/sock.c @@ -1538,6 +1538,7 @@ struct sock *sk_clone_lock(const struct sock *sk, const gfp_t priority) newsk->sk_err = 0; newsk->sk_priority = 0; newsk->sk_incoming_cpu = raw_smp_processor_id(); + atomic64_set(&newsk->sk_cookie, 0); /* * Before updating sk_refcnt, we must commit prior changes to memory * (Documentation/RCU/rculist_nulls.txt for details) diff --git a/net/core/sock_diag.c b/net/core/sock_diag.c index 96e70ee05a8d..74dddf84adcd 100644 --- a/net/core/sock_diag.c +++ b/net/core/sock_diag.c @@ -13,22 +13,39 @@ static const struct sock_diag_handler *sock_diag_handlers[AF_MAX]; static int (*inet_rcv_compat)(struct sk_buff *skb, struct nlmsghdr *nlh); static DEFINE_MUTEX(sock_diag_table_mutex); -int sock_diag_check_cookie(void *sk, const __u32 *cookie) +static u64 sock_gen_cookie(struct sock *sk) { - if ((cookie[0] != INET_DIAG_NOCOOKIE || - cookie[1] != INET_DIAG_NOCOOKIE) && - ((u32)(unsigned long)sk != cookie[0] || - (u32)((((unsigned long)sk) >> 31) >> 1) != cookie[1])) - return -ESTALE; - else + while (1) { + u64 res = atomic64_read(&sk->sk_cookie); + + if (res) + return res; + res = atomic64_inc_return(&sock_net(sk)->cookie_gen); + atomic64_cmpxchg(&sk->sk_cookie, 0, res); + } +} + +int sock_diag_check_cookie(struct sock *sk, const __u32 *cookie) +{ + u64 res; + + if (cookie[0] == INET_DIAG_NOCOOKIE && cookie[1] == INET_DIAG_NOCOOKIE) return 0; + + res = sock_gen_cookie(sk); + if ((u32)res != cookie[0] || (u32)(res >> 32) != cookie[1]) + return -ESTALE; + + return 0; } EXPORT_SYMBOL_GPL(sock_diag_check_cookie); -void sock_diag_save_cookie(void *sk, __u32 *cookie) +void sock_diag_save_cookie(struct sock *sk, __u32 *cookie) { - cookie[0] = (u32)(unsigned long)sk; - cookie[1] = (u32)(((unsigned long)sk >> 31) >> 1); + u64 res = sock_gen_cookie(sk); + + cookie[0] = (u32)res; + cookie[1] = (u32)(res >> 32); } EXPORT_SYMBOL_GPL(sock_diag_save_cookie); diff --git a/net/dccp/ipv4.c b/net/dccp/ipv4.c index e45b968613a4..207281ae3536 100644 --- a/net/dccp/ipv4.c +++ b/net/dccp/ipv4.c @@ -641,6 +641,8 @@ int dccp_v4_conn_request(struct sock *sk, struct sk_buff *skb) ireq = inet_rsk(req); ireq->ir_loc_addr = ip_hdr(skb)->daddr; ireq->ir_rmt_addr = ip_hdr(skb)->saddr; + ireq->ireq_net = sock_net(sk); + atomic64_set(&ireq->ir_cookie, 0); /* * Step 3: Process LISTEN state diff --git a/net/ipv4/inet_connection_sock.c b/net/ipv4/inet_connection_sock.c index 14d02ea905b6..34581f928afa 100644 --- a/net/ipv4/inet_connection_sock.c +++ b/net/ipv4/inet_connection_sock.c @@ -678,6 +678,8 @@ struct sock *inet_csk_clone_lock(const struct sock *sk, newsk->sk_write_space = sk_stream_write_space; newsk->sk_mark = inet_rsk(req)->ir_mark; + atomic64_set(&newsk->sk_cookie, + atomic64_read(&inet_rsk(req)->ir_cookie)); newicsk->icsk_retransmits = 0; newicsk->icsk_backoff = 0; diff --git a/net/ipv4/inet_diag.c b/net/ipv4/inet_diag.c index ac3bfb458afd..29317ff4a007 100644 --- a/net/ipv4/inet_diag.c +++ b/net/ipv4/inet_diag.c @@ -221,12 +221,13 @@ static int inet_csk_diag_fill(struct sock *sk, user_ns, portid, seq, nlmsg_flags, unlh); } -static int inet_twsk_diag_fill(struct inet_timewait_sock *tw, +static int inet_twsk_diag_fill(struct sock *sk, struct sk_buff *skb, const struct inet_diag_req_v2 *req, u32 portid, u32 seq, u16 nlmsg_flags, const struct nlmsghdr *unlh) { + struct inet_timewait_sock *tw = inet_twsk(sk); struct inet_diag_msg *r; struct nlmsghdr *nlh; s32 tmo; @@ -247,7 +248,7 @@ static int inet_twsk_diag_fill(struct inet_timewait_sock *tw, r->idiag_retrans = 0; r->id.idiag_if = tw->tw_bound_dev_if; - sock_diag_save_cookie(tw, r->id.idiag_cookie); + sock_diag_save_cookie(sk, r->id.idiag_cookie); r->id.idiag_sport = tw->tw_sport; r->id.idiag_dport = tw->tw_dport; @@ -283,7 +284,7 @@ static int sk_diag_fill(struct sock *sk, struct sk_buff *skb, const struct nlmsghdr *unlh) { if (sk->sk_state == TCP_TIME_WAIT) - return inet_twsk_diag_fill(inet_twsk(sk), skb, r, portid, seq, + return inet_twsk_diag_fill(sk, skb, r, portid, seq, nlmsg_flags, unlh); return inet_csk_diag_fill(sk, skb, r, user_ns, portid, seq, @@ -675,7 +676,7 @@ static int inet_twsk_diag_dump(struct sock *sk, if (!inet_diag_bc_sk(bc, sk)) return 0; - return inet_twsk_diag_fill(inet_twsk(sk), skb, r, + return inet_twsk_diag_fill(sk, skb, r, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq, NLM_F_MULTI, cb->nlh); } @@ -734,7 +735,10 @@ static int inet_diag_fill_req(struct sk_buff *skb, struct sock *sk, r->idiag_retrans = req->num_retrans; r->id.idiag_if = sk->sk_bound_dev_if; - sock_diag_save_cookie(req, r->id.idiag_cookie); + + BUILD_BUG_ON(offsetof(struct inet_request_sock, ir_cookie) != + offsetof(struct sock, sk_cookie)); + sock_diag_save_cookie((struct sock *)ireq, r->id.idiag_cookie); tmo = req->expires - jiffies; if (tmo < 0) diff --git a/net/ipv4/inet_timewait_sock.c b/net/ipv4/inet_timewait_sock.c index 6d592f8555fb..2bd980526631 100644 --- a/net/ipv4/inet_timewait_sock.c +++ b/net/ipv4/inet_timewait_sock.c @@ -195,6 +195,7 @@ struct inet_timewait_sock *inet_twsk_alloc(const struct sock *sk, const int stat tw->tw_ipv6only = 0; tw->tw_transparent = inet->transparent; tw->tw_prot = sk->sk_prot_creator; + atomic64_set(&tw->tw_cookie, atomic64_read(&sk->sk_cookie)); twsk_net_set(tw, hold_net(sock_net(sk))); /* * Because we use RCU lookups, we should not set tw_refcnt diff --git a/net/ipv4/syncookies.c b/net/ipv4/syncookies.c index 45fe60c5238e..ece31b426013 100644 --- a/net/ipv4/syncookies.c +++ b/net/ipv4/syncookies.c @@ -346,6 +346,7 @@ struct sock *cookie_v4_check(struct sock *sk, struct sk_buff *skb) req->ts_recent = tcp_opt.saw_tstamp ? tcp_opt.rcv_tsval : 0; treq->snt_synack = tcp_opt.saw_tstamp ? tcp_opt.rcv_tsecr : 0; treq->listener = NULL; + ireq->ireq_net = sock_net(sk); /* We throwed the options of the initial SYN away, so we hope * the ACK carries the same options again (see RFC1122 4.2.3.8) diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index fb4cf8b8e121..d7045f5f6ebf 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -5965,6 +5965,8 @@ int tcp_conn_request(struct request_sock_ops *rsk_ops, tmp_opt.tstamp_ok = tmp_opt.saw_tstamp; tcp_openreq_init(req, &tmp_opt, skb, sk); + inet_rsk(req)->ireq_net = sock_net(sk); + atomic64_set(&inet_rsk(req)->ir_cookie, 0); af_ops->init_req(req, sk, skb); -- cgit v1.2.3 From d299ce149c1aa6a2e2c8db44974a6a1c07d7303b Mon Sep 17 00:00:00 2001 From: Simon Horman Date: Thu, 12 Mar 2015 11:00:10 +0900 Subject: vxlan: Correct path typo in comment Flags are used in the return path rather than the return patch. Fixes: af33c1adae1e ("vxlan: Eliminate dependency on UDP socket in transmit path") Signed-off-by: Simon Horman Signed-off-by: David S. Miller --- include/net/vxlan.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/net/vxlan.h b/include/net/vxlan.h index eabd3a038674..9564b779246f 100644 --- a/include/net/vxlan.h +++ b/include/net/vxlan.h @@ -130,7 +130,7 @@ struct vxlan_sock { #define VXLAN_F_GBP 0x800 #define VXLAN_F_REMCSUM_NOPARTIAL 0x1000 -/* Flags that are used in the receive patch. These flags must match in +/* Flags that are used in the receive path. These flags must match in * order for a socket to be shareable */ #define VXLAN_F_RCV_FLAGS (VXLAN_F_GBP | \ -- cgit v1.2.3 From c78ba6d64c78634a875d1e316676667cabfea256 Mon Sep 17 00:00:00 2001 From: Lubomir Rintel Date: Wed, 11 Mar 2015 15:39:21 +0100 Subject: ipv6: expose RFC4191 route preference via rtnetlink This makes it possible to retain the route preference when RAs are handled in userspace. Signed-off-by: Lubomir Rintel Reviewed-by: Jiri Pirko Signed-off-by: David S. Miller --- include/uapi/linux/rtnetlink.h | 1 + net/ipv6/route.c | 16 +++++++++++++++- 2 files changed, 16 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/uapi/linux/rtnetlink.h b/include/uapi/linux/rtnetlink.h index c3722b024e73..bea910f924dd 100644 --- a/include/uapi/linux/rtnetlink.h +++ b/include/uapi/linux/rtnetlink.h @@ -305,6 +305,7 @@ enum rtattr_type_t { RTA_MFC_STATS, RTA_VIA, RTA_NEWDST, + RTA_PREF, __RTA_MAX }; diff --git a/net/ipv6/route.c b/net/ipv6/route.c index 06fa819c43c9..58c0e6a4d15d 100644 --- a/net/ipv6/route.c +++ b/net/ipv6/route.c @@ -2398,6 +2398,7 @@ static const struct nla_policy rtm_ipv6_policy[RTA_MAX+1] = { [RTA_PRIORITY] = { .type = NLA_U32 }, [RTA_METRICS] = { .type = NLA_NESTED }, [RTA_MULTIPATH] = { .len = sizeof(struct rtnexthop) }, + [RTA_PREF] = { .type = NLA_U8 }, }; static int rtm_to_fib6_config(struct sk_buff *skb, struct nlmsghdr *nlh, @@ -2405,6 +2406,7 @@ static int rtm_to_fib6_config(struct sk_buff *skb, struct nlmsghdr *nlh, { struct rtmsg *rtm; struct nlattr *tb[RTA_MAX+1]; + unsigned int pref; int err; err = nlmsg_parse(nlh, sizeof(*rtm), tb, RTA_MAX, rtm_ipv6_policy); @@ -2480,6 +2482,14 @@ static int rtm_to_fib6_config(struct sk_buff *skb, struct nlmsghdr *nlh, cfg->fc_mp_len = nla_len(tb[RTA_MULTIPATH]); } + if (tb[RTA_PREF]) { + pref = nla_get_u8(tb[RTA_PREF]); + if (pref != ICMPV6_ROUTER_PREF_LOW && + pref != ICMPV6_ROUTER_PREF_HIGH) + pref = ICMPV6_ROUTER_PREF_MEDIUM; + cfg->fc_flags |= RTF_PREF(pref); + } + err = 0; errout: return err; @@ -2583,7 +2593,8 @@ static inline size_t rt6_nlmsg_size(void) + nla_total_size(4) /* RTA_PRIORITY */ + RTAX_MAX * nla_total_size(4) /* RTA_METRICS */ + nla_total_size(sizeof(struct rta_cacheinfo)) - + nla_total_size(TCP_CA_NAME_MAX); /* RTAX_CC_ALGO */ + + nla_total_size(TCP_CA_NAME_MAX) /* RTAX_CC_ALGO */ + + nla_total_size(1); /* RTA_PREF */ } static int rt6_fill_node(struct net *net, @@ -2724,6 +2735,9 @@ static int rt6_fill_node(struct net *net, if (rtnl_put_cacheinfo(skb, &rt->dst, 0, expires, rt->dst.error) < 0) goto nla_put_failure; + if (nla_put_u8(skb, RTA_PREF, IPV6_EXTRACT_PREF(rt->rt6i_flags))) + goto nla_put_failure; + nlmsg_end(skb, nlh); return 0; -- cgit v1.2.3 From 315491e5d6ee66838a18a8ca0c14e6ffb376e48c Mon Sep 17 00:00:00 2001 From: Suman Anna Date: Fri, 9 Jan 2015 15:21:58 -0600 Subject: remoteproc: add IOMMU hardware capability flag The remoteproc framework currently relies on iommu_present() on the bus the device is on, to perform MMU management. However, this logic doesn't scale for multi-arch, especially for processors that do not have an IOMMU. Replace this logic instead by using a h/w capability flag for the presence of IOMMU in the rproc structure. This issue is seen on OMAP platforms when trying to add a remoteproc driver for a small Cortex M3 called the WkupM3 used for suspend / resume management on TI AM335/AM437x SoCs. This processor does not have an MMU. Same is the case with another processor subsystem PRU-ICSS on AM335/AM437x. All these are platform devices, and the current iommu_present check will not scale for the same kernel image to support OMAP4/OMAP5 and AM335/AM437x. The existing platform implementation drivers - OMAP remoteproc, STE Modem remoteproc and DA8xx remoteproc, are updated as well to properly configure the newly added rproc field. Cc: Robert Tivy Cc: Linus Walleij Signed-off-by: Suman Anna [small change in the commit title and in a single comment] Signed-off-by: Ohad Ben-Cohen --- drivers/remoteproc/da8xx_remoteproc.c | 1 + drivers/remoteproc/omap_remoteproc.c | 2 ++ drivers/remoteproc/remoteproc_core.c | 15 ++------------- drivers/remoteproc/ste_modem_rproc.c | 1 + include/linux/remoteproc.h | 2 ++ 5 files changed, 8 insertions(+), 13 deletions(-) (limited to 'include') diff --git a/drivers/remoteproc/da8xx_remoteproc.c b/drivers/remoteproc/da8xx_remoteproc.c index 89fd057e5f1d..f8d6a0661c14 100644 --- a/drivers/remoteproc/da8xx_remoteproc.c +++ b/drivers/remoteproc/da8xx_remoteproc.c @@ -224,6 +224,7 @@ static int da8xx_rproc_probe(struct platform_device *pdev) drproc = rproc->priv; drproc->rproc = rproc; + rproc->has_iommu = false; platform_set_drvdata(pdev, rproc); diff --git a/drivers/remoteproc/omap_remoteproc.c b/drivers/remoteproc/omap_remoteproc.c index e85f30370760..b74368a91235 100644 --- a/drivers/remoteproc/omap_remoteproc.c +++ b/drivers/remoteproc/omap_remoteproc.c @@ -202,6 +202,8 @@ static int omap_rproc_probe(struct platform_device *pdev) oproc = rproc->priv; oproc->rproc = rproc; + /* All existing OMAP IPU and DSP processors have an MMU */ + rproc->has_iommu = true; platform_set_drvdata(pdev, rproc); diff --git a/drivers/remoteproc/remoteproc_core.c b/drivers/remoteproc/remoteproc_core.c index 3cd85a638afa..11cdb119e4f3 100644 --- a/drivers/remoteproc/remoteproc_core.c +++ b/drivers/remoteproc/remoteproc_core.c @@ -94,19 +94,8 @@ static int rproc_enable_iommu(struct rproc *rproc) struct device *dev = rproc->dev.parent; int ret; - /* - * We currently use iommu_present() to decide if an IOMMU - * setup is needed. - * - * This works for simple cases, but will easily fail with - * platforms that do have an IOMMU, but not for this specific - * rproc. - * - * This will be easily solved by introducing hw capabilities - * that will be set by the remoteproc driver. - */ - if (!iommu_present(dev->bus)) { - dev_dbg(dev, "iommu not found\n"); + if (!rproc->has_iommu) { + dev_dbg(dev, "iommu not present\n"); return 0; } diff --git a/drivers/remoteproc/ste_modem_rproc.c b/drivers/remoteproc/ste_modem_rproc.c index 16b7b7bd805b..dd193f35a1ff 100644 --- a/drivers/remoteproc/ste_modem_rproc.c +++ b/drivers/remoteproc/ste_modem_rproc.c @@ -289,6 +289,7 @@ static int sproc_probe(struct platform_device *pdev) sproc = rproc->priv; sproc->mdev = mdev; sproc->rproc = rproc; + rproc->has_iommu = false; mdev->drv_data = sproc; /* Provide callback functions to modem device */ diff --git a/include/linux/remoteproc.h b/include/linux/remoteproc.h index 9e7e745dac55..78b8a9b9d40a 100644 --- a/include/linux/remoteproc.h +++ b/include/linux/remoteproc.h @@ -404,6 +404,7 @@ enum rproc_crash_type { * @table_ptr: pointer to the resource table in effect * @cached_table: copy of the resource table * @table_csum: checksum of the resource table + * @has_iommu: flag to indicate if remote processor is behind an MMU */ struct rproc { struct klist_node node; @@ -435,6 +436,7 @@ struct rproc { struct resource_table *table_ptr; struct resource_table *cached_table; u32 table_csum; + bool has_iommu; }; /* we currently support only two vrings per rvdev */ -- cgit v1.2.3 From e19f742885e87ab9582fdf5940f214419eb9275b Mon Sep 17 00:00:00 2001 From: Chris Zhong Date: Sat, 28 Feb 2015 18:09:06 +0800 Subject: mfd: rk808: Disable the under voltage detect Rk808 has a under voltage detect function, when the voltage of buck is under 85% the target voltage, the buck output will reset. But if the power load is too heavy, this function maybe err, when current over 4.2A, although the voltage is normal, but RK808 mistakenly think it is under 85%, and reset the buck. So let's disable this function. Signed-off-by: Chris Zhong Signed-off-by: Lee Jones --- drivers/mfd/rk808.c | 1 + include/linux/mfd/rk808.h | 3 +++ 2 files changed, 4 insertions(+) (limited to 'include') diff --git a/drivers/mfd/rk808.c b/drivers/mfd/rk808.c index bd0215069875..7cf25c7c8d81 100644 --- a/drivers/mfd/rk808.c +++ b/drivers/mfd/rk808.c @@ -89,6 +89,7 @@ static const struct rk808_reg_data pre_init_reg[] = { { RK808_BOOST_CONFIG_REG, BOOST_ILMIN_MASK, BOOST_ILMIN_100MA }, { RK808_BUCK1_CONFIG_REG, BUCK1_RATE_MASK, BUCK_ILMIN_200MA }, { RK808_BUCK2_CONFIG_REG, BUCK2_RATE_MASK, BUCK_ILMIN_200MA }, + { RK808_DCDC_UV_ACT_REG, BUCK_UV_ACT_MASK, BUCK_UV_ACT_DISABLE}, { RK808_VB_MON_REG, MASK_ALL, VB_LO_ACT | VB_LO_SEL_3500MV }, }; diff --git a/include/linux/mfd/rk808.h b/include/linux/mfd/rk808.h index fb09312d854b..441b6ee72691 100644 --- a/include/linux/mfd/rk808.h +++ b/include/linux/mfd/rk808.h @@ -156,6 +156,9 @@ enum rk808_reg { #define BUCK2_RATE_MASK (3 << 3) #define MASK_ALL 0xff +#define BUCK_UV_ACT_MASK 0x0f +#define BUCK_UV_ACT_DISABLE 0 + #define SWITCH2_EN BIT(6) #define SWITCH1_EN BIT(5) #define DEV_OFF_RST BIT(3) -- cgit v1.2.3 From 3be62b957d2c02cf744d3845091dd8a5fdca2039 Mon Sep 17 00:00:00 2001 From: Jacek Anaszewski Date: Wed, 4 Mar 2015 17:14:29 +0100 Subject: mfd: max77693: Adjust FLASH_EN_SHIFT and TORCH_EN_SHIFT macros Modify FLASH_EN_SHIFT and TORCH_EN_SHIFT macros to work properly when passed enum max77693_fled values (0 for FLED1 and 1 for FLED2) from leds-max77693 driver. Previous definitions were compatible with one of the previous RFC versions of leds-max77693.c driver, which was not merged. Signed-off-by: Jacek Anaszewski Acked-by: Kyungmin Park Signed-off-by: Lee Jones --- include/linux/mfd/max77693-private.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/linux/mfd/max77693-private.h b/include/linux/mfd/max77693-private.h index 955dd990beaf..5acd7104ad34 100644 --- a/include/linux/mfd/max77693-private.h +++ b/include/linux/mfd/max77693-private.h @@ -113,8 +113,8 @@ enum max77693_pmic_reg { #define FLASH_EN_FLASH 0x1 #define FLASH_EN_TORCH 0x2 #define FLASH_EN_ON 0x3 -#define FLASH_EN_SHIFT(x) (6 - ((x) - 1) * 2) -#define TORCH_EN_SHIFT(x) (2 - ((x) - 1) * 2) +#define FLASH_EN_SHIFT(x) (6 - (x) * 2) +#define TORCH_EN_SHIFT(x) (2 - (x) * 2) /* MAX77693 MAX_FLASH1 register */ #define MAX_FLASH1_MAX_FL_EN 0x80 -- cgit v1.2.3 From a307de2aab5bcb96bae3d778b01ff815f027ad88 Mon Sep 17 00:00:00 2001 From: Jacek Anaszewski Date: Wed, 4 Mar 2015 17:14:27 +0100 Subject: mfd: max77693: Remove struct max77693_led_platform_data The flash part of the max77693 device will depend only on OF, and thus will not use board files. Since there are no other users of the struct max77693_led_platform_data its existence is unjustified. Signed-off-by: Jacek Anaszewski Acked-by: Kyungmin Park Signed-off-by: Lee Jones --- include/linux/mfd/max77693.h | 13 ------------- 1 file changed, 13 deletions(-) (limited to 'include') diff --git a/include/linux/mfd/max77693.h b/include/linux/mfd/max77693.h index f0b6585cd874..ce894b6e6315 100644 --- a/include/linux/mfd/max77693.h +++ b/include/linux/mfd/max77693.h @@ -87,19 +87,6 @@ enum max77693_led_boost_mode { MAX77693_LED_BOOST_FIXED, }; -struct max77693_led_platform_data { - u32 fleds[2]; - u32 iout_torch[2]; - u32 iout_flash[2]; - u32 trigger[2]; - u32 trigger_type[2]; - u32 num_leds; - u32 boost_mode; - u32 flash_timeout; - u32 boost_vout; - u32 low_vsys; -}; - /* MAX77693 */ struct max77693_platform_data { -- cgit v1.2.3 From 419d55bbb80370ed1e8a36d7884cfcf977e73e29 Mon Sep 17 00:00:00 2001 From: Jacek Anaszewski Date: Wed, 4 Mar 2015 17:14:28 +0100 Subject: mfd: max77693: Add TORCH_IOUT_MASK macro Add a macro for obtaining the mask of ITORCH register bit fields related either to FLED1 or FLED2 current output. The expected arguments are TORCH_IOUT1_SHIFT or TORCH_IOUT2_SHIFT. Signed-off-by: Jacek Anaszewski Acked-by: Kyungmin Park Signed-off-by: Lee Jones --- include/linux/mfd/max77693-private.h | 1 + 1 file changed, 1 insertion(+) (limited to 'include') diff --git a/include/linux/mfd/max77693-private.h b/include/linux/mfd/max77693-private.h index 5acd7104ad34..51633ea6f910 100644 --- a/include/linux/mfd/max77693-private.h +++ b/include/linux/mfd/max77693-private.h @@ -87,6 +87,7 @@ enum max77693_pmic_reg { /* MAX77693 ITORCH register */ #define TORCH_IOUT1_SHIFT 0 #define TORCH_IOUT2_SHIFT 4 +#define TORCH_IOUT_MASK(x) (0xf << (x)) #define TORCH_IOUT_MIN 15625 #define TORCH_IOUT_MAX 250000 #define TORCH_IOUT_STEP 15625 -- cgit v1.2.3 From 2698dc22292e3e5fc2b24b2748018dcc09d70989 Mon Sep 17 00:00:00 2001 From: Gyungoh Yoo Date: Fri, 27 Feb 2015 15:42:21 +0900 Subject: mfd: Add support for Skyworks SKY81452 driver Signed-off-by: Gyungoh Yoo Signed-off-by: Lee Jones --- drivers/mfd/Kconfig | 12 +++++ drivers/mfd/Makefile | 1 + drivers/mfd/sky81452.c | 108 +++++++++++++++++++++++++++++++++++++++++++ include/linux/mfd/sky81452.h | 31 +++++++++++++ 4 files changed, 152 insertions(+) create mode 100644 drivers/mfd/sky81452.c create mode 100644 include/linux/mfd/sky81452.h (limited to 'include') diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index b277f37ae3be..5e68fdef64e7 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -791,6 +791,18 @@ config MFD_SM501_GPIO lines on the SM501. The platform data is used to supply the base number for the first GPIO line to register. +config MFD_SKY81452 + tristate "Skyworks Solutions SKY81452" + select MFD_CORE + select REGMAP_I2C + depends on I2C + help + This is the core driver for the Skyworks SKY81452 backlight and + voltage regulator device. + + This driver can also be built as a module. If so, the module + will be called sky81452. + config MFD_SMSC bool "SMSC ECE1099 series chips" depends on I2C=y diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index 5ebe443b595a..0e5cfeba107c 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -180,6 +180,7 @@ obj-$(CONFIG_MFD_MENF21BMC) += menf21bmc.o obj-$(CONFIG_MFD_HI6421_PMIC) += hi6421-pmic-core.o obj-$(CONFIG_MFD_DLN2) += dln2.o obj-$(CONFIG_MFD_RT5033) += rt5033.o +obj-$(CONFIG_MFD_SKY81452) += sky81452.o intel-soc-pmic-objs := intel_soc_pmic_core.o intel_soc_pmic_crc.o obj-$(CONFIG_INTEL_SOC_PMIC) += intel-soc-pmic.o diff --git a/drivers/mfd/sky81452.c b/drivers/mfd/sky81452.c new file mode 100644 index 000000000000..b0c9b0415650 --- /dev/null +++ b/drivers/mfd/sky81452.c @@ -0,0 +1,108 @@ +/* + * sky81452.c SKY81452 MFD driver + * + * Copyright 2014 Skyworks Solutions Inc. + * Author : Gyungoh Yoo + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static const struct regmap_config sky81452_config = { + .reg_bits = 8, + .val_bits = 8, +}; + +static int sky81452_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct device *dev = &client->dev; + const struct sky81452_platform_data *pdata = dev_get_platdata(dev); + struct mfd_cell cells[2]; + struct regmap *regmap; + int ret; + + if (!pdata) { + pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) + return -ENOMEM; + } + + regmap = devm_regmap_init_i2c(client, &sky81452_config); + if (IS_ERR(regmap)) { + dev_err(dev, "failed to initialize.err=%ld\n", PTR_ERR(regmap)); + return PTR_ERR(regmap); + } + + i2c_set_clientdata(client, regmap); + + memset(cells, 0, sizeof(cells)); + cells[0].name = "sky81452-backlight"; + cells[0].of_compatible = "skyworks,sky81452-backlight"; + cells[0].platform_data = pdata->bl_pdata; + cells[0].pdata_size = sizeof(*pdata->bl_pdata); + cells[1].name = "sky81452-regulator"; + cells[1].platform_data = pdata->regulator_init_data; + cells[1].pdata_size = sizeof(*pdata->regulator_init_data); + + ret = mfd_add_devices(dev, -1, cells, ARRAY_SIZE(cells), NULL, 0, NULL); + if (ret) + dev_err(dev, "failed to add child devices. err=%d\n", ret); + + return ret; +} + +static int sky81452_remove(struct i2c_client *client) +{ + mfd_remove_devices(&client->dev); + return 0; +} + +static const struct i2c_device_id sky81452_ids[] = { + { "sky81452" }, + { } +}; +MODULE_DEVICE_TABLE(i2c, sky81452_ids); + +#ifdef CONFIG_OF +static const struct of_device_id sky81452_of_match[] = { + { .compatible = "skyworks,sky81452", }, + { } +}; +MODULE_DEVICE_TABLE(of, sky81452_of_match); +#endif + +static struct i2c_driver sky81452_driver = { + .driver = { + .name = "sky81452", + .of_match_table = of_match_ptr(sky81452_of_match), + }, + .probe = sky81452_probe, + .remove = sky81452_remove, + .id_table = sky81452_ids, +}; + +module_i2c_driver(sky81452_driver); + +MODULE_DESCRIPTION("Skyworks SKY81452 MFD driver"); +MODULE_AUTHOR("Gyungoh Yoo "); +MODULE_LICENSE("GPL v2"); diff --git a/include/linux/mfd/sky81452.h b/include/linux/mfd/sky81452.h new file mode 100644 index 000000000000..b0925fa3e9ef --- /dev/null +++ b/include/linux/mfd/sky81452.h @@ -0,0 +1,31 @@ +/* + * sky81452.h SKY81452 MFD driver + * + * Copyright 2014 Skyworks Solutions Inc. + * Author : Gyungoh Yoo + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, see . + */ + +#ifndef _SKY81452_H +#define _SKY81452_H + +#include +#include + +struct sky81452_platform_data { + struct sky81452_bl_platform_data *bl_pdata; + struct regulator_init_data *regulator_init_data; +}; + +#endif -- cgit v1.2.3 From f705806c9f355fc63911dea72a65d8eeff0c2586 Mon Sep 17 00:00:00 2001 From: Gyungoh Yoo Date: Fri, 27 Feb 2015 15:42:22 +0900 Subject: backlight: Add support Skyworks SKY81452 backlight driver Signed-off-by: Gyungoh Yoo Acked-by: Jingoo Han Acked-by: Bryan Wu Signed-off-by: Lee Jones --- drivers/video/backlight/Kconfig | 10 + drivers/video/backlight/Makefile | 1 + drivers/video/backlight/sky81452-backlight.c | 353 +++++++++++++++++++++++ include/linux/platform_data/sky81452-backlight.h | 46 +++ 4 files changed, 410 insertions(+) create mode 100644 drivers/video/backlight/sky81452-backlight.c create mode 100644 include/linux/platform_data/sky81452-backlight.h (limited to 'include') diff --git a/drivers/video/backlight/Kconfig b/drivers/video/backlight/Kconfig index efb09046a8cf..2d9923a60076 100644 --- a/drivers/video/backlight/Kconfig +++ b/drivers/video/backlight/Kconfig @@ -408,6 +408,16 @@ config BACKLIGHT_PANDORA If you have a Pandora console, say Y to enable the backlight driver. +config BACKLIGHT_SKY81452 + tristate "Backlight driver for SKY81452" + depends on BACKLIGHT_CLASS_DEVICE && MFD_SKY81452 + help + If you have a Skyworks SKY81452, say Y to enable the + backlight driver. + + To compile this driver as a module, choose M here: the module will + be called sky81452-backlight + config BACKLIGHT_TPS65217 tristate "TPS65217 Backlight" depends on BACKLIGHT_CLASS_DEVICE && MFD_TPS65217 diff --git a/drivers/video/backlight/Makefile b/drivers/video/backlight/Makefile index fcd50b732165..d67073f9d421 100644 --- a/drivers/video/backlight/Makefile +++ b/drivers/video/backlight/Makefile @@ -50,6 +50,7 @@ obj-$(CONFIG_BACKLIGHT_PANDORA) += pandora_bl.o obj-$(CONFIG_BACKLIGHT_PCF50633) += pcf50633-backlight.o obj-$(CONFIG_BACKLIGHT_PWM) += pwm_bl.o obj-$(CONFIG_BACKLIGHT_SAHARA) += kb3886_bl.o +obj-$(CONFIG_BACKLIGHT_SKY81452) += sky81452-backlight.o obj-$(CONFIG_BACKLIGHT_TOSA) += tosa_bl.o obj-$(CONFIG_BACKLIGHT_TPS65217) += tps65217_bl.o obj-$(CONFIG_BACKLIGHT_WM831X) += wm831x_bl.o diff --git a/drivers/video/backlight/sky81452-backlight.c b/drivers/video/backlight/sky81452-backlight.c new file mode 100644 index 000000000000..052fa1bac03d --- /dev/null +++ b/drivers/video/backlight/sky81452-backlight.c @@ -0,0 +1,353 @@ +/* + * sky81452-backlight.c SKY81452 backlight driver + * + * Copyright 2014 Skyworks Solutions Inc. + * Author : Gyungoh Yoo + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* registers */ +#define SKY81452_REG0 0x00 +#define SKY81452_REG1 0x01 +#define SKY81452_REG2 0x02 +#define SKY81452_REG4 0x04 +#define SKY81452_REG5 0x05 + +/* bit mask */ +#define SKY81452_CS 0xFF +#define SKY81452_EN 0x3F +#define SKY81452_IGPW 0x20 +#define SKY81452_PWMMD 0x10 +#define SKY81452_PHASE 0x08 +#define SKY81452_ILIM 0x04 +#define SKY81452_VSHRT 0x03 +#define SKY81452_OCP 0x80 +#define SKY81452_OTMP 0x40 +#define SKY81452_SHRT 0x3F +#define SKY81452_OPN 0x3F + +#define SKY81452_DEFAULT_NAME "lcd-backlight" +#define SKY81452_MAX_BRIGHTNESS (SKY81452_CS + 1) + +#define CTZ(b) __builtin_ctz(b) + +static int sky81452_bl_update_status(struct backlight_device *bd) +{ + const struct sky81452_bl_platform_data *pdata = + dev_get_platdata(bd->dev.parent); + const unsigned int brightness = (unsigned int)bd->props.brightness; + struct regmap *regmap = bl_get_data(bd); + int ret; + + if (brightness > 0) { + ret = regmap_write(regmap, SKY81452_REG0, brightness - 1); + if (IS_ERR_VALUE(ret)) + return ret; + + return regmap_update_bits(regmap, SKY81452_REG1, SKY81452_EN, + pdata->enable << CTZ(SKY81452_EN)); + } + + return regmap_update_bits(regmap, SKY81452_REG1, SKY81452_EN, 0); +} + +static const struct backlight_ops sky81452_bl_ops = { + .update_status = sky81452_bl_update_status, +}; + +static ssize_t sky81452_bl_store_enable(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct regmap *regmap = bl_get_data(to_backlight_device(dev)); + unsigned long value; + int ret; + + ret = kstrtoul(buf, 16, &value); + if (IS_ERR_VALUE(ret)) + return ret; + + ret = regmap_update_bits(regmap, SKY81452_REG1, SKY81452_EN, + value << CTZ(SKY81452_EN)); + if (IS_ERR_VALUE(ret)) + return ret; + + return count; +} + +static ssize_t sky81452_bl_show_open_short(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct regmap *regmap = bl_get_data(to_backlight_device(dev)); + unsigned int reg, value = 0; + char tmp[3]; + int i, ret; + + reg = !strcmp(attr->attr.name, "open") ? SKY81452_REG5 : SKY81452_REG4; + ret = regmap_read(regmap, reg, &value); + if (IS_ERR_VALUE(ret)) + return ret; + + if (value & SKY81452_SHRT) { + *buf = 0; + for (i = 0; i < 6; i++) { + if (value & 0x01) { + sprintf(tmp, "%d ", i + 1); + strcat(buf, tmp); + } + value >>= 1; + } + strcat(buf, "\n"); + } else { + strcpy(buf, "none\n"); + } + + return strlen(buf); +} + +static ssize_t sky81452_bl_show_fault(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct regmap *regmap = bl_get_data(to_backlight_device(dev)); + unsigned int value = 0; + int ret; + + ret = regmap_read(regmap, SKY81452_REG4, &value); + if (IS_ERR_VALUE(ret)) + return ret; + + *buf = 0; + + if (value & SKY81452_OCP) + strcat(buf, "over-current "); + + if (value & SKY81452_OTMP) + strcat(buf, "over-temperature"); + + strcat(buf, "\n"); + return strlen(buf); +} + +static DEVICE_ATTR(enable, S_IWGRP | S_IWUSR, NULL, sky81452_bl_store_enable); +static DEVICE_ATTR(open, S_IRUGO, sky81452_bl_show_open_short, NULL); +static DEVICE_ATTR(short, S_IRUGO, sky81452_bl_show_open_short, NULL); +static DEVICE_ATTR(fault, S_IRUGO, sky81452_bl_show_fault, NULL); + +static struct attribute *sky81452_bl_attribute[] = { + &dev_attr_enable.attr, + &dev_attr_open.attr, + &dev_attr_short.attr, + &dev_attr_fault.attr, + NULL +}; + +static const struct attribute_group sky81452_bl_attr_group = { + .attrs = sky81452_bl_attribute, +}; + +#ifdef CONFIG_OF +static struct sky81452_bl_platform_data *sky81452_bl_parse_dt( + struct device *dev) +{ + struct device_node *np = of_node_get(dev->of_node); + struct sky81452_bl_platform_data *pdata; + int num_entry; + unsigned int sources[6]; + int ret; + + if (!np) { + dev_err(dev, "backlight node not found.\n"); + return ERR_PTR(-ENODATA); + } + + pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) { + of_node_put(np); + return ERR_PTR(-ENOMEM); + } + + of_property_read_string(np, "name", &pdata->name); + pdata->ignore_pwm = of_property_read_bool(np, "skyworks,ignore-pwm"); + pdata->dpwm_mode = of_property_read_bool(np, "skyworks,dpwm-mode"); + pdata->phase_shift = of_property_read_bool(np, "skyworks,phase-shift"); + pdata->gpio_enable = of_get_gpio(np, 0); + + ret = of_property_count_u32_elems(np, "led-sources"); + if (IS_ERR_VALUE(ret)) { + pdata->enable = SKY81452_EN >> CTZ(SKY81452_EN); + } else { + num_entry = ret; + if (num_entry > 6) + num_entry = 6; + + ret = of_property_read_u32_array(np, "led-sources", sources, + num_entry); + if (IS_ERR_VALUE(ret)) { + dev_err(dev, "led-sources node is invalid.\n"); + return ERR_PTR(-EINVAL); + } + + pdata->enable = 0; + while (--num_entry) + pdata->enable |= (1 << sources[num_entry]); + } + + ret = of_property_read_u32(np, + "skyworks,short-detection-threshold-volt", + &pdata->short_detection_threshold); + if (IS_ERR_VALUE(ret)) + pdata->short_detection_threshold = 7; + + ret = of_property_read_u32(np, "skyworks,current-limit-mA", + &pdata->boost_current_limit); + if (IS_ERR_VALUE(ret)) + pdata->boost_current_limit = 2750; + + of_node_put(np); + return pdata; +} +#else +static struct sky81452_bl_platform_data *sky81452_bl_parse_dt( + struct device *dev) +{ + return ERR_PTR(-EINVAL); +} +#endif + +static int sky81452_bl_init_device(struct regmap *regmap, + struct sky81452_bl_platform_data *pdata) +{ + unsigned int value; + + value = pdata->ignore_pwm ? SKY81452_IGPW : 0; + value |= pdata->dpwm_mode ? SKY81452_PWMMD : 0; + value |= pdata->phase_shift ? 0 : SKY81452_PHASE; + + if (pdata->boost_current_limit == 2300) + value |= SKY81452_ILIM; + else if (pdata->boost_current_limit != 2750) + return -EINVAL; + + if (pdata->short_detection_threshold < 4 || + pdata->short_detection_threshold > 7) + return -EINVAL; + value |= (7 - pdata->short_detection_threshold) << CTZ(SKY81452_VSHRT); + + return regmap_write(regmap, SKY81452_REG2, value); +} + +static int sky81452_bl_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct regmap *regmap = dev_get_drvdata(dev->parent); + struct sky81452_bl_platform_data *pdata = dev_get_platdata(dev); + struct backlight_device *bd; + struct backlight_properties props; + const char *name; + int ret; + + if (!pdata) { + pdata = sky81452_bl_parse_dt(dev); + if (IS_ERR(pdata)) + return PTR_ERR(pdata); + } + + if (gpio_is_valid(pdata->gpio_enable)) { + ret = devm_gpio_request_one(dev, pdata->gpio_enable, + GPIOF_OUT_INIT_HIGH, "sky81452-en"); + if (IS_ERR_VALUE(ret)) { + dev_err(dev, "failed to request GPIO. err=%d\n", ret); + return ret; + } + } + + ret = sky81452_bl_init_device(regmap, pdata); + if (IS_ERR_VALUE(ret)) { + dev_err(dev, "failed to initialize. err=%d\n", ret); + return ret; + } + + memset(&props, 0, sizeof(props)); + props.max_brightness = SKY81452_MAX_BRIGHTNESS, + name = pdata->name ? pdata->name : SKY81452_DEFAULT_NAME; + bd = devm_backlight_device_register(dev, name, dev, regmap, + &sky81452_bl_ops, &props); + if (IS_ERR(bd)) { + dev_err(dev, "failed to register. err=%ld\n", PTR_ERR(bd)); + return PTR_ERR(bd); + } + + platform_set_drvdata(pdev, bd); + + ret = sysfs_create_group(&bd->dev.kobj, &sky81452_bl_attr_group); + if (IS_ERR_VALUE(ret)) { + dev_err(dev, "failed to create attribute. err=%d\n", ret); + return ret; + } + + return ret; +} + +static int sky81452_bl_remove(struct platform_device *pdev) +{ + const struct sky81452_bl_platform_data *pdata = + dev_get_platdata(&pdev->dev); + struct backlight_device *bd = platform_get_drvdata(pdev); + + sysfs_remove_group(&bd->dev.kobj, &sky81452_bl_attr_group); + + bd->props.power = FB_BLANK_UNBLANK; + bd->props.brightness = 0; + backlight_update_status(bd); + + if (gpio_is_valid(pdata->gpio_enable)) + gpio_set_value_cansleep(pdata->gpio_enable, 0); + + return 0; +} + +#ifdef CONFIG_OF +static const struct of_device_id sky81452_bl_of_match[] = { + { .compatible = "skyworks,sky81452-backlight", }, + { } +}; +MODULE_DEVICE_TABLE(of, sky81452_bl_of_match); +#endif + +static struct platform_driver sky81452_bl_driver = { + .driver = { + .name = "sky81452-backlight", + .of_match_table = of_match_ptr(sky81452_bl_of_match), + }, + .probe = sky81452_bl_probe, + .remove = sky81452_bl_remove, +}; + +module_platform_driver(sky81452_bl_driver); + +MODULE_DESCRIPTION("Skyworks SKY81452 backlight driver"); +MODULE_AUTHOR("Gyungoh Yoo "); +MODULE_LICENSE("GPL v2"); diff --git a/include/linux/platform_data/sky81452-backlight.h b/include/linux/platform_data/sky81452-backlight.h new file mode 100644 index 000000000000..1231e9bb00f1 --- /dev/null +++ b/include/linux/platform_data/sky81452-backlight.h @@ -0,0 +1,46 @@ +/* + * sky81452.h SKY81452 backlight driver + * + * Copyright 2014 Skyworks Solutions Inc. + * Author : Gyungoh Yoo + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, see . + */ + +#ifndef _SKY81452_BACKLIGHT_H +#define _SKY81452_BACKLIGHT_H + +/** + * struct sky81452_platform_data + * @name: backlight driver name. + If it is not defined, default name is lcd-backlight. + * @gpio_enable:GPIO number which control EN pin + * @enable: Enable mask for current sink channel 1, 2, 3, 4, 5 and 6. + * @ignore_pwm: true if DPWMI should be ignored. + * @dpwm_mode: true is DPWM dimming mode, otherwise Analog dimming mode. + * @phase_shift:true is phase shift mode. + * @short_detecion_threshold: It should be one of 4, 5, 6 and 7V. + * @boost_current_limit: It should be one of 2300, 2750mA. + */ +struct sky81452_bl_platform_data { + const char *name; + int gpio_enable; + unsigned int enable; + bool ignore_pwm; + bool dpwm_mode; + bool phase_shift; + unsigned int short_detection_threshold; + unsigned int boost_current_limit; +}; + +#endif -- cgit v1.2.3 From efd7ef1c1929d7a0329d4349252863c04d6f1729 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Wed, 11 Mar 2015 23:04:08 -0500 Subject: net: Kill hold_net release_net hold_net and release_net were an idea that turned out to be useless. The code has been disabled since 2008. Kill the code it is long past due. Signed-off-by: "Eric W. Biederman" Acked-by: Eric Dumazet Signed-off-by: David S. Miller --- include/linux/netdevice.h | 3 +-- include/net/fib_rules.h | 9 +-------- include/net/net_namespace.h | 29 ----------------------------- include/net/sock.h | 2 +- net/core/dev.c | 2 -- net/core/fib_rules.c | 17 +++-------------- net/core/neighbour.c | 9 ++------- net/core/net_namespace.c | 11 ----------- net/core/sock.c | 1 - net/ipv4/fib_semantics.c | 3 +-- net/ipv4/inet_hashtables.c | 3 +-- net/ipv4/inet_timewait_sock.c | 3 +-- net/ipv6/addrlabel.c | 5 +---- net/ipv6/ip6_flowlabel.c | 3 +-- net/openvswitch/datapath.c | 4 +--- 15 files changed, 14 insertions(+), 90 deletions(-) (limited to 'include') diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 1354ae83efc8..cede40d9cac9 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -1864,8 +1864,7 @@ static inline void dev_net_set(struct net_device *dev, struct net *net) { #ifdef CONFIG_NET_NS - release_net(dev->nd_net); - dev->nd_net = hold_net(net); + dev->nd_net = net; #endif } diff --git a/include/net/fib_rules.h b/include/net/fib_rules.h index 88d2ae526961..6d67383a5114 100644 --- a/include/net/fib_rules.h +++ b/include/net/fib_rules.h @@ -95,17 +95,10 @@ static inline void fib_rule_get(struct fib_rule *rule) atomic_inc(&rule->refcnt); } -static inline void fib_rule_put_rcu(struct rcu_head *head) -{ - struct fib_rule *rule = container_of(head, struct fib_rule, rcu); - release_net(rule->fr_net); - kfree(rule); -} - static inline void fib_rule_put(struct fib_rule *rule) { if (atomic_dec_and_test(&rule->refcnt)) - call_rcu(&rule->rcu, fib_rule_put_rcu); + kfree_rcu(rule, rcu); } static inline u32 frh_get_table(struct fib_rule_hdr *frh, struct nlattr **nla) diff --git a/include/net/net_namespace.h b/include/net/net_namespace.h index e086f4030dd2..fab51ceeabf3 100644 --- a/include/net/net_namespace.h +++ b/include/net/net_namespace.h @@ -49,11 +49,6 @@ struct net { atomic_t count; /* To decided when the network * namespace should be shut down. */ -#ifdef NETNS_REFCNT_DEBUG - atomic_t use_count; /* To track references we - * destroy on demand - */ -#endif spinlock_t rules_mod_lock; atomic64_t cookie_gen; @@ -236,30 +231,6 @@ int net_eq(const struct net *net1, const struct net *net2) #endif -#ifdef NETNS_REFCNT_DEBUG -static inline struct net *hold_net(struct net *net) -{ - if (net) - atomic_inc(&net->use_count); - return net; -} - -static inline void release_net(struct net *net) -{ - if (net) - atomic_dec(&net->use_count); -} -#else -static inline struct net *hold_net(struct net *net) -{ - return net; -} - -static inline void release_net(struct net *net) -{ -} -#endif - #ifdef CONFIG_NET_NS static inline void write_pnet(struct net **pnet, struct net *net) diff --git a/include/net/sock.h b/include/net/sock.h index d996c633bec2..95b2c1c220f9 100644 --- a/include/net/sock.h +++ b/include/net/sock.h @@ -2204,7 +2204,7 @@ static inline void sk_change_net(struct sock *sk, struct net *net) if (!net_eq(current_net, net)) { put_net(current_net); - sock_net_set(sk, hold_net(net)); + sock_net_set(sk, net); } } diff --git a/net/core/dev.c b/net/core/dev.c index 962ee9d71964..39fe369b46ad 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -6841,8 +6841,6 @@ void free_netdev(struct net_device *dev) { struct napi_struct *p, *n; - release_net(dev_net(dev)); - netif_free_tx_queues(dev); #ifdef CONFIG_SYSFS kvfree(dev->_rx); diff --git a/net/core/fib_rules.c b/net/core/fib_rules.c index b55677fed1c8..68ea6950cad1 100644 --- a/net/core/fib_rules.c +++ b/net/core/fib_rules.c @@ -31,7 +31,7 @@ int fib_default_rule_add(struct fib_rules_ops *ops, r->pref = pref; r->table = table; r->flags = flags; - r->fr_net = hold_net(ops->fro_net); + r->fr_net = ops->fro_net; r->suppress_prefixlen = -1; r->suppress_ifgroup = -1; @@ -116,7 +116,6 @@ static int __fib_rules_register(struct fib_rules_ops *ops) if (ops->family == o->family) goto errout; - hold_net(net); list_add_tail_rcu(&ops->list, &net->rules_ops); err = 0; errout: @@ -160,15 +159,6 @@ static void fib_rules_cleanup_ops(struct fib_rules_ops *ops) } } -static void fib_rules_put_rcu(struct rcu_head *head) -{ - struct fib_rules_ops *ops = container_of(head, struct fib_rules_ops, rcu); - struct net *net = ops->fro_net; - - release_net(net); - kfree(ops); -} - void fib_rules_unregister(struct fib_rules_ops *ops) { struct net *net = ops->fro_net; @@ -178,7 +168,7 @@ void fib_rules_unregister(struct fib_rules_ops *ops) fib_rules_cleanup_ops(ops); spin_unlock(&net->rules_mod_lock); - call_rcu(&ops->rcu, fib_rules_put_rcu); + kfree_rcu(ops, rcu); } EXPORT_SYMBOL_GPL(fib_rules_unregister); @@ -303,7 +293,7 @@ static int fib_nl_newrule(struct sk_buff *skb, struct nlmsghdr* nlh) err = -ENOMEM; goto errout; } - rule->fr_net = hold_net(net); + rule->fr_net = net; if (tb[FRA_PRIORITY]) rule->pref = nla_get_u32(tb[FRA_PRIORITY]); @@ -423,7 +413,6 @@ static int fib_nl_newrule(struct sk_buff *skb, struct nlmsghdr* nlh) return 0; errout_free: - release_net(rule->fr_net); kfree(rule); errout: rules_ops_put(ops); diff --git a/net/core/neighbour.c b/net/core/neighbour.c index ad07990e943d..0e8b32efc031 100644 --- a/net/core/neighbour.c +++ b/net/core/neighbour.c @@ -591,7 +591,7 @@ struct pneigh_entry * pneigh_lookup(struct neigh_table *tbl, if (!n) goto out; - write_pnet(&n->net, hold_net(net)); + write_pnet(&n->net, net); memcpy(n->key, pkey, key_len); n->dev = dev; if (dev) @@ -600,7 +600,6 @@ struct pneigh_entry * pneigh_lookup(struct neigh_table *tbl, if (tbl->pconstructor && tbl->pconstructor(n)) { if (dev) dev_put(dev); - release_net(net); kfree(n); n = NULL; goto out; @@ -634,7 +633,6 @@ int pneigh_delete(struct neigh_table *tbl, struct net *net, const void *pkey, tbl->pdestructor(n); if (n->dev) dev_put(n->dev); - release_net(pneigh_net(n)); kfree(n); return 0; } @@ -657,7 +655,6 @@ static int pneigh_ifdown(struct neigh_table *tbl, struct net_device *dev) tbl->pdestructor(n); if (n->dev) dev_put(n->dev); - release_net(pneigh_net(n)); kfree(n); continue; } @@ -1428,11 +1425,10 @@ struct neigh_parms *neigh_parms_alloc(struct net_device *dev, neigh_rand_reach_time(NEIGH_VAR(p, BASE_REACHABLE_TIME)); dev_hold(dev); p->dev = dev; - write_pnet(&p->net, hold_net(net)); + write_pnet(&p->net, net); p->sysctl_table = NULL; if (ops->ndo_neigh_setup && ops->ndo_neigh_setup(dev, p)) { - release_net(net); dev_put(dev); kfree(p); return NULL; @@ -1472,7 +1468,6 @@ EXPORT_SYMBOL(neigh_parms_release); static void neigh_parms_destroy(struct neigh_parms *parms) { - release_net(neigh_parms_net(parms)); kfree(parms); } diff --git a/net/core/net_namespace.c b/net/core/net_namespace.c index cb5290b8c428..e5e96b0f6717 100644 --- a/net/core/net_namespace.c +++ b/net/core/net_namespace.c @@ -236,10 +236,6 @@ static __net_init int setup_net(struct net *net, struct user_namespace *user_ns) net->user_ns = user_ns; idr_init(&net->netns_ids); -#ifdef NETNS_REFCNT_DEBUG - atomic_set(&net->use_count, 0); -#endif - list_for_each_entry(ops, &pernet_list, list) { error = ops_init(ops, net); if (error < 0) @@ -294,13 +290,6 @@ out_free: static void net_free(struct net *net) { -#ifdef NETNS_REFCNT_DEBUG - if (unlikely(atomic_read(&net->use_count) != 0)) { - pr_emerg("network namespace not free! Usage: %d\n", - atomic_read(&net->use_count)); - return; - } -#endif kfree(rcu_access_pointer(net->gen)); kmem_cache_free(net_cachep, net); } diff --git a/net/core/sock.c b/net/core/sock.c index a9a9c2ff9260..c8842f279f7a 100644 --- a/net/core/sock.c +++ b/net/core/sock.c @@ -1455,7 +1455,6 @@ void sk_release_kernel(struct sock *sk) sock_hold(sk); sock_release(sk->sk_socket); - release_net(sock_net(sk)); sock_net_set(sk, get_net(&init_net)); sock_put(sk); } diff --git a/net/ipv4/fib_semantics.c b/net/ipv4/fib_semantics.c index c6d267442dac..66c1e4fbf884 100644 --- a/net/ipv4/fib_semantics.c +++ b/net/ipv4/fib_semantics.c @@ -213,7 +213,6 @@ static void free_fib_info_rcu(struct rcu_head *head) rt_fibinfo_free(&nexthop_nh->nh_rth_input); } endfor_nexthops(fi); - release_net(fi->fib_net); if (fi->fib_metrics != (u32 *) dst_default_metrics) kfree(fi->fib_metrics); kfree(fi); @@ -814,7 +813,7 @@ struct fib_info *fib_create_info(struct fib_config *cfg) } else fi->fib_metrics = (u32 *) dst_default_metrics; - fi->fib_net = hold_net(net); + fi->fib_net = net; fi->fib_protocol = cfg->fc_protocol; fi->fib_scope = cfg->fc_scope; fi->fib_flags = cfg->fc_flags; diff --git a/net/ipv4/inet_hashtables.c b/net/ipv4/inet_hashtables.c index 9111a4e22155..f6a12b97d12b 100644 --- a/net/ipv4/inet_hashtables.c +++ b/net/ipv4/inet_hashtables.c @@ -61,7 +61,7 @@ struct inet_bind_bucket *inet_bind_bucket_create(struct kmem_cache *cachep, struct inet_bind_bucket *tb = kmem_cache_alloc(cachep, GFP_ATOMIC); if (tb != NULL) { - write_pnet(&tb->ib_net, hold_net(net)); + write_pnet(&tb->ib_net, net); tb->port = snum; tb->fastreuse = 0; tb->fastreuseport = 0; @@ -79,7 +79,6 @@ void inet_bind_bucket_destroy(struct kmem_cache *cachep, struct inet_bind_bucket { if (hlist_empty(&tb->owners)) { __hlist_del(&tb->node); - release_net(ib_net(tb)); kmem_cache_free(cachep, tb); } } diff --git a/net/ipv4/inet_timewait_sock.c b/net/ipv4/inet_timewait_sock.c index 2bd980526631..86ebf020925b 100644 --- a/net/ipv4/inet_timewait_sock.c +++ b/net/ipv4/inet_timewait_sock.c @@ -98,7 +98,6 @@ void inet_twsk_free(struct inet_timewait_sock *tw) #ifdef SOCK_REFCNT_DEBUG pr_debug("%s timewait_sock %p released\n", tw->tw_prot->name, tw); #endif - release_net(twsk_net(tw)); kmem_cache_free(tw->tw_prot->twsk_prot->twsk_slab, tw); module_put(owner); } @@ -196,7 +195,7 @@ struct inet_timewait_sock *inet_twsk_alloc(const struct sock *sk, const int stat tw->tw_transparent = inet->transparent; tw->tw_prot = sk->sk_prot_creator; atomic64_set(&tw->tw_cookie, atomic64_read(&sk->sk_cookie)); - twsk_net_set(tw, hold_net(sock_net(sk))); + twsk_net_set(tw, sock_net(sk)); /* * Because we use RCU lookups, we should not set tw_refcnt * to a non null value before everything is setup for this diff --git a/net/ipv6/addrlabel.c b/net/ipv6/addrlabel.c index e43e79d0a612..59c793040498 100644 --- a/net/ipv6/addrlabel.c +++ b/net/ipv6/addrlabel.c @@ -129,9 +129,6 @@ static const __net_initconst struct ip6addrlbl_init_table /* Object management */ static inline void ip6addrlbl_free(struct ip6addrlbl_entry *p) { -#ifdef CONFIG_NET_NS - release_net(p->lbl_net); -#endif kfree(p); } @@ -241,7 +238,7 @@ static struct ip6addrlbl_entry *ip6addrlbl_alloc(struct net *net, newp->label = label; INIT_HLIST_NODE(&newp->list); #ifdef CONFIG_NET_NS - newp->lbl_net = hold_net(net); + newp->lbl_net = net; #endif atomic_set(&newp->refcnt, 1); return newp; diff --git a/net/ipv6/ip6_flowlabel.c b/net/ipv6/ip6_flowlabel.c index f45d6db50a45..457303886fd4 100644 --- a/net/ipv6/ip6_flowlabel.c +++ b/net/ipv6/ip6_flowlabel.c @@ -100,7 +100,6 @@ static void fl_free(struct ip6_flowlabel *fl) if (fl) { if (fl->share == IPV6_FL_S_PROCESS) put_pid(fl->owner.pid); - release_net(fl->fl_net); kfree(fl->opt); kfree_rcu(fl, rcu); } @@ -403,7 +402,7 @@ fl_create(struct net *net, struct sock *sk, struct in6_flowlabel_req *freq, } } - fl->fl_net = hold_net(net); + fl->fl_net = net; fl->expires = jiffies; err = fl6_renew(fl, freq->flr_linger, freq->flr_expires); if (err) diff --git a/net/openvswitch/datapath.c b/net/openvswitch/datapath.c index 5bae7243c577..096c6276e6b9 100644 --- a/net/openvswitch/datapath.c +++ b/net/openvswitch/datapath.c @@ -203,7 +203,6 @@ static void destroy_dp_rcu(struct rcu_head *rcu) ovs_flow_tbl_destroy(&dp->table); free_percpu(dp->stats_percpu); - release_net(ovs_dp_get_net(dp)); kfree(dp->ports); kfree(dp); } @@ -1501,7 +1500,7 @@ static int ovs_dp_cmd_new(struct sk_buff *skb, struct genl_info *info) if (dp == NULL) goto err_free_reply; - ovs_dp_set_net(dp, hold_net(sock_net(skb->sk))); + ovs_dp_set_net(dp, sock_net(skb->sk)); /* Allocate table. */ err = ovs_flow_tbl_init(&dp->table); @@ -1575,7 +1574,6 @@ err_destroy_percpu: err_destroy_table: ovs_flow_tbl_destroy(&dp->table); err_free_dp: - release_net(ovs_dp_get_net(dp)); kfree(dp); err_free_reply: kfree_skb(reply); -- cgit v1.2.3 From 0c5c9fb55106333e773de8c9dd321fa8240caeb3 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Wed, 11 Mar 2015 23:06:44 -0500 Subject: net: Introduce possible_net_t Having to say > #ifdef CONFIG_NET_NS > struct net *net; > #endif in structures is a little bit wordy and a little bit error prone. Instead it is possible to say: > typedef struct { > #ifdef CONFIG_NET_NS > struct net *net; > #endif > } possible_net_t; And then in a header say: > possible_net_t net; Which is cleaner and easier to use and easier to test, as the possible_net_t is always there no matter what the compile options. Further this allows read_pnet and write_pnet to be functions in all cases which is better at catching typos. This change adds possible_net_t, updates the definitions of read_pnet and write_pnet, updates optional struct net * variables that write_pnet uses on to have the type possible_net_t, and finally fixes up the b0rked users of read_pnet and write_pnet. Signed-off-by: "Eric W. Biederman" Acked-by: Eric Dumazet Signed-off-by: David S. Miller --- include/linux/netdevice.h | 8 ++------ include/net/cfg80211.h | 4 +--- include/net/genetlink.h | 4 +--- include/net/inet_hashtables.h | 4 +--- include/net/ip_vs.h | 8 ++++---- include/net/neighbour.h | 8 ++------ include/net/net_namespace.h | 23 +++++++++++++---------- include/net/netfilter/nf_conntrack.h | 5 ++--- include/net/sock.h | 4 +--- include/net/xfrm.h | 8 ++------ net/9p/trans_fd.c | 4 ++-- net/ipv4/ipmr.c | 4 +--- net/ipv6/addrlabel.c | 8 ++------ net/ipv6/ip6mr.c | 4 +--- net/openvswitch/datapath.h | 4 +--- net/packet/internal.h | 4 +--- 16 files changed, 37 insertions(+), 67 deletions(-) (limited to 'include') diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index cede40d9cac9..ddab1a2a07a0 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -1721,9 +1721,7 @@ struct net_device { struct netpoll_info __rcu *npinfo; #endif -#ifdef CONFIG_NET_NS - struct net *nd_net; -#endif + possible_net_t nd_net; /* mid-layer private */ union { @@ -1863,9 +1861,7 @@ struct net *dev_net(const struct net_device *dev) static inline void dev_net_set(struct net_device *dev, struct net *net) { -#ifdef CONFIG_NET_NS - dev->nd_net = net; -#endif + write_pnet(&dev->nd_net, net); } static inline bool netdev_uses_dsa(struct net_device *dev) diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 64e09e1e8099..f977abec07f6 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -3183,10 +3183,8 @@ struct wiphy { const struct ieee80211_ht_cap *ht_capa_mod_mask; const struct ieee80211_vht_cap *vht_capa_mod_mask; -#ifdef CONFIG_NET_NS /* the network namespace this phy lives in currently */ - struct net *_net; -#endif + possible_net_t _net; #ifdef CONFIG_CFG80211_WEXT const struct iw_handler_def *wext; diff --git a/include/net/genetlink.h b/include/net/genetlink.h index 0574abd3db86..a9af1cc8c1bc 100644 --- a/include/net/genetlink.h +++ b/include/net/genetlink.h @@ -92,9 +92,7 @@ struct genl_info { struct genlmsghdr * genlhdr; void * userhdr; struct nlattr ** attrs; -#ifdef CONFIG_NET_NS - struct net * _net; -#endif + possible_net_t _net; void * user_ptr[2]; struct sock * dst_sk; }; diff --git a/include/net/inet_hashtables.h b/include/net/inet_hashtables.h index dd1950a7e273..bcd64756e5fe 100644 --- a/include/net/inet_hashtables.h +++ b/include/net/inet_hashtables.h @@ -76,9 +76,7 @@ struct inet_ehash_bucket { * ports are created in O(1) time? I thought so. ;-) -DaveM */ struct inet_bind_bucket { -#ifdef CONFIG_NET_NS - struct net *ib_net; -#endif + possible_net_t ib_net; unsigned short port; signed char fastreuse; signed char fastreuseport; diff --git a/include/net/ip_vs.h b/include/net/ip_vs.h index 20fd23398537..4e3731ee4eac 100644 --- a/include/net/ip_vs.h +++ b/include/net/ip_vs.h @@ -47,13 +47,13 @@ static inline struct net *skb_net(const struct sk_buff *skb) * Start with the most likely hit * End with BUG */ - if (likely(skb->dev && skb->dev->nd_net)) + if (likely(skb->dev && dev_net(skb->dev))) return dev_net(skb->dev); if (skb_dst(skb) && skb_dst(skb)->dev) return dev_net(skb_dst(skb)->dev); WARN(skb->sk, "Maybe skb_sknet should be used in %s() at line:%d\n", __func__, __LINE__); - if (likely(skb->sk && skb->sk->sk_net)) + if (likely(skb->sk && sock_net(skb->sk))) return sock_net(skb->sk); pr_err("There is no net ptr to find in the skb in %s() line:%d\n", __func__, __LINE__); @@ -71,11 +71,11 @@ static inline struct net *skb_sknet(const struct sk_buff *skb) #ifdef CONFIG_NET_NS #ifdef CONFIG_IP_VS_DEBUG /* Start with the most likely hit */ - if (likely(skb->sk && skb->sk->sk_net)) + if (likely(skb->sk && sock_net(skb->sk))) return sock_net(skb->sk); WARN(skb->dev, "Maybe skb_net should be used instead in %s() line:%d\n", __func__, __LINE__); - if (likely(skb->dev && skb->dev->nd_net)) + if (likely(skb->dev && dev_net(skb->dev))) return dev_net(skb->dev); pr_err("There is no net ptr to find in the skb in %s() line:%d\n", __func__, __LINE__); diff --git a/include/net/neighbour.h b/include/net/neighbour.h index d48b8ec8b5f4..e7bdf5170802 100644 --- a/include/net/neighbour.h +++ b/include/net/neighbour.h @@ -65,9 +65,7 @@ enum { }; struct neigh_parms { -#ifdef CONFIG_NET_NS - struct net *net; -#endif + possible_net_t net; struct net_device *dev; struct list_head list; int (*neigh_setup)(struct neighbour *); @@ -167,9 +165,7 @@ struct neigh_ops { struct pneigh_entry { struct pneigh_entry *next; -#ifdef CONFIG_NET_NS - struct net *net; -#endif + possible_net_t net; struct net_device *dev; u8 flags; u8 key[0]; diff --git a/include/net/net_namespace.h b/include/net/net_namespace.h index fab51ceeabf3..f733656404de 100644 --- a/include/net/net_namespace.h +++ b/include/net/net_namespace.h @@ -231,24 +231,27 @@ int net_eq(const struct net *net1, const struct net *net2) #endif +typedef struct { #ifdef CONFIG_NET_NS + struct net *net; +#endif +} possible_net_t; -static inline void write_pnet(struct net **pnet, struct net *net) +static inline void write_pnet(possible_net_t *pnet, struct net *net) { - *pnet = net; +#ifdef CONFIG_NET_NS + pnet->net = net; +#endif } -static inline struct net *read_pnet(struct net * const *pnet) +static inline struct net *read_pnet(const possible_net_t *pnet) { - return *pnet; -} - +#ifdef CONFIG_NET_NS + return pnet->net; #else - -#define write_pnet(pnet, net) do { (void)(net);} while (0) -#define read_pnet(pnet) (&init_net) - + return &init_net; #endif +} #define for_each_net(VAR) \ list_for_each_entry(VAR, &net_namespace_list, list) diff --git a/include/net/netfilter/nf_conntrack.h b/include/net/netfilter/nf_conntrack.h index 74f271a172dd..095433b8a8b0 100644 --- a/include/net/netfilter/nf_conntrack.h +++ b/include/net/netfilter/nf_conntrack.h @@ -95,9 +95,8 @@ struct nf_conn { /* Timer function; drops refcnt when it goes off. */ struct timer_list timeout; -#ifdef CONFIG_NET_NS - struct net *ct_net; -#endif + possible_net_t ct_net; + /* all members below initialized via memset */ u8 __nfct_init_offset[0]; diff --git a/include/net/sock.h b/include/net/sock.h index 95b2c1c220f9..9411c3421dd3 100644 --- a/include/net/sock.h +++ b/include/net/sock.h @@ -190,9 +190,7 @@ struct sock_common { struct hlist_nulls_node skc_portaddr_node; }; struct proto *skc_prot; -#ifdef CONFIG_NET_NS - struct net *skc_net; -#endif + possible_net_t skc_net; #if IS_ENABLED(CONFIG_IPV6) struct in6_addr skc_v6_daddr; diff --git a/include/net/xfrm.h b/include/net/xfrm.h index dc4865e90fe4..d0ac7d7be8a7 100644 --- a/include/net/xfrm.h +++ b/include/net/xfrm.h @@ -126,9 +126,7 @@ struct xfrm_state_walk { /* Full description of state of transformer. */ struct xfrm_state { -#ifdef CONFIG_NET_NS - struct net *xs_net; -#endif + possible_net_t xs_net; union { struct hlist_node gclist; struct hlist_node bydst; @@ -522,9 +520,7 @@ struct xfrm_policy_queue { }; struct xfrm_policy { -#ifdef CONFIG_NET_NS - struct net *xp_net; -#endif + possible_net_t xp_net; struct hlist_node bydst; struct hlist_node byidx; diff --git a/net/9p/trans_fd.c b/net/9p/trans_fd.c index 80d08f6664cb..3e3d82d8ff70 100644 --- a/net/9p/trans_fd.c +++ b/net/9p/trans_fd.c @@ -940,7 +940,7 @@ p9_fd_create_tcp(struct p9_client *client, const char *addr, char *args) sin_server.sin_family = AF_INET; sin_server.sin_addr.s_addr = in_aton(addr); sin_server.sin_port = htons(opts.port); - err = __sock_create(read_pnet(¤t->nsproxy->net_ns), PF_INET, + err = __sock_create(current->nsproxy->net_ns, PF_INET, SOCK_STREAM, IPPROTO_TCP, &csocket, 1); if (err) { pr_err("%s (%d): problem creating socket\n", @@ -988,7 +988,7 @@ p9_fd_create_unix(struct p9_client *client, const char *addr, char *args) sun_server.sun_family = PF_UNIX; strcpy(sun_server.sun_path, addr); - err = __sock_create(read_pnet(¤t->nsproxy->net_ns), PF_UNIX, + err = __sock_create(current->nsproxy->net_ns, PF_UNIX, SOCK_STREAM, 0, &csocket, 1); if (err < 0) { pr_err("%s (%d): problem creating socket\n", diff --git a/net/ipv4/ipmr.c b/net/ipv4/ipmr.c index 9d78427652d2..5b188832800f 100644 --- a/net/ipv4/ipmr.c +++ b/net/ipv4/ipmr.c @@ -73,9 +73,7 @@ struct mr_table { struct list_head list; -#ifdef CONFIG_NET_NS - struct net *net; -#endif + possible_net_t net; u32 id; struct sock __rcu *mroute_sk; struct timer_list ipmr_expire_timer; diff --git a/net/ipv6/addrlabel.c b/net/ipv6/addrlabel.c index 59c793040498..3cc50e2d3bf5 100644 --- a/net/ipv6/addrlabel.c +++ b/net/ipv6/addrlabel.c @@ -29,9 +29,7 @@ * Policy Table */ struct ip6addrlbl_entry { -#ifdef CONFIG_NET_NS - struct net *lbl_net; -#endif + possible_net_t lbl_net; struct in6_addr prefix; int prefixlen; int ifindex; @@ -237,9 +235,7 @@ static struct ip6addrlbl_entry *ip6addrlbl_alloc(struct net *net, newp->addrtype = addrtype; newp->label = label; INIT_HLIST_NODE(&newp->list); -#ifdef CONFIG_NET_NS - newp->lbl_net = net; -#endif + write_pnet(&newp->lbl_net, net); atomic_set(&newp->refcnt, 1); return newp; } diff --git a/net/ipv6/ip6mr.c b/net/ipv6/ip6mr.c index 34b682617f50..4b9315aa273e 100644 --- a/net/ipv6/ip6mr.c +++ b/net/ipv6/ip6mr.c @@ -56,9 +56,7 @@ struct mr6_table { struct list_head list; -#ifdef CONFIG_NET_NS - struct net *net; -#endif + possible_net_t net; u32 id; struct sock *mroute6_sk; struct timer_list ipmr_expire_timer; diff --git a/net/openvswitch/datapath.h b/net/openvswitch/datapath.h index 3ece94563079..4ec4a480b147 100644 --- a/net/openvswitch/datapath.h +++ b/net/openvswitch/datapath.h @@ -84,10 +84,8 @@ struct datapath { /* Stats. */ struct dp_stats_percpu __percpu *stats_percpu; -#ifdef CONFIG_NET_NS /* Network namespace ref. */ - struct net *net; -#endif + possible_net_t net; u32 user_features; }; diff --git a/net/packet/internal.h b/net/packet/internal.h index cdddf6a30399..fe6e20caea1d 100644 --- a/net/packet/internal.h +++ b/net/packet/internal.h @@ -74,9 +74,7 @@ extern struct mutex fanout_mutex; #define PACKET_FANOUT_MAX 256 struct packet_fanout { -#ifdef CONFIG_NET_NS - struct net *net; -#endif + possible_net_t net; unsigned int num_members; u16 id; u8 type; -- cgit v1.2.3 From dfc202ead3123988793ac1160849676000b77ee4 Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Mon, 2 Feb 2015 14:37:41 -0800 Subject: clk: Add tracepoints for hardware operations It's useful to have tracepoints around operations that change the hardware state so that we can debug clock hardware performance and operations. Four basic types of events are supported: on/off events for enable, disable, prepare, unprepare that only record an event and a clock name, rate changing events for clk_set_{min_,max_}rate{_range}(), phase changing events for clk_set_phase() and parent changing events for clk_set_parent(). Cc: Steven Rostedt Signed-off-by: Stephen Boyd Signed-off-by: Michael Turquette --- drivers/clk/clk.c | 56 ++++++++++--- include/trace/events/clk.h | 198 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 244 insertions(+), 10 deletions(-) create mode 100644 include/trace/events/clk.h (limited to 'include') diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c index 0b3f39c03785..42064cec2364 100644 --- a/drivers/clk/clk.c +++ b/drivers/clk/clk.c @@ -77,6 +77,9 @@ struct clk_core { struct kref ref; }; +#define CREATE_TRACE_POINTS +#include + struct clk { struct clk_core *core; const char *dev_id; @@ -492,10 +495,12 @@ static void clk_unprepare_unused_subtree(struct clk_core *clk) return; if (clk_core_is_prepared(clk)) { + trace_clk_unprepare(clk); if (clk->ops->unprepare_unused) clk->ops->unprepare_unused(clk->hw); else if (clk->ops->unprepare) clk->ops->unprepare(clk->hw); + trace_clk_unprepare_complete(clk); } } @@ -524,10 +529,12 @@ static void clk_disable_unused_subtree(struct clk_core *clk) * back to .disable */ if (clk_core_is_enabled(clk)) { + trace_clk_disable(clk); if (clk->ops->disable_unused) clk->ops->disable_unused(clk->hw); else if (clk->ops->disable) clk->ops->disable(clk->hw); + trace_clk_disable_complete(clk); } unlock_out: @@ -907,9 +914,12 @@ static void clk_core_unprepare(struct clk_core *clk) WARN_ON(clk->enable_count > 0); + trace_clk_unprepare(clk); + if (clk->ops->unprepare) clk->ops->unprepare(clk->hw); + trace_clk_unprepare_complete(clk); clk_core_unprepare(clk->parent); } @@ -947,12 +957,16 @@ static int clk_core_prepare(struct clk_core *clk) if (ret) return ret; - if (clk->ops->prepare) { + trace_clk_prepare(clk); + + if (clk->ops->prepare) ret = clk->ops->prepare(clk->hw); - if (ret) { - clk_core_unprepare(clk->parent); - return ret; - } + + trace_clk_prepare_complete(clk); + + if (ret) { + clk_core_unprepare(clk->parent); + return ret; } } @@ -999,9 +1013,13 @@ static void clk_core_disable(struct clk_core *clk) if (--clk->enable_count > 0) return; + trace_clk_disable(clk); + if (clk->ops->disable) clk->ops->disable(clk->hw); + trace_clk_disable_complete(clk); + clk_core_disable(clk->parent); } @@ -1054,12 +1072,16 @@ static int clk_core_enable(struct clk_core *clk) if (ret) return ret; - if (clk->ops->enable) { + trace_clk_enable(clk); + + if (clk->ops->enable) ret = clk->ops->enable(clk->hw); - if (ret) { - clk_core_disable(clk->parent); - return ret; - } + + trace_clk_enable_complete(clk); + + if (ret) { + clk_core_disable(clk->parent); + return ret; } } @@ -1490,10 +1512,14 @@ static int __clk_set_parent(struct clk_core *clk, struct clk_core *parent, old_parent = __clk_set_parent_before(clk, parent); + trace_clk_set_parent(clk, parent); + /* change clock input source */ if (parent && clk->ops->set_parent) ret = clk->ops->set_parent(clk->hw, p_index); + trace_clk_set_parent_complete(clk, parent); + if (ret) { flags = clk_enable_lock(); clk_reparent(clk, old_parent); @@ -1719,6 +1745,7 @@ static void clk_change_rate(struct clk_core *clk) if (clk->new_parent && clk->new_parent != clk->parent) { old_parent = __clk_set_parent_before(clk, clk->new_parent); + trace_clk_set_parent(clk, clk->new_parent); if (clk->ops->set_rate_and_parent) { skip_set_rate = true; @@ -1729,12 +1756,17 @@ static void clk_change_rate(struct clk_core *clk) clk->ops->set_parent(clk->hw, clk->new_parent_index); } + trace_clk_set_parent_complete(clk, clk->new_parent); __clk_set_parent_after(clk, clk->new_parent, old_parent); } + trace_clk_set_rate(clk, clk->new_rate); + if (!skip_set_rate && clk->ops->set_rate) clk->ops->set_rate(clk->hw, clk->new_rate, best_parent_rate); + trace_clk_set_rate_complete(clk, clk->new_rate); + clk->rate = clk_recalc(clk, best_parent_rate); if (clk->notifier_count && old_rate != clk->rate) @@ -2135,9 +2167,13 @@ int clk_set_phase(struct clk *clk, int degrees) clk_prepare_lock(); + trace_clk_set_phase(clk->core, degrees); + if (clk->core->ops->set_phase) ret = clk->core->ops->set_phase(clk->core->hw, degrees); + trace_clk_set_phase_complete(clk->core, degrees); + if (!ret) clk->core->phase = degrees; diff --git a/include/trace/events/clk.h b/include/trace/events/clk.h new file mode 100644 index 000000000000..758607226bfd --- /dev/null +++ b/include/trace/events/clk.h @@ -0,0 +1,198 @@ +/* + * Copyright (c) 2014-2015, The Linux Foundation. All rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#undef TRACE_SYSTEM +#define TRACE_SYSTEM clk + +#if !defined(_TRACE_CLK_H) || defined(TRACE_HEADER_MULTI_READ) +#define _TRACE_CLK_H + +#include + +struct clk_core; + +DECLARE_EVENT_CLASS(clk, + + TP_PROTO(struct clk_core *core), + + TP_ARGS(core), + + TP_STRUCT__entry( + __string( name, core->name ) + ), + + TP_fast_assign( + __assign_str(name, core->name); + ), + + TP_printk("%s", __get_str(name)) +); + +DEFINE_EVENT(clk, clk_enable, + + TP_PROTO(struct clk_core *core), + + TP_ARGS(core) +); + +DEFINE_EVENT(clk, clk_enable_complete, + + TP_PROTO(struct clk_core *core), + + TP_ARGS(core) +); + +DEFINE_EVENT(clk, clk_disable, + + TP_PROTO(struct clk_core *core), + + TP_ARGS(core) +); + +DEFINE_EVENT(clk, clk_disable_complete, + + TP_PROTO(struct clk_core *core), + + TP_ARGS(core) +); + +DEFINE_EVENT(clk, clk_prepare, + + TP_PROTO(struct clk_core *core), + + TP_ARGS(core) +); + +DEFINE_EVENT(clk, clk_prepare_complete, + + TP_PROTO(struct clk_core *core), + + TP_ARGS(core) +); + +DEFINE_EVENT(clk, clk_unprepare, + + TP_PROTO(struct clk_core *core), + + TP_ARGS(core) +); + +DEFINE_EVENT(clk, clk_unprepare_complete, + + TP_PROTO(struct clk_core *core), + + TP_ARGS(core) +); + +DECLARE_EVENT_CLASS(clk_rate, + + TP_PROTO(struct clk_core *core, unsigned long rate), + + TP_ARGS(core, rate), + + TP_STRUCT__entry( + __string( name, core->name ) + __field(unsigned long, rate ) + ), + + TP_fast_assign( + __assign_str(name, core->name); + __entry->rate = rate; + ), + + TP_printk("%s %lu", __get_str(name), (unsigned long)__entry->rate) +); + +DEFINE_EVENT(clk_rate, clk_set_rate, + + TP_PROTO(struct clk_core *core, unsigned long rate), + + TP_ARGS(core, rate) +); + +DEFINE_EVENT(clk_rate, clk_set_rate_complete, + + TP_PROTO(struct clk_core *core, unsigned long rate), + + TP_ARGS(core, rate) +); + +DECLARE_EVENT_CLASS(clk_parent, + + TP_PROTO(struct clk_core *core, struct clk_core *parent), + + TP_ARGS(core, parent), + + TP_STRUCT__entry( + __string( name, core->name ) + __string( pname, parent->name ) + ), + + TP_fast_assign( + __assign_str(name, core->name); + __assign_str(pname, parent->name); + ), + + TP_printk("%s %s", __get_str(name), __get_str(pname)) +); + +DEFINE_EVENT(clk_parent, clk_set_parent, + + TP_PROTO(struct clk_core *core, struct clk_core *parent), + + TP_ARGS(core, parent) +); + +DEFINE_EVENT(clk_parent, clk_set_parent_complete, + + TP_PROTO(struct clk_core *core, struct clk_core *parent), + + TP_ARGS(core, parent) +); + +DECLARE_EVENT_CLASS(clk_phase, + + TP_PROTO(struct clk_core *core, int phase), + + TP_ARGS(core, phase), + + TP_STRUCT__entry( + __string( name, core->name ) + __field( int, phase ) + ), + + TP_fast_assign( + __assign_str(name, core->name); + __entry->phase = phase; + ), + + TP_printk("%s %d", __get_str(name), (int)__entry->phase) +); + +DEFINE_EVENT(clk_phase, clk_set_phase, + + TP_PROTO(struct clk_core *core, int phase), + + TP_ARGS(core, phase) +); + +DEFINE_EVENT(clk_phase, clk_set_phase_complete, + + TP_PROTO(struct clk_core *core, int phase), + + TP_ARGS(core, phase) +); + +#endif /* _TRACE_CLK_H */ + +/* This part must be outside protection */ +#include -- cgit v1.2.3 From 80f1d68ccba70b1060c9c7360ca83da430f66bed Mon Sep 17 00:00:00 2001 From: Daniel Borkmann Date: Thu, 12 Mar 2015 17:21:42 +0100 Subject: ebpf: verifier: check that call reg with ARG_ANYTHING is initialized I noticed that a helper function with argument type ARG_ANYTHING does not need to have an initialized value (register). This can worst case lead to unintented stack memory leakage in future helper functions if they are not carefully designed, or unintended application behaviour in case the application developer was not careful enough to match a correct helper function signature in the API. The underlying issue is that ARG_ANYTHING should actually be split into two different semantics: 1) ARG_DONTCARE for function arguments that the helper function does not care about (in other words: the default for unused function arguments), and 2) ARG_ANYTHING that is an argument actually being used by a helper function and *guaranteed* to be an initialized register. The current risk is low: ARG_ANYTHING is only used for the 'flags' argument (r4) in bpf_map_update_elem() that internally does strict checking. Fixes: 17a5267067f3 ("bpf: verifier (add verifier core)") Signed-off-by: Daniel Borkmann Acked-by: Alexei Starovoitov Signed-off-by: David S. Miller --- include/linux/bpf.h | 4 +++- kernel/bpf/verifier.c | 5 ++++- 2 files changed, 7 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/linux/bpf.h b/include/linux/bpf.h index a884f5a2c503..80f2e0fc3d02 100644 --- a/include/linux/bpf.h +++ b/include/linux/bpf.h @@ -44,7 +44,7 @@ struct bpf_map_type_list { /* function argument constraints */ enum bpf_arg_type { - ARG_ANYTHING = 0, /* any argument is ok */ + ARG_DONTCARE = 0, /* unused argument in helper function */ /* the following constraints used to prototype * bpf_map_lookup/update/delete_elem() functions @@ -58,6 +58,8 @@ enum bpf_arg_type { */ ARG_PTR_TO_STACK, /* any pointer to eBPF program stack */ ARG_CONST_STACK_SIZE, /* number of bytes accessed from stack */ + + ARG_ANYTHING, /* any (initialized) argument is ok */ }; /* type of values returned from helper functions */ diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index bdf4192a889b..e6b522496250 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -755,7 +755,7 @@ static int check_func_arg(struct verifier_env *env, u32 regno, enum bpf_reg_type expected_type; int err = 0; - if (arg_type == ARG_ANYTHING) + if (arg_type == ARG_DONTCARE) return 0; if (reg->type == NOT_INIT) { @@ -763,6 +763,9 @@ static int check_func_arg(struct verifier_env *env, u32 regno, return -EACCES; } + if (arg_type == ARG_ANYTHING) + return 0; + if (arg_type == ARG_PTR_TO_STACK || arg_type == ARG_PTR_TO_MAP_KEY || arg_type == ARG_PTR_TO_MAP_VALUE) { expected_type = PTR_TO_STACK; -- cgit v1.2.3 From 88428cc5c27c63a4313e213813bc39b9899224d5 Mon Sep 17 00:00:00 2001 From: "Paul E. McKenney" Date: Wed, 28 Jan 2015 14:42:09 -0800 Subject: rcu: Handle outgoing CPUs on exit from idle loop This commit informs RCU of an outgoing CPU just before that CPU invokes arch_cpu_idle_dead() during its last pass through the idle loop (via a new CPU_DYING_IDLE notifier value). This change means that RCU need not deal with outgoing CPUs passing through the scheduler after informing RCU that they are no longer online. Note that removing the CPU from the rcu_node ->qsmaskinit bit masks is done at CPU_DYING_IDLE time, and orphaning callbacks is still done at CPU_DEAD time, the reason being that at CPU_DEAD time we have another CPU that can adopt them. Signed-off-by: Paul E. McKenney --- include/linux/cpu.h | 2 ++ include/linux/rcupdate.h | 2 ++ kernel/rcu/tree.c | 41 +++++++++++++++++++++++++++++++---------- kernel/sched/idle.c | 2 ++ 4 files changed, 37 insertions(+), 10 deletions(-) (limited to 'include') diff --git a/include/linux/cpu.h b/include/linux/cpu.h index 4744ef915acd..d028721748d4 100644 --- a/include/linux/cpu.h +++ b/include/linux/cpu.h @@ -95,6 +95,8 @@ enum { * Called on the new cpu, just before * enabling interrupts. Must not sleep, * must not fail */ +#define CPU_DYING_IDLE 0x000B /* CPU (unsigned)v dying, reached + * idle loop. */ #define CPU_BROKEN 0x000C /* CPU (unsigned)v did not die properly, * perhaps due to preemption. */ diff --git a/include/linux/rcupdate.h b/include/linux/rcupdate.h index 78097491cd99..762022f07afd 100644 --- a/include/linux/rcupdate.h +++ b/include/linux/rcupdate.h @@ -266,6 +266,8 @@ void rcu_idle_enter(void); void rcu_idle_exit(void); void rcu_irq_enter(void); void rcu_irq_exit(void); +int rcu_cpu_notify(struct notifier_block *self, + unsigned long action, void *hcpu); #ifdef CONFIG_RCU_STALL_COMMON void rcu_sysrq_start(void); diff --git a/kernel/rcu/tree.c b/kernel/rcu/tree.c index 79d53399247e..d5247ed44004 100644 --- a/kernel/rcu/tree.c +++ b/kernel/rcu/tree.c @@ -2475,6 +2475,26 @@ static void rcu_cleanup_dead_rnp(struct rcu_node *rnp_leaf) } } +/* + * The CPU is exiting the idle loop into the arch_cpu_idle_dead() + * function. We now remove it from the rcu_node tree's ->qsmaskinit + * bit masks. + */ +static void rcu_cleanup_dying_idle_cpu(int cpu, struct rcu_state *rsp) +{ + unsigned long flags; + unsigned long mask; + struct rcu_data *rdp = per_cpu_ptr(rsp->rda, cpu); + struct rcu_node *rnp = rdp->mynode; /* Outgoing CPU's rdp & rnp. */ + + /* Remove outgoing CPU from mask in the leaf rcu_node structure. */ + mask = rdp->grpmask; + raw_spin_lock_irqsave(&rnp->lock, flags); + smp_mb__after_unlock_lock(); /* Enforce GP memory-order guarantee. */ + rnp->qsmaskinitnext &= ~mask; + raw_spin_unlock_irqrestore(&rnp->lock, flags); +} + /* * The CPU has been completely removed, and some other CPU is reporting * this fact from process context. Do the remainder of the cleanup, @@ -2485,7 +2505,6 @@ static void rcu_cleanup_dead_rnp(struct rcu_node *rnp_leaf) static void rcu_cleanup_dead_cpu(int cpu, struct rcu_state *rsp) { unsigned long flags; - unsigned long mask; struct rcu_data *rdp = per_cpu_ptr(rsp->rda, cpu); struct rcu_node *rnp = rdp->mynode; /* Outgoing CPU's rdp & rnp. */ @@ -2498,13 +2517,6 @@ static void rcu_cleanup_dead_cpu(int cpu, struct rcu_state *rsp) rcu_adopt_orphan_cbs(rsp, flags); raw_spin_unlock_irqrestore(&rsp->orphan_lock, flags); - /* Remove outgoing CPU from mask in the leaf rcu_node structure. */ - mask = rdp->grpmask; - raw_spin_lock_irqsave(&rnp->lock, flags); - smp_mb__after_unlock_lock(); /* Enforce GP memory-order guarantee. */ - rnp->qsmaskinitnext &= ~mask; - raw_spin_unlock_irqrestore(&rnp->lock, flags); - WARN_ONCE(rdp->qlen != 0 || rdp->nxtlist != NULL, "rcu_cleanup_dead_cpu: Callbacks on offline CPU %d: qlen=%lu, nxtlist=%p\n", cpu, rdp->qlen, rdp->nxtlist); @@ -2520,6 +2532,10 @@ static void __maybe_unused rcu_cleanup_dead_rnp(struct rcu_node *rnp_leaf) { } +static void rcu_cleanup_dying_idle_cpu(int cpu, struct rcu_state *rsp) +{ +} + static void rcu_cleanup_dead_cpu(int cpu, struct rcu_state *rsp) { } @@ -3733,8 +3749,8 @@ static void rcu_prepare_cpu(int cpu) /* * Handle CPU online/offline notification events. */ -static int rcu_cpu_notify(struct notifier_block *self, - unsigned long action, void *hcpu) +int rcu_cpu_notify(struct notifier_block *self, + unsigned long action, void *hcpu) { long cpu = (long)hcpu; struct rcu_data *rdp = per_cpu_ptr(rcu_state_p->rda, cpu); @@ -3760,6 +3776,11 @@ static int rcu_cpu_notify(struct notifier_block *self, for_each_rcu_flavor(rsp) rcu_cleanup_dying_cpu(rsp); break; + case CPU_DYING_IDLE: + for_each_rcu_flavor(rsp) { + rcu_cleanup_dying_idle_cpu(cpu, rsp); + } + break; case CPU_DEAD: case CPU_DEAD_FROZEN: case CPU_UP_CANCELED: diff --git a/kernel/sched/idle.c b/kernel/sched/idle.c index e99e361ade20..b0090accfb5b 100644 --- a/kernel/sched/idle.c +++ b/kernel/sched/idle.c @@ -225,6 +225,8 @@ static void cpu_idle_loop(void) rmb(); if (cpu_is_offline(smp_processor_id())) { + rcu_cpu_notify(NULL, CPU_DYING_IDLE, + (void *)(long)smp_processor_id()); smp_mb(); /* all activity before dead. */ this_cpu_write(cpu_dead_idle, true); arch_cpu_idle_dead(); -- cgit v1.2.3 From bd337c581b2b0d933d37f664bf55b342577fed3a Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Thu, 12 Mar 2015 16:44:03 -0700 Subject: ipv6: add missing ireq_net & ir_cookie initializations I forgot to update dccp_v6_conn_request() & cookie_v6_check(). They both need to set ireq->ireq_net and ireq->ir_cookie Lets clear ireq->ir_cookie in inet_reqsk_alloc() Signed-off-by: Eric Dumazet Fixes: 33cf7c90fe2f ("net: add real socket cookies") Signed-off-by: David S. Miller --- include/net/inet_sock.h | 1 + net/dccp/ipv4.c | 1 - net/dccp/ipv6.c | 1 + net/ipv4/tcp_input.c | 1 - net/ipv6/syncookies.c | 1 + 5 files changed, 3 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/net/inet_sock.h b/include/net/inet_sock.h index e565afdc14ad..30f7170abbf3 100644 --- a/include/net/inet_sock.h +++ b/include/net/inet_sock.h @@ -249,6 +249,7 @@ static inline struct request_sock *inet_reqsk_alloc(struct request_sock_ops *ops if (req != NULL) { kmemcheck_annotate_bitfield(ireq, flags); ireq->opt = NULL; + atomic64_set(&ireq->ir_cookie, 0); } return req; diff --git a/net/dccp/ipv4.c b/net/dccp/ipv4.c index a78e0b999f96..f695874b5ade 100644 --- a/net/dccp/ipv4.c +++ b/net/dccp/ipv4.c @@ -642,7 +642,6 @@ int dccp_v4_conn_request(struct sock *sk, struct sk_buff *skb) ireq->ir_loc_addr = ip_hdr(skb)->daddr; ireq->ir_rmt_addr = ip_hdr(skb)->saddr; write_pnet(&ireq->ireq_net, sock_net(sk)); - atomic64_set(&ireq->ir_cookie, 0); /* * Step 3: Process LISTEN state diff --git a/net/dccp/ipv6.c b/net/dccp/ipv6.c index 6bcaa33cd804..703a21acf434 100644 --- a/net/dccp/ipv6.c +++ b/net/dccp/ipv6.c @@ -403,6 +403,7 @@ static int dccp_v6_conn_request(struct sock *sk, struct sk_buff *skb) ireq = inet_rsk(req); ireq->ir_v6_rmt_addr = ipv6_hdr(skb)->saddr; ireq->ir_v6_loc_addr = ipv6_hdr(skb)->daddr; + write_pnet(&ireq->ireq_net, sock_net(sk)); if (ipv6_opt_accepted(sk, skb, IP6CB(skb)) || np->rxopt.bits.rxinfo || np->rxopt.bits.rxoinfo || diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index 26f24995bd3d..da61a8e75f68 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -5966,7 +5966,6 @@ int tcp_conn_request(struct request_sock_ops *rsk_ops, tmp_opt.tstamp_ok = tmp_opt.saw_tstamp; tcp_openreq_init(req, &tmp_opt, skb, sk); write_pnet(&inet_rsk(req)->ireq_net, sock_net(sk)); - atomic64_set(&inet_rsk(req)->ir_cookie, 0); af_ops->init_req(req, sk, skb); diff --git a/net/ipv6/syncookies.c b/net/ipv6/syncookies.c index 7337fc7947e2..66bba6a84e47 100644 --- a/net/ipv6/syncookies.c +++ b/net/ipv6/syncookies.c @@ -196,6 +196,7 @@ struct sock *cookie_v6_check(struct sock *sk, struct sk_buff *skb) ireq = inet_rsk(req); treq = tcp_rsk(req); treq->listener = NULL; + write_pnet(&ireq->ireq_net, sock_net(sk)); if (security_inet_conn_request(sk, skb, req)) goto out_free; -- cgit v1.2.3 From 10feb428a5045d5eb18a5d755fbb8f0cc9645626 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Thu, 12 Mar 2015 16:44:04 -0700 Subject: inet: add TCP_NEW_SYN_RECV state TCP_SYN_RECV state is currently used by fast open sockets. Initial TCP requests (the pseudo sockets created when a SYN is received) are not yet associated to a state. They are attached to their parent, and the parent is in TCP_LISTEN state. This commit adds TCP_NEW_SYN_RECV state, so that we can convert TCP stack to a different schem gradually. This state is not exported to user space. Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- include/net/tcp_states.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/net/tcp_states.h b/include/net/tcp_states.h index b0b645988bd8..50e78a74d0df 100644 --- a/include/net/tcp_states.h +++ b/include/net/tcp_states.h @@ -25,6 +25,7 @@ enum { TCP_LAST_ACK, TCP_LISTEN, TCP_CLOSING, /* Now a valid state */ + TCP_NEW_SYN_RECV, TCP_MAX_STATES /* Leave at the end! */ }; @@ -44,7 +45,8 @@ enum { TCPF_CLOSE_WAIT = (1 << 8), TCPF_LAST_ACK = (1 << 9), TCPF_LISTEN = (1 << 10), - TCPF_CLOSING = (1 << 11) + TCPF_CLOSING = (1 << 11), + TCPF_NEW_SYN_RECV = (1 << 12), }; #endif /* _LINUX_TCP_STATES_H */ -- cgit v1.2.3 From d34ac51b76e8c7de6094cfb11780ef9c2b93469f Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Thu, 12 Mar 2015 16:44:05 -0700 Subject: inet: add ireq_state field to inet_request_sock We need to identify request sock when they'll be visible in global ehash table. ireq_state is an alias to req.__req_common.skc_state. Its value is set to TCP_NEW_SYN_RECV Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- include/net/inet_sock.h | 3 +++ 1 file changed, 3 insertions(+) (limited to 'include') diff --git a/include/net/inet_sock.h b/include/net/inet_sock.h index 30f7170abbf3..b31f01de5cd6 100644 --- a/include/net/inet_sock.h +++ b/include/net/inet_sock.h @@ -27,6 +27,7 @@ #include #include #include +#include /** struct ip_options - IP Options * @@ -79,6 +80,7 @@ struct inet_request_sock { #define ir_iif req.__req_common.skc_bound_dev_if #define ir_cookie req.__req_common.skc_cookie #define ireq_net req.__req_common.skc_net +#define ireq_state req.__req_common.skc_state kmemcheck_bitfield_begin(flags); u16 snd_wscale : 4, @@ -250,6 +252,7 @@ static inline struct request_sock *inet_reqsk_alloc(struct request_sock_ops *ops kmemcheck_annotate_bitfield(ireq, flags); ireq->opt = NULL; atomic64_set(&ireq->ir_cookie, 0); + ireq->ireq_state = TCP_NEW_SYN_RECV; } return req; -- cgit v1.2.3 From 1e2e01172fd11b4dbfee746c0c8fbcaa9dbf22a0 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Thu, 12 Mar 2015 16:44:06 -0700 Subject: inet: add rsk_refcnt/ireq_refcnt to request socks When request socks will be in ehash, they'll need to be refcounted. This patch adds rsk_refcnt/ireq_refcnt macros, and adds reqsk_put() function, but nothing yet use them. Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- include/net/inet_sock.h | 1 + include/net/request_sock.h | 8 ++++++++ 2 files changed, 9 insertions(+) (limited to 'include') diff --git a/include/net/inet_sock.h b/include/net/inet_sock.h index b31f01de5cd6..9d6470c16a27 100644 --- a/include/net/inet_sock.h +++ b/include/net/inet_sock.h @@ -81,6 +81,7 @@ struct inet_request_sock { #define ir_cookie req.__req_common.skc_cookie #define ireq_net req.__req_common.skc_net #define ireq_state req.__req_common.skc_state +#define ireq_refcnt req.__req_common.skc_refcnt kmemcheck_bitfield_begin(flags); u16 snd_wscale : 4, diff --git a/include/net/request_sock.h b/include/net/request_sock.h index 7f830ff67f08..e255ecf8bb40 100644 --- a/include/net/request_sock.h +++ b/include/net/request_sock.h @@ -49,6 +49,8 @@ int inet_rtx_syn_ack(struct sock *parent, struct request_sock *req); */ struct request_sock { struct sock_common __req_common; +#define rsk_refcnt __req_common.skc_refcnt + struct request_sock *dl_next; u16 mss; u8 num_retrans; /* number of retransmits */ @@ -86,6 +88,12 @@ static inline void reqsk_free(struct request_sock *req) __reqsk_free(req); } +static inline void reqsk_put(struct request_sock *req) +{ + if (atomic_dec_and_test(&req->rsk_refcnt)) + reqsk_free(req); +} + extern int sysctl_max_syn_backlog; /** struct listen_sock - listen state -- cgit v1.2.3 From 41b822c59e21414d829bcfd00df0c8f7f13b1b95 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Thu, 12 Mar 2015 16:44:08 -0700 Subject: inet: prepare sock_edemux() & sock_gen_put() for new SYN_RECV state sock_edemux() & sock_gen_put() should be ready to cope with request socks. Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- include/net/request_sock.h | 5 +++++ include/net/sock.h | 2 +- net/core/sock.c | 2 ++ net/ipv4/inet_hashtables.c | 2 ++ 4 files changed, 10 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/net/request_sock.h b/include/net/request_sock.h index e255ecf8bb40..3275cf31f731 100644 --- a/include/net/request_sock.h +++ b/include/net/request_sock.h @@ -77,6 +77,11 @@ static inline struct request_sock *reqsk_alloc(const struct request_sock_ops *op return req; } +static inline struct request_sock *inet_reqsk(struct sock *sk) +{ + return (struct request_sock *)sk; +} + static inline void __reqsk_free(struct request_sock *req) { kmem_cache_free(req->rsk_ops->slab, req); diff --git a/include/net/sock.h b/include/net/sock.h index 9411c3421dd3..f10832ca2e90 100644 --- a/include/net/sock.h +++ b/include/net/sock.h @@ -1625,7 +1625,7 @@ static inline void sock_put(struct sock *sk) sk_free(sk); } /* Generic version of sock_put(), dealing with all sockets - * (TCP_TIMEWAIT, ESTABLISHED...) + * (TCP_TIMEWAIT, TCP_NEW_SYN_RECV, ESTABLISHED...) */ void sock_gen_put(struct sock *sk); diff --git a/net/core/sock.c b/net/core/sock.c index 63d871a91b5c..4bc42efb3e40 100644 --- a/net/core/sock.c +++ b/net/core/sock.c @@ -1668,6 +1668,8 @@ void sock_edemux(struct sk_buff *skb) if (sk->sk_state == TCP_TIME_WAIT) inet_twsk_put(inet_twsk(sk)); + else if (sk->sk_state == TCP_NEW_SYN_RECV) + reqsk_put(inet_reqsk(sk)); else sock_put(sk); } diff --git a/net/ipv4/inet_hashtables.c b/net/ipv4/inet_hashtables.c index f6a12b97d12b..64401a2fdd33 100644 --- a/net/ipv4/inet_hashtables.c +++ b/net/ipv4/inet_hashtables.c @@ -262,6 +262,8 @@ void sock_gen_put(struct sock *sk) if (sk->sk_state == TCP_TIME_WAIT) inet_twsk_free(inet_twsk(sk)); + else if (sk->sk_state == TCP_NEW_SYN_RECV) + reqsk_free(inet_reqsk(sk)); else sk_free(sk); } -- cgit v1.2.3 From 3f66b083a5b7f1a63540c24df3679c24f2e935a9 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Thu, 12 Mar 2015 16:44:10 -0700 Subject: inet: introduce ireq_family Before inserting request socks into general hash table, fill their socket family. Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- include/net/inet_sock.h | 1 + net/dccp/ipv4.c | 1 + net/dccp/ipv6.c | 1 + net/ipv4/inet_diag.c | 2 +- net/ipv4/syncookies.c | 1 + net/ipv4/tcp_ipv4.c | 1 + net/ipv6/syncookies.c | 1 + net/ipv6/tcp_ipv6.c | 1 + 8 files changed, 8 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/net/inet_sock.h b/include/net/inet_sock.h index 9d6470c16a27..b3053fdd871e 100644 --- a/include/net/inet_sock.h +++ b/include/net/inet_sock.h @@ -82,6 +82,7 @@ struct inet_request_sock { #define ireq_net req.__req_common.skc_net #define ireq_state req.__req_common.skc_state #define ireq_refcnt req.__req_common.skc_refcnt +#define ireq_family req.__req_common.skc_family kmemcheck_bitfield_begin(flags); u16 snd_wscale : 4, diff --git a/net/dccp/ipv4.c b/net/dccp/ipv4.c index f695874b5ade..8f6f4004daac 100644 --- a/net/dccp/ipv4.c +++ b/net/dccp/ipv4.c @@ -642,6 +642,7 @@ int dccp_v4_conn_request(struct sock *sk, struct sk_buff *skb) ireq->ir_loc_addr = ip_hdr(skb)->daddr; ireq->ir_rmt_addr = ip_hdr(skb)->saddr; write_pnet(&ireq->ireq_net, sock_net(sk)); + ireq->ireq_family = AF_INET; /* * Step 3: Process LISTEN state diff --git a/net/dccp/ipv6.c b/net/dccp/ipv6.c index 703a21acf434..5166b0043f95 100644 --- a/net/dccp/ipv6.c +++ b/net/dccp/ipv6.c @@ -404,6 +404,7 @@ static int dccp_v6_conn_request(struct sock *sk, struct sk_buff *skb) ireq->ir_v6_rmt_addr = ipv6_hdr(skb)->saddr; ireq->ir_v6_loc_addr = ipv6_hdr(skb)->daddr; write_pnet(&ireq->ireq_net, sock_net(sk)); + ireq->ireq_family = AF_INET6; if (ipv6_opt_accepted(sk, skb, IP6CB(skb)) || np->rxopt.bits.rxinfo || np->rxopt.bits.rxoinfo || diff --git a/net/ipv4/inet_diag.c b/net/ipv4/inet_diag.c index c55a6fa3162d..43789c99031f 100644 --- a/net/ipv4/inet_diag.c +++ b/net/ipv4/inet_diag.c @@ -728,7 +728,7 @@ static int inet_diag_fill_req(struct sk_buff *skb, struct sock *sk, return -EMSGSIZE; r = nlmsg_data(nlh); - r->idiag_family = sk->sk_family; + r->idiag_family = ireq->ireq_family; r->idiag_state = TCP_SYN_RECV; r->idiag_timer = 1; r->idiag_retrans = req->num_retrans; diff --git a/net/ipv4/syncookies.c b/net/ipv4/syncookies.c index 18e5a67fda81..0c432730c7b4 100644 --- a/net/ipv4/syncookies.c +++ b/net/ipv4/syncookies.c @@ -347,6 +347,7 @@ struct sock *cookie_v4_check(struct sock *sk, struct sk_buff *skb) treq->snt_synack = tcp_opt.saw_tstamp ? tcp_opt.rcv_tsecr : 0; treq->listener = NULL; write_pnet(&ireq->ireq_net, sock_net(sk)); + ireq->ireq_family = AF_INET; /* We throwed the options of the initial SYN away, so we hope * the ACK carries the same options again (see RFC1122 4.2.3.8) diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index 70b0f701bbdb..1f514a0c5e60 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -1228,6 +1228,7 @@ static void tcp_v4_init_req(struct request_sock *req, struct sock *sk, ireq->ir_rmt_addr = ip_hdr(skb)->saddr; ireq->no_srccheck = inet_sk(sk)->transparent; ireq->opt = tcp_v4_save_options(skb); + ireq->ireq_family = AF_INET; } static struct dst_entry *tcp_v4_route_req(struct sock *sk, struct flowi *fl, diff --git a/net/ipv6/syncookies.c b/net/ipv6/syncookies.c index 66bba6a84e47..58875ce8e178 100644 --- a/net/ipv6/syncookies.c +++ b/net/ipv6/syncookies.c @@ -197,6 +197,7 @@ struct sock *cookie_v6_check(struct sock *sk, struct sk_buff *skb) treq = tcp_rsk(req); treq->listener = NULL; write_pnet(&ireq->ireq_net, sock_net(sk)); + ireq->ireq_family = AF_INET6; if (security_inet_conn_request(sk, skb, req)) goto out_free; diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c index 1ccfede7d55f..c5fc6a5e4adc 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c @@ -749,6 +749,7 @@ static void tcp_v6_init_req(struct request_sock *req, struct sock *sk, atomic_inc(&skb->users); ireq->pktopts = skb; } + ireq->ireq_family = AF_INET6; } static struct dst_entry *tcp_v6_route_req(struct sock *sk, struct flowi *fl, -- cgit v1.2.3 From a5b6846f9e1a080493210013385c28faecee36f0 Mon Sep 17 00:00:00 2001 From: Daniel Borkmann Date: Thu, 12 Mar 2015 15:28:40 +0100 Subject: rhashtable: kill ht->shift atomic operations Commit c0c09bfdc415 ("rhashtable: avoid unnecessary wakeup for worker queue") changed ht->shift to be atomic, which is actually unnecessary. Instead of leaving the current shift in the core rhashtable structure, it can be cached inside the individual bucket tables. There, it will only be initialized once during a new table allocation in the shrink/expansion slow path, and from then onward it stays immutable for the rest of the bucket table liftime. That allows shift to be non-atomic. The patch also moves hash_rnd management into the table setup. The rhashtable structure now consumes 3 instead of 4 cachelines. Signed-off-by: Daniel Borkmann Cc: Ying Xue Acked-by: Thomas Graf Signed-off-by: David S. Miller --- include/linux/rhashtable.h | 6 ++--- lib/rhashtable.c | 55 +++++++++++++++++++++------------------------- 2 files changed, 28 insertions(+), 33 deletions(-) (limited to 'include') diff --git a/include/linux/rhashtable.h b/include/linux/rhashtable.h index 5ef8ea551556..c93ff8ac474a 100644 --- a/include/linux/rhashtable.h +++ b/include/linux/rhashtable.h @@ -50,6 +50,7 @@ struct rhash_head { * struct bucket_table - Table of hash buckets * @size: Number of hash buckets * @hash_rnd: Random seed to fold into hash + * @shift: Current size (1 << shift) * @locks_mask: Mask to apply before accessing locks[] * @locks: Array of spinlocks protecting individual buckets * @buckets: size * hash buckets @@ -57,6 +58,7 @@ struct rhash_head { struct bucket_table { size_t size; u32 hash_rnd; + u32 shift; unsigned int locks_mask; spinlock_t *locks; @@ -99,7 +101,6 @@ struct rhashtable_params { * @tbl: Bucket table * @future_tbl: Table under construction during expansion/shrinking * @nelems: Number of elements in table - * @shift: Current size (1 << shift) * @p: Configuration parameters * @run_work: Deferred worker to expand/shrink asynchronously * @mutex: Mutex to protect current/future table swapping @@ -110,12 +111,11 @@ struct rhashtable { struct bucket_table __rcu *tbl; struct bucket_table __rcu *future_tbl; atomic_t nelems; - atomic_t shift; + bool being_destroyed; struct rhashtable_params p; struct work_struct run_work; struct mutex mutex; struct list_head walkers; - bool being_destroyed; }; /** diff --git a/lib/rhashtable.c b/lib/rhashtable.c index 68210cc2bab8..adea791ea3ab 100644 --- a/lib/rhashtable.c +++ b/lib/rhashtable.c @@ -147,7 +147,7 @@ static void bucket_table_free(const struct bucket_table *tbl) } static struct bucket_table *bucket_table_alloc(struct rhashtable *ht, - size_t nbuckets) + size_t nbuckets, u32 hash_rnd) { struct bucket_table *tbl = NULL; size_t size; @@ -162,6 +162,8 @@ static struct bucket_table *bucket_table_alloc(struct rhashtable *ht, return NULL; tbl->size = nbuckets; + tbl->shift = ilog2(nbuckets); + tbl->hash_rnd = hash_rnd; if (alloc_bucket_locks(ht, tbl) < 0) { bucket_table_free(tbl); @@ -177,25 +179,27 @@ static struct bucket_table *bucket_table_alloc(struct rhashtable *ht, /** * rht_grow_above_75 - returns true if nelems > 0.75 * table-size * @ht: hash table - * @new_size: new table size + * @tbl: current table */ -static bool rht_grow_above_75(const struct rhashtable *ht, size_t new_size) +static bool rht_grow_above_75(const struct rhashtable *ht, + const struct bucket_table *tbl) { /* Expand table when exceeding 75% load */ - return atomic_read(&ht->nelems) > (new_size / 4 * 3) && - (!ht->p.max_shift || atomic_read(&ht->shift) < ht->p.max_shift); + return atomic_read(&ht->nelems) > (tbl->size / 4 * 3) && + (!ht->p.max_shift || tbl->shift < ht->p.max_shift); } /** * rht_shrink_below_30 - returns true if nelems < 0.3 * table-size * @ht: hash table - * @new_size: new table size + * @tbl: current table */ -static bool rht_shrink_below_30(const struct rhashtable *ht, size_t new_size) +static bool rht_shrink_below_30(const struct rhashtable *ht, + const struct bucket_table *tbl) { /* Shrink table beneath 30% load */ - return atomic_read(&ht->nelems) < (new_size * 3 / 10) && - (atomic_read(&ht->shift) > ht->p.min_shift); + return atomic_read(&ht->nelems) < (tbl->size * 3 / 10) && + tbl->shift > ht->p.min_shift; } static int rhashtable_rehash_one(struct rhashtable *ht, unsigned old_hash) @@ -310,16 +314,11 @@ int rhashtable_expand(struct rhashtable *ht) ASSERT_RHT_MUTEX(ht); - new_tbl = bucket_table_alloc(ht, old_tbl->size * 2); + new_tbl = bucket_table_alloc(ht, old_tbl->size * 2, old_tbl->hash_rnd); if (new_tbl == NULL) return -ENOMEM; - new_tbl->hash_rnd = old_tbl->hash_rnd; - - atomic_inc(&ht->shift); - rhashtable_rehash(ht, new_tbl); - return 0; } EXPORT_SYMBOL_GPL(rhashtable_expand); @@ -342,20 +341,15 @@ EXPORT_SYMBOL_GPL(rhashtable_expand); */ int rhashtable_shrink(struct rhashtable *ht) { - struct bucket_table *new_tbl, *tbl = rht_dereference(ht->tbl, ht); + struct bucket_table *new_tbl, *old_tbl = rht_dereference(ht->tbl, ht); ASSERT_RHT_MUTEX(ht); - new_tbl = bucket_table_alloc(ht, tbl->size / 2); + new_tbl = bucket_table_alloc(ht, old_tbl->size / 2, old_tbl->hash_rnd); if (new_tbl == NULL) return -ENOMEM; - new_tbl->hash_rnd = tbl->hash_rnd; - - atomic_dec(&ht->shift); - rhashtable_rehash(ht, new_tbl); - return 0; } EXPORT_SYMBOL_GPL(rhashtable_shrink); @@ -376,9 +370,9 @@ static void rht_deferred_worker(struct work_struct *work) list_for_each_entry(walker, &ht->walkers, list) walker->resize = true; - if (rht_grow_above_75(ht, tbl->size)) + if (rht_grow_above_75(ht, tbl)) rhashtable_expand(ht); - else if (rht_shrink_below_30(ht, tbl->size)) + else if (rht_shrink_below_30(ht, tbl)) rhashtable_shrink(ht); unlock: mutex_unlock(&ht->mutex); @@ -431,7 +425,7 @@ static bool __rhashtable_insert(struct rhashtable *ht, struct rhash_head *obj, rcu_assign_pointer(tbl->buckets[hash], obj); atomic_inc(&ht->nelems); - if (no_resize_running && rht_grow_above_75(ht, tbl->size)) + if (no_resize_running && rht_grow_above_75(ht, tbl)) schedule_work(&ht->run_work); exit: @@ -539,7 +533,7 @@ bool rhashtable_remove(struct rhashtable *ht, struct rhash_head *obj) bool no_resize_running = tbl == old_tbl; atomic_dec(&ht->nelems); - if (no_resize_running && rht_shrink_below_30(ht, tbl->size)) + if (no_resize_running && rht_shrink_below_30(ht, tbl)) schedule_work(&ht->run_work); } @@ -913,6 +907,7 @@ int rhashtable_init(struct rhashtable *ht, struct rhashtable_params *params) { struct bucket_table *tbl; size_t size; + u32 hash_rnd; size = HASH_DEFAULT_SIZE; @@ -939,14 +934,14 @@ int rhashtable_init(struct rhashtable *ht, struct rhashtable_params *params) else ht->p.locks_mul = BUCKET_LOCKS_PER_CPU; - tbl = bucket_table_alloc(ht, size); + get_random_bytes(&hash_rnd, sizeof(hash_rnd)); + + tbl = bucket_table_alloc(ht, size, hash_rnd); if (tbl == NULL) return -ENOMEM; - get_random_bytes(&tbl->hash_rnd, sizeof(tbl->hash_rnd)); - atomic_set(&ht->nelems, 0); - atomic_set(&ht->shift, ilog2(tbl->size)); + RCU_INIT_POINTER(ht->tbl, tbl); RCU_INIT_POINTER(ht->future_tbl, tbl); -- cgit v1.2.3 From 66ee59af630fd8d5f4f56fb28162857e629aa0ab Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Wed, 11 Feb 2015 19:56:46 +0100 Subject: fs: remove ki_nbytes There is no need to pass the total request length in the kiocb, as we already get passed in through the iov_iter argument. Signed-off-by: Christoph Hellwig Signed-off-by: Al Viro --- fs/aio.c | 34 ++++++++++++++++++---------------- fs/ceph/file.c | 2 +- fs/nfs/direct.c | 2 +- fs/ocfs2/file.c | 8 +++----- fs/read_write.c | 8 -------- fs/udf/file.c | 2 +- include/linux/aio.h | 1 - kernel/printk/printk.c | 2 +- mm/page_io.c | 1 - net/socket.c | 6 +++--- 10 files changed, 28 insertions(+), 38 deletions(-) (limited to 'include') diff --git a/fs/aio.c b/fs/aio.c index 118a2e0088d8..667054c7c067 100644 --- a/fs/aio.c +++ b/fs/aio.c @@ -1344,12 +1344,13 @@ typedef ssize_t (rw_iter_op)(struct kiocb *, struct iov_iter *); static ssize_t aio_setup_vectored_rw(struct kiocb *kiocb, int rw, char __user *buf, unsigned long *nr_segs, + size_t *len, struct iovec **iovec, bool compat) { ssize_t ret; - *nr_segs = kiocb->ki_nbytes; + *nr_segs = *len; #ifdef CONFIG_COMPAT if (compat) @@ -1364,21 +1365,22 @@ static ssize_t aio_setup_vectored_rw(struct kiocb *kiocb, if (ret < 0) return ret; - /* ki_nbytes now reflect bytes instead of segs */ - kiocb->ki_nbytes = ret; + /* len now reflect bytes instead of segs */ + *len = ret; return 0; } static ssize_t aio_setup_single_vector(struct kiocb *kiocb, int rw, char __user *buf, unsigned long *nr_segs, + size_t len, struct iovec *iovec) { - if (unlikely(!access_ok(!rw, buf, kiocb->ki_nbytes))) + if (unlikely(!access_ok(!rw, buf, len))) return -EFAULT; iovec->iov_base = buf; - iovec->iov_len = kiocb->ki_nbytes; + iovec->iov_len = len; *nr_segs = 1; return 0; } @@ -1388,7 +1390,7 @@ static ssize_t aio_setup_single_vector(struct kiocb *kiocb, * Performs the initial checks and io submission. */ static ssize_t aio_run_iocb(struct kiocb *req, unsigned opcode, - char __user *buf, bool compat) + char __user *buf, size_t len, bool compat) { struct file *file = req->ki_filp; ssize_t ret; @@ -1423,21 +1425,21 @@ rw_common: if (!rw_op && !iter_op) return -EINVAL; - ret = (opcode == IOCB_CMD_PREADV || - opcode == IOCB_CMD_PWRITEV) - ? aio_setup_vectored_rw(req, rw, buf, &nr_segs, - &iovec, compat) - : aio_setup_single_vector(req, rw, buf, &nr_segs, - iovec); + if (opcode == IOCB_CMD_PREADV || opcode == IOCB_CMD_PWRITEV) + ret = aio_setup_vectored_rw(req, rw, buf, &nr_segs, + &len, &iovec, compat); + else + ret = aio_setup_single_vector(req, rw, buf, &nr_segs, + len, iovec); if (!ret) - ret = rw_verify_area(rw, file, &req->ki_pos, req->ki_nbytes); + ret = rw_verify_area(rw, file, &req->ki_pos, len); if (ret < 0) { if (iovec != inline_vecs) kfree(iovec); return ret; } - req->ki_nbytes = ret; + len = ret; /* XXX: move/kill - rw_verify_area()? */ /* This matches the pread()/pwrite() logic */ @@ -1450,7 +1452,7 @@ rw_common: file_start_write(file); if (iter_op) { - iov_iter_init(&iter, rw, iovec, nr_segs, req->ki_nbytes); + iov_iter_init(&iter, rw, iovec, nr_segs, len); ret = iter_op(req, &iter); } else { ret = rw_op(req, iovec, nr_segs, req->ki_pos); @@ -1553,10 +1555,10 @@ static int io_submit_one(struct kioctx *ctx, struct iocb __user *user_iocb, req->ki_obj.user = user_iocb; req->ki_user_data = iocb->aio_data; req->ki_pos = iocb->aio_offset; - req->ki_nbytes = iocb->aio_nbytes; ret = aio_run_iocb(req, iocb->aio_lio_opcode, (char __user *)(unsigned long)iocb->aio_buf, + iocb->aio_nbytes, compat); if (ret) goto out_put_req; diff --git a/fs/ceph/file.c b/fs/ceph/file.c index 905986dd4c3c..081c4e3f9e49 100644 --- a/fs/ceph/file.c +++ b/fs/ceph/file.c @@ -807,7 +807,7 @@ static ssize_t ceph_read_iter(struct kiocb *iocb, struct iov_iter *to) { struct file *filp = iocb->ki_filp; struct ceph_file_info *fi = filp->private_data; - size_t len = iocb->ki_nbytes; + size_t len = iov_iter_count(to); struct inode *inode = file_inode(filp); struct ceph_inode_info *ci = ceph_inode(inode); struct page *pinned_page = NULL; diff --git a/fs/nfs/direct.c b/fs/nfs/direct.c index 7077521acdf4..27cebf164070 100644 --- a/fs/nfs/direct.c +++ b/fs/nfs/direct.c @@ -265,7 +265,7 @@ ssize_t nfs_direct_IO(int rw, struct kiocb *iocb, struct iov_iter *iter, loff_t return -EINVAL; #else - VM_BUG_ON(iocb->ki_nbytes != PAGE_SIZE); + VM_BUG_ON(iov_iter_count(iter) != PAGE_SIZE); if (rw == READ) return nfs_file_direct_read(iocb, iter, pos); diff --git a/fs/ocfs2/file.c b/fs/ocfs2/file.c index 46e0d4e857c7..266845de2100 100644 --- a/fs/ocfs2/file.c +++ b/fs/ocfs2/file.c @@ -2280,7 +2280,7 @@ static ssize_t ocfs2_file_write_iter(struct kiocb *iocb, file->f_path.dentry->d_name.name, (unsigned int)from->nr_segs); /* GRRRRR */ - if (iocb->ki_nbytes == 0) + if (count == 0) return 0; appending = file->f_flags & O_APPEND ? 1 : 0; @@ -2330,8 +2330,7 @@ relock: } can_do_direct = direct_io; - ret = ocfs2_prepare_inode_for_write(file, ppos, - iocb->ki_nbytes, appending, + ret = ocfs2_prepare_inode_for_write(file, ppos, count, appending, &can_do_direct, &has_refcount); if (ret < 0) { mlog_errno(ret); @@ -2339,8 +2338,7 @@ relock: } if (direct_io && !is_sync_kiocb(iocb)) - unaligned_dio = ocfs2_is_io_unaligned(inode, iocb->ki_nbytes, - *ppos); + unaligned_dio = ocfs2_is_io_unaligned(inode, count, *ppos); /* * We can't complete the direct I/O as requested, fall back to diff --git a/fs/read_write.c b/fs/read_write.c index 8e1b68786d66..f8b8fc1316ab 100644 --- a/fs/read_write.c +++ b/fs/read_write.c @@ -343,7 +343,6 @@ ssize_t vfs_iter_read(struct file *file, struct iov_iter *iter, loff_t *ppos) init_sync_kiocb(&kiocb, file); kiocb.ki_pos = *ppos; - kiocb.ki_nbytes = iov_iter_count(iter); iter->type |= READ; ret = file->f_op->read_iter(&kiocb, iter); @@ -366,7 +365,6 @@ ssize_t vfs_iter_write(struct file *file, struct iov_iter *iter, loff_t *ppos) init_sync_kiocb(&kiocb, file); kiocb.ki_pos = *ppos; - kiocb.ki_nbytes = iov_iter_count(iter); iter->type |= WRITE; ret = file->f_op->write_iter(&kiocb, iter); @@ -426,7 +424,6 @@ ssize_t do_sync_read(struct file *filp, char __user *buf, size_t len, loff_t *pp init_sync_kiocb(&kiocb, filp); kiocb.ki_pos = *ppos; - kiocb.ki_nbytes = len; ret = filp->f_op->aio_read(&kiocb, &iov, 1, kiocb.ki_pos); if (-EIOCBQUEUED == ret) @@ -446,7 +443,6 @@ ssize_t new_sync_read(struct file *filp, char __user *buf, size_t len, loff_t *p init_sync_kiocb(&kiocb, filp); kiocb.ki_pos = *ppos; - kiocb.ki_nbytes = len; iov_iter_init(&iter, READ, &iov, 1, len); ret = filp->f_op->read_iter(&kiocb, &iter); @@ -510,7 +506,6 @@ ssize_t do_sync_write(struct file *filp, const char __user *buf, size_t len, lof init_sync_kiocb(&kiocb, filp); kiocb.ki_pos = *ppos; - kiocb.ki_nbytes = len; ret = filp->f_op->aio_write(&kiocb, &iov, 1, kiocb.ki_pos); if (-EIOCBQUEUED == ret) @@ -530,7 +525,6 @@ ssize_t new_sync_write(struct file *filp, const char __user *buf, size_t len, lo init_sync_kiocb(&kiocb, filp); kiocb.ki_pos = *ppos; - kiocb.ki_nbytes = len; iov_iter_init(&iter, WRITE, &iov, 1, len); ret = filp->f_op->write_iter(&kiocb, &iter); @@ -719,7 +713,6 @@ static ssize_t do_iter_readv_writev(struct file *filp, int rw, const struct iove init_sync_kiocb(&kiocb, filp); kiocb.ki_pos = *ppos; - kiocb.ki_nbytes = len; iov_iter_init(&iter, rw, iov, nr_segs, len); ret = fn(&kiocb, &iter); @@ -737,7 +730,6 @@ static ssize_t do_sync_readv_writev(struct file *filp, const struct iovec *iov, init_sync_kiocb(&kiocb, filp); kiocb.ki_pos = *ppos; - kiocb.ki_nbytes = len; ret = fn(&kiocb, iov, nr_segs, kiocb.ki_pos); if (ret == -EIOCBQUEUED) diff --git a/fs/udf/file.c b/fs/udf/file.c index 08f3555fbeac..9c0b6da9dbb3 100644 --- a/fs/udf/file.c +++ b/fs/udf/file.c @@ -122,7 +122,7 @@ static ssize_t udf_file_write_iter(struct kiocb *iocb, struct iov_iter *from) struct file *file = iocb->ki_filp; struct inode *inode = file_inode(file); int err, pos; - size_t count = iocb->ki_nbytes; + size_t count = iov_iter_count(from); struct udf_inode_info *iinfo = UDF_I(inode); mutex_lock(&inode->i_mutex); diff --git a/include/linux/aio.h b/include/linux/aio.h index d9c92daa3944..132d1ecba435 100644 --- a/include/linux/aio.h +++ b/include/linux/aio.h @@ -42,7 +42,6 @@ struct kiocb { __u64 ki_user_data; /* user's data for completion */ loff_t ki_pos; - size_t ki_nbytes; /* copy of iocb->aio_nbytes */ struct list_head ki_list; /* the aio core uses this * for cancellation */ diff --git a/kernel/printk/printk.c b/kernel/printk/printk.c index c06df7de0963..60b2aa2a2da9 100644 --- a/kernel/printk/printk.c +++ b/kernel/printk/printk.c @@ -521,7 +521,7 @@ static ssize_t devkmsg_write(struct kiocb *iocb, struct iov_iter *from) int i; int level = default_message_loglevel; int facility = 1; /* LOG_USER */ - size_t len = iocb->ki_nbytes; + size_t len = iov_iter_count(from); ssize_t ret = len; if (len > LOG_LINE_MAX) diff --git a/mm/page_io.c b/mm/page_io.c index e6045804c8d8..7ef21577856c 100644 --- a/mm/page_io.c +++ b/mm/page_io.c @@ -274,7 +274,6 @@ int __swap_writepage(struct page *page, struct writeback_control *wbc, iov_iter_bvec(&from, ITER_BVEC | WRITE, &bv, 1, PAGE_SIZE); init_sync_kiocb(&kiocb, swap_file); kiocb.ki_pos = page_file_offset(page); - kiocb.ki_nbytes = PAGE_SIZE; set_page_writeback(page); unlock_page(page); diff --git a/net/socket.c b/net/socket.c index bbedbfcb42c2..f92145554f34 100644 --- a/net/socket.c +++ b/net/socket.c @@ -858,11 +858,11 @@ static ssize_t sock_read_iter(struct kiocb *iocb, struct iov_iter *to) if (iocb->ki_pos != 0) return -ESPIPE; - if (iocb->ki_nbytes == 0) /* Match SYS5 behaviour */ + if (!iov_iter_count(to)) /* Match SYS5 behaviour */ return 0; res = __sock_recvmsg(iocb, sock, &msg, - iocb->ki_nbytes, msg.msg_flags); + iov_iter_count(to), msg.msg_flags); *to = msg.msg_iter; return res; } @@ -883,7 +883,7 @@ static ssize_t sock_write_iter(struct kiocb *iocb, struct iov_iter *from) if (sock->type == SOCK_SEQPACKET) msg.msg_flags |= MSG_EOR; - res = __sock_sendmsg(iocb, sock, &msg, iocb->ki_nbytes); + res = __sock_sendmsg(iocb, sock, &msg, iov_iter_count(from)); *from = msg.msg_iter; return res; } -- cgit v1.2.3 From 098a697b497e3154a1a583c1d34c67568acaadcc Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Fri, 13 Mar 2015 00:07:44 -0500 Subject: tcp_metrics: Use a single hash table for all network namespaces. Now that all of the operations are safe on a single hash table accross network namespaces, allocate a single global hash table and update the code to use it. Signed-off-by: "Eric W. Biederman" Signed-off-by: David S. Miller --- include/net/netns/ipv4.h | 2 -- net/ipv4/tcp_metrics.c | 66 ++++++++++++++++++++++-------------------------- 2 files changed, 30 insertions(+), 38 deletions(-) (limited to 'include') diff --git a/include/net/netns/ipv4.h b/include/net/netns/ipv4.h index 8f3a1a1a5a94..614a49be68a9 100644 --- a/include/net/netns/ipv4.h +++ b/include/net/netns/ipv4.h @@ -54,8 +54,6 @@ struct netns_ipv4 { struct sock *mc_autojoin_sk; struct inet_peer_base *peers; - struct tcpm_hash_bucket *tcp_metrics_hash; - unsigned int tcp_metrics_hash_log; struct sock * __percpu *tcp_sk; struct netns_frags frags; #ifdef CONFIG_NETFILTER diff --git a/net/ipv4/tcp_metrics.c b/net/ipv4/tcp_metrics.c index baccb070427d..366728cbee4a 100644 --- a/net/ipv4/tcp_metrics.c +++ b/net/ipv4/tcp_metrics.c @@ -97,6 +97,9 @@ struct tcpm_hash_bucket { struct tcp_metrics_block __rcu *chain; }; +static struct tcpm_hash_bucket *tcp_metrics_hash __read_mostly; +static unsigned int tcp_metrics_hash_log __read_mostly; + static DEFINE_SPINLOCK(tcp_metrics_lock); static void tcpm_suck_dst(struct tcp_metrics_block *tm, @@ -177,7 +180,7 @@ static struct tcp_metrics_block *tcpm_new(struct dst_entry *dst, if (unlikely(reclaim)) { struct tcp_metrics_block *oldest; - oldest = rcu_dereference(net->ipv4.tcp_metrics_hash[hash].chain); + oldest = rcu_dereference(tcp_metrics_hash[hash].chain); for (tm = rcu_dereference(oldest->tcpm_next); tm; tm = rcu_dereference(tm->tcpm_next)) { if (time_before(tm->tcpm_stamp, oldest->tcpm_stamp)) @@ -196,8 +199,8 @@ static struct tcp_metrics_block *tcpm_new(struct dst_entry *dst, tcpm_suck_dst(tm, dst, true); if (likely(!reclaim)) { - tm->tcpm_next = net->ipv4.tcp_metrics_hash[hash].chain; - rcu_assign_pointer(net->ipv4.tcp_metrics_hash[hash].chain, tm); + tm->tcpm_next = tcp_metrics_hash[hash].chain; + rcu_assign_pointer(tcp_metrics_hash[hash].chain, tm); } out_unlock: @@ -221,7 +224,7 @@ static struct tcp_metrics_block *__tcp_get_metrics(const struct inetpeer_addr *s struct tcp_metrics_block *tm; int depth = 0; - for (tm = rcu_dereference(net->ipv4.tcp_metrics_hash[hash].chain); tm; + for (tm = rcu_dereference(tcp_metrics_hash[hash].chain); tm; tm = rcu_dereference(tm->tcpm_next)) { if (addr_same(&tm->tcpm_saddr, saddr) && addr_same(&tm->tcpm_daddr, daddr) && @@ -261,9 +264,9 @@ static struct tcp_metrics_block *__tcp_get_metrics_req(struct request_sock *req, net = dev_net(dst->dev); hash ^= net_hash_mix(net); - hash = hash_32(hash, net->ipv4.tcp_metrics_hash_log); + hash = hash_32(hash, tcp_metrics_hash_log); - for (tm = rcu_dereference(net->ipv4.tcp_metrics_hash[hash].chain); tm; + for (tm = rcu_dereference(tcp_metrics_hash[hash].chain); tm; tm = rcu_dereference(tm->tcpm_next)) { if (addr_same(&tm->tcpm_saddr, &saddr) && addr_same(&tm->tcpm_daddr, &daddr) && @@ -310,9 +313,9 @@ static struct tcp_metrics_block *__tcp_get_metrics_tw(struct inet_timewait_sock net = twsk_net(tw); hash ^= net_hash_mix(net); - hash = hash_32(hash, net->ipv4.tcp_metrics_hash_log); + hash = hash_32(hash, tcp_metrics_hash_log); - for (tm = rcu_dereference(net->ipv4.tcp_metrics_hash[hash].chain); tm; + for (tm = rcu_dereference(tcp_metrics_hash[hash].chain); tm; tm = rcu_dereference(tm->tcpm_next)) { if (addr_same(&tm->tcpm_saddr, &saddr) && addr_same(&tm->tcpm_daddr, &daddr) && @@ -360,7 +363,7 @@ static struct tcp_metrics_block *tcp_get_metrics(struct sock *sk, net = dev_net(dst->dev); hash ^= net_hash_mix(net); - hash = hash_32(hash, net->ipv4.tcp_metrics_hash_log); + hash = hash_32(hash, tcp_metrics_hash_log); tm = __tcp_get_metrics(&saddr, &daddr, net, hash); if (tm == TCP_METRICS_RECLAIM_PTR) @@ -911,13 +914,13 @@ static int tcp_metrics_nl_dump(struct sk_buff *skb, struct netlink_callback *cb) { struct net *net = sock_net(skb->sk); - unsigned int max_rows = 1U << net->ipv4.tcp_metrics_hash_log; + unsigned int max_rows = 1U << tcp_metrics_hash_log; unsigned int row, s_row = cb->args[0]; int s_col = cb->args[1], col = s_col; for (row = s_row; row < max_rows; row++, s_col = 0) { struct tcp_metrics_block *tm; - struct tcpm_hash_bucket *hb = net->ipv4.tcp_metrics_hash + row; + struct tcpm_hash_bucket *hb = tcp_metrics_hash + row; rcu_read_lock(); for (col = 0, tm = rcu_dereference(hb->chain); tm; @@ -1010,10 +1013,10 @@ static int tcp_metrics_nl_cmd_get(struct sk_buff *skb, struct genl_info *info) goto nla_put_failure; hash ^= net_hash_mix(net); - hash = hash_32(hash, net->ipv4.tcp_metrics_hash_log); + hash = hash_32(hash, tcp_metrics_hash_log); ret = -ESRCH; rcu_read_lock(); - for (tm = rcu_dereference(net->ipv4.tcp_metrics_hash[hash].chain); tm; + for (tm = rcu_dereference(tcp_metrics_hash[hash].chain); tm; tm = rcu_dereference(tm->tcpm_next)) { if (addr_same(&tm->tcpm_daddr, &daddr) && (!src || addr_same(&tm->tcpm_saddr, &saddr)) && @@ -1045,8 +1048,8 @@ out_free: static void tcp_metrics_flush_all(struct net *net) { - unsigned int max_rows = 1U << net->ipv4.tcp_metrics_hash_log; - struct tcpm_hash_bucket *hb = net->ipv4.tcp_metrics_hash; + unsigned int max_rows = 1U << tcp_metrics_hash_log; + struct tcpm_hash_bucket *hb = tcp_metrics_hash; struct tcp_metrics_block *tm; unsigned int row; @@ -1090,8 +1093,8 @@ static int tcp_metrics_nl_cmd_del(struct sk_buff *skb, struct genl_info *info) src = false; hash ^= net_hash_mix(net); - hash = hash_32(hash, net->ipv4.tcp_metrics_hash_log); - hb = net->ipv4.tcp_metrics_hash + hash; + hash = hash_32(hash, tcp_metrics_hash_log); + hb = tcp_metrics_hash + hash; pp = &hb->chain; spin_lock_bh(&tcp_metrics_lock); for (tm = deref_locked_genl(*pp); tm; tm = deref_locked_genl(*pp)) { @@ -1147,6 +1150,9 @@ static int __net_init tcp_net_metrics_init(struct net *net) size_t size; unsigned int slots; + if (!net_eq(net, &init_net)) + return 0; + slots = tcpmhash_entries; if (!slots) { if (totalram_pages >= 128 * 1024) @@ -1155,14 +1161,14 @@ static int __net_init tcp_net_metrics_init(struct net *net) slots = 8 * 1024; } - net->ipv4.tcp_metrics_hash_log = order_base_2(slots); - size = sizeof(struct tcpm_hash_bucket) << net->ipv4.tcp_metrics_hash_log; + tcp_metrics_hash_log = order_base_2(slots); + size = sizeof(struct tcpm_hash_bucket) << tcp_metrics_hash_log; - net->ipv4.tcp_metrics_hash = kzalloc(size, GFP_KERNEL | __GFP_NOWARN); - if (!net->ipv4.tcp_metrics_hash) - net->ipv4.tcp_metrics_hash = vzalloc(size); + tcp_metrics_hash = kzalloc(size, GFP_KERNEL | __GFP_NOWARN); + if (!tcp_metrics_hash) + tcp_metrics_hash = vzalloc(size); - if (!net->ipv4.tcp_metrics_hash) + if (!tcp_metrics_hash) return -ENOMEM; return 0; @@ -1170,19 +1176,7 @@ static int __net_init tcp_net_metrics_init(struct net *net) static void __net_exit tcp_net_metrics_exit(struct net *net) { - unsigned int i; - - for (i = 0; i < (1U << net->ipv4.tcp_metrics_hash_log) ; i++) { - struct tcp_metrics_block *tm, *next; - - tm = rcu_dereference_protected(net->ipv4.tcp_metrics_hash[i].chain, 1); - while (tm) { - next = rcu_dereference_protected(tm->tcpm_next, 1); - kfree(tm); - tm = next; - } - } - kvfree(net->ipv4.tcp_metrics_hash); + tcp_metrics_flush_all(net); } static __net_initdata struct pernet_operations tcp_net_metrics_ops = { -- cgit v1.2.3 From cc91cb042ce5dab66fedeb94260b18f193ed33d9 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Thu, 12 Mar 2015 22:30:58 -0700 Subject: Bluetooth: Add support connectable advertising setting The patch adds a second advertising setting that allows switching of the controller into connectable mode independent of the global connectable setting. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- include/net/bluetooth/hci.h | 1 + net/bluetooth/mgmt.c | 39 ++++++++++++++++++++++++++++++--------- 2 files changed, 31 insertions(+), 9 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index f76f45ae76c3..0f3413b285a5 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -217,6 +217,7 @@ enum { HCI_HS_ENABLED, HCI_LE_ENABLED, HCI_ADVERTISING, + HCI_ADVERTISING_CONNECTABLE, HCI_CONNECTABLE, HCI_DISCOVERABLE, HCI_LIMITED_DISCOVERABLE, diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 025f29bf1f1a..0761f2e0fefa 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -1110,7 +1110,10 @@ static void enable_advertising(struct hci_request *req) */ clear_bit(HCI_LE_ADV, &hdev->dev_flags); - connectable = get_connectable(hdev); + if (test_bit(HCI_ADVERTISING_CONNECTABLE, &hdev->dev_flags)) + connectable = true; + else + connectable = get_connectable(hdev); /* Set require_privacy to true only when non-connectable * advertising is used. In that case it is fine to use a @@ -4430,7 +4433,7 @@ static int set_advertising(struct sock *sk, struct hci_dev *hdev, void *data, struct mgmt_mode *cp = data; struct mgmt_pending_cmd *cmd; struct hci_request req; - u8 val, enabled, status; + u8 val, status; int err; BT_DBG("request for %s", hdev->name); @@ -4440,29 +4443,42 @@ static int set_advertising(struct sock *sk, struct hci_dev *hdev, void *data, return mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_ADVERTISING, status); - if (cp->val != 0x00 && cp->val != 0x01) + if (cp->val != 0x00 && cp->val != 0x01 && cp->val != 0x02) return mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_ADVERTISING, MGMT_STATUS_INVALID_PARAMS); hci_dev_lock(hdev); val = !!cp->val; - enabled = test_bit(HCI_ADVERTISING, &hdev->dev_flags); /* The following conditions are ones which mean that we should * not do any HCI communication but directly send a mgmt * response to user space (after toggling the flag if * necessary). */ - if (!hdev_is_powered(hdev) || val == enabled || + if (!hdev_is_powered(hdev) || + (val == test_bit(HCI_ADVERTISING, &hdev->dev_flags) && + (cp->val == 0x02) == test_bit(HCI_ADVERTISING_CONNECTABLE, + &hdev->dev_flags)) || hci_conn_num(hdev, LE_LINK) > 0 || (test_bit(HCI_LE_SCAN, &hdev->dev_flags) && hdev->le_scan_type == LE_SCAN_ACTIVE)) { - bool changed = false; + bool changed; - if (val != test_bit(HCI_ADVERTISING, &hdev->dev_flags)) { - change_bit(HCI_ADVERTISING, &hdev->dev_flags); - changed = true; + if (cp->val) { + changed = !test_and_set_bit(HCI_ADVERTISING, + &hdev->dev_flags); + if (cp->val == 0x02) + set_bit(HCI_ADVERTISING_CONNECTABLE, + &hdev->dev_flags); + else + clear_bit(HCI_ADVERTISING_CONNECTABLE, + &hdev->dev_flags); + } else { + changed = test_and_clear_bit(HCI_ADVERTISING, + &hdev->dev_flags); + clear_bit(HCI_ADVERTISING_CONNECTABLE, + &hdev->dev_flags); } err = send_settings_rsp(sk, MGMT_OP_SET_ADVERTISING, hdev); @@ -4490,6 +4506,11 @@ static int set_advertising(struct sock *sk, struct hci_dev *hdev, void *data, hci_req_init(&req, hdev); + if (cp->val == 0x02) + set_bit(HCI_ADVERTISING_CONNECTABLE, &hdev->dev_flags); + else + clear_bit(HCI_ADVERTISING_CONNECTABLE, &hdev->dev_flags); + if (val) enable_advertising(&req); else -- cgit v1.2.3 From d7a5a11d7fa80beb43d5f7cb421c86f9b4d21200 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Fri, 13 Mar 2015 02:11:00 -0700 Subject: Bluetooth: Introduce hci_dev_test_flag helper macro Instead of manually coding test_bit on hdev->dev_flags all the time, use hci_dev_test_flag helper macro. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- include/net/bluetooth/hci_core.h | 14 +-- net/bluetooth/hci_conn.c | 14 +-- net/bluetooth/hci_core.c | 120 ++++++++++----------- net/bluetooth/hci_debugfs.c | 4 +- net/bluetooth/hci_event.c | 92 ++++++++-------- net/bluetooth/hci_request.c | 28 ++--- net/bluetooth/hci_sock.c | 8 +- net/bluetooth/l2cap_core.c | 8 +- net/bluetooth/mgmt.c | 224 +++++++++++++++++++-------------------- net/bluetooth/smp.c | 44 ++++---- 10 files changed, 278 insertions(+), 278 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 5cc5a192359d..05e95a75aba5 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -502,6 +502,8 @@ extern struct list_head hci_cb_list; extern rwlock_t hci_dev_list_lock; extern struct mutex hci_cb_list_lock; +#define hci_dev_test_flag(hdev, nr) test_bit((nr), &(hdev)->dev_flags) + /* ----- HCI interface to upper protocols ----- */ int l2cap_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr); int l2cap_disconn_ind(struct hci_conn *hcon); @@ -598,14 +600,14 @@ enum { static inline bool hci_conn_ssp_enabled(struct hci_conn *conn) { struct hci_dev *hdev = conn->hdev; - return test_bit(HCI_SSP_ENABLED, &hdev->dev_flags) && + return hci_dev_test_flag(hdev, HCI_SSP_ENABLED) && test_bit(HCI_CONN_SSP_ENABLED, &conn->flags); } static inline bool hci_conn_sc_enabled(struct hci_conn *conn) { struct hci_dev *hdev = conn->hdev; - return test_bit(HCI_SC_ENABLED, &hdev->dev_flags) && + return hci_dev_test_flag(hdev, HCI_SC_ENABLED) && test_bit(HCI_CONN_SC_ENABLED, &conn->flags); } @@ -1025,10 +1027,10 @@ void hci_conn_del_sysfs(struct hci_conn *conn); #define lmp_host_le_capable(dev) (!!((dev)->features[1][0] & LMP_HOST_LE)) #define lmp_host_le_br_capable(dev) (!!((dev)->features[1][0] & LMP_HOST_LE_BREDR)) -#define hdev_is_powered(hdev) (test_bit(HCI_UP, &hdev->flags) && \ - !test_bit(HCI_AUTO_OFF, &hdev->dev_flags)) -#define bredr_sc_enabled(dev) (lmp_sc_capable(dev) && \ - test_bit(HCI_SC_ENABLED, &(dev)->dev_flags)) +#define hdev_is_powered(dev) (test_bit(HCI_UP, &(dev)->flags) && \ + !hci_dev_test_flag(dev, HCI_AUTO_OFF)) +#define bredr_sc_enabled(dev) (lmp_sc_capable(dev) && \ + hci_dev_test_flag(dev, HCI_SC_ENABLED)) /* ----- HCI protocols ----- */ #define HCI_PROTO_DEFER 0x01 diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c index 91ebb9cb31de..5444e194eb4e 100644 --- a/net/bluetooth/hci_conn.c +++ b/net/bluetooth/hci_conn.c @@ -571,7 +571,7 @@ struct hci_dev *hci_get_route(bdaddr_t *dst, bdaddr_t *src) list_for_each_entry(d, &hci_dev_list, list) { if (!test_bit(HCI_UP, &d->flags) || - test_bit(HCI_USER_CHANNEL, &d->dev_flags) || + hci_dev_test_flag(d, HCI_USER_CHANNEL) || d->dev_type != HCI_BREDR) continue; @@ -734,7 +734,7 @@ struct hci_conn *hci_connect_le(struct hci_dev *hdev, bdaddr_t *dst, int err; /* Let's make sure that le is enabled.*/ - if (!test_bit(HCI_LE_ENABLED, &hdev->dev_flags)) { + if (!hci_dev_test_flag(hdev, HCI_LE_ENABLED)) { if (lmp_le_capable(hdev)) return ERR_PTR(-ECONNREFUSED); @@ -799,7 +799,7 @@ struct hci_conn *hci_connect_le(struct hci_dev *hdev, bdaddr_t *dst, * anyway have to disable it in order to start directed * advertising. */ - if (test_bit(HCI_LE_ADV, &hdev->dev_flags)) { + if (hci_dev_test_flag(hdev, HCI_LE_ADV)) { u8 enable = 0x00; hci_req_add(&req, HCI_OP_LE_SET_ADV_ENABLE, sizeof(enable), &enable); @@ -810,7 +810,7 @@ struct hci_conn *hci_connect_le(struct hci_dev *hdev, bdaddr_t *dst, /* If we're active scanning most controllers are unable * to initiate advertising. Simply reject the attempt. */ - if (test_bit(HCI_LE_SCAN, &hdev->dev_flags) && + if (hci_dev_test_flag(hdev, HCI_LE_SCAN) && hdev->le_scan_type == LE_SCAN_ACTIVE) { skb_queue_purge(&req.cmd_q); hci_conn_del(conn); @@ -840,7 +840,7 @@ struct hci_conn *hci_connect_le(struct hci_dev *hdev, bdaddr_t *dst, * handler for scan disabling knows to set the correct discovery * state. */ - if (test_bit(HCI_LE_SCAN, &hdev->dev_flags)) { + if (hci_dev_test_flag(hdev, HCI_LE_SCAN)) { hci_req_add_le_scan_disable(&req); set_bit(HCI_LE_SCAN_INTERRUPTED, &hdev->dev_flags); } @@ -864,7 +864,7 @@ struct hci_conn *hci_connect_acl(struct hci_dev *hdev, bdaddr_t *dst, { struct hci_conn *acl; - if (!test_bit(HCI_BREDR_ENABLED, &hdev->dev_flags)) { + if (!hci_dev_test_flag(hdev, HCI_BREDR_ENABLED)) { if (lmp_bredr_capable(hdev)) return ERR_PTR(-ECONNREFUSED); @@ -942,7 +942,7 @@ int hci_conn_check_link_mode(struct hci_conn *conn) * Connections is used and the link is encrypted with AES-CCM * using a P-256 authenticated combination key. */ - if (test_bit(HCI_SC_ONLY, &conn->hdev->flags)) { + if (hci_dev_test_flag(conn->hdev, HCI_SC_ONLY)) { if (!hci_conn_sc_enabled(conn) || !test_bit(HCI_CONN_AES_CCM, &conn->flags) || conn->key_type != HCI_LK_AUTH_COMBINATION_P256) diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index e3bbdd537b90..20fe5ef6abc5 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -617,7 +617,7 @@ static void hci_init2_req(struct hci_request *req, unsigned long opt) */ hdev->max_page = 0x01; - if (test_bit(HCI_SSP_ENABLED, &hdev->dev_flags)) { + if (hci_dev_test_flag(hdev, HCI_SSP_ENABLED)) { u8 mode = 0x01; hci_req_add(req, HCI_OP_WRITE_SSP_MODE, @@ -656,7 +656,7 @@ static void hci_init2_req(struct hci_request *req, unsigned long opt) sizeof(cp), &cp); } - if (test_bit(HCI_LINK_SECURITY, &hdev->dev_flags)) { + if (hci_dev_test_flag(hdev, HCI_LINK_SECURITY)) { u8 enable = 1; hci_req_add(req, HCI_OP_WRITE_AUTH_ENABLE, sizeof(enable), &enable); @@ -693,7 +693,7 @@ static void hci_set_le_support(struct hci_request *req) memset(&cp, 0, sizeof(cp)); - if (test_bit(HCI_LE_ENABLED, &hdev->dev_flags)) { + if (hci_dev_test_flag(hdev, HCI_LE_ENABLED)) { cp.le = 0x01; cp.simul = 0x00; } @@ -881,7 +881,7 @@ static void hci_init4_req(struct hci_request *req, unsigned long opt) hci_req_add(req, HCI_OP_READ_SYNC_TRAIN_PARAMS, 0, NULL); /* Enable Secure Connections if supported and configured */ - if (test_bit(HCI_SSP_ENABLED, &hdev->dev_flags) && + if (hci_dev_test_flag(hdev, HCI_SSP_ENABLED) && bredr_sc_enabled(hdev)) { u8 support = 0x01; @@ -901,7 +901,7 @@ static int __hci_init(struct hci_dev *hdev) /* The Device Under Test (DUT) mode is special and available for * all controller types. So just create it early on. */ - if (test_bit(HCI_SETUP, &hdev->dev_flags)) { + if (hci_dev_test_flag(hdev, HCI_SETUP)) { debugfs_create_file("dut_mode", 0644, hdev->debugfs, hdev, &dut_mode_fops); } @@ -937,8 +937,8 @@ static int __hci_init(struct hci_dev *hdev) * So only when in setup phase or config phase, create the debugfs * entries and register the SMP channels. */ - if (!test_bit(HCI_SETUP, &hdev->dev_flags) && - !test_bit(HCI_CONFIG, &hdev->dev_flags)) + if (!hci_dev_test_flag(hdev, HCI_SETUP) && + !hci_dev_test_flag(hdev, HCI_CONFIG)) return 0; hci_debugfs_create_common(hdev); @@ -1300,12 +1300,12 @@ int hci_inquiry(void __user *arg) if (!hdev) return -ENODEV; - if (test_bit(HCI_USER_CHANNEL, &hdev->dev_flags)) { + if (hci_dev_test_flag(hdev, HCI_USER_CHANNEL)) { err = -EBUSY; goto done; } - if (test_bit(HCI_UNCONFIGURED, &hdev->dev_flags)) { + if (hci_dev_test_flag(hdev, HCI_UNCONFIGURED)) { err = -EOPNOTSUPP; goto done; } @@ -1315,7 +1315,7 @@ int hci_inquiry(void __user *arg) goto done; } - if (!test_bit(HCI_BREDR_ENABLED, &hdev->dev_flags)) { + if (!hci_dev_test_flag(hdev, HCI_BREDR_ENABLED)) { err = -EOPNOTSUPP; goto done; } @@ -1387,17 +1387,17 @@ static int hci_dev_do_open(struct hci_dev *hdev) hci_req_lock(hdev); - if (test_bit(HCI_UNREGISTER, &hdev->dev_flags)) { + if (hci_dev_test_flag(hdev, HCI_UNREGISTER)) { ret = -ENODEV; goto done; } - if (!test_bit(HCI_SETUP, &hdev->dev_flags) && - !test_bit(HCI_CONFIG, &hdev->dev_flags)) { + if (!hci_dev_test_flag(hdev, HCI_SETUP) && + !hci_dev_test_flag(hdev, HCI_CONFIG)) { /* Check for rfkill but allow the HCI setup stage to * proceed (which in itself doesn't cause any RF activity). */ - if (test_bit(HCI_RFKILLED, &hdev->dev_flags)) { + if (hci_dev_test_flag(hdev, HCI_RFKILLED)) { ret = -ERFKILL; goto done; } @@ -1414,7 +1414,7 @@ static int hci_dev_do_open(struct hci_dev *hdev) * This check is only valid for BR/EDR controllers * since AMP controllers do not have an address. */ - if (!test_bit(HCI_USER_CHANNEL, &hdev->dev_flags) && + if (!hci_dev_test_flag(hdev, HCI_USER_CHANNEL) && hdev->dev_type == HCI_BREDR && !bacmp(&hdev->bdaddr, BDADDR_ANY) && !bacmp(&hdev->static_addr, BDADDR_ANY)) { @@ -1436,7 +1436,7 @@ static int hci_dev_do_open(struct hci_dev *hdev) atomic_set(&hdev->cmd_cnt, 1); set_bit(HCI_INIT, &hdev->flags); - if (test_bit(HCI_SETUP, &hdev->dev_flags)) { + if (hci_dev_test_flag(hdev, HCI_SETUP)) { if (hdev->setup) ret = hdev->setup(hdev); @@ -1458,11 +1458,11 @@ static int hci_dev_do_open(struct hci_dev *hdev) * also the original Bluetooth public device address * will be read using the Read BD Address command. */ - if (test_bit(HCI_UNCONFIGURED, &hdev->dev_flags)) + if (hci_dev_test_flag(hdev, HCI_UNCONFIGURED)) ret = __hci_unconf_init(hdev); } - if (test_bit(HCI_CONFIG, &hdev->dev_flags)) { + if (hci_dev_test_flag(hdev, HCI_CONFIG)) { /* If public address change is configured, ensure that * the address gets programmed. If the driver does not * support changing the public address, fail the power @@ -1476,8 +1476,8 @@ static int hci_dev_do_open(struct hci_dev *hdev) } if (!ret) { - if (!test_bit(HCI_UNCONFIGURED, &hdev->dev_flags) && - !test_bit(HCI_USER_CHANNEL, &hdev->dev_flags)) + if (!hci_dev_test_flag(hdev, HCI_UNCONFIGURED) && + !hci_dev_test_flag(hdev, HCI_USER_CHANNEL)) ret = __hci_init(hdev); } @@ -1488,10 +1488,10 @@ static int hci_dev_do_open(struct hci_dev *hdev) set_bit(HCI_RPA_EXPIRED, &hdev->dev_flags); set_bit(HCI_UP, &hdev->flags); hci_notify(hdev, HCI_DEV_UP); - if (!test_bit(HCI_SETUP, &hdev->dev_flags) && - !test_bit(HCI_CONFIG, &hdev->dev_flags) && - !test_bit(HCI_UNCONFIGURED, &hdev->dev_flags) && - !test_bit(HCI_USER_CHANNEL, &hdev->dev_flags) && + if (!hci_dev_test_flag(hdev, HCI_SETUP) && + !hci_dev_test_flag(hdev, HCI_CONFIG) && + !hci_dev_test_flag(hdev, HCI_UNCONFIGURED) && + !hci_dev_test_flag(hdev, HCI_USER_CHANNEL) && hdev->dev_type == HCI_BREDR) { hci_dev_lock(hdev); mgmt_powered(hdev, 1); @@ -1543,8 +1543,8 @@ int hci_dev_open(__u16 dev) * HCI_USER_CHANNEL will be set first before attempting to * open the device. */ - if (test_bit(HCI_UNCONFIGURED, &hdev->dev_flags) && - !test_bit(HCI_USER_CHANNEL, &hdev->dev_flags)) { + if (hci_dev_test_flag(hdev, HCI_UNCONFIGURED) && + !hci_dev_test_flag(hdev, HCI_USER_CHANNEL)) { err = -EOPNOTSUPP; goto done; } @@ -1569,8 +1569,8 @@ int hci_dev_open(__u16 dev) * is in use this bit will be cleared again and userspace has * to explicitly enable it. */ - if (!test_bit(HCI_USER_CHANNEL, &hdev->dev_flags) && - !test_bit(HCI_MGMT, &hdev->dev_flags)) + if (!hci_dev_test_flag(hdev, HCI_USER_CHANNEL) && + !hci_dev_test_flag(hdev, HCI_MGMT)) set_bit(HCI_BONDABLE, &hdev->dev_flags); err = hci_dev_do_open(hdev); @@ -1601,7 +1601,7 @@ static int hci_dev_do_close(struct hci_dev *hdev) { BT_DBG("%s %p", hdev->name, hdev); - if (!test_bit(HCI_UNREGISTER, &hdev->dev_flags)) { + if (!hci_dev_test_flag(hdev, HCI_UNREGISTER)) { /* Execute vendor specific shutdown routine */ if (hdev->shutdown) hdev->shutdown(hdev); @@ -1635,7 +1635,7 @@ static int hci_dev_do_close(struct hci_dev *hdev) cancel_delayed_work_sync(&hdev->le_scan_disable); cancel_delayed_work_sync(&hdev->le_scan_restart); - if (test_bit(HCI_MGMT, &hdev->dev_flags)) + if (hci_dev_test_flag(hdev, HCI_MGMT)) cancel_delayed_work_sync(&hdev->rpa_expired); /* Avoid potential lockdep warnings from the *_flush() calls by @@ -1667,8 +1667,8 @@ static int hci_dev_do_close(struct hci_dev *hdev) /* Reset device */ skb_queue_purge(&hdev->cmd_q); atomic_set(&hdev->cmd_cnt, 1); - if (!test_bit(HCI_AUTO_OFF, &hdev->dev_flags) && - !test_bit(HCI_UNCONFIGURED, &hdev->dev_flags) && + if (!hci_dev_test_flag(hdev, HCI_AUTO_OFF) && + !hci_dev_test_flag(hdev, HCI_UNCONFIGURED) && test_bit(HCI_QUIRK_RESET_ON_CLOSE, &hdev->quirks)) { set_bit(HCI_INIT, &hdev->flags); __hci_req_sync(hdev, hci_reset_req, 0, HCI_CMD_TIMEOUT); @@ -1723,7 +1723,7 @@ int hci_dev_close(__u16 dev) if (!hdev) return -ENODEV; - if (test_bit(HCI_USER_CHANNEL, &hdev->dev_flags)) { + if (hci_dev_test_flag(hdev, HCI_USER_CHANNEL)) { err = -EBUSY; goto done; } @@ -1786,12 +1786,12 @@ int hci_dev_reset(__u16 dev) goto done; } - if (test_bit(HCI_USER_CHANNEL, &hdev->dev_flags)) { + if (hci_dev_test_flag(hdev, HCI_USER_CHANNEL)) { err = -EBUSY; goto done; } - if (test_bit(HCI_UNCONFIGURED, &hdev->dev_flags)) { + if (hci_dev_test_flag(hdev, HCI_UNCONFIGURED)) { err = -EOPNOTSUPP; goto done; } @@ -1812,12 +1812,12 @@ int hci_dev_reset_stat(__u16 dev) if (!hdev) return -ENODEV; - if (test_bit(HCI_USER_CHANNEL, &hdev->dev_flags)) { + if (hci_dev_test_flag(hdev, HCI_USER_CHANNEL)) { ret = -EBUSY; goto done; } - if (test_bit(HCI_UNCONFIGURED, &hdev->dev_flags)) { + if (hci_dev_test_flag(hdev, HCI_UNCONFIGURED)) { ret = -EOPNOTSUPP; goto done; } @@ -1851,14 +1851,14 @@ static void hci_update_scan_state(struct hci_dev *hdev, u8 scan) &hdev->dev_flags); } - if (!test_bit(HCI_MGMT, &hdev->dev_flags)) + if (!hci_dev_test_flag(hdev, HCI_MGMT)) return; if (conn_changed || discov_changed) { /* In case this was disabled through mgmt */ set_bit(HCI_BREDR_ENABLED, &hdev->dev_flags); - if (test_bit(HCI_LE_ENABLED, &hdev->dev_flags)) + if (hci_dev_test_flag(hdev, HCI_LE_ENABLED)) mgmt_update_adv_data(hdev); mgmt_new_settings(hdev); @@ -1878,12 +1878,12 @@ int hci_dev_cmd(unsigned int cmd, void __user *arg) if (!hdev) return -ENODEV; - if (test_bit(HCI_USER_CHANNEL, &hdev->dev_flags)) { + if (hci_dev_test_flag(hdev, HCI_USER_CHANNEL)) { err = -EBUSY; goto done; } - if (test_bit(HCI_UNCONFIGURED, &hdev->dev_flags)) { + if (hci_dev_test_flag(hdev, HCI_UNCONFIGURED)) { err = -EOPNOTSUPP; goto done; } @@ -1893,7 +1893,7 @@ int hci_dev_cmd(unsigned int cmd, void __user *arg) goto done; } - if (!test_bit(HCI_BREDR_ENABLED, &hdev->dev_flags)) { + if (!hci_dev_test_flag(hdev, HCI_BREDR_ENABLED)) { err = -EOPNOTSUPP; goto done; } @@ -1997,7 +1997,7 @@ int hci_get_dev_list(void __user *arg) * is running, but in that case still indicate that the * device is actually down. */ - if (test_bit(HCI_AUTO_OFF, &hdev->dev_flags)) + if (hci_dev_test_flag(hdev, HCI_AUTO_OFF)) flags &= ~BIT(HCI_UP); (dr + n)->dev_id = hdev->id; @@ -2035,7 +2035,7 @@ int hci_get_dev_info(void __user *arg) * is running, but in that case still indicate that the * device is actually down. */ - if (test_bit(HCI_AUTO_OFF, &hdev->dev_flags)) + if (hci_dev_test_flag(hdev, HCI_AUTO_OFF)) flags = hdev->flags & ~BIT(HCI_UP); else flags = hdev->flags; @@ -2078,13 +2078,13 @@ static int hci_rfkill_set_block(void *data, bool blocked) BT_DBG("%p name %s blocked %d", hdev, hdev->name, blocked); - if (test_bit(HCI_USER_CHANNEL, &hdev->dev_flags)) + if (hci_dev_test_flag(hdev, HCI_USER_CHANNEL)) return -EBUSY; if (blocked) { set_bit(HCI_RFKILLED, &hdev->dev_flags); - if (!test_bit(HCI_SETUP, &hdev->dev_flags) && - !test_bit(HCI_CONFIG, &hdev->dev_flags)) + if (!hci_dev_test_flag(hdev, HCI_SETUP) && + !hci_dev_test_flag(hdev, HCI_CONFIG)) hci_dev_do_close(hdev); } else { clear_bit(HCI_RFKILLED, &hdev->dev_flags); @@ -2116,14 +2116,14 @@ static void hci_power_on(struct work_struct *work) * ignored and they need to be checked now. If they are still * valid, it is important to turn the device back off. */ - if (test_bit(HCI_RFKILLED, &hdev->dev_flags) || - test_bit(HCI_UNCONFIGURED, &hdev->dev_flags) || + if (hci_dev_test_flag(hdev, HCI_RFKILLED) || + hci_dev_test_flag(hdev, HCI_UNCONFIGURED) || (hdev->dev_type == HCI_BREDR && !bacmp(&hdev->bdaddr, BDADDR_ANY) && !bacmp(&hdev->static_addr, BDADDR_ANY))) { clear_bit(HCI_AUTO_OFF, &hdev->dev_flags); hci_dev_do_close(hdev); - } else if (test_bit(HCI_AUTO_OFF, &hdev->dev_flags)) { + } else if (hci_dev_test_flag(hdev, HCI_AUTO_OFF)) { queue_delayed_work(hdev->req_workqueue, &hdev->power_off, HCI_AUTO_OFF_TIMEOUT); } @@ -2132,7 +2132,7 @@ static void hci_power_on(struct work_struct *work) /* For unconfigured devices, set the HCI_RAW flag * so that userspace can easily identify them. */ - if (test_bit(HCI_UNCONFIGURED, &hdev->dev_flags)) + if (hci_dev_test_flag(hdev, HCI_UNCONFIGURED)) set_bit(HCI_RAW, &hdev->flags); /* For fully configured devices, this will send @@ -2147,7 +2147,7 @@ static void hci_power_on(struct work_struct *work) /* When the controller is now configured, then it * is important to clear the HCI_RAW flag. */ - if (!test_bit(HCI_UNCONFIGURED, &hdev->dev_flags)) + if (!hci_dev_test_flag(hdev, HCI_UNCONFIGURED)) clear_bit(HCI_RAW, &hdev->flags); /* Powering on the controller with HCI_CONFIG set only @@ -2986,7 +2986,7 @@ static void le_scan_restart_work(struct work_struct *work) BT_DBG("%s", hdev->name); /* If controller is not scanning we are done. */ - if (!test_bit(HCI_LE_SCAN, &hdev->dev_flags)) + if (!hci_dev_test_flag(hdev, HCI_LE_SCAN)) return; hci_req_init(&req, hdev); @@ -3021,7 +3021,7 @@ void hci_copy_identity_address(struct hci_dev *hdev, bdaddr_t *bdaddr, { if (test_bit(HCI_FORCE_STATIC_ADDR, &hdev->dbg_flags) || !bacmp(&hdev->bdaddr, BDADDR_ANY) || - (!test_bit(HCI_BREDR_ENABLED, &hdev->dev_flags) && + (!hci_dev_test_flag(hdev, HCI_BREDR_ENABLED) && bacmp(&hdev->static_addr, BDADDR_ANY))) { bacpy(bdaddr, &hdev->static_addr); *bdaddr_type = ADDR_LE_DEV_RANDOM; @@ -3251,8 +3251,8 @@ void hci_unregister_dev(struct hci_dev *hdev) cancel_work_sync(&hdev->power_on); if (!test_bit(HCI_INIT, &hdev->flags) && - !test_bit(HCI_SETUP, &hdev->dev_flags) && - !test_bit(HCI_CONFIG, &hdev->dev_flags)) { + !hci_dev_test_flag(hdev, HCI_SETUP) && + !hci_dev_test_flag(hdev, HCI_CONFIG)) { hci_dev_lock(hdev); mgmt_index_removed(hdev); hci_dev_unlock(hdev); @@ -3926,7 +3926,7 @@ static inline int __get_blocks(struct hci_dev *hdev, struct sk_buff *skb) static void __check_timeout(struct hci_dev *hdev, unsigned int cnt) { - if (!test_bit(HCI_UNCONFIGURED, &hdev->dev_flags)) { + if (!hci_dev_test_flag(hdev, HCI_UNCONFIGURED)) { /* ACL tx timeout must be longer than maximum * link supervision timeout (40.9 seconds) */ if (!cnt && time_after(jiffies, hdev->acl_last_tx + @@ -4109,7 +4109,7 @@ static void hci_sched_le(struct hci_dev *hdev) if (!hci_conn_num(hdev, LE_LINK)) return; - if (!test_bit(HCI_UNCONFIGURED, &hdev->dev_flags)) { + if (!hci_dev_test_flag(hdev, HCI_UNCONFIGURED)) { /* LE tx timeout must be longer than maximum * link supervision timeout (40.9 seconds) */ if (!hdev->le_cnt && hdev->le_pkts && @@ -4157,7 +4157,7 @@ static void hci_tx_work(struct work_struct *work) BT_DBG("%s acl %d sco %d le %d", hdev->name, hdev->acl_cnt, hdev->sco_cnt, hdev->le_cnt); - if (!test_bit(HCI_USER_CHANNEL, &hdev->dev_flags)) { + if (!hci_dev_test_flag(hdev, HCI_USER_CHANNEL)) { /* Schedule queues and send stuff to HCI driver */ hci_sched_acl(hdev); hci_sched_sco(hdev); @@ -4354,7 +4354,7 @@ static void hci_rx_work(struct work_struct *work) hci_send_to_sock(hdev, skb); } - if (test_bit(HCI_USER_CHANNEL, &hdev->dev_flags)) { + if (hci_dev_test_flag(hdev, HCI_USER_CHANNEL)) { kfree_skb(skb); continue; } diff --git a/net/bluetooth/hci_debugfs.c b/net/bluetooth/hci_debugfs.c index 65261e5d4b84..3c025ee5572c 100644 --- a/net/bluetooth/hci_debugfs.c +++ b/net/bluetooth/hci_debugfs.c @@ -247,7 +247,7 @@ static ssize_t use_debug_keys_read(struct file *file, char __user *user_buf, struct hci_dev *hdev = file->private_data; char buf[3]; - buf[0] = test_bit(HCI_USE_DEBUG_KEYS, &hdev->dev_flags) ? 'Y': 'N'; + buf[0] = hci_dev_test_flag(hdev, HCI_USE_DEBUG_KEYS) ? 'Y': 'N'; buf[1] = '\n'; buf[2] = '\0'; return simple_read_from_buffer(user_buf, count, ppos, buf, 2); @@ -265,7 +265,7 @@ static ssize_t sc_only_mode_read(struct file *file, char __user *user_buf, struct hci_dev *hdev = file->private_data; char buf[3]; - buf[0] = test_bit(HCI_SC_ONLY, &hdev->dev_flags) ? 'Y': 'N'; + buf[0] = hci_dev_test_flag(hdev, HCI_SC_ONLY) ? 'Y': 'N'; buf[1] = '\n'; buf[2] = '\0'; return simple_read_from_buffer(user_buf, count, ppos, buf, 2); diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 39653d46932b..f1ed3fe9e0df 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -265,7 +265,7 @@ static void hci_cc_write_local_name(struct hci_dev *hdev, struct sk_buff *skb) hci_dev_lock(hdev); - if (test_bit(HCI_MGMT, &hdev->dev_flags)) + if (hci_dev_test_flag(hdev, HCI_MGMT)) mgmt_set_local_name_complete(hdev, sent, status); else if (!status) memcpy(hdev->dev_name, sent, HCI_MAX_NAME_LENGTH); @@ -282,8 +282,8 @@ static void hci_cc_read_local_name(struct hci_dev *hdev, struct sk_buff *skb) if (rp->status) return; - if (test_bit(HCI_SETUP, &hdev->dev_flags) || - test_bit(HCI_CONFIG, &hdev->dev_flags)) + if (hci_dev_test_flag(hdev, HCI_SETUP) || + hci_dev_test_flag(hdev, HCI_CONFIG)) memcpy(hdev->dev_name, rp->name, HCI_MAX_NAME_LENGTH); } @@ -309,7 +309,7 @@ static void hci_cc_write_auth_enable(struct hci_dev *hdev, struct sk_buff *skb) clear_bit(HCI_AUTH, &hdev->flags); } - if (test_bit(HCI_MGMT, &hdev->dev_flags)) + if (hci_dev_test_flag(hdev, HCI_MGMT)) mgmt_auth_enable_complete(hdev, status); hci_dev_unlock(hdev); @@ -404,7 +404,7 @@ static void hci_cc_write_class_of_dev(struct hci_dev *hdev, struct sk_buff *skb) if (status == 0) memcpy(hdev->dev_class, sent, 3); - if (test_bit(HCI_MGMT, &hdev->dev_flags)) + if (hci_dev_test_flag(hdev, HCI_MGMT)) mgmt_set_class_of_dev_complete(hdev, sent, status); hci_dev_unlock(hdev); @@ -497,7 +497,7 @@ static void hci_cc_write_ssp_mode(struct hci_dev *hdev, struct sk_buff *skb) hdev->features[1][0] &= ~LMP_HOST_SSP; } - if (test_bit(HCI_MGMT, &hdev->dev_flags)) + if (hci_dev_test_flag(hdev, HCI_MGMT)) mgmt_ssp_enable_complete(hdev, sent->mode, status); else if (!status) { if (sent->mode) @@ -529,7 +529,7 @@ static void hci_cc_write_sc_support(struct hci_dev *hdev, struct sk_buff *skb) hdev->features[1][0] &= ~LMP_HOST_SC; } - if (!test_bit(HCI_MGMT, &hdev->dev_flags) && !status) { + if (!hci_dev_test_flag(hdev, HCI_MGMT) && !status) { if (sent->support) set_bit(HCI_SC_ENABLED, &hdev->dev_flags); else @@ -548,8 +548,8 @@ static void hci_cc_read_local_version(struct hci_dev *hdev, struct sk_buff *skb) if (rp->status) return; - if (test_bit(HCI_SETUP, &hdev->dev_flags) || - test_bit(HCI_CONFIG, &hdev->dev_flags)) { + if (hci_dev_test_flag(hdev, HCI_SETUP) || + hci_dev_test_flag(hdev, HCI_CONFIG)) { hdev->hci_ver = rp->hci_ver; hdev->hci_rev = __le16_to_cpu(rp->hci_rev); hdev->lmp_ver = rp->lmp_ver; @@ -568,8 +568,8 @@ static void hci_cc_read_local_commands(struct hci_dev *hdev, if (rp->status) return; - if (test_bit(HCI_SETUP, &hdev->dev_flags) || - test_bit(HCI_CONFIG, &hdev->dev_flags)) + if (hci_dev_test_flag(hdev, HCI_SETUP) || + hci_dev_test_flag(hdev, HCI_CONFIG)) memcpy(hdev->commands, rp->commands, sizeof(hdev->commands)); } @@ -691,7 +691,7 @@ static void hci_cc_read_bd_addr(struct hci_dev *hdev, struct sk_buff *skb) if (test_bit(HCI_INIT, &hdev->flags)) bacpy(&hdev->bdaddr, &rp->bdaddr); - if (test_bit(HCI_SETUP, &hdev->dev_flags)) + if (hci_dev_test_flag(hdev, HCI_SETUP)) bacpy(&hdev->setup_addr, &rp->bdaddr); } @@ -900,7 +900,7 @@ static void hci_cc_pin_code_reply(struct hci_dev *hdev, struct sk_buff *skb) hci_dev_lock(hdev); - if (test_bit(HCI_MGMT, &hdev->dev_flags)) + if (hci_dev_test_flag(hdev, HCI_MGMT)) mgmt_pin_code_reply_complete(hdev, &rp->bdaddr, rp->status); if (rp->status) @@ -926,7 +926,7 @@ static void hci_cc_pin_code_neg_reply(struct hci_dev *hdev, struct sk_buff *skb) hci_dev_lock(hdev); - if (test_bit(HCI_MGMT, &hdev->dev_flags)) + if (hci_dev_test_flag(hdev, HCI_MGMT)) mgmt_pin_code_neg_reply_complete(hdev, &rp->bdaddr, rp->status); @@ -985,7 +985,7 @@ static void hci_cc_user_confirm_reply(struct hci_dev *hdev, struct sk_buff *skb) hci_dev_lock(hdev); - if (test_bit(HCI_MGMT, &hdev->dev_flags)) + if (hci_dev_test_flag(hdev, HCI_MGMT)) mgmt_user_confirm_reply_complete(hdev, &rp->bdaddr, ACL_LINK, 0, rp->status); @@ -1001,7 +1001,7 @@ static void hci_cc_user_confirm_neg_reply(struct hci_dev *hdev, hci_dev_lock(hdev); - if (test_bit(HCI_MGMT, &hdev->dev_flags)) + if (hci_dev_test_flag(hdev, HCI_MGMT)) mgmt_user_confirm_neg_reply_complete(hdev, &rp->bdaddr, ACL_LINK, 0, rp->status); @@ -1016,7 +1016,7 @@ static void hci_cc_user_passkey_reply(struct hci_dev *hdev, struct sk_buff *skb) hci_dev_lock(hdev); - if (test_bit(HCI_MGMT, &hdev->dev_flags)) + if (hci_dev_test_flag(hdev, HCI_MGMT)) mgmt_user_passkey_reply_complete(hdev, &rp->bdaddr, ACL_LINK, 0, rp->status); @@ -1032,7 +1032,7 @@ static void hci_cc_user_passkey_neg_reply(struct hci_dev *hdev, hci_dev_lock(hdev); - if (test_bit(HCI_MGMT, &hdev->dev_flags)) + if (hci_dev_test_flag(hdev, HCI_MGMT)) mgmt_user_passkey_neg_reply_complete(hdev, &rp->bdaddr, ACL_LINK, 0, rp->status); @@ -1229,7 +1229,7 @@ static void hci_cc_le_set_scan_enable(struct hci_dev *hdev, if (test_and_clear_bit(HCI_LE_SCAN_INTERRUPTED, &hdev->dev_flags)) hci_discovery_set_state(hdev, DISCOVERY_STOPPED); - else if (!test_bit(HCI_LE_ADV, &hdev->dev_flags) && + else if (!hci_dev_test_flag(hdev, HCI_LE_ADV) && hdev->discovery.state == DISCOVERY_FINDING) mgmt_reenable_advertising(hdev); @@ -1769,7 +1769,7 @@ static void hci_cs_remote_name_req(struct hci_dev *hdev, __u8 status) conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &cp->bdaddr); - if (test_bit(HCI_MGMT, &hdev->dev_flags)) + if (hci_dev_test_flag(hdev, HCI_MGMT)) hci_check_pending_name(hdev, conn, &cp->bdaddr, NULL, 0); if (!conn) @@ -2118,7 +2118,7 @@ static void hci_inquiry_complete_evt(struct hci_dev *hdev, struct sk_buff *skb) smp_mb__after_atomic(); /* wake_up_bit advises about this barrier */ wake_up_bit(&hdev->flags, HCI_INQUIRY); - if (!test_bit(HCI_MGMT, &hdev->dev_flags)) + if (!hci_dev_test_flag(hdev, HCI_MGMT)) return; hci_dev_lock(hdev); @@ -2154,7 +2154,7 @@ static void hci_inquiry_result_evt(struct hci_dev *hdev, struct sk_buff *skb) if (!num_rsp) return; - if (test_bit(HCI_PERIODIC_INQ, &hdev->dev_flags)) + if (hci_dev_test_flag(hdev, HCI_PERIODIC_INQ)) return; hci_dev_lock(hdev); @@ -2304,8 +2304,8 @@ static void hci_conn_request_evt(struct hci_dev *hdev, struct sk_buff *skb) * connection. These features are only touched through mgmt so * only do the checks if HCI_MGMT is set. */ - if (test_bit(HCI_MGMT, &hdev->dev_flags) && - !test_bit(HCI_CONNECTABLE, &hdev->dev_flags) && + if (hci_dev_test_flag(hdev, HCI_MGMT) && + !hci_dev_test_flag(hdev, HCI_CONNECTABLE) && !hci_bdaddr_list_lookup(&hdev->whitelist, &ev->bdaddr, BDADDR_BREDR)) { hci_reject_conn(hdev, &ev->bdaddr); @@ -2542,7 +2542,7 @@ static void hci_remote_name_evt(struct hci_dev *hdev, struct sk_buff *skb) conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &ev->bdaddr); - if (!test_bit(HCI_MGMT, &hdev->dev_flags)) + if (!hci_dev_test_flag(hdev, HCI_MGMT)) goto check_auth; if (ev->status == 0) @@ -2626,7 +2626,7 @@ static void hci_encrypt_change_evt(struct hci_dev *hdev, struct sk_buff *skb) * connections that are not encrypted with AES-CCM * using a P-256 authenticated combination key. */ - if (test_bit(HCI_SC_ONLY, &hdev->dev_flags) && + if (hci_dev_test_flag(hdev, HCI_SC_ONLY) && (!test_bit(HCI_CONN_AES_CCM, &conn->flags) || conn->key_type != HCI_LK_AUTH_COMBINATION_P256)) { hci_connect_cfm(conn, HCI_ERROR_AUTH_FAILURE); @@ -3331,11 +3331,11 @@ static void hci_pin_code_request_evt(struct hci_dev *hdev, struct sk_buff *skb) hci_conn_drop(conn); } - if (!test_bit(HCI_BONDABLE, &hdev->dev_flags) && + if (!hci_dev_test_flag(hdev, HCI_BONDABLE) && !test_bit(HCI_CONN_AUTH_INITIATOR, &conn->flags)) { hci_send_cmd(hdev, HCI_OP_PIN_CODE_NEG_REPLY, sizeof(ev->bdaddr), &ev->bdaddr); - } else if (test_bit(HCI_MGMT, &hdev->dev_flags)) { + } else if (hci_dev_test_flag(hdev, HCI_MGMT)) { u8 secure; if (conn->pending_sec_level == BT_SECURITY_HIGH) @@ -3391,7 +3391,7 @@ static void hci_link_key_request_evt(struct hci_dev *hdev, struct sk_buff *skb) BT_DBG("%s", hdev->name); - if (!test_bit(HCI_MGMT, &hdev->dev_flags)) + if (!hci_dev_test_flag(hdev, HCI_MGMT)) return; hci_dev_lock(hdev); @@ -3465,7 +3465,7 @@ static void hci_link_key_notify_evt(struct hci_dev *hdev, struct sk_buff *skb) set_bit(HCI_CONN_NEW_LINK_KEY, &conn->flags); conn_set_key(conn, ev->key_type, conn->pin_length); - if (!test_bit(HCI_MGMT, &hdev->dev_flags)) + if (!hci_dev_test_flag(hdev, HCI_MGMT)) goto unlock; key = hci_add_link_key(hdev, conn, &ev->bdaddr, ev->link_key, @@ -3487,7 +3487,7 @@ static void hci_link_key_notify_evt(struct hci_dev *hdev, struct sk_buff *skb) * store_hint being 0). */ if (key->type == HCI_LK_DEBUG_COMBINATION && - !test_bit(HCI_KEEP_DEBUG_KEYS, &hdev->dev_flags)) { + !hci_dev_test_flag(hdev, HCI_KEEP_DEBUG_KEYS)) { list_del_rcu(&key->list); kfree_rcu(key, rcu); goto unlock; @@ -3570,7 +3570,7 @@ static void hci_inquiry_result_with_rssi_evt(struct hci_dev *hdev, if (!num_rsp) return; - if (test_bit(HCI_PERIODIC_INQ, &hdev->dev_flags)) + if (hci_dev_test_flag(hdev, HCI_PERIODIC_INQ)) return; hci_dev_lock(hdev); @@ -3776,7 +3776,7 @@ static void hci_extended_inquiry_result_evt(struct hci_dev *hdev, if (!num_rsp) return; - if (test_bit(HCI_PERIODIC_INQ, &hdev->dev_flags)) + if (hci_dev_test_flag(hdev, HCI_PERIODIC_INQ)) return; hci_dev_lock(hdev); @@ -3794,7 +3794,7 @@ static void hci_extended_inquiry_result_evt(struct hci_dev *hdev, data.rssi = info->rssi; data.ssp_mode = 0x01; - if (test_bit(HCI_MGMT, &hdev->dev_flags)) + if (hci_dev_test_flag(hdev, HCI_MGMT)) name_known = eir_has_data_type(info->data, sizeof(info->data), EIR_NAME_COMPLETE); @@ -3898,7 +3898,7 @@ static u8 bredr_oob_data_present(struct hci_conn *conn) * information. However it can only be trusted when * not in Secure Connection Only mode. */ - if (!test_bit(HCI_SC_ONLY, &hdev->dev_flags)) + if (!hci_dev_test_flag(hdev, HCI_SC_ONLY)) return data->present; /* When Secure Connections Only mode is enabled, then @@ -3942,13 +3942,13 @@ static void hci_io_capa_request_evt(struct hci_dev *hdev, struct sk_buff *skb) hci_conn_hold(conn); - if (!test_bit(HCI_MGMT, &hdev->dev_flags)) + if (!hci_dev_test_flag(hdev, HCI_MGMT)) goto unlock; /* Allow pairing if we're pairable, the initiators of the * pairing or if the remote is not requesting bonding. */ - if (test_bit(HCI_BONDABLE, &hdev->dev_flags) || + if (hci_dev_test_flag(hdev, HCI_BONDABLE) || test_bit(HCI_CONN_AUTH_INITIATOR, &conn->flags) || (conn->remote_auth & ~0x01) == HCI_AT_NO_BONDING) { struct hci_cp_io_capability_reply cp; @@ -3974,7 +3974,7 @@ static void hci_io_capa_request_evt(struct hci_dev *hdev, struct sk_buff *skb) /* If we're not bondable, force one of the non-bondable * authentication requirement values. */ - if (!test_bit(HCI_BONDABLE, &hdev->dev_flags)) + if (!hci_dev_test_flag(hdev, HCI_BONDABLE)) conn->auth_type &= HCI_AT_NO_BONDING_MITM; cp.authentication = conn->auth_type; @@ -4029,7 +4029,7 @@ static void hci_user_confirm_request_evt(struct hci_dev *hdev, hci_dev_lock(hdev); - if (!test_bit(HCI_MGMT, &hdev->dev_flags)) + if (!hci_dev_test_flag(hdev, HCI_MGMT)) goto unlock; conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &ev->bdaddr); @@ -4100,7 +4100,7 @@ static void hci_user_passkey_request_evt(struct hci_dev *hdev, BT_DBG("%s", hdev->name); - if (test_bit(HCI_MGMT, &hdev->dev_flags)) + if (hci_dev_test_flag(hdev, HCI_MGMT)) mgmt_user_passkey_request(hdev, &ev->bdaddr, ACL_LINK, 0); } @@ -4119,7 +4119,7 @@ static void hci_user_passkey_notify_evt(struct hci_dev *hdev, conn->passkey_notify = __le32_to_cpu(ev->passkey); conn->passkey_entered = 0; - if (test_bit(HCI_MGMT, &hdev->dev_flags)) + if (hci_dev_test_flag(hdev, HCI_MGMT)) mgmt_user_passkey_notify(hdev, &conn->dst, conn->type, conn->dst_type, conn->passkey_notify, conn->passkey_entered); @@ -4157,7 +4157,7 @@ static void hci_keypress_notify_evt(struct hci_dev *hdev, struct sk_buff *skb) return; } - if (test_bit(HCI_MGMT, &hdev->dev_flags)) + if (hci_dev_test_flag(hdev, HCI_MGMT)) mgmt_user_passkey_notify(hdev, &conn->dst, conn->type, conn->dst_type, conn->passkey_notify, conn->passkey_entered); @@ -4226,7 +4226,7 @@ static void hci_remote_oob_data_request_evt(struct hci_dev *hdev, hci_dev_lock(hdev); - if (!test_bit(HCI_MGMT, &hdev->dev_flags)) + if (!hci_dev_test_flag(hdev, HCI_MGMT)) goto unlock; data = hci_find_remote_oob_data(hdev, &ev->bdaddr, BDADDR_BREDR); @@ -4243,7 +4243,7 @@ static void hci_remote_oob_data_request_evt(struct hci_dev *hdev, struct hci_cp_remote_oob_ext_data_reply cp; bacpy(&cp.bdaddr, &ev->bdaddr); - if (test_bit(HCI_SC_ONLY, &hdev->dev_flags)) { + if (hci_dev_test_flag(hdev, HCI_SC_ONLY)) { memset(cp.hash192, 0, sizeof(cp.hash192)); memset(cp.rand192, 0, sizeof(cp.rand192)); } else { @@ -4432,7 +4432,7 @@ static void hci_le_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *skb) if (conn->out) { conn->resp_addr_type = ev->bdaddr_type; bacpy(&conn->resp_addr, &ev->bdaddr); - if (test_bit(HCI_PRIVACY, &hdev->dev_flags)) { + if (hci_dev_test_flag(hdev, HCI_PRIVACY)) { conn->init_addr_type = ADDR_LE_DEV_RANDOM; bacpy(&conn->init_addr, &hdev->rpa); } else { @@ -4658,7 +4658,7 @@ static void process_adv_report(struct hci_dev *hdev, u8 type, bdaddr_t *bdaddr, /* If the controller is not using resolvable random * addresses, then this report can be ignored. */ - if (!test_bit(HCI_PRIVACY, &hdev->dev_flags)) + if (!hci_dev_test_flag(hdev, HCI_PRIVACY)) return; /* If the local IRK of the controller does not match diff --git a/net/bluetooth/hci_request.c b/net/bluetooth/hci_request.c index f857e765e081..42fa10522e89 100644 --- a/net/bluetooth/hci_request.c +++ b/net/bluetooth/hci_request.c @@ -270,7 +270,7 @@ void hci_req_add_le_passive_scan(struct hci_request *req) * and 0x01 (whitelist enabled) use the new filter policies * 0x02 (no whitelist) and 0x03 (whitelist enabled). */ - if (test_bit(HCI_PRIVACY, &hdev->dev_flags) && + if (hci_dev_test_flag(hdev, HCI_PRIVACY) && (hdev->le_features[0] & HCI_LE_EXT_SCAN_POLICY)) filter_policy |= 0x02; @@ -304,7 +304,7 @@ static void set_random_addr(struct hci_request *req, bdaddr_t *rpa) * In this kind of scenario skip the update and let the random * address be updated at the next cycle. */ - if (test_bit(HCI_LE_ADV, &hdev->dev_flags) || + if (hci_dev_test_flag(hdev, HCI_LE_ADV) || hci_conn_hash_lookup_state(hdev, LE_LINK, BT_CONNECT)) { BT_DBG("Deferring random address update"); set_bit(HCI_RPA_EXPIRED, &hdev->dev_flags); @@ -324,7 +324,7 @@ int hci_update_random_address(struct hci_request *req, bool require_privacy, * current RPA has expired or there is something else than * the current RPA in use, then generate a new one. */ - if (test_bit(HCI_PRIVACY, &hdev->dev_flags)) { + if (hci_dev_test_flag(hdev, HCI_PRIVACY)) { int to; *own_addr_type = ADDR_LE_DEV_RANDOM; @@ -385,7 +385,7 @@ int hci_update_random_address(struct hci_request *req, bool require_privacy, */ if (test_bit(HCI_FORCE_STATIC_ADDR, &hdev->dbg_flags) || !bacmp(&hdev->bdaddr, BDADDR_ANY) || - (!test_bit(HCI_BREDR_ENABLED, &hdev->dev_flags) && + (!hci_dev_test_flag(hdev, HCI_BREDR_ENABLED) && bacmp(&hdev->static_addr, BDADDR_ANY))) { *own_addr_type = ADDR_LE_DEV_RANDOM; if (bacmp(&hdev->static_addr, &hdev->random_addr)) @@ -425,7 +425,7 @@ void __hci_update_page_scan(struct hci_request *req) struct hci_dev *hdev = req->hdev; u8 scan; - if (!test_bit(HCI_BREDR_ENABLED, &hdev->dev_flags)) + if (!hci_dev_test_flag(hdev, HCI_BREDR_ENABLED)) return; if (!hdev_is_powered(hdev)) @@ -434,7 +434,7 @@ void __hci_update_page_scan(struct hci_request *req) if (mgmt_powering_down(hdev)) return; - if (test_bit(HCI_CONNECTABLE, &hdev->dev_flags) || + if (hci_dev_test_flag(hdev, HCI_CONNECTABLE) || disconnected_whitelist_entries(hdev)) scan = SCAN_PAGE; else @@ -443,7 +443,7 @@ void __hci_update_page_scan(struct hci_request *req) if (test_bit(HCI_PSCAN, &hdev->flags) == !!(scan & SCAN_PAGE)) return; - if (test_bit(HCI_DISCOVERABLE, &hdev->dev_flags)) + if (hci_dev_test_flag(hdev, HCI_DISCOVERABLE)) scan |= SCAN_INQUIRY; hci_req_add(req, HCI_OP_WRITE_SCAN_ENABLE, 1, &scan); @@ -471,14 +471,14 @@ void __hci_update_background_scan(struct hci_request *req) if (!test_bit(HCI_UP, &hdev->flags) || test_bit(HCI_INIT, &hdev->flags) || - test_bit(HCI_SETUP, &hdev->dev_flags) || - test_bit(HCI_CONFIG, &hdev->dev_flags) || - test_bit(HCI_AUTO_OFF, &hdev->dev_flags) || - test_bit(HCI_UNREGISTER, &hdev->dev_flags)) + hci_dev_test_flag(hdev, HCI_SETUP) || + hci_dev_test_flag(hdev, HCI_CONFIG) || + hci_dev_test_flag(hdev, HCI_AUTO_OFF) || + hci_dev_test_flag(hdev, HCI_UNREGISTER)) return; /* No point in doing scanning if LE support hasn't been enabled */ - if (!test_bit(HCI_LE_ENABLED, &hdev->dev_flags)) + if (!hci_dev_test_flag(hdev, HCI_LE_ENABLED)) return; /* If discovery is active don't interfere with it */ @@ -502,7 +502,7 @@ void __hci_update_background_scan(struct hci_request *req) */ /* If controller is not scanning we are done. */ - if (!test_bit(HCI_LE_SCAN, &hdev->dev_flags)) + if (!hci_dev_test_flag(hdev, HCI_LE_SCAN)) return; hci_req_add_le_scan_disable(req); @@ -524,7 +524,7 @@ void __hci_update_background_scan(struct hci_request *req) /* If controller is currently scanning, we stop it to ensure we * don't miss any advertising (due to duplicates filter). */ - if (test_bit(HCI_LE_SCAN, &hdev->dev_flags)) + if (hci_dev_test_flag(hdev, HCI_LE_SCAN)) hci_req_add_le_scan_disable(req); hci_req_add_le_passive_scan(req); diff --git a/net/bluetooth/hci_sock.c b/net/bluetooth/hci_sock.c index aa9ffcb9481f..b297709d82bf 100644 --- a/net/bluetooth/hci_sock.c +++ b/net/bluetooth/hci_sock.c @@ -534,10 +534,10 @@ static int hci_sock_bound_ioctl(struct sock *sk, unsigned int cmd, if (!hdev) return -EBADFD; - if (test_bit(HCI_USER_CHANNEL, &hdev->dev_flags)) + if (hci_dev_test_flag(hdev, HCI_USER_CHANNEL)) return -EBUSY; - if (test_bit(HCI_UNCONFIGURED, &hdev->dev_flags)) + if (hci_dev_test_flag(hdev, HCI_UNCONFIGURED)) return -EOPNOTSUPP; if (hdev->dev_type != HCI_BREDR) @@ -713,8 +713,8 @@ static int hci_sock_bind(struct socket *sock, struct sockaddr *addr, if (test_bit(HCI_UP, &hdev->flags) || test_bit(HCI_INIT, &hdev->flags) || - test_bit(HCI_SETUP, &hdev->dev_flags) || - test_bit(HCI_CONFIG, &hdev->dev_flags)) { + hci_dev_test_flag(hdev, HCI_SETUP) || + hci_dev_test_flag(hdev, HCI_CONFIG)) { err = -EBUSY; hci_dev_put(hdev); goto done; diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index 91c682846bcf..af30d8240c80 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -3900,7 +3900,7 @@ static int l2cap_connect_req(struct l2cap_conn *conn, return -EPROTO; hci_dev_lock(hdev); - if (test_bit(HCI_MGMT, &hdev->dev_flags) && + if (hci_dev_test_flag(hdev, HCI_MGMT) && !test_and_set_bit(HCI_CONN_MGMT_CONNECTED, &hcon->flags)) mgmt_device_connected(hdev, hcon, 0, NULL, 0); hci_dev_unlock(hdev); @@ -6987,10 +6987,10 @@ static struct l2cap_conn *l2cap_conn_add(struct hci_conn *hcon) conn->local_fixed_chan = L2CAP_FC_SIG_BREDR | L2CAP_FC_CONNLESS; if (hcon->type == ACL_LINK && - test_bit(HCI_HS_ENABLED, &hcon->hdev->dev_flags)) + hci_dev_test_flag(hcon->hdev, HCI_HS_ENABLED)) conn->local_fixed_chan |= L2CAP_FC_A2MP; - if (test_bit(HCI_LE_ENABLED, &hcon->hdev->dev_flags) && + if (hci_dev_test_flag(hcon->hdev, HCI_LE_ENABLED) && (bredr_sc_enabled(hcon->hdev) || test_bit(HCI_FORCE_BREDR_SMP, &hcon->hdev->dbg_flags))) conn->local_fixed_chan |= L2CAP_FC_SMP_BREDR; @@ -7112,7 +7112,7 @@ int l2cap_chan_connect(struct l2cap_chan *chan, __le16 psm, u16 cid, else dst_type = ADDR_LE_DEV_RANDOM; - if (test_bit(HCI_ADVERTISING, &hdev->dev_flags)) + if (hci_dev_test_flag(hdev, HCI_ADVERTISING)) role = HCI_ROLE_SLAVE; else role = HCI_ROLE_MASTER; diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 0761f2e0fefa..596b36111e64 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -385,7 +385,7 @@ static int read_index_list(struct sock *sk, struct hci_dev *hdev, void *data, count = 0; list_for_each_entry(d, &hci_dev_list, list) { if (d->dev_type == HCI_BREDR && - !test_bit(HCI_UNCONFIGURED, &d->dev_flags)) + !hci_dev_test_flag(d, HCI_UNCONFIGURED)) count++; } @@ -398,9 +398,9 @@ static int read_index_list(struct sock *sk, struct hci_dev *hdev, void *data, count = 0; list_for_each_entry(d, &hci_dev_list, list) { - if (test_bit(HCI_SETUP, &d->dev_flags) || - test_bit(HCI_CONFIG, &d->dev_flags) || - test_bit(HCI_USER_CHANNEL, &d->dev_flags)) + if (hci_dev_test_flag(d, HCI_SETUP) || + hci_dev_test_flag(d, HCI_CONFIG) || + hci_dev_test_flag(d, HCI_USER_CHANNEL)) continue; /* Devices marked as raw-only are neither configured @@ -410,7 +410,7 @@ static int read_index_list(struct sock *sk, struct hci_dev *hdev, void *data, continue; if (d->dev_type == HCI_BREDR && - !test_bit(HCI_UNCONFIGURED, &d->dev_flags)) { + !hci_dev_test_flag(d, HCI_UNCONFIGURED)) { rp->index[count++] = cpu_to_le16(d->id); BT_DBG("Added hci%u", d->id); } @@ -445,7 +445,7 @@ static int read_unconf_index_list(struct sock *sk, struct hci_dev *hdev, count = 0; list_for_each_entry(d, &hci_dev_list, list) { if (d->dev_type == HCI_BREDR && - test_bit(HCI_UNCONFIGURED, &d->dev_flags)) + hci_dev_test_flag(d, HCI_UNCONFIGURED)) count++; } @@ -458,9 +458,9 @@ static int read_unconf_index_list(struct sock *sk, struct hci_dev *hdev, count = 0; list_for_each_entry(d, &hci_dev_list, list) { - if (test_bit(HCI_SETUP, &d->dev_flags) || - test_bit(HCI_CONFIG, &d->dev_flags) || - test_bit(HCI_USER_CHANNEL, &d->dev_flags)) + if (hci_dev_test_flag(d, HCI_SETUP) || + hci_dev_test_flag(d, HCI_CONFIG) || + hci_dev_test_flag(d, HCI_USER_CHANNEL)) continue; /* Devices marked as raw-only are neither configured @@ -470,7 +470,7 @@ static int read_unconf_index_list(struct sock *sk, struct hci_dev *hdev, continue; if (d->dev_type == HCI_BREDR && - test_bit(HCI_UNCONFIGURED, &d->dev_flags)) { + hci_dev_test_flag(d, HCI_UNCONFIGURED)) { rp->index[count++] = cpu_to_le16(d->id); BT_DBG("Added hci%u", d->id); } @@ -492,7 +492,7 @@ static int read_unconf_index_list(struct sock *sk, struct hci_dev *hdev, static bool is_configured(struct hci_dev *hdev) { if (test_bit(HCI_QUIRK_EXTERNAL_CONFIG, &hdev->quirks) && - !test_bit(HCI_EXT_CONFIGURED, &hdev->dev_flags)) + !hci_dev_test_flag(hdev, HCI_EXT_CONFIGURED)) return false; if (test_bit(HCI_QUIRK_INVALID_BDADDR, &hdev->quirks) && @@ -507,7 +507,7 @@ static __le32 get_missing_options(struct hci_dev *hdev) u32 options = 0; if (test_bit(HCI_QUIRK_EXTERNAL_CONFIG, &hdev->quirks) && - !test_bit(HCI_EXT_CONFIGURED, &hdev->dev_flags)) + !hci_dev_test_flag(hdev, HCI_EXT_CONFIGURED)) options |= MGMT_OPTION_EXTERNAL_CONFIG; if (test_bit(HCI_QUIRK_INVALID_BDADDR, &hdev->quirks) && @@ -608,43 +608,43 @@ static u32 get_current_settings(struct hci_dev *hdev) if (hdev_is_powered(hdev)) settings |= MGMT_SETTING_POWERED; - if (test_bit(HCI_CONNECTABLE, &hdev->dev_flags)) + if (hci_dev_test_flag(hdev, HCI_CONNECTABLE)) settings |= MGMT_SETTING_CONNECTABLE; - if (test_bit(HCI_FAST_CONNECTABLE, &hdev->dev_flags)) + if (hci_dev_test_flag(hdev, HCI_FAST_CONNECTABLE)) settings |= MGMT_SETTING_FAST_CONNECTABLE; - if (test_bit(HCI_DISCOVERABLE, &hdev->dev_flags)) + if (hci_dev_test_flag(hdev, HCI_DISCOVERABLE)) settings |= MGMT_SETTING_DISCOVERABLE; - if (test_bit(HCI_BONDABLE, &hdev->dev_flags)) + if (hci_dev_test_flag(hdev, HCI_BONDABLE)) settings |= MGMT_SETTING_BONDABLE; - if (test_bit(HCI_BREDR_ENABLED, &hdev->dev_flags)) + if (hci_dev_test_flag(hdev, HCI_BREDR_ENABLED)) settings |= MGMT_SETTING_BREDR; - if (test_bit(HCI_LE_ENABLED, &hdev->dev_flags)) + if (hci_dev_test_flag(hdev, HCI_LE_ENABLED)) settings |= MGMT_SETTING_LE; - if (test_bit(HCI_LINK_SECURITY, &hdev->dev_flags)) + if (hci_dev_test_flag(hdev, HCI_LINK_SECURITY)) settings |= MGMT_SETTING_LINK_SECURITY; - if (test_bit(HCI_SSP_ENABLED, &hdev->dev_flags)) + if (hci_dev_test_flag(hdev, HCI_SSP_ENABLED)) settings |= MGMT_SETTING_SSP; - if (test_bit(HCI_HS_ENABLED, &hdev->dev_flags)) + if (hci_dev_test_flag(hdev, HCI_HS_ENABLED)) settings |= MGMT_SETTING_HS; - if (test_bit(HCI_ADVERTISING, &hdev->dev_flags)) + if (hci_dev_test_flag(hdev, HCI_ADVERTISING)) settings |= MGMT_SETTING_ADVERTISING; - if (test_bit(HCI_SC_ENABLED, &hdev->dev_flags)) + if (hci_dev_test_flag(hdev, HCI_SC_ENABLED)) settings |= MGMT_SETTING_SECURE_CONN; - if (test_bit(HCI_KEEP_DEBUG_KEYS, &hdev->dev_flags)) + if (hci_dev_test_flag(hdev, HCI_KEEP_DEBUG_KEYS)) settings |= MGMT_SETTING_DEBUG_KEYS; - if (test_bit(HCI_PRIVACY, &hdev->dev_flags)) + if (hci_dev_test_flag(hdev, HCI_PRIVACY)) settings |= MGMT_SETTING_PRIVACY; /* The current setting for static address has two purposes. The @@ -660,7 +660,7 @@ static u32 get_current_settings(struct hci_dev *hdev) * be evaluated. */ if (test_bit(HCI_FORCE_STATIC_ADDR, &hdev->dbg_flags) || - !test_bit(HCI_BREDR_ENABLED, &hdev->dev_flags) || + !hci_dev_test_flag(hdev, HCI_BREDR_ENABLED) || !bacmp(&hdev->bdaddr, BDADDR_ANY)) { if (bacmp(&hdev->static_addr, BDADDR_ANY)) settings |= MGMT_SETTING_STATIC_ADDRESS; @@ -840,7 +840,7 @@ static void update_scan_rsp_data(struct hci_request *req) struct hci_cp_le_set_scan_rsp_data cp; u8 len; - if (!test_bit(HCI_LE_ENABLED, &hdev->dev_flags)) + if (!hci_dev_test_flag(hdev, HCI_LE_ENABLED)) return; memset(&cp, 0, sizeof(cp)); @@ -874,9 +874,9 @@ static u8 get_adv_discov_flags(struct hci_dev *hdev) else if (cp->val == 0x02) return LE_AD_LIMITED; } else { - if (test_bit(HCI_LIMITED_DISCOVERABLE, &hdev->dev_flags)) + if (hci_dev_test_flag(hdev, HCI_LIMITED_DISCOVERABLE)) return LE_AD_LIMITED; - else if (test_bit(HCI_DISCOVERABLE, &hdev->dev_flags)) + else if (hci_dev_test_flag(hdev, HCI_DISCOVERABLE)) return LE_AD_GENERAL; } @@ -889,7 +889,7 @@ static u8 create_adv_data(struct hci_dev *hdev, u8 *ptr) flags |= get_adv_discov_flags(hdev); - if (!test_bit(HCI_BREDR_ENABLED, &hdev->dev_flags)) + if (!hci_dev_test_flag(hdev, HCI_BREDR_ENABLED)) flags |= LE_AD_NO_BREDR; if (flags) { @@ -921,7 +921,7 @@ static void update_adv_data(struct hci_request *req) struct hci_cp_le_set_adv_data cp; u8 len; - if (!test_bit(HCI_LE_ENABLED, &hdev->dev_flags)) + if (!hci_dev_test_flag(hdev, HCI_LE_ENABLED)) return; memset(&cp, 0, sizeof(cp)); @@ -1009,10 +1009,10 @@ static void update_eir(struct hci_request *req) if (!lmp_ext_inq_capable(hdev)) return; - if (!test_bit(HCI_SSP_ENABLED, &hdev->dev_flags)) + if (!hci_dev_test_flag(hdev, HCI_SSP_ENABLED)) return; - if (test_bit(HCI_SERVICE_CACHE, &hdev->dev_flags)) + if (hci_dev_test_flag(hdev, HCI_SERVICE_CACHE)) return; memset(&cp, 0, sizeof(cp)); @@ -1048,17 +1048,17 @@ static void update_class(struct hci_request *req) if (!hdev_is_powered(hdev)) return; - if (!test_bit(HCI_BREDR_ENABLED, &hdev->dev_flags)) + if (!hci_dev_test_flag(hdev, HCI_BREDR_ENABLED)) return; - if (test_bit(HCI_SERVICE_CACHE, &hdev->dev_flags)) + if (hci_dev_test_flag(hdev, HCI_SERVICE_CACHE)) return; cod[0] = hdev->minor_class; cod[1] = hdev->major_class; cod[2] = get_service_classes(hdev); - if (test_bit(HCI_LIMITED_DISCOVERABLE, &hdev->dev_flags)) + if (hci_dev_test_flag(hdev, HCI_LIMITED_DISCOVERABLE)) cod[1] |= 0x20; if (memcmp(cod, hdev->dev_class, 3) == 0) @@ -1080,7 +1080,7 @@ static bool get_connectable(struct hci_dev *hdev) return cp->val; } - return test_bit(HCI_CONNECTABLE, &hdev->dev_flags); + return hci_dev_test_flag(hdev, HCI_CONNECTABLE); } static void disable_advertising(struct hci_request *req) @@ -1100,7 +1100,7 @@ static void enable_advertising(struct hci_request *req) if (hci_conn_num(hdev, LE_LINK) > 0) return; - if (test_bit(HCI_LE_ADV, &hdev->dev_flags)) + if (hci_dev_test_flag(hdev, HCI_LE_ADV)) disable_advertising(req); /* Clear the HCI_LE_ADV bit temporarily so that the @@ -1110,7 +1110,7 @@ static void enable_advertising(struct hci_request *req) */ clear_bit(HCI_LE_ADV, &hdev->dev_flags); - if (test_bit(HCI_ADVERTISING_CONNECTABLE, &hdev->dev_flags)) + if (hci_dev_test_flag(hdev, HCI_ADVERTISING_CONNECTABLE)) connectable = true; else connectable = get_connectable(hdev); @@ -1165,7 +1165,7 @@ static void rpa_expired(struct work_struct *work) set_bit(HCI_RPA_EXPIRED, &hdev->dev_flags); - if (!test_bit(HCI_ADVERTISING, &hdev->dev_flags)) + if (!hci_dev_test_flag(hdev, HCI_ADVERTISING)) return; /* The generation of a new RPA and programming it into the @@ -1328,7 +1328,7 @@ static bool hci_stop_discovery(struct hci_request *req) default: /* Passive scanning */ - if (test_bit(HCI_LE_SCAN, &hdev->dev_flags)) { + if (hci_dev_test_flag(hdev, HCI_LE_SCAN)) { hci_req_add_le_scan_disable(req); return true; } @@ -1354,7 +1354,7 @@ static int clean_up_hci_state(struct hci_dev *hdev) hci_req_add(&req, HCI_OP_WRITE_SCAN_ENABLE, 1, &scan); } - if (test_bit(HCI_LE_ADV, &hdev->dev_flags)) + if (hci_dev_test_flag(hdev, HCI_LE_ADV)) disable_advertising(&req); discov_stopped = hci_stop_discovery(&req); @@ -1538,7 +1538,7 @@ static u8 mgmt_bredr_support(struct hci_dev *hdev) { if (!lmp_bredr_capable(hdev)) return MGMT_STATUS_NOT_SUPPORTED; - else if (!test_bit(HCI_BREDR_ENABLED, &hdev->dev_flags)) + else if (!hci_dev_test_flag(hdev, HCI_BREDR_ENABLED)) return MGMT_STATUS_REJECTED; else return MGMT_STATUS_SUCCESS; @@ -1548,7 +1548,7 @@ static u8 mgmt_le_support(struct hci_dev *hdev) { if (!lmp_le_capable(hdev)) return MGMT_STATUS_NOT_SUPPORTED; - else if (!test_bit(HCI_LE_ENABLED, &hdev->dev_flags)) + else if (!hci_dev_test_flag(hdev, HCI_LE_ENABLED)) return MGMT_STATUS_REJECTED; else return MGMT_STATUS_SUCCESS; @@ -1626,8 +1626,8 @@ static int set_discoverable(struct sock *sk, struct hci_dev *hdev, void *data, BT_DBG("request for %s", hdev->name); - if (!test_bit(HCI_LE_ENABLED, &hdev->dev_flags) && - !test_bit(HCI_BREDR_ENABLED, &hdev->dev_flags)) + if (!hci_dev_test_flag(hdev, HCI_LE_ENABLED) && + !hci_dev_test_flag(hdev, HCI_BREDR_ENABLED)) return mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_DISCOVERABLE, MGMT_STATUS_REJECTED); @@ -1660,7 +1660,7 @@ static int set_discoverable(struct sock *sk, struct hci_dev *hdev, void *data, goto failed; } - if (!test_bit(HCI_CONNECTABLE, &hdev->dev_flags)) { + if (!hci_dev_test_flag(hdev, HCI_CONNECTABLE)) { err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_DISCOVERABLE, MGMT_STATUS_REJECTED); goto failed; @@ -1673,7 +1673,7 @@ static int set_discoverable(struct sock *sk, struct hci_dev *hdev, void *data, * not a valid operation since it requires a timeout * and so no need to check HCI_LIMITED_DISCOVERABLE. */ - if (!!cp->val != test_bit(HCI_DISCOVERABLE, &hdev->dev_flags)) { + if (!!cp->val != hci_dev_test_flag(hdev, HCI_DISCOVERABLE)) { change_bit(HCI_DISCOVERABLE, &hdev->dev_flags); changed = true; } @@ -1692,9 +1692,9 @@ static int set_discoverable(struct sock *sk, struct hci_dev *hdev, void *data, * value with the new value. And if only the timeout gets updated, * then no need for any HCI transactions. */ - if (!!cp->val == test_bit(HCI_DISCOVERABLE, &hdev->dev_flags) && - (cp->val == 0x02) == test_bit(HCI_LIMITED_DISCOVERABLE, - &hdev->dev_flags)) { + if (!!cp->val == hci_dev_test_flag(hdev, HCI_DISCOVERABLE) && + (cp->val == 0x02) == hci_dev_test_flag(hdev, + HCI_LIMITED_DISCOVERABLE)) { cancel_delayed_work(&hdev->discov_off); hdev->discov_timeout = timeout; @@ -1732,7 +1732,7 @@ static int set_discoverable(struct sock *sk, struct hci_dev *hdev, void *data, /* The procedure for LE-only controllers is much simpler - just * update the advertising data. */ - if (!test_bit(HCI_BREDR_ENABLED, &hdev->dev_flags)) + if (!hci_dev_test_flag(hdev, HCI_BREDR_ENABLED)) goto update_ad; scan = SCAN_PAGE; @@ -1785,7 +1785,7 @@ static void write_fast_connectable(struct hci_request *req, bool enable) struct hci_cp_write_page_scan_activity acp; u8 type; - if (!test_bit(HCI_BREDR_ENABLED, &hdev->dev_flags)) + if (!hci_dev_test_flag(hdev, HCI_BREDR_ENABLED)) return; if (hdev->hci_ver < BLUETOOTH_VER_1_2) @@ -1870,7 +1870,7 @@ static int set_connectable_update_settings(struct hci_dev *hdev, bool changed = false; int err; - if (!!val != test_bit(HCI_CONNECTABLE, &hdev->dev_flags)) + if (!!val != hci_dev_test_flag(hdev, HCI_CONNECTABLE)) changed = true; if (val) { @@ -1904,8 +1904,8 @@ static int set_connectable(struct sock *sk, struct hci_dev *hdev, void *data, BT_DBG("request for %s", hdev->name); - if (!test_bit(HCI_LE_ENABLED, &hdev->dev_flags) && - !test_bit(HCI_BREDR_ENABLED, &hdev->dev_flags)) + if (!hci_dev_test_flag(hdev, HCI_LE_ENABLED) && + !hci_dev_test_flag(hdev, HCI_BREDR_ENABLED)) return mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_CONNECTABLE, MGMT_STATUS_REJECTED); @@ -1939,7 +1939,7 @@ static int set_connectable(struct sock *sk, struct hci_dev *hdev, void *data, * by-product of disabling connectable, we need to update the * advertising flags. */ - if (!test_bit(HCI_BREDR_ENABLED, &hdev->dev_flags)) { + if (!hci_dev_test_flag(hdev, HCI_BREDR_ENABLED)) { if (!cp->val) { clear_bit(HCI_LIMITED_DISCOVERABLE, &hdev->dev_flags); clear_bit(HCI_DISCOVERABLE, &hdev->dev_flags); @@ -1972,7 +1972,7 @@ static int set_connectable(struct sock *sk, struct hci_dev *hdev, void *data, no_scan_update: /* Update the advertising parameters if necessary */ - if (test_bit(HCI_ADVERTISING, &hdev->dev_flags)) + if (hci_dev_test_flag(hdev, HCI_ADVERTISING)) enable_advertising(&req); err = hci_req_run(&req, set_connectable_complete); @@ -2045,8 +2045,7 @@ static int set_link_security(struct sock *sk, struct hci_dev *hdev, void *data, if (!hdev_is_powered(hdev)) { bool changed = false; - if (!!cp->val != test_bit(HCI_LINK_SECURITY, - &hdev->dev_flags)) { + if (!!cp->val != hci_dev_test_flag(hdev, HCI_LINK_SECURITY)) { change_bit(HCI_LINK_SECURITY, &hdev->dev_flags); changed = true; } @@ -2146,7 +2145,7 @@ static int set_ssp(struct sock *sk, struct hci_dev *hdev, void *data, u16 len) goto failed; } - if (!!cp->val == test_bit(HCI_SSP_ENABLED, &hdev->dev_flags)) { + if (!!cp->val == hci_dev_test_flag(hdev, HCI_SSP_ENABLED)) { err = send_settings_rsp(sk, MGMT_OP_SET_SSP, hdev); goto failed; } @@ -2157,7 +2156,7 @@ static int set_ssp(struct sock *sk, struct hci_dev *hdev, void *data, u16 len) goto failed; } - if (!cp->val && test_bit(HCI_USE_DEBUG_KEYS, &hdev->dev_flags)) + if (!cp->val && hci_dev_test_flag(hdev, HCI_USE_DEBUG_KEYS)) hci_send_cmd(hdev, HCI_OP_WRITE_SSP_DEBUG_MODE, sizeof(cp->val), &cp->val); @@ -2189,7 +2188,7 @@ static int set_hs(struct sock *sk, struct hci_dev *hdev, void *data, u16 len) return mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_HS, MGMT_STATUS_NOT_SUPPORTED); - if (!test_bit(HCI_SSP_ENABLED, &hdev->dev_flags)) + if (!hci_dev_test_flag(hdev, HCI_SSP_ENABLED)) return mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_HS, MGMT_STATUS_REJECTED); @@ -2255,7 +2254,7 @@ static void le_enable_complete(struct hci_dev *hdev, u8 status, u16 opcode) * has actually been enabled. During power on, the * update in powered_update_hci will take care of it. */ - if (test_bit(HCI_LE_ENABLED, &hdev->dev_flags)) { + if (hci_dev_test_flag(hdev, HCI_LE_ENABLED)) { struct hci_request req; hci_req_init(&req, hdev); @@ -2289,7 +2288,7 @@ static int set_le(struct sock *sk, struct hci_dev *hdev, void *data, u16 len) MGMT_STATUS_INVALID_PARAMS); /* LE-only devices do not allow toggling LE on/off */ - if (!test_bit(HCI_BREDR_ENABLED, &hdev->dev_flags)) + if (!hci_dev_test_flag(hdev, HCI_BREDR_ENABLED)) return mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_LE, MGMT_STATUS_REJECTED); @@ -2301,12 +2300,12 @@ static int set_le(struct sock *sk, struct hci_dev *hdev, void *data, u16 len) if (!hdev_is_powered(hdev) || val == enabled) { bool changed = false; - if (val != test_bit(HCI_LE_ENABLED, &hdev->dev_flags)) { + if (val != hci_dev_test_flag(hdev, HCI_LE_ENABLED)) { change_bit(HCI_LE_ENABLED, &hdev->dev_flags); changed = true; } - if (!val && test_bit(HCI_ADVERTISING, &hdev->dev_flags)) { + if (!val && hci_dev_test_flag(hdev, HCI_ADVERTISING)) { clear_bit(HCI_ADVERTISING, &hdev->dev_flags); changed = true; } @@ -2342,7 +2341,7 @@ static int set_le(struct sock *sk, struct hci_dev *hdev, void *data, u16 len) hci_cp.le = val; hci_cp.simul = 0x00; } else { - if (test_bit(HCI_LE_ADV, &hdev->dev_flags)) + if (hci_dev_test_flag(hdev, HCI_LE_ADV)) disable_advertising(&req); } @@ -3860,12 +3859,12 @@ static bool trigger_discovery(struct hci_request *req, u8 *status) return false; if (hdev->discovery.type == DISCOV_TYPE_INTERLEAVED && - !test_bit(HCI_BREDR_ENABLED, &hdev->dev_flags)) { + !hci_dev_test_flag(hdev, HCI_BREDR_ENABLED)) { *status = MGMT_STATUS_NOT_SUPPORTED; return false; } - if (test_bit(HCI_LE_ADV, &hdev->dev_flags)) { + if (hci_dev_test_flag(hdev, HCI_LE_ADV)) { /* Don't let discovery abort an outgoing * connection attempt that's using directed * advertising. @@ -3883,7 +3882,7 @@ static bool trigger_discovery(struct hci_request *req, u8 *status) * is running. Thus, we should temporarily stop it in order to * set the discovery scanning parameters. */ - if (test_bit(HCI_LE_SCAN, &hdev->dev_flags)) + if (hci_dev_test_flag(hdev, HCI_LE_SCAN)) hci_req_add_le_scan_disable(req); memset(¶m_cp, 0, sizeof(param_cp)); @@ -4007,7 +4006,7 @@ static int start_discovery(struct sock *sk, struct hci_dev *hdev, } if (hdev->discovery.state != DISCOVERY_STOPPED || - test_bit(HCI_PERIODIC_INQ, &hdev->dev_flags)) { + hci_dev_test_flag(hdev, HCI_PERIODIC_INQ)) { err = mgmt_cmd_complete(sk, hdev->id, MGMT_OP_START_DISCOVERY, MGMT_STATUS_BUSY, &cp->type, sizeof(cp->type)); @@ -4083,7 +4082,7 @@ static int start_service_discovery(struct sock *sk, struct hci_dev *hdev, } if (hdev->discovery.state != DISCOVERY_STOPPED || - test_bit(HCI_PERIODIC_INQ, &hdev->dev_flags)) { + hci_dev_test_flag(hdev, HCI_PERIODIC_INQ)) { err = mgmt_cmd_complete(sk, hdev->id, MGMT_OP_START_SERVICE_DISCOVERY, MGMT_STATUS_BUSY, &cp->type, @@ -4410,7 +4409,7 @@ static void set_advertising_complete(struct hci_dev *hdev, u8 status, goto unlock; } - if (test_bit(HCI_LE_ADV, &hdev->dev_flags)) + if (hci_dev_test_flag(hdev, HCI_LE_ADV)) set_bit(HCI_ADVERTISING, &hdev->dev_flags); else clear_bit(HCI_ADVERTISING, &hdev->dev_flags); @@ -4457,11 +4456,10 @@ static int set_advertising(struct sock *sk, struct hci_dev *hdev, void *data, * necessary). */ if (!hdev_is_powered(hdev) || - (val == test_bit(HCI_ADVERTISING, &hdev->dev_flags) && - (cp->val == 0x02) == test_bit(HCI_ADVERTISING_CONNECTABLE, - &hdev->dev_flags)) || + (val == hci_dev_test_flag(hdev, HCI_ADVERTISING) && + (cp->val == 0x02) == hci_dev_test_flag(hdev, HCI_ADVERTISING_CONNECTABLE)) || hci_conn_num(hdev, LE_LINK) > 0 || - (test_bit(HCI_LE_SCAN, &hdev->dev_flags) && + (hci_dev_test_flag(hdev, HCI_LE_SCAN) && hdev->le_scan_type == LE_SCAN_ACTIVE)) { bool changed; @@ -4609,7 +4607,7 @@ static int set_scan_params(struct sock *sk, struct hci_dev *hdev, /* If background scan is running, restart it so new parameters are * loaded. */ - if (test_bit(HCI_LE_SCAN, &hdev->dev_flags) && + if (hci_dev_test_flag(hdev, HCI_LE_SCAN) && hdev->discovery.state == DISCOVERY_STOPPED) { struct hci_request req; @@ -4670,7 +4668,7 @@ static int set_fast_connectable(struct sock *sk, struct hci_dev *hdev, BT_DBG("%s", hdev->name); - if (!test_bit(HCI_BREDR_ENABLED, &hdev->dev_flags) || + if (!hci_dev_test_flag(hdev, HCI_BREDR_ENABLED) || hdev->hci_ver < BLUETOOTH_VER_1_2) return mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_FAST_CONNECTABLE, MGMT_STATUS_NOT_SUPPORTED); @@ -4687,7 +4685,7 @@ static int set_fast_connectable(struct sock *sk, struct hci_dev *hdev, goto unlock; } - if (!!cp->val == test_bit(HCI_FAST_CONNECTABLE, &hdev->dev_flags)) { + if (!!cp->val == hci_dev_test_flag(hdev, HCI_FAST_CONNECTABLE)) { err = send_settings_rsp(sk, MGMT_OP_SET_FAST_CONNECTABLE, hdev); goto unlock; @@ -4770,7 +4768,7 @@ static int set_bredr(struct sock *sk, struct hci_dev *hdev, void *data, u16 len) return mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_BREDR, MGMT_STATUS_NOT_SUPPORTED); - if (!test_bit(HCI_LE_ENABLED, &hdev->dev_flags)) + if (!hci_dev_test_flag(hdev, HCI_LE_ENABLED)) return mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_BREDR, MGMT_STATUS_REJECTED); @@ -4780,7 +4778,7 @@ static int set_bredr(struct sock *sk, struct hci_dev *hdev, void *data, u16 len) hci_dev_lock(hdev); - if (cp->val == test_bit(HCI_BREDR_ENABLED, &hdev->dev_flags)) { + if (cp->val == hci_dev_test_flag(hdev, HCI_BREDR_ENABLED)) { err = send_settings_rsp(sk, MGMT_OP_SET_BREDR, hdev); goto unlock; } @@ -4824,9 +4822,9 @@ static int set_bredr(struct sock *sk, struct hci_dev *hdev, void *data, u16 len) * switching BR/EDR back on when secure connections has been * enabled is not a supported transaction. */ - if (!test_bit(HCI_BREDR_ENABLED, &hdev->dev_flags) && + if (!hci_dev_test_flag(hdev, HCI_BREDR_ENABLED) && (bacmp(&hdev->static_addr, BDADDR_ANY) || - test_bit(HCI_SC_ENABLED, &hdev->dev_flags))) { + hci_dev_test_flag(hdev, HCI_SC_ENABLED))) { err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_BREDR, MGMT_STATUS_REJECTED); goto unlock; @@ -4926,13 +4924,13 @@ static int set_secure_conn(struct sock *sk, struct hci_dev *hdev, BT_DBG("request for %s", hdev->name); if (!lmp_sc_capable(hdev) && - !test_bit(HCI_LE_ENABLED, &hdev->dev_flags)) + !hci_dev_test_flag(hdev, HCI_LE_ENABLED)) return mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_SECURE_CONN, MGMT_STATUS_NOT_SUPPORTED); - if (test_bit(HCI_BREDR_ENABLED, &hdev->dev_flags) && + if (hci_dev_test_flag(hdev, HCI_BREDR_ENABLED) && lmp_sc_capable(hdev) && - !test_bit(HCI_SSP_ENABLED, &hdev->dev_flags)) + !hci_dev_test_flag(hdev, HCI_SSP_ENABLED)) return mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_SECURE_CONN, MGMT_STATUS_REJECTED); @@ -4943,7 +4941,7 @@ static int set_secure_conn(struct sock *sk, struct hci_dev *hdev, hci_dev_lock(hdev); if (!hdev_is_powered(hdev) || !lmp_sc_capable(hdev) || - !test_bit(HCI_BREDR_ENABLED, &hdev->dev_flags)) { + !hci_dev_test_flag(hdev, HCI_BREDR_ENABLED)) { bool changed; if (cp->val) { @@ -4977,8 +4975,8 @@ static int set_secure_conn(struct sock *sk, struct hci_dev *hdev, val = !!cp->val; - if (val == test_bit(HCI_SC_ENABLED, &hdev->dev_flags) && - (cp->val == 0x02) == test_bit(HCI_SC_ONLY, &hdev->dev_flags)) { + if (val == hci_dev_test_flag(hdev, HCI_SC_ENABLED) && + (cp->val == 0x02) == hci_dev_test_flag(hdev, HCI_SC_ONLY)) { err = send_settings_rsp(sk, MGMT_OP_SET_SECURE_CONN, hdev); goto failed; } @@ -5032,7 +5030,7 @@ static int set_debug_keys(struct sock *sk, struct hci_dev *hdev, &hdev->dev_flags); if (hdev_is_powered(hdev) && use_changed && - test_bit(HCI_SSP_ENABLED, &hdev->dev_flags)) { + hci_dev_test_flag(hdev, HCI_SSP_ENABLED)) { u8 mode = (cp->val == 0x02) ? 0x01 : 0x00; hci_send_cmd(hdev, HCI_OP_WRITE_SSP_DEBUG_MODE, sizeof(mode), &mode); @@ -6104,7 +6102,7 @@ static int set_external_config(struct sock *sk, struct hci_dev *hdev, err = new_options(hdev, sk); - if (test_bit(HCI_UNCONFIGURED, &hdev->dev_flags) == is_configured(hdev)) { + if (hci_dev_test_flag(hdev, HCI_UNCONFIGURED) == is_configured(hdev)) { mgmt_index_removed(hdev); if (test_and_change_bit(HCI_UNCONFIGURED, &hdev->dev_flags)) { @@ -6156,7 +6154,7 @@ static int set_public_address(struct sock *sk, struct hci_dev *hdev, if (!changed) goto unlock; - if (test_bit(HCI_UNCONFIGURED, &hdev->dev_flags)) + if (hci_dev_test_flag(hdev, HCI_UNCONFIGURED)) err = new_options(hdev, sk); if (is_configured(hdev)) { @@ -6304,15 +6302,15 @@ int mgmt_control(struct hci_mgmt_chan *chan, struct sock *sk, goto done; } - if (test_bit(HCI_SETUP, &hdev->dev_flags) || - test_bit(HCI_CONFIG, &hdev->dev_flags) || - test_bit(HCI_USER_CHANNEL, &hdev->dev_flags)) { + if (hci_dev_test_flag(hdev, HCI_SETUP) || + hci_dev_test_flag(hdev, HCI_CONFIG) || + hci_dev_test_flag(hdev, HCI_USER_CHANNEL)) { err = mgmt_cmd_status(sk, index, opcode, MGMT_STATUS_INVALID_INDEX); goto done; } - if (test_bit(HCI_UNCONFIGURED, &hdev->dev_flags) && + if (hci_dev_test_flag(hdev, HCI_UNCONFIGURED) && !(handler->flags & HCI_MGMT_UNCONFIGURED)) { err = mgmt_cmd_status(sk, index, opcode, MGMT_STATUS_INVALID_INDEX); @@ -6362,7 +6360,7 @@ void mgmt_index_added(struct hci_dev *hdev) if (test_bit(HCI_QUIRK_RAW_DEVICE, &hdev->quirks)) return; - if (test_bit(HCI_UNCONFIGURED, &hdev->dev_flags)) + if (hci_dev_test_flag(hdev, HCI_UNCONFIGURED)) mgmt_event(MGMT_EV_UNCONF_INDEX_ADDED, hdev, NULL, 0, NULL); else mgmt_event(MGMT_EV_INDEX_ADDED, hdev, NULL, 0, NULL); @@ -6380,7 +6378,7 @@ void mgmt_index_removed(struct hci_dev *hdev) mgmt_pending_foreach(0, hdev, cmd_complete_rsp, &status); - if (test_bit(HCI_UNCONFIGURED, &hdev->dev_flags)) + if (hci_dev_test_flag(hdev, HCI_UNCONFIGURED)) mgmt_event(MGMT_EV_UNCONF_INDEX_REMOVED, hdev, NULL, 0, NULL); else mgmt_event(MGMT_EV_INDEX_REMOVED, hdev, NULL, 0, NULL); @@ -6448,7 +6446,7 @@ static int powered_update_hci(struct hci_dev *hdev) hci_req_init(&req, hdev); - if (test_bit(HCI_SSP_ENABLED, &hdev->dev_flags) && + if (hci_dev_test_flag(hdev, HCI_SSP_ENABLED) && !lmp_host_ssp_capable(hdev)) { u8 mode = 0x01; @@ -6462,7 +6460,7 @@ static int powered_update_hci(struct hci_dev *hdev) } } - if (test_bit(HCI_LE_ENABLED, &hdev->dev_flags) && + if (hci_dev_test_flag(hdev, HCI_LE_ENABLED) && lmp_bredr_capable(hdev)) { struct hci_cp_write_le_host_supported cp; @@ -6483,24 +6481,24 @@ static int powered_update_hci(struct hci_dev *hdev) * advertising data. This also applies to the case * where BR/EDR was toggled during the AUTO_OFF phase. */ - if (test_bit(HCI_LE_ENABLED, &hdev->dev_flags)) { + if (hci_dev_test_flag(hdev, HCI_LE_ENABLED)) { update_adv_data(&req); update_scan_rsp_data(&req); } - if (test_bit(HCI_ADVERTISING, &hdev->dev_flags)) + if (hci_dev_test_flag(hdev, HCI_ADVERTISING)) enable_advertising(&req); restart_le_actions(&req); } - link_sec = test_bit(HCI_LINK_SECURITY, &hdev->dev_flags); + link_sec = hci_dev_test_flag(hdev, HCI_LINK_SECURITY); if (link_sec != test_bit(HCI_AUTH, &hdev->flags)) hci_req_add(&req, HCI_OP_WRITE_AUTH_ENABLE, sizeof(link_sec), &link_sec); if (lmp_bredr_capable(hdev)) { - if (test_bit(HCI_FAST_CONNECTABLE, &hdev->dev_flags)) + if (hci_dev_test_flag(hdev, HCI_FAST_CONNECTABLE)) write_fast_connectable(&req, true); else write_fast_connectable(&req, false); @@ -6519,7 +6517,7 @@ int mgmt_powered(struct hci_dev *hdev, u8 powered) u8 status, zero_cod[] = { 0, 0, 0 }; int err; - if (!test_bit(HCI_MGMT, &hdev->dev_flags)) + if (!hci_dev_test_flag(hdev, HCI_MGMT)) return 0; if (powered) { @@ -6540,7 +6538,7 @@ int mgmt_powered(struct hci_dev *hdev, u8 powered) * been triggered, potentially causing misleading DISCONNECTED * status responses. */ - if (test_bit(HCI_UNREGISTER, &hdev->dev_flags)) + if (hci_dev_test_flag(hdev, HCI_UNREGISTER)) status = MGMT_STATUS_INVALID_INDEX; else status = MGMT_STATUS_NOT_POWERED; @@ -6594,7 +6592,7 @@ void mgmt_discoverable_timeout(struct hci_dev *hdev) clear_bit(HCI_DISCOVERABLE, &hdev->dev_flags); hci_req_init(&req, hdev); - if (test_bit(HCI_BREDR_ENABLED, &hdev->dev_flags)) { + if (hci_dev_test_flag(hdev, HCI_BREDR_ENABLED)) { u8 scan = SCAN_PAGE; hci_req_add(&req, HCI_OP_WRITE_SCAN_ENABLE, sizeof(scan), &scan); @@ -7170,8 +7168,8 @@ void mgmt_ssp_enable_complete(struct hci_dev *hdev, u8 enable, u8 status) hci_req_init(&req, hdev); - if (test_bit(HCI_SSP_ENABLED, &hdev->dev_flags)) { - if (test_bit(HCI_USE_DEBUG_KEYS, &hdev->dev_flags)) + if (hci_dev_test_flag(hdev, HCI_SSP_ENABLED)) { + if (hci_dev_test_flag(hdev, HCI_USE_DEBUG_KEYS)) hci_req_add(&req, HCI_OP_WRITE_SSP_DEBUG_MODE, sizeof(enable), &enable); update_eir(&req); @@ -7343,7 +7341,7 @@ static bool eir_has_uuids(u8 *eir, u16 eir_len, u16 uuid_count, u8 (*uuids)[16]) static void restart_le_scan(struct hci_dev *hdev) { /* If controller is not scanning we are done. */ - if (!test_bit(HCI_LE_SCAN, &hdev->dev_flags)) + if (!hci_dev_test_flag(hdev, HCI_LE_SCAN)) return; if (time_after(jiffies + DISCOV_LE_RESTART_DELAY, @@ -7514,7 +7512,7 @@ void mgmt_reenable_advertising(struct hci_dev *hdev) { struct hci_request req; - if (!test_bit(HCI_ADVERTISING, &hdev->dev_flags)) + if (!hci_dev_test_flag(hdev, HCI_ADVERTISING)) return; hci_req_init(&req, hdev); diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c index d6ef7e48c2c3..81975f274c2b 100644 --- a/net/bluetooth/smp.c +++ b/net/bluetooth/smp.c @@ -52,7 +52,7 @@ #define SMP_TIMEOUT msecs_to_jiffies(30000) -#define AUTH_REQ_MASK(dev) (test_bit(HCI_SC_ENABLED, &(dev)->dev_flags) ? \ +#define AUTH_REQ_MASK(dev) (hci_dev_test_flag(dev, HCI_SC_ENABLED) ? \ 0x1f : 0x07) #define KEY_DIST_MASK 0x07 @@ -589,7 +589,7 @@ static void build_pairing_cmd(struct l2cap_conn *conn, struct hci_dev *hdev = hcon->hdev; u8 local_dist = 0, remote_dist = 0, oob_flag = SMP_OOB_NOT_PRESENT; - if (test_bit(HCI_BONDABLE, &conn->hcon->hdev->dev_flags)) { + if (hci_dev_test_flag(hdev, HCI_BONDABLE)) { local_dist = SMP_DIST_ENC_KEY | SMP_DIST_SIGN; remote_dist = SMP_DIST_ENC_KEY | SMP_DIST_SIGN; authreq |= SMP_AUTH_BONDING; @@ -597,18 +597,18 @@ static void build_pairing_cmd(struct l2cap_conn *conn, authreq &= ~SMP_AUTH_BONDING; } - if (test_bit(HCI_RPA_RESOLVING, &hdev->dev_flags)) + if (hci_dev_test_flag(hdev, HCI_RPA_RESOLVING)) remote_dist |= SMP_DIST_ID_KEY; - if (test_bit(HCI_PRIVACY, &hdev->dev_flags)) + if (hci_dev_test_flag(hdev, HCI_PRIVACY)) local_dist |= SMP_DIST_ID_KEY; - if (test_bit(HCI_SC_ENABLED, &hdev->dev_flags) && + if (hci_dev_test_flag(hdev, HCI_SC_ENABLED) && (authreq & SMP_AUTH_SC)) { struct oob_data *oob_data; u8 bdaddr_type; - if (test_bit(HCI_SSP_ENABLED, &hdev->dev_flags)) { + if (hci_dev_test_flag(hdev, HCI_SSP_ENABLED)) { local_dist |= SMP_DIST_LINK_KEY; remote_dist |= SMP_DIST_LINK_KEY; } @@ -692,7 +692,7 @@ static void smp_chan_destroy(struct l2cap_conn *conn) * support hasn't been explicitly enabled. */ if (smp->ltk && smp->ltk->type == SMP_LTK_P256_DEBUG && - !test_bit(HCI_KEEP_DEBUG_KEYS, &hcon->hdev->dev_flags)) { + !hci_dev_test_flag(hcon->hdev, HCI_KEEP_DEBUG_KEYS)) { list_del_rcu(&smp->ltk->list); kfree_rcu(smp->ltk, rcu); smp->ltk = NULL; @@ -1052,7 +1052,7 @@ static void smp_notify_keys(struct l2cap_conn *conn) /* Don't keep debug keys around if the relevant * flag is not set. */ - if (!test_bit(HCI_KEEP_DEBUG_KEYS, &hdev->dev_flags) && + if (!hci_dev_test_flag(hdev, HCI_KEEP_DEBUG_KEYS) && key->type == HCI_LK_DEBUG_COMBINATION) { list_del_rcu(&key->list); kfree_rcu(key, rcu); @@ -1604,15 +1604,15 @@ static void build_bredr_pairing_cmd(struct smp_chan *smp, struct hci_dev *hdev = conn->hcon->hdev; u8 local_dist = 0, remote_dist = 0; - if (test_bit(HCI_BONDABLE, &hdev->dev_flags)) { + if (hci_dev_test_flag(hdev, HCI_BONDABLE)) { local_dist = SMP_DIST_ENC_KEY | SMP_DIST_SIGN; remote_dist = SMP_DIST_ENC_KEY | SMP_DIST_SIGN; } - if (test_bit(HCI_RPA_RESOLVING, &hdev->dev_flags)) + if (hci_dev_test_flag(hdev, HCI_RPA_RESOLVING)) remote_dist |= SMP_DIST_ID_KEY; - if (test_bit(HCI_PRIVACY, &hdev->dev_flags)) + if (hci_dev_test_flag(hdev, HCI_PRIVACY)) local_dist |= SMP_DIST_ID_KEY; if (!rsp) { @@ -1664,11 +1664,11 @@ static u8 smp_cmd_pairing_req(struct l2cap_conn *conn, struct sk_buff *skb) /* We didn't start the pairing, so match remote */ auth = req->auth_req & AUTH_REQ_MASK(hdev); - if (!test_bit(HCI_BONDABLE, &hdev->dev_flags) && + if (!hci_dev_test_flag(hdev, HCI_BONDABLE) && (auth & SMP_AUTH_BONDING)) return SMP_PAIRING_NOTSUPP; - if (test_bit(HCI_SC_ONLY, &hdev->dev_flags) && !(auth & SMP_AUTH_SC)) + if (hci_dev_test_flag(hdev, HCI_SC_ONLY) && !(auth & SMP_AUTH_SC)) return SMP_AUTH_REQUIREMENTS; smp->preq[0] = SMP_CMD_PAIRING_REQ; @@ -1761,7 +1761,7 @@ static u8 sc_send_public_key(struct smp_chan *smp) BT_DBG(""); - if (test_bit(HCI_USE_DEBUG_KEYS, &hdev->dev_flags)) { + if (hci_dev_test_flag(hdev, HCI_USE_DEBUG_KEYS)) { BT_DBG("Using debug keys"); memcpy(smp->local_pk, debug_pk, 64); memcpy(smp->local_sk, debug_sk, 32); @@ -1816,7 +1816,7 @@ static u8 smp_cmd_pairing_rsp(struct l2cap_conn *conn, struct sk_buff *skb) auth = rsp->auth_req & AUTH_REQ_MASK(hdev); - if (test_bit(HCI_SC_ONLY, &hdev->dev_flags) && !(auth & SMP_AUTH_SC)) + if (hci_dev_test_flag(hdev, HCI_SC_ONLY) && !(auth & SMP_AUTH_SC)) return SMP_AUTH_REQUIREMENTS; smp->prsp[0] = SMP_CMD_PAIRING_RSP; @@ -2086,7 +2086,7 @@ static u8 smp_cmd_security_req(struct l2cap_conn *conn, struct sk_buff *skb) auth = rp->auth_req & AUTH_REQ_MASK(hdev); - if (test_bit(HCI_SC_ONLY, &hdev->dev_flags) && !(auth & SMP_AUTH_SC)) + if (hci_dev_test_flag(hdev, HCI_SC_ONLY) && !(auth & SMP_AUTH_SC)) return SMP_AUTH_REQUIREMENTS; if (hcon->io_capability == HCI_IO_NO_INPUT_OUTPUT) @@ -2107,7 +2107,7 @@ static u8 smp_cmd_security_req(struct l2cap_conn *conn, struct sk_buff *skb) if (!smp) return SMP_UNSPECIFIED; - if (!test_bit(HCI_BONDABLE, &hcon->hdev->dev_flags) && + if (!hci_dev_test_flag(hdev, HCI_BONDABLE) && (auth & SMP_AUTH_BONDING)) return SMP_PAIRING_NOTSUPP; @@ -2141,7 +2141,7 @@ int smp_conn_security(struct hci_conn *hcon, __u8 sec_level) chan = conn->smp; - if (!test_bit(HCI_LE_ENABLED, &hcon->hdev->dev_flags)) + if (!hci_dev_test_flag(hcon->hdev, HCI_LE_ENABLED)) return 1; if (smp_sufficient_security(hcon, sec_level, SMP_USE_LTK)) @@ -2170,7 +2170,7 @@ int smp_conn_security(struct hci_conn *hcon, __u8 sec_level) authreq = seclevel_to_authreq(sec_level); - if (test_bit(HCI_SC_ENABLED, &hcon->hdev->dev_flags)) + if (hci_dev_test_flag(hcon->hdev, HCI_SC_ENABLED)) authreq |= SMP_AUTH_SC; /* Require MITM if IO Capability allows or the security level @@ -2606,7 +2606,7 @@ static int smp_sig_channel(struct l2cap_chan *chan, struct sk_buff *skb) if (skb->len < 1) return -EILSEQ; - if (!test_bit(HCI_LE_ENABLED, &hcon->hdev->dev_flags)) { + if (!hci_dev_test_flag(hcon->hdev, HCI_LE_ENABLED)) { reason = SMP_PAIRING_NOTSUPP; goto done; } @@ -2744,7 +2744,7 @@ static void bredr_pairing(struct l2cap_chan *chan) return; /* Secure Connections support must be enabled */ - if (!test_bit(HCI_SC_ENABLED, &hdev->dev_flags)) + if (!hci_dev_test_flag(hdev, HCI_SC_ENABLED)) return; /* BR/EDR must use Secure Connections for SMP */ @@ -2753,7 +2753,7 @@ static void bredr_pairing(struct l2cap_chan *chan) return; /* If our LE support is not enabled don't do anything */ - if (!test_bit(HCI_LE_ENABLED, &hdev->dev_flags)) + if (!hci_dev_test_flag(hdev, HCI_LE_ENABLED)) return; /* Don't bother if remote LE support is not enabled */ -- cgit v1.2.3 From a1536da255f16f42b8f069b2769134b32558b265 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Fri, 13 Mar 2015 02:11:01 -0700 Subject: Bluetooth: Introduce hci_dev_set_flag helper macro Instead of manually coding set_bit on hdev->dev_flags all the time, use hci_dev_set_flag helper macro. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- include/net/bluetooth/hci_core.h | 1 + net/bluetooth/hci_conn.c | 2 +- net/bluetooth/hci_core.c | 24 ++++++++++++------------ net/bluetooth/hci_event.c | 14 +++++++------- net/bluetooth/hci_request.c | 2 +- net/bluetooth/mgmt.c | 39 +++++++++++++++++++-------------------- 6 files changed, 41 insertions(+), 41 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 05e95a75aba5..bc2a7e918da7 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -502,6 +502,7 @@ extern struct list_head hci_cb_list; extern rwlock_t hci_dev_list_lock; extern struct mutex hci_cb_list_lock; +#define hci_dev_set_flag(hdev, nr) set_bit((nr), &(hdev)->dev_flags) #define hci_dev_test_flag(hdev, nr) test_bit((nr), &(hdev)->dev_flags) /* ----- HCI interface to upper protocols ----- */ diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c index 5444e194eb4e..c0fd42ceff61 100644 --- a/net/bluetooth/hci_conn.c +++ b/net/bluetooth/hci_conn.c @@ -842,7 +842,7 @@ struct hci_conn *hci_connect_le(struct hci_dev *hdev, bdaddr_t *dst, */ if (hci_dev_test_flag(hdev, HCI_LE_SCAN)) { hci_req_add_le_scan_disable(&req); - set_bit(HCI_LE_SCAN_INTERRUPTED, &hdev->dev_flags); + hci_dev_set_flag(hdev, HCI_LE_SCAN_INTERRUPTED); } hci_req_add_le_create_conn(&req, conn); diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 20fe5ef6abc5..252b597362eb 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -501,7 +501,7 @@ static void le_setup(struct hci_request *req) /* LE-only controllers have LE implicitly enabled */ if (!lmp_bredr_capable(hdev)) - set_bit(HCI_LE_ENABLED, &hdev->dev_flags); + hci_dev_set_flag(hdev, HCI_LE_ENABLED); } static void hci_setup_event_mask(struct hci_request *req) @@ -1448,7 +1448,7 @@ static int hci_dev_do_open(struct hci_dev *hdev) */ if (test_bit(HCI_QUIRK_EXTERNAL_CONFIG, &hdev->quirks) || test_bit(HCI_QUIRK_INVALID_BDADDR, &hdev->quirks)) - set_bit(HCI_UNCONFIGURED, &hdev->dev_flags); + hci_dev_set_flag(hdev, HCI_UNCONFIGURED); /* For an unconfigured controller it is required to * read at least the version information provided by @@ -1485,7 +1485,7 @@ static int hci_dev_do_open(struct hci_dev *hdev) if (!ret) { hci_dev_hold(hdev); - set_bit(HCI_RPA_EXPIRED, &hdev->dev_flags); + hci_dev_set_flag(hdev, HCI_RPA_EXPIRED); set_bit(HCI_UP, &hdev->flags); hci_notify(hdev, HCI_DEV_UP); if (!hci_dev_test_flag(hdev, HCI_SETUP) && @@ -1571,7 +1571,7 @@ int hci_dev_open(__u16 dev) */ if (!hci_dev_test_flag(hdev, HCI_USER_CHANNEL) && !hci_dev_test_flag(hdev, HCI_MGMT)) - set_bit(HCI_BONDABLE, &hdev->dev_flags); + hci_dev_set_flag(hdev, HCI_BONDABLE); err = hci_dev_do_open(hdev); @@ -1856,7 +1856,7 @@ static void hci_update_scan_state(struct hci_dev *hdev, u8 scan) if (conn_changed || discov_changed) { /* In case this was disabled through mgmt */ - set_bit(HCI_BREDR_ENABLED, &hdev->dev_flags); + hci_dev_set_flag(hdev, HCI_BREDR_ENABLED); if (hci_dev_test_flag(hdev, HCI_LE_ENABLED)) mgmt_update_adv_data(hdev); @@ -2082,7 +2082,7 @@ static int hci_rfkill_set_block(void *data, bool blocked) return -EBUSY; if (blocked) { - set_bit(HCI_RFKILLED, &hdev->dev_flags); + hci_dev_set_flag(hdev, HCI_RFKILLED); if (!hci_dev_test_flag(hdev, HCI_SETUP) && !hci_dev_test_flag(hdev, HCI_CONFIG)) hci_dev_do_close(hdev); @@ -3189,16 +3189,16 @@ int hci_register_dev(struct hci_dev *hdev) } if (hdev->rfkill && rfkill_blocked(hdev->rfkill)) - set_bit(HCI_RFKILLED, &hdev->dev_flags); + hci_dev_set_flag(hdev, HCI_RFKILLED); - set_bit(HCI_SETUP, &hdev->dev_flags); - set_bit(HCI_AUTO_OFF, &hdev->dev_flags); + hci_dev_set_flag(hdev, HCI_SETUP); + hci_dev_set_flag(hdev, HCI_AUTO_OFF); if (hdev->dev_type == HCI_BREDR) { /* Assume BR/EDR support until proven otherwise (such as * through reading supported features during init. */ - set_bit(HCI_BREDR_ENABLED, &hdev->dev_flags); + hci_dev_set_flag(hdev, HCI_BREDR_ENABLED); } write_lock(&hci_dev_list_lock); @@ -3209,7 +3209,7 @@ int hci_register_dev(struct hci_dev *hdev) * and should not be included in normal operation. */ if (test_bit(HCI_QUIRK_RAW_DEVICE, &hdev->quirks)) - set_bit(HCI_UNCONFIGURED, &hdev->dev_flags); + hci_dev_set_flag(hdev, HCI_UNCONFIGURED); hci_notify(hdev, HCI_DEV_REG); hci_dev_hold(hdev); @@ -3235,7 +3235,7 @@ void hci_unregister_dev(struct hci_dev *hdev) BT_DBG("%p name %s bus %d", hdev, hdev->name, hdev->bus); - set_bit(HCI_UNREGISTER, &hdev->dev_flags); + hci_dev_set_flag(hdev, HCI_UNREGISTER); id = hdev->id; diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index f1ed3fe9e0df..b01a93efada8 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -70,7 +70,7 @@ static void hci_cc_periodic_inq(struct hci_dev *hdev, struct sk_buff *skb) if (status) return; - set_bit(HCI_PERIODIC_INQ, &hdev->dev_flags); + hci_dev_set_flag(hdev, HCI_PERIODIC_INQ); } static void hci_cc_exit_periodic_inq(struct hci_dev *hdev, struct sk_buff *skb) @@ -501,7 +501,7 @@ static void hci_cc_write_ssp_mode(struct hci_dev *hdev, struct sk_buff *skb) mgmt_ssp_enable_complete(hdev, sent->mode, status); else if (!status) { if (sent->mode) - set_bit(HCI_SSP_ENABLED, &hdev->dev_flags); + hci_dev_set_flag(hdev, HCI_SSP_ENABLED); else clear_bit(HCI_SSP_ENABLED, &hdev->dev_flags); } @@ -531,7 +531,7 @@ static void hci_cc_write_sc_support(struct hci_dev *hdev, struct sk_buff *skb) if (!hci_dev_test_flag(hdev, HCI_MGMT) && !status) { if (sent->support) - set_bit(HCI_SC_ENABLED, &hdev->dev_flags); + hci_dev_set_flag(hdev, HCI_SC_ENABLED); else clear_bit(HCI_SC_ENABLED, &hdev->dev_flags); } @@ -1109,7 +1109,7 @@ static void hci_cc_le_set_adv_enable(struct hci_dev *hdev, struct sk_buff *skb) if (*sent) { struct hci_conn *conn; - set_bit(HCI_LE_ADV, &hdev->dev_flags); + hci_dev_set_flag(hdev, HCI_LE_ADV); conn = hci_conn_hash_lookup_state(hdev, LE_LINK, BT_CONNECT); if (conn) @@ -1192,7 +1192,7 @@ static void hci_cc_le_set_scan_enable(struct hci_dev *hdev, switch (cp->enable) { case LE_SCAN_ENABLE: - set_bit(HCI_LE_SCAN, &hdev->dev_flags); + hci_dev_set_flag(hdev, HCI_LE_SCAN); if (hdev->le_scan_type == LE_SCAN_ACTIVE) clear_pending_adv_report(hdev); break; @@ -1388,7 +1388,7 @@ static void hci_cc_write_le_host_supported(struct hci_dev *hdev, if (sent->le) { hdev->features[1][0] |= LMP_HOST_LE; - set_bit(HCI_LE_ENABLED, &hdev->dev_flags); + hci_dev_set_flag(hdev, HCI_LE_ENABLED); } else { hdev->features[1][0] &= ~LMP_HOST_LE; clear_bit(HCI_LE_ENABLED, &hdev->dev_flags); @@ -2608,7 +2608,7 @@ static void hci_encrypt_change_evt(struct hci_dev *hdev, struct sk_buff *skb) * whenever the encryption procedure fails. */ if (ev->status && conn->type == LE_LINK) - set_bit(HCI_RPA_EXPIRED, &hdev->dev_flags); + hci_dev_set_flag(hdev, HCI_RPA_EXPIRED); clear_bit(HCI_CONN_ENCRYPT_PEND, &conn->flags); diff --git a/net/bluetooth/hci_request.c b/net/bluetooth/hci_request.c index 42fa10522e89..fd7b2a97740b 100644 --- a/net/bluetooth/hci_request.c +++ b/net/bluetooth/hci_request.c @@ -307,7 +307,7 @@ static void set_random_addr(struct hci_request *req, bdaddr_t *rpa) if (hci_dev_test_flag(hdev, HCI_LE_ADV) || hci_conn_hash_lookup_state(hdev, LE_LINK, BT_CONNECT)) { BT_DBG("Deferring random address update"); - set_bit(HCI_RPA_EXPIRED, &hdev->dev_flags); + hci_dev_set_flag(hdev, HCI_RPA_EXPIRED); return; } diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 596b36111e64..924bf3ee6261 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -1163,7 +1163,7 @@ static void rpa_expired(struct work_struct *work) BT_DBG(""); - set_bit(HCI_RPA_EXPIRED, &hdev->dev_flags); + hci_dev_set_flag(hdev, HCI_RPA_EXPIRED); if (!hci_dev_test_flag(hdev, HCI_ADVERTISING)) return; @@ -1723,7 +1723,7 @@ static int set_discoverable(struct sock *sk, struct hci_dev *hdev, void *data, /* Limited discoverable mode */ if (cp->val == 0x02) - set_bit(HCI_LIMITED_DISCOVERABLE, &hdev->dev_flags); + hci_dev_set_flag(hdev, HCI_LIMITED_DISCOVERABLE); else clear_bit(HCI_LIMITED_DISCOVERABLE, &hdev->dev_flags); @@ -1874,7 +1874,7 @@ static int set_connectable_update_settings(struct hci_dev *hdev, changed = true; if (val) { - set_bit(HCI_CONNECTABLE, &hdev->dev_flags); + hci_dev_set_flag(hdev, HCI_CONNECTABLE); } else { clear_bit(HCI_CONNECTABLE, &hdev->dev_flags); clear_bit(HCI_DISCOVERABLE, &hdev->dev_flags); @@ -4410,7 +4410,7 @@ static void set_advertising_complete(struct hci_dev *hdev, u8 status, } if (hci_dev_test_flag(hdev, HCI_LE_ADV)) - set_bit(HCI_ADVERTISING, &hdev->dev_flags); + hci_dev_set_flag(hdev, HCI_ADVERTISING); else clear_bit(HCI_ADVERTISING, &hdev->dev_flags); @@ -4467,8 +4467,7 @@ static int set_advertising(struct sock *sk, struct hci_dev *hdev, void *data, changed = !test_and_set_bit(HCI_ADVERTISING, &hdev->dev_flags); if (cp->val == 0x02) - set_bit(HCI_ADVERTISING_CONNECTABLE, - &hdev->dev_flags); + hci_dev_set_flag(hdev, HCI_ADVERTISING_CONNECTABLE); else clear_bit(HCI_ADVERTISING_CONNECTABLE, &hdev->dev_flags); @@ -4505,7 +4504,7 @@ static int set_advertising(struct sock *sk, struct hci_dev *hdev, void *data, hci_req_init(&req, hdev); if (cp->val == 0x02) - set_bit(HCI_ADVERTISING_CONNECTABLE, &hdev->dev_flags); + hci_dev_set_flag(hdev, HCI_ADVERTISING_CONNECTABLE); else clear_bit(HCI_ADVERTISING_CONNECTABLE, &hdev->dev_flags); @@ -4644,7 +4643,7 @@ static void fast_connectable_complete(struct hci_dev *hdev, u8 status, struct mgmt_mode *cp = cmd->param; if (cp->val) - set_bit(HCI_FAST_CONNECTABLE, &hdev->dev_flags); + hci_dev_set_flag(hdev, HCI_FAST_CONNECTABLE); else clear_bit(HCI_FAST_CONNECTABLE, &hdev->dev_flags); @@ -4846,7 +4845,7 @@ static int set_bredr(struct sock *sk, struct hci_dev *hdev, void *data, u16 len) /* We need to flip the bit already here so that update_adv_data * generates the correct flags. */ - set_bit(HCI_BREDR_ENABLED, &hdev->dev_flags); + hci_dev_set_flag(hdev, HCI_BREDR_ENABLED); hci_req_init(&req, hdev); @@ -4894,12 +4893,12 @@ static void sc_enable_complete(struct hci_dev *hdev, u8 status, u16 opcode) clear_bit(HCI_SC_ONLY, &hdev->dev_flags); break; case 0x01: - set_bit(HCI_SC_ENABLED, &hdev->dev_flags); + hci_dev_set_flag(hdev, HCI_SC_ENABLED); clear_bit(HCI_SC_ONLY, &hdev->dev_flags); break; case 0x02: - set_bit(HCI_SC_ENABLED, &hdev->dev_flags); - set_bit(HCI_SC_ONLY, &hdev->dev_flags); + hci_dev_set_flag(hdev, HCI_SC_ENABLED); + hci_dev_set_flag(hdev, HCI_SC_ONLY); break; } @@ -4948,7 +4947,7 @@ static int set_secure_conn(struct sock *sk, struct hci_dev *hdev, changed = !test_and_set_bit(HCI_SC_ENABLED, &hdev->dev_flags); if (cp->val == 0x02) - set_bit(HCI_SC_ONLY, &hdev->dev_flags); + hci_dev_set_flag(hdev, HCI_SC_ONLY); else clear_bit(HCI_SC_ONLY, &hdev->dev_flags); } else { @@ -5074,12 +5073,12 @@ static int set_privacy(struct sock *sk, struct hci_dev *hdev, void *cp_data, /* If user space supports this command it is also expected to * handle IRKs. Therefore, set the HCI_RPA_RESOLVING flag. */ - set_bit(HCI_RPA_RESOLVING, &hdev->dev_flags); + hci_dev_set_flag(hdev, HCI_RPA_RESOLVING); if (cp->privacy) { changed = !test_and_set_bit(HCI_PRIVACY, &hdev->dev_flags); memcpy(hdev->irk, cp->irk, sizeof(hdev->irk)); - set_bit(HCI_RPA_EXPIRED, &hdev->dev_flags); + hci_dev_set_flag(hdev, HCI_RPA_EXPIRED); } else { changed = test_and_clear_bit(HCI_PRIVACY, &hdev->dev_flags); memset(hdev->irk, 0, sizeof(hdev->irk)); @@ -5172,7 +5171,7 @@ static int load_irks(struct sock *sk, struct hci_dev *hdev, void *cp_data, BDADDR_ANY); } - set_bit(HCI_RPA_RESOLVING, &hdev->dev_flags); + hci_dev_set_flag(hdev, HCI_RPA_RESOLVING); err = mgmt_cmd_complete(sk, hdev->id, MGMT_OP_LOAD_IRKS, 0, NULL, 0); @@ -6106,8 +6105,8 @@ static int set_external_config(struct sock *sk, struct hci_dev *hdev, mgmt_index_removed(hdev); if (test_and_change_bit(HCI_UNCONFIGURED, &hdev->dev_flags)) { - set_bit(HCI_CONFIG, &hdev->dev_flags); - set_bit(HCI_AUTO_OFF, &hdev->dev_flags); + hci_dev_set_flag(hdev, HCI_CONFIG); + hci_dev_set_flag(hdev, HCI_AUTO_OFF); queue_work(hdev->req_workqueue, &hdev->power_on); } else { @@ -6162,8 +6161,8 @@ static int set_public_address(struct sock *sk, struct hci_dev *hdev, clear_bit(HCI_UNCONFIGURED, &hdev->dev_flags); - set_bit(HCI_CONFIG, &hdev->dev_flags); - set_bit(HCI_AUTO_OFF, &hdev->dev_flags); + hci_dev_set_flag(hdev, HCI_CONFIG); + hci_dev_set_flag(hdev, HCI_AUTO_OFF); queue_work(hdev->req_workqueue, &hdev->power_on); } -- cgit v1.2.3 From a358dc11d80ecaca443aa1fd2fd9d4f3425922e7 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Fri, 13 Mar 2015 02:11:02 -0700 Subject: Bluetooth: Introduce hci_dev_clear_flag helper macro Instead of manually coding clear_bit on hdev->dev_flags all the time, use hci_dev_clear_flag helper macro. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- include/net/bluetooth/hci_core.h | 1 + net/bluetooth/hci_conn.c | 2 +- net/bluetooth/hci_core.c | 12 +++---- net/bluetooth/hci_event.c | 16 +++++----- net/bluetooth/hci_sock.c | 4 +-- net/bluetooth/mgmt.c | 68 +++++++++++++++++++--------------------- 6 files changed, 51 insertions(+), 52 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index bc2a7e918da7..535b23c73c92 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -503,6 +503,7 @@ extern rwlock_t hci_dev_list_lock; extern struct mutex hci_cb_list_lock; #define hci_dev_set_flag(hdev, nr) set_bit((nr), &(hdev)->dev_flags) +#define hci_dev_clear_flag(hdev, nr) clear_bit((nr), &(hdev)->dev_flags) #define hci_dev_test_flag(hdev, nr) test_bit((nr), &(hdev)->dev_flags) /* ----- HCI interface to upper protocols ----- */ diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c index c0fd42ceff61..ee5e59839b02 100644 --- a/net/bluetooth/hci_conn.c +++ b/net/bluetooth/hci_conn.c @@ -700,7 +700,7 @@ static void hci_req_directed_advertising(struct hci_request *req, * and write a new random address. The flag will be set back on * as soon as the SET_ADV_ENABLE HCI command completes. */ - clear_bit(HCI_LE_ADV, &hdev->dev_flags); + hci_dev_clear_flag(hdev, HCI_LE_ADV); /* Set require_privacy to false so that the remote device has a * chance of identifying us. diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 252b597362eb..01710698e547 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -591,7 +591,7 @@ static void hci_init2_req(struct hci_request *req, unsigned long opt) if (lmp_bredr_capable(hdev)) bredr_setup(req); else - clear_bit(HCI_BREDR_ENABLED, &hdev->dev_flags); + hci_dev_clear_flag(hdev, HCI_BREDR_ENABLED); if (lmp_le_capable(hdev)) le_setup(req); @@ -1625,8 +1625,8 @@ static int hci_dev_do_close(struct hci_dev *hdev) if (hdev->discov_timeout > 0) { cancel_delayed_work(&hdev->discov_off); hdev->discov_timeout = 0; - clear_bit(HCI_DISCOVERABLE, &hdev->dev_flags); - clear_bit(HCI_LIMITED_DISCOVERABLE, &hdev->dev_flags); + hci_dev_clear_flag(hdev, HCI_DISCOVERABLE); + hci_dev_clear_flag(hdev, HCI_LIMITED_DISCOVERABLE); } if (test_and_clear_bit(HCI_SERVICE_CACHE, &hdev->dev_flags)) @@ -1846,7 +1846,7 @@ static void hci_update_scan_state(struct hci_dev *hdev, u8 scan) discov_changed = !test_and_set_bit(HCI_DISCOVERABLE, &hdev->dev_flags); } else { - clear_bit(HCI_LIMITED_DISCOVERABLE, &hdev->dev_flags); + hci_dev_clear_flag(hdev, HCI_LIMITED_DISCOVERABLE); discov_changed = test_and_clear_bit(HCI_DISCOVERABLE, &hdev->dev_flags); } @@ -2087,7 +2087,7 @@ static int hci_rfkill_set_block(void *data, bool blocked) !hci_dev_test_flag(hdev, HCI_CONFIG)) hci_dev_do_close(hdev); } else { - clear_bit(HCI_RFKILLED, &hdev->dev_flags); + hci_dev_clear_flag(hdev, HCI_RFKILLED); } return 0; @@ -2121,7 +2121,7 @@ static void hci_power_on(struct work_struct *work) (hdev->dev_type == HCI_BREDR && !bacmp(&hdev->bdaddr, BDADDR_ANY) && !bacmp(&hdev->static_addr, BDADDR_ANY))) { - clear_bit(HCI_AUTO_OFF, &hdev->dev_flags); + hci_dev_clear_flag(hdev, HCI_AUTO_OFF); hci_dev_do_close(hdev); } else if (hci_dev_test_flag(hdev, HCI_AUTO_OFF)) { queue_delayed_work(hdev->req_workqueue, &hdev->power_off, diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index b01a93efada8..808b78cf8ad0 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -82,7 +82,7 @@ static void hci_cc_exit_periodic_inq(struct hci_dev *hdev, struct sk_buff *skb) if (status) return; - clear_bit(HCI_PERIODIC_INQ, &hdev->dev_flags); + hci_dev_clear_flag(hdev, HCI_PERIODIC_INQ); hci_conn_check_pending(hdev); } @@ -503,7 +503,7 @@ static void hci_cc_write_ssp_mode(struct hci_dev *hdev, struct sk_buff *skb) if (sent->mode) hci_dev_set_flag(hdev, HCI_SSP_ENABLED); else - clear_bit(HCI_SSP_ENABLED, &hdev->dev_flags); + hci_dev_clear_flag(hdev, HCI_SSP_ENABLED); } hci_dev_unlock(hdev); @@ -533,7 +533,7 @@ static void hci_cc_write_sc_support(struct hci_dev *hdev, struct sk_buff *skb) if (sent->support) hci_dev_set_flag(hdev, HCI_SC_ENABLED); else - clear_bit(HCI_SC_ENABLED, &hdev->dev_flags); + hci_dev_clear_flag(hdev, HCI_SC_ENABLED); } hci_dev_unlock(hdev); @@ -1117,7 +1117,7 @@ static void hci_cc_le_set_adv_enable(struct hci_dev *hdev, struct sk_buff *skb) &conn->le_conn_timeout, conn->conn_timeout); } else { - clear_bit(HCI_LE_ADV, &hdev->dev_flags); + hci_dev_clear_flag(hdev, HCI_LE_ADV); } hci_dev_unlock(hdev); @@ -1217,7 +1217,7 @@ static void hci_cc_le_set_scan_enable(struct hci_dev *hdev, */ cancel_delayed_work(&hdev->le_scan_disable); - clear_bit(HCI_LE_SCAN, &hdev->dev_flags); + hci_dev_clear_flag(hdev, HCI_LE_SCAN); /* The HCI_LE_SCAN_INTERRUPTED flag indicates that we * interrupted scanning due to a connect request. Mark @@ -1391,8 +1391,8 @@ static void hci_cc_write_le_host_supported(struct hci_dev *hdev, hci_dev_set_flag(hdev, HCI_LE_ENABLED); } else { hdev->features[1][0] &= ~LMP_HOST_LE; - clear_bit(HCI_LE_ENABLED, &hdev->dev_flags); - clear_bit(HCI_ADVERTISING, &hdev->dev_flags); + hci_dev_clear_flag(hdev, HCI_LE_ENABLED); + hci_dev_clear_flag(hdev, HCI_ADVERTISING); } if (sent->simul) @@ -4409,7 +4409,7 @@ static void hci_le_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *skb) /* All controllers implicitly stop advertising in the event of a * connection, so ensure that the state bit is cleared. */ - clear_bit(HCI_LE_ADV, &hdev->dev_flags); + hci_dev_clear_flag(hdev, HCI_LE_ADV); conn = hci_conn_hash_lookup_state(hdev, LE_LINK, BT_CONNECT); if (!conn) { diff --git a/net/bluetooth/hci_sock.c b/net/bluetooth/hci_sock.c index b297709d82bf..ca402a0a6740 100644 --- a/net/bluetooth/hci_sock.c +++ b/net/bluetooth/hci_sock.c @@ -474,7 +474,7 @@ static int hci_sock_release(struct socket *sock) if (hdev) { if (hci_pi(sk)->channel == HCI_CHANNEL_USER) { mgmt_index_added(hdev); - clear_bit(HCI_USER_CHANNEL, &hdev->dev_flags); + hci_dev_clear_flag(hdev, HCI_USER_CHANNEL); hci_dev_close(hdev->id); } @@ -730,7 +730,7 @@ static int hci_sock_bind(struct socket *sock, struct sockaddr *addr, err = hci_dev_open(hdev->id); if (err) { - clear_bit(HCI_USER_CHANNEL, &hdev->dev_flags); + hci_dev_clear_flag(hdev, HCI_USER_CHANNEL); mgmt_index_added(hdev); hci_dev_put(hdev); goto done; diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 924bf3ee6261..aa233e37fc93 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -1108,7 +1108,7 @@ static void enable_advertising(struct hci_request *req) * and write a new random address. The flag will be set back on * as soon as the SET_ADV_ENABLE HCI command completes. */ - clear_bit(HCI_LE_ADV, &hdev->dev_flags); + hci_dev_clear_flag(hdev, HCI_LE_ADV); if (hci_dev_test_flag(hdev, HCI_ADVERTISING_CONNECTABLE)) connectable = true; @@ -1189,7 +1189,7 @@ static void mgmt_init_hdev(struct sock *sk, struct hci_dev *hdev) * for mgmt we require user-space to explicitly enable * it */ - clear_bit(HCI_BONDABLE, &hdev->dev_flags); + hci_dev_clear_flag(hdev, HCI_BONDABLE); } static int read_controller_info(struct sock *sk, struct hci_dev *hdev, @@ -1573,7 +1573,7 @@ static void set_discoverable_complete(struct hci_dev *hdev, u8 status, if (status) { u8 mgmt_err = mgmt_status(status); mgmt_cmd_status(cmd->sk, cmd->index, cmd->opcode, mgmt_err); - clear_bit(HCI_LIMITED_DISCOVERABLE, &hdev->dev_flags); + hci_dev_clear_flag(hdev, HCI_LIMITED_DISCOVERABLE); goto remove_cmd; } @@ -1725,7 +1725,7 @@ static int set_discoverable(struct sock *sk, struct hci_dev *hdev, void *data, if (cp->val == 0x02) hci_dev_set_flag(hdev, HCI_LIMITED_DISCOVERABLE); else - clear_bit(HCI_LIMITED_DISCOVERABLE, &hdev->dev_flags); + hci_dev_clear_flag(hdev, HCI_LIMITED_DISCOVERABLE); hci_req_init(&req, hdev); @@ -1762,7 +1762,7 @@ static int set_discoverable(struct sock *sk, struct hci_dev *hdev, void *data, scan |= SCAN_INQUIRY; } else { - clear_bit(HCI_LIMITED_DISCOVERABLE, &hdev->dev_flags); + hci_dev_clear_flag(hdev, HCI_LIMITED_DISCOVERABLE); } hci_req_add(&req, HCI_OP_WRITE_SCAN_ENABLE, sizeof(scan), &scan); @@ -1876,8 +1876,8 @@ static int set_connectable_update_settings(struct hci_dev *hdev, if (val) { hci_dev_set_flag(hdev, HCI_CONNECTABLE); } else { - clear_bit(HCI_CONNECTABLE, &hdev->dev_flags); - clear_bit(HCI_DISCOVERABLE, &hdev->dev_flags); + hci_dev_clear_flag(hdev, HCI_CONNECTABLE); + hci_dev_clear_flag(hdev, HCI_DISCOVERABLE); } err = send_settings_rsp(sk, MGMT_OP_SET_CONNECTABLE, hdev); @@ -1941,8 +1941,8 @@ static int set_connectable(struct sock *sk, struct hci_dev *hdev, void *data, */ if (!hci_dev_test_flag(hdev, HCI_BREDR_ENABLED)) { if (!cp->val) { - clear_bit(HCI_LIMITED_DISCOVERABLE, &hdev->dev_flags); - clear_bit(HCI_DISCOVERABLE, &hdev->dev_flags); + hci_dev_clear_flag(hdev, HCI_LIMITED_DISCOVERABLE); + hci_dev_clear_flag(hdev, HCI_DISCOVERABLE); } update_adv_data(&req); } else if (cp->val != test_bit(HCI_PSCAN, &hdev->flags)) { @@ -2126,7 +2126,7 @@ static int set_ssp(struct sock *sk, struct hci_dev *hdev, void *data, u16 len) changed = test_and_clear_bit(HCI_HS_ENABLED, &hdev->dev_flags); else - clear_bit(HCI_HS_ENABLED, &hdev->dev_flags); + hci_dev_clear_flag(hdev, HCI_HS_ENABLED); } err = send_settings_rsp(sk, MGMT_OP_SET_SSP, hdev); @@ -2306,7 +2306,7 @@ static int set_le(struct sock *sk, struct hci_dev *hdev, void *data, u16 len) } if (!val && hci_dev_test_flag(hdev, HCI_ADVERTISING)) { - clear_bit(HCI_ADVERTISING, &hdev->dev_flags); + hci_dev_clear_flag(hdev, HCI_ADVERTISING); changed = true; } @@ -4412,7 +4412,7 @@ static void set_advertising_complete(struct hci_dev *hdev, u8 status, if (hci_dev_test_flag(hdev, HCI_LE_ADV)) hci_dev_set_flag(hdev, HCI_ADVERTISING); else - clear_bit(HCI_ADVERTISING, &hdev->dev_flags); + hci_dev_clear_flag(hdev, HCI_ADVERTISING); mgmt_pending_foreach(MGMT_OP_SET_ADVERTISING, hdev, settings_rsp, &match); @@ -4469,13 +4469,11 @@ static int set_advertising(struct sock *sk, struct hci_dev *hdev, void *data, if (cp->val == 0x02) hci_dev_set_flag(hdev, HCI_ADVERTISING_CONNECTABLE); else - clear_bit(HCI_ADVERTISING_CONNECTABLE, - &hdev->dev_flags); + hci_dev_clear_flag(hdev, HCI_ADVERTISING_CONNECTABLE); } else { changed = test_and_clear_bit(HCI_ADVERTISING, &hdev->dev_flags); - clear_bit(HCI_ADVERTISING_CONNECTABLE, - &hdev->dev_flags); + hci_dev_clear_flag(hdev, HCI_ADVERTISING_CONNECTABLE); } err = send_settings_rsp(sk, MGMT_OP_SET_ADVERTISING, hdev); @@ -4506,7 +4504,7 @@ static int set_advertising(struct sock *sk, struct hci_dev *hdev, void *data, if (cp->val == 0x02) hci_dev_set_flag(hdev, HCI_ADVERTISING_CONNECTABLE); else - clear_bit(HCI_ADVERTISING_CONNECTABLE, &hdev->dev_flags); + hci_dev_clear_flag(hdev, HCI_ADVERTISING_CONNECTABLE); if (val) enable_advertising(&req); @@ -4645,7 +4643,7 @@ static void fast_connectable_complete(struct hci_dev *hdev, u8 status, if (cp->val) hci_dev_set_flag(hdev, HCI_FAST_CONNECTABLE); else - clear_bit(HCI_FAST_CONNECTABLE, &hdev->dev_flags); + hci_dev_clear_flag(hdev, HCI_FAST_CONNECTABLE); send_settings_rsp(cmd->sk, MGMT_OP_SET_FAST_CONNECTABLE, hdev); new_settings(hdev, cmd->sk); @@ -4740,7 +4738,7 @@ static void set_bredr_complete(struct hci_dev *hdev, u8 status, u16 opcode) /* We need to restore the flag if related HCI commands * failed. */ - clear_bit(HCI_BREDR_ENABLED, &hdev->dev_flags); + hci_dev_clear_flag(hdev, HCI_BREDR_ENABLED); mgmt_cmd_status(cmd->sk, cmd->index, cmd->opcode, mgmt_err); } else { @@ -4784,11 +4782,11 @@ static int set_bredr(struct sock *sk, struct hci_dev *hdev, void *data, u16 len) if (!hdev_is_powered(hdev)) { if (!cp->val) { - clear_bit(HCI_DISCOVERABLE, &hdev->dev_flags); - clear_bit(HCI_SSP_ENABLED, &hdev->dev_flags); - clear_bit(HCI_LINK_SECURITY, &hdev->dev_flags); - clear_bit(HCI_FAST_CONNECTABLE, &hdev->dev_flags); - clear_bit(HCI_HS_ENABLED, &hdev->dev_flags); + hci_dev_clear_flag(hdev, HCI_DISCOVERABLE); + hci_dev_clear_flag(hdev, HCI_SSP_ENABLED); + hci_dev_clear_flag(hdev, HCI_LINK_SECURITY); + hci_dev_clear_flag(hdev, HCI_FAST_CONNECTABLE); + hci_dev_clear_flag(hdev, HCI_HS_ENABLED); } change_bit(HCI_BREDR_ENABLED, &hdev->dev_flags); @@ -4889,12 +4887,12 @@ static void sc_enable_complete(struct hci_dev *hdev, u8 status, u16 opcode) switch (cp->val) { case 0x00: - clear_bit(HCI_SC_ENABLED, &hdev->dev_flags); - clear_bit(HCI_SC_ONLY, &hdev->dev_flags); + hci_dev_clear_flag(hdev, HCI_SC_ENABLED); + hci_dev_clear_flag(hdev, HCI_SC_ONLY); break; case 0x01: hci_dev_set_flag(hdev, HCI_SC_ENABLED); - clear_bit(HCI_SC_ONLY, &hdev->dev_flags); + hci_dev_clear_flag(hdev, HCI_SC_ONLY); break; case 0x02: hci_dev_set_flag(hdev, HCI_SC_ENABLED); @@ -4949,11 +4947,11 @@ static int set_secure_conn(struct sock *sk, struct hci_dev *hdev, if (cp->val == 0x02) hci_dev_set_flag(hdev, HCI_SC_ONLY); else - clear_bit(HCI_SC_ONLY, &hdev->dev_flags); + hci_dev_clear_flag(hdev, HCI_SC_ONLY); } else { changed = test_and_clear_bit(HCI_SC_ENABLED, &hdev->dev_flags); - clear_bit(HCI_SC_ONLY, &hdev->dev_flags); + hci_dev_clear_flag(hdev, HCI_SC_ONLY); } err = send_settings_rsp(sk, MGMT_OP_SET_SECURE_CONN, hdev); @@ -5082,7 +5080,7 @@ static int set_privacy(struct sock *sk, struct hci_dev *hdev, void *cp_data, } else { changed = test_and_clear_bit(HCI_PRIVACY, &hdev->dev_flags); memset(hdev->irk, 0, sizeof(hdev->irk)); - clear_bit(HCI_RPA_EXPIRED, &hdev->dev_flags); + hci_dev_clear_flag(hdev, HCI_RPA_EXPIRED); } err = send_settings_rsp(sk, MGMT_OP_SET_PRIVACY, hdev); @@ -6159,7 +6157,7 @@ static int set_public_address(struct sock *sk, struct hci_dev *hdev, if (is_configured(hdev)) { mgmt_index_removed(hdev); - clear_bit(HCI_UNCONFIGURED, &hdev->dev_flags); + hci_dev_clear_flag(hdev, HCI_UNCONFIGURED); hci_dev_set_flag(hdev, HCI_CONFIG); hci_dev_set_flag(hdev, HCI_AUTO_OFF); @@ -6587,8 +6585,8 @@ void mgmt_discoverable_timeout(struct hci_dev *hdev) * of a timeout triggered from general discoverable, it is * safe to unconditionally clear the flag. */ - clear_bit(HCI_LIMITED_DISCOVERABLE, &hdev->dev_flags); - clear_bit(HCI_DISCOVERABLE, &hdev->dev_flags); + hci_dev_clear_flag(hdev, HCI_LIMITED_DISCOVERABLE); + hci_dev_clear_flag(hdev, HCI_DISCOVERABLE); hci_req_init(&req, hdev); if (hci_dev_test_flag(hdev, HCI_BREDR_ENABLED)) { @@ -7137,7 +7135,7 @@ void mgmt_ssp_enable_complete(struct hci_dev *hdev, u8 enable, u8 status) if (enable && test_and_clear_bit(HCI_SSP_ENABLED, &hdev->dev_flags)) { - clear_bit(HCI_HS_ENABLED, &hdev->dev_flags); + hci_dev_clear_flag(hdev, HCI_HS_ENABLED); new_settings(hdev, NULL); } @@ -7154,7 +7152,7 @@ void mgmt_ssp_enable_complete(struct hci_dev *hdev, u8 enable, u8 status) changed = test_and_clear_bit(HCI_HS_ENABLED, &hdev->dev_flags); else - clear_bit(HCI_HS_ENABLED, &hdev->dev_flags); + hci_dev_clear_flag(hdev, HCI_HS_ENABLED); } mgmt_pending_foreach(MGMT_OP_SET_SSP, hdev, settings_rsp, &match); -- cgit v1.2.3 From ce05d603af7c9b5be66a1f3358443e20e2a2ae7a Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Fri, 13 Mar 2015 02:11:03 -0700 Subject: Bluetooth: Introduce hci_dev_change_flag helper macro Instead of manually coding change_bit on hdev->dev_flags all the time, use hci_dev_change_flag helper macro. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- include/net/bluetooth/hci_core.h | 1 + net/bluetooth/mgmt.c | 10 +++++----- 2 files changed, 6 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 535b23c73c92..92b2148702e6 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -504,6 +504,7 @@ extern struct mutex hci_cb_list_lock; #define hci_dev_set_flag(hdev, nr) set_bit((nr), &(hdev)->dev_flags) #define hci_dev_clear_flag(hdev, nr) clear_bit((nr), &(hdev)->dev_flags) +#define hci_dev_change_flag(hdev, nr) change_bit((nr), &(hdev)->dev_flags) #define hci_dev_test_flag(hdev, nr) test_bit((nr), &(hdev)->dev_flags) /* ----- HCI interface to upper protocols ----- */ diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index aa233e37fc93..c3af3b87dbb5 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -1674,7 +1674,7 @@ static int set_discoverable(struct sock *sk, struct hci_dev *hdev, void *data, * and so no need to check HCI_LIMITED_DISCOVERABLE. */ if (!!cp->val != hci_dev_test_flag(hdev, HCI_DISCOVERABLE)) { - change_bit(HCI_DISCOVERABLE, &hdev->dev_flags); + hci_dev_change_flag(hdev, HCI_DISCOVERABLE); changed = true; } @@ -2046,7 +2046,7 @@ static int set_link_security(struct sock *sk, struct hci_dev *hdev, void *data, bool changed = false; if (!!cp->val != hci_dev_test_flag(hdev, HCI_LINK_SECURITY)) { - change_bit(HCI_LINK_SECURITY, &hdev->dev_flags); + hci_dev_change_flag(hdev, HCI_LINK_SECURITY); changed = true; } @@ -2301,7 +2301,7 @@ static int set_le(struct sock *sk, struct hci_dev *hdev, void *data, u16 len) bool changed = false; if (val != hci_dev_test_flag(hdev, HCI_LE_ENABLED)) { - change_bit(HCI_LE_ENABLED, &hdev->dev_flags); + hci_dev_change_flag(hdev, HCI_LE_ENABLED); changed = true; } @@ -4689,7 +4689,7 @@ static int set_fast_connectable(struct sock *sk, struct hci_dev *hdev, } if (!hdev_is_powered(hdev)) { - change_bit(HCI_FAST_CONNECTABLE, &hdev->dev_flags); + hci_dev_change_flag(hdev, HCI_FAST_CONNECTABLE); err = send_settings_rsp(sk, MGMT_OP_SET_FAST_CONNECTABLE, hdev); new_settings(hdev, sk); @@ -4789,7 +4789,7 @@ static int set_bredr(struct sock *sk, struct hci_dev *hdev, void *data, u16 len) hci_dev_clear_flag(hdev, HCI_HS_ENABLED); } - change_bit(HCI_BREDR_ENABLED, &hdev->dev_flags); + hci_dev_change_flag(hdev, HCI_BREDR_ENABLED); err = send_settings_rsp(sk, MGMT_OP_SET_BREDR, hdev); if (err < 0) -- cgit v1.2.3 From 516018a9c057a7c179dd6b4df917a6f5d43b3547 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Fri, 13 Mar 2015 02:11:04 -0700 Subject: Bluetooth: Introduce hci_dev_test_and_change_flag helper macro Instead of manually coding test_and_change_bit on hdev->dev_flags all the time, use hci_dev_test_and_change_flag helper macro. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- include/net/bluetooth/hci_core.h | 2 ++ net/bluetooth/mgmt.c | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 92b2148702e6..cd2682c64a4c 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -507,6 +507,8 @@ extern struct mutex hci_cb_list_lock; #define hci_dev_change_flag(hdev, nr) change_bit((nr), &(hdev)->dev_flags) #define hci_dev_test_flag(hdev, nr) test_bit((nr), &(hdev)->dev_flags) +#define hci_dev_test_and_change_flag(hdev, nr) test_and_change_bit((nr), &(hdev)->dev_flags) + /* ----- HCI interface to upper protocols ----- */ int l2cap_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr); int l2cap_disconn_ind(struct hci_conn *hcon); diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index c3af3b87dbb5..3c579a45cff0 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -6102,7 +6102,7 @@ static int set_external_config(struct sock *sk, struct hci_dev *hdev, if (hci_dev_test_flag(hdev, HCI_UNCONFIGURED) == is_configured(hdev)) { mgmt_index_removed(hdev); - if (test_and_change_bit(HCI_UNCONFIGURED, &hdev->dev_flags)) { + if (hci_dev_test_and_change_flag(hdev, HCI_UNCONFIGURED)) { hci_dev_set_flag(hdev, HCI_CONFIG); hci_dev_set_flag(hdev, HCI_AUTO_OFF); -- cgit v1.2.3 From a69d89272698d1c31ccb78348562af6461cf1eb7 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Fri, 13 Mar 2015 02:11:05 -0700 Subject: Bluetooth: Introduce hci_dev_test_and_clear_flag helper macro Instead of manually coding test_and_clear_bit on hdev->dev_flags all the time, use hci_dev_test_and_clear_flag helper macro. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- include/net/bluetooth/hci_core.h | 1 + net/bluetooth/hci_core.c | 20 ++++++------ net/bluetooth/hci_event.c | 3 +- net/bluetooth/hci_request.c | 2 +- net/bluetooth/mgmt.c | 66 +++++++++++++++++++--------------------- 5 files changed, 44 insertions(+), 48 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index cd2682c64a4c..4b7632b15051 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -507,6 +507,7 @@ extern struct mutex hci_cb_list_lock; #define hci_dev_change_flag(hdev, nr) change_bit((nr), &(hdev)->dev_flags) #define hci_dev_test_flag(hdev, nr) test_bit((nr), &(hdev)->dev_flags) +#define hci_dev_test_and_clear_flag(hdev, nr) test_and_clear_bit((nr), &(hdev)->dev_flags) #define hci_dev_test_and_change_flag(hdev, nr) test_and_change_bit((nr), &(hdev)->dev_flags) /* ----- HCI interface to upper protocols ----- */ diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 01710698e547..85d5222c70ae 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -1554,7 +1554,7 @@ int hci_dev_open(__u16 dev) * particularly important if the setup procedure has not yet * completed. */ - if (test_and_clear_bit(HCI_AUTO_OFF, &hdev->dev_flags)) + if (hci_dev_test_and_clear_flag(hdev, HCI_AUTO_OFF)) cancel_delayed_work(&hdev->power_off); /* After this call it is guaranteed that the setup procedure @@ -1629,7 +1629,7 @@ static int hci_dev_do_close(struct hci_dev *hdev) hci_dev_clear_flag(hdev, HCI_LIMITED_DISCOVERABLE); } - if (test_and_clear_bit(HCI_SERVICE_CACHE, &hdev->dev_flags)) + if (hci_dev_test_and_clear_flag(hdev, HCI_SERVICE_CACHE)) cancel_delayed_work(&hdev->service_cache); cancel_delayed_work_sync(&hdev->le_scan_disable); @@ -1647,7 +1647,7 @@ static int hci_dev_do_close(struct hci_dev *hdev) hci_discovery_set_state(hdev, DISCOVERY_STOPPED); - if (!test_and_clear_bit(HCI_AUTO_OFF, &hdev->dev_flags)) { + if (!hci_dev_test_and_clear_flag(hdev, HCI_AUTO_OFF)) { if (hdev->dev_type == HCI_BREDR) mgmt_powered(hdev, 0); } @@ -1728,7 +1728,7 @@ int hci_dev_close(__u16 dev) goto done; } - if (test_and_clear_bit(HCI_AUTO_OFF, &hdev->dev_flags)) + if (hci_dev_test_and_clear_flag(hdev, HCI_AUTO_OFF)) cancel_delayed_work(&hdev->power_off); err = hci_dev_do_close(hdev); @@ -1839,16 +1839,16 @@ static void hci_update_scan_state(struct hci_dev *hdev, u8 scan) conn_changed = !test_and_set_bit(HCI_CONNECTABLE, &hdev->dev_flags); else - conn_changed = test_and_clear_bit(HCI_CONNECTABLE, - &hdev->dev_flags); + conn_changed = hci_dev_test_and_clear_flag(hdev, + HCI_CONNECTABLE); if ((scan & SCAN_INQUIRY)) { discov_changed = !test_and_set_bit(HCI_DISCOVERABLE, &hdev->dev_flags); } else { hci_dev_clear_flag(hdev, HCI_LIMITED_DISCOVERABLE); - discov_changed = test_and_clear_bit(HCI_DISCOVERABLE, - &hdev->dev_flags); + discov_changed = hci_dev_test_and_clear_flag(hdev, + HCI_DISCOVERABLE); } if (!hci_dev_test_flag(hdev, HCI_MGMT)) @@ -2128,7 +2128,7 @@ static void hci_power_on(struct work_struct *work) HCI_AUTO_OFF_TIMEOUT); } - if (test_and_clear_bit(HCI_SETUP, &hdev->dev_flags)) { + if (hci_dev_test_and_clear_flag(hdev, HCI_SETUP)) { /* For unconfigured devices, set the HCI_RAW flag * so that userspace can easily identify them. */ @@ -2143,7 +2143,7 @@ static void hci_power_on(struct work_struct *work) * and no event will be send. */ mgmt_index_added(hdev); - } else if (test_and_clear_bit(HCI_CONFIG, &hdev->dev_flags)) { + } else if (hci_dev_test_and_clear_flag(hdev, HCI_CONFIG)) { /* When the controller is now configured, then it * is important to clear the HCI_RAW flag. */ diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 808b78cf8ad0..4958b24ae5c7 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -1226,8 +1226,7 @@ static void hci_cc_le_set_scan_enable(struct hci_dev *hdev, * been disabled because of active scanning, so * re-enable it again if necessary. */ - if (test_and_clear_bit(HCI_LE_SCAN_INTERRUPTED, - &hdev->dev_flags)) + if (hci_dev_test_and_clear_flag(hdev, HCI_LE_SCAN_INTERRUPTED)) hci_discovery_set_state(hdev, DISCOVERY_STOPPED); else if (!hci_dev_test_flag(hdev, HCI_LE_ADV) && hdev->discovery.state == DISCOVERY_FINDING) diff --git a/net/bluetooth/hci_request.c b/net/bluetooth/hci_request.c index fd7b2a97740b..e85f9ec9f73a 100644 --- a/net/bluetooth/hci_request.c +++ b/net/bluetooth/hci_request.c @@ -329,7 +329,7 @@ int hci_update_random_address(struct hci_request *req, bool require_privacy, *own_addr_type = ADDR_LE_DEV_RANDOM; - if (!test_and_clear_bit(HCI_RPA_EXPIRED, &hdev->dev_flags) && + if (!hci_dev_test_and_clear_flag(hdev, HCI_RPA_EXPIRED) && !bacmp(&hdev->random_addr, &hdev->rpa)) return 0; diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 3c579a45cff0..cc5c04728848 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -1140,7 +1140,7 @@ static void service_cache_off(struct work_struct *work) service_cache.work); struct hci_request req; - if (!test_and_clear_bit(HCI_SERVICE_CACHE, &hdev->dev_flags)) + if (!hci_dev_test_and_clear_flag(hdev, HCI_SERVICE_CACHE)) return; hci_req_init(&req, hdev); @@ -1419,7 +1419,7 @@ static int set_powered(struct sock *sk, struct hci_dev *hdev, void *data, goto failed; } - if (test_and_clear_bit(HCI_AUTO_OFF, &hdev->dev_flags)) { + if (hci_dev_test_and_clear_flag(hdev, HCI_AUTO_OFF)) { cancel_delayed_work(&hdev->power_off); if (cp->val) { @@ -1588,8 +1588,7 @@ static void set_discoverable_complete(struct hci_dev *hdev, u8 status, to); } } else { - changed = test_and_clear_bit(HCI_DISCOVERABLE, - &hdev->dev_flags); + changed = hci_dev_test_and_clear_flag(hdev, HCI_DISCOVERABLE); } send_settings_rsp(cmd->sk, MGMT_OP_SET_DISCOVERABLE, hdev); @@ -1841,10 +1840,10 @@ static void set_connectable_complete(struct hci_dev *hdev, u8 status, &hdev->dev_flags); discov_changed = false; } else { - conn_changed = test_and_clear_bit(HCI_CONNECTABLE, - &hdev->dev_flags); - discov_changed = test_and_clear_bit(HCI_DISCOVERABLE, - &hdev->dev_flags); + conn_changed = hci_dev_test_and_clear_flag(hdev, + HCI_CONNECTABLE); + discov_changed = hci_dev_test_and_clear_flag(hdev, + HCI_DISCOVERABLE); } send_settings_rsp(cmd->sk, MGMT_OP_SET_CONNECTABLE, hdev); @@ -2007,7 +2006,7 @@ static int set_bondable(struct sock *sk, struct hci_dev *hdev, void *data, if (cp->val) changed = !test_and_set_bit(HCI_BONDABLE, &hdev->dev_flags); else - changed = test_and_clear_bit(HCI_BONDABLE, &hdev->dev_flags); + changed = hci_dev_test_and_clear_flag(hdev, HCI_BONDABLE); err = send_settings_rsp(sk, MGMT_OP_SET_BONDABLE, hdev); if (err < 0) @@ -2120,11 +2119,11 @@ static int set_ssp(struct sock *sk, struct hci_dev *hdev, void *data, u16 len) changed = !test_and_set_bit(HCI_SSP_ENABLED, &hdev->dev_flags); } else { - changed = test_and_clear_bit(HCI_SSP_ENABLED, - &hdev->dev_flags); + changed = hci_dev_test_and_clear_flag(hdev, + HCI_SSP_ENABLED); if (!changed) - changed = test_and_clear_bit(HCI_HS_ENABLED, - &hdev->dev_flags); + changed = hci_dev_test_and_clear_flag(hdev, + HCI_HS_ENABLED); else hci_dev_clear_flag(hdev, HCI_HS_ENABLED); } @@ -2213,7 +2212,7 @@ static int set_hs(struct sock *sk, struct hci_dev *hdev, void *data, u16 len) goto unlock; } - changed = test_and_clear_bit(HCI_HS_ENABLED, &hdev->dev_flags); + changed = hci_dev_test_and_clear_flag(hdev, HCI_HS_ENABLED); } err = send_settings_rsp(sk, MGMT_OP_SET_HS, hdev); @@ -2629,7 +2628,7 @@ static int set_dev_class(struct sock *sk, struct hci_dev *hdev, void *data, hci_req_init(&req, hdev); - if (test_and_clear_bit(HCI_SERVICE_CACHE, &hdev->dev_flags)) { + if (hci_dev_test_and_clear_flag(hdev, HCI_SERVICE_CACHE)) { hci_dev_unlock(hdev); cancel_delayed_work_sync(&hdev->service_cache); hci_dev_lock(hdev); @@ -2718,8 +2717,8 @@ static int load_link_keys(struct sock *sk, struct hci_dev *hdev, void *data, changed = !test_and_set_bit(HCI_KEEP_DEBUG_KEYS, &hdev->dev_flags); else - changed = test_and_clear_bit(HCI_KEEP_DEBUG_KEYS, - &hdev->dev_flags); + changed = hci_dev_test_and_clear_flag(hdev, + HCI_KEEP_DEBUG_KEYS); if (changed) new_settings(hdev, NULL); @@ -4471,8 +4470,7 @@ static int set_advertising(struct sock *sk, struct hci_dev *hdev, void *data, else hci_dev_clear_flag(hdev, HCI_ADVERTISING_CONNECTABLE); } else { - changed = test_and_clear_bit(HCI_ADVERTISING, - &hdev->dev_flags); + changed = hci_dev_test_and_clear_flag(hdev, HCI_ADVERTISING); hci_dev_clear_flag(hdev, HCI_ADVERTISING_CONNECTABLE); } @@ -4949,8 +4947,8 @@ static int set_secure_conn(struct sock *sk, struct hci_dev *hdev, else hci_dev_clear_flag(hdev, HCI_SC_ONLY); } else { - changed = test_and_clear_bit(HCI_SC_ENABLED, - &hdev->dev_flags); + changed = hci_dev_test_and_clear_flag(hdev, + HCI_SC_ENABLED); hci_dev_clear_flag(hdev, HCI_SC_ONLY); } @@ -5016,15 +5014,15 @@ static int set_debug_keys(struct sock *sk, struct hci_dev *hdev, changed = !test_and_set_bit(HCI_KEEP_DEBUG_KEYS, &hdev->dev_flags); else - changed = test_and_clear_bit(HCI_KEEP_DEBUG_KEYS, - &hdev->dev_flags); + changed = hci_dev_test_and_clear_flag(hdev, + HCI_KEEP_DEBUG_KEYS); if (cp->val == 0x02) use_changed = !test_and_set_bit(HCI_USE_DEBUG_KEYS, &hdev->dev_flags); else - use_changed = test_and_clear_bit(HCI_USE_DEBUG_KEYS, - &hdev->dev_flags); + use_changed = hci_dev_test_and_clear_flag(hdev, + HCI_USE_DEBUG_KEYS); if (hdev_is_powered(hdev) && use_changed && hci_dev_test_flag(hdev, HCI_SSP_ENABLED)) { @@ -5078,7 +5076,7 @@ static int set_privacy(struct sock *sk, struct hci_dev *hdev, void *cp_data, memcpy(hdev->irk, cp->irk, sizeof(hdev->irk)); hci_dev_set_flag(hdev, HCI_RPA_EXPIRED); } else { - changed = test_and_clear_bit(HCI_PRIVACY, &hdev->dev_flags); + changed = hci_dev_test_and_clear_flag(hdev, HCI_PRIVACY); memset(hdev->irk, 0, sizeof(hdev->irk)); hci_dev_clear_flag(hdev, HCI_RPA_EXPIRED); } @@ -6087,8 +6085,7 @@ static int set_external_config(struct sock *sk, struct hci_dev *hdev, changed = !test_and_set_bit(HCI_EXT_CONFIGURED, &hdev->dev_flags); else - changed = test_and_clear_bit(HCI_EXT_CONFIGURED, - &hdev->dev_flags); + changed = hci_dev_test_and_clear_flag(hdev, HCI_EXT_CONFIGURED); err = send_options_rsp(sk, MGMT_OP_SET_EXTERNAL_CONFIG, hdev); if (err < 0) @@ -7096,8 +7093,7 @@ void mgmt_auth_enable_complete(struct hci_dev *hdev, u8 status) changed = !test_and_set_bit(HCI_LINK_SECURITY, &hdev->dev_flags); else - changed = test_and_clear_bit(HCI_LINK_SECURITY, - &hdev->dev_flags); + changed = hci_dev_test_and_clear_flag(hdev, HCI_LINK_SECURITY); mgmt_pending_foreach(MGMT_OP_SET_LINK_SECURITY, hdev, settings_rsp, &match); @@ -7133,8 +7129,8 @@ void mgmt_ssp_enable_complete(struct hci_dev *hdev, u8 enable, u8 status) if (status) { u8 mgmt_err = mgmt_status(status); - if (enable && test_and_clear_bit(HCI_SSP_ENABLED, - &hdev->dev_flags)) { + if (enable && hci_dev_test_and_clear_flag(hdev, + HCI_SSP_ENABLED)) { hci_dev_clear_flag(hdev, HCI_HS_ENABLED); new_settings(hdev, NULL); } @@ -7147,10 +7143,10 @@ void mgmt_ssp_enable_complete(struct hci_dev *hdev, u8 enable, u8 status) if (enable) { changed = !test_and_set_bit(HCI_SSP_ENABLED, &hdev->dev_flags); } else { - changed = test_and_clear_bit(HCI_SSP_ENABLED, &hdev->dev_flags); + changed = hci_dev_test_and_clear_flag(hdev, HCI_SSP_ENABLED); if (!changed) - changed = test_and_clear_bit(HCI_HS_ENABLED, - &hdev->dev_flags); + changed = hci_dev_test_and_clear_flag(hdev, + HCI_HS_ENABLED); else hci_dev_clear_flag(hdev, HCI_HS_ENABLED); } -- cgit v1.2.3 From 238be788fcb75870661ec165dc90f2a2674e7fcb Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Fri, 13 Mar 2015 02:11:06 -0700 Subject: Bluetooth: Introduce hci_dev_test_and_set_flag helper macro Instead of manually coding test_and_set_bit on hdev->dev_flags all the time, use hci_dev_test_and_set_flag helper macro. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- include/net/bluetooth/hci_core.h | 1 + net/bluetooth/hci_core.c | 8 +++---- net/bluetooth/hci_sock.c | 2 +- net/bluetooth/mgmt.c | 46 +++++++++++++++++----------------------- 4 files changed, 26 insertions(+), 31 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 4b7632b15051..6db1333a114f 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -507,6 +507,7 @@ extern struct mutex hci_cb_list_lock; #define hci_dev_change_flag(hdev, nr) change_bit((nr), &(hdev)->dev_flags) #define hci_dev_test_flag(hdev, nr) test_bit((nr), &(hdev)->dev_flags) +#define hci_dev_test_and_set_flag(hdev, nr) test_and_set_bit((nr), &(hdev)->dev_flags) #define hci_dev_test_and_clear_flag(hdev, nr) test_and_clear_bit((nr), &(hdev)->dev_flags) #define hci_dev_test_and_change_flag(hdev, nr) test_and_change_bit((nr), &(hdev)->dev_flags) diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 85d5222c70ae..c6ed46c4f45a 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -1836,15 +1836,15 @@ static void hci_update_scan_state(struct hci_dev *hdev, u8 scan) BT_DBG("%s scan 0x%02x", hdev->name, scan); if ((scan & SCAN_PAGE)) - conn_changed = !test_and_set_bit(HCI_CONNECTABLE, - &hdev->dev_flags); + conn_changed = !hci_dev_test_and_set_flag(hdev, + HCI_CONNECTABLE); else conn_changed = hci_dev_test_and_clear_flag(hdev, HCI_CONNECTABLE); if ((scan & SCAN_INQUIRY)) { - discov_changed = !test_and_set_bit(HCI_DISCOVERABLE, - &hdev->dev_flags); + discov_changed = !hci_dev_test_and_set_flag(hdev, + HCI_DISCOVERABLE); } else { hci_dev_clear_flag(hdev, HCI_LIMITED_DISCOVERABLE); discov_changed = hci_dev_test_and_clear_flag(hdev, diff --git a/net/bluetooth/hci_sock.c b/net/bluetooth/hci_sock.c index ca402a0a6740..b614543b4fe3 100644 --- a/net/bluetooth/hci_sock.c +++ b/net/bluetooth/hci_sock.c @@ -720,7 +720,7 @@ static int hci_sock_bind(struct socket *sock, struct sockaddr *addr, goto done; } - if (test_and_set_bit(HCI_USER_CHANNEL, &hdev->dev_flags)) { + if (hci_dev_test_and_set_flag(hdev, HCI_USER_CHANNEL)) { err = -EUSERS; hci_dev_put(hdev); goto done; diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index cc5c04728848..d97719d04be0 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -1178,7 +1178,7 @@ static void rpa_expired(struct work_struct *work) static void mgmt_init_hdev(struct sock *sk, struct hci_dev *hdev) { - if (test_and_set_bit(HCI_MGMT, &hdev->dev_flags)) + if (hci_dev_test_and_set_flag(hdev, HCI_MGMT)) return; INIT_DELAYED_WORK(&hdev->service_cache, service_cache_off); @@ -1579,8 +1579,7 @@ static void set_discoverable_complete(struct hci_dev *hdev, u8 status, cp = cmd->param; if (cp->val) { - changed = !test_and_set_bit(HCI_DISCOVERABLE, - &hdev->dev_flags); + changed = !hci_dev_test_and_set_flag(hdev, HCI_DISCOVERABLE); if (hdev->discov_timeout > 0) { int to = msecs_to_jiffies(hdev->discov_timeout * 1000); @@ -1836,8 +1835,8 @@ static void set_connectable_complete(struct hci_dev *hdev, u8 status, cp = cmd->param; if (cp->val) { - conn_changed = !test_and_set_bit(HCI_CONNECTABLE, - &hdev->dev_flags); + conn_changed = !hci_dev_test_and_set_flag(hdev, + HCI_CONNECTABLE); discov_changed = false; } else { conn_changed = hci_dev_test_and_clear_flag(hdev, @@ -2004,7 +2003,7 @@ static int set_bondable(struct sock *sk, struct hci_dev *hdev, void *data, hci_dev_lock(hdev); if (cp->val) - changed = !test_and_set_bit(HCI_BONDABLE, &hdev->dev_flags); + changed = !hci_dev_test_and_set_flag(hdev, HCI_BONDABLE); else changed = hci_dev_test_and_clear_flag(hdev, HCI_BONDABLE); @@ -2116,8 +2115,8 @@ static int set_ssp(struct sock *sk, struct hci_dev *hdev, void *data, u16 len) bool changed; if (cp->val) { - changed = !test_and_set_bit(HCI_SSP_ENABLED, - &hdev->dev_flags); + changed = !hci_dev_test_and_set_flag(hdev, + HCI_SSP_ENABLED); } else { changed = hci_dev_test_and_clear_flag(hdev, HCI_SSP_ENABLED); @@ -2204,7 +2203,7 @@ static int set_hs(struct sock *sk, struct hci_dev *hdev, void *data, u16 len) } if (cp->val) { - changed = !test_and_set_bit(HCI_HS_ENABLED, &hdev->dev_flags); + changed = !hci_dev_test_and_set_flag(hdev, HCI_HS_ENABLED); } else { if (hdev_is_powered(hdev)) { err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_HS, @@ -2487,7 +2486,7 @@ static bool enable_service_cache(struct hci_dev *hdev) if (!hdev_is_powered(hdev)) return false; - if (!test_and_set_bit(HCI_SERVICE_CACHE, &hdev->dev_flags)) { + if (!hci_dev_test_and_set_flag(hdev, HCI_SERVICE_CACHE)) { queue_delayed_work(hdev->workqueue, &hdev->service_cache, CACHE_TIMEOUT); return true; @@ -2714,8 +2713,7 @@ static int load_link_keys(struct sock *sk, struct hci_dev *hdev, void *data, hci_link_keys_clear(hdev); if (cp->debug_keys) - changed = !test_and_set_bit(HCI_KEEP_DEBUG_KEYS, - &hdev->dev_flags); + changed = !hci_dev_test_and_set_flag(hdev, HCI_KEEP_DEBUG_KEYS); else changed = hci_dev_test_and_clear_flag(hdev, HCI_KEEP_DEBUG_KEYS); @@ -4463,8 +4461,7 @@ static int set_advertising(struct sock *sk, struct hci_dev *hdev, void *data, bool changed; if (cp->val) { - changed = !test_and_set_bit(HCI_ADVERTISING, - &hdev->dev_flags); + changed = !hci_dev_test_and_set_flag(hdev, HCI_ADVERTISING); if (cp->val == 0x02) hci_dev_set_flag(hdev, HCI_ADVERTISING_CONNECTABLE); else @@ -4940,8 +4937,8 @@ static int set_secure_conn(struct sock *sk, struct hci_dev *hdev, bool changed; if (cp->val) { - changed = !test_and_set_bit(HCI_SC_ENABLED, - &hdev->dev_flags); + changed = !hci_dev_test_and_set_flag(hdev, + HCI_SC_ENABLED); if (cp->val == 0x02) hci_dev_set_flag(hdev, HCI_SC_ONLY); else @@ -5011,15 +5008,14 @@ static int set_debug_keys(struct sock *sk, struct hci_dev *hdev, hci_dev_lock(hdev); if (cp->val) - changed = !test_and_set_bit(HCI_KEEP_DEBUG_KEYS, - &hdev->dev_flags); + changed = !hci_dev_test_and_set_flag(hdev, HCI_KEEP_DEBUG_KEYS); else changed = hci_dev_test_and_clear_flag(hdev, HCI_KEEP_DEBUG_KEYS); if (cp->val == 0x02) - use_changed = !test_and_set_bit(HCI_USE_DEBUG_KEYS, - &hdev->dev_flags); + use_changed = !hci_dev_test_and_set_flag(hdev, + HCI_USE_DEBUG_KEYS); else use_changed = hci_dev_test_and_clear_flag(hdev, HCI_USE_DEBUG_KEYS); @@ -5072,7 +5068,7 @@ static int set_privacy(struct sock *sk, struct hci_dev *hdev, void *cp_data, hci_dev_set_flag(hdev, HCI_RPA_RESOLVING); if (cp->privacy) { - changed = !test_and_set_bit(HCI_PRIVACY, &hdev->dev_flags); + changed = !hci_dev_test_and_set_flag(hdev, HCI_PRIVACY); memcpy(hdev->irk, cp->irk, sizeof(hdev->irk)); hci_dev_set_flag(hdev, HCI_RPA_EXPIRED); } else { @@ -6082,8 +6078,7 @@ static int set_external_config(struct sock *sk, struct hci_dev *hdev, hci_dev_lock(hdev); if (cp->config) - changed = !test_and_set_bit(HCI_EXT_CONFIGURED, - &hdev->dev_flags); + changed = !hci_dev_test_and_set_flag(hdev, HCI_EXT_CONFIGURED); else changed = hci_dev_test_and_clear_flag(hdev, HCI_EXT_CONFIGURED); @@ -7090,8 +7085,7 @@ void mgmt_auth_enable_complete(struct hci_dev *hdev, u8 status) } if (test_bit(HCI_AUTH, &hdev->flags)) - changed = !test_and_set_bit(HCI_LINK_SECURITY, - &hdev->dev_flags); + changed = !hci_dev_test_and_set_flag(hdev, HCI_LINK_SECURITY); else changed = hci_dev_test_and_clear_flag(hdev, HCI_LINK_SECURITY); @@ -7141,7 +7135,7 @@ void mgmt_ssp_enable_complete(struct hci_dev *hdev, u8 enable, u8 status) } if (enable) { - changed = !test_and_set_bit(HCI_SSP_ENABLED, &hdev->dev_flags); + changed = !hci_dev_test_and_set_flag(hdev, HCI_SSP_ENABLED); } else { changed = hci_dev_test_and_clear_flag(hdev, HCI_SSP_ENABLED); if (!changed) -- cgit v1.2.3 From 9c51026509d7fd11d84e0035008e1a9768960f2b Mon Sep 17 00:00:00 2001 From: Syed Asifful Dayyan Date: Fri, 6 Mar 2015 18:40:42 +0100 Subject: brcmfmac: Add support for BCM4345 SDIO chipset. These changes add support for BCM4345 SDIO chipset. Reviewed-by: Pieter-Paul Giesberts Reviewed-by: Arend Van Spriel Reviewed-by: Hante Meuleman Reviewed-by: Daniel (Deognyoun) Kim Signed-off-by: Syed Asifful Dayyan Signed-off-by: Arend van Spriel Signed-off-by: Kalle Valo --- drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c | 1 + drivers/net/wireless/brcm80211/brcmfmac/chip.c | 4 ++++ drivers/net/wireless/brcm80211/brcmfmac/sdio.c | 5 +++++ drivers/net/wireless/brcm80211/include/brcm_hw_ids.h | 1 + include/linux/mmc/sdio_ids.h | 1 + 5 files changed, 12 insertions(+) (limited to 'include') diff --git a/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c b/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c index ffb0e2d382ea..43995308307d 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c @@ -1096,6 +1096,7 @@ static const struct sdio_device_id brcmf_sdmmc_ids[] = { BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_43341), BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_43362), BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_4335_4339), + BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_4345), BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_4354), { /* end: all zeroes */ } }; diff --git a/drivers/net/wireless/brcm80211/brcmfmac/chip.c b/drivers/net/wireless/brcm80211/brcmfmac/chip.c index 04d2ca0d87d6..e679edca081d 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/chip.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/chip.c @@ -491,6 +491,10 @@ static void brcmf_chip_get_raminfo(struct brcmf_chip_priv *ci) case BRCM_CC_43362_CHIP_ID: ci->pub.ramsize = 0x3c000; break; + case BRCM_CC_4345_CHIP_ID: + ci->pub.ramsize = 0xc8000; + ci->pub.rambase = 0x198000; + break; case BRCM_CC_4339_CHIP_ID: case BRCM_CC_4354_CHIP_ID: case BRCM_CC_4356_CHIP_ID: diff --git a/drivers/net/wireless/brcm80211/brcmfmac/sdio.c b/drivers/net/wireless/brcm80211/brcmfmac/sdio.c index c9a9ff191616..973fa2afc2fc 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/sdio.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/sdio.c @@ -617,6 +617,8 @@ static const struct sdiod_drive_str sdiod_drvstr_tab2_3v3[] = { #define BCM43362_NVRAM_NAME "brcm/brcmfmac43362-sdio.txt" #define BCM4339_FIRMWARE_NAME "brcm/brcmfmac4339-sdio.bin" #define BCM4339_NVRAM_NAME "brcm/brcmfmac4339-sdio.txt" +#define BCM4345_FIRMWARE_NAME "brcm/brcmfmac4345-sdio.bin" +#define BCM4345_NVRAM_NAME "brcm/brcmfmac4345-sdio.txt" #define BCM4354_FIRMWARE_NAME "brcm/brcmfmac4354-sdio.bin" #define BCM4354_NVRAM_NAME "brcm/brcmfmac4354-sdio.txt" @@ -640,6 +642,8 @@ MODULE_FIRMWARE(BCM43362_FIRMWARE_NAME); MODULE_FIRMWARE(BCM43362_NVRAM_NAME); MODULE_FIRMWARE(BCM4339_FIRMWARE_NAME); MODULE_FIRMWARE(BCM4339_NVRAM_NAME); +MODULE_FIRMWARE(BCM4345_FIRMWARE_NAME); +MODULE_FIRMWARE(BCM4345_NVRAM_NAME); MODULE_FIRMWARE(BCM4354_FIRMWARE_NAME); MODULE_FIRMWARE(BCM4354_NVRAM_NAME); @@ -669,6 +673,7 @@ static const struct brcmf_firmware_names brcmf_fwname_data[] = { { BRCM_CC_4335_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM4335) }, { BRCM_CC_43362_CHIP_ID, 0xFFFFFFFE, BRCMF_FIRMWARE_NVRAM(BCM43362) }, { BRCM_CC_4339_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM4339) }, + { BRCM_CC_4345_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM4345) }, { BRCM_CC_4354_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM4354) } }; diff --git a/drivers/net/wireless/brcm80211/include/brcm_hw_ids.h b/drivers/net/wireless/brcm80211/include/brcm_hw_ids.h index 2124a17d0bfd..b599e7e41148 100644 --- a/drivers/net/wireless/brcm80211/include/brcm_hw_ids.h +++ b/drivers/net/wireless/brcm80211/include/brcm_hw_ids.h @@ -37,6 +37,7 @@ #define BRCM_CC_43362_CHIP_ID 43362 #define BRCM_CC_4335_CHIP_ID 0x4335 #define BRCM_CC_4339_CHIP_ID 0x4339 +#define BRCM_CC_4345_CHIP_ID 0x4345 #define BRCM_CC_4354_CHIP_ID 0x4354 #define BRCM_CC_4356_CHIP_ID 0x4356 #define BRCM_CC_43566_CHIP_ID 43566 diff --git a/include/linux/mmc/sdio_ids.h b/include/linux/mmc/sdio_ids.h index 996807963716..91397858dc95 100644 --- a/include/linux/mmc/sdio_ids.h +++ b/include/linux/mmc/sdio_ids.h @@ -33,6 +33,7 @@ #define SDIO_DEVICE_ID_BROADCOM_43341 0xa94d #define SDIO_DEVICE_ID_BROADCOM_4335_4339 0x4335 #define SDIO_DEVICE_ID_BROADCOM_43362 0xa962 +#define SDIO_DEVICE_ID_BROADCOM_4345 0x4345 #define SDIO_DEVICE_ID_BROADCOM_4354 0x4354 #define SDIO_VENDOR_ID_INTEL 0x0089 -- cgit v1.2.3 From 2187f03a9576c4fb8342deed912b5ba6fcf98cba Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Mon, 5 Jan 2015 15:35:39 +0100 Subject: i2c: add quirk structure to describe adapter flaws The number of I2C adapters which are not fully I2C compatible is rising, sadly. Drivers usually do handle the flaws, still the user receives only some errno for a transfer which normally can be expected to work. This patch introduces a formal description of flaws. One advantage is that the core can check before the actual transfer if the messages could be transferred at all. This is done in the next patch. Another advantage is that we can pass this information to the user so the restrictions are exactly known and further actions can be based on that. This will be done later after some stabilization period for this description. Signed-off-by: Wolfram Sang Tested-by: Ray Jui Tested-by: Ivan T. Ivanov Tested-by: Neelesh Gupta Tested-By: Ludovic Desroches --- include/linux/i2c.h | 43 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) (limited to 'include') diff --git a/include/linux/i2c.h b/include/linux/i2c.h index f17da50402a4..243d1a1d78b2 100644 --- a/include/linux/i2c.h +++ b/include/linux/i2c.h @@ -449,6 +449,48 @@ int i2c_recover_bus(struct i2c_adapter *adap); int i2c_generic_gpio_recovery(struct i2c_adapter *adap); int i2c_generic_scl_recovery(struct i2c_adapter *adap); +/** + * struct i2c_adapter_quirks - describe flaws of an i2c adapter + * @flags: see I2C_AQ_* for possible flags and read below + * @max_num_msgs: maximum number of messages per transfer + * @max_write_len: maximum length of a write message + * @max_read_len: maximum length of a read message + * @max_comb_1st_msg_len: maximum length of the first msg in a combined message + * @max_comb_2nd_msg_len: maximum length of the second msg in a combined message + * + * Note about combined messages: Some I2C controllers can only send one message + * per transfer, plus something called combined message or write-then-read. + * This is (usually) a small write message followed by a read message and + * barely enough to access register based devices like EEPROMs. There is a flag + * to support this mode. It implies max_num_msg = 2 and does the length checks + * with max_comb_*_len because combined message mode usually has its own + * limitations. Because of HW implementations, some controllers can actually do + * write-then-anything or other variants. To support that, write-then-read has + * been broken out into smaller bits like write-first and read-second which can + * be combined as needed. + */ + +struct i2c_adapter_quirks { + u64 flags; + int max_num_msgs; + u16 max_write_len; + u16 max_read_len; + u16 max_comb_1st_msg_len; + u16 max_comb_2nd_msg_len; +}; + +/* enforce max_num_msgs = 2 and use max_comb_*_len for length checks */ +#define I2C_AQ_COMB BIT(0) +/* first combined message must be write */ +#define I2C_AQ_COMB_WRITE_FIRST BIT(1) +/* second combined message must be read */ +#define I2C_AQ_COMB_READ_SECOND BIT(2) +/* both combined messages must have the same target address */ +#define I2C_AQ_COMB_SAME_ADDR BIT(3) +/* convenience macro for typical write-then read case */ +#define I2C_AQ_COMB_WRITE_THEN_READ (I2C_AQ_COMB | I2C_AQ_COMB_WRITE_FIRST | \ + I2C_AQ_COMB_READ_SECOND | I2C_AQ_COMB_SAME_ADDR) + /* * i2c_adapter is the structure used to identify a physical i2c bus along * with the access algorithms necessary to access it. @@ -474,6 +516,7 @@ struct i2c_adapter { struct list_head userspace_clients; struct i2c_bus_recovery_info *bus_recovery_info; + const struct i2c_adapter_quirks *quirks; }; #define to_i2c_adapter(d) container_of(d, struct i2c_adapter, dev) -- cgit v1.2.3 From 702131e2a393b45174be326f1dbe20b658b4f157 Mon Sep 17 00:00:00 2001 From: Rafał Miłecki Date: Thu, 5 Mar 2015 18:25:10 +0100 Subject: bcma: move PCI IRQ control function to host specific code MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This function isn't really related to any bus core. It touches PCI device config registers only, so move it to the (PCI) host file. Signed-off-by: Rafał Miłecki Signed-off-by: Kalle Valo --- drivers/bcma/driver_pci.c | 33 ------------------------- drivers/bcma/host_pci.c | 34 ++++++++++++++++++++++++++ drivers/net/wireless/b43/main.c | 2 +- drivers/net/wireless/brcm80211/brcmsmac/main.c | 2 +- include/linux/bcma/bcma.h | 9 +++++++ include/linux/bcma/bcma_driver_pci.h | 2 -- 6 files changed, 45 insertions(+), 37 deletions(-) (limited to 'include') diff --git a/drivers/bcma/driver_pci.c b/drivers/bcma/driver_pci.c index cfd35bc1c5a3..f499a469e66d 100644 --- a/drivers/bcma/driver_pci.c +++ b/drivers/bcma/driver_pci.c @@ -282,39 +282,6 @@ void bcma_core_pci_power_save(struct bcma_bus *bus, bool up) } EXPORT_SYMBOL_GPL(bcma_core_pci_power_save); -int bcma_core_pci_irq_ctl(struct bcma_bus *bus, struct bcma_device *core, - bool enable) -{ - struct pci_dev *pdev; - u32 coremask, tmp; - int err = 0; - - if (bus->hosttype != BCMA_HOSTTYPE_PCI) { - /* This bcma device is not on a PCI host-bus. So the IRQs are - * not routed through the PCI core. - * So we must not enable routing through the PCI core. */ - goto out; - } - - pdev = bus->host_pci; - - err = pci_read_config_dword(pdev, BCMA_PCI_IRQMASK, &tmp); - if (err) - goto out; - - coremask = BIT(core->core_index) << 8; - if (enable) - tmp |= coremask; - else - tmp &= ~coremask; - - err = pci_write_config_dword(pdev, BCMA_PCI_IRQMASK, tmp); - -out: - return err; -} -EXPORT_SYMBOL_GPL(bcma_core_pci_irq_ctl); - static void bcma_core_pci_extend_L1timer(struct bcma_drv_pci *pc, bool extend) { u32 w; diff --git a/drivers/bcma/host_pci.c b/drivers/bcma/host_pci.c index a62a2f9091f5..0856189c065f 100644 --- a/drivers/bcma/host_pci.c +++ b/drivers/bcma/host_pci.c @@ -351,3 +351,37 @@ void bcma_host_pci_down(struct bcma_bus *bus) bcma_core_pci_down(&bus->drv_pci[0]); } EXPORT_SYMBOL_GPL(bcma_host_pci_down); + +/* See also si_pci_setup */ +int bcma_host_pci_irq_ctl(struct bcma_bus *bus, struct bcma_device *core, + bool enable) +{ + struct pci_dev *pdev; + u32 coremask, tmp; + int err = 0; + + if (bus->hosttype != BCMA_HOSTTYPE_PCI) { + /* This bcma device is not on a PCI host-bus. So the IRQs are + * not routed through the PCI core. + * So we must not enable routing through the PCI core. */ + goto out; + } + + pdev = bus->host_pci; + + err = pci_read_config_dword(pdev, BCMA_PCI_IRQMASK, &tmp); + if (err) + goto out; + + coremask = BIT(core->core_index) << 8; + if (enable) + tmp |= coremask; + else + tmp &= ~coremask; + + err = pci_write_config_dword(pdev, BCMA_PCI_IRQMASK, tmp); + +out: + return err; +} +EXPORT_SYMBOL_GPL(bcma_host_pci_irq_ctl); diff --git a/drivers/net/wireless/b43/main.c b/drivers/net/wireless/b43/main.c index ac99798570e8..998490a7b167 100644 --- a/drivers/net/wireless/b43/main.c +++ b/drivers/net/wireless/b43/main.c @@ -4866,7 +4866,7 @@ static int b43_wireless_core_init(struct b43_wldev *dev) switch (dev->dev->bus_type) { #ifdef CONFIG_B43_BCMA case B43_BUS_BCMA: - bcma_core_pci_irq_ctl(dev->dev->bdev->bus, + bcma_host_pci_irq_ctl(dev->dev->bdev->bus, dev->dev->bdev, true); bcma_host_pci_up(dev->dev->bdev->bus); break; diff --git a/drivers/net/wireless/brcm80211/brcmsmac/main.c b/drivers/net/wireless/brcm80211/brcmsmac/main.c index c84af1dfc88f..369527e27689 100644 --- a/drivers/net/wireless/brcm80211/brcmsmac/main.c +++ b/drivers/net/wireless/brcm80211/brcmsmac/main.c @@ -4959,7 +4959,7 @@ static int brcms_b_up_prep(struct brcms_hardware *wlc_hw) * Configure pci/pcmcia here instead of in brcms_c_attach() * to allow mfg hotswap: down, hotswap (chip power cycle), up. */ - bcma_core_pci_irq_ctl(wlc_hw->d11core->bus, wlc_hw->d11core, + bcma_host_pci_irq_ctl(wlc_hw->d11core->bus, wlc_hw->d11core, true); /* diff --git a/include/linux/bcma/bcma.h b/include/linux/bcma/bcma.h index 44057b45ed32..e34f906647d3 100644 --- a/include/linux/bcma/bcma.h +++ b/include/linux/bcma/bcma.h @@ -437,6 +437,8 @@ static inline struct bcma_device *bcma_find_core(struct bcma_bus *bus, #ifdef CONFIG_BCMA_HOST_PCI extern void bcma_host_pci_up(struct bcma_bus *bus); extern void bcma_host_pci_down(struct bcma_bus *bus); +extern int bcma_host_pci_irq_ctl(struct bcma_bus *bus, + struct bcma_device *core, bool enable); #else static inline void bcma_host_pci_up(struct bcma_bus *bus) { @@ -444,6 +446,13 @@ static inline void bcma_host_pci_up(struct bcma_bus *bus) static inline void bcma_host_pci_down(struct bcma_bus *bus) { } +static inline int bcma_host_pci_irq_ctl(struct bcma_bus *bus, + struct bcma_device *core, bool enable) +{ + if (bus->hosttype == BCMA_HOSTTYPE_PCI) + return -ENOTSUPP; + return 0; +} #endif extern bool bcma_core_is_enabled(struct bcma_device *core); diff --git a/include/linux/bcma/bcma_driver_pci.h b/include/linux/bcma/bcma_driver_pci.h index 8e90004fdfd7..3a468687c170 100644 --- a/include/linux/bcma/bcma_driver_pci.h +++ b/include/linux/bcma/bcma_driver_pci.h @@ -238,8 +238,6 @@ struct bcma_drv_pci { #define pcicore_write16(pc, offset, val) bcma_write16((pc)->core, offset, val) #define pcicore_write32(pc, offset, val) bcma_write32((pc)->core, offset, val) -extern int bcma_core_pci_irq_ctl(struct bcma_bus *bus, - struct bcma_device *core, bool enable); extern void bcma_core_pci_power_save(struct bcma_bus *bus, bool up); extern int bcma_core_pci_pcibios_map_irq(const struct pci_dev *dev); -- cgit v1.2.3 From 982a40f5c0bb03368989a6b1ae833b474854e931 Mon Sep 17 00:00:00 2001 From: Rafał Miłecki Date: Thu, 5 Mar 2015 18:25:11 +0100 Subject: bcma: allow disabling (not building) PCI driver MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It isn't required for bcma bus on SoCs, so provide some empty functions and allow disabling it. Signed-off-by: Rafał Miłecki Signed-off-by: Kalle Valo --- drivers/bcma/Kconfig | 4 ++-- drivers/bcma/bcma_private.h | 20 ++++++++++++++++++++ include/linux/bcma/bcma_driver_pci.h | 6 ++++++ 3 files changed, 28 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/drivers/bcma/Kconfig b/drivers/bcma/Kconfig index 9be17d3431bb..1500b7120fc7 100644 --- a/drivers/bcma/Kconfig +++ b/drivers/bcma/Kconfig @@ -45,9 +45,9 @@ config BCMA_HOST_SOC If unsure, say N -# TODO: make it depend on PCI when ready config BCMA_DRIVER_PCI - bool + bool "BCMA Broadcom PCI core driver" + depends on BCMA && PCI default y help BCMA bus may have many versions of PCIe core. This driver diff --git a/drivers/bcma/bcma_private.h b/drivers/bcma/bcma_private.h index 5a1d22489afc..15f2b2e242ea 100644 --- a/drivers/bcma/bcma_private.h +++ b/drivers/bcma/bcma_private.h @@ -106,15 +106,35 @@ static inline void __exit bcma_host_soc_unregister_driver(void) #endif /* CONFIG_BCMA_HOST_SOC && CONFIG_OF */ /* driver_pci.c */ +#ifdef CONFIG_BCMA_DRIVER_PCI u32 bcma_pcie_read(struct bcma_drv_pci *pc, u32 address); void bcma_core_pci_early_init(struct bcma_drv_pci *pc); void bcma_core_pci_init(struct bcma_drv_pci *pc); void bcma_core_pci_up(struct bcma_drv_pci *pc); void bcma_core_pci_down(struct bcma_drv_pci *pc); +#else +static inline void bcma_core_pci_early_init(struct bcma_drv_pci *pc) +{ + WARN_ON(pc->core->bus->hosttype == BCMA_HOSTTYPE_PCI); +} +static inline void bcma_core_pci_init(struct bcma_drv_pci *pc) +{ + /* Initialization is required for PCI hosted bus */ + WARN_ON(pc->core->bus->hosttype == BCMA_HOSTTYPE_PCI); +} +#endif /* driver_pcie2.c */ +#ifdef CONFIG_BCMA_DRIVER_PCI void bcma_core_pcie2_init(struct bcma_drv_pcie2 *pcie2); void bcma_core_pcie2_up(struct bcma_drv_pcie2 *pcie2); +#else +static inline void bcma_core_pcie2_init(struct bcma_drv_pcie2 *pcie2) +{ + /* Initialization is required for PCI hosted bus */ + WARN_ON(pcie2->core->bus->hosttype == BCMA_HOSTTYPE_PCI); +} +#endif extern int bcma_chipco_watchdog_register(struct bcma_drv_cc *cc); diff --git a/include/linux/bcma/bcma_driver_pci.h b/include/linux/bcma/bcma_driver_pci.h index 3a468687c170..5ba6918ca20b 100644 --- a/include/linux/bcma/bcma_driver_pci.h +++ b/include/linux/bcma/bcma_driver_pci.h @@ -238,7 +238,13 @@ struct bcma_drv_pci { #define pcicore_write16(pc, offset, val) bcma_write16((pc)->core, offset, val) #define pcicore_write32(pc, offset, val) bcma_write32((pc)->core, offset, val) +#ifdef CONFIG_BCMA_DRIVER_PCI extern void bcma_core_pci_power_save(struct bcma_bus *bus, bool up); +#else +static inline void bcma_core_pci_power_save(struct bcma_bus *bus, bool up) +{ +} +#endif extern int bcma_core_pci_pcibios_map_irq(const struct pci_dev *dev); extern int bcma_core_pci_plat_dev_init(struct pci_dev *dev); -- cgit v1.2.3 From b62c21b71f08b7a4bfd025616ff1da2913a82904 Mon Sep 17 00:00:00 2001 From: Mike Snitzer Date: Thu, 12 Mar 2015 23:56:02 -0400 Subject: blk-mq: add blk_mq_init_allocated_queue and export blk_mq_register_disk Add a variant of blk_mq_init_queue that allows a previously allocated queue to be initialized. blk_mq_init_allocated_queue models blk_init_allocated_queue -- which was also created for DM's use. DM's approach to device creation requires a placeholder request_queue be allocated for use with alloc_dev() but the decision about what type of request_queue will be ultimately created is deferred until all component devices referenced in the DM table are processed to determine the table type (request-based, blk-mq request-based, or bio-based). Also, because of DM's late finalization of the request_queue type the call to blk_mq_register_disk() doesn't happen during alloc_dev(). Must export blk_mq_register_disk() so that DM can backfill the 'mq' dir once the blk-mq queue is fully allocated. Signed-off-by: Mike Snitzer Reviewed-by: Ming Lei Signed-off-by: Jens Axboe --- block/blk-mq-sysfs.c | 1 + block/blk-mq.c | 30 ++++++++++++++++++++---------- include/linux/blk-mq.h | 2 ++ 3 files changed, 23 insertions(+), 10 deletions(-) (limited to 'include') diff --git a/block/blk-mq-sysfs.c b/block/blk-mq-sysfs.c index 1630a20d5dcf..b79685e06b70 100644 --- a/block/blk-mq-sysfs.c +++ b/block/blk-mq-sysfs.c @@ -436,6 +436,7 @@ int blk_mq_register_disk(struct gendisk *disk) return 0; } +EXPORT_SYMBOL_GPL(blk_mq_register_disk); void blk_mq_sysfs_unregister(struct request_queue *q) { diff --git a/block/blk-mq.c b/block/blk-mq.c index b7b8933ec241..3000121840bb 100644 --- a/block/blk-mq.c +++ b/block/blk-mq.c @@ -1890,10 +1890,26 @@ void blk_mq_release(struct request_queue *q) } struct request_queue *blk_mq_init_queue(struct blk_mq_tag_set *set) +{ + struct request_queue *uninit_q, *q; + + uninit_q = blk_alloc_queue_node(GFP_KERNEL, set->numa_node); + if (!uninit_q) + return ERR_PTR(-ENOMEM); + + q = blk_mq_init_allocated_queue(set, uninit_q); + if (IS_ERR(q)) + blk_cleanup_queue(uninit_q); + + return q; +} +EXPORT_SYMBOL(blk_mq_init_queue); + +struct request_queue *blk_mq_init_allocated_queue(struct blk_mq_tag_set *set, + struct request_queue *q) { struct blk_mq_hw_ctx **hctxs; struct blk_mq_ctx __percpu *ctx; - struct request_queue *q; unsigned int *map; int i; @@ -1928,17 +1944,13 @@ struct request_queue *blk_mq_init_queue(struct blk_mq_tag_set *set) hctxs[i]->queue_num = i; } - q = blk_alloc_queue_node(GFP_KERNEL, set->numa_node); - if (!q) - goto err_hctxs; - /* * Init percpu_ref in atomic mode so that it's faster to shutdown. * See blk_register_queue() for details. */ if (percpu_ref_init(&q->mq_usage_counter, blk_mq_usage_counter_release, PERCPU_REF_INIT_ATOMIC, GFP_KERNEL)) - goto err_mq_usage; + goto err_hctxs; setup_timer(&q->timeout, blk_mq_rq_timer, (unsigned long) q); blk_queue_rq_timeout(q, 30000); @@ -1981,7 +1993,7 @@ struct request_queue *blk_mq_init_queue(struct blk_mq_tag_set *set) blk_mq_init_cpu_queues(q, set->nr_hw_queues); if (blk_mq_init_hw_queues(q, set)) - goto err_mq_usage; + goto err_hctxs; mutex_lock(&all_q_mutex); list_add_tail(&q->all_q_node, &all_q_list); @@ -1993,8 +2005,6 @@ struct request_queue *blk_mq_init_queue(struct blk_mq_tag_set *set) return q; -err_mq_usage: - blk_cleanup_queue(q); err_hctxs: kfree(map); for (i = 0; i < set->nr_hw_queues; i++) { @@ -2009,7 +2019,7 @@ err_percpu: free_percpu(ctx); return ERR_PTR(-ENOMEM); } -EXPORT_SYMBOL(blk_mq_init_queue); +EXPORT_SYMBOL(blk_mq_init_allocated_queue); void blk_mq_free_queue(struct request_queue *q) { diff --git a/include/linux/blk-mq.h b/include/linux/blk-mq.h index 7aec86127335..9a75c88e8908 100644 --- a/include/linux/blk-mq.h +++ b/include/linux/blk-mq.h @@ -164,6 +164,8 @@ enum { << BLK_MQ_F_ALLOC_POLICY_START_BIT) struct request_queue *blk_mq_init_queue(struct blk_mq_tag_set *); +struct request_queue *blk_mq_init_allocated_queue(struct blk_mq_tag_set *set, + struct request_queue *q); void blk_mq_finish_init(struct request_queue *q); int blk_mq_register_disk(struct gendisk *); void blk_mq_unregister_disk(struct gendisk *); -- cgit v1.2.3 From b94ec296403e99d5ac9a8c48332cec4118d44b94 Mon Sep 17 00:00:00 2001 From: Mike Snitzer Date: Wed, 11 Mar 2015 23:56:38 -0400 Subject: blk-mq: export blk_mq_run_hw_queues Rename blk_mq_run_queues to blk_mq_run_hw_queues, add async argument, and export it. DM's suspend support must be able to run the queue without starting stopped hw queues. Signed-off-by: Mike Snitzer Signed-off-by: Jens Axboe --- block/blk-mq.c | 8 ++++---- include/linux/blk-mq.h | 1 + 2 files changed, 5 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/block/blk-mq.c b/block/blk-mq.c index 3000121840bb..06614ce0f475 100644 --- a/block/blk-mq.c +++ b/block/blk-mq.c @@ -33,7 +33,6 @@ static DEFINE_MUTEX(all_q_mutex); static LIST_HEAD(all_q_list); static void __blk_mq_run_hw_queue(struct blk_mq_hw_ctx *hctx); -static void blk_mq_run_queues(struct request_queue *q); /* * Check if any of the ctx's have pending work in this hardware queue @@ -118,7 +117,7 @@ void blk_mq_freeze_queue_start(struct request_queue *q) if (freeze) { percpu_ref_kill(&q->mq_usage_counter); - blk_mq_run_queues(q); + blk_mq_run_hw_queues(q, false); } } EXPORT_SYMBOL_GPL(blk_mq_freeze_queue_start); @@ -904,7 +903,7 @@ void blk_mq_run_hw_queue(struct blk_mq_hw_ctx *hctx, bool async) &hctx->run_work, 0); } -static void blk_mq_run_queues(struct request_queue *q) +void blk_mq_run_hw_queues(struct request_queue *q, bool async) { struct blk_mq_hw_ctx *hctx; int i; @@ -915,9 +914,10 @@ static void blk_mq_run_queues(struct request_queue *q) test_bit(BLK_MQ_S_STOPPED, &hctx->state)) continue; - blk_mq_run_hw_queue(hctx, false); + blk_mq_run_hw_queue(hctx, async); } } +EXPORT_SYMBOL(blk_mq_run_hw_queues); void blk_mq_stop_hw_queue(struct blk_mq_hw_ctx *hctx) { diff --git a/include/linux/blk-mq.h b/include/linux/blk-mq.h index 9a75c88e8908..ebfe707cf722 100644 --- a/include/linux/blk-mq.h +++ b/include/linux/blk-mq.h @@ -220,6 +220,7 @@ void blk_mq_start_hw_queue(struct blk_mq_hw_ctx *hctx); void blk_mq_stop_hw_queues(struct request_queue *q); void blk_mq_start_hw_queues(struct request_queue *q); void blk_mq_start_stopped_hw_queues(struct request_queue *q, bool async); +void blk_mq_run_hw_queues(struct request_queue *q, bool async); void blk_mq_delay_queue(struct blk_mq_hw_ctx *hctx, unsigned long msecs); void blk_mq_tag_busy_iter(struct blk_mq_hw_ctx *hctx, busy_iter_fn *fn, void *priv); -- cgit v1.2.3 From 599bd19bdc4c6b20fd91d50f2f79dececbaf80c1 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Wed, 11 Feb 2015 19:59:44 +0100 Subject: fs: don't allow to complete sync iocbs through aio_complete The AIO interface is fairly complex because it tries to allow filesystems to always work async and then wakeup a synchronous caller through aio_complete. It turns out that basically no one was doing this to avoid the complexity and context switches, and we've already fixed up the remaining users and can now get rid of this case. Signed-off-by: Christoph Hellwig Signed-off-by: Al Viro --- fs/aio.c | 24 +----------------------- fs/ecryptfs/file.c | 6 ------ fs/read_write.c | 26 ++++++++------------------ include/linux/aio.h | 4 ---- net/socket.c | 9 +++------ 5 files changed, 12 insertions(+), 57 deletions(-) (limited to 'include') diff --git a/fs/aio.c b/fs/aio.c index 667054c7c067..8ca8df1c3550 100644 --- a/fs/aio.c +++ b/fs/aio.c @@ -778,22 +778,6 @@ static int kill_ioctx(struct mm_struct *mm, struct kioctx *ctx, return 0; } -/* wait_on_sync_kiocb: - * Waits on the given sync kiocb to complete. - */ -ssize_t wait_on_sync_kiocb(struct kiocb *req) -{ - while (!req->ki_ctx) { - set_current_state(TASK_UNINTERRUPTIBLE); - if (req->ki_ctx) - break; - io_schedule(); - } - __set_current_state(TASK_RUNNING); - return req->ki_user_data; -} -EXPORT_SYMBOL(wait_on_sync_kiocb); - /* * exit_aio: called when the last user of mm goes away. At this point, there is * no way for any new requests to be submited or any of the io_* syscalls to be @@ -1025,13 +1009,7 @@ void aio_complete(struct kiocb *iocb, long res, long res2) * ref, no other paths have a way to get another ref * - the sync task helpfully left a reference to itself in the iocb */ - if (is_sync_kiocb(iocb)) { - iocb->ki_user_data = res; - smp_wmb(); - iocb->ki_ctx = ERR_PTR(-EXDEV); - wake_up_process(iocb->ki_obj.tsk); - return; - } + BUG_ON(is_sync_kiocb(iocb)); if (iocb->ki_list.next) { unsigned long flags; diff --git a/fs/ecryptfs/file.c b/fs/ecryptfs/file.c index 6f4e659f508f..a36da8841e0c 100644 --- a/fs/ecryptfs/file.c +++ b/fs/ecryptfs/file.c @@ -52,12 +52,6 @@ static ssize_t ecryptfs_read_update_atime(struct kiocb *iocb, struct file *file = iocb->ki_filp; rc = generic_file_read_iter(iocb, to); - /* - * Even though this is a async interface, we need to wait - * for IO to finish to update atime - */ - if (-EIOCBQUEUED == rc) - rc = wait_on_sync_kiocb(iocb); if (rc >= 0) { path = ecryptfs_dentry_to_lower_path(file->f_path.dentry); touch_atime(path); diff --git a/fs/read_write.c b/fs/read_write.c index f8b8fc1316ab..76e324e8ce8d 100644 --- a/fs/read_write.c +++ b/fs/read_write.c @@ -346,9 +346,7 @@ ssize_t vfs_iter_read(struct file *file, struct iov_iter *iter, loff_t *ppos) iter->type |= READ; ret = file->f_op->read_iter(&kiocb, iter); - if (ret == -EIOCBQUEUED) - ret = wait_on_sync_kiocb(&kiocb); - + BUG_ON(ret == -EIOCBQUEUED); if (ret > 0) *ppos = kiocb.ki_pos; return ret; @@ -368,9 +366,7 @@ ssize_t vfs_iter_write(struct file *file, struct iov_iter *iter, loff_t *ppos) iter->type |= WRITE; ret = file->f_op->write_iter(&kiocb, iter); - if (ret == -EIOCBQUEUED) - ret = wait_on_sync_kiocb(&kiocb); - + BUG_ON(ret == -EIOCBQUEUED); if (ret > 0) *ppos = kiocb.ki_pos; return ret; @@ -426,8 +422,7 @@ ssize_t do_sync_read(struct file *filp, char __user *buf, size_t len, loff_t *pp kiocb.ki_pos = *ppos; ret = filp->f_op->aio_read(&kiocb, &iov, 1, kiocb.ki_pos); - if (-EIOCBQUEUED == ret) - ret = wait_on_sync_kiocb(&kiocb); + BUG_ON(ret == -EIOCBQUEUED); *ppos = kiocb.ki_pos; return ret; } @@ -446,8 +441,7 @@ ssize_t new_sync_read(struct file *filp, char __user *buf, size_t len, loff_t *p iov_iter_init(&iter, READ, &iov, 1, len); ret = filp->f_op->read_iter(&kiocb, &iter); - if (-EIOCBQUEUED == ret) - ret = wait_on_sync_kiocb(&kiocb); + BUG_ON(ret == -EIOCBQUEUED); *ppos = kiocb.ki_pos; return ret; } @@ -508,8 +502,7 @@ ssize_t do_sync_write(struct file *filp, const char __user *buf, size_t len, lof kiocb.ki_pos = *ppos; ret = filp->f_op->aio_write(&kiocb, &iov, 1, kiocb.ki_pos); - if (-EIOCBQUEUED == ret) - ret = wait_on_sync_kiocb(&kiocb); + BUG_ON(ret == -EIOCBQUEUED); *ppos = kiocb.ki_pos; return ret; } @@ -528,8 +521,7 @@ ssize_t new_sync_write(struct file *filp, const char __user *buf, size_t len, lo iov_iter_init(&iter, WRITE, &iov, 1, len); ret = filp->f_op->write_iter(&kiocb, &iter); - if (-EIOCBQUEUED == ret) - ret = wait_on_sync_kiocb(&kiocb); + BUG_ON(ret == -EIOCBQUEUED); *ppos = kiocb.ki_pos; return ret; } @@ -716,8 +708,7 @@ static ssize_t do_iter_readv_writev(struct file *filp, int rw, const struct iove iov_iter_init(&iter, rw, iov, nr_segs, len); ret = fn(&kiocb, &iter); - if (ret == -EIOCBQUEUED) - ret = wait_on_sync_kiocb(&kiocb); + BUG_ON(ret == -EIOCBQUEUED); *ppos = kiocb.ki_pos; return ret; } @@ -732,8 +723,7 @@ static ssize_t do_sync_readv_writev(struct file *filp, const struct iovec *iov, kiocb.ki_pos = *ppos; ret = fn(&kiocb, iov, nr_segs, kiocb.ki_pos); - if (ret == -EIOCBQUEUED) - ret = wait_on_sync_kiocb(&kiocb); + BUG_ON(ret == -EIOCBQUEUED); *ppos = kiocb.ki_pos; return ret; } diff --git a/include/linux/aio.h b/include/linux/aio.h index 132d1ecba435..f8516430490d 100644 --- a/include/linux/aio.h +++ b/include/linux/aio.h @@ -37,7 +37,6 @@ struct kiocb { union { void __user *user; - struct task_struct *tsk; } ki_obj; __u64 ki_user_data; /* user's data for completion */ @@ -63,13 +62,11 @@ static inline void init_sync_kiocb(struct kiocb *kiocb, struct file *filp) *kiocb = (struct kiocb) { .ki_ctx = NULL, .ki_filp = filp, - .ki_obj.tsk = current, }; } /* prototypes */ #ifdef CONFIG_AIO -extern ssize_t wait_on_sync_kiocb(struct kiocb *iocb); extern void aio_complete(struct kiocb *iocb, long res, long res2); struct mm_struct; extern void exit_aio(struct mm_struct *mm); @@ -77,7 +74,6 @@ extern long do_io_submit(aio_context_t ctx_id, long nr, struct iocb __user *__user *iocbpp, bool compat); void kiocb_set_cancel_fn(struct kiocb *req, kiocb_cancel_fn *cancel); #else -static inline ssize_t wait_on_sync_kiocb(struct kiocb *iocb) { return 0; } static inline void aio_complete(struct kiocb *iocb, long res, long res2) { } struct mm_struct; static inline void exit_aio(struct mm_struct *mm) { } diff --git a/net/socket.c b/net/socket.c index f92145554f34..f6c519d7b3ba 100644 --- a/net/socket.c +++ b/net/socket.c @@ -633,8 +633,7 @@ static int do_sock_sendmsg(struct socket *sock, struct msghdr *msg, init_sync_kiocb(&iocb, NULL); ret = nosec ? __sock_sendmsg_nosec(&iocb, sock, msg, size) : __sock_sendmsg(&iocb, sock, msg, size); - if (-EIOCBQUEUED == ret) - ret = wait_on_sync_kiocb(&iocb); + BUG_ON(ret == -EIOCBQUEUED); return ret; } @@ -766,8 +765,7 @@ int sock_recvmsg(struct socket *sock, struct msghdr *msg, init_sync_kiocb(&iocb, NULL); ret = __sock_recvmsg(&iocb, sock, msg, size, flags); - if (-EIOCBQUEUED == ret) - ret = wait_on_sync_kiocb(&iocb); + BUG_ON(ret == -EIOCBQUEUED); return ret; } EXPORT_SYMBOL(sock_recvmsg); @@ -780,8 +778,7 @@ static int sock_recvmsg_nosec(struct socket *sock, struct msghdr *msg, init_sync_kiocb(&iocb, NULL); ret = __sock_recvmsg_nosec(&iocb, sock, msg, size, flags); - if (-EIOCBQUEUED == ret) - ret = wait_on_sync_kiocb(&iocb); + BUG_ON(ret == -EIOCBQUEUED); return ret; } -- cgit v1.2.3 From 04b2fa9f8f36ec6fb6fd1c9dc9df6fff0cd27323 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Mon, 2 Feb 2015 14:49:06 +0100 Subject: fs: split generic and aio kiocb Most callers in the kernel want to perform synchronous file I/O, but still have to bloat the stack with a full struct kiocb. Split out the parts needed in filesystem code from those in the aio code, and only allocate those needed to pass down argument on the stack. The aio code embedds the generic iocb in the one it allocates and can easily get back to it by using container_of. Also add a ->ki_complete method to struct kiocb, this is used to call into the aio code and thus removes the dependency on aio for filesystems impementing asynchronous operations. It will also allow other callers to substitute their own completion callback. We also add a new ->ki_flags field to work around the nasty layering violation recently introduced in commit 5e33f6 ("usb: gadget: ffs: add eventfd notification about ffs events"). Signed-off-by: Christoph Hellwig Signed-off-by: Al Viro --- drivers/usb/gadget/function/f_fs.c | 5 +- drivers/usb/gadget/legacy/inode.c | 5 +- fs/aio.c | 94 ++++++++++++++++++++++++++------------ fs/direct-io.c | 4 +- fs/fuse/file.c | 2 +- fs/nfs/direct.c | 2 +- include/linux/aio.h | 46 +++---------------- 7 files changed, 81 insertions(+), 77 deletions(-) (limited to 'include') diff --git a/drivers/usb/gadget/function/f_fs.c b/drivers/usb/gadget/function/f_fs.c index 175c9956cbe3..b64538b498dc 100644 --- a/drivers/usb/gadget/function/f_fs.c +++ b/drivers/usb/gadget/function/f_fs.c @@ -655,9 +655,10 @@ static void ffs_user_copy_worker(struct work_struct *work) unuse_mm(io_data->mm); } - aio_complete(io_data->kiocb, ret, ret); + io_data->kiocb->ki_complete(io_data->kiocb, ret, ret); - if (io_data->ffs->ffs_eventfd && !io_data->kiocb->ki_eventfd) + if (io_data->ffs->ffs_eventfd && + !(io_data->kiocb->ki_flags & IOCB_EVENTFD)) eventfd_signal(io_data->ffs->ffs_eventfd, 1); usb_ep_free_request(io_data->ep, io_data->req); diff --git a/drivers/usb/gadget/legacy/inode.c b/drivers/usb/gadget/legacy/inode.c index 200f9a584064..a4a80694f607 100644 --- a/drivers/usb/gadget/legacy/inode.c +++ b/drivers/usb/gadget/legacy/inode.c @@ -469,7 +469,7 @@ static void ep_user_copy_worker(struct work_struct *work) ret = -EFAULT; /* completing the iocb can drop the ctx and mm, don't touch mm after */ - aio_complete(iocb, ret, ret); + iocb->ki_complete(iocb, ret, ret); kfree(priv->buf); kfree(priv->to_free); @@ -497,7 +497,8 @@ static void ep_aio_complete(struct usb_ep *ep, struct usb_request *req) kfree(priv); iocb->private = NULL; /* aio_complete() reports bytes-transferred _and_ faults */ - aio_complete(iocb, req->actual ? req->actual : req->status, + + iocb->ki_complete(iocb, req->actual ? req->actual : req->status, req->status); } else { /* ep_copy_to_user() won't report both; we hide some faults */ diff --git a/fs/aio.c b/fs/aio.c index 8ca8df1c3550..958286536a10 100644 --- a/fs/aio.c +++ b/fs/aio.c @@ -151,6 +151,38 @@ struct kioctx { unsigned id; }; +/* + * We use ki_cancel == KIOCB_CANCELLED to indicate that a kiocb has been either + * cancelled or completed (this makes a certain amount of sense because + * successful cancellation - io_cancel() - does deliver the completion to + * userspace). + * + * And since most things don't implement kiocb cancellation and we'd really like + * kiocb completion to be lockless when possible, we use ki_cancel to + * synchronize cancellation and completion - we only set it to KIOCB_CANCELLED + * with xchg() or cmpxchg(), see batch_complete_aio() and kiocb_cancel(). + */ +#define KIOCB_CANCELLED ((void *) (~0ULL)) + +struct aio_kiocb { + struct kiocb common; + + struct kioctx *ki_ctx; + kiocb_cancel_fn *ki_cancel; + + struct iocb __user *ki_user_iocb; /* user's aiocb */ + __u64 ki_user_data; /* user's data for completion */ + + struct list_head ki_list; /* the aio core uses this + * for cancellation */ + + /* + * If the aio_resfd field of the userspace iocb is not zero, + * this is the underlying eventfd context to deliver events to. + */ + struct eventfd_ctx *ki_eventfd; +}; + /*------ sysctl variables----*/ static DEFINE_SPINLOCK(aio_nr_lock); unsigned long aio_nr; /* current system wide number of aio requests */ @@ -220,7 +252,7 @@ static int __init aio_setup(void) if (IS_ERR(aio_mnt)) panic("Failed to create aio fs mount."); - kiocb_cachep = KMEM_CACHE(kiocb, SLAB_HWCACHE_ALIGN|SLAB_PANIC); + kiocb_cachep = KMEM_CACHE(aio_kiocb, SLAB_HWCACHE_ALIGN|SLAB_PANIC); kioctx_cachep = KMEM_CACHE(kioctx,SLAB_HWCACHE_ALIGN|SLAB_PANIC); pr_debug("sizeof(struct page) = %zu\n", sizeof(struct page)); @@ -480,8 +512,9 @@ static int aio_setup_ring(struct kioctx *ctx) #define AIO_EVENTS_FIRST_PAGE ((PAGE_SIZE - sizeof(struct aio_ring)) / sizeof(struct io_event)) #define AIO_EVENTS_OFFSET (AIO_EVENTS_PER_PAGE - AIO_EVENTS_FIRST_PAGE) -void kiocb_set_cancel_fn(struct kiocb *req, kiocb_cancel_fn *cancel) +void kiocb_set_cancel_fn(struct kiocb *iocb, kiocb_cancel_fn *cancel) { + struct aio_kiocb *req = container_of(iocb, struct aio_kiocb, common); struct kioctx *ctx = req->ki_ctx; unsigned long flags; @@ -496,7 +529,7 @@ void kiocb_set_cancel_fn(struct kiocb *req, kiocb_cancel_fn *cancel) } EXPORT_SYMBOL(kiocb_set_cancel_fn); -static int kiocb_cancel(struct kiocb *kiocb) +static int kiocb_cancel(struct aio_kiocb *kiocb) { kiocb_cancel_fn *old, *cancel; @@ -514,7 +547,7 @@ static int kiocb_cancel(struct kiocb *kiocb) cancel = cmpxchg(&kiocb->ki_cancel, old, KIOCB_CANCELLED); } while (cancel != old); - return cancel(kiocb); + return cancel(&kiocb->common); } static void free_ioctx(struct work_struct *work) @@ -550,13 +583,13 @@ static void free_ioctx_reqs(struct percpu_ref *ref) static void free_ioctx_users(struct percpu_ref *ref) { struct kioctx *ctx = container_of(ref, struct kioctx, users); - struct kiocb *req; + struct aio_kiocb *req; spin_lock_irq(&ctx->ctx_lock); while (!list_empty(&ctx->active_reqs)) { req = list_first_entry(&ctx->active_reqs, - struct kiocb, ki_list); + struct aio_kiocb, ki_list); list_del_init(&req->ki_list); kiocb_cancel(req); @@ -932,9 +965,9 @@ static void user_refill_reqs_available(struct kioctx *ctx) * Allocate a slot for an aio request. * Returns NULL if no requests are free. */ -static inline struct kiocb *aio_get_req(struct kioctx *ctx) +static inline struct aio_kiocb *aio_get_req(struct kioctx *ctx) { - struct kiocb *req; + struct aio_kiocb *req; if (!get_reqs_available(ctx)) { user_refill_reqs_available(ctx); @@ -955,10 +988,10 @@ out_put: return NULL; } -static void kiocb_free(struct kiocb *req) +static void kiocb_free(struct aio_kiocb *req) { - if (req->ki_filp) - fput(req->ki_filp); + if (req->common.ki_filp) + fput(req->common.ki_filp); if (req->ki_eventfd != NULL) eventfd_ctx_put(req->ki_eventfd); kmem_cache_free(kiocb_cachep, req); @@ -994,8 +1027,9 @@ out: /* aio_complete * Called when the io request on the given iocb is complete. */ -void aio_complete(struct kiocb *iocb, long res, long res2) +static void aio_complete(struct kiocb *kiocb, long res, long res2) { + struct aio_kiocb *iocb = container_of(kiocb, struct aio_kiocb, common); struct kioctx *ctx = iocb->ki_ctx; struct aio_ring *ring; struct io_event *ev_page, *event; @@ -1009,7 +1043,7 @@ void aio_complete(struct kiocb *iocb, long res, long res2) * ref, no other paths have a way to get another ref * - the sync task helpfully left a reference to itself in the iocb */ - BUG_ON(is_sync_kiocb(iocb)); + BUG_ON(is_sync_kiocb(kiocb)); if (iocb->ki_list.next) { unsigned long flags; @@ -1035,7 +1069,7 @@ void aio_complete(struct kiocb *iocb, long res, long res2) ev_page = kmap_atomic(ctx->ring_pages[pos / AIO_EVENTS_PER_PAGE]); event = ev_page + pos % AIO_EVENTS_PER_PAGE; - event->obj = (u64)(unsigned long)iocb->ki_obj.user; + event->obj = (u64)(unsigned long)iocb->ki_user_iocb; event->data = iocb->ki_user_data; event->res = res; event->res2 = res2; @@ -1044,7 +1078,7 @@ void aio_complete(struct kiocb *iocb, long res, long res2) flush_dcache_page(ctx->ring_pages[pos / AIO_EVENTS_PER_PAGE]); pr_debug("%p[%u]: %p: %p %Lx %lx %lx\n", - ctx, tail, iocb, iocb->ki_obj.user, iocb->ki_user_data, + ctx, tail, iocb, iocb->ki_user_iocb, iocb->ki_user_data, res, res2); /* after flagging the request as done, we @@ -1091,7 +1125,6 @@ void aio_complete(struct kiocb *iocb, long res, long res2) percpu_ref_put(&ctx->reqs); } -EXPORT_SYMBOL(aio_complete); /* aio_read_events_ring * Pull an event off of the ioctx's event ring. Returns the number of @@ -1480,7 +1513,7 @@ rw_common: static int io_submit_one(struct kioctx *ctx, struct iocb __user *user_iocb, struct iocb *iocb, bool compat) { - struct kiocb *req; + struct aio_kiocb *req; ssize_t ret; /* enforce forwards compatibility on users */ @@ -1503,11 +1536,14 @@ static int io_submit_one(struct kioctx *ctx, struct iocb __user *user_iocb, if (unlikely(!req)) return -EAGAIN; - req->ki_filp = fget(iocb->aio_fildes); - if (unlikely(!req->ki_filp)) { + req->common.ki_filp = fget(iocb->aio_fildes); + if (unlikely(!req->common.ki_filp)) { ret = -EBADF; goto out_put_req; } + req->common.ki_pos = iocb->aio_offset; + req->common.ki_complete = aio_complete; + req->common.ki_flags = 0; if (iocb->aio_flags & IOCB_FLAG_RESFD) { /* @@ -1522,6 +1558,8 @@ static int io_submit_one(struct kioctx *ctx, struct iocb __user *user_iocb, req->ki_eventfd = NULL; goto out_put_req; } + + req->common.ki_flags |= IOCB_EVENTFD; } ret = put_user(KIOCB_KEY, &user_iocb->aio_key); @@ -1530,11 +1568,10 @@ static int io_submit_one(struct kioctx *ctx, struct iocb __user *user_iocb, goto out_put_req; } - req->ki_obj.user = user_iocb; + req->ki_user_iocb = user_iocb; req->ki_user_data = iocb->aio_data; - req->ki_pos = iocb->aio_offset; - ret = aio_run_iocb(req, iocb->aio_lio_opcode, + ret = aio_run_iocb(&req->common, iocb->aio_lio_opcode, (char __user *)(unsigned long)iocb->aio_buf, iocb->aio_nbytes, compat); @@ -1623,10 +1660,10 @@ SYSCALL_DEFINE3(io_submit, aio_context_t, ctx_id, long, nr, /* lookup_kiocb * Finds a given iocb for cancellation. */ -static struct kiocb *lookup_kiocb(struct kioctx *ctx, struct iocb __user *iocb, - u32 key) +static struct aio_kiocb * +lookup_kiocb(struct kioctx *ctx, struct iocb __user *iocb, u32 key) { - struct list_head *pos; + struct aio_kiocb *kiocb; assert_spin_locked(&ctx->ctx_lock); @@ -1634,9 +1671,8 @@ static struct kiocb *lookup_kiocb(struct kioctx *ctx, struct iocb __user *iocb, return NULL; /* TODO: use a hash or array, this sucks. */ - list_for_each(pos, &ctx->active_reqs) { - struct kiocb *kiocb = list_kiocb(pos); - if (kiocb->ki_obj.user == iocb) + list_for_each_entry(kiocb, &ctx->active_reqs, ki_list) { + if (kiocb->ki_user_iocb == iocb) return kiocb; } return NULL; @@ -1656,7 +1692,7 @@ SYSCALL_DEFINE3(io_cancel, aio_context_t, ctx_id, struct iocb __user *, iocb, struct io_event __user *, result) { struct kioctx *ctx; - struct kiocb *kiocb; + struct aio_kiocb *kiocb; u32 key; int ret; diff --git a/fs/direct-io.c b/fs/direct-io.c index e181b6b2e297..c38b460776e6 100644 --- a/fs/direct-io.c +++ b/fs/direct-io.c @@ -265,7 +265,7 @@ static ssize_t dio_complete(struct dio *dio, loff_t offset, ssize_t ret, ret = err; } - aio_complete(dio->iocb, ret, 0); + dio->iocb->ki_complete(dio->iocb, ret, 0); } kmem_cache_free(dio_cache, dio); @@ -1056,7 +1056,7 @@ static inline int drop_refcount(struct dio *dio) * operation. AIO can if it was a broken operation described above or * in fact if all the bios race to complete before we get here. In * that case dio_complete() translates the EIOCBQUEUED into the proper - * return code that the caller will hand to aio_complete(). + * return code that the caller will hand to ->complete(). * * This is managed by the bio_lock instead of being an atomic_t so that * completion paths can drop their ref and use the remaining count to diff --git a/fs/fuse/file.c b/fs/fuse/file.c index f81d83eb9758..a5c5e38b3ff8 100644 --- a/fs/fuse/file.c +++ b/fs/fuse/file.c @@ -584,7 +584,7 @@ static void fuse_aio_complete(struct fuse_io_priv *io, int err, ssize_t pos) spin_unlock(&fc->lock); } - aio_complete(io->iocb, res, 0); + io->iocb->ki_complete(io->iocb, res, 0); kfree(io); } } diff --git a/fs/nfs/direct.c b/fs/nfs/direct.c index 27cebf164070..5db3385fc108 100644 --- a/fs/nfs/direct.c +++ b/fs/nfs/direct.c @@ -393,7 +393,7 @@ static void nfs_direct_complete(struct nfs_direct_req *dreq, bool write) long res = (long) dreq->error; if (!res) res = (long) dreq->count; - aio_complete(dreq->iocb, res, 0); + dreq->iocb->ki_complete(dreq->iocb, res, 0); } complete_all(&dreq->completion); diff --git a/include/linux/aio.h b/include/linux/aio.h index f8516430490d..5c40b61285ac 100644 --- a/include/linux/aio.h +++ b/include/linux/aio.h @@ -14,67 +14,38 @@ struct kiocb; #define KIOCB_KEY 0 -/* - * We use ki_cancel == KIOCB_CANCELLED to indicate that a kiocb has been either - * cancelled or completed (this makes a certain amount of sense because - * successful cancellation - io_cancel() - does deliver the completion to - * userspace). - * - * And since most things don't implement kiocb cancellation and we'd really like - * kiocb completion to be lockless when possible, we use ki_cancel to - * synchronize cancellation and completion - we only set it to KIOCB_CANCELLED - * with xchg() or cmpxchg(), see batch_complete_aio() and kiocb_cancel(). - */ -#define KIOCB_CANCELLED ((void *) (~0ULL)) - typedef int (kiocb_cancel_fn)(struct kiocb *); +#define IOCB_EVENTFD (1 << 0) + struct kiocb { struct file *ki_filp; - struct kioctx *ki_ctx; /* NULL for sync ops */ - kiocb_cancel_fn *ki_cancel; - void *private; - - union { - void __user *user; - } ki_obj; - - __u64 ki_user_data; /* user's data for completion */ loff_t ki_pos; - - struct list_head ki_list; /* the aio core uses this - * for cancellation */ - - /* - * If the aio_resfd field of the userspace iocb is not zero, - * this is the underlying eventfd context to deliver events to. - */ - struct eventfd_ctx *ki_eventfd; + void (*ki_complete)(struct kiocb *iocb, long ret, long ret2); + void *private; + int ki_flags; }; static inline bool is_sync_kiocb(struct kiocb *kiocb) { - return kiocb->ki_ctx == NULL; + return kiocb->ki_complete == NULL; } static inline void init_sync_kiocb(struct kiocb *kiocb, struct file *filp) { *kiocb = (struct kiocb) { - .ki_ctx = NULL, .ki_filp = filp, }; } /* prototypes */ #ifdef CONFIG_AIO -extern void aio_complete(struct kiocb *iocb, long res, long res2); struct mm_struct; extern void exit_aio(struct mm_struct *mm); extern long do_io_submit(aio_context_t ctx_id, long nr, struct iocb __user *__user *iocbpp, bool compat); void kiocb_set_cancel_fn(struct kiocb *req, kiocb_cancel_fn *cancel); #else -static inline void aio_complete(struct kiocb *iocb, long res, long res2) { } struct mm_struct; static inline void exit_aio(struct mm_struct *mm) { } static inline long do_io_submit(aio_context_t ctx_id, long nr, @@ -84,11 +55,6 @@ static inline void kiocb_set_cancel_fn(struct kiocb *req, kiocb_cancel_fn *cancel) { } #endif /* CONFIG_AIO */ -static inline struct kiocb *list_kiocb(struct list_head *h) -{ - return list_entry(h, struct kiocb, ki_list); -} - /* for sysctl: */ extern unsigned long aio_nr; extern unsigned long aio_max_nr; -- cgit v1.2.3 From eacb44dff98559d4682072c0061e1ecb63687e9c Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Fri, 13 Mar 2015 09:04:17 -0700 Subject: Bluetooth: Use DECLARE_BITMAP for hdev->dev_flags field The hdev->dev_flags field has outgrown itself on 32-bit systems. So instead of hacking around it, switch to using DECLARE_BITMAP. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- include/net/bluetooth/hci.h | 7 +------ include/net/bluetooth/hci_core.h | 24 +++++++++++++++--------- net/bluetooth/hci_core.c | 2 +- net/bluetooth/hci_event.c | 2 +- 4 files changed, 18 insertions(+), 17 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index 0f3413b285a5..7a0272a6f0ba 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -226,14 +226,9 @@ enum { HCI_FAST_CONNECTABLE, HCI_BREDR_ENABLED, HCI_LE_SCAN_INTERRUPTED, + __HCI_NUM_FLAGS, }; -/* A mask for the flags that are supposed to remain when a reset happens - * or the HCI device is closed. - */ -#define HCI_PERSISTENT_MASK (BIT(HCI_LE_SCAN) | BIT(HCI_PERIODIC_INQ) | \ - BIT(HCI_LE_ADV)) - /* HCI timeouts */ #define HCI_DISCONN_TIMEOUT msecs_to_jiffies(2000) /* 2 seconds */ #define HCI_PAIRING_TIMEOUT msecs_to_jiffies(60000) /* 60 seconds */ diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 6db1333a114f..889a489d913f 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -354,7 +354,7 @@ struct hci_dev { struct rfkill *rfkill; unsigned long dbg_flags; - unsigned long dev_flags; + DECLARE_BITMAP(dev_flags, __HCI_NUM_FLAGS); struct delayed_work le_scan_disable; struct delayed_work le_scan_restart; @@ -502,14 +502,20 @@ extern struct list_head hci_cb_list; extern rwlock_t hci_dev_list_lock; extern struct mutex hci_cb_list_lock; -#define hci_dev_set_flag(hdev, nr) set_bit((nr), &(hdev)->dev_flags) -#define hci_dev_clear_flag(hdev, nr) clear_bit((nr), &(hdev)->dev_flags) -#define hci_dev_change_flag(hdev, nr) change_bit((nr), &(hdev)->dev_flags) -#define hci_dev_test_flag(hdev, nr) test_bit((nr), &(hdev)->dev_flags) - -#define hci_dev_test_and_set_flag(hdev, nr) test_and_set_bit((nr), &(hdev)->dev_flags) -#define hci_dev_test_and_clear_flag(hdev, nr) test_and_clear_bit((nr), &(hdev)->dev_flags) -#define hci_dev_test_and_change_flag(hdev, nr) test_and_change_bit((nr), &(hdev)->dev_flags) +#define hci_dev_set_flag(hdev, nr) set_bit((nr), (hdev)->dev_flags) +#define hci_dev_clear_flag(hdev, nr) clear_bit((nr), (hdev)->dev_flags) +#define hci_dev_change_flag(hdev, nr) change_bit((nr), (hdev)->dev_flags) +#define hci_dev_test_flag(hdev, nr) test_bit((nr), (hdev)->dev_flags) +#define hci_dev_test_and_set_flag(hdev, nr) test_and_set_bit((nr), (hdev)->dev_flags) +#define hci_dev_test_and_clear_flag(hdev, nr) test_and_clear_bit((nr), (hdev)->dev_flags) +#define hci_dev_test_and_change_flag(hdev, nr) test_and_change_bit((nr), (hdev)->dev_flags) + +#define hci_dev_clear_volatile_flags(hdev) \ + do { \ + hci_dev_clear_flag(hdev, HCI_LE_SCAN); \ + hci_dev_clear_flag(hdev, HCI_LE_ADV); \ + hci_dev_clear_flag(hdev, HCI_PERIODIC_INQ); \ + } while (0) /* ----- HCI interface to upper protocols ----- */ int l2cap_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr); diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index c6ed46c4f45a..23a43ca98785 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -1699,7 +1699,7 @@ static int hci_dev_do_close(struct hci_dev *hdev) /* Clear flags */ hdev->flags &= BIT(HCI_RAW); - hdev->dev_flags &= ~HCI_PERSISTENT_MASK; + hci_dev_clear_volatile_flags(hdev); /* Controller radio is available but is currently powered down */ hdev->amp_status = AMP_STATUS_POWERED_DOWN; diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 4958b24ae5c7..c7376cd42b1c 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -198,7 +198,7 @@ static void hci_cc_reset(struct hci_dev *hdev, struct sk_buff *skb) return; /* Reset all non-persistent flags */ - hdev->dev_flags &= ~HCI_PERSISTENT_MASK; + hci_dev_clear_volatile_flags(hdev); hci_discovery_set_state(hdev, DISCOVERY_STOPPED); -- cgit v1.2.3 From b7cb93e52839ee44959adabc17c2a17422e6bd4b Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Fri, 13 Mar 2015 10:20:35 -0700 Subject: Bluetooth: Merge hdev->dbg_flags fields into hdev->dev_flags With the extension of hdev->dev_flags utilizing a bitmap now, the space is no longer restricted. Merge the hdev->dbg_flags into hdev->dev_flags to save space on 64-bit architectures. On 32-bit architectures no size reduction happens. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- include/net/bluetooth/hci.h | 14 +++++--------- include/net/bluetooth/hci_core.h | 1 - net/bluetooth/hci_core.c | 8 ++++---- net/bluetooth/hci_debugfs.c | 6 +++--- net/bluetooth/hci_request.c | 2 +- net/bluetooth/l2cap_core.c | 2 +- net/bluetooth/mgmt.c | 2 +- net/bluetooth/smp.c | 10 +++++----- 8 files changed, 20 insertions(+), 25 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index 7a0272a6f0ba..d942fedbaedd 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -179,15 +179,6 @@ enum { HCI_RESET, }; -/* BR/EDR and/or LE controller flags: the flags defined here should represent - * states configured via debugfs for debugging and testing purposes only. - */ -enum { - HCI_DUT_MODE, - HCI_FORCE_BREDR_SMP, - HCI_FORCE_STATIC_ADDR, -}; - /* * BR/EDR and/or LE controller flags: the flags defined here should represent * states from the controller. @@ -226,6 +217,11 @@ enum { HCI_FAST_CONNECTABLE, HCI_BREDR_ENABLED, HCI_LE_SCAN_INTERRUPTED, + + HCI_DUT_MODE, + HCI_FORCE_BREDR_SMP, + HCI_FORCE_STATIC_ADDR, + __HCI_NUM_FLAGS, }; diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 889a489d913f..6afbf5b014a1 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -353,7 +353,6 @@ struct hci_dev { struct rfkill *rfkill; - unsigned long dbg_flags; DECLARE_BITMAP(dev_flags, __HCI_NUM_FLAGS); struct delayed_work le_scan_disable; diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 23a43ca98785..750d3445f2d2 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -80,7 +80,7 @@ static ssize_t dut_mode_read(struct file *file, char __user *user_buf, struct hci_dev *hdev = file->private_data; char buf[3]; - buf[0] = test_bit(HCI_DUT_MODE, &hdev->dbg_flags) ? 'Y': 'N'; + buf[0] = hci_dev_test_flag(hdev, HCI_DUT_MODE) ? 'Y': 'N'; buf[1] = '\n'; buf[2] = '\0'; return simple_read_from_buffer(user_buf, count, ppos, buf, 2); @@ -106,7 +106,7 @@ static ssize_t dut_mode_write(struct file *file, const char __user *user_buf, if (strtobool(buf, &enable)) return -EINVAL; - if (enable == test_bit(HCI_DUT_MODE, &hdev->dbg_flags)) + if (enable == hci_dev_test_flag(hdev, HCI_DUT_MODE)) return -EALREADY; hci_req_lock(hdev); @@ -127,7 +127,7 @@ static ssize_t dut_mode_write(struct file *file, const char __user *user_buf, if (err < 0) return err; - change_bit(HCI_DUT_MODE, &hdev->dbg_flags); + hci_dev_change_flag(hdev, HCI_DUT_MODE); return count; } @@ -3019,7 +3019,7 @@ static void le_scan_restart_work(struct work_struct *work) void hci_copy_identity_address(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 *bdaddr_type) { - if (test_bit(HCI_FORCE_STATIC_ADDR, &hdev->dbg_flags) || + if (hci_dev_test_flag(hdev, HCI_FORCE_STATIC_ADDR) || !bacmp(&hdev->bdaddr, BDADDR_ANY) || (!hci_dev_test_flag(hdev, HCI_BREDR_ENABLED) && bacmp(&hdev->static_addr, BDADDR_ANY))) { diff --git a/net/bluetooth/hci_debugfs.c b/net/bluetooth/hci_debugfs.c index 3c025ee5572c..bc801e9db834 100644 --- a/net/bluetooth/hci_debugfs.c +++ b/net/bluetooth/hci_debugfs.c @@ -679,7 +679,7 @@ static ssize_t force_static_address_read(struct file *file, struct hci_dev *hdev = file->private_data; char buf[3]; - buf[0] = test_bit(HCI_FORCE_STATIC_ADDR, &hdev->dbg_flags) ? 'Y': 'N'; + buf[0] = hci_dev_test_flag(hdev, HCI_FORCE_STATIC_ADDR) ? 'Y': 'N'; buf[1] = '\n'; buf[2] = '\0'; return simple_read_from_buffer(user_buf, count, ppos, buf, 2); @@ -704,10 +704,10 @@ static ssize_t force_static_address_write(struct file *file, if (strtobool(buf, &enable)) return -EINVAL; - if (enable == test_bit(HCI_FORCE_STATIC_ADDR, &hdev->dbg_flags)) + if (enable == hci_dev_test_flag(hdev, HCI_FORCE_STATIC_ADDR)) return -EALREADY; - change_bit(HCI_FORCE_STATIC_ADDR, &hdev->dbg_flags); + hci_dev_change_flag(hdev, HCI_FORCE_STATIC_ADDR); return count; } diff --git a/net/bluetooth/hci_request.c b/net/bluetooth/hci_request.c index e85f9ec9f73a..55e096d20a0f 100644 --- a/net/bluetooth/hci_request.c +++ b/net/bluetooth/hci_request.c @@ -383,7 +383,7 @@ int hci_update_random_address(struct hci_request *req, bool require_privacy, * and a static address has been configured, then use that * address instead of the public BR/EDR address. */ - if (test_bit(HCI_FORCE_STATIC_ADDR, &hdev->dbg_flags) || + if (hci_dev_test_flag(hdev, HCI_FORCE_STATIC_ADDR) || !bacmp(&hdev->bdaddr, BDADDR_ANY) || (!hci_dev_test_flag(hdev, HCI_BREDR_ENABLED) && bacmp(&hdev->static_addr, BDADDR_ANY))) { diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index af30d8240c80..d69861c89bb5 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -6992,7 +6992,7 @@ static struct l2cap_conn *l2cap_conn_add(struct hci_conn *hcon) if (hci_dev_test_flag(hcon->hdev, HCI_LE_ENABLED) && (bredr_sc_enabled(hcon->hdev) || - test_bit(HCI_FORCE_BREDR_SMP, &hcon->hdev->dbg_flags))) + hci_dev_test_flag(hcon->hdev, HCI_FORCE_BREDR_SMP))) conn->local_fixed_chan |= L2CAP_FC_SMP_BREDR; mutex_init(&conn->ident_lock); diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index d97719d04be0..c58908652519 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -659,7 +659,7 @@ static u32 get_current_settings(struct hci_dev *hdev) * with BR/EDR disabled, the existence of the static address will * be evaluated. */ - if (test_bit(HCI_FORCE_STATIC_ADDR, &hdev->dbg_flags) || + if (hci_dev_test_flag(hdev, HCI_FORCE_STATIC_ADDR) || !hci_dev_test_flag(hdev, HCI_BREDR_ENABLED) || !bacmp(&hdev->bdaddr, BDADDR_ANY)) { if (bacmp(&hdev->static_addr, BDADDR_ANY)) diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c index 81975f274c2b..9155840068cf 100644 --- a/net/bluetooth/smp.c +++ b/net/bluetooth/smp.c @@ -1679,7 +1679,7 @@ static u8 smp_cmd_pairing_req(struct l2cap_conn *conn, struct sk_buff *skb) if (conn->hcon->type == ACL_LINK) { /* We must have a BR/EDR SC link */ if (!test_bit(HCI_CONN_AES_CCM, &conn->hcon->flags) && - !test_bit(HCI_FORCE_BREDR_SMP, &hdev->dbg_flags)) + !hci_dev_test_flag(hdev, HCI_FORCE_BREDR_SMP)) return SMP_CROSS_TRANSP_NOT_ALLOWED; set_bit(SMP_FLAG_SC, &smp->flags); @@ -2749,7 +2749,7 @@ static void bredr_pairing(struct l2cap_chan *chan) /* BR/EDR must use Secure Connections for SMP */ if (!test_bit(HCI_CONN_AES_CCM, &hcon->flags) && - !test_bit(HCI_FORCE_BREDR_SMP, &hdev->dbg_flags)) + !hci_dev_test_flag(hdev, HCI_FORCE_BREDR_SMP)) return; /* If our LE support is not enabled don't do anything */ @@ -3003,7 +3003,7 @@ static ssize_t force_bredr_smp_read(struct file *file, struct hci_dev *hdev = file->private_data; char buf[3]; - buf[0] = test_bit(HCI_FORCE_BREDR_SMP, &hdev->dbg_flags) ? 'Y': 'N'; + buf[0] = hci_dev_test_flag(hdev, HCI_FORCE_BREDR_SMP) ? 'Y': 'N'; buf[1] = '\n'; buf[2] = '\0'; return simple_read_from_buffer(user_buf, count, ppos, buf, 2); @@ -3025,7 +3025,7 @@ static ssize_t force_bredr_smp_write(struct file *file, if (strtobool(buf, &enable)) return -EINVAL; - if (enable == test_bit(HCI_FORCE_BREDR_SMP, &hdev->dbg_flags)) + if (enable == hci_dev_test_flag(hdev, HCI_FORCE_BREDR_SMP)) return -EALREADY; if (enable) { @@ -3044,7 +3044,7 @@ static ssize_t force_bredr_smp_write(struct file *file, smp_del_chan(chan); } - change_bit(HCI_FORCE_BREDR_SMP, &hdev->dbg_flags); + hci_dev_change_flag(hdev, HCI_FORCE_BREDR_SMP); return count; } -- cgit v1.2.3 From 3f3c4bb5ec7c645d1151e1e8d6e56c71a050cf85 Mon Sep 17 00:00:00 2001 From: Alexander Aring Date: Wed, 4 Mar 2015 21:19:59 +0100 Subject: mac802154: correct max sifs size handling This patch fix the max sifs size correction when the IEEE802154_HW_TX_OMIT_CKSUM flag is set. With this flag the sk_buff doesn't contain the CRC, because the transceiver will add the CRC while transmit. Also add some defines for the max sifs frame size value and frame check sequence according to 802.15.4 standard. Signed-off-by: Alexander Aring Acked-by: Marc Kleine-Budde Signed-off-by: Marcel Holtmann --- include/linux/ieee802154.h | 2 ++ net/mac802154/util.c | 13 ++++++++++++- 2 files changed, 14 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/ieee802154.h b/include/linux/ieee802154.h index 40b0ab953937..8872ca103d06 100644 --- a/include/linux/ieee802154.h +++ b/include/linux/ieee802154.h @@ -30,6 +30,7 @@ #define IEEE802154_MTU 127 #define IEEE802154_ACK_PSDU_LEN 5 #define IEEE802154_MIN_PSDU_LEN 9 +#define IEEE802154_FCS_LEN 2 #define IEEE802154_PAN_ID_BROADCAST 0xffff #define IEEE802154_ADDR_SHORT_BROADCAST 0xffff @@ -39,6 +40,7 @@ #define IEEE802154_LIFS_PERIOD 40 #define IEEE802154_SIFS_PERIOD 12 +#define IEEE802154_MAX_SIFS_FRAME_SIZE 18 #define IEEE802154_MAX_CHANNEL 26 #define IEEE802154_MAX_PAGE 31 diff --git a/net/mac802154/util.c b/net/mac802154/util.c index 5fc979027919..150bf807e572 100644 --- a/net/mac802154/util.c +++ b/net/mac802154/util.c @@ -65,8 +65,19 @@ void ieee802154_xmit_complete(struct ieee802154_hw *hw, struct sk_buff *skb, { if (ifs_handling) { struct ieee802154_local *local = hw_to_local(hw); + u8 max_sifs_size; - if (skb->len > 18) + /* If transceiver sets CRC on his own we need to use lifs + * threshold len above 16 otherwise 18, because it's not + * part of skb->len. + */ + if (hw->flags & IEEE802154_HW_TX_OMIT_CKSUM) + max_sifs_size = IEEE802154_MAX_SIFS_FRAME_SIZE - + IEEE802154_FCS_LEN; + else + max_sifs_size = IEEE802154_MAX_SIFS_FRAME_SIZE; + + if (skb->len > max_sifs_size) hrtimer_start(&local->ifs_timer, ktime_set(0, hw->phy->lifs_period * NSEC_PER_USEC), HRTIMER_MODE_REL); -- cgit v1.2.3 From eddee5ba34eb6c9890ef106f19ead2b370e5342f Mon Sep 17 00:00:00 2001 From: Herbert Xu Date: Sat, 14 Mar 2015 13:57:20 +1100 Subject: rhashtable: Fix walker behaviour during rehash Previously whenever the walker encountered a resize it simply snaps back to the beginning and starts again. However, this only works if the rehash started and completed while the walker was idle. If the walker attempts to restart while the rehash is still ongoing, we may miss objects that we shouldn't have. This patch fixes this by making the walker walk the old table followed by the new table just like all other readers. If a rehash is detected we will still signal our caller of the fact so they can prepare for duplicates but we will simply continue the walk onto the new table after the old one is finished either by us or by the rehasher. Signed-off-by: Herbert Xu Signed-off-by: David S. Miller --- include/linux/rhashtable.h | 8 +++--- lib/rhashtable.c | 69 ++++++++++++++++++++++++++++++---------------- 2 files changed, 50 insertions(+), 27 deletions(-) (limited to 'include') diff --git a/include/linux/rhashtable.h b/include/linux/rhashtable.h index c93ff8ac474a..4192682c1d5c 100644 --- a/include/linux/rhashtable.h +++ b/include/linux/rhashtable.h @@ -53,6 +53,7 @@ struct rhash_head { * @shift: Current size (1 << shift) * @locks_mask: Mask to apply before accessing locks[] * @locks: Array of spinlocks protecting individual buckets + * @walkers: List of active walkers * @buckets: size * hash buckets */ struct bucket_table { @@ -61,6 +62,7 @@ struct bucket_table { u32 shift; unsigned int locks_mask; spinlock_t *locks; + struct list_head walkers; struct rhash_head __rcu *buckets[] ____cacheline_aligned_in_smp; }; @@ -104,7 +106,6 @@ struct rhashtable_params { * @p: Configuration parameters * @run_work: Deferred worker to expand/shrink asynchronously * @mutex: Mutex to protect current/future table swapping - * @walkers: List of active walkers * @being_destroyed: True if table is set up for destruction */ struct rhashtable { @@ -115,17 +116,16 @@ struct rhashtable { struct rhashtable_params p; struct work_struct run_work; struct mutex mutex; - struct list_head walkers; }; /** * struct rhashtable_walker - Hash table walker * @list: List entry on list of walkers - * @resize: Resize event occured + * @tbl: The table that we were walking over */ struct rhashtable_walker { struct list_head list; - bool resize; + struct bucket_table *tbl; }; /** diff --git a/lib/rhashtable.c b/lib/rhashtable.c index fc0d451279f0..f7c76079f8f1 100644 --- a/lib/rhashtable.c +++ b/lib/rhashtable.c @@ -170,6 +170,8 @@ static struct bucket_table *bucket_table_alloc(struct rhashtable *ht, return NULL; } + INIT_LIST_HEAD(&tbl->walkers); + for (i = 0; i < nbuckets; i++) INIT_RHT_NULLS_HEAD(tbl->buckets[i], ht, i); @@ -264,6 +266,7 @@ static void rhashtable_rehash(struct rhashtable *ht, struct bucket_table *new_tbl) { struct bucket_table *old_tbl = rht_dereference(ht->tbl, ht); + struct rhashtable_walker *walker; unsigned old_hash; get_random_bytes(&new_tbl->hash_rnd, sizeof(new_tbl->hash_rnd)); @@ -284,6 +287,9 @@ static void rhashtable_rehash(struct rhashtable *ht, /* Publish the new table pointer. */ rcu_assign_pointer(ht->tbl, new_tbl); + list_for_each_entry(walker, &old_tbl->walkers, list) + walker->tbl = NULL; + /* Wait for readers. All new readers will see the new * table, and thus no references to the old table will * remain. @@ -358,7 +364,6 @@ static void rht_deferred_worker(struct work_struct *work) { struct rhashtable *ht; struct bucket_table *tbl; - struct rhashtable_walker *walker; ht = container_of(work, struct rhashtable, run_work); mutex_lock(&ht->mutex); @@ -367,9 +372,6 @@ static void rht_deferred_worker(struct work_struct *work) tbl = rht_dereference(ht->tbl, ht); - list_for_each_entry(walker, &ht->walkers, list) - walker->resize = true; - if (rht_grow_above_75(ht, tbl)) rhashtable_expand(ht); else if (rht_shrink_below_30(ht, tbl)) @@ -725,11 +727,9 @@ int rhashtable_walk_init(struct rhashtable *ht, struct rhashtable_iter *iter) if (!iter->walker) return -ENOMEM; - INIT_LIST_HEAD(&iter->walker->list); - iter->walker->resize = false; - mutex_lock(&ht->mutex); - list_add(&iter->walker->list, &ht->walkers); + iter->walker->tbl = rht_dereference(ht->tbl, ht); + list_add(&iter->walker->list, &iter->walker->tbl->walkers); mutex_unlock(&ht->mutex); return 0; @@ -745,7 +745,8 @@ EXPORT_SYMBOL_GPL(rhashtable_walk_init); void rhashtable_walk_exit(struct rhashtable_iter *iter) { mutex_lock(&iter->ht->mutex); - list_del(&iter->walker->list); + if (iter->walker->tbl) + list_del(&iter->walker->list); mutex_unlock(&iter->ht->mutex); kfree(iter->walker); } @@ -767,12 +768,19 @@ EXPORT_SYMBOL_GPL(rhashtable_walk_exit); */ int rhashtable_walk_start(struct rhashtable_iter *iter) { + struct rhashtable *ht = iter->ht; + + mutex_lock(&ht->mutex); + + if (iter->walker->tbl) + list_del(&iter->walker->list); + rcu_read_lock(); - if (iter->walker->resize) { - iter->slot = 0; - iter->skip = 0; - iter->walker->resize = false; + mutex_unlock(&ht->mutex); + + if (!iter->walker->tbl) { + iter->walker->tbl = rht_dereference_rcu(ht->tbl, ht); return -EAGAIN; } @@ -794,13 +802,11 @@ EXPORT_SYMBOL_GPL(rhashtable_walk_start); */ void *rhashtable_walk_next(struct rhashtable_iter *iter) { - const struct bucket_table *tbl; + struct bucket_table *tbl = iter->walker->tbl; struct rhashtable *ht = iter->ht; struct rhash_head *p = iter->p; void *obj = NULL; - tbl = rht_dereference_rcu(ht->tbl, ht); - if (p) { p = rht_dereference_bucket_rcu(p->next, tbl, iter->slot); goto next; @@ -826,17 +832,18 @@ next: iter->skip = 0; } - iter->p = NULL; - -out: - if (iter->walker->resize) { - iter->p = NULL; + iter->walker->tbl = rht_dereference_rcu(ht->future_tbl, ht); + if (iter->walker->tbl != tbl) { iter->slot = 0; iter->skip = 0; - iter->walker->resize = false; return ERR_PTR(-EAGAIN); } + iter->walker->tbl = NULL; + iter->p = NULL; + +out: + return obj; } EXPORT_SYMBOL_GPL(rhashtable_walk_next); @@ -849,7 +856,24 @@ EXPORT_SYMBOL_GPL(rhashtable_walk_next); */ void rhashtable_walk_stop(struct rhashtable_iter *iter) { + struct rhashtable *ht; + struct bucket_table *tbl = iter->walker->tbl; + rcu_read_unlock(); + + if (!tbl) + return; + + ht = iter->ht; + + mutex_lock(&ht->mutex); + if (rht_dereference(ht->tbl, ht) == tbl || + rht_dereference(ht->future_tbl, ht) == tbl) + list_add(&iter->walker->list, &tbl->walkers); + else + iter->walker->tbl = NULL; + mutex_unlock(&ht->mutex); + iter->p = NULL; } EXPORT_SYMBOL_GPL(rhashtable_walk_stop); @@ -927,7 +951,6 @@ int rhashtable_init(struct rhashtable *ht, struct rhashtable_params *params) memset(ht, 0, sizeof(*ht)); mutex_init(&ht->mutex); memcpy(&ht->p, params, sizeof(*params)); - INIT_LIST_HEAD(&ht->walkers); if (params->locks_mul) ht->p.locks_mul = roundup_pow_of_two(params->locks_mul); -- cgit v1.2.3 From 9d901bc05153bbf33b5da2cd6266865e531f0545 Mon Sep 17 00:00:00 2001 From: Herbert Xu Date: Sat, 14 Mar 2015 13:57:23 +1100 Subject: rhashtable: Free bucket tables asynchronously after rehash There is in fact no need to wait for an RCU grace period in the rehash function, since all insertions are guaranteed to go into the new table through spin locks. This patch uses call_rcu to free the old/rehashed table at our leisure. Signed-off-by: Herbert Xu Signed-off-by: David S. Miller --- include/linux/rhashtable.h | 2 ++ lib/rhashtable.c | 9 ++++++--- 2 files changed, 8 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/include/linux/rhashtable.h b/include/linux/rhashtable.h index 4192682c1d5c..a0abddd226b3 100644 --- a/include/linux/rhashtable.h +++ b/include/linux/rhashtable.h @@ -54,6 +54,7 @@ struct rhash_head { * @locks_mask: Mask to apply before accessing locks[] * @locks: Array of spinlocks protecting individual buckets * @walkers: List of active walkers + * @rcu: RCU structure for freeing the table * @buckets: size * hash buckets */ struct bucket_table { @@ -63,6 +64,7 @@ struct bucket_table { unsigned int locks_mask; spinlock_t *locks; struct list_head walkers; + struct rcu_head rcu; struct rhash_head __rcu *buckets[] ____cacheline_aligned_in_smp; }; diff --git a/lib/rhashtable.c b/lib/rhashtable.c index e55bbc84c449..36fb0910bec2 100644 --- a/lib/rhashtable.c +++ b/lib/rhashtable.c @@ -141,6 +141,11 @@ static void bucket_table_free(const struct bucket_table *tbl) kvfree(tbl); } +static void bucket_table_free_rcu(struct rcu_head *head) +{ + bucket_table_free(container_of(head, struct bucket_table, rcu)); +} + static struct bucket_table *bucket_table_alloc(struct rhashtable *ht, size_t nbuckets) { @@ -288,9 +293,7 @@ static void rhashtable_rehash(struct rhashtable *ht, * table, and thus no references to the old table will * remain. */ - synchronize_rcu(); - - bucket_table_free(old_tbl); + call_rcu(&old_tbl->rcu, bucket_table_free_rcu); } /** -- cgit v1.2.3 From 63d512d0cffcae40505d9448abd509972465e846 Mon Sep 17 00:00:00 2001 From: Herbert Xu Date: Sat, 14 Mar 2015 13:57:24 +1100 Subject: rhashtable: Add rehash counter to bucket_table This patch adds a rehash counter to bucket_table to indicate the last bucket that has been rehashed. This serves two purposes: 1. Any bucket that has been rehashed can never gain a new object. 2. If the rehash counter reaches the size of the table, the table will forever remain empty. This patch also downsizes bucket_table->size to an unsigned int since we do not support sizes greater than 32 bits yet. Signed-off-by: Herbert Xu Signed-off-by: David S. Miller --- include/linux/rhashtable.h | 4 +++- lib/rhashtable.c | 1 + lib/test_rhashtable.c | 2 +- 3 files changed, 5 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/linux/rhashtable.h b/include/linux/rhashtable.h index a0abddd226b3..ed7562ad4ca0 100644 --- a/include/linux/rhashtable.h +++ b/include/linux/rhashtable.h @@ -49,6 +49,7 @@ struct rhash_head { /** * struct bucket_table - Table of hash buckets * @size: Number of hash buckets + * @rehash: Current bucket being rehashed * @hash_rnd: Random seed to fold into hash * @shift: Current size (1 << shift) * @locks_mask: Mask to apply before accessing locks[] @@ -58,7 +59,8 @@ struct rhash_head { * @buckets: size * hash buckets */ struct bucket_table { - size_t size; + unsigned int size; + unsigned int rehash; u32 hash_rnd; u32 shift; unsigned int locks_mask; diff --git a/lib/rhashtable.c b/lib/rhashtable.c index 36fb0910bec2..ff4ea1704546 100644 --- a/lib/rhashtable.c +++ b/lib/rhashtable.c @@ -260,6 +260,7 @@ static void rhashtable_rehash_chain(struct rhashtable *ht, unsigned old_hash) spin_lock_bh(old_bucket_lock); while (!rhashtable_rehash_one(ht, old_hash)) ; + old_tbl->rehash++; spin_unlock_bh(old_bucket_lock); } diff --git a/lib/test_rhashtable.c b/lib/test_rhashtable.c index 67c7593d1dd6..16974fd89e4e 100644 --- a/lib/test_rhashtable.c +++ b/lib/test_rhashtable.c @@ -80,7 +80,7 @@ static void test_bucket_stats(struct rhashtable *ht, bool quiet) rcu_cnt = cnt = 0; if (!quiet) - pr_info(" [%#4x/%zu]", i, tbl->size); + pr_info(" [%#4x/%u]", i, tbl->size); rht_for_each_entry_rcu(obj, pos, tbl, i, node) { cnt++; -- cgit v1.2.3 From c4db8848af6af92f90462258603be844baeab44d Mon Sep 17 00:00:00 2001 From: Herbert Xu Date: Sat, 14 Mar 2015 13:57:25 +1100 Subject: rhashtable: Move future_tbl into struct bucket_table This patch moves future_tbl to open up the possibility of having multiple rehashes on the same table. Signed-off-by: Herbert Xu Signed-off-by: David S. Miller --- include/linux/rhashtable.h | 5 +++-- lib/rhashtable.c | 27 +++++++++++---------------- 2 files changed, 14 insertions(+), 18 deletions(-) (limited to 'include') diff --git a/include/linux/rhashtable.h b/include/linux/rhashtable.h index ed7562ad4ca0..1695378b3c5b 100644 --- a/include/linux/rhashtable.h +++ b/include/linux/rhashtable.h @@ -56,6 +56,7 @@ struct rhash_head { * @locks: Array of spinlocks protecting individual buckets * @walkers: List of active walkers * @rcu: RCU structure for freeing the table + * @future_tbl: Table under construction during rehashing * @buckets: size * hash buckets */ struct bucket_table { @@ -68,6 +69,8 @@ struct bucket_table { struct list_head walkers; struct rcu_head rcu; + struct bucket_table __rcu *future_tbl; + struct rhash_head __rcu *buckets[] ____cacheline_aligned_in_smp; }; @@ -105,7 +108,6 @@ struct rhashtable_params { /** * struct rhashtable - Hash table handle * @tbl: Bucket table - * @future_tbl: Table under construction during expansion/shrinking * @nelems: Number of elements in table * @p: Configuration parameters * @run_work: Deferred worker to expand/shrink asynchronously @@ -114,7 +116,6 @@ struct rhashtable_params { */ struct rhashtable { struct bucket_table __rcu *tbl; - struct bucket_table __rcu *future_tbl; atomic_t nelems; bool being_destroyed; struct rhashtable_params p; diff --git a/lib/rhashtable.c b/lib/rhashtable.c index ff4ea1704546..9d53a46dcca9 100644 --- a/lib/rhashtable.c +++ b/lib/rhashtable.c @@ -207,8 +207,9 @@ static bool rht_shrink_below_30(const struct rhashtable *ht, static int rhashtable_rehash_one(struct rhashtable *ht, unsigned old_hash) { - struct bucket_table *new_tbl = rht_dereference(ht->future_tbl, ht); struct bucket_table *old_tbl = rht_dereference(ht->tbl, ht); + struct bucket_table *new_tbl = + rht_dereference(old_tbl->future_tbl, ht) ?: old_tbl; struct rhash_head __rcu **pprev = &old_tbl->buckets[old_hash]; int err = -ENOENT; struct rhash_head *head, *next, *entry; @@ -273,10 +274,8 @@ static void rhashtable_rehash(struct rhashtable *ht, /* Make insertions go into the new, empty table right away. Deletions * and lookups will be attempted in both tables until we synchronize. - * The synchronize_rcu() guarantees for the new table to be picked up - * so no new additions go into the old table while we relink. */ - rcu_assign_pointer(ht->future_tbl, new_tbl); + rcu_assign_pointer(old_tbl->future_tbl, new_tbl); /* Ensure the new table is visible to readers. */ smp_wmb(); @@ -400,7 +399,7 @@ static bool __rhashtable_insert(struct rhashtable *ht, struct rhash_head *obj, * also grab the bucket lock in old_tbl because until the * rehash completes ht->tbl won't be changed. */ - tbl = rht_dereference_rcu(ht->future_tbl, ht); + tbl = rht_dereference_rcu(old_tbl->future_tbl, ht) ?: old_tbl; if (tbl != old_tbl) { hash = head_hashfn(ht, tbl, obj); spin_lock_nested(bucket_lock(tbl, hash), SINGLE_DEPTH_NESTING); @@ -525,7 +524,7 @@ bool rhashtable_remove(struct rhashtable *ht, struct rhash_head *obj) * visible then that guarantees the entry to still be in * old_tbl if it exists. */ - tbl = rht_dereference_rcu(ht->future_tbl, ht); + tbl = rht_dereference_rcu(old_tbl->future_tbl, ht) ?: old_tbl; if (!ret && old_tbl != tbl) ret = __rhashtable_remove(ht, tbl, obj); @@ -599,7 +598,7 @@ EXPORT_SYMBOL_GPL(rhashtable_lookup); void *rhashtable_lookup_compare(struct rhashtable *ht, const void *key, bool (*compare)(void *, void *), void *arg) { - const struct bucket_table *tbl, *old_tbl; + const struct bucket_table *tbl; struct rhash_head *he; u32 hash; @@ -618,9 +617,8 @@ restart: /* Ensure we see any new tables. */ smp_rmb(); - old_tbl = tbl; - tbl = rht_dereference_rcu(ht->future_tbl, ht); - if (unlikely(tbl != old_tbl)) + tbl = rht_dereference_rcu(tbl->future_tbl, ht); + if (unlikely(tbl)) goto restart; rcu_read_unlock(); @@ -830,14 +828,13 @@ next: iter->skip = 0; } - iter->walker->tbl = rht_dereference_rcu(ht->future_tbl, ht); - if (iter->walker->tbl != tbl) { + iter->walker->tbl = rht_dereference_rcu(tbl->future_tbl, ht); + if (iter->walker->tbl) { iter->slot = 0; iter->skip = 0; return ERR_PTR(-EAGAIN); } - iter->walker->tbl = NULL; iter->p = NULL; out: @@ -865,8 +862,7 @@ void rhashtable_walk_stop(struct rhashtable_iter *iter) ht = iter->ht; mutex_lock(&ht->mutex); - if (rht_dereference(ht->tbl, ht) == tbl || - rht_dereference(ht->future_tbl, ht) == tbl) + if (tbl->rehash < tbl->size) list_add(&iter->walker->list, &tbl->walkers); else iter->walker->tbl = NULL; @@ -961,7 +957,6 @@ int rhashtable_init(struct rhashtable *ht, struct rhashtable_params *params) atomic_set(&ht->nelems, 0); RCU_INIT_POINTER(ht->tbl, tbl); - RCU_INIT_POINTER(ht->future_tbl, tbl); INIT_WORK(&ht->run_work, rht_deferred_worker); -- cgit v1.2.3 From 6befc6445ffc6868ee6e6d0e012fc149e88d96db Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Sat, 14 Mar 2015 19:27:53 -0700 Subject: Bluetooth: Add flags field and setting function for HCI sockets To filter out certain actions for certain HCI sockets introcuce a flags field that allows to configure specific settings on individual sockets. Since the hci_pinfo structure is private in hci_sock.c, provide helper functions for setting and clearing a given flag. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- include/net/bluetooth/bluetooth.h | 3 +++ net/bluetooth/hci_sock.c | 11 +++++++++++ 2 files changed, 14 insertions(+) (limited to 'include') diff --git a/include/net/bluetooth/bluetooth.h b/include/net/bluetooth/bluetooth.h index e598ca096ec9..ae1f2ee1eaf3 100644 --- a/include/net/bluetooth/bluetooth.h +++ b/include/net/bluetooth/bluetooth.h @@ -335,6 +335,9 @@ out: int bt_to_errno(__u16 code); +void hci_sock_set_flag(struct sock *sk, int nr); +void hci_sock_clear_flag(struct sock *sk, int nr); + int hci_sock_init(void); void hci_sock_cleanup(void); diff --git a/net/bluetooth/hci_sock.c b/net/bluetooth/hci_sock.c index b614543b4fe3..bf5365c49c9c 100644 --- a/net/bluetooth/hci_sock.c +++ b/net/bluetooth/hci_sock.c @@ -47,8 +47,19 @@ struct hci_pinfo { struct hci_filter filter; __u32 cmsg_mask; unsigned short channel; + unsigned long flags; }; +void hci_sock_set_flag(struct sock *sk, int nr) +{ + set_bit(nr, &hci_pi(sk)->flags); +} + +void hci_sock_clear_flag(struct sock *sk, int nr) +{ + clear_bit(nr, &hci_pi(sk)->flags); +} + static inline int hci_test_bit(int nr, const void *addr) { return *((const __u32 *) addr + (nr >> 5)) & ((__u32) 1 << (nr & 31)); -- cgit v1.2.3 From 17711c62915dd62ab83a5a83a64c0d6105d13b6c Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Sat, 14 Mar 2015 19:27:54 -0700 Subject: Bluetooth: Provide hci_send_to_flagged_channel helper function The hci_send_to_flagged_channel helper function can be used to send packets to all channels that have a certain HCI socket flag set. This is especially useful for managment events that are limited to sockets that have first enabled certain functionality. This allows for filtering of events without confusing existing users. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- include/net/bluetooth/hci_core.h | 2 ++ net/bluetooth/hci_sock.c | 33 +++++++++++++++++++++++++++++++++ 2 files changed, 35 insertions(+) (limited to 'include') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 6afbf5b014a1..d38f6e426e84 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -1285,6 +1285,8 @@ void *hci_sent_cmd_data(struct hci_dev *hdev, __u16 opcode); void hci_send_to_sock(struct hci_dev *hdev, struct sk_buff *skb); void hci_send_to_channel(unsigned short channel, struct sk_buff *skb, struct sock *skip_sk); +void hci_send_to_flagged_channel(unsigned short channel, struct sk_buff *skb, + int flag); void hci_send_to_monitor(struct hci_dev *hdev, struct sk_buff *skb); void hci_sock_dev_event(struct hci_dev *hdev, int event); diff --git a/net/bluetooth/hci_sock.c b/net/bluetooth/hci_sock.c index bf5365c49c9c..174a353a7dcf 100644 --- a/net/bluetooth/hci_sock.c +++ b/net/bluetooth/hci_sock.c @@ -231,6 +231,39 @@ void hci_send_to_channel(unsigned short channel, struct sk_buff *skb, read_unlock(&hci_sk_list.lock); } +/* Send frame to sockets with specific channel flag set */ +void hci_send_to_flagged_channel(unsigned short channel, struct sk_buff *skb, + int flag) +{ + struct sock *sk; + + BT_DBG("channel %u len %d", channel, skb->len); + + read_lock(&hci_sk_list.lock); + + sk_for_each(sk, &hci_sk_list.head) { + struct sk_buff *nskb; + + if (!test_bit(flag, &hci_pi(sk)->flags)) + continue; + + if (sk->sk_state != BT_BOUND) + continue; + + if (hci_pi(sk)->channel != channel) + continue; + + nskb = skb_clone(skb, GFP_ATOMIC); + if (!nskb) + continue; + + if (sock_queue_rcv_skb(sk, nskb)) + kfree_skb(nskb); + } + + read_unlock(&hci_sk_list.lock); +} + /* Send frame to monitor socket */ void hci_send_to_monitor(struct hci_dev *hdev, struct sk_buff *skb) { -- cgit v1.2.3 From f920733885546af2fd8d4b3dd5f8a1ac029f6248 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Sat, 14 Mar 2015 19:27:55 -0700 Subject: Bluetooth: Use special function to send filter management index events For sending Index Added, Index Removed, Unconfigured Index Added and Unconfigured Index Removed managment events the new helper functions allows taking into account if these events are enabled for a certain management socket or not. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- include/net/bluetooth/hci.h | 6 +++++ net/bluetooth/hci_sock.c | 10 +++++++ net/bluetooth/mgmt.c | 64 +++++++++++++++++++++++++++++++++++---------- 3 files changed, 66 insertions(+), 14 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index d942fedbaedd..0995ec755959 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -179,6 +179,12 @@ enum { HCI_RESET, }; +/* HCI socket flags */ +enum { + HCI_MGMT_INDEX_EVENTS, + HCI_MGMT_UNCONF_INDEX_EVENTS, +}; + /* * BR/EDR and/or LE controller flags: the flags defined here should represent * states from the controller. diff --git a/net/bluetooth/hci_sock.c b/net/bluetooth/hci_sock.c index 174a353a7dcf..00775c4fef83 100644 --- a/net/bluetooth/hci_sock.c +++ b/net/bluetooth/hci_sock.c @@ -817,6 +817,16 @@ static int hci_sock_bind(struct socket *sock, struct sockaddr *addr, goto done; } + /* At the moment the index and unconfigured index events + * are enabled unconditionally. Setting them on each + * socket when binding keeps this functionality. They + * however might be cleared later and then sending of these + * events will be disabled, but that is then intentional. + */ + if (haddr.hci_channel == HCI_CHANNEL_CONTROL) { + hci_sock_set_flag(sk, HCI_MGMT_INDEX_EVENTS); + hci_sock_set_flag(sk, HCI_MGMT_UNCONF_INDEX_EVENTS); + } break; } diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index c58908652519..6b58c13b2b51 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -250,6 +250,33 @@ static int mgmt_send_event(u16 event, struct hci_dev *hdev, return 0; } +static int mgmt_index_event(u16 event, struct hci_dev *hdev, + void *data, u16 data_len, int flag) +{ + struct sk_buff *skb; + struct mgmt_hdr *hdr; + + skb = alloc_skb(sizeof(*hdr) + data_len, GFP_KERNEL); + if (!skb) + return -ENOMEM; + + hdr = (void *) skb_put(skb, sizeof(*hdr)); + hdr->opcode = cpu_to_le16(event); + hdr->index = cpu_to_le16(hdev->id); + hdr->len = cpu_to_le16(data_len); + + if (data) + memcpy(skb_put(skb, data_len), data, data_len); + + /* Time stamp */ + __net_timestamp(skb); + + hci_send_to_flagged_channel(HCI_CHANNEL_CONTROL, skb, flag); + kfree_skb(skb); + + return 0; +} + static int mgmt_event(u16 event, struct hci_dev *hdev, void *data, u16 len, struct sock *skip_sk) { @@ -6343,34 +6370,43 @@ done: void mgmt_index_added(struct hci_dev *hdev) { - if (hdev->dev_type != HCI_BREDR) - return; if (test_bit(HCI_QUIRK_RAW_DEVICE, &hdev->quirks)) return; - if (hci_dev_test_flag(hdev, HCI_UNCONFIGURED)) - mgmt_event(MGMT_EV_UNCONF_INDEX_ADDED, hdev, NULL, 0, NULL); - else - mgmt_event(MGMT_EV_INDEX_ADDED, hdev, NULL, 0, NULL); + switch (hdev->dev_type) { + case HCI_BREDR: + if (hci_dev_test_flag(hdev, HCI_UNCONFIGURED)) { + mgmt_index_event(MGMT_EV_UNCONF_INDEX_ADDED, hdev, + NULL, 0, HCI_MGMT_UNCONF_INDEX_EVENTS); + } else { + mgmt_index_event(MGMT_EV_INDEX_ADDED, hdev, NULL, 0, + HCI_MGMT_INDEX_EVENTS); + } + break; + } } void mgmt_index_removed(struct hci_dev *hdev) { u8 status = MGMT_STATUS_INVALID_INDEX; - if (hdev->dev_type != HCI_BREDR) - return; - if (test_bit(HCI_QUIRK_RAW_DEVICE, &hdev->quirks)) return; - mgmt_pending_foreach(0, hdev, cmd_complete_rsp, &status); + switch (hdev->dev_type) { + case HCI_BREDR: + mgmt_pending_foreach(0, hdev, cmd_complete_rsp, &status); - if (hci_dev_test_flag(hdev, HCI_UNCONFIGURED)) - mgmt_event(MGMT_EV_UNCONF_INDEX_REMOVED, hdev, NULL, 0, NULL); - else - mgmt_event(MGMT_EV_INDEX_REMOVED, hdev, NULL, 0, NULL); + if (hci_dev_test_flag(hdev, HCI_UNCONFIGURED)) { + mgmt_index_event(MGMT_EV_UNCONF_INDEX_REMOVED, hdev, + NULL, 0, HCI_MGMT_UNCONF_INDEX_EVENTS); + } else { + mgmt_index_event(MGMT_EV_INDEX_REMOVED, hdev, NULL, 0, + HCI_MGMT_INDEX_EVENTS); + } + break; + } } /* This function requires the caller holds hdev->lock */ -- cgit v1.2.3 From ced85549c3a769dfb9d084bb8d6d9ca8075f8728 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Sat, 14 Mar 2015 19:27:56 -0700 Subject: Bluetooth: Add support for extended index management events This introduces support for using Extended Index Added and Extended Index Removed events. These events contain the controller type and also the hardware bus information from the driver. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- include/net/bluetooth/hci.h | 1 + include/net/bluetooth/mgmt.h | 9 +++++++++ net/bluetooth/mgmt.c | 28 ++++++++++++++++++++++++++++ 3 files changed, 38 insertions(+) (limited to 'include') diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index 0995ec755959..b8318711135a 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -183,6 +183,7 @@ enum { enum { HCI_MGMT_INDEX_EVENTS, HCI_MGMT_UNCONF_INDEX_EVENTS, + HCI_MGMT_EXT_INDEX_EVENTS, }; /* diff --git a/include/net/bluetooth/mgmt.h b/include/net/bluetooth/mgmt.h index 5bf6af9cee78..8562f9ecf230 100644 --- a/include/net/bluetooth/mgmt.h +++ b/include/net/bluetooth/mgmt.h @@ -692,3 +692,12 @@ struct mgmt_ev_new_conn_param { #define MGMT_EV_UNCONF_INDEX_REMOVED 0x001e #define MGMT_EV_NEW_CONFIG_OPTIONS 0x001f + +struct mgmt_ev_ext_index { + __u8 type; + __u8 bus; +} __packed; + +#define MGMT_EV_EXT_INDEX_ADDED 0x0020 + +#define MGMT_EV_EXT_INDEX_REMOVED 0x0021 diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 6b58c13b2b51..56f49e9c4189 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -128,6 +128,8 @@ static const u16 mgmt_events[] = { MGMT_EV_UNCONF_INDEX_ADDED, MGMT_EV_UNCONF_INDEX_REMOVED, MGMT_EV_NEW_CONFIG_OPTIONS, + MGMT_EV_EXT_INDEX_ADDED, + MGMT_EV_EXT_INDEX_REMOVED, }; #define CACHE_TIMEOUT msecs_to_jiffies(2 * 1000) @@ -6370,6 +6372,7 @@ done: void mgmt_index_added(struct hci_dev *hdev) { + struct mgmt_ev_ext_index ev; if (test_bit(HCI_QUIRK_RAW_DEVICE, &hdev->quirks)) return; @@ -6379,16 +6382,29 @@ void mgmt_index_added(struct hci_dev *hdev) if (hci_dev_test_flag(hdev, HCI_UNCONFIGURED)) { mgmt_index_event(MGMT_EV_UNCONF_INDEX_ADDED, hdev, NULL, 0, HCI_MGMT_UNCONF_INDEX_EVENTS); + ev.type = 0x01; } else { mgmt_index_event(MGMT_EV_INDEX_ADDED, hdev, NULL, 0, HCI_MGMT_INDEX_EVENTS); + ev.type = 0x00; } break; + case HCI_AMP: + ev.type = 0x02; + break; + default: + return; } + + ev.bus = hdev->bus; + + mgmt_index_event(MGMT_EV_EXT_INDEX_ADDED, hdev, &ev, sizeof(ev), + HCI_MGMT_EXT_INDEX_EVENTS); } void mgmt_index_removed(struct hci_dev *hdev) { + struct mgmt_ev_ext_index ev; u8 status = MGMT_STATUS_INVALID_INDEX; if (test_bit(HCI_QUIRK_RAW_DEVICE, &hdev->quirks)) @@ -6401,12 +6417,24 @@ void mgmt_index_removed(struct hci_dev *hdev) if (hci_dev_test_flag(hdev, HCI_UNCONFIGURED)) { mgmt_index_event(MGMT_EV_UNCONF_INDEX_REMOVED, hdev, NULL, 0, HCI_MGMT_UNCONF_INDEX_EVENTS); + ev.type = 0x01; } else { mgmt_index_event(MGMT_EV_INDEX_REMOVED, hdev, NULL, 0, HCI_MGMT_INDEX_EVENTS); + ev.type = 0x00; } break; + case HCI_AMP: + ev.type = 0x02; + break; + default: + return; } + + ev.bus = hdev->bus; + + mgmt_index_event(MGMT_EV_EXT_INDEX_REMOVED, hdev, &ev, sizeof(ev), + HCI_MGMT_EXT_INDEX_EVENTS); } /* This function requires the caller holds hdev->lock */ -- cgit v1.2.3 From 96f1474af040a4ec267efe141cbf264891e67e5a Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Sat, 14 Mar 2015 19:27:57 -0700 Subject: Bluetooth: Add support for extended index management command The Read Extended Contoller Index List command can be used for retrieving the complete list of local available controllers. This included configured, unconfigured and also AMP controllers. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- include/net/bluetooth/mgmt.h | 11 ++++++ net/bluetooth/mgmt.c | 80 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 91 insertions(+) (limited to 'include') diff --git a/include/net/bluetooth/mgmt.h b/include/net/bluetooth/mgmt.h index 8562f9ecf230..2534bd4d22b2 100644 --- a/include/net/bluetooth/mgmt.h +++ b/include/net/bluetooth/mgmt.h @@ -505,6 +505,17 @@ struct mgmt_cp_start_service_discovery { } __packed; #define MGMT_START_SERVICE_DISCOVERY_SIZE 4 +#define MGMT_OP_READ_EXT_INDEX_LIST 0x003C +#define MGMT_READ_EXT_INDEX_LIST_SIZE 0 +struct mgmt_rp_read_ext_index_list { + __le16 num_controllers; + struct { + __le16 index; + __u8 type; + __u8 bus; + } entry[0]; +} __packed; + #define MGMT_EV_CMD_COMPLETE 0x0001 struct mgmt_ev_cmd_complete { __le16 opcode; diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 56f49e9c4189..ff636bd9523b 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -96,6 +96,7 @@ static const u16 mgmt_commands[] = { MGMT_OP_SET_EXTERNAL_CONFIG, MGMT_OP_SET_PUBLIC_ADDRESS, MGMT_OP_START_SERVICE_DISCOVERY, + MGMT_OP_READ_EXT_INDEX_LIST, }; static const u16 mgmt_events[] = { @@ -518,6 +519,82 @@ static int read_unconf_index_list(struct sock *sk, struct hci_dev *hdev, return err; } +static int read_ext_index_list(struct sock *sk, struct hci_dev *hdev, + void *data, u16 data_len) +{ + struct mgmt_rp_read_ext_index_list *rp; + struct hci_dev *d; + size_t rp_len; + u16 count; + int err; + + BT_DBG("sock %p", sk); + + read_lock(&hci_dev_list_lock); + + count = 0; + list_for_each_entry(d, &hci_dev_list, list) { + if (d->dev_type == HCI_BREDR || d->dev_type == HCI_AMP) + count++; + } + + rp_len = sizeof(*rp) + (sizeof(rp->entry[0]) * count); + rp = kmalloc(rp_len, GFP_ATOMIC); + if (!rp) { + read_unlock(&hci_dev_list_lock); + return -ENOMEM; + } + + count = 0; + list_for_each_entry(d, &hci_dev_list, list) { + if (hci_dev_test_flag(d, HCI_SETUP) || + hci_dev_test_flag(d, HCI_CONFIG) || + hci_dev_test_flag(d, HCI_USER_CHANNEL)) + continue; + + /* Devices marked as raw-only are neither configured + * nor unconfigured controllers. + */ + if (test_bit(HCI_QUIRK_RAW_DEVICE, &d->quirks)) + continue; + + if (d->dev_type == HCI_BREDR) { + if (hci_dev_test_flag(d, HCI_UNCONFIGURED)) + rp->entry[count].type = 0x01; + else + rp->entry[count].type = 0x00; + } else if (d->dev_type == HCI_AMP) { + rp->entry[count].type = 0x02; + } else { + continue; + } + + rp->entry[count].bus = d->bus; + rp->entry[count++].index = cpu_to_le16(d->id); + BT_DBG("Added hci%u", d->id); + } + + rp->num_controllers = cpu_to_le16(count); + rp_len = sizeof(*rp) + (sizeof(rp->entry[0]) * count); + + read_unlock(&hci_dev_list_lock); + + /* If this command is called at least once, then all the + * default index and unconfigured index events are disabled + * and from now on only extended index events are used. + */ + hci_sock_set_flag(sk, HCI_MGMT_EXT_INDEX_EVENTS); + hci_sock_clear_flag(sk, HCI_MGMT_INDEX_EVENTS); + hci_sock_clear_flag(sk, HCI_MGMT_UNCONF_INDEX_EVENTS); + + err = mgmt_cmd_complete(sk, MGMT_INDEX_NONE, + MGMT_OP_READ_EXT_INDEX_LIST, 0, rp, rp_len); + + kfree(rp); + + return err; +} + static bool is_configured(struct hci_dev *hdev) { if (test_bit(HCI_QUIRK_EXTERNAL_CONFIG, &hdev->quirks) && @@ -6264,6 +6341,9 @@ static const struct hci_mgmt_handler mgmt_handlers[] = { HCI_MGMT_UNCONFIGURED }, { start_service_discovery, MGMT_START_SERVICE_DISCOVERY_SIZE, HCI_MGMT_VAR_LEN }, + { NULL }, + { read_ext_index_list, MGMT_READ_EXT_INDEX_LIST_SIZE, + HCI_MGMT_NO_HDEV }, }; int mgmt_control(struct hci_mgmt_chan *chan, struct sock *sk, -- cgit v1.2.3 From 50ebc055fa758c731e6e1ce174608327aab07aec Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Sat, 14 Mar 2015 19:27:58 -0700 Subject: Bluetooth: Introduce trusted flag for management control sockets Providing a global trusted flag for management control sockets provides an easy way for identifying sockets and imposing restriction on it. For now all management sockets are trusted since they require CAP_NET_ADMIN. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- include/net/bluetooth/hci.h | 1 + net/bluetooth/hci_sock.c | 11 +++++++++++ 2 files changed, 12 insertions(+) (limited to 'include') diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index b8318711135a..7a24acaafeea 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -181,6 +181,7 @@ enum { /* HCI socket flags */ enum { + HCI_SOCK_TRUSTED, HCI_MGMT_INDEX_EVENTS, HCI_MGMT_UNCONF_INDEX_EVENTS, HCI_MGMT_EXT_INDEX_EVENTS, diff --git a/net/bluetooth/hci_sock.c b/net/bluetooth/hci_sock.c index 00775c4fef83..54118868b3f6 100644 --- a/net/bluetooth/hci_sock.c +++ b/net/bluetooth/hci_sock.c @@ -796,6 +796,11 @@ static int hci_sock_bind(struct socket *sock, struct sockaddr *addr, goto done; } + /* The monitor interface is restricted to CAP_NET_RAW + * capabilities and with that implicitly trusted. + */ + hci_sock_set_flag(sk, HCI_SOCK_TRUSTED); + send_monitor_replay(sk); atomic_inc(&monitor_promisc); @@ -817,6 +822,12 @@ static int hci_sock_bind(struct socket *sock, struct sockaddr *addr, goto done; } + /* Since the access to control channels is currently + * restricted to CAP_NET_ADMIN capabilities, every + * socket is implicitly trusted. + */ + hci_sock_set_flag(sk, HCI_SOCK_TRUSTED); + /* At the moment the index and unconfigured index events * are enabled unconditionally. Setting them on each * socket when binding keeps this functionality. They -- cgit v1.2.3 From c08b1a1dba524c1cdef331c1f169db3a1b37bb4c Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Sat, 14 Mar 2015 19:27:59 -0700 Subject: Bluetooth: Consolidate socket channel sending function back into one With the introduction of trusted socket flag for control and monitor channels, it is now possible to use a single function for sending packets to these sockets. And with that consolidate the handling. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- include/net/bluetooth/hci_core.h | 4 +--- net/bluetooth/hci_sock.c | 45 ++++++++-------------------------------- net/bluetooth/mgmt.c | 34 +++++++----------------------- 3 files changed, 17 insertions(+), 66 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index d38f6e426e84..859005c9a8fc 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -1284,9 +1284,7 @@ void *hci_sent_cmd_data(struct hci_dev *hdev, __u16 opcode); /* ----- HCI Sockets ----- */ void hci_send_to_sock(struct hci_dev *hdev, struct sk_buff *skb); void hci_send_to_channel(unsigned short channel, struct sk_buff *skb, - struct sock *skip_sk); -void hci_send_to_flagged_channel(unsigned short channel, struct sk_buff *skb, - int flag); + int flag, struct sock *skip_sk); void hci_send_to_monitor(struct hci_dev *hdev, struct sk_buff *skb); void hci_sock_dev_event(struct hci_dev *hdev, int event); diff --git a/net/bluetooth/hci_sock.c b/net/bluetooth/hci_sock.c index 54118868b3f6..e7f463f6fd69 100644 --- a/net/bluetooth/hci_sock.c +++ b/net/bluetooth/hci_sock.c @@ -199,7 +199,7 @@ void hci_send_to_sock(struct hci_dev *hdev, struct sk_buff *skb) /* Send frame to sockets with specific channel */ void hci_send_to_channel(unsigned short channel, struct sk_buff *skb, - struct sock *skip_sk) + int flag, struct sock *skip_sk) { struct sock *sk; @@ -210,41 +210,12 @@ void hci_send_to_channel(unsigned short channel, struct sk_buff *skb, sk_for_each(sk, &hci_sk_list.head) { struct sk_buff *nskb; - /* Skip the original socket */ - if (sk == skip_sk) - continue; - - if (sk->sk_state != BT_BOUND) - continue; - - if (hci_pi(sk)->channel != channel) - continue; - - nskb = skb_clone(skb, GFP_ATOMIC); - if (!nskb) + /* Ignore socket without the flag set */ + if (!test_bit(flag, &hci_pi(sk)->flags)) continue; - if (sock_queue_rcv_skb(sk, nskb)) - kfree_skb(nskb); - } - - read_unlock(&hci_sk_list.lock); -} - -/* Send frame to sockets with specific channel flag set */ -void hci_send_to_flagged_channel(unsigned short channel, struct sk_buff *skb, - int flag) -{ - struct sock *sk; - - BT_DBG("channel %u len %d", channel, skb->len); - - read_lock(&hci_sk_list.lock); - - sk_for_each(sk, &hci_sk_list.head) { - struct sk_buff *nskb; - - if (!test_bit(flag, &hci_pi(sk)->flags)) + /* Skip the original socket */ + if (sk == skip_sk) continue; if (sk->sk_state != BT_BOUND) @@ -310,7 +281,8 @@ void hci_send_to_monitor(struct hci_dev *hdev, struct sk_buff *skb) hdr->index = cpu_to_le16(hdev->id); hdr->len = cpu_to_le16(skb->len); - hci_send_to_channel(HCI_CHANNEL_MONITOR, skb_copy, NULL); + hci_send_to_channel(HCI_CHANNEL_MONITOR, skb_copy, + HCI_SOCK_TRUSTED, NULL); kfree_skb(skb_copy); } @@ -417,7 +389,8 @@ void hci_sock_dev_event(struct hci_dev *hdev, int event) skb = create_monitor_event(hdev, event); if (skb) { - hci_send_to_channel(HCI_CHANNEL_MONITOR, skb, NULL); + hci_send_to_channel(HCI_CHANNEL_MONITOR, skb, + HCI_SOCK_TRUSTED, NULL); kfree_skb(skb); } } diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index ff636bd9523b..1e5afa76e371 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -224,7 +224,7 @@ static u8 mgmt_status(u8 hci_status) static int mgmt_send_event(u16 event, struct hci_dev *hdev, unsigned short channel, void *data, u16 data_len, - struct sock *skip_sk) + int flag, struct sock *skip_sk) { struct sk_buff *skb; struct mgmt_hdr *hdr; @@ -247,44 +247,24 @@ static int mgmt_send_event(u16 event, struct hci_dev *hdev, /* Time stamp */ __net_timestamp(skb); - hci_send_to_channel(channel, skb, skip_sk); + hci_send_to_channel(channel, skb, flag, skip_sk); kfree_skb(skb); return 0; } -static int mgmt_index_event(u16 event, struct hci_dev *hdev, - void *data, u16 data_len, int flag) +static int mgmt_index_event(u16 event, struct hci_dev *hdev, void *data, + u16 len, int flag) { - struct sk_buff *skb; - struct mgmt_hdr *hdr; - - skb = alloc_skb(sizeof(*hdr) + data_len, GFP_KERNEL); - if (!skb) - return -ENOMEM; - - hdr = (void *) skb_put(skb, sizeof(*hdr)); - hdr->opcode = cpu_to_le16(event); - hdr->index = cpu_to_le16(hdev->id); - hdr->len = cpu_to_le16(data_len); - - if (data) - memcpy(skb_put(skb, data_len), data, data_len); - - /* Time stamp */ - __net_timestamp(skb); - - hci_send_to_flagged_channel(HCI_CHANNEL_CONTROL, skb, flag); - kfree_skb(skb); - - return 0; + return mgmt_send_event(event, hdev, HCI_CHANNEL_CONTROL, data, len, + flag, NULL); } static int mgmt_event(u16 event, struct hci_dev *hdev, void *data, u16 len, struct sock *skip_sk) { return mgmt_send_event(event, hdev, HCI_CHANNEL_CONTROL, data, len, - skip_sk); + HCI_SOCK_TRUSTED, skip_sk); } static int mgmt_cmd_status(struct sock *sk, u16 index, u16 cmd, u8 status) -- cgit v1.2.3 From c85be545ea23a4fe590c89683242a9be823394e0 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Sat, 14 Mar 2015 19:28:00 -0700 Subject: Bluetooth: Add hci_sock_test_flag helper function The management interface will need access to the socket flags and so provide a helper function for checking them. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- include/net/bluetooth/bluetooth.h | 1 + net/bluetooth/hci_sock.c | 7 ++++++- 2 files changed, 7 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/net/bluetooth/bluetooth.h b/include/net/bluetooth/bluetooth.h index ae1f2ee1eaf3..d871ba313f64 100644 --- a/include/net/bluetooth/bluetooth.h +++ b/include/net/bluetooth/bluetooth.h @@ -337,6 +337,7 @@ int bt_to_errno(__u16 code); void hci_sock_set_flag(struct sock *sk, int nr); void hci_sock_clear_flag(struct sock *sk, int nr); +int hci_sock_test_flag(struct sock *sk, int nr); int hci_sock_init(void); void hci_sock_cleanup(void); diff --git a/net/bluetooth/hci_sock.c b/net/bluetooth/hci_sock.c index e7f463f6fd69..df23c184c897 100644 --- a/net/bluetooth/hci_sock.c +++ b/net/bluetooth/hci_sock.c @@ -60,6 +60,11 @@ void hci_sock_clear_flag(struct sock *sk, int nr) clear_bit(nr, &hci_pi(sk)->flags); } +int hci_sock_test_flag(struct sock *sk, int nr) +{ + return test_bit(nr, &hci_pi(sk)->flags); +} + static inline int hci_test_bit(int nr, const void *addr) { return *((const __u32 *) addr + (nr >> 5)) & ((__u32) 1 << (nr & 31)); @@ -211,7 +216,7 @@ void hci_send_to_channel(unsigned short channel, struct sk_buff *skb, struct sk_buff *nskb; /* Ignore socket without the flag set */ - if (!test_bit(flag, &hci_pi(sk)->flags)) + if (!hci_sock_test_flag(sk, flag)) continue; /* Skip the original socket */ -- cgit v1.2.3 From c91041dc4efff71f29f1dd4c9a4a5e80b841395c Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Sat, 14 Mar 2015 19:28:01 -0700 Subject: Bluetooth: Add support for untrusted access to management commands Some management commands are safe to be accessed from any user without special permissions. First step for allowing access to any of these commands from untrusted application is to mark them accordingly. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- include/net/bluetooth/hci_core.h | 3 ++- net/bluetooth/mgmt.c | 21 ++++++++++++++------- 2 files changed, 16 insertions(+), 8 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 859005c9a8fc..3546789c1616 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -1291,7 +1291,8 @@ void hci_sock_dev_event(struct hci_dev *hdev, int event); #define HCI_MGMT_VAR_LEN (1 << 0) #define HCI_MGMT_NO_HDEV (1 << 1) -#define HCI_MGMT_UNCONFIGURED (1 << 2) +#define HCI_MGMT_UNTRUSTED (1 << 2) +#define HCI_MGMT_UNCONFIGURED (1 << 3) struct hci_mgmt_handler { int (*func) (struct sock *sk, struct hci_dev *hdev, void *data, diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 1e5afa76e371..09f31f9642b7 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -6251,12 +6251,16 @@ unlock: static const struct hci_mgmt_handler mgmt_handlers[] = { { NULL }, /* 0x0000 (no command) */ { read_version, MGMT_READ_VERSION_SIZE, - HCI_MGMT_NO_HDEV }, + HCI_MGMT_NO_HDEV | + HCI_MGMT_UNTRUSTED }, { read_commands, MGMT_READ_COMMANDS_SIZE, - HCI_MGMT_NO_HDEV }, + HCI_MGMT_NO_HDEV | + HCI_MGMT_UNTRUSTED }, { read_index_list, MGMT_READ_INDEX_LIST_SIZE, - HCI_MGMT_NO_HDEV }, - { read_controller_info, MGMT_READ_INFO_SIZE, 0 }, + HCI_MGMT_NO_HDEV | + HCI_MGMT_UNTRUSTED }, + { read_controller_info, MGMT_READ_INFO_SIZE, + HCI_MGMT_UNTRUSTED }, { set_powered, MGMT_SETTING_SIZE, 0 }, { set_discoverable, MGMT_SET_DISCOVERABLE_SIZE, 0 }, { set_connectable, MGMT_SETTING_SIZE, 0 }, @@ -6312,9 +6316,11 @@ static const struct hci_mgmt_handler mgmt_handlers[] = { { load_conn_param, MGMT_LOAD_CONN_PARAM_SIZE, HCI_MGMT_VAR_LEN }, { read_unconf_index_list, MGMT_READ_UNCONF_INDEX_LIST_SIZE, - HCI_MGMT_NO_HDEV }, + HCI_MGMT_NO_HDEV | + HCI_MGMT_UNTRUSTED }, { read_config_info, MGMT_READ_CONFIG_INFO_SIZE, - HCI_MGMT_UNCONFIGURED }, + HCI_MGMT_UNCONFIGURED | + HCI_MGMT_UNTRUSTED }, { set_external_config, MGMT_SET_EXTERNAL_CONFIG_SIZE, HCI_MGMT_UNCONFIGURED }, { set_public_address, MGMT_SET_PUBLIC_ADDRESS_SIZE, @@ -6323,7 +6329,8 @@ static const struct hci_mgmt_handler mgmt_handlers[] = { HCI_MGMT_VAR_LEN }, { NULL }, { read_ext_index_list, MGMT_READ_EXT_INDEX_LIST_SIZE, - HCI_MGMT_NO_HDEV }, + HCI_MGMT_NO_HDEV | + HCI_MGMT_UNTRUSTED }, }; int mgmt_control(struct hci_mgmt_chan *chan, struct sock *sk, -- cgit v1.2.3 From c927a10487477eeed0441e5c88147700e69e5db9 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Sat, 14 Mar 2015 19:28:03 -0700 Subject: Bluetooth: Add support for trust verification of management commands Check the required trust level of each management command with the trust level of the management socket. If it does not match up, then return the newly introduced permission denied error. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- include/net/bluetooth/mgmt.h | 1 + net/bluetooth/mgmt.c | 7 +++++++ 2 files changed, 8 insertions(+) (limited to 'include') diff --git a/include/net/bluetooth/mgmt.h b/include/net/bluetooth/mgmt.h index 2534bd4d22b2..f3baad589db0 100644 --- a/include/net/bluetooth/mgmt.h +++ b/include/net/bluetooth/mgmt.h @@ -44,6 +44,7 @@ #define MGMT_STATUS_INVALID_INDEX 0x11 #define MGMT_STATUS_RFKILLED 0x12 #define MGMT_STATUS_ALREADY_PAIRED 0x13 +#define MGMT_STATUS_PERMISSION_DENIED 0x14 struct mgmt_hdr { __le16 opcode; diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 2162f7bc89be..920acf0625f6 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -6379,6 +6379,13 @@ int mgmt_control(struct hci_mgmt_chan *chan, struct sock *sk, handler = &chan->handlers[opcode]; + if (!hci_sock_test_flag(sk, HCI_SOCK_TRUSTED) && + !(handler->flags & HCI_MGMT_UNTRUSTED)) { + err = mgmt_cmd_status(sk, index, opcode, + MGMT_STATUS_PERMISSION_DENIED); + goto done; + } + if (index != MGMT_INDEX_NONE) { hdev = hci_dev_get(index); if (!hdev) { -- cgit v1.2.3 From f6b7712eb660c50877a56772908326cd31125b21 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Sat, 14 Mar 2015 19:28:05 -0700 Subject: Bluetooth: Send global configuration updates to all management users Changes to the global configuration updates like settings, class of device, name etc. can be received by every user. They are allowed to read them in the first place so provide the updates via events as well. Otherwise untrusted users start polling for updates and that is not a desired behavior. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- include/net/bluetooth/hci.h | 1 + net/bluetooth/hci_sock.c | 5 +++++ net/bluetooth/mgmt.c | 34 ++++++++++++++++++++-------------- 3 files changed, 26 insertions(+), 14 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index 7a24acaafeea..7f41c7741e76 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -185,6 +185,7 @@ enum { HCI_MGMT_INDEX_EVENTS, HCI_MGMT_UNCONF_INDEX_EVENTS, HCI_MGMT_EXT_INDEX_EVENTS, + HCI_MGMT_GENERIC_EVENTS, }; /* diff --git a/net/bluetooth/hci_sock.c b/net/bluetooth/hci_sock.c index f4b10344b1e5..7c719602dbca 100644 --- a/net/bluetooth/hci_sock.c +++ b/net/bluetooth/hci_sock.c @@ -808,10 +808,15 @@ static int hci_sock_bind(struct socket *sock, struct sockaddr *addr, * socket when binding keeps this functionality. They * however might be cleared later and then sending of these * events will be disabled, but that is then intentional. + * + * This also enables generic events that are safe to be + * received by untrusted users. Example for such events + * are changes to settings, class of device, name etc. */ if (haddr.hci_channel == HCI_CHANNEL_CONTROL) { hci_sock_set_flag(sk, HCI_MGMT_INDEX_EVENTS); hci_sock_set_flag(sk, HCI_MGMT_UNCONF_INDEX_EVENTS); + hci_sock_set_flag(sk, HCI_MGMT_GENERIC_EVENTS); } break; } diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 920acf0625f6..fa5654d89702 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -260,6 +260,13 @@ static int mgmt_index_event(u16 event, struct hci_dev *hdev, void *data, flag, NULL); } +static int mgmt_generic_event(u16 event, struct hci_dev *hdev, void *data, + u16 len, struct sock *skip_sk) +{ + return mgmt_send_event(event, hdev, HCI_CHANNEL_CONTROL, data, len, + HCI_MGMT_GENERIC_EVENTS, skip_sk); +} + static int mgmt_event(u16 event, struct hci_dev *hdev, void *data, u16 len, struct sock *skip_sk) { @@ -607,8 +614,8 @@ static int new_options(struct hci_dev *hdev, struct sock *skip) { __le32 options = get_missing_options(hdev); - return mgmt_event(MGMT_EV_NEW_CONFIG_OPTIONS, hdev, &options, - sizeof(options), skip); + return mgmt_generic_event(MGMT_EV_NEW_CONFIG_OPTIONS, hdev, &options, + sizeof(options), skip); } static int send_options_rsp(struct sock *sk, u16 opcode, struct hci_dev *hdev) @@ -1552,11 +1559,10 @@ failed: static int new_settings(struct hci_dev *hdev, struct sock *skip) { - __le32 ev; - - ev = cpu_to_le32(get_current_settings(hdev)); + __le32 ev = cpu_to_le32(get_current_settings(hdev)); - return mgmt_event(MGMT_EV_NEW_SETTINGS, hdev, &ev, sizeof(ev), skip); + return mgmt_generic_event(MGMT_EV_NEW_SETTINGS, hdev, &ev, + sizeof(ev), skip); } int mgmt_new_settings(struct hci_dev *hdev) @@ -3677,8 +3683,8 @@ static int set_local_name(struct sock *sk, struct hci_dev *hdev, void *data, if (err < 0) goto failed; - err = mgmt_event(MGMT_EV_LOCAL_NAME_CHANGED, hdev, data, len, - sk); + err = mgmt_generic_event(MGMT_EV_LOCAL_NAME_CHANGED, hdev, + data, len, sk); goto failed; } @@ -6673,8 +6679,8 @@ int mgmt_powered(struct hci_dev *hdev, u8 powered) mgmt_pending_foreach(0, hdev, cmd_complete_rsp, &status); if (memcmp(hdev->dev_class, zero_cod, sizeof(zero_cod)) != 0) - mgmt_event(MGMT_EV_CLASS_OF_DEV_CHANGED, hdev, - zero_cod, sizeof(zero_cod), NULL); + mgmt_generic_event(MGMT_EV_CLASS_OF_DEV_CHANGED, hdev, + zero_cod, sizeof(zero_cod), NULL); new_settings: err = new_settings(hdev, match.sk); @@ -7325,8 +7331,8 @@ void mgmt_set_class_of_dev_complete(struct hci_dev *hdev, u8 *dev_class, mgmt_pending_foreach(MGMT_OP_REMOVE_UUID, hdev, sk_lookup, &match); if (!status) - mgmt_event(MGMT_EV_CLASS_OF_DEV_CHANGED, hdev, dev_class, 3, - NULL); + mgmt_generic_event(MGMT_EV_CLASS_OF_DEV_CHANGED, hdev, + dev_class, 3, NULL); if (match.sk) sock_put(match.sk); @@ -7355,8 +7361,8 @@ void mgmt_set_local_name_complete(struct hci_dev *hdev, u8 *name, u8 status) return; } - mgmt_event(MGMT_EV_LOCAL_NAME_CHANGED, hdev, &ev, sizeof(ev), - cmd ? cmd->sk : NULL); + mgmt_generic_event(MGMT_EV_LOCAL_NAME_CHANGED, hdev, &ev, sizeof(ev), + cmd ? cmd->sk : NULL); } void mgmt_read_local_oob_data_complete(struct hci_dev *hdev, u8 *hash192, -- cgit v1.2.3 From a958452aa40c57a0407ecf84ba1bfa31ad532313 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Sat, 14 Mar 2015 19:28:06 -0700 Subject: Bluetooth: Use BIT(n) macro instead of manually encoding (1 << n) The flags for the management command table used manual encoding of bits in the form of (1 << n). It is however preferred to use BIT(n) macro instead. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- include/net/bluetooth/hci_core.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 3546789c1616..ce94bcb33600 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -1289,10 +1289,10 @@ void hci_send_to_monitor(struct hci_dev *hdev, struct sk_buff *skb); void hci_sock_dev_event(struct hci_dev *hdev, int event); -#define HCI_MGMT_VAR_LEN (1 << 0) -#define HCI_MGMT_NO_HDEV (1 << 1) -#define HCI_MGMT_UNTRUSTED (1 << 2) -#define HCI_MGMT_UNCONFIGURED (1 << 3) +#define HCI_MGMT_VAR_LEN BIT(0) +#define HCI_MGMT_NO_HDEV BIT(1) +#define HCI_MGMT_UNTRUSTED BIT(2) +#define HCI_MGMT_UNCONFIGURED BIT(3) struct hci_mgmt_handler { int (*func) (struct sock *sk, struct hci_dev *hdev, void *data, -- cgit v1.2.3 From d3d5305bfd1cb48c8f44207abb567276a1e09cc7 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Sat, 14 Mar 2015 20:53:25 -0700 Subject: Bluetooth: Add simple version of Read Advertising Features command This adds support for the simplest possible version of Read Advertising Features management command. It allows basic testing of the interface. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- include/net/bluetooth/mgmt.h | 11 +++++++++++ net/bluetooth/mgmt.c | 36 ++++++++++++++++++++++++++++++++++++ 2 files changed, 47 insertions(+) (limited to 'include') diff --git a/include/net/bluetooth/mgmt.h b/include/net/bluetooth/mgmt.h index f3baad589db0..4d0ccd194c01 100644 --- a/include/net/bluetooth/mgmt.h +++ b/include/net/bluetooth/mgmt.h @@ -517,6 +517,17 @@ struct mgmt_rp_read_ext_index_list { } entry[0]; } __packed; +#define MGMT_OP_READ_ADV_FEATURES 0x0003D +#define MGMT_READ_ADV_FEATURES_SIZE 0 +struct mgmt_rp_read_adv_features { + __le32 supported_flags; + __u8 max_adv_data_len; + __u8 max_scan_rsp_len; + __u8 max_instances; + __u8 num_instances; + __u8 instance[0]; +} __packed; + #define MGMT_EV_CMD_COMPLETE 0x0001 struct mgmt_ev_cmd_complete { __le16 opcode; diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index fa5654d89702..25a687c2a112 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -97,6 +97,7 @@ static const u16 mgmt_commands[] = { MGMT_OP_SET_PUBLIC_ADDRESS, MGMT_OP_START_SERVICE_DISCOVERY, MGMT_OP_READ_EXT_INDEX_LIST, + MGMT_OP_READ_ADV_FEATURES, }; static const u16 mgmt_events[] = { @@ -6254,6 +6255,40 @@ unlock: return err; } +static int read_adv_features(struct sock *sk, struct hci_dev *hdev, + void *data, u16 data_len) +{ + struct mgmt_rp_read_adv_features *rp; + size_t rp_len; + int err; + + BT_DBG("%s", hdev->name); + + hci_dev_lock(hdev); + + rp_len = sizeof(*rp); + rp = kmalloc(rp_len, GFP_ATOMIC); + if (!rp) { + hci_dev_unlock(hdev); + return -ENOMEM; + } + + rp->supported_flags = cpu_to_le32(0); + rp->max_adv_data_len = 31; + rp->max_scan_rsp_len = 31; + rp->max_instances = 0; + rp->num_instances = 0; + + hci_dev_unlock(hdev); + + err = mgmt_cmd_complete(sk, hdev->id, MGMT_OP_READ_ADV_FEATURES, + MGMT_STATUS_SUCCESS, rp, rp_len); + + kfree(rp); + + return err; +} + static const struct hci_mgmt_handler mgmt_handlers[] = { { NULL }, /* 0x0000 (no command) */ { read_version, MGMT_READ_VERSION_SIZE, @@ -6337,6 +6372,7 @@ static const struct hci_mgmt_handler mgmt_handlers[] = { { read_ext_index_list, MGMT_READ_EXT_INDEX_LIST_SIZE, HCI_MGMT_NO_HDEV | HCI_MGMT_UNTRUSTED }, + { read_adv_features, MGMT_READ_ADV_FEATURES_SIZE }, }; int mgmt_control(struct hci_mgmt_chan *chan, struct sock *sk, -- cgit v1.2.3 From 1471aae0d04d4e0df2bf1e5a5af861e28371b8b4 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Sat, 14 Mar 2015 22:43:18 -0700 Subject: Bluetooth: Add defines for LE Bluetooth Device Address and LE Role The OOB data requires to include LE Bluetooth Device Address and LE Role and so add the type constants for these fields. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- include/net/bluetooth/hci.h | 2 ++ 1 file changed, 2 insertions(+) (limited to 'include') diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index 7f41c7741e76..af9893b704ff 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -456,6 +456,8 @@ enum { #define EIR_SSP_HASH_C 0x0E /* Simple Pairing Hash C */ #define EIR_SSP_RAND_R 0x0F /* Simple Pairing Randomizer R */ #define EIR_DEVICE_ID 0x10 /* device ID */ +#define EIR_LE_BDADDR 0x1B /* LE Bluetooth device address */ +#define EIR_LE_ROLE 0x1C /* LE role */ /* Low Energy Advertising Flags */ #define LE_AD_LIMITED 0x01 /* Limited Discoverable */ -- cgit v1.2.3 From 4f0f155ceaf7e1b59d210a8afb24d4ea63ce13cc Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Sat, 14 Mar 2015 22:43:19 -0700 Subject: Bluetooth: Add simple version of Read Local OOB Extended Data command This adds support for the simplest possible version of Read Local OOB Extended Data management command. It includes all mandatory fields, but none of the actual pairing related ones. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- include/net/bluetooth/mgmt.h | 11 +++++ net/bluetooth/mgmt.c | 111 ++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 121 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/net/bluetooth/mgmt.h b/include/net/bluetooth/mgmt.h index 4d0ccd194c01..543c1ba3d892 100644 --- a/include/net/bluetooth/mgmt.h +++ b/include/net/bluetooth/mgmt.h @@ -506,6 +506,17 @@ struct mgmt_cp_start_service_discovery { } __packed; #define MGMT_START_SERVICE_DISCOVERY_SIZE 4 +#define MGMT_OP_READ_LOCAL_OOB_EXT_DATA 0x003B +struct mgmt_cp_read_local_oob_ext_data { + __u8 type; +} __packed; +#define MGMT_READ_LOCAL_OOB_EXT_DATA_SIZE 1 +struct mgmt_rp_read_local_oob_ext_data { + __u8 type; + __le16 eir_len; + __u8 eir[0]; +} __packed; + #define MGMT_OP_READ_EXT_INDEX_LIST 0x003C #define MGMT_READ_EXT_INDEX_LIST_SIZE 0 struct mgmt_rp_read_ext_index_list { diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 07c75a04829a..6cb0a304182f 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -96,6 +96,7 @@ static const u16 mgmt_commands[] = { MGMT_OP_SET_EXTERNAL_CONFIG, MGMT_OP_SET_PUBLIC_ADDRESS, MGMT_OP_START_SERVICE_DISCOVERY, + MGMT_OP_READ_LOCAL_OOB_EXT_DATA, MGMT_OP_READ_EXT_INDEX_LIST, MGMT_OP_READ_ADV_FEATURES, }; @@ -6266,6 +6267,114 @@ static inline u16 eir_append_data(u8 *eir, u16 eir_len, u8 type, u8 *data, return eir_len; } +static int read_local_oob_ext_data(struct sock *sk, struct hci_dev *hdev, + void *data, u16 data_len) +{ + struct mgmt_cp_read_local_oob_ext_data *cp = data; + struct mgmt_rp_read_local_oob_ext_data *rp; + size_t rp_len; + u16 eir_len; + u8 status, flags, role, addr[7]; + int err; + + BT_DBG("%s", hdev->name); + + if (!hdev_is_powered(hdev)) + return mgmt_cmd_complete(sk, hdev->id, + MGMT_OP_READ_LOCAL_OOB_EXT_DATA, + MGMT_STATUS_NOT_POWERED, + &cp->type, sizeof(cp->type)); + + switch (cp->type) { + case BIT(BDADDR_BREDR): + status = mgmt_bredr_support(hdev); + if (status) + return mgmt_cmd_complete(sk, hdev->id, + MGMT_OP_READ_LOCAL_OOB_EXT_DATA, + status, &cp->type, + sizeof(cp->type)); + eir_len = 5; + break; + case (BIT(BDADDR_LE_PUBLIC) | BIT(BDADDR_LE_RANDOM)): + status = mgmt_le_support(hdev); + if (status) + return mgmt_cmd_complete(sk, hdev->id, + MGMT_OP_READ_LOCAL_OOB_EXT_DATA, + status, &cp->type, + sizeof(cp->type)); + eir_len = 15; + break; + default: + return mgmt_cmd_complete(sk, hdev->id, + MGMT_OP_READ_LOCAL_OOB_EXT_DATA, + MGMT_STATUS_INVALID_PARAMS, + &cp->type, sizeof(cp->type)); + } + + hci_dev_lock(hdev); + + rp_len = sizeof(*rp) + eir_len; + rp = kmalloc(rp_len, GFP_ATOMIC); + if (!rp) { + hci_dev_unlock(hdev); + return -ENOMEM; + } + + eir_len = 0; + switch (cp->type) { + case BIT(BDADDR_BREDR): + eir_len = eir_append_data(rp->eir, eir_len, EIR_CLASS_OF_DEV, + hdev->dev_class, 3); + break; + case (BIT(BDADDR_LE_PUBLIC) | BIT(BDADDR_LE_RANDOM)): + if (hci_dev_test_flag(hdev, HCI_PRIVACY)) { + memcpy(addr, &hdev->rpa, 6); + addr[6] = 0x01; + } else if (hci_dev_test_flag(hdev, HCI_FORCE_STATIC_ADDR) || + !bacmp(&hdev->bdaddr, BDADDR_ANY) || + (!hci_dev_test_flag(hdev, HCI_BREDR_ENABLED) && + bacmp(&hdev->static_addr, BDADDR_ANY))) { + memcpy(addr, &hdev->static_addr, 6); + addr[6] = 0x01; + } else { + memcpy(addr, &hdev->bdaddr, 6); + addr[6] = 0x00; + } + + eir_len = eir_append_data(rp->eir, eir_len, EIR_LE_BDADDR, + addr, sizeof(addr)); + + if (hci_dev_test_flag(hdev, HCI_ADVERTISING)) + role = 0x02; + else + role = 0x01; + + eir_len = eir_append_data(rp->eir, eir_len, EIR_LE_ROLE, + &role, sizeof(role)); + + flags = get_adv_discov_flags(hdev); + + if (!hci_dev_test_flag(hdev, HCI_BREDR_ENABLED)) + flags |= LE_AD_NO_BREDR; + + eir_len = eir_append_data(rp->eir, eir_len, EIR_FLAGS, + &flags, sizeof(flags)); + break; + } + + rp->type = cp->type; + rp->eir_len = cpu_to_le16(eir_len); + + hci_dev_unlock(hdev); + + err = mgmt_cmd_complete(sk, hdev->id, MGMT_OP_READ_LOCAL_OOB_EXT_DATA, + MGMT_STATUS_SUCCESS, rp, rp_len); + + kfree(rp); + + return err; +} + static int read_adv_features(struct sock *sk, struct hci_dev *hdev, void *data, u16 data_len) { @@ -6379,7 +6488,7 @@ static const struct hci_mgmt_handler mgmt_handlers[] = { HCI_MGMT_UNCONFIGURED }, { start_service_discovery, MGMT_START_SERVICE_DISCOVERY_SIZE, HCI_MGMT_VAR_LEN }, - { NULL }, + { read_local_oob_ext_data, MGMT_READ_LOCAL_OOB_EXT_DATA_SIZE }, { read_ext_index_list, MGMT_READ_EXT_INDEX_LIST_SIZE, HCI_MGMT_NO_HDEV | HCI_MGMT_UNTRUSTED }, -- cgit v1.2.3 From 03e69b508b6f7c51743055c9f61d1dfeadf4b635 Mon Sep 17 00:00:00 2001 From: Daniel Borkmann Date: Sat, 14 Mar 2015 02:27:16 +0100 Subject: ebpf: add prandom helper for packet sampling This work is similar to commit 4cd3675ebf74 ("filter: added BPF random opcode") and adds a possibility for packet sampling in eBPF. Currently, this is only possible in classic BPF and useful to combine sampling with f.e. packet sockets, possible also with tc. Example function proto-type looks like: u32 (*prandom_u32)(void) = (void *)BPF_FUNC_get_prandom_u32; Signed-off-by: Daniel Borkmann Signed-off-by: David S. Miller --- include/linux/bpf.h | 2 ++ include/uapi/linux/bpf.h | 1 + kernel/bpf/core.c | 2 ++ kernel/bpf/helpers.c | 12 ++++++++++++ net/core/filter.c | 2 ++ 5 files changed, 19 insertions(+) (limited to 'include') diff --git a/include/linux/bpf.h b/include/linux/bpf.h index 80f2e0fc3d02..50bf95e29a96 100644 --- a/include/linux/bpf.h +++ b/include/linux/bpf.h @@ -154,4 +154,6 @@ extern const struct bpf_func_proto bpf_map_lookup_elem_proto; extern const struct bpf_func_proto bpf_map_update_elem_proto; extern const struct bpf_func_proto bpf_map_delete_elem_proto; +extern const struct bpf_func_proto bpf_get_prandom_u32_proto; + #endif /* _LINUX_BPF_H */ diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h index 3fa1af8a58d7..1c2ca2b477c8 100644 --- a/include/uapi/linux/bpf.h +++ b/include/uapi/linux/bpf.h @@ -165,6 +165,7 @@ enum bpf_func_id { BPF_FUNC_map_lookup_elem, /* void *map_lookup_elem(&map, &key) */ BPF_FUNC_map_update_elem, /* int map_update_elem(&map, &key, &value, flags) */ BPF_FUNC_map_delete_elem, /* int map_delete_elem(&map, &key) */ + BPF_FUNC_get_prandom_u32, /* u32 prandom_u32(void) */ __BPF_FUNC_MAX_ID, }; diff --git a/kernel/bpf/core.c b/kernel/bpf/core.c index 50603aec766a..c1dbbb5d289b 100644 --- a/kernel/bpf/core.c +++ b/kernel/bpf/core.c @@ -661,6 +661,8 @@ const struct bpf_func_proto bpf_map_lookup_elem_proto __weak; const struct bpf_func_proto bpf_map_update_elem_proto __weak; const struct bpf_func_proto bpf_map_delete_elem_proto __weak; +const struct bpf_func_proto bpf_get_prandom_u32_proto __weak; + /* To execute LD_ABS/LD_IND instructions __bpf_prog_run() may call * skb_copy_bits(), so provide a weak definition of it for NET-less config. */ diff --git a/kernel/bpf/helpers.c b/kernel/bpf/helpers.c index a3c7701a8b5e..95eb59a045ea 100644 --- a/kernel/bpf/helpers.c +++ b/kernel/bpf/helpers.c @@ -11,6 +11,7 @@ */ #include #include +#include /* If kernel subsystem is allowing eBPF programs to call this function, * inside its own verifier_ops->get_func_proto() callback it should return @@ -87,3 +88,14 @@ const struct bpf_func_proto bpf_map_delete_elem_proto = { .arg1_type = ARG_CONST_MAP_PTR, .arg2_type = ARG_PTR_TO_MAP_KEY, }; + +static u64 bpf_get_prandom_u32(u64 r1, u64 r2, u64 r3, u64 r4, u64 r5) +{ + return prandom_u32(); +} + +const struct bpf_func_proto bpf_get_prandom_u32_proto = { + .func = bpf_get_prandom_u32, + .gpl_only = false, + .ret_type = RET_INTEGER, +}; diff --git a/net/core/filter.c b/net/core/filter.c index 7a4eb7030dba..4344db39af2e 100644 --- a/net/core/filter.c +++ b/net/core/filter.c @@ -1139,6 +1139,8 @@ sk_filter_func_proto(enum bpf_func_id func_id) return &bpf_map_update_elem_proto; case BPF_FUNC_map_delete_elem: return &bpf_map_delete_elem_proto; + case BPF_FUNC_get_prandom_u32: + return &bpf_get_prandom_u32_proto; default: return NULL; } -- cgit v1.2.3 From c04167ce2ca0ecaeaafef006cb0d65cf01b68e42 Mon Sep 17 00:00:00 2001 From: Daniel Borkmann Date: Sat, 14 Mar 2015 02:27:17 +0100 Subject: ebpf: add helper for obtaining current processor id This patch adds the possibility to obtain raw_smp_processor_id() in eBPF. Currently, this is only possible in classic BPF where commit da2033c28226 ("filter: add SKF_AD_RXHASH and SKF_AD_CPU") has added facilities for this. Perhaps most importantly, this would also allow us to track per CPU statistics with eBPF maps, or to implement a poor-man's per CPU data structure through eBPF maps. Example function proto-type looks like: u32 (*smp_processor_id)(void) = (void *)BPF_FUNC_get_smp_processor_id; Signed-off-by: Daniel Borkmann Signed-off-by: David S. Miller --- include/linux/bpf.h | 1 + include/uapi/linux/bpf.h | 1 + kernel/bpf/core.c | 1 + kernel/bpf/helpers.c | 12 ++++++++++++ net/core/filter.c | 2 ++ 5 files changed, 17 insertions(+) (limited to 'include') diff --git a/include/linux/bpf.h b/include/linux/bpf.h index 50bf95e29a96..30bfd331882a 100644 --- a/include/linux/bpf.h +++ b/include/linux/bpf.h @@ -155,5 +155,6 @@ extern const struct bpf_func_proto bpf_map_update_elem_proto; extern const struct bpf_func_proto bpf_map_delete_elem_proto; extern const struct bpf_func_proto bpf_get_prandom_u32_proto; +extern const struct bpf_func_proto bpf_get_smp_processor_id_proto; #endif /* _LINUX_BPF_H */ diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h index 1c2ca2b477c8..de1f63668daf 100644 --- a/include/uapi/linux/bpf.h +++ b/include/uapi/linux/bpf.h @@ -166,6 +166,7 @@ enum bpf_func_id { BPF_FUNC_map_update_elem, /* int map_update_elem(&map, &key, &value, flags) */ BPF_FUNC_map_delete_elem, /* int map_delete_elem(&map, &key) */ BPF_FUNC_get_prandom_u32, /* u32 prandom_u32(void) */ + BPF_FUNC_get_smp_processor_id, /* u32 raw_smp_processor_id(void) */ __BPF_FUNC_MAX_ID, }; diff --git a/kernel/bpf/core.c b/kernel/bpf/core.c index c1dbbb5d289b..4139a0f8b558 100644 --- a/kernel/bpf/core.c +++ b/kernel/bpf/core.c @@ -662,6 +662,7 @@ const struct bpf_func_proto bpf_map_update_elem_proto __weak; const struct bpf_func_proto bpf_map_delete_elem_proto __weak; const struct bpf_func_proto bpf_get_prandom_u32_proto __weak; +const struct bpf_func_proto bpf_get_smp_processor_id_proto __weak; /* To execute LD_ABS/LD_IND instructions __bpf_prog_run() may call * skb_copy_bits(), so provide a weak definition of it for NET-less config. diff --git a/kernel/bpf/helpers.c b/kernel/bpf/helpers.c index 95eb59a045ea..bd7f5988ed9c 100644 --- a/kernel/bpf/helpers.c +++ b/kernel/bpf/helpers.c @@ -12,6 +12,7 @@ #include #include #include +#include /* If kernel subsystem is allowing eBPF programs to call this function, * inside its own verifier_ops->get_func_proto() callback it should return @@ -99,3 +100,14 @@ const struct bpf_func_proto bpf_get_prandom_u32_proto = { .gpl_only = false, .ret_type = RET_INTEGER, }; + +static u64 bpf_get_smp_processor_id(u64 r1, u64 r2, u64 r3, u64 r4, u64 r5) +{ + return raw_smp_processor_id(); +} + +const struct bpf_func_proto bpf_get_smp_processor_id_proto = { + .func = bpf_get_smp_processor_id, + .gpl_only = false, + .ret_type = RET_INTEGER, +}; diff --git a/net/core/filter.c b/net/core/filter.c index 4344db39af2e..33310eee6134 100644 --- a/net/core/filter.c +++ b/net/core/filter.c @@ -1141,6 +1141,8 @@ sk_filter_func_proto(enum bpf_func_id func_id) return &bpf_map_delete_elem_proto; case BPF_FUNC_get_prandom_u32: return &bpf_get_prandom_u32_proto; + case BPF_FUNC_get_smp_processor_id: + return &bpf_get_smp_processor_id_proto; default: return NULL; } -- cgit v1.2.3 From 9bac3d6d548e5cc925570b263f35b70a00a00ffd Mon Sep 17 00:00:00 2001 From: Alexei Starovoitov Date: Fri, 13 Mar 2015 11:57:42 -0700 Subject: bpf: allow extended BPF programs access skb fields introduce user accessible mirror of in-kernel 'struct sk_buff': struct __sk_buff { __u32 len; __u32 pkt_type; __u32 mark; __u32 queue_mapping; }; bpf programs can do: int bpf_prog(struct __sk_buff *skb) { __u32 var = skb->pkt_type; which will be compiled to bpf assembler as: dst_reg = *(u32 *)(src_reg + 4) // 4 == offsetof(struct __sk_buff, pkt_type) bpf verifier will check validity of access and will convert it to: dst_reg = *(u8 *)(src_reg + offsetof(struct sk_buff, __pkt_type_offset)) dst_reg &= 7 since skb->pkt_type is a bitfield. Signed-off-by: Alexei Starovoitov Signed-off-by: David S. Miller --- include/linux/bpf.h | 5 +- include/uapi/linux/bpf.h | 10 ++++ kernel/bpf/syscall.c | 2 +- kernel/bpf/verifier.c | 152 ++++++++++++++++++++++++++++++++++++++++++----- net/core/filter.c | 100 +++++++++++++++++++++++++------ 5 files changed, 234 insertions(+), 35 deletions(-) (limited to 'include') diff --git a/include/linux/bpf.h b/include/linux/bpf.h index 30bfd331882a..280a315de8d6 100644 --- a/include/linux/bpf.h +++ b/include/linux/bpf.h @@ -103,6 +103,9 @@ struct bpf_verifier_ops { * with 'type' (read or write) is allowed */ bool (*is_valid_access)(int off, int size, enum bpf_access_type type); + + u32 (*convert_ctx_access)(int dst_reg, int src_reg, int ctx_off, + struct bpf_insn *insn); }; struct bpf_prog_type_list { @@ -133,7 +136,7 @@ struct bpf_map *bpf_map_get(struct fd f); void bpf_map_put(struct bpf_map *map); /* verify correctness of eBPF program */ -int bpf_check(struct bpf_prog *fp, union bpf_attr *attr); +int bpf_check(struct bpf_prog **fp, union bpf_attr *attr); #else static inline void bpf_register_prog_type(struct bpf_prog_type_list *tl) { diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h index de1f63668daf..929545a27546 100644 --- a/include/uapi/linux/bpf.h +++ b/include/uapi/linux/bpf.h @@ -170,4 +170,14 @@ enum bpf_func_id { __BPF_FUNC_MAX_ID, }; +/* user accessible mirror of in-kernel sk_buff. + * new fields can only be added to the end of this structure + */ +struct __sk_buff { + __u32 len; + __u32 pkt_type; + __u32 mark; + __u32 queue_mapping; +}; + #endif /* _UAPI__LINUX_BPF_H__ */ diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c index 669719ccc9ee..ea75c654af1b 100644 --- a/kernel/bpf/syscall.c +++ b/kernel/bpf/syscall.c @@ -519,7 +519,7 @@ static int bpf_prog_load(union bpf_attr *attr) goto free_prog; /* run eBPF verifier */ - err = bpf_check(prog, attr); + err = bpf_check(&prog, attr); if (err < 0) goto free_used_maps; diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index e6b522496250..c22ebd36fa4b 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -1620,11 +1620,10 @@ static int do_check(struct verifier_env *env) return err; } else if (class == BPF_LDX) { - if (BPF_MODE(insn->code) != BPF_MEM || - insn->imm != 0) { - verbose("BPF_LDX uses reserved fields\n"); - return -EINVAL; - } + enum bpf_reg_type src_reg_type; + + /* check for reserved fields is already done */ + /* check src operand */ err = check_reg_arg(regs, insn->src_reg, SRC_OP); if (err) @@ -1643,6 +1642,29 @@ static int do_check(struct verifier_env *env) if (err) return err; + src_reg_type = regs[insn->src_reg].type; + + if (insn->imm == 0 && BPF_SIZE(insn->code) == BPF_W) { + /* saw a valid insn + * dst_reg = *(u32 *)(src_reg + off) + * use reserved 'imm' field to mark this insn + */ + insn->imm = src_reg_type; + + } else if (src_reg_type != insn->imm && + (src_reg_type == PTR_TO_CTX || + insn->imm == PTR_TO_CTX)) { + /* ABuser program is trying to use the same insn + * dst_reg = *(u32*) (src_reg + off) + * with different pointer types: + * src_reg == ctx in one branch and + * src_reg == stack|map in some other branch. + * Reject it. + */ + verbose("same insn cannot be used with different pointers\n"); + return -EINVAL; + } + } else if (class == BPF_STX) { if (BPF_MODE(insn->code) == BPF_XADD) { err = check_xadd(env, insn); @@ -1790,6 +1812,13 @@ static int replace_map_fd_with_map_ptr(struct verifier_env *env) int i, j; for (i = 0; i < insn_cnt; i++, insn++) { + if (BPF_CLASS(insn->code) == BPF_LDX && + (BPF_MODE(insn->code) != BPF_MEM || + insn->imm != 0)) { + verbose("BPF_LDX uses reserved fields\n"); + return -EINVAL; + } + if (insn[0].code == (BPF_LD | BPF_IMM | BPF_DW)) { struct bpf_map *map; struct fd f; @@ -1881,6 +1910,92 @@ static void convert_pseudo_ld_imm64(struct verifier_env *env) insn->src_reg = 0; } +static void adjust_branches(struct bpf_prog *prog, int pos, int delta) +{ + struct bpf_insn *insn = prog->insnsi; + int insn_cnt = prog->len; + int i; + + for (i = 0; i < insn_cnt; i++, insn++) { + if (BPF_CLASS(insn->code) != BPF_JMP || + BPF_OP(insn->code) == BPF_CALL || + BPF_OP(insn->code) == BPF_EXIT) + continue; + + /* adjust offset of jmps if necessary */ + if (i < pos && i + insn->off + 1 > pos) + insn->off += delta; + else if (i > pos && i + insn->off + 1 < pos) + insn->off -= delta; + } +} + +/* convert load instructions that access fields of 'struct __sk_buff' + * into sequence of instructions that access fields of 'struct sk_buff' + */ +static int convert_ctx_accesses(struct verifier_env *env) +{ + struct bpf_insn *insn = env->prog->insnsi; + int insn_cnt = env->prog->len; + struct bpf_insn insn_buf[16]; + struct bpf_prog *new_prog; + u32 cnt; + int i; + + if (!env->prog->aux->ops->convert_ctx_access) + return 0; + + for (i = 0; i < insn_cnt; i++, insn++) { + if (insn->code != (BPF_LDX | BPF_MEM | BPF_W)) + continue; + + if (insn->imm != PTR_TO_CTX) { + /* clear internal mark */ + insn->imm = 0; + continue; + } + + cnt = env->prog->aux->ops-> + convert_ctx_access(insn->dst_reg, insn->src_reg, + insn->off, insn_buf); + if (cnt == 0 || cnt >= ARRAY_SIZE(insn_buf)) { + verbose("bpf verifier is misconfigured\n"); + return -EINVAL; + } + + if (cnt == 1) { + memcpy(insn, insn_buf, sizeof(*insn)); + continue; + } + + /* several new insns need to be inserted. Make room for them */ + insn_cnt += cnt - 1; + new_prog = bpf_prog_realloc(env->prog, + bpf_prog_size(insn_cnt), + GFP_USER); + if (!new_prog) + return -ENOMEM; + + new_prog->len = insn_cnt; + + memmove(new_prog->insnsi + i + cnt, new_prog->insns + i + 1, + sizeof(*insn) * (insn_cnt - i - cnt)); + + /* copy substitute insns in place of load instruction */ + memcpy(new_prog->insnsi + i, insn_buf, sizeof(*insn) * cnt); + + /* adjust branches in the whole program */ + adjust_branches(new_prog, i, cnt - 1); + + /* keep walking new program and skip insns we just inserted */ + env->prog = new_prog; + insn = new_prog->insnsi + i + cnt - 1; + i += cnt - 1; + } + + return 0; +} + static void free_states(struct verifier_env *env) { struct verifier_state_list *sl, *sln; @@ -1903,13 +2018,13 @@ static void free_states(struct verifier_env *env) kfree(env->explored_states); } -int bpf_check(struct bpf_prog *prog, union bpf_attr *attr) +int bpf_check(struct bpf_prog **prog, union bpf_attr *attr) { char __user *log_ubuf = NULL; struct verifier_env *env; int ret = -EINVAL; - if (prog->len <= 0 || prog->len > BPF_MAXINSNS) + if ((*prog)->len <= 0 || (*prog)->len > BPF_MAXINSNS) return -E2BIG; /* 'struct verifier_env' can be global, but since it's not small, @@ -1919,7 +2034,7 @@ int bpf_check(struct bpf_prog *prog, union bpf_attr *attr) if (!env) return -ENOMEM; - env->prog = prog; + env->prog = *prog; /* grab the mutex to protect few globals used by verifier */ mutex_lock(&bpf_verifier_lock); @@ -1951,7 +2066,7 @@ int bpf_check(struct bpf_prog *prog, union bpf_attr *attr) if (ret < 0) goto skip_full_check; - env->explored_states = kcalloc(prog->len, + env->explored_states = kcalloc(env->prog->len, sizeof(struct verifier_state_list *), GFP_USER); ret = -ENOMEM; @@ -1968,6 +2083,10 @@ skip_full_check: while (pop_stack(env, NULL) >= 0); free_states(env); + if (ret == 0) + /* program is valid, convert *(u32*)(ctx + off) accesses */ + ret = convert_ctx_accesses(env); + if (log_level && log_len >= log_size - 1) { BUG_ON(log_len >= log_size); /* verifier log exceeded user supplied buffer */ @@ -1983,18 +2102,18 @@ skip_full_check: if (ret == 0 && env->used_map_cnt) { /* if program passed verifier, update used_maps in bpf_prog_info */ - prog->aux->used_maps = kmalloc_array(env->used_map_cnt, - sizeof(env->used_maps[0]), - GFP_KERNEL); + env->prog->aux->used_maps = kmalloc_array(env->used_map_cnt, + sizeof(env->used_maps[0]), + GFP_KERNEL); - if (!prog->aux->used_maps) { + if (!env->prog->aux->used_maps) { ret = -ENOMEM; goto free_log_buf; } - memcpy(prog->aux->used_maps, env->used_maps, + memcpy(env->prog->aux->used_maps, env->used_maps, sizeof(env->used_maps[0]) * env->used_map_cnt); - prog->aux->used_map_cnt = env->used_map_cnt; + env->prog->aux->used_map_cnt = env->used_map_cnt; /* program is valid. Convert pseudo bpf_ld_imm64 into generic * bpf_ld_imm64 instructions @@ -2006,11 +2125,12 @@ free_log_buf: if (log_level) vfree(log_buf); free_env: - if (!prog->aux->used_maps) + if (!env->prog->aux->used_maps) /* if we didn't copy map pointers into bpf_prog_info, release * them now. Otherwise free_bpf_prog_info() will release them. */ release_maps(env); + *prog = env->prog; kfree(env); mutex_unlock(&bpf_verifier_lock); return ret; diff --git a/net/core/filter.c b/net/core/filter.c index 33310eee6134..4e9dd0ad0d5b 100644 --- a/net/core/filter.c +++ b/net/core/filter.c @@ -150,10 +150,43 @@ static u64 __get_random_u32(u64 ctx, u64 a, u64 x, u64 r4, u64 r5) return prandom_u32(); } +static u32 convert_skb_access(int skb_field, int dst_reg, int src_reg, + struct bpf_insn *insn_buf) +{ + struct bpf_insn *insn = insn_buf; + + switch (skb_field) { + case SKF_AD_MARK: + BUILD_BUG_ON(FIELD_SIZEOF(struct sk_buff, mark) != 4); + + *insn++ = BPF_LDX_MEM(BPF_W, dst_reg, src_reg, + offsetof(struct sk_buff, mark)); + break; + + case SKF_AD_PKTTYPE: + *insn++ = BPF_LDX_MEM(BPF_B, dst_reg, src_reg, PKT_TYPE_OFFSET()); + *insn++ = BPF_ALU32_IMM(BPF_AND, dst_reg, PKT_TYPE_MAX); +#ifdef __BIG_ENDIAN_BITFIELD + *insn++ = BPF_ALU32_IMM(BPF_RSH, dst_reg, 5); +#endif + break; + + case SKF_AD_QUEUE: + BUILD_BUG_ON(FIELD_SIZEOF(struct sk_buff, queue_mapping) != 2); + + *insn++ = BPF_LDX_MEM(BPF_H, dst_reg, src_reg, + offsetof(struct sk_buff, queue_mapping)); + break; + } + + return insn - insn_buf; +} + static bool convert_bpf_extensions(struct sock_filter *fp, struct bpf_insn **insnp) { struct bpf_insn *insn = *insnp; + u32 cnt; switch (fp->k) { case SKF_AD_OFF + SKF_AD_PROTOCOL: @@ -167,13 +200,8 @@ static bool convert_bpf_extensions(struct sock_filter *fp, break; case SKF_AD_OFF + SKF_AD_PKTTYPE: - *insn++ = BPF_LDX_MEM(BPF_B, BPF_REG_A, BPF_REG_CTX, - PKT_TYPE_OFFSET()); - *insn = BPF_ALU32_IMM(BPF_AND, BPF_REG_A, PKT_TYPE_MAX); -#ifdef __BIG_ENDIAN_BITFIELD - insn++; - *insn = BPF_ALU32_IMM(BPF_RSH, BPF_REG_A, 5); -#endif + cnt = convert_skb_access(SKF_AD_PKTTYPE, BPF_REG_A, BPF_REG_CTX, insn); + insn += cnt - 1; break; case SKF_AD_OFF + SKF_AD_IFINDEX: @@ -197,10 +225,8 @@ static bool convert_bpf_extensions(struct sock_filter *fp, break; case SKF_AD_OFF + SKF_AD_MARK: - BUILD_BUG_ON(FIELD_SIZEOF(struct sk_buff, mark) != 4); - - *insn = BPF_LDX_MEM(BPF_W, BPF_REG_A, BPF_REG_CTX, - offsetof(struct sk_buff, mark)); + cnt = convert_skb_access(SKF_AD_MARK, BPF_REG_A, BPF_REG_CTX, insn); + insn += cnt - 1; break; case SKF_AD_OFF + SKF_AD_RXHASH: @@ -211,10 +237,8 @@ static bool convert_bpf_extensions(struct sock_filter *fp, break; case SKF_AD_OFF + SKF_AD_QUEUE: - BUILD_BUG_ON(FIELD_SIZEOF(struct sk_buff, queue_mapping) != 2); - - *insn = BPF_LDX_MEM(BPF_H, BPF_REG_A, BPF_REG_CTX, - offsetof(struct sk_buff, queue_mapping)); + cnt = convert_skb_access(SKF_AD_QUEUE, BPF_REG_A, BPF_REG_CTX, insn); + insn += cnt - 1; break; case SKF_AD_OFF + SKF_AD_VLAN_TAG: @@ -1151,13 +1175,55 @@ sk_filter_func_proto(enum bpf_func_id func_id) static bool sk_filter_is_valid_access(int off, int size, enum bpf_access_type type) { - /* skb fields cannot be accessed yet */ - return false; + /* only read is allowed */ + if (type != BPF_READ) + return false; + + /* check bounds */ + if (off < 0 || off >= sizeof(struct __sk_buff)) + return false; + + /* disallow misaligned access */ + if (off % size != 0) + return false; + + /* all __sk_buff fields are __u32 */ + if (size != 4) + return false; + + return true; +} + +static u32 sk_filter_convert_ctx_access(int dst_reg, int src_reg, int ctx_off, + struct bpf_insn *insn_buf) +{ + struct bpf_insn *insn = insn_buf; + + switch (ctx_off) { + case offsetof(struct __sk_buff, len): + BUILD_BUG_ON(FIELD_SIZEOF(struct sk_buff, len) != 4); + + *insn++ = BPF_LDX_MEM(BPF_W, dst_reg, src_reg, + offsetof(struct sk_buff, len)); + break; + + case offsetof(struct __sk_buff, mark): + return convert_skb_access(SKF_AD_MARK, dst_reg, src_reg, insn); + + case offsetof(struct __sk_buff, pkt_type): + return convert_skb_access(SKF_AD_PKTTYPE, dst_reg, src_reg, insn); + + case offsetof(struct __sk_buff, queue_mapping): + return convert_skb_access(SKF_AD_QUEUE, dst_reg, src_reg, insn); + } + + return insn - insn_buf; } static const struct bpf_verifier_ops sk_filter_ops = { .get_func_proto = sk_filter_func_proto, .is_valid_access = sk_filter_is_valid_access, + .convert_ctx_access = sk_filter_convert_ctx_access, }; static struct bpf_prog_type_list sk_filter_type __read_mostly = { -- cgit v1.2.3 From 4170604feec780d00e7511c24fa0f6e5c2e4ed75 Mon Sep 17 00:00:00 2001 From: Scott Feldman Date: Sun, 15 Mar 2015 21:07:14 -0700 Subject: switchdev: add swdev ops As discussed at netconf, introduce swdev_ops as first step to move switchdev ops from ndo to swdev. This will keep switchdev from cluttering up ndo ops space. Signed-off-by: Scott Feldman Signed-off-by: David S. Miller --- include/linux/netdevice.h | 3 +++ include/net/switchdev.h | 38 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 41 insertions(+) (limited to 'include') diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index ddab1a2a07a0..9e8a2a933c68 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -1577,6 +1577,9 @@ struct net_device { const struct net_device_ops *netdev_ops; const struct ethtool_ops *ethtool_ops; const struct forwarding_accel_ops *fwd_ops; +#ifdef CONFIG_NET_SWITCHDEV + const struct swdev_ops *swdev_ops; +#endif const struct header_ops *header_ops; diff --git a/include/net/switchdev.h b/include/net/switchdev.h index 1a9382febcc3..e5de53f92482 100644 --- a/include/net/switchdev.h +++ b/include/net/switchdev.h @@ -14,6 +14,44 @@ #include #include +struct fib_info; + +/** + * struct switchdev_ops - switchdev operations + * + * int (*swdev_parent_id_get)(struct net_device *dev, + * struct netdev_phys_item_id *psid); + * Called to get an ID of the switch chip this port is part of. + * If driver implements this, it indicates that it represents a port + * of a switch chip. + * + * int (*swdev_port_stp_update)(struct net_device *dev, u8 state); + * Called to notify switch device port of bridge port STP + * state change. + * + * int (*swdev_fib_ipv4_add)(struct net_device *dev, __be32 dst, + * int dst_len, struct fib_info *fi, + * u8 tos, u8 type, u32 nlflags, u32 tb_id); + * Called to add/modify IPv4 route to switch device. + * + * int (*swdev_fib_ipv4_del)(struct net_device *dev, __be32 dst, + * int dst_len, struct fib_info *fi, + * u8 tos, u8 type, u32 tb_id); + * Called to delete IPv4 route from switch device. + */ +struct swdev_ops { + int (*swdev_parent_id_get)(struct net_device *dev, + struct netdev_phys_item_id *psid); + int (*swdev_port_stp_update)(struct net_device *dev, u8 state); + int (*swdev_fib_ipv4_add)(struct net_device *dev, __be32 dst, + int dst_len, struct fib_info *fi, + u8 tos, u8 type, u32 nlflags, + u32 tb_id); + int (*swdev_fib_ipv4_del)(struct net_device *dev, __be32 dst, + int dst_len, struct fib_info *fi, + u8 tos, u8 type, u32 tb_id); +}; + enum netdev_switch_notifier_type { NETDEV_SWITCH_FDB_ADD = 1, NETDEV_SWITCH_FDB_DEL, -- cgit v1.2.3 From 812a1c3ff3ee9d5100e0e71edb06681014e84a9b Mon Sep 17 00:00:00 2001 From: Scott Feldman Date: Sun, 15 Mar 2015 21:07:16 -0700 Subject: netdev: remove ndo ops for switchdev Signed-off-by: Scott Feldman Signed-off-by: David S. Miller --- include/linux/netdevice.h | 38 -------------------------------------- 1 file changed, 38 deletions(-) (limited to 'include') diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 9e8a2a933c68..dd1d069758be 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -768,8 +768,6 @@ struct netdev_phys_item_id { typedef u16 (*select_queue_fallback_t)(struct net_device *dev, struct sk_buff *skb); -struct fib_info; - /* * This structure defines the management hooks for network devices. * The following hooks can be defined; unless noted otherwise, they are @@ -1024,23 +1022,6 @@ struct fib_info; * be otherwise expressed by feature flags. The check is called with * the set of features that the stack has calculated and it returns * those the driver believes to be appropriate. - * - * int (*ndo_switch_parent_id_get)(struct net_device *dev, - * struct netdev_phys_item_id *psid); - * Called to get an ID of the switch chip this port is part of. - * If driver implements this, it indicates that it represents a port - * of a switch chip. - * int (*ndo_switch_port_stp_update)(struct net_device *dev, u8 state); - * Called to notify switch device port of bridge port STP - * state change. - * int (*ndo_sw_parent_fib_ipv4_add)(struct net_device *dev, __be32 dst, - * int dst_len, struct fib_info *fi, - * u8 tos, u8 type, u32 nlflags, u32 tb_id); - * Called to add/modify IPv4 route to switch device. - * int (*ndo_sw_parent_fib_ipv4_del)(struct net_device *dev, __be32 dst, - * int dst_len, struct fib_info *fi, - * u8 tos, u8 type, u32 tb_id); - * Called to delete IPv4 route from switch device. */ struct net_device_ops { int (*ndo_init)(struct net_device *dev); @@ -1197,25 +1178,6 @@ struct net_device_ops { netdev_features_t (*ndo_features_check) (struct sk_buff *skb, struct net_device *dev, netdev_features_t features); -#ifdef CONFIG_NET_SWITCHDEV - int (*ndo_switch_parent_id_get)(struct net_device *dev, - struct netdev_phys_item_id *psid); - int (*ndo_switch_port_stp_update)(struct net_device *dev, - u8 state); - int (*ndo_switch_fib_ipv4_add)(struct net_device *dev, - __be32 dst, - int dst_len, - struct fib_info *fi, - u8 tos, u8 type, - u32 nlflags, - u32 tb_id); - int (*ndo_switch_fib_ipv4_del)(struct net_device *dev, - __be32 dst, - int dst_len, - struct fib_info *fi, - u8 tos, u8 type, - u32 tb_id); -#endif }; /** -- cgit v1.2.3 From aefedc1a4cfe4c874b8a7ac743f9deedc289d9e6 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Sun, 15 Mar 2015 17:08:19 -0700 Subject: Bluetooth: Remove unneeded HCI_CONN_REMOTE_OOB connection flag The HCI_CONN_REMOTE_OOB connection flag is used to indicate if the pairing initiator has provided out-of-band data. However since that value is no longer used in any decision making, just remove it. It is actually unclear what purpose the OOB data present field from the HCI IO Capability Response event serves in the first place. If either side provided out-of-band data, then that data will be used for pairing. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- include/net/bluetooth/hci_core.h | 1 - net/bluetooth/hci_event.c | 2 -- 2 files changed, 3 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index ce94bcb33600..e4dc18eed446 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -596,7 +596,6 @@ enum { HCI_CONN_SC_ENABLED, HCI_CONN_AES_CCM, HCI_CONN_POWER_SAVE, - HCI_CONN_REMOTE_OOB, HCI_CONN_FLUSH_KEY, HCI_CONN_ENCRYPT, HCI_CONN_AUTH, diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 10d760c46df1..d800f0c5aa21 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -4006,8 +4006,6 @@ static void hci_io_capa_reply_evt(struct hci_dev *hdev, struct sk_buff *skb) conn->remote_cap = ev->capability; conn->remote_auth = ev->authentication; - if (ev->oob_data) - set_bit(HCI_CONN_REMOTE_OOB, &conn->flags); unlock: hci_dev_unlock(hdev); -- cgit v1.2.3 From dc5a1ad7bd830b7789ba2950342bdecfe4787945 Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Thu, 12 Mar 2015 08:53:24 +0200 Subject: mac80211: allow to get wireless_dev structure from ieee80211_vif This will allow mac80211 drivers to call cfg80211 APIs with the right handle. Signed-off-by: Emmanuel Grumbach Signed-off-by: Johannes Berg --- include/net/mac80211.h | 13 +++++++++++++ net/mac80211/util.c | 12 ++++++++++++ 2 files changed, 25 insertions(+) (limited to 'include') diff --git a/include/net/mac80211.h b/include/net/mac80211.h index a7756e45465e..157c0f151766 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -1281,6 +1281,19 @@ static inline bool ieee80211_vif_is_mesh(struct ieee80211_vif *vif) */ struct ieee80211_vif *wdev_to_ieee80211_vif(struct wireless_dev *wdev); +/** + * ieee80211_vif_to_wdev - return a wdev struct from a vif + * @vif: the vif to get the wdev for + * + * This can be used by mac80211 drivers with direct cfg80211 APIs + * (like the vendor commands) that needs to get the wdev for a vif. + * + * Note that this function may return %NULL if the given wdev isn't + * associated with a vif that the driver knows about (e.g. monitor + * or AP_VLAN interfaces.) + */ +struct wireless_dev *ieee80211_vif_to_wdev(struct ieee80211_vif *vif); + /** * enum ieee80211_key_flags - key flags * diff --git a/net/mac80211/util.c b/net/mac80211/util.c index 37d85d36dd2c..e664b28821a2 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -745,6 +745,18 @@ struct ieee80211_vif *wdev_to_ieee80211_vif(struct wireless_dev *wdev) } EXPORT_SYMBOL_GPL(wdev_to_ieee80211_vif); +struct wireless_dev *ieee80211_vif_to_wdev(struct ieee80211_vif *vif) +{ + struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif); + + if (!ieee80211_sdata_running(sdata) || + !(sdata->flags & IEEE80211_SDATA_IN_DRIVER)) + return NULL; + + return &sdata->wdev; +} +EXPORT_SYMBOL_GPL(ieee80211_vif_to_wdev); + /* * Nothing should have been stuffed into the workqueue during * the suspend->resume cycle. Since we can't check each caller -- cgit v1.2.3 From f709bfcf6a292560ce187c33fd099ee495c0a404 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Mon, 16 Mar 2015 01:10:20 -0700 Subject: Bluetooth: Add constants for LE SC Confirmation and Random values The LE Secure Connections Confirmation Value and LE Secure Connections Random Value contants are required for the out-of-band data and so just define them. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- include/net/bluetooth/hci.h | 2 ++ 1 file changed, 2 insertions(+) (limited to 'include') diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index af9893b704ff..ce757303dc07 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -458,6 +458,8 @@ enum { #define EIR_DEVICE_ID 0x10 /* device ID */ #define EIR_LE_BDADDR 0x1B /* LE Bluetooth device address */ #define EIR_LE_ROLE 0x1C /* LE role */ +#define EIR_LE_SC_CONFIRM 0x22 /* LE SC Confirmation Value */ +#define EIR_LE_SC_RANDOM 0x23 /* LE SC Random Value */ /* Low Energy Advertising Flags */ #define LE_AD_LIMITED 0x01 /* Limited Discoverable */ -- cgit v1.2.3 From 4d9b519c9bcab5718053f8717dadad7b09b41f5e Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Thu, 12 Mar 2015 14:00:02 -0700 Subject: hwrng: add devm_* interfaces This change adds devm_hwrng_register and devm_hwrng_unregister which use can simplify error unwinding and unbinding code paths in device drivers. Signed-off-by: Dmitry Torokhov Signed-off-by: Herbert Xu --- drivers/char/hw_random/core.c | 42 ++++++++++++++++++++++++++++++++++++++++++ include/linux/hw_random.h | 4 ++++ 2 files changed, 46 insertions(+) (limited to 'include') diff --git a/drivers/char/hw_random/core.c b/drivers/char/hw_random/core.c index 32a8a867f7f8..83161dde53ee 100644 --- a/drivers/char/hw_random/core.c +++ b/drivers/char/hw_random/core.c @@ -536,6 +536,48 @@ void hwrng_unregister(struct hwrng *rng) } EXPORT_SYMBOL_GPL(hwrng_unregister); +static void devm_hwrng_release(struct device *dev, void *res) +{ + hwrng_unregister(*(struct hwrng **)res); +} + +static int devm_hwrng_match(struct device *dev, void *res, void *data) +{ + struct hwrng **r = res; + + if (WARN_ON(!r || !*r)) + return 0; + + return *r == data; +} + +int devm_hwrng_register(struct device *dev, struct hwrng *rng) +{ + struct hwrng **ptr; + int error; + + ptr = devres_alloc(devm_hwrng_release, sizeof(*ptr), GFP_KERNEL); + if (!ptr) + return -ENOMEM; + + error = hwrng_register(rng); + if (error) { + devres_free(ptr); + return error; + } + + *ptr = rng; + devres_add(dev, ptr); + return 0; +} +EXPORT_SYMBOL_GPL(devm_hwrng_register); + +void devm_hwrng_unregister(struct device *dev, struct hwrng *rng) +{ + devres_release(dev, devm_hwrng_release, devm_hwrng_match, rng); +} +EXPORT_SYMBOL_GPL(devm_hwrng_unregister); + static int __init hwrng_modinit(void) { return register_miscdev(); diff --git a/include/linux/hw_random.h b/include/linux/hw_random.h index eb7b414d232b..4f7d8f4b1e9a 100644 --- a/include/linux/hw_random.h +++ b/include/linux/hw_random.h @@ -50,10 +50,14 @@ struct hwrng { struct completion cleanup_done; }; +struct device; + /** Register a new Hardware Random Number Generator driver. */ extern int hwrng_register(struct hwrng *rng); +extern int devm_hwrng_register(struct device *dev, struct hwrng *rng); /** Unregister a Hardware Random Number Generator driver. */ extern void hwrng_unregister(struct hwrng *rng); +extern void devm_hwrng_unregister(struct device *dve, struct hwrng *rng); /** Feed random bits into the pool. */ extern void add_hwgenerator_randomness(const char *buffer, size_t count, size_t entropy); -- cgit v1.2.3 From 6b5b042d4c675cb9d3446a1cdcaca98e715ba812 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Sun, 15 Mar 2015 10:27:20 +0100 Subject: ASoC: Make snd_soc_dapm_kcontrol_codec() inline snd_soc_dapm_kcontrol_codec() is a extremely simple function and inlining it typically results in less code than necessary for calling the non-inlined version of the function. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown --- include/sound/soc-dapm.h | 1 - include/sound/soc.h | 13 +++++++++++++ sound/soc/soc-dapm.c | 10 ---------- 3 files changed, 13 insertions(+), 11 deletions(-) (limited to 'include') diff --git a/include/sound/soc-dapm.h b/include/sound/soc-dapm.h index 8d7416e46861..78633efd40ee 100644 --- a/include/sound/soc-dapm.h +++ b/include/sound/soc-dapm.h @@ -440,7 +440,6 @@ void dapm_mark_endpoints_dirty(struct snd_soc_card *card); int snd_soc_dapm_dai_get_connected_widgets(struct snd_soc_dai *dai, int stream, struct snd_soc_dapm_widget_list **list); -struct snd_soc_codec *snd_soc_dapm_kcontrol_codec(struct snd_kcontrol *kcontrol); struct snd_soc_dapm_context *snd_soc_dapm_kcontrol_dapm( struct snd_kcontrol *kcontrol); diff --git a/include/sound/soc.h b/include/sound/soc.h index 0d1ade195628..85a6853a40bb 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -1258,6 +1258,19 @@ static inline struct snd_soc_dapm_context *snd_soc_component_get_dapm( return component->dapm_ptr; } +/** + * snd_soc_dapm_kcontrol_codec() - Returns the codec associated to a kcontrol + * @kcontrol: The kcontrol + * + * This function must only be used on DAPM contexts that are known to be part of + * a CODEC (e.g. in a CODEC driver). Otherwise the behavior is undefined. + */ +static inline struct snd_soc_codec *snd_soc_dapm_kcontrol_codec( + struct snd_kcontrol *kcontrol) +{ + return snd_soc_dapm_to_codec(snd_soc_dapm_kcontrol_dapm(kcontrol)); +} + /* codec IO */ unsigned int snd_soc_read(struct snd_soc_codec *codec, unsigned int reg); int snd_soc_write(struct snd_soc_codec *codec, unsigned int reg, diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index b6f88202b8c9..95337c832258 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -473,16 +473,6 @@ struct snd_soc_dapm_context *snd_soc_dapm_kcontrol_dapm( } EXPORT_SYMBOL_GPL(snd_soc_dapm_kcontrol_dapm); -/** - * snd_soc_dapm_kcontrol_codec() - Returns the codec associated to a kcontrol - * @kcontrol: The kcontrol - */ -struct snd_soc_codec *snd_soc_dapm_kcontrol_codec(struct snd_kcontrol *kcontrol) -{ - return snd_soc_dapm_to_codec(snd_soc_dapm_kcontrol_dapm(kcontrol)); -} -EXPORT_SYMBOL_GPL(snd_soc_dapm_kcontrol_codec); - static void dapm_reset(struct snd_soc_card *card) { struct snd_soc_dapm_widget *w; -- cgit v1.2.3 From c055d5b03bb4cb69d349d787c9787c0383abd8b2 Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Tue, 10 Mar 2015 05:08:19 +0100 Subject: netfilter: bridge: query conntrack about skb dnat ask conntrack instead of storing ipv4 address in nf_bridge_info->data. Ths avoids the need to use ->data during NF_PRE_ROUTING. Only two functions that need ->data remain. These will be addressed in followup patches. Signed-off-by: Florian Westphal Signed-off-by: Pablo Neira Ayuso --- include/linux/netfilter_bridge.h | 6 ------ net/bridge/br_netfilter.c | 27 +++++++++++++++++++++------ 2 files changed, 21 insertions(+), 12 deletions(-) (limited to 'include') diff --git a/include/linux/netfilter_bridge.h b/include/linux/netfilter_bridge.h index bb39113ea596..de123d769ffc 100644 --- a/include/linux/netfilter_bridge.h +++ b/include/linux/netfilter_bridge.h @@ -54,12 +54,6 @@ static inline unsigned int nf_bridge_pad(const struct sk_buff *skb) return 0; } -struct bridge_skb_cb { - union { - __be32 ipv4; - } daddr; -}; - static inline void br_drop_fake_rtable(struct sk_buff *skb) { struct dst_entry *dst = skb_dst(skb); diff --git a/net/bridge/br_netfilter.c b/net/bridge/br_netfilter.c index b260a97275db..261fcd5a42d6 100644 --- a/net/bridge/br_netfilter.c +++ b/net/bridge/br_netfilter.c @@ -37,17 +37,16 @@ #include #include +#if IS_ENABLED(CONFIG_NF_CONNTRACK) +#include +#endif + #include #include "br_private.h" #ifdef CONFIG_SYSCTL #include #endif -#define skb_origaddr(skb) (((struct bridge_skb_cb *) \ - (skb->nf_bridge->data))->daddr.ipv4) -#define store_orig_dstaddr(skb) (skb_origaddr(skb) = ip_hdr(skb)->daddr) -#define dnat_took_place(skb) (skb_origaddr(skb) != ip_hdr(skb)->daddr) - #ifdef CONFIG_SYSCTL static struct ctl_table_header *brnf_sysctl_header; static int brnf_call_iptables __read_mostly = 1; @@ -322,6 +321,22 @@ free_skb: return 0; } +static bool dnat_took_place(const struct sk_buff *skb) +{ +#if IS_ENABLED(CONFIG_NF_CONNTRACK) + enum ip_conntrack_info ctinfo; + struct nf_conn *ct; + + ct = nf_ct_get(skb, &ctinfo); + if (!ct || nf_ct_is_untracked(ct)) + return false; + + return test_bit(IPS_DST_NAT_BIT, &ct->status); +#else + return false; +#endif +} + /* This requires some explaining. If DNAT has taken place, * we will need to fix up the destination Ethernet address. * @@ -625,7 +640,7 @@ static unsigned int br_nf_pre_routing(const struct nf_hook_ops *ops, return NF_DROP; if (!setup_pre_routing(skb)) return NF_DROP; - store_orig_dstaddr(skb); + skb->protocol = htons(ETH_P_IP); NF_HOOK(NFPROTO_IPV4, NF_INET_PRE_ROUTING, skb, skb->dev, NULL, -- cgit v1.2.3 From e4bb9bcbfb7d67431dfd49860f62770a7f40193b Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Tue, 10 Mar 2015 10:36:48 +0100 Subject: netfilter: bridge: remove BRNF_STATE_BRIDGED flag Its not needed anymore since 2bf540b73ed5b ([NETFILTER]: bridge-netfilter: remove deferred hooks). Before this it was possible to have physoutdev set for locally generated packets -- this isn't the case anymore: BRNF_STATE_BRIDGED flag is set when we assign nf_bridge->physoutdev, so physoutdev != NULL means BRNF_STATE_BRIDGED is set. If physoutdev is NULL, then we are looking at locally-delivered and routed packet. Signed-off-by: Florian Westphal Signed-off-by: Pablo Neira Ayuso --- include/linux/netfilter_bridge.h | 1 - net/bridge/br_netfilter.c | 9 ++++++--- net/netfilter/xt_physdev.c | 3 +-- 3 files changed, 7 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/include/linux/netfilter_bridge.h b/include/linux/netfilter_bridge.h index de123d769ffc..ed0d3bf953c3 100644 --- a/include/linux/netfilter_bridge.h +++ b/include/linux/netfilter_bridge.h @@ -19,7 +19,6 @@ enum nf_br_hook_priorities { #define BRNF_PKT_TYPE 0x01 #define BRNF_BRIDGED_DNAT 0x02 -#define BRNF_BRIDGED 0x04 #define BRNF_NF_BRIDGE_PREROUTING 0x08 #define BRNF_8021Q 0x10 #define BRNF_PPPoE 0x20 diff --git a/net/bridge/br_netfilter.c b/net/bridge/br_netfilter.c index 261fcd5a42d6..bd2d24d1ff21 100644 --- a/net/bridge/br_netfilter.c +++ b/net/bridge/br_netfilter.c @@ -736,8 +736,6 @@ static unsigned int br_nf_forward_ip(const struct nf_hook_ops *ops, if (pf == NFPROTO_IPV4 && br_parse_ip_options(skb)) return NF_DROP; - /* The physdev module checks on this */ - nf_bridge->mask |= BRNF_BRIDGED; nf_bridge->physoutdev = skb->dev; if (pf == NFPROTO_IPV4) skb->protocol = htons(ETH_P_IP); @@ -857,7 +855,12 @@ static unsigned int br_nf_post_routing(const struct nf_hook_ops *ops, struct net_device *realoutdev = bridge_parent(skb->dev); u_int8_t pf; - if (!nf_bridge || !(nf_bridge->mask & BRNF_BRIDGED)) + /* if nf_bridge is set, but ->physoutdev is NULL, this packet came in + * on a bridge, but was delivered locally and is now being routed: + * + * POST_ROUTING was already invoked from the ip stack. + */ + if (!nf_bridge || !nf_bridge->physoutdev) return NF_ACCEPT; if (!realoutdev) diff --git a/net/netfilter/xt_physdev.c b/net/netfilter/xt_physdev.c index f440f57a452f..50a52043650f 100644 --- a/net/netfilter/xt_physdev.c +++ b/net/netfilter/xt_physdev.c @@ -56,8 +56,7 @@ physdev_mt(const struct sk_buff *skb, struct xt_action_param *par) /* This only makes sense in the FORWARD and POSTROUTING chains */ if ((info->bitmask & XT_PHYSDEV_OP_BRIDGED) && - (!!(nf_bridge->mask & BRNF_BRIDGED) ^ - !(info->invert & XT_PHYSDEV_OP_BRIDGED))) + (!!nf_bridge->physoutdev ^ !(info->invert & XT_PHYSDEV_OP_BRIDGED))) return false; if ((info->bitmask & XT_PHYSDEV_OP_ISIN && -- cgit v1.2.3 From 16b12d605795e775cd80b5901c85dd2e320a6ec2 Mon Sep 17 00:00:00 2001 From: Juergen Gross Date: Wed, 21 Jan 2015 08:49:23 +0100 Subject: xen: synchronize include/xen/interface/xen.h with xen The header include/xen/interface/xen.h doesn't contain all definitions from Xen's version of that header. Update it accordingly. Signed-off-by: Juergen Gross Reviewed-by: David Vrabel Signed-off-by: David Vrabel --- arch/x86/xen/trace.c | 2 +- include/xen/interface/xen.h | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/arch/x86/xen/trace.c b/arch/x86/xen/trace.c index 520022d1a181..8296cdf4e581 100644 --- a/arch/x86/xen/trace.c +++ b/arch/x86/xen/trace.c @@ -29,7 +29,7 @@ static const char *xen_hypercall_names[] = { N(vcpu_op), N(set_segment_base), N(mmuext_op), - N(acm_op), + N(xsm_op), N(nmi_op), N(sched_op), N(callback_op), diff --git a/include/xen/interface/xen.h b/include/xen/interface/xen.h index f68719f405af..a48378958062 100644 --- a/include/xen/interface/xen.h +++ b/include/xen/interface/xen.h @@ -67,7 +67,7 @@ #define __HYPERVISOR_vcpu_op 24 #define __HYPERVISOR_set_segment_base 25 /* x86/64 only */ #define __HYPERVISOR_mmuext_op 26 -#define __HYPERVISOR_acm_op 27 +#define __HYPERVISOR_xsm_op 27 #define __HYPERVISOR_nmi_op 28 #define __HYPERVISOR_sched_op 29 #define __HYPERVISOR_callback_op 30 @@ -75,7 +75,11 @@ #define __HYPERVISOR_event_channel_op 32 #define __HYPERVISOR_physdev_op 33 #define __HYPERVISOR_hvm_op 34 +#define __HYPERVISOR_sysctl 35 +#define __HYPERVISOR_domctl 36 +#define __HYPERVISOR_kexec_op 37 #define __HYPERVISOR_tmem_op 38 +#define __HYPERVISOR_xc_reserved_op 39 /* reserved for XenClient */ /* Architecture-specific hypercall definitions. */ #define __HYPERVISOR_arch_0 48 -- cgit v1.2.3 From 628c28eefd6f2cef03b212081b466ae43fd093a3 Mon Sep 17 00:00:00 2001 From: David Vrabel Date: Wed, 11 Mar 2015 14:49:56 +0000 Subject: xen: unify foreign GFN map/unmap for auto-xlated physmap guests Auto-translated physmap guests (arm, arm64 and x86 PVHVM/PVH) map and unmap foreign GFNs using the same method (updating the physmap). Unify the two arm and x86 implementations into one commont one. Note that on arm and arm64, the correct error code will be returned (instead of always -EFAULT) and map/unmap failure warnings are no longer printed. These changes are required if the foreign domain is paging (-ENOENT failures are expected and must be propagated up to the caller). Signed-off-by: David Vrabel Reviewed-by: Stefano Stabellini --- arch/arm/xen/enlighten.c | 90 ++------------------------------ arch/x86/xen/mmu.c | 110 ++------------------------------------- drivers/xen/Kconfig | 6 +++ drivers/xen/Makefile | 1 + drivers/xen/xlate_mmu.c | 133 +++++++++++++++++++++++++++++++++++++++++++++++ include/xen/xen-ops.h | 8 +++ 6 files changed, 154 insertions(+), 194 deletions(-) create mode 100644 drivers/xen/xlate_mmu.c (limited to 'include') diff --git a/arch/arm/xen/enlighten.c b/arch/arm/xen/enlighten.c index 263a2044c65b..5c04389fc9ef 100644 --- a/arch/arm/xen/enlighten.c +++ b/arch/arm/xen/enlighten.c @@ -53,105 +53,21 @@ EXPORT_SYMBOL_GPL(xen_platform_pci_unplug); static __read_mostly int xen_events_irq = -1; -/* map fgmfn of domid to lpfn in the current domain */ -static int map_foreign_page(unsigned long lpfn, unsigned long fgmfn, - unsigned int domid) -{ - int rc; - struct xen_add_to_physmap_range xatp = { - .domid = DOMID_SELF, - .foreign_domid = domid, - .size = 1, - .space = XENMAPSPACE_gmfn_foreign, - }; - xen_ulong_t idx = fgmfn; - xen_pfn_t gpfn = lpfn; - int err = 0; - - set_xen_guest_handle(xatp.idxs, &idx); - set_xen_guest_handle(xatp.gpfns, &gpfn); - set_xen_guest_handle(xatp.errs, &err); - - rc = HYPERVISOR_memory_op(XENMEM_add_to_physmap_range, &xatp); - if (rc || err) { - pr_warn("Failed to map pfn to mfn rc:%d:%d pfn:%lx mfn:%lx\n", - rc, err, lpfn, fgmfn); - return 1; - } - return 0; -} - -struct remap_data { - xen_pfn_t fgmfn; /* foreign domain's gmfn */ - pgprot_t prot; - domid_t domid; - struct vm_area_struct *vma; - int index; - struct page **pages; - struct xen_remap_mfn_info *info; -}; - -static int remap_pte_fn(pte_t *ptep, pgtable_t token, unsigned long addr, - void *data) -{ - struct remap_data *info = data; - struct page *page = info->pages[info->index++]; - unsigned long pfn = page_to_pfn(page); - pte_t pte = pte_mkspecial(pfn_pte(pfn, info->prot)); - - if (map_foreign_page(pfn, info->fgmfn, info->domid)) - return -EFAULT; - set_pte_at(info->vma->vm_mm, addr, ptep, pte); - - return 0; -} - int xen_remap_domain_mfn_range(struct vm_area_struct *vma, unsigned long addr, xen_pfn_t mfn, int nr, pgprot_t prot, unsigned domid, struct page **pages) { - int err; - struct remap_data data; - - /* TBD: Batching, current sole caller only does page at a time */ - if (nr > 1) - return -EINVAL; - - data.fgmfn = mfn; - data.prot = prot; - data.domid = domid; - data.vma = vma; - data.index = 0; - data.pages = pages; - err = apply_to_page_range(vma->vm_mm, addr, nr << PAGE_SHIFT, - remap_pte_fn, &data); - return err; + return xen_xlate_remap_gfn_range(vma, addr, mfn, nr, + prot, domid, pages); } EXPORT_SYMBOL_GPL(xen_remap_domain_mfn_range); int xen_unmap_domain_mfn_range(struct vm_area_struct *vma, int nr, struct page **pages) { - int i; - - for (i = 0; i < nr; i++) { - struct xen_remove_from_physmap xrp; - unsigned long rc, pfn; - - pfn = page_to_pfn(pages[i]); - - xrp.domid = DOMID_SELF; - xrp.gpfn = pfn; - rc = HYPERVISOR_memory_op(XENMEM_remove_from_physmap, &xrp); - if (rc) { - pr_warn("Failed to unmap pfn:%lx rc:%ld\n", - pfn, rc); - return rc; - } - } - return 0; + return xen_xlate_unmap_gfn_range(vma, nr, pages); } EXPORT_SYMBOL_GPL(xen_unmap_domain_mfn_range); diff --git a/arch/x86/xen/mmu.c b/arch/x86/xen/mmu.c index adca9e2b6553..3d536a56ddf1 100644 --- a/arch/x86/xen/mmu.c +++ b/arch/x86/xen/mmu.c @@ -2436,95 +2436,6 @@ void __init xen_hvm_init_mmu_ops(void) } #endif -#ifdef CONFIG_XEN_PVH -/* - * Map foreign gfn (fgfn), to local pfn (lpfn). This for the user - * space creating new guest on pvh dom0 and needing to map domU pages. - */ -static int xlate_add_to_p2m(unsigned long lpfn, unsigned long fgfn, - unsigned int domid) -{ - int rc, err = 0; - xen_pfn_t gpfn = lpfn; - xen_ulong_t idx = fgfn; - - struct xen_add_to_physmap_range xatp = { - .domid = DOMID_SELF, - .foreign_domid = domid, - .size = 1, - .space = XENMAPSPACE_gmfn_foreign, - }; - set_xen_guest_handle(xatp.idxs, &idx); - set_xen_guest_handle(xatp.gpfns, &gpfn); - set_xen_guest_handle(xatp.errs, &err); - - rc = HYPERVISOR_memory_op(XENMEM_add_to_physmap_range, &xatp); - if (rc < 0) - return rc; - return err; -} - -static int xlate_remove_from_p2m(unsigned long spfn, int count) -{ - struct xen_remove_from_physmap xrp; - int i, rc; - - for (i = 0; i < count; i++) { - xrp.domid = DOMID_SELF; - xrp.gpfn = spfn+i; - rc = HYPERVISOR_memory_op(XENMEM_remove_from_physmap, &xrp); - if (rc) - break; - } - return rc; -} - -struct xlate_remap_data { - unsigned long fgfn; /* foreign domain's gfn */ - pgprot_t prot; - domid_t domid; - int index; - struct page **pages; -}; - -static int xlate_map_pte_fn(pte_t *ptep, pgtable_t token, unsigned long addr, - void *data) -{ - int rc; - struct xlate_remap_data *remap = data; - unsigned long pfn = page_to_pfn(remap->pages[remap->index++]); - pte_t pteval = pte_mkspecial(pfn_pte(pfn, remap->prot)); - - rc = xlate_add_to_p2m(pfn, remap->fgfn, remap->domid); - if (rc) - return rc; - native_set_pte(ptep, pteval); - - return 0; -} - -static int xlate_remap_gfn_range(struct vm_area_struct *vma, - unsigned long addr, unsigned long mfn, - int nr, pgprot_t prot, unsigned domid, - struct page **pages) -{ - int err; - struct xlate_remap_data pvhdata; - - BUG_ON(!pages); - - pvhdata.fgfn = mfn; - pvhdata.prot = prot; - pvhdata.domid = domid; - pvhdata.index = 0; - pvhdata.pages = pages; - err = apply_to_page_range(vma->vm_mm, addr, nr << PAGE_SHIFT, - xlate_map_pte_fn, &pvhdata); - flush_tlb_all(); - return err; -} -#endif - #define REMAP_BATCH_SIZE 16 struct remap_data { @@ -2564,8 +2475,8 @@ int xen_remap_domain_mfn_range(struct vm_area_struct *vma, if (xen_feature(XENFEAT_auto_translated_physmap)) { #ifdef CONFIG_XEN_PVH /* We need to update the local page tables and the xen HAP */ - return xlate_remap_gfn_range(vma, addr, mfn, nr, prot, - domid, pages); + return xen_xlate_remap_gfn_range(vma, addr, mfn, nr, prot, + domid, pages); #else return -EINVAL; #endif @@ -2609,22 +2520,7 @@ int xen_unmap_domain_mfn_range(struct vm_area_struct *vma, return 0; #ifdef CONFIG_XEN_PVH - while (numpgs--) { - /* - * The mmu has already cleaned up the process mmu - * resources at this point (lookup_address will return - * NULL). - */ - unsigned long pfn = page_to_pfn(pages[numpgs]); - - xlate_remove_from_p2m(pfn, 1); - } - /* - * We don't need to flush tlbs because as part of - * xlate_remove_from_p2m, the hypervisor will do tlb flushes - * after removing the p2m entries from the EPT/NPT - */ - return 0; + return xen_xlate_unmap_gfn_range(vma, numpgs, pages); #else return -EINVAL; #endif diff --git a/drivers/xen/Kconfig b/drivers/xen/Kconfig index b812462083fc..afc39ca5cc4f 100644 --- a/drivers/xen/Kconfig +++ b/drivers/xen/Kconfig @@ -253,4 +253,10 @@ config XEN_EFI def_bool y depends on X86_64 && EFI +config XEN_AUTO_XLATE + def_bool y + depends on ARM || ARM64 || XEN_PVHVM + help + Support for auto-translated physmap guests. + endmenu diff --git a/drivers/xen/Makefile b/drivers/xen/Makefile index 2ccd3592d41f..40edd1cbb60d 100644 --- a/drivers/xen/Makefile +++ b/drivers/xen/Makefile @@ -37,6 +37,7 @@ obj-$(CONFIG_XEN_ACPI_HOTPLUG_CPU) += xen-acpi-cpuhotplug.o obj-$(CONFIG_XEN_ACPI_PROCESSOR) += xen-acpi-processor.o obj-$(CONFIG_XEN_EFI) += efi.o obj-$(CONFIG_XEN_SCSI_BACKEND) += xen-scsiback.o +obj-$(CONFIG_XEN_AUTO_XLATE) += xlate_mmu.o xen-evtchn-y := evtchn.o xen-gntdev-y := gntdev.o xen-gntalloc-y := gntalloc.o diff --git a/drivers/xen/xlate_mmu.c b/drivers/xen/xlate_mmu.c new file mode 100644 index 000000000000..7724d90fc697 --- /dev/null +++ b/drivers/xen/xlate_mmu.c @@ -0,0 +1,133 @@ +/* + * MMU operations common to all auto-translated physmap guests. + * + * Copyright (C) 2015 Citrix Systems R&D Ltd. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation; or, when distributed + * separately from the Linux kernel or incorporated into other + * software packages, subject to the following license: + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this source file (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, modify, + * merge, publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +#include +#include + +#include +#include + +#include +#include +#include +#include + +/* map fgmfn of domid to lpfn in the current domain */ +static int map_foreign_page(unsigned long lpfn, unsigned long fgmfn, + unsigned int domid) +{ + int rc; + struct xen_add_to_physmap_range xatp = { + .domid = DOMID_SELF, + .foreign_domid = domid, + .size = 1, + .space = XENMAPSPACE_gmfn_foreign, + }; + xen_ulong_t idx = fgmfn; + xen_pfn_t gpfn = lpfn; + int err = 0; + + set_xen_guest_handle(xatp.idxs, &idx); + set_xen_guest_handle(xatp.gpfns, &gpfn); + set_xen_guest_handle(xatp.errs, &err); + + rc = HYPERVISOR_memory_op(XENMEM_add_to_physmap_range, &xatp); + return rc < 0 ? rc : err; +} + +struct remap_data { + xen_pfn_t fgmfn; /* foreign domain's gmfn */ + pgprot_t prot; + domid_t domid; + struct vm_area_struct *vma; + int index; + struct page **pages; + struct xen_remap_mfn_info *info; +}; + +static int remap_pte_fn(pte_t *ptep, pgtable_t token, unsigned long addr, + void *data) +{ + struct remap_data *info = data; + struct page *page = info->pages[info->index++]; + unsigned long pfn = page_to_pfn(page); + pte_t pte = pte_mkspecial(pfn_pte(pfn, info->prot)); + int rc; + + rc = map_foreign_page(pfn, info->fgmfn, info->domid); + if (rc < 0) + return rc; + set_pte_at(info->vma->vm_mm, addr, ptep, pte); + + return 0; +} + +int xen_xlate_remap_gfn_range(struct vm_area_struct *vma, + unsigned long addr, + xen_pfn_t gfn, int nr, + pgprot_t prot, unsigned domid, + struct page **pages) +{ + int err; + struct remap_data data; + + /* TBD: Batching, current sole caller only does page at a time */ + if (nr > 1) + return -EINVAL; + + data.fgmfn = gfn; + data.prot = prot; + data.domid = domid; + data.vma = vma; + data.index = 0; + data.pages = pages; + err = apply_to_page_range(vma->vm_mm, addr, nr << PAGE_SHIFT, + remap_pte_fn, &data); + return err; +} +EXPORT_SYMBOL_GPL(xen_xlate_remap_gfn_range); + +int xen_xlate_unmap_gfn_range(struct vm_area_struct *vma, + int nr, struct page **pages) +{ + int i; + + for (i = 0; i < nr; i++) { + struct xen_remove_from_physmap xrp; + unsigned long pfn; + + pfn = page_to_pfn(pages[i]); + + xrp.domid = DOMID_SELF; + xrp.gpfn = pfn; + (void)HYPERVISOR_memory_op(XENMEM_remove_from_physmap, &xrp); + } + return 0; +} +EXPORT_SYMBOL_GPL(xen_xlate_unmap_gfn_range); diff --git a/include/xen/xen-ops.h b/include/xen/xen-ops.h index 83338210ee04..9eb88a4512bd 100644 --- a/include/xen/xen-ops.h +++ b/include/xen/xen-ops.h @@ -34,6 +34,14 @@ int xen_remap_domain_mfn_range(struct vm_area_struct *vma, struct page **pages); int xen_unmap_domain_mfn_range(struct vm_area_struct *vma, int numpgs, struct page **pages); +int xen_xlate_remap_gfn_range(struct vm_area_struct *vma, + unsigned long addr, + xen_pfn_t gfn, int nr, + pgprot_t prot, + unsigned domid, + struct page **pages); +int xen_xlate_unmap_gfn_range(struct vm_area_struct *vma, + int nr, struct page **pages); bool xen_running_on_version_or_later(unsigned int major, unsigned int minor); -- cgit v1.2.3 From 4e8c0c8c4bf3a5b5c98046e146ab3884bf7a7d0e Mon Sep 17 00:00:00 2001 From: David Vrabel Date: Wed, 11 Mar 2015 14:49:57 +0000 Subject: xen/privcmd: improve performance of MMAPBATCH_V2 Make the IOCTL_PRIVCMD_MMAPBATCH_V2 (and older V1 version) map multiple frames at a time rather than one at a time, despite the pages being non-consecutive GFNs. xen_remap_foreign_mfn_array() is added which maps an array of GFNs (instead of a consecutive range of GFNs). Since per-frame errors are returned in an array, privcmd must set the MMAPBATCH_V1 error bits as part of the "report errors" phase, after all the frames are mapped. Migrate times are significantly improved (when using a PV toolstack domain). For example, for an idle 12 GiB PV guest: Before After real 0m38.179s 0m26.868s user 0m15.096s 0m13.652s sys 0m28.988s 0m18.732s Signed-off-by: David Vrabel Reviewed-by: Stefano Stabellini --- arch/arm/xen/enlighten.c | 20 ++++++-- arch/x86/xen/mmu.c | 101 ++++++++++++++++++++++++++++++++-------- drivers/xen/privcmd.c | 117 +++++++++++++++++++++++++++++++++-------------- drivers/xen/xlate_mmu.c | 46 +++++++++++-------- include/xen/xen-ops.h | 45 ++++++++++++++++-- 5 files changed, 249 insertions(+), 80 deletions(-) (limited to 'include') diff --git a/arch/arm/xen/enlighten.c b/arch/arm/xen/enlighten.c index 5c04389fc9ef..224081ccc92f 100644 --- a/arch/arm/xen/enlighten.c +++ b/arch/arm/xen/enlighten.c @@ -53,15 +53,27 @@ EXPORT_SYMBOL_GPL(xen_platform_pci_unplug); static __read_mostly int xen_events_irq = -1; -int xen_remap_domain_mfn_range(struct vm_area_struct *vma, +int xen_remap_domain_mfn_array(struct vm_area_struct *vma, unsigned long addr, - xen_pfn_t mfn, int nr, - pgprot_t prot, unsigned domid, + xen_pfn_t *mfn, int nr, + int *err_ptr, pgprot_t prot, + unsigned domid, struct page **pages) { - return xen_xlate_remap_gfn_range(vma, addr, mfn, nr, + return xen_xlate_remap_gfn_array(vma, addr, mfn, nr, err_ptr, prot, domid, pages); } +EXPORT_SYMBOL_GPL(xen_remap_domain_mfn_array); + +/* Not used by XENFEAT_auto_translated guests. */ +int xen_remap_domain_mfn_range(struct vm_area_struct *vma, + unsigned long addr, + xen_pfn_t mfn, int nr, + pgprot_t prot, unsigned domid, + struct page **pages) +{ + return -ENOSYS; +} EXPORT_SYMBOL_GPL(xen_remap_domain_mfn_range); int xen_unmap_domain_mfn_range(struct vm_area_struct *vma, diff --git a/arch/x86/xen/mmu.c b/arch/x86/xen/mmu.c index 3d536a56ddf1..29b3be230ede 100644 --- a/arch/x86/xen/mmu.c +++ b/arch/x86/xen/mmu.c @@ -2439,7 +2439,8 @@ void __init xen_hvm_init_mmu_ops(void) #define REMAP_BATCH_SIZE 16 struct remap_data { - unsigned long mfn; + xen_pfn_t *mfn; + bool contiguous; pgprot_t prot; struct mmu_update *mmu_update; }; @@ -2448,7 +2449,14 @@ static int remap_area_mfn_pte_fn(pte_t *ptep, pgtable_t token, unsigned long addr, void *data) { struct remap_data *rmd = data; - pte_t pte = pte_mkspecial(mfn_pte(rmd->mfn++, rmd->prot)); + pte_t pte = pte_mkspecial(mfn_pte(*rmd->mfn, rmd->prot)); + + /* If we have a contigious range, just update the mfn itself, + else update pointer to be "next mfn". */ + if (rmd->contiguous) + (*rmd->mfn)++; + else + rmd->mfn++; rmd->mmu_update->ptr = virt_to_machine(ptep).maddr; rmd->mmu_update->val = pte_val_ma(pte); @@ -2457,26 +2465,26 @@ static int remap_area_mfn_pte_fn(pte_t *ptep, pgtable_t token, return 0; } -int xen_remap_domain_mfn_range(struct vm_area_struct *vma, - unsigned long addr, - xen_pfn_t mfn, int nr, - pgprot_t prot, unsigned domid, - struct page **pages) - +static int do_remap_mfn(struct vm_area_struct *vma, + unsigned long addr, + xen_pfn_t *mfn, int nr, + int *err_ptr, pgprot_t prot, + unsigned domid, + struct page **pages) { + int err = 0; struct remap_data rmd; struct mmu_update mmu_update[REMAP_BATCH_SIZE]; - int batch; unsigned long range; - int err = 0; + int mapped = 0; BUG_ON(!((vma->vm_flags & (VM_PFNMAP | VM_IO)) == (VM_PFNMAP | VM_IO))); if (xen_feature(XENFEAT_auto_translated_physmap)) { #ifdef CONFIG_XEN_PVH /* We need to update the local page tables and the xen HAP */ - return xen_xlate_remap_gfn_range(vma, addr, mfn, nr, prot, - domid, pages); + return xen_xlate_remap_gfn_array(vma, addr, mfn, nr, err_ptr, + prot, domid, pages); #else return -EINVAL; #endif @@ -2484,9 +2492,15 @@ int xen_remap_domain_mfn_range(struct vm_area_struct *vma, rmd.mfn = mfn; rmd.prot = prot; + /* We use the err_ptr to indicate if there we are doing a contigious + * mapping or a discontigious mapping. */ + rmd.contiguous = !err_ptr; while (nr) { - batch = min(REMAP_BATCH_SIZE, nr); + int index = 0; + int done = 0; + int batch = min(REMAP_BATCH_SIZE, nr); + int batch_left = batch; range = (unsigned long)batch << PAGE_SHIFT; rmd.mmu_update = mmu_update; @@ -2495,23 +2509,72 @@ int xen_remap_domain_mfn_range(struct vm_area_struct *vma, if (err) goto out; - err = HYPERVISOR_mmu_update(mmu_update, batch, NULL, domid); - if (err < 0) - goto out; + /* We record the error for each page that gives an error, but + * continue mapping until the whole set is done */ + do { + int i; + + err = HYPERVISOR_mmu_update(&mmu_update[index], + batch_left, &done, domid); + + /* + * @err_ptr may be the same buffer as @mfn, so + * only clear it after each chunk of @mfn is + * used. + */ + if (err_ptr) { + for (i = index; i < index + done; i++) + err_ptr[i] = 0; + } + if (err < 0) { + if (!err_ptr) + goto out; + err_ptr[i] = err; + done++; /* Skip failed frame. */ + } else + mapped += done; + batch_left -= done; + index += done; + } while (batch_left); nr -= batch; addr += range; + if (err_ptr) + err_ptr += batch; } - - err = 0; out: xen_flush_tlb_all(); - return err; + return err < 0 ? err : mapped; +} + +int xen_remap_domain_mfn_range(struct vm_area_struct *vma, + unsigned long addr, + xen_pfn_t mfn, int nr, + pgprot_t prot, unsigned domid, + struct page **pages) +{ + return do_remap_mfn(vma, addr, &mfn, nr, NULL, prot, domid, pages); } EXPORT_SYMBOL_GPL(xen_remap_domain_mfn_range); +int xen_remap_domain_mfn_array(struct vm_area_struct *vma, + unsigned long addr, + xen_pfn_t *mfn, int nr, + int *err_ptr, pgprot_t prot, + unsigned domid, struct page **pages) +{ + /* We BUG_ON because it's a programmer error to pass a NULL err_ptr, + * and the consequences later is quite hard to detect what the actual + * cause of "wrong memory was mapped in". + */ + BUG_ON(err_ptr == NULL); + return do_remap_mfn(vma, addr, mfn, nr, err_ptr, prot, domid, pages); +} +EXPORT_SYMBOL_GPL(xen_remap_domain_mfn_array); + + /* Returns: 0 success */ int xen_unmap_domain_mfn_range(struct vm_area_struct *vma, int numpgs, struct page **pages) diff --git a/drivers/xen/privcmd.c b/drivers/xen/privcmd.c index 59ac71c4a043..5a296161d843 100644 --- a/drivers/xen/privcmd.c +++ b/drivers/xen/privcmd.c @@ -159,6 +159,40 @@ static int traverse_pages(unsigned nelem, size_t size, return ret; } +/* + * Similar to traverse_pages, but use each page as a "block" of + * data to be processed as one unit. + */ +static int traverse_pages_block(unsigned nelem, size_t size, + struct list_head *pos, + int (*fn)(void *data, int nr, void *state), + void *state) +{ + void *pagedata; + unsigned pageidx; + int ret = 0; + + BUG_ON(size > PAGE_SIZE); + + pageidx = PAGE_SIZE; + + while (nelem) { + int nr = (PAGE_SIZE/size); + struct page *page; + if (nr > nelem) + nr = nelem; + pos = pos->next; + page = list_entry(pos, struct page, lru); + pagedata = page_address(page); + ret = (*fn)(pagedata, nr, state); + if (ret) + break; + nelem -= nr; + } + + return ret; +} + struct mmap_mfn_state { unsigned long va; struct vm_area_struct *vma; @@ -274,39 +308,25 @@ struct mmap_batch_state { /* auto translated dom0 note: if domU being created is PV, then mfn is * mfn(addr on bus). If it's auto xlated, then mfn is pfn (input to HAP). */ -static int mmap_batch_fn(void *data, void *state) +static int mmap_batch_fn(void *data, int nr, void *state) { xen_pfn_t *mfnp = data; struct mmap_batch_state *st = state; struct vm_area_struct *vma = st->vma; struct page **pages = vma->vm_private_data; - struct page *cur_page = NULL; + struct page **cur_pages = NULL; int ret; if (xen_feature(XENFEAT_auto_translated_physmap)) - cur_page = pages[st->index++]; + cur_pages = &pages[st->index]; - ret = xen_remap_domain_mfn_range(st->vma, st->va & PAGE_MASK, *mfnp, 1, - st->vma->vm_page_prot, st->domain, - &cur_page); + BUG_ON(nr < 0); + ret = xen_remap_domain_mfn_array(st->vma, st->va & PAGE_MASK, mfnp, nr, + (int *)mfnp, st->vma->vm_page_prot, + st->domain, cur_pages); - /* Store error code for second pass. */ - if (st->version == 1) { - if (ret < 0) { - /* - * V1 encodes the error codes in the 32bit top nibble of the - * mfn (with its known limitations vis-a-vis 64 bit callers). - */ - *mfnp |= (ret == -ENOENT) ? - PRIVCMD_MMAPBATCH_PAGED_ERROR : - PRIVCMD_MMAPBATCH_MFN_ERROR; - } - } else { /* st->version == 2 */ - *((int *) mfnp) = ret; - } - - /* And see if it affects the global_error. */ - if (ret < 0) { + /* Adjust the global_error? */ + if (ret != nr) { if (ret == -ENOENT) st->global_error = -ENOENT; else { @@ -315,23 +335,35 @@ static int mmap_batch_fn(void *data, void *state) st->global_error = 1; } } - st->va += PAGE_SIZE; + st->va += PAGE_SIZE * nr; + st->index += nr; return 0; } -static int mmap_return_errors(void *data, void *state) +static int mmap_return_error(int err, struct mmap_batch_state *st) { - struct mmap_batch_state *st = state; + int ret; if (st->version == 1) { - xen_pfn_t mfnp = *((xen_pfn_t *) data); - if (mfnp & PRIVCMD_MMAPBATCH_MFN_ERROR) - return __put_user(mfnp, st->user_mfn++); - else + if (err) { + xen_pfn_t mfn; + + ret = get_user(mfn, st->user_mfn); + if (ret < 0) + return ret; + /* + * V1 encodes the error codes in the 32bit top + * nibble of the mfn (with its known + * limitations vis-a-vis 64 bit callers). + */ + mfn |= (err == -ENOENT) ? + PRIVCMD_MMAPBATCH_PAGED_ERROR : + PRIVCMD_MMAPBATCH_MFN_ERROR; + return __put_user(mfn, st->user_mfn++); + } else st->user_mfn++; } else { /* st->version == 2 */ - int err = *((int *) data); if (err) return __put_user(err, st->user_err++); else @@ -341,6 +373,21 @@ static int mmap_return_errors(void *data, void *state) return 0; } +static int mmap_return_errors(void *data, int nr, void *state) +{ + struct mmap_batch_state *st = state; + int *errs = data; + int i; + int ret; + + for (i = 0; i < nr; i++) { + ret = mmap_return_error(errs[i], st); + if (ret < 0) + return ret; + } + return 0; +} + /* Allocate pfns that are then mapped with gmfns from foreign domid. Update * the vma with the page info to use later. * Returns: 0 if success, otherwise -errno @@ -472,8 +519,8 @@ static long privcmd_ioctl_mmap_batch(void __user *udata, int version) state.version = version; /* mmap_batch_fn guarantees ret == 0 */ - BUG_ON(traverse_pages(m.num, sizeof(xen_pfn_t), - &pagelist, mmap_batch_fn, &state)); + BUG_ON(traverse_pages_block(m.num, sizeof(xen_pfn_t), + &pagelist, mmap_batch_fn, &state)); up_write(&mm->mmap_sem); @@ -481,8 +528,8 @@ static long privcmd_ioctl_mmap_batch(void __user *udata, int version) /* Write back errors in second pass. */ state.user_mfn = (xen_pfn_t *)m.arr; state.user_err = m.err; - ret = traverse_pages(m.num, sizeof(xen_pfn_t), - &pagelist, mmap_return_errors, &state); + ret = traverse_pages_block(m.num, sizeof(xen_pfn_t), + &pagelist, mmap_return_errors, &state); } else ret = 0; diff --git a/drivers/xen/xlate_mmu.c b/drivers/xen/xlate_mmu.c index 7724d90fc697..58a5389aec89 100644 --- a/drivers/xen/xlate_mmu.c +++ b/drivers/xen/xlate_mmu.c @@ -62,13 +62,15 @@ static int map_foreign_page(unsigned long lpfn, unsigned long fgmfn, } struct remap_data { - xen_pfn_t fgmfn; /* foreign domain's gmfn */ + xen_pfn_t *fgmfn; /* foreign domain's gmfn */ pgprot_t prot; domid_t domid; struct vm_area_struct *vma; int index; struct page **pages; struct xen_remap_mfn_info *info; + int *err_ptr; + int mapped; }; static int remap_pte_fn(pte_t *ptep, pgtable_t token, unsigned long addr, @@ -80,38 +82,46 @@ static int remap_pte_fn(pte_t *ptep, pgtable_t token, unsigned long addr, pte_t pte = pte_mkspecial(pfn_pte(pfn, info->prot)); int rc; - rc = map_foreign_page(pfn, info->fgmfn, info->domid); - if (rc < 0) - return rc; - set_pte_at(info->vma->vm_mm, addr, ptep, pte); + rc = map_foreign_page(pfn, *info->fgmfn, info->domid); + *info->err_ptr++ = rc; + if (!rc) { + set_pte_at(info->vma->vm_mm, addr, ptep, pte); + info->mapped++; + } + info->fgmfn++; return 0; } -int xen_xlate_remap_gfn_range(struct vm_area_struct *vma, +int xen_xlate_remap_gfn_array(struct vm_area_struct *vma, unsigned long addr, - xen_pfn_t gfn, int nr, - pgprot_t prot, unsigned domid, + xen_pfn_t *mfn, int nr, + int *err_ptr, pgprot_t prot, + unsigned domid, struct page **pages) { int err; struct remap_data data; + unsigned long range = nr << PAGE_SHIFT; - /* TBD: Batching, current sole caller only does page at a time */ - if (nr > 1) - return -EINVAL; + /* Kept here for the purpose of making sure code doesn't break + x86 PVOPS */ + BUG_ON(!((vma->vm_flags & (VM_PFNMAP | VM_IO)) == (VM_PFNMAP | VM_IO))); - data.fgmfn = gfn; - data.prot = prot; + data.fgmfn = mfn; + data.prot = prot; data.domid = domid; - data.vma = vma; - data.index = 0; + data.vma = vma; data.pages = pages; - err = apply_to_page_range(vma->vm_mm, addr, nr << PAGE_SHIFT, + data.index = 0; + data.err_ptr = err_ptr; + data.mapped = 0; + + err = apply_to_page_range(vma->vm_mm, addr, range, remap_pte_fn, &data); - return err; + return err < 0 ? err : data.mapped; } -EXPORT_SYMBOL_GPL(xen_xlate_remap_gfn_range); +EXPORT_SYMBOL_GPL(xen_xlate_remap_gfn_array); int xen_xlate_unmap_gfn_range(struct vm_area_struct *vma, int nr, struct page **pages) diff --git a/include/xen/xen-ops.h b/include/xen/xen-ops.h index 9eb88a4512bd..c643e6a94c9a 100644 --- a/include/xen/xen-ops.h +++ b/include/xen/xen-ops.h @@ -27,17 +27,54 @@ int xen_create_contiguous_region(phys_addr_t pstart, unsigned int order, void xen_destroy_contiguous_region(phys_addr_t pstart, unsigned int order); struct vm_area_struct; + +/* + * xen_remap_domain_mfn_array() - map an array of foreign frames + * @vma: VMA to map the pages into + * @addr: Address at which to map the pages + * @gfn: Array of GFNs to map + * @nr: Number entries in the GFN array + * @err_ptr: Returns per-GFN error status. + * @prot: page protection mask + * @domid: Domain owning the pages + * @pages: Array of pages if this domain has an auto-translated physmap + * + * @gfn and @err_ptr may point to the same buffer, the GFNs will be + * overwritten by the error codes after they are mapped. + * + * Returns the number of successfully mapped frames, or a -ve error + * code. + */ +int xen_remap_domain_mfn_array(struct vm_area_struct *vma, + unsigned long addr, + xen_pfn_t *gfn, int nr, + int *err_ptr, pgprot_t prot, + unsigned domid, + struct page **pages); + +/* xen_remap_domain_mfn_range() - map a range of foreign frames + * @vma: VMA to map the pages into + * @addr: Address at which to map the pages + * @gfn: First GFN to map. + * @nr: Number frames to map + * @prot: page protection mask + * @domid: Domain owning the pages + * @pages: Array of pages if this domain has an auto-translated physmap + * + * Returns the number of successfully mapped frames, or a -ve error + * code. + */ int xen_remap_domain_mfn_range(struct vm_area_struct *vma, unsigned long addr, - xen_pfn_t mfn, int nr, + xen_pfn_t gfn, int nr, pgprot_t prot, unsigned domid, struct page **pages); int xen_unmap_domain_mfn_range(struct vm_area_struct *vma, int numpgs, struct page **pages); -int xen_xlate_remap_gfn_range(struct vm_area_struct *vma, +int xen_xlate_remap_gfn_array(struct vm_area_struct *vma, unsigned long addr, - xen_pfn_t gfn, int nr, - pgprot_t prot, + xen_pfn_t *gfn, int nr, + int *err_ptr, pgprot_t prot, unsigned domid, struct page **pages); int xen_xlate_unmap_gfn_range(struct vm_area_struct *vma, -- cgit v1.2.3 From 1aa15f4e00c9be2c7e4c3bd36886ed65d25cc075 Mon Sep 17 00:00:00 2001 From: Alexandre Belloni Date: Thu, 12 Mar 2015 13:07:26 +0100 Subject: mfd: syscon: Add atmel system timer registers definition AT91RM920 has a memory range reserved for timer and watchdog configuration. Expose those registers so that drivers can make use of the system timer syscon declared in at91 DTs. Signed-off-by: Alexandre Belloni Acked-by: Lee Jones Signed-off-by: Nicolas Ferre --- include/linux/mfd/syscon/atmel-st.h | 49 +++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 include/linux/mfd/syscon/atmel-st.h (limited to 'include') diff --git a/include/linux/mfd/syscon/atmel-st.h b/include/linux/mfd/syscon/atmel-st.h new file mode 100644 index 000000000000..8acf1ec1fa32 --- /dev/null +++ b/include/linux/mfd/syscon/atmel-st.h @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2005 Ivan Kokshaysky + * Copyright (C) SAN People + * + * System Timer (ST) - System peripherals registers. + * Based on AT91RM9200 datasheet revision E. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#ifndef _LINUX_MFD_SYSCON_ATMEL_ST_H +#define _LINUX_MFD_SYSCON_ATMEL_ST_H + +#include + +#define AT91_ST_CR 0x00 /* Control Register */ +#define AT91_ST_WDRST BIT(0) /* Watchdog Timer Restart */ + +#define AT91_ST_PIMR 0x04 /* Period Interval Mode Register */ +#define AT91_ST_PIV 0xffff /* Period Interval Value */ + +#define AT91_ST_WDMR 0x08 /* Watchdog Mode Register */ +#define AT91_ST_WDV 0xffff /* Watchdog Counter Value */ +#define AT91_ST_RSTEN BIT(16) /* Reset Enable */ +#define AT91_ST_EXTEN BIT(17) /* External Signal Assertion Enable */ + +#define AT91_ST_RTMR 0x0c /* Real-time Mode Register */ +#define AT91_ST_RTPRES 0xffff /* Real-time Prescalar Value */ + +#define AT91_ST_SR 0x10 /* Status Register */ +#define AT91_ST_PITS BIT(0) /* Period Interval Timer Status */ +#define AT91_ST_WDOVF BIT(1) /* Watchdog Overflow */ +#define AT91_ST_RTTINC BIT(2) /* Real-time Timer Increment */ +#define AT91_ST_ALMS BIT(3) /* Alarm Status */ + +#define AT91_ST_IER 0x14 /* Interrupt Enable Register */ +#define AT91_ST_IDR 0x18 /* Interrupt Disable Register */ +#define AT91_ST_IMR 0x1c /* Interrupt Mask Register */ + +#define AT91_ST_RTAR 0x20 /* Real-time Alarm Register */ +#define AT91_ST_ALMV 0xfffff /* Alarm Value */ + +#define AT91_ST_CRTR 0x24 /* Current Real-time Register */ +#define AT91_ST_CRTV 0xfffff /* Current Real-Time Value */ + +#endif /* _LINUX_MFD_SYSCON_ATMEL_ST_H */ -- cgit v1.2.3 From 3b62286d0ef785815994e2558e8cfb686597b0cd Mon Sep 17 00:00:00 2001 From: Jarkko Nikula Date: Mon, 16 Mar 2015 09:37:24 +0200 Subject: dmaengine: Remove FSF mailing addresses Free Software Foundation mailing address has been moved in the past and some of the addresses here are outdated. Remove them from file headers since the COPYING file in the kernel sources includes it. Signed-off-by: Jarkko Nikula Signed-off-by: Vinod Koul --- drivers/dma/amba-pl08x.c | 4 ---- drivers/dma/dma-jz4740.c | 4 ---- drivers/dma/dmaengine.c | 4 ---- drivers/dma/intel_mid_dma.c | 4 ---- drivers/dma/intel_mid_dma_regs.h | 4 ---- drivers/dma/ioat/dca.c | 4 ---- drivers/dma/ioat/dma.c | 4 ---- drivers/dma/ioat/dma.h | 4 ---- drivers/dma/ioat/dma_v2.c | 4 ---- drivers/dma/ioat/dma_v2.h | 4 ---- drivers/dma/ioat/dma_v3.c | 4 ---- drivers/dma/ioat/hw.h | 4 ---- drivers/dma/ioat/pci.c | 4 ---- drivers/dma/ioat/registers.h | 4 ---- drivers/dma/iop-adma.c | 4 ---- drivers/dma/mpc512x_dma.c | 4 ---- drivers/dma/mv_xor.c | 4 ---- drivers/dma/mv_xor.h | 4 ---- drivers/dma/ppc4xx/adma.c | 4 ---- include/linux/dmaengine.h | 4 ---- 20 files changed, 80 deletions(-) (limited to 'include') diff --git a/drivers/dma/amba-pl08x.c b/drivers/dma/amba-pl08x.c index 4a5fd245014e..171e768888c0 100644 --- a/drivers/dma/amba-pl08x.c +++ b/drivers/dma/amba-pl08x.c @@ -15,10 +15,6 @@ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., 59 - * Temple Place - Suite 330, Boston, MA 02111-1307, USA. - * * The full GNU General Public License is in this distribution in the file * called COPYING. * diff --git a/drivers/dma/dma-jz4740.c b/drivers/dma/dma-jz4740.c index f6a4c4270dcd..248a6ee1775b 100644 --- a/drivers/dma/dma-jz4740.c +++ b/drivers/dma/dma-jz4740.c @@ -7,10 +7,6 @@ * Free Software Foundation; either version 2 of the License, or (at your * option) any later version. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 675 Mass Ave, Cambridge, MA 02139, USA. - * */ #include diff --git a/drivers/dma/dmaengine.c b/drivers/dma/dmaengine.c index 344b0ac6d985..24967c89f5d4 100644 --- a/drivers/dma/dmaengine.c +++ b/drivers/dma/dmaengine.c @@ -11,10 +11,6 @@ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., 59 - * Temple Place - Suite 330, Boston, MA 02111-1307, USA. - * * The full GNU General Public License is included in this distribution in the * file called COPYING. */ diff --git a/drivers/dma/intel_mid_dma.c b/drivers/dma/intel_mid_dma.c index 5aaead9b56f7..c17e18b909b6 100644 --- a/drivers/dma/intel_mid_dma.c +++ b/drivers/dma/intel_mid_dma.c @@ -15,10 +15,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. - * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * * diff --git a/drivers/dma/intel_mid_dma_regs.h b/drivers/dma/intel_mid_dma_regs.h index 17b42192ea58..ebdd567dac1e 100644 --- a/drivers/dma/intel_mid_dma_regs.h +++ b/drivers/dma/intel_mid_dma_regs.h @@ -14,10 +14,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. - * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * * diff --git a/drivers/dma/ioat/dca.c b/drivers/dma/ioat/dca.c index 3b55bb8d969a..ea1e107ae884 100644 --- a/drivers/dma/ioat/dca.c +++ b/drivers/dma/ioat/dca.c @@ -11,10 +11,6 @@ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. - * * The full GNU General Public License is included in this distribution in * the file called "COPYING". * diff --git a/drivers/dma/ioat/dma.c b/drivers/dma/ioat/dma.c index 940c1502a8b5..ee0aa9f4ccfa 100644 --- a/drivers/dma/ioat/dma.c +++ b/drivers/dma/ioat/dma.c @@ -11,10 +11,6 @@ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. - * * The full GNU General Public License is included in this distribution in * the file called "COPYING". * diff --git a/drivers/dma/ioat/dma.h b/drivers/dma/ioat/dma.h index d63f68b1aa35..30f5c7eede16 100644 --- a/drivers/dma/ioat/dma.h +++ b/drivers/dma/ioat/dma.h @@ -11,10 +11,6 @@ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., 59 - * Temple Place - Suite 330, Boston, MA 02111-1307, USA. - * * The full GNU General Public License is included in this distribution in the * file called COPYING. */ diff --git a/drivers/dma/ioat/dma_v2.c b/drivers/dma/ioat/dma_v2.c index 695483e6be32..69c7dfcad023 100644 --- a/drivers/dma/ioat/dma_v2.c +++ b/drivers/dma/ioat/dma_v2.c @@ -11,10 +11,6 @@ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. - * * The full GNU General Public License is included in this distribution in * the file called "COPYING". * diff --git a/drivers/dma/ioat/dma_v2.h b/drivers/dma/ioat/dma_v2.h index 470292767e68..bf24ebe874b0 100644 --- a/drivers/dma/ioat/dma_v2.h +++ b/drivers/dma/ioat/dma_v2.h @@ -11,10 +11,6 @@ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., 59 - * Temple Place - Suite 330, Boston, MA 02111-1307, USA. - * * The full GNU General Public License is included in this distribution in the * file called COPYING. */ diff --git a/drivers/dma/ioat/dma_v3.c b/drivers/dma/ioat/dma_v3.c index 77a6dcf25b98..3d19a3187a77 100644 --- a/drivers/dma/ioat/dma_v3.c +++ b/drivers/dma/ioat/dma_v3.c @@ -15,10 +15,6 @@ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. - * * The full GNU General Public License is included in this distribution in * the file called "COPYING". * diff --git a/drivers/dma/ioat/hw.h b/drivers/dma/ioat/hw.h index 02177ecf09f8..a3e731edce57 100644 --- a/drivers/dma/ioat/hw.h +++ b/drivers/dma/ioat/hw.h @@ -11,10 +11,6 @@ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., 59 - * Temple Place - Suite 330, Boston, MA 02111-1307, USA. - * * The full GNU General Public License is included in this distribution in the * file called COPYING. */ diff --git a/drivers/dma/ioat/pci.c b/drivers/dma/ioat/pci.c index 5501eb072d69..76f0dc688a19 100644 --- a/drivers/dma/ioat/pci.c +++ b/drivers/dma/ioat/pci.c @@ -11,10 +11,6 @@ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. - * * The full GNU General Public License is included in this distribution in * the file called "COPYING". * diff --git a/drivers/dma/ioat/registers.h b/drivers/dma/ioat/registers.h index 2f1cfa0f1f47..909352f74c89 100644 --- a/drivers/dma/ioat/registers.h +++ b/drivers/dma/ioat/registers.h @@ -11,10 +11,6 @@ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., 59 - * Temple Place - Suite 330, Boston, MA 02111-1307, USA. - * * The full GNU General Public License is included in this distribution in the * file called COPYING. */ diff --git a/drivers/dma/iop-adma.c b/drivers/dma/iop-adma.c index 263d9f6a207e..998826854fdd 100644 --- a/drivers/dma/iop-adma.c +++ b/drivers/dma/iop-adma.c @@ -11,10 +11,6 @@ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. - * */ /* diff --git a/drivers/dma/mpc512x_dma.c b/drivers/dma/mpc512x_dma.c index 57d2457545f3..c66a82ea2559 100644 --- a/drivers/dma/mpc512x_dma.c +++ b/drivers/dma/mpc512x_dma.c @@ -21,10 +21,6 @@ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., 59 - * Temple Place - Suite 330, Boston, MA 02111-1307, USA. - * * The full GNU General Public License is included in this distribution in the * file called COPYING. */ diff --git a/drivers/dma/mv_xor.c b/drivers/dma/mv_xor.c index b03e8137b918..5ef567ee335b 100644 --- a/drivers/dma/mv_xor.c +++ b/drivers/dma/mv_xor.c @@ -10,10 +10,6 @@ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. */ #include diff --git a/drivers/dma/mv_xor.h b/drivers/dma/mv_xor.h index 78edc7e44569..91958dba39a2 100644 --- a/drivers/dma/mv_xor.h +++ b/drivers/dma/mv_xor.h @@ -9,10 +9,6 @@ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef MV_XOR_H diff --git a/drivers/dma/ppc4xx/adma.c b/drivers/dma/ppc4xx/adma.c index fa764a39cd36..9217f893b0d1 100644 --- a/drivers/dma/ppc4xx/adma.c +++ b/drivers/dma/ppc4xx/adma.c @@ -16,10 +16,6 @@ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., 59 - * Temple Place - Suite 330, Boston, MA 02111-1307, USA. - * * The full GNU General Public License is included in this distribution in the * file called COPYING. */ diff --git a/include/linux/dmaengine.h b/include/linux/dmaengine.h index 2bff9abc162a..ad419757241f 100644 --- a/include/linux/dmaengine.h +++ b/include/linux/dmaengine.h @@ -11,10 +11,6 @@ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., 59 - * Temple Place - Suite 330, Boston, MA 02111-1307, USA. - * * The full GNU General Public License is included in this distribution in the * file called COPYING. */ -- cgit v1.2.3 From 1d0ab253872cdd3d8e7913f59c266c7fd01771d0 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Sun, 15 Mar 2015 21:12:12 -0700 Subject: net: add sk_fullsock() helper We have many places where we want to check if a socket is not a timewait or request socket. Use a helper to avoid hard coding this. Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- include/net/sock.h | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'include') diff --git a/include/net/sock.h b/include/net/sock.h index f10832ca2e90..e0360f5a53e9 100644 --- a/include/net/sock.h +++ b/include/net/sock.h @@ -67,6 +67,7 @@ #include #include #include +#include #include struct cgroup; @@ -2218,6 +2219,14 @@ static inline struct sock *skb_steal_sock(struct sk_buff *skb) return NULL; } +/* This helper checks if a socket is a full socket, + * ie _not_ a timewait or request socket. + */ +static inline bool sk_fullsock(const struct sock *sk) +{ + return (1 << sk->sk_state) & ~(TCPF_TIME_WAIT | TCPF_NEW_SYN_RECV); +} + void sock_enable_timestamp(struct sock *sk, int flag); int sock_get_timestamp(struct sock *, struct timeval __user *); int sock_get_timestampns(struct sock *, struct timespec __user *); -- cgit v1.2.3 From 13854e5a60461daee08ce99842b7f4d37553d911 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Sun, 15 Mar 2015 21:12:16 -0700 Subject: inet: add proper refcounting to request sock reqsk_put() is the generic function that should be used to release a refcount (and automatically call reqsk_free()) reqsk_free() might be called if refcount is known to be 0 or undefined. refcnt is set to one in inet_csk_reqsk_queue_add() As request socks are not yet in global ehash table, I added temporary debugging checks in reqsk_put() and reqsk_free() Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- include/net/inet_connection_sock.h | 5 +++++ include/net/inet_sock.h | 5 +++++ include/net/request_sock.h | 13 +++++++------ net/core/request_sock.c | 4 ++-- net/ipv4/inet_connection_sock.c | 8 ++++---- net/ipv4/syncookies.c | 10 +++++----- net/ipv4/tcp_fastopen.c | 2 +- 7 files changed, 29 insertions(+), 18 deletions(-) (limited to 'include') diff --git a/include/net/inet_connection_sock.h b/include/net/inet_connection_sock.h index b9a6b0a94cc6..191feec60205 100644 --- a/include/net/inet_connection_sock.h +++ b/include/net/inet_connection_sock.h @@ -275,6 +275,11 @@ static inline void inet_csk_reqsk_queue_add(struct sock *sk, struct sock *child) { reqsk_queue_add(&inet_csk(sk)->icsk_accept_queue, req, sk, child); + /* before letting lookups find us, make sure all req fields + * are committed to memory. + */ + smp_wmb(); + atomic_set(&req->rsk_refcnt, 1); } void inet_csk_reqsk_queue_hash_add(struct sock *sk, struct request_sock *req, diff --git a/include/net/inet_sock.h b/include/net/inet_sock.h index b3053fdd871e..3d8c09abb097 100644 --- a/include/net/inet_sock.h +++ b/include/net/inet_sock.h @@ -255,6 +255,11 @@ static inline struct request_sock *inet_reqsk_alloc(struct request_sock_ops *ops ireq->opt = NULL; atomic64_set(&ireq->ir_cookie, 0); ireq->ireq_state = TCP_NEW_SYN_RECV; + + /* Following is temporary. It is coupled with debugging + * helpers in reqsk_put() & reqsk_free() + */ + atomic_set(&ireq->ireq_refcnt, 0); } return req; diff --git a/include/net/request_sock.h b/include/net/request_sock.h index 3275cf31f731..56dc2faba47e 100644 --- a/include/net/request_sock.h +++ b/include/net/request_sock.h @@ -82,19 +82,20 @@ static inline struct request_sock *inet_reqsk(struct sock *sk) return (struct request_sock *)sk; } -static inline void __reqsk_free(struct request_sock *req) -{ - kmem_cache_free(req->rsk_ops->slab, req); -} - static inline void reqsk_free(struct request_sock *req) { + /* temporary debugging */ + WARN_ON_ONCE(atomic_read(&req->rsk_refcnt) != 0); + req->rsk_ops->destructor(req); - __reqsk_free(req); + kmem_cache_free(req->rsk_ops->slab, req); } static inline void reqsk_put(struct request_sock *req) { + /* temporary debugging, until req sock are put into ehash table */ + WARN_ON_ONCE(atomic_read(&req->rsk_refcnt) != 1); + if (atomic_dec_and_test(&req->rsk_refcnt)) reqsk_free(req); } diff --git a/net/core/request_sock.c b/net/core/request_sock.c index 04db318e6218..e910317ef6d9 100644 --- a/net/core/request_sock.c +++ b/net/core/request_sock.c @@ -103,7 +103,7 @@ void reqsk_queue_destroy(struct request_sock_queue *queue) while ((req = lopt->syn_table[i]) != NULL) { lopt->syn_table[i] = req->dl_next; lopt->qlen--; - reqsk_free(req); + reqsk_put(req); } } } @@ -180,7 +180,7 @@ void reqsk_fastopen_remove(struct sock *sk, struct request_sock *req, */ spin_unlock_bh(&fastopenq->lock); sock_put(lsk); - reqsk_free(req); + reqsk_put(req); return; } /* Wait for 60secs before removing a req that has triggered RST. diff --git a/net/ipv4/inet_connection_sock.c b/net/ipv4/inet_connection_sock.c index 34581f928afa..3390ba6f96b2 100644 --- a/net/ipv4/inet_connection_sock.c +++ b/net/ipv4/inet_connection_sock.c @@ -340,7 +340,7 @@ struct sock *inet_csk_accept(struct sock *sk, int flags, int *err) out: release_sock(sk); if (req) - __reqsk_free(req); + reqsk_put(req); return newsk; out_err: newsk = NULL; @@ -635,7 +635,7 @@ void inet_csk_reqsk_queue_prune(struct sock *parent, /* Drop this request */ inet_csk_reqsk_queue_unlink(parent, req, reqp); reqsk_queue_removed(queue, req); - reqsk_free(req); + reqsk_put(req); continue; } reqp = &req->dl_next; @@ -837,7 +837,7 @@ void inet_csk_listen_stop(struct sock *sk) sock_put(child); sk_acceptq_removed(sk); - __reqsk_free(req); + reqsk_put(req); } if (queue->fastopenq != NULL) { /* Free all the reqs queued in rskq_rst_head. */ @@ -847,7 +847,7 @@ void inet_csk_listen_stop(struct sock *sk) spin_unlock_bh(&queue->fastopenq->lock); while ((req = acc_req) != NULL) { acc_req = req->dl_next; - __reqsk_free(req); + reqsk_put(req); } } WARN_ON(sk->sk_ack_backlog); diff --git a/net/ipv4/syncookies.c b/net/ipv4/syncookies.c index f17db898ed26..5ae0c49f5e2e 100644 --- a/net/ipv4/syncookies.c +++ b/net/ipv4/syncookies.c @@ -219,9 +219,9 @@ int __cookie_v4_check(const struct iphdr *iph, const struct tcphdr *th, } EXPORT_SYMBOL_GPL(__cookie_v4_check); -static inline struct sock *get_cookie_sock(struct sock *sk, struct sk_buff *skb, - struct request_sock *req, - struct dst_entry *dst) +static struct sock *get_cookie_sock(struct sock *sk, struct sk_buff *skb, + struct request_sock *req, + struct dst_entry *dst) { struct inet_connection_sock *icsk = inet_csk(sk); struct sock *child; @@ -357,7 +357,7 @@ struct sock *cookie_v4_check(struct sock *sk, struct sk_buff *skb) ireq->opt = tcp_v4_save_options(skb); if (security_inet_conn_request(sk, skb, req)) { - reqsk_free(req); + reqsk_put(req); goto out; } @@ -378,7 +378,7 @@ struct sock *cookie_v4_check(struct sock *sk, struct sk_buff *skb) security_req_classify_flow(req, flowi4_to_flowi(&fl4)); rt = ip_route_output_key(sock_net(sk), &fl4); if (IS_ERR(rt)) { - reqsk_free(req); + reqsk_put(req); goto out; } diff --git a/net/ipv4/tcp_fastopen.c b/net/ipv4/tcp_fastopen.c index fe77417fc137..84381319e1bc 100644 --- a/net/ipv4/tcp_fastopen.c +++ b/net/ipv4/tcp_fastopen.c @@ -253,7 +253,7 @@ static bool tcp_fastopen_queue_check(struct sock *sk) fastopenq->rskq_rst_head = req1->dl_next; fastopenq->qlen--; spin_unlock(&fastopenq->lock); - reqsk_free(req1); + reqsk_put(req1); } return true; } -- cgit v1.2.3 From 9df85aaa43297cb12dc85155695dd1bfdf94f91d Mon Sep 17 00:00:00 2001 From: Antonios Motakis Date: Mon, 16 Mar 2015 14:08:43 -0600 Subject: vfio: platform: probe to devices on the platform bus Driver to bind to Linux platform devices, and callbacks to discover their resources to be used by the main VFIO PLATFORM code. Signed-off-by: Antonios Motakis Signed-off-by: Baptiste Reynal Reviewed-by: Eric Auger Tested-by: Eric Auger Signed-off-by: Alex Williamson --- drivers/vfio/platform/vfio_platform.c | 103 ++++++++++++++++++++++++++++++++++ include/uapi/linux/vfio.h | 1 + 2 files changed, 104 insertions(+) create mode 100644 drivers/vfio/platform/vfio_platform.c (limited to 'include') diff --git a/drivers/vfio/platform/vfio_platform.c b/drivers/vfio/platform/vfio_platform.c new file mode 100644 index 000000000000..cef645c83996 --- /dev/null +++ b/drivers/vfio/platform/vfio_platform.c @@ -0,0 +1,103 @@ +/* + * Copyright (C) 2013 - Virtual Open Systems + * Author: Antonios Motakis + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, version 2, as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include + +#include "vfio_platform_private.h" + +#define DRIVER_VERSION "0.10" +#define DRIVER_AUTHOR "Antonios Motakis " +#define DRIVER_DESC "VFIO for platform devices - User Level meta-driver" + +/* probing devices from the linux platform bus */ + +static struct resource *get_platform_resource(struct vfio_platform_device *vdev, + int num) +{ + struct platform_device *dev = (struct platform_device *) vdev->opaque; + int i; + + for (i = 0; i < dev->num_resources; i++) { + struct resource *r = &dev->resource[i]; + + if (resource_type(r) & (IORESOURCE_MEM|IORESOURCE_IO)) { + if (!num) + return r; + + num--; + } + } + return NULL; +} + +static int get_platform_irq(struct vfio_platform_device *vdev, int i) +{ + struct platform_device *pdev = (struct platform_device *) vdev->opaque; + + return platform_get_irq(pdev, i); +} + +static int vfio_platform_probe(struct platform_device *pdev) +{ + struct vfio_platform_device *vdev; + int ret; + + vdev = kzalloc(sizeof(*vdev), GFP_KERNEL); + if (!vdev) + return -ENOMEM; + + vdev->opaque = (void *) pdev; + vdev->name = pdev->name; + vdev->flags = VFIO_DEVICE_FLAGS_PLATFORM; + vdev->get_resource = get_platform_resource; + vdev->get_irq = get_platform_irq; + + ret = vfio_platform_probe_common(vdev, &pdev->dev); + if (ret) + kfree(vdev); + + return ret; +} + +static int vfio_platform_remove(struct platform_device *pdev) +{ + struct vfio_platform_device *vdev; + + vdev = vfio_platform_remove_common(&pdev->dev); + if (vdev) { + kfree(vdev); + return 0; + } + + return -EINVAL; +} + +static struct platform_driver vfio_platform_driver = { + .probe = vfio_platform_probe, + .remove = vfio_platform_remove, + .driver = { + .name = "vfio-platform", + .owner = THIS_MODULE, + }, +}; + +module_platform_driver(vfio_platform_driver); + +MODULE_VERSION(DRIVER_VERSION); +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_DESCRIPTION(DRIVER_DESC); diff --git a/include/uapi/linux/vfio.h b/include/uapi/linux/vfio.h index 82889c30f4f5..ea9514b6bb5c 100644 --- a/include/uapi/linux/vfio.h +++ b/include/uapi/linux/vfio.h @@ -160,6 +160,7 @@ struct vfio_device_info { __u32 flags; #define VFIO_DEVICE_FLAGS_RESET (1 << 0) /* Device supports reset */ #define VFIO_DEVICE_FLAGS_PCI (1 << 1) /* vfio-pci device */ +#define VFIO_DEVICE_FLAGS_PLATFORM (1 << 2) /* vfio-platform device */ __u32 num_regions; /* Max region index + 1 */ __u32 num_irqs; /* Max IRQ index + 1 */ }; -- cgit v1.2.3 From 36fe431f2811fa3b5fed15d272c585d5a47977aa Mon Sep 17 00:00:00 2001 From: Antonios Motakis Date: Mon, 16 Mar 2015 14:08:44 -0600 Subject: vfio: amba: VFIO support for AMBA devices Add support for discovering AMBA devices with VFIO and handle them similarly to Linux platform devices. Signed-off-by: Antonios Motakis Signed-off-by: Baptiste Reynal Reviewed-by: Eric Auger Signed-off-by: Alex Williamson --- drivers/vfio/platform/vfio_amba.c | 115 ++++++++++++++++++++++++++++++++++++++ include/uapi/linux/vfio.h | 1 + 2 files changed, 116 insertions(+) create mode 100644 drivers/vfio/platform/vfio_amba.c (limited to 'include') diff --git a/drivers/vfio/platform/vfio_amba.c b/drivers/vfio/platform/vfio_amba.c new file mode 100644 index 000000000000..ff0331f72526 --- /dev/null +++ b/drivers/vfio/platform/vfio_amba.c @@ -0,0 +1,115 @@ +/* + * Copyright (C) 2013 - Virtual Open Systems + * Author: Antonios Motakis + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, version 2, as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include + +#include "vfio_platform_private.h" + +#define DRIVER_VERSION "0.10" +#define DRIVER_AUTHOR "Antonios Motakis " +#define DRIVER_DESC "VFIO for AMBA devices - User Level meta-driver" + +/* probing devices from the AMBA bus */ + +static struct resource *get_amba_resource(struct vfio_platform_device *vdev, + int i) +{ + struct amba_device *adev = (struct amba_device *) vdev->opaque; + + if (i == 0) + return &adev->res; + + return NULL; +} + +static int get_amba_irq(struct vfio_platform_device *vdev, int i) +{ + struct amba_device *adev = (struct amba_device *) vdev->opaque; + int ret = 0; + + if (i < AMBA_NR_IRQS) + ret = adev->irq[i]; + + /* zero is an unset IRQ for AMBA devices */ + return ret ? ret : -ENXIO; +} + +static int vfio_amba_probe(struct amba_device *adev, const struct amba_id *id) +{ + struct vfio_platform_device *vdev; + int ret; + + vdev = kzalloc(sizeof(*vdev), GFP_KERNEL); + if (!vdev) + return -ENOMEM; + + vdev->name = kasprintf(GFP_KERNEL, "vfio-amba-%08x", adev->periphid); + if (!vdev->name) { + kfree(vdev); + return -ENOMEM; + } + + vdev->opaque = (void *) adev; + vdev->flags = VFIO_DEVICE_FLAGS_AMBA; + vdev->get_resource = get_amba_resource; + vdev->get_irq = get_amba_irq; + + ret = vfio_platform_probe_common(vdev, &adev->dev); + if (ret) { + kfree(vdev->name); + kfree(vdev); + } + + return ret; +} + +static int vfio_amba_remove(struct amba_device *adev) +{ + struct vfio_platform_device *vdev; + + vdev = vfio_platform_remove_common(&adev->dev); + if (vdev) { + kfree(vdev->name); + kfree(vdev); + return 0; + } + + return -EINVAL; +} + +static struct amba_id pl330_ids[] = { + { 0, 0 }, +}; + +MODULE_DEVICE_TABLE(amba, pl330_ids); + +static struct amba_driver vfio_amba_driver = { + .probe = vfio_amba_probe, + .remove = vfio_amba_remove, + .id_table = pl330_ids, + .drv = { + .name = "vfio-amba", + .owner = THIS_MODULE, + }, +}; + +module_amba_driver(vfio_amba_driver); + +MODULE_VERSION(DRIVER_VERSION); +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_DESCRIPTION(DRIVER_DESC); diff --git a/include/uapi/linux/vfio.h b/include/uapi/linux/vfio.h index ea9514b6bb5c..b57b750c222f 100644 --- a/include/uapi/linux/vfio.h +++ b/include/uapi/linux/vfio.h @@ -161,6 +161,7 @@ struct vfio_device_info { #define VFIO_DEVICE_FLAGS_RESET (1 << 0) /* Device supports reset */ #define VFIO_DEVICE_FLAGS_PCI (1 << 1) /* vfio-pci device */ #define VFIO_DEVICE_FLAGS_PLATFORM (1 << 2) /* vfio-platform device */ +#define VFIO_DEVICE_FLAGS_AMBA (1 << 3) /* vfio-amba device */ __u32 num_regions; /* Max region index + 1 */ __u32 num_irqs; /* Max IRQ index + 1 */ }; -- cgit v1.2.3 From 7e992d692750b2938224eb43fee907181d92a602 Mon Sep 17 00:00:00 2001 From: Antonios Motakis Date: Mon, 16 Mar 2015 14:08:54 -0600 Subject: vfio: move eventfd support code for VFIO_PCI to a separate file The virqfd functionality that is used by VFIO_PCI to implement interrupt masking and unmasking via an eventfd, is generic enough and can be reused by another driver. Move it to a separate file in order to allow the code to be shared. Signed-off-by: Antonios Motakis Signed-off-by: Baptiste Reynal Reviewed-by: Eric Auger Tested-by: Eric Auger Signed-off-by: Alex Williamson --- drivers/vfio/pci/Makefile | 3 +- drivers/vfio/pci/vfio_pci_intrs.c | 215 ------------------------------------ drivers/vfio/pci/vfio_pci_private.h | 3 - drivers/vfio/virqfd.c | 213 +++++++++++++++++++++++++++++++++++ include/linux/vfio.h | 27 +++++ 5 files changed, 242 insertions(+), 219 deletions(-) create mode 100644 drivers/vfio/virqfd.c (limited to 'include') diff --git a/drivers/vfio/pci/Makefile b/drivers/vfio/pci/Makefile index 131079255fd9..c7c864436896 100644 --- a/drivers/vfio/pci/Makefile +++ b/drivers/vfio/pci/Makefile @@ -1,4 +1,5 @@ -vfio-pci-y := vfio_pci.o vfio_pci_intrs.o vfio_pci_rdwr.o vfio_pci_config.o +vfio-pci-y := vfio_pci.o vfio_pci_intrs.o vfio_pci_rdwr.o vfio_pci_config.o \ + ../virqfd.o obj-$(CONFIG_VFIO_PCI) += vfio-pci.o diff --git a/drivers/vfio/pci/vfio_pci_intrs.c b/drivers/vfio/pci/vfio_pci_intrs.c index c84a129cc527..1f577b4ac126 100644 --- a/drivers/vfio/pci/vfio_pci_intrs.c +++ b/drivers/vfio/pci/vfio_pci_intrs.c @@ -19,227 +19,12 @@ #include #include #include -#include #include #include -#include #include #include "vfio_pci_private.h" -/* - * IRQfd - generic - */ -struct virqfd { - void *opaque; - struct eventfd_ctx *eventfd; - int (*handler)(void *, void *); - void (*thread)(void *, void *); - void *data; - struct work_struct inject; - wait_queue_t wait; - poll_table pt; - struct work_struct shutdown; - struct virqfd **pvirqfd; -}; - -static struct workqueue_struct *vfio_irqfd_cleanup_wq; -DEFINE_SPINLOCK(virqfd_lock); - -int __init vfio_virqfd_init(void) -{ - vfio_irqfd_cleanup_wq = - create_singlethread_workqueue("vfio-irqfd-cleanup"); - if (!vfio_irqfd_cleanup_wq) - return -ENOMEM; - - return 0; -} - -void vfio_virqfd_exit(void) -{ - destroy_workqueue(vfio_irqfd_cleanup_wq); -} - -static void virqfd_deactivate(struct virqfd *virqfd) -{ - queue_work(vfio_irqfd_cleanup_wq, &virqfd->shutdown); -} - -static int virqfd_wakeup(wait_queue_t *wait, unsigned mode, int sync, void *key) -{ - struct virqfd *virqfd = container_of(wait, struct virqfd, wait); - unsigned long flags = (unsigned long)key; - - if (flags & POLLIN) { - /* An event has been signaled, call function */ - if ((!virqfd->handler || - virqfd->handler(virqfd->opaque, virqfd->data)) && - virqfd->thread) - schedule_work(&virqfd->inject); - } - - if (flags & POLLHUP) { - unsigned long flags; - spin_lock_irqsave(&virqfd_lock, flags); - - /* - * The eventfd is closing, if the virqfd has not yet been - * queued for release, as determined by testing whether the - * virqfd pointer to it is still valid, queue it now. As - * with kvm irqfds, we know we won't race against the virqfd - * going away because we hold the lock to get here. - */ - if (*(virqfd->pvirqfd) == virqfd) { - *(virqfd->pvirqfd) = NULL; - virqfd_deactivate(virqfd); - } - - spin_unlock_irqrestore(&virqfd_lock, flags); - } - - return 0; -} - -static void virqfd_ptable_queue_proc(struct file *file, - wait_queue_head_t *wqh, poll_table *pt) -{ - struct virqfd *virqfd = container_of(pt, struct virqfd, pt); - add_wait_queue(wqh, &virqfd->wait); -} - -static void virqfd_shutdown(struct work_struct *work) -{ - struct virqfd *virqfd = container_of(work, struct virqfd, shutdown); - u64 cnt; - - eventfd_ctx_remove_wait_queue(virqfd->eventfd, &virqfd->wait, &cnt); - flush_work(&virqfd->inject); - eventfd_ctx_put(virqfd->eventfd); - - kfree(virqfd); -} - -static void virqfd_inject(struct work_struct *work) -{ - struct virqfd *virqfd = container_of(work, struct virqfd, inject); - if (virqfd->thread) - virqfd->thread(virqfd->opaque, virqfd->data); -} - -int vfio_virqfd_enable(void *opaque, - int (*handler)(void *, void *), - void (*thread)(void *, void *), - void *data, struct virqfd **pvirqfd, int fd) -{ - struct fd irqfd; - struct eventfd_ctx *ctx; - struct virqfd *virqfd; - int ret = 0; - unsigned int events; - - virqfd = kzalloc(sizeof(*virqfd), GFP_KERNEL); - if (!virqfd) - return -ENOMEM; - - virqfd->pvirqfd = pvirqfd; - virqfd->opaque = opaque; - virqfd->handler = handler; - virqfd->thread = thread; - virqfd->data = data; - - INIT_WORK(&virqfd->shutdown, virqfd_shutdown); - INIT_WORK(&virqfd->inject, virqfd_inject); - - irqfd = fdget(fd); - if (!irqfd.file) { - ret = -EBADF; - goto err_fd; - } - - ctx = eventfd_ctx_fileget(irqfd.file); - if (IS_ERR(ctx)) { - ret = PTR_ERR(ctx); - goto err_ctx; - } - - virqfd->eventfd = ctx; - - /* - * virqfds can be released by closing the eventfd or directly - * through ioctl. These are both done through a workqueue, so - * we update the pointer to the virqfd under lock to avoid - * pushing multiple jobs to release the same virqfd. - */ - spin_lock_irq(&virqfd_lock); - - if (*pvirqfd) { - spin_unlock_irq(&virqfd_lock); - ret = -EBUSY; - goto err_busy; - } - *pvirqfd = virqfd; - - spin_unlock_irq(&virqfd_lock); - - /* - * Install our own custom wake-up handling so we are notified via - * a callback whenever someone signals the underlying eventfd. - */ - init_waitqueue_func_entry(&virqfd->wait, virqfd_wakeup); - init_poll_funcptr(&virqfd->pt, virqfd_ptable_queue_proc); - - events = irqfd.file->f_op->poll(irqfd.file, &virqfd->pt); - - /* - * Check if there was an event already pending on the eventfd - * before we registered and trigger it as if we didn't miss it. - */ - if (events & POLLIN) { - if ((!handler || handler(opaque, data)) && thread) - schedule_work(&virqfd->inject); - } - - /* - * Do not drop the file until the irqfd is fully initialized, - * otherwise we might race against the POLLHUP. - */ - fdput(irqfd); - - return 0; -err_busy: - eventfd_ctx_put(ctx); -err_ctx: - fdput(irqfd); -err_fd: - kfree(virqfd); - - return ret; -} -EXPORT_SYMBOL_GPL(vfio_virqfd_enable); - -void vfio_virqfd_disable(struct virqfd **pvirqfd) -{ - unsigned long flags; - - spin_lock_irqsave(&virqfd_lock, flags); - - if (*pvirqfd) { - virqfd_deactivate(*pvirqfd); - *pvirqfd = NULL; - } - - spin_unlock_irqrestore(&virqfd_lock, flags); - - /* - * Block until we know all outstanding shutdown jobs have completed. - * Even if we don't queue the job, flush the wq to be sure it's - * been released. - */ - flush_workqueue(vfio_irqfd_cleanup_wq); -} -EXPORT_SYMBOL_GPL(vfio_virqfd_disable); - /* * INTx */ diff --git a/drivers/vfio/pci/vfio_pci_private.h b/drivers/vfio/pci/vfio_pci_private.h index 02539651bc3a..ae0e1b4c1711 100644 --- a/drivers/vfio/pci/vfio_pci_private.h +++ b/drivers/vfio/pci/vfio_pci_private.h @@ -87,9 +87,6 @@ extern ssize_t vfio_pci_vga_rw(struct vfio_pci_device *vdev, char __user *buf, extern int vfio_pci_init_perm_bits(void); extern void vfio_pci_uninit_perm_bits(void); -extern int vfio_virqfd_init(void); -extern void vfio_virqfd_exit(void); - extern int vfio_config_init(struct vfio_pci_device *vdev); extern void vfio_config_free(struct vfio_pci_device *vdev); #endif /* VFIO_PCI_PRIVATE_H */ diff --git a/drivers/vfio/virqfd.c b/drivers/vfio/virqfd.c new file mode 100644 index 000000000000..5967899645c5 --- /dev/null +++ b/drivers/vfio/virqfd.c @@ -0,0 +1,213 @@ +/* + * VFIO generic eventfd code for IRQFD support. + * Derived from drivers/vfio/pci/vfio_pci_intrs.c + * + * Copyright (C) 2012 Red Hat, Inc. All rights reserved. + * Author: Alex Williamson + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include + +static struct workqueue_struct *vfio_irqfd_cleanup_wq; +DEFINE_SPINLOCK(virqfd_lock); + +int __init vfio_virqfd_init(void) +{ + vfio_irqfd_cleanup_wq = + create_singlethread_workqueue("vfio-irqfd-cleanup"); + if (!vfio_irqfd_cleanup_wq) + return -ENOMEM; + + return 0; +} + +void vfio_virqfd_exit(void) +{ + destroy_workqueue(vfio_irqfd_cleanup_wq); +} + +static void virqfd_deactivate(struct virqfd *virqfd) +{ + queue_work(vfio_irqfd_cleanup_wq, &virqfd->shutdown); +} + +static int virqfd_wakeup(wait_queue_t *wait, unsigned mode, int sync, void *key) +{ + struct virqfd *virqfd = container_of(wait, struct virqfd, wait); + unsigned long flags = (unsigned long)key; + + if (flags & POLLIN) { + /* An event has been signaled, call function */ + if ((!virqfd->handler || + virqfd->handler(virqfd->opaque, virqfd->data)) && + virqfd->thread) + schedule_work(&virqfd->inject); + } + + if (flags & POLLHUP) { + unsigned long flags; + spin_lock_irqsave(&virqfd_lock, flags); + + /* + * The eventfd is closing, if the virqfd has not yet been + * queued for release, as determined by testing whether the + * virqfd pointer to it is still valid, queue it now. As + * with kvm irqfds, we know we won't race against the virqfd + * going away because we hold the lock to get here. + */ + if (*(virqfd->pvirqfd) == virqfd) { + *(virqfd->pvirqfd) = NULL; + virqfd_deactivate(virqfd); + } + + spin_unlock_irqrestore(&virqfd_lock, flags); + } + + return 0; +} + +static void virqfd_ptable_queue_proc(struct file *file, + wait_queue_head_t *wqh, poll_table *pt) +{ + struct virqfd *virqfd = container_of(pt, struct virqfd, pt); + add_wait_queue(wqh, &virqfd->wait); +} + +static void virqfd_shutdown(struct work_struct *work) +{ + struct virqfd *virqfd = container_of(work, struct virqfd, shutdown); + u64 cnt; + + eventfd_ctx_remove_wait_queue(virqfd->eventfd, &virqfd->wait, &cnt); + flush_work(&virqfd->inject); + eventfd_ctx_put(virqfd->eventfd); + + kfree(virqfd); +} + +static void virqfd_inject(struct work_struct *work) +{ + struct virqfd *virqfd = container_of(work, struct virqfd, inject); + if (virqfd->thread) + virqfd->thread(virqfd->opaque, virqfd->data); +} + +int vfio_virqfd_enable(void *opaque, + int (*handler)(void *, void *), + void (*thread)(void *, void *), + void *data, struct virqfd **pvirqfd, int fd) +{ + struct fd irqfd; + struct eventfd_ctx *ctx; + struct virqfd *virqfd; + int ret = 0; + unsigned int events; + + virqfd = kzalloc(sizeof(*virqfd), GFP_KERNEL); + if (!virqfd) + return -ENOMEM; + + virqfd->pvirqfd = pvirqfd; + virqfd->opaque = opaque; + virqfd->handler = handler; + virqfd->thread = thread; + virqfd->data = data; + + INIT_WORK(&virqfd->shutdown, virqfd_shutdown); + INIT_WORK(&virqfd->inject, virqfd_inject); + + irqfd = fdget(fd); + if (!irqfd.file) { + ret = -EBADF; + goto err_fd; + } + + ctx = eventfd_ctx_fileget(irqfd.file); + if (IS_ERR(ctx)) { + ret = PTR_ERR(ctx); + goto err_ctx; + } + + virqfd->eventfd = ctx; + + /* + * virqfds can be released by closing the eventfd or directly + * through ioctl. These are both done through a workqueue, so + * we update the pointer to the virqfd under lock to avoid + * pushing multiple jobs to release the same virqfd. + */ + spin_lock_irq(&virqfd_lock); + + if (*pvirqfd) { + spin_unlock_irq(&virqfd_lock); + ret = -EBUSY; + goto err_busy; + } + *pvirqfd = virqfd; + + spin_unlock_irq(&virqfd_lock); + + /* + * Install our own custom wake-up handling so we are notified via + * a callback whenever someone signals the underlying eventfd. + */ + init_waitqueue_func_entry(&virqfd->wait, virqfd_wakeup); + init_poll_funcptr(&virqfd->pt, virqfd_ptable_queue_proc); + + events = irqfd.file->f_op->poll(irqfd.file, &virqfd->pt); + + /* + * Check if there was an event already pending on the eventfd + * before we registered and trigger it as if we didn't miss it. + */ + if (events & POLLIN) { + if ((!handler || handler(opaque, data)) && thread) + schedule_work(&virqfd->inject); + } + + /* + * Do not drop the file until the irqfd is fully initialized, + * otherwise we might race against the POLLHUP. + */ + fdput(irqfd); + + return 0; +err_busy: + eventfd_ctx_put(ctx); +err_ctx: + fdput(irqfd); +err_fd: + kfree(virqfd); + + return ret; +} +EXPORT_SYMBOL_GPL(vfio_virqfd_enable); + +void vfio_virqfd_disable(struct virqfd **pvirqfd) +{ + unsigned long flags; + + spin_lock_irqsave(&virqfd_lock, flags); + + if (*pvirqfd) { + virqfd_deactivate(*pvirqfd); + *pvirqfd = NULL; + } + + spin_unlock_irqrestore(&virqfd_lock, flags); + + /* + * Block until we know all outstanding shutdown jobs have completed. + * Even if we don't queue the job, flush the wq to be sure it's + * been released. + */ + flush_workqueue(vfio_irqfd_cleanup_wq); +} +EXPORT_SYMBOL_GPL(vfio_virqfd_disable); diff --git a/include/linux/vfio.h b/include/linux/vfio.h index 2d67b8998fd8..683b5146022e 100644 --- a/include/linux/vfio.h +++ b/include/linux/vfio.h @@ -14,6 +14,8 @@ #include #include +#include +#include #include /** @@ -123,4 +125,29 @@ static inline long vfio_spapr_iommu_eeh_ioctl(struct iommu_group *group, return -ENOTTY; } #endif /* CONFIG_EEH */ + +/* + * IRQfd - generic + */ +struct virqfd { + void *opaque; + struct eventfd_ctx *eventfd; + int (*handler)(void *, void *); + void (*thread)(void *, void *); + void *data; + struct work_struct inject; + wait_queue_t wait; + poll_table pt; + struct work_struct shutdown; + struct virqfd **pvirqfd; +}; + +extern int vfio_virqfd_init(void); +extern void vfio_virqfd_exit(void); +extern int vfio_virqfd_enable(void *opaque, + int (*handler)(void *, void *), + void (*thread)(void *, void *), + void *data, struct virqfd **pvirqfd, int fd); +extern void vfio_virqfd_disable(struct virqfd **pvirqfd); + #endif /* VFIO_H */ -- cgit v1.2.3 From 34644524bce91883d5051a7eaf3ec5464ed149bf Mon Sep 17 00:00:00 2001 From: Abhilash Kesavan Date: Fri, 6 Feb 2015 19:15:27 +0530 Subject: lib: devres: add a helper function for ioremap_wc Implement a resource managed writecombine ioremap function. Signed-off-by: Abhilash Kesavan Acked-by: Catalin Marinas Signed-off-by: Greg Kroah-Hartman --- Documentation/driver-model/devres.txt | 1 + include/linux/io.h | 2 ++ lib/devres.c | 28 ++++++++++++++++++++++++++++ 3 files changed, 31 insertions(+) (limited to 'include') diff --git a/Documentation/driver-model/devres.txt b/Documentation/driver-model/devres.txt index 6d1e8eeb5990..7fe7fd263aba 100644 --- a/Documentation/driver-model/devres.txt +++ b/Documentation/driver-model/devres.txt @@ -276,6 +276,7 @@ IOMAP devm_ioport_unmap() devm_ioremap() devm_ioremap_nocache() + devm_ioremap_wc() devm_ioremap_resource() : checks resource, requests memory region, ioremaps devm_iounmap() pcim_iomap() diff --git a/include/linux/io.h b/include/linux/io.h index fa02e55e5a2e..42b33f03d1df 100644 --- a/include/linux/io.h +++ b/include/linux/io.h @@ -64,6 +64,8 @@ void __iomem *devm_ioremap(struct device *dev, resource_size_t offset, resource_size_t size); void __iomem *devm_ioremap_nocache(struct device *dev, resource_size_t offset, resource_size_t size); +void __iomem *devm_ioremap_wc(struct device *dev, resource_size_t offset, + resource_size_t size); void devm_iounmap(struct device *dev, void __iomem *addr); int check_signature(const volatile void __iomem *io_addr, const unsigned char *signature, int length); diff --git a/lib/devres.c b/lib/devres.c index 0f1dd2e9d2c1..fbe2aac522e6 100644 --- a/lib/devres.c +++ b/lib/devres.c @@ -71,6 +71,34 @@ void __iomem *devm_ioremap_nocache(struct device *dev, resource_size_t offset, } EXPORT_SYMBOL(devm_ioremap_nocache); +/** + * devm_ioremap_wc - Managed ioremap_wc() + * @dev: Generic device to remap IO address for + * @offset: BUS offset to map + * @size: Size of map + * + * Managed ioremap_wc(). Map is automatically unmapped on driver detach. + */ +void __iomem *devm_ioremap_wc(struct device *dev, resource_size_t offset, + resource_size_t size) +{ + void __iomem **ptr, *addr; + + ptr = devres_alloc(devm_ioremap_release, sizeof(*ptr), GFP_KERNEL); + if (!ptr) + return NULL; + + addr = ioremap_wc(offset, size); + if (addr) { + *ptr = addr; + devres_add(dev, ptr); + } else + devres_free(ptr); + + return addr; +} +EXPORT_SYMBOL(devm_ioremap_wc); + /** * devm_iounmap - Managed iounmap() * @dev: Generic device to unmap for -- cgit v1.2.3 From ce793486e23e0162a732c605189c8028e0910e86 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Mon, 16 Mar 2015 23:49:03 +0100 Subject: driver core / ACPI: Represent ACPI companions using fwnode_handle Now that we have struct fwnode_handle, we can use that to point to ACPI companions from struct device objects instead of pointing to struct acpi_device directly. There are two benefits from that. First, the somewhat ugly and hackish struct acpi_dev_node can be dropped and, second, the same struct fwnode_handle pointer can be used in the future to point to other (non-ACPI) firmware device node types. Signed-off-by: Rafael J. Wysocki Acked-by: Greg Kroah-Hartman Acked-by: Grant Likely --- drivers/acpi/acpi_platform.c | 2 +- drivers/acpi/dock.c | 2 +- drivers/base/platform.c | 2 +- drivers/gpio/gpiolib.h | 2 ++ drivers/i2c/i2c-core.c | 4 ++-- include/acpi/acpi_bus.h | 3 ++- include/linux/acpi.h | 5 +++-- include/linux/device.h | 13 +++---------- include/linux/fwnode.h | 25 +++++++++++++++++++++++++ include/linux/i2c.h | 4 ++-- include/linux/platform_device.h | 2 +- include/linux/property.h | 11 +---------- 12 files changed, 44 insertions(+), 31 deletions(-) create mode 100644 include/linux/fwnode.h (limited to 'include') diff --git a/drivers/acpi/acpi_platform.c b/drivers/acpi/acpi_platform.c index 1284138e42ab..4bf75597f732 100644 --- a/drivers/acpi/acpi_platform.c +++ b/drivers/acpi/acpi_platform.c @@ -102,7 +102,7 @@ struct platform_device *acpi_create_platform_device(struct acpi_device *adev) pdevinfo.id = -1; pdevinfo.res = resources; pdevinfo.num_res = count; - pdevinfo.acpi_node.companion = adev; + pdevinfo.fwnode = acpi_fwnode_handle(adev); pdevinfo.dma_mask = DMA_BIT_MASK(32); pdev = platform_device_register_full(&pdevinfo); if (IS_ERR(pdev)) diff --git a/drivers/acpi/dock.c b/drivers/acpi/dock.c index d9339b442a4e..a688aa243f6c 100644 --- a/drivers/acpi/dock.c +++ b/drivers/acpi/dock.c @@ -615,7 +615,7 @@ void acpi_dock_add(struct acpi_device *adev) memset(&pdevinfo, 0, sizeof(pdevinfo)); pdevinfo.name = "dock"; pdevinfo.id = dock_station_count; - pdevinfo.acpi_node.companion = adev; + pdevinfo.fwnode = acpi_fwnode_handle(adev); pdevinfo.data = &ds; pdevinfo.size_data = sizeof(ds); dd = platform_device_register_full(&pdevinfo); diff --git a/drivers/base/platform.c b/drivers/base/platform.c index 9421fed40905..17f0204fabef 100644 --- a/drivers/base/platform.c +++ b/drivers/base/platform.c @@ -454,7 +454,7 @@ struct platform_device *platform_device_register_full( goto err_alloc; pdev->dev.parent = pdevinfo->parent; - ACPI_COMPANION_SET(&pdev->dev, pdevinfo->acpi_node.companion); + pdev->dev.fwnode = pdevinfo->fwnode; if (pdevinfo->dma_mask) { /* diff --git a/drivers/gpio/gpiolib.h b/drivers/gpio/gpiolib.h index 550a5eafbd38..ab892be26dc2 100644 --- a/drivers/gpio/gpiolib.h +++ b/drivers/gpio/gpiolib.h @@ -17,6 +17,8 @@ enum of_gpio_flags; +struct acpi_device; + /** * struct acpi_gpio_info - ACPI GPIO specific information * @gpioint: if %true this GPIO is of type GpioInt otherwise type is GpioIo diff --git a/drivers/i2c/i2c-core.c b/drivers/i2c/i2c-core.c index edf274cabe81..c87c31387e2d 100644 --- a/drivers/i2c/i2c-core.c +++ b/drivers/i2c/i2c-core.c @@ -133,7 +133,7 @@ static acpi_status acpi_i2c_add_device(acpi_handle handle, u32 level, return AE_OK; memset(&info, 0, sizeof(info)); - info.acpi_node.companion = adev; + info.fwnode = acpi_fwnode_handle(adev); info.irq = -1; INIT_LIST_HEAD(&resource_list); @@ -971,7 +971,7 @@ i2c_new_device(struct i2c_adapter *adap, struct i2c_board_info const *info) client->dev.bus = &i2c_bus_type; client->dev.type = &i2c_client_type; client->dev.of_node = info->of_node; - ACPI_COMPANION_SET(&client->dev, info->acpi_node.companion); + client->dev.fwnode = info->fwnode; i2c_dev_set_name(adap, client); status = device_register(&client->dev); diff --git a/include/acpi/acpi_bus.h b/include/acpi/acpi_bus.h index 61e32ec1fc4d..dae2a16fe93f 100644 --- a/include/acpi/acpi_bus.h +++ b/include/acpi/acpi_bus.h @@ -386,7 +386,8 @@ static inline bool is_acpi_node(struct fwnode_handle *fwnode) static inline struct acpi_device *acpi_node(struct fwnode_handle *fwnode) { - return fwnode ? container_of(fwnode, struct acpi_device, fwnode) : NULL; + return is_acpi_node(fwnode) ? + container_of(fwnode, struct acpi_device, fwnode) : NULL; } static inline struct fwnode_handle *acpi_fwnode_handle(struct acpi_device *adev) diff --git a/include/linux/acpi.h b/include/linux/acpi.h index 24c7aa8b1d20..402ddbdc2da1 100644 --- a/include/linux/acpi.h +++ b/include/linux/acpi.h @@ -53,8 +53,9 @@ static inline acpi_handle acpi_device_handle(struct acpi_device *adev) return adev ? adev->handle : NULL; } -#define ACPI_COMPANION(dev) ((dev)->acpi_node.companion) -#define ACPI_COMPANION_SET(dev, adev) ACPI_COMPANION(dev) = (adev) +#define ACPI_COMPANION(dev) acpi_node((dev)->fwnode) +#define ACPI_COMPANION_SET(dev, adev) (dev)->fwnode = (adev) ? \ + acpi_fwnode_handle(adev) : NULL #define ACPI_HANDLE(dev) acpi_device_handle(ACPI_COMPANION(dev)) static inline void acpi_preset_companion(struct device *dev, diff --git a/include/linux/device.h b/include/linux/device.h index 0eb8ee2dc6d1..badef20b876a 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -38,6 +38,7 @@ struct class; struct subsys_private; struct bus_type; struct device_node; +struct fwnode_handle; struct iommu_ops; struct iommu_group; @@ -650,14 +651,6 @@ struct device_dma_parameters { unsigned long segment_boundary_mask; }; -struct acpi_device; - -struct acpi_dev_node { -#ifdef CONFIG_ACPI - struct acpi_device *companion; -#endif -}; - /** * struct device - The basic device structure * @parent: The device's "parent" device, the device to which it is attached. @@ -703,7 +696,7 @@ struct acpi_dev_node { * @cma_area: Contiguous memory area for dma allocations * @archdata: For arch-specific additions. * @of_node: Associated device tree node. - * @acpi_node: Associated ACPI device node. + * @fwnode: Associated device node supplied by platform firmware. * @devt: For creating the sysfs "dev". * @id: device instance * @devres_lock: Spinlock to protect the resource of the device. @@ -779,7 +772,7 @@ struct device { struct dev_archdata archdata; struct device_node *of_node; /* associated device tree node */ - struct acpi_dev_node acpi_node; /* associated ACPI device node */ + struct fwnode_handle *fwnode; /* firmware device node */ dev_t devt; /* dev_t, creates the sysfs "dev" */ u32 id; /* device instance */ diff --git a/include/linux/fwnode.h b/include/linux/fwnode.h new file mode 100644 index 000000000000..17bb5f039509 --- /dev/null +++ b/include/linux/fwnode.h @@ -0,0 +1,25 @@ +/* + * fwnode.h - Firmware device node object handle type definition. + * + * Copyright (C) 2015, Intel Corporation + * Author: Rafael J. Wysocki + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef _LINUX_FWNODE_H_ +#define _LINUX_FWNODE_H_ + +enum fwnode_type { + FWNODE_INVALID = 0, + FWNODE_OF, + FWNODE_ACPI, +}; + +struct fwnode_handle { + enum fwnode_type type; +}; + +#endif diff --git a/include/linux/i2c.h b/include/linux/i2c.h index f17da50402a4..6d89575361a8 100644 --- a/include/linux/i2c.h +++ b/include/linux/i2c.h @@ -278,7 +278,7 @@ static inline int i2c_slave_event(struct i2c_client *client, * @platform_data: stored in i2c_client.dev.platform_data * @archdata: copied into i2c_client.dev.archdata * @of_node: pointer to OpenFirmware device node - * @acpi_node: ACPI device node + * @fwnode: device node supplied by the platform firmware * @irq: stored in i2c_client.irq * * I2C doesn't actually support hardware probing, although controllers and @@ -299,7 +299,7 @@ struct i2c_board_info { void *platform_data; struct dev_archdata *archdata; struct device_node *of_node; - struct acpi_dev_node acpi_node; + struct fwnode_handle *fwnode; int irq; }; diff --git a/include/linux/platform_device.h b/include/linux/platform_device.h index ae4882ca4a64..58f1e75ba105 100644 --- a/include/linux/platform_device.h +++ b/include/linux/platform_device.h @@ -59,7 +59,7 @@ extern int platform_add_devices(struct platform_device **, int); struct platform_device_info { struct device *parent; - struct acpi_dev_node acpi_node; + struct fwnode_handle *fwnode; const char *name; int id; diff --git a/include/linux/property.h b/include/linux/property.h index a6a3d98bd7e9..31dfd3db35d6 100644 --- a/include/linux/property.h +++ b/include/linux/property.h @@ -13,6 +13,7 @@ #ifndef _LINUX_PROPERTY_H_ #define _LINUX_PROPERTY_H_ +#include #include struct device; @@ -40,16 +41,6 @@ int device_property_read_string_array(struct device *dev, const char *propname, int device_property_read_string(struct device *dev, const char *propname, const char **val); -enum fwnode_type { - FWNODE_INVALID = 0, - FWNODE_OF, - FWNODE_ACPI, -}; - -struct fwnode_handle { - enum fwnode_type type; -}; - bool fwnode_property_present(struct fwnode_handle *fwnode, const char *propname); int fwnode_property_read_u8_array(struct fwnode_handle *fwnode, const char *propname, u8 *val, -- cgit v1.2.3 From ca5b74d2675a44f54aacb919c1cf022463e2f738 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Mon, 16 Mar 2015 23:49:08 +0100 Subject: ACPI: Introduce has_acpi_companion() Now that the ACPI companions of devices are represented by pointers to struct fwnode_handle, it is not quite efficient to check whether or not an ACPI companion of a device is present by evaluating the ACPI_COMPANION() macro. For this reason, introduce a special static inline routine for that, has_acpi_companion(), and update the code to use it where applicable. Signed-off-by: Rafael J. Wysocki --- drivers/acpi/glue.c | 4 ++-- drivers/i2c/busses/i2c-designware-platdrv.c | 4 ++-- drivers/iommu/intel-iommu.c | 2 +- include/linux/acpi.h | 10 ++++++++++ 4 files changed, 15 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/drivers/acpi/glue.c b/drivers/acpi/glue.c index f774c65ecb8b..39c485b0c25c 100644 --- a/drivers/acpi/glue.c +++ b/drivers/acpi/glue.c @@ -168,7 +168,7 @@ int acpi_bind_one(struct device *dev, struct acpi_device *acpi_dev) unsigned int node_id; int retval = -EINVAL; - if (ACPI_COMPANION(dev)) { + if (has_acpi_companion(dev)) { if (acpi_dev) { dev_warn(dev, "ACPI companion already set\n"); return -EINVAL; @@ -220,7 +220,7 @@ int acpi_bind_one(struct device *dev, struct acpi_device *acpi_dev) list_add(&physical_node->node, physnode_list); acpi_dev->physical_node_count++; - if (!ACPI_COMPANION(dev)) + if (!has_acpi_companion(dev)) ACPI_COMPANION_SET(dev, acpi_dev); acpi_physnode_link_name(physical_node_name, node_id); diff --git a/drivers/i2c/busses/i2c-designware-platdrv.c b/drivers/i2c/busses/i2c-designware-platdrv.c index c270f5f9a8f9..538d6910b550 100644 --- a/drivers/i2c/busses/i2c-designware-platdrv.c +++ b/drivers/i2c/busses/i2c-designware-platdrv.c @@ -166,7 +166,7 @@ static int dw_i2c_probe(struct platform_device *pdev) /* fast mode by default because of legacy reasons */ clk_freq = 400000; - if (ACPI_COMPANION(&pdev->dev)) { + if (has_acpi_companion(&pdev->dev)) { dw_i2c_acpi_configure(pdev); } else if (pdev->dev.of_node) { of_property_read_u32(pdev->dev.of_node, @@ -286,7 +286,7 @@ static int dw_i2c_remove(struct platform_device *pdev) pm_runtime_put(&pdev->dev); pm_runtime_disable(&pdev->dev); - if (ACPI_COMPANION(&pdev->dev)) + if (has_acpi_companion(&pdev->dev)) dw_i2c_acpi_unconfigure(pdev); return 0; diff --git a/drivers/iommu/intel-iommu.c b/drivers/iommu/intel-iommu.c index ae4c1a854e57..591b84331315 100644 --- a/drivers/iommu/intel-iommu.c +++ b/drivers/iommu/intel-iommu.c @@ -684,7 +684,7 @@ static struct intel_iommu *device_to_iommu(struct device *dev, u8 *bus, u8 *devf if (dev_is_pci(dev)) { pdev = to_pci_dev(dev); segment = pci_domain_nr(pdev->bus); - } else if (ACPI_COMPANION(dev)) + } else if (has_acpi_companion(dev)) dev = &ACPI_COMPANION(dev)->dev; rcu_read_lock(); diff --git a/include/linux/acpi.h b/include/linux/acpi.h index 402ddbdc2da1..ec488d03b518 100644 --- a/include/linux/acpi.h +++ b/include/linux/acpi.h @@ -58,6 +58,11 @@ static inline acpi_handle acpi_device_handle(struct acpi_device *adev) acpi_fwnode_handle(adev) : NULL #define ACPI_HANDLE(dev) acpi_device_handle(ACPI_COMPANION(dev)) +static inline bool has_acpi_companion(struct device *dev) +{ + return is_acpi_node(dev->fwnode); +} + static inline void acpi_preset_companion(struct device *dev, struct acpi_device *parent, u64 addr) { @@ -472,6 +477,11 @@ static inline struct fwnode_handle *acpi_fwnode_handle(struct acpi_device *adev) return NULL; } +static inline bool has_acpi_companion(struct device *dev) +{ + return false; +} + static inline const char *acpi_dev_name(struct acpi_device *adev) { return NULL; -- cgit v1.2.3 From 72000df2c01d6927319ad7e3f43460f6d0227de5 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Mon, 16 Mar 2015 16:11:21 -0700 Subject: Bluetooth: Add support for Local OOB Extended Data Update events When a different user requests a new set of local out-of-band data, then inform all previous users that the data has been updated. To limit the scope of users, the updates are limited to previous users. If a user has never requested out-of-band data, it will also not see the update. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- include/net/bluetooth/hci.h | 1 + include/net/bluetooth/mgmt.h | 7 +++++++ net/bluetooth/mgmt.c | 16 ++++++++++++++++ 3 files changed, 24 insertions(+) (limited to 'include') diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index ce757303dc07..a6ea156dc7e9 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -186,6 +186,7 @@ enum { HCI_MGMT_UNCONF_INDEX_EVENTS, HCI_MGMT_EXT_INDEX_EVENTS, HCI_MGMT_GENERIC_EVENTS, + HCI_MGMT_OOB_DATA_EVENTS, }; /* diff --git a/include/net/bluetooth/mgmt.h b/include/net/bluetooth/mgmt.h index 543c1ba3d892..a1a68671bf88 100644 --- a/include/net/bluetooth/mgmt.h +++ b/include/net/bluetooth/mgmt.h @@ -735,3 +735,10 @@ struct mgmt_ev_ext_index { #define MGMT_EV_EXT_INDEX_ADDED 0x0020 #define MGMT_EV_EXT_INDEX_REMOVED 0x0021 + +#define MGMT_EV_LOCAL_OOB_DATA_UPDATED 0x0022 +struct mgmt_ev_local_oob_data_updated { + __u8 type; + __le16 eir_len; + __u8 eir[0]; +} __packed; diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 7fa3c4b8384b..72e41d29e301 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -133,6 +133,7 @@ static const u16 mgmt_events[] = { MGMT_EV_NEW_CONFIG_OPTIONS, MGMT_EV_EXT_INDEX_ADDED, MGMT_EV_EXT_INDEX_REMOVED, + MGMT_EV_LOCAL_OOB_DATA_UPDATED, }; #define CACHE_TIMEOUT msecs_to_jiffies(2 * 1000) @@ -262,6 +263,13 @@ static int mgmt_index_event(u16 event, struct hci_dev *hdev, void *data, flag, NULL); } +static int mgmt_limited_event(u16 event, struct hci_dev *hdev, void *data, + u16 len, int flag, struct sock *skip_sk) +{ + return mgmt_send_event(event, hdev, HCI_CHANNEL_CONTROL, data, len, + flag, skip_sk); +} + static int mgmt_generic_event(u16 event, struct hci_dev *hdev, void *data, u16 len, struct sock *skip_sk) { @@ -6387,8 +6395,16 @@ static int read_local_oob_ext_data(struct sock *sk, struct hci_dev *hdev, hci_dev_unlock(hdev); + hci_sock_set_flag(sk, HCI_MGMT_OOB_DATA_EVENTS); + err = mgmt_cmd_complete(sk, hdev->id, MGMT_OP_READ_LOCAL_OOB_EXT_DATA, MGMT_STATUS_SUCCESS, rp, sizeof(*rp) + eir_len); + if (err < 0) + goto done; + + err = mgmt_limited_event(MGMT_EV_LOCAL_OOB_DATA_UPDATED, hdev, + rp, sizeof(*rp) + eir_len, + HCI_MGMT_OOB_DATA_EVENTS, sk); done: kfree(rp); -- cgit v1.2.3 From 937abe88aea3161cd3a563e577fc9cf4522c7aad Mon Sep 17 00:00:00 2001 From: Kedareswara rao Appana Date: Mon, 2 Mar 2015 23:24:24 +0530 Subject: dmaengine: xilinx-dma: move header file to common location This patch moves the xilinx_dma.h header file to the include/linux/dma. Signed-off-by: Kedareswara rao Appana Signed-off-by: Vinod Koul --- drivers/dma/xilinx/xilinx_vdma.c | 2 +- include/linux/amba/xilinx_dma.h | 47 ---------------------------------------- include/linux/dma/xilinx_dma.h | 47 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 48 insertions(+), 48 deletions(-) delete mode 100644 include/linux/amba/xilinx_dma.h create mode 100644 include/linux/dma/xilinx_dma.h (limited to 'include') diff --git a/drivers/dma/xilinx/xilinx_vdma.c b/drivers/dma/xilinx/xilinx_vdma.c index bdd2a5dd7220..d8434d465885 100644 --- a/drivers/dma/xilinx/xilinx_vdma.c +++ b/drivers/dma/xilinx/xilinx_vdma.c @@ -22,9 +22,9 @@ * (at your option) any later version. */ -#include #include #include +#include #include #include #include diff --git a/include/linux/amba/xilinx_dma.h b/include/linux/amba/xilinx_dma.h deleted file mode 100644 index 34b98f276ed0..000000000000 --- a/include/linux/amba/xilinx_dma.h +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Xilinx DMA Engine drivers support header file - * - * Copyright (C) 2010-2014 Xilinx, Inc. All rights reserved. - * - * This is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - */ - -#ifndef __DMA_XILINX_DMA_H -#define __DMA_XILINX_DMA_H - -#include -#include - -/** - * struct xilinx_vdma_config - VDMA Configuration structure - * @frm_dly: Frame delay - * @gen_lock: Whether in gen-lock mode - * @master: Master that it syncs to - * @frm_cnt_en: Enable frame count enable - * @park: Whether wants to park - * @park_frm: Frame to park on - * @coalesc: Interrupt coalescing threshold - * @delay: Delay counter - * @reset: Reset Channel - * @ext_fsync: External Frame Sync source - */ -struct xilinx_vdma_config { - int frm_dly; - int gen_lock; - int master; - int frm_cnt_en; - int park; - int park_frm; - int coalesc; - int delay; - int reset; - int ext_fsync; -}; - -int xilinx_vdma_channel_set_config(struct dma_chan *dchan, - struct xilinx_vdma_config *cfg); - -#endif diff --git a/include/linux/dma/xilinx_dma.h b/include/linux/dma/xilinx_dma.h new file mode 100644 index 000000000000..34b98f276ed0 --- /dev/null +++ b/include/linux/dma/xilinx_dma.h @@ -0,0 +1,47 @@ +/* + * Xilinx DMA Engine drivers support header file + * + * Copyright (C) 2010-2014 Xilinx, Inc. All rights reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#ifndef __DMA_XILINX_DMA_H +#define __DMA_XILINX_DMA_H + +#include +#include + +/** + * struct xilinx_vdma_config - VDMA Configuration structure + * @frm_dly: Frame delay + * @gen_lock: Whether in gen-lock mode + * @master: Master that it syncs to + * @frm_cnt_en: Enable frame count enable + * @park: Whether wants to park + * @park_frm: Frame to park on + * @coalesc: Interrupt coalescing threshold + * @delay: Delay counter + * @reset: Reset Channel + * @ext_fsync: External Frame Sync source + */ +struct xilinx_vdma_config { + int frm_dly; + int gen_lock; + int master; + int frm_cnt_en; + int park; + int park_frm; + int coalesc; + int delay; + int reset; + int ext_fsync; +}; + +int xilinx_vdma_channel_set_config(struct dma_chan *dchan, + struct xilinx_vdma_config *cfg); + +#endif -- cgit v1.2.3 From 71be3423a62be548c56bab5b818e1a1383e659d2 Mon Sep 17 00:00:00 2001 From: Alex Williamson Date: Tue, 17 Mar 2015 08:33:38 -0600 Subject: vfio: Split virqfd into a separate module for vfio bus drivers An unintended consequence of commit 42ac9bd18d4f ("vfio: initialize the virqfd workqueue in VFIO generic code") is that the vfio module is renamed to vfio_core so that it can include both vfio and virqfd. That's a user visible change that may break module loading scritps and it imposes eventfd support as a dependency on the core vfio code, which it's really not. virqfd is intended to be provided as a service to vfio bus drivers, so instead of wrapping it into vfio.ko, we can make it a stand-alone module toggled by vfio bus drivers. This has the additional benefit of removing initialization and exit from the core vfio code. Signed-off-by: Alex Williamson --- drivers/vfio/Kconfig | 5 +++++ drivers/vfio/Makefile | 5 +++-- drivers/vfio/pci/Kconfig | 1 + drivers/vfio/platform/Kconfig | 1 + drivers/vfio/vfio.c | 8 -------- drivers/vfio/virqfd.c | 17 +++++++++++++++-- include/linux/vfio.h | 2 -- 7 files changed, 25 insertions(+), 14 deletions(-) (limited to 'include') diff --git a/drivers/vfio/Kconfig b/drivers/vfio/Kconfig index d5322a434a7a..7d092ddc8119 100644 --- a/drivers/vfio/Kconfig +++ b/drivers/vfio/Kconfig @@ -13,6 +13,11 @@ config VFIO_SPAPR_EEH depends on EEH && VFIO_IOMMU_SPAPR_TCE default n +config VFIO_VIRQFD + tristate + depends on VFIO && EVENTFD + default n + menuconfig VFIO tristate "VFIO Non-Privileged userspace driver framework" depends on IOMMU_API diff --git a/drivers/vfio/Makefile b/drivers/vfio/Makefile index d798b0959603..7b8a31f63fea 100644 --- a/drivers/vfio/Makefile +++ b/drivers/vfio/Makefile @@ -1,6 +1,7 @@ -vfio_core-y := vfio.o virqfd.o +vfio_virqfd-y := virqfd.o -obj-$(CONFIG_VFIO) += vfio_core.o +obj-$(CONFIG_VFIO) += vfio.o +obj-$(CONFIG_VFIO_VIRQFD) += vfio_virqfd.o obj-$(CONFIG_VFIO_IOMMU_TYPE1) += vfio_iommu_type1.o obj-$(CONFIG_VFIO_IOMMU_SPAPR_TCE) += vfio_iommu_spapr_tce.o obj-$(CONFIG_VFIO_SPAPR_EEH) += vfio_spapr_eeh.o diff --git a/drivers/vfio/pci/Kconfig b/drivers/vfio/pci/Kconfig index c6bb5da2d2a7..579d83bf5358 100644 --- a/drivers/vfio/pci/Kconfig +++ b/drivers/vfio/pci/Kconfig @@ -1,6 +1,7 @@ config VFIO_PCI tristate "VFIO support for PCI devices" depends on VFIO && PCI && EVENTFD + select VFIO_VIRQFD help Support for the PCI VFIO bus driver. This is required to make use of PCI drivers using the VFIO framework. diff --git a/drivers/vfio/platform/Kconfig b/drivers/vfio/platform/Kconfig index c0a3bff8baee..9a4403e2a36c 100644 --- a/drivers/vfio/platform/Kconfig +++ b/drivers/vfio/platform/Kconfig @@ -1,6 +1,7 @@ config VFIO_PLATFORM tristate "VFIO support for platform devices" depends on VFIO && EVENTFD && ARM + select VFIO_VIRQFD help Support for platform devices with VFIO. This is required to make use of platform devices present on the system using the VFIO diff --git a/drivers/vfio/vfio.c b/drivers/vfio/vfio.c index 86aac7e4a050..0d336625ac71 100644 --- a/drivers/vfio/vfio.c +++ b/drivers/vfio/vfio.c @@ -1552,11 +1552,6 @@ static int __init vfio_init(void) if (ret) goto err_cdev_add; - /* Start the virqfd cleanup handler used by some VFIO bus drivers */ - ret = vfio_virqfd_init(); - if (ret) - goto err_virqfd; - pr_info(DRIVER_DESC " version: " DRIVER_VERSION "\n"); /* @@ -1569,8 +1564,6 @@ static int __init vfio_init(void) return 0; -err_virqfd: - cdev_del(&vfio.group_cdev); err_cdev_add: unregister_chrdev_region(vfio.group_devt, MINORMASK); err_alloc_chrdev: @@ -1585,7 +1578,6 @@ static void __exit vfio_cleanup(void) { WARN_ON(!list_empty(&vfio.group_list)); - vfio_virqfd_exit(); idr_destroy(&vfio.group_idr); cdev_del(&vfio.group_cdev); unregister_chrdev_region(vfio.group_devt, MINORMASK); diff --git a/drivers/vfio/virqfd.c b/drivers/vfio/virqfd.c index 3d19aaf0e6c9..27c89cd5d70b 100644 --- a/drivers/vfio/virqfd.c +++ b/drivers/vfio/virqfd.c @@ -13,12 +13,17 @@ #include #include #include +#include #include +#define DRIVER_VERSION "0.1" +#define DRIVER_AUTHOR "Alex Williamson " +#define DRIVER_DESC "IRQFD support for VFIO bus drivers" + static struct workqueue_struct *vfio_irqfd_cleanup_wq; static DEFINE_SPINLOCK(virqfd_lock); -int __init vfio_virqfd_init(void) +static int __init vfio_virqfd_init(void) { vfio_irqfd_cleanup_wq = create_singlethread_workqueue("vfio-irqfd-cleanup"); @@ -28,7 +33,7 @@ int __init vfio_virqfd_init(void) return 0; } -void vfio_virqfd_exit(void) +static void __exit vfio_virqfd_exit(void) { destroy_workqueue(vfio_irqfd_cleanup_wq); } @@ -211,3 +216,11 @@ void vfio_virqfd_disable(struct virqfd **pvirqfd) flush_workqueue(vfio_irqfd_cleanup_wq); } EXPORT_SYMBOL_GPL(vfio_virqfd_disable); + +module_init(vfio_virqfd_init); +module_exit(vfio_virqfd_exit); + +MODULE_VERSION(DRIVER_VERSION); +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_DESCRIPTION(DRIVER_DESC); diff --git a/include/linux/vfio.h b/include/linux/vfio.h index 683b5146022e..cbed15f194e0 100644 --- a/include/linux/vfio.h +++ b/include/linux/vfio.h @@ -142,8 +142,6 @@ struct virqfd { struct virqfd **pvirqfd; }; -extern int vfio_virqfd_init(void); -extern void vfio_virqfd_exit(void); extern int vfio_virqfd_enable(void *opaque, int (*handler)(void *, void *), void (*thread)(void *, void *), -- cgit v1.2.3 From 695c4cb61926ddef2481dbcefef44498a7c1e5b5 Mon Sep 17 00:00:00 2001 From: Jakub Pawlowski Date: Tue, 17 Mar 2015 09:04:12 -0700 Subject: Bluetooth: Introduce HCI_QUIRK_SIMULTANEOUS_DISCOVERY Some controllers allow both LE scan and BR/EDR inquiry to run at the same time, while others allow only one, LE SCAN or BR/EDR inquiry at given time. Since this is specific to each controller, add a new quirk setting that allows drivers to tell the core wether given controller can do both LE scan and BR/EDR inquiry at same time. Signed-off-by: Jakub Pawlowski Signed-off-by: Johan Hedberg --- include/net/bluetooth/hci.h | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'include') diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index a6ea156dc7e9..06e7eee31ce4 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -160,6 +160,14 @@ enum { * during the hdev->setup vendor callback. */ HCI_QUIRK_STRICT_DUPLICATE_FILTER, + + /* When this quirk is set, LE scan and BR/EDR inquiry is done + * simultaneously, otherwise it's interleaved. + * + * This quirk can be set before hci_register_dev is called or + * during the hdev->setup vendor callback. + */ + HCI_QUIRK_SIMULTANEOUS_DISCOVERY, }; /* HCI device flags */ -- cgit v1.2.3 From d0f172b14afa7e0d8a19db5baa07e20b3ec8dcc8 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Tue, 17 Mar 2015 13:48:46 +0200 Subject: Bluetooth: Add helper to get HCI channel of a socket We'll need to have access to which HCI channel a socket is bound to, in order to manage pending mgmt commands in clean way. This patch adds a helper for the purpose. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- include/net/bluetooth/bluetooth.h | 1 + net/bluetooth/hci_sock.c | 5 +++++ 2 files changed, 6 insertions(+) (limited to 'include') diff --git a/include/net/bluetooth/bluetooth.h b/include/net/bluetooth/bluetooth.h index d871ba313f64..33a5e00025aa 100644 --- a/include/net/bluetooth/bluetooth.h +++ b/include/net/bluetooth/bluetooth.h @@ -338,6 +338,7 @@ int bt_to_errno(__u16 code); void hci_sock_set_flag(struct sock *sk, int nr); void hci_sock_clear_flag(struct sock *sk, int nr); int hci_sock_test_flag(struct sock *sk, int nr); +unsigned short hci_sock_get_channel(struct sock *sk); int hci_sock_init(void); void hci_sock_cleanup(void); diff --git a/net/bluetooth/hci_sock.c b/net/bluetooth/hci_sock.c index 7c719602dbca..9ba1a2667eaa 100644 --- a/net/bluetooth/hci_sock.c +++ b/net/bluetooth/hci_sock.c @@ -65,6 +65,11 @@ int hci_sock_test_flag(struct sock *sk, int nr) return test_bit(nr, &hci_pi(sk)->flags); } +unsigned short hci_sock_get_channel(struct sock *sk) +{ + return hci_pi(sk)->channel; +} + static inline int hci_test_bit(int nr, const void *addr) { return *((const __u32 *) addr + (nr >> 5)) & ((__u32) 1 << (nr & 31)); -- cgit v1.2.3 From 88b94ce925cb103851f39bfa7e23e09823573d30 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Tue, 17 Mar 2015 13:48:49 +0200 Subject: Bluetooth: Add hdev_init callback for HCI channels In order to make the mgmt command handling more generic we can't have a direct call to mgmt_init_hdev() from mgmt_control(). This patch adds a new callback to struct hci_mgmt_chan. And sets it to point to the mgmt_init_hdev() function for the HCI_CHANNEL_CONTROL instance. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- include/net/bluetooth/hci_core.h | 1 + net/bluetooth/mgmt.c | 5 +++-- 2 files changed, 4 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index e4dc18eed446..93e7b2b05a17 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -1305,6 +1305,7 @@ struct hci_mgmt_chan { unsigned short channel; size_t handler_count; const struct hci_mgmt_handler *handlers; + void (*hdev_init) (struct sock *sk, struct hci_dev *hdev); }; int hci_mgmt_chan_register(struct hci_mgmt_chan *c); diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 915a2a1f0a71..ac897e676d5e 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -6493,8 +6493,8 @@ int mgmt_control(struct hci_mgmt_chan *chan, struct sock *sk, goto done; } - if (hdev) - mgmt_init_hdev(sk, hdev); + if (hdev && chan->hdev_init) + chan->hdev_init(sk, hdev); cp = buf + sizeof(*hdr); @@ -7706,6 +7706,7 @@ static struct hci_mgmt_chan chan = { .channel = HCI_CHANNEL_CONTROL, .handler_count = ARRAY_SIZE(mgmt_handlers), .handlers = mgmt_handlers, + .hdev_init = mgmt_init_hdev, }; int mgmt_init(void) -- cgit v1.2.3 From fa4335d71a1408d0112c15874538f3f4e153ba01 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Tue, 17 Mar 2015 13:48:50 +0200 Subject: Bluetooth: Move generic mgmt command dispatcher to hci_sock.c The mgmt.c file should be reserved purely for HCI_CHANNEL_CONTROL. The mgmt_control() function in it is already completely generic and has a single user in hci_sock.c. This patch moves the function there and renames it a bit more appropriately to hci_mgmt_cmd() (as it's a command dispatcher). Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- include/net/bluetooth/hci_core.h | 3 - net/bluetooth/hci_sock.c | 116 ++++++++++++++++++++++++++++++++++++++- net/bluetooth/mgmt.c | 111 ------------------------------------- 3 files changed, 115 insertions(+), 115 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 93e7b2b05a17..b65c53de6a69 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -1330,9 +1330,6 @@ void hci_mgmt_chan_unregister(struct hci_mgmt_chan *c); #define DISCOV_BREDR_INQUIRY_LEN 0x08 #define DISCOV_LE_RESTART_DELAY msecs_to_jiffies(200) /* msec */ -int mgmt_control(struct hci_mgmt_chan *chan, struct sock *sk, - struct msghdr *msg, size_t msglen); - int mgmt_new_settings(struct hci_dev *hdev); void mgmt_index_added(struct hci_dev *hdev); void mgmt_index_removed(struct hci_dev *hdev); diff --git a/net/bluetooth/hci_sock.c b/net/bluetooth/hci_sock.c index 9ba1a2667eaa..85a44a7dc150 100644 --- a/net/bluetooth/hci_sock.c +++ b/net/bluetooth/hci_sock.c @@ -30,6 +30,9 @@ #include #include #include +#include + +#include "mgmt_util.h" static LIST_HEAD(mgmt_chan_list); static DEFINE_MUTEX(mgmt_chan_list_lock); @@ -951,6 +954,117 @@ static int hci_sock_recvmsg(struct socket *sock, struct msghdr *msg, size_t len, return err ? : copied; } +static int hci_mgmt_cmd(struct hci_mgmt_chan *chan, struct sock *sk, + struct msghdr *msg, size_t msglen) +{ + void *buf; + u8 *cp; + struct mgmt_hdr *hdr; + u16 opcode, index, len; + struct hci_dev *hdev = NULL; + const struct hci_mgmt_handler *handler; + bool var_len, no_hdev; + int err; + + BT_DBG("got %zu bytes", msglen); + + if (msglen < sizeof(*hdr)) + return -EINVAL; + + buf = kmalloc(msglen, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + if (memcpy_from_msg(buf, msg, msglen)) { + err = -EFAULT; + goto done; + } + + hdr = buf; + opcode = __le16_to_cpu(hdr->opcode); + index = __le16_to_cpu(hdr->index); + len = __le16_to_cpu(hdr->len); + + if (len != msglen - sizeof(*hdr)) { + err = -EINVAL; + goto done; + } + + if (opcode >= chan->handler_count || + chan->handlers[opcode].func == NULL) { + BT_DBG("Unknown op %u", opcode); + err = mgmt_cmd_status(sk, index, opcode, + MGMT_STATUS_UNKNOWN_COMMAND); + goto done; + } + + handler = &chan->handlers[opcode]; + + if (!hci_sock_test_flag(sk, HCI_SOCK_TRUSTED) && + !(handler->flags & HCI_MGMT_UNTRUSTED)) { + err = mgmt_cmd_status(sk, index, opcode, + MGMT_STATUS_PERMISSION_DENIED); + goto done; + } + + if (index != MGMT_INDEX_NONE) { + hdev = hci_dev_get(index); + if (!hdev) { + err = mgmt_cmd_status(sk, index, opcode, + MGMT_STATUS_INVALID_INDEX); + goto done; + } + + if (hci_dev_test_flag(hdev, HCI_SETUP) || + hci_dev_test_flag(hdev, HCI_CONFIG) || + hci_dev_test_flag(hdev, HCI_USER_CHANNEL)) { + err = mgmt_cmd_status(sk, index, opcode, + MGMT_STATUS_INVALID_INDEX); + goto done; + } + + if (hci_dev_test_flag(hdev, HCI_UNCONFIGURED) && + !(handler->flags & HCI_MGMT_UNCONFIGURED)) { + err = mgmt_cmd_status(sk, index, opcode, + MGMT_STATUS_INVALID_INDEX); + goto done; + } + } + + no_hdev = (handler->flags & HCI_MGMT_NO_HDEV); + if (no_hdev != !hdev) { + err = mgmt_cmd_status(sk, index, opcode, + MGMT_STATUS_INVALID_INDEX); + goto done; + } + + var_len = (handler->flags & HCI_MGMT_VAR_LEN); + if ((var_len && len < handler->data_len) || + (!var_len && len != handler->data_len)) { + err = mgmt_cmd_status(sk, index, opcode, + MGMT_STATUS_INVALID_PARAMS); + goto done; + } + + if (hdev && chan->hdev_init) + chan->hdev_init(sk, hdev); + + cp = buf + sizeof(*hdr); + + err = handler->func(sk, hdev, cp, len); + if (err < 0) + goto done; + + err = msglen; + +done: + if (hdev) + hci_dev_put(hdev); + + kfree(buf); + return err; +} + static int hci_sock_sendmsg(struct socket *sock, struct msghdr *msg, size_t len) { @@ -984,7 +1098,7 @@ static int hci_sock_sendmsg(struct socket *sock, struct msghdr *msg, mutex_lock(&mgmt_chan_list_lock); chan = __hci_mgmt_chan_find(hci_pi(sk)->channel); if (chan) - err = mgmt_control(chan, sk, msg, len); + err = hci_mgmt_cmd(chan, sk, msg, len); else err = -EINVAL; diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index ac897e676d5e..f3a957905193 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -6401,117 +6401,6 @@ static const struct hci_mgmt_handler mgmt_handlers[] = { { read_adv_features, MGMT_READ_ADV_FEATURES_SIZE }, }; -int mgmt_control(struct hci_mgmt_chan *chan, struct sock *sk, - struct msghdr *msg, size_t msglen) -{ - void *buf; - u8 *cp; - struct mgmt_hdr *hdr; - u16 opcode, index, len; - struct hci_dev *hdev = NULL; - const struct hci_mgmt_handler *handler; - bool var_len, no_hdev; - int err; - - BT_DBG("got %zu bytes", msglen); - - if (msglen < sizeof(*hdr)) - return -EINVAL; - - buf = kmalloc(msglen, GFP_KERNEL); - if (!buf) - return -ENOMEM; - - if (memcpy_from_msg(buf, msg, msglen)) { - err = -EFAULT; - goto done; - } - - hdr = buf; - opcode = __le16_to_cpu(hdr->opcode); - index = __le16_to_cpu(hdr->index); - len = __le16_to_cpu(hdr->len); - - if (len != msglen - sizeof(*hdr)) { - err = -EINVAL; - goto done; - } - - if (opcode >= chan->handler_count || - chan->handlers[opcode].func == NULL) { - BT_DBG("Unknown op %u", opcode); - err = mgmt_cmd_status(sk, index, opcode, - MGMT_STATUS_UNKNOWN_COMMAND); - goto done; - } - - handler = &chan->handlers[opcode]; - - if (!hci_sock_test_flag(sk, HCI_SOCK_TRUSTED) && - !(handler->flags & HCI_MGMT_UNTRUSTED)) { - err = mgmt_cmd_status(sk, index, opcode, - MGMT_STATUS_PERMISSION_DENIED); - goto done; - } - - if (index != MGMT_INDEX_NONE) { - hdev = hci_dev_get(index); - if (!hdev) { - err = mgmt_cmd_status(sk, index, opcode, - MGMT_STATUS_INVALID_INDEX); - goto done; - } - - if (hci_dev_test_flag(hdev, HCI_SETUP) || - hci_dev_test_flag(hdev, HCI_CONFIG) || - hci_dev_test_flag(hdev, HCI_USER_CHANNEL)) { - err = mgmt_cmd_status(sk, index, opcode, - MGMT_STATUS_INVALID_INDEX); - goto done; - } - - if (hci_dev_test_flag(hdev, HCI_UNCONFIGURED) && - !(handler->flags & HCI_MGMT_UNCONFIGURED)) { - err = mgmt_cmd_status(sk, index, opcode, - MGMT_STATUS_INVALID_INDEX); - goto done; - } - } - - no_hdev = (handler->flags & HCI_MGMT_NO_HDEV); - if (no_hdev != !hdev) { - err = mgmt_cmd_status(sk, index, opcode, - MGMT_STATUS_INVALID_INDEX); - goto done; - } - - var_len = (handler->flags & HCI_MGMT_VAR_LEN); - if ((var_len && len < handler->data_len) || - (!var_len && len != handler->data_len)) { - err = mgmt_cmd_status(sk, index, opcode, - MGMT_STATUS_INVALID_PARAMS); - goto done; - } - - if (hdev && chan->hdev_init) - chan->hdev_init(sk, hdev); - - cp = buf + sizeof(*hdr); - - err = handler->func(sk, hdev, cp, len); - if (err < 0) - goto done; - - err = msglen; - -done: - if (hdev) - hci_dev_put(hdev); - - kfree(buf); - return err; -} - void mgmt_index_added(struct hci_dev *hdev) { struct mgmt_ev_ext_index ev; -- cgit v1.2.3 From c24973957975403521ca76a776c2dfd12fbe9add Mon Sep 17 00:00:00 2001 From: Alexei Starovoitov Date: Mon, 16 Mar 2015 18:06:02 -0700 Subject: bpf: allow BPF programs access 'protocol' and 'vlan_tci' fields as a follow on to patch 70006af95515 ("bpf: allow eBPF access skb fields") this patch allows 'protocol' and 'vlan_tci' fields to be accessible from extended BPF programs. The usage of 'protocol', 'vlan_present' and 'vlan_tci' fields is the same as corresponding SKF_AD_PROTOCOL, SKF_AD_VLAN_TAG_PRESENT and SKF_AD_VLAN_TAG accesses in classic BPF. Signed-off-by: Alexei Starovoitov Signed-off-by: David S. Miller --- include/uapi/linux/bpf.h | 3 ++ net/core/filter.c | 72 +++++++++++++++++++++++++++++++-------------- samples/bpf/test_verifier.c | 9 ++++++ 3 files changed, 62 insertions(+), 22 deletions(-) (limited to 'include') diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h index 929545a27546..1623047af463 100644 --- a/include/uapi/linux/bpf.h +++ b/include/uapi/linux/bpf.h @@ -178,6 +178,9 @@ struct __sk_buff { __u32 pkt_type; __u32 mark; __u32 queue_mapping; + __u32 protocol; + __u32 vlan_present; + __u32 vlan_tci; }; #endif /* _UAPI__LINUX_BPF_H__ */ diff --git a/net/core/filter.c b/net/core/filter.c index 4e9dd0ad0d5b..b95ae7fe7e4f 100644 --- a/net/core/filter.c +++ b/net/core/filter.c @@ -177,6 +177,35 @@ static u32 convert_skb_access(int skb_field, int dst_reg, int src_reg, *insn++ = BPF_LDX_MEM(BPF_H, dst_reg, src_reg, offsetof(struct sk_buff, queue_mapping)); break; + + case SKF_AD_PROTOCOL: + BUILD_BUG_ON(FIELD_SIZEOF(struct sk_buff, protocol) != 2); + + /* dst_reg = *(u16 *) (src_reg + offsetof(protocol)) */ + *insn++ = BPF_LDX_MEM(BPF_H, dst_reg, src_reg, + offsetof(struct sk_buff, protocol)); + /* dst_reg = ntohs(dst_reg) [emitting a nop or swap16] */ + *insn++ = BPF_ENDIAN(BPF_FROM_BE, dst_reg, 16); + break; + + case SKF_AD_VLAN_TAG: + case SKF_AD_VLAN_TAG_PRESENT: + BUILD_BUG_ON(FIELD_SIZEOF(struct sk_buff, vlan_tci) != 2); + BUILD_BUG_ON(VLAN_TAG_PRESENT != 0x1000); + + /* dst_reg = *(u16 *) (src_reg + offsetof(vlan_tci)) */ + *insn++ = BPF_LDX_MEM(BPF_H, dst_reg, src_reg, + offsetof(struct sk_buff, vlan_tci)); + if (skb_field == SKF_AD_VLAN_TAG) { + *insn++ = BPF_ALU32_IMM(BPF_AND, dst_reg, + ~VLAN_TAG_PRESENT); + } else { + /* dst_reg >>= 12 */ + *insn++ = BPF_ALU32_IMM(BPF_RSH, dst_reg, 12); + /* dst_reg &= 1 */ + *insn++ = BPF_ALU32_IMM(BPF_AND, dst_reg, 1); + } + break; } return insn - insn_buf; @@ -190,13 +219,8 @@ static bool convert_bpf_extensions(struct sock_filter *fp, switch (fp->k) { case SKF_AD_OFF + SKF_AD_PROTOCOL: - BUILD_BUG_ON(FIELD_SIZEOF(struct sk_buff, protocol) != 2); - - /* A = *(u16 *) (CTX + offsetof(protocol)) */ - *insn++ = BPF_LDX_MEM(BPF_H, BPF_REG_A, BPF_REG_CTX, - offsetof(struct sk_buff, protocol)); - /* A = ntohs(A) [emitting a nop or swap16] */ - *insn = BPF_ENDIAN(BPF_FROM_BE, BPF_REG_A, 16); + cnt = convert_skb_access(SKF_AD_PROTOCOL, BPF_REG_A, BPF_REG_CTX, insn); + insn += cnt - 1; break; case SKF_AD_OFF + SKF_AD_PKTTYPE: @@ -242,22 +266,15 @@ static bool convert_bpf_extensions(struct sock_filter *fp, break; case SKF_AD_OFF + SKF_AD_VLAN_TAG: - case SKF_AD_OFF + SKF_AD_VLAN_TAG_PRESENT: - BUILD_BUG_ON(FIELD_SIZEOF(struct sk_buff, vlan_tci) != 2); - BUILD_BUG_ON(VLAN_TAG_PRESENT != 0x1000); + cnt = convert_skb_access(SKF_AD_VLAN_TAG, + BPF_REG_A, BPF_REG_CTX, insn); + insn += cnt - 1; + break; - /* A = *(u16 *) (CTX + offsetof(vlan_tci)) */ - *insn++ = BPF_LDX_MEM(BPF_H, BPF_REG_A, BPF_REG_CTX, - offsetof(struct sk_buff, vlan_tci)); - if (fp->k == SKF_AD_OFF + SKF_AD_VLAN_TAG) { - *insn = BPF_ALU32_IMM(BPF_AND, BPF_REG_A, - ~VLAN_TAG_PRESENT); - } else { - /* A >>= 12 */ - *insn++ = BPF_ALU32_IMM(BPF_RSH, BPF_REG_A, 12); - /* A &= 1 */ - *insn = BPF_ALU32_IMM(BPF_AND, BPF_REG_A, 1); - } + case SKF_AD_OFF + SKF_AD_VLAN_TAG_PRESENT: + cnt = convert_skb_access(SKF_AD_VLAN_TAG_PRESENT, + BPF_REG_A, BPF_REG_CTX, insn); + insn += cnt - 1; break; case SKF_AD_OFF + SKF_AD_PAY_OFFSET: @@ -1215,6 +1232,17 @@ static u32 sk_filter_convert_ctx_access(int dst_reg, int src_reg, int ctx_off, case offsetof(struct __sk_buff, queue_mapping): return convert_skb_access(SKF_AD_QUEUE, dst_reg, src_reg, insn); + + case offsetof(struct __sk_buff, protocol): + return convert_skb_access(SKF_AD_PROTOCOL, dst_reg, src_reg, insn); + + case offsetof(struct __sk_buff, vlan_present): + return convert_skb_access(SKF_AD_VLAN_TAG_PRESENT, + dst_reg, src_reg, insn); + + case offsetof(struct __sk_buff, vlan_tci): + return convert_skb_access(SKF_AD_VLAN_TAG, + dst_reg, src_reg, insn); } return insn - insn_buf; diff --git a/samples/bpf/test_verifier.c b/samples/bpf/test_verifier.c index df6dbb6576f6..75d561f9fd6a 100644 --- a/samples/bpf/test_verifier.c +++ b/samples/bpf/test_verifier.c @@ -658,6 +658,15 @@ static struct bpf_test tests[] = { BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_1, offsetof(struct __sk_buff, queue_mapping)), BPF_JMP_IMM(BPF_JGE, BPF_REG_0, 0, 0), + BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_1, + offsetof(struct __sk_buff, protocol)), + BPF_JMP_IMM(BPF_JGE, BPF_REG_0, 0, 0), + BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_1, + offsetof(struct __sk_buff, vlan_present)), + BPF_JMP_IMM(BPF_JGE, BPF_REG_0, 0, 0), + BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_1, + offsetof(struct __sk_buff, vlan_tci)), + BPF_JMP_IMM(BPF_JGE, BPF_REG_0, 0, 0), BPF_EXIT_INSN(), }, .result = ACCEPT, -- cgit v1.2.3 From adc17d6a6ca08d11f70f6c49f3d40b87b68fe53f Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Mon, 16 Mar 2015 21:06:18 -0700 Subject: inet: move ir_mark to fill a hole On 64bit arches, we can save 8 bytes in inet_request_sock by moving ir_mark to fill a hole. While we are at it, inet_request_mark() can get a const qualifier for listener socket. Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- include/net/inet_sock.h | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/include/net/inet_sock.h b/include/net/inet_sock.h index 3d8c09abb097..c9ed91891887 100644 --- a/include/net/inet_sock.h +++ b/include/net/inet_sock.h @@ -94,11 +94,11 @@ struct inet_request_sock { acked : 1, no_srccheck: 1; kmemcheck_bitfield_end(flags); + u32 ir_mark; union { struct ip_options_rcu *opt; struct sk_buff *pktopts; }; - u32 ir_mark; }; static inline struct inet_request_sock *inet_rsk(const struct request_sock *sk) @@ -106,13 +106,12 @@ static inline struct inet_request_sock *inet_rsk(const struct request_sock *sk) return (struct inet_request_sock *)sk; } -static inline u32 inet_request_mark(struct sock *sk, struct sk_buff *skb) +static inline u32 inet_request_mark(const struct sock *sk, struct sk_buff *skb) { - if (!sk->sk_mark && sock_net(sk)->ipv4.sysctl_tcp_fwmark_accept) { + if (!sk->sk_mark && sock_net(sk)->ipv4.sysctl_tcp_fwmark_accept) return skb->mark; - } else { - return sk->sk_mark; - } + + return sk->sk_mark; } struct inet_cork { -- cgit v1.2.3 From 1bfc4438a7ef99e8a6cba0bd3a86249430256621 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Mon, 16 Mar 2015 21:06:19 -0700 Subject: tcp: move tcp_openreq_init() to tcp_input.c This big helper is called once from tcp_conn_request(), there is no point having it in an include. Compiler will inline it anyway. Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- include/net/tcp.h | 25 ------------------------- net/ipv4/tcp_input.c | 25 +++++++++++++++++++++++++ 2 files changed, 25 insertions(+), 25 deletions(-) (limited to 'include') diff --git a/include/net/tcp.h b/include/net/tcp.h index 2e11e38205c2..9fe1d535cd6c 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -1137,31 +1137,6 @@ static inline int tcp_full_space(const struct sock *sk) return tcp_win_from_space(sk->sk_rcvbuf); } -static inline void tcp_openreq_init(struct request_sock *req, - struct tcp_options_received *rx_opt, - struct sk_buff *skb, struct sock *sk) -{ - struct inet_request_sock *ireq = inet_rsk(req); - - req->rcv_wnd = 0; /* So that tcp_send_synack() knows! */ - req->cookie_ts = 0; - tcp_rsk(req)->rcv_isn = TCP_SKB_CB(skb)->seq; - tcp_rsk(req)->rcv_nxt = TCP_SKB_CB(skb)->seq + 1; - tcp_rsk(req)->snt_synack = tcp_time_stamp; - tcp_rsk(req)->last_oow_ack_time = 0; - req->mss = rx_opt->mss_clamp; - req->ts_recent = rx_opt->saw_tstamp ? rx_opt->rcv_tsval : 0; - ireq->tstamp_ok = rx_opt->tstamp_ok; - ireq->sack_ok = rx_opt->sack_ok; - ireq->snd_wscale = rx_opt->snd_wscale; - ireq->wscale_ok = rx_opt->wscale_ok; - ireq->acked = 0; - ireq->ecn_ok = 0; - ireq->ir_rmt_port = tcp_hdr(skb)->source; - ireq->ir_num = ntohs(tcp_hdr(skb)->dest); - ireq->ir_mark = inet_request_mark(sk, skb); -} - extern void tcp_openreq_init_rwin(struct request_sock *req, struct sock *sk, struct dst_entry *dst); diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index 717d437b6ce1..10d6bd93f229 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -5912,6 +5912,31 @@ static void tcp_ecn_create_request(struct request_sock *req, inet_rsk(req)->ecn_ok = 1; } +static void tcp_openreq_init(struct request_sock *req, + const struct tcp_options_received *rx_opt, + struct sk_buff *skb, const struct sock *sk) +{ + struct inet_request_sock *ireq = inet_rsk(req); + + req->rcv_wnd = 0; /* So that tcp_send_synack() knows! */ + req->cookie_ts = 0; + tcp_rsk(req)->rcv_isn = TCP_SKB_CB(skb)->seq; + tcp_rsk(req)->rcv_nxt = TCP_SKB_CB(skb)->seq + 1; + tcp_rsk(req)->snt_synack = tcp_time_stamp; + tcp_rsk(req)->last_oow_ack_time = 0; + req->mss = rx_opt->mss_clamp; + req->ts_recent = rx_opt->saw_tstamp ? rx_opt->rcv_tsval : 0; + ireq->tstamp_ok = rx_opt->tstamp_ok; + ireq->sack_ok = rx_opt->sack_ok; + ireq->snd_wscale = rx_opt->snd_wscale; + ireq->wscale_ok = rx_opt->wscale_ok; + ireq->acked = 0; + ireq->ecn_ok = 0; + ireq->ir_rmt_port = tcp_hdr(skb)->source; + ireq->ir_num = ntohs(tcp_hdr(skb)->dest); + ireq->ir_mark = inet_request_mark(sk, skb); +} + int tcp_conn_request(struct request_sock_ops *rsk_ops, const struct tcp_request_sock_ops *af_ops, struct sock *sk, struct sk_buff *skb) -- cgit v1.2.3 From 7970ddc8f9ffe149b392975da60739ccd1796dea Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Mon, 16 Mar 2015 21:06:20 -0700 Subject: tcp: uninline tcp_oow_rate_limited() tcp_oow_rate_limited() is hardly used in fast path, there is no point inlining it. Signed-of-by: Eric Dumazet Signed-off-by: David S. Miller --- include/net/tcp.h | 32 ++------------------------------ net/ipv4/tcp_input.c | 30 ++++++++++++++++++++++++++++++ 2 files changed, 32 insertions(+), 30 deletions(-) (limited to 'include') diff --git a/include/net/tcp.h b/include/net/tcp.h index 9fe1d535cd6c..5b29835b81d8 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -1216,36 +1216,8 @@ static inline bool tcp_paws_reject(const struct tcp_options_received *rx_opt, return true; } -/* Return true if we're currently rate-limiting out-of-window ACKs and - * thus shouldn't send a dupack right now. We rate-limit dupacks in - * response to out-of-window SYNs or ACKs to mitigate ACK loops or DoS - * attacks that send repeated SYNs or ACKs for the same connection. To - * do this, we do not send a duplicate SYNACK or ACK if the remote - * endpoint is sending out-of-window SYNs or pure ACKs at a high rate. - */ -static inline bool tcp_oow_rate_limited(struct net *net, - const struct sk_buff *skb, - int mib_idx, u32 *last_oow_ack_time) -{ - /* Data packets without SYNs are not likely part of an ACK loop. */ - if ((TCP_SKB_CB(skb)->seq != TCP_SKB_CB(skb)->end_seq) && - !tcp_hdr(skb)->syn) - goto not_rate_limited; - - if (*last_oow_ack_time) { - s32 elapsed = (s32)(tcp_time_stamp - *last_oow_ack_time); - - if (0 <= elapsed && elapsed < sysctl_tcp_invalid_ratelimit) { - NET_INC_STATS_BH(net, mib_idx); - return true; /* rate-limited: don't send yet! */ - } - } - - *last_oow_ack_time = tcp_time_stamp; - -not_rate_limited: - return false; /* not rate-limited: go ahead, send dupack now! */ -} +bool tcp_oow_rate_limited(struct net *net, const struct sk_buff *skb, + int mib_idx, u32 *last_oow_ack_time); static inline void tcp_mib_init(struct net *net) { diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index 10d6bd93f229..7257eb206c07 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -3321,6 +3321,36 @@ static int tcp_ack_update_window(struct sock *sk, const struct sk_buff *skb, u32 return flag; } +/* Return true if we're currently rate-limiting out-of-window ACKs and + * thus shouldn't send a dupack right now. We rate-limit dupacks in + * response to out-of-window SYNs or ACKs to mitigate ACK loops or DoS + * attacks that send repeated SYNs or ACKs for the same connection. To + * do this, we do not send a duplicate SYNACK or ACK if the remote + * endpoint is sending out-of-window SYNs or pure ACKs at a high rate. + */ +bool tcp_oow_rate_limited(struct net *net, const struct sk_buff *skb, + int mib_idx, u32 *last_oow_ack_time) +{ + /* Data packets without SYNs are not likely part of an ACK loop. */ + if ((TCP_SKB_CB(skb)->seq != TCP_SKB_CB(skb)->end_seq) && + !tcp_hdr(skb)->syn) + goto not_rate_limited; + + if (*last_oow_ack_time) { + s32 elapsed = (s32)(tcp_time_stamp - *last_oow_ack_time); + + if (0 <= elapsed && elapsed < sysctl_tcp_invalid_ratelimit) { + NET_INC_STATS_BH(net, mib_idx); + return true; /* rate-limited: don't send yet! */ + } + } + + *last_oow_ack_time = tcp_time_stamp; + +not_rate_limited: + return false; /* not rate-limited: go ahead, send dupack now! */ +} + /* RFC 5961 7 [ACK Throttling] */ static void tcp_send_challenge_ack(struct sock *sk, const struct sk_buff *skb) { -- cgit v1.2.3 From 27cd44618b92fc8c6889e4628407791e45422bac Mon Sep 17 00:00:00 2001 From: Neil Roberts Date: Wed, 4 Mar 2015 14:41:16 +0000 Subject: drm/i915: Add I915_PARAM_REVISION Adds a parameter which can be used with DRM_I915_GETPARAM to query the GPU revision. The intention is to use this in Mesa to implement the WaDisableSIMD16On3SrcInstr workaround on Skylake but only for revision 2. Signed-off-by: Neil Roberts Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_dma.c | 3 +++ include/uapi/drm/i915_drm.h | 1 + 2 files changed, 4 insertions(+) (limited to 'include') diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c index 053e1788f578..3b64c526b6b9 100644 --- a/drivers/gpu/drm/i915/i915_dma.c +++ b/drivers/gpu/drm/i915/i915_dma.c @@ -68,6 +68,9 @@ static int i915_getparam(struct drm_device *dev, void *data, case I915_PARAM_CHIPSET_ID: value = dev->pdev->device; break; + case I915_PARAM_REVISION: + value = dev->pdev->revision; + break; case I915_PARAM_HAS_GEM: value = 1; break; diff --git a/include/uapi/drm/i915_drm.h b/include/uapi/drm/i915_drm.h index 6eed16b92a24..b768f3b21eaa 100644 --- a/include/uapi/drm/i915_drm.h +++ b/include/uapi/drm/i915_drm.h @@ -347,6 +347,7 @@ typedef struct drm_i915_irq_wait { #define I915_PARAM_HAS_COHERENT_PHYS_GTT 29 #define I915_PARAM_MMAP_VERSION 30 #define I915_PARAM_HAS_BSD2 31 +#define I915_PARAM_REVISION 32 typedef struct drm_i915_getparam { int param; -- cgit v1.2.3 From 8c4f83fb1e8bf317e894f62d17a63c32b7a6b75e Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Wed, 11 Mar 2015 08:26:23 +0100 Subject: drm/fourcc: 64 #defines need ULL postfix I have no idea about the exact rules, but this angered Dave's 32bit rhel gcc. Reported-by: Dave Airlie Signed-off-by: Daniel Vetter --- include/uapi/drm/drm_fourcc.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/uapi/drm/drm_fourcc.h b/include/uapi/drm/drm_fourcc.h index e6efac23c7ea..07735822a28f 100644 --- a/include/uapi/drm/drm_fourcc.h +++ b/include/uapi/drm/drm_fourcc.h @@ -151,7 +151,7 @@ /* add more to the end as needed */ #define fourcc_mod_code(vendor, val) \ - ((((u64)DRM_FORMAT_MOD_VENDOR_## vendor) << 56) | (val & 0x00ffffffffffffffL)) + ((((u64)DRM_FORMAT_MOD_VENDOR_## vendor) << 56) | (val & 0x00ffffffffffffffULL)) /* * Format Modifier tokens: -- cgit v1.2.3 From 44e5e28bf64fcd7c4d3f933cb4c7f69d8aa11781 Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Tue, 3 Feb 2015 14:34:05 +0200 Subject: drm/i915: remove indirection in the PCI ID macros Spell all the PCI IDs out to be able to quickly grep for the IDs. No functional changes. Signed-off-by: Jani Nikula Reviewed-by: Damien Lespiau [danvet: Add GT1/2 to comments to not loose that distinction.] Signed-off-by: Daniel Vetter --- include/drm/i915_pciids.h | 49 ++++++++++++++++++++++++----------------------- 1 file changed, 25 insertions(+), 24 deletions(-) (limited to 'include') diff --git a/include/drm/i915_pciids.h b/include/drm/i915_pciids.h index f2e47fd56751..613372375ada 100644 --- a/include/drm/i915_pciids.h +++ b/include/drm/i915_pciids.h @@ -208,40 +208,41 @@ #define INTEL_VLV_D_IDS(info) \ INTEL_VGA_DEVICE(0x0155, info) -#define _INTEL_BDW_M(gt, id, info) \ - INTEL_VGA_DEVICE((((gt) - 1) << 4) | (id), info) -#define _INTEL_BDW_D(gt, id, info) \ - INTEL_VGA_DEVICE((((gt) - 1) << 4) | (id), info) - -#define _INTEL_BDW_M_IDS(gt, info) \ - _INTEL_BDW_M(gt, 0x1602, info), /* Halo */ \ - _INTEL_BDW_M(gt, 0x1606, info), /* ULT */ \ - _INTEL_BDW_M(gt, 0x160B, info), /* ULT */ \ - _INTEL_BDW_M(gt, 0x160E, info) /* ULX */ - -#define _INTEL_BDW_D_IDS(gt, info) \ - _INTEL_BDW_D(gt, 0x160A, info), /* Server */ \ - _INTEL_BDW_D(gt, 0x160D, info) /* Workstation */ - -#define INTEL_BDW_GT12M_IDS(info) \ - _INTEL_BDW_M_IDS(1, info), \ - _INTEL_BDW_M_IDS(2, info) +#define INTEL_BDW_GT12M_IDS(info) \ + INTEL_VGA_DEVICE(0x1602, info), /* GT1 ULT */ \ + INTEL_VGA_DEVICE(0x1606, info), /* GT1 ULT */ \ + INTEL_VGA_DEVICE(0x160B, info), /* GT1 Iris */ \ + INTEL_VGA_DEVICE(0x160E, info), /* GT1 ULX */ \ + INTEL_VGA_DEVICE(0x1612, info), /* GT2 Halo */ \ + INTEL_VGA_DEVICE(0x1616, info), /* GT2 ULT */ \ + INTEL_VGA_DEVICE(0x161B, info), /* GT2 ULT */ \ + INTEL_VGA_DEVICE(0x161E, info) /* GT2 ULX */ #define INTEL_BDW_GT12D_IDS(info) \ - _INTEL_BDW_D_IDS(1, info), \ - _INTEL_BDW_D_IDS(2, info) + INTEL_VGA_DEVICE(0x160A, info), /* GT1 Server */ \ + INTEL_VGA_DEVICE(0x160D, info), /* GT1 Workstation */ \ + INTEL_VGA_DEVICE(0x161A, info), /* GT2 Server */ \ + INTEL_VGA_DEVICE(0x161D, info) /* GT2 Workstation */ #define INTEL_BDW_GT3M_IDS(info) \ - _INTEL_BDW_M_IDS(3, info) + INTEL_VGA_DEVICE(0x1622, info), /* ULT */ \ + INTEL_VGA_DEVICE(0x1626, info), /* ULT */ \ + INTEL_VGA_DEVICE(0x162B, info), /* Iris */ \ + INTEL_VGA_DEVICE(0x162E, info) /* ULX */ #define INTEL_BDW_GT3D_IDS(info) \ - _INTEL_BDW_D_IDS(3, info) + INTEL_VGA_DEVICE(0x162A, info), /* Server */ \ + INTEL_VGA_DEVICE(0x162D, info) /* Workstation */ #define INTEL_BDW_RSVDM_IDS(info) \ - _INTEL_BDW_M_IDS(4, info) + INTEL_VGA_DEVICE(0x1632, info), /* ULT */ \ + INTEL_VGA_DEVICE(0x1636, info), /* ULT */ \ + INTEL_VGA_DEVICE(0x163B, info), /* Iris */ \ + INTEL_VGA_DEVICE(0x163E, info) /* ULX */ #define INTEL_BDW_RSVDD_IDS(info) \ - _INTEL_BDW_D_IDS(4, info) + INTEL_VGA_DEVICE(0x163A, info), /* Server */ \ + INTEL_VGA_DEVICE(0x163D, info) /* Workstation */ #define INTEL_BDW_M_IDS(info) \ INTEL_BDW_GT12M_IDS(info), \ -- cgit v1.2.3 From a1559ffefb2a80eabbee65d7cc04e828d4fd557d Mon Sep 17 00:00:00 2001 From: Jeff McGee Date: Mon, 9 Mar 2015 16:06:54 -0700 Subject: drm/i915: Export total subslice and EU counts Setup new I915_GETPARAM ioctl entries for subslice total and EU total. Userspace drivers need these values when constructing GPGPU commands. This kernel query method is intended to replace the PCI ID-based tables that userspace drivers currently maintain. The kernel driver can employ fuse register reads as needed to ensure the most accurate determination of GT config attributes. This first became important with Cherryview in which the config could differ between devices with the same PCI ID. The kernel detection of these values is device-specific and not included in this patch. Because zero is not a valid value for any of these parameters, a value of zero is interpreted as unknown for the device. Userspace drivers should continue to maintain ID-based tables for older devices not supported by the new query method. v2: Increment our I915_GETPARAM indices to fit after REVISION which was merged ahead of us. For: VIZ-4636 Signed-off-by: Jeff McGee Tested-by: Zhigang Gong Acked-by: Zhigang Gong Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_dma.c | 10 ++++++++++ include/uapi/drm/i915_drm.h | 2 ++ 2 files changed, 12 insertions(+) (limited to 'include') diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c index 8e914303b831..d49ed68f041e 100644 --- a/drivers/gpu/drm/i915/i915_dma.c +++ b/drivers/gpu/drm/i915/i915_dma.c @@ -153,6 +153,16 @@ static int i915_getparam(struct drm_device *dev, void *data, case I915_PARAM_MMAP_VERSION: value = 1; break; + case I915_PARAM_SUBSLICE_TOTAL: + value = INTEL_INFO(dev)->subslice_total; + if (!value) + return -ENODEV; + break; + case I915_PARAM_EU_TOTAL: + value = INTEL_INFO(dev)->eu_total; + if (!value) + return -ENODEV; + break; default: DRM_DEBUG("Unknown parameter %d\n", param->param); return -EINVAL; diff --git a/include/uapi/drm/i915_drm.h b/include/uapi/drm/i915_drm.h index b768f3b21eaa..8d1be9073380 100644 --- a/include/uapi/drm/i915_drm.h +++ b/include/uapi/drm/i915_drm.h @@ -348,6 +348,8 @@ typedef struct drm_i915_irq_wait { #define I915_PARAM_MMAP_VERSION 30 #define I915_PARAM_HAS_BSD2 31 #define I915_PARAM_REVISION 32 +#define I915_PARAM_SUBSLICE_TOTAL 33 +#define I915_PARAM_EU_TOTAL 34 typedef struct drm_i915_getparam { int param; -- cgit v1.2.3 From c66150824b8a809a502fd833fa9b18082cd89a39 Mon Sep 17 00:00:00 2001 From: Nikesh Oswal Date: Mon, 2 Feb 2015 17:06:44 +0000 Subject: ASoC: dapm: add code to configure dai link parameters dai-link params for codec-codec links were fixed. The fixed link between codec and another chip which may be another codec, baseband, bluetooth codec etc may require run time configuaration changes. This change provides an optional alsa control to select one of the params from a list of params. Signed-off-by: Nikesh Oswal Signed-off-by: Mark Brown --- include/sound/soc-dapm.h | 3 + include/sound/soc.h | 1 + sound/soc/soc-core.c | 6 +- sound/soc/soc-dapm.c | 164 +++++++++++++++++++++++++++++++++++++++++++++-- 4 files changed, 166 insertions(+), 8 deletions(-) (limited to 'include') diff --git a/include/sound/soc-dapm.h b/include/sound/soc-dapm.h index 8d7416e46861..eda881402dda 100644 --- a/include/sound/soc-dapm.h +++ b/include/sound/soc-dapm.h @@ -378,6 +378,7 @@ int snd_soc_dapm_link_dai_widgets(struct snd_soc_card *card); void snd_soc_dapm_connect_dai_link_widgets(struct snd_soc_card *card); int snd_soc_dapm_new_pcm(struct snd_soc_card *card, const struct snd_soc_pcm_stream *params, + unsigned int num_params, struct snd_soc_dapm_widget *source, struct snd_soc_dapm_widget *sink); @@ -531,6 +532,8 @@ struct snd_soc_dapm_widget { void *priv; /* widget specific data */ struct regulator *regulator; /* attached regulator */ const struct snd_soc_pcm_stream *params; /* params for dai links */ + unsigned int num_params; /* number of params for dai links */ + unsigned int params_select; /* currently selected param for dai link */ /* dapm control */ int reg; /* negative reg = no direct dapm */ diff --git a/include/sound/soc.h b/include/sound/soc.h index 0d1ade195628..4636a058372b 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -941,6 +941,7 @@ struct snd_soc_dai_link { int be_id; /* optional ID for machine driver BE identification */ const struct snd_soc_pcm_stream *params; + unsigned int num_params; unsigned int dai_fmt; /* format to set on init */ diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 30579ca5bacb..700ac2ffe696 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -1245,7 +1245,8 @@ static int soc_link_dai_widgets(struct snd_soc_card *card, capture_w = cpu_dai->capture_widget; if (play_w && capture_w) { ret = snd_soc_dapm_new_pcm(card, dai_link->params, - capture_w, play_w); + dai_link->num_params, capture_w, + play_w); if (ret != 0) { dev_err(card->dev, "ASoC: Can't link %s to %s: %d\n", play_w->name, capture_w->name, ret); @@ -1257,7 +1258,8 @@ static int soc_link_dai_widgets(struct snd_soc_card *card, capture_w = codec_dai->capture_widget; if (play_w && capture_w) { ret = snd_soc_dapm_new_pcm(card, dai_link->params, - capture_w, play_w); + dai_link->num_params, capture_w, + play_w); if (ret != 0) { dev_err(card->dev, "ASoC: Can't link %s to %s: %d\n", play_w->name, capture_w->name, ret); diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index b6f88202b8c9..6828b4ed5447 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -853,6 +853,36 @@ static int dapm_new_pga(struct snd_soc_dapm_widget *w) return 0; } +/* create new dapm dai link control */ +static int dapm_new_dai_link(struct snd_soc_dapm_widget *w) +{ + int i, ret; + struct snd_kcontrol *kcontrol; + struct snd_soc_dapm_context *dapm = w->dapm; + struct snd_card *card = dapm->card->snd_card; + + /* create control for links with > 1 config */ + if (w->num_params <= 1) + return 0; + + /* add kcontrol */ + for (i = 0; i < w->num_kcontrols; i++) { + kcontrol = snd_soc_cnew(&w->kcontrol_news[i], w, + w->name, NULL); + ret = snd_ctl_add(card, kcontrol); + if (ret < 0) { + dev_err(dapm->dev, + "ASoC: failed to add widget %s dapm kcontrol %s: %d\n", + w->name, w->kcontrol_news[i].name, ret); + return ret; + } + kcontrol->private_data = w; + w->kcontrols[i] = kcontrol; + } + + return 0; +} + /* We implement power down on suspend by checking the power state of * the ALSA card - when we are suspending the ALSA state for the card * is set to D3. @@ -2719,6 +2749,9 @@ int snd_soc_dapm_new_widgets(struct snd_soc_card *card) case snd_soc_dapm_out_drv: dapm_new_pga(w); break; + case snd_soc_dapm_dai_link: + dapm_new_dai_link(w); + break; default: break; } @@ -3193,7 +3226,7 @@ static int snd_soc_dai_link_event(struct snd_soc_dapm_widget *w, { struct snd_soc_dapm_path *source_p, *sink_p; struct snd_soc_dai *source, *sink; - const struct snd_soc_pcm_stream *config = w->params; + const struct snd_soc_pcm_stream *config = w->params + w->params_select; struct snd_pcm_substream substream; struct snd_pcm_hw_params *params = NULL; u64 fmt; @@ -3285,8 +3318,39 @@ out: return ret; } +static int snd_soc_dapm_dai_link_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dapm_widget *w = snd_kcontrol_chip(kcontrol); + + ucontrol->value.integer.value[0] = w->params_select; + + return 0; +} + +static int snd_soc_dapm_dai_link_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dapm_widget *w = snd_kcontrol_chip(kcontrol); + + /* Can't change the config when widget is already powered */ + if (w->power) + return -EBUSY; + + if (ucontrol->value.integer.value[0] == w->params_select) + return 0; + + if (ucontrol->value.integer.value[0] >= w->num_params) + return -EINVAL; + + w->params_select = ucontrol->value.integer.value[0]; + + return 0; +} + int snd_soc_dapm_new_pcm(struct snd_soc_card *card, const struct snd_soc_pcm_stream *params, + unsigned int num_params, struct snd_soc_dapm_widget *source, struct snd_soc_dapm_widget *sink) { @@ -3294,14 +3358,61 @@ int snd_soc_dapm_new_pcm(struct snd_soc_card *card, struct snd_soc_dapm_widget *w; size_t len; char *link_name; - int ret; + int ret, count; + unsigned long private_value; + const char **w_param_text; + struct soc_enum w_param_enum[] = { + SOC_ENUM_SINGLE(0, 0, 0, NULL), + }; + struct snd_kcontrol_new kcontrol_dai_link[] = { + SOC_ENUM_EXT(NULL, w_param_enum[0], + snd_soc_dapm_dai_link_get, + snd_soc_dapm_dai_link_put), + }; + const struct snd_soc_pcm_stream *config = params; + + w_param_text = devm_kcalloc(card->dev, num_params, + sizeof(char *), GFP_KERNEL); + if (!w_param_text) + return -ENOMEM; len = strlen(source->name) + strlen(sink->name) + 2; link_name = devm_kzalloc(card->dev, len, GFP_KERNEL); - if (!link_name) - return -ENOMEM; + if (!link_name) { + ret = -ENOMEM; + goto outfree_w_param; + } snprintf(link_name, len, "%s-%s", source->name, sink->name); + for (count = 0 ; count < num_params; count++) { + if (!config->stream_name) { + dev_warn(card->dapm.dev, + "ASoC: anonymous config %d for dai link %s\n", + count, link_name); + len = strlen("Anonymous Configuration ") + 3; + w_param_text[count] = + devm_kzalloc(card->dev, len, GFP_KERNEL); + if (!w_param_text[count]) { + ret = -ENOMEM; + goto outfree_link_name; + } + snprintf(w_param_text[count], len, + "Anonymous Configuration %d", count); + } else { + w_param_text[count] = devm_kmemdup(card->dev, + config->stream_name, + strlen(config->stream_name) + 1, + GFP_KERNEL); + if (!w_param_text[count]) { + ret = -ENOMEM; + goto outfree_link_name; + } + } + config++; + } + w_param_enum[0].items = num_params; + w_param_enum[0].texts = w_param_text; + memset(&template, 0, sizeof(template)); template.reg = SND_SOC_NOPM; template.id = snd_soc_dapm_dai_link; @@ -3309,6 +3420,30 @@ int snd_soc_dapm_new_pcm(struct snd_soc_card *card, template.event = snd_soc_dai_link_event; template.event_flags = SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD; + template.num_kcontrols = 1; + /* duplicate w_param_enum on heap so that memory persists */ + private_value = + (unsigned long) devm_kmemdup(card->dev, + (void *)(kcontrol_dai_link[0].private_value), + sizeof(struct soc_enum), GFP_KERNEL); + if (!private_value) { + dev_err(card->dev, "ASoC: Failed to create control for %s widget\n", + link_name); + ret = -ENOMEM; + goto outfree_link_name; + } + kcontrol_dai_link[0].private_value = private_value; + /* duplicate kcontrol_dai_link on heap so that memory persists */ + template.kcontrol_news = + devm_kmemdup(card->dev, &kcontrol_dai_link[0], + sizeof(struct snd_kcontrol_new), + GFP_KERNEL); + if (!template.kcontrol_news) { + dev_err(card->dev, "ASoC: Failed to create control for %s widget\n", + link_name); + ret = -ENOMEM; + goto outfree_private_value; + } dev_dbg(card->dev, "ASoC: adding %s widget\n", link_name); @@ -3316,15 +3451,32 @@ int snd_soc_dapm_new_pcm(struct snd_soc_card *card, if (!w) { dev_err(card->dev, "ASoC: Failed to create %s widget\n", link_name); - return -ENOMEM; + ret = -ENOMEM; + goto outfree_kcontrol_news; } w->params = params; + w->num_params = num_params; ret = snd_soc_dapm_add_path(&card->dapm, source, w, NULL, NULL); if (ret) - return ret; + goto outfree_w; return snd_soc_dapm_add_path(&card->dapm, w, sink, NULL, NULL); + +outfree_w: + devm_kfree(card->dev, w); +outfree_kcontrol_news: + devm_kfree(card->dev, (void *)template.kcontrol_news); +outfree_private_value: + devm_kfree(card->dev, (void *)private_value); +outfree_link_name: + devm_kfree(card->dev, link_name); +outfree_w_param: + for (count = 0 ; count < num_params; count++) + devm_kfree(card->dev, (void *)w_param_text[count]); + devm_kfree(card->dev, w_param_text); + + return ret; } int snd_soc_dapm_new_dai_widgets(struct snd_soc_dapm_context *dapm, -- cgit v1.2.3 From a6df410d420aa4ff316797d352f69e7ebae5ff98 Mon Sep 17 00:00:00 2001 From: Hongzhou Yang Date: Wed, 21 Jan 2015 13:28:15 +0800 Subject: pinctrl: mediatek: Add Pinctrl/GPIO driver for mt8135. The mediatek SoCs have GPIO controller that handle both the muxing and GPIOs. The GPIO controller have pinmux, pull enable, pull select, direction and output high/low control. This driver include common driver and mt8135 part. The common driver include the pinctrl driver and GPIO driver. The mt8135 part contain its special device data. Signed-off-by: Hongzhou Yang Signed-off-by: Linus Walleij --- drivers/pinctrl/Kconfig | 1 + drivers/pinctrl/Makefile | 1 + drivers/pinctrl/mediatek/Kconfig | 14 + drivers/pinctrl/mediatek/Makefile | 5 + drivers/pinctrl/mediatek/pinctrl-mt8135.c | 350 ++++ drivers/pinctrl/mediatek/pinctrl-mtk-common.c | 800 ++++++++++ drivers/pinctrl/mediatek/pinctrl-mtk-common.h | 185 +++ drivers/pinctrl/mediatek/pinctrl-mtk-mt8135.h | 2114 +++++++++++++++++++++++++ include/dt-bindings/pinctrl/mt65xx.h | 40 + 9 files changed, 3510 insertions(+) create mode 100644 drivers/pinctrl/mediatek/Kconfig create mode 100644 drivers/pinctrl/mediatek/Makefile create mode 100644 drivers/pinctrl/mediatek/pinctrl-mt8135.c create mode 100644 drivers/pinctrl/mediatek/pinctrl-mtk-common.c create mode 100644 drivers/pinctrl/mediatek/pinctrl-mtk-common.h create mode 100644 drivers/pinctrl/mediatek/pinctrl-mtk-mt8135.h create mode 100644 include/dt-bindings/pinctrl/mt65xx.h (limited to 'include') diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig index adfe8546abc2..3d44b6cfffbf 100644 --- a/drivers/pinctrl/Kconfig +++ b/drivers/pinctrl/Kconfig @@ -205,6 +205,7 @@ source "drivers/pinctrl/sh-pfc/Kconfig" source "drivers/pinctrl/spear/Kconfig" source "drivers/pinctrl/sunxi/Kconfig" source "drivers/pinctrl/vt8500/Kconfig" +source "drivers/pinctrl/mediatek/Kconfig" config PINCTRL_XWAY bool diff --git a/drivers/pinctrl/Makefile b/drivers/pinctrl/Makefile index 3d62946f3f29..e54d2166bd09 100644 --- a/drivers/pinctrl/Makefile +++ b/drivers/pinctrl/Makefile @@ -49,3 +49,4 @@ obj-$(CONFIG_PINCTRL_SH_PFC) += sh-pfc/ obj-$(CONFIG_PLAT_SPEAR) += spear/ obj-$(CONFIG_ARCH_SUNXI) += sunxi/ obj-$(CONFIG_ARCH_VT8500) += vt8500/ +obj-$(CONFIG_ARCH_MEDIATEK) += mediatek/ diff --git a/drivers/pinctrl/mediatek/Kconfig b/drivers/pinctrl/mediatek/Kconfig new file mode 100644 index 000000000000..70bbf39379ae --- /dev/null +++ b/drivers/pinctrl/mediatek/Kconfig @@ -0,0 +1,14 @@ +if ARCH_MEDIATEK + +config PINCTRL_MTK_COMMON + bool + select PINMUX + select GENERIC_PINCONF + select GPIOLIB + select OF_GPIO + +config PINCTRL_MT8135 + def_bool MACH_MT8135 + select PINCTRL_MTK_COMMON + +endif diff --git a/drivers/pinctrl/mediatek/Makefile b/drivers/pinctrl/mediatek/Makefile new file mode 100644 index 000000000000..8157dad9d51d --- /dev/null +++ b/drivers/pinctrl/mediatek/Makefile @@ -0,0 +1,5 @@ +# Core +obj-$(CONFIG_PINCTRL_MTK_COMMON) += pinctrl-mtk-common.o + +# SoC Drivers +obj-$(CONFIG_PINCTRL_MT8135) += pinctrl-mt8135.o diff --git a/drivers/pinctrl/mediatek/pinctrl-mt8135.c b/drivers/pinctrl/mediatek/pinctrl-mt8135.c new file mode 100644 index 000000000000..13694b8924c4 --- /dev/null +++ b/drivers/pinctrl/mediatek/pinctrl-mt8135.c @@ -0,0 +1,350 @@ +/* + * Copyright (c) 2014 MediaTek Inc. + * Author: Hongzhou.Yang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "pinctrl-mtk-common.h" +#include "pinctrl-mtk-mt8135.h" + +#define DRV_BASE1 0x500 +#define DRV_BASE2 0x510 +#define PUPD_BASE1 0x400 +#define PUPD_BASE2 0x450 +#define R0_BASE1 0x4d0 +#define R1_BASE1 0x200 +#define R1_BASE2 0x250 + +struct mtk_spec_pull_set { + unsigned int pin; + unsigned int pupd_offset; + unsigned char pupd_bit; + unsigned int r0_offset; + unsigned char r0_bit; + unsigned int r1_offset; + unsigned char r1_bit; +}; + +#define SPEC_PULL(_pin, _pupd_offset, _pupd_bit, _r0_offset, \ + _r0_bit, _r1_offset, _r1_bit) \ + { \ + .pin = _pin, \ + .pupd_offset = _pupd_offset, \ + .pupd_bit = _pupd_bit, \ + .r0_offset = _r0_offset, \ + .r0_bit = _r0_bit, \ + .r1_offset = _r1_offset, \ + .r1_bit = _r1_bit, \ + } + +static const struct mtk_drv_group_desc mt8135_drv_grp[] = { + /* E8E4E2 2/4/6/8/10/12/14/16 */ + MTK_DRV_GRP(2, 16, 0, 2, 2), + /* E8E4 4/8/12/16 */ + MTK_DRV_GRP(4, 16, 1, 2, 4), + /* E4E2 2/4/6/8 */ + MTK_DRV_GRP(2, 8, 0, 1, 2), + /* E16E8E4 4/8/12/16/20/24/28/32 */ + MTK_DRV_GRP(4, 32, 0, 2, 4) +}; + +static const struct mtk_pin_drv_grp mt8135_pin_drv[] = { + MTK_PIN_DRV_GRP(0, DRV_BASE1, 0, 0), + MTK_PIN_DRV_GRP(1, DRV_BASE1, 0, 0), + MTK_PIN_DRV_GRP(2, DRV_BASE1, 0, 0), + MTK_PIN_DRV_GRP(3, DRV_BASE1, 0, 0), + MTK_PIN_DRV_GRP(4, DRV_BASE1, 4, 0), + MTK_PIN_DRV_GRP(5, DRV_BASE1, 8, 0), + MTK_PIN_DRV_GRP(6, DRV_BASE1, 0, 0), + MTK_PIN_DRV_GRP(7, DRV_BASE1, 0, 0), + MTK_PIN_DRV_GRP(8, DRV_BASE1, 0, 0), + MTK_PIN_DRV_GRP(9, DRV_BASE1, 0, 0), + + MTK_PIN_DRV_GRP(10, DRV_BASE1, 12, 1), + MTK_PIN_DRV_GRP(11, DRV_BASE1, 12, 1), + MTK_PIN_DRV_GRP(12, DRV_BASE1, 12, 1), + MTK_PIN_DRV_GRP(13, DRV_BASE1, 12, 1), + MTK_PIN_DRV_GRP(14, DRV_BASE1, 12, 1), + MTK_PIN_DRV_GRP(15, DRV_BASE1, 12, 1), + MTK_PIN_DRV_GRP(16, DRV_BASE1, 12, 1), + MTK_PIN_DRV_GRP(17, DRV_BASE1, 16, 1), + MTK_PIN_DRV_GRP(18, DRV_BASE1, 16, 1), + MTK_PIN_DRV_GRP(19, DRV_BASE1, 16, 1), + MTK_PIN_DRV_GRP(20, DRV_BASE1, 16, 1), + MTK_PIN_DRV_GRP(21, DRV_BASE1, 16, 1), + MTK_PIN_DRV_GRP(22, DRV_BASE1, 16, 1), + MTK_PIN_DRV_GRP(23, DRV_BASE1, 16, 1), + MTK_PIN_DRV_GRP(24, DRV_BASE1, 16, 1), + MTK_PIN_DRV_GRP(33, DRV_BASE1, 24, 1), + MTK_PIN_DRV_GRP(34, DRV_BASE2, 12, 2), + MTK_PIN_DRV_GRP(37, DRV_BASE2, 20, 1), + MTK_PIN_DRV_GRP(38, DRV_BASE2, 20, 1), + MTK_PIN_DRV_GRP(39, DRV_BASE2, 20, 1), + MTK_PIN_DRV_GRP(40, DRV_BASE2, 24, 1), + MTK_PIN_DRV_GRP(41, DRV_BASE2, 24, 1), + MTK_PIN_DRV_GRP(42, DRV_BASE2, 24, 1), + MTK_PIN_DRV_GRP(43, DRV_BASE2, 28, 1), + MTK_PIN_DRV_GRP(44, DRV_BASE2, 28, 1), + MTK_PIN_DRV_GRP(45, DRV_BASE2, 28, 1), + MTK_PIN_DRV_GRP(46, DRV_BASE2, 28, 1), + MTK_PIN_DRV_GRP(47, DRV_BASE2, 28, 1), + + MTK_PIN_DRV_GRP(49, DRV_BASE2+0x10, 0, 1), + MTK_PIN_DRV_GRP(50, DRV_BASE2+0x10, 4, 1), + MTK_PIN_DRV_GRP(51, DRV_BASE2+0x10, 8, 1), + MTK_PIN_DRV_GRP(52, DRV_BASE2+0x10, 12, 2), + MTK_PIN_DRV_GRP(53, DRV_BASE2+0x10, 16, 1), + MTK_PIN_DRV_GRP(54, DRV_BASE2+0x10, 20, 1), + MTK_PIN_DRV_GRP(55, DRV_BASE2+0x10, 24, 1), + MTK_PIN_DRV_GRP(56, DRV_BASE2+0x10, 28, 1), + + MTK_PIN_DRV_GRP(57, DRV_BASE2+0x20, 0, 1), + MTK_PIN_DRV_GRP(58, DRV_BASE2+0x20, 0, 1), + MTK_PIN_DRV_GRP(59, DRV_BASE2+0x20, 0, 1), + MTK_PIN_DRV_GRP(60, DRV_BASE2+0x20, 0, 1), + MTK_PIN_DRV_GRP(61, DRV_BASE2+0x20, 0, 1), + MTK_PIN_DRV_GRP(62, DRV_BASE2+0x20, 0, 1), + MTK_PIN_DRV_GRP(63, DRV_BASE2+0x20, 4, 1), + MTK_PIN_DRV_GRP(64, DRV_BASE2+0x20, 8, 1), + MTK_PIN_DRV_GRP(65, DRV_BASE2+0x20, 12, 1), + MTK_PIN_DRV_GRP(66, DRV_BASE2+0x20, 16, 1), + MTK_PIN_DRV_GRP(67, DRV_BASE2+0x20, 20, 1), + MTK_PIN_DRV_GRP(68, DRV_BASE2+0x20, 24, 1), + MTK_PIN_DRV_GRP(69, DRV_BASE2+0x20, 28, 1), + + MTK_PIN_DRV_GRP(70, DRV_BASE2+0x30, 0, 1), + MTK_PIN_DRV_GRP(71, DRV_BASE2+0x30, 4, 1), + MTK_PIN_DRV_GRP(72, DRV_BASE2+0x30, 8, 1), + MTK_PIN_DRV_GRP(73, DRV_BASE2+0x30, 12, 1), + MTK_PIN_DRV_GRP(74, DRV_BASE2+0x30, 16, 1), + MTK_PIN_DRV_GRP(75, DRV_BASE2+0x30, 20, 1), + MTK_PIN_DRV_GRP(76, DRV_BASE2+0x30, 24, 1), + MTK_PIN_DRV_GRP(77, DRV_BASE2+0x30, 28, 3), + MTK_PIN_DRV_GRP(78, DRV_BASE2+0x30, 28, 3), + + MTK_PIN_DRV_GRP(79, DRV_BASE2+0x40, 0, 3), + MTK_PIN_DRV_GRP(80, DRV_BASE2+0x40, 4, 3), + + MTK_PIN_DRV_GRP(81, DRV_BASE2+0x30, 28, 3), + MTK_PIN_DRV_GRP(82, DRV_BASE2+0x30, 28, 3), + + MTK_PIN_DRV_GRP(83, DRV_BASE2+0x40, 8, 3), + MTK_PIN_DRV_GRP(84, DRV_BASE2+0x40, 8, 3), + MTK_PIN_DRV_GRP(85, DRV_BASE2+0x40, 12, 3), + MTK_PIN_DRV_GRP(86, DRV_BASE2+0x40, 16, 3), + MTK_PIN_DRV_GRP(87, DRV_BASE2+0x40, 8, 3), + MTK_PIN_DRV_GRP(88, DRV_BASE2+0x40, 8, 3), + + MTK_PIN_DRV_GRP(89, DRV_BASE2+0x50, 12, 0), + MTK_PIN_DRV_GRP(90, DRV_BASE2+0x50, 12, 0), + MTK_PIN_DRV_GRP(91, DRV_BASE2+0x50, 12, 0), + MTK_PIN_DRV_GRP(92, DRV_BASE2+0x50, 12, 0), + MTK_PIN_DRV_GRP(93, DRV_BASE2+0x50, 12, 0), + MTK_PIN_DRV_GRP(94, DRV_BASE2+0x50, 12, 0), + MTK_PIN_DRV_GRP(95, DRV_BASE2+0x50, 12, 0), + + MTK_PIN_DRV_GRP(96, DRV_BASE1+0xb0, 28, 0), + + MTK_PIN_DRV_GRP(97, DRV_BASE2+0x50, 12, 0), + MTK_PIN_DRV_GRP(98, DRV_BASE2+0x50, 16, 0), + MTK_PIN_DRV_GRP(99, DRV_BASE2+0x50, 20, 1), + MTK_PIN_DRV_GRP(102, DRV_BASE2+0x50, 24, 1), + MTK_PIN_DRV_GRP(103, DRV_BASE2+0x50, 28, 1), + + + MTK_PIN_DRV_GRP(104, DRV_BASE2+0x60, 0, 1), + MTK_PIN_DRV_GRP(105, DRV_BASE2+0x60, 4, 1), + MTK_PIN_DRV_GRP(106, DRV_BASE2+0x60, 4, 1), + MTK_PIN_DRV_GRP(107, DRV_BASE2+0x60, 4, 1), + MTK_PIN_DRV_GRP(108, DRV_BASE2+0x60, 4, 1), + MTK_PIN_DRV_GRP(109, DRV_BASE2+0x60, 8, 2), + MTK_PIN_DRV_GRP(110, DRV_BASE2+0x60, 12, 2), + MTK_PIN_DRV_GRP(111, DRV_BASE2+0x60, 16, 2), + MTK_PIN_DRV_GRP(112, DRV_BASE2+0x60, 20, 2), + MTK_PIN_DRV_GRP(113, DRV_BASE2+0x60, 24, 2), + MTK_PIN_DRV_GRP(114, DRV_BASE2+0x60, 28, 2), + + MTK_PIN_DRV_GRP(115, DRV_BASE2+0x70, 0, 2), + MTK_PIN_DRV_GRP(116, DRV_BASE2+0x70, 4, 2), + MTK_PIN_DRV_GRP(117, DRV_BASE2+0x70, 8, 2), + MTK_PIN_DRV_GRP(118, DRV_BASE2+0x70, 12, 2), + MTK_PIN_DRV_GRP(119, DRV_BASE2+0x70, 16, 2), + MTK_PIN_DRV_GRP(120, DRV_BASE2+0x70, 20, 2), + + MTK_PIN_DRV_GRP(181, DRV_BASE1+0xa0, 12, 1), + MTK_PIN_DRV_GRP(182, DRV_BASE1+0xa0, 16, 1), + MTK_PIN_DRV_GRP(183, DRV_BASE1+0xa0, 20, 1), + MTK_PIN_DRV_GRP(184, DRV_BASE1+0xa0, 24, 1), + MTK_PIN_DRV_GRP(185, DRV_BASE1+0xa0, 28, 1), + + MTK_PIN_DRV_GRP(186, DRV_BASE1+0xb0, 0, 2), + MTK_PIN_DRV_GRP(187, DRV_BASE1+0xb0, 0, 2), + MTK_PIN_DRV_GRP(188, DRV_BASE1+0xb0, 0, 2), + MTK_PIN_DRV_GRP(189, DRV_BASE1+0xb0, 0, 2), + MTK_PIN_DRV_GRP(190, DRV_BASE1+0xb0, 4, 1), + MTK_PIN_DRV_GRP(191, DRV_BASE1+0xb0, 8, 1), + MTK_PIN_DRV_GRP(192, DRV_BASE1+0xb0, 12, 1), + + MTK_PIN_DRV_GRP(197, DRV_BASE1+0xb0, 16, 0), + MTK_PIN_DRV_GRP(198, DRV_BASE1+0xb0, 16, 0), + MTK_PIN_DRV_GRP(199, DRV_BASE1+0xb0, 20, 0), + MTK_PIN_DRV_GRP(200, DRV_BASE1+0xb0, 24, 0), + MTK_PIN_DRV_GRP(201, DRV_BASE1+0xb0, 16, 0), + MTK_PIN_DRV_GRP(202, DRV_BASE1+0xb0, 16, 0) +}; + +static const struct mtk_spec_pull_set spec_pupd[] = { + SPEC_PULL(0, PUPD_BASE1, 0, R0_BASE1, 9, R1_BASE1, 0), + SPEC_PULL(1, PUPD_BASE1, 1, R0_BASE1, 8, R1_BASE1, 1), + SPEC_PULL(2, PUPD_BASE1, 2, R0_BASE1, 7, R1_BASE1, 2), + SPEC_PULL(3, PUPD_BASE1, 3, R0_BASE1, 6, R1_BASE1, 3), + SPEC_PULL(4, PUPD_BASE1, 4, R0_BASE1, 1, R1_BASE1, 4), + SPEC_PULL(5, PUPD_BASE1, 5, R0_BASE1, 0, R1_BASE1, 5), + SPEC_PULL(6, PUPD_BASE1, 6, R0_BASE1, 5, R1_BASE1, 6), + SPEC_PULL(7, PUPD_BASE1, 7, R0_BASE1, 4, R1_BASE1, 7), + SPEC_PULL(8, PUPD_BASE1, 8, R0_BASE1, 3, R1_BASE1, 8), + SPEC_PULL(9, PUPD_BASE1, 9, R0_BASE1, 2, R1_BASE1, 9), + SPEC_PULL(89, PUPD_BASE2, 9, R0_BASE1, 18, R1_BASE2, 9), + SPEC_PULL(90, PUPD_BASE2, 10, R0_BASE1, 19, R1_BASE2, 10), + SPEC_PULL(91, PUPD_BASE2, 11, R0_BASE1, 23, R1_BASE2, 11), + SPEC_PULL(92, PUPD_BASE2, 12, R0_BASE1, 24, R1_BASE2, 12), + SPEC_PULL(93, PUPD_BASE2, 13, R0_BASE1, 25, R1_BASE2, 13), + SPEC_PULL(94, PUPD_BASE2, 14, R0_BASE1, 22, R1_BASE2, 14), + SPEC_PULL(95, PUPD_BASE2, 15, R0_BASE1, 20, R1_BASE2, 15), + SPEC_PULL(96, PUPD_BASE2+0x10, 0, R0_BASE1, 16, R1_BASE2+0x10, 0), + SPEC_PULL(97, PUPD_BASE2+0x10, 1, R0_BASE1, 21, R1_BASE2+0x10, 1), + SPEC_PULL(98, PUPD_BASE2+0x10, 2, R0_BASE1, 17, R1_BASE2+0x10, 2), + SPEC_PULL(197, PUPD_BASE1+0xc0, 5, R0_BASE1, 13, R1_BASE2+0xc0, 5), + SPEC_PULL(198, PUPD_BASE2+0xc0, 6, R0_BASE1, 14, R1_BASE2+0xc0, 6), + SPEC_PULL(199, PUPD_BASE2+0xc0, 7, R0_BASE1, 11, R1_BASE2+0xc0, 7), + SPEC_PULL(200, PUPD_BASE2+0xc0, 8, R0_BASE1, 10, R1_BASE2+0xc0, 8), + SPEC_PULL(201, PUPD_BASE2+0xc0, 9, R0_BASE1, 13, R1_BASE2+0xc0, 9), + SPEC_PULL(202, PUPD_BASE2+0xc0, 10, R0_BASE1, 12, R1_BASE2+0xc0, 10) +}; + +static int spec_pull_set(struct regmap *regmap, unsigned int pin, + unsigned char align, bool isup, unsigned int r1r0) +{ + unsigned int i; + unsigned int reg_pupd, reg_set_r0, reg_set_r1; + unsigned int reg_rst_r0, reg_rst_r1; + bool find = false; + + for (i = 0; i < ARRAY_SIZE(spec_pupd); i++) { + if (pin == spec_pupd[i].pin) { + find = true; + break; + } + } + + if (!find) + return -EINVAL; + + if (isup) + reg_pupd = spec_pupd[i].pupd_offset + align; + else + reg_pupd = spec_pupd[i].pupd_offset + (align << 1); + + regmap_write(regmap, reg_pupd, spec_pupd[i].pupd_bit); + + reg_set_r0 = spec_pupd[i].r0_offset + align; + reg_rst_r0 = spec_pupd[i].r0_offset + (align << 1); + reg_set_r1 = spec_pupd[i].r1_offset + align; + reg_rst_r1 = spec_pupd[i].r1_offset + (align << 1); + + switch (r1r0) { + case MTK_PUPD_SET_R1R0_00: + regmap_write(regmap, reg_rst_r0, spec_pupd[i].r0_bit); + regmap_write(regmap, reg_rst_r1, spec_pupd[i].r1_bit); + break; + case MTK_PUPD_SET_R1R0_01: + regmap_write(regmap, reg_set_r0, spec_pupd[i].r0_bit); + regmap_write(regmap, reg_rst_r1, spec_pupd[i].r1_bit); + break; + case MTK_PUPD_SET_R1R0_10: + regmap_write(regmap, reg_rst_r0, spec_pupd[i].r0_bit); + regmap_write(regmap, reg_set_r1, spec_pupd[i].r1_bit); + break; + case MTK_PUPD_SET_R1R0_11: + regmap_write(regmap, reg_set_r0, spec_pupd[i].r0_bit); + regmap_write(regmap, reg_set_r1, spec_pupd[i].r1_bit); + break; + default: + return -EINVAL; + } + + return 0; +} + +static const struct mtk_pinctrl_devdata mt8135_pinctrl_data = { + .pins = mtk_pins_mt8135, + .npins = ARRAY_SIZE(mtk_pins_mt8135), + .grp_desc = mt8135_drv_grp, + .n_grp_cls = ARRAY_SIZE(mt8135_drv_grp), + .pin_drv_grp = mt8135_pin_drv, + .n_pin_drv_grps = ARRAY_SIZE(mt8135_pin_drv), + .spec_pull_set = spec_pull_set, + .dir_offset = 0x0000, + .ies_offset = 0x0100, + .pullen_offset = 0x0200, + .smt_offset = 0x0300, + .pullsel_offset = 0x0400, + .invser_offset = 0x0600, + .dout_offset = 0x0800, + .din_offset = 0x0A00, + .pinmux_offset = 0x0C00, + .type1_start = 34, + .type1_end = 149, + .port_shf = 4, + .port_mask = 0xf, + .port_align = 4, +}; + +static int mt8135_pinctrl_probe(struct platform_device *pdev) +{ + return mtk_pctrl_init(pdev, &mt8135_pinctrl_data); +} + +static struct of_device_id mt8135_pctrl_match[] = { + { + .compatible = "mediatek,mt8135-pinctrl", + }, { + } +}; +MODULE_DEVICE_TABLE(of, mt8135_pctrl_match); + +static struct platform_driver mtk_pinctrl_driver = { + .probe = mt8135_pinctrl_probe, + .driver = { + .name = "mediatek-mt8135-pinctrl", + .owner = THIS_MODULE, + .of_match_table = mt8135_pctrl_match, + }, +}; + +static int __init mtk_pinctrl_init(void) +{ + return platform_driver_register(&mtk_pinctrl_driver); +} + +module_init(mtk_pinctrl_init); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("MediaTek Pinctrl Driver"); +MODULE_AUTHOR("Hongzhou Yang "); diff --git a/drivers/pinctrl/mediatek/pinctrl-mtk-common.c b/drivers/pinctrl/mediatek/pinctrl-mtk-common.c new file mode 100644 index 000000000000..5d680c89b5d8 --- /dev/null +++ b/drivers/pinctrl/mediatek/pinctrl-mtk-common.c @@ -0,0 +1,800 @@ +/* + * mt65xx pinctrl driver based on Allwinner A1X pinctrl driver. + * Copyright (c) 2014 MediaTek Inc. + * Author: Hongzhou.Yang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../core.h" +#include "../pinconf.h" +#include "../pinctrl-utils.h" +#include "pinctrl-mtk-common.h" + +#define MAX_GPIO_MODE_PER_REG 5 +#define GPIO_MODE_BITS 3 + +static const char * const mtk_gpio_functions[] = { + "func0", "func1", "func2", "func3", + "func4", "func5", "func6", "func7", +}; + +/* + * There are two base address for pull related configuration + * in mt8135, and different GPIO pins use different base address. + * When pin number greater than type1_start and less than type1_end, + * should use the second base address. + */ +static struct regmap *mtk_get_regmap(struct mtk_pinctrl *pctl, + unsigned long pin) +{ + if (pin >= pctl->devdata->type1_start && pin < pctl->devdata->type1_end) + return pctl->regmap2; + return pctl->regmap1; +} + +static unsigned int mtk_get_port(struct mtk_pinctrl *pctl, unsigned long pin) +{ + /* Different SoC has different mask and port shift. */ + return ((pin >> 4) & pctl->devdata->port_mask) + << pctl->devdata->port_shf; +} + +static int mtk_pmx_gpio_set_direction(struct pinctrl_dev *pctldev, + struct pinctrl_gpio_range *range, unsigned offset, + bool input) +{ + unsigned int reg_addr; + unsigned int bit; + struct mtk_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev); + + reg_addr = mtk_get_port(pctl, offset) + pctl->devdata->dir_offset; + bit = BIT(offset & 0xf); + + if (input) + /* Different SoC has different alignment offset. */ + reg_addr = CLR_ADDR(reg_addr, pctl); + else + reg_addr = SET_ADDR(reg_addr, pctl); + + regmap_write(mtk_get_regmap(pctl, offset), reg_addr, bit); + return 0; +} + +static void mtk_gpio_set(struct gpio_chip *chip, unsigned offset, int value) +{ + unsigned int reg_addr; + unsigned int bit; + struct mtk_pinctrl *pctl = dev_get_drvdata(chip->dev); + + reg_addr = mtk_get_port(pctl, offset) + pctl->devdata->dout_offset; + bit = BIT(offset & 0xf); + + if (value) + reg_addr = SET_ADDR(reg_addr, pctl); + else + reg_addr = CLR_ADDR(reg_addr, pctl); + + regmap_write(mtk_get_regmap(pctl, offset), reg_addr, bit); +} + +static void mtk_pconf_set_ies_smt(struct mtk_pinctrl *pctl, unsigned pin, + int value, enum pin_config_param param) +{ + unsigned int reg_addr, offset; + unsigned int bit; + + bit = BIT(pin & 0xf); + + if (param == PIN_CONFIG_INPUT_ENABLE) + offset = pctl->devdata->ies_offset; + else + offset = pctl->devdata->smt_offset; + + if (value) + reg_addr = SET_ADDR(mtk_get_port(pctl, pin) + offset, pctl); + else + reg_addr = CLR_ADDR(mtk_get_port(pctl, pin) + offset, pctl); + + regmap_write(mtk_get_regmap(pctl, pin), reg_addr, bit); +} + +static const struct mtk_pin_drv_grp *mtk_find_pin_drv_grp_by_pin( + struct mtk_pinctrl *pctl, unsigned long pin) { + int i; + + for (i = 0; i < pctl->devdata->n_pin_drv_grps; i++) { + const struct mtk_pin_drv_grp *pin_drv = + pctl->devdata->pin_drv_grp + i; + if (pin == pin_drv->pin) + return pin_drv; + } + + return NULL; +} + +static int mtk_pconf_set_driving(struct mtk_pinctrl *pctl, + unsigned int pin, unsigned char driving) +{ + const struct mtk_pin_drv_grp *pin_drv; + unsigned int val; + unsigned int bits, mask, shift; + const struct mtk_drv_group_desc *drv_grp; + + if (pin >= pctl->devdata->npins) + return -EINVAL; + + pin_drv = mtk_find_pin_drv_grp_by_pin(pctl, pin); + if (!pin_drv || pin_drv->grp > pctl->devdata->n_grp_cls) + return -EINVAL; + + drv_grp = pctl->devdata->grp_desc + pin_drv->grp; + if (driving >= drv_grp->min_drv && driving <= drv_grp->max_drv + && !(driving % drv_grp->step)) { + val = driving / drv_grp->step - 1; + bits = drv_grp->high_bit - drv_grp->low_bit + 1; + mask = BIT(bits) - 1; + shift = pin_drv->bit + drv_grp->low_bit; + mask <<= shift; + val <<= shift; + return regmap_update_bits(mtk_get_regmap(pctl, pin), + pin_drv->offset, mask, val); + } + + return -EINVAL; +} + +static int mtk_pconf_set_pull_select(struct mtk_pinctrl *pctl, + unsigned int pin, bool enable, bool isup, unsigned int arg) +{ + unsigned int bit; + unsigned int reg_pullen, reg_pullsel; + int ret; + + /* Some pins' pull setting are very different, + * they have separate pull up/down bit, R0 and R1 + * resistor bit, so we need this special handle. + */ + if (pctl->devdata->spec_pull_set) { + ret = pctl->devdata->spec_pull_set(mtk_get_regmap(pctl, pin), + pin, pctl->devdata->port_align, isup, arg); + if (!ret) + return 0; + } + + /* For generic pull config, default arg value should be 0 or 1. */ + if (arg != 0 && arg != 1) { + dev_err(pctl->dev, "invalid pull-up argument %d on pin %d .\n", + arg, pin); + return -EINVAL; + } + + bit = BIT(pin & 0xf); + if (enable) + reg_pullen = SET_ADDR(mtk_get_port(pctl, pin) + + pctl->devdata->pullen_offset, pctl); + else + reg_pullen = CLR_ADDR(mtk_get_port(pctl, pin) + + pctl->devdata->pullen_offset, pctl); + + if (isup) + reg_pullsel = SET_ADDR(mtk_get_port(pctl, pin) + + pctl->devdata->pullsel_offset, pctl); + else + reg_pullsel = CLR_ADDR(mtk_get_port(pctl, pin) + + pctl->devdata->pullsel_offset, pctl); + + regmap_write(mtk_get_regmap(pctl, pin), reg_pullen, bit); + regmap_write(mtk_get_regmap(pctl, pin), reg_pullsel, bit); + return 0; +} + +static int mtk_pconf_parse_conf(struct pinctrl_dev *pctldev, + unsigned int pin, enum pin_config_param param, + enum pin_config_param arg) +{ + struct mtk_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev); + + switch (param) { + case PIN_CONFIG_BIAS_DISABLE: + mtk_pconf_set_pull_select(pctl, pin, false, false, arg); + break; + case PIN_CONFIG_BIAS_PULL_UP: + mtk_pconf_set_pull_select(pctl, pin, true, true, arg); + break; + case PIN_CONFIG_BIAS_PULL_DOWN: + mtk_pconf_set_pull_select(pctl, pin, true, false, arg); + break; + case PIN_CONFIG_INPUT_ENABLE: + mtk_pconf_set_ies_smt(pctl, pin, arg, param); + break; + case PIN_CONFIG_OUTPUT: + mtk_gpio_set(pctl->chip, pin, arg); + mtk_pmx_gpio_set_direction(pctldev, NULL, pin, false); + break; + case PIN_CONFIG_INPUT_SCHMITT_ENABLE: + mtk_pconf_set_ies_smt(pctl, pin, arg, param); + break; + case PIN_CONFIG_DRIVE_STRENGTH: + mtk_pconf_set_driving(pctl, pin, arg); + break; + default: + return -EINVAL; + } + + return 0; +} + +static int mtk_pconf_group_get(struct pinctrl_dev *pctldev, + unsigned group, + unsigned long *config) +{ + struct mtk_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev); + + *config = pctl->groups[group].config; + + return 0; +} + +static int mtk_pconf_group_set(struct pinctrl_dev *pctldev, unsigned group, + unsigned long *configs, unsigned num_configs) +{ + struct mtk_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev); + struct mtk_pinctrl_group *g = &pctl->groups[group]; + int i; + + for (i = 0; i < num_configs; i++) { + mtk_pconf_parse_conf(pctldev, g->pin, + pinconf_to_config_param(configs[i]), + pinconf_to_config_argument(configs[i])); + + g->config = configs[i]; + } + + return 0; +} + +static const struct pinconf_ops mtk_pconf_ops = { + .pin_config_group_get = mtk_pconf_group_get, + .pin_config_group_set = mtk_pconf_group_set, +}; + +static struct mtk_pinctrl_group * +mtk_pctrl_find_group_by_pin(struct mtk_pinctrl *pctl, u32 pin) +{ + int i; + + for (i = 0; i < pctl->ngroups; i++) { + struct mtk_pinctrl_group *grp = pctl->groups + i; + + if (grp->pin == pin) + return grp; + } + + return NULL; +} + +static const struct mtk_desc_function *mtk_pctrl_find_function_by_pin( + struct mtk_pinctrl *pctl, u32 pin_num, u32 fnum) +{ + const struct mtk_desc_pin *pin = pctl->devdata->pins + pin_num; + const struct mtk_desc_function *func = pin->functions; + + while (func && func->name) { + if (func->muxval == fnum) + return func; + func++; + } + + return NULL; +} + +static bool mtk_pctrl_is_function_valid(struct mtk_pinctrl *pctl, + u32 pin_num, u32 fnum) +{ + int i; + + for (i = 0; i < pctl->devdata->npins; i++) { + const struct mtk_desc_pin *pin = pctl->devdata->pins + i; + + if (pin->pin.number == pin_num) { + const struct mtk_desc_function *func = + pin->functions; + + while (func && func->name) { + if (func->muxval == fnum) + return true; + func++; + } + + break; + } + } + + return false; +} + +static int mtk_pctrl_dt_node_to_map_func(struct mtk_pinctrl *pctl, + u32 pin, u32 fnum, struct mtk_pinctrl_group *grp, + struct pinctrl_map **map, unsigned *reserved_maps, + unsigned *num_maps) +{ + bool ret; + + if (*num_maps == *reserved_maps) + return -ENOSPC; + + (*map)[*num_maps].type = PIN_MAP_TYPE_MUX_GROUP; + (*map)[*num_maps].data.mux.group = grp->name; + + ret = mtk_pctrl_is_function_valid(pctl, pin, fnum); + if (!ret) { + dev_err(pctl->dev, "invalid function %d on pin %d .\n", + fnum, pin); + return -EINVAL; + } + + (*map)[*num_maps].data.mux.function = mtk_gpio_functions[fnum]; + (*num_maps)++; + + return 0; +} + +static int mtk_pctrl_dt_subnode_to_map(struct pinctrl_dev *pctldev, + struct device_node *node, + struct pinctrl_map **map, + unsigned *reserved_maps, + unsigned *num_maps) +{ + struct property *pins; + u32 pinfunc, pin, func; + int num_pins, num_funcs, maps_per_pin; + unsigned long *configs; + unsigned int num_configs; + bool has_config = 0; + int i, err; + unsigned reserve = 0; + struct mtk_pinctrl_group *grp; + struct mtk_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev); + + pins = of_find_property(node, "pinmux", NULL); + if (!pins) { + dev_err(pctl->dev, "missing pins property in node %s .\n", + node->name); + return -EINVAL; + } + + err = pinconf_generic_parse_dt_config(node, &configs, &num_configs); + if (num_configs) + has_config = 1; + + num_pins = pins->length / sizeof(u32); + num_funcs = num_pins; + maps_per_pin = 0; + if (num_funcs) + maps_per_pin++; + if (has_config && num_pins >= 1) + maps_per_pin++; + + if (!num_pins || !maps_per_pin) + return -EINVAL; + + reserve = num_pins * maps_per_pin; + + err = pinctrl_utils_reserve_map(pctldev, map, + reserved_maps, num_maps, reserve); + if (err < 0) + goto fail; + + for (i = 0; i < num_pins; i++) { + err = of_property_read_u32_index(node, "pinmux", + i, &pinfunc); + if (err) + goto fail; + + pin = MTK_GET_PIN_NO(pinfunc); + func = MTK_GET_PIN_FUNC(pinfunc); + + if (pin >= pctl->devdata->npins || + func >= ARRAY_SIZE(mtk_gpio_functions)) { + dev_err(pctl->dev, "invalid pins value.\n"); + err = -EINVAL; + goto fail; + } + + grp = mtk_pctrl_find_group_by_pin(pctl, pin); + if (!grp) { + dev_err(pctl->dev, "unable to match pin %d to group\n", + pin); + return -EINVAL; + } + + err = mtk_pctrl_dt_node_to_map_func(pctl, pin, func, grp, map, + reserved_maps, num_maps); + if (err < 0) + goto fail; + + if (has_config) { + err = pinctrl_utils_add_map_configs(pctldev, map, + reserved_maps, num_maps, grp->name, + configs, num_configs, + PIN_MAP_TYPE_CONFIGS_GROUP); + if (err < 0) + goto fail; + } + } + + return 0; + +fail: + return err; +} + +static int mtk_pctrl_dt_node_to_map(struct pinctrl_dev *pctldev, + struct device_node *np_config, + struct pinctrl_map **map, unsigned *num_maps) +{ + struct device_node *np; + unsigned reserved_maps; + int ret; + + *map = NULL; + *num_maps = 0; + reserved_maps = 0; + + for_each_child_of_node(np_config, np) { + ret = mtk_pctrl_dt_subnode_to_map(pctldev, np, map, + &reserved_maps, num_maps); + if (ret < 0) { + pinctrl_utils_dt_free_map(pctldev, *map, *num_maps); + return ret; + } + } + + return 0; +} + +static int mtk_pctrl_get_groups_count(struct pinctrl_dev *pctldev) +{ + struct mtk_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev); + + return pctl->ngroups; +} + +static const char *mtk_pctrl_get_group_name(struct pinctrl_dev *pctldev, + unsigned group) +{ + struct mtk_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev); + + return pctl->groups[group].name; +} + +static int mtk_pctrl_get_group_pins(struct pinctrl_dev *pctldev, + unsigned group, + const unsigned **pins, + unsigned *num_pins) +{ + struct mtk_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev); + + *pins = (unsigned *)&pctl->groups[group].pin; + *num_pins = 1; + + return 0; +} + +static const struct pinctrl_ops mtk_pctrl_ops = { + .dt_node_to_map = mtk_pctrl_dt_node_to_map, + .dt_free_map = pinctrl_utils_dt_free_map, + .get_groups_count = mtk_pctrl_get_groups_count, + .get_group_name = mtk_pctrl_get_group_name, + .get_group_pins = mtk_pctrl_get_group_pins, +}; + +static int mtk_pmx_get_funcs_cnt(struct pinctrl_dev *pctldev) +{ + return ARRAY_SIZE(mtk_gpio_functions); +} + +static const char *mtk_pmx_get_func_name(struct pinctrl_dev *pctldev, + unsigned selector) +{ + return mtk_gpio_functions[selector]; +} + +static int mtk_pmx_get_func_groups(struct pinctrl_dev *pctldev, + unsigned function, + const char * const **groups, + unsigned * const num_groups) +{ + struct mtk_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev); + + *groups = pctl->grp_names; + *num_groups = pctl->ngroups; + + return 0; +} + +static int mtk_pmx_set_mode(struct pinctrl_dev *pctldev, + unsigned long pin, unsigned long mode) +{ + unsigned int reg_addr; + unsigned char bit; + unsigned int val; + unsigned int mask = (1L << GPIO_MODE_BITS) - 1; + struct mtk_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev); + + reg_addr = ((pin / MAX_GPIO_MODE_PER_REG) << pctl->devdata->port_shf) + + pctl->devdata->pinmux_offset; + + bit = pin % MAX_GPIO_MODE_PER_REG; + mask <<= (GPIO_MODE_BITS * bit); + val = (mode << (GPIO_MODE_BITS * bit)); + return regmap_update_bits(mtk_get_regmap(pctl, pin), + reg_addr, mask, val); +} + +static int mtk_pmx_set_mux(struct pinctrl_dev *pctldev, + unsigned function, + unsigned group) +{ + bool ret; + const struct mtk_desc_function *desc; + struct mtk_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev); + struct mtk_pinctrl_group *g = pctl->groups + group; + + ret = mtk_pctrl_is_function_valid(pctl, g->pin, function); + if (!ret) { + dev_err(pctl->dev, "invaild function %d on group %d .\n", + function, group); + return -EINVAL; + } + + desc = mtk_pctrl_find_function_by_pin(pctl, g->pin, function); + if (!desc) + return -EINVAL; + mtk_pmx_set_mode(pctldev, g->pin, desc->muxval); + return 0; +} + +static const struct pinmux_ops mtk_pmx_ops = { + .get_functions_count = mtk_pmx_get_funcs_cnt, + .get_function_name = mtk_pmx_get_func_name, + .get_function_groups = mtk_pmx_get_func_groups, + .set_mux = mtk_pmx_set_mux, + .gpio_set_direction = mtk_pmx_gpio_set_direction, +}; + +static int mtk_gpio_request(struct gpio_chip *chip, unsigned offset) +{ + return pinctrl_request_gpio(chip->base + offset); +} + +static void mtk_gpio_free(struct gpio_chip *chip, unsigned offset) +{ + pinctrl_free_gpio(chip->base + offset); +} + +static int mtk_gpio_direction_input(struct gpio_chip *chip, + unsigned offset) +{ + return pinctrl_gpio_direction_input(chip->base + offset); +} + +static int mtk_gpio_direction_output(struct gpio_chip *chip, + unsigned offset, int value) +{ + mtk_gpio_set(chip, offset, value); + return pinctrl_gpio_direction_output(chip->base + offset); +} + +static int mtk_gpio_get_direction(struct gpio_chip *chip, unsigned offset) +{ + unsigned int reg_addr; + unsigned int bit; + unsigned int read_val = 0; + + struct mtk_pinctrl *pctl = dev_get_drvdata(chip->dev); + + reg_addr = mtk_get_port(pctl, offset) + pctl->devdata->dir_offset; + bit = BIT(offset & 0xf); + regmap_read(pctl->regmap1, reg_addr, &read_val); + return !!(read_val & bit); +} + +static int mtk_gpio_get(struct gpio_chip *chip, unsigned offset) +{ + unsigned int reg_addr; + unsigned int bit; + unsigned int read_val = 0; + struct mtk_pinctrl *pctl = dev_get_drvdata(chip->dev); + + if (mtk_gpio_get_direction(chip, offset)) + reg_addr = mtk_get_port(pctl, offset) + + pctl->devdata->dout_offset; + else + reg_addr = mtk_get_port(pctl, offset) + + pctl->devdata->din_offset; + + bit = BIT(offset & 0xf); + regmap_read(pctl->regmap1, reg_addr, &read_val); + return !!(read_val & bit); +} + +static struct gpio_chip mtk_gpio_chip = { + .owner = THIS_MODULE, + .request = mtk_gpio_request, + .free = mtk_gpio_free, + .direction_input = mtk_gpio_direction_input, + .direction_output = mtk_gpio_direction_output, + .get = mtk_gpio_get, + .set = mtk_gpio_set, + .of_gpio_n_cells = 2, +}; + +static int mtk_pctrl_build_state(struct platform_device *pdev) +{ + struct mtk_pinctrl *pctl = platform_get_drvdata(pdev); + int i; + + pctl->ngroups = pctl->devdata->npins; + + /* Allocate groups */ + pctl->groups = devm_kzalloc(&pdev->dev, + pctl->ngroups * sizeof(*pctl->groups), + GFP_KERNEL); + if (!pctl->groups) + return -ENOMEM; + + /* We assume that one pin is one group, use pin name as group name. */ + pctl->grp_names = devm_kzalloc(&pdev->dev, + pctl->ngroups * sizeof(*pctl->grp_names), + GFP_KERNEL); + if (!pctl->grp_names) + return -ENOMEM; + + for (i = 0; i < pctl->devdata->npins; i++) { + const struct mtk_desc_pin *pin = pctl->devdata->pins + i; + struct mtk_pinctrl_group *group = pctl->groups + i; + + group->name = pin->pin.name; + group->pin = pin->pin.number; + + pctl->grp_names[i] = pin->pin.name; + } + + return 0; +} + +static struct pinctrl_desc mtk_pctrl_desc = { + .confops = &mtk_pconf_ops, + .pctlops = &mtk_pctrl_ops, + .pmxops = &mtk_pmx_ops, +}; + +int mtk_pctrl_init(struct platform_device *pdev, + const struct mtk_pinctrl_devdata *data) +{ + struct pinctrl_pin_desc *pins; + struct mtk_pinctrl *pctl; + struct device_node *np = pdev->dev.of_node, *node; + struct property *prop; + int i, ret; + + pctl = devm_kzalloc(&pdev->dev, sizeof(*pctl), GFP_KERNEL); + if (!pctl) + return -ENOMEM; + + platform_set_drvdata(pdev, pctl); + + prop = of_find_property(np, "pins-are-numbered", NULL); + if (!prop) { + dev_err(&pdev->dev, "only support pins-are-numbered format\n", ret); + return -EINVAL; + } + + node = of_parse_phandle(np, "mediatek,pctl-regmap", 0); + if (node) { + pctl->regmap1 = syscon_node_to_regmap(node); + if (IS_ERR(pctl->regmap1)) + return PTR_ERR(pctl->regmap1); + } + + /* Only 8135 has two base addr, other SoCs have only one. */ + node = of_parse_phandle(np, "mediatek,pctl-regmap", 1); + if (node) { + pctl->regmap2 = syscon_node_to_regmap(node); + if (IS_ERR(pctl->regmap2)) + return PTR_ERR(pctl->regmap2); + } + + pctl->devdata = data; + ret = mtk_pctrl_build_state(pdev); + if (ret) { + dev_err(&pdev->dev, "build state failed: %d\n", ret); + return -EINVAL; + } + + pins = devm_kzalloc(&pdev->dev, + pctl->devdata->npins * sizeof(*pins), + GFP_KERNEL); + if (!pins) + return -ENOMEM; + + for (i = 0; i < pctl->devdata->npins; i++) + pins[i] = pctl->devdata->pins[i].pin; + mtk_pctrl_desc.name = dev_name(&pdev->dev); + mtk_pctrl_desc.owner = THIS_MODULE; + mtk_pctrl_desc.pins = pins; + mtk_pctrl_desc.npins = pctl->devdata->npins; + pctl->dev = &pdev->dev; + pctl->pctl_dev = pinctrl_register(&mtk_pctrl_desc, &pdev->dev, pctl); + if (!pctl->pctl_dev) { + dev_err(&pdev->dev, "couldn't register pinctrl driver\n"); + return -EINVAL; + } + + pctl->chip = devm_kzalloc(&pdev->dev, sizeof(*pctl->chip), GFP_KERNEL); + if (!pctl->chip) { + ret = -ENOMEM; + goto pctrl_error; + } + + pctl->chip = &mtk_gpio_chip; + pctl->chip->ngpio = pctl->devdata->npins; + pctl->chip->label = dev_name(&pdev->dev); + pctl->chip->dev = &pdev->dev; + pctl->chip->base = 0; + + ret = gpiochip_add(pctl->chip); + if (ret) { + ret = -EINVAL; + goto pctrl_error; + } + + /* Register the GPIO to pin mappings. */ + ret = gpiochip_add_pin_range(pctl->chip, dev_name(&pdev->dev), + 0, 0, pctl->devdata->npins); + if (ret) { + ret = -EINVAL; + goto chip_error; + } + + return 0; + +chip_error: + gpiochip_remove(pctl->chip); +pctrl_error: + pinctrl_unregister(pctl->pctl_dev); + return ret; +} + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("MediaTek Pinctrl Driver"); +MODULE_AUTHOR("Hongzhou Yang "); diff --git a/drivers/pinctrl/mediatek/pinctrl-mtk-common.h b/drivers/pinctrl/mediatek/pinctrl-mtk-common.h new file mode 100644 index 000000000000..95a9d57ca1fd --- /dev/null +++ b/drivers/pinctrl/mediatek/pinctrl-mtk-common.h @@ -0,0 +1,185 @@ +/* + * Copyright (c) 2014 MediaTek Inc. + * Author: Hongzhou.Yang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef __PINCTRL_MTK_COMMON_H +#define __PINCTRL_MTK_COMMON_H + +#include +#include +#include + +#define NO_EINT_SUPPORT 255 + +struct mtk_desc_function { + const char *name; + unsigned char muxval; +}; + +struct mtk_desc_eint { + unsigned char eintmux; + unsigned char eintnum; +}; + +struct mtk_desc_pin { + struct pinctrl_pin_desc pin; + const char *chip; + const struct mtk_desc_eint eint; + const struct mtk_desc_function *functions; +}; + +#define MTK_PIN(_pin, _pad, _chip, _eint, ...) \ + { \ + .pin = _pin, \ + .chip = _chip, \ + .eint = _eint, \ + .functions = (struct mtk_desc_function[]){ \ + __VA_ARGS__, { } }, \ + } + +#define MTK_EINT_FUNCTION(_eintmux, _eintnum) \ + { \ + .eintmux = _eintmux, \ + .eintnum = _eintnum, \ + } + +#define MTK_FUNCTION(_val, _name) \ + { \ + .muxval = _val, \ + .name = _name, \ + } + +#define SET_ADDR(x, y) (x + (y->devdata->port_align)) +#define CLR_ADDR(x, y) (x + (y->devdata->port_align << 1)) + +struct mtk_pinctrl_group { + const char *name; + unsigned long config; + unsigned pin; +}; + +/** + * struct mtk_drv_group_desc - Provide driving group data. + * @max_drv: The maximum current of this group. + * @min_drv: The minimum current of this group. + * @low_bit: The lowest bit of this group. + * @high_bit: The highest bit of this group. + * @step: The step current of this group. + */ +struct mtk_drv_group_desc { + unsigned char min_drv; + unsigned char max_drv; + unsigned char low_bit; + unsigned char high_bit; + unsigned char step; +}; + +#define MTK_DRV_GRP(_min, _max, _low, _high, _step) \ + { \ + .min_drv = _min, \ + .max_drv = _max, \ + .low_bit = _low, \ + .high_bit = _high, \ + .step = _step, \ + } + +/** + * struct mtk_pin_drv_grp - Provide each pin driving info. + * @pin: The pin number. + * @offset: The offset of driving register for this pin. + * @bit: The bit of driving register for this pin. + * @grp: The group for this pin belongs to. + */ +struct mtk_pin_drv_grp { + unsigned int pin; + unsigned int offset; + unsigned char bit; + unsigned char grp; +}; + +#define MTK_PIN_DRV_GRP(_pin, _offset, _bit, _grp) \ + { \ + .pin = _pin, \ + .offset = _offset, \ + .bit = _bit, \ + .grp = _grp, \ + } + +/** + * struct mtk_pinctrl_devdata - Provide HW GPIO related data. + * @pins: An array describing all pins the pin controller affects. + * @npins: The number of entries in @pins. + * + * @grp_desc: The driving group info. + * @pin_drv_grp: The driving group for all pins. + * @spec_pull_set: Each SoC may have special pins for pull up/down setting, + * these pins' pull setting are very different, they have separate pull + * up/down bit, R0 and R1 resistor bit, so they need special pull setting. + * If special setting is success, this should return 0, otherwise it should + * return non-zero value. + * + * @dir_offset: The direction register offset. + * @pullen_offset: The pull-up/pull-down enable register offset. + * @pinmux_offset: The pinmux register offset. + * + * @type1_start: Some chips have two base addresses for pull select register, + * that means some pins use the first address and others use the second. This + * member record the start of pin number to use the second address. + * @type1_end: The end of pin number to use the second address. + * + * @port_shf: The shift between two registers. + * @port_mask: The mask of register. + * @port_align: Provide clear register and set register step. + */ +struct mtk_pinctrl_devdata { + const struct mtk_desc_pin *pins; + unsigned int npins; + const struct mtk_drv_group_desc *grp_desc; + unsigned int n_grp_cls; + const struct mtk_pin_drv_grp *pin_drv_grp; + unsigned int n_pin_drv_grps; + int (*spec_pull_set)(struct regmap *reg, unsigned int pin, + unsigned char align, bool isup, unsigned int arg); + unsigned int dir_offset; + unsigned int ies_offset; + unsigned int smt_offset; + unsigned int pullen_offset; + unsigned int pullsel_offset; + unsigned int drv_offset; + unsigned int invser_offset; + unsigned int dout_offset; + unsigned int din_offset; + unsigned int pinmux_offset; + unsigned short type1_start; + unsigned short type1_end; + unsigned char port_shf; + unsigned char port_mask; + unsigned char port_align; +}; + +struct mtk_pinctrl { + struct regmap *regmap1; + struct regmap *regmap2; + struct device *dev; + struct gpio_chip *chip; + struct mtk_pinctrl_group *groups; + unsigned ngroups; + const char **grp_names; + struct pinctrl_dev *pctl_dev; + const struct mtk_pinctrl_devdata *devdata; +}; + +int mtk_pctrl_init(struct platform_device *pdev, + const struct mtk_pinctrl_devdata *data); + +#endif /* __PINCTRL_MTK_COMMON_H */ diff --git a/drivers/pinctrl/mediatek/pinctrl-mtk-mt8135.h b/drivers/pinctrl/mediatek/pinctrl-mtk-mt8135.h new file mode 100644 index 000000000000..5019cef132f3 --- /dev/null +++ b/drivers/pinctrl/mediatek/pinctrl-mtk-mt8135.h @@ -0,0 +1,2114 @@ +/* + * Copyright (c) 2014 MediaTek Inc. + * Author: Hongzhou.Yang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef __PINCTRL_MTK_MT8135_H +#define __PINCTRL_MTK_MT8135_H + +#include +#include + +static const struct mtk_desc_pin mtk_pins_mt8135[] = { + MTK_PIN( + PINCTRL_PIN(0, "MSDC0_DAT7"), + "D21", "mt8135", + MTK_EINT_FUNCTION(2, 49), + MTK_FUNCTION(0, "GPIO0"), + MTK_FUNCTION(1, "MSDC0_DAT7"), + MTK_FUNCTION(2, "EINT49"), + MTK_FUNCTION(3, "I2SOUT_DAT"), + MTK_FUNCTION(4, "DAC_DAT_OUT"), + MTK_FUNCTION(5, "PCM1_DO"), + MTK_FUNCTION(6, "SPI1_MO"), + MTK_FUNCTION(7, "NALE") + ), + MTK_PIN( + PINCTRL_PIN(1, "MSDC0_DAT6"), + "D22", "mt8135", + MTK_EINT_FUNCTION(2, 48), + MTK_FUNCTION(0, "GPIO1"), + MTK_FUNCTION(1, "MSDC0_DAT6"), + MTK_FUNCTION(2, "EINT48"), + MTK_FUNCTION(3, "I2SIN_WS"), + MTK_FUNCTION(4, "DAC_WS"), + MTK_FUNCTION(5, "PCM1_WS"), + MTK_FUNCTION(6, "SPI1_CSN"), + MTK_FUNCTION(7, "NCLE") + ), + MTK_PIN( + PINCTRL_PIN(2, "MSDC0_DAT5"), + "E22", "mt8135", + MTK_EINT_FUNCTION(2, 47), + MTK_FUNCTION(0, "GPIO2"), + MTK_FUNCTION(1, "MSDC0_DAT5"), + MTK_FUNCTION(2, "EINT47"), + MTK_FUNCTION(3, "I2SIN_CK"), + MTK_FUNCTION(4, "DAC_CK"), + MTK_FUNCTION(5, "PCM1_CK"), + MTK_FUNCTION(6, "SPI1_CLK"), + MTK_FUNCTION(7, "NLD4") + ), + MTK_PIN( + PINCTRL_PIN(3, "MSDC0_DAT4"), + "F21", "mt8135", + MTK_EINT_FUNCTION(2, 46), + MTK_FUNCTION(0, "GPIO3"), + MTK_FUNCTION(1, "MSDC0_DAT4"), + MTK_FUNCTION(2, "EINT46"), + MTK_FUNCTION(3, "A_FUNC_CK"), + MTK_FUNCTION(6, "LSCE1B_2X"), + MTK_FUNCTION(7, "NLD5") + ), + MTK_PIN( + PINCTRL_PIN(4, "MSDC0_CMD"), + "F20", "mt8135", + MTK_EINT_FUNCTION(2, 41), + MTK_FUNCTION(0, "GPIO4"), + MTK_FUNCTION(1, "MSDC0_CMD"), + MTK_FUNCTION(2, "EINT41"), + MTK_FUNCTION(3, "A_FUNC_DOUT[0]"), + MTK_FUNCTION(5, "USB_TEST_IO[0]"), + MTK_FUNCTION(6, "LRSTB_2X"), + MTK_FUNCTION(7, "NRNB") + ), + MTK_PIN( + PINCTRL_PIN(5, "MSDC0_CLK"), + "G18", "mt8135", + MTK_EINT_FUNCTION(2, 40), + MTK_FUNCTION(0, "GPIO5"), + MTK_FUNCTION(1, "MSDC0_CLK"), + MTK_FUNCTION(2, "EINT40"), + MTK_FUNCTION(3, "A_FUNC_DOUT[1]"), + MTK_FUNCTION(5, "USB_TEST_IO[1]"), + MTK_FUNCTION(6, "LPTE"), + MTK_FUNCTION(7, "NREB") + ), + MTK_PIN( + PINCTRL_PIN(6, "MSDC0_DAT3"), + "G21", "mt8135", + MTK_EINT_FUNCTION(2, 45), + MTK_FUNCTION(0, "GPIO6"), + MTK_FUNCTION(1, "MSDC0_DAT3"), + MTK_FUNCTION(2, "EINT45"), + MTK_FUNCTION(3, "A_FUNC_DOUT[2]"), + MTK_FUNCTION(5, "USB_TEST_IO[2]"), + MTK_FUNCTION(6, "LSCE0B_2X"), + MTK_FUNCTION(7, "NLD7") + ), + MTK_PIN( + PINCTRL_PIN(7, "MSDC0_DAT2"), + "E21", "mt8135", + MTK_EINT_FUNCTION(2, 44), + MTK_FUNCTION(0, "GPIO7"), + MTK_FUNCTION(1, "MSDC0_DAT2"), + MTK_FUNCTION(2, "EINT44"), + MTK_FUNCTION(3, "A_FUNC_DOUT[3]"), + MTK_FUNCTION(5, "USB_TEST_IO[3]"), + MTK_FUNCTION(6, "LSA0_2X"), + MTK_FUNCTION(7, "NLD14") + ), + MTK_PIN( + PINCTRL_PIN(8, "MSDC0_DAT1"), + "E23", "mt8135", + MTK_EINT_FUNCTION(2, 43), + MTK_FUNCTION(0, "GPIO8"), + MTK_FUNCTION(1, "MSDC0_DAT1"), + MTK_FUNCTION(2, "EINT43"), + MTK_FUNCTION(5, "USB_TEST_IO[4]"), + MTK_FUNCTION(6, "LSCK_2X"), + MTK_FUNCTION(7, "NLD11") + ), + MTK_PIN( + PINCTRL_PIN(9, "MSDC0_DAT0"), + "F22", "mt8135", + MTK_EINT_FUNCTION(2, 42), + MTK_FUNCTION(0, "GPIO9"), + MTK_FUNCTION(1, "MSDC0_DAT0"), + MTK_FUNCTION(2, "EINT42"), + MTK_FUNCTION(5, "USB_TEST_IO[5]"), + MTK_FUNCTION(6, "LSDA_2X") + ), + MTK_PIN( + PINCTRL_PIN(10, "NCEB0"), + "G20", "mt8135", + MTK_EINT_FUNCTION(2, 139), + MTK_FUNCTION(0, "GPIO10"), + MTK_FUNCTION(1, "NCEB0"), + MTK_FUNCTION(2, "EINT139"), + MTK_FUNCTION(7, "TESTA_OUT4") + ), + MTK_PIN( + PINCTRL_PIN(11, "NCEB1"), + "L17", "mt8135", + MTK_EINT_FUNCTION(2, 140), + MTK_FUNCTION(0, "GPIO11"), + MTK_FUNCTION(1, "NCEB1"), + MTK_FUNCTION(2, "EINT140"), + MTK_FUNCTION(6, "USB_DRVVBUS"), + MTK_FUNCTION(7, "TESTA_OUT5") + ), + MTK_PIN( + PINCTRL_PIN(12, "NRNB"), + "G19", "mt8135", + MTK_EINT_FUNCTION(2, 141), + MTK_FUNCTION(0, "GPIO12"), + MTK_FUNCTION(1, "NRNB"), + MTK_FUNCTION(2, "EINT141"), + MTK_FUNCTION(3, "A_FUNC_DOUT[4]"), + MTK_FUNCTION(7, "TESTA_OUT6") + ), + MTK_PIN( + PINCTRL_PIN(13, "NCLE"), + "J18", "mt8135", + MTK_EINT_FUNCTION(2, 142), + MTK_FUNCTION(0, "GPIO13"), + MTK_FUNCTION(1, "NCLE"), + MTK_FUNCTION(2, "EINT142"), + MTK_FUNCTION(3, "A_FUNC_DOUT[5]"), + MTK_FUNCTION(4, "CM2PDN_1X"), + MTK_FUNCTION(6, "NALE"), + MTK_FUNCTION(7, "TESTA_OUT7") + ), + MTK_PIN( + PINCTRL_PIN(14, "NALE"), + "J19", "mt8135", + MTK_EINT_FUNCTION(2, 143), + MTK_FUNCTION(0, "GPIO14"), + MTK_FUNCTION(1, "NALE"), + MTK_FUNCTION(2, "EINT143"), + MTK_FUNCTION(3, "A_FUNC_DOUT[6]"), + MTK_FUNCTION(4, "CM2MCLK_1X"), + MTK_FUNCTION(5, "IRDA_RXD"), + MTK_FUNCTION(6, "NCLE"), + MTK_FUNCTION(7, "TESTA_OUT8") + ), + MTK_PIN( + PINCTRL_PIN(15, "NREB"), + "L18", "mt8135", + MTK_EINT_FUNCTION(2, 144), + MTK_FUNCTION(0, "GPIO15"), + MTK_FUNCTION(1, "NREB"), + MTK_FUNCTION(2, "EINT144"), + MTK_FUNCTION(3, "A_FUNC_DOUT[7]"), + MTK_FUNCTION(4, "CM2RST_1X"), + MTK_FUNCTION(5, "IRDA_TXD"), + MTK_FUNCTION(7, "TESTA_OUT9") + ), + MTK_PIN( + PINCTRL_PIN(16, "NWEB"), + "J20", "mt8135", + MTK_EINT_FUNCTION(2, 145), + MTK_FUNCTION(0, "GPIO16"), + MTK_FUNCTION(1, "NWEB"), + MTK_FUNCTION(2, "EINT145"), + MTK_FUNCTION(3, "A_FUNC_DIN[0]"), + MTK_FUNCTION(4, "CM2PCLK_1X"), + MTK_FUNCTION(5, "IRDA_PDN"), + MTK_FUNCTION(7, "TESTA_OUT10") + ), + MTK_PIN( + PINCTRL_PIN(17, "NLD0"), + "K21", "mt8135", + MTK_EINT_FUNCTION(2, 146), + MTK_FUNCTION(0, "GPIO17"), + MTK_FUNCTION(1, "NLD0"), + MTK_FUNCTION(2, "EINT146"), + MTK_FUNCTION(3, "A_FUNC_DIN[1]"), + MTK_FUNCTION(4, "CM2DAT_1X[0]"), + MTK_FUNCTION(5, "I2SIN_CK"), + MTK_FUNCTION(6, "DAC_CK"), + MTK_FUNCTION(7, "TESTA_OUT11") + ), + MTK_PIN( + PINCTRL_PIN(18, "NLD1"), + "K22", "mt8135", + MTK_EINT_FUNCTION(2, 147), + MTK_FUNCTION(0, "GPIO18"), + MTK_FUNCTION(1, "NLD1"), + MTK_FUNCTION(2, "EINT147"), + MTK_FUNCTION(3, "A_FUNC_DIN[2]"), + MTK_FUNCTION(4, "CM2DAT_1X[1]"), + MTK_FUNCTION(5, "I2SIN_WS"), + MTK_FUNCTION(6, "DAC_WS"), + MTK_FUNCTION(7, "TESTA_OUT12") + ), + MTK_PIN( + PINCTRL_PIN(19, "NLD2"), + "J21", "mt8135", + MTK_EINT_FUNCTION(2, 148), + MTK_FUNCTION(0, "GPIO19"), + MTK_FUNCTION(1, "NLD2"), + MTK_FUNCTION(2, "EINT148"), + MTK_FUNCTION(3, "A_FUNC_DIN[3]"), + MTK_FUNCTION(4, "CM2DAT_1X[2]"), + MTK_FUNCTION(5, "I2SOUT_DAT"), + MTK_FUNCTION(6, "DAC_DAT_OUT"), + MTK_FUNCTION(7, "TESTA_OUT13") + ), + MTK_PIN( + PINCTRL_PIN(20, "NLD3"), + "J23", "mt8135", + MTK_EINT_FUNCTION(2, 149), + MTK_FUNCTION(0, "GPIO20"), + MTK_FUNCTION(1, "NLD3"), + MTK_FUNCTION(2, "EINT149"), + MTK_FUNCTION(3, "A_FUNC_DIN[4]"), + MTK_FUNCTION(4, "CM2DAT_1X[3]"), + MTK_FUNCTION(7, "TESTA_OUT14") + ), + MTK_PIN( + PINCTRL_PIN(21, "NLD4"), + "J22", "mt8135", + MTK_EINT_FUNCTION(2, 150), + MTK_FUNCTION(0, "GPIO21"), + MTK_FUNCTION(1, "NLD4"), + MTK_FUNCTION(2, "EINT150"), + MTK_FUNCTION(3, "A_FUNC_DIN[5]"), + MTK_FUNCTION(4, "CM2DAT_1X[4]"), + MTK_FUNCTION(7, "TESTA_OUT15") + ), + MTK_PIN( + PINCTRL_PIN(22, "NLD5"), + "H21", "mt8135", + MTK_EINT_FUNCTION(2, 151), + MTK_FUNCTION(0, "GPIO22"), + MTK_FUNCTION(1, "NLD5"), + MTK_FUNCTION(2, "EINT151"), + MTK_FUNCTION(3, "A_FUNC_DIN[6]"), + MTK_FUNCTION(4, "CM2DAT_1X[5]"), + MTK_FUNCTION(7, "TESTA_OUT16") + ), + MTK_PIN( + PINCTRL_PIN(23, "NLD6"), + "H22", "mt8135", + MTK_EINT_FUNCTION(2, 152), + MTK_FUNCTION(0, "GPIO23"), + MTK_FUNCTION(1, "NLD6"), + MTK_FUNCTION(2, "EINT152"), + MTK_FUNCTION(3, "A_FUNC_DIN[7]"), + MTK_FUNCTION(4, "CM2DAT_1X[6]"), + MTK_FUNCTION(7, "TESTA_OUT17") + ), + MTK_PIN( + PINCTRL_PIN(24, "NLD7"), + "H20", "mt8135", + MTK_EINT_FUNCTION(2, 153), + MTK_FUNCTION(0, "GPIO24"), + MTK_FUNCTION(1, "NLD7"), + MTK_FUNCTION(2, "EINT153"), + MTK_FUNCTION(3, "A_FUNC_DIN[8]"), + MTK_FUNCTION(4, "CM2DAT_1X[7]"), + MTK_FUNCTION(7, "TESTA_OUT18") + ), + MTK_PIN( + PINCTRL_PIN(25, "NLD8"), + NULL, "mt8135", + MTK_EINT_FUNCTION(2, 154), + MTK_FUNCTION(0, "GPIO25"), + MTK_FUNCTION(1, "NLD8"), + MTK_FUNCTION(2, "EINT154"), + MTK_FUNCTION(4, "CM2DAT_1X[8]") + ), + MTK_PIN( + PINCTRL_PIN(26, "NLD9"), + NULL, "mt8135", + MTK_EINT_FUNCTION(2, 155), + MTK_FUNCTION(0, "GPIO26"), + MTK_FUNCTION(1, "NLD9"), + MTK_FUNCTION(2, "EINT155"), + MTK_FUNCTION(4, "CM2DAT_1X[9]"), + MTK_FUNCTION(5, "PWM1") + ), + MTK_PIN( + PINCTRL_PIN(27, "NLD10"), + NULL, "mt8135", + MTK_EINT_FUNCTION(2, 156), + MTK_FUNCTION(0, "GPIO27"), + MTK_FUNCTION(1, "NLD10"), + MTK_FUNCTION(2, "EINT156"), + MTK_FUNCTION(4, "CM2VSYNC_1X"), + MTK_FUNCTION(5, "PWM2") + ), + MTK_PIN( + PINCTRL_PIN(28, "NLD11"), + NULL, "mt8135", + MTK_EINT_FUNCTION(2, 157), + MTK_FUNCTION(0, "GPIO28"), + MTK_FUNCTION(1, "NLD11"), + MTK_FUNCTION(2, "EINT157"), + MTK_FUNCTION(4, "CM2HSYNC_1X"), + MTK_FUNCTION(5, "PWM3") + ), + MTK_PIN( + PINCTRL_PIN(29, "NLD12"), + NULL, "mt8135", + MTK_EINT_FUNCTION(2, 158), + MTK_FUNCTION(0, "GPIO29"), + MTK_FUNCTION(1, "NLD12"), + MTK_FUNCTION(2, "EINT158"), + MTK_FUNCTION(3, "I2SIN_CK"), + MTK_FUNCTION(4, "DAC_CK"), + MTK_FUNCTION(5, "PCM1_CK") + ), + MTK_PIN( + PINCTRL_PIN(30, "NLD13"), + NULL, "mt8135", + MTK_EINT_FUNCTION(2, 159), + MTK_FUNCTION(0, "GPIO30"), + MTK_FUNCTION(1, "NLD13"), + MTK_FUNCTION(2, "EINT159"), + MTK_FUNCTION(3, "I2SIN_WS"), + MTK_FUNCTION(4, "DAC_WS"), + MTK_FUNCTION(5, "PCM1_WS") + ), + MTK_PIN( + PINCTRL_PIN(31, "NLD14"), + NULL, "mt8135", + MTK_EINT_FUNCTION(2, 160), + MTK_FUNCTION(0, "GPIO31"), + MTK_FUNCTION(1, "NLD14"), + MTK_FUNCTION(2, "EINT160"), + MTK_FUNCTION(3, "I2SOUT_DAT"), + MTK_FUNCTION(4, "DAC_DAT_OUT"), + MTK_FUNCTION(5, "PCM1_DO") + ), + MTK_PIN( + PINCTRL_PIN(32, "NLD15"), + NULL, "mt8135", + MTK_EINT_FUNCTION(2, 161), + MTK_FUNCTION(0, "GPIO32"), + MTK_FUNCTION(1, "NLD15"), + MTK_FUNCTION(2, "EINT161"), + MTK_FUNCTION(3, "DISP_PWM"), + MTK_FUNCTION(4, "PWM4"), + MTK_FUNCTION(5, "PCM1_DI") + ), + MTK_PIN( + PINCTRL_PIN(33, "MSDC0_RSTB"), + "G22", "mt8135", + MTK_EINT_FUNCTION(2, 50), + MTK_FUNCTION(0, "GPIO33"), + MTK_FUNCTION(1, "MSDC0_RSTB"), + MTK_FUNCTION(2, "EINT50"), + MTK_FUNCTION(3, "I2SIN_DAT"), + MTK_FUNCTION(5, "PCM1_DI"), + MTK_FUNCTION(6, "SPI1_MI"), + MTK_FUNCTION(7, "NLD10") + ), + MTK_PIN( + PINCTRL_PIN(34, "IDDIG"), + "N17", "mt8135", + MTK_EINT_FUNCTION(2, 34), + MTK_FUNCTION(0, "GPIO34"), + MTK_FUNCTION(1, "IDDIG"), + MTK_FUNCTION(2, "EINT34") + ), + MTK_PIN( + PINCTRL_PIN(35, "SCL3"), + "L19", "mt8135", + MTK_EINT_FUNCTION(2, 96), + MTK_FUNCTION(0, "GPIO35"), + MTK_FUNCTION(1, "SCL3"), + MTK_FUNCTION(2, "EINT96"), + MTK_FUNCTION(3, "CLKM6"), + MTK_FUNCTION(4, "PWM6") + ), + MTK_PIN( + PINCTRL_PIN(36, "SDA3"), + "L20", "mt8135", + MTK_EINT_FUNCTION(2, 97), + MTK_FUNCTION(0, "GPIO36"), + MTK_FUNCTION(1, "SDA3"), + MTK_FUNCTION(2, "EINT97") + ), + MTK_PIN( + PINCTRL_PIN(37, "AUD_CLK_MOSI"), + "L21", "mt8135", + MTK_EINT_FUNCTION(4, 19), + MTK_FUNCTION(0, "GPIO37"), + MTK_FUNCTION(1, "AUD_CLK"), + MTK_FUNCTION(2, "ADC_CK"), + MTK_FUNCTION(3, " HDMI_SDATA0"), + MTK_FUNCTION(4, "EINT19"), + MTK_FUNCTION(5, "USB_TEST_IO[6]"), + MTK_FUNCTION(7, "TESTA_OUT19") + ), + MTK_PIN( + PINCTRL_PIN(38, "AUD_DAT_MOSI"), + "L23", "mt8135", + MTK_EINT_FUNCTION(4, 21), + MTK_FUNCTION(0, "GPIO38"), + MTK_FUNCTION(1, "AUD_DAT_MOSI"), + MTK_FUNCTION(2, "ADC_WS"), + MTK_FUNCTION(3, "AUD_DAT_MISO"), + MTK_FUNCTION(4, "EINT21"), + MTK_FUNCTION(5, "USB_TEST_IO[7]"), + MTK_FUNCTION(7, "TESTA_OUT20") + ), + MTK_PIN( + PINCTRL_PIN(39, "AUD_DAT_MISO"), + "L22", "mt8135", + MTK_EINT_FUNCTION(4, 20), + MTK_FUNCTION(0, "GPIO39"), + MTK_FUNCTION(1, "AUD_DAT_MISO"), + MTK_FUNCTION(2, "ADC_DAT_IN"), + MTK_FUNCTION(3, "AUD_DAT_MOSI"), + MTK_FUNCTION(4, "EINT20"), + MTK_FUNCTION(5, "USB_TEST_IO[8]"), + MTK_FUNCTION(7, "TESTA_OUT21") + ), + MTK_PIN( + PINCTRL_PIN(40, "DAC_CLK"), + "P21", "mt8135", + MTK_EINT_FUNCTION(2, 22), + MTK_FUNCTION(0, "GPIO40"), + MTK_FUNCTION(1, "DAC_CK"), + MTK_FUNCTION(2, "EINT22"), + MTK_FUNCTION(3, " HDMI_SDATA1"), + MTK_FUNCTION(5, "USB_TEST_IO[9]"), + MTK_FUNCTION(7, "TESTA_OUT22") + ), + MTK_PIN( + PINCTRL_PIN(41, "DAC_WS"), + "N18", "mt8135", + MTK_EINT_FUNCTION(2, 24), + MTK_FUNCTION(0, "GPIO41"), + MTK_FUNCTION(1, "DAC_WS"), + MTK_FUNCTION(2, "EINT24"), + MTK_FUNCTION(3, " HDMI_SDATA2"), + MTK_FUNCTION(5, "USB_TEST_IO[10]"), + MTK_FUNCTION(7, "TESTA_OUT23") + ), + MTK_PIN( + PINCTRL_PIN(42, "DAC_DAT_OUT"), + "N22", "mt8135", + MTK_EINT_FUNCTION(2, 23), + MTK_FUNCTION(0, "GPIO42"), + MTK_FUNCTION(1, "DAC_DAT_OUT"), + MTK_FUNCTION(2, "EINT23"), + MTK_FUNCTION(3, " HDMI_SDATA3"), + MTK_FUNCTION(5, "USB_TEST_IO[11]"), + MTK_FUNCTION(7, "TESTA_OUT24") + ), + MTK_PIN( + PINCTRL_PIN(43, "PWRAP_SPI0_MO"), + "M22", "mt8135", + MTK_EINT_FUNCTION(2, 29), + MTK_FUNCTION(0, "GPIO43"), + MTK_FUNCTION(1, "PWRAP_SPIDI"), + MTK_FUNCTION(2, "EINT29") + ), + MTK_PIN( + PINCTRL_PIN(44, "PWRAP_SPI0_MI"), + "P23", "mt8135", + MTK_EINT_FUNCTION(2, 28), + MTK_FUNCTION(0, "GPIO44"), + MTK_FUNCTION(1, "PWRAP_SPIDO"), + MTK_FUNCTION(2, "EINT28") + ), + MTK_PIN( + PINCTRL_PIN(45, "PWRAP_SPI0_CSN"), + "M21", "mt8135", + MTK_EINT_FUNCTION(2, 27), + MTK_FUNCTION(0, "GPIO45"), + MTK_FUNCTION(1, "PWRAP_SPICS_B_I"), + MTK_FUNCTION(2, "EINT27") + ), + MTK_PIN( + PINCTRL_PIN(46, "PWRAP_SPI0_CLK"), + "P22", "mt8135", + MTK_EINT_FUNCTION(2, 26), + MTK_FUNCTION(0, "GPIO46"), + MTK_FUNCTION(1, "PWRAP_SPICK_I"), + MTK_FUNCTION(2, "EINT26") + ), + MTK_PIN( + PINCTRL_PIN(47, "PWRAP_EVENT"), + "M23", "mt8135", + MTK_EINT_FUNCTION(2, 25), + MTK_FUNCTION(0, "GPIO47"), + MTK_FUNCTION(1, "PWRAP_EVENT_IN"), + MTK_FUNCTION(2, "EINT25"), + MTK_FUNCTION(7, "TESTA_OUT2") + ), + MTK_PIN( + PINCTRL_PIN(48, "RTC32K_CK"), + "N20", "mt8135", + MTK_EINT_FUNCTION(NO_EINT_SUPPORT, NO_EINT_SUPPORT), + MTK_FUNCTION(0, "GPIO48"), + MTK_FUNCTION(1, "RTC32K_CK") + ), + MTK_PIN( + PINCTRL_PIN(49, "WATCHDOG"), + "R22", "mt8135", + MTK_EINT_FUNCTION(2, 36), + MTK_FUNCTION(0, "GPIO49"), + MTK_FUNCTION(1, "WATCHDOG"), + MTK_FUNCTION(2, "EINT36") + ), + MTK_PIN( + PINCTRL_PIN(50, "SRCLKENA"), + "T22", "mt8135", + MTK_EINT_FUNCTION(2, 38), + MTK_FUNCTION(0, "GPIO50"), + MTK_FUNCTION(1, "SRCLKENA"), + MTK_FUNCTION(2, "EINT38") + ), + MTK_PIN( + PINCTRL_PIN(51, "SRCVOLTEN"), + "T23", "mt8135", + MTK_EINT_FUNCTION(2, 37), + MTK_FUNCTION(0, "GPIO51"), + MTK_FUNCTION(1, "SRCVOLTEN"), + MTK_FUNCTION(2, "EINT37") + ), + MTK_PIN( + PINCTRL_PIN(52, "EINT0"), + "T21", "mt8135", + MTK_EINT_FUNCTION(1, 0), + MTK_FUNCTION(0, "GPIO52"), + MTK_FUNCTION(1, "EINT0"), + MTK_FUNCTION(2, "PWM1"), + MTK_FUNCTION(3, "CLKM0"), + MTK_FUNCTION(4, " SPDIF_OUT"), + MTK_FUNCTION(5, "USB_TEST_IO[12]"), + MTK_FUNCTION(7, "USB_SCL") + ), + MTK_PIN( + PINCTRL_PIN(53, "URXD2"), + "R18", "mt8135", + MTK_EINT_FUNCTION(2, 83), + MTK_FUNCTION(0, "GPIO53"), + MTK_FUNCTION(1, "URXD2"), + MTK_FUNCTION(2, "EINT83"), + MTK_FUNCTION(4, " HDMI_LRCK"), + MTK_FUNCTION(5, "CLKM3"), + MTK_FUNCTION(7, "UTXD2") + ), + MTK_PIN( + PINCTRL_PIN(54, "UTXD2"), + "R17", "mt8135", + MTK_EINT_FUNCTION(2, 82), + MTK_FUNCTION(0, "GPIO54"), + MTK_FUNCTION(1, "UTXD2"), + MTK_FUNCTION(2, "EINT82"), + MTK_FUNCTION(4, " HDMI_BCK_OUT"), + MTK_FUNCTION(5, "CLKM2"), + MTK_FUNCTION(7, "URXD2") + ), + MTK_PIN( + PINCTRL_PIN(55, "UCTS2"), + "R20", "mt8135", + MTK_EINT_FUNCTION(2, 84), + MTK_FUNCTION(0, "GPIO55"), + MTK_FUNCTION(1, "UCTS2"), + MTK_FUNCTION(2, "EINT84"), + MTK_FUNCTION(5, "PWM1"), + MTK_FUNCTION(7, "URTS2") + ), + MTK_PIN( + PINCTRL_PIN(56, "URTS2"), + "R19", "mt8135", + MTK_EINT_FUNCTION(2, 85), + MTK_FUNCTION(0, "GPIO56"), + MTK_FUNCTION(1, "URTS2"), + MTK_FUNCTION(2, "EINT85"), + MTK_FUNCTION(5, "PWM2"), + MTK_FUNCTION(7, "UCTS2") + ), + MTK_PIN( + PINCTRL_PIN(57, "JTCK"), + "V17", "mt8135", + MTK_EINT_FUNCTION(2, 188), + MTK_FUNCTION(0, "GPIO57"), + MTK_FUNCTION(1, "JTCK"), + MTK_FUNCTION(2, "EINT188"), + MTK_FUNCTION(3, "DSP1_ICK") + ), + MTK_PIN( + PINCTRL_PIN(58, "JTDO"), + "T16", "mt8135", + MTK_EINT_FUNCTION(2, 190), + MTK_FUNCTION(0, "GPIO58"), + MTK_FUNCTION(1, "JTDO"), + MTK_FUNCTION(2, "EINT190"), + MTK_FUNCTION(3, "DSP2_IMS") + ), + MTK_PIN( + PINCTRL_PIN(59, "JTRST_B"), + "T19", "mt8135", + MTK_EINT_FUNCTION(2, 0), + MTK_FUNCTION(0, "GPIO59"), + MTK_FUNCTION(1, "JTRST_B"), + MTK_FUNCTION(2, "EINT0"), + MTK_FUNCTION(3, "DSP2_ICK") + ), + MTK_PIN( + PINCTRL_PIN(60, "JTDI"), + "T18", "mt8135", + MTK_EINT_FUNCTION(2, 189), + MTK_FUNCTION(0, "GPIO60"), + MTK_FUNCTION(1, "JTDI"), + MTK_FUNCTION(2, "EINT189"), + MTK_FUNCTION(3, "DSP1_IMS") + ), + MTK_PIN( + PINCTRL_PIN(61, "JRTCK"), + "T20", "mt8135", + MTK_EINT_FUNCTION(2, 187), + MTK_FUNCTION(0, "GPIO61"), + MTK_FUNCTION(1, "JRTCK"), + MTK_FUNCTION(2, "EINT187"), + MTK_FUNCTION(3, "DSP1_ID") + ), + MTK_PIN( + PINCTRL_PIN(62, "JTMS"), + "T17", "mt8135", + MTK_EINT_FUNCTION(2, 191), + MTK_FUNCTION(0, "GPIO62"), + MTK_FUNCTION(1, "JTMS"), + MTK_FUNCTION(2, "EINT191"), + MTK_FUNCTION(3, "DSP2_ID") + ), + MTK_PIN( + PINCTRL_PIN(63, "MSDC1_INSI"), + "V18", "mt8135", + MTK_EINT_FUNCTION(1, 15), + MTK_FUNCTION(0, "GPIO63"), + MTK_FUNCTION(1, "MSDC1_INSI"), + MTK_FUNCTION(3, "SCL5"), + MTK_FUNCTION(4, "PWM6"), + MTK_FUNCTION(5, "CLKM5"), + MTK_FUNCTION(7, "TESTB_OUT6") + ), + MTK_PIN( + PINCTRL_PIN(64, "MSDC1_SDWPI"), + "W18", "mt8135", + MTK_EINT_FUNCTION(2, 58), + MTK_FUNCTION(0, "GPIO64"), + MTK_FUNCTION(1, "MSDC1_SDWPI"), + MTK_FUNCTION(2, "EINT58"), + MTK_FUNCTION(3, "SDA5"), + MTK_FUNCTION(4, "PWM7"), + MTK_FUNCTION(5, "CLKM6"), + MTK_FUNCTION(7, "TESTB_OUT7") + ), + MTK_PIN( + PINCTRL_PIN(65, "MSDC2_INSI"), + "U22", "mt8135", + MTK_EINT_FUNCTION(1, 14), + MTK_FUNCTION(0, "GPIO65"), + MTK_FUNCTION(1, "MSDC2_INSI"), + MTK_FUNCTION(5, "USB_TEST_IO[27]"), + MTK_FUNCTION(7, "TESTA_OUT3") + ), + MTK_PIN( + PINCTRL_PIN(66, "MSDC2_SDWPI"), + "U21", "mt8135", + MTK_EINT_FUNCTION(2, 66), + MTK_FUNCTION(0, "GPIO66"), + MTK_FUNCTION(1, "MSDC2_SDWPI"), + MTK_FUNCTION(2, "EINT66"), + MTK_FUNCTION(5, "USB_TEST_IO[28]") + ), + MTK_PIN( + PINCTRL_PIN(67, "URXD4"), + "V23", "mt8135", + MTK_EINT_FUNCTION(2, 89), + MTK_FUNCTION(0, "GPIO67"), + MTK_FUNCTION(1, "URXD4"), + MTK_FUNCTION(2, "EINT89"), + MTK_FUNCTION(3, "URXD1"), + MTK_FUNCTION(6, "UTXD4"), + MTK_FUNCTION(7, "TESTB_OUT10") + ), + MTK_PIN( + PINCTRL_PIN(68, "UTXD4"), + "V22", "mt8135", + MTK_EINT_FUNCTION(2, 88), + MTK_FUNCTION(0, "GPIO68"), + MTK_FUNCTION(1, "UTXD4"), + MTK_FUNCTION(2, "EINT88"), + MTK_FUNCTION(3, "UTXD1"), + MTK_FUNCTION(6, "URXD4"), + MTK_FUNCTION(7, "TESTB_OUT11") + ), + MTK_PIN( + PINCTRL_PIN(69, "URXD1"), + "W22", "mt8135", + MTK_EINT_FUNCTION(2, 79), + MTK_FUNCTION(0, "GPIO69"), + MTK_FUNCTION(1, "URXD1"), + MTK_FUNCTION(2, "EINT79"), + MTK_FUNCTION(3, "URXD4"), + MTK_FUNCTION(6, "UTXD1"), + MTK_FUNCTION(7, "TESTB_OUT24") + ), + MTK_PIN( + PINCTRL_PIN(70, "UTXD1"), + "V21", "mt8135", + MTK_EINT_FUNCTION(2, 78), + MTK_FUNCTION(0, "GPIO70"), + MTK_FUNCTION(1, "UTXD1"), + MTK_FUNCTION(2, "EINT78"), + MTK_FUNCTION(3, "UTXD4"), + MTK_FUNCTION(6, "URXD1"), + MTK_FUNCTION(7, "TESTB_OUT25") + ), + MTK_PIN( + PINCTRL_PIN(71, "UCTS1"), + "V19", "mt8135", + MTK_EINT_FUNCTION(2, 80), + MTK_FUNCTION(0, "GPIO71"), + MTK_FUNCTION(1, "UCTS1"), + MTK_FUNCTION(2, "EINT80"), + MTK_FUNCTION(5, "CLKM0"), + MTK_FUNCTION(6, "URTS1"), + MTK_FUNCTION(7, "TESTB_OUT31") + ), + MTK_PIN( + PINCTRL_PIN(72, "URTS1"), + "V20", "mt8135", + MTK_EINT_FUNCTION(2, 81), + MTK_FUNCTION(0, "GPIO72"), + MTK_FUNCTION(1, "URTS1"), + MTK_FUNCTION(2, "EINT81"), + MTK_FUNCTION(5, "CLKM1"), + MTK_FUNCTION(6, "UCTS1"), + MTK_FUNCTION(7, "TESTB_OUT21") + ), + MTK_PIN( + PINCTRL_PIN(73, "PWM1"), + "W17", "mt8135", + MTK_EINT_FUNCTION(2, 73), + MTK_FUNCTION(0, "GPIO73"), + MTK_FUNCTION(1, "PWM1"), + MTK_FUNCTION(2, "EINT73"), + MTK_FUNCTION(5, "USB_DRVVBUS"), + MTK_FUNCTION(6, "DISP_PWM"), + MTK_FUNCTION(7, "TESTB_OUT8") + ), + MTK_PIN( + PINCTRL_PIN(74, "PWM2"), + "Y17", "mt8135", + MTK_EINT_FUNCTION(2, 74), + MTK_FUNCTION(0, "GPIO74"), + MTK_FUNCTION(1, "PWM2"), + MTK_FUNCTION(2, "EINT74"), + MTK_FUNCTION(3, "DPI33_CK"), + MTK_FUNCTION(4, "PWM5"), + MTK_FUNCTION(5, "URXD2"), + MTK_FUNCTION(6, "DISP_PWM"), + MTK_FUNCTION(7, "TESTB_OUT9") + ), + MTK_PIN( + PINCTRL_PIN(75, "PWM3"), + "Y19", "mt8135", + MTK_EINT_FUNCTION(2, 75), + MTK_FUNCTION(0, "GPIO75"), + MTK_FUNCTION(1, "PWM3"), + MTK_FUNCTION(2, "EINT75"), + MTK_FUNCTION(3, "DPI33_D0"), + MTK_FUNCTION(4, "PWM6"), + MTK_FUNCTION(5, "UTXD2"), + MTK_FUNCTION(6, "DISP_PWM"), + MTK_FUNCTION(7, "TESTB_OUT12") + ), + MTK_PIN( + PINCTRL_PIN(76, "PWM4"), + "W19", "mt8135", + MTK_EINT_FUNCTION(2, 76), + MTK_FUNCTION(0, "GPIO76"), + MTK_FUNCTION(1, "PWM4"), + MTK_FUNCTION(2, "EINT76"), + MTK_FUNCTION(3, "DPI33_D1"), + MTK_FUNCTION(4, "PWM7"), + MTK_FUNCTION(6, "DISP_PWM"), + MTK_FUNCTION(7, "TESTB_OUT13") + ), + MTK_PIN( + PINCTRL_PIN(77, "MSDC2_DAT2"), + "W21", "mt8135", + MTK_EINT_FUNCTION(2, 63), + MTK_FUNCTION(0, "GPIO77"), + MTK_FUNCTION(1, "MSDC2_DAT2"), + MTK_FUNCTION(2, "EINT63"), + MTK_FUNCTION(4, "DSP2_IMS"), + MTK_FUNCTION(6, "DPI33_D6"), + MTK_FUNCTION(7, "TESTA_OUT25") + ), + MTK_PIN( + PINCTRL_PIN(78, "MSDC2_DAT3"), + "AA23", "mt8135", + MTK_EINT_FUNCTION(2, 64), + MTK_FUNCTION(0, "GPIO78"), + MTK_FUNCTION(1, "MSDC2_DAT3"), + MTK_FUNCTION(2, "EINT64"), + MTK_FUNCTION(4, "DSP2_ID"), + MTK_FUNCTION(6, "DPI33_D7"), + MTK_FUNCTION(7, "TESTA_OUT26") + ), + MTK_PIN( + PINCTRL_PIN(79, "MSDC2_CMD"), + "Y22", "mt8135", + MTK_EINT_FUNCTION(2, 60), + MTK_FUNCTION(0, "GPIO79"), + MTK_FUNCTION(1, "MSDC2_CMD"), + MTK_FUNCTION(2, "EINT60"), + MTK_FUNCTION(4, "DSP1_IMS"), + MTK_FUNCTION(5, "PCM1_WS"), + MTK_FUNCTION(6, "DPI33_D3"), + MTK_FUNCTION(7, "TESTA_OUT0") + ), + MTK_PIN( + PINCTRL_PIN(80, "MSDC2_CLK"), + "AA22", "mt8135", + MTK_EINT_FUNCTION(2, 59), + MTK_FUNCTION(0, "GPIO80"), + MTK_FUNCTION(1, "MSDC2_CLK"), + MTK_FUNCTION(2, "EINT59"), + MTK_FUNCTION(4, "DSP1_ICK"), + MTK_FUNCTION(5, "PCM1_CK"), + MTK_FUNCTION(6, "DPI33_D2"), + MTK_FUNCTION(7, "TESTA_OUT1") + ), + MTK_PIN( + PINCTRL_PIN(81, "MSDC2_DAT1"), + "Y21", "mt8135", + MTK_EINT_FUNCTION(2, 62), + MTK_FUNCTION(0, "GPIO81"), + MTK_FUNCTION(1, "MSDC2_DAT1"), + MTK_FUNCTION(2, "EINT62"), + MTK_FUNCTION(4, "DSP2_ICK"), + MTK_FUNCTION(5, "PCM1_DO"), + MTK_FUNCTION(6, "DPI33_D5") + ), + MTK_PIN( + PINCTRL_PIN(82, "MSDC2_DAT0"), + "AB22", "mt8135", + MTK_EINT_FUNCTION(2, 61), + MTK_FUNCTION(0, "GPIO82"), + MTK_FUNCTION(1, "MSDC2_DAT0"), + MTK_FUNCTION(2, "EINT61"), + MTK_FUNCTION(4, "DSP1_ID"), + MTK_FUNCTION(5, "PCM1_DI"), + MTK_FUNCTION(6, "DPI33_D4") + ), + MTK_PIN( + PINCTRL_PIN(83, "MSDC1_DAT0"), + "AC19", "mt8135", + MTK_EINT_FUNCTION(2, 53), + MTK_FUNCTION(0, "GPIO83"), + MTK_FUNCTION(1, "MSDC1_DAT0"), + MTK_FUNCTION(2, "EINT53"), + MTK_FUNCTION(3, "SCL1"), + MTK_FUNCTION(4, "PWM2"), + MTK_FUNCTION(5, "CLKM1"), + MTK_FUNCTION(7, "TESTB_OUT2") + ), + MTK_PIN( + PINCTRL_PIN(84, "MSDC1_DAT1"), + "AA19", "mt8135", + MTK_EINT_FUNCTION(2, 54), + MTK_FUNCTION(0, "GPIO84"), + MTK_FUNCTION(1, "MSDC1_DAT1"), + MTK_FUNCTION(2, "EINT54"), + MTK_FUNCTION(3, "SDA1"), + MTK_FUNCTION(4, "PWM3"), + MTK_FUNCTION(5, "CLKM2"), + MTK_FUNCTION(7, "TESTB_OUT3") + ), + MTK_PIN( + PINCTRL_PIN(85, "MSDC1_CMD"), + "AA20", "mt8135", + MTK_EINT_FUNCTION(2, 52), + MTK_FUNCTION(0, "GPIO85"), + MTK_FUNCTION(1, "MSDC1_CMD"), + MTK_FUNCTION(2, "EINT52"), + MTK_FUNCTION(3, "SDA0"), + MTK_FUNCTION(4, "PWM1"), + MTK_FUNCTION(5, "CLKM0"), + MTK_FUNCTION(7, "TESTB_OUT1") + ), + MTK_PIN( + PINCTRL_PIN(86, "MSDC1_CLK"), + "AB19", "mt8135", + MTK_EINT_FUNCTION(2, 51), + MTK_FUNCTION(0, "GPIO86"), + MTK_FUNCTION(1, "MSDC1_CLK"), + MTK_FUNCTION(2, "EINT51"), + MTK_FUNCTION(3, "SCL0"), + MTK_FUNCTION(4, "DISP_PWM"), + MTK_FUNCTION(7, "TESTB_OUT0") + ), + MTK_PIN( + PINCTRL_PIN(87, "MSDC1_DAT2"), + "AA21", "mt8135", + MTK_EINT_FUNCTION(2, 55), + MTK_FUNCTION(0, "GPIO87"), + MTK_FUNCTION(1, "MSDC1_DAT2"), + MTK_FUNCTION(2, "EINT55"), + MTK_FUNCTION(3, "SCL4"), + MTK_FUNCTION(4, "PWM4"), + MTK_FUNCTION(5, "CLKM3"), + MTK_FUNCTION(7, "TESTB_OUT4") + ), + MTK_PIN( + PINCTRL_PIN(88, "MSDC1_DAT3"), + "AB20", "mt8135", + MTK_EINT_FUNCTION(2, 56), + MTK_FUNCTION(0, "GPIO88"), + MTK_FUNCTION(1, "MSDC1_DAT3"), + MTK_FUNCTION(2, "EINT56"), + MTK_FUNCTION(3, "SDA4"), + MTK_FUNCTION(4, "PWM5"), + MTK_FUNCTION(5, "CLKM4"), + MTK_FUNCTION(7, "TESTB_OUT5") + ), + MTK_PIN( + PINCTRL_PIN(89, "MSDC4_DAT0"), + "AB8", "mt8135", + MTK_EINT_FUNCTION(2, 133), + MTK_FUNCTION(0, "GPIO89"), + MTK_FUNCTION(1, "MSDC4_DAT0"), + MTK_FUNCTION(2, "EINT133"), + MTK_FUNCTION(4, "EXT_FRAME_SYNC"), + MTK_FUNCTION(5, "USB_DRVVBUS"), + MTK_FUNCTION(6, "A_FUNC_DIN[9]"), + MTK_FUNCTION(7, "LPTE") + ), + MTK_PIN( + PINCTRL_PIN(90, "MSDC4_DAT1"), + "AB7", "mt8135", + MTK_EINT_FUNCTION(2, 134), + MTK_FUNCTION(0, "GPIO90"), + MTK_FUNCTION(1, "MSDC4_DAT1"), + MTK_FUNCTION(2, "EINT134"), + MTK_FUNCTION(6, "A_FUNC_DIN[10]"), + MTK_FUNCTION(7, "LRSTB_1X") + ), + MTK_PIN( + PINCTRL_PIN(91, "MSDC4_DAT5"), + "AA8", "mt8135", + MTK_EINT_FUNCTION(2, 136), + MTK_FUNCTION(0, "GPIO91"), + MTK_FUNCTION(1, "MSDC4_DAT5"), + MTK_FUNCTION(2, "EINT136"), + MTK_FUNCTION(3, "I2SIN_WS"), + MTK_FUNCTION(4, "DAC_WS"), + MTK_FUNCTION(5, "PCM1_WS"), + MTK_FUNCTION(6, "A_FUNC_DIN[11]"), + MTK_FUNCTION(7, "SPI1_CSN") + ), + MTK_PIN( + PINCTRL_PIN(92, "MSDC4_DAT6"), + "AC4", "mt8135", + MTK_EINT_FUNCTION(2, 137), + MTK_FUNCTION(0, "GPIO92"), + MTK_FUNCTION(1, "MSDC4_DAT6"), + MTK_FUNCTION(2, "EINT137"), + MTK_FUNCTION(3, "I2SOUT_DAT"), + MTK_FUNCTION(4, "DAC_DAT_OUT"), + MTK_FUNCTION(5, "PCM1_DO"), + MTK_FUNCTION(6, "A_FUNC_DIN[12]"), + MTK_FUNCTION(7, "SPI1_MO") + ), + MTK_PIN( + PINCTRL_PIN(93, "MSDC4_DAT7"), + "AC6", "mt8135", + MTK_EINT_FUNCTION(2, 138), + MTK_FUNCTION(0, "GPIO93"), + MTK_FUNCTION(1, "MSDC4_DAT7"), + MTK_FUNCTION(2, "EINT138"), + MTK_FUNCTION(3, "I2SIN_DAT"), + MTK_FUNCTION(5, "PCM1_DI"), + MTK_FUNCTION(6, "A_FUNC_DIN[13]"), + MTK_FUNCTION(7, "SPI1_MI") + ), + MTK_PIN( + PINCTRL_PIN(94, "MSDC4_DAT4"), + "AA7", "mt8135", + MTK_EINT_FUNCTION(2, 135), + MTK_FUNCTION(0, "GPIO94"), + MTK_FUNCTION(1, "MSDC4_DAT4"), + MTK_FUNCTION(2, "EINT135"), + MTK_FUNCTION(3, "I2SIN_CK"), + MTK_FUNCTION(4, "DAC_CK"), + MTK_FUNCTION(5, "PCM1_CK"), + MTK_FUNCTION(6, "A_FUNC_DIN[14]"), + MTK_FUNCTION(7, "SPI1_CLK") + ), + MTK_PIN( + PINCTRL_PIN(95, "MSDC4_DAT2"), + "AB6", "mt8135", + MTK_EINT_FUNCTION(2, 131), + MTK_FUNCTION(0, "GPIO95"), + MTK_FUNCTION(1, "MSDC4_DAT2"), + MTK_FUNCTION(2, "EINT131"), + MTK_FUNCTION(3, "I2SIN_WS"), + MTK_FUNCTION(4, "CM2PDN_2X"), + MTK_FUNCTION(5, "DAC_WS"), + MTK_FUNCTION(6, "PCM1_WS"), + MTK_FUNCTION(7, "LSCE0B_1X") + ), + MTK_PIN( + PINCTRL_PIN(96, "MSDC4_CLK"), + "AB5", "mt8135", + MTK_EINT_FUNCTION(2, 129), + MTK_FUNCTION(0, "GPIO96"), + MTK_FUNCTION(1, "MSDC4_CLK"), + MTK_FUNCTION(2, "EINT129"), + MTK_FUNCTION(3, "DPI1_CK_2X"), + MTK_FUNCTION(4, "CM2PCLK_2X"), + MTK_FUNCTION(5, "PWM4"), + MTK_FUNCTION(6, "PCM1_DI"), + MTK_FUNCTION(7, "LSCK_1X") + ), + MTK_PIN( + PINCTRL_PIN(97, "MSDC4_DAT3"), + "Y8", "mt8135", + MTK_EINT_FUNCTION(2, 132), + MTK_FUNCTION(0, "GPIO97"), + MTK_FUNCTION(1, "MSDC4_DAT3"), + MTK_FUNCTION(2, "EINT132"), + MTK_FUNCTION(3, "I2SOUT_DAT"), + MTK_FUNCTION(4, "CM2RST_2X"), + MTK_FUNCTION(5, "DAC_DAT_OUT"), + MTK_FUNCTION(6, "PCM1_DO"), + MTK_FUNCTION(7, "LSCE1B_1X") + ), + MTK_PIN( + PINCTRL_PIN(98, "MSDC4_CMD"), + "AC3", "mt8135", + MTK_EINT_FUNCTION(2, 128), + MTK_FUNCTION(0, "GPIO98"), + MTK_FUNCTION(1, "MSDC4_CMD"), + MTK_FUNCTION(2, "EINT128"), + MTK_FUNCTION(3, "DPI1_DE_2X"), + MTK_FUNCTION(5, "PWM3"), + MTK_FUNCTION(7, "LSDA_1X") + ), + MTK_PIN( + PINCTRL_PIN(99, "MSDC4_RSTB"), + "AB4", "mt8135", + MTK_EINT_FUNCTION(2, 130), + MTK_FUNCTION(0, "GPIO99"), + MTK_FUNCTION(1, "MSDC4_RSTB"), + MTK_FUNCTION(2, "EINT130"), + MTK_FUNCTION(3, "I2SIN_CK"), + MTK_FUNCTION(4, "CM2MCLK_2X"), + MTK_FUNCTION(5, "DAC_CK"), + MTK_FUNCTION(6, "PCM1_CK"), + MTK_FUNCTION(7, "LSA0_1X") + ), + MTK_PIN( + PINCTRL_PIN(100, "SDA0"), + "W9", "mt8135", + MTK_EINT_FUNCTION(2, 91), + MTK_FUNCTION(0, "GPIO100"), + MTK_FUNCTION(1, "SDA0"), + MTK_FUNCTION(2, "EINT91"), + MTK_FUNCTION(3, "CLKM1"), + MTK_FUNCTION(4, "PWM1"), + MTK_FUNCTION(7, "A_FUNC_DIN[15]") + ), + MTK_PIN( + PINCTRL_PIN(101, "SCL0"), + "W11", "mt8135", + MTK_EINT_FUNCTION(2, 90), + MTK_FUNCTION(0, "GPIO101"), + MTK_FUNCTION(1, "SCL0"), + MTK_FUNCTION(2, "EINT90"), + MTK_FUNCTION(3, "CLKM0"), + MTK_FUNCTION(4, "DISP_PWM"), + MTK_FUNCTION(7, "A_FUNC_DIN[16]") + ), + MTK_PIN( + PINCTRL_PIN(102, "EINT10_AUXIN2"), + "AA3", "mt8135", + MTK_EINT_FUNCTION(1, 10), + MTK_FUNCTION(0, "GPIO102"), + MTK_FUNCTION(1, "EINT10"), + MTK_FUNCTION(5, "USB_TEST_IO[16]"), + MTK_FUNCTION(6, "TESTB_OUT16"), + MTK_FUNCTION(7, "A_FUNC_DIN[17]") + ), + MTK_PIN( + PINCTRL_PIN(103, "EINT11_AUXIN3"), + "AB2", "mt8135", + MTK_EINT_FUNCTION(1, 11), + MTK_FUNCTION(0, "GPIO103"), + MTK_FUNCTION(1, "EINT11"), + MTK_FUNCTION(5, "USB_TEST_IO[17]"), + MTK_FUNCTION(6, "TESTB_OUT17"), + MTK_FUNCTION(7, "A_FUNC_DIN[18]") + ), + MTK_PIN( + PINCTRL_PIN(104, "EINT16_AUXIN4"), + "AB3", "mt8135", + MTK_EINT_FUNCTION(1, 16), + MTK_FUNCTION(0, "GPIO104"), + MTK_FUNCTION(1, "EINT16"), + MTK_FUNCTION(5, "USB_TEST_IO[18]"), + MTK_FUNCTION(6, "TESTB_OUT18"), + MTK_FUNCTION(7, "A_FUNC_DIN[19]") + ), + MTK_PIN( + PINCTRL_PIN(105, "I2S_CLK"), + "W6", "mt8135", + MTK_EINT_FUNCTION(2, 10), + MTK_FUNCTION(0, "GPIO105"), + MTK_FUNCTION(1, "I2SIN_CK"), + MTK_FUNCTION(2, "EINT10"), + MTK_FUNCTION(3, "DAC_CK"), + MTK_FUNCTION(4, "PCM1_CK"), + MTK_FUNCTION(5, "USB_TEST_IO[19]"), + MTK_FUNCTION(6, "TESTB_OUT19"), + MTK_FUNCTION(7, "A_FUNC_DIN[20]") + ), + MTK_PIN( + PINCTRL_PIN(106, "I2S_WS"), + "AA6", "mt8135", + MTK_EINT_FUNCTION(NO_EINT_SUPPORT, NO_EINT_SUPPORT), + MTK_FUNCTION(0, "GPIO106"), + MTK_FUNCTION(1, "I2SIN_WS"), + MTK_FUNCTION(3, "DAC_WS"), + MTK_FUNCTION(4, "PCM1_WS"), + MTK_FUNCTION(5, "USB_TEST_IO[20]"), + MTK_FUNCTION(6, "TESTB_OUT20"), + MTK_FUNCTION(7, "A_FUNC_DIN[21]") + ), + MTK_PIN( + PINCTRL_PIN(107, "I2S_DATA_IN"), + "AA5", "mt8135", + MTK_EINT_FUNCTION(2, 11), + MTK_FUNCTION(0, "GPIO107"), + MTK_FUNCTION(1, "I2SIN_DAT"), + MTK_FUNCTION(2, "EINT11"), + MTK_FUNCTION(4, "PCM1_DI"), + MTK_FUNCTION(5, "USB_TEST_IO[21]"), + MTK_FUNCTION(6, "TESTB_OUT22"), + MTK_FUNCTION(7, "A_FUNC_DIN[22]") + ), + MTK_PIN( + PINCTRL_PIN(108, "I2S_DATA_OUT"), + "AA4", "mt8135", + MTK_EINT_FUNCTION(NO_EINT_SUPPORT, NO_EINT_SUPPORT), + MTK_FUNCTION(0, "GPIO108"), + MTK_FUNCTION(1, "I2SOUT_DAT"), + MTK_FUNCTION(3, "DAC_DAT_OUT"), + MTK_FUNCTION(4, "PCM1_DO"), + MTK_FUNCTION(5, "USB_TEST_IO[22]"), + MTK_FUNCTION(6, "TESTB_OUT23"), + MTK_FUNCTION(7, "A_FUNC_DIN[23]") + ), + MTK_PIN( + PINCTRL_PIN(109, "EINT5"), + "W5", "mt8135", + MTK_EINT_FUNCTION(1, 5), + MTK_FUNCTION(0, "GPIO109"), + MTK_FUNCTION(1, "EINT5"), + MTK_FUNCTION(2, "PWM5"), + MTK_FUNCTION(3, "CLKM3"), + MTK_FUNCTION(4, "GPU_JTRSTB"), + MTK_FUNCTION(5, "USB_TEST_IO[23]"), + MTK_FUNCTION(6, "TESTB_OUT26"), + MTK_FUNCTION(7, "A_FUNC_DIN[24]") + ), + MTK_PIN( + PINCTRL_PIN(110, "EINT6"), + "V5", "mt8135", + MTK_EINT_FUNCTION(1, 6), + MTK_FUNCTION(0, "GPIO110"), + MTK_FUNCTION(1, "EINT6"), + MTK_FUNCTION(2, "PWM6"), + MTK_FUNCTION(3, "CLKM4"), + MTK_FUNCTION(4, "GPU_JTMS"), + MTK_FUNCTION(5, "USB_TEST_IO[24]"), + MTK_FUNCTION(6, "TESTB_OUT27"), + MTK_FUNCTION(7, "A_FUNC_DIN[25]") + ), + MTK_PIN( + PINCTRL_PIN(111, "EINT7"), + "W3", "mt8135", + MTK_EINT_FUNCTION(1, 7), + MTK_FUNCTION(0, "GPIO111"), + MTK_FUNCTION(1, "EINT7"), + MTK_FUNCTION(2, "PWM7"), + MTK_FUNCTION(3, "CLKM5"), + MTK_FUNCTION(4, "GPU_JTDO"), + MTK_FUNCTION(5, "USB_TEST_IO[25]"), + MTK_FUNCTION(6, "TESTB_OUT28"), + MTK_FUNCTION(7, "A_FUNC_DIN[26]") + ), + MTK_PIN( + PINCTRL_PIN(112, "EINT8"), + "V6", "mt8135", + MTK_EINT_FUNCTION(1, 8), + MTK_FUNCTION(0, "GPIO112"), + MTK_FUNCTION(1, "EINT8"), + MTK_FUNCTION(2, "DISP_PWM"), + MTK_FUNCTION(3, "CLKM6"), + MTK_FUNCTION(4, "GPU_JTDI"), + MTK_FUNCTION(5, "USB_TEST_IO[26]"), + MTK_FUNCTION(6, "TESTB_OUT29"), + MTK_FUNCTION(7, "EXT_FRAME_SYNC") + ), + MTK_PIN( + PINCTRL_PIN(113, "EINT9"), + "W8", "mt8135", + MTK_EINT_FUNCTION(1, 9), + MTK_FUNCTION(0, "GPIO113"), + MTK_FUNCTION(1, "EINT9"), + MTK_FUNCTION(4, "GPU_JTCK"), + MTK_FUNCTION(5, "USB_DRVVBUS"), + MTK_FUNCTION(6, "TESTB_OUT30"), + MTK_FUNCTION(7, "A_FUNC_DIN[27]") + ), + MTK_PIN( + PINCTRL_PIN(114, "LPCE1B"), + "W4", "mt8135", + MTK_EINT_FUNCTION(2, 127), + MTK_FUNCTION(0, "GPIO114"), + MTK_FUNCTION(1, "LPCE1B"), + MTK_FUNCTION(2, "EINT127"), + MTK_FUNCTION(5, "PWM2"), + MTK_FUNCTION(6, "TESTB_OUT14"), + MTK_FUNCTION(7, "A_FUNC_DIN[28]") + ), + MTK_PIN( + PINCTRL_PIN(115, "LPCE0B"), + "T5", "mt8135", + MTK_EINT_FUNCTION(2, 126), + MTK_FUNCTION(0, "GPIO115"), + MTK_FUNCTION(1, "LPCE0B"), + MTK_FUNCTION(2, "EINT126"), + MTK_FUNCTION(5, "PWM1"), + MTK_FUNCTION(6, "TESTB_OUT15"), + MTK_FUNCTION(7, "A_FUNC_DIN[29]") + ), + MTK_PIN( + PINCTRL_PIN(116, "DISP_PWM"), + "V4", "mt8135", + MTK_EINT_FUNCTION(2, 77), + MTK_FUNCTION(0, "GPIO116"), + MTK_FUNCTION(1, "DISP_PWM"), + MTK_FUNCTION(2, "EINT77"), + MTK_FUNCTION(3, "LSDI"), + MTK_FUNCTION(4, "PWM1"), + MTK_FUNCTION(5, "PWM2"), + MTK_FUNCTION(7, "PWM3") + ), + MTK_PIN( + PINCTRL_PIN(117, "EINT1"), + "T6", "mt8135", + MTK_EINT_FUNCTION(1, 1), + MTK_FUNCTION(0, "GPIO117"), + MTK_FUNCTION(1, "EINT1"), + MTK_FUNCTION(2, "PWM2"), + MTK_FUNCTION(3, "CLKM1"), + MTK_FUNCTION(5, "USB_TEST_IO[13]"), + MTK_FUNCTION(7, "USB_SDA") + ), + MTK_PIN( + PINCTRL_PIN(118, "EINT2"), + "T4", "mt8135", + MTK_EINT_FUNCTION(1, 2), + MTK_FUNCTION(0, "GPIO118"), + MTK_FUNCTION(1, "EINT2"), + MTK_FUNCTION(2, "PWM3"), + MTK_FUNCTION(3, "CLKM2"), + MTK_FUNCTION(5, "USB_TEST_IO[14]"), + MTK_FUNCTION(6, "SRCLKENAI2"), + MTK_FUNCTION(7, "A_FUNC_DIN[30]") + ), + MTK_PIN( + PINCTRL_PIN(119, "EINT3"), + "R4", "mt8135", + MTK_EINT_FUNCTION(1, 3), + MTK_FUNCTION(0, "GPIO119"), + MTK_FUNCTION(1, "EINT3"), + MTK_FUNCTION(5, "USB_TEST_IO[15]"), + MTK_FUNCTION(6, "SRCLKENAI1"), + MTK_FUNCTION(7, "EXT_26M_CK") + ), + MTK_PIN( + PINCTRL_PIN(120, "EINT4"), + "R5", "mt8135", + MTK_EINT_FUNCTION(1, 4), + MTK_FUNCTION(0, "GPIO120"), + MTK_FUNCTION(1, "EINT4"), + MTK_FUNCTION(2, "PWM4"), + MTK_FUNCTION(5, "USB_DRVVBUS"), + MTK_FUNCTION(7, "A_FUNC_DIN[31]") + ), + MTK_PIN( + PINCTRL_PIN(121, "DPIDE"), + NULL, "mt8135", + MTK_EINT_FUNCTION(2, 100), + MTK_FUNCTION(0, "GPIO121"), + MTK_FUNCTION(1, "DPI0_DE"), + MTK_FUNCTION(2, "EINT100"), + MTK_FUNCTION(3, "I2SOUT_DAT"), + MTK_FUNCTION(4, "DAC_DAT_OUT"), + MTK_FUNCTION(5, "PCM1_DO"), + MTK_FUNCTION(6, "IRDA_TXD") + ), + MTK_PIN( + PINCTRL_PIN(122, "DPICK"), + NULL, "mt8135", + MTK_EINT_FUNCTION(2, 101), + MTK_FUNCTION(0, "GPIO122"), + MTK_FUNCTION(1, "DPI0_CK"), + MTK_FUNCTION(2, "EINT101"), + MTK_FUNCTION(3, "I2SIN_DAT"), + MTK_FUNCTION(5, "PCM1_DI"), + MTK_FUNCTION(6, "IRDA_PDN") + ), + MTK_PIN( + PINCTRL_PIN(123, "DPIG4"), + NULL, "mt8135", + MTK_EINT_FUNCTION(2, 114), + MTK_FUNCTION(0, "GPIO123"), + MTK_FUNCTION(1, "DPI0_G4"), + MTK_FUNCTION(2, "EINT114"), + MTK_FUNCTION(4, "CM2DAT_2X[0]"), + MTK_FUNCTION(5, "DSP2_ID") + ), + MTK_PIN( + PINCTRL_PIN(124, "DPIG5"), + NULL, "mt8135", + MTK_EINT_FUNCTION(2, 115), + MTK_FUNCTION(0, "GPIO124"), + MTK_FUNCTION(1, "DPI0_G5"), + MTK_FUNCTION(2, "EINT115"), + MTK_FUNCTION(4, "CM2DAT_2X[1]"), + MTK_FUNCTION(5, "DSP2_ICK") + ), + MTK_PIN( + PINCTRL_PIN(125, "DPIR3"), + NULL, "mt8135", + MTK_EINT_FUNCTION(2, 121), + MTK_FUNCTION(0, "GPIO125"), + MTK_FUNCTION(1, "DPI0_R3"), + MTK_FUNCTION(2, "EINT121"), + MTK_FUNCTION(4, "CM2DAT_2X[7]") + ), + MTK_PIN( + PINCTRL_PIN(126, "DPIG1"), + NULL, "mt8135", + MTK_EINT_FUNCTION(2, 111), + MTK_FUNCTION(0, "GPIO126"), + MTK_FUNCTION(1, "DPI0_G1"), + MTK_FUNCTION(2, "EINT111"), + MTK_FUNCTION(5, "DSP1_ICK") + ), + MTK_PIN( + PINCTRL_PIN(127, "DPIVSYNC"), + NULL, "mt8135", + MTK_EINT_FUNCTION(2, 98), + MTK_FUNCTION(0, "GPIO127"), + MTK_FUNCTION(1, "DPI0_VSYNC"), + MTK_FUNCTION(2, "EINT98"), + MTK_FUNCTION(3, "I2SIN_CK"), + MTK_FUNCTION(4, "DAC_CK"), + MTK_FUNCTION(5, "PCM1_CK") + ), + MTK_PIN( + PINCTRL_PIN(128, "DPIHSYNC"), + NULL, "mt8135", + MTK_EINT_FUNCTION(2, 99), + MTK_FUNCTION(0, "GPIO128"), + MTK_FUNCTION(1, "DPI0_HSYNC"), + MTK_FUNCTION(2, "EINT99"), + MTK_FUNCTION(3, "I2SIN_WS"), + MTK_FUNCTION(4, "DAC_WS"), + MTK_FUNCTION(5, "PCM1_WS"), + MTK_FUNCTION(6, "IRDA_RXD") + ), + MTK_PIN( + PINCTRL_PIN(129, "DPIB0"), + NULL, "mt8135", + MTK_EINT_FUNCTION(2, 102), + MTK_FUNCTION(0, "GPIO129"), + MTK_FUNCTION(1, "DPI0_B0"), + MTK_FUNCTION(2, "EINT102"), + MTK_FUNCTION(4, "SCL0"), + MTK_FUNCTION(5, "DISP_PWM") + ), + MTK_PIN( + PINCTRL_PIN(130, "DPIB1"), + NULL, "mt8135", + MTK_EINT_FUNCTION(2, 103), + MTK_FUNCTION(0, "GPIO130"), + MTK_FUNCTION(1, "DPI0_B1"), + MTK_FUNCTION(2, "EINT103"), + MTK_FUNCTION(3, "CLKM0"), + MTK_FUNCTION(4, "SDA0"), + MTK_FUNCTION(5, "PWM1") + ), + MTK_PIN( + PINCTRL_PIN(131, "DPIB2"), + NULL, "mt8135", + MTK_EINT_FUNCTION(2, 104), + MTK_FUNCTION(0, "GPIO131"), + MTK_FUNCTION(1, "DPI0_B2"), + MTK_FUNCTION(2, "EINT104"), + MTK_FUNCTION(3, "CLKM1"), + MTK_FUNCTION(4, "SCL1"), + MTK_FUNCTION(5, "PWM2") + ), + MTK_PIN( + PINCTRL_PIN(132, "DPIB3"), + NULL, "mt8135", + MTK_EINT_FUNCTION(2, 105), + MTK_FUNCTION(0, "GPIO132"), + MTK_FUNCTION(1, "DPI0_B3"), + MTK_FUNCTION(2, "EINT105"), + MTK_FUNCTION(3, "CLKM2"), + MTK_FUNCTION(4, "SDA1"), + MTK_FUNCTION(5, "PWM3") + ), + MTK_PIN( + PINCTRL_PIN(133, "DPIB4"), + NULL, "mt8135", + MTK_EINT_FUNCTION(2, 106), + MTK_FUNCTION(0, "GPIO133"), + MTK_FUNCTION(1, "DPI0_B4"), + MTK_FUNCTION(2, "EINT106"), + MTK_FUNCTION(3, "CLKM3"), + MTK_FUNCTION(4, "SCL2"), + MTK_FUNCTION(5, "PWM4") + ), + MTK_PIN( + PINCTRL_PIN(134, "DPIB5"), + NULL, "mt8135", + MTK_EINT_FUNCTION(2, 107), + MTK_FUNCTION(0, "GPIO134"), + MTK_FUNCTION(1, "DPI0_B5"), + MTK_FUNCTION(2, "EINT107"), + MTK_FUNCTION(3, "CLKM4"), + MTK_FUNCTION(4, "SDA2"), + MTK_FUNCTION(5, "PWM5") + ), + MTK_PIN( + PINCTRL_PIN(135, "DPIB6"), + NULL, "mt8135", + MTK_EINT_FUNCTION(2, 108), + MTK_FUNCTION(0, "GPIO135"), + MTK_FUNCTION(1, "DPI0_B6"), + MTK_FUNCTION(2, "EINT108"), + MTK_FUNCTION(3, "CLKM5"), + MTK_FUNCTION(4, "SCL3"), + MTK_FUNCTION(5, "PWM6") + ), + MTK_PIN( + PINCTRL_PIN(136, "DPIB7"), + NULL, "mt8135", + MTK_EINT_FUNCTION(2, 109), + MTK_FUNCTION(0, "GPIO136"), + MTK_FUNCTION(1, "DPI0_B7"), + MTK_FUNCTION(2, "EINT109"), + MTK_FUNCTION(3, "CLKM6"), + MTK_FUNCTION(4, "SDA3"), + MTK_FUNCTION(5, "PWM7") + ), + MTK_PIN( + PINCTRL_PIN(137, "DPIG0"), + NULL, "mt8135", + MTK_EINT_FUNCTION(2, 110), + MTK_FUNCTION(0, "GPIO137"), + MTK_FUNCTION(1, "DPI0_G0"), + MTK_FUNCTION(2, "EINT110"), + MTK_FUNCTION(5, "DSP1_ID") + ), + MTK_PIN( + PINCTRL_PIN(138, "DPIG2"), + NULL, "mt8135", + MTK_EINT_FUNCTION(2, 112), + MTK_FUNCTION(0, "GPIO138"), + MTK_FUNCTION(1, "DPI0_G2"), + MTK_FUNCTION(2, "EINT112"), + MTK_FUNCTION(5, "DSP1_IMS") + ), + MTK_PIN( + PINCTRL_PIN(139, "DPIG3"), + NULL, "mt8135", + MTK_EINT_FUNCTION(2, 113), + MTK_FUNCTION(0, "GPIO139"), + MTK_FUNCTION(1, "DPI0_G3"), + MTK_FUNCTION(2, "EINT113"), + MTK_FUNCTION(5, "DSP2_IMS") + ), + MTK_PIN( + PINCTRL_PIN(140, "DPIG6"), + NULL, "mt8135", + MTK_EINT_FUNCTION(2, 116), + MTK_FUNCTION(0, "GPIO140"), + MTK_FUNCTION(1, "DPI0_G6"), + MTK_FUNCTION(2, "EINT116"), + MTK_FUNCTION(4, "CM2DAT_2X[2]") + ), + MTK_PIN( + PINCTRL_PIN(141, "DPIG7"), + NULL, "mt8135", + MTK_EINT_FUNCTION(2, 117), + MTK_FUNCTION(0, "GPIO141"), + MTK_FUNCTION(1, "DPI0_G7"), + MTK_FUNCTION(2, "EINT117"), + MTK_FUNCTION(4, "CM2DAT_2X[3]") + ), + MTK_PIN( + PINCTRL_PIN(142, "DPIR0"), + NULL, "mt8135", + MTK_EINT_FUNCTION(2, 118), + MTK_FUNCTION(0, "GPIO142"), + MTK_FUNCTION(1, "DPI0_R0"), + MTK_FUNCTION(2, "EINT118"), + MTK_FUNCTION(4, "CM2DAT_2X[4]") + ), + MTK_PIN( + PINCTRL_PIN(143, "DPIR1"), + NULL, "mt8135", + MTK_EINT_FUNCTION(2, 119), + MTK_FUNCTION(0, "GPIO143"), + MTK_FUNCTION(1, "DPI0_R1"), + MTK_FUNCTION(2, "EINT119"), + MTK_FUNCTION(4, "CM2DAT_2X[5]") + ), + MTK_PIN( + PINCTRL_PIN(144, "DPIR2"), + NULL, "mt8135", + MTK_EINT_FUNCTION(2, 120), + MTK_FUNCTION(0, "GPIO144"), + MTK_FUNCTION(1, "DPI0_R2"), + MTK_FUNCTION(2, "EINT120"), + MTK_FUNCTION(4, "CM2DAT_2X[6]") + ), + MTK_PIN( + PINCTRL_PIN(145, "DPIR4"), + NULL, "mt8135", + MTK_EINT_FUNCTION(2, 122), + MTK_FUNCTION(0, "GPIO145"), + MTK_FUNCTION(1, "DPI0_R4"), + MTK_FUNCTION(2, "EINT122"), + MTK_FUNCTION(4, "CM2DAT_2X[8]") + ), + MTK_PIN( + PINCTRL_PIN(146, "DPIR5"), + NULL, "mt8135", + MTK_EINT_FUNCTION(2, 123), + MTK_FUNCTION(0, "GPIO146"), + MTK_FUNCTION(1, "DPI0_R5"), + MTK_FUNCTION(2, "EINT123"), + MTK_FUNCTION(4, "CM2DAT_2X[9]") + ), + MTK_PIN( + PINCTRL_PIN(147, "DPIR6"), + NULL, "mt8135", + MTK_EINT_FUNCTION(2, 124), + MTK_FUNCTION(0, "GPIO147"), + MTK_FUNCTION(1, "DPI0_R6"), + MTK_FUNCTION(2, "EINT124"), + MTK_FUNCTION(4, "CM2VSYNC_2X") + ), + MTK_PIN( + PINCTRL_PIN(148, "DPIR7"), + NULL, "mt8135", + MTK_EINT_FUNCTION(2, 125), + MTK_FUNCTION(0, "GPIO148"), + MTK_FUNCTION(1, "DPI0_R7"), + MTK_FUNCTION(2, "EINT125"), + MTK_FUNCTION(4, "CM2HSYNC_2X") + ), + MTK_PIN( + PINCTRL_PIN(149, "TDN3/LVDS(TDN3)"), + "AA2", "mt8135", + MTK_EINT_FUNCTION(2, 36), + MTK_FUNCTION(0, "GPIO149"), + MTK_FUNCTION(2, "EINT36") + ), + MTK_PIN( + PINCTRL_PIN(150, "TDP3/LVDS(TDP3)"), + "AA1", "mt8135", + MTK_EINT_FUNCTION(2, 35), + MTK_FUNCTION(0, "GPIO150"), + MTK_FUNCTION(2, "EINT35") + ), + MTK_PIN( + PINCTRL_PIN(151, "TDN2/LVDS(TCN)"), + "Y2", "mt8135", + MTK_EINT_FUNCTION(2, 169), + MTK_FUNCTION(0, "GPIO151"), + MTK_FUNCTION(2, "EINT169") + ), + MTK_PIN( + PINCTRL_PIN(152, "TDP2/LVDS(TCP)"), + "Y1", "mt8135", + MTK_EINT_FUNCTION(2, 168), + MTK_FUNCTION(0, "GPIO152"), + MTK_FUNCTION(2, "EINT168") + ), + MTK_PIN( + PINCTRL_PIN(153, "TCN/LVDS(TDN2)"), + "W2", "mt8135", + MTK_EINT_FUNCTION(2, 163), + MTK_FUNCTION(0, "GPIO153"), + MTK_FUNCTION(2, "EINT163") + ), + MTK_PIN( + PINCTRL_PIN(154, "TCP/LVDS(TDP2)"), + "W1", "mt8135", + MTK_EINT_FUNCTION(2, 162), + MTK_FUNCTION(0, "GPIO154"), + MTK_FUNCTION(2, "EINT162") + ), + MTK_PIN( + PINCTRL_PIN(155, "TDN1/LVDS(TDN1)"), + "V3", "mt8135", + MTK_EINT_FUNCTION(2, 167), + MTK_FUNCTION(0, "GPIO155"), + MTK_FUNCTION(2, "EINT167") + ), + MTK_PIN( + PINCTRL_PIN(156, "TDP1/LVDS(TDP1)"), + "V2", "mt8135", + MTK_EINT_FUNCTION(2, 166), + MTK_FUNCTION(0, "GPIO156"), + MTK_FUNCTION(2, "EINT166") + ), + MTK_PIN( + PINCTRL_PIN(157, "TDN0/LVDS(TDN0)"), + "U3", "mt8135", + MTK_EINT_FUNCTION(2, 165), + MTK_FUNCTION(0, "GPIO157"), + MTK_FUNCTION(2, "EINT165") + ), + MTK_PIN( + PINCTRL_PIN(158, "TDP0/LVDS(TDP0)"), + "U2", "mt8135", + MTK_EINT_FUNCTION(2, 164), + MTK_FUNCTION(0, "GPIO158"), + MTK_FUNCTION(2, "EINT164") + ), + MTK_PIN( + PINCTRL_PIN(159, "RDN3"), + "N5", "mt8135", + MTK_EINT_FUNCTION(2, 18), + MTK_FUNCTION(0, "GPIO159"), + MTK_FUNCTION(2, "EINT18") + ), + MTK_PIN( + PINCTRL_PIN(160, "RDP3"), + "N4", "mt8135", + MTK_EINT_FUNCTION(2, 30), + MTK_FUNCTION(0, "GPIO160"), + MTK_FUNCTION(2, "EINT30") + ), + MTK_PIN( + PINCTRL_PIN(161, "RDN2"), + "T2", "mt8135", + MTK_EINT_FUNCTION(2, 31), + MTK_FUNCTION(0, "GPIO161"), + MTK_FUNCTION(2, "EINT31") + ), + MTK_PIN( + PINCTRL_PIN(162, "RDP2"), + "T3", "mt8135", + MTK_EINT_FUNCTION(2, 32), + MTK_FUNCTION(0, "GPIO162"), + MTK_FUNCTION(2, "EINT32") + ), + MTK_PIN( + PINCTRL_PIN(163, "RCN"), + "P2", "mt8135", + MTK_EINT_FUNCTION(2, 33), + MTK_FUNCTION(0, "GPIO163"), + MTK_FUNCTION(2, "EINT33") + ), + MTK_PIN( + PINCTRL_PIN(164, "RCP"), + "P3", "mt8135", + MTK_EINT_FUNCTION(2, 39), + MTK_FUNCTION(0, "GPIO164"), + MTK_FUNCTION(2, "EINT39") + ), + MTK_PIN( + PINCTRL_PIN(165, "RDN1"), + "R3", "mt8135", + MTK_EINT_FUNCTION(NO_EINT_SUPPORT, NO_EINT_SUPPORT), + MTK_FUNCTION(0, "GPIO165") + ), + MTK_PIN( + PINCTRL_PIN(166, "RDP1"), + "R2", "mt8135", + MTK_EINT_FUNCTION(NO_EINT_SUPPORT, NO_EINT_SUPPORT), + MTK_FUNCTION(0, "GPIO166") + ), + MTK_PIN( + PINCTRL_PIN(167, "RDN0"), + "N3", "mt8135", + MTK_EINT_FUNCTION(NO_EINT_SUPPORT, NO_EINT_SUPPORT), + MTK_FUNCTION(0, "GPIO167") + ), + MTK_PIN( + PINCTRL_PIN(168, "RDP0"), + "N2", "mt8135", + MTK_EINT_FUNCTION(NO_EINT_SUPPORT, NO_EINT_SUPPORT), + MTK_FUNCTION(0, "GPIO168") + ), + MTK_PIN( + PINCTRL_PIN(169, "RDN1_A"), + "M4", "mt8135", + MTK_EINT_FUNCTION(2, 175), + MTK_FUNCTION(0, "GPIO169"), + MTK_FUNCTION(1, "CMDAT6"), + MTK_FUNCTION(2, "EINT175") + ), + MTK_PIN( + PINCTRL_PIN(170, "RDP1_A"), + "M3", "mt8135", + MTK_EINT_FUNCTION(2, 174), + MTK_FUNCTION(0, "GPIO170"), + MTK_FUNCTION(1, "CMDAT7"), + MTK_FUNCTION(2, "EINT174") + ), + MTK_PIN( + PINCTRL_PIN(171, "RCN_A"), + "L3", "mt8135", + MTK_EINT_FUNCTION(2, 171), + MTK_FUNCTION(0, "GPIO171"), + MTK_FUNCTION(1, "CMDAT8"), + MTK_FUNCTION(2, "EINT171") + ), + MTK_PIN( + PINCTRL_PIN(172, "RCP_A"), + "L2", "mt8135", + MTK_EINT_FUNCTION(2, 170), + MTK_FUNCTION(0, "GPIO172"), + MTK_FUNCTION(1, "CMDAT9"), + MTK_FUNCTION(2, "EINT170") + ), + MTK_PIN( + PINCTRL_PIN(173, "RDN0_A"), + "M2", "mt8135", + MTK_EINT_FUNCTION(2, 173), + MTK_FUNCTION(0, "GPIO173"), + MTK_FUNCTION(1, "CMHSYNC"), + MTK_FUNCTION(2, "EINT173") + ), + MTK_PIN( + PINCTRL_PIN(174, "RDP0_A"), + "M1", "mt8135", + MTK_EINT_FUNCTION(2, 172), + MTK_FUNCTION(0, "GPIO174"), + MTK_FUNCTION(1, "CMVSYNC"), + MTK_FUNCTION(2, "EINT172") + ), + MTK_PIN( + PINCTRL_PIN(175, "RDN1_B"), + "H2", "mt8135", + MTK_EINT_FUNCTION(2, 181), + MTK_FUNCTION(0, "GPIO175"), + MTK_FUNCTION(1, "CMDAT2"), + MTK_FUNCTION(2, "EINT181"), + MTK_FUNCTION(3, "CMCSD2") + ), + MTK_PIN( + PINCTRL_PIN(176, "RDP1_B"), + "H1", "mt8135", + MTK_EINT_FUNCTION(2, 180), + MTK_FUNCTION(0, "GPIO176"), + MTK_FUNCTION(1, "CMDAT3"), + MTK_FUNCTION(2, "EINT180"), + MTK_FUNCTION(3, "CMCSD3") + ), + MTK_PIN( + PINCTRL_PIN(177, "RCN_B"), + "K3", "mt8135", + MTK_EINT_FUNCTION(2, 177), + MTK_FUNCTION(0, "GPIO177"), + MTK_FUNCTION(1, "CMDAT4"), + MTK_FUNCTION(2, "EINT177") + ), + MTK_PIN( + PINCTRL_PIN(178, "RCP_B"), + "K2", "mt8135", + MTK_EINT_FUNCTION(2, 176), + MTK_FUNCTION(0, "GPIO178"), + MTK_FUNCTION(1, "CMDAT5"), + MTK_FUNCTION(2, "EINT176") + ), + MTK_PIN( + PINCTRL_PIN(179, "RDN0_B"), + "J3", "mt8135", + MTK_EINT_FUNCTION(2, 179), + MTK_FUNCTION(0, "GPIO179"), + MTK_FUNCTION(1, "CMDAT0"), + MTK_FUNCTION(2, "EINT179"), + MTK_FUNCTION(3, "CMCSD0") + ), + MTK_PIN( + PINCTRL_PIN(180, "RDP0_B"), + "J2", "mt8135", + MTK_EINT_FUNCTION(2, 178), + MTK_FUNCTION(0, "GPIO180"), + MTK_FUNCTION(1, "CMDAT1"), + MTK_FUNCTION(2, "EINT178"), + MTK_FUNCTION(3, "CMCSD1") + ), + MTK_PIN( + PINCTRL_PIN(181, "CMPCLK"), + "K4", "mt8135", + MTK_EINT_FUNCTION(2, 182), + MTK_FUNCTION(0, "GPIO181"), + MTK_FUNCTION(1, "CMPCLK"), + MTK_FUNCTION(2, "EINT182"), + MTK_FUNCTION(3, "CMCSK"), + MTK_FUNCTION(4, "CM2MCLK_4X"), + MTK_FUNCTION(5, "TS_AUXADC_SEL[3]"), + MTK_FUNCTION(6, "VENC_TEST_CK"), + MTK_FUNCTION(7, "TESTA_OUT27") + ), + MTK_PIN( + PINCTRL_PIN(182, "CMMCLK"), + "J5", "mt8135", + MTK_EINT_FUNCTION(2, 183), + MTK_FUNCTION(0, "GPIO182"), + MTK_FUNCTION(1, "CMMCLK"), + MTK_FUNCTION(2, "EINT183"), + MTK_FUNCTION(5, "TS_AUXADC_SEL[2]"), + MTK_FUNCTION(7, "TESTA_OUT28") + ), + MTK_PIN( + PINCTRL_PIN(183, "CMRST"), + "J6", "mt8135", + MTK_EINT_FUNCTION(2, 185), + MTK_FUNCTION(0, "GPIO183"), + MTK_FUNCTION(1, "CMRST"), + MTK_FUNCTION(2, "EINT185"), + MTK_FUNCTION(5, "TS_AUXADC_SEL[1]"), + MTK_FUNCTION(7, "TESTA_OUT30") + ), + MTK_PIN( + PINCTRL_PIN(184, "CMPDN"), + "J4", "mt8135", + MTK_EINT_FUNCTION(2, 184), + MTK_FUNCTION(0, "GPIO184"), + MTK_FUNCTION(1, "CMPDN"), + MTK_FUNCTION(2, "EINT184"), + MTK_FUNCTION(5, "TS_AUXADC_SEL[0]"), + MTK_FUNCTION(7, "TESTA_OUT29") + ), + MTK_PIN( + PINCTRL_PIN(185, "CMFLASH"), + "G4", "mt8135", + MTK_EINT_FUNCTION(2, 186), + MTK_FUNCTION(0, "GPIO185"), + MTK_FUNCTION(1, "CMFLASH"), + MTK_FUNCTION(2, "EINT186"), + MTK_FUNCTION(3, "CM2MCLK_3X"), + MTK_FUNCTION(6, "MFG_TEST_CK_1"), + MTK_FUNCTION(7, "TESTA_OUT31") + ), + MTK_PIN( + PINCTRL_PIN(186, "MRG_I2S_PCM_CLK"), + "F5", "mt8135", + MTK_EINT_FUNCTION(NO_EINT_SUPPORT, NO_EINT_SUPPORT), + MTK_FUNCTION(0, "GPIO186"), + MTK_FUNCTION(1, "MRG_I2S_PCM_CLK"), + MTK_FUNCTION(3, "I2SIN_CK"), + MTK_FUNCTION(4, "PCM0_CK"), + MTK_FUNCTION(5, "DSP2_ICK"), + MTK_FUNCTION(6, "IMG_TEST_CK"), + MTK_FUNCTION(7, "USB_SCL") + ), + MTK_PIN( + PINCTRL_PIN(187, "MRG_I2S_PCM_SYNC"), + "G6", "mt8135", + MTK_EINT_FUNCTION(2, 16), + MTK_FUNCTION(0, "GPIO187"), + MTK_FUNCTION(1, "MRG_I2S_PCM_SYNC"), + MTK_FUNCTION(2, "EINT16"), + MTK_FUNCTION(3, "I2SIN_WS"), + MTK_FUNCTION(4, "PCM0_WS"), + MTK_FUNCTION(6, "DISP_TEST_CK") + ), + MTK_PIN( + PINCTRL_PIN(188, "MRG_I2S_PCM_RX"), + "G3", "mt8135", + MTK_EINT_FUNCTION(NO_EINT_SUPPORT, NO_EINT_SUPPORT), + MTK_FUNCTION(0, "GPIO188"), + MTK_FUNCTION(1, "MRG_I2S_PCM_RX"), + MTK_FUNCTION(3, "I2SIN_DAT"), + MTK_FUNCTION(4, "PCM0_DI"), + MTK_FUNCTION(5, "DSP2_ID"), + MTK_FUNCTION(6, "MFG_TEST_CK"), + MTK_FUNCTION(7, "USB_SDA") + ), + MTK_PIN( + PINCTRL_PIN(189, "MRG_I2S_PCM_TX"), + "G5", "mt8135", + MTK_EINT_FUNCTION(2, 17), + MTK_FUNCTION(0, "GPIO189"), + MTK_FUNCTION(1, "MRG_I2S_PCM_TX"), + MTK_FUNCTION(2, "EINT17"), + MTK_FUNCTION(3, "I2SOUT_DAT"), + MTK_FUNCTION(4, "PCM0_DO"), + MTK_FUNCTION(6, "VDEC_TEST_CK") + ), + MTK_PIN( + PINCTRL_PIN(190, "SRCLKENAI"), + "K5", "mt8135", + MTK_EINT_FUNCTION(NO_EINT_SUPPORT, NO_EINT_SUPPORT), + MTK_FUNCTION(0, "GPIO190"), + MTK_FUNCTION(1, "SRCLKENAI") + ), + MTK_PIN( + PINCTRL_PIN(191, "URXD3"), + "C3", "mt8135", + MTK_EINT_FUNCTION(2, 87), + MTK_FUNCTION(0, "GPIO191"), + MTK_FUNCTION(1, "URXD3"), + MTK_FUNCTION(2, "EINT87"), + MTK_FUNCTION(3, "UTXD3"), + MTK_FUNCTION(5, "TS_AUX_ST"), + MTK_FUNCTION(6, "PWM4") + ), + MTK_PIN( + PINCTRL_PIN(192, "UTXD3"), + "B2", "mt8135", + MTK_EINT_FUNCTION(2, 86), + MTK_FUNCTION(0, "GPIO192"), + MTK_FUNCTION(1, "UTXD3"), + MTK_FUNCTION(2, "EINT86"), + MTK_FUNCTION(3, "URXD3"), + MTK_FUNCTION(5, "TS_AUX_CS_B"), + MTK_FUNCTION(6, "PWM3") + ), + MTK_PIN( + PINCTRL_PIN(193, "SDA2"), + "G2", "mt8135", + MTK_EINT_FUNCTION(2, 95), + MTK_FUNCTION(0, "GPIO193"), + MTK_FUNCTION(1, "SDA2"), + MTK_FUNCTION(2, "EINT95"), + MTK_FUNCTION(3, "CLKM5"), + MTK_FUNCTION(4, "PWM5"), + MTK_FUNCTION(5, "TS_AUX_PWDB") + ), + MTK_PIN( + PINCTRL_PIN(194, "SCL2"), + "F4", "mt8135", + MTK_EINT_FUNCTION(2, 94), + MTK_FUNCTION(0, "GPIO194"), + MTK_FUNCTION(1, "SCL2"), + MTK_FUNCTION(2, "EINT94"), + MTK_FUNCTION(3, "CLKM4"), + MTK_FUNCTION(4, "PWM4"), + MTK_FUNCTION(5, "TS_AUXADC_TEST_CK") + ), + MTK_PIN( + PINCTRL_PIN(195, "SDA1"), + "F2", "mt8135", + MTK_EINT_FUNCTION(2, 93), + MTK_FUNCTION(0, "GPIO195"), + MTK_FUNCTION(1, "SDA1"), + MTK_FUNCTION(2, "EINT93"), + MTK_FUNCTION(3, "CLKM3"), + MTK_FUNCTION(4, "PWM3"), + MTK_FUNCTION(5, "TS_AUX_SCLK_PWDB") + ), + MTK_PIN( + PINCTRL_PIN(196, "SCL1"), + "F3", "mt8135", + MTK_EINT_FUNCTION(2, 92), + MTK_FUNCTION(0, "GPIO196"), + MTK_FUNCTION(1, "SCL1"), + MTK_FUNCTION(2, "EINT92"), + MTK_FUNCTION(3, "CLKM2"), + MTK_FUNCTION(4, "PWM2"), + MTK_FUNCTION(5, "TS_AUX_DIN") + ), + MTK_PIN( + PINCTRL_PIN(197, "MSDC3_DAT2"), + "E1", "mt8135", + MTK_EINT_FUNCTION(2, 71), + MTK_FUNCTION(0, "GPIO197"), + MTK_FUNCTION(1, "MSDC3_DAT2"), + MTK_FUNCTION(2, "EINT71"), + MTK_FUNCTION(3, "SCL6"), + MTK_FUNCTION(4, "PWM5"), + MTK_FUNCTION(5, "CLKM4"), + MTK_FUNCTION(6, "MFG_TEST_CK_2") + ), + MTK_PIN( + PINCTRL_PIN(198, "MSDC3_DAT3"), + "C2", "mt8135", + MTK_EINT_FUNCTION(2, 72), + MTK_FUNCTION(0, "GPIO198"), + MTK_FUNCTION(1, "MSDC3_DAT3"), + MTK_FUNCTION(2, "EINT72"), + MTK_FUNCTION(3, "SDA6"), + MTK_FUNCTION(4, "PWM6"), + MTK_FUNCTION(5, "CLKM5"), + MTK_FUNCTION(6, "MFG_TEST_CK_3") + ), + MTK_PIN( + PINCTRL_PIN(199, "MSDC3_CMD"), + "D2", "mt8135", + MTK_EINT_FUNCTION(2, 68), + MTK_FUNCTION(0, "GPIO199"), + MTK_FUNCTION(1, "MSDC3_CMD"), + MTK_FUNCTION(2, "EINT68"), + MTK_FUNCTION(3, "SDA2"), + MTK_FUNCTION(4, "PWM2"), + MTK_FUNCTION(5, "CLKM1"), + MTK_FUNCTION(6, "MFG_TEST_CK_4") + ), + MTK_PIN( + PINCTRL_PIN(200, "MSDC3_CLK"), + "E2", "mt8135", + MTK_EINT_FUNCTION(2, 67), + MTK_FUNCTION(0, "GPIO200"), + MTK_FUNCTION(1, "MSDC3_CLK"), + MTK_FUNCTION(2, "EINT67"), + MTK_FUNCTION(3, "SCL2"), + MTK_FUNCTION(4, "PWM1"), + MTK_FUNCTION(5, "CLKM0") + ), + MTK_PIN( + PINCTRL_PIN(201, "MSDC3_DAT1"), + "D3", "mt8135", + MTK_EINT_FUNCTION(2, 70), + MTK_FUNCTION(0, "GPIO201"), + MTK_FUNCTION(1, "MSDC3_DAT1"), + MTK_FUNCTION(2, "EINT70"), + MTK_FUNCTION(3, "SDA3"), + MTK_FUNCTION(4, "PWM4"), + MTK_FUNCTION(5, "CLKM3") + ), + MTK_PIN( + PINCTRL_PIN(202, "MSDC3_DAT0"), + "E3", "mt8135", + MTK_EINT_FUNCTION(2, 69), + MTK_FUNCTION(0, "GPIO202"), + MTK_FUNCTION(1, "MSDC3_DAT0"), + MTK_FUNCTION(2, "EINT69"), + MTK_FUNCTION(3, "SCL3"), + MTK_FUNCTION(4, "PWM3"), + MTK_FUNCTION(5, "CLKM2") + ), +}; + +#endif /* __PINCTRL_MTK_MT8135_H */ diff --git a/include/dt-bindings/pinctrl/mt65xx.h b/include/dt-bindings/pinctrl/mt65xx.h new file mode 100644 index 000000000000..1198f4541327 --- /dev/null +++ b/include/dt-bindings/pinctrl/mt65xx.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2014 MediaTek Inc. + * Author: Hongzhou.Yang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef _DT_BINDINGS_PINCTRL_MT65XX_H +#define _DT_BINDINGS_PINCTRL_MT65XX_H + +#define MTK_PIN_NO(x) ((x) << 8) +#define MTK_GET_PIN_NO(x) ((x) >> 8) +#define MTK_GET_PIN_FUNC(x) ((x) & 0xf) + +#define MTK_PUPD_SET_R1R0_00 100 +#define MTK_PUPD_SET_R1R0_01 101 +#define MTK_PUPD_SET_R1R0_10 102 +#define MTK_PUPD_SET_R1R0_11 103 + +#define MTK_DRIVE_2mA 2 +#define MTK_DRIVE_4mA 4 +#define MTK_DRIVE_6mA 6 +#define MTK_DRIVE_8mA 8 +#define MTK_DRIVE_10mA 10 +#define MTK_DRIVE_12mA 12 +#define MTK_DRIVE_14mA 14 +#define MTK_DRIVE_16mA 16 +#define MTK_DRIVE_20mA 20 +#define MTK_DRIVE_24mA 24 +#define MTK_DRIVE_28mA 28 +#define MTK_DRIVE_32mA 32 + +#endif /* _DT_BINDINGS_PINCTRL_MT65XX_H */ -- cgit v1.2.3 From 359597cb374b7935bf804cde590ac55f9cb275b7 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Tue, 17 Mar 2015 11:48:30 +0200 Subject: ACPI: Add acpi_device_uid() for convenience Add a nicer way to get the ACPI _UID. Signed-off-by: Adrian Hunter Signed-off-by: Rafael J. Wysocki --- include/acpi/acpi_bus.h | 1 + 1 file changed, 1 insertion(+) (limited to 'include') diff --git a/include/acpi/acpi_bus.h b/include/acpi/acpi_bus.h index 61e32ec1fc4d..f8009d99190a 100644 --- a/include/acpi/acpi_bus.h +++ b/include/acpi/acpi_bus.h @@ -252,6 +252,7 @@ struct acpi_device_pnp { #define acpi_device_bid(d) ((d)->pnp.bus_id) #define acpi_device_adr(d) ((d)->pnp.bus_address) const char *acpi_device_hid(struct acpi_device *device); +#define acpi_device_uid(d) ((d)->pnp.unique_id) #define acpi_device_name(d) ((d)->pnp.device_name) #define acpi_device_class(d) ((d)->pnp.device_class) -- cgit v1.2.3 From 407640de2152e33341ce1131dac269672c3d50f7 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Tue, 17 Mar 2015 18:32:26 -0700 Subject: inet: add sk_listener argument to inet_reqsk_alloc() listener socket can be used to set net pointer, and will be later used to hold a reference on listener. Add a const qualifier to first argument (struct request_sock_ops *), and factorize all write_pnet(&ireq->ireq_net, sock_net(sk)); Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- include/net/inet_sock.h | 9 ++++++--- net/dccp/ipv4.c | 3 +-- net/dccp/ipv6.c | 3 +-- net/ipv4/syncookies.c | 3 +-- net/ipv4/tcp_input.c | 3 +-- net/ipv6/syncookies.c | 3 +-- 6 files changed, 11 insertions(+), 13 deletions(-) (limited to 'include') diff --git a/include/net/inet_sock.h b/include/net/inet_sock.h index c9ed91891887..cf7abb00941b 100644 --- a/include/net/inet_sock.h +++ b/include/net/inet_sock.h @@ -244,16 +244,19 @@ static inline unsigned int __inet_ehashfn(const __be32 laddr, initval); } -static inline struct request_sock *inet_reqsk_alloc(struct request_sock_ops *ops) +static inline struct request_sock * +inet_reqsk_alloc(const struct request_sock_ops *ops, struct sock *sk_listener) { struct request_sock *req = reqsk_alloc(ops); - struct inet_request_sock *ireq = inet_rsk(req); - if (req != NULL) { + if (req) { + struct inet_request_sock *ireq = inet_rsk(req); + kmemcheck_annotate_bitfield(ireq, flags); ireq->opt = NULL; atomic64_set(&ireq->ir_cookie, 0); ireq->ireq_state = TCP_NEW_SYN_RECV; + write_pnet(&ireq->ireq_net, sock_net(sk_listener)); /* Following is temporary. It is coupled with debugging * helpers in reqsk_put() & reqsk_free() diff --git a/net/dccp/ipv4.c b/net/dccp/ipv4.c index 7f6456afbaec..bf897829f4f0 100644 --- a/net/dccp/ipv4.c +++ b/net/dccp/ipv4.c @@ -624,7 +624,7 @@ int dccp_v4_conn_request(struct sock *sk, struct sk_buff *skb) if (sk_acceptq_is_full(sk) && inet_csk_reqsk_queue_young(sk) > 1) goto drop; - req = inet_reqsk_alloc(&dccp_request_sock_ops); + req = inet_reqsk_alloc(&dccp_request_sock_ops, sk); if (req == NULL) goto drop; @@ -641,7 +641,6 @@ int dccp_v4_conn_request(struct sock *sk, struct sk_buff *skb) ireq = inet_rsk(req); ireq->ir_loc_addr = ip_hdr(skb)->daddr; ireq->ir_rmt_addr = ip_hdr(skb)->saddr; - write_pnet(&ireq->ireq_net, sock_net(sk)); ireq->ireq_family = AF_INET; ireq->ir_iif = sk->sk_bound_dev_if; diff --git a/net/dccp/ipv6.c b/net/dccp/ipv6.c index 5166b0043f95..d7e7c7b0a3f1 100644 --- a/net/dccp/ipv6.c +++ b/net/dccp/ipv6.c @@ -386,7 +386,7 @@ static int dccp_v6_conn_request(struct sock *sk, struct sk_buff *skb) if (sk_acceptq_is_full(sk) && inet_csk_reqsk_queue_young(sk) > 1) goto drop; - req = inet_reqsk_alloc(&dccp6_request_sock_ops); + req = inet_reqsk_alloc(&dccp6_request_sock_ops, sk); if (req == NULL) goto drop; @@ -403,7 +403,6 @@ static int dccp_v6_conn_request(struct sock *sk, struct sk_buff *skb) ireq = inet_rsk(req); ireq->ir_v6_rmt_addr = ipv6_hdr(skb)->saddr; ireq->ir_v6_loc_addr = ipv6_hdr(skb)->daddr; - write_pnet(&ireq->ireq_net, sock_net(sk)); ireq->ireq_family = AF_INET6; if (ipv6_opt_accepted(sk, skb, IP6CB(skb)) || diff --git a/net/ipv4/syncookies.c b/net/ipv4/syncookies.c index 5ae0c49f5e2e..eb940750bb1b 100644 --- a/net/ipv4/syncookies.c +++ b/net/ipv4/syncookies.c @@ -325,7 +325,7 @@ struct sock *cookie_v4_check(struct sock *sk, struct sk_buff *skb) goto out; ret = NULL; - req = inet_reqsk_alloc(&tcp_request_sock_ops); /* for safety */ + req = inet_reqsk_alloc(&tcp_request_sock_ops, sk); /* for safety */ if (!req) goto out; @@ -346,7 +346,6 @@ struct sock *cookie_v4_check(struct sock *sk, struct sk_buff *skb) req->ts_recent = tcp_opt.saw_tstamp ? tcp_opt.rcv_tsval : 0; treq->snt_synack = tcp_opt.saw_tstamp ? tcp_opt.rcv_tsecr : 0; treq->listener = NULL; - write_pnet(&ireq->ireq_net, sock_net(sk)); ireq->ireq_family = AF_INET; ireq->ir_iif = sk->sk_bound_dev_if; diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index 7257eb206c07..2a480f6811ea 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -6004,7 +6004,7 @@ int tcp_conn_request(struct request_sock_ops *rsk_ops, goto drop; } - req = inet_reqsk_alloc(rsk_ops); + req = inet_reqsk_alloc(rsk_ops, sk); if (!req) goto drop; @@ -6020,7 +6020,6 @@ int tcp_conn_request(struct request_sock_ops *rsk_ops, tmp_opt.tstamp_ok = tmp_opt.saw_tstamp; tcp_openreq_init(req, &tmp_opt, skb, sk); - write_pnet(&inet_rsk(req)->ireq_net, sock_net(sk)); /* Note: tcp_v6_init_req() might override ir_iif for link locals */ inet_rsk(req)->ir_iif = sk->sk_bound_dev_if; diff --git a/net/ipv6/syncookies.c b/net/ipv6/syncookies.c index 58875ce8e178..039e74dd29fe 100644 --- a/net/ipv6/syncookies.c +++ b/net/ipv6/syncookies.c @@ -189,14 +189,13 @@ struct sock *cookie_v6_check(struct sock *sk, struct sk_buff *skb) goto out; ret = NULL; - req = inet_reqsk_alloc(&tcp6_request_sock_ops); + req = inet_reqsk_alloc(&tcp6_request_sock_ops, sk); if (!req) goto out; ireq = inet_rsk(req); treq = tcp_rsk(req); treq->listener = NULL; - write_pnet(&ireq->ireq_net, sock_net(sk)); ireq->ireq_family = AF_INET6; if (security_inet_conn_request(sk, skb, req)) -- cgit v1.2.3 From e49bb337d77d54afebe4fe5b9008955e1337f83d Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Tue, 17 Mar 2015 18:32:27 -0700 Subject: inet: uninline inet_reqsk_alloc() inet_reqsk_alloc() is becoming fat and should not be inlined. Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- include/net/inet_sock.h | 24 ++---------------------- net/ipv4/tcp_input.c | 24 ++++++++++++++++++++++++ 2 files changed, 26 insertions(+), 22 deletions(-) (limited to 'include') diff --git a/include/net/inet_sock.h b/include/net/inet_sock.h index cf7abb00941b..6fec7343070f 100644 --- a/include/net/inet_sock.h +++ b/include/net/inet_sock.h @@ -244,28 +244,8 @@ static inline unsigned int __inet_ehashfn(const __be32 laddr, initval); } -static inline struct request_sock * -inet_reqsk_alloc(const struct request_sock_ops *ops, struct sock *sk_listener) -{ - struct request_sock *req = reqsk_alloc(ops); - - if (req) { - struct inet_request_sock *ireq = inet_rsk(req); - - kmemcheck_annotate_bitfield(ireq, flags); - ireq->opt = NULL; - atomic64_set(&ireq->ir_cookie, 0); - ireq->ireq_state = TCP_NEW_SYN_RECV; - write_pnet(&ireq->ireq_net, sock_net(sk_listener)); - - /* Following is temporary. It is coupled with debugging - * helpers in reqsk_put() & reqsk_free() - */ - atomic_set(&ireq->ireq_refcnt, 0); - } - - return req; -} +struct request_sock *inet_reqsk_alloc(const struct request_sock_ops *ops, + struct sock *sk_listener); static inline __u8 inet_sk_flowi_flags(const struct sock *sk) { diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index 2a480f6811ea..52b74e0eab2a 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -5967,6 +5967,30 @@ static void tcp_openreq_init(struct request_sock *req, ireq->ir_mark = inet_request_mark(sk, skb); } +struct request_sock *inet_reqsk_alloc(const struct request_sock_ops *ops, + struct sock *sk_listener) +{ + struct request_sock *req = reqsk_alloc(ops); + + if (req) { + struct inet_request_sock *ireq = inet_rsk(req); + + kmemcheck_annotate_bitfield(ireq, flags); + ireq->opt = NULL; + atomic64_set(&ireq->ir_cookie, 0); + ireq->ireq_state = TCP_NEW_SYN_RECV; + write_pnet(&ireq->ireq_net, sock_net(sk_listener)); + + /* Following is temporary. It is coupled with debugging + * helpers in reqsk_put() & reqsk_free() + */ + atomic_set(&ireq->ireq_refcnt, 0); + } + + return req; +} +EXPORT_SYMBOL(inet_reqsk_alloc); + int tcp_conn_request(struct request_sock_ops *rsk_ops, const struct tcp_request_sock_ops *af_ops, struct sock *sk, struct sk_buff *skb) -- cgit v1.2.3 From 4e9a578e5b6bdfa8b7fed7a41f28a86a7cffc85f Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Tue, 17 Mar 2015 18:32:28 -0700 Subject: inet: add rsk_listener field to struct request_sock Once we'll be able to lookup request sockets in ehash table, we'll need to get access to listener which created this request. This avoid doing a lookup to find the listener, which benefits for a more solid SO_REUSEPORT, and is needed once we no longer queue request sock into a listener private queue. Note that 'struct tcp_request_sock'->listener could be reduced to a single bit, as TFO listener should match req->rsk_listener. TFO will no longer need to hold a reference on the listener. Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- include/net/request_sock.h | 12 +++++++++--- net/ipv4/tcp_input.c | 2 +- 2 files changed, 10 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/include/net/request_sock.h b/include/net/request_sock.h index 56dc2faba47e..723d1cbdf20e 100644 --- a/include/net/request_sock.h +++ b/include/net/request_sock.h @@ -52,6 +52,7 @@ struct request_sock { #define rsk_refcnt __req_common.skc_refcnt struct request_sock *dl_next; + struct sock *rsk_listener; u16 mss; u8 num_retrans; /* number of retransmits */ u8 cookie_ts:1; /* syncookie: encode tcpopts in timestamp */ @@ -67,13 +68,16 @@ struct request_sock { u32 peer_secid; }; -static inline struct request_sock *reqsk_alloc(const struct request_sock_ops *ops) +static inline struct request_sock * +reqsk_alloc(const struct request_sock_ops *ops, struct sock *sk_listener) { struct request_sock *req = kmem_cache_alloc(ops->slab, GFP_ATOMIC); - if (req != NULL) + if (req) { req->rsk_ops = ops; - + sock_hold(sk_listener); + req->rsk_listener = sk_listener; + } return req; } @@ -88,6 +92,8 @@ static inline void reqsk_free(struct request_sock *req) WARN_ON_ONCE(atomic_read(&req->rsk_refcnt) != 0); req->rsk_ops->destructor(req); + if (req->rsk_listener) + sock_put(req->rsk_listener); kmem_cache_free(req->rsk_ops->slab, req); } diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index 52b74e0eab2a..fbe518981d36 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -5970,7 +5970,7 @@ static void tcp_openreq_init(struct request_sock *req, struct request_sock *inet_reqsk_alloc(const struct request_sock_ops *ops, struct sock *sk_listener) { - struct request_sock *req = reqsk_alloc(ops); + struct request_sock *req = reqsk_alloc(ops, sk_listener); if (req) { struct inet_request_sock *ireq = inet_rsk(req); -- cgit v1.2.3 From 9439ce00f208d95703a6725e4ea986dd90e37ffd Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Tue, 17 Mar 2015 18:32:29 -0700 Subject: tcp: rename struct tcp_request_sock listener The listener field in struct tcp_request_sock is a pointer back to the listener. We now have req->rsk_listener, so TCP only needs one boolean and not a full pointer. Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- include/linux/tcp.h | 2 +- net/core/request_sock.c | 18 +++++++----------- net/ipv4/inet_connection_sock.c | 7 +++---- net/ipv4/syncookies.c | 2 +- net/ipv4/tcp_fastopen.c | 7 +------ net/ipv4/tcp_input.c | 2 +- net/ipv6/syncookies.c | 2 +- 7 files changed, 15 insertions(+), 25 deletions(-) (limited to 'include') diff --git a/include/linux/tcp.h b/include/linux/tcp.h index 97dbf16f7d9d..f869ae8afbaf 100644 --- a/include/linux/tcp.h +++ b/include/linux/tcp.h @@ -111,7 +111,7 @@ struct tcp_request_sock_ops; struct tcp_request_sock { struct inet_request_sock req; const struct tcp_request_sock_ops *af_specific; - struct sock *listener; /* needed for TFO */ + bool tfo_listener; u32 rcv_isn; u32 snt_isn; u32 snt_synack; /* synack sent time */ diff --git a/net/core/request_sock.c b/net/core/request_sock.c index e910317ef6d9..cc39a2aa663a 100644 --- a/net/core/request_sock.c +++ b/net/core/request_sock.c @@ -153,24 +153,22 @@ void reqsk_queue_destroy(struct request_sock_queue *queue) * case might also exist in tcp_v4_hnd_req() that will trigger this locking * order. * - * When a TFO req is created, it needs to sock_hold its listener to prevent - * the latter data structure from going away. - * - * This function also sets "treq->listener" to NULL and unreference listener - * socket. treq->listener is used by the listener so it is protected by the + * This function also sets "treq->tfo_listener" to false. + * treq->tfo_listener is used by the listener so it is protected by the * fastopenq->lock in this function. */ void reqsk_fastopen_remove(struct sock *sk, struct request_sock *req, bool reset) { - struct sock *lsk = tcp_rsk(req)->listener; - struct fastopen_queue *fastopenq = - inet_csk(lsk)->icsk_accept_queue.fastopenq; + struct sock *lsk = req->rsk_listener; + struct fastopen_queue *fastopenq; + + fastopenq = inet_csk(lsk)->icsk_accept_queue.fastopenq; tcp_sk(sk)->fastopen_rsk = NULL; spin_lock_bh(&fastopenq->lock); fastopenq->qlen--; - tcp_rsk(req)->listener = NULL; + tcp_rsk(req)->tfo_listener = false; if (req->sk) /* the child socket hasn't been accepted yet */ goto out; @@ -179,7 +177,6 @@ void reqsk_fastopen_remove(struct sock *sk, struct request_sock *req, * special RST handling below. */ spin_unlock_bh(&fastopenq->lock); - sock_put(lsk); reqsk_put(req); return; } @@ -201,5 +198,4 @@ void reqsk_fastopen_remove(struct sock *sk, struct request_sock *req, fastopenq->qlen++; out: spin_unlock_bh(&fastopenq->lock); - sock_put(lsk); } diff --git a/net/ipv4/inet_connection_sock.c b/net/ipv4/inet_connection_sock.c index 3390ba6f96b2..741f0d96a7f7 100644 --- a/net/ipv4/inet_connection_sock.c +++ b/net/ipv4/inet_connection_sock.c @@ -325,7 +325,7 @@ struct sock *inet_csk_accept(struct sock *sk, int flags, int *err) sk_acceptq_removed(sk); if (sk->sk_protocol == IPPROTO_TCP && queue->fastopenq != NULL) { spin_lock_bh(&queue->fastopenq->lock); - if (tcp_rsk(req)->listener) { + if (tcp_rsk(req)->tfo_listener) { /* We are still waiting for the final ACK from 3WHS * so can't free req now. Instead, we set req->sk to * NULL to signify that the child socket is taken @@ -817,9 +817,9 @@ void inet_csk_listen_stop(struct sock *sk) percpu_counter_inc(sk->sk_prot->orphan_count); - if (sk->sk_protocol == IPPROTO_TCP && tcp_rsk(req)->listener) { + if (sk->sk_protocol == IPPROTO_TCP && tcp_rsk(req)->tfo_listener) { BUG_ON(tcp_sk(child)->fastopen_rsk != req); - BUG_ON(sk != tcp_rsk(req)->listener); + BUG_ON(sk != req->rsk_listener); /* Paranoid, to prevent race condition if * an inbound pkt destined for child is @@ -828,7 +828,6 @@ void inet_csk_listen_stop(struct sock *sk) * tcp_v4_destroy_sock(). */ tcp_sk(child)->fastopen_rsk = NULL; - sock_put(sk); } inet_csk_destroy_sock(child); diff --git a/net/ipv4/syncookies.c b/net/ipv4/syncookies.c index eb940750bb1b..574b67765a06 100644 --- a/net/ipv4/syncookies.c +++ b/net/ipv4/syncookies.c @@ -345,7 +345,7 @@ struct sock *cookie_v4_check(struct sock *sk, struct sk_buff *skb) ireq->tstamp_ok = tcp_opt.saw_tstamp; req->ts_recent = tcp_opt.saw_tstamp ? tcp_opt.rcv_tsval : 0; treq->snt_synack = tcp_opt.saw_tstamp ? tcp_opt.rcv_tsecr : 0; - treq->listener = NULL; + treq->tfo_listener = false; ireq->ireq_family = AF_INET; ireq->ir_iif = sk->sk_bound_dev_if; diff --git a/net/ipv4/tcp_fastopen.c b/net/ipv4/tcp_fastopen.c index 84381319e1bc..186fd394ec0a 100644 --- a/net/ipv4/tcp_fastopen.c +++ b/net/ipv4/tcp_fastopen.c @@ -155,12 +155,7 @@ static bool tcp_fastopen_create_child(struct sock *sk, tp = tcp_sk(child); tp->fastopen_rsk = req; - /* Do a hold on the listner sk so that if the listener is being - * closed, the child that has been accepted can live on and still - * access listen_lock. - */ - sock_hold(sk); - tcp_rsk(req)->listener = sk; + tcp_rsk(req)->tfo_listener = true; /* RFC1323: The window in SYN & SYN/ACK segments is never * scaled. So correct it appropriately. diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index fbe518981d36..a94ddb96fc85 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -6120,7 +6120,7 @@ int tcp_conn_request(struct request_sock_ops *rsk_ops, if (err || want_cookie) goto drop_and_free; - tcp_rsk(req)->listener = NULL; + tcp_rsk(req)->tfo_listener = false; af_ops->queue_hash_add(sk, req, TCP_TIMEOUT_INIT); } diff --git a/net/ipv6/syncookies.c b/net/ipv6/syncookies.c index 039e74dd29fe..1ef0c926ce9d 100644 --- a/net/ipv6/syncookies.c +++ b/net/ipv6/syncookies.c @@ -195,7 +195,7 @@ struct sock *cookie_v6_check(struct sock *sk, struct sk_buff *skb) ireq = inet_rsk(req); treq = tcp_rsk(req); - treq->listener = NULL; + treq->tfo_listener = false; ireq->ireq_family = AF_INET6; if (security_inet_conn_request(sk, skb, req)) -- cgit v1.2.3 From 0470c8ca1d57927f2cc3e1d5add1fb2834609447 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Tue, 17 Mar 2015 18:32:31 -0700 Subject: inet: fix request sock refcounting While testing last patch series, I found req sock refcounting was wrong. We must set skc_refcnt to 1 for all request socks added in hashes, but also on request sockets created by FastOpen or syncookies. It is tricky because we need to defer this initialization so that future RCU lookups do not try to take a refcount on a not yet fully initialized request socket. Also get rid of ireq_refcnt alias. Signed-off-by: Eric Dumazet Fixes: 13854e5a6046 ("inet: add proper refcounting to request sock") Signed-off-by: David S. Miller --- include/net/inet_connection_sock.h | 5 ----- include/net/inet_sock.h | 1 - include/net/request_sock.h | 11 +++++++++++ net/ipv4/syncookies.c | 11 ++++++----- net/ipv4/tcp_fastopen.c | 1 + net/ipv4/tcp_input.c | 4 ---- net/ipv6/syncookies.c | 7 ++++--- 7 files changed, 22 insertions(+), 18 deletions(-) (limited to 'include') diff --git a/include/net/inet_connection_sock.h b/include/net/inet_connection_sock.h index 191feec60205..b9a6b0a94cc6 100644 --- a/include/net/inet_connection_sock.h +++ b/include/net/inet_connection_sock.h @@ -275,11 +275,6 @@ static inline void inet_csk_reqsk_queue_add(struct sock *sk, struct sock *child) { reqsk_queue_add(&inet_csk(sk)->icsk_accept_queue, req, sk, child); - /* before letting lookups find us, make sure all req fields - * are committed to memory. - */ - smp_wmb(); - atomic_set(&req->rsk_refcnt, 1); } void inet_csk_reqsk_queue_hash_add(struct sock *sk, struct request_sock *req, diff --git a/include/net/inet_sock.h b/include/net/inet_sock.h index 6fec7343070f..b6c3737da4e9 100644 --- a/include/net/inet_sock.h +++ b/include/net/inet_sock.h @@ -81,7 +81,6 @@ struct inet_request_sock { #define ir_cookie req.__req_common.skc_cookie #define ireq_net req.__req_common.skc_net #define ireq_state req.__req_common.skc_state -#define ireq_refcnt req.__req_common.skc_refcnt #define ireq_family req.__req_common.skc_family kmemcheck_bitfield_begin(flags); diff --git a/include/net/request_sock.h b/include/net/request_sock.h index 723d1cbdf20e..3fa4f824900a 100644 --- a/include/net/request_sock.h +++ b/include/net/request_sock.h @@ -77,6 +77,11 @@ reqsk_alloc(const struct request_sock_ops *ops, struct sock *sk_listener) req->rsk_ops = ops; sock_hold(sk_listener); req->rsk_listener = sk_listener; + + /* Following is temporary. It is coupled with debugging + * helpers in reqsk_put() & reqsk_free() + */ + atomic_set(&req->rsk_refcnt, 0); } return req; } @@ -292,6 +297,12 @@ static inline void reqsk_queue_hash_req(struct request_sock_queue *queue, req->sk = NULL; req->dl_next = lopt->syn_table[hash]; + /* before letting lookups find us, make sure all req fields + * are committed to memory and refcnt initialized. + */ + smp_wmb(); + atomic_set(&req->rsk_refcnt, 1); + write_lock(&queue->syn_wait_lock); lopt->syn_table[hash] = req; write_unlock(&queue->syn_wait_lock); diff --git a/net/ipv4/syncookies.c b/net/ipv4/syncookies.c index 574b67765a06..34e755403715 100644 --- a/net/ipv4/syncookies.c +++ b/net/ipv4/syncookies.c @@ -227,11 +227,12 @@ static struct sock *get_cookie_sock(struct sock *sk, struct sk_buff *skb, struct sock *child; child = icsk->icsk_af_ops->syn_recv_sock(sk, skb, req, dst); - if (child) + if (child) { + atomic_set(&req->rsk_refcnt, 1); inet_csk_reqsk_queue_add(sk, req, child); - else + } else { reqsk_free(req); - + } return child; } @@ -356,7 +357,7 @@ struct sock *cookie_v4_check(struct sock *sk, struct sk_buff *skb) ireq->opt = tcp_v4_save_options(skb); if (security_inet_conn_request(sk, skb, req)) { - reqsk_put(req); + reqsk_free(req); goto out; } @@ -377,7 +378,7 @@ struct sock *cookie_v4_check(struct sock *sk, struct sk_buff *skb) security_req_classify_flow(req, flowi4_to_flowi(&fl4)); rt = ip_route_output_key(sock_net(sk), &fl4); if (IS_ERR(rt)) { - reqsk_put(req); + reqsk_free(req); goto out; } diff --git a/net/ipv4/tcp_fastopen.c b/net/ipv4/tcp_fastopen.c index 186fd394ec0a..82e375a0cbcf 100644 --- a/net/ipv4/tcp_fastopen.c +++ b/net/ipv4/tcp_fastopen.c @@ -169,6 +169,7 @@ static bool tcp_fastopen_create_child(struct sock *sk, inet_csk_reset_xmit_timer(child, ICSK_TIME_RETRANS, TCP_TIMEOUT_INIT, TCP_RTO_MAX); + atomic_set(&req->rsk_refcnt, 1); /* Add the child socket directly into the accept queue */ inet_csk_reqsk_queue_add(sk, req, child); diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index a94ddb96fc85..1dfbaee3554e 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -5981,10 +5981,6 @@ struct request_sock *inet_reqsk_alloc(const struct request_sock_ops *ops, ireq->ireq_state = TCP_NEW_SYN_RECV; write_pnet(&ireq->ireq_net, sock_net(sk_listener)); - /* Following is temporary. It is coupled with debugging - * helpers in reqsk_put() & reqsk_free() - */ - atomic_set(&ireq->ireq_refcnt, 0); } return req; diff --git a/net/ipv6/syncookies.c b/net/ipv6/syncookies.c index 1ef0c926ce9d..da5823e5e5a7 100644 --- a/net/ipv6/syncookies.c +++ b/net/ipv6/syncookies.c @@ -49,11 +49,12 @@ static inline struct sock *get_cookie_sock(struct sock *sk, struct sk_buff *skb, struct sock *child; child = icsk->icsk_af_ops->syn_recv_sock(sk, skb, req, dst); - if (child) + if (child) { + atomic_set(&req->rsk_refcnt, 1); inet_csk_reqsk_queue_add(sk, req, child); - else + } else { reqsk_free(req); - + } return child; } -- cgit v1.2.3 From a572460be9cfb423c60275943f7921003b8cd372 Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Wed, 11 Mar 2015 12:30:58 -0300 Subject: dmaengine: imx-sdma: Add support for version 3 firmware Currently when version 3.1 of the mx6q SDMA firmware is used we get: [ 0.392169] imx-sdma 20ec000.sdma: unknown firmware version [ 0.399281] imx-sdma 20ec000.sdma: initialized Add support for it. Based on a patch from Shengjiu Wang from the internal FSL kernel. Signed-off-by: Fabio Estevam Signed-off-by: Vinod Koul --- drivers/dma/imx-sdma.c | 4 ++++ include/linux/platform_data/dma-imx-sdma.h | 3 +++ 2 files changed, 7 insertions(+) (limited to 'include') diff --git a/drivers/dma/imx-sdma.c b/drivers/dma/imx-sdma.c index 18c0a131e4e4..eb10109c55ad 100644 --- a/drivers/dma/imx-sdma.c +++ b/drivers/dma/imx-sdma.c @@ -1256,6 +1256,7 @@ static void sdma_issue_pending(struct dma_chan *chan) #define SDMA_SCRIPT_ADDRS_ARRAY_SIZE_V1 34 #define SDMA_SCRIPT_ADDRS_ARRAY_SIZE_V2 38 +#define SDMA_SCRIPT_ADDRS_ARRAY_SIZE_V3 41 static void sdma_add_scripts(struct sdma_engine *sdma, const struct sdma_script_start_addrs *addr) @@ -1302,6 +1303,9 @@ static void sdma_load_firmware(const struct firmware *fw, void *context) case 2: sdma->script_number = SDMA_SCRIPT_ADDRS_ARRAY_SIZE_V2; break; + case 3: + sdma->script_number = SDMA_SCRIPT_ADDRS_ARRAY_SIZE_V3; + break; default: dev_err(sdma->dev, "unknown firmware version\n"); goto err_firmware; diff --git a/include/linux/platform_data/dma-imx-sdma.h b/include/linux/platform_data/dma-imx-sdma.h index eabac4e2fc99..2d08816720f6 100644 --- a/include/linux/platform_data/dma-imx-sdma.h +++ b/include/linux/platform_data/dma-imx-sdma.h @@ -48,6 +48,9 @@ struct sdma_script_start_addrs { s32 ssish_2_mcu_addr; s32 hdmi_dma_addr; /* End of v2 array */ + s32 zcanfd_2_mcu_addr; + s32 zqspi_2_mcu_addr; + /* End of v3 array */ }; /** -- cgit v1.2.3 From 351d224f64afc1b3b359a1738b7d4600c7e64061 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Thu, 12 Mar 2015 17:17:58 +0100 Subject: of: base: add function to get highest id of an alias stem I2C supports adding adapters using either a dynamic or fixed id. The latter is provided by aliases in the DT case. To prevent id collisions of those two types, install this function which gives us the highest fixed id, so we can then let the dynamically created ones come after this highest number. Signed-off-by: Wolfram Sang Acked-by: Rob Herring Signed-off-by: Wolfram Sang --- drivers/of/base.c | 26 ++++++++++++++++++++++++++ include/linux/of.h | 6 ++++++ 2 files changed, 32 insertions(+) (limited to 'include') diff --git a/drivers/of/base.c b/drivers/of/base.c index 0a8aeb8523fe..63cba04aacf6 100644 --- a/drivers/of/base.c +++ b/drivers/of/base.c @@ -1958,6 +1958,32 @@ int of_alias_get_id(struct device_node *np, const char *stem) } EXPORT_SYMBOL_GPL(of_alias_get_id); +/** + * of_alias_get_highest_id - Get highest alias id for the given stem + * @stem: Alias stem to be examined + * + * The function travels the lookup table to get the highest alias id for the + * given alias stem. It returns the alias id if found. + */ +int of_alias_get_highest_id(const char *stem) +{ + struct alias_prop *app; + int id = -ENODEV; + + mutex_lock(&of_mutex); + list_for_each_entry(app, &aliases_lookup, link) { + if (strcmp(app->stem, stem) != 0) + continue; + + if (app->id > id) + id = app->id; + } + mutex_unlock(&of_mutex); + + return id; +} +EXPORT_SYMBOL_GPL(of_alias_get_highest_id); + const __be32 *of_prop_next_u32(struct property *prop, const __be32 *cur, u32 *pu) { diff --git a/include/linux/of.h b/include/linux/of.h index dfde07e77a63..9bfcc18ceab3 100644 --- a/include/linux/of.h +++ b/include/linux/of.h @@ -332,6 +332,7 @@ extern int of_count_phandle_with_args(const struct device_node *np, extern void of_alias_scan(void * (*dt_alloc)(u64 size, u64 align)); extern int of_alias_get_id(struct device_node *np, const char *stem); +extern int of_alias_get_highest_id(const char *stem); extern int of_machine_is_compatible(const char *compat); @@ -594,6 +595,11 @@ static inline int of_alias_get_id(struct device_node *np, const char *stem) return -ENOSYS; } +static inline int of_alias_get_highest_id(const char *stem) +{ + return -ENOSYS; +} + static inline int of_machine_is_compatible(const char *compat) { return 0; -- cgit v1.2.3 From 431d452af13720463dda498999b2e9a08729c03a Mon Sep 17 00:00:00 2001 From: Zhonghui Fu Date: Wed, 18 Mar 2015 15:54:27 +0100 Subject: PM / sleep: add pm-trace support for suspending phase Occasionally, the system can't come back up after suspend/resume due to problems of device suspending phase. This patch make PM_TRACE infrastructure cover device suspending phase of suspend/resume process, and the information in RTC can tell developers which device suspending function make system hang. Signed-off-by: Zhonghui Fu Signed-off-by: Rafael J. Wysocki --- arch/x86/include/asm/pm-trace.h | 23 +++++++++++++++++++++++ arch/x86/include/asm/resume-trace.h | 21 --------------------- drivers/base/power/main.c | 20 ++++++++++++++++---- drivers/base/power/trace.c | 6 +++--- include/linux/pm-trace.h | 35 +++++++++++++++++++++++++++++++++++ include/linux/resume-trace.h | 34 ---------------------------------- kernel/power/main.c | 2 +- 7 files changed, 78 insertions(+), 63 deletions(-) create mode 100644 arch/x86/include/asm/pm-trace.h delete mode 100644 arch/x86/include/asm/resume-trace.h create mode 100644 include/linux/pm-trace.h delete mode 100644 include/linux/resume-trace.h (limited to 'include') diff --git a/arch/x86/include/asm/pm-trace.h b/arch/x86/include/asm/pm-trace.h new file mode 100644 index 000000000000..7b7ac42c3661 --- /dev/null +++ b/arch/x86/include/asm/pm-trace.h @@ -0,0 +1,23 @@ +#ifndef _ASM_X86_PM_TRACE_H +#define _ASM_X86_PM_TRACE_H + +#include + +#define TRACE_RESUME(user) \ +do { \ + if (pm_trace_enabled) { \ + const void *tracedata; \ + asm volatile(_ASM_MOV " $1f,%0\n" \ + ".section .tracedata,\"a\"\n" \ + "1:\t.word %c1\n\t" \ + _ASM_PTR " %c2\n" \ + ".previous" \ + :"=r" (tracedata) \ + : "i" (__LINE__), "i" (__FILE__)); \ + generate_pm_trace(tracedata, user); \ + } \ +} while (0) + +#define TRACE_SUSPEND(user) TRACE_RESUME(user) + +#endif /* _ASM_X86_PM_TRACE_H */ diff --git a/arch/x86/include/asm/resume-trace.h b/arch/x86/include/asm/resume-trace.h deleted file mode 100644 index 3ff1c2cb1da5..000000000000 --- a/arch/x86/include/asm/resume-trace.h +++ /dev/null @@ -1,21 +0,0 @@ -#ifndef _ASM_X86_RESUME_TRACE_H -#define _ASM_X86_RESUME_TRACE_H - -#include - -#define TRACE_RESUME(user) \ -do { \ - if (pm_trace_enabled) { \ - const void *tracedata; \ - asm volatile(_ASM_MOV " $1f,%0\n" \ - ".section .tracedata,\"a\"\n" \ - "1:\t.word %c1\n\t" \ - _ASM_PTR " %c2\n" \ - ".previous" \ - :"=r" (tracedata) \ - : "i" (__LINE__), "i" (__FILE__)); \ - generate_resume_trace(tracedata, user); \ - } \ -} while (0) - -#endif /* _ASM_X86_RESUME_TRACE_H */ diff --git a/drivers/base/power/main.c b/drivers/base/power/main.c index 9717d5f20139..3d874eca7104 100644 --- a/drivers/base/power/main.c +++ b/drivers/base/power/main.c @@ -23,7 +23,7 @@ #include #include #include -#include +#include #include #include #include @@ -1017,6 +1017,9 @@ static int __device_suspend_noirq(struct device *dev, pm_message_t state, bool a char *info = NULL; int error = 0; + TRACE_DEVICE(dev); + TRACE_SUSPEND(0); + if (async_error) goto Complete; @@ -1057,6 +1060,7 @@ static int __device_suspend_noirq(struct device *dev, pm_message_t state, bool a Complete: complete_all(&dev->power.completion); + TRACE_SUSPEND(error); return error; } @@ -1078,7 +1082,7 @@ static int device_suspend_noirq(struct device *dev) { reinit_completion(&dev->power.completion); - if (pm_async_enabled && dev->power.async_suspend) { + if (is_async(dev)) { get_device(dev); async_schedule(async_suspend_noirq, dev); return 0; @@ -1157,6 +1161,9 @@ static int __device_suspend_late(struct device *dev, pm_message_t state, bool as char *info = NULL; int error = 0; + TRACE_DEVICE(dev); + TRACE_SUSPEND(0); + __pm_runtime_disable(dev, false); if (async_error) @@ -1198,6 +1205,7 @@ static int __device_suspend_late(struct device *dev, pm_message_t state, bool as async_error = error; Complete: + TRACE_SUSPEND(error); complete_all(&dev->power.completion); return error; } @@ -1219,7 +1227,7 @@ static int device_suspend_late(struct device *dev) { reinit_completion(&dev->power.completion); - if (pm_async_enabled && dev->power.async_suspend) { + if (is_async(dev)) { get_device(dev); async_schedule(async_suspend_late, dev); return 0; @@ -1338,6 +1346,9 @@ static int __device_suspend(struct device *dev, pm_message_t state, bool async) int error = 0; DECLARE_DPM_WATCHDOG_ON_STACK(wd); + TRACE_DEVICE(dev); + TRACE_SUSPEND(0); + dpm_wait_for_children(dev, async); if (async_error) @@ -1444,6 +1455,7 @@ static int __device_suspend(struct device *dev, pm_message_t state, bool async) if (error) async_error = error; + TRACE_SUSPEND(error); return error; } @@ -1465,7 +1477,7 @@ static int device_suspend(struct device *dev) { reinit_completion(&dev->power.completion); - if (pm_async_enabled && dev->power.async_suspend) { + if (is_async(dev)) { get_device(dev); async_schedule(async_suspend, dev); return 0; diff --git a/drivers/base/power/trace.c b/drivers/base/power/trace.c index d94a1f5121cf..a311cfa4c5bd 100644 --- a/drivers/base/power/trace.c +++ b/drivers/base/power/trace.c @@ -7,7 +7,7 @@ * devices may be working. */ -#include +#include #include #include @@ -154,7 +154,7 @@ EXPORT_SYMBOL(set_trace_device); * it's not any guarantee, but it's a high _likelihood_ that * the match is valid). */ -void generate_resume_trace(const void *tracedata, unsigned int user) +void generate_pm_trace(const void *tracedata, unsigned int user) { unsigned short lineno = *(unsigned short *)tracedata; const char *file = *(const char **)(tracedata + 2); @@ -164,7 +164,7 @@ void generate_resume_trace(const void *tracedata, unsigned int user) file_hash_value = hash_string(lineno, file, FILEHASH); set_magic_time(user_hash_value, file_hash_value, dev_hash_value); } -EXPORT_SYMBOL(generate_resume_trace); +EXPORT_SYMBOL(generate_pm_trace); extern char __tracedata_start, __tracedata_end; static int show_file_hash(unsigned int value) diff --git a/include/linux/pm-trace.h b/include/linux/pm-trace.h new file mode 100644 index 000000000000..ecbde7a5548e --- /dev/null +++ b/include/linux/pm-trace.h @@ -0,0 +1,35 @@ +#ifndef PM_TRACE_H +#define PM_TRACE_H + +#ifdef CONFIG_PM_TRACE +#include +#include + +extern int pm_trace_enabled; + +static inline int pm_trace_is_enabled(void) +{ + return pm_trace_enabled; +} + +struct device; +extern void set_trace_device(struct device *); +extern void generate_pm_trace(const void *tracedata, unsigned int user); +extern int show_trace_dev_match(char *buf, size_t size); + +#define TRACE_DEVICE(dev) do { \ + if (pm_trace_enabled) \ + set_trace_device(dev); \ + } while(0) + +#else + +static inline int pm_trace_is_enabled(void) { return 0; } + +#define TRACE_DEVICE(dev) do { } while (0) +#define TRACE_RESUME(dev) do { } while (0) +#define TRACE_SUSPEND(dev) do { } while (0) + +#endif + +#endif diff --git a/include/linux/resume-trace.h b/include/linux/resume-trace.h deleted file mode 100644 index f31db2368782..000000000000 --- a/include/linux/resume-trace.h +++ /dev/null @@ -1,34 +0,0 @@ -#ifndef RESUME_TRACE_H -#define RESUME_TRACE_H - -#ifdef CONFIG_PM_TRACE -#include -#include - -extern int pm_trace_enabled; - -static inline int pm_trace_is_enabled(void) -{ - return pm_trace_enabled; -} - -struct device; -extern void set_trace_device(struct device *); -extern void generate_resume_trace(const void *tracedata, unsigned int user); -extern int show_trace_dev_match(char *buf, size_t size); - -#define TRACE_DEVICE(dev) do { \ - if (pm_trace_enabled) \ - set_trace_device(dev); \ - } while(0) - -#else - -static inline int pm_trace_is_enabled(void) { return 0; } - -#define TRACE_DEVICE(dev) do { } while (0) -#define TRACE_RESUME(dev) do { } while (0) - -#endif - -#endif diff --git a/kernel/power/main.c b/kernel/power/main.c index 9a59d042ea84..86e8157a450f 100644 --- a/kernel/power/main.c +++ b/kernel/power/main.c @@ -11,7 +11,7 @@ #include #include #include -#include +#include #include #include #include -- cgit v1.2.3 From 6aebd940840a4d3a0a8ffc5883d3892f4bd61e90 Mon Sep 17 00:00:00 2001 From: Herbert Xu Date: Wed, 18 Mar 2015 20:01:15 +1100 Subject: rhashtable: Remove shift from bucket_table Keeping both size and shift is silly. We only need one. Signed-off-by: Herbert Xu Signed-off-by: David S. Miller --- include/linux/rhashtable.h | 2 -- lib/rhashtable.c | 5 ++--- 2 files changed, 2 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/include/linux/rhashtable.h b/include/linux/rhashtable.h index 1695378b3c5b..f16e85692959 100644 --- a/include/linux/rhashtable.h +++ b/include/linux/rhashtable.h @@ -51,7 +51,6 @@ struct rhash_head { * @size: Number of hash buckets * @rehash: Current bucket being rehashed * @hash_rnd: Random seed to fold into hash - * @shift: Current size (1 << shift) * @locks_mask: Mask to apply before accessing locks[] * @locks: Array of spinlocks protecting individual buckets * @walkers: List of active walkers @@ -63,7 +62,6 @@ struct bucket_table { unsigned int size; unsigned int rehash; u32 hash_rnd; - u32 shift; unsigned int locks_mask; spinlock_t *locks; struct list_head walkers; diff --git a/lib/rhashtable.c b/lib/rhashtable.c index 09a7ada89ade..097400362467 100644 --- a/lib/rhashtable.c +++ b/lib/rhashtable.c @@ -162,7 +162,6 @@ static struct bucket_table *bucket_table_alloc(struct rhashtable *ht, return NULL; tbl->size = nbuckets; - tbl->shift = ilog2(nbuckets); if (alloc_bucket_locks(ht, tbl) < 0) { bucket_table_free(tbl); @@ -189,7 +188,7 @@ static bool rht_grow_above_75(const struct rhashtable *ht, { /* Expand table when exceeding 75% load */ return atomic_read(&ht->nelems) > (tbl->size / 4 * 3) && - (!ht->p.max_shift || tbl->shift < ht->p.max_shift); + (!ht->p.max_shift || tbl->size < (1 << ht->p.max_shift)); } /** @@ -202,7 +201,7 @@ static bool rht_shrink_below_30(const struct rhashtable *ht, { /* Shrink table beneath 30% load */ return atomic_read(&ht->nelems) < (tbl->size * 3 / 10) && - tbl->shift > ht->p.min_shift; + tbl->size > (1 << ht->p.min_shift); } static int rhashtable_rehash_one(struct rhashtable *ht, unsigned old_hash) -- cgit v1.2.3 From c2e213cff701fce71a0aba8de82f2c2a4acf52ae Mon Sep 17 00:00:00 2001 From: Herbert Xu Date: Wed, 18 Mar 2015 20:01:16 +1100 Subject: rhashtable: Introduce max_size/min_size This patch adds the parameters max_size and min_size which are meant to replace max_shift and min_shift. Signed-off-by: Herbert Xu Signed-off-by: David S. Miller --- include/linux/rhashtable.h | 4 ++++ lib/rhashtable.c | 12 ++++++++---- 2 files changed, 12 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/include/linux/rhashtable.h b/include/linux/rhashtable.h index f16e85692959..81267fef85d7 100644 --- a/include/linux/rhashtable.h +++ b/include/linux/rhashtable.h @@ -85,6 +85,8 @@ struct rhashtable; * @head_offset: Offset of rhash_head in struct to be hashed * @max_shift: Maximum number of shifts while expanding * @min_shift: Minimum number of shifts while shrinking + * @max_size: Maximum size while expanding + * @min_size: Minimum size while shrinking * @nulls_base: Base value to generate nulls marker * @locks_mul: Number of bucket locks to allocate per cpu (default: 128) * @hashfn: Function to hash key @@ -97,6 +99,8 @@ struct rhashtable_params { size_t head_offset; size_t max_shift; size_t min_shift; + unsigned int max_size; + unsigned int min_size; u32 nulls_base; size_t locks_mul; rht_hashfn_t hashfn; diff --git a/lib/rhashtable.c b/lib/rhashtable.c index 097400362467..c4061bbd0113 100644 --- a/lib/rhashtable.c +++ b/lib/rhashtable.c @@ -27,7 +27,7 @@ #include #define HASH_DEFAULT_SIZE 64UL -#define HASH_MIN_SIZE 4UL +#define HASH_MIN_SIZE 4U #define BUCKET_LOCKS_PER_CPU 128UL /* Base bits plus 1 bit for nulls marker */ @@ -188,7 +188,8 @@ static bool rht_grow_above_75(const struct rhashtable *ht, { /* Expand table when exceeding 75% load */ return atomic_read(&ht->nelems) > (tbl->size / 4 * 3) && - (!ht->p.max_shift || tbl->size < (1 << ht->p.max_shift)); + (!ht->p.max_shift || tbl->size < (1 << ht->p.max_shift)) && + (!ht->p.max_size || tbl->size < ht->p.max_size); } /** @@ -201,7 +202,8 @@ static bool rht_shrink_below_30(const struct rhashtable *ht, { /* Shrink table beneath 30% load */ return atomic_read(&ht->nelems) < (tbl->size * 3 / 10) && - tbl->size > (1 << ht->p.min_shift); + tbl->size > (1 << ht->p.min_shift) && + tbl->size > ht->p.min_size; } static int rhashtable_rehash_one(struct rhashtable *ht, unsigned old_hash) @@ -873,7 +875,8 @@ EXPORT_SYMBOL_GPL(rhashtable_walk_stop); static size_t rounded_hashtable_size(struct rhashtable_params *params) { return max(roundup_pow_of_two(params->nelem_hint * 4 / 3), - 1UL << params->min_shift); + max(1UL << params->min_shift, + (unsigned long)params->min_size)); } /** @@ -935,6 +938,7 @@ int rhashtable_init(struct rhashtable *ht, struct rhashtable_params *params) params->min_shift = max_t(size_t, params->min_shift, ilog2(HASH_MIN_SIZE)); + params->min_size = max(params->min_size, HASH_MIN_SIZE); if (params->nelem_hint) size = rounded_hashtable_size(params); -- cgit v1.2.3 From e2e21c1c5808e5dfd88d3606cd6386cf85f6f5b1 Mon Sep 17 00:00:00 2001 From: Herbert Xu Date: Wed, 18 Mar 2015 20:01:21 +1100 Subject: rhashtable: Remove max_shift and min_shift Now that nobody uses max_shift and min_shift, we can safely remove them. Signed-off-by: Herbert Xu Signed-off-by: David S. Miller --- include/linux/rhashtable.h | 4 ---- lib/rhashtable.c | 7 +------ 2 files changed, 1 insertion(+), 10 deletions(-) (limited to 'include') diff --git a/include/linux/rhashtable.h b/include/linux/rhashtable.h index 81267fef85d7..99425f2be708 100644 --- a/include/linux/rhashtable.h +++ b/include/linux/rhashtable.h @@ -83,8 +83,6 @@ struct rhashtable; * @key_len: Length of key * @key_offset: Offset of key in struct to be hashed * @head_offset: Offset of rhash_head in struct to be hashed - * @max_shift: Maximum number of shifts while expanding - * @min_shift: Minimum number of shifts while shrinking * @max_size: Maximum size while expanding * @min_size: Minimum size while shrinking * @nulls_base: Base value to generate nulls marker @@ -97,8 +95,6 @@ struct rhashtable_params { size_t key_len; size_t key_offset; size_t head_offset; - size_t max_shift; - size_t min_shift; unsigned int max_size; unsigned int min_size; u32 nulls_base; diff --git a/lib/rhashtable.c b/lib/rhashtable.c index c4061bbd0113..5f8fe3e88219 100644 --- a/lib/rhashtable.c +++ b/lib/rhashtable.c @@ -188,7 +188,6 @@ static bool rht_grow_above_75(const struct rhashtable *ht, { /* Expand table when exceeding 75% load */ return atomic_read(&ht->nelems) > (tbl->size / 4 * 3) && - (!ht->p.max_shift || tbl->size < (1 << ht->p.max_shift)) && (!ht->p.max_size || tbl->size < ht->p.max_size); } @@ -202,7 +201,6 @@ static bool rht_shrink_below_30(const struct rhashtable *ht, { /* Shrink table beneath 30% load */ return atomic_read(&ht->nelems) < (tbl->size * 3 / 10) && - tbl->size > (1 << ht->p.min_shift) && tbl->size > ht->p.min_size; } @@ -875,8 +873,7 @@ EXPORT_SYMBOL_GPL(rhashtable_walk_stop); static size_t rounded_hashtable_size(struct rhashtable_params *params) { return max(roundup_pow_of_two(params->nelem_hint * 4 / 3), - max(1UL << params->min_shift, - (unsigned long)params->min_size)); + (unsigned long)params->min_size); } /** @@ -936,8 +933,6 @@ int rhashtable_init(struct rhashtable *ht, struct rhashtable_params *params) if (params->nulls_base && params->nulls_base < (1U << RHT_BASE_SHIFT)) return -EINVAL; - params->min_shift = max_t(size_t, params->min_shift, - ilog2(HASH_MIN_SIZE)); params->min_size = max(params->min_size, HASH_MIN_SIZE); if (params->nelem_hint) -- cgit v1.2.3 From f0b7d43c8a28155f50adb087a563cfc97566e477 Mon Sep 17 00:00:00 2001 From: Brad Campbell Date: Tue, 17 Mar 2015 16:25:46 -0400 Subject: cc2520: Add support for CC2591 amplifier. The TI CC2521 is an RF power amplifier that is designed to interface with the CC2520. Conveniently, it directly interfaces with the CC2520 and does not require any pins to be connected to a microcontroller/processor. Adding a CC2591 increases the CC2520's range, which is useful for border router and other wall-powered applications. Using the CC2591 with the CC2520 requires configuring the CC2520 GPIOs that are connected to the CC2591 to correctly set the CC2591 into TX and RX modes. Further, TI recommends that the CC2520_TXPOWER and CC2520_AGCCTRL1 registers are set differently to maximize the CC2591's performance. These settings are covered in TI Application Note AN065. This patch adds an optional `amplified` field to the cc2520 entry in the device tree. If present, the CC2520 will be configured to operate with a CC2591. The expected pin mapping is: CC2520 GPIO0 --> CC2591 EN CC2520 GPIO5 --> CC2591 PAEN Signed-off-by: Brad Campbell Acked-by: Varka Bhadram Signed-off-by: Marcel Holtmann --- .../devicetree/bindings/net/ieee802154/cc2520.txt | 4 ++ drivers/net/ieee802154/cc2520.c | 55 ++++++++++++++++++---- include/linux/spi/cc2520.h | 1 + 3 files changed, 52 insertions(+), 8 deletions(-) (limited to 'include') diff --git a/Documentation/devicetree/bindings/net/ieee802154/cc2520.txt b/Documentation/devicetree/bindings/net/ieee802154/cc2520.txt index 0071883c08d8..fb6d49f184ed 100644 --- a/Documentation/devicetree/bindings/net/ieee802154/cc2520.txt +++ b/Documentation/devicetree/bindings/net/ieee802154/cc2520.txt @@ -13,11 +13,15 @@ Required properties: - cca-gpio: GPIO spec for the CCA pin - vreg-gpio: GPIO spec for the VREG pin - reset-gpio: GPIO spec for the RESET pin +Optional properties: + - amplified: include if the CC2520 is connected to a CC2591 amplifier + Example: cc2520@0 { compatible = "ti,cc2520"; reg = <0>; spi-max-frequency = <4000000>; + amplified; pinctrl-names = "default"; pinctrl-0 = <&cc2520_cape_pins>; fifo-gpio = <&gpio1 18 0>; diff --git a/drivers/net/ieee802154/cc2520.c b/drivers/net/ieee802154/cc2520.c index 233b6c6017d4..f833b8bb6663 100644 --- a/drivers/net/ieee802154/cc2520.c +++ b/drivers/net/ieee802154/cc2520.c @@ -738,6 +738,8 @@ static int cc2520_get_platform_data(struct spi_device *spi, pdata->vreg = of_get_named_gpio(np, "vreg-gpio", 0); pdata->reset = of_get_named_gpio(np, "reset-gpio", 0); + pdata->amplified = of_property_read_bool(np, "amplified"); + return 0; } @@ -746,6 +748,11 @@ static int cc2520_hw_init(struct cc2520_private *priv) u8 status = 0, state = 0xff; int ret; int timeout = 100; + struct cc2520_platform_data pdata; + + ret = cc2520_get_platform_data(priv->spi, &pdata); + if (ret) + goto err_ret; ret = cc2520_read_register(priv, CC2520_FSMSTAT1, &state); if (ret) @@ -768,11 +775,47 @@ static int cc2520_hw_init(struct cc2520_private *priv) dev_vdbg(&priv->spi->dev, "oscillator brought up\n"); - /* Registers default value: section 28.1 in Datasheet */ - ret = cc2520_write_register(priv, CC2520_TXPOWER, 0xF7); - if (ret) - goto err_ret; + /* If the CC2520 is connected to a CC2591 amplifier, we must both + * configure GPIOs on the CC2520 to correctly configure the CC2591 + * and change a couple settings of the CC2520 to work with the + * amplifier. See section 8 page 17 of TI application note AN065. + * http://www.ti.com/lit/an/swra229a/swra229a.pdf + */ + if (pdata.amplified) { + ret = cc2520_write_register(priv, CC2520_TXPOWER, 0xF9); + if (ret) + goto err_ret; + ret = cc2520_write_register(priv, CC2520_AGCCTRL1, 0x16); + if (ret) + goto err_ret; + + ret = cc2520_write_register(priv, CC2520_GPIOCTRL0, 0x46); + if (ret) + goto err_ret; + + ret = cc2520_write_register(priv, CC2520_GPIOCTRL5, 0x47); + if (ret) + goto err_ret; + + ret = cc2520_write_register(priv, CC2520_GPIOPOLARITY, 0x1e); + if (ret) + goto err_ret; + + ret = cc2520_write_register(priv, CC2520_TXCTRL, 0xc1); + if (ret) + goto err_ret; + } else { + ret = cc2520_write_register(priv, CC2520_TXPOWER, 0xF7); + if (ret) + goto err_ret; + + ret = cc2520_write_register(priv, CC2520_AGCCTRL1, 0x11); + if (ret) + goto err_ret; + } + + /* Registers default value: section 28.1 in Datasheet */ ret = cc2520_write_register(priv, CC2520_CCACTRL0, 0x1A); if (ret) goto err_ret; @@ -797,10 +840,6 @@ static int cc2520_hw_init(struct cc2520_private *priv) if (ret) goto err_ret; - ret = cc2520_write_register(priv, CC2520_AGCCTRL1, 0x11); - if (ret) - goto err_ret; - ret = cc2520_write_register(priv, CC2520_ADCTEST0, 0x10); if (ret) goto err_ret; diff --git a/include/linux/spi/cc2520.h b/include/linux/spi/cc2520.h index 85b8ee67e937..e741e8baad92 100644 --- a/include/linux/spi/cc2520.h +++ b/include/linux/spi/cc2520.h @@ -21,6 +21,7 @@ struct cc2520_platform_data { int sfd; int reset; int vreg; + bool amplified; }; #endif -- cgit v1.2.3 From 822b3b2ebfff8e9b3d006086c527738a7ca00cd0 Mon Sep 17 00:00:00 2001 From: John Fastabend Date: Wed, 18 Mar 2015 14:57:33 +0200 Subject: net: Add max rate tx queue attribute This adds a tx_maxrate attribute to the tx queue sysfs entry allowing for max-rate limiting. Along with DCB-ETS and BQL this provides another knob to tune queue performance. The limit units are Mbps. By default it is disabled. To disable the rate limitation after it has been set for a queue, it should be set to zero. Signed-off-by: John Fastabend Signed-off-by: Or Gerlitz Signed-off-by: David S. Miller --- Documentation/ABI/testing/sysfs-class-net-queues | 8 +++ Documentation/networking/scaling.txt | 9 ++++ include/linux/netdevice.h | 8 +++ net/core/net-sysfs.c | 67 +++++++++++++++++++----- 4 files changed, 80 insertions(+), 12 deletions(-) (limited to 'include') diff --git a/Documentation/ABI/testing/sysfs-class-net-queues b/Documentation/ABI/testing/sysfs-class-net-queues index 5e9aeb91d355..0c0df91b1516 100644 --- a/Documentation/ABI/testing/sysfs-class-net-queues +++ b/Documentation/ABI/testing/sysfs-class-net-queues @@ -24,6 +24,14 @@ Description: Indicates the number of transmit timeout events seen by this network interface transmit queue. +What: /sys/class//queues/tx-/tx_maxrate +Date: March 2015 +KernelVersion: 4.1 +Contact: netdev@vger.kernel.org +Description: + A Mbps max-rate set for the queue, a value of zero means disabled, + default is disabled. + What: /sys/class//queues/tx-/xps_cpus Date: November 2010 KernelVersion: 2.6.38 diff --git a/Documentation/networking/scaling.txt b/Documentation/networking/scaling.txt index 99ca40e8e810..cbfac0949635 100644 --- a/Documentation/networking/scaling.txt +++ b/Documentation/networking/scaling.txt @@ -421,6 +421,15 @@ best CPUs to share a given queue are probably those that share the cache with the CPU that processes transmit completions for that queue (transmit interrupts). +Per TX Queue rate limitation: +============================= + +These are rate-limitation mechanisms implemented by HW, where currently +a max-rate attribute is supported, by setting a Mbps value to + +/sys/class/net//queues/tx-/tx_maxrate + +A value of zero means disabled, and this is the default. Further Information =================== diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index dd1d069758be..76c5de4978a8 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -587,6 +587,7 @@ struct netdev_queue { #ifdef CONFIG_BQL struct dql dql; #endif + unsigned long tx_maxrate; } ____cacheline_aligned_in_smp; static inline int netdev_queue_numa_node_read(const struct netdev_queue *q) @@ -1022,6 +1023,10 @@ typedef u16 (*select_queue_fallback_t)(struct net_device *dev, * be otherwise expressed by feature flags. The check is called with * the set of features that the stack has calculated and it returns * those the driver believes to be appropriate. + * int (*ndo_set_tx_maxrate)(struct net_device *dev, + * int queue_index, u32 maxrate); + * Called when a user wants to set a max-rate limitation of specific + * TX queue. */ struct net_device_ops { int (*ndo_init)(struct net_device *dev); @@ -1178,6 +1183,9 @@ struct net_device_ops { netdev_features_t (*ndo_features_check) (struct sk_buff *skb, struct net_device *dev, netdev_features_t features); + int (*ndo_set_tx_maxrate)(struct net_device *dev, + int queue_index, + u32 maxrate); }; /** diff --git a/net/core/net-sysfs.c b/net/core/net-sysfs.c index cf30620a88e1..7e58bd7ec232 100644 --- a/net/core/net-sysfs.c +++ b/net/core/net-sysfs.c @@ -951,6 +951,60 @@ static ssize_t show_trans_timeout(struct netdev_queue *queue, return sprintf(buf, "%lu", trans_timeout); } +#ifdef CONFIG_XPS +static inline unsigned int get_netdev_queue_index(struct netdev_queue *queue) +{ + struct net_device *dev = queue->dev; + int i; + + for (i = 0; i < dev->num_tx_queues; i++) + if (queue == &dev->_tx[i]) + break; + + BUG_ON(i >= dev->num_tx_queues); + + return i; +} + +static ssize_t show_tx_maxrate(struct netdev_queue *queue, + struct netdev_queue_attribute *attribute, + char *buf) +{ + return sprintf(buf, "%lu\n", queue->tx_maxrate); +} + +static ssize_t set_tx_maxrate(struct netdev_queue *queue, + struct netdev_queue_attribute *attribute, + const char *buf, size_t len) +{ + struct net_device *dev = queue->dev; + int err, index = get_netdev_queue_index(queue); + u32 rate = 0; + + err = kstrtou32(buf, 10, &rate); + if (err < 0) + return err; + + if (!rtnl_trylock()) + return restart_syscall(); + + err = -EOPNOTSUPP; + if (dev->netdev_ops->ndo_set_tx_maxrate) + err = dev->netdev_ops->ndo_set_tx_maxrate(dev, index, rate); + + rtnl_unlock(); + if (!err) { + queue->tx_maxrate = rate; + return len; + } + return err; +} + +static struct netdev_queue_attribute queue_tx_maxrate = + __ATTR(tx_maxrate, S_IRUGO | S_IWUSR, + show_tx_maxrate, set_tx_maxrate); +#endif + static struct netdev_queue_attribute queue_trans_timeout = __ATTR(tx_timeout, S_IRUGO, show_trans_timeout, NULL); @@ -1065,18 +1119,6 @@ static struct attribute_group dql_group = { #endif /* CONFIG_BQL */ #ifdef CONFIG_XPS -static unsigned int get_netdev_queue_index(struct netdev_queue *queue) -{ - struct net_device *dev = queue->dev; - unsigned int i; - - i = queue - dev->_tx; - BUG_ON(i >= dev->num_tx_queues); - - return i; -} - - static ssize_t show_xps_map(struct netdev_queue *queue, struct netdev_queue_attribute *attribute, char *buf) { @@ -1153,6 +1195,7 @@ static struct attribute *netdev_queue_default_attrs[] = { &queue_trans_timeout.attr, #ifdef CONFIG_XPS &xps_cpus_attribute.attr, + &queue_tx_maxrate.attr, #endif NULL }; -- cgit v1.2.3 From fc31e2560a2443410fe45c27116fae736541a7b5 Mon Sep 17 00:00:00 2001 From: Or Gerlitz Date: Wed, 18 Mar 2015 14:57:34 +0200 Subject: net/mlx4_core: Add basic support for QP max-rate limiting Add the low-level device commands and definitions used for QP max-rate limiting. This is done through the following elements: - read rate-limit device caps in QUERY_DEV_CAP: number of different rates and the min/max rates in Kbs/Mbs/Gbs units - enhance the QP context struct to contain rate limit units and value - allow to do run time rate-limit setting to QPs through the update-qp firmware command - QP rate-limiting is disallowed for VFs Signed-off-by: Or Gerlitz Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlx4/fw.c | 33 +++++++++++++++++++++- drivers/net/ethernet/mellanox/mlx4/fw.h | 1 + drivers/net/ethernet/mellanox/mlx4/main.c | 2 ++ drivers/net/ethernet/mellanox/mlx4/qp.c | 5 ++++ .../net/ethernet/mellanox/mlx4/resource_tracker.c | 6 +++- include/linux/mlx4/device.h | 17 +++++++++++ include/linux/mlx4/qp.h | 14 ++++++--- 7 files changed, 72 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/drivers/net/ethernet/mellanox/mlx4/fw.c b/drivers/net/ethernet/mellanox/mlx4/fw.c index 242bcee5d774..4a471f5d1b56 100644 --- a/drivers/net/ethernet/mellanox/mlx4/fw.c +++ b/drivers/net/ethernet/mellanox/mlx4/fw.c @@ -144,7 +144,8 @@ static void dump_dev_cap_flags2(struct mlx4_dev *dev, u64 flags) [19] = "Performance optimized for limited rule configuration flow steering support", [20] = "Recoverable error events support", [21] = "Port Remap support", - [22] = "QCN support" + [22] = "QCN support", + [23] = "QP rate limiting support" }; int i; @@ -697,6 +698,10 @@ int mlx4_QUERY_DEV_CAP(struct mlx4_dev *dev, struct mlx4_dev_cap *dev_cap) #define QUERY_DEV_CAP_MAD_DEMUX_OFFSET 0xb0 #define QUERY_DEV_CAP_DMFS_HIGH_RATE_QPN_BASE_OFFSET 0xa8 #define QUERY_DEV_CAP_DMFS_HIGH_RATE_QPN_RANGE_OFFSET 0xac +#define QUERY_DEV_CAP_QP_RATE_LIMIT_NUM_OFFSET 0xcc +#define QUERY_DEV_CAP_QP_RATE_LIMIT_MAX_OFFSET 0xd0 +#define QUERY_DEV_CAP_QP_RATE_LIMIT_MIN_OFFSET 0xd2 + dev_cap->flags2 = 0; mailbox = mlx4_alloc_cmd_mailbox(dev); @@ -904,6 +909,18 @@ int mlx4_QUERY_DEV_CAP(struct mlx4_dev *dev, struct mlx4_dev_cap *dev_cap) QUERY_DEV_CAP_DMFS_HIGH_RATE_QPN_RANGE_OFFSET); dev_cap->dmfs_high_rate_qpn_range &= MGM_QPN_MASK; + MLX4_GET(size, outbox, QUERY_DEV_CAP_QP_RATE_LIMIT_NUM_OFFSET); + dev_cap->rl_caps.num_rates = size; + if (dev_cap->rl_caps.num_rates) { + dev_cap->flags2 |= MLX4_DEV_CAP_FLAG2_QP_RATE_LIMIT; + MLX4_GET(size, outbox, QUERY_DEV_CAP_QP_RATE_LIMIT_MAX_OFFSET); + dev_cap->rl_caps.max_val = size & 0xfff; + dev_cap->rl_caps.max_unit = size >> 14; + MLX4_GET(size, outbox, QUERY_DEV_CAP_QP_RATE_LIMIT_MIN_OFFSET); + dev_cap->rl_caps.min_val = size & 0xfff; + dev_cap->rl_caps.min_unit = size >> 14; + } + MLX4_GET(field32, outbox, QUERY_DEV_CAP_EXT_2_FLAGS_OFFSET); if (field32 & (1 << 16)) dev_cap->flags2 |= MLX4_DEV_CAP_FLAG2_UPDATE_QP; @@ -979,6 +996,15 @@ void mlx4_dev_cap_dump(struct mlx4_dev *dev, struct mlx4_dev_cap *dev_cap) dev_cap->dmfs_high_rate_qpn_base); mlx4_dbg(dev, "DMFS high rate steer QPn range: %d\n", dev_cap->dmfs_high_rate_qpn_range); + + if (dev_cap->flags2 & MLX4_DEV_CAP_FLAG2_QP_RATE_LIMIT) { + struct mlx4_rate_limit_caps *rl_caps = &dev_cap->rl_caps; + + mlx4_dbg(dev, "QP Rate-Limit: #rates %d, unit/val max %d/%d, min %d/%d\n", + rl_caps->num_rates, rl_caps->max_unit, rl_caps->max_val, + rl_caps->min_unit, rl_caps->min_val); + } + dump_dev_cap_flags(dev, dev_cap->flags); dump_dev_cap_flags2(dev, dev_cap->flags2); } @@ -1075,6 +1101,7 @@ int mlx4_QUERY_DEV_CAP_wrapper(struct mlx4_dev *dev, int slave, u64 flags; int err = 0; u8 field; + u16 field16; u32 bmme_flags, field32; int real_port; int slave_port; @@ -1158,6 +1185,10 @@ int mlx4_QUERY_DEV_CAP_wrapper(struct mlx4_dev *dev, int slave, field &= 0xfe; MLX4_PUT(outbox->buf, field, QUERY_DEV_CAP_ECN_QCN_VER_OFFSET); + /* turn off QP max-rate limiting for guests */ + field16 = 0; + MLX4_PUT(outbox->buf, field16, QUERY_DEV_CAP_QP_RATE_LIMIT_NUM_OFFSET); + return 0; } diff --git a/drivers/net/ethernet/mellanox/mlx4/fw.h b/drivers/net/ethernet/mellanox/mlx4/fw.h index f44f7f6017ed..863655bd3947 100644 --- a/drivers/net/ethernet/mellanox/mlx4/fw.h +++ b/drivers/net/ethernet/mellanox/mlx4/fw.h @@ -127,6 +127,7 @@ struct mlx4_dev_cap { u32 max_counters; u32 dmfs_high_rate_qpn_base; u32 dmfs_high_rate_qpn_range; + struct mlx4_rate_limit_caps rl_caps; struct mlx4_port_cap port_cap[MLX4_MAX_PORTS + 1]; }; diff --git a/drivers/net/ethernet/mellanox/mlx4/main.c b/drivers/net/ethernet/mellanox/mlx4/main.c index 7e487223489a..43aa76775b5f 100644 --- a/drivers/net/ethernet/mellanox/mlx4/main.c +++ b/drivers/net/ethernet/mellanox/mlx4/main.c @@ -489,6 +489,8 @@ static int mlx4_dev_cap(struct mlx4_dev *dev, struct mlx4_dev_cap *dev_cap) dev->caps.dmfs_high_rate_qpn_range = MLX4_A0_STEERING_TABLE_SIZE; } + dev->caps.rl_caps = dev_cap->rl_caps; + dev->caps.reserved_qps_cnt[MLX4_QP_REGION_RSS_RAW_ETH] = dev->caps.dmfs_high_rate_qpn_range; diff --git a/drivers/net/ethernet/mellanox/mlx4/qp.c b/drivers/net/ethernet/mellanox/mlx4/qp.c index eda29dbbfcd2..69e4462e4ee4 100644 --- a/drivers/net/ethernet/mellanox/mlx4/qp.c +++ b/drivers/net/ethernet/mellanox/mlx4/qp.c @@ -442,6 +442,11 @@ int mlx4_update_qp(struct mlx4_dev *dev, u32 qpn, cmd->qp_context.param3 |= cpu_to_be32(MLX4_STRIP_VLAN); } + if (attr & MLX4_UPDATE_QP_RATE_LIMIT) { + qp_mask |= 1ULL << MLX4_UPD_QP_MASK_RATE_LIMIT; + cmd->qp_context.rate_limit_params = cpu_to_be16((params->rate_unit << 14) | params->rate_val); + } + cmd->primary_addr_path_mask = cpu_to_be64(pri_addr_path_mask); cmd->qp_mask = cpu_to_be64(qp_mask); diff --git a/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c b/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c index d43e25914d19..c258f8625aac 100644 --- a/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c +++ b/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c @@ -2947,8 +2947,12 @@ static int verify_qp_parameters(struct mlx4_dev *dev, qp_type = (be32_to_cpu(qp_ctx->flags) >> 16) & 0xff; optpar = be32_to_cpu(*(__be32 *) inbox->buf); - if (slave != mlx4_master_func_num(dev)) + if (slave != mlx4_master_func_num(dev)) { qp_ctx->params2 &= ~MLX4_QP_BIT_FPP; + /* setting QP rate-limit is disallowed for VFs */ + if (qp_ctx->rate_limit_params) + return -EPERM; + } switch (qp_type) { case MLX4_QP_ST_RC: diff --git a/include/linux/mlx4/device.h b/include/linux/mlx4/device.h index 1cc54822b931..4550c67b92e4 100644 --- a/include/linux/mlx4/device.h +++ b/include/linux/mlx4/device.h @@ -205,6 +205,7 @@ enum { MLX4_DEV_CAP_FLAG2_RECOVERABLE_ERROR_EVENT = 1LL << 20, MLX4_DEV_CAP_FLAG2_PORT_REMAP = 1LL << 21, MLX4_DEV_CAP_FLAG2_QCN = 1LL << 22, + MLX4_DEV_CAP_FLAG2_QP_RATE_LIMIT = 1LL << 23 }; enum { @@ -450,6 +451,21 @@ enum mlx4_module_id { MLX4_MODULE_ID_QSFP28 = 0x11, }; +enum { /* rl */ + MLX4_QP_RATE_LIMIT_NONE = 0, + MLX4_QP_RATE_LIMIT_KBS = 1, + MLX4_QP_RATE_LIMIT_MBS = 2, + MLX4_QP_RATE_LIMIT_GBS = 3 +}; + +struct mlx4_rate_limit_caps { + u16 num_rates; /* Number of different rates */ + u8 min_unit; + u16 min_val; + u8 max_unit; + u16 max_val; +}; + static inline u64 mlx4_fw_ver(u64 major, u64 minor, u64 subminor) { return (major << 32) | (minor << 16) | subminor; @@ -565,6 +581,7 @@ struct mlx4_caps { u32 dmfs_high_rate_qpn_base; u32 dmfs_high_rate_qpn_range; u32 vf_caps; + struct mlx4_rate_limit_caps rl_caps; }; struct mlx4_buf_list { diff --git a/include/linux/mlx4/qp.h b/include/linux/mlx4/qp.h index 551f85456c11..1023ebe035b7 100644 --- a/include/linux/mlx4/qp.h +++ b/include/linux/mlx4/qp.h @@ -207,14 +207,16 @@ struct mlx4_qp_context { __be32 msn; __be16 rq_wqe_counter; __be16 sq_wqe_counter; - u32 reserved3[2]; + u32 reserved3; + __be16 rate_limit_params; + __be16 reserved4; __be32 param3; __be32 nummmcpeers_basemkey; u8 log_page_size; - u8 reserved4[2]; + u8 reserved5[2]; u8 mtt_base_addr_h; __be32 mtt_base_addr_l; - u32 reserved5[10]; + u32 reserved6[10]; }; struct mlx4_update_qp_context { @@ -229,6 +231,7 @@ struct mlx4_update_qp_context { enum { MLX4_UPD_QP_MASK_PM_STATE = 32, MLX4_UPD_QP_MASK_VSD = 33, + MLX4_UPD_QP_MASK_RATE_LIMIT = 35, }; enum { @@ -428,7 +431,8 @@ struct mlx4_wqe_inline_seg { enum mlx4_update_qp_attr { MLX4_UPDATE_QP_SMAC = 1 << 0, MLX4_UPDATE_QP_VSD = 1 << 1, - MLX4_UPDATE_QP_SUPPORTED_ATTRS = (1 << 2) - 1 + MLX4_UPDATE_QP_RATE_LIMIT = 1 << 2, + MLX4_UPDATE_QP_SUPPORTED_ATTRS = (1 << 3) - 1 }; enum mlx4_update_qp_params_flags { @@ -438,6 +442,8 @@ enum mlx4_update_qp_params_flags { struct mlx4_update_qp_params { u8 smac_index; u32 flags; + u16 rate_unit; + u16 rate_val; }; int mlx4_update_qp(struct mlx4_dev *dev, u32 qpn, -- cgit v1.2.3 From 847aac644e92e5624f2c153bab409bf713d5ff9a Mon Sep 17 00:00:00 2001 From: Li Xi Date: Thu, 19 Mar 2015 04:04:53 +0900 Subject: vfs: Add general support to enforce project quota limits This patch adds support for a new quota type PRJQUOTA for project quota enforcement. Also a new method get_projid() is added into dquot_operations structure. Signed-off-by: Li Xi Signed-off-by: Dmitry Monakhov Reviewed-by: Jan Kara Signed-off-by: Jan Kara --- fs/quota/dquot.c | 20 +++++++++++++++++--- fs/quota/quotaio_v2.h | 6 ++++-- include/linux/quota.h | 2 ++ include/uapi/linux/quota.h | 6 ++++-- 4 files changed, 27 insertions(+), 7 deletions(-) (limited to 'include') diff --git a/fs/quota/dquot.c b/fs/quota/dquot.c index 327a58448592..ecc25cf0ee6e 100644 --- a/fs/quota/dquot.c +++ b/fs/quota/dquot.c @@ -1163,8 +1163,8 @@ static int need_print_warning(struct dquot_warn *warn) return uid_eq(current_fsuid(), warn->w_dq_id.uid); case GRPQUOTA: return in_group_p(warn->w_dq_id.gid); - case PRJQUOTA: /* Never taken... Just make gcc happy */ - return 0; + case PRJQUOTA: + return 1; } return 0; } @@ -1405,6 +1405,9 @@ static void __dquot_initialize(struct inode *inode, int type) /* First get references to structures we might need. */ for (cnt = 0; cnt < MAXQUOTAS; cnt++) { struct kqid qid; + kprojid_t projid; + int rc; + got[cnt] = NULL; if (type != -1 && cnt != type) continue; @@ -1415,6 +1418,10 @@ static void __dquot_initialize(struct inode *inode, int type) */ if (dquots[cnt]) continue; + + if (!sb_has_quota_active(sb, cnt)) + continue; + init_needed = 1; switch (cnt) { @@ -1424,6 +1431,12 @@ static void __dquot_initialize(struct inode *inode, int type) case GRPQUOTA: qid = make_kqid_gid(inode->i_gid); break; + case PRJQUOTA: + rc = inode->i_sb->dq_op->get_projid(inode, &projid); + if (rc) + continue; + qid = make_kqid_projid(projid); + break; } got[cnt] = dqget(sb, qid); } @@ -2176,7 +2189,8 @@ static int vfs_load_quota_inode(struct inode *inode, int type, int format_id, error = -EROFS; goto out_fmt; } - if (!sb->s_op->quota_write || !sb->s_op->quota_read) { + if (!sb->s_op->quota_write || !sb->s_op->quota_read || + (type == PRJQUOTA && sb->dq_op->get_projid == NULL)) { error = -EINVAL; goto out_fmt; } diff --git a/fs/quota/quotaio_v2.h b/fs/quota/quotaio_v2.h index f1966b42c2fd..4e95430093d9 100644 --- a/fs/quota/quotaio_v2.h +++ b/fs/quota/quotaio_v2.h @@ -13,12 +13,14 @@ */ #define V2_INITQMAGICS {\ 0xd9c01f11, /* USRQUOTA */\ - 0xd9c01927 /* GRPQUOTA */\ + 0xd9c01927, /* GRPQUOTA */\ + 0xd9c03f14, /* PRJQUOTA */\ } #define V2_INITQVERSIONS {\ 1, /* USRQUOTA */\ - 1 /* GRPQUOTA */\ + 1, /* GRPQUOTA */\ + 1, /* PRJQUOTA */\ } /* First generic header */ diff --git a/include/linux/quota.h b/include/linux/quota.h index cf910d1f8efa..b2505acfd3c0 100644 --- a/include/linux/quota.h +++ b/include/linux/quota.h @@ -50,6 +50,7 @@ #undef USRQUOTA #undef GRPQUOTA +#undef PRJQUOTA enum quota_type { USRQUOTA = 0, /* element used for user quotas */ GRPQUOTA = 1, /* element used for group quotas */ @@ -319,6 +320,7 @@ struct dquot_operations { /* get reserved quota for delayed alloc, value returned is managed by * quota code only */ qsize_t *(*get_reserved_space) (struct inode *); + int (*get_projid) (struct inode *, kprojid_t *);/* Get project ID */ }; struct path; diff --git a/include/uapi/linux/quota.h b/include/uapi/linux/quota.h index 1f49b8341c99..9c95b2c1c88a 100644 --- a/include/uapi/linux/quota.h +++ b/include/uapi/linux/quota.h @@ -36,11 +36,12 @@ #include #include -#define __DQUOT_VERSION__ "dquot_6.5.2" +#define __DQUOT_VERSION__ "dquot_6.6.0" -#define MAXQUOTAS 2 +#define MAXQUOTAS 3 #define USRQUOTA 0 /* element used for user quotas */ #define GRPQUOTA 1 /* element used for group quotas */ +#define PRJQUOTA 2 /* element used for project quotas */ /* * Definitions for the default names of the quotas files. @@ -48,6 +49,7 @@ #define INITQFNAMES { \ "user", /* USRQUOTA */ \ "group", /* GRPQUOTA */ \ + "project", /* PRJQUOTA */ \ "undefined", \ }; -- cgit v1.2.3 From 95c0fd457b03099b5ce385f3982e06d01ad42c5c Mon Sep 17 00:00:00 2001 From: Peter Huewe Date: Mon, 16 Mar 2015 21:46:30 +0100 Subject: PNP: Add helper macro for pnp_register_driver boilerplate This patch introduces the module_pnp_driver macro which is a convenience macro for PNP driver modules similar to module_pci_driver. It is intended to be used by drivers which init/exit section does nothing but register/unregister the PNP driver. By using this macro it is possible to eliminate a few lines of boilerplate code per PNP driver. Based on work done by Lars-Peter Clausen for other busses (i2c and spi) and Greg KH for PCI. Signed-off-by: Peter Huewe Signed-off-by: Rafael J. Wysocki --- include/linux/pnp.h | 12 ++++++++++++ 1 file changed, 12 insertions(+) (limited to 'include') diff --git a/include/linux/pnp.h b/include/linux/pnp.h index 6512e9cbc6d5..5df733b8f704 100644 --- a/include/linux/pnp.h +++ b/include/linux/pnp.h @@ -510,4 +510,16 @@ static inline void pnp_unregister_driver(struct pnp_driver *drv) { } #endif /* CONFIG_PNP */ +/** + * module_pnp_driver() - Helper macro for registering a PnP driver + * @__pnp_driver: pnp_driver struct + * + * Helper macro for PnP drivers which do not do anything special in module + * init/exit. This eliminates a lot of boilerplate. Each module may only + * use this macro once, and calling it replaces module_init() and module_exit() + */ +#define module_pnp_driver(__pnp_driver) \ + module_driver(__pnp_driver, pnp_register_driver, \ + pnp_unregister_driver) + #endif /* _LINUX_PNP_H */ -- cgit v1.2.3 From bf38b8710892333cec2d8069644eb36ff435fd6f Mon Sep 17 00:00:00 2001 From: Christophe Ricard Date: Sun, 8 Mar 2015 11:17:14 +0100 Subject: tpm/tpm_i2c_stm_st33: Split tpm_i2c_tpm_st33 in 2 layers (core + phy) tpm_i2c_stm_st33 is a TIS 1.2 TPM with a core interface which can be used by different phy such as i2c or spi. The core part is called st33zp24 which is also the main part reference. include/linux/platform_data/tpm_stm_st33.h is renamed consequently. The driver is also split into an i2c phy in charge of sending/receiving data as well as managing platform data or dts configuration. Acked-by: Jarkko Sakkinen Reviewed-by: Jason Gunthorpe Signed-off-by: Christophe Ricard Signed-off-by: Peter Huewe --- drivers/char/tpm/Kconfig | 11 +- drivers/char/tpm/Makefile | 2 +- drivers/char/tpm/st33zp24/Kconfig | 20 + drivers/char/tpm/st33zp24/Makefile | 9 + drivers/char/tpm/st33zp24/i2c.c | 278 +++++++++ drivers/char/tpm/st33zp24/st33zp24.c | 688 ++++++++++++++++++++++ drivers/char/tpm/st33zp24/st33zp24.h | 34 ++ drivers/char/tpm/tpm_i2c_stm_st33.c | 915 ----------------------------- include/linux/platform_data/st33zp24.h | 28 + include/linux/platform_data/tpm_stm_st33.h | 39 -- 10 files changed, 1059 insertions(+), 965 deletions(-) create mode 100644 drivers/char/tpm/st33zp24/Kconfig create mode 100644 drivers/char/tpm/st33zp24/Makefile create mode 100644 drivers/char/tpm/st33zp24/i2c.c create mode 100644 drivers/char/tpm/st33zp24/st33zp24.c create mode 100644 drivers/char/tpm/st33zp24/st33zp24.h delete mode 100644 drivers/char/tpm/tpm_i2c_stm_st33.c create mode 100644 include/linux/platform_data/st33zp24.h delete mode 100644 include/linux/platform_data/tpm_stm_st33.h (limited to 'include') diff --git a/drivers/char/tpm/Kconfig b/drivers/char/tpm/Kconfig index 9d4e37549eb2..2dc16d3b2336 100644 --- a/drivers/char/tpm/Kconfig +++ b/drivers/char/tpm/Kconfig @@ -100,16 +100,6 @@ config TCG_IBMVTPM will be accessible from within Linux. To compile this driver as a module, choose M here; the module will be called tpm_ibmvtpm. -config TCG_TIS_I2C_ST33 - tristate "TPM Interface Specification 1.2 Interface (I2C - STMicroelectronics)" - depends on I2C - depends on GPIOLIB - ---help--- - If you have a TPM security chip from STMicroelectronics working with - an I2C bus say Yes and it will be accessible from within Linux. - To compile this driver as a module, choose M here; the module will be - called tpm_i2c_stm_st33. - config TCG_XEN tristate "XEN TPM Interface" depends on TCG_TPM && XEN @@ -131,4 +121,5 @@ config TCG_CRB from within Linux. To compile this driver as a module, choose M here; the module will be called tpm_crb. +source "drivers/char/tpm/st33zp24/Kconfig" endif # TCG_TPM diff --git a/drivers/char/tpm/Makefile b/drivers/char/tpm/Makefile index 990cf183931d..56e8f1f3dc7e 100644 --- a/drivers/char/tpm/Makefile +++ b/drivers/char/tpm/Makefile @@ -20,6 +20,6 @@ obj-$(CONFIG_TCG_NSC) += tpm_nsc.o obj-$(CONFIG_TCG_ATMEL) += tpm_atmel.o obj-$(CONFIG_TCG_INFINEON) += tpm_infineon.o obj-$(CONFIG_TCG_IBMVTPM) += tpm_ibmvtpm.o -obj-$(CONFIG_TCG_TIS_I2C_ST33) += tpm_i2c_stm_st33.o +obj-$(CONFIG_TCG_TIS_ST33ZP24) += st33zp24/ obj-$(CONFIG_TCG_XEN) += xen-tpmfront.o obj-$(CONFIG_TCG_CRB) += tpm_crb.o diff --git a/drivers/char/tpm/st33zp24/Kconfig b/drivers/char/tpm/st33zp24/Kconfig new file mode 100644 index 000000000000..51dcef520d3f --- /dev/null +++ b/drivers/char/tpm/st33zp24/Kconfig @@ -0,0 +1,20 @@ +config TCG_TIS_ST33ZP24 + tristate "STMicroelectronics TPM Interface Specification 1.2 Interface" + depends on GPIOLIB + ---help--- + STMicroelectronics ST33ZP24 core driver. It implements the core + TPM1.2 logic and hooks into the TPM kernel APIs. Physical layers will + register against it. + + To compile this driver as a module, choose m here. The module will be called + tpm_st33zp24. + +config TCG_TIS_ST33ZP24_I2C + tristate "TPM 1.2 ST33ZP24 I2C support" + depends on TCG_TIS_ST33ZP24 + depends on I2C + ---help--- + This module adds support for the STMicroelectronics TPM security chip + ST33ZP24 with i2c interface. + To compile this driver as a module, choose M here; the module will be + called tpm_st33zp24_i2c. diff --git a/drivers/char/tpm/st33zp24/Makefile b/drivers/char/tpm/st33zp24/Makefile new file mode 100644 index 000000000000..414497f00e5a --- /dev/null +++ b/drivers/char/tpm/st33zp24/Makefile @@ -0,0 +1,9 @@ +# +# Makefile for ST33ZP24 TPM 1.2 driver +# + +tpm_st33zp24-objs = st33zp24.o +obj-$(CONFIG_TCG_TIS_ST33ZP24) += tpm_st33zp24.o + +tpm_st33zp24_i2c-objs = i2c.o +obj-$(CONFIG_TCG_TIS_ST33ZP24_I2C) += tpm_st33zp24_i2c.o diff --git a/drivers/char/tpm/st33zp24/i2c.c b/drivers/char/tpm/st33zp24/i2c.c new file mode 100644 index 000000000000..95e3091c4590 --- /dev/null +++ b/drivers/char/tpm/st33zp24/i2c.c @@ -0,0 +1,278 @@ +/* + * STMicroelectronics TPM I2C Linux driver for TPM ST33ZP24 + * Copyright (C) 2009 - 2015 STMicroelectronics + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "st33zp24.h" + +#define TPM_DUMMY_BYTE 0xAA +#define TPM_WRITE_DIRECTION 0x80 +#define TPM_BUFSIZE 2048 + +struct st33zp24_i2c_phy { + struct i2c_client *client; + u8 buf[TPM_BUFSIZE + 1]; + int io_lpcpd; +}; + +/* + * write8_reg + * Send byte to the TIS register according to the ST33ZP24 I2C protocol. + * @param: tpm_register, the tpm tis register where the data should be written + * @param: tpm_data, the tpm_data to write inside the tpm_register + * @param: tpm_size, The length of the data + * @return: Returns negative errno, or else the number of bytes written. + */ +static int write8_reg(void *phy_id, u8 tpm_register, u8 *tpm_data, int tpm_size) +{ + struct st33zp24_i2c_phy *phy = phy_id; + + phy->buf[0] = tpm_register; + memcpy(phy->buf + 1, tpm_data, tpm_size); + return i2c_master_send(phy->client, phy->buf, tpm_size + 1); +} /* write8_reg() */ + +/* + * read8_reg + * Recv byte from the TIS register according to the ST33ZP24 I2C protocol. + * @param: tpm_register, the tpm tis register where the data should be read + * @param: tpm_data, the TPM response + * @param: tpm_size, tpm TPM response size to read. + * @return: number of byte read successfully: should be one if success. + */ +static int read8_reg(void *phy_id, u8 tpm_register, u8 *tpm_data, int tpm_size) +{ + struct st33zp24_i2c_phy *phy = phy_id; + u8 status = 0; + u8 data; + + data = TPM_DUMMY_BYTE; + status = write8_reg(phy, tpm_register, &data, 1); + if (status == 2) + status = i2c_master_recv(phy->client, tpm_data, tpm_size); + return status; +} /* read8_reg() */ + +/* + * st33zp24_i2c_send + * Send byte to the TIS register according to the ST33ZP24 I2C protocol. + * @param: phy_id, the phy description + * @param: tpm_register, the tpm tis register where the data should be written + * @param: tpm_data, the tpm_data to write inside the tpm_register + * @param: tpm_size, the length of the data + * @return: number of byte written successfully: should be one if success. + */ +static int st33zp24_i2c_send(void *phy_id, u8 tpm_register, u8 *tpm_data, + int tpm_size) +{ + return write8_reg(phy_id, tpm_register | TPM_WRITE_DIRECTION, tpm_data, + tpm_size); +} + +/* + * st33zp24_i2c_recv + * Recv byte from the TIS register according to the ST33ZP24 I2C protocol. + * @param: phy_id, the phy description + * @param: tpm_register, the tpm tis register where the data should be read + * @param: tpm_data, the TPM response + * @param: tpm_size, tpm TPM response size to read. + * @return: number of byte read successfully: should be one if success. + */ +static int st33zp24_i2c_recv(void *phy_id, u8 tpm_register, u8 *tpm_data, + int tpm_size) +{ + return read8_reg(phy_id, tpm_register, tpm_data, tpm_size); +} + +static const struct st33zp24_phy_ops i2c_phy_ops = { + .send = st33zp24_i2c_send, + .recv = st33zp24_i2c_recv, +}; + +#ifdef CONFIG_OF +static int st33zp24_i2c_of_request_resources(struct st33zp24_i2c_phy *phy) +{ + struct device_node *pp; + struct i2c_client *client = phy->client; + int gpio; + int ret; + + pp = client->dev.of_node; + if (!pp) { + dev_err(&client->dev, "No platform data\n"); + return -ENODEV; + } + + /* Get GPIO from device tree */ + gpio = of_get_named_gpio(pp, "lpcpd-gpios", 0); + if (gpio < 0) { + dev_err(&client->dev, + "Failed to retrieve lpcpd-gpios from dts.\n"); + phy->io_lpcpd = -1; + /* + * lpcpd pin is not specified. This is not an issue as + * power management can be also managed by TPM specific + * commands. So leave with a success status code. + */ + return 0; + } + /* GPIO request and configuration */ + ret = devm_gpio_request_one(&client->dev, gpio, + GPIOF_OUT_INIT_HIGH, "TPM IO LPCPD"); + if (ret) { + dev_err(&client->dev, "Failed to request lpcpd pin\n"); + return -ENODEV; + } + phy->io_lpcpd = gpio; + + return 0; +} +#else +static int st33zp24_i2c_of_request_resources(struct st33zp24_i2c_phy *phy) +{ + return -ENODEV; +} +#endif + +static int st33zp24_i2c_request_resources(struct i2c_client *client, + struct st33zp24_i2c_phy *phy) +{ + struct st33zp24_platform_data *pdata; + int ret; + + pdata = client->dev.platform_data; + if (!pdata) { + dev_err(&client->dev, "No platform data\n"); + return -ENODEV; + } + + /* store for late use */ + phy->io_lpcpd = pdata->io_lpcpd; + + if (gpio_is_valid(pdata->io_lpcpd)) { + ret = devm_gpio_request_one(&client->dev, + pdata->io_lpcpd, GPIOF_OUT_INIT_HIGH, + "TPM IO_LPCPD"); + if (ret) { + dev_err(&client->dev, "Failed to request lpcpd pin\n"); + return ret; + } + } + + return 0; +} + +/* + * st33zp24_i2c_probe initialize the TPM device + * @param: client, the i2c_client drescription (TPM I2C description). + * @param: id, the i2c_device_id struct. + * @return: 0 in case of success. + * -1 in other case. + */ +static int st33zp24_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + int ret; + struct st33zp24_platform_data *pdata; + struct st33zp24_i2c_phy *phy; + + if (!client) { + pr_info("%s: i2c client is NULL. Device not accessible.\n", + __func__); + return -ENODEV; + } + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { + dev_info(&client->dev, "client not i2c capable\n"); + return -ENODEV; + } + + phy = devm_kzalloc(&client->dev, sizeof(struct st33zp24_i2c_phy), + GFP_KERNEL); + if (!phy) + return -ENOMEM; + + phy->client = client; + pdata = client->dev.platform_data; + if (!pdata && client->dev.of_node) { + ret = st33zp24_i2c_of_request_resources(phy); + if (ret) + return ret; + } else if (pdata) { + ret = st33zp24_i2c_request_resources(client, phy); + if (ret) + return ret; + } + + return st33zp24_probe(phy, &i2c_phy_ops, &client->dev, client->irq, + phy->io_lpcpd); +} + +/* + * st33zp24_i2c_remove remove the TPM device + * @param: client, the i2c_client description (TPM I2C description). + * @return: 0 in case of success. + */ +static int st33zp24_i2c_remove(struct i2c_client *client) +{ + struct tpm_chip *chip = i2c_get_clientdata(client); + + return st33zp24_remove(chip); +} + +static const struct i2c_device_id st33zp24_i2c_id[] = { + {TPM_ST33_I2C, 0}, + {} +}; +MODULE_DEVICE_TABLE(i2c, st33zp24_i2c_id); + +#ifdef CONFIG_OF +static const struct of_device_id of_st33zp24_i2c_match[] = { + { .compatible = "st,st33zp24-i2c", }, + {} +}; +MODULE_DEVICE_TABLE(of, of_st33zp24_i2c_match); +#endif + +static SIMPLE_DEV_PM_OPS(st33zp24_i2c_ops, st33zp24_pm_suspend, + st33zp24_pm_resume); + +static struct i2c_driver st33zp24_i2c_driver = { + .driver = { + .owner = THIS_MODULE, + .name = TPM_ST33_I2C, + .pm = &st33zp24_i2c_ops, + .of_match_table = of_match_ptr(of_st33zp24_i2c_match), + }, + .probe = st33zp24_i2c_probe, + .remove = st33zp24_i2c_remove, + .id_table = st33zp24_i2c_id +}; + +module_i2c_driver(st33zp24_i2c_driver); + +MODULE_AUTHOR("TPM support (TPMsupport@list.st.com)"); +MODULE_DESCRIPTION("STM TPM 1.2 I2C ST33 Driver"); +MODULE_VERSION("1.3.0"); +MODULE_LICENSE("GPL"); diff --git a/drivers/char/tpm/st33zp24/st33zp24.c b/drivers/char/tpm/st33zp24/st33zp24.c new file mode 100644 index 000000000000..03f254384585 --- /dev/null +++ b/drivers/char/tpm/st33zp24/st33zp24.c @@ -0,0 +1,688 @@ +/* + * STMicroelectronics TPM Linux driver for TPM ST33ZP24 + * Copyright (C) 2009 - 2015 STMicroelectronics + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../tpm.h" +#include "st33zp24.h" + +#define TPM_ACCESS 0x0 +#define TPM_STS 0x18 +#define TPM_DATA_FIFO 0x24 +#define TPM_INTF_CAPABILITY 0x14 +#define TPM_INT_STATUS 0x10 +#define TPM_INT_ENABLE 0x08 + +#define LOCALITY0 0 + +enum st33zp24_access { + TPM_ACCESS_VALID = 0x80, + TPM_ACCESS_ACTIVE_LOCALITY = 0x20, + TPM_ACCESS_REQUEST_PENDING = 0x04, + TPM_ACCESS_REQUEST_USE = 0x02, +}; + +enum st33zp24_status { + TPM_STS_VALID = 0x80, + TPM_STS_COMMAND_READY = 0x40, + TPM_STS_GO = 0x20, + TPM_STS_DATA_AVAIL = 0x10, + TPM_STS_DATA_EXPECT = 0x08, +}; + +enum st33zp24_int_flags { + TPM_GLOBAL_INT_ENABLE = 0x80, + TPM_INTF_CMD_READY_INT = 0x080, + TPM_INTF_FIFO_AVALAIBLE_INT = 0x040, + TPM_INTF_WAKE_UP_READY_INT = 0x020, + TPM_INTF_LOCALITY_CHANGE_INT = 0x004, + TPM_INTF_STS_VALID_INT = 0x002, + TPM_INTF_DATA_AVAIL_INT = 0x001, +}; + +enum tis_defaults { + TIS_SHORT_TIMEOUT = 750, + TIS_LONG_TIMEOUT = 2000, +}; + +struct st33zp24_dev { + struct tpm_chip *chip; + void *phy_id; + const struct st33zp24_phy_ops *ops; + u32 intrs; + int io_lpcpd; +}; + +/* + * clear_interruption clear the pending interrupt. + * @param: tpm_dev, the tpm device device. + * @return: the interrupt status value. + */ +static u8 clear_interruption(struct st33zp24_dev *tpm_dev) +{ + u8 interrupt; + + tpm_dev->ops->recv(tpm_dev->phy_id, TPM_INT_STATUS, &interrupt, 1); + tpm_dev->ops->send(tpm_dev->phy_id, TPM_INT_STATUS, &interrupt, 1); + return interrupt; +} /* clear_interruption() */ + +/* + * st33zp24_cancel, cancel the current command execution or + * set STS to COMMAND READY. + * @param: chip, the tpm_chip description as specified in driver/char/tpm/tpm.h + */ +static void st33zp24_cancel(struct tpm_chip *chip) +{ + struct st33zp24_dev *tpm_dev; + u8 data; + + tpm_dev = (struct st33zp24_dev *)TPM_VPRIV(chip); + + data = TPM_STS_COMMAND_READY; + tpm_dev->ops->send(tpm_dev->phy_id, TPM_STS, &data, 1); +} /* st33zp24_cancel() */ + +/* + * st33zp24_status return the TPM_STS register + * @param: chip, the tpm chip description + * @return: the TPM_STS register value. + */ +static u8 st33zp24_status(struct tpm_chip *chip) +{ + struct st33zp24_dev *tpm_dev; + u8 data; + + tpm_dev = (struct st33zp24_dev *)TPM_VPRIV(chip); + + tpm_dev->ops->recv(tpm_dev->phy_id, TPM_STS, &data, 1); + return data; +} /* st33zp24_status() */ + +/* + * check_locality if the locality is active + * @param: chip, the tpm chip description + * @return: the active locality or -EACCESS. + */ +static int check_locality(struct tpm_chip *chip) +{ + struct st33zp24_dev *tpm_dev; + u8 data; + u8 status; + + tpm_dev = (struct st33zp24_dev *)TPM_VPRIV(chip); + + status = tpm_dev->ops->recv(tpm_dev->phy_id, TPM_ACCESS, &data, 1); + if (status && (data & + (TPM_ACCESS_ACTIVE_LOCALITY | TPM_ACCESS_VALID)) == + (TPM_ACCESS_ACTIVE_LOCALITY | TPM_ACCESS_VALID)) + return chip->vendor.locality; + + return -EACCES; +} /* check_locality() */ + +/* + * request_locality request the TPM locality + * @param: chip, the chip description + * @return: the active locality or negative value. + */ +static int request_locality(struct tpm_chip *chip) +{ + unsigned long stop; + long ret; + struct st33zp24_dev *tpm_dev; + u8 data; + + if (check_locality(chip) == chip->vendor.locality) + return chip->vendor.locality; + + tpm_dev = (struct st33zp24_dev *)TPM_VPRIV(chip); + + data = TPM_ACCESS_REQUEST_USE; + ret = tpm_dev->ops->send(tpm_dev->phy_id, TPM_ACCESS, &data, 1); + if (ret < 0) + return ret; + + stop = jiffies + chip->vendor.timeout_a; + + /* Request locality is usually effective after the request */ + do { + if (check_locality(chip) >= 0) + return chip->vendor.locality; + msleep(TPM_TIMEOUT); + } while (time_before(jiffies, stop)); + + /* could not get locality */ + return -EACCES; +} /* request_locality() */ + +/* + * release_locality release the active locality + * @param: chip, the tpm chip description. + */ +static void release_locality(struct tpm_chip *chip) +{ + struct st33zp24_dev *tpm_dev; + u8 data; + + tpm_dev = (struct st33zp24_dev *)TPM_VPRIV(chip); + data = TPM_ACCESS_ACTIVE_LOCALITY; + + tpm_dev->ops->send(tpm_dev->phy_id, TPM_ACCESS, &data, 1); +} + +/* + * get_burstcount return the burstcount value + * @param: chip, the chip description + * return: the burstcount or negative value. + */ +static int get_burstcount(struct tpm_chip *chip) +{ + unsigned long stop; + int burstcnt, status; + u8 tpm_reg, temp; + struct st33zp24_dev *tpm_dev; + + tpm_dev = (struct st33zp24_dev *)TPM_VPRIV(chip); + + stop = jiffies + chip->vendor.timeout_d; + do { + tpm_reg = TPM_STS + 1; + status = tpm_dev->ops->recv(tpm_dev->phy_id, tpm_reg, &temp, 1); + if (status < 0) + return -EBUSY; + + tpm_reg = TPM_STS + 2; + burstcnt = temp; + status = tpm_dev->ops->recv(tpm_dev->phy_id, tpm_reg, &temp, 1); + if (status < 0) + return -EBUSY; + + burstcnt |= temp << 8; + if (burstcnt) + return burstcnt; + msleep(TPM_TIMEOUT); + } while (time_before(jiffies, stop)); + return -EBUSY; +} /* get_burstcount() */ + + +/* + * wait_for_tpm_stat_cond + * @param: chip, chip description + * @param: mask, expected mask value + * @param: check_cancel, does the command expected to be canceled ? + * @param: canceled, did we received a cancel request ? + * @return: true if status == mask or if the command is canceled. + * false in other cases. + */ +static bool wait_for_tpm_stat_cond(struct tpm_chip *chip, u8 mask, + bool check_cancel, bool *canceled) +{ + u8 status = chip->ops->status(chip); + + *canceled = false; + if ((status & mask) == mask) + return true; + if (check_cancel && chip->ops->req_canceled(chip, status)) { + *canceled = true; + return true; + } + return false; +} + +/* + * wait_for_stat wait for a TPM_STS value + * @param: chip, the tpm chip description + * @param: mask, the value mask to wait + * @param: timeout, the timeout + * @param: queue, the wait queue. + * @param: check_cancel, does the command can be cancelled ? + * @return: the tpm status, 0 if success, -ETIME if timeout is reached. + */ +static int wait_for_stat(struct tpm_chip *chip, u8 mask, unsigned long timeout, + wait_queue_head_t *queue, bool check_cancel) +{ + unsigned long stop; + int ret = 0; + bool canceled = false; + bool condition; + u32 cur_intrs; + u8 status; + struct st33zp24_dev *tpm_dev; + + tpm_dev = (struct st33zp24_dev *)TPM_VPRIV(chip); + + /* check current status */ + status = st33zp24_status(chip); + if ((status & mask) == mask) + return 0; + + stop = jiffies + timeout; + + if (chip->vendor.irq) { + cur_intrs = tpm_dev->intrs; + clear_interruption(tpm_dev); + enable_irq(chip->vendor.irq); + + do { + if (ret == -ERESTARTSYS && freezing(current)) + clear_thread_flag(TIF_SIGPENDING); + + timeout = stop - jiffies; + if ((long) timeout <= 0) + return -1; + + ret = wait_event_interruptible_timeout(*queue, + cur_intrs != tpm_dev->intrs, + timeout); + clear_interruption(tpm_dev); + condition = wait_for_tpm_stat_cond(chip, mask, + check_cancel, &canceled); + if (ret >= 0 && condition) { + if (canceled) + return -ECANCELED; + return 0; + } + } while (ret == -ERESTARTSYS && freezing(current)); + + disable_irq_nosync(chip->vendor.irq); + + } else { + do { + msleep(TPM_TIMEOUT); + status = chip->ops->status(chip); + if ((status & mask) == mask) + return 0; + } while (time_before(jiffies, stop)); + } + + return -ETIME; +} /* wait_for_stat() */ + +/* + * recv_data receive data + * @param: chip, the tpm chip description + * @param: buf, the buffer where the data are received + * @param: count, the number of data to receive + * @return: the number of bytes read from TPM FIFO. + */ +static int recv_data(struct tpm_chip *chip, u8 *buf, size_t count) +{ + int size = 0, burstcnt, len, ret; + struct st33zp24_dev *tpm_dev; + + tpm_dev = (struct st33zp24_dev *)TPM_VPRIV(chip); + + while (size < count && + wait_for_stat(chip, + TPM_STS_DATA_AVAIL | TPM_STS_VALID, + chip->vendor.timeout_c, + &chip->vendor.read_queue, true) == 0) { + burstcnt = get_burstcount(chip); + if (burstcnt < 0) + return burstcnt; + len = min_t(int, burstcnt, count - size); + ret = tpm_dev->ops->recv(tpm_dev->phy_id, TPM_DATA_FIFO, + buf + size, len); + if (ret < 0) + return ret; + + size += len; + } + return size; +} + +/* + * tpm_ioserirq_handler the serirq irq handler + * @param: irq, the tpm chip description + * @param: dev_id, the description of the chip + * @return: the status of the handler. + */ +static irqreturn_t tpm_ioserirq_handler(int irq, void *dev_id) +{ + struct tpm_chip *chip = dev_id; + struct st33zp24_dev *tpm_dev; + + tpm_dev = (struct st33zp24_dev *)TPM_VPRIV(chip); + + tpm_dev->intrs++; + wake_up_interruptible(&chip->vendor.read_queue); + disable_irq_nosync(chip->vendor.irq); + + return IRQ_HANDLED; +} /* tpm_ioserirq_handler() */ + +/* + * st33zp24_send send TPM commands through the I2C bus. + * + * @param: chip, the tpm_chip description as specified in driver/char/tpm/tpm.h + * @param: buf, the buffer to send. + * @param: count, the number of bytes to send. + * @return: In case of success the number of bytes sent. + * In other case, a < 0 value describing the issue. + */ +static int st33zp24_send(struct tpm_chip *chip, unsigned char *buf, + size_t len) +{ + u32 status, i, size; + int burstcnt = 0; + int ret; + u8 data; + struct st33zp24_dev *tpm_dev; + + if (!chip) + return -EBUSY; + if (len < TPM_HEADER_SIZE) + return -EBUSY; + + tpm_dev = (struct st33zp24_dev *)TPM_VPRIV(chip); + + ret = request_locality(chip); + if (ret < 0) + return ret; + + status = st33zp24_status(chip); + if ((status & TPM_STS_COMMAND_READY) == 0) { + st33zp24_cancel(chip); + if (wait_for_stat + (chip, TPM_STS_COMMAND_READY, chip->vendor.timeout_b, + &chip->vendor.read_queue, false) < 0) { + ret = -ETIME; + goto out_err; + } + } + + for (i = 0; i < len - 1;) { + burstcnt = get_burstcount(chip); + if (burstcnt < 0) + return burstcnt; + size = min_t(int, len - i - 1, burstcnt); + ret = tpm_dev->ops->send(tpm_dev->phy_id, TPM_DATA_FIFO, + buf + i, size); + if (ret < 0) + goto out_err; + + i += size; + } + + status = st33zp24_status(chip); + if ((status & TPM_STS_DATA_EXPECT) == 0) { + ret = -EIO; + goto out_err; + } + + ret = tpm_dev->ops->send(tpm_dev->phy_id, TPM_DATA_FIFO, + buf + len - 1, 1); + if (ret < 0) + goto out_err; + + status = st33zp24_status(chip); + if ((status & TPM_STS_DATA_EXPECT) != 0) { + ret = -EIO; + goto out_err; + } + + data = TPM_STS_GO; + ret = tpm_dev->ops->send(tpm_dev->phy_id, TPM_STS, &data, 1); + if (ret < 0) + goto out_err; + + return len; +out_err: + st33zp24_cancel(chip); + release_locality(chip); + return ret; +} + +/* + * st33zp24_recv received TPM response through TPM phy. + * @param: chip, the tpm_chip description as specified in driver/char/tpm/tpm.h. + * @param: buf, the buffer to store datas. + * @param: count, the number of bytes to send. + * @return: In case of success the number of bytes received. + * In other case, a < 0 value describing the issue. + */ +static int st33zp24_recv(struct tpm_chip *chip, unsigned char *buf, + size_t count) +{ + int size = 0; + int expected; + + if (!chip) + return -EBUSY; + + if (count < TPM_HEADER_SIZE) { + size = -EIO; + goto out; + } + + size = recv_data(chip, buf, TPM_HEADER_SIZE); + if (size < TPM_HEADER_SIZE) { + dev_err(&chip->dev, "Unable to read header\n"); + goto out; + } + + expected = be32_to_cpu(*(__be32 *)(buf + 2)); + if (expected > count) { + size = -EIO; + goto out; + } + + size += recv_data(chip, &buf[TPM_HEADER_SIZE], + expected - TPM_HEADER_SIZE); + if (size < expected) { + dev_err(&chip->dev, "Unable to read remainder of result\n"); + size = -ETIME; + } + +out: + st33zp24_cancel(chip); + release_locality(chip); + return size; +} + +/* + * st33zp24_req_canceled + * @param: chip, the tpm_chip description as specified in driver/char/tpm/tpm.h. + * @param: status, the TPM status. + * @return: Does TPM ready to compute a new command ? true. + */ +static bool st33zp24_req_canceled(struct tpm_chip *chip, u8 status) +{ + return (status == TPM_STS_COMMAND_READY); +} + +static const struct tpm_class_ops st33zp24_tpm = { + .send = st33zp24_send, + .recv = st33zp24_recv, + .cancel = st33zp24_cancel, + .status = st33zp24_status, + .req_complete_mask = TPM_STS_DATA_AVAIL | TPM_STS_VALID, + .req_complete_val = TPM_STS_DATA_AVAIL | TPM_STS_VALID, + .req_canceled = st33zp24_req_canceled, +}; + +/* + * st33zp24_probe initialize the TPM device + * @param: client, the i2c_client drescription (TPM I2C description). + * @param: id, the i2c_device_id struct. + * @return: 0 in case of success. + * -1 in other case. + */ +int st33zp24_probe(void *phy_id, const struct st33zp24_phy_ops *ops, + struct device *dev, int irq, int io_lpcpd) +{ + int ret; + u8 intmask = 0; + struct tpm_chip *chip; + struct st33zp24_dev *tpm_dev; + + chip = tpmm_chip_alloc(dev, &st33zp24_tpm); + if (IS_ERR(chip)) + return PTR_ERR(chip); + + tpm_dev = devm_kzalloc(dev, sizeof(struct st33zp24_dev), + GFP_KERNEL); + if (!tpm_dev) + return -ENOMEM; + + TPM_VPRIV(chip) = tpm_dev; + tpm_dev->phy_id = phy_id; + tpm_dev->ops = ops; + + chip->vendor.timeout_a = msecs_to_jiffies(TIS_SHORT_TIMEOUT); + chip->vendor.timeout_b = msecs_to_jiffies(TIS_LONG_TIMEOUT); + chip->vendor.timeout_c = msecs_to_jiffies(TIS_SHORT_TIMEOUT); + chip->vendor.timeout_d = msecs_to_jiffies(TIS_SHORT_TIMEOUT); + + chip->vendor.locality = LOCALITY0; + + if (irq) { + /* INTERRUPT Setup */ + init_waitqueue_head(&chip->vendor.read_queue); + tpm_dev->intrs = 0; + + if (request_locality(chip) != LOCALITY0) { + ret = -ENODEV; + goto _tpm_clean_answer; + } + + clear_interruption(tpm_dev); + ret = devm_request_irq(dev, irq, tpm_ioserirq_handler, + IRQF_TRIGGER_HIGH, "TPM SERIRQ management", + chip); + if (ret < 0) { + dev_err(&chip->dev, "TPM SERIRQ signals %d not available\n", + irq); + goto _tpm_clean_answer; + } + + intmask |= TPM_INTF_CMD_READY_INT + | TPM_INTF_STS_VALID_INT + | TPM_INTF_DATA_AVAIL_INT; + + ret = tpm_dev->ops->send(tpm_dev->phy_id, TPM_INT_ENABLE, + &intmask, 1); + if (ret < 0) + goto _tpm_clean_answer; + + intmask = TPM_GLOBAL_INT_ENABLE; + ret = tpm_dev->ops->send(tpm_dev->phy_id, (TPM_INT_ENABLE + 3), + &intmask, 1); + if (ret < 0) + goto _tpm_clean_answer; + + chip->vendor.irq = irq; + + disable_irq_nosync(chip->vendor.irq); + + tpm_gen_interrupt(chip); + } + + tpm_get_timeouts(chip); + tpm_do_selftest(chip); + + return tpm_chip_register(chip); +_tpm_clean_answer: + dev_info(&chip->dev, "TPM initialization fail\n"); + return ret; +} +EXPORT_SYMBOL(st33zp24_probe); + +/* + * st33zp24_remove remove the TPM device + * @param: tpm_data, the tpm phy. + * @return: 0 in case of success. + */ +int st33zp24_remove(struct tpm_chip *chip) +{ + tpm_chip_unregister(chip); + return 0; +} +EXPORT_SYMBOL(st33zp24_remove); + +#ifdef CONFIG_PM_SLEEP +/* + * st33zp24_pm_suspend suspend the TPM device + * @param: tpm_data, the tpm phy. + * @param: mesg, the power management message. + * @return: 0 in case of success. + */ +int st33zp24_pm_suspend(struct device *dev) +{ + struct tpm_chip *chip = dev_get_drvdata(dev); + struct st33zp24_dev *tpm_dev; + int ret = 0; + + tpm_dev = (struct st33zp24_dev *)TPM_VPRIV(chip); + + if (gpio_is_valid(tpm_dev->io_lpcpd)) + gpio_set_value(tpm_dev->io_lpcpd, 0); + else + ret = tpm_pm_suspend(dev); + + return ret; +} /* st33zp24_pm_suspend() */ +EXPORT_SYMBOL(st33zp24_pm_suspend); + +/* + * st33zp24_pm_resume resume the TPM device + * @param: tpm_data, the tpm phy. + * @return: 0 in case of success. + */ +int st33zp24_pm_resume(struct device *dev) +{ + struct tpm_chip *chip = dev_get_drvdata(dev); + struct st33zp24_dev *tpm_dev; + int ret = 0; + + tpm_dev = (struct st33zp24_dev *)TPM_VPRIV(chip); + + if (gpio_is_valid(tpm_dev->io_lpcpd)) { + gpio_set_value(tpm_dev->io_lpcpd, 1); + ret = wait_for_stat(chip, + TPM_STS_VALID, chip->vendor.timeout_b, + &chip->vendor.read_queue, false); + } else { + ret = tpm_pm_resume(dev); + if (!ret) + tpm_do_selftest(chip); + } + return ret; +} /* st33zp24_pm_resume() */ +EXPORT_SYMBOL(st33zp24_pm_resume); +#endif + +MODULE_AUTHOR("TPM support (TPMsupport@list.st.com)"); +MODULE_DESCRIPTION("ST33ZP24 TPM 1.2 driver"); +MODULE_VERSION("1.3.0"); +MODULE_LICENSE("GPL"); diff --git a/drivers/char/tpm/st33zp24/st33zp24.h b/drivers/char/tpm/st33zp24/st33zp24.h new file mode 100644 index 000000000000..43ad39a27f8d --- /dev/null +++ b/drivers/char/tpm/st33zp24/st33zp24.h @@ -0,0 +1,34 @@ +/* + * STMicroelectronics TPM Linux driver for TPM ST33ZP24 + * Copyright (C) 2009 - 2015 STMicroelectronics + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ + +#ifndef __LOCAL_ST33ZP24_H__ +#define __LOCAL_ST33ZP24_H__ + +struct st33zp24_phy_ops { + int (*send)(void *phy_id, u8 tpm_register, u8 *tpm_data, int tpm_size); + int (*recv)(void *phy_id, u8 tpm_register, u8 *tpm_data, int tpm_size); +}; + +#ifdef CONFIG_PM_SLEEP +int st33zp24_pm_suspend(struct device *dev); +int st33zp24_pm_resume(struct device *dev); +#endif + +int st33zp24_probe(void *phy_id, const struct st33zp24_phy_ops *ops, + struct device *dev, int irq, int io_lpcpd); +int st33zp24_remove(struct tpm_chip *chip); +#endif /* __LOCAL_ST33ZP24_H__ */ diff --git a/drivers/char/tpm/tpm_i2c_stm_st33.c b/drivers/char/tpm/tpm_i2c_stm_st33.c deleted file mode 100644 index 882c60a433fd..000000000000 --- a/drivers/char/tpm/tpm_i2c_stm_st33.c +++ /dev/null @@ -1,915 +0,0 @@ -/* - * STMicroelectronics TPM I2C Linux driver for TPM ST33ZP24 - * Copyright (C) 2009, 2010, 2014 STMicroelectronics - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, see . - * - * STMicroelectronics version 1.2.1, Copyright (C) 2014 - * STMicroelectronics comes with ABSOLUTELY NO WARRANTY. - * This is free software, and you are welcome to redistribute it - * under certain conditions. - * - * @Author: Christophe RICARD tpmsupport@st.com - * - * @File: tpm_stm_st33_i2c.c - * - * @Synopsis: - * 09/15/2010: First shot driver tpm_tis driver for - * lpc is used as model. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include "tpm.h" - -#define TPM_ACCESS 0x0 -#define TPM_STS 0x18 -#define TPM_HASH_END 0x20 -#define TPM_DATA_FIFO 0x24 -#define TPM_HASH_DATA 0x24 -#define TPM_HASH_START 0x28 -#define TPM_INTF_CAPABILITY 0x14 -#define TPM_INT_STATUS 0x10 -#define TPM_INT_ENABLE 0x08 - -#define TPM_DUMMY_BYTE 0xAA -#define TPM_WRITE_DIRECTION 0x80 -#define TPM_HEADER_SIZE 10 -#define TPM_BUFSIZE 2048 - -#define LOCALITY0 0 - - -enum stm33zp24_access { - TPM_ACCESS_VALID = 0x80, - TPM_ACCESS_ACTIVE_LOCALITY = 0x20, - TPM_ACCESS_REQUEST_PENDING = 0x04, - TPM_ACCESS_REQUEST_USE = 0x02, -}; - -enum stm33zp24_status { - TPM_STS_VALID = 0x80, - TPM_STS_COMMAND_READY = 0x40, - TPM_STS_GO = 0x20, - TPM_STS_DATA_AVAIL = 0x10, - TPM_STS_DATA_EXPECT = 0x08, -}; - -enum stm33zp24_int_flags { - TPM_GLOBAL_INT_ENABLE = 0x80, - TPM_INTF_CMD_READY_INT = 0x080, - TPM_INTF_FIFO_AVALAIBLE_INT = 0x040, - TPM_INTF_WAKE_UP_READY_INT = 0x020, - TPM_INTF_LOCALITY_CHANGE_INT = 0x004, - TPM_INTF_STS_VALID_INT = 0x002, - TPM_INTF_DATA_AVAIL_INT = 0x001, -}; - -enum tis_defaults { - TIS_SHORT_TIMEOUT = 750, - TIS_LONG_TIMEOUT = 2000, -}; - -struct tpm_stm_dev { - struct i2c_client *client; - struct tpm_chip *chip; - u8 buf[TPM_BUFSIZE + 1]; - u32 intrs; - int io_lpcpd; -}; - -/* - * write8_reg - * Send byte to the TIS register according to the ST33ZP24 I2C protocol. - * @param: tpm_register, the tpm tis register where the data should be written - * @param: tpm_data, the tpm_data to write inside the tpm_register - * @param: tpm_size, The length of the data - * @return: Returns negative errno, or else the number of bytes written. - */ -static int write8_reg(struct tpm_stm_dev *tpm_dev, u8 tpm_register, - u8 *tpm_data, u16 tpm_size) -{ - tpm_dev->buf[0] = tpm_register; - memcpy(tpm_dev->buf + 1, tpm_data, tpm_size); - return i2c_master_send(tpm_dev->client, tpm_dev->buf, tpm_size + 1); -} /* write8_reg() */ - -/* - * read8_reg - * Recv byte from the TIS register according to the ST33ZP24 I2C protocol. - * @param: tpm_register, the tpm tis register where the data should be read - * @param: tpm_data, the TPM response - * @param: tpm_size, tpm TPM response size to read. - * @return: number of byte read successfully: should be one if success. - */ -static int read8_reg(struct tpm_stm_dev *tpm_dev, u8 tpm_register, - u8 *tpm_data, int tpm_size) -{ - u8 status = 0; - u8 data; - - data = TPM_DUMMY_BYTE; - status = write8_reg(tpm_dev, tpm_register, &data, 1); - if (status == 2) - status = i2c_master_recv(tpm_dev->client, tpm_data, tpm_size); - return status; -} /* read8_reg() */ - -/* - * I2C_WRITE_DATA - * Send byte to the TIS register according to the ST33ZP24 I2C protocol. - * @param: tpm_dev, the chip description - * @param: tpm_register, the tpm tis register where the data should be written - * @param: tpm_data, the tpm_data to write inside the tpm_register - * @param: tpm_size, The length of the data - * @return: number of byte written successfully: should be one if success. - */ -#define I2C_WRITE_DATA(tpm_dev, tpm_register, tpm_data, tpm_size) \ - (write8_reg(tpm_dev, tpm_register | \ - TPM_WRITE_DIRECTION, tpm_data, tpm_size)) - -/* - * I2C_READ_DATA - * Recv byte from the TIS register according to the ST33ZP24 I2C protocol. - * @param: tpm_dev, the chip description - * @param: tpm_register, the tpm tis register where the data should be read - * @param: tpm_data, the TPM response - * @param: tpm_size, tpm TPM response size to read. - * @return: number of byte read successfully: should be one if success. - */ -#define I2C_READ_DATA(tpm_dev, tpm_register, tpm_data, tpm_size) \ - (read8_reg(tpm_dev, tpm_register, tpm_data, tpm_size)) - -/* - * clear_interruption - * clear the TPM interrupt register. - * @param: tpm, the chip description - * @return: the TPM_INT_STATUS value - */ -static u8 clear_interruption(struct tpm_stm_dev *tpm_dev) -{ - u8 interrupt; - - I2C_READ_DATA(tpm_dev, TPM_INT_STATUS, &interrupt, 1); - I2C_WRITE_DATA(tpm_dev, TPM_INT_STATUS, &interrupt, 1); - return interrupt; -} /* clear_interruption() */ - -/* - * tpm_stm_i2c_cancel, cancel is not implemented. - * @param: chip, the tpm_chip description as specified in driver/char/tpm/tpm.h - */ -static void tpm_stm_i2c_cancel(struct tpm_chip *chip) -{ - struct tpm_stm_dev *tpm_dev; - u8 data; - - tpm_dev = (struct tpm_stm_dev *)TPM_VPRIV(chip); - - data = TPM_STS_COMMAND_READY; - I2C_WRITE_DATA(tpm_dev, TPM_STS, &data, 1); -} /* tpm_stm_i2c_cancel() */ - -/* - * tpm_stm_spi_status return the TPM_STS register - * @param: chip, the tpm chip description - * @return: the TPM_STS register value. - */ -static u8 tpm_stm_i2c_status(struct tpm_chip *chip) -{ - struct tpm_stm_dev *tpm_dev; - u8 data; - - tpm_dev = (struct tpm_stm_dev *)TPM_VPRIV(chip); - - I2C_READ_DATA(tpm_dev, TPM_STS, &data, 1); - return data; -} /* tpm_stm_i2c_status() */ - - -/* - * check_locality if the locality is active - * @param: chip, the tpm chip description - * @return: the active locality or -EACCESS. - */ -static int check_locality(struct tpm_chip *chip) -{ - struct tpm_stm_dev *tpm_dev; - u8 data; - u8 status; - - tpm_dev = (struct tpm_stm_dev *)TPM_VPRIV(chip); - - status = I2C_READ_DATA(tpm_dev, TPM_ACCESS, &data, 1); - if (status && (data & - (TPM_ACCESS_ACTIVE_LOCALITY | TPM_ACCESS_VALID)) == - (TPM_ACCESS_ACTIVE_LOCALITY | TPM_ACCESS_VALID)) - return chip->vendor.locality; - - return -EACCES; -} /* check_locality() */ - -/* - * request_locality request the TPM locality - * @param: chip, the chip description - * @return: the active locality or EACCESS. - */ -static int request_locality(struct tpm_chip *chip) -{ - unsigned long stop; - long ret; - struct tpm_stm_dev *tpm_dev; - u8 data; - - if (check_locality(chip) == chip->vendor.locality) - return chip->vendor.locality; - - tpm_dev = (struct tpm_stm_dev *)TPM_VPRIV(chip); - - data = TPM_ACCESS_REQUEST_USE; - ret = I2C_WRITE_DATA(tpm_dev, TPM_ACCESS, &data, 1); - if (ret < 0) - goto end; - - stop = jiffies + chip->vendor.timeout_a; - - /* Request locality is usually effective after the request */ - do { - if (check_locality(chip) >= 0) - return chip->vendor.locality; - msleep(TPM_TIMEOUT); - } while (time_before(jiffies, stop)); - ret = -EACCES; -end: - return ret; -} /* request_locality() */ - -/* - * release_locality release the active locality - * @param: chip, the tpm chip description. - */ -static void release_locality(struct tpm_chip *chip) -{ - struct tpm_stm_dev *tpm_dev; - u8 data; - - tpm_dev = (struct tpm_stm_dev *)TPM_VPRIV(chip); - data = TPM_ACCESS_ACTIVE_LOCALITY; - - I2C_WRITE_DATA(tpm_dev, TPM_ACCESS, &data, 1); -} - -/* - * get_burstcount return the burstcount address 0x19 0x1A - * @param: chip, the chip description - * return: the burstcount. - */ -static int get_burstcount(struct tpm_chip *chip) -{ - unsigned long stop; - int burstcnt, status; - u8 tpm_reg, temp; - struct tpm_stm_dev *tpm_dev; - - tpm_dev = (struct tpm_stm_dev *)TPM_VPRIV(chip); - - stop = jiffies + chip->vendor.timeout_d; - do { - tpm_reg = TPM_STS + 1; - status = I2C_READ_DATA(tpm_dev, tpm_reg, &temp, 1); - if (status < 0) - goto end; - - tpm_reg = tpm_reg + 1; - burstcnt = temp; - status = I2C_READ_DATA(tpm_dev, tpm_reg, &temp, 1); - if (status < 0) - goto end; - - burstcnt |= temp << 8; - if (burstcnt) - return burstcnt; - msleep(TPM_TIMEOUT); - } while (time_before(jiffies, stop)); - -end: - return -EBUSY; -} /* get_burstcount() */ - -static bool wait_for_tpm_stat_cond(struct tpm_chip *chip, u8 mask, - bool check_cancel, bool *canceled) -{ - u8 status = chip->ops->status(chip); - - *canceled = false; - if ((status & mask) == mask) - return true; - if (check_cancel && chip->ops->req_canceled(chip, status)) { - *canceled = true; - return true; - } - return false; -} - -/* - * interrupt_to_status - * @param: irq_mask, the irq mask value to wait - * @return: the corresponding tpm_sts value - */ -static u8 interrupt_to_status(u8 irq_mask) -{ - u8 status = 0; - - if ((irq_mask & TPM_INTF_STS_VALID_INT) == TPM_INTF_STS_VALID_INT) - status |= TPM_STS_VALID; - if ((irq_mask & TPM_INTF_DATA_AVAIL_INT) == TPM_INTF_DATA_AVAIL_INT) - status |= TPM_STS_DATA_AVAIL; - if ((irq_mask & TPM_INTF_CMD_READY_INT) == TPM_INTF_CMD_READY_INT) - status |= TPM_STS_COMMAND_READY; - - return status; -} /* status_to_interrupt() */ - -/* - * wait_for_stat wait for a TPM_STS value - * @param: chip, the tpm chip description - * @param: mask, the value mask to wait - * @param: timeout, the timeout - * @param: queue, the wait queue. - * @param: check_cancel, does the command can be cancelled ? - * @return: the tpm status, 0 if success, -ETIME if timeout is reached. - */ -static int wait_for_stat(struct tpm_chip *chip, u8 mask, unsigned long timeout, - wait_queue_head_t *queue, bool check_cancel) -{ - unsigned long stop; - int ret; - bool canceled = false; - bool condition; - u32 cur_intrs; - u8 interrupt, status; - struct tpm_stm_dev *tpm_dev; - - tpm_dev = (struct tpm_stm_dev *)TPM_VPRIV(chip); - - /* check current status */ - status = tpm_stm_i2c_status(chip); - if ((status & mask) == mask) - return 0; - - stop = jiffies + timeout; - - if (chip->vendor.irq) { - cur_intrs = tpm_dev->intrs; - interrupt = clear_interruption(tpm_dev); - enable_irq(chip->vendor.irq); - -again: - timeout = stop - jiffies; - if ((long) timeout <= 0) - return -1; - - ret = wait_event_interruptible_timeout(*queue, - cur_intrs != tpm_dev->intrs, timeout); - - interrupt |= clear_interruption(tpm_dev); - status = interrupt_to_status(interrupt); - condition = wait_for_tpm_stat_cond(chip, mask, - check_cancel, &canceled); - - if (ret >= 0 && condition) { - if (canceled) - return -ECANCELED; - return 0; - } - if (ret == -ERESTARTSYS && freezing(current)) { - clear_thread_flag(TIF_SIGPENDING); - goto again; - } - disable_irq_nosync(chip->vendor.irq); - - } else { - do { - msleep(TPM_TIMEOUT); - status = chip->ops->status(chip); - if ((status & mask) == mask) - return 0; - } while (time_before(jiffies, stop)); - } - - return -ETIME; -} /* wait_for_stat() */ - -/* - * recv_data receive data - * @param: chip, the tpm chip description - * @param: buf, the buffer where the data are received - * @param: count, the number of data to receive - * @return: the number of bytes read from TPM FIFO. - */ -static int recv_data(struct tpm_chip *chip, u8 *buf, size_t count) -{ - int size = 0, burstcnt, len, ret; - struct tpm_stm_dev *tpm_dev; - - tpm_dev = (struct tpm_stm_dev *)TPM_VPRIV(chip); - - while (size < count && - wait_for_stat(chip, - TPM_STS_DATA_AVAIL | TPM_STS_VALID, - chip->vendor.timeout_c, - &chip->vendor.read_queue, true) == 0) { - burstcnt = get_burstcount(chip); - if (burstcnt < 0) - return burstcnt; - len = min_t(int, burstcnt, count - size); - ret = I2C_READ_DATA(tpm_dev, TPM_DATA_FIFO, buf + size, len); - if (ret < 0) - return ret; - - size += len; - } - return size; -} - -/* - * tpm_ioserirq_handler the serirq irq handler - * @param: irq, the tpm chip description - * @param: dev_id, the description of the chip - * @return: the status of the handler. - */ -static irqreturn_t tpm_ioserirq_handler(int irq, void *dev_id) -{ - struct tpm_chip *chip = dev_id; - struct tpm_stm_dev *tpm_dev; - - tpm_dev = (struct tpm_stm_dev *)TPM_VPRIV(chip); - - tpm_dev->intrs++; - wake_up_interruptible(&chip->vendor.read_queue); - disable_irq_nosync(chip->vendor.irq); - - return IRQ_HANDLED; -} /* tpm_ioserirq_handler() */ - - -/* - * tpm_stm_i2c_send send TPM commands through the I2C bus. - * - * @param: chip, the tpm_chip description as specified in driver/char/tpm/tpm.h - * @param: buf, the buffer to send. - * @param: count, the number of bytes to send. - * @return: In case of success the number of bytes sent. - * In other case, a < 0 value describing the issue. - */ -static int tpm_stm_i2c_send(struct tpm_chip *chip, unsigned char *buf, - size_t len) -{ - u32 status, i, size; - int burstcnt = 0; - int ret; - u8 data; - struct i2c_client *client; - struct tpm_stm_dev *tpm_dev; - - if (!chip) - return -EBUSY; - if (len < TPM_HEADER_SIZE) - return -EBUSY; - - tpm_dev = (struct tpm_stm_dev *)TPM_VPRIV(chip); - client = tpm_dev->client; - - client->flags = 0; - - ret = request_locality(chip); - if (ret < 0) - return ret; - - status = tpm_stm_i2c_status(chip); - if ((status & TPM_STS_COMMAND_READY) == 0) { - tpm_stm_i2c_cancel(chip); - if (wait_for_stat - (chip, TPM_STS_COMMAND_READY, chip->vendor.timeout_b, - &chip->vendor.read_queue, false) < 0) { - ret = -ETIME; - goto out_err; - } - } - - for (i = 0; i < len - 1;) { - burstcnt = get_burstcount(chip); - if (burstcnt < 0) - return burstcnt; - size = min_t(int, len - i - 1, burstcnt); - ret = I2C_WRITE_DATA(tpm_dev, TPM_DATA_FIFO, buf + i, size); - if (ret < 0) - goto out_err; - - i += size; - } - - status = tpm_stm_i2c_status(chip); - if ((status & TPM_STS_DATA_EXPECT) == 0) { - ret = -EIO; - goto out_err; - } - - ret = I2C_WRITE_DATA(tpm_dev, TPM_DATA_FIFO, buf + len - 1, 1); - if (ret < 0) - goto out_err; - - status = tpm_stm_i2c_status(chip); - if ((status & TPM_STS_DATA_EXPECT) != 0) { - ret = -EIO; - goto out_err; - } - - data = TPM_STS_GO; - I2C_WRITE_DATA(tpm_dev, TPM_STS, &data, 1); - - return len; -out_err: - tpm_stm_i2c_cancel(chip); - release_locality(chip); - return ret; -} - -/* - * tpm_stm_i2c_recv received TPM response through the I2C bus. - * @param: chip, the tpm_chip description as specified in driver/char/tpm/tpm.h. - * @param: buf, the buffer to store datas. - * @param: count, the number of bytes to send. - * @return: In case of success the number of bytes received. - * In other case, a < 0 value describing the issue. - */ -static int tpm_stm_i2c_recv(struct tpm_chip *chip, unsigned char *buf, - size_t count) -{ - int size = 0; - int expected; - - if (!chip) - return -EBUSY; - - if (count < TPM_HEADER_SIZE) { - size = -EIO; - goto out; - } - - size = recv_data(chip, buf, TPM_HEADER_SIZE); - if (size < TPM_HEADER_SIZE) { - dev_err(chip->pdev, "Unable to read header\n"); - goto out; - } - - expected = be32_to_cpu(*(__be32 *)(buf + 2)); - if (expected > count) { - size = -EIO; - goto out; - } - - size += recv_data(chip, &buf[TPM_HEADER_SIZE], - expected - TPM_HEADER_SIZE); - if (size < expected) { - dev_err(chip->pdev, "Unable to read remainder of result\n"); - size = -ETIME; - goto out; - } - -out: - chip->ops->cancel(chip); - release_locality(chip); - return size; -} - -static bool tpm_stm_i2c_req_canceled(struct tpm_chip *chip, u8 status) -{ - return (status == TPM_STS_COMMAND_READY); -} - -static const struct tpm_class_ops st_i2c_tpm = { - .send = tpm_stm_i2c_send, - .recv = tpm_stm_i2c_recv, - .cancel = tpm_stm_i2c_cancel, - .status = tpm_stm_i2c_status, - .req_complete_mask = TPM_STS_DATA_AVAIL | TPM_STS_VALID, - .req_complete_val = TPM_STS_DATA_AVAIL | TPM_STS_VALID, - .req_canceled = tpm_stm_i2c_req_canceled, -}; - -#ifdef CONFIG_OF -static int tpm_stm_i2c_of_request_resources(struct tpm_chip *chip) -{ - struct device_node *pp; - struct tpm_stm_dev *tpm_dev = (struct tpm_stm_dev *)TPM_VPRIV(chip); - struct i2c_client *client = tpm_dev->client; - int gpio; - int ret; - - pp = client->dev.of_node; - if (!pp) { - dev_err(chip->pdev, "No platform data\n"); - return -ENODEV; - } - - /* Get GPIO from device tree */ - gpio = of_get_named_gpio(pp, "lpcpd-gpios", 0); - if (gpio < 0) { - dev_err(chip->pdev, "Failed to retrieve lpcpd-gpios from dts.\n"); - tpm_dev->io_lpcpd = -1; - /* - * lpcpd pin is not specified. This is not an issue as - * power management can be also managed by TPM specific - * commands. So leave with a success status code. - */ - return 0; - } - /* GPIO request and configuration */ - ret = devm_gpio_request_one(&client->dev, gpio, - GPIOF_OUT_INIT_HIGH, "TPM IO LPCPD"); - if (ret) { - dev_err(chip->pdev, "Failed to request lpcpd pin\n"); - return -ENODEV; - } - tpm_dev->io_lpcpd = gpio; - - return 0; -} -#else -static int tpm_stm_i2c_of_request_resources(struct tpm_chip *chip) -{ - return -ENODEV; -} -#endif - -static int tpm_stm_i2c_request_resources(struct i2c_client *client, - struct tpm_chip *chip) -{ - struct st33zp24_platform_data *pdata; - struct tpm_stm_dev *tpm_dev = (struct tpm_stm_dev *)TPM_VPRIV(chip); - int ret; - - pdata = client->dev.platform_data; - if (!pdata) { - dev_err(chip->pdev, "No platform data\n"); - return -ENODEV; - } - - /* store for late use */ - tpm_dev->io_lpcpd = pdata->io_lpcpd; - - if (gpio_is_valid(pdata->io_lpcpd)) { - ret = devm_gpio_request_one(&client->dev, - pdata->io_lpcpd, GPIOF_OUT_INIT_HIGH, - "TPM IO_LPCPD"); - if (ret) { - dev_err(chip->pdev, "%s : reset gpio_request failed\n", - __FILE__); - return ret; - } - } - - return 0; -} - -/* - * tpm_stm_i2c_probe initialize the TPM device - * @param: client, the i2c_client drescription (TPM I2C description). - * @param: id, the i2c_device_id struct. - * @return: 0 in case of success. - * -1 in other case. - */ -static int -tpm_stm_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id) -{ - int ret; - u8 intmask = 0; - struct tpm_chip *chip; - struct st33zp24_platform_data *platform_data; - struct tpm_stm_dev *tpm_dev; - - if (!client) { - pr_info("%s: i2c client is NULL. Device not accessible.\n", - __func__); - return -ENODEV; - } - - if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { - dev_info(&client->dev, "client not i2c capable\n"); - return -ENODEV; - } - - tpm_dev = devm_kzalloc(&client->dev, sizeof(struct tpm_stm_dev), - GFP_KERNEL); - if (!tpm_dev) - return -ENOMEM; - - chip = tpmm_chip_alloc(&client->dev, &st_i2c_tpm); - if (IS_ERR(chip)) - return PTR_ERR(chip); - - TPM_VPRIV(chip) = tpm_dev; - tpm_dev->client = client; - - platform_data = client->dev.platform_data; - if (!platform_data && client->dev.of_node) { - ret = tpm_stm_i2c_of_request_resources(chip); - if (ret) - goto _tpm_clean_answer; - } else if (platform_data) { - ret = tpm_stm_i2c_request_resources(client, chip); - if (ret) - goto _tpm_clean_answer; - } - - chip->vendor.timeout_a = msecs_to_jiffies(TIS_SHORT_TIMEOUT); - chip->vendor.timeout_b = msecs_to_jiffies(TIS_LONG_TIMEOUT); - chip->vendor.timeout_c = msecs_to_jiffies(TIS_SHORT_TIMEOUT); - chip->vendor.timeout_d = msecs_to_jiffies(TIS_SHORT_TIMEOUT); - - chip->vendor.locality = LOCALITY0; - - if (client->irq) { - /* INTERRUPT Setup */ - init_waitqueue_head(&chip->vendor.read_queue); - tpm_dev->intrs = 0; - - if (request_locality(chip) != LOCALITY0) { - ret = -ENODEV; - goto _tpm_clean_answer; - } - - clear_interruption(tpm_dev); - ret = devm_request_irq(&client->dev, client->irq, - tpm_ioserirq_handler, - IRQF_TRIGGER_HIGH, - "TPM SERIRQ management", chip); - if (ret < 0) { - dev_err(chip->pdev, "TPM SERIRQ signals %d not available\n", - client->irq); - goto _tpm_clean_answer; - } - - intmask |= TPM_INTF_CMD_READY_INT - | TPM_INTF_STS_VALID_INT - | TPM_INTF_DATA_AVAIL_INT; - - ret = I2C_WRITE_DATA(tpm_dev, TPM_INT_ENABLE, &intmask, 1); - if (ret < 0) - goto _tpm_clean_answer; - - intmask = TPM_GLOBAL_INT_ENABLE; - ret = I2C_WRITE_DATA(tpm_dev, (TPM_INT_ENABLE + 3), - &intmask, 1); - if (ret < 0) - goto _tpm_clean_answer; - - chip->vendor.irq = client->irq; - - disable_irq_nosync(chip->vendor.irq); - - tpm_gen_interrupt(chip); - } - - tpm_get_timeouts(chip); - tpm_do_selftest(chip); - - return tpm_chip_register(chip); -_tpm_clean_answer: - dev_info(chip->pdev, "TPM I2C initialisation fail\n"); - return ret; -} - -/* - * tpm_stm_i2c_remove remove the TPM device - * @param: client, the i2c_client description (TPM I2C description). - * @return: 0 in case of success. - */ -static int tpm_stm_i2c_remove(struct i2c_client *client) -{ - struct tpm_chip *chip = - (struct tpm_chip *) i2c_get_clientdata(client); - - if (chip) - tpm_chip_unregister(chip); - - return 0; -} - -#ifdef CONFIG_PM_SLEEP -/* - * tpm_stm_i2c_pm_suspend suspend the TPM device - * @param: client, the i2c_client drescription (TPM I2C description). - * @param: mesg, the power management message. - * @return: 0 in case of success. - */ -static int tpm_stm_i2c_pm_suspend(struct device *dev) -{ - struct tpm_chip *chip = dev_get_drvdata(dev); - struct tpm_stm_dev *tpm_dev; - int ret = 0; - - tpm_dev = (struct tpm_stm_dev *)TPM_VPRIV(chip); - - if (gpio_is_valid(tpm_dev->io_lpcpd)) - gpio_set_value(tpm_dev->io_lpcpd, 0); - else - ret = tpm_pm_suspend(dev); - - return ret; -} /* tpm_stm_i2c_suspend() */ - -/* - * tpm_stm_i2c_pm_resume resume the TPM device - * @param: client, the i2c_client drescription (TPM I2C description). - * @return: 0 in case of success. - */ -static int tpm_stm_i2c_pm_resume(struct device *dev) -{ - struct tpm_chip *chip = dev_get_drvdata(dev); - struct tpm_stm_dev *tpm_dev; - int ret = 0; - - tpm_dev = (struct tpm_stm_dev *)TPM_VPRIV(chip); - - if (gpio_is_valid(tpm_dev->io_lpcpd)) { - gpio_set_value(tpm_dev->io_lpcpd, 1); - ret = wait_for_stat(chip, - TPM_STS_VALID, chip->vendor.timeout_b, - &chip->vendor.read_queue, false); - } else { - ret = tpm_pm_resume(dev); - if (!ret) - tpm_do_selftest(chip); - } - return ret; -} /* tpm_stm_i2c_pm_resume() */ -#endif - -static const struct i2c_device_id tpm_stm_i2c_id[] = { - {TPM_ST33_I2C, 0}, - {} -}; -MODULE_DEVICE_TABLE(i2c, tpm_stm_i2c_id); - -#ifdef CONFIG_OF -static const struct of_device_id of_st33zp24_i2c_match[] = { - { .compatible = "st,st33zp24-i2c", }, - {} -}; -MODULE_DEVICE_TABLE(of, of_st33zp24_i2c_match); -#endif - -static SIMPLE_DEV_PM_OPS(tpm_stm_i2c_ops, tpm_stm_i2c_pm_suspend, - tpm_stm_i2c_pm_resume); - -static struct i2c_driver tpm_stm_i2c_driver = { - .driver = { - .owner = THIS_MODULE, - .name = TPM_ST33_I2C, - .pm = &tpm_stm_i2c_ops, - .of_match_table = of_match_ptr(of_st33zp24_i2c_match), - }, - .probe = tpm_stm_i2c_probe, - .remove = tpm_stm_i2c_remove, - .id_table = tpm_stm_i2c_id -}; - -module_i2c_driver(tpm_stm_i2c_driver); - -MODULE_AUTHOR("Christophe Ricard (tpmsupport@st.com)"); -MODULE_DESCRIPTION("STM TPM I2C ST33 Driver"); -MODULE_VERSION("1.2.1"); -MODULE_LICENSE("GPL"); diff --git a/include/linux/platform_data/st33zp24.h b/include/linux/platform_data/st33zp24.h new file mode 100644 index 000000000000..817dfdb37885 --- /dev/null +++ b/include/linux/platform_data/st33zp24.h @@ -0,0 +1,28 @@ +/* + * STMicroelectronics TPM Linux driver for TPM 1.2 ST33ZP24 + * Copyright (C) 2009 - 2015 STMicroelectronics + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ +#ifndef __ST33ZP24_H__ +#define __ST33ZP24_H__ + +#define TPM_ST33_I2C "st33zp24-i2c" +#define TPM_ST33_SPI "st33zp24-spi" + +struct st33zp24_platform_data { + int io_lpcpd; +}; + +#endif /* __ST33ZP24_H__ */ diff --git a/include/linux/platform_data/tpm_stm_st33.h b/include/linux/platform_data/tpm_stm_st33.h deleted file mode 100644 index ff75310c0f47..000000000000 --- a/include/linux/platform_data/tpm_stm_st33.h +++ /dev/null @@ -1,39 +0,0 @@ -/* - * STMicroelectronics TPM I2C Linux driver for TPM ST33ZP24 - * Copyright (C) 2009, 2010 STMicroelectronics - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, see . - * - * STMicroelectronics version 1.2.0, Copyright (C) 2010 - * STMicroelectronics comes with ABSOLUTELY NO WARRANTY. - * This is free software, and you are welcome to redistribute it - * under certain conditions. - * - * @Author: Christophe RICARD tpmsupport@st.com - * - * @File: stm_st33_tpm.h - * - * @Date: 09/15/2010 - */ -#ifndef __STM_ST33_TPM_H__ -#define __STM_ST33_TPM_H__ - -#define TPM_ST33_I2C "st33zp24-i2c" -#define TPM_ST33_SPI "st33zp24-spi" - -struct st33zp24_platform_data { - int io_lpcpd; -}; - -#endif /* __STM_ST33_TPM_H__ */ -- cgit v1.2.3 From 6eada0110c8984477f5f1e57a0b7f7b2fc841e30 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Wed, 18 Mar 2015 14:05:33 -0700 Subject: netns: constify net_hash_mix() and various callers const qualifiers ease code review by making clear which objects are not written in a function. Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- include/linux/udp.h | 2 +- include/net/inet_hashtables.h | 6 +++--- include/net/netns/hash.h | 4 ++-- net/ipv4/devinet.c | 2 +- net/ipv4/inet_hashtables.c | 6 +++--- net/ipv4/ping.c | 6 +++--- net/ipv4/udp.c | 10 +++++----- net/ipv6/inet6_hashtables.c | 10 +++++----- net/ipv6/udp.c | 16 ++++++++-------- 9 files changed, 31 insertions(+), 31 deletions(-) (limited to 'include') diff --git a/include/linux/udp.h b/include/linux/udp.h index 247cfdcc4b08..87c094961bd5 100644 --- a/include/linux/udp.h +++ b/include/linux/udp.h @@ -34,7 +34,7 @@ static inline struct udphdr *inner_udp_hdr(const struct sk_buff *skb) #define UDP_HTABLE_SIZE_MIN (CONFIG_BASE_SMALL ? 128 : 256) -static inline int udp_hashfn(struct net *net, unsigned num, unsigned mask) +static inline u32 udp_hashfn(const struct net *net, u32 num, u32 mask) { return (num + net_hash_mix(net)) & mask; } diff --git a/include/net/inet_hashtables.h b/include/net/inet_hashtables.h index bcd64756e5fe..eb1963af0ebd 100644 --- a/include/net/inet_hashtables.h +++ b/include/net/inet_hashtables.h @@ -221,8 +221,8 @@ inet_bind_bucket_create(struct kmem_cache *cachep, struct net *net, void inet_bind_bucket_destroy(struct kmem_cache *cachep, struct inet_bind_bucket *tb); -static inline int inet_bhashfn(struct net *net, const __u16 lport, - const int bhash_size) +static inline u32 inet_bhashfn(const struct net *net, const __u16 lport, + const u32 bhash_size) { return (lport + net_hash_mix(net)) & (bhash_size - 1); } @@ -231,7 +231,7 @@ void inet_bind_hash(struct sock *sk, struct inet_bind_bucket *tb, const unsigned short snum); /* These can have wildcards, don't try too hard. */ -static inline int inet_lhashfn(struct net *net, const unsigned short num) +static inline u32 inet_lhashfn(const struct net *net, const unsigned short num) { return (num + net_hash_mix(net)) & (INET_LHTABLE_SIZE - 1); } diff --git a/include/net/netns/hash.h b/include/net/netns/hash.h index c06ac58ca107..69a6715d9f3f 100644 --- a/include/net/netns/hash.h +++ b/include/net/netns/hash.h @@ -5,7 +5,7 @@ struct net; -static inline unsigned int net_hash_mix(struct net *net) +static inline u32 net_hash_mix(const struct net *net) { #ifdef CONFIG_NET_NS /* @@ -13,7 +13,7 @@ static inline unsigned int net_hash_mix(struct net *net) * always zeroed */ - return (unsigned)(((unsigned long)net) >> L1_CACHE_SHIFT); + return (u32)(((unsigned long)net) >> L1_CACHE_SHIFT); #else return 0; #endif diff --git a/net/ipv4/devinet.c b/net/ipv4/devinet.c index 5105759e4e00..375dc71b9a64 100644 --- a/net/ipv4/devinet.c +++ b/net/ipv4/devinet.c @@ -107,7 +107,7 @@ static const struct nla_policy ifa_ipv4_policy[IFA_MAX+1] = { static struct hlist_head inet_addr_lst[IN4_ADDR_HSIZE]; -static u32 inet_addr_hash(struct net *net, __be32 addr) +static u32 inet_addr_hash(const struct net *net, __be32 addr) { u32 val = (__force u32) addr ^ net_hash_mix(net); diff --git a/net/ipv4/inet_hashtables.c b/net/ipv4/inet_hashtables.c index c28bca4cc15b..330a08bcd1c1 100644 --- a/net/ipv4/inet_hashtables.c +++ b/net/ipv4/inet_hashtables.c @@ -24,9 +24,9 @@ #include #include -static unsigned int inet_ehashfn(struct net *net, const __be32 laddr, - const __u16 lport, const __be32 faddr, - const __be16 fport) +static u32 inet_ehashfn(const struct net *net, const __be32 laddr, + const __u16 lport, const __be32 faddr, + const __be16 fport) { static u32 inet_ehash_secret __read_mostly; diff --git a/net/ipv4/ping.c b/net/ipv4/ping.c index fd88f868776f..344e7cdfb8d4 100644 --- a/net/ipv4/ping.c +++ b/net/ipv4/ping.c @@ -64,11 +64,11 @@ EXPORT_SYMBOL_GPL(pingv6_ops); static u16 ping_port_rover; -static inline int ping_hashfn(struct net *net, unsigned int num, unsigned int mask) +static inline u32 ping_hashfn(const struct net *net, u32 num, u32 mask) { - int res = (num + net_hash_mix(net)) & mask; + u32 res = (num + net_hash_mix(net)) & mask; - pr_debug("hash(%d) = %d\n", num, res); + pr_debug("hash(%u) = %u\n", num, res); return res; } EXPORT_SYMBOL_GPL(ping_hash); diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c index f27556e2158b..294af16633af 100644 --- a/net/ipv4/udp.c +++ b/net/ipv4/udp.c @@ -318,8 +318,8 @@ static int ipv4_rcv_saddr_equal(const struct sock *sk1, const struct sock *sk2) inet1->inet_rcv_saddr == inet2->inet_rcv_saddr)); } -static unsigned int udp4_portaddr_hash(struct net *net, __be32 saddr, - unsigned int port) +static u32 udp4_portaddr_hash(const struct net *net, __be32 saddr, + unsigned int port) { return jhash_1word((__force u32)saddr, net_hash_mix(net)) ^ port; } @@ -421,9 +421,9 @@ static inline int compute_score2(struct sock *sk, struct net *net, return score; } -static unsigned int udp_ehashfn(struct net *net, const __be32 laddr, - const __u16 lport, const __be32 faddr, - const __be16 fport) +static u32 udp_ehashfn(const struct net *net, const __be32 laddr, + const __u16 lport, const __be32 faddr, + const __be16 fport) { static u32 udp_ehash_secret __read_mostly; diff --git a/net/ipv6/inet6_hashtables.c b/net/ipv6/inet6_hashtables.c index 051dffb49c90..df7df99d1d7e 100644 --- a/net/ipv6/inet6_hashtables.c +++ b/net/ipv6/inet6_hashtables.c @@ -23,11 +23,11 @@ #include #include -static unsigned int inet6_ehashfn(struct net *net, - const struct in6_addr *laddr, - const u16 lport, - const struct in6_addr *faddr, - const __be16 fport) +static u32 inet6_ehashfn(const struct net *net, + const struct in6_addr *laddr, + const u16 lport, + const struct in6_addr *faddr, + const __be16 fport) { static u32 inet6_ehash_secret __read_mostly; static u32 ipv6_hash_secret __read_mostly; diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c index 70568a4548e4..7fe0329c0d37 100644 --- a/net/ipv6/udp.c +++ b/net/ipv6/udp.c @@ -53,11 +53,11 @@ #include #include "udp_impl.h" -static unsigned int udp6_ehashfn(struct net *net, - const struct in6_addr *laddr, - const u16 lport, - const struct in6_addr *faddr, - const __be16 fport) +static u32 udp6_ehashfn(const struct net *net, + const struct in6_addr *laddr, + const u16 lport, + const struct in6_addr *faddr, + const __be16 fport) { static u32 udp6_ehash_secret __read_mostly; static u32 udp_ipv6_hash_secret __read_mostly; @@ -104,9 +104,9 @@ int ipv6_rcv_saddr_equal(const struct sock *sk, const struct sock *sk2) return 0; } -static unsigned int udp6_portaddr_hash(struct net *net, - const struct in6_addr *addr6, - unsigned int port) +static u32 udp6_portaddr_hash(const struct net *net, + const struct in6_addr *addr6, + unsigned int port) { unsigned int hash, mix = net_hash_mix(net); -- cgit v1.2.3 From 5b441f76f1b83591e8cd9d60ba1df3a2aacde27f Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Wed, 18 Mar 2015 14:05:34 -0700 Subject: net: introduce sk_ehashfn() helper Goal is to unify IPv4/IPv6 inet_hash handling, and use common helpers for all kind of sockets (full sockets, timewait and request sockets) inet_sk_ehashfn() becomes sk_ehashfn() but still only copes with IPv4 Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- include/net/inet_hashtables.h | 2 ++ net/ipv4/inet_hashtables.c | 17 ++++++----------- 2 files changed, 8 insertions(+), 11 deletions(-) (limited to 'include') diff --git a/include/net/inet_hashtables.h b/include/net/inet_hashtables.h index eb1963af0ebd..ef993ef571ea 100644 --- a/include/net/inet_hashtables.h +++ b/include/net/inet_hashtables.h @@ -383,6 +383,8 @@ static inline struct sock *__inet_lookup_skb(struct inet_hashinfo *hashinfo, iph->daddr, dport, inet_iif(skb)); } +u32 sk_ehashfn(const struct sock *sk); + int __inet_hash_connect(struct inet_timewait_death_row *death_row, struct sock *sk, u32 port_offset, int (*check_established)(struct inet_timewait_death_row *, diff --git a/net/ipv4/inet_hashtables.c b/net/ipv4/inet_hashtables.c index 330a08bcd1c1..3a86dfd7ae33 100644 --- a/net/ipv4/inet_hashtables.c +++ b/net/ipv4/inet_hashtables.c @@ -37,16 +37,11 @@ static u32 inet_ehashfn(const struct net *net, const __be32 laddr, } -static unsigned int inet_sk_ehashfn(const struct sock *sk) +u32 sk_ehashfn(const struct sock *sk) { - const struct inet_sock *inet = inet_sk(sk); - const __be32 laddr = inet->inet_rcv_saddr; - const __u16 lport = inet->inet_num; - const __be32 faddr = inet->inet_daddr; - const __be16 fport = inet->inet_dport; - struct net *net = sock_net(sk); - - return inet_ehashfn(net, laddr, lport, faddr, fport); + return inet_ehashfn(sock_net(sk), + sk->sk_rcv_saddr, sk->sk_num, + sk->sk_daddr, sk->sk_dport); } /* @@ -407,13 +402,13 @@ int __inet_hash_nolisten(struct sock *sk, struct inet_timewait_sock *tw) { struct inet_hashinfo *hashinfo = sk->sk_prot->h.hashinfo; struct hlist_nulls_head *list; - spinlock_t *lock; struct inet_ehash_bucket *head; + spinlock_t *lock; int twrefcnt = 0; WARN_ON(!sk_unhashed(sk)); - sk->sk_hash = inet_sk_ehashfn(sk); + sk->sk_hash = sk_ehashfn(sk); head = inet_ehash_bucket(hashinfo, sk->sk_hash); list = &head->chain; lock = inet_ehash_lockp(hashinfo, sk->sk_hash); -- cgit v1.2.3 From d1e559d0b1b0d02f76a6bd5b768a99dc834ae926 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Wed, 18 Mar 2015 14:05:35 -0700 Subject: inet: add IPv6 support to sk_ehashfn() Intent is to converge IPv4 & IPv6 inet_hash functions to factorize code. IPv4 sockets initialize sk_rcv_saddr and sk_v6_daddr in this patch, thanks to new sk_daddr_set() and sk_rcv_saddr_set() helpers. __inet6_hash can now use sk_ehashfn() instead of a private inet6_sk_ehashfn() and will simply use __inet_hash() in a following patch. Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- include/net/inet_hashtables.h | 19 +++++++++++++++++++ net/dccp/ipv4.c | 9 ++++----- net/dccp/ipv6.c | 10 ++-------- net/ipv4/inet_hashtables.c | 11 ++++++++++- net/ipv4/tcp_ipv4.c | 8 ++++---- net/ipv6/inet6_hashtables.c | 22 ++++------------------ net/ipv6/tcp_ipv6.c | 11 ++--------- 7 files changed, 45 insertions(+), 45 deletions(-) (limited to 'include') diff --git a/include/net/inet_hashtables.h b/include/net/inet_hashtables.h index ef993ef571ea..06ad42182ec2 100644 --- a/include/net/inet_hashtables.h +++ b/include/net/inet_hashtables.h @@ -384,6 +384,25 @@ static inline struct sock *__inet_lookup_skb(struct inet_hashinfo *hashinfo, } u32 sk_ehashfn(const struct sock *sk); +u32 inet6_ehashfn(const struct net *net, + const struct in6_addr *laddr, const u16 lport, + const struct in6_addr *faddr, const __be16 fport); + +static inline void sk_daddr_set(struct sock *sk, __be32 addr) +{ + sk->sk_daddr = addr; /* alias of inet_daddr */ +#if IS_ENABLED(CONFIG_IPV6) + ipv6_addr_set_v4mapped(addr, &sk->sk_v6_daddr); +#endif +} + +static inline void sk_rcv_saddr_set(struct sock *sk, __be32 addr) +{ + sk->sk_rcv_saddr = addr; /* alias of inet_rcv_saddr */ +#if IS_ENABLED(CONFIG_IPV6) + ipv6_addr_set_v4mapped(addr, &sk->sk_v6_rcv_saddr); +#endif +} int __inet_hash_connect(struct inet_timewait_death_row *death_row, struct sock *sk, u32 port_offset, diff --git a/net/dccp/ipv4.c b/net/dccp/ipv4.c index bf897829f4f0..f3f8906f482e 100644 --- a/net/dccp/ipv4.c +++ b/net/dccp/ipv4.c @@ -89,10 +89,9 @@ int dccp_v4_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len) if (inet->inet_saddr == 0) inet->inet_saddr = fl4->saddr; - inet->inet_rcv_saddr = inet->inet_saddr; - + sk_rcv_saddr_set(sk, inet->inet_saddr); inet->inet_dport = usin->sin_port; - inet->inet_daddr = daddr; + sk_daddr_set(sk, daddr); inet_csk(sk)->icsk_ext_hdr_len = 0; if (inet_opt) @@ -408,8 +407,8 @@ struct sock *dccp_v4_request_recv_sock(struct sock *sk, struct sk_buff *skb, newinet = inet_sk(newsk); ireq = inet_rsk(req); - newinet->inet_daddr = ireq->ir_rmt_addr; - newinet->inet_rcv_saddr = ireq->ir_loc_addr; + sk_daddr_set(newsk, ireq->ir_rmt_addr); + sk_rcv_saddr_set(newsk, ireq->ir_loc_addr); newinet->inet_saddr = ireq->ir_loc_addr; newinet->inet_opt = ireq->opt; ireq->opt = NULL; diff --git a/net/dccp/ipv6.c b/net/dccp/ipv6.c index d7e7c7b0a3f1..9216d173dd5f 100644 --- a/net/dccp/ipv6.c +++ b/net/dccp/ipv6.c @@ -470,11 +470,7 @@ static struct sock *dccp_v6_request_recv_sock(struct sock *sk, memcpy(newnp, np, sizeof(struct ipv6_pinfo)); - ipv6_addr_set_v4mapped(newinet->inet_daddr, &newsk->sk_v6_daddr); - - ipv6_addr_set_v4mapped(newinet->inet_saddr, &newnp->saddr); - - newsk->sk_v6_rcv_saddr = newnp->saddr; + newnp->saddr = newsk->sk_v6_rcv_saddr; inet_csk(newsk)->icsk_af_ops = &dccp_ipv6_mapped; newsk->sk_backlog_rcv = dccp_v4_do_rcv; @@ -917,9 +913,7 @@ static int dccp_v6_connect(struct sock *sk, struct sockaddr *uaddr, sk->sk_backlog_rcv = dccp_v6_do_rcv; goto failure; } - ipv6_addr_set_v4mapped(inet->inet_saddr, &np->saddr); - ipv6_addr_set_v4mapped(inet->inet_rcv_saddr, &sk->sk_v6_rcv_saddr); - + np->saddr = sk->sk_v6_rcv_saddr; return err; } diff --git a/net/ipv4/inet_hashtables.c b/net/ipv4/inet_hashtables.c index 3a86dfd7ae33..ab7f677a97db 100644 --- a/net/ipv4/inet_hashtables.c +++ b/net/ipv4/inet_hashtables.c @@ -36,9 +36,18 @@ static u32 inet_ehashfn(const struct net *net, const __be32 laddr, inet_ehash_secret + net_hash_mix(net)); } - +/* This function handles inet_sock, but also timewait and request sockets + * for IPv4/IPv6. + */ u32 sk_ehashfn(const struct sock *sk) { +#if IS_ENABLED(CONFIG_IPV6) + if (sk->sk_family == AF_INET6 && + !ipv6_addr_v4mapped(&sk->sk_v6_daddr)) + return inet6_ehashfn(sock_net(sk), + &sk->sk_v6_rcv_saddr, sk->sk_num, + &sk->sk_v6_daddr, sk->sk_dport); +#endif return inet_ehashfn(sock_net(sk), sk->sk_rcv_saddr, sk->sk_num, sk->sk_daddr, sk->sk_dport); diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index 80067d5858b4..ca207df4af1c 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -189,7 +189,7 @@ int tcp_v4_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len) if (!inet->inet_saddr) inet->inet_saddr = fl4->saddr; - inet->inet_rcv_saddr = inet->inet_saddr; + sk_rcv_saddr_set(sk, inet->inet_saddr); if (tp->rx_opt.ts_recent_stamp && inet->inet_daddr != daddr) { /* Reset inherited state */ @@ -204,7 +204,7 @@ int tcp_v4_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len) tcp_fetch_timewait_stamp(sk, &rt->dst); inet->inet_dport = usin->sin_port; - inet->inet_daddr = daddr; + sk_daddr_set(sk, daddr); inet_csk(sk)->icsk_ext_hdr_len = 0; if (inet_opt) @@ -1319,8 +1319,8 @@ struct sock *tcp_v4_syn_recv_sock(struct sock *sk, struct sk_buff *skb, newtp = tcp_sk(newsk); newinet = inet_sk(newsk); ireq = inet_rsk(req); - newinet->inet_daddr = ireq->ir_rmt_addr; - newinet->inet_rcv_saddr = ireq->ir_loc_addr; + sk_daddr_set(newsk, ireq->ir_rmt_addr); + sk_rcv_saddr_set(newsk, ireq->ir_loc_addr); newinet->inet_saddr = ireq->ir_loc_addr; inet_opt = ireq->opt; rcu_assign_pointer(newinet->inet_opt, inet_opt); diff --git a/net/ipv6/inet6_hashtables.c b/net/ipv6/inet6_hashtables.c index df7df99d1d7e..ed5787b20192 100644 --- a/net/ipv6/inet6_hashtables.c +++ b/net/ipv6/inet6_hashtables.c @@ -23,11 +23,9 @@ #include #include -static u32 inet6_ehashfn(const struct net *net, - const struct in6_addr *laddr, - const u16 lport, - const struct in6_addr *faddr, - const __be16 fport) +u32 inet6_ehashfn(const struct net *net, + const struct in6_addr *laddr, const u16 lport, + const struct in6_addr *faddr, const __be16 fport) { static u32 inet6_ehash_secret __read_mostly; static u32 ipv6_hash_secret __read_mostly; @@ -44,18 +42,6 @@ static u32 inet6_ehashfn(const struct net *net, inet6_ehash_secret + net_hash_mix(net)); } -static int inet6_sk_ehashfn(const struct sock *sk) -{ - const struct inet_sock *inet = inet_sk(sk); - const struct in6_addr *laddr = &sk->sk_v6_rcv_saddr; - const struct in6_addr *faddr = &sk->sk_v6_daddr; - const __u16 lport = inet->inet_num; - const __be16 fport = inet->inet_dport; - struct net *net = sock_net(sk); - - return inet6_ehashfn(net, laddr, lport, faddr, fport); -} - int __inet6_hash(struct sock *sk, struct inet_timewait_sock *tw) { struct inet_hashinfo *hashinfo = sk->sk_prot->h.hashinfo; @@ -75,7 +61,7 @@ int __inet6_hash(struct sock *sk, struct inet_timewait_sock *tw) struct hlist_nulls_head *list; spinlock_t *lock; - sk->sk_hash = hash = inet6_sk_ehashfn(sk); + sk->sk_hash = hash = sk_ehashfn(sk); list = &inet_ehash_bucket(hashinfo, hash)->chain; lock = inet_ehash_lockp(hashinfo, hash); spin_lock(lock); diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c index e4761b22307b..5546df074583 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c @@ -233,11 +233,8 @@ static int tcp_v6_connect(struct sock *sk, struct sockaddr *uaddr, tp->af_specific = &tcp_sock_ipv6_specific; #endif goto failure; - } else { - ipv6_addr_set_v4mapped(inet->inet_saddr, &np->saddr); - ipv6_addr_set_v4mapped(inet->inet_rcv_saddr, - &sk->sk_v6_rcv_saddr); } + np->saddr = sk->sk_v6_rcv_saddr; return err; } @@ -1078,11 +1075,7 @@ static struct sock *tcp_v6_syn_recv_sock(struct sock *sk, struct sk_buff *skb, memcpy(newnp, np, sizeof(struct ipv6_pinfo)); - ipv6_addr_set_v4mapped(newinet->inet_daddr, &newsk->sk_v6_daddr); - - ipv6_addr_set_v4mapped(newinet->inet_saddr, &newnp->saddr); - - newsk->sk_v6_rcv_saddr = newnp->saddr; + newnp->saddr = newsk->sk_v6_rcv_saddr; inet_csk(newsk)->icsk_af_ops = &ipv6_mapped; newsk->sk_backlog_rcv = tcp_v4_do_rcv; -- cgit v1.2.3 From 77a6a471bc18763cb2e80a8cc92f4c04eae37d32 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Wed, 18 Mar 2015 14:05:36 -0700 Subject: ipv6: get rid of __inet6_hash() We can now use inet_hash() and __inet_hash() instead of private functions. Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- include/net/inet6_hashtables.h | 2 -- include/net/inet_hashtables.h | 1 + net/dccp/ipv6.c | 17 ++--------------- net/ipv4/inet_hashtables.c | 12 ++++++------ net/ipv6/inet6_hashtables.c | 38 +------------------------------------- net/ipv6/tcp_ipv6.c | 17 ++--------------- 6 files changed, 12 insertions(+), 75 deletions(-) (limited to 'include') diff --git a/include/net/inet6_hashtables.h b/include/net/inet6_hashtables.h index 9201afe083fa..7ff588ca6817 100644 --- a/include/net/inet6_hashtables.h +++ b/include/net/inet6_hashtables.h @@ -38,8 +38,6 @@ static inline unsigned int __inet6_ehashfn(const u32 lhash, return jhash_3words(lhash, fhash, ports, initval); } -int __inet6_hash(struct sock *sk, struct inet_timewait_sock *twp); - /* * Sockets in TCP_CLOSE state are _always_ taken out of the hash, so * we need not check it for TCP lookups anymore, thanks Alexey. -DaveM diff --git a/include/net/inet_hashtables.h b/include/net/inet_hashtables.h index 06ad42182ec2..eee6c3399990 100644 --- a/include/net/inet_hashtables.h +++ b/include/net/inet_hashtables.h @@ -249,6 +249,7 @@ void inet_put_port(struct sock *sk); void inet_hashinfo_init(struct inet_hashinfo *h); int __inet_hash_nolisten(struct sock *sk, struct inet_timewait_sock *tw); +int __inet_hash(struct sock *sk, struct inet_timewait_sock *tw); void inet_hash(struct sock *sk); void inet_unhash(struct sock *sk); diff --git a/net/dccp/ipv6.c b/net/dccp/ipv6.c index 9216d173dd5f..c655de5f67c9 100644 --- a/net/dccp/ipv6.c +++ b/net/dccp/ipv6.c @@ -40,19 +40,6 @@ static const struct inet_connection_sock_af_ops dccp_ipv6_mapped; static const struct inet_connection_sock_af_ops dccp_ipv6_af_ops; -static void dccp_v6_hash(struct sock *sk) -{ - if (sk->sk_state != DCCP_CLOSED) { - if (inet_csk(sk)->icsk_af_ops == &dccp_ipv6_mapped) { - inet_hash(sk); - return; - } - local_bh_disable(); - __inet6_hash(sk, NULL); - local_bh_enable(); - } -} - /* add pseudo-header to DCCP checksum stored in skb->csum */ static inline __sum16 dccp_v6_csum_finish(struct sk_buff *skb, const struct in6_addr *saddr, @@ -588,7 +575,7 @@ static struct sock *dccp_v6_request_recv_sock(struct sock *sk, dccp_done(newsk); goto out; } - __inet6_hash(newsk, NULL); + __inet_hash(newsk, NULL); return newsk; @@ -1056,7 +1043,7 @@ static struct proto dccp_v6_prot = { .sendmsg = dccp_sendmsg, .recvmsg = dccp_recvmsg, .backlog_rcv = dccp_v6_do_rcv, - .hash = dccp_v6_hash, + .hash = inet_hash, .unhash = inet_unhash, .accept = inet_csk_accept, .get_port = inet_csk_get_port, diff --git a/net/ipv4/inet_hashtables.c b/net/ipv4/inet_hashtables.c index ab7f677a97db..82753bd57e79 100644 --- a/net/ipv4/inet_hashtables.c +++ b/net/ipv4/inet_hashtables.c @@ -434,15 +434,13 @@ int __inet_hash_nolisten(struct sock *sk, struct inet_timewait_sock *tw) } EXPORT_SYMBOL_GPL(__inet_hash_nolisten); -static void __inet_hash(struct sock *sk) +int __inet_hash(struct sock *sk, struct inet_timewait_sock *tw) { struct inet_hashinfo *hashinfo = sk->sk_prot->h.hashinfo; struct inet_listen_hashbucket *ilb; - if (sk->sk_state != TCP_LISTEN) { - __inet_hash_nolisten(sk, NULL); - return; - } + if (sk->sk_state != TCP_LISTEN) + return __inet_hash_nolisten(sk, tw); WARN_ON(!sk_unhashed(sk)); ilb = &hashinfo->listening_hash[inet_sk_listen_hashfn(sk)]; @@ -451,13 +449,15 @@ static void __inet_hash(struct sock *sk) __sk_nulls_add_node_rcu(sk, &ilb->head); sock_prot_inuse_add(sock_net(sk), sk->sk_prot, 1); spin_unlock(&ilb->lock); + return 0; } +EXPORT_SYMBOL(__inet_hash); void inet_hash(struct sock *sk) { if (sk->sk_state != TCP_CLOSE) { local_bh_disable(); - __inet_hash(sk); + __inet_hash(sk, NULL); local_bh_enable(); } } diff --git a/net/ipv6/inet6_hashtables.c b/net/ipv6/inet6_hashtables.c index ed5787b20192..b86b429f5f81 100644 --- a/net/ipv6/inet6_hashtables.c +++ b/net/ipv6/inet6_hashtables.c @@ -42,42 +42,6 @@ u32 inet6_ehashfn(const struct net *net, inet6_ehash_secret + net_hash_mix(net)); } -int __inet6_hash(struct sock *sk, struct inet_timewait_sock *tw) -{ - struct inet_hashinfo *hashinfo = sk->sk_prot->h.hashinfo; - int twrefcnt = 0; - - WARN_ON(!sk_unhashed(sk)); - - if (sk->sk_state == TCP_LISTEN) { - struct inet_listen_hashbucket *ilb; - - ilb = &hashinfo->listening_hash[inet_sk_listen_hashfn(sk)]; - spin_lock(&ilb->lock); - __sk_nulls_add_node_rcu(sk, &ilb->head); - spin_unlock(&ilb->lock); - } else { - unsigned int hash; - struct hlist_nulls_head *list; - spinlock_t *lock; - - sk->sk_hash = hash = sk_ehashfn(sk); - list = &inet_ehash_bucket(hashinfo, hash)->chain; - lock = inet_ehash_lockp(hashinfo, hash); - spin_lock(lock); - __sk_nulls_add_node_rcu(sk, list); - if (tw) { - WARN_ON(sk->sk_hash != tw->tw_hash); - twrefcnt = inet_twsk_unhash(tw); - } - spin_unlock(lock); - } - - sock_prot_inuse_add(sock_net(sk), sk->sk_prot, 1); - return twrefcnt; -} -EXPORT_SYMBOL(__inet6_hash); - /* * Sockets in TCP_CLOSE state are _always_ taken out of the hash, so * we need not check it for TCP lookups anymore, thanks Alexey. -DaveM @@ -306,6 +270,6 @@ int inet6_hash_connect(struct inet_timewait_death_row *death_row, struct sock *sk) { return __inet_hash_connect(death_row, sk, inet6_sk_port_offset(sk), - __inet6_check_established, __inet6_hash); + __inet6_check_established, __inet_hash_nolisten); } EXPORT_SYMBOL_GPL(inet6_hash_connect); diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c index 5546df074583..720676d073d9 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c @@ -104,19 +104,6 @@ static void inet6_sk_rx_dst_set(struct sock *sk, const struct sk_buff *skb) } } -static void tcp_v6_hash(struct sock *sk) -{ - if (sk->sk_state != TCP_CLOSE) { - if (inet_csk(sk)->icsk_af_ops == &ipv6_mapped) { - tcp_prot.hash(sk); - return; - } - local_bh_disable(); - __inet6_hash(sk, NULL); - local_bh_enable(); - } -} - static __u32 tcp_v6_init_sequence(const struct sk_buff *skb) { return secure_tcpv6_sequence_number(ipv6_hdr(skb)->daddr.s6_addr32, @@ -1224,7 +1211,7 @@ static struct sock *tcp_v6_syn_recv_sock(struct sock *sk, struct sk_buff *skb, tcp_done(newsk); goto out; } - __inet6_hash(newsk, NULL); + __inet_hash(newsk, NULL); return newsk; @@ -1883,7 +1870,7 @@ struct proto tcpv6_prot = { .sendpage = tcp_sendpage, .backlog_rcv = tcp_v6_do_rcv, .release_cb = tcp_release_cb, - .hash = tcp_v6_hash, + .hash = inet_hash, .unhash = inet_unhash, .get_port = inet_csk_get_port, .enter_memory_pressure = tcp_enter_memory_pressure, -- cgit v1.2.3 From b4d6444ea3b50bf368639432657bcf2b4e5e1062 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Wed, 18 Mar 2015 14:05:37 -0700 Subject: inet: get rid of last __inet_hash_connect() argument We now always call __inet_hash_nolisten(), no need to pass it as an argument. Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- include/net/inet_hashtables.h | 4 +--- net/ipv4/inet_hashtables.c | 9 ++++----- net/ipv6/inet6_hashtables.c | 2 +- 3 files changed, 6 insertions(+), 9 deletions(-) (limited to 'include') diff --git a/include/net/inet_hashtables.h b/include/net/inet_hashtables.h index eee6c3399990..73fe0f9525d9 100644 --- a/include/net/inet_hashtables.h +++ b/include/net/inet_hashtables.h @@ -409,9 +409,7 @@ int __inet_hash_connect(struct inet_timewait_death_row *death_row, struct sock *sk, u32 port_offset, int (*check_established)(struct inet_timewait_death_row *, struct sock *, __u16, - struct inet_timewait_sock **), - int (*hash)(struct sock *sk, - struct inet_timewait_sock *twp)); + struct inet_timewait_sock **)); int inet_hash_connect(struct inet_timewait_death_row *death_row, struct sock *sk); diff --git a/net/ipv4/inet_hashtables.c b/net/ipv4/inet_hashtables.c index 82753bd57e79..0fb841b9d834 100644 --- a/net/ipv4/inet_hashtables.c +++ b/net/ipv4/inet_hashtables.c @@ -488,8 +488,7 @@ EXPORT_SYMBOL_GPL(inet_unhash); int __inet_hash_connect(struct inet_timewait_death_row *death_row, struct sock *sk, u32 port_offset, int (*check_established)(struct inet_timewait_death_row *, - struct sock *, __u16, struct inet_timewait_sock **), - int (*hash)(struct sock *sk, struct inet_timewait_sock *twp)) + struct sock *, __u16, struct inet_timewait_sock **)) { struct inet_hashinfo *hinfo = death_row->hashinfo; const unsigned short snum = inet_sk(sk)->inet_num; @@ -559,7 +558,7 @@ ok: inet_bind_hash(sk, tb, port); if (sk_unhashed(sk)) { inet_sk(sk)->inet_sport = htons(port); - twrefcnt += hash(sk, tw); + twrefcnt += __inet_hash_nolisten(sk, tw); } if (tw) twrefcnt += inet_twsk_bind_unhash(tw, hinfo); @@ -581,7 +580,7 @@ ok: tb = inet_csk(sk)->icsk_bind_hash; spin_lock_bh(&head->lock); if (sk_head(&tb->owners) == sk && !sk->sk_bind_node.next) { - hash(sk, NULL); + __inet_hash_nolisten(sk, NULL); spin_unlock_bh(&head->lock); return 0; } else { @@ -601,7 +600,7 @@ int inet_hash_connect(struct inet_timewait_death_row *death_row, struct sock *sk) { return __inet_hash_connect(death_row, sk, inet_sk_port_offset(sk), - __inet_check_established, __inet_hash_nolisten); + __inet_check_established); } EXPORT_SYMBOL_GPL(inet_hash_connect); diff --git a/net/ipv6/inet6_hashtables.c b/net/ipv6/inet6_hashtables.c index b86b429f5f81..033f17816ef4 100644 --- a/net/ipv6/inet6_hashtables.c +++ b/net/ipv6/inet6_hashtables.c @@ -270,6 +270,6 @@ int inet6_hash_connect(struct inet_timewait_death_row *death_row, struct sock *sk) { return __inet_hash_connect(death_row, sk, inet6_sk_port_offset(sk), - __inet6_check_established, __inet_hash_nolisten); + __inet6_check_established); } EXPORT_SYMBOL_GPL(inet6_hash_connect); -- cgit v1.2.3 From 08d2cc3b26554cae21f279b520ae5c2a3b2be421 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Wed, 18 Mar 2015 14:05:38 -0700 Subject: inet: request sock should init IPv6/IPv4 addresses In order to be able to use sk_ehashfn() for request socks, we need to initialize their IPv6/IPv4 addresses. Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- include/net/request_sock.h | 5 +++++ net/dccp/ipv4.c | 4 ++-- net/ipv4/inet_diag.c | 4 ++-- net/ipv4/syncookies.c | 4 ++-- net/ipv4/tcp_ipv4.c | 8 ++++---- 5 files changed, 15 insertions(+), 10 deletions(-) (limited to 'include') diff --git a/include/net/request_sock.h b/include/net/request_sock.h index 3fa4f824900a..e7ef86340514 100644 --- a/include/net/request_sock.h +++ b/include/net/request_sock.h @@ -91,6 +91,11 @@ static inline struct request_sock *inet_reqsk(struct sock *sk) return (struct request_sock *)sk; } +static inline struct sock *req_to_sk(struct request_sock *req) +{ + return (struct sock *)req; +} + static inline void reqsk_free(struct request_sock *req) { /* temporary debugging */ diff --git a/net/dccp/ipv4.c b/net/dccp/ipv4.c index f3f8906f482e..e7ad291cd96b 100644 --- a/net/dccp/ipv4.c +++ b/net/dccp/ipv4.c @@ -638,8 +638,8 @@ int dccp_v4_conn_request(struct sock *sk, struct sk_buff *skb) goto drop_and_free; ireq = inet_rsk(req); - ireq->ir_loc_addr = ip_hdr(skb)->daddr; - ireq->ir_rmt_addr = ip_hdr(skb)->saddr; + sk_rcv_saddr_set(req_to_sk(req), ip_hdr(skb)->daddr); + sk_daddr_set(req_to_sk(req), ip_hdr(skb)->saddr); ireq->ireq_family = AF_INET; ireq->ir_iif = sk->sk_bound_dev_if; diff --git a/net/ipv4/inet_diag.c b/net/ipv4/inet_diag.c index e7ba59038c8d..74c39c9f3e11 100644 --- a/net/ipv4/inet_diag.c +++ b/net/ipv4/inet_diag.c @@ -742,14 +742,14 @@ static int inet_diag_dump_reqs(struct sk_buff *skb, struct sock *sk, if (bc) { /* Note: entry.sport and entry.userlocks are already set */ - entry_fill_addrs(&entry, (struct sock *)req); + entry_fill_addrs(&entry, req_to_sk(req)); entry.dport = ntohs(ireq->ir_rmt_port); if (!inet_diag_bc_run(bc, &entry)) continue; } - err = inet_req_diag_fill((struct sock *)req, skb, + err = inet_req_diag_fill(req_to_sk(req), skb, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq, NLM_F_MULTI, cb->nlh); diff --git a/net/ipv4/syncookies.c b/net/ipv4/syncookies.c index 34e755403715..ef01d8570358 100644 --- a/net/ipv4/syncookies.c +++ b/net/ipv4/syncookies.c @@ -337,8 +337,8 @@ struct sock *cookie_v4_check(struct sock *sk, struct sk_buff *skb) req->mss = mss; ireq->ir_num = ntohs(th->dest); ireq->ir_rmt_port = th->source; - ireq->ir_loc_addr = ip_hdr(skb)->daddr; - ireq->ir_rmt_addr = ip_hdr(skb)->saddr; + sk_rcv_saddr_set(req_to_sk(req), ip_hdr(skb)->daddr); + sk_daddr_set(req_to_sk(req), ip_hdr(skb)->saddr); ireq->ir_mark = inet_request_mark(sk, skb); ireq->snd_wscale = tcp_opt.snd_wscale; ireq->sack_ok = tcp_opt.sack_ok; diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index ca207df4af1c..ddd0b1f25b96 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -1219,14 +1219,14 @@ static bool tcp_v4_inbound_md5_hash(struct sock *sk, const struct sk_buff *skb) #endif -static void tcp_v4_init_req(struct request_sock *req, struct sock *sk, +static void tcp_v4_init_req(struct request_sock *req, struct sock *sk_listener, struct sk_buff *skb) { struct inet_request_sock *ireq = inet_rsk(req); - ireq->ir_loc_addr = ip_hdr(skb)->daddr; - ireq->ir_rmt_addr = ip_hdr(skb)->saddr; - ireq->no_srccheck = inet_sk(sk)->transparent; + sk_rcv_saddr_set(req_to_sk(req), ip_hdr(skb)->daddr); + sk_daddr_set(req_to_sk(req), ip_hdr(skb)->saddr); + ireq->no_srccheck = inet_sk(sk_listener)->transparent; ireq->opt = tcp_v4_save_options(skb); ireq->ireq_family = AF_INET; } -- cgit v1.2.3 From 54ff9ef36bdf84d469a098cbf8e2a103fbc77054 Mon Sep 17 00:00:00 2001 From: Marcelo Ricardo Leitner Date: Wed, 18 Mar 2015 14:50:43 -0300 Subject: ipv4, ipv6: kill ip_mc_{join, leave}_group and ipv6_sock_mc_{join, drop} in favor of their inner __ ones, which doesn't grab rtnl. As these functions need to operate on a locked socket, we can't be grabbing rtnl by then. It's too late and doing so causes reversed locking. So this patch: - move rtnl handling to callers instead while already fixing some reversed locking situations, like on vxlan and ipvs code. - renames __ ones to not have the __ mark: __ip_mc_{join,leave}_group -> ip_mc_{join,leave}_group __ipv6_sock_mc_{join,drop} -> ipv6_sock_mc_{join,drop} Signed-off-by: Marcelo Ricardo Leitner Acked-by: Hannes Frederic Sowa Signed-off-by: David S. Miller --- drivers/net/vxlan.c | 5 ++++- include/linux/igmp.h | 2 -- include/net/ipv6.h | 4 ---- net/ipv4/devinet.c | 4 ++-- net/ipv4/igmp.c | 41 ++++++++--------------------------------- net/ipv4/ip_sockglue.c | 21 +++++++++++++++------ net/ipv6/addrconf.c | 4 ++-- net/ipv6/ipv6_sockglue.c | 21 +++++++++++++-------- net/ipv6/mcast.c | 30 +++--------------------------- net/netfilter/ipvs/ip_vs_sync.c | 2 ++ net/tipc/udp_media.c | 4 ++-- 11 files changed, 51 insertions(+), 87 deletions(-) (limited to 'include') diff --git a/drivers/net/vxlan.c b/drivers/net/vxlan.c index 25d92d4fc625..8b8ca7492d56 100644 --- a/drivers/net/vxlan.c +++ b/drivers/net/vxlan.c @@ -1097,7 +1097,6 @@ EXPORT_SYMBOL_GPL(vxlan_sock_release); /* Callback to update multicast group membership when first VNI on * multicast asddress is brought up - * Done as workqueue because ip_mc_join_group acquires RTNL. */ static void vxlan_igmp_join(struct work_struct *work) { @@ -1107,6 +1106,7 @@ static void vxlan_igmp_join(struct work_struct *work) union vxlan_addr *ip = &vxlan->default_dst.remote_ip; int ifindex = vxlan->default_dst.remote_ifindex; + rtnl_lock(); lock_sock(sk); if (ip->sa.sa_family == AF_INET) { struct ip_mreqn mreq = { @@ -1122,6 +1122,7 @@ static void vxlan_igmp_join(struct work_struct *work) #endif } release_sock(sk); + rtnl_unlock(); vxlan_sock_release(vs); dev_put(vxlan->dev); @@ -1136,6 +1137,7 @@ static void vxlan_igmp_leave(struct work_struct *work) union vxlan_addr *ip = &vxlan->default_dst.remote_ip; int ifindex = vxlan->default_dst.remote_ifindex; + rtnl_lock(); lock_sock(sk); if (ip->sa.sa_family == AF_INET) { struct ip_mreqn mreq = { @@ -1152,6 +1154,7 @@ static void vxlan_igmp_leave(struct work_struct *work) } release_sock(sk); + rtnl_unlock(); vxlan_sock_release(vs); dev_put(vxlan->dev); diff --git a/include/linux/igmp.h b/include/linux/igmp.h index b5a6470e686c..2c677afeea47 100644 --- a/include/linux/igmp.h +++ b/include/linux/igmp.h @@ -111,9 +111,7 @@ struct ip_mc_list { extern int ip_check_mc_rcu(struct in_device *dev, __be32 mc_addr, __be32 src_addr, u16 proto); extern int igmp_rcv(struct sk_buff *); -extern int __ip_mc_join_group(struct sock *sk, struct ip_mreqn *imr); extern int ip_mc_join_group(struct sock *sk, struct ip_mreqn *imr); -extern int __ip_mc_leave_group(struct sock *sk, struct ip_mreqn *imr); extern int ip_mc_leave_group(struct sock *sk, struct ip_mreqn *imr); extern void ip_mc_drop_socket(struct sock *sk); extern int ip_mc_source(int add, int omode, struct sock *sk, diff --git a/include/net/ipv6.h b/include/net/ipv6.h index b7673065c074..e7ba9758a345 100644 --- a/include/net/ipv6.h +++ b/include/net/ipv6.h @@ -942,10 +942,6 @@ void ipv6_sysctl_unregister(void); int ipv6_sock_mc_join(struct sock *sk, int ifindex, const struct in6_addr *addr); -int __ipv6_sock_mc_join(struct sock *sk, int ifindex, - const struct in6_addr *addr); int ipv6_sock_mc_drop(struct sock *sk, int ifindex, const struct in6_addr *addr); -int __ipv6_sock_mc_drop(struct sock *sk, int ifindex, - const struct in6_addr *addr); #endif /* _NET_IPV6_H */ diff --git a/net/ipv4/devinet.c b/net/ipv4/devinet.c index 375dc71b9a64..975ee5e30c64 100644 --- a/net/ipv4/devinet.c +++ b/net/ipv4/devinet.c @@ -560,9 +560,9 @@ static int ip_mc_config(struct sock *sk, bool join, const struct in_ifaddr *ifa) lock_sock(sk); if (join) - ret = __ip_mc_join_group(sk, &mreq); + ret = ip_mc_join_group(sk, &mreq); else - ret = __ip_mc_leave_group(sk, &mreq); + ret = ip_mc_leave_group(sk, &mreq); release_sock(sk); return ret; diff --git a/net/ipv4/igmp.c b/net/ipv4/igmp.c index 5cb1ef4ce292..ad3f866085de 100644 --- a/net/ipv4/igmp.c +++ b/net/ipv4/igmp.c @@ -1850,7 +1850,10 @@ static void ip_mc_clear_src(struct ip_mc_list *pmc) pmc->sfcount[MCAST_EXCLUDE] = 1; } -int __ip_mc_join_group(struct sock *sk, struct ip_mreqn *imr) +/* Join a multicast group + */ + +int ip_mc_join_group(struct sock *sk, struct ip_mreqn *imr) { __be32 addr = imr->imr_multiaddr.s_addr; struct ip_mc_socklist *iml, *i; @@ -1898,20 +1901,6 @@ int __ip_mc_join_group(struct sock *sk, struct ip_mreqn *imr) done: return err; } -EXPORT_SYMBOL(__ip_mc_join_group); - -/* Join a multicast group - */ -int ip_mc_join_group(struct sock *sk, struct ip_mreqn *imr) -{ - int ret; - - rtnl_lock(); - ret = __ip_mc_join_group(sk, imr); - rtnl_unlock(); - - return ret; -} EXPORT_SYMBOL(ip_mc_join_group); static int ip_mc_leave_src(struct sock *sk, struct ip_mc_socklist *iml, @@ -1934,7 +1923,7 @@ static int ip_mc_leave_src(struct sock *sk, struct ip_mc_socklist *iml, return err; } -int __ip_mc_leave_group(struct sock *sk, struct ip_mreqn *imr) +int ip_mc_leave_group(struct sock *sk, struct ip_mreqn *imr) { struct inet_sock *inet = inet_sk(sk); struct ip_mc_socklist *iml; @@ -1979,18 +1968,6 @@ int __ip_mc_leave_group(struct sock *sk, struct ip_mreqn *imr) out: return ret; } -EXPORT_SYMBOL(__ip_mc_leave_group); - -int ip_mc_leave_group(struct sock *sk, struct ip_mreqn *imr) -{ - int ret; - - rtnl_lock(); - ret = __ip_mc_leave_group(sk, imr); - rtnl_unlock(); - - return ret; -} EXPORT_SYMBOL(ip_mc_leave_group); int ip_mc_source(int add, int omode, struct sock *sk, struct @@ -2010,7 +1987,7 @@ int ip_mc_source(int add, int omode, struct sock *sk, struct if (!ipv4_is_multicast(addr)) return -EINVAL; - rtnl_lock(); + ASSERT_RTNL(); imr.imr_multiaddr.s_addr = mreqs->imr_multiaddr; imr.imr_address.s_addr = mreqs->imr_interface; @@ -2124,9 +2101,8 @@ int ip_mc_source(int add, int omode, struct sock *sk, struct ip_mc_add_src(in_dev, &mreqs->imr_multiaddr, omode, 1, &mreqs->imr_sourceaddr, 1); done: - rtnl_unlock(); if (leavegroup) - return ip_mc_leave_group(sk, &imr); + err = ip_mc_leave_group(sk, &imr); return err; } @@ -2148,7 +2124,7 @@ int ip_mc_msfilter(struct sock *sk, struct ip_msfilter *msf, int ifindex) msf->imsf_fmode != MCAST_EXCLUDE) return -EINVAL; - rtnl_lock(); + ASSERT_RTNL(); imr.imr_multiaddr.s_addr = msf->imsf_multiaddr; imr.imr_address.s_addr = msf->imsf_interface; @@ -2210,7 +2186,6 @@ int ip_mc_msfilter(struct sock *sk, struct ip_msfilter *msf, int ifindex) pmc->sfmode = msf->imsf_fmode; err = 0; done: - rtnl_unlock(); if (leavegroup) err = ip_mc_leave_group(sk, &imr); return err; diff --git a/net/ipv4/ip_sockglue.c b/net/ipv4/ip_sockglue.c index 5171709199f4..f6a0d54b308a 100644 --- a/net/ipv4/ip_sockglue.c +++ b/net/ipv4/ip_sockglue.c @@ -541,9 +541,18 @@ static bool setsockopt_needs_rtnl(int optname) switch (optname) { case IP_ADD_MEMBERSHIP: case IP_ADD_SOURCE_MEMBERSHIP: + case IP_BLOCK_SOURCE: case IP_DROP_MEMBERSHIP: + case IP_DROP_SOURCE_MEMBERSHIP: + case IP_MSFILTER: + case IP_UNBLOCK_SOURCE: + case MCAST_BLOCK_SOURCE: + case MCAST_MSFILTER: case MCAST_JOIN_GROUP: + case MCAST_JOIN_SOURCE_GROUP: case MCAST_LEAVE_GROUP: + case MCAST_LEAVE_SOURCE_GROUP: + case MCAST_UNBLOCK_SOURCE: return true; } return false; @@ -861,9 +870,9 @@ static int do_ip_setsockopt(struct sock *sk, int level, } if (optname == IP_ADD_MEMBERSHIP) - err = __ip_mc_join_group(sk, &mreq); + err = ip_mc_join_group(sk, &mreq); else - err = __ip_mc_leave_group(sk, &mreq); + err = ip_mc_leave_group(sk, &mreq); break; } case IP_MSFILTER: @@ -928,7 +937,7 @@ static int do_ip_setsockopt(struct sock *sk, int level, mreq.imr_multiaddr.s_addr = mreqs.imr_multiaddr; mreq.imr_address.s_addr = mreqs.imr_interface; mreq.imr_ifindex = 0; - err = __ip_mc_join_group(sk, &mreq); + err = ip_mc_join_group(sk, &mreq); if (err && err != -EADDRINUSE) break; omode = MCAST_INCLUDE; @@ -960,9 +969,9 @@ static int do_ip_setsockopt(struct sock *sk, int level, mreq.imr_ifindex = greq.gr_interface; if (optname == MCAST_JOIN_GROUP) - err = __ip_mc_join_group(sk, &mreq); + err = ip_mc_join_group(sk, &mreq); else - err = __ip_mc_leave_group(sk, &mreq); + err = ip_mc_leave_group(sk, &mreq); break; } case MCAST_JOIN_SOURCE_GROUP: @@ -1005,7 +1014,7 @@ static int do_ip_setsockopt(struct sock *sk, int level, mreq.imr_multiaddr = psin->sin_addr; mreq.imr_address.s_addr = 0; mreq.imr_ifindex = greqs.gsr_interface; - err = __ip_mc_join_group(sk, &mreq); + err = ip_mc_join_group(sk, &mreq); if (err && err != -EADDRINUSE) break; greqs.gsr_interface = mreq.imr_ifindex; diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c index 88d2cf0cae52..158378e73f0a 100644 --- a/net/ipv6/addrconf.c +++ b/net/ipv6/addrconf.c @@ -2473,9 +2473,9 @@ static int ipv6_mc_config(struct sock *sk, bool join, lock_sock(sk); if (join) - ret = __ipv6_sock_mc_join(sk, ifindex, addr); + ret = ipv6_sock_mc_join(sk, ifindex, addr); else - ret = __ipv6_sock_mc_drop(sk, ifindex, addr); + ret = ipv6_sock_mc_drop(sk, ifindex, addr); release_sock(sk); return ret; diff --git a/net/ipv6/ipv6_sockglue.c b/net/ipv6/ipv6_sockglue.c index f2b731df8d77..cc5883791bac 100644 --- a/net/ipv6/ipv6_sockglue.c +++ b/net/ipv6/ipv6_sockglue.c @@ -124,6 +124,11 @@ static bool setsockopt_needs_rtnl(int optname) case IPV6_DROP_MEMBERSHIP: case MCAST_JOIN_GROUP: case MCAST_LEAVE_GROUP: + case MCAST_JOIN_SOURCE_GROUP: + case MCAST_LEAVE_SOURCE_GROUP: + case MCAST_BLOCK_SOURCE: + case MCAST_UNBLOCK_SOURCE: + case MCAST_MSFILTER: return true; } return false; @@ -597,9 +602,9 @@ done: break; if (optname == IPV6_ADD_MEMBERSHIP) - retv = __ipv6_sock_mc_join(sk, mreq.ipv6mr_ifindex, &mreq.ipv6mr_multiaddr); + retv = ipv6_sock_mc_join(sk, mreq.ipv6mr_ifindex, &mreq.ipv6mr_multiaddr); else - retv = __ipv6_sock_mc_drop(sk, mreq.ipv6mr_ifindex, &mreq.ipv6mr_multiaddr); + retv = ipv6_sock_mc_drop(sk, mreq.ipv6mr_ifindex, &mreq.ipv6mr_multiaddr); break; } case IPV6_JOIN_ANYCAST: @@ -638,11 +643,11 @@ done: } psin6 = (struct sockaddr_in6 *)&greq.gr_group; if (optname == MCAST_JOIN_GROUP) - retv = __ipv6_sock_mc_join(sk, greq.gr_interface, - &psin6->sin6_addr); + retv = ipv6_sock_mc_join(sk, greq.gr_interface, + &psin6->sin6_addr); else - retv = __ipv6_sock_mc_drop(sk, greq.gr_interface, - &psin6->sin6_addr); + retv = ipv6_sock_mc_drop(sk, greq.gr_interface, + &psin6->sin6_addr); break; } case MCAST_JOIN_SOURCE_GROUP: @@ -674,8 +679,8 @@ done: struct sockaddr_in6 *psin6; psin6 = (struct sockaddr_in6 *)&greqs.gsr_group; - retv = __ipv6_sock_mc_join(sk, greqs.gsr_interface, - &psin6->sin6_addr); + retv = ipv6_sock_mc_join(sk, greqs.gsr_interface, + &psin6->sin6_addr); /* prior join w/ different source is ok */ if (retv && retv != -EADDRINUSE) break; diff --git a/net/ipv6/mcast.c b/net/ipv6/mcast.c index 1dd1fedff9f4..cbb66fd3da6d 100644 --- a/net/ipv6/mcast.c +++ b/net/ipv6/mcast.c @@ -132,7 +132,7 @@ static int unsolicited_report_interval(struct inet6_dev *idev) return iv > 0 ? iv : 1; } -int __ipv6_sock_mc_join(struct sock *sk, int ifindex, const struct in6_addr *addr) +int ipv6_sock_mc_join(struct sock *sk, int ifindex, const struct in6_addr *addr) { struct net_device *dev = NULL; struct ipv6_mc_socklist *mc_lst; @@ -199,24 +199,12 @@ int __ipv6_sock_mc_join(struct sock *sk, int ifindex, const struct in6_addr *add return 0; } -EXPORT_SYMBOL(__ipv6_sock_mc_join); - -int ipv6_sock_mc_join(struct sock *sk, int ifindex, const struct in6_addr *addr) -{ - int ret; - - rtnl_lock(); - ret = __ipv6_sock_mc_join(sk, ifindex, addr); - rtnl_unlock(); - - return ret; -} EXPORT_SYMBOL(ipv6_sock_mc_join); /* * socket leave on multicast group */ -int __ipv6_sock_mc_drop(struct sock *sk, int ifindex, const struct in6_addr *addr) +int ipv6_sock_mc_drop(struct sock *sk, int ifindex, const struct in6_addr *addr) { struct ipv6_pinfo *np = inet6_sk(sk); struct ipv6_mc_socklist *mc_lst; @@ -255,18 +243,6 @@ int __ipv6_sock_mc_drop(struct sock *sk, int ifindex, const struct in6_addr *add return -EADDRNOTAVAIL; } -EXPORT_SYMBOL(__ipv6_sock_mc_drop); - -int ipv6_sock_mc_drop(struct sock *sk, int ifindex, const struct in6_addr *addr) -{ - int ret; - - rtnl_lock(); - ret = __ipv6_sock_mc_drop(sk, ifindex, addr); - rtnl_unlock(); - - return ret; -} EXPORT_SYMBOL(ipv6_sock_mc_drop); /* called with rcu_read_lock() */ @@ -460,7 +436,7 @@ done: read_unlock_bh(&idev->lock); rcu_read_unlock(); if (leavegroup) - return ipv6_sock_mc_drop(sk, pgsr->gsr_interface, group); + err = ipv6_sock_mc_drop(sk, pgsr->gsr_interface, group); return err; } diff --git a/net/netfilter/ipvs/ip_vs_sync.c b/net/netfilter/ipvs/ip_vs_sync.c index 08d95559b6f7..19b9cce6c210 100644 --- a/net/netfilter/ipvs/ip_vs_sync.c +++ b/net/netfilter/ipvs/ip_vs_sync.c @@ -1405,9 +1405,11 @@ join_mcast_group(struct sock *sk, struct in_addr *addr, char *ifname) mreq.imr_ifindex = dev->ifindex; + rtnl_lock(); lock_sock(sk); ret = ip_mc_join_group(sk, &mreq); release_sock(sk); + rtnl_unlock(); return ret; } diff --git a/net/tipc/udp_media.c b/net/tipc/udp_media.c index fc2fb11a354d..04836dd70c2b 100644 --- a/net/tipc/udp_media.c +++ b/net/tipc/udp_media.c @@ -246,11 +246,11 @@ static int enable_mcast(struct udp_bearer *ub, struct udp_media_addr *remote) return 0; mreqn.imr_multiaddr = remote->ipv4; mreqn.imr_ifindex = ub->ifindex; - err = __ip_mc_join_group(sk, &mreqn); + err = ip_mc_join_group(sk, &mreqn); } else { if (!ipv6_addr_is_multicast(&remote->ipv6)) return 0; - err = __ipv6_sock_mc_join(sk, ub->ifindex, &remote->ipv6); + err = ipv6_sock_mc_join(sk, ub->ifindex, &remote->ipv6); } return err; } -- cgit v1.2.3 From db24a9044ee191c397dcd1c6574f56d67d7c8df5 Mon Sep 17 00:00:00 2001 From: David Ahern Date: Tue, 17 Mar 2015 20:23:15 -0600 Subject: net: add support for phys_port_name Similar to port id allow netdevices to specify port names and export the name via sysfs. Drivers can implement the netdevice operation to assist udev in having sane default names for the devices using the rule: $ cat /etc/udev/rules.d/80-net-setup-link.rules SUBSYSTEM=="net", ACTION=="add", ATTR{phys_port_name}!="", NAME="$attr{phys_port_name}" Use of phys_name versus phys_id was suggested-by Jiri Pirko. Signed-off-by: David Ahern Acked-by: Jiri Pirko Acked-by: Scott Feldman Signed-off-by: David S. Miller --- Documentation/ABI/testing/sysfs-class-net | 8 ++++++++ include/linux/netdevice.h | 4 ++++ include/uapi/linux/if_link.h | 1 + net/core/dev.c | 18 ++++++++++++++++++ net/core/net-sysfs.c | 23 +++++++++++++++++++++++ net/core/rtnetlink.c | 21 +++++++++++++++++++++ 6 files changed, 75 insertions(+) (limited to 'include') diff --git a/Documentation/ABI/testing/sysfs-class-net b/Documentation/ABI/testing/sysfs-class-net index beb8ec4dabbc..5ecfd72ba684 100644 --- a/Documentation/ABI/testing/sysfs-class-net +++ b/Documentation/ABI/testing/sysfs-class-net @@ -188,6 +188,14 @@ Description: Indicates the interface unique physical port identifier within the NIC, as a string. +What: /sys/class/net//phys_port_name +Date: March 2015 +KernelVersion: 4.0 +Contact: netdev@vger.kernel.org +Description: + Indicates the interface physical port name within the NIC, + as a string. + What: /sys/class/net//speed Date: October 2009 KernelVersion: 2.6.33 diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 76c5de4978a8..ec8f9b5f6500 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -1164,6 +1164,8 @@ struct net_device_ops { bool new_carrier); int (*ndo_get_phys_port_id)(struct net_device *dev, struct netdev_phys_item_id *ppid); + int (*ndo_get_phys_port_name)(struct net_device *dev, + char *name, size_t len); void (*ndo_add_vxlan_port)(struct net_device *dev, sa_family_t sa_family, __be16 port); @@ -2947,6 +2949,8 @@ int dev_set_mac_address(struct net_device *, struct sockaddr *); int dev_change_carrier(struct net_device *, bool new_carrier); int dev_get_phys_port_id(struct net_device *dev, struct netdev_phys_item_id *ppid); +int dev_get_phys_port_name(struct net_device *dev, + char *name, size_t len); struct sk_buff *validate_xmit_skb_list(struct sk_buff *skb, struct net_device *dev); struct sk_buff *dev_hard_start_xmit(struct sk_buff *skb, struct net_device *dev, struct netdev_queue *txq, int *ret); diff --git a/include/uapi/linux/if_link.h b/include/uapi/linux/if_link.h index 756436e1ce89..7158fd00a109 100644 --- a/include/uapi/linux/if_link.h +++ b/include/uapi/linux/if_link.h @@ -147,6 +147,7 @@ enum { IFLA_CARRIER_CHANGES, IFLA_PHYS_SWITCH_ID, IFLA_LINK_NETNSID, + IFLA_PHYS_PORT_NAME, __IFLA_MAX }; diff --git a/net/core/dev.c b/net/core/dev.c index 39fe369b46ad..a1f24151db5b 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -5911,6 +5911,24 @@ int dev_get_phys_port_id(struct net_device *dev, } EXPORT_SYMBOL(dev_get_phys_port_id); +/** + * dev_get_phys_port_name - Get device physical port name + * @dev: device + * @name: port name + * + * Get device physical port name + */ +int dev_get_phys_port_name(struct net_device *dev, + char *name, size_t len) +{ + const struct net_device_ops *ops = dev->netdev_ops; + + if (!ops->ndo_get_phys_port_name) + return -EOPNOTSUPP; + return ops->ndo_get_phys_port_name(dev, name, len); +} +EXPORT_SYMBOL(dev_get_phys_port_name); + /** * dev_new_index - allocate an ifindex * @net: the applicable net namespace diff --git a/net/core/net-sysfs.c b/net/core/net-sysfs.c index 7e58bd7ec232..cc5cf689809c 100644 --- a/net/core/net-sysfs.c +++ b/net/core/net-sysfs.c @@ -418,6 +418,28 @@ static ssize_t phys_port_id_show(struct device *dev, } static DEVICE_ATTR_RO(phys_port_id); +static ssize_t phys_port_name_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct net_device *netdev = to_net_dev(dev); + ssize_t ret = -EINVAL; + + if (!rtnl_trylock()) + return restart_syscall(); + + if (dev_isalive(netdev)) { + char name[IFNAMSIZ]; + + ret = dev_get_phys_port_name(netdev, name, sizeof(name)); + if (!ret) + ret = sprintf(buf, "%s\n", name); + } + rtnl_unlock(); + + return ret; +} +static DEVICE_ATTR_RO(phys_port_name); + static ssize_t phys_switch_id_show(struct device *dev, struct device_attribute *attr, char *buf) { @@ -465,6 +487,7 @@ static struct attribute *net_class_attrs[] = { &dev_attr_tx_queue_len.attr, &dev_attr_gro_flush_timeout.attr, &dev_attr_phys_port_id.attr, + &dev_attr_phys_port_name.attr, &dev_attr_phys_switch_id.attr, NULL, }; diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c index 25b4b5d23485..6abe634c666c 100644 --- a/net/core/rtnetlink.c +++ b/net/core/rtnetlink.c @@ -982,6 +982,24 @@ static int rtnl_phys_port_id_fill(struct sk_buff *skb, struct net_device *dev) return 0; } +static int rtnl_phys_port_name_fill(struct sk_buff *skb, struct net_device *dev) +{ + char name[IFNAMSIZ]; + int err; + + err = dev_get_phys_port_name(dev, name, sizeof(name)); + if (err) { + if (err == -EOPNOTSUPP) + return 0; + return err; + } + + if (nla_put(skb, IFLA_PHYS_PORT_NAME, strlen(name), name)) + return -EMSGSIZE; + + return 0; +} + static int rtnl_phys_switch_id_fill(struct sk_buff *skb, struct net_device *dev) { int err; @@ -1072,6 +1090,9 @@ static int rtnl_fill_ifinfo(struct sk_buff *skb, struct net_device *dev, if (rtnl_phys_port_id_fill(skb, dev)) goto nla_put_failure; + if (rtnl_phys_port_name_fill(skb, dev)) + goto nla_put_failure; + if (rtnl_phys_switch_id_fill(skb, dev)) goto nla_put_failure; -- cgit v1.2.3 From 99c4a26a159b28fa46a3e746a9b41b297e73d261 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Wed, 18 Mar 2015 22:52:33 -0400 Subject: net: Fix high overhead of vlan sub-device teardown. When a networking device is taken down that has a non-trivial number of VLAN devices configured under it, we eat a full synchronize_net() for every such VLAN device. This is because of the call chain: NETDEV_DOWN notifier --> vlan_device_event() --> dev_change_flags() --> __dev_change_flags() --> __dev_close() --> __dev_close_many() --> dev_deactivate_many() --> synchronize_net() This is kind of rediculous because we already have infrastructure for batching doing operation X to a list of net devices so that we only incur one sync. So make use of that by exporting dev_close_many() and adjusting it's interfaace so that the caller can fully manage the batch list. Use this in vlan_device_event() and all the overhead goes away. Reported-by: Salam Noureddine Signed-off-by: David S. Miller --- include/linux/netdevice.h | 1 + net/8021q/vlan.c | 16 +++++++++++++--- net/core/dev.c | 10 ++++++---- 3 files changed, 20 insertions(+), 7 deletions(-) (limited to 'include') diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index ec8f9b5f6500..76951c5fbedf 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -2156,6 +2156,7 @@ struct net_device *__dev_get_by_name(struct net *net, const char *name); int dev_alloc_name(struct net_device *dev, const char *name); int dev_open(struct net_device *dev); int dev_close(struct net_device *dev); +int dev_close_many(struct list_head *head, bool unlink); void dev_disable_lro(struct net_device *dev); int dev_loopback_xmit(struct sk_buff *newskb); int dev_queue_xmit(struct sk_buff *skb); diff --git a/net/8021q/vlan.c b/net/8021q/vlan.c index 64c6bed4a3d3..98a30a5b8664 100644 --- a/net/8021q/vlan.c +++ b/net/8021q/vlan.c @@ -413,7 +413,10 @@ static int vlan_device_event(struct notifier_block *unused, unsigned long event, vlan_transfer_features(dev, vlandev); break; - case NETDEV_DOWN: + case NETDEV_DOWN: { + struct net_device *tmp; + LIST_HEAD(close_list); + if (dev->features & NETIF_F_HW_VLAN_CTAG_FILTER) vlan_vid_del(dev, htons(ETH_P_8021Q), 0); @@ -425,11 +428,18 @@ static int vlan_device_event(struct notifier_block *unused, unsigned long event, vlan = vlan_dev_priv(vlandev); if (!(vlan->flags & VLAN_FLAG_LOOSE_BINDING)) - dev_change_flags(vlandev, flgs & ~IFF_UP); + list_add(&vlandev->close_list, &close_list); + } + + dev_close_many(&close_list, false); + + list_for_each_entry_safe(vlandev, tmp, &close_list, close_list) { netif_stacked_transfer_operstate(dev, vlandev); + list_del_init(&vlandev->close_list); } + list_del(&close_list); break; - + } case NETDEV_UP: /* Put all VLANs for this dev in the up state too. */ vlan_group_for_each_dev(grp, i, vlandev) { diff --git a/net/core/dev.c b/net/core/dev.c index a1f24151db5b..5d43e010ef87 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -1385,7 +1385,7 @@ static int __dev_close(struct net_device *dev) return retval; } -static int dev_close_many(struct list_head *head) +int dev_close_many(struct list_head *head, bool unlink) { struct net_device *dev, *tmp; @@ -1399,11 +1399,13 @@ static int dev_close_many(struct list_head *head) list_for_each_entry_safe(dev, tmp, head, close_list) { rtmsg_ifinfo(RTM_NEWLINK, dev, IFF_UP|IFF_RUNNING, GFP_KERNEL); call_netdevice_notifiers(NETDEV_DOWN, dev); - list_del_init(&dev->close_list); + if (unlink) + list_del_init(&dev->close_list); } return 0; } +EXPORT_SYMBOL(dev_close_many); /** * dev_close - shutdown an interface. @@ -1420,7 +1422,7 @@ int dev_close(struct net_device *dev) LIST_HEAD(single); list_add(&dev->close_list, &single); - dev_close_many(&single); + dev_close_many(&single, true); list_del(&single); } return 0; @@ -5986,7 +5988,7 @@ static void rollback_registered_many(struct list_head *head) /* If device is running, close it first. */ list_for_each_entry(dev, head, unreg_list) list_add_tail(&dev->close_list, &close_head); - dev_close_many(&close_head); + dev_close_many(&close_head, true); list_for_each_entry(dev, head, unreg_list) { /* And unlink it from device chain. */ -- cgit v1.2.3 From af615762e972be0c66cf1d156ca4fac13b93c0b0 Mon Sep 17 00:00:00 2001 From: Jörg Thalheim Date: Wed, 18 Mar 2015 10:06:58 +0100 Subject: bridge: add ageing_time, stp_state, priority over netlink MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Jörg Thalheim Signed-off-by: David S. Miller --- include/uapi/linux/if_link.h | 3 +++ net/bridge/br_netlink.c | 32 +++++++++++++++++++++++++++++++- 2 files changed, 34 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/uapi/linux/if_link.h b/include/uapi/linux/if_link.h index 7158fd00a109..f5f5edd5ae5f 100644 --- a/include/uapi/linux/if_link.h +++ b/include/uapi/linux/if_link.h @@ -225,6 +225,9 @@ enum { IFLA_BR_FORWARD_DELAY, IFLA_BR_HELLO_TIME, IFLA_BR_MAX_AGE, + IFLA_BR_AGEING_TIME, + IFLA_BR_STP_STATE, + IFLA_BR_PRIORITY, __IFLA_BR_MAX, }; diff --git a/net/bridge/br_netlink.c b/net/bridge/br_netlink.c index 8bc6b67457dc..e1115a224a95 100644 --- a/net/bridge/br_netlink.c +++ b/net/bridge/br_netlink.c @@ -733,6 +733,9 @@ static const struct nla_policy br_policy[IFLA_BR_MAX + 1] = { [IFLA_BR_FORWARD_DELAY] = { .type = NLA_U32 }, [IFLA_BR_HELLO_TIME] = { .type = NLA_U32 }, [IFLA_BR_MAX_AGE] = { .type = NLA_U32 }, + [IFLA_BR_AGEING_TIME] = { .type = NLA_U32 }, + [IFLA_BR_STP_STATE] = { .type = NLA_U32 }, + [IFLA_BR_PRIORITY] = { .type = NLA_U16 }, }; static int br_changelink(struct net_device *brdev, struct nlattr *tb[], @@ -762,6 +765,24 @@ static int br_changelink(struct net_device *brdev, struct nlattr *tb[], return err; } + if (data[IFLA_BR_AGEING_TIME]) { + u32 ageing_time = nla_get_u32(data[IFLA_BR_AGEING_TIME]); + + br->ageing_time = clock_t_to_jiffies(ageing_time); + } + + if (data[IFLA_BR_STP_STATE]) { + u32 stp_enabled = nla_get_u32(data[IFLA_BR_STP_STATE]); + + br_stp_set_enabled(br, stp_enabled); + } + + if (data[IFLA_BR_PRIORITY]) { + u32 priority = nla_get_u16(data[IFLA_BR_PRIORITY]); + + br_stp_set_bridge_priority(br, priority); + } + return 0; } @@ -770,6 +791,9 @@ static size_t br_get_size(const struct net_device *brdev) return nla_total_size(sizeof(u32)) + /* IFLA_BR_FORWARD_DELAY */ nla_total_size(sizeof(u32)) + /* IFLA_BR_HELLO_TIME */ nla_total_size(sizeof(u32)) + /* IFLA_BR_MAX_AGE */ + nla_total_size(sizeof(u32)) + /* IFLA_BR_AGEING_TIME */ + nla_total_size(sizeof(u32)) + /* IFLA_BR_STP_STATE */ + nla_total_size(sizeof(u16)) + /* IFLA_BR_PRIORITY */ 0; } @@ -779,10 +803,16 @@ static int br_fill_info(struct sk_buff *skb, const struct net_device *brdev) u32 forward_delay = jiffies_to_clock_t(br->forward_delay); u32 hello_time = jiffies_to_clock_t(br->hello_time); u32 age_time = jiffies_to_clock_t(br->max_age); + u32 ageing_time = jiffies_to_clock_t(br->ageing_time); + u32 stp_enabled = br->stp_enabled; + u16 priority = (br->bridge_id.prio[0] << 8) | br->bridge_id.prio[1]; if (nla_put_u32(skb, IFLA_BR_FORWARD_DELAY, forward_delay) || nla_put_u32(skb, IFLA_BR_HELLO_TIME, hello_time) || - nla_put_u32(skb, IFLA_BR_MAX_AGE, age_time)) + nla_put_u32(skb, IFLA_BR_MAX_AGE, age_time) || + nla_put_u32(skb, IFLA_BR_AGEING_TIME, ageing_time) || + nla_put_u32(skb, IFLA_BR_STP_STATE, stp_enabled) || + nla_put_u16(skb, IFLA_BR_PRIORITY, priority)) return -EMSGSIZE; return 0; -- cgit v1.2.3 From 964cb341882f920a1a1043864178f22def3193e4 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Wed, 18 Mar 2015 01:56:17 +0100 Subject: gpio: move pincontrol calls to These functions do not belong in since the split into separate GPIO headers under . Move them to as is apropriate. Acked-by: Alexandre Courbot Signed-off-by: Linus Walleij --- include/asm-generic/gpio.h | 48 --------------------------------------------- include/linux/gpio/driver.h | 48 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 48 insertions(+), 48 deletions(-) (limited to 'include') diff --git a/include/asm-generic/gpio.h b/include/asm-generic/gpio.h index 383ade1a211b..9bb0d11729c9 100644 --- a/include/asm-generic/gpio.h +++ b/include/asm-generic/gpio.h @@ -5,7 +5,6 @@ #include #include #include -#include #ifdef CONFIG_GPIOLIB @@ -139,53 +138,6 @@ static inline void gpio_unexport(unsigned gpio) gpiod_unexport(gpio_to_desc(gpio)); } -#ifdef CONFIG_PINCTRL - -/** - * struct gpio_pin_range - pin range controlled by a gpio chip - * @head: list for maintaining set of pin ranges, used internally - * @pctldev: pinctrl device which handles corresponding pins - * @range: actual range of pins controlled by a gpio controller - */ - -struct gpio_pin_range { - struct list_head node; - struct pinctrl_dev *pctldev; - struct pinctrl_gpio_range range; -}; - -int gpiochip_add_pin_range(struct gpio_chip *chip, const char *pinctl_name, - unsigned int gpio_offset, unsigned int pin_offset, - unsigned int npins); -int gpiochip_add_pingroup_range(struct gpio_chip *chip, - struct pinctrl_dev *pctldev, - unsigned int gpio_offset, const char *pin_group); -void gpiochip_remove_pin_ranges(struct gpio_chip *chip); - -#else - -static inline int -gpiochip_add_pin_range(struct gpio_chip *chip, const char *pinctl_name, - unsigned int gpio_offset, unsigned int pin_offset, - unsigned int npins) -{ - return 0; -} -static inline int -gpiochip_add_pingroup_range(struct gpio_chip *chip, - struct pinctrl_dev *pctldev, - unsigned int gpio_offset, const char *pin_group) -{ - return 0; -} - -static inline void -gpiochip_remove_pin_ranges(struct gpio_chip *chip) -{ -} - -#endif /* CONFIG_PINCTRL */ - #else /* !CONFIG_GPIOLIB */ static inline bool gpio_is_valid(int number) diff --git a/include/linux/gpio/driver.h b/include/linux/gpio/driver.h index c497c62889d1..f1b36593ec9f 100644 --- a/include/linux/gpio/driver.h +++ b/include/linux/gpio/driver.h @@ -6,6 +6,7 @@ #include #include #include +#include struct device; struct gpio_desc; @@ -173,6 +174,53 @@ int gpiochip_irqchip_add(struct gpio_chip *gpiochip, #endif /* CONFIG_GPIOLIB_IRQCHIP */ +#ifdef CONFIG_PINCTRL + +/** + * struct gpio_pin_range - pin range controlled by a gpio chip + * @head: list for maintaining set of pin ranges, used internally + * @pctldev: pinctrl device which handles corresponding pins + * @range: actual range of pins controlled by a gpio controller + */ + +struct gpio_pin_range { + struct list_head node; + struct pinctrl_dev *pctldev; + struct pinctrl_gpio_range range; +}; + +int gpiochip_add_pin_range(struct gpio_chip *chip, const char *pinctl_name, + unsigned int gpio_offset, unsigned int pin_offset, + unsigned int npins); +int gpiochip_add_pingroup_range(struct gpio_chip *chip, + struct pinctrl_dev *pctldev, + unsigned int gpio_offset, const char *pin_group); +void gpiochip_remove_pin_ranges(struct gpio_chip *chip); + +#else + +static inline int +gpiochip_add_pin_range(struct gpio_chip *chip, const char *pinctl_name, + unsigned int gpio_offset, unsigned int pin_offset, + unsigned int npins) +{ + return 0; +} +static inline int +gpiochip_add_pingroup_range(struct gpio_chip *chip, + struct pinctrl_dev *pctldev, + unsigned int gpio_offset, const char *pin_group) +{ + return 0; +} + +static inline void +gpiochip_remove_pin_ranges(struct gpio_chip *chip) +{ +} + +#endif /* CONFIG_PINCTRL */ + struct gpio_desc *gpiochip_request_own_desc(struct gpio_chip *chip, u16 hwnum, const char *label); void gpiochip_free_own_desc(struct gpio_desc *desc); -- cgit v1.2.3 From d6d2a1882a79c1a5425d6f82b2fc7b934916f893 Mon Sep 17 00:00:00 2001 From: Alex Deucher Date: Tue, 30 Sep 2014 10:04:40 -0400 Subject: drm/radeon: add INFO query for GPU temperature MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Useful for profiling. Tested-by: Marek Olšák Signed-off-by: Alex Deucher --- drivers/gpu/drm/radeon/radeon_kms.c | 7 +++++++ include/uapi/drm/radeon_drm.h | 1 + 2 files changed, 8 insertions(+) (limited to 'include') diff --git a/drivers/gpu/drm/radeon/radeon_kms.c b/drivers/gpu/drm/radeon/radeon_kms.c index 686411e4e4f6..7904887c6cc7 100644 --- a/drivers/gpu/drm/radeon/radeon_kms.c +++ b/drivers/gpu/drm/radeon/radeon_kms.c @@ -547,6 +547,13 @@ static int radeon_info_ioctl(struct drm_device *dev, void *data, struct drm_file else *value = 1; break; + case RADEON_INFO_CURRENT_GPU_TEMP: + /* get temperature in millidegrees C */ + if (rdev->asic->pm.get_temperature) + *value = radeon_get_temperature(rdev); + else + *value = 0; + break; default: DRM_DEBUG_KMS("Invalid request %d\n", info->request); return -EINVAL; diff --git a/include/uapi/drm/radeon_drm.h b/include/uapi/drm/radeon_drm.h index 50d0fb41a3bf..66b1131bb296 100644 --- a/include/uapi/drm/radeon_drm.h +++ b/include/uapi/drm/radeon_drm.h @@ -1034,6 +1034,7 @@ struct drm_radeon_cs { #define RADEON_INFO_VRAM_USAGE 0x1e #define RADEON_INFO_GTT_USAGE 0x1f #define RADEON_INFO_ACTIVE_CU_COUNT 0x20 +#define RADEON_INFO_CURRENT_GPU_TEMP 0x21 struct drm_radeon_info { uint32_t request; -- cgit v1.2.3 From 5c363a860398e2a09e5cff7f4654cf82ed8485e1 Mon Sep 17 00:00:00 2001 From: Alex Deucher Date: Tue, 30 Sep 2014 11:33:30 -0400 Subject: drm/radeon: add INFO query for current sclk/mclk MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Allow the UMDs to query the current sclk/mclk for profiling, etc. Tested-by: Marek Olšák Signed-off-by: Alex Deucher --- drivers/gpu/drm/radeon/radeon_kms.c | 14 ++++++++++++++ include/uapi/drm/radeon_drm.h | 2 ++ 2 files changed, 16 insertions(+) (limited to 'include') diff --git a/drivers/gpu/drm/radeon/radeon_kms.c b/drivers/gpu/drm/radeon/radeon_kms.c index 7904887c6cc7..cf7e54e9b0d1 100644 --- a/drivers/gpu/drm/radeon/radeon_kms.c +++ b/drivers/gpu/drm/radeon/radeon_kms.c @@ -554,6 +554,20 @@ static int radeon_info_ioctl(struct drm_device *dev, void *data, struct drm_file else *value = 0; break; + case RADEON_INFO_CURRENT_GPU_SCLK: + /* get sclk in Mhz */ + if (rdev->pm.dpm_enabled) + *value = radeon_dpm_get_current_sclk(rdev) / 100; + else + *value = rdev->pm.current_sclk / 100; + break; + case RADEON_INFO_CURRENT_GPU_MCLK: + /* get mclk in Mhz */ + if (rdev->pm.dpm_enabled) + *value = radeon_dpm_get_current_mclk(rdev) / 100; + else + *value = rdev->pm.current_mclk / 100; + break; default: DRM_DEBUG_KMS("Invalid request %d\n", info->request); return -EINVAL; diff --git a/include/uapi/drm/radeon_drm.h b/include/uapi/drm/radeon_drm.h index 66b1131bb296..de7ee21efea2 100644 --- a/include/uapi/drm/radeon_drm.h +++ b/include/uapi/drm/radeon_drm.h @@ -1035,6 +1035,8 @@ struct drm_radeon_cs { #define RADEON_INFO_GTT_USAGE 0x1f #define RADEON_INFO_ACTIVE_CU_COUNT 0x20 #define RADEON_INFO_CURRENT_GPU_TEMP 0x21 +#define RADEON_INFO_CURRENT_GPU_SCLK 0x22 +#define RADEON_INFO_CURRENT_GPU_MCLK 0x23 struct drm_radeon_info { uint32_t request; -- cgit v1.2.3 From 4535cb9cefbc736b47bc1e7f8628065aba5ca0d7 Mon Sep 17 00:00:00 2001 From: Alex Deucher Date: Wed, 1 Oct 2014 11:26:50 -0400 Subject: drm/radeon: add support for read reg query from radeon info ioctl MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This allows us to query certain registers from userspace for profiling and harvest configuration. E.g., it can be used by the GALLIUM_HUD for profiling the status of various gfx blocks. Tested-by: Marek Olšák Signed-off-by: Alex Deucher --- drivers/gpu/drm/radeon/radeon_kms.c | 8 ++++++++ include/uapi/drm/radeon_drm.h | 1 + 2 files changed, 9 insertions(+) (limited to 'include') diff --git a/drivers/gpu/drm/radeon/radeon_kms.c b/drivers/gpu/drm/radeon/radeon_kms.c index cf7e54e9b0d1..7b2a7335cc5d 100644 --- a/drivers/gpu/drm/radeon/radeon_kms.c +++ b/drivers/gpu/drm/radeon/radeon_kms.c @@ -568,6 +568,14 @@ static int radeon_info_ioctl(struct drm_device *dev, void *data, struct drm_file else *value = rdev->pm.current_mclk / 100; break; + case RADEON_INFO_READ_REG: + if (copy_from_user(value, value_ptr, sizeof(uint32_t))) { + DRM_ERROR("copy_from_user %s:%u\n", __func__, __LINE__); + return -EFAULT; + } + if (radeon_get_allowed_info_register(rdev, *value, value)) + return -EINVAL; + break; default: DRM_DEBUG_KMS("Invalid request %d\n", info->request); return -EINVAL; diff --git a/include/uapi/drm/radeon_drm.h b/include/uapi/drm/radeon_drm.h index de7ee21efea2..871e73f99a4d 100644 --- a/include/uapi/drm/radeon_drm.h +++ b/include/uapi/drm/radeon_drm.h @@ -1037,6 +1037,7 @@ struct drm_radeon_cs { #define RADEON_INFO_CURRENT_GPU_TEMP 0x21 #define RADEON_INFO_CURRENT_GPU_SCLK 0x22 #define RADEON_INFO_CURRENT_GPU_MCLK 0x23 +#define RADEON_INFO_READ_REG 0x24 struct drm_radeon_info { uint32_t request; -- cgit v1.2.3 From 87f5942d1f7bc320a0ec70ac4a8a3396024c7091 Mon Sep 17 00:00:00 2001 From: Dave Airlie Date: Tue, 24 Feb 2015 09:23:55 +1000 Subject: drm/dp_mst: add a function to retrieve vcpi slots radeon requires this to get the slots for later filling out a table on every transition. Signed-off-by: Dave Airlie Signed-off-by: Alex Deucher --- drivers/gpu/drm/drm_dp_mst_topology.c | 13 +++++++++++++ include/drm/drm_dp_mst_helper.h | 2 ++ 2 files changed, 15 insertions(+) (limited to 'include') diff --git a/drivers/gpu/drm/drm_dp_mst_topology.c b/drivers/gpu/drm/drm_dp_mst_topology.c index 9a5b68717ec8..0d15e6e30732 100644 --- a/drivers/gpu/drm/drm_dp_mst_topology.c +++ b/drivers/gpu/drm/drm_dp_mst_topology.c @@ -2319,6 +2319,19 @@ out: } EXPORT_SYMBOL(drm_dp_mst_allocate_vcpi); +int drm_dp_mst_get_vcpi_slots(struct drm_dp_mst_topology_mgr *mgr, struct drm_dp_mst_port *port) +{ + int slots = 0; + port = drm_dp_get_validated_port_ref(mgr, port); + if (!port) + return slots; + + slots = port->vcpi.num_slots; + drm_dp_put_port(port); + return slots; +} +EXPORT_SYMBOL(drm_dp_mst_get_vcpi_slots); + /** * drm_dp_mst_reset_vcpi_slots() - Reset number of slots to 0 for VCPI * @mgr: manager for this port diff --git a/include/drm/drm_dp_mst_helper.h b/include/drm/drm_dp_mst_helper.h index 00c1da927245..a2507817be41 100644 --- a/include/drm/drm_dp_mst_helper.h +++ b/include/drm/drm_dp_mst_helper.h @@ -486,6 +486,8 @@ int drm_dp_calc_pbn_mode(int clock, int bpp); bool drm_dp_mst_allocate_vcpi(struct drm_dp_mst_topology_mgr *mgr, struct drm_dp_mst_port *port, int pbn, int *slots); +int drm_dp_mst_get_vcpi_slots(struct drm_dp_mst_topology_mgr *mgr, struct drm_dp_mst_port *port); + void drm_dp_mst_reset_vcpi_slots(struct drm_dp_mst_topology_mgr *mgr, struct drm_dp_mst_port *port); -- cgit v1.2.3 From 7eec52db361a6ae6fbbd86c2299718586866b664 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Thu, 19 Mar 2015 01:50:22 +0200 Subject: Documentation: DT: Add bindings for omap3isp Signed-off-by: Sakari Ailus Acked-by: Laurent Pinchart Signed-off-by: Tony Lindgren --- .../devicetree/bindings/media/ti,omap3isp.txt | 71 ++++++++++++++++++++++ MAINTAINERS | 1 + include/dt-bindings/media/omap3-isp.h | 22 +++++++ 3 files changed, 94 insertions(+) create mode 100644 Documentation/devicetree/bindings/media/ti,omap3isp.txt create mode 100644 include/dt-bindings/media/omap3-isp.h (limited to 'include') diff --git a/Documentation/devicetree/bindings/media/ti,omap3isp.txt b/Documentation/devicetree/bindings/media/ti,omap3isp.txt new file mode 100644 index 000000000000..ac23de855641 --- /dev/null +++ b/Documentation/devicetree/bindings/media/ti,omap3isp.txt @@ -0,0 +1,71 @@ +OMAP 3 ISP Device Tree bindings +=============================== + +The DT definitions can be found in include/dt-bindings/media/omap3-isp.h. + +Required properties +=================== + +compatible : must contain "ti,omap3-isp" + +reg : the two registers sets (physical address and length) for the + ISP. The first set contains the core ISP registers up to + the end of the SBL block. The second set contains the + CSI PHYs and receivers registers. +interrupts : the ISP interrupt specifier +iommus : phandle and IOMMU specifier for the IOMMU that serves the ISP +syscon : the phandle and register offset to the Complex I/O or CSI-PHY + register +ti,phy-type : 0 -- OMAP3ISP_PHY_TYPE_COMPLEX_IO (e.g. 3430) + 1 -- OMAP3ISP_PHY_TYPE_CSIPHY (e.g. 3630) +#clock-cells : Must be 1 --- the ISP provides two external clocks, + cam_xclka and cam_xclkb, at indices 0 and 1, + respectively. Please find more information on common + clock bindings in ../clock/clock-bindings.txt. + +Port nodes (optional) +--------------------- + +More documentation on these bindings is available in +video-interfaces.txt in the same directory. + +reg : The interface: + 0 - parallel (CCDC) + 1 - CSIPHY1 -- CSI2C / CCP2B on 3630; + CSI1 -- CSIb on 3430 + 2 - CSIPHY2 -- CSI2A / CCP2B on 3630; + CSI2 -- CSIa on 3430 + +Optional properties +=================== + +vdd-csiphy1-supply : voltage supply of the CSI-2 PHY 1 +vdd-csiphy2-supply : voltage supply of the CSI-2 PHY 2 + +Endpoint nodes +-------------- + +lane-polarities : lane polarity (required on CSI-2) + 0 -- not inverted; 1 -- inverted +data-lanes : an array of data lanes from 1 to 3. The length can + be either 1 or 2. (required on CSI-2) +clock-lanes : the clock lane (from 1 to 3). (required on CSI-2) + + +Example +======= + + isp@480bc000 { + compatible = "ti,omap3-isp"; + reg = <0x480bc000 0x12fc + 0x480bd800 0x0600>; + interrupts = <24>; + iommus = <&mmu_isp>; + syscon = <&scm_conf 0x2f0>; + ti,phy-type = ; + #clock-cells = <1>; + ports { + #address-cells = <1>; + #size-cells = <0>; + }; + }; diff --git a/MAINTAINERS b/MAINTAINERS index eaf999638a65..6a271a6ff903 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -7077,6 +7077,7 @@ OMAP IMAGING SUBSYSTEM (OMAP3 ISP and OMAP4 ISS) M: Laurent Pinchart L: linux-media@vger.kernel.org S: Maintained +F: Documentation/devicetree/bindings/media/ti,omap3isp.txt F: drivers/media/platform/omap3isp/ F: drivers/staging/media/omap4iss/ diff --git a/include/dt-bindings/media/omap3-isp.h b/include/dt-bindings/media/omap3-isp.h new file mode 100644 index 000000000000..b18c60e468c7 --- /dev/null +++ b/include/dt-bindings/media/omap3-isp.h @@ -0,0 +1,22 @@ +/* + * include/dt-bindings/media/omap3-isp.h + * + * Copyright (C) 2015 Sakari Ailus + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +#ifndef __DT_BINDINGS_OMAP3_ISP_H__ +#define __DT_BINDINGS_OMAP3_ISP_H__ + +#define OMAP3ISP_PHY_TYPE_COMPLEX_IO 0 +#define OMAP3ISP_PHY_TYPE_CSIPHY 1 + +#endif /* __DT_BINDINGS_OMAP3_ISP_H__ */ -- cgit v1.2.3 From 6f921fab5844941f7605b7f1a265f5fc7fe969a7 Mon Sep 17 00:00:00 2001 From: Luciano Coelho Date: Wed, 18 Mar 2015 18:38:25 +0200 Subject: wlcore: set irq_trigger in board files instead of hiding behind a quirk The platform_quirk element in the platform data was used to change the way the IRQ is triggered. When set, the EDGE_IRQ quirk would change the irqflags used and treat edge trigger differently from the rest. Instead of hiding this irq flag setting behind the quirk, have the board files set the irq_trigger explicitly. This will allow us to use standard irq DT definitions later on. Signed-off-by: Luciano Coelho [Eliad - rebase, add irq_trigger field and pass it, update board file changes] Signed-off-by: Eliad Peller Tested-by: Nikita Kiryanov Acked-by: Kalle Valo Acked-by: Sekhar Nori Signed-off-by: Tony Lindgren --- arch/arm/mach-davinci/board-da850-evm.c | 2 +- arch/arm/mach-omap2/pdata-quirks.c | 1 + drivers/net/wireless/ti/wlcore/debugfs.c | 2 +- drivers/net/wireless/ti/wlcore/main.c | 27 ++++++++++++++++----------- drivers/net/wireless/ti/wlcore/sdio.c | 2 +- drivers/net/wireless/ti/wlcore/wlcore.h | 5 ++--- include/linux/wl12xx.h | 5 +---- 7 files changed, 23 insertions(+), 21 deletions(-) (limited to 'include') diff --git a/arch/arm/mach-davinci/board-da850-evm.c b/arch/arm/mach-davinci/board-da850-evm.c index 6b5a97da9fe3..916589ca8d44 100644 --- a/arch/arm/mach-davinci/board-da850-evm.c +++ b/arch/arm/mach-davinci/board-da850-evm.c @@ -1385,8 +1385,8 @@ static const short da850_wl12xx_pins[] __initconst = { static struct wl12xx_platform_data da850_wl12xx_wlan_data __initdata = { .irq = -1, + .irq_trigger = IRQ_TYPE_EDGE_RISING, .board_ref_clock = WL12XX_REFCLOCK_38, - .platform_quirks = WL12XX_PLATFORM_QUIRK_EDGE_IRQ, }; static __init int da850_wl12xx_init(void) diff --git a/arch/arm/mach-omap2/pdata-quirks.c b/arch/arm/mach-omap2/pdata-quirks.c index e642b079e9f3..e86fb0d55c59 100644 --- a/arch/arm/mach-omap2/pdata-quirks.c +++ b/arch/arm/mach-omap2/pdata-quirks.c @@ -48,6 +48,7 @@ static void __init __used legacy_init_wl12xx(unsigned ref_clock, wl12xx.board_ref_clock = ref_clock; wl12xx.board_tcxo_clock = tcxo_clock; wl12xx.irq = gpio_to_irq(gpio); + wl12xx.irq_trigger = IRQ_TYPE_LEVEL_HIGH; res = wl12xx_set_platform_data(&wl12xx); if (res) { diff --git a/drivers/net/wireless/ti/wlcore/debugfs.c b/drivers/net/wireless/ti/wlcore/debugfs.c index 68f3bf229b5a..eb43f94a1597 100644 --- a/drivers/net/wireless/ti/wlcore/debugfs.c +++ b/drivers/net/wireless/ti/wlcore/debugfs.c @@ -502,7 +502,7 @@ static ssize_t driver_state_read(struct file *file, char __user *user_buf, DRIVER_STATE_PRINT_HEX(irq); /* TODO: ref_clock and tcxo_clock were moved to wl12xx priv */ DRIVER_STATE_PRINT_HEX(hw_pg_ver); - DRIVER_STATE_PRINT_HEX(platform_quirks); + DRIVER_STATE_PRINT_HEX(irq_flags); DRIVER_STATE_PRINT_HEX(chip.id); DRIVER_STATE_PRINT_STR(chip.fw_ver_str); DRIVER_STATE_PRINT_STR(chip.phy_fw_ver_str); diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c index 1e136993580f..67518f692cfc 100644 --- a/drivers/net/wireless/ti/wlcore/main.c +++ b/drivers/net/wireless/ti/wlcore/main.c @@ -27,6 +27,7 @@ #include #include #include +#include #include "wlcore.h" #include "debug.h" @@ -538,7 +539,7 @@ static int wlcore_irq_locked(struct wl1271 *wl) * In case edge triggered interrupt must be used, we cannot iterate * more than once without introducing race conditions with the hardirq. */ - if (wl->platform_quirks & WL12XX_PLATFORM_QUIRK_EDGE_IRQ) + if (wl->irq_flags & (IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING)) loopcount = 1; wl1271_debug(DEBUG_IRQ, "IRQ work"); @@ -6249,7 +6250,6 @@ struct ieee80211_hw *wlcore_alloc_hw(size_t priv_size, u32 aggr_buf_size, wl->ap_ps_map = 0; wl->ap_fw_ps_map = 0; wl->quirks = 0; - wl->platform_quirks = 0; wl->system_hlid = WL12XX_SYSTEM_HLID; wl->active_sta_count = 0; wl->active_link_count = 0; @@ -6391,7 +6391,8 @@ static void wlcore_nvs_cb(const struct firmware *fw, void *context) struct platform_device *pdev = wl->pdev; struct wlcore_platdev_data *pdev_data = dev_get_platdata(&pdev->dev); struct wl12xx_platform_data *pdata = pdev_data->pdata; - unsigned long irqflags; + struct resource *res; + int ret; irq_handler_t hardirq_fn = NULL; @@ -6418,19 +6419,23 @@ static void wlcore_nvs_cb(const struct firmware *fw, void *context) /* adjust some runtime configuration parameters */ wlcore_adjust_conf(wl); - wl->irq = platform_get_irq(pdev, 0); - wl->platform_quirks = pdata->platform_quirks; + res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + if (!res) { + wl1271_error("Could not get IRQ resource"); + goto out_free_nvs; + } + + wl->irq = res->start; + wl->irq_flags = res->flags & IRQF_TRIGGER_MASK; wl->if_ops = pdev_data->if_ops; - if (wl->platform_quirks & WL12XX_PLATFORM_QUIRK_EDGE_IRQ) { - irqflags = IRQF_TRIGGER_RISING; + if (wl->irq_flags & (IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING)) hardirq_fn = wlcore_hardirq; - } else { - irqflags = IRQF_TRIGGER_HIGH | IRQF_ONESHOT; - } + else + wl->irq_flags |= IRQF_ONESHOT; ret = request_threaded_irq(wl->irq, hardirq_fn, wlcore_irq, - irqflags, pdev->name, wl); + wl->irq_flags, pdev->name, wl); if (ret < 0) { wl1271_error("request_irq() failed: %d", ret); goto out_free_nvs; diff --git a/drivers/net/wireless/ti/wlcore/sdio.c b/drivers/net/wireless/ti/wlcore/sdio.c index d3dd7bfdf3f1..2bce00afea1f 100644 --- a/drivers/net/wireless/ti/wlcore/sdio.c +++ b/drivers/net/wireless/ti/wlcore/sdio.c @@ -287,7 +287,7 @@ static int wl1271_probe(struct sdio_func *func, memset(res, 0x00, sizeof(res)); res[0].start = pdev_data.pdata->irq; - res[0].flags = IORESOURCE_IRQ; + res[0].flags = IORESOURCE_IRQ | pdev_data.pdata->irq_trigger; res[0].name = "irq"; ret = platform_device_add_resources(glue->core, res, ARRAY_SIZE(res)); diff --git a/drivers/net/wireless/ti/wlcore/wlcore.h b/drivers/net/wireless/ti/wlcore/wlcore.h index d599c869e6e8..7f363fa566a3 100644 --- a/drivers/net/wireless/ti/wlcore/wlcore.h +++ b/drivers/net/wireless/ti/wlcore/wlcore.h @@ -197,6 +197,8 @@ struct wl1271 { int irq; + int irq_flags; + spinlock_t wl_lock; enum wlcore_state state; @@ -404,9 +406,6 @@ struct wl1271 { /* Quirks of specific hardware revisions */ unsigned int quirks; - /* Platform limitations */ - unsigned int platform_quirks; - /* number of currently active RX BA sessions */ int ba_rx_session_count; diff --git a/include/linux/wl12xx.h b/include/linux/wl12xx.h index a9c723be1acf..3876b67dbcbc 100644 --- a/include/linux/wl12xx.h +++ b/include/linux/wl12xx.h @@ -57,15 +57,12 @@ struct wl1251_platform_data { struct wl12xx_platform_data { int irq; + u32 irq_trigger; int board_ref_clock; int board_tcxo_clock; - unsigned long platform_quirks; bool pwr_in_suspend; }; -/* Platform does not support level trigger interrupts */ -#define WL12XX_PLATFORM_QUIRK_EDGE_IRQ BIT(0) - #ifdef CONFIG_WILINK_PLATFORM_DATA int wl12xx_set_platform_data(const struct wl12xx_platform_data *data); -- cgit v1.2.3 From 25911556283e5093fca235d7ba03faae7ed5dbf2 Mon Sep 17 00:00:00 2001 From: Arend van Spriel Date: Wed, 18 Mar 2015 13:25:26 +0100 Subject: brcmfmac: add support for BCM43430 SDIO chipset This patch added support for the BCM43430 802.11n SDIO chipset. Reviewed-by: Hante Meuleman Reviewed-by: Daniel (Deognyoun) Kim Reviewed-by: Franky (Zhenhui) Lin Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Arend van Spriel Signed-off-by: Kalle Valo --- drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c | 1 + drivers/net/wireless/brcm80211/brcmfmac/chip.c | 18 ++++++++++++++++++ drivers/net/wireless/brcm80211/brcmfmac/sdio.c | 5 +++++ drivers/net/wireless/brcm80211/include/brcm_hw_ids.h | 1 + drivers/net/wireless/brcm80211/include/chipcommon.h | 9 ++++++++- include/linux/mmc/sdio_ids.h | 1 + 6 files changed, 34 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c b/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c index 9667b6aabc00..9b508bd3b839 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c @@ -1098,6 +1098,7 @@ static const struct sdio_device_id brcmf_sdmmc_ids[] = { BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_43341), BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_43362), BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_4335_4339), + BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_43430), BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_4345), BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_4354), { /* end: all zeroes */ } diff --git a/drivers/net/wireless/brcm80211/brcmfmac/chip.c b/drivers/net/wireless/brcm80211/brcmfmac/chip.c index 734172570054..ab2fac8b2760 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/chip.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/chip.c @@ -600,6 +600,12 @@ static void brcmf_chip_socram_ramsize(struct brcmf_core_priv *sr, u32 *ramsize, if (sr->chip->pub.chiprev < 2) *srsize = (32 * 1024); break; + case BRCM_CC_43430_CHIP_ID: + /* assume sr for now as we can not check + * firmware sr capability at this point. + */ + *srsize = (64 * 1024); + break; default: break; } @@ -1072,6 +1078,7 @@ static void brcmf_chip_cm3_set_passive(struct brcmf_chip_priv *chip) { struct brcmf_core *core; + struct brcmf_core_priv *sr; brcmf_chip_disable_arm(chip, BCMA_CORE_ARM_CM3); core = brcmf_chip_get_core(&chip->pub, BCMA_CORE_80211); @@ -1081,6 +1088,13 @@ brcmf_chip_cm3_set_passive(struct brcmf_chip_priv *chip) D11_BCMA_IOCTL_PHYCLOCKEN); core = brcmf_chip_get_core(&chip->pub, BCMA_CORE_INTERNAL_MEM); brcmf_chip_resetcore(core, 0, 0, 0); + + /* disable bank #3 remap for this device */ + if (chip->pub.chip == BRCM_CC_43430_CHIP_ID) { + sr = container_of(core, struct brcmf_core_priv, pub); + brcmf_chip_core_write32(sr, SOCRAMREGOFFS(bankidx), 3); + brcmf_chip_core_write32(sr, SOCRAMREGOFFS(bankpda), 0); + } } static bool brcmf_chip_cm3_set_active(struct brcmf_chip_priv *chip) @@ -1188,6 +1202,10 @@ bool brcmf_chip_sr_capable(struct brcmf_chip *pub) addr = CORE_CC_REG(base, chipcontrol_data); reg = chip->ops->read32(chip->ctx, addr); return (reg & pmu_cc3_mask) != 0; + case BRCM_CC_43430_CHIP_ID: + addr = CORE_CC_REG(base, sr_control1); + reg = chip->ops->read32(chip->ctx, addr); + return reg != 0; default: addr = CORE_CC_REG(base, pmucapabilities_ext); reg = chip->ops->read32(chip->ctx, addr); diff --git a/drivers/net/wireless/brcm80211/brcmfmac/sdio.c b/drivers/net/wireless/brcm80211/brcmfmac/sdio.c index 38fa0e8f859d..e162860b265c 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/sdio.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/sdio.c @@ -615,6 +615,8 @@ static const struct sdiod_drive_str sdiod_drvstr_tab2_3v3[] = { #define BCM43362_NVRAM_NAME "brcm/brcmfmac43362-sdio.txt" #define BCM4339_FIRMWARE_NAME "brcm/brcmfmac4339-sdio.bin" #define BCM4339_NVRAM_NAME "brcm/brcmfmac4339-sdio.txt" +#define BCM43430_FIRMWARE_NAME "brcm/brcmfmac43430-sdio.bin" +#define BCM43430_NVRAM_NAME "brcm/brcmfmac43430-sdio.txt" #define BCM4345_FIRMWARE_NAME "brcm/brcmfmac4345-sdio.bin" #define BCM4345_NVRAM_NAME "brcm/brcmfmac4345-sdio.txt" #define BCM4354_FIRMWARE_NAME "brcm/brcmfmac4354-sdio.bin" @@ -640,6 +642,8 @@ MODULE_FIRMWARE(BCM43362_FIRMWARE_NAME); MODULE_FIRMWARE(BCM43362_NVRAM_NAME); MODULE_FIRMWARE(BCM4339_FIRMWARE_NAME); MODULE_FIRMWARE(BCM4339_NVRAM_NAME); +MODULE_FIRMWARE(BCM43430_FIRMWARE_NAME); +MODULE_FIRMWARE(BCM43430_NVRAM_NAME); MODULE_FIRMWARE(BCM4345_FIRMWARE_NAME); MODULE_FIRMWARE(BCM4345_NVRAM_NAME); MODULE_FIRMWARE(BCM4354_FIRMWARE_NAME); @@ -671,6 +675,7 @@ static const struct brcmf_firmware_names brcmf_fwname_data[] = { { BRCM_CC_4335_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM4335) }, { BRCM_CC_43362_CHIP_ID, 0xFFFFFFFE, BRCMF_FIRMWARE_NVRAM(BCM43362) }, { BRCM_CC_4339_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM4339) }, + { BRCM_CC_43430_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM43430) }, { BRCM_CC_4345_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM4345) }, { BRCM_CC_4354_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM4354) } }; diff --git a/drivers/net/wireless/brcm80211/include/brcm_hw_ids.h b/drivers/net/wireless/brcm80211/include/brcm_hw_ids.h index b599e7e41148..4efdd51af9c8 100644 --- a/drivers/net/wireless/brcm80211/include/brcm_hw_ids.h +++ b/drivers/net/wireless/brcm80211/include/brcm_hw_ids.h @@ -37,6 +37,7 @@ #define BRCM_CC_43362_CHIP_ID 43362 #define BRCM_CC_4335_CHIP_ID 0x4335 #define BRCM_CC_4339_CHIP_ID 0x4339 +#define BRCM_CC_43430_CHIP_ID 43430 #define BRCM_CC_4345_CHIP_ID 0x4345 #define BRCM_CC_4354_CHIP_ID 0x4354 #define BRCM_CC_4356_CHIP_ID 0x4356 diff --git a/drivers/net/wireless/brcm80211/include/chipcommon.h b/drivers/net/wireless/brcm80211/include/chipcommon.h index d242333b7559..e1fd499930a0 100644 --- a/drivers/net/wireless/brcm80211/include/chipcommon.h +++ b/drivers/net/wireless/brcm80211/include/chipcommon.h @@ -183,7 +183,14 @@ struct chipcregs { u8 uart1lsr; u8 uart1msr; u8 uart1scratch; - u32 PAD[126]; + u32 PAD[62]; + + /* save/restore, corerev >= 48 */ + u32 sr_capability; /* 0x500 */ + u32 sr_control0; /* 0x504 */ + u32 sr_control1; /* 0x508 */ + u32 gpio_control; /* 0x50C */ + u32 PAD[60]; /* PMU registers (corerev >= 20) */ u32 pmucontrol; /* 0x600 */ diff --git a/include/linux/mmc/sdio_ids.h b/include/linux/mmc/sdio_ids.h index 91397858dc95..83430f2ea757 100644 --- a/include/linux/mmc/sdio_ids.h +++ b/include/linux/mmc/sdio_ids.h @@ -33,6 +33,7 @@ #define SDIO_DEVICE_ID_BROADCOM_43341 0xa94d #define SDIO_DEVICE_ID_BROADCOM_4335_4339 0x4335 #define SDIO_DEVICE_ID_BROADCOM_43362 0xa962 +#define SDIO_DEVICE_ID_BROADCOM_43430 0xa9a6 #define SDIO_DEVICE_ID_BROADCOM_4345 0x4345 #define SDIO_DEVICE_ID_BROADCOM_4354 0x4354 -- cgit v1.2.3 From 52452c542559ac980b48dbf22a30ee7fa0af507c Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Thu, 19 Mar 2015 19:04:19 -0700 Subject: inet: drop prev pointer handling in request sock When request sock are put in ehash table, the whole notion of having a previous request to update dl_next is pointless. Also, following patch will get rid of big purge timer, so we want to delete a request sock without holding listener lock. Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- include/net/inet6_connection_sock.h | 1 - include/net/inet_connection_sock.h | 11 ++++------- include/net/request_sock.h | 15 +++++++++++---- include/net/tcp.h | 3 +-- net/dccp/dccp.h | 3 +-- net/dccp/ipv4.c | 14 ++++++-------- net/dccp/ipv6.c | 19 ++++++++----------- net/dccp/minisocks.c | 7 +++---- net/ipv4/inet_connection_sock.c | 22 ++++++++++++---------- net/ipv4/tcp_input.c | 2 +- net/ipv4/tcp_ipv4.c | 17 ++++++++--------- net/ipv4/tcp_minisocks.c | 5 ++--- net/ipv6/inet6_connection_sock.c | 10 ++++------ net/ipv6/tcp_ipv6.c | 12 ++++++------ 14 files changed, 67 insertions(+), 74 deletions(-) (limited to 'include') diff --git a/include/net/inet6_connection_sock.h b/include/net/inet6_connection_sock.h index 74af137304be..15bd40878d2a 100644 --- a/include/net/inet6_connection_sock.h +++ b/include/net/inet6_connection_sock.h @@ -29,7 +29,6 @@ struct dst_entry *inet6_csk_route_req(struct sock *sk, struct flowi6 *fl6, const struct request_sock *req); struct request_sock *inet6_csk_search_req(const struct sock *sk, - struct request_sock ***prevp, const __be16 rport, const struct in6_addr *raddr, const struct in6_addr *laddr, diff --git a/include/net/inet_connection_sock.h b/include/net/inet_connection_sock.h index b9a6b0a94cc6..423a46106e57 100644 --- a/include/net/inet_connection_sock.h +++ b/include/net/inet_connection_sock.h @@ -257,7 +257,6 @@ inet_csk_rto_backoff(const struct inet_connection_sock *icsk, struct sock *inet_csk_accept(struct sock *sk, int flags, int *err); struct request_sock *inet_csk_search_req(const struct sock *sk, - struct request_sock ***prevp, const __be16 rport, const __be32 raddr, const __be32 laddr); @@ -310,17 +309,15 @@ static inline int inet_csk_reqsk_queue_is_full(const struct sock *sk) } static inline void inet_csk_reqsk_queue_unlink(struct sock *sk, - struct request_sock *req, - struct request_sock **prev) + struct request_sock *req) { - reqsk_queue_unlink(&inet_csk(sk)->icsk_accept_queue, req, prev); + reqsk_queue_unlink(&inet_csk(sk)->icsk_accept_queue, req); } static inline void inet_csk_reqsk_queue_drop(struct sock *sk, - struct request_sock *req, - struct request_sock **prev) + struct request_sock *req) { - inet_csk_reqsk_queue_unlink(sk, req, prev); + inet_csk_reqsk_queue_unlink(sk, req); inet_csk_reqsk_queue_removed(sk, req); reqsk_free(req); } diff --git a/include/net/request_sock.h b/include/net/request_sock.h index e7ef86340514..65223905d139 100644 --- a/include/net/request_sock.h +++ b/include/net/request_sock.h @@ -50,6 +50,7 @@ int inet_rtx_syn_ack(struct sock *parent, struct request_sock *req); struct request_sock { struct sock_common __req_common; #define rsk_refcnt __req_common.skc_refcnt +#define rsk_hash __req_common.skc_hash struct request_sock *dl_next; struct sock *rsk_listener; @@ -216,11 +217,16 @@ static inline int reqsk_queue_empty(struct request_sock_queue *queue) } static inline void reqsk_queue_unlink(struct request_sock_queue *queue, - struct request_sock *req, - struct request_sock **prev_req) + struct request_sock *req) { + struct listen_sock *lopt = queue->listen_opt; + struct request_sock **prev; + write_lock(&queue->syn_wait_lock); - *prev_req = req->dl_next; + prev = &lopt->syn_table[req->rsk_hash]; + while (*prev != req) + prev = &(*prev)->dl_next; + *prev = req->dl_next; write_unlock(&queue->syn_wait_lock); } @@ -300,7 +306,6 @@ static inline void reqsk_queue_hash_req(struct request_sock_queue *queue, req->num_retrans = 0; req->num_timeout = 0; req->sk = NULL; - req->dl_next = lopt->syn_table[hash]; /* before letting lookups find us, make sure all req fields * are committed to memory and refcnt initialized. @@ -308,7 +313,9 @@ static inline void reqsk_queue_hash_req(struct request_sock_queue *queue, smp_wmb(); atomic_set(&req->rsk_refcnt, 1); + req->rsk_hash = hash; write_lock(&queue->syn_wait_lock); + req->dl_next = lopt->syn_table[hash]; lopt->syn_table[hash] = req; write_unlock(&queue->syn_wait_lock); } diff --git a/include/net/tcp.h b/include/net/tcp.h index 5b29835b81d8..082fd79132b7 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -406,8 +406,7 @@ enum tcp_tw_status tcp_timewait_state_process(struct inet_timewait_sock *tw, struct sk_buff *skb, const struct tcphdr *th); struct sock *tcp_check_req(struct sock *sk, struct sk_buff *skb, - struct request_sock *req, struct request_sock **prev, - bool fastopen); + struct request_sock *req, bool fastopen); int tcp_child_process(struct sock *parent, struct sock *child, struct sk_buff *skb); void tcp_enter_loss(struct sock *sk); diff --git a/net/dccp/dccp.h b/net/dccp/dccp.h index 3b1d64d6e093..2396f50c5b04 100644 --- a/net/dccp/dccp.h +++ b/net/dccp/dccp.h @@ -280,8 +280,7 @@ struct sock *dccp_v4_request_recv_sock(struct sock *sk, struct sk_buff *skb, struct request_sock *req, struct dst_entry *dst); struct sock *dccp_check_req(struct sock *sk, struct sk_buff *skb, - struct request_sock *req, - struct request_sock **prev); + struct request_sock *req); int dccp_child_process(struct sock *parent, struct sock *child, struct sk_buff *skb); diff --git a/net/dccp/ipv4.c b/net/dccp/ipv4.c index e7ad291cd96b..5bffbbaf1fac 100644 --- a/net/dccp/ipv4.c +++ b/net/dccp/ipv4.c @@ -288,11 +288,11 @@ static void dccp_v4_err(struct sk_buff *skb, u32 info) } switch (sk->sk_state) { - struct request_sock *req , **prev; + struct request_sock *req; case DCCP_LISTEN: if (sock_owned_by_user(sk)) goto out; - req = inet_csk_search_req(sk, &prev, dh->dccph_dport, + req = inet_csk_search_req(sk, dh->dccph_dport, iph->daddr, iph->saddr); if (!req) goto out; @@ -314,7 +314,7 @@ static void dccp_v4_err(struct sk_buff *skb, u32 info) * created socket, and POSIX does not want network * errors returned from accept(). */ - inet_csk_reqsk_queue_drop(sk, req, prev); + inet_csk_reqsk_queue_drop(sk, req); goto out; case DCCP_REQUESTING: @@ -448,13 +448,11 @@ static struct sock *dccp_v4_hnd_req(struct sock *sk, struct sk_buff *skb) const struct dccp_hdr *dh = dccp_hdr(skb); const struct iphdr *iph = ip_hdr(skb); struct sock *nsk; - struct request_sock **prev; /* Find possible connection requests. */ - struct request_sock *req = inet_csk_search_req(sk, &prev, - dh->dccph_sport, + struct request_sock *req = inet_csk_search_req(sk, dh->dccph_sport, iph->saddr, iph->daddr); - if (req != NULL) - return dccp_check_req(sk, skb, req, prev); + if (req) + return dccp_check_req(sk, skb, req); nsk = inet_lookup_established(sock_net(sk), &dccp_hashinfo, iph->saddr, dh->dccph_sport, diff --git a/net/dccp/ipv6.c b/net/dccp/ipv6.c index c655de5f67c9..ae2184039fe3 100644 --- a/net/dccp/ipv6.c +++ b/net/dccp/ipv6.c @@ -149,12 +149,12 @@ static void dccp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, /* Might be for an request_sock */ switch (sk->sk_state) { - struct request_sock *req, **prev; + struct request_sock *req; case DCCP_LISTEN: if (sock_owned_by_user(sk)) goto out; - req = inet6_csk_search_req(sk, &prev, dh->dccph_dport, + req = inet6_csk_search_req(sk, dh->dccph_dport, &hdr->daddr, &hdr->saddr, inet6_iif(skb)); if (req == NULL) @@ -172,7 +172,7 @@ static void dccp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, goto out; } - inet_csk_reqsk_queue_drop(sk, req, prev); + inet_csk_reqsk_queue_drop(sk, req); goto out; case DCCP_REQUESTING: @@ -317,16 +317,13 @@ static struct sock *dccp_v6_hnd_req(struct sock *sk,struct sk_buff *skb) { const struct dccp_hdr *dh = dccp_hdr(skb); const struct ipv6hdr *iph = ipv6_hdr(skb); + struct request_sock *req; struct sock *nsk; - struct request_sock **prev; - /* Find possible connection requests. */ - struct request_sock *req = inet6_csk_search_req(sk, &prev, - dh->dccph_sport, - &iph->saddr, - &iph->daddr, - inet6_iif(skb)); + + req = inet6_csk_search_req(sk, dh->dccph_sport, &iph->saddr, + &iph->daddr, inet6_iif(skb)); if (req != NULL) - return dccp_check_req(sk, skb, req, prev); + return dccp_check_req(sk, skb, req); nsk = __inet6_lookup_established(sock_net(sk), &dccp_hashinfo, &iph->saddr, dh->dccph_sport, diff --git a/net/dccp/minisocks.c b/net/dccp/minisocks.c index b50dc436db1f..332f7d6d9942 100644 --- a/net/dccp/minisocks.c +++ b/net/dccp/minisocks.c @@ -152,8 +152,7 @@ EXPORT_SYMBOL_GPL(dccp_create_openreq_child); * as an request_sock. */ struct sock *dccp_check_req(struct sock *sk, struct sk_buff *skb, - struct request_sock *req, - struct request_sock **prev) + struct request_sock *req) { struct sock *child = NULL; struct dccp_request_sock *dreq = dccp_rsk(req); @@ -200,7 +199,7 @@ struct sock *dccp_check_req(struct sock *sk, struct sk_buff *skb, if (child == NULL) goto listen_overflow; - inet_csk_reqsk_queue_unlink(sk, req, prev); + inet_csk_reqsk_queue_unlink(sk, req); inet_csk_reqsk_queue_removed(sk, req); inet_csk_reqsk_queue_add(sk, req, child); out: @@ -212,7 +211,7 @@ drop: if (dccp_hdr(skb)->dccph_type != DCCP_PKT_RESET) req->rsk_ops->send_reset(sk, skb); - inet_csk_reqsk_queue_drop(sk, req, prev); + inet_csk_reqsk_queue_drop(sk, req); goto out; } diff --git a/net/ipv4/inet_connection_sock.c b/net/ipv4/inet_connection_sock.c index f0f91858aecf..4f57a017928c 100644 --- a/net/ipv4/inet_connection_sock.c +++ b/net/ipv4/inet_connection_sock.c @@ -480,18 +480,17 @@ static inline u32 inet_synq_hash(const __be32 raddr, const __be16 rport, #endif struct request_sock *inet_csk_search_req(const struct sock *sk, - struct request_sock ***prevp, const __be16 rport, const __be32 raddr, const __be32 laddr) { const struct inet_connection_sock *icsk = inet_csk(sk); struct listen_sock *lopt = icsk->icsk_accept_queue.listen_opt; - struct request_sock *req, **prev; + struct request_sock *req; - for (prev = &lopt->syn_table[inet_synq_hash(raddr, rport, lopt->hash_rnd, - lopt->nr_table_entries)]; - (req = *prev) != NULL; - prev = &req->dl_next) { + for (req = lopt->syn_table[inet_synq_hash(raddr, rport, lopt->hash_rnd, + lopt->nr_table_entries)]; + req != NULL; + req = req->dl_next) { const struct inet_request_sock *ireq = inet_rsk(req); if (ireq->ir_rmt_port == rport && @@ -499,7 +498,6 @@ struct request_sock *inet_csk_search_req(const struct sock *sk, ireq->ir_loc_addr == laddr && AF_INET_FAMILY(req->rsk_ops->family)) { WARN_ON(req->sk); - *prevp = prev; break; } } @@ -610,7 +608,10 @@ void inet_csk_reqsk_queue_prune(struct sock *parent, i = lopt->clock_hand; do { - reqp=&lopt->syn_table[i]; + reqp = &lopt->syn_table[i]; + if (!*reqp) + goto next_bucket; + write_lock(&queue->syn_wait_lock); while ((req = *reqp) != NULL) { if (time_after_eq(now, req->expires)) { int expire = 0, resend = 0; @@ -635,14 +636,15 @@ void inet_csk_reqsk_queue_prune(struct sock *parent, } /* Drop this request */ - inet_csk_reqsk_queue_unlink(parent, req, reqp); + *reqp = req->dl_next; reqsk_queue_removed(queue, req); reqsk_put(req); continue; } reqp = &req->dl_next; } - + write_unlock(&queue->syn_wait_lock); +next_bucket: i = (i + 1) & (lopt->nr_table_entries - 1); } while (--budget > 0); diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index 1dfbaee3554e..95caea707f54 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -5694,7 +5694,7 @@ int tcp_rcv_state_process(struct sock *sk, struct sk_buff *skb, WARN_ON_ONCE(sk->sk_state != TCP_SYN_RECV && sk->sk_state != TCP_FIN_WAIT1); - if (tcp_check_req(sk, skb, req, NULL, true) == NULL) + if (tcp_check_req(sk, skb, req, true) == NULL) goto discard; } diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index ddd0b1f25b96..19c3770f1e97 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -458,12 +458,12 @@ void tcp_v4_err(struct sk_buff *icmp_skb, u32 info) } switch (sk->sk_state) { - struct request_sock *req, **prev; + struct request_sock *req; case TCP_LISTEN: if (sock_owned_by_user(sk)) goto out; - req = inet_csk_search_req(sk, &prev, th->dest, + req = inet_csk_search_req(sk, th->dest, iph->daddr, iph->saddr); if (!req) goto out; @@ -484,7 +484,7 @@ void tcp_v4_err(struct sk_buff *icmp_skb, u32 info) * created socket, and POSIX does not want network * errors returned from accept(). */ - inet_csk_reqsk_queue_drop(sk, req, prev); + inet_csk_reqsk_queue_drop(sk, req); NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_LISTENDROPS); goto out; @@ -1392,15 +1392,14 @@ EXPORT_SYMBOL(tcp_v4_syn_recv_sock); static struct sock *tcp_v4_hnd_req(struct sock *sk, struct sk_buff *skb) { - struct tcphdr *th = tcp_hdr(skb); + const struct tcphdr *th = tcp_hdr(skb); const struct iphdr *iph = ip_hdr(skb); + struct request_sock *req; struct sock *nsk; - struct request_sock **prev; - /* Find possible connection requests. */ - struct request_sock *req = inet_csk_search_req(sk, &prev, th->source, - iph->saddr, iph->daddr); + + req = inet_csk_search_req(sk, th->source, iph->saddr, iph->daddr); if (req) - return tcp_check_req(sk, skb, req, prev, false); + return tcp_check_req(sk, skb, req, false); nsk = inet_lookup_established(sock_net(sk), &tcp_hashinfo, iph->saddr, th->source, iph->daddr, th->dest, inet_iif(skb)); diff --git a/net/ipv4/tcp_minisocks.c b/net/ipv4/tcp_minisocks.c index dd11ac7798c6..848bcab358e4 100644 --- a/net/ipv4/tcp_minisocks.c +++ b/net/ipv4/tcp_minisocks.c @@ -572,7 +572,6 @@ EXPORT_SYMBOL(tcp_create_openreq_child); struct sock *tcp_check_req(struct sock *sk, struct sk_buff *skb, struct request_sock *req, - struct request_sock **prev, bool fastopen) { struct tcp_options_received tmp_opt; @@ -766,7 +765,7 @@ struct sock *tcp_check_req(struct sock *sk, struct sk_buff *skb, if (child == NULL) goto listen_overflow; - inet_csk_reqsk_queue_unlink(sk, req, prev); + inet_csk_reqsk_queue_unlink(sk, req); inet_csk_reqsk_queue_removed(sk, req); inet_csk_reqsk_queue_add(sk, req, child); @@ -791,7 +790,7 @@ embryonic_reset: tcp_reset(sk); } if (!fastopen) { - inet_csk_reqsk_queue_drop(sk, req, prev); + inet_csk_reqsk_queue_drop(sk, req); NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_EMBRYONICRSTS); } return NULL; diff --git a/net/ipv6/inet6_connection_sock.c b/net/ipv6/inet6_connection_sock.c index 29b32206e494..b7acb9ebc4f5 100644 --- a/net/ipv6/inet6_connection_sock.c +++ b/net/ipv6/inet6_connection_sock.c @@ -113,7 +113,6 @@ static u32 inet6_synq_hash(const struct in6_addr *raddr, const __be16 rport, } struct request_sock *inet6_csk_search_req(const struct sock *sk, - struct request_sock ***prevp, const __be16 rport, const struct in6_addr *raddr, const struct in6_addr *laddr, @@ -121,13 +120,13 @@ struct request_sock *inet6_csk_search_req(const struct sock *sk, { const struct inet_connection_sock *icsk = inet_csk(sk); struct listen_sock *lopt = icsk->icsk_accept_queue.listen_opt; - struct request_sock *req, **prev; + struct request_sock *req; - for (prev = &lopt->syn_table[inet6_synq_hash(raddr, rport, + for (req = lopt->syn_table[inet6_synq_hash(raddr, rport, lopt->hash_rnd, lopt->nr_table_entries)]; - (req = *prev) != NULL; - prev = &req->dl_next) { + req != NULL; + req = req->dl_next) { const struct inet_request_sock *ireq = inet_rsk(req); if (ireq->ir_rmt_port == rport && @@ -136,7 +135,6 @@ struct request_sock *inet6_csk_search_req(const struct sock *sk, ipv6_addr_equal(&ireq->ir_v6_loc_addr, laddr) && (!ireq->ir_iif || ireq->ir_iif == iif)) { WARN_ON(req->sk != NULL); - *prevp = prev; return req; } } diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c index 720676d073d9..146f123b52c9 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c @@ -403,13 +403,13 @@ static void tcp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, /* Might be for an request_sock */ switch (sk->sk_state) { - struct request_sock *req, **prev; + struct request_sock *req; case TCP_LISTEN: if (sock_owned_by_user(sk)) goto out; /* Note : We use inet6_iif() here, not tcp_v6_iif() */ - req = inet6_csk_search_req(sk, &prev, th->dest, &hdr->daddr, + req = inet6_csk_search_req(sk, th->dest, &hdr->daddr, &hdr->saddr, inet6_iif(skb)); if (!req) goto out; @@ -424,7 +424,7 @@ static void tcp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, goto out; } - inet_csk_reqsk_queue_drop(sk, req, prev); + inet_csk_reqsk_queue_drop(sk, req); NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_LISTENDROPS); goto out; @@ -980,16 +980,16 @@ static void tcp_v6_reqsk_send_ack(struct sock *sk, struct sk_buff *skb, static struct sock *tcp_v6_hnd_req(struct sock *sk, struct sk_buff *skb) { - struct request_sock *req, **prev; const struct tcphdr *th = tcp_hdr(skb); + struct request_sock *req; struct sock *nsk; /* Find possible connection requests. */ - req = inet6_csk_search_req(sk, &prev, th->source, + req = inet6_csk_search_req(sk, th->source, &ipv6_hdr(skb)->saddr, &ipv6_hdr(skb)->daddr, tcp_v6_iif(skb)); if (req) - return tcp_check_req(sk, skb, req, prev, false); + return tcp_check_req(sk, skb, req, false); nsk = __inet6_lookup_established(sock_net(sk), &tcp_hashinfo, &ipv6_hdr(skb)->saddr, th->source, -- cgit v1.2.3 From fa76ce7328b289b6edd476e24eb52fd634261720 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Thu, 19 Mar 2015 19:04:20 -0700 Subject: inet: get rid of central tcp/dccp listener timer One of the major issue for TCP is the SYNACK rtx handling, done by inet_csk_reqsk_queue_prune(), fired by the keepalive timer of a TCP_LISTEN socket. This function runs for awful long times, with socket lock held, meaning that other cpus needing this lock have to spin for hundred of ms. SYNACK are sent in huge bursts, likely to cause severe drops anyway. This model was OK 15 years ago when memory was very tight. We now can afford to have a timer per request sock. Timer invocations no longer need to lock the listener, and can be run from all cpus in parallel. With following patch increasing somaxconn width to 32 bits, I tested a listener with more than 4 million active request sockets, and a steady SYNFLOOD of ~200,000 SYN per second. Host was sending ~830,000 SYNACK per second. This is ~100 times more what we could achieve before this patch. Later, we will get rid of the listener hash and use ehash instead. Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- include/net/inet6_connection_sock.h | 2 +- include/net/inet_connection_sock.h | 15 ++-- include/net/request_sock.h | 87 +++++++++++----------- net/core/request_sock.c | 13 ++-- net/core/sock.c | 2 +- net/dccp/ipv4.c | 10 ++- net/dccp/ipv6.c | 12 ++-- net/dccp/timer.c | 24 +------ net/ipv4/inet_connection_sock.c | 139 ++++++++++++++++++------------------ net/ipv4/inet_diag.c | 4 +- net/ipv4/syncookies.c | 1 - net/ipv4/tcp_fastopen.c | 2 +- net/ipv4/tcp_ipv4.c | 11 ++- net/ipv4/tcp_minisocks.c | 5 +- net/ipv4/tcp_timer.c | 12 +--- net/ipv6/inet6_connection_sock.c | 19 ++--- net/ipv6/syncookies.c | 1 - net/ipv6/tcp_ipv6.c | 12 ++-- 18 files changed, 173 insertions(+), 198 deletions(-) (limited to 'include') diff --git a/include/net/inet6_connection_sock.h b/include/net/inet6_connection_sock.h index 15bd40878d2a..6d539e4e5ba7 100644 --- a/include/net/inet6_connection_sock.h +++ b/include/net/inet6_connection_sock.h @@ -28,7 +28,7 @@ int inet6_csk_bind_conflict(const struct sock *sk, struct dst_entry *inet6_csk_route_req(struct sock *sk, struct flowi6 *fl6, const struct request_sock *req); -struct request_sock *inet6_csk_search_req(const struct sock *sk, +struct request_sock *inet6_csk_search_req(struct sock *sk, const __be16 rport, const struct in6_addr *raddr, const struct in6_addr *laddr, diff --git a/include/net/inet_connection_sock.h b/include/net/inet_connection_sock.h index 423a46106e57..7b5887cd1172 100644 --- a/include/net/inet_connection_sock.h +++ b/include/net/inet_connection_sock.h @@ -256,7 +256,7 @@ inet_csk_rto_backoff(const struct inet_connection_sock *icsk, struct sock *inet_csk_accept(struct sock *sk, int flags, int *err); -struct request_sock *inet_csk_search_req(const struct sock *sk, +struct request_sock *inet_csk_search_req(struct sock *sk, const __be16 rport, const __be32 raddr, const __be32 laddr); @@ -282,15 +282,13 @@ void inet_csk_reqsk_queue_hash_add(struct sock *sk, struct request_sock *req, static inline void inet_csk_reqsk_queue_removed(struct sock *sk, struct request_sock *req) { - if (reqsk_queue_removed(&inet_csk(sk)->icsk_accept_queue, req) == 0) - inet_csk_delete_keepalive_timer(sk); + reqsk_queue_removed(&inet_csk(sk)->icsk_accept_queue, req); } static inline void inet_csk_reqsk_queue_added(struct sock *sk, const unsigned long timeout) { - if (reqsk_queue_added(&inet_csk(sk)->icsk_accept_queue) == 0) - inet_csk_reset_keepalive_timer(sk, timeout); + reqsk_queue_added(&inet_csk(sk)->icsk_accept_queue); } static inline int inet_csk_reqsk_queue_len(const struct sock *sk) @@ -319,14 +317,9 @@ static inline void inet_csk_reqsk_queue_drop(struct sock *sk, { inet_csk_reqsk_queue_unlink(sk, req); inet_csk_reqsk_queue_removed(sk, req); - reqsk_free(req); + reqsk_put(req); } -void inet_csk_reqsk_queue_prune(struct sock *parent, - const unsigned long interval, - const unsigned long timeout, - const unsigned long max_rto); - void inet_csk_destroy_sock(struct sock *sk); void inet_csk_prepare_forced_close(struct sock *sk); diff --git a/include/net/request_sock.h b/include/net/request_sock.h index 65223905d139..6a91261d9b7b 100644 --- a/include/net/request_sock.h +++ b/include/net/request_sock.h @@ -62,7 +62,7 @@ struct request_sock { u32 window_clamp; /* window clamp at creation time */ u32 rcv_wnd; /* rcv_wnd offered first time */ u32 ts_recent; - unsigned long expires; + struct timer_list rsk_timer; const struct request_sock_ops *rsk_ops; struct sock *sk; u32 secid; @@ -110,9 +110,6 @@ static inline void reqsk_free(struct request_sock *req) static inline void reqsk_put(struct request_sock *req) { - /* temporary debugging, until req sock are put into ehash table */ - WARN_ON_ONCE(atomic_read(&req->rsk_refcnt) != 1); - if (atomic_dec_and_test(&req->rsk_refcnt)) reqsk_free(req); } @@ -124,12 +121,16 @@ extern int sysctl_max_syn_backlog; * @max_qlen_log - log_2 of maximal queued SYNs/REQUESTs */ struct listen_sock { - u8 max_qlen_log; + int qlen_inc; /* protected by listener lock */ + int young_inc;/* protected by listener lock */ + + /* following fields can be updated by timer */ + atomic_t qlen_dec; /* qlen = qlen_inc - qlen_dec */ + atomic_t young_dec; + + u8 max_qlen_log ____cacheline_aligned_in_smp; u8 synflood_warned; /* 2 bytes hole, try to use */ - int qlen; - int qlen_young; - int clock_hand; u32 hash_rnd; u32 nr_table_entries; struct request_sock *syn_table[0]; @@ -182,9 +183,7 @@ struct fastopen_queue { struct request_sock_queue { struct request_sock *rskq_accept_head; struct request_sock *rskq_accept_tail; - rwlock_t syn_wait_lock; u8 rskq_defer_accept; - /* 3 bytes hole, try to pack */ struct listen_sock *listen_opt; struct fastopen_queue *fastopenq; /* This is non-NULL iff TFO has been * enabled on this listener. Check @@ -192,6 +191,9 @@ struct request_sock_queue { * to determine if TFO is enabled * right at this moment. */ + + /* temporary alignment, our goal is to get rid of this lock */ + rwlock_t syn_wait_lock ____cacheline_aligned_in_smp; }; int reqsk_queue_alloc(struct request_sock_queue *queue, @@ -223,11 +225,15 @@ static inline void reqsk_queue_unlink(struct request_sock_queue *queue, struct request_sock **prev; write_lock(&queue->syn_wait_lock); + prev = &lopt->syn_table[req->rsk_hash]; while (*prev != req) prev = &(*prev)->dl_next; *prev = req->dl_next; + write_unlock(&queue->syn_wait_lock); + if (del_timer(&req->rsk_timer)) + reqsk_put(req); } static inline void reqsk_queue_add(struct request_sock_queue *queue, @@ -260,64 +266,53 @@ static inline struct request_sock *reqsk_queue_remove(struct request_sock_queue return req; } -static inline int reqsk_queue_removed(struct request_sock_queue *queue, - struct request_sock *req) +static inline void reqsk_queue_removed(struct request_sock_queue *queue, + const struct request_sock *req) { struct listen_sock *lopt = queue->listen_opt; if (req->num_timeout == 0) - --lopt->qlen_young; - - return --lopt->qlen; + atomic_inc(&lopt->young_dec); + atomic_inc(&lopt->qlen_dec); } -static inline int reqsk_queue_added(struct request_sock_queue *queue) +static inline void reqsk_queue_added(struct request_sock_queue *queue) { struct listen_sock *lopt = queue->listen_opt; - const int prev_qlen = lopt->qlen; - lopt->qlen_young++; - lopt->qlen++; - return prev_qlen; + lopt->young_inc++; + lopt->qlen_inc++; } -static inline int reqsk_queue_len(const struct request_sock_queue *queue) +static inline int listen_sock_qlen(const struct listen_sock *lopt) { - return queue->listen_opt != NULL ? queue->listen_opt->qlen : 0; + return lopt->qlen_inc - atomic_read(&lopt->qlen_dec); } -static inline int reqsk_queue_len_young(const struct request_sock_queue *queue) +static inline int listen_sock_young(const struct listen_sock *lopt) { - return queue->listen_opt->qlen_young; + return lopt->young_inc - atomic_read(&lopt->young_dec); } -static inline int reqsk_queue_is_full(const struct request_sock_queue *queue) +static inline int reqsk_queue_len(const struct request_sock_queue *queue) { - return queue->listen_opt->qlen >> queue->listen_opt->max_qlen_log; + const struct listen_sock *lopt = queue->listen_opt; + + return lopt ? listen_sock_qlen(lopt) : 0; } -static inline void reqsk_queue_hash_req(struct request_sock_queue *queue, - u32 hash, struct request_sock *req, - unsigned long timeout) +static inline int reqsk_queue_len_young(const struct request_sock_queue *queue) { - struct listen_sock *lopt = queue->listen_opt; - - req->expires = jiffies + timeout; - req->num_retrans = 0; - req->num_timeout = 0; - req->sk = NULL; - - /* before letting lookups find us, make sure all req fields - * are committed to memory and refcnt initialized. - */ - smp_wmb(); - atomic_set(&req->rsk_refcnt, 1); + return listen_sock_young(queue->listen_opt); +} - req->rsk_hash = hash; - write_lock(&queue->syn_wait_lock); - req->dl_next = lopt->syn_table[hash]; - lopt->syn_table[hash] = req; - write_unlock(&queue->syn_wait_lock); +static inline int reqsk_queue_is_full(const struct request_sock_queue *queue) +{ + return reqsk_queue_len(queue) >> queue->listen_opt->max_qlen_log; } +void reqsk_queue_hash_req(struct request_sock_queue *queue, + u32 hash, struct request_sock *req, + unsigned long timeout); + #endif /* _REQUEST_SOCK_H */ diff --git a/net/core/request_sock.c b/net/core/request_sock.c index cc39a2aa663a..cdc0ddd9ac9f 100644 --- a/net/core/request_sock.c +++ b/net/core/request_sock.c @@ -94,21 +94,26 @@ void reqsk_queue_destroy(struct request_sock_queue *queue) /* make all the listen_opt local to us */ struct listen_sock *lopt = reqsk_queue_yank_listen_sk(queue); - if (lopt->qlen != 0) { + if (listen_sock_qlen(lopt) != 0) { unsigned int i; for (i = 0; i < lopt->nr_table_entries; i++) { struct request_sock *req; + write_lock_bh(&queue->syn_wait_lock); while ((req = lopt->syn_table[i]) != NULL) { lopt->syn_table[i] = req->dl_next; - lopt->qlen--; + atomic_inc(&lopt->qlen_dec); + if (del_timer(&req->rsk_timer)) + reqsk_put(req); reqsk_put(req); } + write_unlock_bh(&queue->syn_wait_lock); } } - WARN_ON(lopt->qlen != 0); + if (WARN_ON(listen_sock_qlen(lopt) != 0)) + pr_err("qlen %u\n", listen_sock_qlen(lopt)); kvfree(lopt); } @@ -187,7 +192,7 @@ void reqsk_fastopen_remove(struct sock *sk, struct request_sock *req, * * For more details see CoNext'11 "TCP Fast Open" paper. */ - req->expires = jiffies + 60*HZ; + req->rsk_timer.expires = jiffies + 60*HZ; if (fastopenq->rskq_rst_head == NULL) fastopenq->rskq_rst_head = req; else diff --git a/net/core/sock.c b/net/core/sock.c index d9f9e4825362..744a04ddb61c 100644 --- a/net/core/sock.c +++ b/net/core/sock.c @@ -2739,7 +2739,7 @@ static int req_prot_init(const struct proto *prot) rsk_prot->slab = kmem_cache_create(rsk_prot->slab_name, rsk_prot->obj_size, 0, - SLAB_HWCACHE_ALIGN, NULL); + 0, NULL); if (!rsk_prot->slab) { pr_crit("%s: Can't create request sock SLAB cache!\n", diff --git a/net/dccp/ipv4.c b/net/dccp/ipv4.c index 5bffbbaf1fac..25a9615b3b88 100644 --- a/net/dccp/ipv4.c +++ b/net/dccp/ipv4.c @@ -306,6 +306,7 @@ static void dccp_v4_err(struct sk_buff *skb, u32 info) if (!between48(seq, dccp_rsk(req)->dreq_iss, dccp_rsk(req)->dreq_gss)) { NET_INC_STATS_BH(net, LINUX_MIB_OUTOFWINDOWICMPS); + reqsk_put(req); goto out; } /* @@ -315,6 +316,7 @@ static void dccp_v4_err(struct sk_buff *skb, u32 info) * errors returned from accept(). */ inet_csk_reqsk_queue_drop(sk, req); + reqsk_put(req); goto out; case DCCP_REQUESTING: @@ -451,9 +453,11 @@ static struct sock *dccp_v4_hnd_req(struct sock *sk, struct sk_buff *skb) /* Find possible connection requests. */ struct request_sock *req = inet_csk_search_req(sk, dh->dccph_sport, iph->saddr, iph->daddr); - if (req) - return dccp_check_req(sk, skb, req); - + if (req) { + nsk = dccp_check_req(sk, skb, req); + reqsk_put(req); + return nsk; + } nsk = inet_lookup_established(sock_net(sk), &dccp_hashinfo, iph->saddr, dh->dccph_sport, iph->daddr, dh->dccph_dport, diff --git a/net/dccp/ipv6.c b/net/dccp/ipv6.c index ae2184039fe3..69d8f13895ba 100644 --- a/net/dccp/ipv6.c +++ b/net/dccp/ipv6.c @@ -157,7 +157,7 @@ static void dccp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, req = inet6_csk_search_req(sk, dh->dccph_dport, &hdr->daddr, &hdr->saddr, inet6_iif(skb)); - if (req == NULL) + if (!req) goto out; /* @@ -169,10 +169,12 @@ static void dccp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, if (!between48(seq, dccp_rsk(req)->dreq_iss, dccp_rsk(req)->dreq_gss)) { NET_INC_STATS_BH(net, LINUX_MIB_OUTOFWINDOWICMPS); + reqsk_put(req); goto out; } inet_csk_reqsk_queue_drop(sk, req); + reqsk_put(req); goto out; case DCCP_REQUESTING: @@ -322,9 +324,11 @@ static struct sock *dccp_v6_hnd_req(struct sock *sk,struct sk_buff *skb) req = inet6_csk_search_req(sk, dh->dccph_sport, &iph->saddr, &iph->daddr, inet6_iif(skb)); - if (req != NULL) - return dccp_check_req(sk, skb, req); - + if (req) { + nsk = dccp_check_req(sk, skb, req); + reqsk_put(req); + return nsk; + } nsk = __inet6_lookup_established(sock_net(sk), &dccp_hashinfo, &iph->saddr, dh->dccph_sport, &iph->daddr, ntohs(dh->dccph_dport), diff --git a/net/dccp/timer.c b/net/dccp/timer.c index 1cd46a345cb0..3ef7acef3ce8 100644 --- a/net/dccp/timer.c +++ b/net/dccp/timer.c @@ -161,33 +161,11 @@ out: sock_put(sk); } -/* - * Timer for listening sockets - */ -static void dccp_response_timer(struct sock *sk) -{ - inet_csk_reqsk_queue_prune(sk, TCP_SYNQ_INTERVAL, DCCP_TIMEOUT_INIT, - DCCP_RTO_MAX); -} - static void dccp_keepalive_timer(unsigned long data) { struct sock *sk = (struct sock *)data; - /* Only process if socket is not in use. */ - bh_lock_sock(sk); - if (sock_owned_by_user(sk)) { - /* Try again later. */ - inet_csk_reset_keepalive_timer(sk, HZ / 20); - goto out; - } - - if (sk->sk_state == DCCP_LISTEN) { - dccp_response_timer(sk); - goto out; - } -out: - bh_unlock_sock(sk); + pr_err("dccp should not use a keepalive timer !\n"); sock_put(sk); } diff --git a/net/ipv4/inet_connection_sock.c b/net/ipv4/inet_connection_sock.c index 4f57a017928c..126a37a156cf 100644 --- a/net/ipv4/inet_connection_sock.c +++ b/net/ipv4/inet_connection_sock.c @@ -23,6 +23,7 @@ #include #include #include +#include #ifdef INET_CSK_DEBUG const char inet_csk_timer_bug_msg[] = "inet_csk BUG: unknown timer value\n"; @@ -476,31 +477,37 @@ static inline u32 inet_synq_hash(const __be32 raddr, const __be16 rport, #if IS_ENABLED(CONFIG_IPV6) #define AF_INET_FAMILY(fam) ((fam) == AF_INET) #else -#define AF_INET_FAMILY(fam) 1 +#define AF_INET_FAMILY(fam) true #endif -struct request_sock *inet_csk_search_req(const struct sock *sk, - const __be16 rport, const __be32 raddr, +/* Note: this is temporary : + * req sock will no longer be in listener hash table +*/ +struct request_sock *inet_csk_search_req(struct sock *sk, + const __be16 rport, + const __be32 raddr, const __be32 laddr) { - const struct inet_connection_sock *icsk = inet_csk(sk); + struct inet_connection_sock *icsk = inet_csk(sk); struct listen_sock *lopt = icsk->icsk_accept_queue.listen_opt; struct request_sock *req; + u32 hash = inet_synq_hash(raddr, rport, lopt->hash_rnd, + lopt->nr_table_entries); - for (req = lopt->syn_table[inet_synq_hash(raddr, rport, lopt->hash_rnd, - lopt->nr_table_entries)]; - req != NULL; - req = req->dl_next) { + write_lock(&icsk->icsk_accept_queue.syn_wait_lock); + for (req = lopt->syn_table[hash]; req != NULL; req = req->dl_next) { const struct inet_request_sock *ireq = inet_rsk(req); if (ireq->ir_rmt_port == rport && ireq->ir_rmt_addr == raddr && ireq->ir_loc_addr == laddr && AF_INET_FAMILY(req->rsk_ops->family)) { + atomic_inc(&req->rsk_refcnt); WARN_ON(req->sk); break; } } + write_unlock(&icsk->icsk_accept_queue.syn_wait_lock); return req; } @@ -556,23 +563,23 @@ int inet_rtx_syn_ack(struct sock *parent, struct request_sock *req) } EXPORT_SYMBOL(inet_rtx_syn_ack); -void inet_csk_reqsk_queue_prune(struct sock *parent, - const unsigned long interval, - const unsigned long timeout, - const unsigned long max_rto) +static void reqsk_timer_handler(unsigned long data) { - struct inet_connection_sock *icsk = inet_csk(parent); + struct request_sock *req = (struct request_sock *)data; + struct sock *sk_listener = req->rsk_listener; + struct inet_connection_sock *icsk = inet_csk(sk_listener); struct request_sock_queue *queue = &icsk->icsk_accept_queue; struct listen_sock *lopt = queue->listen_opt; - int max_retries = icsk->icsk_syn_retries ? : sysctl_tcp_synack_retries; - int thresh = max_retries; - unsigned long now = jiffies; - struct request_sock **reqp, *req; - int i, budget; + int expire = 0, resend = 0; + int max_retries, thresh; - if (lopt == NULL || lopt->qlen == 0) + if (sk_listener->sk_state != TCP_LISTEN || !lopt) { + reqsk_put(req); return; + } + max_retries = icsk->icsk_syn_retries ? : sysctl_tcp_synack_retries; + thresh = max_retries; /* Normally all the openreqs are young and become mature * (i.e. converted to established socket) for first timeout. * If synack was not acknowledged for 1 second, it means @@ -590,71 +597,63 @@ void inet_csk_reqsk_queue_prune(struct sock *parent, * embrions; and abort old ones without pity, if old * ones are about to clog our table. */ - if (lopt->qlen>>(lopt->max_qlen_log-1)) { - int young = (lopt->qlen_young<<1); + if (listen_sock_qlen(lopt) >> (lopt->max_qlen_log - 1)) { + int young = listen_sock_young(lopt) << 1; while (thresh > 2) { - if (lopt->qlen < young) + if (listen_sock_qlen(lopt) < young) break; thresh--; young <<= 1; } } - if (queue->rskq_defer_accept) max_retries = queue->rskq_defer_accept; + syn_ack_recalc(req, thresh, max_retries, queue->rskq_defer_accept, + &expire, &resend); + req->rsk_ops->syn_ack_timeout(sk_listener, req); + if (!expire && + (!resend || + !inet_rtx_syn_ack(sk_listener, req) || + inet_rsk(req)->acked)) { + unsigned long timeo; + + if (req->num_timeout++ == 0) + atomic_inc(&lopt->young_dec); + timeo = min(TCP_TIMEOUT_INIT << req->num_timeout, TCP_RTO_MAX); + mod_timer_pinned(&req->rsk_timer, jiffies + timeo); + return; + } + inet_csk_reqsk_queue_drop(sk_listener, req); + reqsk_put(req); +} - budget = 2 * (lopt->nr_table_entries / (timeout / interval)); - i = lopt->clock_hand; - - do { - reqp = &lopt->syn_table[i]; - if (!*reqp) - goto next_bucket; - write_lock(&queue->syn_wait_lock); - while ((req = *reqp) != NULL) { - if (time_after_eq(now, req->expires)) { - int expire = 0, resend = 0; - - syn_ack_recalc(req, thresh, max_retries, - queue->rskq_defer_accept, - &expire, &resend); - req->rsk_ops->syn_ack_timeout(parent, req); - if (!expire && - (!resend || - !inet_rtx_syn_ack(parent, req) || - inet_rsk(req)->acked)) { - unsigned long timeo; - - if (req->num_timeout++ == 0) - lopt->qlen_young--; - timeo = min(timeout << req->num_timeout, - max_rto); - req->expires = now + timeo; - reqp = &req->dl_next; - continue; - } +void reqsk_queue_hash_req(struct request_sock_queue *queue, + u32 hash, struct request_sock *req, + unsigned long timeout) +{ + struct listen_sock *lopt = queue->listen_opt; - /* Drop this request */ - *reqp = req->dl_next; - reqsk_queue_removed(queue, req); - reqsk_put(req); - continue; - } - reqp = &req->dl_next; - } - write_unlock(&queue->syn_wait_lock); -next_bucket: - i = (i + 1) & (lopt->nr_table_entries - 1); + req->num_retrans = 0; + req->num_timeout = 0; + req->sk = NULL; - } while (--budget > 0); + /* before letting lookups find us, make sure all req fields + * are committed to memory and refcnt initialized. + */ + smp_wmb(); + atomic_set(&req->rsk_refcnt, 2); + setup_timer(&req->rsk_timer, reqsk_timer_handler, (unsigned long)req); + req->rsk_hash = hash; - lopt->clock_hand = i; + write_lock(&queue->syn_wait_lock); + req->dl_next = lopt->syn_table[hash]; + lopt->syn_table[hash] = req; + write_unlock(&queue->syn_wait_lock); - if (lopt->qlen) - inet_csk_reset_keepalive_timer(parent, interval); + mod_timer_pinned(&req->rsk_timer, jiffies + timeout); } -EXPORT_SYMBOL_GPL(inet_csk_reqsk_queue_prune); +EXPORT_SYMBOL(reqsk_queue_hash_req); /** * inet_csk_clone_lock - clone an inet socket, and lock its clone @@ -790,8 +789,6 @@ void inet_csk_listen_stop(struct sock *sk) struct request_sock *acc_req; struct request_sock *req; - inet_csk_delete_keepalive_timer(sk); - /* make all the listen_opt local to us */ acc_req = reqsk_queue_yank_acceptq(queue); diff --git a/net/ipv4/inet_diag.c b/net/ipv4/inet_diag.c index 74c39c9f3e11..34073bbe2700 100644 --- a/net/ipv4/inet_diag.c +++ b/net/ipv4/inet_diag.c @@ -285,7 +285,7 @@ static int inet_req_diag_fill(struct sock *sk, struct sk_buff *skb, BUILD_BUG_ON(offsetof(struct inet_request_sock, ir_cookie) != offsetof(struct sock, sk_cookie)); - tmo = inet_reqsk(sk)->expires - jiffies; + tmo = inet_reqsk(sk)->rsk_timer.expires - jiffies; r->idiag_expires = (tmo >= 0) ? jiffies_to_msecs(tmo) : 0; r->idiag_rqueue = 0; r->idiag_wqueue = 0; @@ -719,7 +719,7 @@ static int inet_diag_dump_reqs(struct sk_buff *skb, struct sock *sk, read_lock_bh(&icsk->icsk_accept_queue.syn_wait_lock); lopt = icsk->icsk_accept_queue.listen_opt; - if (!lopt || !lopt->qlen) + if (!lopt || !listen_sock_qlen(lopt)) goto out; if (bc) { diff --git a/net/ipv4/syncookies.c b/net/ipv4/syncookies.c index ef01d8570358..805dc444741d 100644 --- a/net/ipv4/syncookies.c +++ b/net/ipv4/syncookies.c @@ -361,7 +361,6 @@ struct sock *cookie_v4_check(struct sock *sk, struct sk_buff *skb) goto out; } - req->expires = 0UL; req->num_retrans = 0; /* diff --git a/net/ipv4/tcp_fastopen.c b/net/ipv4/tcp_fastopen.c index 82e375a0cbcf..2eb887ec0ce3 100644 --- a/net/ipv4/tcp_fastopen.c +++ b/net/ipv4/tcp_fastopen.c @@ -240,7 +240,7 @@ static bool tcp_fastopen_queue_check(struct sock *sk) struct request_sock *req1; spin_lock(&fastopenq->lock); req1 = fastopenq->rskq_rst_head; - if ((req1 == NULL) || time_after(req1->expires, jiffies)) { + if (!req1 || time_after(req1->rsk_timer.expires, jiffies)) { spin_unlock(&fastopenq->lock); NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_TCPFASTOPENLISTENOVERFLOW); diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index 19c3770f1e97..5554b8f33d41 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -475,6 +475,7 @@ void tcp_v4_err(struct sk_buff *icmp_skb, u32 info) if (seq != tcp_rsk(req)->snt_isn) { NET_INC_STATS_BH(net, LINUX_MIB_OUTOFWINDOWICMPS); + reqsk_put(req); goto out; } @@ -486,6 +487,7 @@ void tcp_v4_err(struct sk_buff *icmp_skb, u32 info) */ inet_csk_reqsk_queue_drop(sk, req); NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_LISTENDROPS); + reqsk_put(req); goto out; case TCP_SYN_SENT: @@ -1398,8 +1400,11 @@ static struct sock *tcp_v4_hnd_req(struct sock *sk, struct sk_buff *skb) struct sock *nsk; req = inet_csk_search_req(sk, th->source, iph->saddr, iph->daddr); - if (req) - return tcp_check_req(sk, skb, req, false); + if (req) { + nsk = tcp_check_req(sk, skb, req, false); + reqsk_put(req); + return nsk; + } nsk = inet_lookup_established(sock_net(sk), &tcp_hashinfo, iph->saddr, th->source, iph->daddr, th->dest, inet_iif(skb)); @@ -2208,7 +2213,7 @@ static void get_openreq4(const struct request_sock *req, struct seq_file *f, int i, kuid_t uid) { const struct inet_request_sock *ireq = inet_rsk(req); - long delta = req->expires - jiffies; + long delta = req->rsk_timer.expires - jiffies; seq_printf(f, "%4d: %08X:%04X %08X:%04X" " %02X %08X:%08X %02X:%08lX %08X %5u %8d %u %d %pK", diff --git a/net/ipv4/tcp_minisocks.c b/net/ipv4/tcp_minisocks.c index 848bcab358e4..274e96fb369b 100644 --- a/net/ipv4/tcp_minisocks.c +++ b/net/ipv4/tcp_minisocks.c @@ -629,8 +629,9 @@ struct sock *tcp_check_req(struct sock *sk, struct sk_buff *skb, &tcp_rsk(req)->last_oow_ack_time) && !inet_rtx_syn_ack(sk, req)) - req->expires = min(TCP_TIMEOUT_INIT << req->num_timeout, - TCP_RTO_MAX) + jiffies; + mod_timer_pending(&req->rsk_timer, jiffies + + min(TCP_TIMEOUT_INIT << req->num_timeout, + TCP_RTO_MAX)); return NULL; } diff --git a/net/ipv4/tcp_timer.c b/net/ipv4/tcp_timer.c index 15505936511d..3daa6b5d766d 100644 --- a/net/ipv4/tcp_timer.c +++ b/net/ipv4/tcp_timer.c @@ -539,16 +539,6 @@ static void tcp_write_timer(unsigned long data) sock_put(sk); } -/* - * Timer for listening sockets - */ - -static void tcp_synack_timer(struct sock *sk) -{ - inet_csk_reqsk_queue_prune(sk, TCP_SYNQ_INTERVAL, - TCP_TIMEOUT_INIT, TCP_RTO_MAX); -} - void tcp_syn_ack_timeout(struct sock *sk, struct request_sock *req) { NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_TCPTIMEOUTS); @@ -583,7 +573,7 @@ static void tcp_keepalive_timer (unsigned long data) } if (sk->sk_state == TCP_LISTEN) { - tcp_synack_timer(sk); + pr_err("Hmm... keepalive on a LISTEN ???\n"); goto out; } diff --git a/net/ipv6/inet6_connection_sock.c b/net/ipv6/inet6_connection_sock.c index b7acb9ebc4f5..2f3bbe569e8f 100644 --- a/net/ipv6/inet6_connection_sock.c +++ b/net/ipv6/inet6_connection_sock.c @@ -112,21 +112,20 @@ static u32 inet6_synq_hash(const struct in6_addr *raddr, const __be16 rport, return c & (synq_hsize - 1); } -struct request_sock *inet6_csk_search_req(const struct sock *sk, +struct request_sock *inet6_csk_search_req(struct sock *sk, const __be16 rport, const struct in6_addr *raddr, const struct in6_addr *laddr, const int iif) { - const struct inet_connection_sock *icsk = inet_csk(sk); + struct inet_connection_sock *icsk = inet_csk(sk); struct listen_sock *lopt = icsk->icsk_accept_queue.listen_opt; struct request_sock *req; + u32 hash = inet6_synq_hash(raddr, rport, lopt->hash_rnd, + lopt->nr_table_entries); - for (req = lopt->syn_table[inet6_synq_hash(raddr, rport, - lopt->hash_rnd, - lopt->nr_table_entries)]; - req != NULL; - req = req->dl_next) { + write_lock(&icsk->icsk_accept_queue.syn_wait_lock); + for (req = lopt->syn_table[hash]; req != NULL; req = req->dl_next) { const struct inet_request_sock *ireq = inet_rsk(req); if (ireq->ir_rmt_port == rport && @@ -134,12 +133,14 @@ struct request_sock *inet6_csk_search_req(const struct sock *sk, ipv6_addr_equal(&ireq->ir_v6_rmt_addr, raddr) && ipv6_addr_equal(&ireq->ir_v6_loc_addr, laddr) && (!ireq->ir_iif || ireq->ir_iif == iif)) { + atomic_inc(&req->rsk_refcnt); WARN_ON(req->sk != NULL); - return req; + break; } } + write_unlock(&icsk->icsk_accept_queue.syn_wait_lock); - return NULL; + return req; } EXPORT_SYMBOL_GPL(inet6_csk_search_req); diff --git a/net/ipv6/syncookies.c b/net/ipv6/syncookies.c index da5823e5e5a7..2819137fc87d 100644 --- a/net/ipv6/syncookies.c +++ b/net/ipv6/syncookies.c @@ -222,7 +222,6 @@ struct sock *cookie_v6_check(struct sock *sk, struct sk_buff *skb) ireq->ir_mark = inet_request_mark(sk, skb); - req->expires = 0UL; req->num_retrans = 0; ireq->snd_wscale = tcp_opt.snd_wscale; ireq->sack_ok = tcp_opt.sack_ok; diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c index 146f123b52c9..6e3f90db038c 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c @@ -421,11 +421,13 @@ static void tcp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, if (seq != tcp_rsk(req)->snt_isn) { NET_INC_STATS_BH(net, LINUX_MIB_OUTOFWINDOWICMPS); + reqsk_put(req); goto out; } inet_csk_reqsk_queue_drop(sk, req); NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_LISTENDROPS); + reqsk_put(req); goto out; case TCP_SYN_SENT: @@ -988,9 +990,11 @@ static struct sock *tcp_v6_hnd_req(struct sock *sk, struct sk_buff *skb) req = inet6_csk_search_req(sk, th->source, &ipv6_hdr(skb)->saddr, &ipv6_hdr(skb)->daddr, tcp_v6_iif(skb)); - if (req) - return tcp_check_req(sk, skb, req, false); - + if (req) { + nsk = tcp_check_req(sk, skb, req, false); + reqsk_put(req); + return nsk; + } nsk = __inet6_lookup_established(sock_net(sk), &tcp_hashinfo, &ipv6_hdr(skb)->saddr, th->source, &ipv6_hdr(skb)->daddr, ntohs(th->dest), @@ -1670,7 +1674,7 @@ static void tcp_v6_destroy_sock(struct sock *sk) static void get_openreq6(struct seq_file *seq, struct request_sock *req, int i, kuid_t uid) { - int ttd = req->expires - jiffies; + long ttd = req->rsk_timer.expires - jiffies; const struct in6_addr *src = &inet_rsk(req)->ir_v6_loc_addr; const struct in6_addr *dest = &inet_rsk(req)->ir_v6_rmt_addr; -- cgit v1.2.3 From becb74f0acca19b5abfcb24dc602530f3deea66a Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Thu, 19 Mar 2015 19:04:21 -0700 Subject: net: increase sk_[max_]ack_backlog sk_ack_backlog & sk_max_ack_backlog were 16bit fields, meaning listen() backlog was limited to 65535. It is time to increase the width to allow much bigger backlog, if admins change /proc/sys/net/core/somaxconn & /proc/sys/net/ipv4/tcp_max_syn_backlog default values. Tested: echo 5000000 >/proc/sys/net/core/somaxconn echo 5000000 >/proc/sys/net/ipv4/tcp_max_syn_backlog Ran a SYNFLOOD test against a listener using listen(fd, 5000000) myhost~# grep request_sock_TCP /proc/slabinfo request_sock_TCP 4185642 4411940 304 13 1 : tunables 54 27 8 : slabdata 339380 339380 0 Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- include/net/sock.h | 4 ++-- net/core/sysctl_net_core.c | 2 -- 2 files changed, 2 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/include/net/sock.h b/include/net/sock.h index e0360f5a53e9..3f9b8ce56948 100644 --- a/include/net/sock.h +++ b/include/net/sock.h @@ -405,8 +405,8 @@ struct sock { rwlock_t sk_callback_lock; int sk_err, sk_err_soft; - unsigned short sk_ack_backlog; - unsigned short sk_max_ack_backlog; + u32 sk_ack_backlog; + u32 sk_max_ack_backlog; __u32 sk_priority; #if IS_ENABLED(CONFIG_CGROUP_NET_PRIO) __u32 sk_cgrp_prioidx; diff --git a/net/core/sysctl_net_core.c b/net/core/sysctl_net_core.c index 433424804284..e1c85db5216f 100644 --- a/net/core/sysctl_net_core.c +++ b/net/core/sysctl_net_core.c @@ -24,7 +24,6 @@ static int zero = 0; static int one = 1; -static int ushort_max = USHRT_MAX; static int net_msg_warn; /* Unused, but still a sysctl */ @@ -401,7 +400,6 @@ static struct ctl_table netns_core_table[] = { .maxlen = sizeof(int), .mode = 0644, .extra1 = &zero, - .extra2 = &ushort_max, .proc_handler = proc_dointvec_minmax }, { } -- cgit v1.2.3 From 488fb86ee91d3b1182c2e30a9f9b45da14eda46f Mon Sep 17 00:00:00 2001 From: Herbert Xu Date: Fri, 20 Mar 2015 21:56:59 +1100 Subject: rhashtable: Make rhashtable_init params argument const This patch marks the rhashtable_init params argument const as there is no reason to modify it since we will always make a copy of it in the rhashtable. This patch also fixes a bug where we don't actually round up the value of min_size unless it is less than HASH_MIN_SIZE. Signed-off-by: Herbert Xu Acked-by: Thomas Graf Signed-off-by: David S. Miller --- include/linux/rhashtable.h | 3 ++- lib/rhashtable.c | 7 ++++--- 2 files changed, 6 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/include/linux/rhashtable.h b/include/linux/rhashtable.h index 99425f2be708..c85363c45fc0 100644 --- a/include/linux/rhashtable.h +++ b/include/linux/rhashtable.h @@ -181,7 +181,8 @@ static inline int lockdep_rht_bucket_is_held(const struct bucket_table *tbl, } #endif /* CONFIG_PROVE_LOCKING */ -int rhashtable_init(struct rhashtable *ht, struct rhashtable_params *params); +int rhashtable_init(struct rhashtable *ht, + const struct rhashtable_params *params); void rhashtable_insert(struct rhashtable *ht, struct rhash_head *node); bool rhashtable_remove(struct rhashtable *ht, struct rhash_head *node); diff --git a/lib/rhashtable.c b/lib/rhashtable.c index e75c48d9d82f..e0a9d59f80d6 100644 --- a/lib/rhashtable.c +++ b/lib/rhashtable.c @@ -870,7 +870,7 @@ out: } EXPORT_SYMBOL_GPL(rhashtable_walk_stop); -static size_t rounded_hashtable_size(struct rhashtable_params *params) +static size_t rounded_hashtable_size(const struct rhashtable_params *params) { return max(roundup_pow_of_two(params->nelem_hint * 4 / 3), (unsigned long)params->min_size); @@ -919,7 +919,8 @@ static size_t rounded_hashtable_size(struct rhashtable_params *params) * .obj_hashfn = my_hash_fn, * }; */ -int rhashtable_init(struct rhashtable *ht, struct rhashtable_params *params) +int rhashtable_init(struct rhashtable *ht, + const struct rhashtable_params *params) { struct bucket_table *tbl; size_t size; @@ -946,7 +947,7 @@ int rhashtable_init(struct rhashtable *ht, struct rhashtable_params *params) if (params->max_size) ht->p.max_size = rounddown_pow_of_two(params->max_size); - ht->p.min_size = max(params->min_size, HASH_MIN_SIZE); + ht->p.min_size = max(ht->p.min_size, HASH_MIN_SIZE); if (params->locks_mul) ht->p.locks_mul = roundup_pow_of_two(params->locks_mul); -- cgit v1.2.3 From 02fd97c3d4a8a14e222b0021c366db7041d28743 Mon Sep 17 00:00:00 2001 From: Herbert Xu Date: Fri, 20 Mar 2015 21:57:00 +1100 Subject: rhashtable: Allow hash/comparison functions to be inlined This patch deals with the complaint that we make indirect function calls on the fast paths unnecessarily in rhashtable. We resolve it by moving the fast paths into inline functions that take struct rhashtable_param (which obviously must be the same set of parameters supplied to rhashtable_init) as an argument. The only remaining indirect call is to obj_hashfn (or key_hashfn it obj_hashfn is unset) on the rehash as well as the insert-during- rehash slow path. This patch also extends the support of vairable-length keys to include those where the key is fixed but scattered in the object. For example, in netlink we want to key off the namespace and the portid but they're not next to each other. This patch does this by directly using the object hash function as the indicator of whether the key is accessible or not. It also adds a new function obj_cmpfn to compare a key against an object. This means that the caller no longer needs to supply explicit compare functions. All this is done in a backwards compatible manner so no existing users are affected until they convert to the new interface. Signed-off-by: Herbert Xu Signed-off-by: David S. Miller --- include/linux/rhashtable.h | 386 +++++++++++++++++++++++++++++++++++++++++++++ lib/rhashtable.c | 163 ++++++------------- 2 files changed, 436 insertions(+), 113 deletions(-) (limited to 'include') diff --git a/include/linux/rhashtable.h b/include/linux/rhashtable.h index c85363c45fc0..a7188eeb135b 100644 --- a/include/linux/rhashtable.h +++ b/include/linux/rhashtable.h @@ -22,6 +22,7 @@ #include #include #include +#include /* * The end of the chain is marked with a special nulls marks which has @@ -42,6 +43,9 @@ #define RHT_HASH_BITS 27 #define RHT_BASE_SHIFT RHT_HASH_BITS +/* Base bits plus 1 bit for nulls marker */ +#define RHT_HASH_RESERVED_SPACE (RHT_BASE_BITS + 1) + struct rhash_head { struct rhash_head __rcu *next; }; @@ -72,8 +76,20 @@ struct bucket_table { struct rhash_head __rcu *buckets[] ____cacheline_aligned_in_smp; }; +/** + * struct rhashtable_compare_arg - Key for the function rhashtable_compare + * @ht: Hash table + * @key: Key to compare against + */ +struct rhashtable_compare_arg { + struct rhashtable *ht; + const void *key; +}; + typedef u32 (*rht_hashfn_t)(const void *data, u32 len, u32 seed); typedef u32 (*rht_obj_hashfn_t)(const void *data, u32 seed); +typedef int (*rht_obj_cmpfn_t)(struct rhashtable_compare_arg *arg, + const void *obj); struct rhashtable; @@ -89,6 +105,7 @@ struct rhashtable; * @locks_mul: Number of bucket locks to allocate per cpu (default: 128) * @hashfn: Function to hash key * @obj_hashfn: Function to hash object + * @obj_cmpfn: Function to compare key with object */ struct rhashtable_params { size_t nelem_hint; @@ -101,6 +118,7 @@ struct rhashtable_params { size_t locks_mul; rht_hashfn_t hashfn; rht_obj_hashfn_t obj_hashfn; + rht_obj_cmpfn_t obj_cmpfn; }; /** @@ -165,6 +183,83 @@ static inline unsigned long rht_get_nulls_value(const struct rhash_head *ptr) return ((unsigned long) ptr) >> 1; } +static inline void *rht_obj(const struct rhashtable *ht, + const struct rhash_head *he) +{ + return (char *)he - ht->p.head_offset; +} + +static inline unsigned int rht_bucket_index(const struct bucket_table *tbl, + unsigned int hash) +{ + return (hash >> RHT_HASH_RESERVED_SPACE) & (tbl->size - 1); +} + +static inline unsigned int rht_key_hashfn( + struct rhashtable *ht, const struct bucket_table *tbl, + const void *key, const struct rhashtable_params params) +{ + return rht_bucket_index(tbl, params.hashfn(key, params.key_len ?: + ht->p.key_len, + tbl->hash_rnd)); +} + +static inline unsigned int rht_head_hashfn( + struct rhashtable *ht, const struct bucket_table *tbl, + const struct rhash_head *he, const struct rhashtable_params params) +{ + const char *ptr = rht_obj(ht, he); + + return likely(params.obj_hashfn) ? + rht_bucket_index(tbl, params.obj_hashfn(ptr, tbl->hash_rnd)) : + rht_key_hashfn(ht, tbl, ptr + params.key_offset, params); +} + +/** + * rht_grow_above_75 - returns true if nelems > 0.75 * table-size + * @ht: hash table + * @tbl: current table + */ +static inline bool rht_grow_above_75(const struct rhashtable *ht, + const struct bucket_table *tbl) +{ + /* Expand table when exceeding 75% load */ + return atomic_read(&ht->nelems) > (tbl->size / 4 * 3) && + (!ht->p.max_size || tbl->size < ht->p.max_size); +} + +/** + * rht_shrink_below_30 - returns true if nelems < 0.3 * table-size + * @ht: hash table + * @tbl: current table + */ +static inline bool rht_shrink_below_30(const struct rhashtable *ht, + const struct bucket_table *tbl) +{ + /* Shrink table beneath 30% load */ + return atomic_read(&ht->nelems) < (tbl->size * 3 / 10) && + tbl->size > ht->p.min_size; +} + +/* The bucket lock is selected based on the hash and protects mutations + * on a group of hash buckets. + * + * A maximum of tbl->size/2 bucket locks is allocated. This ensures that + * a single lock always covers both buckets which may both contains + * entries which link to the same bucket of the old table during resizing. + * This allows to simplify the locking as locking the bucket in both + * tables during resize always guarantee protection. + * + * IMPORTANT: When holding the bucket lock of both the old and new table + * during expansions and shrinking, the old bucket lock must always be + * acquired first. + */ +static inline spinlock_t *rht_bucket_lock(const struct bucket_table *tbl, + unsigned int hash) +{ + return &tbl->locks[hash & tbl->locks_mask]; +} + #ifdef CONFIG_PROVE_LOCKING int lockdep_rht_mutex_is_held(struct rhashtable *ht); int lockdep_rht_bucket_is_held(const struct bucket_table *tbl, u32 hash); @@ -184,6 +279,9 @@ static inline int lockdep_rht_bucket_is_held(const struct bucket_table *tbl, int rhashtable_init(struct rhashtable *ht, const struct rhashtable_params *params); +int rhashtable_insert_slow(struct rhashtable *ht, const void *key, + struct rhash_head *obj, + struct bucket_table *old_tbl); void rhashtable_insert(struct rhashtable *ht, struct rhash_head *node); bool rhashtable_remove(struct rhashtable *ht, struct rhash_head *node); @@ -356,4 +454,292 @@ void rhashtable_destroy(struct rhashtable *ht); rht_for_each_entry_rcu_continue(tpos, pos, (tbl)->buckets[hash],\ tbl, hash, member) +static inline int rhashtable_compare(struct rhashtable_compare_arg *arg, + const void *obj) +{ + struct rhashtable *ht = arg->ht; + const char *ptr = obj; + + return memcmp(ptr + ht->p.key_offset, arg->key, ht->p.key_len); +} + +/** + * rhashtable_lookup_fast - search hash table, inlined version + * @ht: hash table + * @key: the pointer to the key + * @params: hash table parameters + * + * Computes the hash value for the key and traverses the bucket chain looking + * for a entry with an identical key. The first matching entry is returned. + * + * Returns the first entry on which the compare function returned true. + */ +static inline void *rhashtable_lookup_fast( + struct rhashtable *ht, const void *key, + const struct rhashtable_params params) +{ + struct rhashtable_compare_arg arg = { + .ht = ht, + .key = key, + }; + const struct bucket_table *tbl; + struct rhash_head *he; + unsigned hash; + + rcu_read_lock(); + + tbl = rht_dereference_rcu(ht->tbl, ht); +restart: + hash = rht_key_hashfn(ht, tbl, key, params); + rht_for_each_rcu(he, tbl, hash) { + if (params.obj_cmpfn ? + params.obj_cmpfn(&arg, rht_obj(ht, he)) : + rhashtable_compare(&arg, rht_obj(ht, he))) + continue; + rcu_read_unlock(); + return rht_obj(ht, he); + } + + /* Ensure we see any new tables. */ + smp_rmb(); + + tbl = rht_dereference_rcu(tbl->future_tbl, ht); + if (unlikely(tbl)) + goto restart; + rcu_read_unlock(); + + return NULL; +} + +static inline int __rhashtable_insert_fast( + struct rhashtable *ht, const void *key, struct rhash_head *obj, + const struct rhashtable_params params) +{ + struct rhashtable_compare_arg arg = { + .ht = ht, + .key = key, + }; + int err = -EEXIST; + struct bucket_table *tbl, *new_tbl; + struct rhash_head *head; + spinlock_t *lock; + unsigned hash; + + rcu_read_lock(); + + tbl = rht_dereference_rcu(ht->tbl, ht); + hash = rht_head_hashfn(ht, tbl, obj, params); + lock = rht_bucket_lock(tbl, hash); + + spin_lock_bh(lock); + + /* Because we have already taken the bucket lock in tbl, + * if we find that future_tbl is not yet visible then + * that guarantees all other insertions of the same entry + * will also grab the bucket lock in tbl because until + * the rehash completes ht->tbl won't be changed. + */ + new_tbl = rht_dereference_rcu(tbl->future_tbl, ht); + if (unlikely(new_tbl)) { + err = rhashtable_insert_slow(ht, key, obj, new_tbl); + goto out; + } + + if (!key) + goto skip_lookup; + + rht_for_each(head, tbl, hash) { + if (unlikely(!(params.obj_cmpfn ? + params.obj_cmpfn(&arg, rht_obj(ht, head)) : + rhashtable_compare(&arg, rht_obj(ht, head))))) + goto out; + } + +skip_lookup: + err = 0; + + head = rht_dereference_bucket(tbl->buckets[hash], tbl, hash); + + RCU_INIT_POINTER(obj->next, head); + + rcu_assign_pointer(tbl->buckets[hash], obj); + + atomic_inc(&ht->nelems); + if (rht_grow_above_75(ht, tbl)) + schedule_work(&ht->run_work); + +out: + spin_unlock_bh(lock); + rcu_read_unlock(); + + return err; +} + +/** + * rhashtable_insert_fast - insert object into hash table + * @ht: hash table + * @obj: pointer to hash head inside object + * @params: hash table parameters + * + * Will take a per bucket spinlock to protect against mutual mutations + * on the same bucket. Multiple insertions may occur in parallel unless + * they map to the same bucket lock. + * + * It is safe to call this function from atomic context. + * + * Will trigger an automatic deferred table resizing if the size grows + * beyond the watermark indicated by grow_decision() which can be passed + * to rhashtable_init(). + */ +static inline int rhashtable_insert_fast( + struct rhashtable *ht, struct rhash_head *obj, + const struct rhashtable_params params) +{ + return __rhashtable_insert_fast(ht, NULL, obj, params); +} + +/** + * rhashtable_lookup_insert_fast - lookup and insert object into hash table + * @ht: hash table + * @obj: pointer to hash head inside object + * @params: hash table parameters + * + * Locks down the bucket chain in both the old and new table if a resize + * is in progress to ensure that writers can't remove from the old table + * and can't insert to the new table during the atomic operation of search + * and insertion. Searches for duplicates in both the old and new table if + * a resize is in progress. + * + * This lookup function may only be used for fixed key hash table (key_len + * parameter set). It will BUG() if used inappropriately. + * + * It is safe to call this function from atomic context. + * + * Will trigger an automatic deferred table resizing if the size grows + * beyond the watermark indicated by grow_decision() which can be passed + * to rhashtable_init(). + */ +static inline int rhashtable_lookup_insert_fast( + struct rhashtable *ht, struct rhash_head *obj, + const struct rhashtable_params params) +{ + const char *key = rht_obj(ht, obj); + + BUG_ON(ht->p.obj_hashfn); + + return __rhashtable_insert_fast(ht, key + ht->p.key_offset, obj, + params); +} + +/** + * rhashtable_lookup_insert_key - search and insert object to hash table + * with explicit key + * @ht: hash table + * @key: key + * @obj: pointer to hash head inside object + * @params: hash table parameters + * + * Locks down the bucket chain in both the old and new table if a resize + * is in progress to ensure that writers can't remove from the old table + * and can't insert to the new table during the atomic operation of search + * and insertion. Searches for duplicates in both the old and new table if + * a resize is in progress. + * + * Lookups may occur in parallel with hashtable mutations and resizing. + * + * Will trigger an automatic deferred table resizing if the size grows + * beyond the watermark indicated by grow_decision() which can be passed + * to rhashtable_init(). + * + * Returns zero on success. + */ +static inline int rhashtable_lookup_insert_key( + struct rhashtable *ht, const void *key, struct rhash_head *obj, + const struct rhashtable_params params) +{ + BUG_ON(!ht->p.obj_hashfn || !key); + + return __rhashtable_insert_fast(ht, key, obj, params); +} + +static inline int __rhashtable_remove_fast( + struct rhashtable *ht, struct bucket_table *tbl, + struct rhash_head *obj, const struct rhashtable_params params) +{ + struct rhash_head __rcu **pprev; + struct rhash_head *he; + spinlock_t * lock; + unsigned hash; + int err = -ENOENT; + + hash = rht_head_hashfn(ht, tbl, obj, params); + lock = rht_bucket_lock(tbl, hash); + + spin_lock_bh(lock); + + pprev = &tbl->buckets[hash]; + rht_for_each(he, tbl, hash) { + if (he != obj) { + pprev = &he->next; + continue; + } + + rcu_assign_pointer(*pprev, obj->next); + err = 0; + break; + } + + spin_unlock_bh(lock); + + return err; +} + +/** + * rhashtable_remove_fast - remove object from hash table + * @ht: hash table + * @obj: pointer to hash head inside object + * @params: hash table parameters + * + * Since the hash chain is single linked, the removal operation needs to + * walk the bucket chain upon removal. The removal operation is thus + * considerable slow if the hash table is not correctly sized. + * + * Will automatically shrink the table via rhashtable_expand() if the + * shrink_decision function specified at rhashtable_init() returns true. + * + * Returns zero on success, -ENOENT if the entry could not be found. + */ +static inline int rhashtable_remove_fast( + struct rhashtable *ht, struct rhash_head *obj, + const struct rhashtable_params params) +{ + struct bucket_table *tbl; + int err; + + rcu_read_lock(); + + tbl = rht_dereference_rcu(ht->tbl, ht); + + /* Because we have already taken (and released) the bucket + * lock in old_tbl, if we find that future_tbl is not yet + * visible then that guarantees the entry to still be in + * the old tbl if it exists. + */ + while ((err = __rhashtable_remove_fast(ht, tbl, obj, params)) && + (tbl = rht_dereference_rcu(tbl->future_tbl, ht))) + ; + + if (err) + goto out; + + atomic_dec(&ht->nelems); + if (rht_shrink_below_30(ht, tbl)) + schedule_work(&ht->run_work); + +out: + rcu_read_unlock(); + + return err; +} + #endif /* _LINUX_RHASHTABLE_H */ diff --git a/lib/rhashtable.c b/lib/rhashtable.c index e0a9d59f80d6..d1d23fb58525 100644 --- a/lib/rhashtable.c +++ b/lib/rhashtable.c @@ -1,13 +1,13 @@ /* * Resizable, Scalable, Concurrent Hash Table * + * Copyright (c) 2015 Herbert Xu * Copyright (c) 2014-2015 Thomas Graf * Copyright (c) 2008-2014 Patrick McHardy * - * Based on the following paper: - * https://www.usenix.org/legacy/event/atc11/tech/final_files/Triplett.pdf - * * Code partially derived from nft_hash + * Rewritten with rehash code from br_multicast plus single list + * pointer as suggested by Josh Triplett * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -30,53 +30,11 @@ #define HASH_MIN_SIZE 4U #define BUCKET_LOCKS_PER_CPU 128UL -/* Base bits plus 1 bit for nulls marker */ -#define HASH_RESERVED_SPACE (RHT_BASE_BITS + 1) - -/* The bucket lock is selected based on the hash and protects mutations - * on a group of hash buckets. - * - * A maximum of tbl->size/2 bucket locks is allocated. This ensures that - * a single lock always covers both buckets which may both contains - * entries which link to the same bucket of the old table during resizing. - * This allows to simplify the locking as locking the bucket in both - * tables during resize always guarantee protection. - * - * IMPORTANT: When holding the bucket lock of both the old and new table - * during expansions and shrinking, the old bucket lock must always be - * acquired first. - */ -static spinlock_t *bucket_lock(const struct bucket_table *tbl, u32 hash) -{ - return &tbl->locks[hash & tbl->locks_mask]; -} - -static void *rht_obj(const struct rhashtable *ht, const struct rhash_head *he) -{ - return (void *) he - ht->p.head_offset; -} - -static u32 rht_bucket_index(const struct bucket_table *tbl, u32 hash) -{ - return (hash >> HASH_RESERVED_SPACE) & (tbl->size - 1); -} - -static u32 key_hashfn(struct rhashtable *ht, const struct bucket_table *tbl, - const void *key) -{ - return rht_bucket_index(tbl, ht->p.hashfn(key, ht->p.key_len, - tbl->hash_rnd)); -} - static u32 head_hashfn(struct rhashtable *ht, const struct bucket_table *tbl, const struct rhash_head *he) { - const char *ptr = rht_obj(ht, he); - - return likely(ht->p.key_len) ? - key_hashfn(ht, tbl, ptr + ht->p.key_offset) : - rht_bucket_index(tbl, ht->p.obj_hashfn(ptr, tbl->hash_rnd)); + return rht_head_hashfn(ht, tbl, he, ht->p); } #ifdef CONFIG_PROVE_LOCKING @@ -90,7 +48,7 @@ EXPORT_SYMBOL_GPL(lockdep_rht_mutex_is_held); int lockdep_rht_bucket_is_held(const struct bucket_table *tbl, u32 hash) { - spinlock_t *lock = bucket_lock(tbl, hash); + spinlock_t *lock = rht_bucket_lock(tbl, hash); return (debug_locks) ? lockdep_is_held(lock) : 1; } @@ -178,32 +136,6 @@ static struct bucket_table *bucket_table_alloc(struct rhashtable *ht, return tbl; } -/** - * rht_grow_above_75 - returns true if nelems > 0.75 * table-size - * @ht: hash table - * @tbl: current table - */ -static bool rht_grow_above_75(const struct rhashtable *ht, - const struct bucket_table *tbl) -{ - /* Expand table when exceeding 75% load */ - return atomic_read(&ht->nelems) > (tbl->size / 4 * 3) && - (!ht->p.max_size || tbl->size < ht->p.max_size); -} - -/** - * rht_shrink_below_30 - returns true if nelems < 0.3 * table-size - * @ht: hash table - * @tbl: current table - */ -static bool rht_shrink_below_30(const struct rhashtable *ht, - const struct bucket_table *tbl) -{ - /* Shrink table beneath 30% load */ - return atomic_read(&ht->nelems) < (tbl->size * 3 / 10) && - tbl->size > ht->p.min_size; -} - static int rhashtable_rehash_one(struct rhashtable *ht, unsigned old_hash) { struct bucket_table *old_tbl = rht_dereference(ht->tbl, ht); @@ -230,7 +162,7 @@ static int rhashtable_rehash_one(struct rhashtable *ht, unsigned old_hash) new_hash = head_hashfn(ht, new_tbl, entry); - new_bucket_lock = bucket_lock(new_tbl, new_hash); + new_bucket_lock = rht_bucket_lock(new_tbl, new_hash); spin_lock_nested(new_bucket_lock, SINGLE_DEPTH_NESTING); head = rht_dereference_bucket(new_tbl->buckets[new_hash], @@ -255,7 +187,7 @@ static void rhashtable_rehash_chain(struct rhashtable *ht, unsigned old_hash) struct bucket_table *old_tbl = rht_dereference(ht->tbl, ht); spinlock_t *old_bucket_lock; - old_bucket_lock = bucket_lock(old_tbl, old_hash); + old_bucket_lock = rht_bucket_lock(old_tbl, old_hash); spin_lock_bh(old_bucket_lock); while (!rhashtable_rehash_one(ht, old_hash)) @@ -376,6 +308,37 @@ unlock: mutex_unlock(&ht->mutex); } +int rhashtable_insert_slow(struct rhashtable *ht, const void *key, + struct rhash_head *obj, + struct bucket_table *tbl) +{ + struct rhash_head *head; + unsigned hash; + int err = -EEXIST; + + hash = head_hashfn(ht, tbl, obj); + spin_lock_nested(rht_bucket_lock(tbl, hash), SINGLE_DEPTH_NESTING); + + if (key && rhashtable_lookup_fast(ht, key, ht->p)) + goto exit; + + err = 0; + + head = rht_dereference_bucket(tbl->buckets[hash], tbl, hash); + + RCU_INIT_POINTER(obj->next, head); + + rcu_assign_pointer(tbl->buckets[hash], obj); + + atomic_inc(&ht->nelems); + +exit: + spin_unlock(rht_bucket_lock(tbl, hash)); + + return err; +} +EXPORT_SYMBOL_GPL(rhashtable_insert_slow); + static bool __rhashtable_insert(struct rhashtable *ht, struct rhash_head *obj, bool (*compare)(void *, void *), void *arg) { @@ -390,7 +353,7 @@ static bool __rhashtable_insert(struct rhashtable *ht, struct rhash_head *obj, old_tbl = rht_dereference_rcu(ht->tbl, ht); hash = head_hashfn(ht, old_tbl, obj); - old_lock = bucket_lock(old_tbl, hash); + old_lock = rht_bucket_lock(old_tbl, hash); spin_lock_bh(old_lock); @@ -403,7 +366,8 @@ static bool __rhashtable_insert(struct rhashtable *ht, struct rhash_head *obj, tbl = rht_dereference_rcu(old_tbl->future_tbl, ht) ?: old_tbl; if (tbl != old_tbl) { hash = head_hashfn(ht, tbl, obj); - spin_lock_nested(bucket_lock(tbl, hash), SINGLE_DEPTH_NESTING); + spin_lock_nested(rht_bucket_lock(tbl, hash), + SINGLE_DEPTH_NESTING); } if (compare && @@ -430,7 +394,7 @@ static bool __rhashtable_insert(struct rhashtable *ht, struct rhash_head *obj, exit: if (tbl != old_tbl) - spin_unlock(bucket_lock(tbl, hash)); + spin_unlock(rht_bucket_lock(tbl, hash)); spin_unlock_bh(old_lock); @@ -471,7 +435,7 @@ static bool __rhashtable_remove(struct rhashtable *ht, bool ret = false; hash = head_hashfn(ht, tbl, obj); - lock = bucket_lock(tbl, hash); + lock = rht_bucket_lock(tbl, hash); spin_lock_bh(lock); @@ -537,19 +501,6 @@ bool rhashtable_remove(struct rhashtable *ht, struct rhash_head *obj) } EXPORT_SYMBOL_GPL(rhashtable_remove); -struct rhashtable_compare_arg { - struct rhashtable *ht; - const void *key; -}; - -static bool rhashtable_compare(void *ptr, void *arg) -{ - struct rhashtable_compare_arg *x = arg; - struct rhashtable *ht = x->ht; - - return !memcmp(ptr + ht->p.key_offset, x->key, ht->p.key_len); -} - /** * rhashtable_lookup - lookup key in hash table * @ht: hash table @@ -565,14 +516,7 @@ static bool rhashtable_compare(void *ptr, void *arg) */ void *rhashtable_lookup(struct rhashtable *ht, const void *key) { - struct rhashtable_compare_arg arg = { - .ht = ht, - .key = key, - }; - - BUG_ON(!ht->p.key_len); - - return rhashtable_lookup_compare(ht, key, &rhashtable_compare, &arg); + return rhashtable_lookup_fast(ht, key, ht->p); } EXPORT_SYMBOL_GPL(rhashtable_lookup); @@ -591,7 +535,8 @@ EXPORT_SYMBOL_GPL(rhashtable_lookup); * Returns the first entry on which the compare function returned true. */ void *rhashtable_lookup_compare(struct rhashtable *ht, const void *key, - bool (*compare)(void *, void *), void *arg) + bool (*compare)(void *, void *), + void *arg) { const struct bucket_table *tbl; struct rhash_head *he; @@ -601,7 +546,7 @@ void *rhashtable_lookup_compare(struct rhashtable *ht, const void *key, tbl = rht_dereference_rcu(ht->tbl, ht); restart: - hash = key_hashfn(ht, tbl, key); + hash = rht_key_hashfn(ht, tbl, key, ht->p); rht_for_each_rcu(he, tbl, hash) { if (!compare(rht_obj(ht, he), arg)) continue; @@ -643,15 +588,7 @@ EXPORT_SYMBOL_GPL(rhashtable_lookup_compare); */ bool rhashtable_lookup_insert(struct rhashtable *ht, struct rhash_head *obj) { - struct rhashtable_compare_arg arg = { - .ht = ht, - .key = rht_obj(ht, obj) + ht->p.key_offset, - }; - - BUG_ON(!ht->p.key_len); - - return rhashtable_lookup_compare_insert(ht, obj, &rhashtable_compare, - &arg); + return rhashtable_lookup_insert_fast(ht, obj, ht->p); } EXPORT_SYMBOL_GPL(rhashtable_lookup_insert); @@ -927,8 +864,8 @@ int rhashtable_init(struct rhashtable *ht, size = HASH_DEFAULT_SIZE; - if ((params->key_len && !params->hashfn) || - (!params->key_len && !params->obj_hashfn)) + if ((!(params->key_len && params->hashfn) && !params->obj_hashfn) || + (params->obj_hashfn && !params->obj_cmpfn)) return -EINVAL; if (params->nulls_base && params->nulls_base < (1U << RHT_BASE_SHIFT)) -- cgit v1.2.3 From dc0ee268d85026530720d8c874716287b7ede25b Mon Sep 17 00:00:00 2001 From: Herbert Xu Date: Fri, 20 Mar 2015 21:57:06 +1100 Subject: rhashtable: Rip out obsolete out-of-line interface Now that all rhashtable users have been converted over to the inline interface, this patch removes the unused out-of-line interface. Signed-off-by: Herbert Xu Signed-off-by: David S. Miller --- include/linux/rhashtable.h | 19 +-- lib/rhashtable.c | 284 --------------------------------------------- 2 files changed, 3 insertions(+), 300 deletions(-) (limited to 'include') diff --git a/include/linux/rhashtable.h b/include/linux/rhashtable.h index a7188eeb135b..c3034de2c235 100644 --- a/include/linux/rhashtable.h +++ b/include/linux/rhashtable.h @@ -1,14 +1,13 @@ /* * Resizable, Scalable, Concurrent Hash Table * + * Copyright (c) 2015 Herbert Xu * Copyright (c) 2014 Thomas Graf * Copyright (c) 2008-2014 Patrick McHardy * - * Based on the following paper by Josh Triplett, Paul E. McKenney - * and Jonathan Walpole: - * https://www.usenix.org/legacy/event/atc11/tech/final_files/Triplett.pdf - * * Code partially derived from nft_hash + * Rewritten with rehash code from br_multicast plus single list + * pointer as suggested by Josh Triplett * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -282,22 +281,10 @@ int rhashtable_init(struct rhashtable *ht, int rhashtable_insert_slow(struct rhashtable *ht, const void *key, struct rhash_head *obj, struct bucket_table *old_tbl); -void rhashtable_insert(struct rhashtable *ht, struct rhash_head *node); -bool rhashtable_remove(struct rhashtable *ht, struct rhash_head *node); int rhashtable_expand(struct rhashtable *ht); int rhashtable_shrink(struct rhashtable *ht); -void *rhashtable_lookup(struct rhashtable *ht, const void *key); -void *rhashtable_lookup_compare(struct rhashtable *ht, const void *key, - bool (*compare)(void *, void *), void *arg); - -bool rhashtable_lookup_insert(struct rhashtable *ht, struct rhash_head *obj); -bool rhashtable_lookup_compare_insert(struct rhashtable *ht, - struct rhash_head *obj, - bool (*compare)(void *, void *), - void *arg); - int rhashtable_walk_init(struct rhashtable *ht, struct rhashtable_iter *iter); void rhashtable_walk_exit(struct rhashtable_iter *iter); int rhashtable_walk_start(struct rhashtable_iter *iter) __acquires(RCU); diff --git a/lib/rhashtable.c b/lib/rhashtable.c index d1d23fb58525..83cfedd6612a 100644 --- a/lib/rhashtable.c +++ b/lib/rhashtable.c @@ -339,290 +339,6 @@ exit: } EXPORT_SYMBOL_GPL(rhashtable_insert_slow); -static bool __rhashtable_insert(struct rhashtable *ht, struct rhash_head *obj, - bool (*compare)(void *, void *), void *arg) -{ - struct bucket_table *tbl, *old_tbl; - struct rhash_head *head; - bool no_resize_running; - unsigned hash; - spinlock_t *old_lock; - bool success = true; - - rcu_read_lock(); - - old_tbl = rht_dereference_rcu(ht->tbl, ht); - hash = head_hashfn(ht, old_tbl, obj); - old_lock = rht_bucket_lock(old_tbl, hash); - - spin_lock_bh(old_lock); - - /* Because we have already taken the bucket lock in old_tbl, - * if we find that future_tbl is not yet visible then that - * guarantees all other insertions of the same entry will - * also grab the bucket lock in old_tbl because until the - * rehash completes ht->tbl won't be changed. - */ - tbl = rht_dereference_rcu(old_tbl->future_tbl, ht) ?: old_tbl; - if (tbl != old_tbl) { - hash = head_hashfn(ht, tbl, obj); - spin_lock_nested(rht_bucket_lock(tbl, hash), - SINGLE_DEPTH_NESTING); - } - - if (compare && - rhashtable_lookup_compare(ht, rht_obj(ht, obj) + ht->p.key_offset, - compare, arg)) { - success = false; - goto exit; - } - - no_resize_running = tbl == old_tbl; - - head = rht_dereference_bucket(tbl->buckets[hash], tbl, hash); - - if (rht_is_a_nulls(head)) - INIT_RHT_NULLS_HEAD(obj->next, ht, hash); - else - RCU_INIT_POINTER(obj->next, head); - - rcu_assign_pointer(tbl->buckets[hash], obj); - - atomic_inc(&ht->nelems); - if (no_resize_running && rht_grow_above_75(ht, tbl)) - schedule_work(&ht->run_work); - -exit: - if (tbl != old_tbl) - spin_unlock(rht_bucket_lock(tbl, hash)); - - spin_unlock_bh(old_lock); - - rcu_read_unlock(); - - return success; -} - -/** - * rhashtable_insert - insert object into hash table - * @ht: hash table - * @obj: pointer to hash head inside object - * - * Will take a per bucket spinlock to protect against mutual mutations - * on the same bucket. Multiple insertions may occur in parallel unless - * they map to the same bucket lock. - * - * It is safe to call this function from atomic context. - * - * Will trigger an automatic deferred table resizing if the size grows - * beyond the watermark indicated by grow_decision() which can be passed - * to rhashtable_init(). - */ -void rhashtable_insert(struct rhashtable *ht, struct rhash_head *obj) -{ - __rhashtable_insert(ht, obj, NULL, NULL); -} -EXPORT_SYMBOL_GPL(rhashtable_insert); - -static bool __rhashtable_remove(struct rhashtable *ht, - struct bucket_table *tbl, - struct rhash_head *obj) -{ - struct rhash_head __rcu **pprev; - struct rhash_head *he; - spinlock_t * lock; - unsigned hash; - bool ret = false; - - hash = head_hashfn(ht, tbl, obj); - lock = rht_bucket_lock(tbl, hash); - - spin_lock_bh(lock); - - pprev = &tbl->buckets[hash]; - rht_for_each(he, tbl, hash) { - if (he != obj) { - pprev = &he->next; - continue; - } - - rcu_assign_pointer(*pprev, obj->next); - ret = true; - break; - } - - spin_unlock_bh(lock); - - return ret; -} - -/** - * rhashtable_remove - remove object from hash table - * @ht: hash table - * @obj: pointer to hash head inside object - * - * Since the hash chain is single linked, the removal operation needs to - * walk the bucket chain upon removal. The removal operation is thus - * considerable slow if the hash table is not correctly sized. - * - * Will automatically shrink the table via rhashtable_expand() if the - * shrink_decision function specified at rhashtable_init() returns true. - * - * The caller must ensure that no concurrent table mutations occur. It is - * however valid to have concurrent lookups if they are RCU protected. - */ -bool rhashtable_remove(struct rhashtable *ht, struct rhash_head *obj) -{ - struct bucket_table *tbl; - bool ret; - - rcu_read_lock(); - - tbl = rht_dereference_rcu(ht->tbl, ht); - - /* Because we have already taken (and released) the bucket - * lock in old_tbl, if we find that future_tbl is not yet - * visible then that guarantees the entry to still be in - * the old tbl if it exists. - */ - while (!(ret = __rhashtable_remove(ht, tbl, obj)) && - (tbl = rht_dereference_rcu(tbl->future_tbl, ht))) - ; - - if (ret) { - atomic_dec(&ht->nelems); - if (rht_shrink_below_30(ht, tbl)) - schedule_work(&ht->run_work); - } - - rcu_read_unlock(); - - return ret; -} -EXPORT_SYMBOL_GPL(rhashtable_remove); - -/** - * rhashtable_lookup - lookup key in hash table - * @ht: hash table - * @key: pointer to key - * - * Computes the hash value for the key and traverses the bucket chain looking - * for a entry with an identical key. The first matching entry is returned. - * - * This lookup function may only be used for fixed key hash table (key_len - * parameter set). It will BUG() if used inappropriately. - * - * Lookups may occur in parallel with hashtable mutations and resizing. - */ -void *rhashtable_lookup(struct rhashtable *ht, const void *key) -{ - return rhashtable_lookup_fast(ht, key, ht->p); -} -EXPORT_SYMBOL_GPL(rhashtable_lookup); - -/** - * rhashtable_lookup_compare - search hash table with compare function - * @ht: hash table - * @key: the pointer to the key - * @compare: compare function, must return true on match - * @arg: argument passed on to compare function - * - * Traverses the bucket chain behind the provided hash value and calls the - * specified compare function for each entry. - * - * Lookups may occur in parallel with hashtable mutations and resizing. - * - * Returns the first entry on which the compare function returned true. - */ -void *rhashtable_lookup_compare(struct rhashtable *ht, const void *key, - bool (*compare)(void *, void *), - void *arg) -{ - const struct bucket_table *tbl; - struct rhash_head *he; - u32 hash; - - rcu_read_lock(); - - tbl = rht_dereference_rcu(ht->tbl, ht); -restart: - hash = rht_key_hashfn(ht, tbl, key, ht->p); - rht_for_each_rcu(he, tbl, hash) { - if (!compare(rht_obj(ht, he), arg)) - continue; - rcu_read_unlock(); - return rht_obj(ht, he); - } - - /* Ensure we see any new tables. */ - smp_rmb(); - - tbl = rht_dereference_rcu(tbl->future_tbl, ht); - if (unlikely(tbl)) - goto restart; - rcu_read_unlock(); - - return NULL; -} -EXPORT_SYMBOL_GPL(rhashtable_lookup_compare); - -/** - * rhashtable_lookup_insert - lookup and insert object into hash table - * @ht: hash table - * @obj: pointer to hash head inside object - * - * Locks down the bucket chain in both the old and new table if a resize - * is in progress to ensure that writers can't remove from the old table - * and can't insert to the new table during the atomic operation of search - * and insertion. Searches for duplicates in both the old and new table if - * a resize is in progress. - * - * This lookup function may only be used for fixed key hash table (key_len - * parameter set). It will BUG() if used inappropriately. - * - * It is safe to call this function from atomic context. - * - * Will trigger an automatic deferred table resizing if the size grows - * beyond the watermark indicated by grow_decision() which can be passed - * to rhashtable_init(). - */ -bool rhashtable_lookup_insert(struct rhashtable *ht, struct rhash_head *obj) -{ - return rhashtable_lookup_insert_fast(ht, obj, ht->p); -} -EXPORT_SYMBOL_GPL(rhashtable_lookup_insert); - -/** - * rhashtable_lookup_compare_insert - search and insert object to hash table - * with compare function - * @ht: hash table - * @obj: pointer to hash head inside object - * @compare: compare function, must return true on match - * @arg: argument passed on to compare function - * - * Locks down the bucket chain in both the old and new table if a resize - * is in progress to ensure that writers can't remove from the old table - * and can't insert to the new table during the atomic operation of search - * and insertion. Searches for duplicates in both the old and new table if - * a resize is in progress. - * - * Lookups may occur in parallel with hashtable mutations and resizing. - * - * Will trigger an automatic deferred table resizing if the size grows - * beyond the watermark indicated by grow_decision() which can be passed - * to rhashtable_init(). - */ -bool rhashtable_lookup_compare_insert(struct rhashtable *ht, - struct rhash_head *obj, - bool (*compare)(void *, void *), - void *arg) -{ - BUG_ON(!ht->p.key_len); - - return __rhashtable_insert(ht, obj, compare, arg); -} -EXPORT_SYMBOL_GPL(rhashtable_lookup_compare_insert); - /** * rhashtable_walk_init - Initialise an iterator * @ht: Table to walk over -- cgit v1.2.3 From 6626af692692b52c8f9e20ad8201a3255e5ab25b Mon Sep 17 00:00:00 2001 From: Herbert Xu Date: Fri, 20 Mar 2015 18:18:45 -0400 Subject: rhashtable: Fix undeclared EEXIST build error on ia64 We need to include linux/errno.h in rhashtable.h since it doesn't always get included otherwise. Reported-by: kbuild test robot Signed-off-by: Herbert Xu Signed-off-by: David S. Miller --- include/linux/rhashtable.h | 1 + 1 file changed, 1 insertion(+) (limited to 'include') diff --git a/include/linux/rhashtable.h b/include/linux/rhashtable.h index c3034de2c235..89d102270570 100644 --- a/include/linux/rhashtable.h +++ b/include/linux/rhashtable.h @@ -18,6 +18,7 @@ #define _LINUX_RHASHTABLE_H #include +#include #include #include #include -- cgit v1.2.3 From 94caee8c312d96522bcdae88791aaa9ebcd5f22c Mon Sep 17 00:00:00 2001 From: Daniel Borkmann Date: Fri, 20 Mar 2015 15:11:11 +0100 Subject: ebpf: add sched_act_type and map it to sk_filter's verifier ops In order to prepare eBPF support for tc action, we need to add sched_act_type, so that the eBPF verifier is aware of what helper function act_bpf may use, that it can load skb data and read out currently available skb fields. This is bascially analogous to 96be4325f443 ("ebpf: add sched_cls_type and map it to sk_filter's verifier ops"). BPF_PROG_TYPE_SCHED_CLS and BPF_PROG_TYPE_SCHED_ACT need to be separate since both will have a different set of functionality in future (classifier vs action), thus we won't run into ABI troubles when the point in time comes to diverge functionality from the classifier. The future plan for act_bpf would be that it will be able to write into skb->data and alter selected fields mirrored in struct __sk_buff. For an initial support, it's sufficient to map it to sk_filter_ops. Signed-off-by: Daniel Borkmann Cc: Jiri Pirko Reviewed-by: Jiri Pirko Acked-by: Alexei Starovoitov Signed-off-by: David S. Miller --- include/uapi/linux/bpf.h | 1 + kernel/bpf/verifier.c | 1 + net/core/filter.c | 6 ++++++ 3 files changed, 8 insertions(+) (limited to 'include') diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h index 1623047af463..3dd314a45d0d 100644 --- a/include/uapi/linux/bpf.h +++ b/include/uapi/linux/bpf.h @@ -119,6 +119,7 @@ enum bpf_prog_type { BPF_PROG_TYPE_UNSPEC, BPF_PROG_TYPE_SOCKET_FILTER, BPF_PROG_TYPE_SCHED_CLS, + BPF_PROG_TYPE_SCHED_ACT, }; #define BPF_PSEUDO_MAP_FD 1 diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index c22ebd36fa4b..0e714f799ec0 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -1180,6 +1180,7 @@ static bool may_access_skb(enum bpf_prog_type type) switch (type) { case BPF_PROG_TYPE_SOCKET_FILTER: case BPF_PROG_TYPE_SCHED_CLS: + case BPF_PROG_TYPE_SCHED_ACT: return true; default: return false; diff --git a/net/core/filter.c b/net/core/filter.c index bdaac5895def..084eacc4d1d4 100644 --- a/net/core/filter.c +++ b/net/core/filter.c @@ -1263,10 +1263,16 @@ static struct bpf_prog_type_list sched_cls_type __read_mostly = { .type = BPF_PROG_TYPE_SCHED_CLS, }; +static struct bpf_prog_type_list sched_act_type __read_mostly = { + .ops = &sk_filter_ops, + .type = BPF_PROG_TYPE_SCHED_ACT, +}; + static int __init register_sk_filter_ops(void) { bpf_register_prog_type(&sk_filter_type); bpf_register_prog_type(&sched_cls_type); + bpf_register_prog_type(&sched_act_type); return 0; } -- cgit v1.2.3 From a8cb5f556b567974d75ea29c15181c445c541b1f Mon Sep 17 00:00:00 2001 From: Daniel Borkmann Date: Fri, 20 Mar 2015 15:11:12 +0100 Subject: act_bpf: add initial eBPF support for actions This work extends the "classic" BPF programmable tc action by extending its scope also to native eBPF code! Together with commit e2e9b6541dd4 ("cls_bpf: add initial eBPF support for programmable classifiers") this adds the facility to implement fully flexible classifier and actions for tc that can be implemented in a C subset in user space, "safely" loaded into the kernel, and being run in native speed when JITed. Also, since eBPF maps can be shared between eBPF programs, it offers the possibility that cls_bpf and act_bpf can share data 1) between themselves and 2) between user space applications. That means that, f.e. customized runtime statistics can be collected in user space, but also more importantly classifier and action behaviour could be altered based on map input from the user space application. For the remaining details on the workflow and integration, see the cls_bpf commit e2e9b6541dd4. Preliminary iproute2 part can be found under [1]. [1] http://git.breakpoint.cc/cgit/dborkman/iproute2.git/log/?h=ebpf-act Signed-off-by: Daniel Borkmann Cc: Jamal Hadi Salim Cc: Jiri Pirko Acked-by: Jiri Pirko Acked-by: Alexei Starovoitov Signed-off-by: David S. Miller --- include/net/tc_act/tc_bpf.h | 6 +- include/uapi/linux/tc_act/tc_bpf.h | 2 + net/sched/act_bpf.c | 295 ++++++++++++++++++++++++++----------- 3 files changed, 220 insertions(+), 83 deletions(-) (limited to 'include') diff --git a/include/net/tc_act/tc_bpf.h b/include/net/tc_act/tc_bpf.h index 86a070ffc930..a152e9858b2c 100644 --- a/include/net/tc_act/tc_bpf.h +++ b/include/net/tc_act/tc_bpf.h @@ -16,8 +16,12 @@ struct tcf_bpf { struct tcf_common common; struct bpf_prog *filter; + union { + u32 bpf_fd; + u16 bpf_num_ops; + }; struct sock_filter *bpf_ops; - u16 bpf_num_ops; + const char *bpf_name; }; #define to_bpf(a) \ container_of(a->priv, struct tcf_bpf, common) diff --git a/include/uapi/linux/tc_act/tc_bpf.h b/include/uapi/linux/tc_act/tc_bpf.h index 5288bd77e63b..07f17cc70bb3 100644 --- a/include/uapi/linux/tc_act/tc_bpf.h +++ b/include/uapi/linux/tc_act/tc_bpf.h @@ -24,6 +24,8 @@ enum { TCA_ACT_BPF_PARMS, TCA_ACT_BPF_OPS_LEN, TCA_ACT_BPF_OPS, + TCA_ACT_BPF_FD, + TCA_ACT_BPF_NAME, __TCA_ACT_BPF_MAX, }; #define TCA_ACT_BPF_MAX (__TCA_ACT_BPF_MAX - 1) diff --git a/net/sched/act_bpf.c b/net/sched/act_bpf.c index 5f6288fa3f12..4d2cede17468 100644 --- a/net/sched/act_bpf.c +++ b/net/sched/act_bpf.c @@ -13,26 +13,40 @@ #include #include #include +#include + #include #include #include #include -#define BPF_TAB_MASK 15 +#define BPF_TAB_MASK 15 +#define ACT_BPF_NAME_LEN 256 + +struct tcf_bpf_cfg { + struct bpf_prog *filter; + struct sock_filter *bpf_ops; + char *bpf_name; + u32 bpf_fd; + u16 bpf_num_ops; +}; -static int tcf_bpf(struct sk_buff *skb, const struct tc_action *a, +static int tcf_bpf(struct sk_buff *skb, const struct tc_action *act, struct tcf_result *res) { - struct tcf_bpf *b = a->priv; + struct tcf_bpf *prog = act->priv; int action, filter_res; - spin_lock(&b->tcf_lock); + spin_lock(&prog->tcf_lock); - b->tcf_tm.lastuse = jiffies; - bstats_update(&b->tcf_bstats, skb); + prog->tcf_tm.lastuse = jiffies; + bstats_update(&prog->tcf_bstats, skb); - filter_res = BPF_PROG_RUN(b->filter, skb); + /* Needed here for accessing maps. */ + rcu_read_lock(); + filter_res = BPF_PROG_RUN(prog->filter, skb); + rcu_read_unlock(); /* A BPF program may overwrite the default action opcode. * Similarly as in cls_bpf, if filter_res == -1 we use the @@ -52,52 +66,87 @@ static int tcf_bpf(struct sk_buff *skb, const struct tc_action *a, break; case TC_ACT_SHOT: action = filter_res; - b->tcf_qstats.drops++; + prog->tcf_qstats.drops++; break; case TC_ACT_UNSPEC: - action = b->tcf_action; + action = prog->tcf_action; break; default: action = TC_ACT_UNSPEC; break; } - spin_unlock(&b->tcf_lock); + spin_unlock(&prog->tcf_lock); return action; } -static int tcf_bpf_dump(struct sk_buff *skb, struct tc_action *a, +static bool tcf_bpf_is_ebpf(const struct tcf_bpf *prog) +{ + return !prog->bpf_ops; +} + +static int tcf_bpf_dump_bpf_info(const struct tcf_bpf *prog, + struct sk_buff *skb) +{ + struct nlattr *nla; + + if (nla_put_u16(skb, TCA_ACT_BPF_OPS_LEN, prog->bpf_num_ops)) + return -EMSGSIZE; + + nla = nla_reserve(skb, TCA_ACT_BPF_OPS, prog->bpf_num_ops * + sizeof(struct sock_filter)); + if (nla == NULL) + return -EMSGSIZE; + + memcpy(nla_data(nla), prog->bpf_ops, nla_len(nla)); + + return 0; +} + +static int tcf_bpf_dump_ebpf_info(const struct tcf_bpf *prog, + struct sk_buff *skb) +{ + if (nla_put_u32(skb, TCA_ACT_BPF_FD, prog->bpf_fd)) + return -EMSGSIZE; + + if (prog->bpf_name && + nla_put_string(skb, TCA_ACT_BPF_NAME, prog->bpf_name)) + return -EMSGSIZE; + + return 0; +} + +static int tcf_bpf_dump(struct sk_buff *skb, struct tc_action *act, int bind, int ref) { unsigned char *tp = skb_tail_pointer(skb); - struct tcf_bpf *b = a->priv; + struct tcf_bpf *prog = act->priv; struct tc_act_bpf opt = { - .index = b->tcf_index, - .refcnt = b->tcf_refcnt - ref, - .bindcnt = b->tcf_bindcnt - bind, - .action = b->tcf_action, + .index = prog->tcf_index, + .refcnt = prog->tcf_refcnt - ref, + .bindcnt = prog->tcf_bindcnt - bind, + .action = prog->tcf_action, }; - struct tcf_t t; - struct nlattr *nla; + struct tcf_t tm; + int ret; if (nla_put(skb, TCA_ACT_BPF_PARMS, sizeof(opt), &opt)) goto nla_put_failure; - if (nla_put_u16(skb, TCA_ACT_BPF_OPS_LEN, b->bpf_num_ops)) - goto nla_put_failure; - - nla = nla_reserve(skb, TCA_ACT_BPF_OPS, b->bpf_num_ops * - sizeof(struct sock_filter)); - if (!nla) + if (tcf_bpf_is_ebpf(prog)) + ret = tcf_bpf_dump_ebpf_info(prog, skb); + else + ret = tcf_bpf_dump_bpf_info(prog, skb); + if (ret) goto nla_put_failure; - memcpy(nla_data(nla), b->bpf_ops, nla_len(nla)); + tm.install = jiffies_to_clock_t(jiffies - prog->tcf_tm.install); + tm.lastuse = jiffies_to_clock_t(jiffies - prog->tcf_tm.lastuse); + tm.expires = jiffies_to_clock_t(prog->tcf_tm.expires); - t.install = jiffies_to_clock_t(jiffies - b->tcf_tm.install); - t.lastuse = jiffies_to_clock_t(jiffies - b->tcf_tm.lastuse); - t.expires = jiffies_to_clock_t(b->tcf_tm.expires); - if (nla_put(skb, TCA_ACT_BPF_TM, sizeof(t), &t)) + if (nla_put(skb, TCA_ACT_BPF_TM, sizeof(tm), &tm)) goto nla_put_failure; + return skb->len; nla_put_failure: @@ -107,36 +156,21 @@ nla_put_failure: static const struct nla_policy act_bpf_policy[TCA_ACT_BPF_MAX + 1] = { [TCA_ACT_BPF_PARMS] = { .len = sizeof(struct tc_act_bpf) }, + [TCA_ACT_BPF_FD] = { .type = NLA_U32 }, + [TCA_ACT_BPF_NAME] = { .type = NLA_NUL_STRING, .len = ACT_BPF_NAME_LEN }, [TCA_ACT_BPF_OPS_LEN] = { .type = NLA_U16 }, [TCA_ACT_BPF_OPS] = { .type = NLA_BINARY, .len = sizeof(struct sock_filter) * BPF_MAXINSNS }, }; -static int tcf_bpf_init(struct net *net, struct nlattr *nla, - struct nlattr *est, struct tc_action *a, - int ovr, int bind) +static int tcf_bpf_init_from_ops(struct nlattr **tb, struct tcf_bpf_cfg *cfg) { - struct nlattr *tb[TCA_ACT_BPF_MAX + 1]; - struct tc_act_bpf *parm; - struct tcf_bpf *b; - u16 bpf_size, bpf_num_ops; struct sock_filter *bpf_ops; - struct sock_fprog_kern tmp; + struct sock_fprog_kern fprog_tmp; struct bpf_prog *fp; + u16 bpf_size, bpf_num_ops; int ret; - if (!nla) - return -EINVAL; - - ret = nla_parse_nested(tb, TCA_ACT_BPF_MAX, nla, act_bpf_policy); - if (ret < 0) - return ret; - - if (!tb[TCA_ACT_BPF_PARMS] || - !tb[TCA_ACT_BPF_OPS_LEN] || !tb[TCA_ACT_BPF_OPS]) - return -EINVAL; - parm = nla_data(tb[TCA_ACT_BPF_PARMS]); - bpf_num_ops = nla_get_u16(tb[TCA_ACT_BPF_OPS_LEN]); if (bpf_num_ops > BPF_MAXINSNS || bpf_num_ops == 0) return -EINVAL; @@ -146,68 +180,165 @@ static int tcf_bpf_init(struct net *net, struct nlattr *nla, return -EINVAL; bpf_ops = kzalloc(bpf_size, GFP_KERNEL); - if (!bpf_ops) + if (bpf_ops == NULL) return -ENOMEM; memcpy(bpf_ops, nla_data(tb[TCA_ACT_BPF_OPS]), bpf_size); - tmp.len = bpf_num_ops; - tmp.filter = bpf_ops; + fprog_tmp.len = bpf_num_ops; + fprog_tmp.filter = bpf_ops; - ret = bpf_prog_create(&fp, &tmp); - if (ret) - goto free_bpf_ops; + ret = bpf_prog_create(&fp, &fprog_tmp); + if (ret < 0) { + kfree(bpf_ops); + return ret; + } - if (!tcf_hash_check(parm->index, a, bind)) { - ret = tcf_hash_create(parm->index, est, a, sizeof(*b), bind); - if (ret) + cfg->bpf_ops = bpf_ops; + cfg->bpf_num_ops = bpf_num_ops; + cfg->filter = fp; + + return 0; +} + +static int tcf_bpf_init_from_efd(struct nlattr **tb, struct tcf_bpf_cfg *cfg) +{ + struct bpf_prog *fp; + char *name = NULL; + u32 bpf_fd; + + bpf_fd = nla_get_u32(tb[TCA_ACT_BPF_FD]); + + fp = bpf_prog_get(bpf_fd); + if (IS_ERR(fp)) + return PTR_ERR(fp); + + if (fp->type != BPF_PROG_TYPE_SCHED_ACT) { + bpf_prog_put(fp); + return -EINVAL; + } + + if (tb[TCA_ACT_BPF_NAME]) { + name = kmemdup(nla_data(tb[TCA_ACT_BPF_NAME]), + nla_len(tb[TCA_ACT_BPF_NAME]), + GFP_KERNEL); + if (!name) { + bpf_prog_put(fp); + return -ENOMEM; + } + } + + cfg->bpf_fd = bpf_fd; + cfg->bpf_name = name; + cfg->filter = fp; + + return 0; +} + +static int tcf_bpf_init(struct net *net, struct nlattr *nla, + struct nlattr *est, struct tc_action *act, + int replace, int bind) +{ + struct nlattr *tb[TCA_ACT_BPF_MAX + 1]; + struct tc_act_bpf *parm; + struct tcf_bpf *prog; + struct tcf_bpf_cfg cfg; + bool is_bpf, is_ebpf; + int ret; + + if (!nla) + return -EINVAL; + + ret = nla_parse_nested(tb, TCA_ACT_BPF_MAX, nla, act_bpf_policy); + if (ret < 0) + return ret; + + is_bpf = tb[TCA_ACT_BPF_OPS_LEN] && tb[TCA_ACT_BPF_OPS]; + is_ebpf = tb[TCA_ACT_BPF_FD]; + + if ((!is_bpf && !is_ebpf) || (is_bpf && is_ebpf) || + !tb[TCA_ACT_BPF_PARMS]) + return -EINVAL; + + parm = nla_data(tb[TCA_ACT_BPF_PARMS]); + + memset(&cfg, 0, sizeof(cfg)); + + ret = is_bpf ? tcf_bpf_init_from_ops(tb, &cfg) : + tcf_bpf_init_from_efd(tb, &cfg); + if (ret < 0) + return ret; + + if (!tcf_hash_check(parm->index, act, bind)) { + ret = tcf_hash_create(parm->index, est, act, + sizeof(*prog), bind); + if (ret < 0) goto destroy_fp; ret = ACT_P_CREATED; } else { + /* Don't override defaults. */ if (bind) goto destroy_fp; - tcf_hash_release(a, bind); - if (!ovr) { + + tcf_hash_release(act, bind); + if (!replace) { ret = -EEXIST; goto destroy_fp; } } - b = to_bpf(a); - spin_lock_bh(&b->tcf_lock); - b->tcf_action = parm->action; - b->bpf_num_ops = bpf_num_ops; - b->bpf_ops = bpf_ops; - b->filter = fp; - spin_unlock_bh(&b->tcf_lock); + prog = to_bpf(act); + spin_lock_bh(&prog->tcf_lock); + + prog->bpf_ops = cfg.bpf_ops; + prog->bpf_name = cfg.bpf_name; + + if (cfg.bpf_num_ops) + prog->bpf_num_ops = cfg.bpf_num_ops; + if (cfg.bpf_fd) + prog->bpf_fd = cfg.bpf_fd; + + prog->tcf_action = parm->action; + prog->filter = cfg.filter; + + spin_unlock_bh(&prog->tcf_lock); if (ret == ACT_P_CREATED) - tcf_hash_insert(a); + tcf_hash_insert(act); + return ret; destroy_fp: - bpf_prog_destroy(fp); -free_bpf_ops: - kfree(bpf_ops); + if (is_ebpf) + bpf_prog_put(cfg.filter); + else + bpf_prog_destroy(cfg.filter); + + kfree(cfg.bpf_ops); + kfree(cfg.bpf_name); + return ret; } -static void tcf_bpf_cleanup(struct tc_action *a, int bind) +static void tcf_bpf_cleanup(struct tc_action *act, int bind) { - struct tcf_bpf *b = a->priv; + const struct tcf_bpf *prog = act->priv; - bpf_prog_destroy(b->filter); + if (tcf_bpf_is_ebpf(prog)) + bpf_prog_put(prog->filter); + else + bpf_prog_destroy(prog->filter); } -static struct tc_action_ops act_bpf_ops = { - .kind = "bpf", - .type = TCA_ACT_BPF, - .owner = THIS_MODULE, - .act = tcf_bpf, - .dump = tcf_bpf_dump, - .cleanup = tcf_bpf_cleanup, - .init = tcf_bpf_init, +static struct tc_action_ops act_bpf_ops __read_mostly = { + .kind = "bpf", + .type = TCA_ACT_BPF, + .owner = THIS_MODULE, + .act = tcf_bpf, + .dump = tcf_bpf_dump, + .cleanup = tcf_bpf_cleanup, + .init = tcf_bpf_init, }; static int __init bpf_init_module(void) -- cgit v1.2.3 From d3593b5cef76db45c864de23c599b58198879e8c Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Fri, 20 Mar 2015 17:15:19 -0700 Subject: Revert "selinux: add a skb_owned_by() hook" This reverts commit ca10b9e9a8ca7342ee07065289cbe74ac128c169. No longer needed after commit eb8895debe1baba41fcb62c78a16f0c63c21662a ("tcp: tcp_make_synack() should use sock_wmalloc") When under SYNFLOOD, we build lot of SYNACK and hit false sharing because of multiple modifications done on sk_listener->sk_wmem_alloc Since tcp_make_synack() uses sock_wmalloc(), there is no need to call skb_set_owner_w() again, as this adds two atomic operations. Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- include/linux/security.h | 8 -------- net/ipv4/tcp_output.c | 1 - security/capability.c | 6 ------ security/security.c | 5 ----- security/selinux/hooks.c | 7 ------- 5 files changed, 27 deletions(-) (limited to 'include') diff --git a/include/linux/security.h b/include/linux/security.h index a1b7dbd127ff..25a079a7c3b3 100644 --- a/include/linux/security.h +++ b/include/linux/security.h @@ -1716,7 +1716,6 @@ struct security_operations { int (*tun_dev_attach_queue) (void *security); int (*tun_dev_attach) (struct sock *sk, void *security); int (*tun_dev_open) (void *security); - void (*skb_owned_by) (struct sk_buff *skb, struct sock *sk); #endif /* CONFIG_SECURITY_NETWORK */ #ifdef CONFIG_SECURITY_NETWORK_XFRM @@ -2735,8 +2734,6 @@ int security_tun_dev_attach_queue(void *security); int security_tun_dev_attach(struct sock *sk, void *security); int security_tun_dev_open(void *security); -void security_skb_owned_by(struct sk_buff *skb, struct sock *sk); - #else /* CONFIG_SECURITY_NETWORK */ static inline int security_unix_stream_connect(struct sock *sock, struct sock *other, @@ -2928,11 +2925,6 @@ static inline int security_tun_dev_open(void *security) { return 0; } - -static inline void security_skb_owned_by(struct sk_buff *skb, struct sock *sk) -{ -} - #endif /* CONFIG_SECURITY_NETWORK */ #ifdef CONFIG_SECURITY_NETWORK_XFRM diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c index c2f0f6065cb1..18474088c3d0 100644 --- a/net/ipv4/tcp_output.c +++ b/net/ipv4/tcp_output.c @@ -2926,7 +2926,6 @@ struct sk_buff *tcp_make_synack(struct sock *sk, struct dst_entry *dst, skb_reserve(skb, MAX_TCP_HEADER); skb_dst_set(skb, dst); - security_skb_owned_by(skb, sk); mss = dst_metric_advmss(dst); if (tp->rx_opt.user_mss && tp->rx_opt.user_mss < mss) diff --git a/security/capability.c b/security/capability.c index 070dd46f62f4..58a1600c149b 100644 --- a/security/capability.c +++ b/security/capability.c @@ -776,11 +776,6 @@ static int cap_tun_dev_open(void *security) { return 0; } - -static void cap_skb_owned_by(struct sk_buff *skb, struct sock *sk) -{ -} - #endif /* CONFIG_SECURITY_NETWORK */ #ifdef CONFIG_SECURITY_NETWORK_XFRM @@ -1134,7 +1129,6 @@ void __init security_fixup_ops(struct security_operations *ops) set_to_cap_if_null(ops, tun_dev_open); set_to_cap_if_null(ops, tun_dev_attach_queue); set_to_cap_if_null(ops, tun_dev_attach); - set_to_cap_if_null(ops, skb_owned_by); #endif /* CONFIG_SECURITY_NETWORK */ #ifdef CONFIG_SECURITY_NETWORK_XFRM set_to_cap_if_null(ops, xfrm_policy_alloc_security); diff --git a/security/security.c b/security/security.c index e81d5bbe7363..1f475aa53288 100644 --- a/security/security.c +++ b/security/security.c @@ -1359,11 +1359,6 @@ int security_tun_dev_open(void *security) } EXPORT_SYMBOL(security_tun_dev_open); -void security_skb_owned_by(struct sk_buff *skb, struct sock *sk) -{ - security_ops->skb_owned_by(skb, sk); -} - #endif /* CONFIG_SECURITY_NETWORK */ #ifdef CONFIG_SECURITY_NETWORK_XFRM diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 4d1a54190388..edc66de39f2e 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -51,7 +51,6 @@ #include #include #include /* for local_port_range[] */ -#include #include /* struct or_callable used in sock_rcv_skb */ #include #include @@ -4652,11 +4651,6 @@ static void selinux_inet_conn_established(struct sock *sk, struct sk_buff *skb) selinux_skb_peerlbl_sid(skb, family, &sksec->peer_sid); } -static void selinux_skb_owned_by(struct sk_buff *skb, struct sock *sk) -{ - skb_set_owner_w(skb, sk); -} - static int selinux_secmark_relabel_packet(u32 sid) { const struct task_security_struct *__tsec; @@ -6041,7 +6035,6 @@ static struct security_operations selinux_ops = { .tun_dev_attach_queue = selinux_tun_dev_attach_queue, .tun_dev_attach = selinux_tun_dev_attach, .tun_dev_open = selinux_tun_dev_open, - .skb_owned_by = selinux_skb_owned_by, #ifdef CONFIG_SECURITY_NETWORK_XFRM .xfrm_policy_alloc_security = selinux_xfrm_policy_alloc, -- cgit v1.2.3 From 13bb8e2eb37eb3badf5bf4b6bb90e485fbb6b1cd Mon Sep 17 00:00:00 2001 From: Scott Feldman Date: Fri, 20 Mar 2015 17:42:46 -0700 Subject: switchdev: kernel-doc cleanup on swithdev ops Suggested-by: Ben Hutchings Signed-off-by: Scott Feldman Signed-off-by: David S. Miller --- include/net/switchdev.h | 23 +++++++---------------- 1 file changed, 7 insertions(+), 16 deletions(-) (limited to 'include') diff --git a/include/net/switchdev.h b/include/net/switchdev.h index e5de53f92482..d2e69ee3019a 100644 --- a/include/net/switchdev.h +++ b/include/net/switchdev.h @@ -19,25 +19,16 @@ struct fib_info; /** * struct switchdev_ops - switchdev operations * - * int (*swdev_parent_id_get)(struct net_device *dev, - * struct netdev_phys_item_id *psid); - * Called to get an ID of the switch chip this port is part of. - * If driver implements this, it indicates that it represents a port - * of a switch chip. + * @swdev_parent_id_get: Called to get an ID of the switch chip this port + * is part of. If driver implements this, it indicates that it + * represents a port of a switch chip. * - * int (*swdev_port_stp_update)(struct net_device *dev, u8 state); - * Called to notify switch device port of bridge port STP - * state change. + * @swdev_port_stp_update: Called to notify switch device port of bridge + * port STP state change. * - * int (*swdev_fib_ipv4_add)(struct net_device *dev, __be32 dst, - * int dst_len, struct fib_info *fi, - * u8 tos, u8 type, u32 nlflags, u32 tb_id); - * Called to add/modify IPv4 route to switch device. + * @swdev_fib_ipv4_add: Called to add/modify IPv4 route to switch device. * - * int (*swdev_fib_ipv4_del)(struct net_device *dev, __be32 dst, - * int dst_len, struct fib_info *fi, - * u8 tos, u8 type, u32 tb_id); - * Called to delete IPv4 route from switch device. + * @swdev_fib_ipv4_del: Called to delete IPv4 route from switch device. */ struct swdev_ops { int (*swdev_parent_id_get)(struct net_device *dev, -- cgit v1.2.3 From 8da86466b83787df0d4b89ec81c310de072d101c Mon Sep 17 00:00:00 2001 From: YOSHIFUJI Hideaki/吉藤英明 Date: Thu, 19 Mar 2015 22:41:46 +0900 Subject: net: neighbour: Add mcast_resolicit to configure the number of multicast resolicitations in PROBE state. We send unicast neighbor (ARP or NDP) solicitations ucast_probes times in PROBE state. Zhu Yanjun reported that some implementation does not reply against them and the entry will become FAILED, which is undesirable. We had been dealt with such nodes by sending multicast probes mcast_ solicit times after unicast probes in PROBE state. In 2003, I made a change not to send them to improve compatibility with IPv6 NDP. Let's introduce per-protocol per-interface sysctl knob "mcast_ reprobe" to configure the number of multicast (re)solicitation for reconfirmation in PROBE state. The default is 0, since we have been doing so for 10+ years. Reported-by: Zhu Yanjun CC: Ulf Samuelsson Signed-off-by: YOSHIFUJI Hideaki Signed-off-by: YOSHIFUJI Hideaki Signed-off-by: David S. Miller --- include/net/neighbour.h | 1 + include/uapi/linux/neighbour.h | 1 + net/core/neighbour.c | 15 +++++++++++---- 3 files changed, 13 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/include/net/neighbour.h b/include/net/neighbour.h index e7bdf5170802..bd33e66f49aa 100644 --- a/include/net/neighbour.h +++ b/include/net/neighbour.h @@ -42,6 +42,7 @@ enum { NEIGH_VAR_MCAST_PROBES, NEIGH_VAR_UCAST_PROBES, NEIGH_VAR_APP_PROBES, + NEIGH_VAR_MCAST_REPROBES, NEIGH_VAR_RETRANS_TIME, NEIGH_VAR_BASE_REACHABLE_TIME, NEIGH_VAR_DELAY_PROBE_TIME, diff --git a/include/uapi/linux/neighbour.h b/include/uapi/linux/neighbour.h index 3873a35509aa..2e35c61bbdd1 100644 --- a/include/uapi/linux/neighbour.h +++ b/include/uapi/linux/neighbour.h @@ -126,6 +126,7 @@ enum { NDTPA_PROXY_QLEN, /* u32 */ NDTPA_LOCKTIME, /* u64, msecs */ NDTPA_QUEUE_LENBYTES, /* u32 */ + NDTPA_MCAST_REPROBES, /* u32 */ __NDTPA_MAX }; #define NDTPA_MAX (__NDTPA_MAX - 1) diff --git a/net/core/neighbour.c b/net/core/neighbour.c index 0e8b32efc031..3de654256028 100644 --- a/net/core/neighbour.c +++ b/net/core/neighbour.c @@ -817,10 +817,9 @@ out: static __inline__ int neigh_max_probes(struct neighbour *n) { struct neigh_parms *p = n->parms; - int max_probes = NEIGH_VAR(p, UCAST_PROBES) + NEIGH_VAR(p, APP_PROBES); - if (!(n->nud_state & NUD_PROBE)) - max_probes += NEIGH_VAR(p, MCAST_PROBES); - return max_probes; + return NEIGH_VAR(p, UCAST_PROBES) + NEIGH_VAR(p, APP_PROBES) + + (n->nud_state & NUD_PROBE ? NEIGH_VAR(p, MCAST_REPROBES) : + NEIGH_VAR(p, MCAST_PROBES)); } static void neigh_invalidate(struct neighbour *neigh) @@ -1742,6 +1741,8 @@ static int neightbl_fill_parms(struct sk_buff *skb, struct neigh_parms *parms) NEIGH_VAR(parms, UCAST_PROBES)) || nla_put_u32(skb, NDTPA_MCAST_PROBES, NEIGH_VAR(parms, MCAST_PROBES)) || + nla_put_u32(skb, NDTPA_MCAST_REPROBES, + NEIGH_VAR(parms, MCAST_REPROBES)) || nla_put_msecs(skb, NDTPA_REACHABLE_TIME, parms->reachable_time) || nla_put_msecs(skb, NDTPA_BASE_REACHABLE_TIME, NEIGH_VAR(parms, BASE_REACHABLE_TIME)) || @@ -1901,6 +1902,7 @@ static const struct nla_policy nl_ntbl_parm_policy[NDTPA_MAX+1] = { [NDTPA_APP_PROBES] = { .type = NLA_U32 }, [NDTPA_UCAST_PROBES] = { .type = NLA_U32 }, [NDTPA_MCAST_PROBES] = { .type = NLA_U32 }, + [NDTPA_MCAST_REPROBES] = { .type = NLA_U32 }, [NDTPA_BASE_REACHABLE_TIME] = { .type = NLA_U64 }, [NDTPA_GC_STALETIME] = { .type = NLA_U64 }, [NDTPA_DELAY_PROBE_TIME] = { .type = NLA_U64 }, @@ -2001,6 +2003,10 @@ static int neightbl_set(struct sk_buff *skb, struct nlmsghdr *nlh) NEIGH_VAR_SET(p, MCAST_PROBES, nla_get_u32(tbp[i])); break; + case NDTPA_MCAST_REPROBES: + NEIGH_VAR_SET(p, MCAST_REPROBES, + nla_get_u32(tbp[i])); + break; case NDTPA_BASE_REACHABLE_TIME: NEIGH_VAR_SET(p, BASE_REACHABLE_TIME, nla_get_msecs(tbp[i])); @@ -2987,6 +2993,7 @@ static struct neigh_sysctl_table { NEIGH_SYSCTL_ZERO_INTMAX_ENTRY(MCAST_PROBES, "mcast_solicit"), NEIGH_SYSCTL_ZERO_INTMAX_ENTRY(UCAST_PROBES, "ucast_solicit"), NEIGH_SYSCTL_ZERO_INTMAX_ENTRY(APP_PROBES, "app_solicit"), + NEIGH_SYSCTL_ZERO_INTMAX_ENTRY(MCAST_REPROBES, "mcast_resolicit"), NEIGH_SYSCTL_USERHZ_JIFFIES_ENTRY(RETRANS_TIME, "retrans_time"), NEIGH_SYSCTL_JIFFIES_ENTRY(BASE_REACHABLE_TIME, "base_reachable_time"), NEIGH_SYSCTL_JIFFIES_ENTRY(DELAY_PROBE_TIME, "delay_first_probe_time"), -- cgit v1.2.3 From 8d0451638ad3f7ccd5250c1dd90e06ad487b2703 Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Wed, 18 Mar 2015 20:55:31 +0100 Subject: netfilter: bridge: kill nf_bridge_pad The br_netfilter frag output function calls skb_cow_head() so in case it needs a larger headroom to e.g. re-add a previously stripped PPPOE or VLAN header things will still work (at cost of reallocation). We can then move nf_bridge_encap_header_len to br_netfilter. Signed-off-by: Florian Westphal Signed-off-by: Pablo Neira Ayuso --- include/linux/netfilter_bridge.h | 22 ---------------------- net/bridge/br_netfilter.c | 12 ++++++++++++ net/ipv4/ip_output.c | 5 +---- 3 files changed, 13 insertions(+), 26 deletions(-) (limited to 'include') diff --git a/include/linux/netfilter_bridge.h b/include/linux/netfilter_bridge.h index ed0d3bf953c3..2734977199ca 100644 --- a/include/linux/netfilter_bridge.h +++ b/include/linux/netfilter_bridge.h @@ -23,18 +23,6 @@ enum nf_br_hook_priorities { #define BRNF_8021Q 0x10 #define BRNF_PPPoE 0x20 -static inline unsigned int nf_bridge_encap_header_len(const struct sk_buff *skb) -{ - switch (skb->protocol) { - case __cpu_to_be16(ETH_P_8021Q): - return VLAN_HLEN; - case __cpu_to_be16(ETH_P_PPP_SES): - return PPPOE_SES_HLEN; - default: - return 0; - } -} - static inline unsigned int nf_bridge_mtu_reduction(const struct sk_buff *skb) { if (unlikely(skb->nf_bridge->mask & BRNF_PPPoE)) @@ -44,15 +32,6 @@ static inline unsigned int nf_bridge_mtu_reduction(const struct sk_buff *skb) int br_handle_frame_finish(struct sk_buff *skb); -/* This is called by the IP fragmenting code and it ensures there is - * enough room for the encapsulating header (if there is one). */ -static inline unsigned int nf_bridge_pad(const struct sk_buff *skb) -{ - if (skb->nf_bridge) - return nf_bridge_encap_header_len(skb); - return 0; -} - static inline void br_drop_fake_rtable(struct sk_buff *skb) { struct dst_entry *dst = skb_dst(skb); @@ -62,7 +41,6 @@ static inline void br_drop_fake_rtable(struct sk_buff *skb) } #else -#define nf_bridge_pad(skb) (0) #define br_drop_fake_rtable(skb) do { } while (0) #endif /* CONFIG_BRIDGE_NETFILTER */ diff --git a/net/bridge/br_netfilter.c b/net/bridge/br_netfilter.c index bd2d24d1ff21..f3884a1b942f 100644 --- a/net/bridge/br_netfilter.c +++ b/net/bridge/br_netfilter.c @@ -153,6 +153,18 @@ static inline struct nf_bridge_info *nf_bridge_unshare(struct sk_buff *skb) return nf_bridge; } +static unsigned int nf_bridge_encap_header_len(const struct sk_buff *skb) +{ + switch (skb->protocol) { + case __cpu_to_be16(ETH_P_8021Q): + return VLAN_HLEN; + case __cpu_to_be16(ETH_P_PPP_SES): + return PPPOE_SES_HLEN; + default: + return 0; + } +} + static inline void nf_bridge_push_encap_header(struct sk_buff *skb) { unsigned int len = nf_bridge_encap_header_len(skb); diff --git a/net/ipv4/ip_output.c b/net/ipv4/ip_output.c index a7aea2048a0d..90b49e88e84a 100644 --- a/net/ipv4/ip_output.c +++ b/net/ipv4/ip_output.c @@ -636,10 +636,7 @@ slow_path: left = skb->len - hlen; /* Space per frame */ ptr = hlen; /* Where to start from */ - /* for bridged IP traffic encapsulated inside f.e. a vlan header, - * we need to make room for the encapsulating header - */ - ll_rs = LL_RESERVED_SPACE_EXTRA(rt->dst.dev, nf_bridge_pad(skb)); + ll_rs = LL_RESERVED_SPACE(rt->dst.dev); /* * Fragment the datagram. -- cgit v1.2.3 From e90d5532773e2bcccc538dd346b9fc3482cd700c Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Fri, 20 Mar 2015 13:59:27 +0100 Subject: driver core / PM: Add PM domain callbacks for device setup/cleanup If PM domains are in use, it may be necessary to prepare the code handling a PM domain for driver probing. For example, in some cases device drivers rely on the ability to power on the devices with the help of the IO runtime PM framework and the PM domain code needs to be ready for that. Also, if that code has not been fully initialized yet, the driver probing should be deferred. Moreover, after the probing is complete, it may be necessary to put the PM domain in question into the state reflecting the current needs of the devices in it, for example, so that power is not drawn in vain. The same should be done after removing a driver from a device, as the PM domain state may need to be changed to reflect the new situation. For these reasons, introduce new PM domain callbacks, ->activate, ->sync and ->dismiss called, respectively, before probing for a device driver, after the probing has completed successfully and if the probing has failed or the driver has been removed. Signed-off-by: Rafael J. Wysocki Acked-by: Ulf Hansson Reviewed-by: Kevin Hilman Acked-by: Greg Kroah-Hartman --- drivers/base/dd.c | 14 ++++++++++++++ include/linux/pm.h | 8 ++++++++ 2 files changed, 22 insertions(+) (limited to 'include') diff --git a/drivers/base/dd.c b/drivers/base/dd.c index cdc779cf79a3..aeb744891e44 100644 --- a/drivers/base/dd.c +++ b/drivers/base/dd.c @@ -298,6 +298,12 @@ static int really_probe(struct device *dev, struct device_driver *drv) goto probe_failed; } + if (dev->pm_domain && dev->pm_domain->activate) { + ret = dev->pm_domain->activate(dev); + if (ret) + goto probe_failed; + } + if (dev->bus->probe) { ret = dev->bus->probe(dev); if (ret) @@ -308,6 +314,9 @@ static int really_probe(struct device *dev, struct device_driver *drv) goto probe_failed; } + if (dev->pm_domain && dev->pm_domain->sync) + dev->pm_domain->sync(dev); + driver_bound(dev); ret = 1; pr_debug("bus: '%s': %s: bound device %s to driver %s\n", @@ -319,6 +328,8 @@ probe_failed: driver_sysfs_remove(dev); dev->driver = NULL; dev_set_drvdata(dev, NULL); + if (dev->pm_domain && dev->pm_domain->dismiss) + dev->pm_domain->dismiss(dev); if (ret == -EPROBE_DEFER) { /* Driver requested deferred probing */ @@ -525,6 +536,9 @@ static void __device_release_driver(struct device *dev) devres_release_all(dev); dev->driver = NULL; dev_set_drvdata(dev, NULL); + if (dev->pm_domain && dev->pm_domain->dismiss) + dev->pm_domain->dismiss(dev); + klist_remove(&dev->p->knode_driver); if (dev->bus) blocking_notifier_call_chain(&dev->bus->p->bus_notifier, diff --git a/include/linux/pm.h b/include/linux/pm.h index e2f1be6dd9dd..2d29c64f8fb1 100644 --- a/include/linux/pm.h +++ b/include/linux/pm.h @@ -603,10 +603,18 @@ extern void dev_pm_put_subsys_data(struct device *dev); * Power domains provide callbacks that are executed during system suspend, * hibernation, system resume and during runtime PM transitions along with * subsystem-level and driver-level callbacks. + * + * @detach: Called when removing a device from the domain. + * @activate: Called before executing probe routines for bus types and drivers. + * @sync: Called after successful driver probe. + * @dismiss: Called after unsuccessful driver probe and after driver removal. */ struct dev_pm_domain { struct dev_pm_ops ops; void (*detach)(struct device *dev, bool power_off); + int (*activate)(struct device *dev); + void (*sync)(struct device *dev); + void (*dismiss)(struct device *dev); }; /* -- cgit v1.2.3 From 446d999c1c92cec996e759dc3c03110596e626f5 Mon Sep 17 00:00:00 2001 From: Russell King Date: Fri, 20 Mar 2015 17:20:33 +0000 Subject: PM / domains: factor out code to get the generic PM domain from a struct device The PM domain code contains two methods to get the generic PM domain for a struct device. One is dev_to_genpd() which is only safe when we know for certain that the device has a generic PM domain attached. The other is coded into genpd_dev_pm_detach() which ensures that the PM domain in the struct device is a generic PM domain (and so is safer). This commit factors out the safer version, documents it, and hides the unsafe dev_to_genpd(). Signed-off-by: Russell King Signed-off-by: Rafael J. Wysocki --- drivers/base/power/domain.c | 46 +++++++++++++++++++++++++++++++-------------- include/linux/pm_domain.h | 6 +++--- 2 files changed, 35 insertions(+), 17 deletions(-) (limited to 'include') diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c index 2e2e6af1f3fe..da25c0630789 100644 --- a/drivers/base/power/domain.c +++ b/drivers/base/power/domain.c @@ -68,7 +68,36 @@ static struct generic_pm_domain *pm_genpd_lookup_name(const char *domain_name) return genpd; } -struct generic_pm_domain *dev_to_genpd(struct device *dev) +/* + * Get the generic PM domain for a particular struct device. + * This validates the struct device pointer, the PM domain pointer, + * and checks that the PM domain pointer is a real generic PM domain. + * Any failure results in NULL being returned. + */ +struct generic_pm_domain *pm_genpd_lookup_dev(struct device *dev) +{ + struct generic_pm_domain *genpd = NULL, *gpd; + + if (IS_ERR_OR_NULL(dev) || IS_ERR_OR_NULL(dev->pm_domain)) + return NULL; + + mutex_lock(&gpd_list_lock); + list_for_each_entry(gpd, &gpd_list, gpd_list_node) { + if (&gpd->domain == dev->pm_domain) { + genpd = gpd; + break; + } + } + mutex_unlock(&gpd_list_lock); + + return genpd; +} + +/* + * This should only be used where we are certain that the pm_domain + * attached to the device is a genpd domain. + */ +static struct generic_pm_domain *dev_to_genpd(struct device *dev) { if (IS_ERR_OR_NULL(dev->pm_domain)) return ERR_PTR(-EINVAL); @@ -2093,21 +2122,10 @@ EXPORT_SYMBOL_GPL(of_genpd_get_from_provider); */ static void genpd_dev_pm_detach(struct device *dev, bool power_off) { - struct generic_pm_domain *pd = NULL, *gpd; + struct generic_pm_domain *pd; int ret = 0; - if (!dev->pm_domain) - return; - - mutex_lock(&gpd_list_lock); - list_for_each_entry(gpd, &gpd_list, gpd_list_node) { - if (&gpd->domain == dev->pm_domain) { - pd = gpd; - break; - } - } - mutex_unlock(&gpd_list_lock); - + pd = pm_genpd_lookup_dev(dev); if (!pd) return; diff --git a/include/linux/pm_domain.h b/include/linux/pm_domain.h index 080e778118ba..681ccb053f72 100644 --- a/include/linux/pm_domain.h +++ b/include/linux/pm_domain.h @@ -127,7 +127,7 @@ static inline struct generic_pm_domain_data *dev_gpd_data(struct device *dev) return to_gpd_data(dev->power.subsys_data->domain_data); } -extern struct generic_pm_domain *dev_to_genpd(struct device *dev); +extern struct generic_pm_domain *pm_genpd_lookup_dev(struct device *dev); extern int __pm_genpd_add_device(struct generic_pm_domain *genpd, struct device *dev, struct gpd_timing_data *td); @@ -163,9 +163,9 @@ static inline struct generic_pm_domain_data *dev_gpd_data(struct device *dev) { return ERR_PTR(-ENOSYS); } -static inline struct generic_pm_domain *dev_to_genpd(struct device *dev) +static inline struct generic_pm_domain *pm_genpd_lookup_dev(struct device *dev) { - return ERR_PTR(-ENOSYS); + return NULL; } static inline int __pm_genpd_add_device(struct generic_pm_domain *genpd, struct device *dev, -- cgit v1.2.3 From 2b290bbb60847c0897c047b5214192810de529df Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Tue, 10 Mar 2015 04:48:20 +0100 Subject: can: use sock_efree instead of own destructor It is identical to the can destructor. Signed-off-by: Florian Westphal Acked-by: Oliver Hartkopp Signed-off-by: Marc Kleine-Budde --- include/linux/can/skb.h | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) (limited to 'include') diff --git a/include/linux/can/skb.h b/include/linux/can/skb.h index cc00d15c6107..b6a52a4b457a 100644 --- a/include/linux/can/skb.h +++ b/include/linux/can/skb.h @@ -44,16 +44,11 @@ static inline void can_skb_reserve(struct sk_buff *skb) skb_reserve(skb, sizeof(struct can_skb_priv)); } -static inline void can_skb_destructor(struct sk_buff *skb) -{ - sock_put(skb->sk); -} - static inline void can_skb_set_owner(struct sk_buff *skb, struct sock *sk) { if (sk) { sock_hold(sk); - skb->destructor = can_skb_destructor; + skb->destructor = sock_efree; skb->sk = sk; } } -- cgit v1.2.3 From c54eb70e3bb8cd0d7b8564bedab63e834656c567 Mon Sep 17 00:00:00 2001 From: Yegor Yefremov Date: Mon, 16 Mar 2015 09:38:13 +0100 Subject: can: add combined rx/tx LED trigger support Add -rxtx trigger, that will be activated both for tx as rx events. This trigger mimics "activity" LED for Ethernet devices. Signed-off-by: Yegor Yefremov Signed-off-by: Marc Kleine-Budde --- drivers/net/can/led.c | 22 +++++++++++++++++++--- include/linux/can/dev.h | 2 ++ include/linux/can/led.h | 6 ++++-- 3 files changed, 25 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/drivers/net/can/led.c b/drivers/net/can/led.c index ab7f1b01be49..c1b667675fa1 100644 --- a/drivers/net/can/led.c +++ b/drivers/net/can/led.c @@ -30,20 +30,28 @@ void can_led_event(struct net_device *netdev, enum can_led_event event) case CAN_LED_EVENT_OPEN: led_trigger_event(priv->tx_led_trig, LED_FULL); led_trigger_event(priv->rx_led_trig, LED_FULL); + led_trigger_event(priv->rxtx_led_trig, LED_FULL); break; case CAN_LED_EVENT_STOP: led_trigger_event(priv->tx_led_trig, LED_OFF); led_trigger_event(priv->rx_led_trig, LED_OFF); + led_trigger_event(priv->rxtx_led_trig, LED_OFF); break; case CAN_LED_EVENT_TX: - if (led_delay) + if (led_delay) { led_trigger_blink_oneshot(priv->tx_led_trig, &led_delay, &led_delay, 1); + led_trigger_blink_oneshot(priv->rxtx_led_trig, + &led_delay, &led_delay, 1); + } break; case CAN_LED_EVENT_RX: - if (led_delay) + if (led_delay) { led_trigger_blink_oneshot(priv->rx_led_trig, &led_delay, &led_delay, 1); + led_trigger_blink_oneshot(priv->rxtx_led_trig, + &led_delay, &led_delay, 1); + } break; } } @@ -55,6 +63,7 @@ static void can_led_release(struct device *gendev, void *res) led_trigger_unregister_simple(priv->tx_led_trig); led_trigger_unregister_simple(priv->rx_led_trig); + led_trigger_unregister_simple(priv->rxtx_led_trig); } /* Register CAN LED triggers for a CAN device @@ -76,11 +85,15 @@ void devm_can_led_init(struct net_device *netdev) "%s-tx", netdev->name); snprintf(priv->rx_led_trig_name, sizeof(priv->rx_led_trig_name), "%s-rx", netdev->name); + snprintf(priv->rxtx_led_trig_name, sizeof(priv->rxtx_led_trig_name), + "%s-rxtx", netdev->name); led_trigger_register_simple(priv->tx_led_trig_name, &priv->tx_led_trig); led_trigger_register_simple(priv->rx_led_trig_name, &priv->rx_led_trig); + led_trigger_register_simple(priv->rxtx_led_trig_name, + &priv->rxtx_led_trig); devres_add(&netdev->dev, res); } @@ -97,7 +110,7 @@ static int can_led_notifier(struct notifier_block *nb, unsigned long msg, if (!priv) return NOTIFY_DONE; - if (!priv->tx_led_trig || !priv->rx_led_trig) + if (!priv->tx_led_trig || !priv->rx_led_trig || !priv->rxtx_led_trig) return NOTIFY_DONE; if (msg == NETDEV_CHANGENAME) { @@ -106,6 +119,9 @@ static int can_led_notifier(struct notifier_block *nb, unsigned long msg, snprintf(name, sizeof(name), "%s-rx", netdev->name); led_trigger_rename_static(name, priv->rx_led_trig); + + snprintf(name, sizeof(name), "%s-rxtx", netdev->name); + led_trigger_rename_static(name, priv->rxtx_led_trig); } return NOTIFY_DONE; diff --git a/include/linux/can/dev.h b/include/linux/can/dev.h index c05ff0f9f9a5..c3a9c8fc60fa 100644 --- a/include/linux/can/dev.h +++ b/include/linux/can/dev.h @@ -61,6 +61,8 @@ struct can_priv { char tx_led_trig_name[CAN_LED_NAME_SZ]; struct led_trigger *rx_led_trig; char rx_led_trig_name[CAN_LED_NAME_SZ]; + struct led_trigger *rxtx_led_trig; + char rxtx_led_trig_name[CAN_LED_NAME_SZ]; #endif }; diff --git a/include/linux/can/led.h b/include/linux/can/led.h index e0475c5cbb92..146de4506d21 100644 --- a/include/linux/can/led.h +++ b/include/linux/can/led.h @@ -21,8 +21,10 @@ enum can_led_event { #ifdef CONFIG_CAN_LEDS -/* keep space for interface name + "-tx"/"-rx" suffix and null terminator */ -#define CAN_LED_NAME_SZ (IFNAMSIZ + 4) +/* keep space for interface name + "-tx"/"-rx"/"-rxtx" + * suffix and null terminator + */ +#define CAN_LED_NAME_SZ (IFNAMSIZ + 6) void can_led_event(struct net_device *netdev, enum can_led_event event); void devm_can_led_init(struct net_device *netdev); -- cgit v1.2.3 From ae011d2e48d2e6c2ae29fd8aab439caf2fbfb5a8 Mon Sep 17 00:00:00 2001 From: Hari Bathini Date: Fri, 6 Feb 2015 01:06:28 +0530 Subject: pstore: Add pstore type id for PPC64 opal nvram partition This patch adds a new PPC64 partition type to be used for opal specific nvram partition. A new partition type is needed as none of the existing type matches this partition type. Signed-off-by: Hari Bathini Reviewed-by: Kees Cook Signed-off-by: Michael Ellerman --- fs/pstore/inode.c | 3 +++ include/linux/pstore.h | 1 + 2 files changed, 4 insertions(+) (limited to 'include') diff --git a/fs/pstore/inode.c b/fs/pstore/inode.c index b32ce53d24ee..56e1ffda4d89 100644 --- a/fs/pstore/inode.c +++ b/fs/pstore/inode.c @@ -364,6 +364,9 @@ int pstore_mkfile(enum pstore_type_id type, char *psname, u64 id, int count, case PSTORE_TYPE_PMSG: scnprintf(name, sizeof(name), "pmsg-%s-%lld", psname, id); break; + case PSTORE_TYPE_PPC_OPAL: + sprintf(name, "powerpc-opal-%s-%lld", psname, id); + break; case PSTORE_TYPE_UNKNOWN: scnprintf(name, sizeof(name), "unknown-%s-%lld", psname, id); break; diff --git a/include/linux/pstore.h b/include/linux/pstore.h index 8884f6e507f7..8e7a25b068b0 100644 --- a/include/linux/pstore.h +++ b/include/linux/pstore.h @@ -40,6 +40,7 @@ enum pstore_type_id { PSTORE_TYPE_PPC_OF = 5, PSTORE_TYPE_PPC_COMMON = 6, PSTORE_TYPE_PMSG = 7, + PSTORE_TYPE_PPC_OPAL = 8, PSTORE_TYPE_UNKNOWN = 255 }; -- cgit v1.2.3 From 50f16a8bf9d7a92c437ed1867d0f7e1dc6a9aca9 Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Thu, 5 Mar 2015 22:10:19 +0100 Subject: perf: Remove type specific target pointers The only reason CQM had to use a hard-coded pmu type was so it could use cqm_target in hw_perf_event. Do away with the {tp,bp,cqm}_target pointers and provide a non type specific one. This allows us to do away with that silly pmu type as well. Signed-off-by: Peter Zijlstra (Intel) Cc: Vince Weaver Cc: acme@kernel.org Cc: acme@redhat.com Cc: hpa@zytor.com Cc: jolsa@redhat.com Cc: kanaka.d.juvva@intel.com Cc: matt.fleming@intel.com Cc: tglx@linutronix.de Cc: torvalds@linux-foundation.org Cc: vikas.shivappa@linux.intel.com Link: http://lkml.kernel.org/r/20150305211019.GU21418@twins.programming.kicks-ass.net Signed-off-by: Ingo Molnar --- arch/arm/kernel/hw_breakpoint.c | 2 +- arch/arm64/kernel/hw_breakpoint.c | 2 +- arch/x86/kernel/cpu/perf_event_intel_cqm.c | 7 +++---- include/linux/perf_event.h | 4 +--- include/uapi/linux/perf_event.h | 1 - kernel/events/core.c | 14 ++++---------- kernel/events/hw_breakpoint.c | 8 ++++---- kernel/trace/trace_uprobe.c | 10 +++++----- 8 files changed, 19 insertions(+), 29 deletions(-) (limited to 'include') diff --git a/arch/arm/kernel/hw_breakpoint.c b/arch/arm/kernel/hw_breakpoint.c index 7fc70ae21185..dc7d0a95bd36 100644 --- a/arch/arm/kernel/hw_breakpoint.c +++ b/arch/arm/kernel/hw_breakpoint.c @@ -648,7 +648,7 @@ int arch_validate_hwbkpt_settings(struct perf_event *bp) * Per-cpu breakpoints are not supported by our stepping * mechanism. */ - if (!bp->hw.bp_target) + if (!bp->hw.target) return -EINVAL; /* diff --git a/arch/arm64/kernel/hw_breakpoint.c b/arch/arm64/kernel/hw_breakpoint.c index df1cf15377b4..d062f35911c2 100644 --- a/arch/arm64/kernel/hw_breakpoint.c +++ b/arch/arm64/kernel/hw_breakpoint.c @@ -527,7 +527,7 @@ int arch_validate_hwbkpt_settings(struct perf_event *bp) * Disallow per-task kernel breakpoints since these would * complicate the stepping code. */ - if (info->ctrl.privilege == AARCH64_BREAKPOINT_EL1 && bp->hw.bp_target) + if (info->ctrl.privilege == AARCH64_BREAKPOINT_EL1 && bp->hw.target) return -EINVAL; return 0; diff --git a/arch/x86/kernel/cpu/perf_event_intel_cqm.c b/arch/x86/kernel/cpu/perf_event_intel_cqm.c index 9a8ef8376fcd..e4d1b8b738fa 100644 --- a/arch/x86/kernel/cpu/perf_event_intel_cqm.c +++ b/arch/x86/kernel/cpu/perf_event_intel_cqm.c @@ -263,7 +263,7 @@ static bool __match_event(struct perf_event *a, struct perf_event *b) /* * Events that target same task are placed into the same cache group. */ - if (a->hw.cqm_target == b->hw.cqm_target) + if (a->hw.target == b->hw.target) return true; /* @@ -279,7 +279,7 @@ static bool __match_event(struct perf_event *a, struct perf_event *b) static inline struct perf_cgroup *event_to_cgroup(struct perf_event *event) { if (event->attach_state & PERF_ATTACH_TASK) - return perf_cgroup_from_task(event->hw.cqm_target); + return perf_cgroup_from_task(event->hw.target); return event->cgrp; } @@ -1365,8 +1365,7 @@ static int __init intel_cqm_init(void) __perf_cpu_notifier(intel_cqm_cpu_notifier); - ret = perf_pmu_register(&intel_cqm_pmu, "intel_cqm", - PERF_TYPE_INTEL_CQM); + ret = perf_pmu_register(&intel_cqm_pmu, "intel_cqm", -1); if (ret) pr_err("Intel CQM perf registration failed: %d\n", ret); else diff --git a/include/linux/perf_event.h b/include/linux/perf_event.h index dac4c2831d82..5aa49d7bfd07 100644 --- a/include/linux/perf_event.h +++ b/include/linux/perf_event.h @@ -119,7 +119,6 @@ struct hw_perf_event { struct hrtimer hrtimer; }; struct { /* tracepoint */ - struct task_struct *tp_target; /* for tp_event->class */ struct list_head tp_list; }; @@ -129,7 +128,6 @@ struct hw_perf_event { struct list_head cqm_events_entry; struct list_head cqm_groups_entry; struct list_head cqm_group_entry; - struct task_struct *cqm_target; }; #ifdef CONFIG_HAVE_HW_BREAKPOINT struct { /* breakpoint */ @@ -138,12 +136,12 @@ struct hw_perf_event { * problem hw_breakpoint has with context * creation and event initalization. */ - struct task_struct *bp_target; struct arch_hw_breakpoint info; struct list_head bp_list; }; #endif }; + struct task_struct *target; int state; local64_t prev_count; u64 sample_period; diff --git a/include/uapi/linux/perf_event.h b/include/uapi/linux/perf_event.h index 3c8b45de57ec..1e3cd07cf76e 100644 --- a/include/uapi/linux/perf_event.h +++ b/include/uapi/linux/perf_event.h @@ -32,7 +32,6 @@ enum perf_type_id { PERF_TYPE_HW_CACHE = 3, PERF_TYPE_RAW = 4, PERF_TYPE_BREAKPOINT = 5, - PERF_TYPE_INTEL_CQM = 6, PERF_TYPE_MAX, /* non-ABI */ }; diff --git a/kernel/events/core.c b/kernel/events/core.c index 71109a045450..525062b6fba1 100644 --- a/kernel/events/core.c +++ b/kernel/events/core.c @@ -7171,18 +7171,12 @@ perf_event_alloc(struct perf_event_attr *attr, int cpu, if (task) { event->attach_state = PERF_ATTACH_TASK; - - if (attr->type == PERF_TYPE_TRACEPOINT) - event->hw.tp_target = task; -#ifdef CONFIG_HAVE_HW_BREAKPOINT /* - * hw_breakpoint is a bit difficult here.. + * XXX pmu::event_init needs to know what task to account to + * and we cannot use the ctx information because we need the + * pmu before we get a ctx. */ - else if (attr->type == PERF_TYPE_BREAKPOINT) - event->hw.bp_target = task; -#endif - else if (attr->type == PERF_TYPE_INTEL_CQM) - event->hw.cqm_target = task; + event->hw.target = task; } if (!overflow_handler && parent_event) { diff --git a/kernel/events/hw_breakpoint.c b/kernel/events/hw_breakpoint.c index 9803a6600d49..92ce5f4ccc26 100644 --- a/kernel/events/hw_breakpoint.c +++ b/kernel/events/hw_breakpoint.c @@ -116,12 +116,12 @@ static unsigned int max_task_bp_pinned(int cpu, enum bp_type_idx type) */ static int task_bp_pinned(int cpu, struct perf_event *bp, enum bp_type_idx type) { - struct task_struct *tsk = bp->hw.bp_target; + struct task_struct *tsk = bp->hw.target; struct perf_event *iter; int count = 0; list_for_each_entry(iter, &bp_task_head, hw.bp_list) { - if (iter->hw.bp_target == tsk && + if (iter->hw.target == tsk && find_slot_idx(iter) == type && (iter->cpu < 0 || cpu == iter->cpu)) count += hw_breakpoint_weight(iter); @@ -153,7 +153,7 @@ fetch_bp_busy_slots(struct bp_busy_slots *slots, struct perf_event *bp, int nr; nr = info->cpu_pinned; - if (!bp->hw.bp_target) + if (!bp->hw.target) nr += max_task_bp_pinned(cpu, type); else nr += task_bp_pinned(cpu, bp, type); @@ -210,7 +210,7 @@ toggle_bp_slot(struct perf_event *bp, bool enable, enum bp_type_idx type, weight = -weight; /* Pinned counter cpu profiling */ - if (!bp->hw.bp_target) { + if (!bp->hw.target) { get_bp_info(bp->cpu, type)->cpu_pinned += weight; return; } diff --git a/kernel/trace/trace_uprobe.c b/kernel/trace/trace_uprobe.c index b11441321e7a..93fdc7791eaa 100644 --- a/kernel/trace/trace_uprobe.c +++ b/kernel/trace/trace_uprobe.c @@ -1005,7 +1005,7 @@ __uprobe_perf_filter(struct trace_uprobe_filter *filter, struct mm_struct *mm) return true; list_for_each_entry(event, &filter->perf_events, hw.tp_list) { - if (event->hw.tp_target->mm == mm) + if (event->hw.target->mm == mm) return true; } @@ -1015,7 +1015,7 @@ __uprobe_perf_filter(struct trace_uprobe_filter *filter, struct mm_struct *mm) static inline bool uprobe_filter_event(struct trace_uprobe *tu, struct perf_event *event) { - return __uprobe_perf_filter(&tu->filter, event->hw.tp_target->mm); + return __uprobe_perf_filter(&tu->filter, event->hw.target->mm); } static int uprobe_perf_close(struct trace_uprobe *tu, struct perf_event *event) @@ -1023,10 +1023,10 @@ static int uprobe_perf_close(struct trace_uprobe *tu, struct perf_event *event) bool done; write_lock(&tu->filter.rwlock); - if (event->hw.tp_target) { + if (event->hw.target) { list_del(&event->hw.tp_list); done = tu->filter.nr_systemwide || - (event->hw.tp_target->flags & PF_EXITING) || + (event->hw.target->flags & PF_EXITING) || uprobe_filter_event(tu, event); } else { tu->filter.nr_systemwide--; @@ -1046,7 +1046,7 @@ static int uprobe_perf_open(struct trace_uprobe *tu, struct perf_event *event) int err; write_lock(&tu->filter.rwlock); - if (event->hw.tp_target) { + if (event->hw.target) { /* * event->parent != NULL means copy_process(), we can avoid * uprobe_apply(). current->mm must be probed and we can rely -- cgit v1.2.3 From e3d280fc6d42017b2379503fbda83655a05294fe Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 17 Feb 2015 21:46:37 +0100 Subject: ALSA: hda - Make snd_hda_bus_type public Define the common hd-audio driver and device types to bind over snd_hda_bus_type publicly. This allows to implement other type of device and driver code over hd-audio bus. Now both struct hda_codec and struct hda_codec_driver inherit these new struct hdac_device and struct hdac_driver, respectively. The bus registration is done in subsys_initcall() to assure it before any other driver registrations. Signed-off-by: Takashi Iwai --- include/sound/hdaudio.h | 42 ++++++++++++++++++++++++++++++++++++++++++ sound/Kconfig | 2 ++ sound/Makefile | 2 +- sound/hda/Kconfig | 2 ++ sound/hda/Makefile | 3 +++ sound/hda/hda_bus_type.c | 42 ++++++++++++++++++++++++++++++++++++++++++ sound/pci/hda/Kconfig | 1 + sound/pci/hda/hda_bind.c | 47 ++++++++++++++--------------------------------- sound/pci/hda/hda_codec.c | 1 + sound/pci/hda/hda_codec.h | 11 +++++------ 10 files changed, 113 insertions(+), 40 deletions(-) create mode 100644 include/sound/hdaudio.h create mode 100644 sound/hda/Kconfig create mode 100644 sound/hda/Makefile create mode 100644 sound/hda/hda_bus_type.c (limited to 'include') diff --git a/include/sound/hdaudio.h b/include/sound/hdaudio.h new file mode 100644 index 000000000000..2381509bee9f --- /dev/null +++ b/include/sound/hdaudio.h @@ -0,0 +1,42 @@ +/* + * HD-audio core stuff + */ + +#ifndef __SOUND_HDAUDIO_H +#define __SOUND_HDAUDIO_H + +#include + +/* + * exported bus type + */ +extern struct bus_type snd_hda_bus_type; + +/* + * HD-audio codec base device + */ +struct hdac_device { + struct device dev; + int type; +}; + +/* device/driver type used for matching */ +enum { + HDA_DEV_CORE, + HDA_DEV_LEGACY, +}; + +#define dev_to_hdac_dev(_dev) container_of(_dev, struct hdac_device, dev) + +/* + * HD-audio codec base driver + */ +struct hdac_driver { + struct device_driver driver; + int type; + int (*match)(struct hdac_device *dev, struct hdac_driver *drv); +}; + +#define drv_to_hdac_driver(_drv) container_of(_drv, struct hdac_driver, driver) + +#endif /* __SOUND_HDAUDIO_H */ diff --git a/sound/Kconfig b/sound/Kconfig index c710ce2c5c37..5a240e050ae6 100644 --- a/sound/Kconfig +++ b/sound/Kconfig @@ -76,6 +76,8 @@ source "sound/isa/Kconfig" source "sound/pci/Kconfig" +source "sound/hda/Kconfig" + source "sound/ppc/Kconfig" source "sound/aoa/Kconfig" diff --git a/sound/Makefile b/sound/Makefile index ce9132b1c395..77320709fd26 100644 --- a/sound/Makefile +++ b/sound/Makefile @@ -6,7 +6,7 @@ obj-$(CONFIG_SOUND_PRIME) += sound_firmware.o obj-$(CONFIG_SOUND_PRIME) += oss/ obj-$(CONFIG_DMASOUND) += oss/ obj-$(CONFIG_SND) += core/ i2c/ drivers/ isa/ pci/ ppc/ arm/ sh/ synth/ usb/ \ - firewire/ sparc/ spi/ parisc/ pcmcia/ mips/ soc/ atmel/ + firewire/ sparc/ spi/ parisc/ pcmcia/ mips/ soc/ atmel/ hda/ obj-$(CONFIG_SND_AOA) += aoa/ # This one must be compilable even if sound is configured out diff --git a/sound/hda/Kconfig b/sound/hda/Kconfig new file mode 100644 index 000000000000..4f428ccf64ad --- /dev/null +++ b/sound/hda/Kconfig @@ -0,0 +1,2 @@ +config SND_HDA_CORE + tristate diff --git a/sound/hda/Makefile b/sound/hda/Makefile new file mode 100644 index 000000000000..59c8d1feb5aa --- /dev/null +++ b/sound/hda/Makefile @@ -0,0 +1,3 @@ +snd-hda-core-objs := hda_bus_type.o + +obj-$(CONFIG_SND_HDA_CORE) += snd-hda-core.o diff --git a/sound/hda/hda_bus_type.c b/sound/hda/hda_bus_type.c new file mode 100644 index 000000000000..519914a12e8a --- /dev/null +++ b/sound/hda/hda_bus_type.c @@ -0,0 +1,42 @@ +/* + * HD-audio bus + */ +#include +#include +#include +#include +#include + +MODULE_DESCRIPTION("HD-audio bus"); +MODULE_LICENSE("GPL"); + +static int hda_bus_match(struct device *dev, struct device_driver *drv) +{ + struct hdac_device *hdev = dev_to_hdac_dev(dev); + struct hdac_driver *hdrv = drv_to_hdac_driver(drv); + + if (hdev->type != hdrv->type) + return 0; + if (hdrv->match) + return hdrv->match(hdev, hdrv); + return 1; +} + +struct bus_type snd_hda_bus_type = { + .name = "hdaudio", + .match = hda_bus_match, +}; +EXPORT_SYMBOL_GPL(snd_hda_bus_type); + +static int __init hda_bus_init(void) +{ + return bus_register(&snd_hda_bus_type); +} + +static void __exit hda_bus_exit(void) +{ + bus_unregister(&snd_hda_bus_type); +} + +subsys_initcall(hda_bus_init); +module_exit(hda_bus_exit); diff --git a/sound/pci/hda/Kconfig b/sound/pci/hda/Kconfig index 7f0f2c5a4e97..a5ed1c181784 100644 --- a/sound/pci/hda/Kconfig +++ b/sound/pci/hda/Kconfig @@ -5,6 +5,7 @@ config SND_HDA select SND_PCM select SND_VMASTER select SND_KCTL_JACK + select SND_HDA_CORE config SND_HDA_INTEL tristate "HD Audio PCI" diff --git a/sound/pci/hda/hda_bind.c b/sound/pci/hda/hda_bind.c index 1f40ce3c1696..e3bd2807b644 100644 --- a/sound/pci/hda/hda_bind.c +++ b/sound/pci/hda/hda_bind.c @@ -47,11 +47,11 @@ static struct hda_vendor_id hda_vendor_ids[] = { /* * find a matching codec preset */ -static int hda_bus_match(struct device *dev, struct device_driver *drv) +static int hda_codec_match(struct hdac_device *dev, struct hdac_driver *drv) { - struct hda_codec *codec = container_of(dev, struct hda_codec, dev); + struct hda_codec *codec = container_of(dev, struct hda_codec, core); struct hda_codec_driver *driver = - container_of(drv, struct hda_codec_driver, driver); + container_of(drv, struct hda_codec_driver, core); const struct hda_codec_preset *preset; /* check probe_id instead of vendor_id if set */ u32 id = codec->probe_id ? codec->probe_id : codec->vendor_id; @@ -154,20 +154,22 @@ static void hda_codec_driver_shutdown(struct device *dev) int __hda_codec_driver_register(struct hda_codec_driver *drv, const char *name, struct module *owner) { - drv->driver.name = name; - drv->driver.owner = owner; - drv->driver.bus = &snd_hda_bus_type; - drv->driver.probe = hda_codec_driver_probe; - drv->driver.remove = hda_codec_driver_remove; - drv->driver.shutdown = hda_codec_driver_shutdown; - drv->driver.pm = &hda_codec_driver_pm; - return driver_register(&drv->driver); + drv->core.driver.name = name; + drv->core.driver.owner = owner; + drv->core.driver.bus = &snd_hda_bus_type; + drv->core.driver.probe = hda_codec_driver_probe; + drv->core.driver.remove = hda_codec_driver_remove; + drv->core.driver.shutdown = hda_codec_driver_shutdown; + drv->core.driver.pm = &hda_codec_driver_pm; + drv->core.type = HDA_DEV_LEGACY; + drv->core.match = hda_codec_match; + return driver_register(&drv->core.driver); } EXPORT_SYMBOL_GPL(__hda_codec_driver_register); void hda_codec_driver_unregister(struct hda_codec_driver *drv) { - driver_unregister(&drv->driver); + driver_unregister(&drv->core.driver); } EXPORT_SYMBOL_GPL(hda_codec_driver_unregister); @@ -319,24 +321,3 @@ int snd_hda_codec_configure(struct hda_codec *codec) return err; } EXPORT_SYMBOL_GPL(snd_hda_codec_configure); - -/* - * bus registration - */ -struct bus_type snd_hda_bus_type = { - .name = "hdaudio", - .match = hda_bus_match, -}; - -static int __init hda_codec_init(void) -{ - return bus_register(&snd_hda_bus_type); -} - -static void __exit hda_codec_exit(void) -{ - bus_unregister(&snd_hda_bus_type); -} - -module_init(hda_codec_init); -module_exit(hda_codec_exit); diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index 7e38d6f7314b..e14f9f562874 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -1294,6 +1294,7 @@ int snd_hda_codec_new(struct hda_bus *bus, struct snd_card *card, dev_set_name(dev, "hdaudioC%dD%d", card->number, codec_addr); dev_set_drvdata(dev, codec); /* for sysfs */ device_enable_async_suspend(dev); + codec->core.type = HDA_DEV_LEGACY; codec->bus = bus; codec->card = card; diff --git a/sound/pci/hda/hda_codec.h b/sound/pci/hda/hda_codec.h index ccf355d4a8fa..31a9e10e5137 100644 --- a/sound/pci/hda/hda_codec.h +++ b/sound/pci/hda/hda_codec.h @@ -26,6 +26,7 @@ #include #include #include +#include #include /* @@ -172,7 +173,7 @@ struct hda_codec_preset { #define HDA_CODEC_ID_GENERIC 0x00000201 struct hda_codec_driver { - struct device_driver driver; + struct hdac_driver core; const struct hda_codec_preset *preset; }; @@ -276,7 +277,7 @@ struct hda_pcm { /* codec information */ struct hda_codec { - struct device dev; + struct hdac_device core; struct hda_bus *bus; struct snd_card *card; unsigned int addr; /* codec addr*/ @@ -409,10 +410,8 @@ struct hda_codec { struct snd_array verbs; }; -#define dev_to_hda_codec(_dev) container_of(_dev, struct hda_codec, dev) -#define hda_codec_dev(_dev) (&(_dev)->dev) - -extern struct bus_type snd_hda_bus_type; +#define dev_to_hda_codec(_dev) container_of(_dev, struct hda_codec, core.dev) +#define hda_codec_dev(_dev) (&(_dev)->core.dev) /* direction */ enum { -- cgit v1.2.3 From d068ebc25e6e1360510ad8023fe7bca3dacd204e Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 2 Mar 2015 23:22:59 +0100 Subject: ALSA: hda - Move some codes up to hdac_bus struct A few basic codes for communicating over HD-audio bus are moved to struct hdac_bus now. It has only command and get_response ops in addition to the unsolicited event handling. Note that the codec-side tracing support is disabled temporarily during this transition due to the code shuffling. It will be re-enabled later once when all pieces are settled down. Signed-off-by: Takashi Iwai --- include/sound/hdaudio.h | 61 +++++++++++++ sound/hda/Makefile | 2 +- sound/hda/hdac_bus.c | 181 ++++++++++++++++++++++++++++++++++++++ sound/pci/hda/hda_bind.c | 10 +++ sound/pci/hda/hda_codec.c | 191 ++++++++++++++--------------------------- sound/pci/hda/hda_codec.h | 34 +++----- sound/pci/hda/hda_controller.c | 8 +- sound/pci/hda/hda_intel.c | 4 +- sound/pci/hda/hda_sysfs.c | 2 +- sound/pci/hda/patch_conexant.c | 4 +- 10 files changed, 335 insertions(+), 162 deletions(-) create mode 100644 sound/hda/hdac_bus.c (limited to 'include') diff --git a/include/sound/hdaudio.h b/include/sound/hdaudio.h index 2381509bee9f..848ab6e68099 100644 --- a/include/sound/hdaudio.h +++ b/include/sound/hdaudio.h @@ -6,6 +6,11 @@ #define __SOUND_HDAUDIO_H #include +#include + +struct hdac_bus; +struct hdac_device; +struct hdac_driver; /* * exported bus type @@ -18,6 +23,9 @@ extern struct bus_type snd_hda_bus_type; struct hdac_device { struct device dev; int type; + struct hdac_bus *bus; + unsigned int addr; /* codec address */ + struct list_head list; /* list point for bus codec_list */ }; /* device/driver type used for matching */ @@ -35,8 +43,61 @@ struct hdac_driver { struct device_driver driver; int type; int (*match)(struct hdac_device *dev, struct hdac_driver *drv); + void (*unsol_event)(struct hdac_device *dev, unsigned int event); }; #define drv_to_hdac_driver(_drv) container_of(_drv, struct hdac_driver, driver) +/* + * HD-audio bus base driver + */ +struct hdac_bus_ops { + /* send a single command */ + int (*command)(struct hdac_bus *bus, unsigned int cmd); + /* get a response from the last command */ + int (*get_response)(struct hdac_bus *bus, unsigned int addr, + unsigned int *res); +}; + +#define HDA_UNSOL_QUEUE_SIZE 64 + +struct hdac_bus { + struct device *dev; + const struct hdac_bus_ops *ops; + + /* codec linked list */ + struct list_head codec_list; + unsigned int num_codecs; + + /* link caddr -> codec */ + struct hdac_device *caddr_tbl[HDA_MAX_CODEC_ADDRESS + 1]; + + /* unsolicited event queue */ + u32 unsol_queue[HDA_UNSOL_QUEUE_SIZE * 2]; /* ring buffer */ + unsigned int unsol_rp, unsol_wp; + struct work_struct unsol_work; + + /* bit flags of powered codecs */ + unsigned long codec_powered; + + /* flags */ + bool sync_write:1; /* sync after verb write */ + + /* locks */ + struct mutex cmd_mutex; +}; + +int snd_hdac_bus_init(struct hdac_bus *bus, struct device *dev, + const struct hdac_bus_ops *ops); +void snd_hdac_bus_exit(struct hdac_bus *bus); +int snd_hdac_bus_exec_verb(struct hdac_bus *bus, unsigned int addr, + unsigned int cmd, unsigned int *res); +int snd_hdac_bus_exec_verb_unlocked(struct hdac_bus *bus, unsigned int addr, + unsigned int cmd, unsigned int *res); +void snd_hdac_bus_queue_event(struct hdac_bus *bus, u32 res, u32 res_ex); + +int snd_hdac_bus_add_device(struct hdac_bus *bus, struct hdac_device *codec); +void snd_hdac_bus_remove_device(struct hdac_bus *bus, + struct hdac_device *codec); + #endif /* __SOUND_HDAUDIO_H */ diff --git a/sound/hda/Makefile b/sound/hda/Makefile index 59c8d1feb5aa..828680b282fa 100644 --- a/sound/hda/Makefile +++ b/sound/hda/Makefile @@ -1,3 +1,3 @@ -snd-hda-core-objs := hda_bus_type.o +snd-hda-core-objs := hda_bus_type.o hdac_bus.o obj-$(CONFIG_SND_HDA_CORE) += snd-hda-core.o diff --git a/sound/hda/hdac_bus.c b/sound/hda/hdac_bus.c new file mode 100644 index 000000000000..364f64c0e4a3 --- /dev/null +++ b/sound/hda/hdac_bus.c @@ -0,0 +1,181 @@ +/* + * HD-audio core bus driver + */ + +#include +#include +#include +#include +#include + +static void process_unsol_events(struct work_struct *work); + +/** + * snd_hdac_bus_init - initialize a HD-audio bas bus + * @bus: the pointer to bus object + * + * Returns 0 if successful, or a negative error code. + */ +int snd_hdac_bus_init(struct hdac_bus *bus, struct device *dev, + const struct hdac_bus_ops *ops) +{ + memset(bus, 0, sizeof(*bus)); + bus->dev = dev; + bus->ops = ops; + INIT_LIST_HEAD(&bus->codec_list); + INIT_WORK(&bus->unsol_work, process_unsol_events); + mutex_init(&bus->cmd_mutex); + return 0; +} +EXPORT_SYMBOL_GPL(snd_hdac_bus_init); + +/** + * snd_hdac_bus_exit - clean up a HD-audio bas bus + * @bus: the pointer to bus object + */ +void snd_hdac_bus_exit(struct hdac_bus *bus) +{ + WARN_ON(!list_empty(&bus->codec_list)); + cancel_work_sync(&bus->unsol_work); +} +EXPORT_SYMBOL_GPL(snd_hdac_bus_exit); + +/** + * snd_hdac_bus_exec_verb - execute a HD-audio verb on the given bus + * @bus: bus object + * @cmd: HD-audio encoded verb + * @res: pointer to store the response, NULL if performing asynchronously + * + * Returns 0 if successful, or a negative error code. + */ +int snd_hdac_bus_exec_verb(struct hdac_bus *bus, unsigned int addr, + unsigned int cmd, unsigned int *res) +{ + int err; + + mutex_lock(&bus->cmd_mutex); + err = snd_hdac_bus_exec_verb_unlocked(bus, addr, cmd, res); + mutex_unlock(&bus->cmd_mutex); + return err; +} +EXPORT_SYMBOL_GPL(snd_hdac_bus_exec_verb); + +/** + * snd_hdac_bus_exec_verb_unlocked - unlocked version + * @bus: bus object + * @cmd: HD-audio encoded verb + * @res: pointer to store the response, NULL if performing asynchronously + * + * Returns 0 if successful, or a negative error code. + */ +int snd_hdac_bus_exec_verb_unlocked(struct hdac_bus *bus, unsigned int addr, + unsigned int cmd, unsigned int *res) +{ + unsigned int tmp; + int err; + + if (cmd == ~0) + return -EINVAL; + + if (res) + *res = -1; + else if (bus->sync_write) + res = &tmp; + for (;;) { + err = bus->ops->command(bus, cmd); + if (err != -EAGAIN) + break; + /* process pending verbs */ + err = bus->ops->get_response(bus, addr, &tmp); + if (err) + break; + } + if (!err && res) + err = bus->ops->get_response(bus, addr, res); + return err; +} +EXPORT_SYMBOL_GPL(snd_hdac_bus_exec_verb_unlocked); + +/** + * snd_hdac_bus_queue_event - add an unsolicited event to queue + * @bus: the BUS + * @res: unsolicited event (lower 32bit of RIRB entry) + * @res_ex: codec addr and flags (upper 32bit or RIRB entry) + * + * Adds the given event to the queue. The events are processed in + * the workqueue asynchronously. Call this function in the interrupt + * hanlder when RIRB receives an unsolicited event. + */ +void snd_hdac_bus_queue_event(struct hdac_bus *bus, u32 res, u32 res_ex) +{ + unsigned int wp; + + if (!bus) + return; + + wp = (bus->unsol_wp + 1) % HDA_UNSOL_QUEUE_SIZE; + bus->unsol_wp = wp; + + wp <<= 1; + bus->unsol_queue[wp] = res; + bus->unsol_queue[wp + 1] = res_ex; + + schedule_work(&bus->unsol_work); +} +EXPORT_SYMBOL_GPL(snd_hdac_bus_queue_event); + +/* + * process queued unsolicited events + */ +static void process_unsol_events(struct work_struct *work) +{ + struct hdac_bus *bus = container_of(work, struct hdac_bus, unsol_work); + struct hdac_device *codec; + struct hdac_driver *drv; + unsigned int rp, caddr, res; + + while (bus->unsol_rp != bus->unsol_wp) { + rp = (bus->unsol_rp + 1) % HDA_UNSOL_QUEUE_SIZE; + bus->unsol_rp = rp; + rp <<= 1; + res = bus->unsol_queue[rp]; + caddr = bus->unsol_queue[rp + 1]; + if (!(caddr & (1 << 4))) /* no unsolicited event? */ + continue; + codec = bus->caddr_tbl[caddr & 0x0f]; + if (!codec || !codec->dev.driver) + continue; + drv = drv_to_hdac_driver(codec->dev.driver); + if (drv->unsol_event) + drv->unsol_event(codec, res); + } +} + +int snd_hdac_bus_add_device(struct hdac_bus *bus, struct hdac_device *codec) +{ + if (bus->caddr_tbl[codec->addr]) { + dev_err(bus->dev, "address 0x%x is already occupied\n", + codec->addr); + return -EBUSY; + } + + list_add_tail(&codec->list, &bus->codec_list); + bus->caddr_tbl[codec->addr] = codec; + set_bit(codec->addr, &bus->codec_powered); + bus->num_codecs++; + return 0; +} +EXPORT_SYMBOL_GPL(snd_hdac_bus_add_device); + +void snd_hdac_bus_remove_device(struct hdac_bus *bus, + struct hdac_device *codec) +{ + WARN_ON(bus != codec->bus); + if (list_empty(&codec->list)) + return; + list_del_init(&codec->list); + bus->caddr_tbl[codec->addr] = NULL; + clear_bit(codec->addr, &bus->codec_powered); + bus->num_codecs--; +} +EXPORT_SYMBOL_GPL(snd_hdac_bus_remove_device); diff --git a/sound/pci/hda/hda_bind.c b/sound/pci/hda/hda_bind.c index e3bd2807b644..0b9ea70c546b 100644 --- a/sound/pci/hda/hda_bind.c +++ b/sound/pci/hda/hda_bind.c @@ -74,6 +74,15 @@ static int hda_codec_match(struct hdac_device *dev, struct hdac_driver *drv) return 0; } +/* process an unsolicited event */ +static void hda_codec_unsol_event(struct hdac_device *dev, unsigned int ev) +{ + struct hda_codec *codec = container_of(dev, struct hda_codec, core); + + if (codec->patch_ops.unsol_event) + codec->patch_ops.unsol_event(codec, ev); +} + /* reset the codec name from the preset */ static int codec_refresh_name(struct hda_codec *codec, const char *name) { @@ -163,6 +172,7 @@ int __hda_codec_driver_register(struct hda_codec_driver *drv, const char *name, drv->core.driver.pm = &hda_codec_driver_pm; drv->core.type = HDA_DEV_LEGACY; drv->core.match = hda_codec_match; + drv->core.unsol_event = hda_codec_unsol_event; return driver_register(&drv->core.driver); } EXPORT_SYMBOL_GPL(__hda_codec_driver_register); diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index e14f9f562874..f96bff37c787 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -39,9 +39,6 @@ #include "hda_jack.h" #include -#define CREATE_TRACE_POINTS -#include "hda_trace.h" - #ifdef CONFIG_PM #define codec_in_pm(codec) atomic_read(&(codec)->in_pm) #define hda_codec_is_power_on(codec) \ @@ -128,16 +125,17 @@ static inline unsigned int make_codec_cmd(struct hda_codec *codec, hda_nid_t nid, int flags, unsigned int verb, unsigned int parm) { + unsigned int addr = codec->core.addr; u32 val; - if ((codec->addr & ~0xf) || (nid & ~0x7f) || + if ((addr & ~0xf) || (nid & ~0x7f) || (verb & ~0xfff) || (parm & ~0xffff)) { codec_err(codec, "hda-codec: out of range cmd %x:%x:%x:%x\n", - codec->addr, nid, verb, parm); + addr, nid, verb, parm); return ~0; } - val = (u32)codec->addr << 28; + val = (u32)addr << 28; val |= (u32)nid << 20; val |= verb << 8; val |= parm; @@ -156,33 +154,20 @@ static int codec_exec_verb(struct hda_codec *codec, unsigned int cmd, if (cmd == ~0) return -1; - if (res) - *res = -1; again: snd_hda_power_up(codec); - mutex_lock(&bus->cmd_mutex); + mutex_lock(&bus->core.cmd_mutex); if (flags & HDA_RW_NO_RESPONSE_FALLBACK) bus->no_response_fallback = 1; - for (;;) { - trace_hda_send_cmd(codec, cmd); - err = bus->ops.command(bus, cmd); - if (err != -EAGAIN) - break; - /* process pending verbs */ - bus->ops.get_response(bus, codec->addr); - } - if (!err && res) { - *res = bus->ops.get_response(bus, codec->addr); - trace_hda_get_response(codec, *res); - } + err = snd_hdac_bus_exec_verb_unlocked(&bus->core, codec->core.addr, + cmd, res); bus->no_response_fallback = 0; - mutex_unlock(&bus->cmd_mutex); + mutex_unlock(&bus->core.cmd_mutex); snd_hda_power_down(codec); - if (!codec_in_pm(codec) && res && *res == -1 && bus->rirb_error) { + if (!codec_in_pm(codec) && res && err < 0 && bus->rirb_error) { if (bus->response_reset) { codec_dbg(codec, "resetting BUS due to fatal communication error\n"); - trace_hda_bus_reset(bus); bus->ops.bus_reset(bus); } goto again; @@ -233,9 +218,7 @@ int snd_hda_codec_write(struct hda_codec *codec, hda_nid_t nid, int flags, unsigned int verb, unsigned int parm) { unsigned int cmd = make_codec_cmd(codec, nid, flags, verb, parm); - unsigned int res; - return codec_exec_verb(codec, cmd, flags, - codec->bus->sync_write ? &res : NULL); + return codec_exec_verb(codec, cmd, flags, NULL); } EXPORT_SYMBOL_GPL(snd_hda_codec_write); @@ -664,65 +647,6 @@ int snd_hda_get_devices(struct hda_codec *codec, hda_nid_t nid, return devices; } -/** - * snd_hda_queue_unsol_event - add an unsolicited event to queue - * @bus: the BUS - * @res: unsolicited event (lower 32bit of RIRB entry) - * @res_ex: codec addr and flags (upper 32bit or RIRB entry) - * - * Adds the given event to the queue. The events are processed in - * the workqueue asynchronously. Call this function in the interrupt - * hanlder when RIRB receives an unsolicited event. - * - * Returns 0 if successful, or a negative error code. - */ -int snd_hda_queue_unsol_event(struct hda_bus *bus, u32 res, u32 res_ex) -{ - struct hda_bus_unsolicited *unsol; - unsigned int wp; - - if (!bus) - return 0; - - trace_hda_unsol_event(bus, res, res_ex); - unsol = &bus->unsol; - wp = (unsol->wp + 1) % HDA_UNSOL_QUEUE_SIZE; - unsol->wp = wp; - - wp <<= 1; - unsol->queue[wp] = res; - unsol->queue[wp + 1] = res_ex; - - schedule_work(&unsol->work); - - return 0; -} -EXPORT_SYMBOL_GPL(snd_hda_queue_unsol_event); - -/* - * process queued unsolicited events - */ -static void process_unsol_events(struct work_struct *work) -{ - struct hda_bus *bus = container_of(work, struct hda_bus, unsol.work); - struct hda_bus_unsolicited *unsol = &bus->unsol; - struct hda_codec *codec; - unsigned int rp, caddr, res; - - while (unsol->rp != unsol->wp) { - rp = (unsol->rp + 1) % HDA_UNSOL_QUEUE_SIZE; - unsol->rp = rp; - rp <<= 1; - res = unsol->queue[rp]; - caddr = unsol->queue[rp + 1]; - if (!(caddr & (1 << 4))) /* no unsolicited event? */ - continue; - codec = bus->caddr_tbl[caddr & 0x0f]; - if (codec && codec->patch_ops.unsol_event) - codec->patch_ops.unsol_event(codec, res); - } -} - /* * destructor */ @@ -730,11 +654,9 @@ static void snd_hda_bus_free(struct hda_bus *bus) { if (!bus) return; - - WARN_ON(!list_empty(&bus->codec_list)); - cancel_work_sync(&bus->unsol.work); if (bus->ops.private_free) bus->ops.private_free(bus); + snd_hdac_bus_exit(&bus->core); kfree(bus); } @@ -751,6 +673,26 @@ static int snd_hda_bus_dev_disconnect(struct snd_device *device) return 0; } +/* hdac_bus_ops translations */ +static int _hda_bus_command(struct hdac_bus *_bus, unsigned int cmd) +{ + struct hda_bus *bus = container_of(_bus, struct hda_bus, core); + return bus->ops.command(bus, cmd); +} + +static int _hda_bus_get_response(struct hdac_bus *_bus, unsigned int addr, + unsigned int *res) +{ + struct hda_bus *bus = container_of(_bus, struct hda_bus, core); + *res = bus->ops.get_response(bus, addr); + return bus->rirb_error ? -EIO : 0; +} + +static const struct hdac_bus_ops bus_ops = { + .command = _hda_bus_command, + .get_response = _hda_bus_get_response, +}; + /** * snd_hda_bus_new - create a HDA bus * @card: the card entry @@ -775,11 +717,14 @@ int snd_hda_bus_new(struct snd_card *card, if (!bus) return -ENOMEM; + err = snd_hdac_bus_init(&bus->core, card->dev, &bus_ops); + if (err < 0) { + kfree(bus); + return err; + } + bus->card = card; - mutex_init(&bus->cmd_mutex); mutex_init(&bus->prepare_mutex); - INIT_LIST_HEAD(&bus->codec_list); - INIT_WORK(&bus->unsol.work, process_unsol_events); err = snd_device_new(card, SNDRV_DEV_BUS, bus, &dev_ops); if (err < 0) { @@ -1233,9 +1178,7 @@ static void snd_hda_codec_dev_release(struct device *dev) struct hda_codec *codec = dev_to_hda_codec(dev); free_init_pincfgs(codec); - list_del(&codec->list); - codec->bus->caddr_tbl[codec->addr] = NULL; - clear_bit(codec->addr, &codec->bus->codec_powered); + snd_hdac_bus_remove_device(&codec->bus->core, &codec->core); snd_hda_sysfs_clear(codec); free_hda_cache(&codec->amp_cache); free_hda_cache(&codec->cmd_cache); @@ -1243,7 +1186,6 @@ static void snd_hda_codec_dev_release(struct device *dev) kfree(codec->chip_name); kfree(codec->modelname); kfree(codec->wcaps); - codec->bus->num_codecs--; kfree(codec); } @@ -1274,27 +1216,23 @@ int snd_hda_codec_new(struct hda_bus *bus, struct snd_card *card, if (snd_BUG_ON(codec_addr > HDA_MAX_CODEC_ADDRESS)) return -EINVAL; - if (bus->caddr_tbl[codec_addr]) { - dev_err(card->dev, - "address 0x%x is already occupied\n", - codec_addr); - return -EBUSY; - } - codec = kzalloc(sizeof(*codec), GFP_KERNEL); if (!codec) return -ENOMEM; + codec->core.bus = &bus->core; + codec->core.addr = codec_addr; + codec->core.type = HDA_DEV_LEGACY; + dev = hda_codec_dev(codec); device_initialize(dev); - dev->parent = card->dev; + dev->parent = bus->core.dev; dev->bus = &snd_hda_bus_type; dev->release = snd_hda_codec_dev_release; dev->groups = snd_hda_dev_attr_groups; dev_set_name(dev, "hdaudioC%dD%d", card->number, codec_addr); dev_set_drvdata(dev, codec); /* for sysfs */ device_enable_async_suspend(dev); - codec->core.type = HDA_DEV_LEGACY; codec->bus = bus; codec->card = card; @@ -1323,7 +1261,7 @@ int snd_hda_codec_new(struct hda_bus *bus, struct snd_card *card, /* snd_hda_codec_new() marks the codec as power-up, and leave it as is. * it's powered down later in snd_hda_codec_dev_register(). */ - set_bit(codec->addr, &bus->codec_powered); + set_bit(codec->core.addr, &bus->core.codec_powered); pm_runtime_set_active(hda_codec_dev(codec)); pm_runtime_get_noresume(hda_codec_dev(codec)); codec->power_jiffies = jiffies; @@ -1339,10 +1277,9 @@ int snd_hda_codec_new(struct hda_bus *bus, struct snd_card *card, } } - list_add_tail(&codec->list, &bus->codec_list); - bus->num_codecs++; - - bus->caddr_tbl[codec_addr] = codec; + err = snd_hdac_bus_add_device(&bus->core, &codec->core); + if (err < 0) + goto error; codec->vendor_id = snd_hda_param_read(codec, AC_NODE_ROOT, AC_PAR_VENDOR_ID); @@ -1516,7 +1453,7 @@ void snd_hda_codec_setup_stream(struct hda_codec *codec, hda_nid_t nid, /* make other inactive cvts with the same stream-tag dirty */ type = get_wcaps_type(get_wcaps(codec, nid)); - list_for_each_entry(c, &codec->bus->codec_list, list) { + list_for_each_codec(c, codec->bus) { for (i = 0; i < c->cvt_setups.used; i++) { p = snd_array_elem(&c->cvt_setups, i); if (!p->active && p->stream_tag == stream_tag && @@ -1583,7 +1520,7 @@ static void purify_inactive_streams(struct hda_codec *codec) struct hda_codec *c; int i; - list_for_each_entry(c, &codec->bus->codec_list, list) { + list_for_each_codec(c, codec->bus) { for (i = 0; i < c->cvt_setups.used; i++) { struct hda_cvt_setup *p; p = snd_array_elem(&c->cvt_setups, i); @@ -2436,7 +2373,7 @@ int snd_hda_lock_devices(struct hda_bus *bus) if (!list_empty(&card->ctl_files)) goto err_clear; - list_for_each_entry(codec, &bus->codec_list, list) { + list_for_each_codec(codec, bus) { struct hda_pcm *cpcm; list_for_each_entry(cpcm, &codec->pcm_list_head, list) { if (!cpcm->pcm) @@ -3607,13 +3544,13 @@ int snd_hda_codec_write_cache(struct hda_codec *codec, hda_nid_t nid, verb = verb | (parm >> 8); parm &= 0xff; key = build_cmd_cache_key(nid, verb); - mutex_lock(&codec->bus->cmd_mutex); + mutex_lock(&codec->bus->core.cmd_mutex); c = get_alloc_hash(&codec->cmd_cache, key); if (c) { c->val = parm; c->dirty = cache_only; } - mutex_unlock(&codec->bus->cmd_mutex); + mutex_unlock(&codec->bus->core.cmd_mutex); return 0; } EXPORT_SYMBOL_GPL(snd_hda_codec_write_cache); @@ -3642,13 +3579,13 @@ int snd_hda_codec_update_cache(struct hda_codec *codec, hda_nid_t nid, verb = verb | (parm >> 8); parm &= 0xff; key = build_cmd_cache_key(nid, verb); - mutex_lock(&codec->bus->cmd_mutex); + mutex_lock(&codec->bus->core.cmd_mutex); c = get_hash(&codec->cmd_cache, key); if (c && c->val == parm) { - mutex_unlock(&codec->bus->cmd_mutex); + mutex_unlock(&codec->bus->core.cmd_mutex); return 0; } - mutex_unlock(&codec->bus->cmd_mutex); + mutex_unlock(&codec->bus->core.cmd_mutex); return snd_hda_codec_write_cache(codec, nid, flags, verb, parm); } EXPORT_SYMBOL_GPL(snd_hda_codec_update_cache); @@ -3927,7 +3864,6 @@ static unsigned int hda_call_codec_suspend(struct hda_codec *codec) codec->patch_ops.suspend(codec); hda_cleanup_all_streams(codec); state = hda_set_power_state(codec, AC_PWRST_D3); - trace_hda_power_down(codec); update_power_acct(codec, true); atomic_dec(&codec->in_pm); return state; @@ -3956,7 +3892,6 @@ static void hda_call_codec_resume(struct hda_codec *codec) { atomic_inc(&codec->in_pm); - trace_hda_power_up(codec); hda_mark_cmd_cache_dirty(codec); codec->power_jiffies = jiffies; @@ -3992,7 +3927,7 @@ static int hda_codec_runtime_suspend(struct device *dev) snd_pcm_suspend_all(pcm->pcm); state = hda_call_codec_suspend(codec); if (codec->d3_stop_clk && codec->epss && (state & AC_PWRST_CLK_STOP_OK)) - clear_bit(codec->addr, &codec->bus->codec_powered); + clear_bit(codec->core.addr, &codec->bus->core.codec_powered); return 0; } @@ -4000,7 +3935,7 @@ static int hda_codec_runtime_resume(struct device *dev) { struct hda_codec *codec = dev_to_hda_codec(dev); - set_bit(codec->addr, &codec->bus->codec_powered); + set_bit(codec->core.addr, &codec->bus->core.codec_powered); hda_call_codec_resume(codec); pm_runtime_mark_last_busy(dev); return 0; @@ -4582,7 +4517,7 @@ int snd_hda_codec_parse_pcms(struct hda_codec *codec) err = codec->patch_ops.build_pcms(codec); if (err < 0) { codec_err(codec, "cannot build PCMs for #%d (error %d)\n", - codec->addr, err); + codec->core.addr, err); return err; } @@ -4638,7 +4573,7 @@ int snd_hda_codec_build_pcms(struct hda_codec *codec) if (err < 0) { codec_err(codec, "cannot attach PCM stream %d for codec #%d\n", - dev, codec->addr); + dev, codec->core.addr); continue; /* no fatal error */ } } @@ -4681,8 +4616,8 @@ int snd_hda_add_new_ctls(struct hda_codec *codec, * the codec addr; if it still fails (or it's the * primary codec), then try another control index */ - if (!addr && codec->addr) - addr = codec->addr; + if (!addr && codec->core.addr) + addr = codec->core.addr; else if (!idx && !knew->index) { idx = find_empty_mixer_ctl_idx(codec, knew->name, 0); @@ -4757,7 +4692,7 @@ void snd_hda_set_power_save(struct hda_bus *bus, int delay) { struct hda_codec *c; - list_for_each_entry(c, &bus->codec_list, list) + list_for_each_codec(c, bus) codec_set_power_save(c, delay); } EXPORT_SYMBOL_GPL(snd_hda_set_power_save); @@ -5344,7 +5279,7 @@ void snd_hda_bus_reset(struct hda_bus *bus) { struct hda_codec *codec; - list_for_each_entry(codec, &bus->codec_list, list) { + list_for_each_codec(codec, bus) { /* FIXME: maybe a better way needed for forced reset */ cancel_delayed_work_sync(&codec->jackpoll_work); #ifdef CONFIG_PM diff --git a/sound/pci/hda/hda_codec.h b/sound/pci/hda/hda_codec.h index 31a9e10e5137..6efcb4ad6935 100644 --- a/sound/pci/hda/hda_codec.h +++ b/sound/pci/hda/hda_codec.h @@ -98,16 +98,6 @@ struct hda_bus_ops { #endif }; -/* unsolicited event handler */ -#define HDA_UNSOL_QUEUE_SIZE 64 -struct hda_bus_unsolicited { - /* ring buffer */ - u32 queue[HDA_UNSOL_QUEUE_SIZE * 2]; - unsigned int rp, wp; - /* workqueue */ - struct work_struct work; -}; - /* * codec bus * @@ -115,6 +105,8 @@ struct hda_bus_unsolicited { * A hda_bus contains several codecs in the list codec_list. */ struct hda_bus { + struct hdac_bus core; + struct snd_card *card; void *private_data; @@ -122,25 +114,14 @@ struct hda_bus { const char *modelname; struct hda_bus_ops ops; - /* codec linked list */ - struct list_head codec_list; - unsigned int num_codecs; - /* link caddr -> codec */ - struct hda_codec *caddr_tbl[HDA_MAX_CODEC_ADDRESS + 1]; - - struct mutex cmd_mutex; struct mutex prepare_mutex; - /* unsolicited event queue */ - struct hda_bus_unsolicited unsol; - /* assigned PCMs */ DECLARE_BITMAP(pcm_dev_bits, SNDRV_PCM_DEVICES); /* misc op flags */ unsigned int needs_damn_long_delay :1; unsigned int allow_bus_reset:1; /* allow bus reset at fatal error */ - unsigned int sync_write:1; /* sync after verb write */ /* status for codec/controller */ unsigned int shutdown :1; /* being unloaded */ unsigned int rirb_error:1; /* error in codec communication */ @@ -149,7 +130,6 @@ struct hda_bus { unsigned int no_response_fallback:1; /* don't fallback at RIRB error */ int primary_dig_out_type; /* primary digital out PCM type */ - unsigned long codec_powered; /* bit flags of powered codecs */ }; /* @@ -281,7 +261,6 @@ struct hda_codec { struct hda_bus *bus; struct snd_card *card; unsigned int addr; /* codec addr*/ - struct list_head list; /* list point */ hda_nid_t afg; /* AFG node id */ hda_nid_t mfg; /* MFG node id */ @@ -413,6 +392,9 @@ struct hda_codec { #define dev_to_hda_codec(_dev) container_of(_dev, struct hda_codec, core.dev) #define hda_codec_dev(_dev) (&(_dev)->core.dev) +#define list_for_each_codec(c, bus) \ + list_for_each_entry(c, &(bus)->core.codec_list, core.list) + /* direction */ enum { HDA_INPUT, HDA_OUTPUT @@ -473,7 +455,11 @@ void snd_hda_sequence_write(struct hda_codec *codec, const struct hda_verb *seq); /* unsolicited event */ -int snd_hda_queue_unsol_event(struct hda_bus *bus, u32 res, u32 res_ex); +static inline void +snd_hda_queue_unsol_event(struct hda_bus *bus, u32 res, u32 res_ex) +{ + snd_hdac_bus_queue_event(&bus->core, res, res_ex); +} /* cached write */ int snd_hda_codec_write_cache(struct hda_codec *codec, hda_nid_t nid, diff --git a/sound/pci/hda/hda_controller.c b/sound/pci/hda/hda_controller.c index 4fd0b2ef26e9..26ce990592a0 100644 --- a/sound/pci/hda/hda_controller.c +++ b/sound/pci/hda/hda_controller.c @@ -1764,12 +1764,12 @@ static int probe_codec(struct azx *chip, int addr) (AC_VERB_PARAMETERS << 8) | AC_PAR_VENDOR_ID; unsigned int res; - mutex_lock(&chip->bus->cmd_mutex); + mutex_lock(&chip->bus->core.cmd_mutex); chip->probing = 1; azx_send_cmd(chip->bus, cmd); res = azx_get_response(chip->bus, addr); chip->probing = 0; - mutex_unlock(&chip->bus->cmd_mutex); + mutex_unlock(&chip->bus->core.cmd_mutex); if (res == -1) return -EIO; dev_dbg(chip->card->dev, "codec #%d probed OK\n", addr); @@ -1848,7 +1848,7 @@ int azx_bus_create(struct azx *chip, const char *model) */ if (chip->driver_caps & AZX_DCAPS_SYNC_WRITE) { dev_dbg(chip->card->dev, "Enable sync_write for stable communication\n"); - bus->sync_write = 1; + bus->core.sync_write = 1; bus->allow_bus_reset = 1; } @@ -1913,7 +1913,7 @@ EXPORT_SYMBOL_GPL(azx_probe_codecs); int azx_codec_configure(struct azx *chip) { struct hda_codec *codec; - list_for_each_entry(codec, &chip->bus->codec_list, list) { + list_for_each_codec(codec, chip->bus) { snd_hda_codec_configure(codec); } return 0; diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index 060f7a2b1aeb..feebc1dda912 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -891,7 +891,7 @@ static int azx_runtime_resume(struct device *dev) bus = chip->bus; if (status && bus) { - list_for_each_entry(codec, &bus->codec_list, list) + list_for_each_codec(codec, bus) if (status & (1 << codec->addr)) schedule_delayed_work(&codec->jackpoll_work, codec->jackpoll_interval); @@ -919,7 +919,7 @@ static int azx_runtime_idle(struct device *dev) return 0; if (!power_save_controller || !azx_has_pm_runtime(chip) || - chip->bus->codec_powered) + chip->bus->core.codec_powered) return -EBUSY; return 0; diff --git a/sound/pci/hda/hda_sysfs.c b/sound/pci/hda/hda_sysfs.c index e13c75d67847..3b5ed1108f9f 100644 --- a/sound/pci/hda/hda_sysfs.c +++ b/sound/pci/hda/hda_sysfs.c @@ -552,7 +552,7 @@ static void parse_codec_mode(char *buf, struct hda_bus *bus, *codecp = NULL; if (sscanf(buf, "%i %i %i", &vendorid, &subid, &caddr) == 3) { - list_for_each_entry(codec, &bus->codec_list, list) { + list_for_each_codec(codec, bus) { if ((vendorid <= 0 || codec->vendor_id == vendorid) && (subid <= 0 || codec->subsystem_id == subid) && codec->addr == caddr) { diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c index 5aa466a13e43..142a6cf786da 100644 --- a/sound/pci/hda/patch_conexant.c +++ b/sound/pci/hda/patch_conexant.c @@ -919,10 +919,10 @@ static int patch_conexant_auto(struct hda_codec *codec) * which falls into the single-cmd mode. * Better to make reset, then. */ - if (!codec->bus->sync_write) { + if (!codec->bus->core.sync_write) { codec_info(codec, "Enable sync_write for stable communication\n"); - codec->bus->sync_write = 1; + codec->bus->core.sync_write = 1; codec->bus->allow_bus_reset = 1; } -- cgit v1.2.3 From 7639a06c23c7d4cda34c2546bd7290d8753849ca Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 3 Mar 2015 10:07:24 +0100 Subject: ALSA: hda - Move a part of hda_codec stuff into hdac_device Now some codes and functionalities of hda_codec struct are moved to hdac_device struct. A few basic attributes like the codec address, vendor ID number, FG numbers, etc are moved to hdac_device, and they are accessed like codec->core.addr. The basic verb exec functions are moved, too. Signed-off-by: Takashi Iwai --- include/sound/hdaudio.h | 66 ++++++ sound/hda/Makefile | 2 +- sound/hda/hdac_device.c | 471 ++++++++++++++++++++++++++++++++++++++++ sound/hda/local.h | 19 ++ sound/pci/hda/hda_auto_parser.c | 33 ++- sound/pci/hda/hda_beep.c | 4 +- sound/pci/hda/hda_bind.c | 95 ++------ sound/pci/hda/hda_codec.c | 396 +++++---------------------------- sound/pci/hda/hda_codec.h | 43 +--- sound/pci/hda/hda_generic.c | 25 +-- sound/pci/hda/hda_local.h | 15 +- sound/pci/hda/hda_proc.c | 42 ++-- sound/pci/hda/hda_sysfs.c | 58 ++--- sound/pci/hda/local.h | 39 ++++ sound/pci/hda/patch_analog.c | 2 +- sound/pci/hda/patch_ca0132.c | 6 +- sound/pci/hda/patch_conexant.c | 16 +- sound/pci/hda/patch_hdmi.c | 20 +- sound/pci/hda/patch_realtek.c | 78 +++---- sound/pci/hda/patch_si3054.c | 6 +- sound/pci/hda/patch_sigmatel.c | 84 +++---- sound/pci/hda/patch_via.c | 23 +- sound/pci/hda/thinkpad_helper.c | 2 +- 23 files changed, 880 insertions(+), 665 deletions(-) create mode 100644 sound/hda/hdac_device.c create mode 100644 sound/hda/local.h create mode 100644 sound/pci/hda/local.h (limited to 'include') diff --git a/include/sound/hdaudio.h b/include/sound/hdaudio.h index 848ab6e68099..b81b4bec6f05 100644 --- a/include/sound/hdaudio.h +++ b/include/sound/hdaudio.h @@ -8,6 +8,9 @@ #include #include +/* codec node id */ +typedef u16 hda_nid_t; + struct hdac_bus; struct hdac_device; struct hdac_driver; @@ -26,6 +29,30 @@ struct hdac_device { struct hdac_bus *bus; unsigned int addr; /* codec address */ struct list_head list; /* list point for bus codec_list */ + + hda_nid_t afg; /* AFG node id */ + hda_nid_t mfg; /* MFG node id */ + + /* ids */ + unsigned int vendor_id; + unsigned int subsystem_id; + unsigned int revision_id; + unsigned int afg_function_id; + unsigned int mfg_function_id; + unsigned int afg_unsol:1; + unsigned int mfg_unsol:1; + + unsigned int power_caps; /* FG power caps */ + + const char *vendor_name; /* codec vendor name */ + const char *chip_name; /* codec chip name */ + + /* widgets */ + unsigned int num_nodes; + hda_nid_t start_nid, end_nid; + + /* misc flags */ + atomic_t in_pm; /* suspend/resume being performed */ }; /* device/driver type used for matching */ @@ -34,8 +61,37 @@ enum { HDA_DEV_LEGACY, }; +/* direction */ +enum { + HDA_INPUT, HDA_OUTPUT +}; + #define dev_to_hdac_dev(_dev) container_of(_dev, struct hdac_device, dev) +int snd_hdac_device_init(struct hdac_device *dev, struct hdac_bus *bus, + const char *name, unsigned int addr); +void snd_hdac_device_exit(struct hdac_device *dev); + +int snd_hdac_refresh_widgets(struct hdac_device *codec); + +unsigned int snd_hdac_make_cmd(struct hdac_device *codec, hda_nid_t nid, + unsigned int verb, unsigned int parm); +int snd_hdac_read(struct hdac_device *codec, hda_nid_t nid, + unsigned int verb, unsigned int parm, unsigned int *res); +int snd_hdac_read_parm(struct hdac_device *codec, hda_nid_t nid, int parm); +int snd_hdac_get_connections(struct hdac_device *codec, hda_nid_t nid, + hda_nid_t *conn_list, int max_conns); +int snd_hdac_get_sub_nodes(struct hdac_device *codec, hda_nid_t nid, + hda_nid_t *start_id); + +#ifdef CONFIG_PM +void snd_hdac_power_up(struct hdac_device *codec); +void snd_hdac_power_down(struct hdac_device *codec); +#else +static inline void snd_hdac_power_up(struct hdac_device *codec) {} +static inline void snd_hdac_power_down(struct hdac_device *codec) {} +#endif + /* * HD-audio codec base driver */ @@ -100,4 +156,14 @@ int snd_hdac_bus_add_device(struct hdac_bus *bus, struct hdac_device *codec); void snd_hdac_bus_remove_device(struct hdac_bus *bus, struct hdac_device *codec); +static inline void snd_hdac_codec_link_up(struct hdac_device *codec) +{ + set_bit(codec->addr, &codec->bus->codec_powered); +} + +static inline void snd_hdac_codec_link_down(struct hdac_device *codec) +{ + clear_bit(codec->addr, &codec->bus->codec_powered); +} + #endif /* __SOUND_HDAUDIO_H */ diff --git a/sound/hda/Makefile b/sound/hda/Makefile index 828680b282fa..3c7625e595cf 100644 --- a/sound/hda/Makefile +++ b/sound/hda/Makefile @@ -1,3 +1,3 @@ -snd-hda-core-objs := hda_bus_type.o hdac_bus.o +snd-hda-core-objs := hda_bus_type.o hdac_bus.o hdac_device.o obj-$(CONFIG_SND_HDA_CORE) += snd-hda-core.o diff --git a/sound/hda/hdac_device.c b/sound/hda/hdac_device.c new file mode 100644 index 000000000000..a3f52ad4de37 --- /dev/null +++ b/sound/hda/hdac_device.c @@ -0,0 +1,471 @@ +/* + * HD-audio codec core device + */ + +#include +#include +#include +#include +#include +#include +#include +#include "local.h" + +static void setup_fg_nodes(struct hdac_device *codec); +static int get_codec_vendor_name(struct hdac_device *codec); + +static void default_release(struct device *dev) +{ + snd_hdac_device_exit(container_of(dev, struct hdac_device, dev)); +} + +/** + * snd_hdac_device_init - initialize the HD-audio codec base device + * @codec: device to initialize + * @bus: but to attach + * @name: device name string + * @addr: codec address + * + * Returns zero for success or a negative error code. + * + * This function increments the runtime PM counter and marks it active. + * The caller needs to turn it off appropriately later. + * + * The caller needs to set the device's release op properly by itself. + */ +int snd_hdac_device_init(struct hdac_device *codec, struct hdac_bus *bus, + const char *name, unsigned int addr) +{ + struct device *dev; + hda_nid_t fg; + int err; + + dev = &codec->dev; + device_initialize(dev); + dev->parent = bus->dev; + dev->bus = &snd_hda_bus_type; + dev->release = default_release; + dev_set_name(dev, "%s", name); + device_enable_async_suspend(dev); + + codec->bus = bus; + codec->addr = addr; + codec->type = HDA_DEV_CORE; + pm_runtime_set_active(&codec->dev); + pm_runtime_get_noresume(&codec->dev); + atomic_set(&codec->in_pm, 0); + + err = snd_hdac_bus_add_device(bus, codec); + if (err < 0) + goto error; + + /* fill parameters */ + codec->vendor_id = snd_hdac_read_parm(codec, AC_NODE_ROOT, + AC_PAR_VENDOR_ID); + if (codec->vendor_id == -1) { + /* read again, hopefully the access method was corrected + * in the last read... + */ + codec->vendor_id = snd_hdac_read_parm(codec, AC_NODE_ROOT, + AC_PAR_VENDOR_ID); + } + + codec->subsystem_id = snd_hdac_read_parm(codec, AC_NODE_ROOT, + AC_PAR_SUBSYSTEM_ID); + codec->revision_id = snd_hdac_read_parm(codec, AC_NODE_ROOT, + AC_PAR_REV_ID); + + setup_fg_nodes(codec); + if (!codec->afg && !codec->mfg) { + dev_err(dev, "no AFG or MFG node found\n"); + err = -ENODEV; + goto error; + } + + fg = codec->afg ? codec->afg : codec->mfg; + + err = snd_hdac_refresh_widgets(codec); + if (err < 0) + goto error; + + codec->power_caps = snd_hdac_read_parm(codec, fg, AC_PAR_POWER_STATE); + /* reread ssid if not set by parameter */ + if (codec->subsystem_id == -1) + snd_hdac_read(codec, fg, AC_VERB_GET_SUBSYSTEM_ID, 0, + &codec->subsystem_id); + + err = get_codec_vendor_name(codec); + if (err < 0) + goto error; + + codec->chip_name = kasprintf(GFP_KERNEL, "ID %x", + codec->vendor_id & 0xffff); + if (!codec->chip_name) { + err = -ENOMEM; + goto error; + } + + return 0; + + error: + pm_runtime_put_noidle(&codec->dev); + put_device(&codec->dev); + return err; +} +EXPORT_SYMBOL_GPL(snd_hdac_device_init); + +/** + * snd_hdac_device_exit - clean up the HD-audio codec base device + * @codec: device to clean up + */ +void snd_hdac_device_exit(struct hdac_device *codec) +{ + /* pm_runtime_put_noidle(&codec->dev); */ + snd_hdac_bus_remove_device(codec->bus, codec); + kfree(codec->vendor_name); + kfree(codec->chip_name); +} +EXPORT_SYMBOL_GPL(snd_hdac_device_exit); + +/** + * snd_hdac_make_cmd - compose a 32bit command word to be sent to the + * HD-audio controller + * @codec: the codec object + * @nid: NID to encode + * @verb: verb to encode + * @parm: parameter to encode + * + * Return an encoded command verb or -1 for error. + */ +unsigned int snd_hdac_make_cmd(struct hdac_device *codec, hda_nid_t nid, + unsigned int verb, unsigned int parm) +{ + u32 val, addr; + + addr = codec->addr; + if ((addr & ~0xf) || (nid & ~0x7f) || + (verb & ~0xfff) || (parm & ~0xffff)) { + dev_err(&codec->dev, "out of range cmd %x:%x:%x:%x\n", + addr, nid, verb, parm); + return -1; + } + + val = addr << 28; + val |= (u32)nid << 20; + val |= verb << 8; + val |= parm; + return val; +} +EXPORT_SYMBOL_GPL(snd_hdac_make_cmd); + +/** + * snd_hdac_read - execute a verb + * @codec: the codec object + * @nid: NID to execute a verb + * @verb: verb to execute + * @parm: parameter for a verb + * @res: the pointer to store the result, NULL if running async + * + * Returns zero if successful, or a negative error code. + */ +int snd_hdac_read(struct hdac_device *codec, hda_nid_t nid, + unsigned int verb, unsigned int parm, unsigned int *res) +{ + unsigned int cmd = snd_hdac_make_cmd(codec, nid, verb, parm); + + return snd_hdac_bus_exec_verb(codec->bus, codec->addr, cmd, res); +} +EXPORT_SYMBOL_GPL(snd_hdac_read); + +/** + * snd_hdac_read_parm - read a codec parameter + * @codec: the codec object + * @nid: NID to read a parameter + * @parm: parameter to read + * + * Returns -1 for error. If you need to distinguish the error more + * strictly, use snd_hdac_read() directly. + */ +int snd_hdac_read_parm(struct hdac_device *codec, hda_nid_t nid, int parm) +{ + int val; + + if (snd_hdac_read(codec, nid, AC_VERB_PARAMETERS, parm, &val)) + return -1; + return val; +} +EXPORT_SYMBOL_GPL(snd_hdac_read_parm); + +/** + * snd_hdac_get_sub_nodes - get start NID and number of subtree nodes + * @codec: the codec object + * @nid: NID to inspect + * @start_id: the pointer to store the starting NID + * + * Returns the number of subtree nodes or zero if not found. + */ +int snd_hdac_get_sub_nodes(struct hdac_device *codec, hda_nid_t nid, + hda_nid_t *start_id) +{ + unsigned int parm; + + parm = snd_hdac_read_parm(codec, nid, AC_PAR_NODE_COUNT); + if (parm == -1) { + *start_id = 0; + return 0; + } + *start_id = (parm >> 16) & 0x7fff; + return (int)(parm & 0x7fff); +} +EXPORT_SYMBOL_GPL(snd_hdac_get_sub_nodes); + +/* + * look for an AFG and MFG nodes + */ +static void setup_fg_nodes(struct hdac_device *codec) +{ + int i, total_nodes, function_id; + hda_nid_t nid; + + total_nodes = snd_hdac_get_sub_nodes(codec, AC_NODE_ROOT, &nid); + for (i = 0; i < total_nodes; i++, nid++) { + function_id = snd_hdac_read_parm(codec, nid, + AC_PAR_FUNCTION_TYPE); + switch (function_id & 0xff) { + case AC_GRP_AUDIO_FUNCTION: + codec->afg = nid; + codec->afg_function_id = function_id & 0xff; + codec->afg_unsol = (function_id >> 8) & 1; + break; + case AC_GRP_MODEM_FUNCTION: + codec->mfg = nid; + codec->mfg_function_id = function_id & 0xff; + codec->mfg_unsol = (function_id >> 8) & 1; + break; + default: + break; + } + } +} + +/** + * snd_hdac_refresh_widgets - Reset the widget start/end nodes + * @codec: the codec object + */ +int snd_hdac_refresh_widgets(struct hdac_device *codec) +{ + hda_nid_t start_nid; + int nums; + + nums = snd_hdac_get_sub_nodes(codec, codec->afg, &start_nid); + if (!start_nid || nums <= 0 || nums >= 0xff) { + dev_err(&codec->dev, "cannot read sub nodes for FG 0x%02x\n", + codec->afg); + return -EINVAL; + } + + codec->num_nodes = nums; + codec->start_nid = start_nid; + codec->end_nid = start_nid + nums; + return 0; +} +EXPORT_SYMBOL_GPL(snd_hdac_refresh_widgets); + +/* return CONNLIST_LEN parameter of the given widget */ +static unsigned int get_num_conns(struct hdac_device *codec, hda_nid_t nid) +{ + unsigned int wcaps = get_wcaps(codec, nid); + unsigned int parm; + + if (!(wcaps & AC_WCAP_CONN_LIST) && + get_wcaps_type(wcaps) != AC_WID_VOL_KNB) + return 0; + + parm = snd_hdac_read_parm(codec, nid, AC_PAR_CONNLIST_LEN); + if (parm == -1) + parm = 0; + return parm; +} + +/** + * snd_hdac_get_connections - get a widget connection list + * @codec: the codec object + * @nid: NID + * @conn_list: the array to store the results, can be NULL + * @max_conns: the max size of the given array + * + * Returns the number of connected widgets, zero for no connection, or a + * negative error code. When the number of elements don't fit with the + * given array size, it returns -ENOSPC. + * + * When @conn_list is NULL, it just checks the number of connections. + */ +int snd_hdac_get_connections(struct hdac_device *codec, hda_nid_t nid, + hda_nid_t *conn_list, int max_conns) +{ + unsigned int parm; + int i, conn_len, conns, err; + unsigned int shift, num_elems, mask; + hda_nid_t prev_nid; + int null_count = 0; + + parm = get_num_conns(codec, nid); + if (!parm) + return 0; + + if (parm & AC_CLIST_LONG) { + /* long form */ + shift = 16; + num_elems = 2; + } else { + /* short form */ + shift = 8; + num_elems = 4; + } + conn_len = parm & AC_CLIST_LENGTH; + mask = (1 << (shift-1)) - 1; + + if (!conn_len) + return 0; /* no connection */ + + if (conn_len == 1) { + /* single connection */ + err = snd_hdac_read(codec, nid, AC_VERB_GET_CONNECT_LIST, 0, + &parm); + if (err < 0) + return err; + if (conn_list) + conn_list[0] = parm & mask; + return 1; + } + + /* multi connection */ + conns = 0; + prev_nid = 0; + for (i = 0; i < conn_len; i++) { + int range_val; + hda_nid_t val, n; + + if (i % num_elems == 0) { + err = snd_hdac_read(codec, nid, + AC_VERB_GET_CONNECT_LIST, i, + &parm); + if (err < 0) + return -EIO; + } + range_val = !!(parm & (1 << (shift-1))); /* ranges */ + val = parm & mask; + if (val == 0 && null_count++) { /* no second chance */ + dev_dbg(&codec->dev, + "invalid CONNECT_LIST verb %x[%i]:%x\n", + nid, i, parm); + return 0; + } + parm >>= shift; + if (range_val) { + /* ranges between the previous and this one */ + if (!prev_nid || prev_nid >= val) { + dev_warn(&codec->dev, + "invalid dep_range_val %x:%x\n", + prev_nid, val); + continue; + } + for (n = prev_nid + 1; n <= val; n++) { + if (conn_list) { + if (conns >= max_conns) + return -ENOSPC; + conn_list[conns] = n; + } + conns++; + } + } else { + if (conn_list) { + if (conns >= max_conns) + return -ENOSPC; + conn_list[conns] = val; + } + conns++; + } + prev_nid = val; + } + return conns; +} +EXPORT_SYMBOL_GPL(snd_hdac_get_connections); + +#ifdef CONFIG_PM +/** + * snd_hdac_power_up - increment the runtime pm counter + * @codec: the codec object + */ +void snd_hdac_power_up(struct hdac_device *codec) +{ + struct device *dev = &codec->dev; + + if (atomic_read(&codec->in_pm)) + return; + pm_runtime_get_sync(dev); +} +EXPORT_SYMBOL_GPL(snd_hdac_power_up); + +/** + * snd_hdac_power_up - decrement the runtime pm counter + * @codec: the codec object + */ +void snd_hdac_power_down(struct hdac_device *codec) +{ + struct device *dev = &codec->dev; + + if (atomic_read(&codec->in_pm)) + return; + pm_runtime_mark_last_busy(dev); + pm_runtime_put_autosuspend(dev); +} +EXPORT_SYMBOL_GPL(snd_hdac_power_down); +#endif + +/* codec vendor labels */ +struct hda_vendor_id { + unsigned int id; + const char *name; +}; + +static struct hda_vendor_id hda_vendor_ids[] = { + { 0x1002, "ATI" }, + { 0x1013, "Cirrus Logic" }, + { 0x1057, "Motorola" }, + { 0x1095, "Silicon Image" }, + { 0x10de, "Nvidia" }, + { 0x10ec, "Realtek" }, + { 0x1102, "Creative" }, + { 0x1106, "VIA" }, + { 0x111d, "IDT" }, + { 0x11c1, "LSI" }, + { 0x11d4, "Analog Devices" }, + { 0x13f6, "C-Media" }, + { 0x14f1, "Conexant" }, + { 0x17e8, "Chrontel" }, + { 0x1854, "LG" }, + { 0x1aec, "Wolfson Microelectronics" }, + { 0x1af4, "QEMU" }, + { 0x434d, "C-Media" }, + { 0x8086, "Intel" }, + { 0x8384, "SigmaTel" }, + {} /* terminator */ +}; + +/* store the codec vendor name */ +static int get_codec_vendor_name(struct hdac_device *codec) +{ + const struct hda_vendor_id *c; + u16 vendor_id = codec->vendor_id >> 16; + + for (c = hda_vendor_ids; c->id; c++) { + if (c->id == vendor_id) { + codec->vendor_name = kstrdup(c->name, GFP_KERNEL); + return codec->vendor_name ? 0 : -ENOMEM; + } + } + + codec->vendor_name = kasprintf(GFP_KERNEL, "Generic %04x", vendor_id); + return codec->vendor_name ? 0 : -ENOMEM; +} diff --git a/sound/hda/local.h b/sound/hda/local.h new file mode 100644 index 000000000000..a077d1f656f6 --- /dev/null +++ b/sound/hda/local.h @@ -0,0 +1,19 @@ +/* + * Local helper macros and functions for HD-audio core drivers + */ + +#ifndef __HDAC_LOCAL_H +#define __HDAC_LOCAL_H + +#define get_wcaps(codec, nid) \ + snd_hdac_read_parm(codec, nid, AC_PAR_AUDIO_WIDGET_CAP) + +/* get the widget type from widget capability bits */ +static inline int get_wcaps_type(unsigned int wcaps) +{ + if (!wcaps) + return -1; /* invalid type */ + return (wcaps & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT; +} + +#endif /* __HDAC_LOCAL_H */ diff --git a/sound/pci/hda/hda_auto_parser.c b/sound/pci/hda/hda_auto_parser.c index 3f8706bb3d16..03b7399bb7f0 100644 --- a/sound/pci/hda/hda_auto_parser.c +++ b/sound/pci/hda/hda_auto_parser.c @@ -172,7 +172,7 @@ int snd_hda_parse_pin_defcfg(struct hda_codec *codec, const hda_nid_t *ignore_nids, unsigned int cond_flags) { - hda_nid_t nid, end_nid; + hda_nid_t nid; short seq, assoc_line_out; struct auto_out_pin line_out[ARRAY_SIZE(cfg->line_out_pins)]; struct auto_out_pin speaker_out[ARRAY_SIZE(cfg->speaker_pins)]; @@ -189,8 +189,7 @@ int snd_hda_parse_pin_defcfg(struct hda_codec *codec, memset(hp_out, 0, sizeof(hp_out)); assoc_line_out = 0; - end_nid = codec->start_nid + codec->num_nodes; - for (nid = codec->start_nid; nid < end_nid; nid++) { + for_each_hda_codec_node(nid, codec) { unsigned int wid_caps = get_wcaps(codec, nid); unsigned int wid_type = get_wcaps_type(wid_caps); unsigned int def_conf; @@ -410,7 +409,7 @@ int snd_hda_parse_pin_defcfg(struct hda_codec *codec, * debug prints of the parsed results */ codec_info(codec, "autoconfig for %s: line_outs=%d (0x%x/0x%x/0x%x/0x%x/0x%x) type:%s\n", - codec->chip_name, cfg->line_outs, cfg->line_out_pins[0], + codec->core.chip_name, cfg->line_outs, cfg->line_out_pins[0], cfg->line_out_pins[1], cfg->line_out_pins[2], cfg->line_out_pins[3], cfg->line_out_pins[4], cfg->line_out_type == AUTO_PIN_HP_OUT ? "hp" : @@ -836,33 +835,33 @@ static void apply_fixup(struct hda_codec *codec, int id, int action, int depth) if (action != HDA_FIXUP_ACT_PRE_PROBE || !fix->v.pins) break; codec_dbg(codec, "%s: Apply pincfg for %s\n", - codec->chip_name, modelname); + codec->core.chip_name, modelname); snd_hda_apply_pincfgs(codec, fix->v.pins); break; case HDA_FIXUP_VERBS: if (action != HDA_FIXUP_ACT_PROBE || !fix->v.verbs) break; codec_dbg(codec, "%s: Apply fix-verbs for %s\n", - codec->chip_name, modelname); + codec->core.chip_name, modelname); snd_hda_add_verbs(codec, fix->v.verbs); break; case HDA_FIXUP_FUNC: if (!fix->v.func) break; codec_dbg(codec, "%s: Apply fix-func for %s\n", - codec->chip_name, modelname); + codec->core.chip_name, modelname); fix->v.func(codec, fix, action); break; case HDA_FIXUP_PINCTLS: if (action != HDA_FIXUP_ACT_PROBE || !fix->v.pins) break; codec_dbg(codec, "%s: Apply pinctl for %s\n", - codec->chip_name, modelname); + codec->core.chip_name, modelname); set_pin_targets(codec, fix->v.pins); break; default: codec_err(codec, "%s: Invalid fixup type %d\n", - codec->chip_name, fix->type); + codec->core.chip_name, fix->type); break; } if (!fix->chained || fix->chained_before) @@ -912,16 +911,16 @@ void snd_hda_pick_pin_fixup(struct hda_codec *codec, return; for (pq = pin_quirk; pq->subvendor; pq++) { - if ((codec->subsystem_id & 0xffff0000) != (pq->subvendor << 16)) + if ((codec->core.subsystem_id & 0xffff0000) != (pq->subvendor << 16)) continue; - if (codec->vendor_id != pq->codec) + if (codec->core.vendor_id != pq->codec) continue; if (pin_config_match(codec, pq->pins)) { codec->fixup_id = pq->value; #ifdef CONFIG_SND_DEBUG_VERBOSE codec->fixup_name = pq->name; codec_dbg(codec, "%s: picked fixup %s (pin match)\n", - codec->chip_name, codec->fixup_name); + codec->core.chip_name, codec->fixup_name); #endif codec->fixup_list = fixlist; return; @@ -963,7 +962,7 @@ void snd_hda_pick_fixup(struct hda_codec *codec, codec->fixup_name = NULL; codec->fixup_id = HDA_FIXUP_ID_NO_FIXUP; codec_dbg(codec, "%s: picked no fixup (nofixup specified)\n", - codec->chip_name); + codec->core.chip_name); return; } @@ -974,7 +973,7 @@ void snd_hda_pick_fixup(struct hda_codec *codec, codec->fixup_name = models->name; codec->fixup_list = fixlist; codec_dbg(codec, "%s: picked fixup %s (model specified)\n", - codec->chip_name, codec->fixup_name); + codec->core.chip_name, codec->fixup_name); return; } models++; @@ -987,7 +986,7 @@ void snd_hda_pick_fixup(struct hda_codec *codec, #ifdef CONFIG_SND_DEBUG_VERBOSE name = q->name; codec_dbg(codec, "%s: picked fixup %s (PCI SSID%s)\n", - codec->chip_name, name, q->subdevice_mask ? "" : " - vendor generic"); + codec->core.chip_name, name, q->subdevice_mask ? "" : " - vendor generic"); #endif } } @@ -996,12 +995,12 @@ void snd_hda_pick_fixup(struct hda_codec *codec, unsigned int vendorid = q->subdevice | (q->subvendor << 16); unsigned int mask = 0xffff0000 | q->subdevice_mask; - if ((codec->subsystem_id & mask) == (vendorid & mask)) { + if ((codec->core.subsystem_id & mask) == (vendorid & mask)) { id = q->value; #ifdef CONFIG_SND_DEBUG_VERBOSE name = q->name; codec_dbg(codec, "%s: picked fixup %s (codec SSID)\n", - codec->chip_name, name); + codec->core.chip_name, name); #endif break; } diff --git a/sound/pci/hda/hda_beep.c b/sound/pci/hda/hda_beep.c index 4cdac3a71cae..3364dc0fdeab 100644 --- a/sound/pci/hda/hda_beep.c +++ b/sound/pci/hda/hda_beep.c @@ -165,8 +165,8 @@ static int snd_hda_do_attach(struct hda_beep *beep) input_dev->id.bustype = BUS_PCI; input_dev->dev.parent = &codec->card->card_dev; - input_dev->id.vendor = codec->vendor_id >> 16; - input_dev->id.product = codec->vendor_id & 0xffff; + input_dev->id.vendor = codec->core.vendor_id >> 16; + input_dev->id.product = codec->core.vendor_id & 0xffff; input_dev->id.version = 0x01; input_dev->evbit[0] = BIT_MASK(EV_SND); diff --git a/sound/pci/hda/hda_bind.c b/sound/pci/hda/hda_bind.c index 0b9ea70c546b..ad276a9771db 100644 --- a/sound/pci/hda/hda_bind.c +++ b/sound/pci/hda/hda_bind.c @@ -14,36 +14,6 @@ #include "hda_codec.h" #include "hda_local.h" -/* codec vendor labels */ -struct hda_vendor_id { - unsigned int id; - const char *name; -}; - -static struct hda_vendor_id hda_vendor_ids[] = { - { 0x1002, "ATI" }, - { 0x1013, "Cirrus Logic" }, - { 0x1057, "Motorola" }, - { 0x1095, "Silicon Image" }, - { 0x10de, "Nvidia" }, - { 0x10ec, "Realtek" }, - { 0x1102, "Creative" }, - { 0x1106, "VIA" }, - { 0x111d, "IDT" }, - { 0x11c1, "LSI" }, - { 0x11d4, "Analog Devices" }, - { 0x13f6, "C-Media" }, - { 0x14f1, "Conexant" }, - { 0x17e8, "Chrontel" }, - { 0x1854, "LG" }, - { 0x1aec, "Wolfson Microelectronics" }, - { 0x1af4, "QEMU" }, - { 0x434d, "C-Media" }, - { 0x8086, "Intel" }, - { 0x8384, "SigmaTel" }, - {} /* terminator */ -}; - /* * find a matching codec preset */ @@ -54,19 +24,19 @@ static int hda_codec_match(struct hdac_device *dev, struct hdac_driver *drv) container_of(drv, struct hda_codec_driver, core); const struct hda_codec_preset *preset; /* check probe_id instead of vendor_id if set */ - u32 id = codec->probe_id ? codec->probe_id : codec->vendor_id; + u32 id = codec->probe_id ? codec->probe_id : codec->core.vendor_id; for (preset = driver->preset; preset->id; preset++) { u32 mask = preset->mask; - if (preset->afg && preset->afg != codec->afg) + if (preset->afg && preset->afg != codec->core.afg) continue; - if (preset->mfg && preset->mfg != codec->mfg) + if (preset->mfg && preset->mfg != codec->core.mfg) continue; if (!mask) mask = ~0; if (preset->id == (id & mask) && - (!preset->rev || preset->rev == codec->revision_id)) { + (!preset->rev || preset->rev == codec->core.revision_id)) { codec->preset = preset; return 1; } @@ -86,15 +56,11 @@ static void hda_codec_unsol_event(struct hdac_device *dev, unsigned int ev) /* reset the codec name from the preset */ static int codec_refresh_name(struct hda_codec *codec, const char *name) { - char tmp[16]; - - kfree(codec->chip_name); - if (!name) { - sprintf(tmp, "ID %x", codec->vendor_id & 0xffff); - name = tmp; + if (name) { + kfree(codec->core.chip_name); + codec->core.chip_name = kstrdup(name, GFP_KERNEL); } - codec->chip_name = kstrdup(name, GFP_KERNEL); - return codec->chip_name ? 0 : -ENOMEM; + return codec->core.chip_name ? 0 : -ENOMEM; } static int hda_codec_driver_probe(struct device *dev) @@ -192,48 +158,23 @@ static inline bool codec_probed(struct hda_codec *codec) static void codec_bind_module(struct hda_codec *codec) { #ifdef MODULE - request_module("snd-hda-codec-id:%08x", codec->vendor_id); + request_module("snd-hda-codec-id:%08x", codec->core.vendor_id); if (codec_probed(codec)) return; request_module("snd-hda-codec-id:%04x*", - (codec->vendor_id >> 16) & 0xffff); + (codec->core.vendor_id >> 16) & 0xffff); if (codec_probed(codec)) return; #endif } -/* store the codec vendor name */ -static int get_codec_vendor_name(struct hda_codec *codec) -{ - const struct hda_vendor_id *c; - const char *vendor = NULL; - u16 vendor_id = codec->vendor_id >> 16; - char tmp[16]; - - for (c = hda_vendor_ids; c->id; c++) { - if (c->id == vendor_id) { - vendor = c->name; - break; - } - } - if (!vendor) { - sprintf(tmp, "Generic %04x", vendor_id); - vendor = tmp; - } - codec->vendor_name = kstrdup(vendor, GFP_KERNEL); - if (!codec->vendor_name) - return -ENOMEM; - return 0; -} - #if IS_ENABLED(CONFIG_SND_HDA_CODEC_HDMI) /* if all audio out widgets are digital, let's assume the codec as a HDMI/DP */ static bool is_likely_hdmi_codec(struct hda_codec *codec) { - hda_nid_t nid = codec->start_nid; - int i; + hda_nid_t nid; - for (i = 0; i < codec->num_nodes; i++, nid++) { + for_each_hda_codec_node(nid, codec) { unsigned int wcaps = get_wcaps(codec, nid); switch (get_wcaps_type(wcaps)) { case AC_WID_AUD_IN: @@ -294,12 +235,6 @@ int snd_hda_codec_configure(struct hda_codec *codec) { int err; - if (!codec->vendor_name) { - err = get_codec_vendor_name(codec); - if (err < 0) - return err; - } - if (is_generic_config(codec)) codec->probe_id = HDA_CODEC_ID_GENERIC; else @@ -320,10 +255,10 @@ int snd_hda_codec_configure(struct hda_codec *codec) } /* audio codec should override the mixer name */ - if (codec->afg || !*codec->card->mixername) + if (codec->core.afg || !*codec->card->mixername) snprintf(codec->card->mixername, - sizeof(codec->card->mixername), - "%s %s", codec->vendor_name, codec->chip_name); + sizeof(codec->card->mixername), "%s %s", + codec->core.vendor_name, codec->core.chip_name); return 0; error: diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index f96bff37c787..ddfc0fbbee23 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -40,7 +40,7 @@ #include #ifdef CONFIG_PM -#define codec_in_pm(codec) atomic_read(&(codec)->in_pm) +#define codec_in_pm(codec) atomic_read(&(codec)->core.in_pm) #define hda_codec_is_power_on(codec) \ (!pm_runtime_suspended(hda_codec_dev(codec))) #else @@ -48,6 +48,11 @@ #define hda_codec_is_power_on(codec) 1 #endif +#define codec_has_epss(codec) \ + ((codec)->core.power_caps & AC_PWRST_EPSS) +#define codec_has_clkstop(codec) \ + ((codec)->core.power_caps & AC_PWRST_CLKSTOP) + /** * snd_hda_get_jack_location - Give a location string of the jack * @cfg: pin default config value @@ -118,30 +123,6 @@ const char *snd_hda_get_jack_type(u32 cfg) } EXPORT_SYMBOL_GPL(snd_hda_get_jack_type); -/* - * Compose a 32bit command word to be sent to the HD-audio controller - */ -static inline unsigned int -make_codec_cmd(struct hda_codec *codec, hda_nid_t nid, int flags, - unsigned int verb, unsigned int parm) -{ - unsigned int addr = codec->core.addr; - u32 val; - - if ((addr & ~0xf) || (nid & ~0x7f) || - (verb & ~0xfff) || (parm & ~0xffff)) { - codec_err(codec, "hda-codec: out of range cmd %x:%x:%x:%x\n", - addr, nid, verb, parm); - return ~0; - } - - val = (u32)addr << 28; - val |= (u32)nid << 20; - val |= verb << 8; - val |= parm; - return val; -} - /* * Send and receive a verb */ @@ -194,7 +175,7 @@ unsigned int snd_hda_codec_read(struct hda_codec *codec, hda_nid_t nid, int flags, unsigned int verb, unsigned int parm) { - unsigned cmd = make_codec_cmd(codec, nid, flags, verb, parm); + unsigned int cmd = snd_hdac_make_cmd(&codec->core, nid, verb, parm); unsigned int res; if (codec_exec_verb(codec, cmd, flags, &res)) return -1; @@ -217,7 +198,7 @@ EXPORT_SYMBOL_GPL(snd_hda_codec_read); int snd_hda_codec_write(struct hda_codec *codec, hda_nid_t nid, int flags, unsigned int verb, unsigned int parm) { - unsigned int cmd = make_codec_cmd(codec, nid, flags, verb, parm); + unsigned int cmd = snd_hdac_make_cmd(&codec->core, nid, verb, parm); return codec_exec_verb(codec, cmd, flags, NULL); } EXPORT_SYMBOL_GPL(snd_hda_codec_write); @@ -237,30 +218,6 @@ void snd_hda_sequence_write(struct hda_codec *codec, const struct hda_verb *seq) } EXPORT_SYMBOL_GPL(snd_hda_sequence_write); -/** - * snd_hda_get_sub_nodes - get the range of sub nodes - * @codec: the HDA codec - * @nid: NID to parse - * @start_id: the pointer to store the start NID - * - * Parse the NID and store the start NID of its sub-nodes. - * Returns the number of sub-nodes. - */ -int snd_hda_get_sub_nodes(struct hda_codec *codec, hda_nid_t nid, - hda_nid_t *start_id) -{ - unsigned int parm; - - parm = snd_hda_param_read(codec, nid, AC_PAR_NODE_COUNT); - if (parm == -1) { - *start_id = 0; - return 0; - } - *start_id = (parm >> 16) & 0x7fff; - return (int)(parm & 0x7fff); -} -EXPORT_SYMBOL_GPL(snd_hda_get_sub_nodes); - /* connection list element */ struct hda_conn_list { struct list_head list; @@ -401,128 +358,6 @@ int snd_hda_get_connections(struct hda_codec *codec, hda_nid_t nid, } EXPORT_SYMBOL_GPL(snd_hda_get_connections); -/* return CONNLIST_LEN parameter of the given widget */ -static unsigned int get_num_conns(struct hda_codec *codec, hda_nid_t nid) -{ - unsigned int wcaps = get_wcaps(codec, nid); - unsigned int parm; - - if (!(wcaps & AC_WCAP_CONN_LIST) && - get_wcaps_type(wcaps) != AC_WID_VOL_KNB) - return 0; - - parm = snd_hda_param_read(codec, nid, AC_PAR_CONNLIST_LEN); - if (parm == -1) - parm = 0; - return parm; -} - -int snd_hda_get_num_raw_conns(struct hda_codec *codec, hda_nid_t nid) -{ - return snd_hda_get_raw_connections(codec, nid, NULL, 0); -} - -/** - * snd_hda_get_raw_connections - copy connection list without cache - * @codec: the HDA codec - * @nid: NID to parse - * @conn_list: connection list array - * @max_conns: max. number of connections to store - * - * Like snd_hda_get_connections(), copy the connection list but without - * checking through the connection-list cache. - * Currently called only from hda_proc.c, so not exported. - */ -int snd_hda_get_raw_connections(struct hda_codec *codec, hda_nid_t nid, - hda_nid_t *conn_list, int max_conns) -{ - unsigned int parm; - int i, conn_len, conns; - unsigned int shift, num_elems, mask; - hda_nid_t prev_nid; - int null_count = 0; - - parm = get_num_conns(codec, nid); - if (!parm) - return 0; - - if (parm & AC_CLIST_LONG) { - /* long form */ - shift = 16; - num_elems = 2; - } else { - /* short form */ - shift = 8; - num_elems = 4; - } - conn_len = parm & AC_CLIST_LENGTH; - mask = (1 << (shift-1)) - 1; - - if (!conn_len) - return 0; /* no connection */ - - if (conn_len == 1) { - /* single connection */ - parm = snd_hda_codec_read(codec, nid, 0, - AC_VERB_GET_CONNECT_LIST, 0); - if (parm == -1 && codec->bus->rirb_error) - return -EIO; - if (conn_list) - conn_list[0] = parm & mask; - return 1; - } - - /* multi connection */ - conns = 0; - prev_nid = 0; - for (i = 0; i < conn_len; i++) { - int range_val; - hda_nid_t val, n; - - if (i % num_elems == 0) { - parm = snd_hda_codec_read(codec, nid, 0, - AC_VERB_GET_CONNECT_LIST, i); - if (parm == -1 && codec->bus->rirb_error) - return -EIO; - } - range_val = !!(parm & (1 << (shift-1))); /* ranges */ - val = parm & mask; - if (val == 0 && null_count++) { /* no second chance */ - codec_dbg(codec, - "invalid CONNECT_LIST verb %x[%i]:%x\n", - nid, i, parm); - return 0; - } - parm >>= shift; - if (range_val) { - /* ranges between the previous and this one */ - if (!prev_nid || prev_nid >= val) { - codec_warn(codec, - "invalid dep_range_val %x:%x\n", - prev_nid, val); - continue; - } - for (n = prev_nid + 1; n <= val; n++) { - if (conn_list) { - if (conns >= max_conns) - return -ENOSPC; - conn_list[conns] = n; - } - conns++; - } - } else { - if (conn_list) { - if (conns >= max_conns) - return -ENOSPC; - conn_list[conns] = val; - } - conns++; - } - prev_nid = val; - } - return conns; -} - /** * snd_hda_override_conn_list - add/modify the connection-list to cache * @codec: the HDA codec @@ -737,35 +572,6 @@ int snd_hda_bus_new(struct snd_card *card, } EXPORT_SYMBOL_GPL(snd_hda_bus_new); -/* - * look for an AFG and MFG nodes - */ -static void setup_fg_nodes(struct hda_codec *codec) -{ - int i, total_nodes, function_id; - hda_nid_t nid; - - total_nodes = snd_hda_get_sub_nodes(codec, AC_NODE_ROOT, &nid); - for (i = 0; i < total_nodes; i++, nid++) { - function_id = snd_hda_param_read(codec, nid, - AC_PAR_FUNCTION_TYPE); - switch (function_id & 0xff) { - case AC_GRP_AUDIO_FUNCTION: - codec->afg = nid; - codec->afg_function_id = function_id & 0xff; - codec->afg_unsol = (function_id >> 8) & 1; - break; - case AC_GRP_MODEM_FUNCTION: - codec->mfg = nid; - codec->mfg_function_id = function_id & 0xff; - codec->mfg_unsol = (function_id >> 8) & 1; - break; - default: - break; - } - } -} - /* * read widget caps for each widget and store in cache */ @@ -774,13 +580,11 @@ static int read_widget_caps(struct hda_codec *codec, hda_nid_t fg_node) int i; hda_nid_t nid; - codec->num_nodes = snd_hda_get_sub_nodes(codec, fg_node, - &codec->start_nid); - codec->wcaps = kmalloc(codec->num_nodes * 4, GFP_KERNEL); + codec->wcaps = kmalloc(codec->core.num_nodes * 4, GFP_KERNEL); if (!codec->wcaps) return -ENOMEM; - nid = codec->start_nid; - for (i = 0; i < codec->num_nodes; i++, nid++) + nid = codec->core.start_nid; + for (i = 0; i < codec->core.num_nodes; i++, nid++) codec->wcaps[i] = snd_hda_param_read(codec, nid, AC_PAR_AUDIO_WIDGET_CAP); return 0; @@ -789,10 +593,9 @@ static int read_widget_caps(struct hda_codec *codec, hda_nid_t fg_node) /* read all pin default configurations and save codec->init_pins */ static int read_pin_defaults(struct hda_codec *codec) { - int i; - hda_nid_t nid = codec->start_nid; + hda_nid_t nid; - for (i = 0; i < codec->num_nodes; i++, nid++) { + for_each_hda_codec_node(nid, codec) { struct hda_pincfg *pin; unsigned int wcaps = get_wcaps(codec, nid); unsigned int wid_type = get_wcaps_type(wcaps); @@ -1136,9 +939,6 @@ void snd_hda_codec_cleanup_for_unbind(struct hda_codec *codec) remove_conn_list(codec); } -static bool snd_hda_codec_get_supported_ps(struct hda_codec *codec, - hda_nid_t fg, unsigned int power_state); - static unsigned int hda_set_power_state(struct hda_codec *codec, unsigned int power_state); @@ -1178,12 +978,10 @@ static void snd_hda_codec_dev_release(struct device *dev) struct hda_codec *codec = dev_to_hda_codec(dev); free_init_pincfgs(codec); - snd_hdac_bus_remove_device(&codec->bus->core, &codec->core); + snd_hdac_device_exit(&codec->core); snd_hda_sysfs_clear(codec); free_hda_cache(&codec->amp_cache); free_hda_cache(&codec->cmd_cache); - kfree(codec->vendor_name); - kfree(codec->chip_name); kfree(codec->modelname); kfree(codec->wcaps); kfree(codec); @@ -1201,7 +999,6 @@ int snd_hda_codec_new(struct hda_bus *bus, struct snd_card *card, unsigned int codec_addr, struct hda_codec **codecp) { struct hda_codec *codec; - struct device *dev; char component[31]; hda_nid_t fg; int err; @@ -1220,19 +1017,16 @@ int snd_hda_codec_new(struct hda_bus *bus, struct snd_card *card, if (!codec) return -ENOMEM; - codec->core.bus = &bus->core; - codec->core.addr = codec_addr; - codec->core.type = HDA_DEV_LEGACY; + sprintf(component, "hdaudioC%dD%d", card->number, codec_addr); + err = snd_hdac_device_init(&codec->core, &bus->core, component, + codec_addr); + if (err < 0) { + kfree(codec); + return err; + } - dev = hda_codec_dev(codec); - device_initialize(dev); - dev->parent = bus->core.dev; - dev->bus = &snd_hda_bus_type; - dev->release = snd_hda_codec_dev_release; - dev->groups = snd_hda_dev_attr_groups; - dev_set_name(dev, "hdaudioC%dD%d", card->number, codec_addr); - dev_set_drvdata(dev, codec); /* for sysfs */ - device_enable_async_suspend(dev); + codec->core.dev.release = snd_hda_codec_dev_release; + codec->core.type = HDA_DEV_LEGACY; codec->bus = bus; codec->card = card; @@ -1258,12 +1052,6 @@ int snd_hda_codec_new(struct hda_bus *bus, struct snd_card *card, codec->fixup_id = HDA_FIXUP_ID_NOT_SET; #ifdef CONFIG_PM - /* snd_hda_codec_new() marks the codec as power-up, and leave it as is. - * it's powered down later in snd_hda_codec_dev_register(). - */ - set_bit(codec->core.addr, &bus->core.codec_powered); - pm_runtime_set_active(hda_codec_dev(codec)); - pm_runtime_get_noresume(hda_codec_dev(codec)); codec->power_jiffies = jiffies; #endif @@ -1277,31 +1065,7 @@ int snd_hda_codec_new(struct hda_bus *bus, struct snd_card *card, } } - err = snd_hdac_bus_add_device(&bus->core, &codec->core); - if (err < 0) - goto error; - - codec->vendor_id = snd_hda_param_read(codec, AC_NODE_ROOT, - AC_PAR_VENDOR_ID); - if (codec->vendor_id == -1) - /* read again, hopefully the access method was corrected - * in the last read... - */ - codec->vendor_id = snd_hda_param_read(codec, AC_NODE_ROOT, - AC_PAR_VENDOR_ID); - codec->subsystem_id = snd_hda_param_read(codec, AC_NODE_ROOT, - AC_PAR_SUBSYSTEM_ID); - codec->revision_id = snd_hda_param_read(codec, AC_NODE_ROOT, - AC_PAR_REV_ID); - - setup_fg_nodes(codec); - if (!codec->afg && !codec->mfg) { - codec_err(codec, "no AFG or MFG node found\n"); - err = -ENODEV; - goto error; - } - - fg = codec->afg ? codec->afg : codec->mfg; + fg = codec->core.afg ? codec->core.afg : codec->core.mfg; err = read_widget_caps(codec, fg); if (err < 0) goto error; @@ -1309,19 +1073,6 @@ int snd_hda_codec_new(struct hda_bus *bus, struct snd_card *card, if (err < 0) goto error; - if (!codec->subsystem_id) { - codec->subsystem_id = - snd_hda_codec_read(codec, fg, 0, - AC_VERB_GET_SUBSYSTEM_ID, 0); - } - -#ifdef CONFIG_PM - codec->d3_stop_clk = snd_hda_codec_get_supported_ps(codec, fg, - AC_PWRST_CLKSTOP); -#endif - codec->epss = snd_hda_codec_get_supported_ps(codec, fg, - AC_PWRST_EPSS); - /* power-up all before initialization */ hda_set_power_state(codec, AC_PWRST_D0); @@ -1329,8 +1080,8 @@ int snd_hda_codec_new(struct hda_bus *bus, struct snd_card *card, snd_hda_create_hwdep(codec); - sprintf(component, "HDA:%08x,%08x,%08x", codec->vendor_id, - codec->subsystem_id, codec->revision_id); + sprintf(component, "HDA:%08x,%08x,%08x", codec->core.vendor_id, + codec->core.subsystem_id, codec->core.revision_id); snd_component_add(card, component); err = snd_device_new(card, SNDRV_DEV_CODEC, codec, &dev_ops); @@ -1342,6 +1093,7 @@ int snd_hda_codec_new(struct hda_bus *bus, struct snd_card *card, return 0; error: + pm_runtime_put_noidle(hda_codec_dev(codec)); put_device(hda_codec_dev(codec)); return err; } @@ -1359,11 +1111,15 @@ int snd_hda_codec_update_widgets(struct hda_codec *codec) hda_nid_t fg; int err; + err = snd_hdac_refresh_widgets(&codec->core); + if (err < 0) + return err; + /* Assume the function group node does not change, * only the widget nodes may change. */ kfree(codec->wcaps); - fg = codec->afg ? codec->afg : codec->mfg; + fg = codec->core.afg ? codec->core.afg : codec->core.mfg; err = read_widget_caps(codec, fg); if (err < 0) return err; @@ -1663,7 +1419,7 @@ static unsigned int read_amp_cap(struct hda_codec *codec, hda_nid_t nid, int direction) { if (!(get_wcaps(codec, nid) & AC_WCAP_AMP_OVRD)) - nid = codec->afg; + nid = codec->core.afg; return snd_hda_param_read(codec, nid, direction == HDA_OUTPUT ? AC_PAR_AMP_OUT_CAP : AC_PAR_AMP_IN_CAP); @@ -3664,10 +3420,9 @@ EXPORT_SYMBOL_GPL(snd_hda_codec_flush_cache); void snd_hda_codec_set_power_to_all(struct hda_codec *codec, hda_nid_t fg, unsigned int power_state) { - hda_nid_t nid = codec->start_nid; - int i; + hda_nid_t nid; - for (i = 0; i < codec->num_nodes; i++, nid++) { + for_each_hda_codec_node(nid, codec) { unsigned int wcaps = get_wcaps(codec, nid); unsigned int state = power_state; if (!(wcaps & AC_WCAP_POWER)) @@ -3683,22 +3438,6 @@ void snd_hda_codec_set_power_to_all(struct hda_codec *codec, hda_nid_t fg, } EXPORT_SYMBOL_GPL(snd_hda_codec_set_power_to_all); -/* - * supported power states check - */ -static bool snd_hda_codec_get_supported_ps(struct hda_codec *codec, hda_nid_t fg, - unsigned int power_state) -{ - int sup = snd_hda_param_read(codec, fg, AC_PAR_POWER_STATE); - - if (sup == -1) - return false; - if (sup & power_state) - return true; - else - return false; -} - /* * wait until the state is reached, returns the current state */ @@ -3738,7 +3477,7 @@ unsigned int snd_hda_codec_eapd_power_filter(struct hda_codec *codec, hda_nid_t nid, unsigned int power_state) { - if (nid == codec->afg || nid == codec->mfg) + if (nid == codec->core.afg || nid == codec->core.mfg) return power_state; if (power_state == AC_PWRST_D3 && get_wcaps_type(get_wcaps(codec, nid)) == AC_WID_PIN && @@ -3758,7 +3497,7 @@ EXPORT_SYMBOL_GPL(snd_hda_codec_eapd_power_filter); static unsigned int hda_set_power_state(struct hda_codec *codec, unsigned int power_state) { - hda_nid_t fg = codec->afg ? codec->afg : codec->mfg; + hda_nid_t fg = codec->core.afg ? codec->core.afg : codec->core.mfg; int count; unsigned int state; int flags = 0; @@ -3766,7 +3505,7 @@ static unsigned int hda_set_power_state(struct hda_codec *codec, /* this delay seems necessary to avoid click noise at power-down */ if (power_state == AC_PWRST_D3) { if (codec->depop_delay < 0) - msleep(codec->epss ? 10 : 100); + msleep(codec_has_epss(codec) ? 10 : 100); else if (codec->depop_delay > 0) msleep(codec->depop_delay); flags = HDA_RW_NO_RESPONSE_FALLBACK; @@ -3800,14 +3539,13 @@ static unsigned int hda_set_power_state(struct hda_codec *codec, */ static void sync_power_up_states(struct hda_codec *codec) { - hda_nid_t nid = codec->start_nid; - int i; + hda_nid_t nid; /* don't care if no filter is used */ if (!codec->power_filter) return; - for (i = 0; i < codec->num_nodes; i++, nid++) { + for_each_hda_codec_node(nid, codec) { unsigned int wcaps = get_wcaps(codec, nid); unsigned int target; if (!(wcaps & AC_WCAP_POWER)) @@ -3858,14 +3596,14 @@ static unsigned int hda_call_codec_suspend(struct hda_codec *codec) { unsigned int state; - atomic_inc(&codec->in_pm); + atomic_inc(&codec->core.in_pm); if (codec->patch_ops.suspend) codec->patch_ops.suspend(codec); hda_cleanup_all_streams(codec); state = hda_set_power_state(codec, AC_PWRST_D3); update_power_acct(codec, true); - atomic_dec(&codec->in_pm); + atomic_dec(&codec->core.in_pm); return state; } @@ -3890,7 +3628,7 @@ static void hda_mark_cmd_cache_dirty(struct hda_codec *codec) */ static void hda_call_codec_resume(struct hda_codec *codec) { - atomic_inc(&codec->in_pm); + atomic_inc(&codec->core.in_pm); hda_mark_cmd_cache_dirty(codec); @@ -3913,7 +3651,7 @@ static void hda_call_codec_resume(struct hda_codec *codec) hda_jackpoll_work(&codec->jackpoll_work.work); else snd_hda_jack_report_sync(codec); - atomic_dec(&codec->in_pm); + atomic_dec(&codec->core.in_pm); } static int hda_codec_runtime_suspend(struct device *dev) @@ -3926,8 +3664,9 @@ static int hda_codec_runtime_suspend(struct device *dev) list_for_each_entry(pcm, &codec->pcm_list_head, list) snd_pcm_suspend_all(pcm->pcm); state = hda_call_codec_suspend(codec); - if (codec->d3_stop_clk && codec->epss && (state & AC_PWRST_CLK_STOP_OK)) - clear_bit(codec->core.addr, &codec->bus->core.codec_powered); + if (codec_has_clkstop(codec) && codec_has_epss(codec) && + (state & AC_PWRST_CLK_STOP_OK)) + snd_hdac_codec_link_down(&codec->core); return 0; } @@ -3935,7 +3674,7 @@ static int hda_codec_runtime_resume(struct device *dev) { struct hda_codec *codec = dev_to_hda_codec(dev); - set_bit(codec->core.addr, &codec->bus->core.codec_powered); + snd_hdac_codec_link_up(&codec->core); hda_call_codec_resume(codec); pm_runtime_mark_last_busy(dev); return 0; @@ -4129,11 +3868,11 @@ static unsigned int get_pcm_param(struct hda_codec *codec, hda_nid_t nid, int dir) { unsigned int val = 0; - if (nid != codec->afg && + if (nid != codec->core.afg && (get_wcaps(codec, nid) & AC_WCAP_FORMAT_OVRD)) val = snd_hda_param_read(codec, nid, AC_PAR_PCM); if (!val || val == -1) - val = snd_hda_param_read(codec, codec->afg, AC_PAR_PCM); + val = snd_hda_param_read(codec, codec->core.afg, AC_PAR_PCM); if (!val || val == -1) return 0; return val; @@ -4150,7 +3889,7 @@ static unsigned int get_stream_param(struct hda_codec *codec, hda_nid_t nid, { unsigned int streams = snd_hda_param_read(codec, nid, AC_PAR_STREAM); if (!streams || streams == -1) - streams = snd_hda_param_read(codec, codec->afg, AC_PAR_STREAM); + streams = snd_hda_param_read(codec, codec->core.afg, AC_PAR_STREAM); if (!streams || streams == -1) return 0; return streams; @@ -4632,39 +4371,6 @@ int snd_hda_add_new_ctls(struct hda_codec *codec, EXPORT_SYMBOL_GPL(snd_hda_add_new_ctls); #ifdef CONFIG_PM -/** - * snd_hda_power_up - Power-up the codec - * @codec: HD-audio codec - * - * Increment the usage counter and resume the device if not done yet. - */ -void snd_hda_power_up(struct hda_codec *codec) -{ - struct device *dev = hda_codec_dev(codec); - - if (codec_in_pm(codec)) - return; - pm_runtime_get_sync(dev); -} -EXPORT_SYMBOL_GPL(snd_hda_power_up); - -/** - * snd_hda_power_down - Power-down the codec - * @codec: HD-audio codec - * - * Decrement the usage counter and schedules the autosuspend if none used. - */ -void snd_hda_power_down(struct hda_codec *codec) -{ - struct device *dev = hda_codec_dev(codec); - - if (codec_in_pm(codec)) - return; - pm_runtime_mark_last_busy(dev); - pm_runtime_put_autosuspend(dev); -} -EXPORT_SYMBOL_GPL(snd_hda_power_down); - static void codec_set_power_save(struct hda_codec *codec, int delay) { struct device *dev = hda_codec_dev(codec); diff --git a/sound/pci/hda/hda_codec.h b/sound/pci/hda/hda_codec.h index 6efcb4ad6935..e7c47a439762 100644 --- a/sound/pci/hda/hda_codec.h +++ b/sound/pci/hda/hda_codec.h @@ -261,24 +261,10 @@ struct hda_codec { struct hda_bus *bus; struct snd_card *card; unsigned int addr; /* codec addr*/ - - hda_nid_t afg; /* AFG node id */ - hda_nid_t mfg; /* MFG node id */ - - /* ids */ - u8 afg_function_id; - u8 mfg_function_id; - u8 afg_unsol; - u8 mfg_unsol; - u32 vendor_id; - u32 subsystem_id; - u32 revision_id; u32 probe_id; /* overridden id for probing */ /* detected preset */ const struct hda_codec_preset *preset; - const char *vendor_name; /* codec vendor name */ - const char *chip_name; /* codec chip name */ const char *modelname; /* model name for preset */ /* set by patch */ @@ -295,8 +281,6 @@ struct hda_codec { unsigned int beep_mode; /* widget capabilities cache */ - unsigned int num_nodes; - hda_nid_t start_nid; u32 *wcaps; struct snd_array mixers; /* list of assigned mixer elements */ @@ -347,14 +331,11 @@ struct hda_codec { unsigned int inv_eapd:1; /* broken h/w: inverted EAPD control */ unsigned int inv_jack_detect:1; /* broken h/w: inverted detection bit */ unsigned int pcm_format_first:1; /* PCM format must be set first */ - unsigned int epss:1; /* supporting EPSS? */ unsigned int cached_write:1; /* write only to caches */ unsigned int dp_mst:1; /* support DP1.2 Multi-stream transport */ unsigned int dump_coef:1; /* dump processing coefs in codec proc file */ unsigned int power_save_node:1; /* advanced PM for each widget */ #ifdef CONFIG_PM - unsigned int d3_stop_clk:1; /* support D3 operation without BCLK */ - atomic_t in_pm; /* suspend/resume being performed */ unsigned long power_on_acct; unsigned long power_off_acct; unsigned long power_jiffies; @@ -395,11 +376,6 @@ struct hda_codec { #define list_for_each_codec(c, bus) \ list_for_each_entry(c, &(bus)->core.codec_list, core.list) -/* direction */ -enum { - HDA_INPUT, HDA_OUTPUT -}; - /* snd_hda_codec_read/write optional flags */ #define HDA_RW_NO_RESPONSE_FALLBACK (1 << 0) @@ -422,8 +398,8 @@ int snd_hda_codec_write(struct hda_codec *codec, hda_nid_t nid, int flags, unsigned int verb, unsigned int parm); #define snd_hda_param_read(codec, nid, param) \ snd_hda_codec_read(codec, nid, 0, AC_VERB_PARAMETERS, param) -int snd_hda_get_sub_nodes(struct hda_codec *codec, hda_nid_t nid, - hda_nid_t *start_id); +#define snd_hda_get_sub_nodes(codec, nid, start_nid) \ + snd_hdac_get_sub_nodes(&(codec)->core, nid, start_nid) int snd_hda_get_connections(struct hda_codec *codec, hda_nid_t nid, hda_nid_t *conn_list, int max_conns); static inline int @@ -431,9 +407,12 @@ snd_hda_get_num_conns(struct hda_codec *codec, hda_nid_t nid) { return snd_hda_get_connections(codec, nid, NULL, 0); } -int snd_hda_get_num_raw_conns(struct hda_codec *codec, hda_nid_t nid); -int snd_hda_get_raw_connections(struct hda_codec *codec, hda_nid_t nid, - hda_nid_t *conn_list, int max_conns); + +#define snd_hda_get_raw_connections(codec, nid, list, max_conns) \ + snd_hdac_get_connections(&(codec)->core, nid, list, max_conns) +#define snd_hda_get_num_raw_conns(codec, nid) \ + snd_hdac_get_connections(&(codec)->core, nid, NULL, 0); + int snd_hda_get_conn_list(struct hda_codec *codec, hda_nid_t nid, const hda_nid_t **listp); int snd_hda_override_conn_list(struct hda_codec *codec, hda_nid_t nid, int nums, @@ -582,14 +561,12 @@ const char *snd_hda_get_jack_location(u32 cfg); /* * power saving */ +#define snd_hda_power_up(codec) snd_hdac_power_up(&(codec)->core) +#define snd_hda_power_down(codec) snd_hdac_power_down(&(codec)->core) #ifdef CONFIG_PM -void snd_hda_power_up(struct hda_codec *codec); -void snd_hda_power_down(struct hda_codec *codec); void snd_hda_set_power_save(struct hda_bus *bus, int delay); void snd_hda_update_power_acct(struct hda_codec *codec); #else -static inline void snd_hda_power_up(struct hda_codec *codec) {} -static inline void snd_hda_power_down(struct hda_codec *codec) {} static inline void snd_hda_set_power_save(struct hda_bus *bus, int delay) {} #endif diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index 0ef2459cd05f..4850f92c89c4 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -654,7 +654,7 @@ static bool is_active_nid(struct hda_codec *codec, hda_nid_t nid, int type = get_wcaps_type(get_wcaps(codec, nid)); int i, n; - if (nid == codec->afg) + if (nid == codec->core.afg) return true; for (n = 0; n < spec->paths.used; n++) { @@ -832,7 +832,7 @@ static hda_nid_t path_power_update(struct hda_codec *codec, for (i = 0; i < path->depth; i++) { nid = path->path[i]; - if (nid == codec->afg) + if (nid == codec->core.afg) continue; if (!allow_powerdown || is_active_nid_for_any(codec, nid)) state = AC_PWRST_D0; @@ -1897,12 +1897,11 @@ static void debug_show_configs(struct hda_codec *codec, static void fill_all_dac_nids(struct hda_codec *codec) { struct hda_gen_spec *spec = codec->spec; - int i; - hda_nid_t nid = codec->start_nid; + hda_nid_t nid; spec->num_all_dacs = 0; memset(spec->all_dacs, 0, sizeof(spec->all_dacs)); - for (i = 0; i < codec->num_nodes; i++, nid++) { + for_each_hda_codec_node(nid, codec) { if (get_wcaps_type(get_wcaps(codec, nid)) != AC_WID_AUD_OUT) continue; if (spec->num_all_dacs >= ARRAY_SIZE(spec->all_dacs)) { @@ -3067,10 +3066,9 @@ static int fill_adc_nids(struct hda_codec *codec) hda_nid_t nid; hda_nid_t *adc_nids = spec->adc_nids; int max_nums = ARRAY_SIZE(spec->adc_nids); - int i, nums = 0; + int nums = 0; - nid = codec->start_nid; - for (i = 0; i < codec->num_nodes; i++, nid++) { + for_each_hda_codec_node(nid, codec) { unsigned int caps = get_wcaps(codec, nid); int type = get_wcaps_type(caps); @@ -3864,8 +3862,7 @@ static void parse_digital(struct hda_codec *codec) if (spec->autocfg.dig_in_pin) { pin = spec->autocfg.dig_in_pin; - dig_nid = codec->start_nid; - for (i = 0; i < codec->num_nodes; i++, dig_nid++) { + for_each_hda_codec_node(dig_nid, codec) { unsigned int wcaps = get_wcaps(codec, dig_nid); if (get_wcaps_type(wcaps) != AC_WID_AUD_IN) continue; @@ -4706,7 +4703,7 @@ unsigned int snd_hda_gen_path_power_filter(struct hda_codec *codec, hda_nid_t nid, unsigned int power_state) { - if (power_state != AC_PWRST_D0 || nid == codec->afg) + if (power_state != AC_PWRST_D0 || nid == codec->core.afg) return power_state; if (get_wcaps_type(get_wcaps(codec, nid)) >= AC_WID_POWER) return power_state; @@ -5478,7 +5475,7 @@ int snd_hda_gen_build_pcms(struct hda_codec *codec) fill_pcm_stream_name(spec->stream_name_analog, sizeof(spec->stream_name_analog), - " Analog", codec->chip_name); + " Analog", codec->core.chip_name); info = snd_hda_codec_pcm_new(codec, "%s", spec->stream_name_analog); if (!info) return -ENOMEM; @@ -5509,7 +5506,7 @@ int snd_hda_gen_build_pcms(struct hda_codec *codec) if (spec->multiout.dig_out_nid || spec->dig_in_nid) { fill_pcm_stream_name(spec->stream_name_digital, sizeof(spec->stream_name_digital), - " Digital", codec->chip_name); + " Digital", codec->core.chip_name); info = snd_hda_codec_pcm_new(codec, "%s", spec->stream_name_digital); if (!info) @@ -5544,7 +5541,7 @@ int snd_hda_gen_build_pcms(struct hda_codec *codec) if (spec->alt_dac_nid || have_multi_adcs) { fill_pcm_stream_name(spec->stream_name_alt_analog, sizeof(spec->stream_name_alt_analog), - " Alt Analog", codec->chip_name); + " Alt Analog", codec->core.chip_name); info = snd_hda_codec_pcm_new(codec, "%s", spec->stream_name_alt_analog); if (!info) diff --git a/sound/pci/hda/hda_local.h b/sound/pci/hda/hda_local.h index 1d001647fc47..e0db30c66e5f 100644 --- a/sound/pci/hda/hda_local.h +++ b/sound/pci/hda/hda_local.h @@ -515,15 +515,18 @@ int snd_hda_codec_get_pin_target(struct hda_codec *codec, hda_nid_t nid); int snd_hda_codec_set_pin_target(struct hda_codec *codec, hda_nid_t nid, unsigned int val); +#define for_each_hda_codec_node(nid, codec) \ + for ((nid) = (codec)->core.start_nid; (nid) < (codec)->core.end_nid; (nid)++) + /* * get widget capabilities */ static inline u32 get_wcaps(struct hda_codec *codec, hda_nid_t nid) { - if (nid < codec->start_nid || - nid >= codec->start_nid + codec->num_nodes) + if (nid < codec->core.start_nid || + nid >= codec->core.start_nid + codec->core.num_nodes) return 0; - return codec->wcaps[nid - codec->start_nid]; + return codec->wcaps[nid - codec->core.start_nid]; } /* get the widget type from widget capability bits */ @@ -547,9 +550,9 @@ static inline unsigned int get_wcaps_channels(u32 wcaps) static inline void snd_hda_override_wcaps(struct hda_codec *codec, hda_nid_t nid, u32 val) { - if (nid >= codec->start_nid && - nid < codec->start_nid + codec->num_nodes) - codec->wcaps[nid - codec->start_nid] = val; + if (nid >= codec->core.start_nid && + nid < codec->core.start_nid + codec->core.num_nodes) + codec->wcaps[nid - codec->core.start_nid] = val; } u32 query_amp_caps(struct hda_codec *codec, hda_nid_t nid, int direction); diff --git a/sound/pci/hda/hda_proc.c b/sound/pci/hda/hda_proc.c index dacfe74a2a1f..a4f5a30f1d41 100644 --- a/sound/pci/hda/hda_proc.c +++ b/sound/pci/hda/hda_proc.c @@ -289,7 +289,7 @@ static void print_pin_caps(struct snd_info_buffer *buffer, snd_iprintf(buffer, " Balanced"); if (caps & AC_PINCAP_HDMI) { /* Realtek uses this bit as a different meaning */ - if ((codec->vendor_id >> 16) == 0x10ec) + if ((codec->core.vendor_id >> 16) == 0x10ec) snd_iprintf(buffer, " R/L"); else { if (caps & AC_PINCAP_HBR) @@ -597,7 +597,7 @@ static void print_gpio(struct snd_info_buffer *buffer, struct hda_codec *codec, hda_nid_t nid) { unsigned int gpio = - snd_hda_param_read(codec, codec->afg, AC_PAR_GPIO_CAP); + snd_hda_param_read(codec, codec->core.afg, AC_PAR_GPIO_CAP); unsigned int enable, direction, wake, unsol, sticky, data; int i, max; snd_iprintf(buffer, "GPIO: io=%d, o=%d, i=%d, " @@ -667,13 +667,9 @@ static void print_device_list(struct snd_info_buffer *buffer, } } -static void print_codec_info(struct snd_info_entry *entry, - struct snd_info_buffer *buffer) +static void print_codec_core_info(struct hdac_device *codec, + struct snd_info_buffer *buffer) { - struct hda_codec *codec = entry->private_data; - hda_nid_t nid; - int i, nodes; - snd_iprintf(buffer, "Codec: "); if (codec->vendor_name && codec->chip_name) snd_iprintf(buffer, "%s %s\n", @@ -695,29 +691,39 @@ static void print_codec_info(struct snd_info_entry *entry, snd_iprintf(buffer, "Modem Function Group: 0x%x\n", codec->mfg); else snd_iprintf(buffer, "No Modem Function Group found\n"); +} + +static void print_codec_info(struct snd_info_entry *entry, + struct snd_info_buffer *buffer) +{ + struct hda_codec *codec = entry->private_data; + hda_nid_t nid, fg; + int i, nodes; - if (! codec->afg) + print_codec_core_info(&codec->core, buffer); + fg = codec->core.afg; + if (!fg) return; snd_hda_power_up(codec); snd_iprintf(buffer, "Default PCM:\n"); - print_pcm_caps(buffer, codec, codec->afg); + print_pcm_caps(buffer, codec, fg); snd_iprintf(buffer, "Default Amp-In caps: "); - print_amp_caps(buffer, codec, codec->afg, HDA_INPUT); + print_amp_caps(buffer, codec, fg, HDA_INPUT); snd_iprintf(buffer, "Default Amp-Out caps: "); - print_amp_caps(buffer, codec, codec->afg, HDA_OUTPUT); - snd_iprintf(buffer, "State of AFG node 0x%02x:\n", codec->afg); - print_power_state(buffer, codec, codec->afg); + print_amp_caps(buffer, codec, fg, HDA_OUTPUT); + snd_iprintf(buffer, "State of AFG node 0x%02x:\n", fg); + print_power_state(buffer, codec, fg); - nodes = snd_hda_get_sub_nodes(codec, codec->afg, &nid); + nodes = snd_hda_get_sub_nodes(codec, fg, &nid); if (! nid || nodes < 0) { snd_iprintf(buffer, "Invalid AFG subtree\n"); snd_hda_power_down(codec); return; } - print_gpio(buffer, codec, codec->afg); + print_gpio(buffer, codec, fg); if (codec->proc_widget_hook) - codec->proc_widget_hook(buffer, codec, codec->afg); + codec->proc_widget_hook(buffer, codec, fg); for (i = 0; i < nodes; i++, nid++) { unsigned int wid_caps = @@ -860,7 +866,7 @@ int snd_hda_codec_proc_new(struct hda_codec *codec) struct snd_info_entry *entry; int err; - snprintf(name, sizeof(name), "codec#%d", codec->addr); + snprintf(name, sizeof(name), "codec#%d", codec->core.addr); err = snd_card_proc_new(codec->card, name, &entry); if (err < 0) return err; diff --git a/sound/pci/hda/hda_sysfs.c b/sound/pci/hda/hda_sysfs.c index 3b5ed1108f9f..a6e3d9b511ab 100644 --- a/sound/pci/hda/hda_sysfs.c +++ b/sound/pci/hda/hda_sysfs.c @@ -48,33 +48,33 @@ static DEVICE_ATTR_RO(power_on_acct); static DEVICE_ATTR_RO(power_off_acct); #endif /* CONFIG_PM */ -#define CODEC_INFO_SHOW(type) \ +#define CODEC_INFO_SHOW(type, field) \ static ssize_t type##_show(struct device *dev, \ struct device_attribute *attr, \ char *buf) \ { \ struct hda_codec *codec = dev_get_drvdata(dev); \ - return sprintf(buf, "0x%x\n", codec->type); \ + return sprintf(buf, "0x%x\n", codec->field); \ } -#define CODEC_INFO_STR_SHOW(type) \ +#define CODEC_INFO_STR_SHOW(type, field) \ static ssize_t type##_show(struct device *dev, \ struct device_attribute *attr, \ char *buf) \ { \ struct hda_codec *codec = dev_get_drvdata(dev); \ return sprintf(buf, "%s\n", \ - codec->type ? codec->type : ""); \ + codec->field ? codec->field : ""); \ } -CODEC_INFO_SHOW(vendor_id); -CODEC_INFO_SHOW(subsystem_id); -CODEC_INFO_SHOW(revision_id); -CODEC_INFO_SHOW(afg); -CODEC_INFO_SHOW(mfg); -CODEC_INFO_STR_SHOW(vendor_name); -CODEC_INFO_STR_SHOW(chip_name); -CODEC_INFO_STR_SHOW(modelname); +CODEC_INFO_SHOW(vendor_id, core.vendor_id); +CODEC_INFO_SHOW(subsystem_id, core.subsystem_id); +CODEC_INFO_SHOW(revision_id, core.revision_id); +CODEC_INFO_SHOW(afg, core.afg); +CODEC_INFO_SHOW(mfg, core.mfg); +CODEC_INFO_STR_SHOW(vendor_name, core.vendor_name); +CODEC_INFO_STR_SHOW(chip_name, core.chip_name); +CODEC_INFO_STR_SHOW(modelname, modelname); static ssize_t pin_configs_show(struct hda_codec *codec, struct snd_array *list, @@ -170,7 +170,7 @@ static char *kstrndup_noeol(const char *src, size_t len) return s; } -#define CODEC_INFO_STORE(type) \ +#define CODEC_INFO_STORE(type, field) \ static ssize_t type##_store(struct device *dev, \ struct device_attribute *attr, \ const char *buf, size_t count) \ @@ -180,11 +180,11 @@ static ssize_t type##_store(struct device *dev, \ int err = kstrtoul(buf, 0, &val); \ if (err < 0) \ return err; \ - codec->type = val; \ + codec->field = val; \ return count; \ } -#define CODEC_INFO_STR_STORE(type) \ +#define CODEC_INFO_STR_STORE(type, field) \ static ssize_t type##_store(struct device *dev, \ struct device_attribute *attr, \ const char *buf, size_t count) \ @@ -193,17 +193,17 @@ static ssize_t type##_store(struct device *dev, \ char *s = kstrndup_noeol(buf, 64); \ if (!s) \ return -ENOMEM; \ - kfree(codec->type); \ - codec->type = s; \ + kfree(codec->field); \ + codec->field = s; \ return count; \ } -CODEC_INFO_STORE(vendor_id); -CODEC_INFO_STORE(subsystem_id); -CODEC_INFO_STORE(revision_id); -CODEC_INFO_STR_STORE(vendor_name); -CODEC_INFO_STR_STORE(chip_name); -CODEC_INFO_STR_STORE(modelname); +CODEC_INFO_STORE(vendor_id, core.vendor_id); +CODEC_INFO_STORE(subsystem_id, core.subsystem_id); +CODEC_INFO_STORE(revision_id, core.revision_id); +CODEC_INFO_STR_STORE(vendor_name, core.vendor_name); +CODEC_INFO_STR_STORE(chip_name, core.chip_name); +CODEC_INFO_STR_STORE(modelname, modelname); #define CODEC_ACTION_STORE(type) \ static ssize_t type##_store(struct device *dev, \ @@ -553,9 +553,9 @@ static void parse_codec_mode(char *buf, struct hda_bus *bus, *codecp = NULL; if (sscanf(buf, "%i %i %i", &vendorid, &subid, &caddr) == 3) { list_for_each_codec(codec, bus) { - if ((vendorid <= 0 || codec->vendor_id == vendorid) && - (subid <= 0 || codec->subsystem_id == subid) && - codec->addr == caddr) { + if ((vendorid <= 0 || codec->core.vendor_id == vendorid) && + (subid <= 0 || codec->core.subsystem_id == subid) && + codec->core.addr == caddr) { *codecp = codec; break; } @@ -595,8 +595,8 @@ static void parse_model_mode(char *buf, struct hda_bus *bus, static void parse_chip_name_mode(char *buf, struct hda_bus *bus, struct hda_codec **codecp) { - kfree((*codecp)->chip_name); - (*codecp)->chip_name = kstrdup(buf, GFP_KERNEL); + kfree((*codecp)->core.chip_name); + (*codecp)->core.chip_name = kstrdup(buf, GFP_KERNEL); } #define DEFINE_PARSE_ID_MODE(name) \ @@ -605,7 +605,7 @@ static void parse_##name##_mode(char *buf, struct hda_bus *bus, \ { \ unsigned long val; \ if (!kstrtoul(buf, 0, &val)) \ - (*codecp)->name = val; \ + (*codecp)->core.name = val; \ } DEFINE_PARSE_ID_MODE(vendor_id); diff --git a/sound/pci/hda/local.h b/sound/pci/hda/local.h new file mode 100644 index 000000000000..28cb7f98982e --- /dev/null +++ b/sound/pci/hda/local.h @@ -0,0 +1,39 @@ +/* + */ + +#ifndef __HDAC_LOCAL_H +#define __HDAC_LOCAL_H + +int hdac_read_parm(struct hdac_device *codec, hda_nid_t nid, int parm); + +#define get_wcaps(codec, nid) \ + hdac_read_parm(codec, nid, AC_PAR_AUDIO_WIDGET_CAP) +/* get the widget type from widget capability bits */ +static inline int get_wcaps_type(unsigned int wcaps) +{ + if (!wcaps) + return -1; /* invalid type */ + return (wcaps & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT; +} + +#define get_pin_caps(codec, nid) \ + hdac_read_parm(codec, nid, AC_PAR_PIN_CAP) + +static inline +unsigned int get_pin_cfg(struct hdac_device *codec, hda_nid_t nid) +{ + unsigned int val; + + if (snd_hdac_read(codec, nid, AC_VERB_GET_CONFIG_DEFAULT, 0, &val)) + return -1; + return val; +} + +#define get_amp_caps(codec, nid, dir) \ + hdac_read_parm(codec, nid, (dir) == HDA_OUTPUT ? \ + AC_PAR_AMP_OUT_CAP : AC_PAR_AMP_IN_CAP) + +#define get_power_caps(codec, nid) \ + hdac_read_parm(codec, nid, AC_PAR_POWER_STATE) + +#endif /* __HDAC_LOCAL_H */ diff --git a/sound/pci/hda/patch_analog.c b/sound/pci/hda/patch_analog.c index af4c7be86c27..2278e83234b5 100644 --- a/sound/pci/hda/patch_analog.c +++ b/sound/pci/hda/patch_analog.c @@ -99,7 +99,7 @@ static void ad198x_power_eapd_write(struct hda_codec *codec, hda_nid_t front, static void ad198x_power_eapd(struct hda_codec *codec) { /* We currently only handle front, HP */ - switch (codec->vendor_id) { + switch (codec->core.vendor_id) { case 0x11d41882: case 0x11d4882a: case 0x11d41884: diff --git a/sound/pci/hda/patch_ca0132.c b/sound/pci/hda/patch_ca0132.c index 72d20652df50..5aff35a09fd4 100644 --- a/sound/pci/hda/patch_ca0132.c +++ b/sound/pci/hda/patch_ca0132.c @@ -4243,13 +4243,9 @@ static void ca0132_refresh_widget_caps(struct hda_codec *codec) { struct ca0132_spec *spec = codec->spec; int i; - hda_nid_t nid; codec_dbg(codec, "ca0132_refresh_widget_caps.\n"); - nid = codec->start_nid; - for (i = 0; i < codec->num_nodes; i++, nid++) - codec->wcaps[i] = snd_hda_param_read(codec, nid, - AC_PAR_AUDIO_WIDGET_CAP); + snd_hda_codec_update_widgets(codec); for (i = 0; i < spec->multiout.num_dacs; i++) refresh_amp_caps(codec, spec->dacs[i], HDA_OUTPUT); diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c index 142a6cf786da..1e21f9fbd54b 100644 --- a/sound/pci/hda/patch_conexant.c +++ b/sound/pci/hda/patch_conexant.c @@ -103,10 +103,9 @@ static int add_beep_ctls(struct hda_codec *codec) static void cx_auto_parse_beep(struct hda_codec *codec) { struct conexant_spec *spec = codec->spec; - hda_nid_t nid, end_nid; + hda_nid_t nid; - end_nid = codec->start_nid + codec->num_nodes; - for (nid = codec->start_nid; nid < end_nid; nid++) + for_each_hda_codec_node(nid, codec) if (get_wcaps_type(get_wcaps(codec, nid)) == AC_WID_BEEP) { set_beep_amp(spec, nid, 0, HDA_OUTPUT); break; @@ -120,10 +119,9 @@ static void cx_auto_parse_beep(struct hda_codec *codec) static void cx_auto_parse_eapd(struct hda_codec *codec) { struct conexant_spec *spec = codec->spec; - hda_nid_t nid, end_nid; + hda_nid_t nid; - end_nid = codec->start_nid + codec->num_nodes; - for (nid = codec->start_nid; nid < end_nid; nid++) { + for_each_hda_codec_node(nid, codec) { if (get_wcaps_type(get_wcaps(codec, nid)) != AC_WID_PIN) continue; if (!(snd_hda_query_pin_caps(codec, nid) & AC_PINCAP_EAPD)) @@ -848,7 +846,7 @@ static int patch_conexant_auto(struct hda_codec *codec) struct conexant_spec *spec; int err; - codec_info(codec, "%s: BIOS auto-probing.\n", codec->chip_name); + codec_info(codec, "%s: BIOS auto-probing.\n", codec->core.chip_name); spec = kzalloc(sizeof(*spec), GFP_KERNEL); if (!spec) @@ -862,7 +860,7 @@ static int patch_conexant_auto(struct hda_codec *codec) if (spec->dynamic_eapd) spec->gen.vmaster_mute.hook = cx_auto_vmaster_hook; - switch (codec->vendor_id) { + switch (codec->core.vendor_id) { case 0x14f15045: codec->single_adc_amp = 1; spec->gen.mixer_nid = 0x17; @@ -896,7 +894,7 @@ static int patch_conexant_auto(struct hda_codec *codec) * others may use EAPD really as an amp switch, so it might be * not good to expose it blindly. */ - switch (codec->subsystem_id >> 16) { + switch (codec->core.subsystem_id >> 16) { case 0x103c: spec->gen.vmaster_mute_enum = 1; break; diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c index 7e9ff7b16e56..35d92a8a99ce 100644 --- a/sound/pci/hda/patch_hdmi.c +++ b/sound/pci/hda/patch_hdmi.c @@ -45,14 +45,14 @@ static bool static_hdmi_pcm; module_param(static_hdmi_pcm, bool, 0644); MODULE_PARM_DESC(static_hdmi_pcm, "Don't restrict PCM parameters per ELD info"); -#define is_haswell(codec) ((codec)->vendor_id == 0x80862807) -#define is_broadwell(codec) ((codec)->vendor_id == 0x80862808) -#define is_skylake(codec) ((codec)->vendor_id == 0x80862809) +#define is_haswell(codec) ((codec)->core.vendor_id == 0x80862807) +#define is_broadwell(codec) ((codec)->core.vendor_id == 0x80862808) +#define is_skylake(codec) ((codec)->core.vendor_id == 0x80862809) #define is_haswell_plus(codec) (is_haswell(codec) || is_broadwell(codec) \ || is_skylake(codec)) -#define is_valleyview(codec) ((codec)->vendor_id == 0x80862882) -#define is_cherryview(codec) ((codec)->vendor_id == 0x80862883) +#define is_valleyview(codec) ((codec)->core.vendor_id == 0x80862882) +#define is_cherryview(codec) ((codec)->core.vendor_id == 0x80862883) #define is_valleyview_plus(codec) (is_valleyview(codec) || is_cherryview(codec)) struct hdmi_spec_per_cvt { @@ -1391,13 +1391,12 @@ static void intel_not_share_assigned_cvt(struct hda_codec *codec, hda_nid_t pin_nid, int mux_idx) { struct hdmi_spec *spec = codec->spec; - hda_nid_t nid, end_nid; + hda_nid_t nid; int cvt_idx, curr; struct hdmi_spec_per_cvt *per_cvt; /* configure all pins, including "no physical connection" ones */ - end_nid = codec->start_nid + codec->num_nodes; - for (nid = codec->start_nid; nid < end_nid; nid++) { + for_each_hda_codec_node(nid, codec) { unsigned int wid_caps = get_wcaps(codec, nid); unsigned int wid_type = get_wcaps_type(wid_caps); @@ -1728,7 +1727,7 @@ static int hdmi_parse_codec(struct hda_codec *codec) hda_nid_t nid; int i, nodes; - nodes = snd_hda_get_sub_nodes(codec, codec->afg, &nid); + nodes = snd_hda_get_sub_nodes(codec, codec->core.afg, &nid); if (!nid || nodes < 0) { codec_warn(codec, "HDMI: failed to get afg sub nodes\n"); return -EINVAL; @@ -2928,7 +2927,8 @@ static int patch_nvhdmi(struct hda_codec *codec) */ #define is_amdhdmi_rev3_or_later(codec) \ - ((codec)->vendor_id == 0x1002aa01 && ((codec)->revision_id & 0xff00) >= 0x0300) + ((codec)->core.vendor_id == 0x1002aa01 && \ + ((codec)->core.revision_id & 0xff00) >= 0x0300) #define has_amd_full_remap_support(codec) is_amdhdmi_rev3_or_later(codec) /* ATI/AMD specific HDA pin verbs, see the AMD HDA Verbs specification */ diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 124eacf67fc4..eee4532ab5f6 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -299,7 +299,7 @@ static void alc_fill_eapd_coef(struct hda_codec *codec) coef = alc_get_coef0(codec); - switch (codec->vendor_id) { + switch (codec->core.vendor_id) { case 0x10ec0262: alc_update_coef_idx(codec, 0x7, 0, 1<<5); break; @@ -432,7 +432,7 @@ static void alc_auto_init_amp(struct hda_codec *codec, int type) snd_hda_sequence_write(codec, alc_gpio3_init_verbs); break; case ALC_INIT_DEFAULT: - switch (codec->vendor_id) { + switch (codec->core.vendor_id) { case 0x10ec0260: alc_update_coefex_idx(codec, 0x1a, 7, 0, 0x2010); break; @@ -498,18 +498,18 @@ static int alc_auto_parse_customize_define(struct hda_codec *codec) if (!codec->bus->pci) return -1; - ass = codec->subsystem_id & 0xffff; + ass = codec->core.subsystem_id & 0xffff; if (ass != codec->bus->pci->subsystem_device && (ass & 1)) goto do_sku; nid = 0x1d; - if (codec->vendor_id == 0x10ec0260) + if (codec->core.vendor_id == 0x10ec0260) nid = 0x17; ass = snd_hda_codec_get_pincfg(codec, nid); if (!(ass & 1)) { codec_info(codec, "%s: SKU not ready 0x%08x\n", - codec->chip_name, ass); + codec->core.chip_name, ass); return -1; } @@ -585,7 +585,7 @@ static int alc_subsystem_id(struct hda_codec *codec, const hda_nid_t *ports) goto do_sku; } - ass = codec->subsystem_id & 0xffff; + ass = codec->core.subsystem_id & 0xffff; if (codec->bus->pci && ass != codec->bus->pci->subsystem_device && (ass & 1)) goto do_sku; @@ -600,7 +600,7 @@ static int alc_subsystem_id(struct hda_codec *codec, const hda_nid_t *ports) * 0 : override */ nid = 0x1d; - if (codec->vendor_id == 0x10ec0260) + if (codec->core.vendor_id == 0x10ec0260) nid = 0x17; ass = snd_hda_codec_get_pincfg(codec, nid); codec_dbg(codec, @@ -621,7 +621,7 @@ static int alc_subsystem_id(struct hda_codec *codec, const hda_nid_t *ports) return 0; do_sku: codec_dbg(codec, "realtek: Enabling init ASM_ID=0x%04x CODEC_ID=%08x\n", - ass & 0xffff, codec->vendor_id); + ass & 0xffff, codec->core.vendor_id); /* * 0 : override * 1 : Swap Jack @@ -826,9 +826,9 @@ static const struct hda_codec_ops alc_patch_ops = { /* replace the codec chip_name with the given string */ static int alc_codec_rename(struct hda_codec *codec, const char *name) { - kfree(codec->chip_name); - codec->chip_name = kstrdup(name, GFP_KERNEL); - if (!codec->chip_name) { + kfree(codec->core.chip_name); + codec->core.chip_name = kstrdup(name, GFP_KERNEL); + if (!codec->core.chip_name) { alc_free(codec); return -ENOMEM; } @@ -904,7 +904,7 @@ static int alc_codec_rename_from_preset(struct hda_codec *codec) const struct alc_codec_rename_pci_table *q; for (p = rename_tbl; p->vendor_id; p++) { - if (p->vendor_id != codec->vendor_id) + if (p->vendor_id != codec->core.vendor_id) continue; if ((alc_get_coef0(codec) & p->coef_mask) == p->coef_bits) return alc_codec_rename(codec, p->name); @@ -913,7 +913,7 @@ static int alc_codec_rename_from_preset(struct hda_codec *codec) if (!codec->bus->pci) return 0; for (q = rename_pci_tbl; q->codec_vendor_id; q++) { - if (q->codec_vendor_id != codec->vendor_id) + if (q->codec_vendor_id != codec->core.vendor_id) continue; if (q->pci_subvendor != codec->bus->pci->subsystem_vendor) continue; @@ -1785,7 +1785,7 @@ static void alc882_gpio_mute(struct hda_codec *codec, int pin, int muted) { unsigned int gpiostate, gpiomask, gpiodir; - gpiostate = snd_hda_codec_read(codec, codec->afg, 0, + gpiostate = snd_hda_codec_read(codec, codec->core.afg, 0, AC_VERB_GET_GPIO_DATA, 0); if (!muted) @@ -1793,23 +1793,23 @@ static void alc882_gpio_mute(struct hda_codec *codec, int pin, int muted) else gpiostate &= ~(1 << pin); - gpiomask = snd_hda_codec_read(codec, codec->afg, 0, + gpiomask = snd_hda_codec_read(codec, codec->core.afg, 0, AC_VERB_GET_GPIO_MASK, 0); gpiomask |= (1 << pin); - gpiodir = snd_hda_codec_read(codec, codec->afg, 0, + gpiodir = snd_hda_codec_read(codec, codec->core.afg, 0, AC_VERB_GET_GPIO_DIRECTION, 0); gpiodir |= (1 << pin); - snd_hda_codec_write(codec, codec->afg, 0, + snd_hda_codec_write(codec, codec->core.afg, 0, AC_VERB_SET_GPIO_MASK, gpiomask); - snd_hda_codec_write(codec, codec->afg, 0, + snd_hda_codec_write(codec, codec->core.afg, 0, AC_VERB_SET_GPIO_DIRECTION, gpiodir); msleep(1); - snd_hda_codec_write(codec, codec->afg, 0, + snd_hda_codec_write(codec, codec->core.afg, 0, AC_VERB_SET_GPIO_DATA, gpiostate); } @@ -2269,7 +2269,7 @@ static int patch_alc882(struct hda_codec *codec) spec = codec->spec; - switch (codec->vendor_id) { + switch (codec->core.vendor_id) { case 0x10ec0882: case 0x10ec0885: case 0x10ec0900: @@ -3067,7 +3067,7 @@ static int alc269_resume(struct hda_codec *codec) * in the driver. */ if (spec->gpio_led) - snd_hda_codec_write(codec, codec->afg, 0, AC_VERB_SET_GPIO_DATA, + snd_hda_codec_write(codec, codec->core.afg, 0, AC_VERB_SET_GPIO_DATA, spec->gpio_led); if (spec->has_alc5505_dsp) @@ -3112,8 +3112,8 @@ static void alc271_fixup_dmic(struct hda_codec *codec, }; unsigned int cfg; - if (strcmp(codec->chip_name, "ALC271X") && - strcmp(codec->chip_name, "ALC269VB")) + if (strcmp(codec->core.chip_name, "ALC271X") && + strcmp(codec->core.chip_name, "ALC269VB")) return; cfg = snd_hda_codec_get_pincfg(codec, 0x12); if (get_defcfg_connect(cfg) == AC_JACK_PORT_FIXED) @@ -3479,9 +3479,9 @@ static void alc280_fixup_hp_gpio2_mic_hotkey(struct hda_codec *codec, } snd_hda_add_verbs(codec, gpio_init); - snd_hda_codec_write_cache(codec, codec->afg, 0, + snd_hda_codec_write_cache(codec, codec->core.afg, 0, AC_VERB_SET_GPIO_UNSOLICITED_RSP_MASK, 0x04); - snd_hda_jack_detect_enable_callback(codec, codec->afg, + snd_hda_jack_detect_enable_callback(codec, codec->core.afg, gpio2_mic_hotkey_event); spec->gen.vmaster_mute.hook = alc_fixup_gpio_mute_hook; @@ -3564,7 +3564,7 @@ static void alc_headset_mode_unplugged(struct hda_codec *codec) {} }; - switch (codec->vendor_id) { + switch (codec->core.vendor_id) { case 0x10ec0255: alc_process_coef_fw(codec, coef0255); break; @@ -3619,7 +3619,7 @@ static void alc_headset_mode_mic_in(struct hda_codec *codec, hda_nid_t hp_pin, {} }; - switch (codec->vendor_id) { + switch (codec->core.vendor_id) { case 0x10ec0255: alc_write_coef_idx(codec, 0x45, 0xc489); snd_hda_set_pin_ctl_cache(codec, hp_pin, 0); @@ -3688,7 +3688,7 @@ static void alc_headset_mode_default(struct hda_codec *codec) {} }; - switch (codec->vendor_id) { + switch (codec->core.vendor_id) { case 0x10ec0255: alc_process_coef_fw(codec, coef0255); break; @@ -3742,7 +3742,7 @@ static void alc_headset_mode_ctia(struct hda_codec *codec) {} }; - switch (codec->vendor_id) { + switch (codec->core.vendor_id) { case 0x10ec0255: alc_process_coef_fw(codec, coef0255); break; @@ -3796,7 +3796,7 @@ static void alc_headset_mode_omtp(struct hda_codec *codec) {} }; - switch (codec->vendor_id) { + switch (codec->core.vendor_id) { case 0x10ec0255: alc_process_coef_fw(codec, coef0255); break; @@ -3841,7 +3841,7 @@ static void alc_determine_headset_type(struct hda_codec *codec) {} }; - switch (codec->vendor_id) { + switch (codec->core.vendor_id) { case 0x10ec0255: alc_process_coef_fw(codec, coef0255); msleep(300); @@ -4078,7 +4078,7 @@ static unsigned int alc_power_filter_xps13(struct hda_codec *codec, /* Avoid pop noises when headphones are plugged in */ if (spec->gen.hp_jack_present) - if (nid == codec->afg || nid == 0x02 || nid == 0x15) + if (nid == codec->core.afg || nid == 0x02 || nid == 0x15) return AC_PWRST_D0; return power_state; } @@ -5428,7 +5428,7 @@ static int patch_alc269(struct hda_codec *codec) if (has_cdefine_beep(codec)) spec->gen.beep_nid = 0x01; - switch (codec->vendor_id) { + switch (codec->core.vendor_id) { case 0x10ec0269: spec->codec_variant = ALC269_TYPE_ALC269VA; switch (alc_get_coef0(codec) & 0x00f0) { @@ -5772,9 +5772,9 @@ static int alc662_parse_auto_config(struct hda_codec *codec) static const hda_nid_t alc662_ssids[] = { 0x15, 0x1b, 0x14, 0 }; const hda_nid_t *ssids; - if (codec->vendor_id == 0x10ec0272 || codec->vendor_id == 0x10ec0663 || - codec->vendor_id == 0x10ec0665 || codec->vendor_id == 0x10ec0670 || - codec->vendor_id == 0x10ec0671) + if (codec->core.vendor_id == 0x10ec0272 || codec->core.vendor_id == 0x10ec0663 || + codec->core.vendor_id == 0x10ec0665 || codec->core.vendor_id == 0x10ec0670 || + codec->core.vendor_id == 0x10ec0671) ssids = alc663_ssids; else ssids = alc662_ssids; @@ -5819,7 +5819,7 @@ static unsigned int gpio_led_power_filter(struct hda_codec *codec, unsigned int power_state) { struct alc_spec *spec = codec->spec; - if (nid == codec->afg && power_state == AC_PWRST_D3 && spec->gpio_led) + if (nid == codec->core.afg && power_state == AC_PWRST_D3 && spec->gpio_led) return AC_PWRST_D0; return power_state; } @@ -6317,7 +6317,7 @@ static int patch_alc662(struct hda_codec *codec) alc_fix_pll_init(codec, 0x20, 0x04, 15); - switch (codec->vendor_id) { + switch (codec->core.vendor_id) { case 0x10ec0668: spec->init_hook = alc668_restore_default_value; break; @@ -6347,7 +6347,7 @@ static int patch_alc662(struct hda_codec *codec) goto error; if (!spec->gen.no_analog && spec->gen.beep_nid) { - switch (codec->vendor_id) { + switch (codec->core.vendor_id) { case 0x10ec0662: set_beep_amp(spec, 0x0b, 0x05, HDA_INPUT); break; diff --git a/sound/pci/hda/patch_si3054.c b/sound/pci/hda/patch_si3054.c index df243134baa8..49b4868797a5 100644 --- a/sound/pci/hda/patch_si3054.c +++ b/sound/pci/hda/patch_si3054.c @@ -205,8 +205,8 @@ static int si3054_build_pcms(struct hda_codec *codec) return -ENOMEM; info->stream[SNDRV_PCM_STREAM_PLAYBACK] = si3054_pcm; info->stream[SNDRV_PCM_STREAM_CAPTURE] = si3054_pcm; - info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = codec->mfg; - info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = codec->mfg; + info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = codec->core.mfg; + info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = codec->core.mfg; info->pcm_type = HDA_PCM_TYPE_MODEM; return 0; } @@ -223,7 +223,7 @@ static int si3054_init(struct hda_codec *codec) u16 val; snd_hda_codec_write(codec, AC_NODE_ROOT, 0, AC_VERB_SET_CODEC_RESET, 0); - snd_hda_codec_write(codec, codec->mfg, 0, AC_VERB_SET_STREAM_FORMAT, 0); + snd_hda_codec_write(codec, codec->core.mfg, 0, AC_VERB_SET_STREAM_FORMAT, 0); SET_REG(codec, SI3054_LINE_RATE, 9600); SET_REG(codec, SI3054_LINE_LEVEL, SI3054_DTAG_MASK|SI3054_ATAG_MASK); SET_REG(codec, SI3054_EXTENDED_MID, 0); diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c index 5b7c173adcb8..b314551749f1 100644 --- a/sound/pci/hda/patch_sigmatel.c +++ b/sound/pci/hda/patch_sigmatel.c @@ -299,32 +299,33 @@ static void stac_gpio_set(struct hda_codec *codec, unsigned int mask, unsigned int dir_mask, unsigned int data) { unsigned int gpiostate, gpiomask, gpiodir; + hda_nid_t fg = codec->core.afg; codec_dbg(codec, "%s msk %x dir %x gpio %x\n", __func__, mask, dir_mask, data); - gpiostate = snd_hda_codec_read(codec, codec->afg, 0, + gpiostate = snd_hda_codec_read(codec, fg, 0, AC_VERB_GET_GPIO_DATA, 0); gpiostate = (gpiostate & ~dir_mask) | (data & dir_mask); - gpiomask = snd_hda_codec_read(codec, codec->afg, 0, + gpiomask = snd_hda_codec_read(codec, fg, 0, AC_VERB_GET_GPIO_MASK, 0); gpiomask |= mask; - gpiodir = snd_hda_codec_read(codec, codec->afg, 0, + gpiodir = snd_hda_codec_read(codec, fg, 0, AC_VERB_GET_GPIO_DIRECTION, 0); gpiodir |= dir_mask; /* Configure GPIOx as CMOS */ - snd_hda_codec_write(codec, codec->afg, 0, 0x7e7, 0); + snd_hda_codec_write(codec, fg, 0, 0x7e7, 0); - snd_hda_codec_write(codec, codec->afg, 0, + snd_hda_codec_write(codec, fg, 0, AC_VERB_SET_GPIO_MASK, gpiomask); - snd_hda_codec_read(codec, codec->afg, 0, + snd_hda_codec_read(codec, fg, 0, AC_VERB_SET_GPIO_DIRECTION, gpiodir); /* sync */ msleep(1); - snd_hda_codec_read(codec, codec->afg, 0, + snd_hda_codec_read(codec, fg, 0, AC_VERB_SET_GPIO_DATA, gpiostate); /* sync */ } @@ -387,7 +388,7 @@ static unsigned int stac_vref_led_power_filter(struct hda_codec *codec, hda_nid_t nid, unsigned int power_state) { - if (nid == codec->afg && power_state == AC_PWRST_D3) + if (nid == codec->core.afg && power_state == AC_PWRST_D3) return AC_PWRST_D1; return snd_hda_gen_path_power_filter(codec, nid, power_state); } @@ -432,7 +433,7 @@ static void stac_update_outputs(struct hda_codec *codec) if (spec->gpio_mute) spec->gen.master_mute = - !(snd_hda_codec_read(codec, codec->afg, 0, + !(snd_hda_codec_read(codec, codec->core.afg, 0, AC_VERB_GET_GPIO_DATA, 0) & spec->gpio_mute); snd_hda_gen_update_outputs(codec); @@ -476,7 +477,7 @@ static void stac_toggle_power_map(struct hda_codec *codec, hda_nid_t nid, if (val != spec->power_map_bits) { spec->power_map_bits = val; if (do_write) - snd_hda_codec_write(codec, codec->afg, 0, + snd_hda_codec_write(codec, codec->core.afg, 0, AC_VERB_IDT_SET_POWER_MAP, val); } } @@ -508,7 +509,8 @@ static void jack_update_power(struct hda_codec *codec, false); } - snd_hda_codec_write(codec, codec->afg, 0, AC_VERB_IDT_SET_POWER_MAP, + snd_hda_codec_write(codec, codec->core.afg, 0, + AC_VERB_IDT_SET_POWER_MAP, spec->power_map_bits); } @@ -517,10 +519,10 @@ static void stac_vref_event(struct hda_codec *codec, { unsigned int data; - data = snd_hda_codec_read(codec, codec->afg, 0, + data = snd_hda_codec_read(codec, codec->core.afg, 0, AC_VERB_GET_GPIO_DATA, 0); /* toggle VREF state based on GPIOx status */ - snd_hda_codec_write(codec, codec->afg, 0, 0x7e0, + snd_hda_codec_write(codec, codec->core.afg, 0, 0x7e0, !!(data & (1 << event->private_data))); } @@ -622,7 +624,7 @@ static int stac_aloopback_put(struct snd_kcontrol *kcontrol, /* Only return the bits defined by the shift value of the * first two bytes of the mask */ - dac_mode = snd_hda_codec_read(codec, codec->afg, 0, + dac_mode = snd_hda_codec_read(codec, codec->core.afg, 0, kcontrol->private_value & 0xFFFF, 0x0); dac_mode >>= spec->aloopback_shift; @@ -634,7 +636,7 @@ static int stac_aloopback_put(struct snd_kcontrol *kcontrol, dac_mode &= ~idx_val; } - snd_hda_codec_write_cache(codec, codec->afg, 0, + snd_hda_codec_write_cache(codec, codec->core.afg, 0, kcontrol->private_value >> 16, dac_mode); return 1; @@ -658,11 +660,11 @@ static int stac_aloopback_put(struct snd_kcontrol *kcontrol, /* check whether it's a HP laptop with a docking port */ static bool hp_bnb2011_with_dock(struct hda_codec *codec) { - if (codec->vendor_id != 0x111d7605 && - codec->vendor_id != 0x111d76d1) + if (codec->core.vendor_id != 0x111d7605 && + codec->core.vendor_id != 0x111d76d1) return false; - switch (codec->subsystem_id) { + switch (codec->core.subsystem_id) { case 0x103c1618: case 0x103c1619: case 0x103c161a: @@ -733,7 +735,7 @@ static void set_hp_led_gpio(struct hda_codec *codec) if (spec->gpio_led) return; - gpio = snd_hda_param_read(codec, codec->afg, AC_PAR_GPIO_CAP); + gpio = snd_hda_param_read(codec, codec->core.afg, AC_PAR_GPIO_CAP); gpio &= AC_GPIO_IO_COUNT; if (gpio > 3) spec->gpio_led = 0x08; /* GPIO 3 */ @@ -777,7 +779,7 @@ static int find_mute_led_cfg(struct hda_codec *codec, int default_polarity) &spec->gpio_led_polarity, &spec->gpio_led) == 2) { unsigned int max_gpio; - max_gpio = snd_hda_param_read(codec, codec->afg, + max_gpio = snd_hda_param_read(codec, codec->core.afg, AC_PAR_GPIO_CAP); max_gpio &= AC_GPIO_IO_COUNT; if (spec->gpio_led < max_gpio) @@ -807,7 +809,7 @@ static int find_mute_led_cfg(struct hda_codec *codec, int default_polarity) * we statically set the GPIO - if not a B-series system * and default polarity is provided */ - if (!hp_blike_system(codec->subsystem_id) && + if (!hp_blike_system(codec->core.subsystem_id) && (default_polarity == 0 || default_polarity == 1)) { set_hp_led_gpio(codec); spec->gpio_led_polarity = default_polarity; @@ -2134,7 +2136,7 @@ static void stac92hd83xxx_fixup_hp_mic_led(struct hda_codec *codec, spec->mic_mute_led_gpio = 0x08; /* GPIO3 */ #ifdef CONFIG_PM /* resetting controller clears GPIO, so we need to keep on */ - codec->d3_stop_clk = 0; + codec->core.power_caps &= ~AC_PWRST_CLKSTOP; #endif } } @@ -3031,9 +3033,9 @@ static void stac92hd71bxx_fixup_hp_m4(struct hda_codec *codec, return; /* Enable VREF power saving on GPIO1 detect */ - snd_hda_codec_write_cache(codec, codec->afg, 0, + snd_hda_codec_write_cache(codec, codec->core.afg, 0, AC_VERB_SET_GPIO_UNSOLICITED_RSP_MASK, 0x02); - jack = snd_hda_jack_detect_enable_callback(codec, codec->afg, + jack = snd_hda_jack_detect_enable_callback(codec, codec->core.afg, stac_vref_event); if (!IS_ERR(jack)) jack->private_data = 0x02; @@ -3093,7 +3095,7 @@ static void stac92hd71bxx_fixup_hp(struct hda_codec *codec, if (action != HDA_FIXUP_ACT_PRE_PROBE) return; - if (hp_blike_system(codec->subsystem_id)) { + if (hp_blike_system(codec->core.subsystem_id)) { unsigned int pin_cfg = snd_hda_codec_get_pincfg(codec, 0x0f); if (get_defcfg_device(pin_cfg) == AC_JACK_LINE_OUT || get_defcfg_device(pin_cfg) == AC_JACK_SPEAKER || @@ -3792,7 +3794,7 @@ static void stac927x_fixup_dell_dmic(struct hda_codec *codec, if (action != HDA_FIXUP_ACT_PRE_PROBE) return; - if (codec->subsystem_id != 0x1028022f) { + if (codec->core.subsystem_id != 0x1028022f) { /* GPIO2 High = Enable EAPD */ spec->eapd_mask = spec->gpio_mask = 0x04; spec->gpio_dir = spec->gpio_data = 0x04; @@ -4053,9 +4055,9 @@ static void stac9205_fixup_dell_m43(struct hda_codec *codec, snd_hda_apply_pincfgs(codec, dell_9205_m43_pin_configs); /* Enable unsol response for GPIO4/Dock HP connection */ - snd_hda_codec_write_cache(codec, codec->afg, 0, + snd_hda_codec_write_cache(codec, codec->core.afg, 0, AC_VERB_SET_GPIO_UNSOLICITED_RSP_MASK, 0x10); - jack = snd_hda_jack_detect_enable_callback(codec, codec->afg, + jack = snd_hda_jack_detect_enable_callback(codec, codec->core.afg, stac_vref_event); if (!IS_ERR(jack)) jack->private_data = 0x01; @@ -4302,7 +4304,7 @@ static int stac_init(struct hda_codec *codec) /* sync the power-map */ if (spec->num_pwrs) - snd_hda_codec_write(codec, codec->afg, 0, + snd_hda_codec_write(codec, codec->core.afg, 0, AC_VERB_IDT_SET_POWER_MAP, spec->power_map_bits); @@ -4338,7 +4340,7 @@ static void stac_shutup(struct hda_codec *codec) static void stac92hd_proc_hook(struct snd_info_buffer *buffer, struct hda_codec *codec, hda_nid_t nid) { - if (nid == codec->afg) + if (nid == codec->core.afg) snd_iprintf(buffer, "Power-Map: 0x%02x\n", snd_hda_codec_read(codec, nid, 0, AC_VERB_IDT_GET_POWER_MAP, 0)); @@ -4349,7 +4351,7 @@ static void analog_loop_proc_hook(struct snd_info_buffer *buffer, unsigned int verb) { snd_iprintf(buffer, "Analog Loopback: 0x%02x\n", - snd_hda_codec_read(codec, codec->afg, 0, verb, 0)); + snd_hda_codec_read(codec, codec->core.afg, 0, verb, 0)); } /* stac92hd71bxx, stac92hd73xx */ @@ -4357,21 +4359,21 @@ static void stac92hd7x_proc_hook(struct snd_info_buffer *buffer, struct hda_codec *codec, hda_nid_t nid) { stac92hd_proc_hook(buffer, codec, nid); - if (nid == codec->afg) + if (nid == codec->core.afg) analog_loop_proc_hook(buffer, codec, 0xfa0); } static void stac9205_proc_hook(struct snd_info_buffer *buffer, struct hda_codec *codec, hda_nid_t nid) { - if (nid == codec->afg) + if (nid == codec->core.afg) analog_loop_proc_hook(buffer, codec, 0xfe0); } static void stac927x_proc_hook(struct snd_info_buffer *buffer, struct hda_codec *codec, hda_nid_t nid) { - if (nid == codec->afg) + if (nid == codec->core.afg) analog_loop_proc_hook(buffer, codec, 0xfeb); } #else @@ -4597,7 +4599,8 @@ static int patch_stac92hd83xxx(struct hda_codec *codec) if (err < 0) return err; - codec->epss = 0; /* longer delay needed for D3 */ + /* longer delay needed for D3 */ + codec->core.power_caps &= ~AC_PWRST_EPSS; spec = codec->spec; codec->power_save_node = 1; @@ -4647,7 +4650,8 @@ static int patch_stac92hd95(struct hda_codec *codec) if (err < 0) return err; - codec->epss = 0; /* longer delay needed for D3 */ + /* longer delay needed for D3 */ + codec->core.power_caps &= ~AC_PWRST_EPSS; spec = codec->spec; codec->power_save_node = 1; @@ -4706,14 +4710,14 @@ static int patch_stac92hd71bxx(struct hda_codec *codec) spec->gpio_dir = 0x01; spec->gpio_data = 0x01; - switch (codec->vendor_id) { + switch (codec->core.vendor_id) { case 0x111d76b6: /* 4 Port without Analog Mixer */ case 0x111d76b7: unmute_init++; break; case 0x111d7608: /* 5 Port with Analog Mixer */ - if ((codec->revision_id & 0xf) == 0 || - (codec->revision_id & 0xf) == 1) + if ((codec->core.revision_id & 0xf) == 0 || + (codec->core.revision_id & 0xf) == 1) spec->stream_delay = 40; /* 40 milliseconds */ /* disable VSW */ @@ -4722,7 +4726,7 @@ static int patch_stac92hd71bxx(struct hda_codec *codec) snd_hda_codec_set_pincfg(codec, 0x19, 0x40f000f3); break; case 0x111d7603: /* 6 Port with Analog Mixer */ - if ((codec->revision_id & 0xf) == 1) + if ((codec->core.revision_id & 0xf) == 1) spec->stream_delay = 40; /* 40 milliseconds */ break; diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c index 485663bb9101..a34d7671937f 100644 --- a/sound/pci/hda/patch_via.c +++ b/sound/pci/hda/patch_via.c @@ -140,7 +140,7 @@ static struct via_spec *via_new_spec(struct hda_codec *codec) static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec) { - u32 vendor_id = codec->vendor_id; + u32 vendor_id = codec->core.vendor_id; u16 ven_id = vendor_id >> 16; u16 dev_id = vendor_id & 0xffff; enum VIA_HDA_CODEC codec_type; @@ -335,7 +335,7 @@ static void __analog_low_current_mode(struct hda_codec *codec, bool force) return; /* other codecs are not supported */ } /* send verb */ - snd_hda_codec_write(codec, codec->afg, 0, verb, parm); + snd_hda_codec_write(codec, codec->core.afg, 0, verb, parm); } static void analog_low_current_mode(struct hda_codec *codec) @@ -558,7 +558,7 @@ static int vt1708_build_pcms(struct hda_codec *codec) int i, err; err = snd_hda_gen_build_pcms(codec); - if (err < 0 || codec->vendor_id != 0x11061708) + if (err < 0 || codec->core.vendor_id != 0x11061708) return err; /* We got noisy outputs on the right channel on VT1708 when @@ -714,19 +714,19 @@ static int patch_vt1708S(struct hda_codec *codec) /* correct names for VT1708BCE */ if (get_codec_type(codec) == VT1708BCE) { - kfree(codec->chip_name); - codec->chip_name = kstrdup("VT1708BCE", GFP_KERNEL); + kfree(codec->core.chip_name); + codec->core.chip_name = kstrdup("VT1708BCE", GFP_KERNEL); snprintf(codec->card->mixername, sizeof(codec->card->mixername), - "%s %s", codec->vendor_name, codec->chip_name); + "%s %s", codec->core.vendor_name, codec->core.chip_name); } /* correct names for VT1705 */ - if (codec->vendor_id == 0x11064397) { - kfree(codec->chip_name); - codec->chip_name = kstrdup("VT1705", GFP_KERNEL); + if (codec->core.vendor_id == 0x11064397) { + kfree(codec->core.chip_name); + codec->core.chip_name = kstrdup("VT1705", GFP_KERNEL); snprintf(codec->card->mixername, sizeof(codec->card->mixername), - "%s %s", codec->vendor_name, codec->chip_name); + "%s %s", codec->core.vendor_name, codec->core.chip_name); } /* automatic parse from the BIOS config */ @@ -815,8 +815,7 @@ static int add_secret_dac_path(struct hda_codec *codec) } /* find the primary DAC and add to the connection list */ - nid = codec->start_nid; - for (i = 0; i < codec->num_nodes; i++, nid++) { + for_each_hda_codec_node(nid, codec) { unsigned int caps = get_wcaps(codec, nid); if (get_wcaps_type(caps) == AC_WID_AUD_OUT && !(caps & AC_WCAP_DIGITAL)) { diff --git a/sound/pci/hda/thinkpad_helper.c b/sound/pci/hda/thinkpad_helper.c index 6ba0b5517c40..0a4ad5feb82e 100644 --- a/sound/pci/hda/thinkpad_helper.c +++ b/sound/pci/hda/thinkpad_helper.c @@ -21,7 +21,7 @@ static acpi_status acpi_check_cb(acpi_handle handle, u32 lvl, void *context, static bool is_thinkpad(struct hda_codec *codec) { bool found = false; - if (codec->subsystem_id >> 16 != 0x17aa) + if (codec->core.subsystem_id >> 16 != 0x17aa) return false; if (ACPI_SUCCESS(acpi_get_devices("LEN0068", acpi_check_cb, &found, NULL)) && found) return true; -- cgit v1.2.3 From 3256be6537751f65c76b3ecfbb4e667f87525a2f Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 24 Feb 2015 14:59:42 +0100 Subject: ALSA: hda - Add widget sysfs tree This patch changes the sysfs files assigned to the codec device on the bus which were formerly identical with hwdep sysfs files. Now it shows only a few core parameter, vendor_id, subsystem_id, revision_id, afg, mfg, vendor_name and chip_name. In addition, now a widget tree is added to the bus device sysfs directory for showing the widget topology and attributes. It's just a flat tree consisting of subdirectories named as the widget NID including various attributes like widget capability bits. The AFG (usually NID 0x01) is always found there, and it contains always amp_in_caps, amp_out_caps and power_caps files. Each of these attributes show a single value. The rest are the widget nodes belonging to that AFG. Note that the child node might not start from 0x02 but from another value like 0x0a. Each child node may contain caps, pin_caps, amp_in_caps, amp_out_caps, power_caps and connections files. The caps (representing the widget capability bits) always contain a value. The rest may contain value(s) if the attribute exists on the node. Only connections file show multiple values while other attributes have zero or one single value. An example of ls -R output is like below: % ls -R /sys/bus/hdaudio/devices/hdaudioC0D0/ /sys/bus/hdaudio/devices/hdaudioC0D0/widgets/: 01/ 04/ 07/ 0a/ 0d/ 10/ 13/ 16/ 19/ 1c/ 1f/ 22/ 02/ 05/ 08/ 0b/ 0e/ 11/ 14/ 17/ 1a/ 1d/ 20/ 23/ 03/ 06/ 09/ 0c/ 0f/ 12/ 15/ 18/ 1b/ 1e/ 21/ /sys/bus/hdaudio/devices/hdaudioC0D0/widgets/01: amp_in_caps amp_out_caps power_caps /sys/bus/hdaudio/devices/hdaudioC0D0/widgets/02: amp_in_caps amp_out_caps caps connections pin_caps pin_cfg power_caps /sys/bus/hdaudio/devices/hdaudioC0D0/widgets/03: ..... Signed-off-by: Takashi Iwai --- include/sound/hdaudio.h | 6 + sound/hda/Makefile | 2 +- sound/hda/hdac_device.c | 35 ++++ sound/hda/hdac_sysfs.c | 404 ++++++++++++++++++++++++++++++++++++++++++++++ sound/hda/local.h | 4 + sound/pci/hda/hda_bind.c | 4 +- sound/pci/hda/hda_codec.c | 6 +- 7 files changed, 454 insertions(+), 7 deletions(-) create mode 100644 sound/hda/hdac_sysfs.c (limited to 'include') diff --git a/include/sound/hdaudio.h b/include/sound/hdaudio.h index b81b4bec6f05..6ed2b421e29e 100644 --- a/include/sound/hdaudio.h +++ b/include/sound/hdaudio.h @@ -14,6 +14,7 @@ typedef u16 hda_nid_t; struct hdac_bus; struct hdac_device; struct hdac_driver; +struct hdac_widget_tree; /* * exported bus type @@ -53,6 +54,9 @@ struct hdac_device { /* misc flags */ atomic_t in_pm; /* suspend/resume being performed */ + + /* sysfs */ + struct hdac_widget_tree *widgets; }; /* device/driver type used for matching */ @@ -71,6 +75,8 @@ enum { int snd_hdac_device_init(struct hdac_device *dev, struct hdac_bus *bus, const char *name, unsigned int addr); void snd_hdac_device_exit(struct hdac_device *dev); +int snd_hdac_device_register(struct hdac_device *codec); +void snd_hdac_device_unregister(struct hdac_device *codec); int snd_hdac_refresh_widgets(struct hdac_device *codec); diff --git a/sound/hda/Makefile b/sound/hda/Makefile index 3c7625e595cf..ae8b5128b5c3 100644 --- a/sound/hda/Makefile +++ b/sound/hda/Makefile @@ -1,3 +1,3 @@ -snd-hda-core-objs := hda_bus_type.o hdac_bus.o hdac_device.o +snd-hda-core-objs := hda_bus_type.o hdac_bus.o hdac_device.o hdac_sysfs.o obj-$(CONFIG_SND_HDA_CORE) += snd-hda-core.o diff --git a/sound/hda/hdac_device.c b/sound/hda/hdac_device.c index a3f52ad4de37..1470ecc354db 100644 --- a/sound/hda/hdac_device.c +++ b/sound/hda/hdac_device.c @@ -45,6 +45,7 @@ int snd_hdac_device_init(struct hdac_device *codec, struct hdac_bus *bus, dev->parent = bus->dev; dev->bus = &snd_hda_bus_type; dev->release = default_release; + dev->groups = hdac_dev_attr_groups; dev_set_name(dev, "%s", name); device_enable_async_suspend(dev); @@ -127,6 +128,40 @@ void snd_hdac_device_exit(struct hdac_device *codec) } EXPORT_SYMBOL_GPL(snd_hdac_device_exit); +/** + * snd_hdac_device_register - register the hd-audio codec base device + * codec: the device to register + */ +int snd_hdac_device_register(struct hdac_device *codec) +{ + int err; + + err = device_add(&codec->dev); + if (err < 0) + return err; + err = hda_widget_sysfs_init(codec); + if (err < 0) { + device_del(&codec->dev); + return err; + } + + return 0; +} +EXPORT_SYMBOL_GPL(snd_hdac_device_register); + +/** + * snd_hdac_device_unregister - unregister the hd-audio codec base device + * codec: the device to unregister + */ +void snd_hdac_device_unregister(struct hdac_device *codec) +{ + if (device_is_registered(&codec->dev)) { + hda_widget_sysfs_exit(codec); + device_del(&codec->dev); + } +} +EXPORT_SYMBOL_GPL(snd_hdac_device_unregister); + /** * snd_hdac_make_cmd - compose a 32bit command word to be sent to the * HD-audio controller diff --git a/sound/hda/hdac_sysfs.c b/sound/hda/hdac_sysfs.c new file mode 100644 index 000000000000..b358d5157802 --- /dev/null +++ b/sound/hda/hdac_sysfs.c @@ -0,0 +1,404 @@ +/* + * sysfs support for HD-audio core device + */ + +#include +#include +#include +#include +#include +#include "local.h" + +struct hdac_widget_tree { + struct kobject *root; + struct kobject *afg; + struct kobject **nodes; +}; + +#define CODEC_ATTR(type) \ +static ssize_t type##_show(struct device *dev, \ + struct device_attribute *attr, \ + char *buf) \ +{ \ + struct hdac_device *codec = dev_to_hdac_dev(dev); \ + return sprintf(buf, "0x%x\n", codec->type); \ +} \ +static DEVICE_ATTR_RO(type) + +#define CODEC_ATTR_STR(type) \ +static ssize_t type##_show(struct device *dev, \ + struct device_attribute *attr, \ + char *buf) \ +{ \ + struct hdac_device *codec = dev_to_hdac_dev(dev); \ + return sprintf(buf, "%s\n", \ + codec->type ? codec->type : ""); \ +} \ +static DEVICE_ATTR_RO(type) + +CODEC_ATTR(vendor_id); +CODEC_ATTR(subsystem_id); +CODEC_ATTR(revision_id); +CODEC_ATTR(afg); +CODEC_ATTR(mfg); +CODEC_ATTR_STR(vendor_name); +CODEC_ATTR_STR(chip_name); + +static struct attribute *hdac_dev_attrs[] = { + &dev_attr_vendor_id.attr, + &dev_attr_subsystem_id.attr, + &dev_attr_revision_id.attr, + &dev_attr_afg.attr, + &dev_attr_mfg.attr, + &dev_attr_vendor_name.attr, + &dev_attr_chip_name.attr, + NULL +}; + +static struct attribute_group hdac_dev_attr_group = { + .attrs = hdac_dev_attrs, +}; + +const struct attribute_group *hdac_dev_attr_groups[] = { + &hdac_dev_attr_group, + NULL +}; + +/* + * Widget tree sysfs + * + * This is a tree showing the attributes of each widget. It appears like + * /sys/bus/hdaudioC0D0/widgets/04/caps + */ + +struct widget_attribute; + +struct widget_attribute { + struct attribute attr; + ssize_t (*show)(struct hdac_device *codec, hda_nid_t nid, + struct widget_attribute *attr, char *buf); + ssize_t (*store)(struct hdac_device *codec, hda_nid_t nid, + struct widget_attribute *attr, + const char *buf, size_t count); +}; + +static int get_codec_nid(struct kobject *kobj, struct hdac_device **codecp) +{ + struct device *dev = kobj_to_dev(kobj->parent->parent); + int nid; + ssize_t ret; + + ret = kstrtoint(kobj->name, 16, &nid); + if (ret < 0) + return ret; + *codecp = dev_to_hdac_dev(dev); + return nid; +} + +static ssize_t widget_attr_show(struct kobject *kobj, struct attribute *attr, + char *buf) +{ + struct widget_attribute *wid_attr = + container_of(attr, struct widget_attribute, attr); + struct hdac_device *codec; + int nid; + + if (!wid_attr->show) + return -EIO; + nid = get_codec_nid(kobj, &codec); + if (nid < 0) + return nid; + return wid_attr->show(codec, nid, wid_attr, buf); +} + +static ssize_t widget_attr_store(struct kobject *kobj, struct attribute *attr, + const char *buf, size_t count) +{ + struct widget_attribute *wid_attr = + container_of(attr, struct widget_attribute, attr); + struct hdac_device *codec; + int nid; + + if (!wid_attr->store) + return -EIO; + nid = get_codec_nid(kobj, &codec); + if (nid < 0) + return nid; + return wid_attr->store(codec, nid, wid_attr, buf, count); +} + +static const struct sysfs_ops widget_sysfs_ops = { + .show = widget_attr_show, + .store = widget_attr_store, +}; + +static void widget_release(struct kobject *kobj) +{ + kfree(kobj); +} + +static struct kobj_type widget_ktype = { + .release = widget_release, + .sysfs_ops = &widget_sysfs_ops, +}; + +#define WIDGET_ATTR_RO(_name) \ + struct widget_attribute wid_attr_##_name = __ATTR_RO(_name) +#define WIDGET_ATTR_RW(_name) \ + struct widget_attribute wid_attr_##_name = __ATTR_RW(_name) + +static ssize_t caps_show(struct hdac_device *codec, hda_nid_t nid, + struct widget_attribute *attr, char *buf) +{ + return sprintf(buf, "0x%08x\n", get_wcaps(codec, nid)); +} + +static ssize_t pin_caps_show(struct hdac_device *codec, hda_nid_t nid, + struct widget_attribute *attr, char *buf) +{ + if (get_wcaps_type(get_wcaps(codec, nid)) != AC_WID_PIN) + return 0; + return sprintf(buf, "0x%08x\n", + snd_hdac_read_parm(codec, nid, AC_PAR_PIN_CAP)); +} + +static ssize_t pin_cfg_show(struct hdac_device *codec, hda_nid_t nid, + struct widget_attribute *attr, char *buf) +{ + unsigned int val; + + if (get_wcaps_type(get_wcaps(codec, nid)) != AC_WID_PIN) + return 0; + if (snd_hdac_read(codec, nid, AC_VERB_GET_CONFIG_DEFAULT, 0, &val)) + return 0; + return sprintf(buf, "0x%08x\n", val); +} + +static bool has_pcm_cap(struct hdac_device *codec, hda_nid_t nid) +{ + if (nid == codec->afg || nid == codec->mfg) + return true; + switch (get_wcaps_type(get_wcaps(codec, nid))) { + case AC_WID_AUD_OUT: + case AC_WID_AUD_IN: + return true; + default: + return false; + } +} + +static ssize_t pcm_caps_show(struct hdac_device *codec, hda_nid_t nid, + struct widget_attribute *attr, char *buf) +{ + if (!has_pcm_cap(codec, nid)) + return 0; + return sprintf(buf, "0x%08x\n", + snd_hdac_read_parm(codec, nid, AC_PAR_PCM)); +} + +static ssize_t pcm_formats_show(struct hdac_device *codec, hda_nid_t nid, + struct widget_attribute *attr, char *buf) +{ + if (!has_pcm_cap(codec, nid)) + return 0; + return sprintf(buf, "0x%08x\n", + snd_hdac_read_parm(codec, nid, AC_PAR_STREAM)); +} + +static ssize_t amp_in_caps_show(struct hdac_device *codec, hda_nid_t nid, + struct widget_attribute *attr, char *buf) +{ + if (nid != codec->afg && !(get_wcaps(codec, nid) & AC_WCAP_IN_AMP)) + return 0; + return sprintf(buf, "0x%08x\n", + snd_hdac_read_parm(codec, nid, AC_PAR_AMP_IN_CAP)); +} + +static ssize_t amp_out_caps_show(struct hdac_device *codec, hda_nid_t nid, + struct widget_attribute *attr, char *buf) +{ + if (nid != codec->afg && !(get_wcaps(codec, nid) & AC_WCAP_OUT_AMP)) + return 0; + return sprintf(buf, "0x%08x\n", + snd_hdac_read_parm(codec, nid, AC_PAR_AMP_OUT_CAP)); +} + +static ssize_t power_caps_show(struct hdac_device *codec, hda_nid_t nid, + struct widget_attribute *attr, char *buf) +{ + if (nid != codec->afg && !(get_wcaps(codec, nid) & AC_WCAP_POWER)) + return 0; + return sprintf(buf, "0x%08x\n", + snd_hdac_read_parm(codec, nid, AC_PAR_POWER_STATE)); +} + +static ssize_t gpio_caps_show(struct hdac_device *codec, hda_nid_t nid, + struct widget_attribute *attr, char *buf) +{ + return sprintf(buf, "0x%08x\n", + snd_hdac_read_parm(codec, nid, AC_PAR_GPIO_CAP)); +} + +static ssize_t connections_show(struct hdac_device *codec, hda_nid_t nid, + struct widget_attribute *attr, char *buf) +{ + hda_nid_t list[32]; + int i, nconns; + ssize_t ret = 0; + + nconns = snd_hdac_get_connections(codec, nid, list, ARRAY_SIZE(list)); + if (nconns <= 0) + return nconns; + for (i = 0; i < nconns; i++) + ret += sprintf(buf + ret, "%s0x%02x", i ? " " : "", list[i]); + ret += sprintf(buf + ret, "\n"); + return ret; +} + +static WIDGET_ATTR_RO(caps); +static WIDGET_ATTR_RO(pin_caps); +static WIDGET_ATTR_RO(pin_cfg); +static WIDGET_ATTR_RO(pcm_caps); +static WIDGET_ATTR_RO(pcm_formats); +static WIDGET_ATTR_RO(amp_in_caps); +static WIDGET_ATTR_RO(amp_out_caps); +static WIDGET_ATTR_RO(power_caps); +static WIDGET_ATTR_RO(gpio_caps); +static WIDGET_ATTR_RO(connections); + +static struct attribute *widget_node_attrs[] = { + &wid_attr_caps.attr, + &wid_attr_pin_caps.attr, + &wid_attr_pin_cfg.attr, + &wid_attr_pcm_caps.attr, + &wid_attr_pcm_formats.attr, + &wid_attr_amp_in_caps.attr, + &wid_attr_amp_out_caps.attr, + &wid_attr_power_caps.attr, + &wid_attr_connections.attr, + NULL, +}; + +static struct attribute *widget_afg_attrs[] = { + &wid_attr_pcm_caps.attr, + &wid_attr_pcm_formats.attr, + &wid_attr_amp_in_caps.attr, + &wid_attr_amp_out_caps.attr, + &wid_attr_power_caps.attr, + &wid_attr_gpio_caps.attr, + NULL, +}; + +static const struct attribute_group widget_node_group = { + .attrs = widget_node_attrs, +}; + +static const struct attribute_group widget_afg_group = { + .attrs = widget_afg_attrs, +}; + +static void free_widget_node(struct kobject *kobj, + const struct attribute_group *group) +{ + if (kobj) { + sysfs_remove_group(kobj, group); + kobject_put(kobj); + } +} + +static void widget_tree_free(struct hdac_device *codec) +{ + struct hdac_widget_tree *tree = codec->widgets; + struct kobject **p; + + if (!tree) + return; + if (tree->nodes) { + for (p = tree->nodes; *p; p++) + free_widget_node(*p, &widget_node_group); + kfree(tree->nodes); + } + free_widget_node(tree->afg, &widget_afg_group); + if (tree->root) + kobject_put(tree->root); + kfree(tree); + codec->widgets = NULL; +} + +static int add_widget_node(struct kobject *parent, hda_nid_t nid, + const struct attribute_group *group, + struct kobject **res) +{ + struct kobject *kobj = kzalloc(sizeof(*kobj), GFP_KERNEL); + int err; + + if (!kobj) + return -ENOMEM; + kobject_init(kobj, &widget_ktype); + err = kobject_add(kobj, parent, "%02x", nid); + if (err < 0) + return err; + err = sysfs_create_group(kobj, group); + if (err < 0) { + kobject_put(kobj); + return err; + } + + *res = kobj; + return 0; +} + +static int widget_tree_create(struct hdac_device *codec) +{ + struct hdac_widget_tree *tree; + int i, err; + hda_nid_t nid; + + tree = codec->widgets = kzalloc(sizeof(*tree), GFP_KERNEL); + if (!tree) + return -ENOMEM; + + tree->root = kobject_create_and_add("widgets", &codec->dev.kobj); + if (!tree->root) + return -ENOMEM; + + if (codec->afg) { + err = add_widget_node(tree->root, codec->afg, + &widget_afg_group, &tree->afg); + if (err < 0) + return err; + } + + tree->nodes = kcalloc(codec->num_nodes + 1, sizeof(*tree->nodes), + GFP_KERNEL); + if (!tree->nodes) + return -ENOMEM; + + for (i = 0, nid = codec->start_nid; i < codec->num_nodes; i++, nid++) { + err = add_widget_node(tree->root, nid, &widget_node_group, + &tree->nodes[i]); + if (err < 0) + return err; + } + + kobject_uevent(tree->root, KOBJ_CHANGE); + return 0; +} + +int hda_widget_sysfs_init(struct hdac_device *codec) +{ + int err; + + err = widget_tree_create(codec); + if (err < 0) { + widget_tree_free(codec); + return err; + } + + return 0; +} + +void hda_widget_sysfs_exit(struct hdac_device *codec) +{ + widget_tree_free(codec); +} diff --git a/sound/hda/local.h b/sound/hda/local.h index a077d1f656f6..d692f417ddc0 100644 --- a/sound/hda/local.h +++ b/sound/hda/local.h @@ -16,4 +16,8 @@ static inline int get_wcaps_type(unsigned int wcaps) return (wcaps & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT; } +extern const struct attribute_group *hdac_dev_attr_groups[]; +int hda_widget_sysfs_init(struct hdac_device *codec); +void hda_widget_sysfs_exit(struct hdac_device *codec); + #endif /* __HDAC_LOCAL_H */ diff --git a/sound/pci/hda/hda_bind.c b/sound/pci/hda/hda_bind.c index ad276a9771db..130f672e6f37 100644 --- a/sound/pci/hda/hda_bind.c +++ b/sound/pci/hda/hda_bind.c @@ -240,7 +240,7 @@ int snd_hda_codec_configure(struct hda_codec *codec) else codec->probe_id = 0; - err = device_add(hda_codec_dev(codec)); + err = snd_hdac_device_register(&codec->core); if (err < 0) return err; @@ -262,7 +262,7 @@ int snd_hda_codec_configure(struct hda_codec *codec) return 0; error: - device_del(hda_codec_dev(codec)); + snd_hdac_device_unregister(&codec->core); return err; } EXPORT_SYMBOL_GPL(snd_hda_codec_configure); diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index ddfc0fbbee23..b162fc40348f 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -967,8 +967,7 @@ static int snd_hda_codec_dev_free(struct snd_device *device) struct hda_codec *codec = device->device_data; codec->in_freeing = 1; - if (device_is_registered(hda_codec_dev(codec))) - device_del(hda_codec_dev(codec)); + snd_hdac_device_unregister(&codec->core); put_device(hda_codec_dev(codec)); return 0; } @@ -2182,8 +2181,7 @@ int snd_hda_codec_reset(struct hda_codec *codec) return -EBUSY; /* OK, let it free */ - if (device_is_registered(hda_codec_dev(codec))) - device_del(hda_codec_dev(codec)); + snd_hdac_device_unregister(&codec->core); /* allow device access again */ snd_hda_unlock_devices(bus); -- cgit v1.2.3 From 05852448690d7d810175f8ceccefba083525aa89 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 3 Mar 2015 15:40:08 +0100 Subject: ALSA: hda - Support indirect execution of verbs Add an overriding exec_verb op to struct hdac_device so that the call via snd_hdac_exec_verb() can switch to a different route depending on the setup. The codec driver sets this field so that it can handle the errors or applying quirks appropriately. Furthermore, this mechanism will be used for smooth transition for the regmap support in later patches. Signed-off-by: Takashi Iwai --- include/sound/hdaudio.h | 6 ++++++ sound/hda/hdac_device.c | 24 +++++++++++++++++++++++- sound/pci/hda/hda_codec.c | 12 +++++++----- 3 files changed, 36 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/include/sound/hdaudio.h b/include/sound/hdaudio.h index 6ed2b421e29e..675614dc2b88 100644 --- a/include/sound/hdaudio.h +++ b/include/sound/hdaudio.h @@ -48,6 +48,10 @@ struct hdac_device { const char *vendor_name; /* codec vendor name */ const char *chip_name; /* codec chip name */ + /* verb exec op override */ + int (*exec_verb)(struct hdac_device *dev, unsigned int cmd, + unsigned int flags, unsigned int *res); + /* widgets */ unsigned int num_nodes; hda_nid_t start_nid, end_nid; @@ -82,6 +86,8 @@ int snd_hdac_refresh_widgets(struct hdac_device *codec); unsigned int snd_hdac_make_cmd(struct hdac_device *codec, hda_nid_t nid, unsigned int verb, unsigned int parm); +int snd_hdac_exec_verb(struct hdac_device *codec, unsigned int cmd, + unsigned int flags, unsigned int *res); int snd_hdac_read(struct hdac_device *codec, hda_nid_t nid, unsigned int verb, unsigned int parm, unsigned int *res); int snd_hdac_read_parm(struct hdac_device *codec, hda_nid_t nid, int parm); diff --git a/sound/hda/hdac_device.c b/sound/hda/hdac_device.c index 1470ecc354db..aaece36247e7 100644 --- a/sound/hda/hdac_device.c +++ b/sound/hda/hdac_device.c @@ -193,6 +193,28 @@ unsigned int snd_hdac_make_cmd(struct hdac_device *codec, hda_nid_t nid, } EXPORT_SYMBOL_GPL(snd_hdac_make_cmd); +/** + * snd_hdac_exec_verb - execute an encoded verb + * @codec: the codec object + * @cmd: encoded verb to execute + * @flags: optional flags, pass zero for default + * @res: the pointer to store the result, NULL if running async + * + * Returns zero if successful, or a negative error code. + * + * This calls the exec_verb op when set in hdac_codec. If not, + * call the default snd_hdac_bus_exec_verb(). + */ +int snd_hdac_exec_verb(struct hdac_device *codec, unsigned int cmd, + unsigned int flags, unsigned int *res) +{ + if (codec->exec_verb) + return codec->exec_verb(codec, cmd, flags, res); + return snd_hdac_bus_exec_verb(codec->bus, codec->addr, cmd, res); +} +EXPORT_SYMBOL_GPL(snd_hdac_exec_verb); + + /** * snd_hdac_read - execute a verb * @codec: the codec object @@ -208,7 +230,7 @@ int snd_hdac_read(struct hdac_device *codec, hda_nid_t nid, { unsigned int cmd = snd_hdac_make_cmd(codec, nid, verb, parm); - return snd_hdac_bus_exec_verb(codec->bus, codec->addr, cmd, res); + return snd_hdac_exec_verb(codec, cmd, 0, res); } EXPORT_SYMBOL_GPL(snd_hdac_read); diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index b162fc40348f..36483f7dd3ce 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -124,11 +124,12 @@ const char *snd_hda_get_jack_type(u32 cfg) EXPORT_SYMBOL_GPL(snd_hda_get_jack_type); /* - * Send and receive a verb + * Send and receive a verb - passed to exec_verb override for hdac_device */ -static int codec_exec_verb(struct hda_codec *codec, unsigned int cmd, - int flags, unsigned int *res) +static int codec_exec_verb(struct hdac_device *dev, unsigned int cmd, + unsigned int flags, unsigned int *res) { + struct hda_codec *codec = container_of(dev, struct hda_codec, core); struct hda_bus *bus = codec->bus; int err; @@ -177,7 +178,7 @@ unsigned int snd_hda_codec_read(struct hda_codec *codec, hda_nid_t nid, { unsigned int cmd = snd_hdac_make_cmd(&codec->core, nid, verb, parm); unsigned int res; - if (codec_exec_verb(codec, cmd, flags, &res)) + if (snd_hdac_exec_verb(&codec->core, cmd, flags, &res)) return -1; return res; } @@ -199,7 +200,7 @@ int snd_hda_codec_write(struct hda_codec *codec, hda_nid_t nid, int flags, unsigned int verb, unsigned int parm) { unsigned int cmd = snd_hdac_make_cmd(&codec->core, nid, verb, parm); - return codec_exec_verb(codec, cmd, flags, NULL); + return snd_hdac_exec_verb(&codec->core, cmd, flags, NULL); } EXPORT_SYMBOL_GPL(snd_hda_codec_write); @@ -1026,6 +1027,7 @@ int snd_hda_codec_new(struct hda_bus *bus, struct snd_card *card, codec->core.dev.release = snd_hda_codec_dev_release; codec->core.type = HDA_DEV_LEGACY; + codec->core.exec_verb = codec_exec_verb; codec->bus = bus; codec->card = card; -- cgit v1.2.3 From 71fc4c7ef5ef2d0ddd22f0545ede4c135b554b84 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 3 Mar 2015 17:33:10 +0100 Subject: ALSA: hda - Move generic array helpers to core lib This will be used by the regmap support. Signed-off-by: Takashi Iwai --- include/sound/hdaudio.h | 33 +++++++++++++++++++++++++++++++ sound/hda/Makefile | 3 ++- sound/hda/array.c | 49 +++++++++++++++++++++++++++++++++++++++++++++++ sound/pci/hda/hda_codec.c | 46 -------------------------------------------- sound/pci/hda/hda_codec.h | 30 ----------------------------- 5 files changed, 84 insertions(+), 77 deletions(-) create mode 100644 sound/hda/array.c (limited to 'include') diff --git a/include/sound/hdaudio.h b/include/sound/hdaudio.h index 675614dc2b88..3abdd3f16528 100644 --- a/include/sound/hdaudio.h +++ b/include/sound/hdaudio.h @@ -21,6 +21,17 @@ struct hdac_widget_tree; */ extern struct bus_type snd_hda_bus_type; +/* + * generic arrays + */ +struct snd_array { + unsigned int used; + unsigned int alloced; + unsigned int elem_size; + unsigned int alloc_align; + void *list; +}; + /* * HD-audio codec base device */ @@ -178,4 +189,26 @@ static inline void snd_hdac_codec_link_down(struct hdac_device *codec) clear_bit(codec->addr, &codec->bus->codec_powered); } +/* + * generic array helpers + */ +void *snd_array_new(struct snd_array *array); +void snd_array_free(struct snd_array *array); +static inline void snd_array_init(struct snd_array *array, unsigned int size, + unsigned int align) +{ + array->elem_size = size; + array->alloc_align = align; +} + +static inline void *snd_array_elem(struct snd_array *array, unsigned int idx) +{ + return array->list + idx * array->elem_size; +} + +static inline unsigned int snd_array_index(struct snd_array *array, void *ptr) +{ + return (unsigned long)(ptr - array->list) / array->elem_size; +} + #endif /* __SOUND_HDAUDIO_H */ diff --git a/sound/hda/Makefile b/sound/hda/Makefile index eec5da03b41f..e508ba1102cb 100644 --- a/sound/hda/Makefile +++ b/sound/hda/Makefile @@ -1,4 +1,5 @@ -snd-hda-core-objs := hda_bus_type.o hdac_bus.o hdac_device.o hdac_sysfs.o +snd-hda-core-objs := hda_bus_type.o hdac_bus.o hdac_device.o hdac_sysfs.o \ + array.o snd-hda-core-objs += trace.o CFLAGS_trace.o := -I$(src) diff --git a/sound/hda/array.c b/sound/hda/array.c new file mode 100644 index 000000000000..516795baa7db --- /dev/null +++ b/sound/hda/array.c @@ -0,0 +1,49 @@ +/* + * generic arrays + */ + +#include +#include +#include + +/** + * snd_array_new - get a new element from the given array + * @array: the array object + * + * Get a new element from the given array. If it exceeds the + * pre-allocated array size, re-allocate the array. + * + * Returns NULL if allocation failed. + */ +void *snd_array_new(struct snd_array *array) +{ + if (snd_BUG_ON(!array->elem_size)) + return NULL; + if (array->used >= array->alloced) { + int num = array->alloced + array->alloc_align; + int size = (num + 1) * array->elem_size; + void *nlist; + if (snd_BUG_ON(num >= 4096)) + return NULL; + nlist = krealloc(array->list, size, GFP_KERNEL | __GFP_ZERO); + if (!nlist) + return NULL; + array->list = nlist; + array->alloced = num; + } + return snd_array_elem(array, array->used++); +} +EXPORT_SYMBOL_GPL(snd_array_new); + +/** + * snd_array_free - free the given array elements + * @array: the array object + */ +void snd_array_free(struct snd_array *array) +{ + kfree(array->list); + array->used = 0; + array->alloced = 0; + array->list = NULL; +} +EXPORT_SYMBOL_GPL(snd_array_free); diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index 145cae7903b6..10e257ff9084 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -5012,52 +5012,6 @@ void snd_hda_bus_reset(struct hda_bus *bus) } EXPORT_SYMBOL_GPL(snd_hda_bus_reset); -/* - * generic arrays - */ - -/** - * snd_array_new - get a new element from the given array - * @array: the array object - * - * Get a new element from the given array. If it exceeds the - * pre-allocated array size, re-allocate the array. - * - * Returns NULL if allocation failed. - */ -void *snd_array_new(struct snd_array *array) -{ - if (snd_BUG_ON(!array->elem_size)) - return NULL; - if (array->used >= array->alloced) { - int num = array->alloced + array->alloc_align; - int size = (num + 1) * array->elem_size; - void *nlist; - if (snd_BUG_ON(num >= 4096)) - return NULL; - nlist = krealloc(array->list, size, GFP_KERNEL | __GFP_ZERO); - if (!nlist) - return NULL; - array->list = nlist; - array->alloced = num; - } - return snd_array_elem(array, array->used++); -} -EXPORT_SYMBOL_GPL(snd_array_new); - -/** - * snd_array_free - free the given array elements - * @array: the array object - */ -void snd_array_free(struct snd_array *array) -{ - kfree(array->list); - array->used = 0; - array->alloced = 0; - array->list = NULL; -} -EXPORT_SYMBOL_GPL(snd_array_free); - /** * snd_print_pcm_bits - Print the supported PCM fmt bits to the string buffer * @pcm: PCM caps bits diff --git a/sound/pci/hda/hda_codec.h b/sound/pci/hda/hda_codec.h index 76776164623d..3068163b3db2 100644 --- a/sound/pci/hda/hda_codec.h +++ b/sound/pci/hda/hda_codec.h @@ -29,36 +29,6 @@ #include #include -/* - * generic arrays - */ -struct snd_array { - unsigned int used; - unsigned int alloced; - unsigned int elem_size; - unsigned int alloc_align; - void *list; -}; - -void *snd_array_new(struct snd_array *array); -void snd_array_free(struct snd_array *array); -static inline void snd_array_init(struct snd_array *array, unsigned int size, - unsigned int align) -{ - array->elem_size = size; - array->alloc_align = align; -} - -static inline void *snd_array_elem(struct snd_array *array, unsigned int idx) -{ - return array->list + idx * array->elem_size; -} - -static inline unsigned int snd_array_index(struct snd_array *array, void *ptr) -{ - return (unsigned long)(ptr - array->list) / array->elem_size; -} - /* * Structures */ -- cgit v1.2.3 From 4d75faa0448a6bb2fd6d56051a3675a3937cbada Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 25 Feb 2015 14:42:38 +0100 Subject: ALSA: hda - Add regmap support This patch adds an infrastructure to support regmap-based verb accesses. Because o the asymmetric nature of HD-audio verbs, especially the amp verbs, we need to translate the verbs as a sort of pseudo registers to be mapped uniquely in regmap. In this patch, a pseudo register is built from the NID, the AC_VERB_GET_* and 8bit parameters, i.e. almost in the form to be sent to HD-audio bus but without codec address field. OTOH, for writing, the same pseudo register is translated to AC_VERB_SET_* automatically. The AC_VERB_SET_AMP_* verb is re-encoded from the corresponding AC_VERB_GET_AMP_* verb and parameter at writing. Some verbs has a single command for read but multiple for writes. A write for such a verb is split automatically to multiple verbs. The patch provides also a few handy helper functions. They are designed to be accessible even without regmap. When no regmap is set up (e.g. before the codec device instantiation), the direct hardware access is used. Also, it tries to avoid the unnecessary power-up. The power up/down sequence is performed only on demand. The codec driver needs to call snd_hdac_regmap_exit() and snd_hdac_regmap_exit() at probe and remove if it wants the regmap access. There is one flag added to hdac_device. When the flag lazy_cache is set, regmap helper ignores a write for a suspended device and returns as if it was actually written. It reduces the hardware access pretty much, e.g. when adjusting the mixer volume while in idle. This assumes that the driver will sync the cache later at resume properly, so use it carefully. Signed-off-by: Takashi Iwai --- include/sound/hda_regmap.h | 145 +++++++++++++++++++++ include/sound/hdaudio.h | 4 + sound/hda/Kconfig | 1 + sound/hda/Makefile | 2 +- sound/hda/hdac_regmap.c | 304 +++++++++++++++++++++++++++++++++++++++++++++ sound/pci/hda/hda_bind.c | 4 + sound/pci/hda/hda_codec.c | 1 + sound/pci/hda/hda_codec.h | 1 + 8 files changed, 461 insertions(+), 1 deletion(-) create mode 100644 include/sound/hda_regmap.h create mode 100644 sound/hda/hdac_regmap.c (limited to 'include') diff --git a/include/sound/hda_regmap.h b/include/sound/hda_regmap.h new file mode 100644 index 000000000000..95651d26437d --- /dev/null +++ b/include/sound/hda_regmap.h @@ -0,0 +1,145 @@ +/* + * HD-audio regmap helpers + */ + +#ifndef __SOUND_HDA_REGMAP_H +#define __SOUND_HDA_REGMAP_H + +#include +#include +#include + +int snd_hdac_regmap_init(struct hdac_device *codec); +void snd_hdac_regmap_exit(struct hdac_device *codec); + +int snd_hdac_regmap_read_raw(struct hdac_device *codec, unsigned int reg, + unsigned int *val); +int snd_hdac_regmap_write_raw(struct hdac_device *codec, unsigned int reg, + unsigned int val); +int snd_hdac_regmap_update_raw(struct hdac_device *codec, unsigned int reg, + unsigned int mask, unsigned int val); + +/** + * snd_hdac_regmap_encode_verb - encode the verb to a pseudo register + * @nid: widget NID + * @verb: codec verb + * + * Returns an encoded pseudo register. + */ +#define snd_hdac_regmap_encode_verb(nid, verb) \ + (((verb) << 8) | 0x80000 | ((unsigned int)(nid) << 20)) + +/** + * snd_hdac_regmap_encode_amp - encode the AMP verb to a pseudo register + * @nid: widget NID + * @ch: channel (left = 0, right = 1) + * @dir: direction (#HDA_INPUT, #HDA_OUTPUT) + * @idx: input index value + * + * Returns an encoded pseudo register. + */ +#define snd_hdac_regmap_encode_amp(nid, ch, dir, idx) \ + (snd_hdac_regmap_encode_verb(nid, AC_VERB_GET_AMP_GAIN_MUTE) | \ + ((ch) ? AC_AMP_GET_RIGHT : AC_AMP_GET_LEFT) | \ + ((dir) == HDA_OUTPUT ? AC_AMP_GET_OUTPUT : AC_AMP_GET_INPUT) | \ + (idx)) + +/** + * snd_hdac_regmap_write - Write a verb with caching + * @nid: codec NID + * @reg: verb to write + * @val: value to write + * + * For writing an amp value, use snd_hda_regmap_amp_update(). + */ +static inline int +snd_hdac_regmap_write(struct hdac_device *codec, hda_nid_t nid, + unsigned int verb, unsigned int val) +{ + unsigned int cmd = snd_hdac_regmap_encode_verb(nid, verb); + + return snd_hdac_regmap_write_raw(codec, cmd, val); +} + +/** + * snd_hda_regmap_update - Update a verb value with caching + * @nid: codec NID + * @verb: verb to update + * @mask: bit mask to update + * @val: value to update + * + * For updating an amp value, use snd_hda_regmap_amp_update(). + */ +static inline int +snd_hdac_regmap_update(struct hdac_device *codec, hda_nid_t nid, + unsigned int verb, unsigned int mask, + unsigned int val) +{ + unsigned int cmd = snd_hdac_regmap_encode_verb(nid, verb); + + return snd_hdac_regmap_update_raw(codec, cmd, mask, val); +} + +/** + * snd_hda_regmap_read - Read a verb with caching + * @nid: codec NID + * @verb: verb to read + * @val: pointer to store the value + * + * For reading an amp value, use snd_hda_regmap_get_amp(). + */ +static inline int +snd_hdac_regmap_read(struct hdac_device *codec, hda_nid_t nid, + unsigned int verb, unsigned int *val) +{ + unsigned int cmd = snd_hdac_regmap_encode_verb(nid, verb); + + return snd_hdac_regmap_read_raw(codec, cmd, val); +} + +/** + * snd_hdac_regmap_get_amp - Read AMP value + * @codec: HD-audio codec + * @nid: NID to read the AMP value + * @ch: channel (left=0 or right=1) + * @direction: #HDA_INPUT or #HDA_OUTPUT + * @index: the index value (only for input direction) + * @val: the pointer to store the value + * + * Read AMP value. The volume is between 0 to 0x7f, 0x80 = mute bit. + * Returns the value or a negative error. + */ +static inline int +snd_hdac_regmap_get_amp(struct hdac_device *codec, hda_nid_t nid, + int ch, int dir, int idx) +{ + unsigned int cmd = snd_hdac_regmap_encode_amp(nid, ch, dir, idx); + int err, val; + + err = snd_hdac_regmap_read_raw(codec, cmd, &val); + return err < 0 ? err : val; +} + +/** + * snd_hdac_regmap_update_amp - update the AMP value + * @codec: HD-audio codec + * @nid: NID to read the AMP value + * @ch: channel (left=0 or right=1) + * @direction: #HDA_INPUT or #HDA_OUTPUT + * @idx: the index value (only for input direction) + * @mask: bit mask to set + * @val: the bits value to set + * + * Update the AMP value with a bit mask. + * Returns 0 if the value is unchanged, 1 if changed, or a negative error. + */ +static inline int +snd_hdac_regmap_update_amp(struct hdac_device *codec, hda_nid_t nid, + int ch, int dir, int idx, int mask, int val) +{ + unsigned int cmd = snd_hdac_regmap_encode_amp(nid, ch, dir, idx); + + return snd_hdac_regmap_update_raw(codec, cmd, mask, val); +} + +#endif /* __SOUND_HDA_REGMAP_H */ diff --git a/include/sound/hdaudio.h b/include/sound/hdaudio.h index 3abdd3f16528..47e20b741c51 100644 --- a/include/sound/hdaudio.h +++ b/include/sound/hdaudio.h @@ -72,6 +72,10 @@ struct hdac_device { /* sysfs */ struct hdac_widget_tree *widgets; + + /* regmap */ + struct regmap *regmap; + bool lazy_cache:1; /* don't wake up for writes */ }; /* device/driver type used for matching */ diff --git a/sound/hda/Kconfig b/sound/hda/Kconfig index 4f428ccf64ad..001c6588a5ff 100644 --- a/sound/hda/Kconfig +++ b/sound/hda/Kconfig @@ -1,2 +1,3 @@ config SND_HDA_CORE tristate + select REGMAP diff --git a/sound/hda/Makefile b/sound/hda/Makefile index e508ba1102cb..7a359f5b7e25 100644 --- a/sound/hda/Makefile +++ b/sound/hda/Makefile @@ -1,5 +1,5 @@ snd-hda-core-objs := hda_bus_type.o hdac_bus.o hdac_device.o hdac_sysfs.o \ - array.o + hdac_regmap.o array.o snd-hda-core-objs += trace.o CFLAGS_trace.o := -I$(src) diff --git a/sound/hda/hdac_regmap.c b/sound/hda/hdac_regmap.c new file mode 100644 index 000000000000..db03d60d9c99 --- /dev/null +++ b/sound/hda/hdac_regmap.c @@ -0,0 +1,304 @@ +/* + * Regmap support for HD-audio verbs + * + * A virtual register is translated to one or more hda verbs for write, + * vice versa for read. + * + * A few limitations: + * - Provided for not all verbs but only subset standard non-volatile verbs. + * - For reading, only AC_VERB_GET_* variants can be used. + * - For writing, mapped to the *corresponding* AC_VERB_SET_* variants, + * so can't handle asymmetric verbs for read and write + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef CONFIG_PM +#define codec_is_running(codec) \ + (atomic_read(&(codec)->in_pm) || \ + !pm_runtime_suspended(&(codec)->dev)) +#else +#define codec_is_running(codec) true +#endif + +#define get_verb(reg) (((reg) >> 8) & 0xfff) + +static bool hda_volatile_reg(struct device *dev, unsigned int reg) +{ + unsigned int verb = get_verb(reg); + + switch (verb) { + case AC_VERB_GET_PROC_COEF: + case AC_VERB_GET_COEF_INDEX: + case AC_VERB_GET_PROC_STATE: + case AC_VERB_GET_POWER_STATE: + case AC_VERB_GET_PIN_SENSE: + case AC_VERB_GET_HDMI_DIP_SIZE: + case AC_VERB_GET_HDMI_ELDD: + case AC_VERB_GET_HDMI_DIP_INDEX: + case AC_VERB_GET_HDMI_DIP_DATA: + case AC_VERB_GET_HDMI_DIP_XMIT: + case AC_VERB_GET_HDMI_CP_CTRL: + case AC_VERB_GET_HDMI_CHAN_SLOT: + case AC_VERB_GET_DEVICE_SEL: + case AC_VERB_GET_DEVICE_LIST: /* read-only volatile */ + return true; + } + + return false; +} + +static bool hda_writeable_reg(struct device *dev, unsigned int reg) +{ + unsigned int verb = get_verb(reg); + + switch (verb & 0xf00) { + case AC_VERB_GET_STREAM_FORMAT: + case AC_VERB_GET_AMP_GAIN_MUTE: + return true; + case 0xf00: + break; + default: + return false; + } + + switch (verb) { + case AC_VERB_GET_CONNECT_SEL: + case AC_VERB_GET_SDI_SELECT: + case AC_VERB_GET_CONV: + case AC_VERB_GET_PIN_WIDGET_CONTROL: + case AC_VERB_GET_UNSOLICITED_RESPONSE: /* only as SET_UNSOLICITED_ENABLE */ + case AC_VERB_GET_BEEP_CONTROL: + case AC_VERB_GET_EAPD_BTLENABLE: + case AC_VERB_GET_DIGI_CONVERT_1: + case AC_VERB_GET_DIGI_CONVERT_2: /* only for beep control */ + case AC_VERB_GET_VOLUME_KNOB_CONTROL: + case AC_VERB_GET_CONFIG_DEFAULT: + case AC_VERB_GET_GPIO_MASK: + case AC_VERB_GET_GPIO_DIRECTION: + case AC_VERB_GET_GPIO_DATA: /* not for volatile read */ + case AC_VERB_GET_GPIO_WAKE_MASK: + case AC_VERB_GET_GPIO_UNSOLICITED_RSP_MASK: + case AC_VERB_GET_GPIO_STICKY_MASK: + case AC_VERB_GET_CVT_CHAN_COUNT: + return true; + } + + return false; +} + +static bool hda_readable_reg(struct device *dev, unsigned int reg) +{ + unsigned int verb = get_verb(reg); + + switch (verb) { + case AC_VERB_PARAMETERS: + case AC_VERB_GET_CONNECT_LIST: + case AC_VERB_GET_SUBSYSTEM_ID: + return true; + } + + return hda_writeable_reg(dev, reg); +} + +static int hda_reg_read(void *context, unsigned int reg, unsigned int *val) +{ + struct hdac_device *codec = context; + + if (!codec_is_running(codec)) + return -EAGAIN; + reg |= (codec->addr << 28); + return snd_hdac_exec_verb(codec, reg, 0, val); +} + +static int hda_reg_write(void *context, unsigned int reg, unsigned int val) +{ + struct hdac_device *codec = context; + unsigned int verb; + int i, bytes, err; + + if (!codec_is_running(codec)) + return codec->lazy_cache ? 0 : -EAGAIN; + + reg &= ~0x00080000U; /* drop GET bit */ + reg |= (codec->addr << 28); + verb = get_verb(reg); + + switch (verb & 0xf00) { + case AC_VERB_SET_AMP_GAIN_MUTE: + verb = AC_VERB_SET_AMP_GAIN_MUTE; + if (reg & AC_AMP_GET_LEFT) + verb |= AC_AMP_SET_LEFT >> 8; + else + verb |= AC_AMP_SET_RIGHT >> 8; + if (reg & AC_AMP_GET_OUTPUT) { + verb |= AC_AMP_SET_OUTPUT >> 8; + } else { + verb |= AC_AMP_SET_INPUT >> 8; + verb |= reg & 0xf; + } + break; + } + + switch (verb) { + case AC_VERB_SET_DIGI_CONVERT_1: + bytes = 2; + break; + case AC_VERB_SET_CONFIG_DEFAULT_BYTES_0: + bytes = 4; + break; + default: + bytes = 1; + break; + } + + for (i = 0; i < bytes; i++) { + reg &= ~0xfffff; + reg |= (verb + i) << 8 | ((val >> (8 * i)) & 0xff); + err = snd_hdac_exec_verb(codec, reg, 0, NULL); + if (err < 0) + return err; + } + + return 0; +} + +static const struct regmap_config hda_regmap_cfg = { + .name = "hdaudio", + .reg_bits = 32, + .val_bits = 32, + .max_register = 0xfffffff, + .writeable_reg = hda_writeable_reg, + .readable_reg = hda_readable_reg, + .volatile_reg = hda_volatile_reg, + .cache_type = REGCACHE_RBTREE, + .reg_read = hda_reg_read, + .reg_write = hda_reg_write, +}; + +int snd_hdac_regmap_init(struct hdac_device *codec) +{ + struct regmap *regmap; + + regmap = regmap_init(&codec->dev, NULL, codec, &hda_regmap_cfg); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + codec->regmap = regmap; + return 0; +} +EXPORT_SYMBOL_GPL(snd_hdac_regmap_init); + +void snd_hdac_regmap_exit(struct hdac_device *codec) +{ + if (codec->regmap) { + regmap_exit(codec->regmap); + codec->regmap = NULL; + } +} +EXPORT_SYMBOL_GPL(snd_hdac_regmap_exit); + +/* + * helper functions + */ + +/* write a pseudo-register value (w/o power sequence) */ +static int reg_raw_write(struct hdac_device *codec, unsigned int reg, + unsigned int val) +{ + if (!codec->regmap) + return hda_reg_write(codec, reg, val); + else + return regmap_write(codec->regmap, reg, val); +} + +/** + * snd_hdac_regmap_write_raw - write a pseudo register with power mgmt + * @codec: the codec object + * @reg: pseudo register + * @val: value to write + * + * Returns zero if successful or a negative error code. + */ +int snd_hdac_regmap_write_raw(struct hdac_device *codec, unsigned int reg, + unsigned int val) +{ + int err; + + err = reg_raw_write(codec, reg, val); + if (err == -EAGAIN) { + snd_hdac_power_up(codec); + err = reg_raw_write(codec, reg, val); + snd_hdac_power_down(codec); + } + return err; +} +EXPORT_SYMBOL_GPL(snd_hdac_regmap_write_raw); + +static int reg_raw_read(struct hdac_device *codec, unsigned int reg, + unsigned int *val) +{ + if (!codec->regmap) + return hda_reg_read(codec, reg, val); + else + return regmap_read(codec->regmap, reg, val); +} + +/** + * snd_hdac_regmap_read_raw - read a pseudo register with power mgmt + * @codec: the codec object + * @reg: pseudo register + * @val: pointer to store the read value + * + * Returns zero if successful or a negative error code. + */ +int snd_hdac_regmap_read_raw(struct hdac_device *codec, unsigned int reg, + unsigned int *val) +{ + int err; + + err = reg_raw_read(codec, reg, val); + if (err == -EAGAIN) { + snd_hdac_power_up(codec); + err = reg_raw_read(codec, reg, val); + snd_hdac_power_down(codec); + } + return err; +} +EXPORT_SYMBOL_GPL(snd_hdac_regmap_read_raw); + +/** + * snd_hdac_regmap_update_raw - update a pseudo register with power mgmt + * @codec: the codec object + * @reg: pseudo register + * @mask: bit mask to udpate + * @val: value to update + * + * Returns zero if successful or a negative error code. + */ +int snd_hdac_regmap_update_raw(struct hdac_device *codec, unsigned int reg, + unsigned int mask, unsigned int val) +{ + unsigned int orig; + int err; + + val &= mask; + err = snd_hdac_regmap_read_raw(codec, reg, &orig); + if (err < 0) + return err; + val |= orig & ~mask; + if (val == orig) + return 0; + err = snd_hdac_regmap_write_raw(codec, reg, val); + if (err < 0) + return err; + return 1; +} +EXPORT_SYMBOL_GPL(snd_hdac_regmap_update_raw); diff --git a/sound/pci/hda/hda_bind.c b/sound/pci/hda/hda_bind.c index 7b269c3237e3..00aa31c5f08e 100644 --- a/sound/pci/hda/hda_bind.c +++ b/sound/pci/hda/hda_bind.c @@ -73,6 +73,9 @@ static int hda_codec_driver_probe(struct device *dev) return -EINVAL; err = codec_refresh_name(codec, codec->preset->name); + if (err < 0) + goto error; + err = snd_hdac_regmap_init(&codec->core); if (err < 0) goto error; @@ -98,6 +101,7 @@ static int hda_codec_driver_probe(struct device *dev) snd_hda_codec_register(codec); } + codec->core.lazy_cache = true; return 0; error_module: diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index 10e257ff9084..8eb42f4226ff 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -945,6 +945,7 @@ void snd_hda_codec_cleanup_for_unbind(struct hda_codec *codec) snd_array_free(&codec->mixers); snd_array_free(&codec->nids); remove_conn_list(codec); + snd_hdac_regmap_exit(&codec->core); } static unsigned int hda_set_power_state(struct hda_codec *codec, diff --git a/sound/pci/hda/hda_codec.h b/sound/pci/hda/hda_codec.h index 3068163b3db2..0f5749ca1600 100644 --- a/sound/pci/hda/hda_codec.h +++ b/sound/pci/hda/hda_codec.h @@ -28,6 +28,7 @@ #include #include #include +#include /* * Structures -- cgit v1.2.3 From 01ed3c06c6d5e7e861650ae76117dd4194d87316 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 26 Feb 2015 13:57:47 +0100 Subject: ALSA: hda - Use regmap for codec parameter reads Let's start converting the access functions to regmap. The first one is the simplest, just converting the codec parameter read helper function snd_hda_param_read(). Signed-off-by: Takashi Iwai --- include/sound/hdaudio.h | 20 +++++++++++++++++++- sound/hda/hdac_device.c | 21 +++++++++------------ sound/pci/hda/hda_codec.h | 2 +- 3 files changed, 29 insertions(+), 14 deletions(-) (limited to 'include') diff --git a/include/sound/hdaudio.h b/include/sound/hdaudio.h index 47e20b741c51..ddfcc44970fa 100644 --- a/include/sound/hdaudio.h +++ b/include/sound/hdaudio.h @@ -105,12 +105,30 @@ int snd_hdac_exec_verb(struct hdac_device *codec, unsigned int cmd, unsigned int flags, unsigned int *res); int snd_hdac_read(struct hdac_device *codec, hda_nid_t nid, unsigned int verb, unsigned int parm, unsigned int *res); -int snd_hdac_read_parm(struct hdac_device *codec, hda_nid_t nid, int parm); +int _snd_hdac_read_parm(struct hdac_device *codec, hda_nid_t nid, int parm, + unsigned int *res); int snd_hdac_get_connections(struct hdac_device *codec, hda_nid_t nid, hda_nid_t *conn_list, int max_conns); int snd_hdac_get_sub_nodes(struct hdac_device *codec, hda_nid_t nid, hda_nid_t *start_id); +/** + * snd_hdac_read_parm - read a codec parameter + * @codec: the codec object + * @nid: NID to read a parameter + * @parm: parameter to read + * + * Returns -1 for error. If you need to distinguish the error more + * strictly, use _snd_hdac_read_parm() directly. + */ +static inline int snd_hdac_read_parm(struct hdac_device *codec, hda_nid_t nid, + int parm) +{ + unsigned int val; + + return _snd_hdac_read_parm(codec, nid, parm, &val) < 0 ? -1 : val; +} + #ifdef CONFIG_PM void snd_hdac_power_up(struct hdac_device *codec); void snd_hdac_power_down(struct hdac_device *codec); diff --git a/sound/hda/hdac_device.c b/sound/hda/hdac_device.c index 6e8ee1d6974a..ba9c1fc6e3ea 100644 --- a/sound/hda/hdac_device.c +++ b/sound/hda/hdac_device.c @@ -9,6 +9,7 @@ #include #include #include +#include #include "local.h" static void setup_fg_nodes(struct hdac_device *codec); @@ -234,23 +235,19 @@ int snd_hdac_read(struct hdac_device *codec, hda_nid_t nid, EXPORT_SYMBOL_GPL(snd_hdac_read); /** - * snd_hdac_read_parm - read a codec parameter - * @codec: the codec object - * @nid: NID to read a parameter - * @parm: parameter to read + * _snd_hdac_read_parm - read a parmeter * - * Returns -1 for error. If you need to distinguish the error more - * strictly, use snd_hdac_read() directly. + * This function returns zero or an error unlike snd_hdac_read_parm(). */ -int snd_hdac_read_parm(struct hdac_device *codec, hda_nid_t nid, int parm) +int _snd_hdac_read_parm(struct hdac_device *codec, hda_nid_t nid, int parm, + unsigned int *res) { - int val; + unsigned int cmd; - if (snd_hdac_read(codec, nid, AC_VERB_PARAMETERS, parm, &val)) - return -1; - return val; + cmd = snd_hdac_regmap_encode_verb(nid, AC_VERB_PARAMETERS) | parm; + return snd_hdac_regmap_read_raw(codec, cmd, res); } -EXPORT_SYMBOL_GPL(snd_hdac_read_parm); +EXPORT_SYMBOL_GPL(_snd_hdac_read_parm); /** * snd_hdac_get_sub_nodes - get start NID and number of subtree nodes diff --git a/sound/pci/hda/hda_codec.h b/sound/pci/hda/hda_codec.h index 0f5749ca1600..135b70f066f1 100644 --- a/sound/pci/hda/hda_codec.h +++ b/sound/pci/hda/hda_codec.h @@ -369,7 +369,7 @@ unsigned int snd_hda_codec_read(struct hda_codec *codec, hda_nid_t nid, int snd_hda_codec_write(struct hda_codec *codec, hda_nid_t nid, int flags, unsigned int verb, unsigned int parm); #define snd_hda_param_read(codec, nid, param) \ - snd_hda_codec_read(codec, nid, 0, AC_VERB_PARAMETERS, param) + snd_hdac_read_parm(&(codec)->core, nid, param) #define snd_hda_get_sub_nodes(codec, nid, start_nid) \ snd_hdac_get_sub_nodes(&(codec)->core, nid, start_nid) int snd_hda_get_connections(struct hda_codec *codec, hda_nid_t nid, -- cgit v1.2.3 From 9ba17b4d132f56a680fa1ba0bc2a8f98b6263d93 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 3 Mar 2015 23:29:47 +0100 Subject: ALSA: hda - Implement uncached version of parameter reads Sometimes we need the uncached reads, e.g. for refreshing the tree. This patch provides the helper function for that and uses it for refreshing widgets, reading subtrees and the whole proc reads. Signed-off-by: Takashi Iwai --- include/sound/hdaudio.h | 2 ++ sound/hda/hdac_device.c | 26 +++++++++++++++++++++++++- sound/pci/hda/hda_codec.c | 4 ++-- sound/pci/hda/hda_proc.c | 28 ++++++++++++++-------------- 4 files changed, 43 insertions(+), 17 deletions(-) (limited to 'include') diff --git a/include/sound/hdaudio.h b/include/sound/hdaudio.h index ddfcc44970fa..65ea6429f3a7 100644 --- a/include/sound/hdaudio.h +++ b/include/sound/hdaudio.h @@ -107,6 +107,8 @@ int snd_hdac_read(struct hdac_device *codec, hda_nid_t nid, unsigned int verb, unsigned int parm, unsigned int *res); int _snd_hdac_read_parm(struct hdac_device *codec, hda_nid_t nid, int parm, unsigned int *res); +int snd_hdac_read_parm_uncached(struct hdac_device *codec, hda_nid_t nid, + int parm); int snd_hdac_get_connections(struct hdac_device *codec, hda_nid_t nid, hda_nid_t *conn_list, int max_conns); int snd_hdac_get_sub_nodes(struct hdac_device *codec, hda_nid_t nid, diff --git a/sound/hda/hdac_device.c b/sound/hda/hdac_device.c index ba9c1fc6e3ea..0dac746df7da 100644 --- a/sound/hda/hdac_device.c +++ b/sound/hda/hdac_device.c @@ -249,6 +249,29 @@ int _snd_hdac_read_parm(struct hdac_device *codec, hda_nid_t nid, int parm, } EXPORT_SYMBOL_GPL(_snd_hdac_read_parm); +/** + * snd_hdac_read_parm_uncached - read a codec parameter without caching + * @codec: the codec object + * @nid: NID to read a parameter + * @parm: parameter to read + * + * Returns -1 for error. If you need to distinguish the error more + * strictly, use snd_hdac_read() directly. + */ +int snd_hdac_read_parm_uncached(struct hdac_device *codec, hda_nid_t nid, + int parm) +{ + int val; + + if (codec->regmap) + regcache_cache_bypass(codec->regmap, true); + val = snd_hdac_read_parm(codec, nid, parm); + if (codec->regmap) + regcache_cache_bypass(codec->regmap, false); + return val; +} +EXPORT_SYMBOL_GPL(snd_hdac_read_parm_uncached); + /** * snd_hdac_get_sub_nodes - get start NID and number of subtree nodes * @codec: the codec object @@ -256,13 +279,14 @@ EXPORT_SYMBOL_GPL(_snd_hdac_read_parm); * @start_id: the pointer to store the starting NID * * Returns the number of subtree nodes or zero if not found. + * This function reads parameters always without caching. */ int snd_hdac_get_sub_nodes(struct hdac_device *codec, hda_nid_t nid, hda_nid_t *start_id) { unsigned int parm; - parm = snd_hdac_read_parm(codec, nid, AC_PAR_NODE_COUNT); + parm = snd_hdac_read_parm_uncached(codec, nid, AC_PAR_NODE_COUNT); if (parm == -1) { *start_id = 0; return 0; diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index 8eb42f4226ff..39b5660653f0 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -586,8 +586,8 @@ static int read_widget_caps(struct hda_codec *codec, hda_nid_t fg_node) return -ENOMEM; nid = codec->core.start_nid; for (i = 0; i < codec->core.num_nodes; i++, nid++) - codec->wcaps[i] = snd_hda_param_read(codec, nid, - AC_PAR_AUDIO_WIDGET_CAP); + codec->wcaps[i] = snd_hdac_read_parm_uncached(&codec->core, + nid, AC_PAR_AUDIO_WIDGET_CAP); return 0; } diff --git a/sound/pci/hda/hda_proc.c b/sound/pci/hda/hda_proc.c index a4f5a30f1d41..ee6230767c64 100644 --- a/sound/pci/hda/hda_proc.c +++ b/sound/pci/hda/hda_proc.c @@ -32,6 +32,10 @@ static int dump_coef = -1; module_param(dump_coef, int, 0644); MODULE_PARM_DESC(dump_coef, "Dump processing coefficients in codec proc file (-1=auto, 0=disable, 1=enable)"); +/* always use noncached version */ +#define param_read(codec, nid, parm) \ + snd_hdac_read_parm_uncached(&(codec)->core, nid, parm) + static char *bits_names(unsigned int bits, char *names[], int size) { int i, n; @@ -119,9 +123,8 @@ static void print_amp_caps(struct snd_info_buffer *buffer, struct hda_codec *codec, hda_nid_t nid, int dir) { unsigned int caps; - caps = snd_hda_param_read(codec, nid, - dir == HDA_OUTPUT ? - AC_PAR_AMP_OUT_CAP : AC_PAR_AMP_IN_CAP); + caps = param_read(codec, nid, dir == HDA_OUTPUT ? + AC_PAR_AMP_OUT_CAP : AC_PAR_AMP_IN_CAP); if (caps == -1 || caps == 0) { snd_iprintf(buffer, "N/A\n"); return; @@ -225,8 +228,8 @@ static void print_pcm_formats(struct snd_info_buffer *buffer, static void print_pcm_caps(struct snd_info_buffer *buffer, struct hda_codec *codec, hda_nid_t nid) { - unsigned int pcm = snd_hda_param_read(codec, nid, AC_PAR_PCM); - unsigned int stream = snd_hda_param_read(codec, nid, AC_PAR_STREAM); + unsigned int pcm = param_read(codec, nid, AC_PAR_PCM); + unsigned int stream = param_read(codec, nid, AC_PAR_STREAM); if (pcm == -1 || stream == -1) { snd_iprintf(buffer, "N/A\n"); return; @@ -273,7 +276,7 @@ static void print_pin_caps(struct snd_info_buffer *buffer, static char *jack_conns[4] = { "Jack", "N/A", "Fixed", "Both" }; unsigned int caps, val; - caps = snd_hda_param_read(codec, nid, AC_PAR_PIN_CAP); + caps = param_read(codec, nid, AC_PAR_PIN_CAP); snd_iprintf(buffer, " Pincap 0x%08x:", caps); if (caps & AC_PINCAP_IN) snd_iprintf(buffer, " IN"); @@ -401,8 +404,7 @@ static void print_pin_ctls(struct snd_info_buffer *buffer, static void print_vol_knob(struct snd_info_buffer *buffer, struct hda_codec *codec, hda_nid_t nid) { - unsigned int cap = snd_hda_param_read(codec, nid, - AC_PAR_VOL_KNB_CAP); + unsigned int cap = param_read(codec, nid, AC_PAR_VOL_KNB_CAP); snd_iprintf(buffer, " Volume-Knob: delta=%d, steps=%d, ", (cap >> 7) & 1, cap & 0x7f); cap = snd_hda_codec_read(codec, nid, 0, @@ -487,7 +489,7 @@ static void print_power_state(struct snd_info_buffer *buffer, [ilog2(AC_PWRST_EPSS)] = "EPSS", }; - int sup = snd_hda_param_read(codec, nid, AC_PAR_POWER_STATE); + int sup = param_read(codec, nid, AC_PAR_POWER_STATE); int pwr = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_POWER_STATE, 0); if (sup != -1) @@ -531,8 +533,7 @@ static void print_proc_caps(struct snd_info_buffer *buffer, struct hda_codec *codec, hda_nid_t nid) { unsigned int i, ncoeff, oldindex; - unsigned int proc_caps = snd_hda_param_read(codec, nid, - AC_PAR_PROC_CAP); + unsigned int proc_caps = param_read(codec, nid, AC_PAR_PROC_CAP); ncoeff = (proc_caps & AC_PCAP_NUM_COEF) >> AC_PCAP_NUM_COEF_SHIFT; snd_iprintf(buffer, " Processing caps: benign=%d, ncoeff=%d\n", proc_caps & AC_PCAP_BENIGN, ncoeff); @@ -597,7 +598,7 @@ static void print_gpio(struct snd_info_buffer *buffer, struct hda_codec *codec, hda_nid_t nid) { unsigned int gpio = - snd_hda_param_read(codec, codec->core.afg, AC_PAR_GPIO_CAP); + param_read(codec, codec->core.afg, AC_PAR_GPIO_CAP); unsigned int enable, direction, wake, unsol, sticky, data; int i, max; snd_iprintf(buffer, "GPIO: io=%d, o=%d, i=%d, " @@ -727,8 +728,7 @@ static void print_codec_info(struct snd_info_entry *entry, for (i = 0; i < nodes; i++, nid++) { unsigned int wid_caps = - snd_hda_param_read(codec, nid, - AC_PAR_AUDIO_WIDGET_CAP); + param_read(codec, nid, AC_PAR_AUDIO_WIDGET_CAP); unsigned int wid_type = get_wcaps_type(wid_caps); hda_nid_t *conn = NULL; int conn_len = 0; -- cgit v1.2.3 From faa75f8a2edf005a5caf43be4875ffeeb9bcb498 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 26 Feb 2015 08:54:56 +0100 Subject: ALSA: hda - Use regmap for parameter caches, too The amp hash table was used for recording the cached reads of some capability values like pin caps or amp caps. Now all these are moved to regmap as well. One addition to the regmap helper is codec->caps_overwriting flag. This is set in snd_hdac_override_parm(), and the regmap helper accepts any register while this flag is set, so that it can overwrite even the read-only verb like AC_VERB_PARAMETERS. The flag is cleared immediately in snd_hdac_override_parm(), as it's a once-off flag. Along with these changes, the no longer needed amp hash and relevant fields are removed from hda_codec struct now. Signed-off-by: Takashi Iwai --- include/sound/hdaudio.h | 3 + sound/hda/hdac_device.c | 23 ++++++++ sound/hda/hdac_regmap.c | 8 +++ sound/pci/hda/hda_codec.c | 144 +++++----------------------------------------- sound/pci/hda/hda_codec.h | 7 --- sound/pci/hda/hda_local.h | 38 +++++++++++- 6 files changed, 82 insertions(+), 141 deletions(-) (limited to 'include') diff --git a/include/sound/hdaudio.h b/include/sound/hdaudio.h index 65ea6429f3a7..ce7d8d1f59c6 100644 --- a/include/sound/hdaudio.h +++ b/include/sound/hdaudio.h @@ -76,6 +76,7 @@ struct hdac_device { /* regmap */ struct regmap *regmap; bool lazy_cache:1; /* don't wake up for writes */ + bool caps_overwriting:1; /* caps overwrite being in process */ }; /* device/driver type used for matching */ @@ -109,6 +110,8 @@ int _snd_hdac_read_parm(struct hdac_device *codec, hda_nid_t nid, int parm, unsigned int *res); int snd_hdac_read_parm_uncached(struct hdac_device *codec, hda_nid_t nid, int parm); +int snd_hdac_override_parm(struct hdac_device *codec, hda_nid_t nid, + unsigned int parm, unsigned int val); int snd_hdac_get_connections(struct hdac_device *codec, hda_nid_t nid, hda_nid_t *conn_list, int max_conns); int snd_hdac_get_sub_nodes(struct hdac_device *codec, hda_nid_t nid, diff --git a/sound/hda/hdac_device.c b/sound/hda/hdac_device.c index 0dac746df7da..72c584eb011b 100644 --- a/sound/hda/hdac_device.c +++ b/sound/hda/hdac_device.c @@ -272,6 +272,29 @@ int snd_hdac_read_parm_uncached(struct hdac_device *codec, hda_nid_t nid, } EXPORT_SYMBOL_GPL(snd_hdac_read_parm_uncached); +/** + * snd_hdac_override_parm - override read-only parameters + * @codec: the codec object + * @nid: NID for the parameter + * @parm: the parameter to change + * @val: the parameter value to overwrite + */ +int snd_hdac_override_parm(struct hdac_device *codec, hda_nid_t nid, + unsigned int parm, unsigned int val) +{ + unsigned int verb = (AC_VERB_PARAMETERS << 8) | (nid << 20) | parm; + int err; + + if (!codec->regmap) + return -EINVAL; + + codec->caps_overwriting = true; + err = snd_hdac_regmap_write_raw(codec, verb, val); + codec->caps_overwriting = false; + return err; +} +EXPORT_SYMBOL_GPL(snd_hdac_override_parm); + /** * snd_hdac_get_sub_nodes - get start NID and number of subtree nodes * @codec: the codec object diff --git a/sound/hda/hdac_regmap.c b/sound/hda/hdac_regmap.c index db03d60d9c99..933907b16457 100644 --- a/sound/hda/hdac_regmap.c +++ b/sound/hda/hdac_regmap.c @@ -58,8 +58,12 @@ static bool hda_volatile_reg(struct device *dev, unsigned int reg) static bool hda_writeable_reg(struct device *dev, unsigned int reg) { + struct hdac_device *codec = dev_to_hdac_dev(dev); unsigned int verb = get_verb(reg); + if (codec->caps_overwriting) + return true; + switch (verb & 0xf00) { case AC_VERB_GET_STREAM_FORMAT: case AC_VERB_GET_AMP_GAIN_MUTE: @@ -97,8 +101,12 @@ static bool hda_writeable_reg(struct device *dev, unsigned int reg) static bool hda_readable_reg(struct device *dev, unsigned int reg) { + struct hdac_device *codec = dev_to_hdac_dev(dev); unsigned int verb = get_verb(reg); + if (codec->caps_overwriting) + return true; + switch (verb) { case AC_VERB_PARAMETERS: case AC_VERB_GET_CONNECT_LIST: diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index 52962f697825..b27f250088c1 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -929,9 +929,7 @@ void snd_hda_codec_cleanup_for_unbind(struct hda_codec *codec) codec->proc_widget_hook = NULL; codec->spec = NULL; - free_hda_cache(&codec->amp_cache); free_hda_cache(&codec->cmd_cache); - init_hda_cache(&codec->amp_cache, sizeof(struct hda_amp_info)); init_hda_cache(&codec->cmd_cache, sizeof(struct hda_cache_head)); /* free only driver_pins so that init_pins + user_pins are restored */ @@ -996,7 +994,6 @@ static void snd_hda_codec_dev_release(struct device *dev) free_init_pincfgs(codec); snd_hdac_device_exit(&codec->core); snd_hda_sysfs_clear(codec); - free_hda_cache(&codec->amp_cache); free_hda_cache(&codec->cmd_cache); kfree(codec->modelname); kfree(codec->wcaps); @@ -1051,7 +1048,6 @@ int snd_hda_codec_new(struct hda_bus *bus, struct snd_card *card, mutex_init(&codec->spdif_mutex); mutex_init(&codec->control_mutex); mutex_init(&codec->hash_mutex); - init_hda_cache(&codec->amp_cache, sizeof(struct hda_amp_info)); init_hda_cache(&codec->cmd_cache, sizeof(struct hda_cache_head)); snd_array_init(&codec->mixers, sizeof(struct hda_nid_item), 32); snd_array_init(&codec->nids, sizeof(struct hda_nid_item), 32); @@ -1380,67 +1376,6 @@ static struct hda_cache_head *get_alloc_hash(struct hda_cache_rec *cache, return info; } -/* query and allocate an amp hash entry */ -static inline struct hda_amp_info * -get_alloc_amp_hash(struct hda_codec *codec, u32 key) -{ - return (struct hda_amp_info *)get_alloc_hash(&codec->amp_cache, key); -} - -/* overwrite the value with the key in the caps hash */ -static int write_caps_hash(struct hda_codec *codec, u32 key, unsigned int val) -{ - struct hda_amp_info *info; - - mutex_lock(&codec->hash_mutex); - info = get_alloc_amp_hash(codec, key); - if (!info) { - mutex_unlock(&codec->hash_mutex); - return -EINVAL; - } - info->amp_caps = val; - info->head.val |= INFO_AMP_CAPS; - mutex_unlock(&codec->hash_mutex); - return 0; -} - -/* query the value from the caps hash; if not found, fetch the current - * value from the given function and store in the hash - */ -static unsigned int -query_caps_hash(struct hda_codec *codec, hda_nid_t nid, int dir, u32 key, - unsigned int (*func)(struct hda_codec *, hda_nid_t, int)) -{ - struct hda_amp_info *info; - unsigned int val; - - mutex_lock(&codec->hash_mutex); - info = get_alloc_amp_hash(codec, key); - if (!info) { - mutex_unlock(&codec->hash_mutex); - return 0; - } - if (!(info->head.val & INFO_AMP_CAPS)) { - mutex_unlock(&codec->hash_mutex); /* for reentrance */ - val = func(codec, nid, dir); - write_caps_hash(codec, key, val); - } else { - val = info->amp_caps; - mutex_unlock(&codec->hash_mutex); - } - return val; -} - -static unsigned int read_amp_cap(struct hda_codec *codec, hda_nid_t nid, - int direction) -{ - if (!(get_wcaps(codec, nid) & AC_WCAP_AMP_OVRD)) - nid = codec->core.afg; - return snd_hda_param_read(codec, nid, - direction == HDA_OUTPUT ? - AC_PAR_AMP_OUT_CAP : AC_PAR_AMP_IN_CAP); -} - /** * query_amp_caps - query AMP capabilities * @codec: the HD-auio codec @@ -1455,9 +1390,11 @@ static unsigned int read_amp_cap(struct hda_codec *codec, hda_nid_t nid, */ u32 query_amp_caps(struct hda_codec *codec, hda_nid_t nid, int direction) { - return query_caps_hash(codec, nid, direction, - HDA_HASH_KEY(nid, direction, 0), - read_amp_cap); + if (!(get_wcaps(codec, nid) & AC_WCAP_AMP_OVRD)) + nid = codec->core.afg; + return snd_hda_param_read(codec, nid, + direction == HDA_OUTPUT ? + AC_PAR_AMP_OUT_CAP : AC_PAR_AMP_IN_CAP); } EXPORT_SYMBOL_GPL(query_amp_caps); @@ -1498,50 +1435,14 @@ EXPORT_SYMBOL_GPL(snd_hda_check_amp_caps); int snd_hda_override_amp_caps(struct hda_codec *codec, hda_nid_t nid, int dir, unsigned int caps) { - return write_caps_hash(codec, HDA_HASH_KEY(nid, dir, 0), caps); -} -EXPORT_SYMBOL_GPL(snd_hda_override_amp_caps); - -static unsigned int read_pin_cap(struct hda_codec *codec, hda_nid_t nid, - int dir) -{ - return snd_hda_param_read(codec, nid, AC_PAR_PIN_CAP); -} - -/** - * snd_hda_query_pin_caps - Query PIN capabilities - * @codec: the HD-auio codec - * @nid: the NID to query - * - * Query PIN capabilities for the given widget. - * Returns the obtained capability bits. - * - * When cap bits have been already read, this doesn't read again but - * returns the cached value. - */ -u32 snd_hda_query_pin_caps(struct hda_codec *codec, hda_nid_t nid) -{ - return query_caps_hash(codec, nid, 0, HDA_HASH_PINCAP_KEY(nid), - read_pin_cap); -} -EXPORT_SYMBOL_GPL(snd_hda_query_pin_caps); + unsigned int parm; -/** - * snd_hda_override_pin_caps - Override the pin capabilities - * @codec: the CODEC - * @nid: the NID to override - * @caps: the capability bits to set - * - * Override the cached PIN capabilitiy bits value by the given one. - * - * Returns zero if successful or a negative error code. - */ -int snd_hda_override_pin_caps(struct hda_codec *codec, hda_nid_t nid, - unsigned int caps) -{ - return write_caps_hash(codec, HDA_HASH_PINCAP_KEY(nid), caps); + snd_hda_override_wcaps(codec, nid, + get_wcaps(codec, nid) | AC_WCAP_AMP_OVRD); + parm = dir == HDA_OUTPUT ? AC_PAR_AMP_OUT_CAP : AC_PAR_AMP_IN_CAP; + return snd_hdac_override_parm(&codec->core, nid, parm, caps); } -EXPORT_SYMBOL_GPL(snd_hda_override_pin_caps); +EXPORT_SYMBOL_GPL(snd_hda_override_amp_caps); /** * snd_hda_codec_amp_stereo - update the AMP stereo values @@ -3462,11 +3363,6 @@ static void hda_mark_cmd_cache_dirty(struct hda_codec *codec) cmd = snd_array_elem(&codec->cmd_cache.buf, i); cmd->dirty = 1; } - for (i = 0; i < codec->amp_cache.buf.used; i++) { - struct hda_amp_info *amp; - amp = snd_array_elem(&codec->amp_cache.buf, i); - amp->head.dirty = 1; - } } /* @@ -3714,8 +3610,7 @@ unsigned int snd_hda_calc_stream_format(struct hda_codec *codec, } EXPORT_SYMBOL_GPL(snd_hda_calc_stream_format); -static unsigned int get_pcm_param(struct hda_codec *codec, hda_nid_t nid, - int dir) +static unsigned int query_pcm_param(struct hda_codec *codec, hda_nid_t nid) { unsigned int val = 0; if (nid != codec->core.afg && @@ -3728,14 +3623,7 @@ static unsigned int get_pcm_param(struct hda_codec *codec, hda_nid_t nid, return val; } -static unsigned int query_pcm_param(struct hda_codec *codec, hda_nid_t nid) -{ - return query_caps_hash(codec, nid, 0, HDA_HASH_PARPCM_KEY(nid), - get_pcm_param); -} - -static unsigned int get_stream_param(struct hda_codec *codec, hda_nid_t nid, - int dir) +static unsigned int query_stream_param(struct hda_codec *codec, hda_nid_t nid) { unsigned int streams = snd_hda_param_read(codec, nid, AC_PAR_STREAM); if (!streams || streams == -1) @@ -3745,12 +3633,6 @@ static unsigned int get_stream_param(struct hda_codec *codec, hda_nid_t nid, return streams; } -static unsigned int query_stream_param(struct hda_codec *codec, hda_nid_t nid) -{ - return query_caps_hash(codec, nid, 0, HDA_HASH_PARSTR_KEY(nid), - get_stream_param); -} - /** * snd_hda_query_supported_pcm - query the supported PCM rates and formats * @codec: the HDA codec diff --git a/sound/pci/hda/hda_codec.h b/sound/pci/hda/hda_codec.h index 135b70f066f1..6af801a5bf89 100644 --- a/sound/pci/hda/hda_codec.h +++ b/sound/pci/hda/hda_codec.h @@ -163,12 +163,6 @@ struct hda_cache_head { u16 next; }; -struct hda_amp_info { - struct hda_cache_head head; - u32 amp_caps; /* amp capabilities */ - u16 vol[2]; /* current volume & mute */ -}; - struct hda_cache_rec { u16 hash[64]; /* hash table for index */ struct snd_array buf; /* record entries */ @@ -257,7 +251,6 @@ struct hda_codec { struct snd_array mixers; /* list of assigned mixer elements */ struct snd_array nids; /* list of mapped mixer elements */ - struct hda_cache_rec amp_cache; /* cache for amp access */ struct hda_cache_rec cmd_cache; /* cache for other commands */ struct list_head conn_list; /* linked-list of connection-list */ diff --git a/sound/pci/hda/hda_local.h b/sound/pci/hda/hda_local.h index 7023eeee8b9d..3b567f42296b 100644 --- a/sound/pci/hda/hda_local.h +++ b/sound/pci/hda/hda_local.h @@ -557,9 +557,41 @@ static inline void snd_hda_override_wcaps(struct hda_codec *codec, u32 query_amp_caps(struct hda_codec *codec, hda_nid_t nid, int direction); int snd_hda_override_amp_caps(struct hda_codec *codec, hda_nid_t nid, int dir, unsigned int caps); -u32 snd_hda_query_pin_caps(struct hda_codec *codec, hda_nid_t nid); -int snd_hda_override_pin_caps(struct hda_codec *codec, hda_nid_t nid, - unsigned int caps); +/** + * snd_hda_query_pin_caps - Query PIN capabilities + * @codec: the HD-auio codec + * @nid: the NID to query + * + * Query PIN capabilities for the given widget. + * Returns the obtained capability bits. + * + * When cap bits have been already read, this doesn't read again but + * returns the cached value. + */ +static inline u32 +snd_hda_query_pin_caps(struct hda_codec *codec, hda_nid_t nid) +{ + return snd_hda_param_read(codec, nid, AC_PAR_PIN_CAP); + +} + +/** + * snd_hda_override_pin_caps - Override the pin capabilities + * @codec: the CODEC + * @nid: the NID to override + * @caps: the capability bits to set + * + * Override the cached PIN capabilitiy bits value by the given one. + * + * Returns zero if successful or a negative error code. + */ +static inline int +snd_hda_override_pin_caps(struct hda_codec *codec, hda_nid_t nid, + unsigned int caps) +{ + return snd_hdac_override_parm(&codec->core, nid, AC_PAR_PIN_CAP, caps); +} + bool snd_hda_check_amp_caps(struct hda_codec *codec, hda_nid_t nid, int dir, unsigned int bits); -- cgit v1.2.3 From 5e56bcea5017b7b7808df60f21ef01738b6e1a25 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 26 Feb 2015 12:29:03 +0100 Subject: ALSA: hda - Allow driver to add vendor-specific verbs for regmap Codecs may have own vendor-specific verbs, and we need to allow each driver to give such verbs for cached accesses. Here a verb can be put into a single array and looked through it at readable and writeable callbacks. Signed-off-by: Takashi Iwai --- include/sound/hda_regmap.h | 3 ++- include/sound/hdaudio.h | 1 + sound/hda/hdac_regmap.c | 28 ++++++++++++++++++++++++++++ 3 files changed, 31 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/sound/hda_regmap.h b/include/sound/hda_regmap.h index 95651d26437d..a6a4f3ddb469 100644 --- a/include/sound/hda_regmap.h +++ b/include/sound/hda_regmap.h @@ -11,7 +11,8 @@ int snd_hdac_regmap_init(struct hdac_device *codec); void snd_hdac_regmap_exit(struct hdac_device *codec); - +int snd_hdac_regmap_add_vendor_verb(struct hdac_device *codec, + unsigned int verb); int snd_hdac_regmap_read_raw(struct hdac_device *codec, unsigned int reg, unsigned int *val); int snd_hdac_regmap_write_raw(struct hdac_device *codec, unsigned int reg, diff --git a/include/sound/hdaudio.h b/include/sound/hdaudio.h index ce7d8d1f59c6..702032598bea 100644 --- a/include/sound/hdaudio.h +++ b/include/sound/hdaudio.h @@ -75,6 +75,7 @@ struct hdac_device { /* regmap */ struct regmap *regmap; + struct snd_array vendor_verbs; bool lazy_cache:1; /* don't wake up for writes */ bool caps_overwriting:1; /* caps overwrite being in process */ }; diff --git a/sound/hda/hdac_regmap.c b/sound/hda/hdac_regmap.c index 933907b16457..486ef720cbff 100644 --- a/sound/hda/hdac_regmap.c +++ b/sound/hda/hdac_regmap.c @@ -60,6 +60,13 @@ static bool hda_writeable_reg(struct device *dev, unsigned int reg) { struct hdac_device *codec = dev_to_hdac_dev(dev); unsigned int verb = get_verb(reg); + int i; + + for (i = 0; i < codec->vendor_verbs.used; i++) { + unsigned int *v = snd_array_elem(&codec->vendor_verbs, i); + if (verb == *v) + return true; + } if (codec->caps_overwriting) return true; @@ -200,6 +207,7 @@ int snd_hdac_regmap_init(struct hdac_device *codec) if (IS_ERR(regmap)) return PTR_ERR(regmap); codec->regmap = regmap; + snd_array_init(&codec->vendor_verbs, sizeof(unsigned int), 8); return 0; } EXPORT_SYMBOL_GPL(snd_hdac_regmap_init); @@ -209,10 +217,30 @@ void snd_hdac_regmap_exit(struct hdac_device *codec) if (codec->regmap) { regmap_exit(codec->regmap); codec->regmap = NULL; + snd_array_free(&codec->vendor_verbs); } } EXPORT_SYMBOL_GPL(snd_hdac_regmap_exit); +/** + * snd_hdac_regmap_add_vendor_verb - add a vendor-specific verb to regmap + * @codec: the codec object + * @verb: verb to allow accessing via regmap + * + * Returns zero for success or a negative error code. + */ +int snd_hdac_regmap_add_vendor_verb(struct hdac_device *codec, + unsigned int verb) +{ + unsigned int *p = snd_array_new(&codec->vendor_verbs); + + if (!p) + return -ENOMEM; + *p = verb; + return 0; +} +EXPORT_SYMBOL_GPL(snd_hdac_regmap_add_vendor_verb); + /* * helper functions */ -- cgit v1.2.3 From d313e0a88d1b29d17198ef659af042a633a2d3de Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 4 Mar 2015 20:43:20 +0100 Subject: ALSA: hda - Add a fake stereo amp register support HD-audio spec is inconvenient regarding the handling of stereo volume controls. It can set and get only single channel at once (although there is a special option to set the same value to both channels). This patch provides a fake pseudo-register via the regmap access so that the stereo channels can be read and written by a single call. It'd be useful, for example, for implementing DAPM widgets. A stereo amp pseudo register consists of the encoding like the normal amp verbs but it has both SET_LEFT (bit 13) and SET_RIGHT (bit 12) bits set. The regmap reads and writes a 16bit value for this pseudo register where the upper 8bit is for the right chanel and the lower 8bit for the left channel. Note that the driver doesn't recognize conflicts when both stereo and mono channel registers are mixed. Mixing them would certainly confuse the operation. So, use carefully. Signed-off-by: Takashi Iwai --- include/sound/hda_regmap.h | 59 ++++++++++++++++++++++++++++++++++++++ sound/hda/hdac_regmap.c | 71 +++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 129 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/sound/hda_regmap.h b/include/sound/hda_regmap.h index a6a4f3ddb469..76648ccfbbf8 100644 --- a/include/sound/hda_regmap.h +++ b/include/sound/hda_regmap.h @@ -45,6 +45,20 @@ int snd_hdac_regmap_update_raw(struct hdac_device *codec, unsigned int reg, ((dir) == HDA_OUTPUT ? AC_AMP_GET_OUTPUT : AC_AMP_GET_INPUT) | \ (idx)) +/** + * snd_hdac_regmap_encode_amp_stereo - encode a pseudo register for stereo AMPs + * @nid: widget NID + * @dir: direction (#HDA_INPUT, #HDA_OUTPUT) + * @idx: input index value + * + * Returns an encoded pseudo register. + */ +#define snd_hdac_regmap_encode_amp_stereo(nid, dir, idx) \ + (snd_hdac_regmap_encode_verb(nid, AC_VERB_GET_AMP_GAIN_MUTE) | \ + AC_AMP_SET_LEFT | AC_AMP_SET_RIGHT | /* both bits set! */ \ + ((dir) == HDA_OUTPUT ? AC_AMP_GET_OUTPUT : AC_AMP_GET_INPUT) | \ + (idx)) + /** * snd_hdac_regmap_write - Write a verb with caching * @nid: codec NID @@ -143,4 +157,49 @@ snd_hdac_regmap_update_amp(struct hdac_device *codec, hda_nid_t nid, return snd_hdac_regmap_update_raw(codec, cmd, mask, val); } +/** + * snd_hdac_regmap_get_amp_stereo - Read stereo AMP values + * @codec: HD-audio codec + * @nid: NID to read the AMP value + * @ch: channel (left=0 or right=1) + * @direction: #HDA_INPUT or #HDA_OUTPUT + * @index: the index value (only for input direction) + * @val: the pointer to store the value + * + * Read stereo AMP values. The lower byte is left, the upper byte is right. + * Returns the value or a negative error. + */ +static inline int +snd_hdac_regmap_get_amp_stereo(struct hdac_device *codec, hda_nid_t nid, + int dir, int idx) +{ + unsigned int cmd = snd_hdac_regmap_encode_amp_stereo(nid, dir, idx); + int err, val; + + err = snd_hdac_regmap_read_raw(codec, cmd, &val); + return err < 0 ? err : val; +} + +/** + * snd_hdac_regmap_update_amp_stereo - update the stereo AMP value + * @codec: HD-audio codec + * @nid: NID to read the AMP value + * @direction: #HDA_INPUT or #HDA_OUTPUT + * @idx: the index value (only for input direction) + * @mask: bit mask to set + * @val: the bits value to set + * + * Update the stereo AMP value with a bit mask. + * The lower byte is left, the upper byte is right. + * Returns 0 if the value is unchanged, 1 if changed, or a negative error. + */ +static inline int +snd_hdac_regmap_update_amp_stereo(struct hdac_device *codec, hda_nid_t nid, + int dir, int idx, int mask, int val) +{ + unsigned int cmd = snd_hdac_regmap_encode_amp_stereo(nid, dir, idx); + + return snd_hdac_regmap_update_raw(codec, cmd, mask, val); +} + #endif /* __SOUND_HDA_REGMAP_H */ diff --git a/sound/hda/hdac_regmap.c b/sound/hda/hdac_regmap.c index 486ef720cbff..fb4a02e0319f 100644 --- a/sound/hda/hdac_regmap.c +++ b/sound/hda/hdac_regmap.c @@ -124,6 +124,70 @@ static bool hda_readable_reg(struct device *dev, unsigned int reg) return hda_writeable_reg(dev, reg); } +/* + * Stereo amp pseudo register: + * for making easier to handle the stereo volume control, we provide a + * fake register to deal both left and right channels by a single + * (pseudo) register access. A verb consisting of SET_AMP_GAIN with + * *both* SET_LEFT and SET_RIGHT bits takes a 16bit value, the lower 8bit + * for the left and the upper 8bit for the right channel. + */ +static bool is_stereo_amp_verb(unsigned int reg) +{ + if (((reg >> 8) & 0x700) != AC_VERB_SET_AMP_GAIN_MUTE) + return false; + return (reg & (AC_AMP_SET_LEFT | AC_AMP_SET_RIGHT)) == + (AC_AMP_SET_LEFT | AC_AMP_SET_RIGHT); +} + +/* read a pseudo stereo amp register (16bit left+right) */ +static int hda_reg_read_stereo_amp(struct hdac_device *codec, + unsigned int reg, unsigned int *val) +{ + unsigned int left, right; + int err; + + reg &= ~(AC_AMP_SET_LEFT | AC_AMP_SET_RIGHT); + err = snd_hdac_exec_verb(codec, reg | AC_AMP_GET_LEFT, 0, &left); + if (err < 0) + return err; + err = snd_hdac_exec_verb(codec, reg | AC_AMP_GET_RIGHT, 0, &right); + if (err < 0) + return err; + *val = left | (right << 8); + return 0; +} + +/* write a pseudo stereo amp register (16bit left+right) */ +static int hda_reg_write_stereo_amp(struct hdac_device *codec, + unsigned int reg, unsigned int val) +{ + int err; + unsigned int verb, left, right; + + verb = AC_VERB_SET_AMP_GAIN_MUTE << 8; + if (reg & AC_AMP_GET_OUTPUT) + verb |= AC_AMP_SET_OUTPUT; + else + verb |= AC_AMP_SET_INPUT | ((reg & 0xf) << 8); + reg = (reg & ~0xfffff) | verb; + + left = val & 0xff; + right = (val >> 8) & 0xff; + if (left == right) { + reg |= AC_AMP_SET_LEFT | AC_AMP_SET_RIGHT; + return snd_hdac_exec_verb(codec, reg | left, 0, NULL); + } + + err = snd_hdac_exec_verb(codec, reg | AC_AMP_SET_LEFT | left, 0, NULL); + if (err < 0) + return err; + err = snd_hdac_exec_verb(codec, reg | AC_AMP_SET_RIGHT | right, 0, NULL); + if (err < 0) + return err; + return 0; +} + static int hda_reg_read(void *context, unsigned int reg, unsigned int *val) { struct hdac_device *codec = context; @@ -131,6 +195,8 @@ static int hda_reg_read(void *context, unsigned int reg, unsigned int *val) if (!codec_is_running(codec)) return -EAGAIN; reg |= (codec->addr << 28); + if (is_stereo_amp_verb(reg)) + return hda_reg_read_stereo_amp(codec, reg, val); return snd_hdac_exec_verb(codec, reg, 0, val); } @@ -145,8 +211,11 @@ static int hda_reg_write(void *context, unsigned int reg, unsigned int val) reg &= ~0x00080000U; /* drop GET bit */ reg |= (codec->addr << 28); - verb = get_verb(reg); + if (is_stereo_amp_verb(reg)) + return hda_reg_write_stereo_amp(codec, reg, val); + + verb = get_verb(reg); switch (verb & 0xf00) { case AC_VERB_SET_AMP_GAIN_MUTE: verb = AC_VERB_SET_AMP_GAIN_MUTE; -- cgit v1.2.3 From 40ba66a702b83f46c53456eaaac692fc12f82cb0 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 13 Mar 2015 15:56:25 +0100 Subject: ALSA: hda - Add cache support for COEF read/write The 16bit COEF read/write is pretty standard for many codecs, and they can be cached in most cases -- more importantly, they need to be restored at resume. For making this easier, add the cache support to regmap. If the codec driver wants to cache the COEF access, set codec->cache_coef flag and issue AC_VERB_GET_PROC_COEF with the coef index in LSB 8 bits. Signed-off-by: Takashi Iwai --- include/sound/hdaudio.h | 1 + sound/hda/hdac_regmap.c | 49 ++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 49 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/sound/hdaudio.h b/include/sound/hdaudio.h index 702032598bea..95acc337aea5 100644 --- a/include/sound/hdaudio.h +++ b/include/sound/hdaudio.h @@ -78,6 +78,7 @@ struct hdac_device { struct snd_array vendor_verbs; bool lazy_cache:1; /* don't wake up for writes */ bool caps_overwriting:1; /* caps overwrite being in process */ + bool cache_coef:1; /* cache COEF read/write too */ }; /* device/driver type used for matching */ diff --git a/sound/hda/hdac_regmap.c b/sound/hda/hdac_regmap.c index 2eea8d4e6a7f..e1dcf104d273 100644 --- a/sound/hda/hdac_regmap.c +++ b/sound/hda/hdac_regmap.c @@ -33,10 +33,12 @@ static bool hda_volatile_reg(struct device *dev, unsigned int reg) { + struct hdac_device *codec = dev_to_hdac_dev(dev); unsigned int verb = get_verb(reg); switch (verb) { case AC_VERB_GET_PROC_COEF: + return !codec->cache_coef; case AC_VERB_GET_COEF_INDEX: case AC_VERB_GET_PROC_STATE: case AC_VERB_GET_POWER_STATE: @@ -75,6 +77,8 @@ static bool hda_writeable_reg(struct device *dev, unsigned int reg) case AC_VERB_GET_STREAM_FORMAT: case AC_VERB_GET_AMP_GAIN_MUTE: return true; + case AC_VERB_GET_PROC_COEF: + return codec->cache_coef; case 0xf00: break; default: @@ -188,9 +192,47 @@ static int hda_reg_write_stereo_amp(struct hdac_device *codec, return 0; } +/* read a pseudo coef register (16bit) */ +static int hda_reg_read_coef(struct hdac_device *codec, unsigned int reg, + unsigned int *val) +{ + unsigned int verb; + int err; + + if (!codec->cache_coef) + return -EINVAL; + /* LSB 8bit = coef index */ + verb = (reg & ~0xfff00) | (AC_VERB_SET_COEF_INDEX << 8); + err = snd_hdac_exec_verb(codec, verb, 0, NULL); + if (err < 0) + return err; + verb = (reg & ~0xfffff) | (AC_VERB_GET_COEF_INDEX << 8); + return snd_hdac_exec_verb(codec, verb, 0, val); +} + +/* write a pseudo coef register (16bit) */ +static int hda_reg_write_coef(struct hdac_device *codec, unsigned int reg, + unsigned int val) +{ + unsigned int verb; + int err; + + if (!codec->cache_coef) + return -EINVAL; + /* LSB 8bit = coef index */ + verb = (reg & ~0xfff00) | (AC_VERB_SET_COEF_INDEX << 8); + err = snd_hdac_exec_verb(codec, verb, 0, NULL); + if (err < 0) + return err; + verb = (reg & ~0xfffff) | (AC_VERB_GET_COEF_INDEX << 8) | + (val & 0xffff); + return snd_hdac_exec_verb(codec, verb, 0, NULL); +} + static int hda_reg_read(void *context, unsigned int reg, unsigned int *val) { struct hdac_device *codec = context; + int verb = get_verb(reg); int err; if (!codec_is_running(codec)) @@ -198,11 +240,13 @@ static int hda_reg_read(void *context, unsigned int reg, unsigned int *val) reg |= (codec->addr << 28); if (is_stereo_amp_verb(reg)) return hda_reg_read_stereo_amp(codec, reg, val); + if (verb == AC_VERB_GET_PROC_COEF) + return hda_reg_read_coef(codec, reg, val); err = snd_hdac_exec_verb(codec, reg, 0, val); if (err < 0) return err; /* special handling for asymmetric reads */ - if (get_verb(reg) == AC_VERB_GET_POWER_STATE) { + if (verb == AC_VERB_GET_POWER_STATE) { if (*val & AC_PWRST_ERROR) *val = -1; else /* take only the actual state */ @@ -227,6 +271,9 @@ static int hda_reg_write(void *context, unsigned int reg, unsigned int val) return hda_reg_write_stereo_amp(codec, reg, val); verb = get_verb(reg); + if (verb == AC_VERB_SET_PROC_COEF) + return hda_reg_write_coef(codec, reg, val); + switch (verb & 0xf00) { case AC_VERB_SET_AMP_GAIN_MUTE: verb = AC_VERB_SET_AMP_GAIN_MUTE; -- cgit v1.2.3 From f7234138f14c2296c5eb6b8224abe00b507faf3f Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Wed, 4 Mar 2015 01:47:54 -0800 Subject: [media] v4l2-subdev: replace v4l2_subdev_fh by v4l2_subdev_pad_config If a subdevice pad op is called from a bridge driver, then there is no v4l2_subdev_fh struct that can be passed to the subdevice. This made it hard to use such subdevs from a bridge driver. This patch replaces the v4l2_subdev_fh pointer by a v4l2_subdev_pad_config pointer in the pad ops. This allows bridge drivers to use the various try_ pad ops by creating a v4l2_subdev_pad_config struct and passing it along to the pad op. The v4l2_subdev_get_try_* macros had to be changed because of this, so I also took the opportunity to use the full name of the v4l2_subdev_get_try_* functions in the __V4L2_SUBDEV_MK_GET_TRY macro arguments: if you now do 'git grep v4l2_subdev_get_try_format' you will actually find the header where it is defined. One remark regarding the drivers/staging/media/davinci_vpfe patches: the *_init_formats() functions assumed that fh could be NULL. However, that's not true for this driver, it's always set. This is almost certainly a copy and paste from the omap3isp driver. I've updated the code to reflect the fact that fh is never NULL. Signed-off-by: Hans Verkuil Acked-by: Lad, Prabhakar Tested-by: Lad, Prabhakar Acked-by: Laurent Pinchart Acked-by: Sakari Ailus Acked-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/adv7180.c | 10 +-- drivers/media/i2c/adv7511.c | 16 +++-- drivers/media/i2c/adv7604.c | 12 ++-- drivers/media/i2c/m5mols/m5mols_core.c | 16 ++--- drivers/media/i2c/mt9m032.c | 34 ++++----- drivers/media/i2c/mt9p031.c | 36 +++++----- drivers/media/i2c/mt9t001.c | 36 +++++----- drivers/media/i2c/mt9v032.c | 36 +++++----- drivers/media/i2c/noon010pc30.c | 17 ++--- drivers/media/i2c/ov9650.c | 16 ++--- drivers/media/i2c/s5c73m3/s5c73m3-core.c | 51 +++++++------- drivers/media/i2c/s5k4ecgx.c | 16 ++--- drivers/media/i2c/s5k5baf.c | 38 +++++----- drivers/media/i2c/s5k6a3.c | 18 ++--- drivers/media/i2c/s5k6aa.c | 34 ++++----- drivers/media/i2c/smiapp/smiapp-core.c | 80 ++++++++++----------- drivers/media/i2c/tvp514x.c | 12 ++-- drivers/media/i2c/tvp7002.c | 14 ++-- drivers/media/platform/exynos4-is/fimc-capture.c | 22 +++--- drivers/media/platform/exynos4-is/fimc-isp.c | 28 ++++---- drivers/media/platform/exynos4-is/fimc-lite.c | 33 ++++----- drivers/media/platform/exynos4-is/mipi-csis.c | 16 ++--- drivers/media/platform/omap3isp/ispccdc.c | 82 +++++++++++----------- drivers/media/platform/omap3isp/ispccp2.c | 44 ++++++------ drivers/media/platform/omap3isp/ispcsi2.c | 40 +++++------ drivers/media/platform/omap3isp/isppreview.c | 70 +++++++++--------- drivers/media/platform/omap3isp/ispresizer.c | 78 ++++++++++---------- drivers/media/platform/s3c-camif/camif-capture.c | 18 ++--- drivers/media/platform/vsp1/vsp1_bru.c | 40 +++++------ drivers/media/platform/vsp1/vsp1_entity.c | 16 ++--- drivers/media/platform/vsp1/vsp1_entity.h | 4 +- drivers/media/platform/vsp1/vsp1_hsit.c | 16 ++--- drivers/media/platform/vsp1/vsp1_lif.c | 18 ++--- drivers/media/platform/vsp1/vsp1_lut.c | 18 ++--- drivers/media/platform/vsp1/vsp1_rwpf.c | 36 +++++----- drivers/media/platform/vsp1/vsp1_rwpf.h | 12 ++-- drivers/media/platform/vsp1/vsp1_sru.c | 26 +++---- drivers/media/platform/vsp1/vsp1_uds.c | 26 +++---- drivers/media/v4l2-core/v4l2-subdev.c | 18 ++--- drivers/staging/media/davinci_vpfe/dm365_ipipe.c | 49 +++++++------ drivers/staging/media/davinci_vpfe/dm365_ipipeif.c | 47 ++++++------- drivers/staging/media/davinci_vpfe/dm365_isif.c | 79 ++++++++++----------- drivers/staging/media/davinci_vpfe/dm365_resizer.c | 57 ++++++++------- drivers/staging/media/omap4iss/iss_csi2.c | 40 +++++------ drivers/staging/media/omap4iss/iss_ipipe.c | 42 +++++------ drivers/staging/media/omap4iss/iss_ipipeif.c | 48 ++++++------- drivers/staging/media/omap4iss/iss_resizer.c | 42 +++++------ include/media/v4l2-subdev.h | 53 ++++++++------ 48 files changed, 813 insertions(+), 797 deletions(-) (limited to 'include') diff --git a/drivers/media/i2c/adv7180.c b/drivers/media/i2c/adv7180.c index b75878c27c2a..a493c0b0b5fe 100644 --- a/drivers/media/i2c/adv7180.c +++ b/drivers/media/i2c/adv7180.c @@ -582,7 +582,7 @@ static void adv7180_exit_controls(struct adv7180_state *state) } static int adv7180_enum_mbus_code(struct v4l2_subdev *sd, - struct v4l2_subdev_fh *fh, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_mbus_code_enum *code) { if (code->index != 0) @@ -645,13 +645,13 @@ static int adv7180_set_field_mode(struct adv7180_state *state) } static int adv7180_get_pad_format(struct v4l2_subdev *sd, - struct v4l2_subdev_fh *fh, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_format *format) { struct adv7180_state *state = to_state(sd); if (format->which == V4L2_SUBDEV_FORMAT_TRY) { - format->format = *v4l2_subdev_get_try_format(fh, 0); + format->format = *v4l2_subdev_get_try_format(sd, cfg, 0); } else { adv7180_mbus_fmt(sd, &format->format); format->format.field = state->field; @@ -661,7 +661,7 @@ static int adv7180_get_pad_format(struct v4l2_subdev *sd, } static int adv7180_set_pad_format(struct v4l2_subdev *sd, - struct v4l2_subdev_fh *fh, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_format *format) { struct adv7180_state *state = to_state(sd); @@ -686,7 +686,7 @@ static int adv7180_set_pad_format(struct v4l2_subdev *sd, adv7180_set_power(state, true); } } else { - framefmt = v4l2_subdev_get_try_format(fh, 0); + framefmt = v4l2_subdev_get_try_format(sd, cfg, 0); *framefmt = format->format; } diff --git a/drivers/media/i2c/adv7511.c b/drivers/media/i2c/adv7511.c index 81736aaf0f31..03ea62f9c074 100644 --- a/drivers/media/i2c/adv7511.c +++ b/drivers/media/i2c/adv7511.c @@ -810,7 +810,7 @@ static int adv7511_get_edid(struct v4l2_subdev *sd, struct v4l2_edid *edid) } static int adv7511_enum_mbus_code(struct v4l2_subdev *sd, - struct v4l2_subdev_fh *fh, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_mbus_code_enum *code) { if (code->pad != 0) @@ -842,8 +842,9 @@ static void adv7511_fill_format(struct adv7511_state *state, format->field = V4L2_FIELD_NONE; } -static int adv7511_get_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, - struct v4l2_subdev_format *format) +static int adv7511_get_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *format) { struct adv7511_state *state = get_adv7511_state(sd); @@ -855,7 +856,7 @@ static int adv7511_get_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, if (format->which == V4L2_SUBDEV_FORMAT_TRY) { struct v4l2_mbus_framefmt *fmt; - fmt = v4l2_subdev_get_try_format(fh, format->pad); + fmt = v4l2_subdev_get_try_format(sd, cfg, format->pad); format->format.code = fmt->code; format->format.colorspace = fmt->colorspace; format->format.ycbcr_enc = fmt->ycbcr_enc; @@ -870,8 +871,9 @@ static int adv7511_get_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, return 0; } -static int adv7511_set_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, - struct v4l2_subdev_format *format) +static int adv7511_set_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *format) { struct adv7511_state *state = get_adv7511_state(sd); /* @@ -905,7 +907,7 @@ static int adv7511_set_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, if (format->which == V4L2_SUBDEV_FORMAT_TRY) { struct v4l2_mbus_framefmt *fmt; - fmt = v4l2_subdev_get_try_format(fh, format->pad); + fmt = v4l2_subdev_get_try_format(sd, cfg, format->pad); fmt->code = format->format.code; fmt->colorspace = format->format.colorspace; fmt->ycbcr_enc = format->format.ycbcr_enc; diff --git a/drivers/media/i2c/adv7604.c b/drivers/media/i2c/adv7604.c index aaab9c9cebee..1e58537c3ea3 100644 --- a/drivers/media/i2c/adv7604.c +++ b/drivers/media/i2c/adv7604.c @@ -1734,7 +1734,7 @@ static int adv76xx_s_routing(struct v4l2_subdev *sd, } static int adv76xx_enum_mbus_code(struct v4l2_subdev *sd, - struct v4l2_subdev_fh *fh, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_mbus_code_enum *code) { struct adv76xx_state *state = to_state(sd); @@ -1811,7 +1811,8 @@ static void adv76xx_setup_format(struct adv76xx_state *state) state->format->swap_cb_cr ? ADV76XX_OP_SWAP_CB_CR : 0); } -static int adv76xx_get_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, +static int adv76xx_get_format(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_format *format) { struct adv76xx_state *state = to_state(sd); @@ -1824,7 +1825,7 @@ static int adv76xx_get_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, if (format->which == V4L2_SUBDEV_FORMAT_TRY) { struct v4l2_mbus_framefmt *fmt; - fmt = v4l2_subdev_get_try_format(fh, format->pad); + fmt = v4l2_subdev_get_try_format(sd, cfg, format->pad); format->format.code = fmt->code; } else { format->format.code = state->format->code; @@ -1833,7 +1834,8 @@ static int adv76xx_get_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, return 0; } -static int adv76xx_set_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, +static int adv76xx_set_format(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_format *format) { struct adv76xx_state *state = to_state(sd); @@ -1852,7 +1854,7 @@ static int adv76xx_set_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, if (format->which == V4L2_SUBDEV_FORMAT_TRY) { struct v4l2_mbus_framefmt *fmt; - fmt = v4l2_subdev_get_try_format(fh, format->pad); + fmt = v4l2_subdev_get_try_format(sd, cfg, format->pad); fmt->code = format->format.code; } else { state->format = info; diff --git a/drivers/media/i2c/m5mols/m5mols_core.c b/drivers/media/i2c/m5mols/m5mols_core.c index 6ed16e569bbf..6404c0d93e7a 100644 --- a/drivers/media/i2c/m5mols/m5mols_core.c +++ b/drivers/media/i2c/m5mols/m5mols_core.c @@ -531,17 +531,17 @@ static int __find_resolution(struct v4l2_subdev *sd, } static struct v4l2_mbus_framefmt *__find_format(struct m5mols_info *info, - struct v4l2_subdev_fh *fh, + struct v4l2_subdev_pad_config *cfg, enum v4l2_subdev_format_whence which, enum m5mols_restype type) { if (which == V4L2_SUBDEV_FORMAT_TRY) - return fh ? v4l2_subdev_get_try_format(fh, 0) : NULL; + return cfg ? v4l2_subdev_get_try_format(&info->sd, cfg, 0) : NULL; return &info->ffmt[type]; } -static int m5mols_get_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, +static int m5mols_get_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_format *fmt) { struct m5mols_info *info = to_m5mols(sd); @@ -550,7 +550,7 @@ static int m5mols_get_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, mutex_lock(&info->lock); - format = __find_format(info, fh, fmt->which, info->res_type); + format = __find_format(info, cfg, fmt->which, info->res_type); if (format) fmt->format = *format; else @@ -560,7 +560,7 @@ static int m5mols_get_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, return ret; } -static int m5mols_set_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, +static int m5mols_set_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_format *fmt) { struct m5mols_info *info = to_m5mols(sd); @@ -574,7 +574,7 @@ static int m5mols_set_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, if (ret < 0) return ret; - sfmt = __find_format(info, fh, fmt->which, type); + sfmt = __find_format(info, cfg, fmt->which, type); if (!sfmt) return 0; @@ -640,7 +640,7 @@ static int m5mols_set_frame_desc(struct v4l2_subdev *sd, unsigned int pad, static int m5mols_enum_mbus_code(struct v4l2_subdev *sd, - struct v4l2_subdev_fh *fh, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_mbus_code_enum *code) { if (!code || code->index >= SIZE_DEFAULT_FFMT) @@ -895,7 +895,7 @@ static const struct v4l2_subdev_core_ops m5mols_core_ops = { */ static int m5mols_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) { - struct v4l2_mbus_framefmt *format = v4l2_subdev_get_try_format(fh, 0); + struct v4l2_mbus_framefmt *format = v4l2_subdev_get_try_format(sd, fh->pad, 0); *format = m5mols_default_ffmt[0]; return 0; diff --git a/drivers/media/i2c/mt9m032.c b/drivers/media/i2c/mt9m032.c index 76431223f0ff..c7747bd0cabb 100644 --- a/drivers/media/i2c/mt9m032.c +++ b/drivers/media/i2c/mt9m032.c @@ -317,7 +317,7 @@ static int mt9m032_setup_pll(struct mt9m032 *sensor) */ static int mt9m032_enum_mbus_code(struct v4l2_subdev *subdev, - struct v4l2_subdev_fh *fh, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_mbus_code_enum *code) { if (code->index != 0) @@ -328,7 +328,7 @@ static int mt9m032_enum_mbus_code(struct v4l2_subdev *subdev, } static int mt9m032_enum_frame_size(struct v4l2_subdev *subdev, - struct v4l2_subdev_fh *fh, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_frame_size_enum *fse) { if (fse->index != 0 || fse->code != MEDIA_BUS_FMT_Y8_1X8) @@ -345,18 +345,18 @@ static int mt9m032_enum_frame_size(struct v4l2_subdev *subdev, /** * __mt9m032_get_pad_crop() - get crop rect * @sensor: pointer to the sensor struct - * @fh: file handle for getting the try crop rect from + * @cfg: v4l2_subdev_pad_config for getting the try crop rect from * @which: select try or active crop rect * * Returns a pointer the current active or fh relative try crop rect */ static struct v4l2_rect * -__mt9m032_get_pad_crop(struct mt9m032 *sensor, struct v4l2_subdev_fh *fh, +__mt9m032_get_pad_crop(struct mt9m032 *sensor, struct v4l2_subdev_pad_config *cfg, enum v4l2_subdev_format_whence which) { switch (which) { case V4L2_SUBDEV_FORMAT_TRY: - return v4l2_subdev_get_try_crop(fh, 0); + return v4l2_subdev_get_try_crop(&sensor->subdev, cfg, 0); case V4L2_SUBDEV_FORMAT_ACTIVE: return &sensor->crop; default: @@ -367,18 +367,18 @@ __mt9m032_get_pad_crop(struct mt9m032 *sensor, struct v4l2_subdev_fh *fh, /** * __mt9m032_get_pad_format() - get format * @sensor: pointer to the sensor struct - * @fh: file handle for getting the try format from + * @cfg: v4l2_subdev_pad_config for getting the try format from * @which: select try or active format * * Returns a pointer the current active or fh relative try format */ static struct v4l2_mbus_framefmt * -__mt9m032_get_pad_format(struct mt9m032 *sensor, struct v4l2_subdev_fh *fh, +__mt9m032_get_pad_format(struct mt9m032 *sensor, struct v4l2_subdev_pad_config *cfg, enum v4l2_subdev_format_whence which) { switch (which) { case V4L2_SUBDEV_FORMAT_TRY: - return v4l2_subdev_get_try_format(fh, 0); + return v4l2_subdev_get_try_format(&sensor->subdev, cfg, 0); case V4L2_SUBDEV_FORMAT_ACTIVE: return &sensor->format; default: @@ -387,20 +387,20 @@ __mt9m032_get_pad_format(struct mt9m032 *sensor, struct v4l2_subdev_fh *fh, } static int mt9m032_get_pad_format(struct v4l2_subdev *subdev, - struct v4l2_subdev_fh *fh, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_format *fmt) { struct mt9m032 *sensor = to_mt9m032(subdev); mutex_lock(&sensor->lock); - fmt->format = *__mt9m032_get_pad_format(sensor, fh, fmt->which); + fmt->format = *__mt9m032_get_pad_format(sensor, cfg, fmt->which); mutex_unlock(&sensor->lock); return 0; } static int mt9m032_set_pad_format(struct v4l2_subdev *subdev, - struct v4l2_subdev_fh *fh, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_format *fmt) { struct mt9m032 *sensor = to_mt9m032(subdev); @@ -414,7 +414,7 @@ static int mt9m032_set_pad_format(struct v4l2_subdev *subdev, } /* Scaling is not supported, the format is thus fixed. */ - fmt->format = *__mt9m032_get_pad_format(sensor, fh, fmt->which); + fmt->format = *__mt9m032_get_pad_format(sensor, cfg, fmt->which); ret = 0; done: @@ -423,7 +423,7 @@ done: } static int mt9m032_get_pad_selection(struct v4l2_subdev *subdev, - struct v4l2_subdev_fh *fh, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_selection *sel) { struct mt9m032 *sensor = to_mt9m032(subdev); @@ -432,14 +432,14 @@ static int mt9m032_get_pad_selection(struct v4l2_subdev *subdev, return -EINVAL; mutex_lock(&sensor->lock); - sel->r = *__mt9m032_get_pad_crop(sensor, fh, sel->which); + sel->r = *__mt9m032_get_pad_crop(sensor, cfg, sel->which); mutex_unlock(&sensor->lock); return 0; } static int mt9m032_set_pad_selection(struct v4l2_subdev *subdev, - struct v4l2_subdev_fh *fh, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_selection *sel) { struct mt9m032 *sensor = to_mt9m032(subdev); @@ -475,13 +475,13 @@ static int mt9m032_set_pad_selection(struct v4l2_subdev *subdev, rect.height = min_t(unsigned int, rect.height, MT9M032_PIXEL_ARRAY_HEIGHT - rect.top); - __crop = __mt9m032_get_pad_crop(sensor, fh, sel->which); + __crop = __mt9m032_get_pad_crop(sensor, cfg, sel->which); if (rect.width != __crop->width || rect.height != __crop->height) { /* Reset the output image size if the crop rectangle size has * been modified. */ - format = __mt9m032_get_pad_format(sensor, fh, sel->which); + format = __mt9m032_get_pad_format(sensor, cfg, sel->which); format->width = rect.width; format->height = rect.height; } diff --git a/drivers/media/i2c/mt9p031.c b/drivers/media/i2c/mt9p031.c index e3acae9a2ec3..c56a58b47c78 100644 --- a/drivers/media/i2c/mt9p031.c +++ b/drivers/media/i2c/mt9p031.c @@ -474,7 +474,7 @@ static int mt9p031_s_stream(struct v4l2_subdev *subdev, int enable) } static int mt9p031_enum_mbus_code(struct v4l2_subdev *subdev, - struct v4l2_subdev_fh *fh, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_mbus_code_enum *code) { struct mt9p031 *mt9p031 = to_mt9p031(subdev); @@ -487,7 +487,7 @@ static int mt9p031_enum_mbus_code(struct v4l2_subdev *subdev, } static int mt9p031_enum_frame_size(struct v4l2_subdev *subdev, - struct v4l2_subdev_fh *fh, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_frame_size_enum *fse) { struct mt9p031 *mt9p031 = to_mt9p031(subdev); @@ -505,12 +505,12 @@ static int mt9p031_enum_frame_size(struct v4l2_subdev *subdev, } static struct v4l2_mbus_framefmt * -__mt9p031_get_pad_format(struct mt9p031 *mt9p031, struct v4l2_subdev_fh *fh, +__mt9p031_get_pad_format(struct mt9p031 *mt9p031, struct v4l2_subdev_pad_config *cfg, unsigned int pad, u32 which) { switch (which) { case V4L2_SUBDEV_FORMAT_TRY: - return v4l2_subdev_get_try_format(fh, pad); + return v4l2_subdev_get_try_format(&mt9p031->subdev, cfg, pad); case V4L2_SUBDEV_FORMAT_ACTIVE: return &mt9p031->format; default: @@ -519,12 +519,12 @@ __mt9p031_get_pad_format(struct mt9p031 *mt9p031, struct v4l2_subdev_fh *fh, } static struct v4l2_rect * -__mt9p031_get_pad_crop(struct mt9p031 *mt9p031, struct v4l2_subdev_fh *fh, +__mt9p031_get_pad_crop(struct mt9p031 *mt9p031, struct v4l2_subdev_pad_config *cfg, unsigned int pad, u32 which) { switch (which) { case V4L2_SUBDEV_FORMAT_TRY: - return v4l2_subdev_get_try_crop(fh, pad); + return v4l2_subdev_get_try_crop(&mt9p031->subdev, cfg, pad); case V4L2_SUBDEV_FORMAT_ACTIVE: return &mt9p031->crop; default: @@ -533,18 +533,18 @@ __mt9p031_get_pad_crop(struct mt9p031 *mt9p031, struct v4l2_subdev_fh *fh, } static int mt9p031_get_format(struct v4l2_subdev *subdev, - struct v4l2_subdev_fh *fh, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_format *fmt) { struct mt9p031 *mt9p031 = to_mt9p031(subdev); - fmt->format = *__mt9p031_get_pad_format(mt9p031, fh, fmt->pad, + fmt->format = *__mt9p031_get_pad_format(mt9p031, cfg, fmt->pad, fmt->which); return 0; } static int mt9p031_set_format(struct v4l2_subdev *subdev, - struct v4l2_subdev_fh *fh, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_format *format) { struct mt9p031 *mt9p031 = to_mt9p031(subdev); @@ -555,7 +555,7 @@ static int mt9p031_set_format(struct v4l2_subdev *subdev, unsigned int hratio; unsigned int vratio; - __crop = __mt9p031_get_pad_crop(mt9p031, fh, format->pad, + __crop = __mt9p031_get_pad_crop(mt9p031, cfg, format->pad, format->which); /* Clamp the width and height to avoid dividing by zero. */ @@ -571,7 +571,7 @@ static int mt9p031_set_format(struct v4l2_subdev *subdev, hratio = DIV_ROUND_CLOSEST(__crop->width, width); vratio = DIV_ROUND_CLOSEST(__crop->height, height); - __format = __mt9p031_get_pad_format(mt9p031, fh, format->pad, + __format = __mt9p031_get_pad_format(mt9p031, cfg, format->pad, format->which); __format->width = __crop->width / hratio; __format->height = __crop->height / vratio; @@ -582,7 +582,7 @@ static int mt9p031_set_format(struct v4l2_subdev *subdev, } static int mt9p031_get_selection(struct v4l2_subdev *subdev, - struct v4l2_subdev_fh *fh, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_selection *sel) { struct mt9p031 *mt9p031 = to_mt9p031(subdev); @@ -590,12 +590,12 @@ static int mt9p031_get_selection(struct v4l2_subdev *subdev, if (sel->target != V4L2_SEL_TGT_CROP) return -EINVAL; - sel->r = *__mt9p031_get_pad_crop(mt9p031, fh, sel->pad, sel->which); + sel->r = *__mt9p031_get_pad_crop(mt9p031, cfg, sel->pad, sel->which); return 0; } static int mt9p031_set_selection(struct v4l2_subdev *subdev, - struct v4l2_subdev_fh *fh, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_selection *sel) { struct mt9p031 *mt9p031 = to_mt9p031(subdev); @@ -625,13 +625,13 @@ static int mt9p031_set_selection(struct v4l2_subdev *subdev, rect.height = min_t(unsigned int, rect.height, MT9P031_PIXEL_ARRAY_HEIGHT - rect.top); - __crop = __mt9p031_get_pad_crop(mt9p031, fh, sel->pad, sel->which); + __crop = __mt9p031_get_pad_crop(mt9p031, cfg, sel->pad, sel->which); if (rect.width != __crop->width || rect.height != __crop->height) { /* Reset the output image size if the crop rectangle size has * been modified. */ - __format = __mt9p031_get_pad_format(mt9p031, fh, sel->pad, + __format = __mt9p031_get_pad_format(mt9p031, cfg, sel->pad, sel->which); __format->width = rect.width; __format->height = rect.height; @@ -946,13 +946,13 @@ static int mt9p031_open(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh) struct v4l2_mbus_framefmt *format; struct v4l2_rect *crop; - crop = v4l2_subdev_get_try_crop(fh, 0); + crop = v4l2_subdev_get_try_crop(subdev, fh->pad, 0); crop->left = MT9P031_COLUMN_START_DEF; crop->top = MT9P031_ROW_START_DEF; crop->width = MT9P031_WINDOW_WIDTH_DEF; crop->height = MT9P031_WINDOW_HEIGHT_DEF; - format = v4l2_subdev_get_try_format(fh, 0); + format = v4l2_subdev_get_try_format(subdev, fh->pad, 0); if (mt9p031->model == MT9P031_MODEL_MONOCHROME) format->code = MEDIA_BUS_FMT_Y12_1X12; diff --git a/drivers/media/i2c/mt9t001.c b/drivers/media/i2c/mt9t001.c index f6ca636b538d..8ae99f7f254c 100644 --- a/drivers/media/i2c/mt9t001.c +++ b/drivers/media/i2c/mt9t001.c @@ -244,12 +244,12 @@ static int __mt9t001_set_power(struct mt9t001 *mt9t001, bool on) */ static struct v4l2_mbus_framefmt * -__mt9t001_get_pad_format(struct mt9t001 *mt9t001, struct v4l2_subdev_fh *fh, +__mt9t001_get_pad_format(struct mt9t001 *mt9t001, struct v4l2_subdev_pad_config *cfg, unsigned int pad, enum v4l2_subdev_format_whence which) { switch (which) { case V4L2_SUBDEV_FORMAT_TRY: - return v4l2_subdev_get_try_format(fh, pad); + return v4l2_subdev_get_try_format(&mt9t001->subdev, cfg, pad); case V4L2_SUBDEV_FORMAT_ACTIVE: return &mt9t001->format; default: @@ -258,12 +258,12 @@ __mt9t001_get_pad_format(struct mt9t001 *mt9t001, struct v4l2_subdev_fh *fh, } static struct v4l2_rect * -__mt9t001_get_pad_crop(struct mt9t001 *mt9t001, struct v4l2_subdev_fh *fh, +__mt9t001_get_pad_crop(struct mt9t001 *mt9t001, struct v4l2_subdev_pad_config *cfg, unsigned int pad, enum v4l2_subdev_format_whence which) { switch (which) { case V4L2_SUBDEV_FORMAT_TRY: - return v4l2_subdev_get_try_crop(fh, pad); + return v4l2_subdev_get_try_crop(&mt9t001->subdev, cfg, pad); case V4L2_SUBDEV_FORMAT_ACTIVE: return &mt9t001->crop; default: @@ -327,7 +327,7 @@ static int mt9t001_s_stream(struct v4l2_subdev *subdev, int enable) } static int mt9t001_enum_mbus_code(struct v4l2_subdev *subdev, - struct v4l2_subdev_fh *fh, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_mbus_code_enum *code) { if (code->index > 0) @@ -338,7 +338,7 @@ static int mt9t001_enum_mbus_code(struct v4l2_subdev *subdev, } static int mt9t001_enum_frame_size(struct v4l2_subdev *subdev, - struct v4l2_subdev_fh *fh, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_frame_size_enum *fse) { if (fse->index >= 8 || fse->code != MEDIA_BUS_FMT_SGRBG10_1X10) @@ -353,18 +353,18 @@ static int mt9t001_enum_frame_size(struct v4l2_subdev *subdev, } static int mt9t001_get_format(struct v4l2_subdev *subdev, - struct v4l2_subdev_fh *fh, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_format *format) { struct mt9t001 *mt9t001 = to_mt9t001(subdev); - format->format = *__mt9t001_get_pad_format(mt9t001, fh, format->pad, + format->format = *__mt9t001_get_pad_format(mt9t001, cfg, format->pad, format->which); return 0; } static int mt9t001_set_format(struct v4l2_subdev *subdev, - struct v4l2_subdev_fh *fh, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_format *format) { struct mt9t001 *mt9t001 = to_mt9t001(subdev); @@ -375,7 +375,7 @@ static int mt9t001_set_format(struct v4l2_subdev *subdev, unsigned int hratio; unsigned int vratio; - __crop = __mt9t001_get_pad_crop(mt9t001, fh, format->pad, + __crop = __mt9t001_get_pad_crop(mt9t001, cfg, format->pad, format->which); /* Clamp the width and height to avoid dividing by zero. */ @@ -391,7 +391,7 @@ static int mt9t001_set_format(struct v4l2_subdev *subdev, hratio = DIV_ROUND_CLOSEST(__crop->width, width); vratio = DIV_ROUND_CLOSEST(__crop->height, height); - __format = __mt9t001_get_pad_format(mt9t001, fh, format->pad, + __format = __mt9t001_get_pad_format(mt9t001, cfg, format->pad, format->which); __format->width = __crop->width / hratio; __format->height = __crop->height / vratio; @@ -402,7 +402,7 @@ static int mt9t001_set_format(struct v4l2_subdev *subdev, } static int mt9t001_get_selection(struct v4l2_subdev *subdev, - struct v4l2_subdev_fh *fh, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_selection *sel) { struct mt9t001 *mt9t001 = to_mt9t001(subdev); @@ -410,12 +410,12 @@ static int mt9t001_get_selection(struct v4l2_subdev *subdev, if (sel->target != V4L2_SEL_TGT_CROP) return -EINVAL; - sel->r = *__mt9t001_get_pad_crop(mt9t001, fh, sel->pad, sel->which); + sel->r = *__mt9t001_get_pad_crop(mt9t001, cfg, sel->pad, sel->which); return 0; } static int mt9t001_set_selection(struct v4l2_subdev *subdev, - struct v4l2_subdev_fh *fh, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_selection *sel) { struct mt9t001 *mt9t001 = to_mt9t001(subdev); @@ -447,13 +447,13 @@ static int mt9t001_set_selection(struct v4l2_subdev *subdev, rect.height = min_t(unsigned int, rect.height, MT9T001_PIXEL_ARRAY_HEIGHT - rect.top); - __crop = __mt9t001_get_pad_crop(mt9t001, fh, sel->pad, sel->which); + __crop = __mt9t001_get_pad_crop(mt9t001, cfg, sel->pad, sel->which); if (rect.width != __crop->width || rect.height != __crop->height) { /* Reset the output image size if the crop rectangle size has * been modified. */ - __format = __mt9t001_get_pad_format(mt9t001, fh, sel->pad, + __format = __mt9t001_get_pad_format(mt9t001, cfg, sel->pad, sel->which); __format->width = rect.width; __format->height = rect.height; @@ -790,13 +790,13 @@ static int mt9t001_open(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh) struct v4l2_mbus_framefmt *format; struct v4l2_rect *crop; - crop = v4l2_subdev_get_try_crop(fh, 0); + crop = v4l2_subdev_get_try_crop(subdev, fh->pad, 0); crop->left = MT9T001_COLUMN_START_DEF; crop->top = MT9T001_ROW_START_DEF; crop->width = MT9T001_WINDOW_WIDTH_DEF + 1; crop->height = MT9T001_WINDOW_HEIGHT_DEF + 1; - format = v4l2_subdev_get_try_format(fh, 0); + format = v4l2_subdev_get_try_format(subdev, fh->pad, 0); format->code = MEDIA_BUS_FMT_SGRBG10_1X10; format->width = MT9T001_WINDOW_WIDTH_DEF + 1; format->height = MT9T001_WINDOW_HEIGHT_DEF + 1; diff --git a/drivers/media/i2c/mt9v032.c b/drivers/media/i2c/mt9v032.c index 3267c183104b..3793d317772b 100644 --- a/drivers/media/i2c/mt9v032.c +++ b/drivers/media/i2c/mt9v032.c @@ -371,12 +371,12 @@ static int __mt9v032_set_power(struct mt9v032 *mt9v032, bool on) */ static struct v4l2_mbus_framefmt * -__mt9v032_get_pad_format(struct mt9v032 *mt9v032, struct v4l2_subdev_fh *fh, +__mt9v032_get_pad_format(struct mt9v032 *mt9v032, struct v4l2_subdev_pad_config *cfg, unsigned int pad, enum v4l2_subdev_format_whence which) { switch (which) { case V4L2_SUBDEV_FORMAT_TRY: - return v4l2_subdev_get_try_format(fh, pad); + return v4l2_subdev_get_try_format(&mt9v032->subdev, cfg, pad); case V4L2_SUBDEV_FORMAT_ACTIVE: return &mt9v032->format; default: @@ -385,12 +385,12 @@ __mt9v032_get_pad_format(struct mt9v032 *mt9v032, struct v4l2_subdev_fh *fh, } static struct v4l2_rect * -__mt9v032_get_pad_crop(struct mt9v032 *mt9v032, struct v4l2_subdev_fh *fh, +__mt9v032_get_pad_crop(struct mt9v032 *mt9v032, struct v4l2_subdev_pad_config *cfg, unsigned int pad, enum v4l2_subdev_format_whence which) { switch (which) { case V4L2_SUBDEV_FORMAT_TRY: - return v4l2_subdev_get_try_crop(fh, pad); + return v4l2_subdev_get_try_crop(&mt9v032->subdev, cfg, pad); case V4L2_SUBDEV_FORMAT_ACTIVE: return &mt9v032->crop; default: @@ -448,7 +448,7 @@ static int mt9v032_s_stream(struct v4l2_subdev *subdev, int enable) } static int mt9v032_enum_mbus_code(struct v4l2_subdev *subdev, - struct v4l2_subdev_fh *fh, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_mbus_code_enum *code) { if (code->index > 0) @@ -459,7 +459,7 @@ static int mt9v032_enum_mbus_code(struct v4l2_subdev *subdev, } static int mt9v032_enum_frame_size(struct v4l2_subdev *subdev, - struct v4l2_subdev_fh *fh, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_frame_size_enum *fse) { if (fse->index >= 3 || fse->code != MEDIA_BUS_FMT_SGRBG10_1X10) @@ -474,12 +474,12 @@ static int mt9v032_enum_frame_size(struct v4l2_subdev *subdev, } static int mt9v032_get_format(struct v4l2_subdev *subdev, - struct v4l2_subdev_fh *fh, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_format *format) { struct mt9v032 *mt9v032 = to_mt9v032(subdev); - format->format = *__mt9v032_get_pad_format(mt9v032, fh, format->pad, + format->format = *__mt9v032_get_pad_format(mt9v032, cfg, format->pad, format->which); return 0; } @@ -509,7 +509,7 @@ static unsigned int mt9v032_calc_ratio(unsigned int input, unsigned int output) } static int mt9v032_set_format(struct v4l2_subdev *subdev, - struct v4l2_subdev_fh *fh, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_format *format) { struct mt9v032 *mt9v032 = to_mt9v032(subdev); @@ -520,7 +520,7 @@ static int mt9v032_set_format(struct v4l2_subdev *subdev, unsigned int hratio; unsigned int vratio; - __crop = __mt9v032_get_pad_crop(mt9v032, fh, format->pad, + __crop = __mt9v032_get_pad_crop(mt9v032, cfg, format->pad, format->which); /* Clamp the width and height to avoid dividing by zero. */ @@ -536,7 +536,7 @@ static int mt9v032_set_format(struct v4l2_subdev *subdev, hratio = mt9v032_calc_ratio(__crop->width, width); vratio = mt9v032_calc_ratio(__crop->height, height); - __format = __mt9v032_get_pad_format(mt9v032, fh, format->pad, + __format = __mt9v032_get_pad_format(mt9v032, cfg, format->pad, format->which); __format->width = __crop->width / hratio; __format->height = __crop->height / vratio; @@ -553,7 +553,7 @@ static int mt9v032_set_format(struct v4l2_subdev *subdev, } static int mt9v032_get_selection(struct v4l2_subdev *subdev, - struct v4l2_subdev_fh *fh, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_selection *sel) { struct mt9v032 *mt9v032 = to_mt9v032(subdev); @@ -561,12 +561,12 @@ static int mt9v032_get_selection(struct v4l2_subdev *subdev, if (sel->target != V4L2_SEL_TGT_CROP) return -EINVAL; - sel->r = *__mt9v032_get_pad_crop(mt9v032, fh, sel->pad, sel->which); + sel->r = *__mt9v032_get_pad_crop(mt9v032, cfg, sel->pad, sel->which); return 0; } static int mt9v032_set_selection(struct v4l2_subdev *subdev, - struct v4l2_subdev_fh *fh, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_selection *sel) { struct mt9v032 *mt9v032 = to_mt9v032(subdev); @@ -598,13 +598,13 @@ static int mt9v032_set_selection(struct v4l2_subdev *subdev, rect.height = min_t(unsigned int, rect.height, MT9V032_PIXEL_ARRAY_HEIGHT - rect.top); - __crop = __mt9v032_get_pad_crop(mt9v032, fh, sel->pad, sel->which); + __crop = __mt9v032_get_pad_crop(mt9v032, cfg, sel->pad, sel->which); if (rect.width != __crop->width || rect.height != __crop->height) { /* Reset the output image size if the crop rectangle size has * been modified. */ - __format = __mt9v032_get_pad_format(mt9v032, fh, sel->pad, + __format = __mt9v032_get_pad_format(mt9v032, cfg, sel->pad, sel->which); __format->width = rect.width; __format->height = rect.height; @@ -810,13 +810,13 @@ static int mt9v032_open(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh) struct v4l2_mbus_framefmt *format; struct v4l2_rect *crop; - crop = v4l2_subdev_get_try_crop(fh, 0); + crop = v4l2_subdev_get_try_crop(subdev, fh->pad, 0); crop->left = MT9V032_COLUMN_START_DEF; crop->top = MT9V032_ROW_START_DEF; crop->width = MT9V032_WINDOW_WIDTH_DEF; crop->height = MT9V032_WINDOW_HEIGHT_DEF; - format = v4l2_subdev_get_try_format(fh, 0); + format = v4l2_subdev_get_try_format(subdev, fh->pad, 0); if (mt9v032->model->color) format->code = MEDIA_BUS_FMT_SGRBG10_1X10; diff --git a/drivers/media/i2c/noon010pc30.c b/drivers/media/i2c/noon010pc30.c index 00c7b26f4823..f197b6cbd407 100644 --- a/drivers/media/i2c/noon010pc30.c +++ b/drivers/media/i2c/noon010pc30.c @@ -492,7 +492,7 @@ unlock: } static int noon010_enum_mbus_code(struct v4l2_subdev *sd, - struct v4l2_subdev_fh *fh, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_mbus_code_enum *code) { if (code->index >= ARRAY_SIZE(noon010_formats)) @@ -502,15 +502,16 @@ static int noon010_enum_mbus_code(struct v4l2_subdev *sd, return 0; } -static int noon010_get_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, +static int noon010_get_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_format *fmt) { struct noon010_info *info = to_noon010(sd); struct v4l2_mbus_framefmt *mf; if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - if (fh) { - mf = v4l2_subdev_get_try_format(fh, 0); + if (cfg) { + mf = v4l2_subdev_get_try_format(sd, cfg, 0); fmt->format = *mf; } return 0; @@ -542,7 +543,7 @@ static const struct noon010_format *noon010_try_fmt(struct v4l2_subdev *sd, return &noon010_formats[i]; } -static int noon010_set_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, +static int noon010_set_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_format *fmt) { struct noon010_info *info = to_noon010(sd); @@ -557,8 +558,8 @@ static int noon010_set_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, fmt->format.field = V4L2_FIELD_NONE; if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - if (fh) { - mf = v4l2_subdev_get_try_format(fh, 0); + if (cfg) { + mf = v4l2_subdev_get_try_format(sd, cfg, 0); *mf = fmt->format; } return 0; @@ -640,7 +641,7 @@ static int noon010_log_status(struct v4l2_subdev *sd) static int noon010_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) { - struct v4l2_mbus_framefmt *mf = v4l2_subdev_get_try_format(fh, 0); + struct v4l2_mbus_framefmt *mf = v4l2_subdev_get_try_format(sd, fh->pad, 0); mf->width = noon010_sizes[0].width; mf->height = noon010_sizes[0].height; diff --git a/drivers/media/i2c/ov9650.c b/drivers/media/i2c/ov9650.c index 2246bd5436ad..2bc473385c91 100644 --- a/drivers/media/i2c/ov9650.c +++ b/drivers/media/i2c/ov9650.c @@ -1067,7 +1067,7 @@ static void ov965x_get_default_format(struct v4l2_mbus_framefmt *mf) } static int ov965x_enum_mbus_code(struct v4l2_subdev *sd, - struct v4l2_subdev_fh *fh, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_mbus_code_enum *code) { if (code->index >= ARRAY_SIZE(ov965x_formats)) @@ -1078,7 +1078,7 @@ static int ov965x_enum_mbus_code(struct v4l2_subdev *sd, } static int ov965x_enum_frame_sizes(struct v4l2_subdev *sd, - struct v4l2_subdev_fh *fh, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_frame_size_enum *fse) { int i = ARRAY_SIZE(ov965x_formats); @@ -1164,14 +1164,14 @@ static int ov965x_s_frame_interval(struct v4l2_subdev *sd, return ret; } -static int ov965x_get_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, +static int ov965x_get_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_format *fmt) { struct ov965x *ov965x = to_ov965x(sd); struct v4l2_mbus_framefmt *mf; if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - mf = v4l2_subdev_get_try_format(fh, 0); + mf = v4l2_subdev_get_try_format(sd, cfg, 0); fmt->format = *mf; return 0; } @@ -1208,7 +1208,7 @@ static void __ov965x_try_frame_size(struct v4l2_mbus_framefmt *mf, *size = match; } -static int ov965x_set_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, +static int ov965x_set_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_format *fmt) { unsigned int index = ARRAY_SIZE(ov965x_formats); @@ -1230,8 +1230,8 @@ static int ov965x_set_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, mutex_lock(&ov965x->lock); if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - if (fh != NULL) { - mf = v4l2_subdev_get_try_format(fh, fmt->pad); + if (cfg != NULL) { + mf = v4l2_subdev_get_try_format(sd, cfg, fmt->pad); *mf = fmt->format; } } else { @@ -1361,7 +1361,7 @@ static int ov965x_s_stream(struct v4l2_subdev *sd, int on) */ static int ov965x_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) { - struct v4l2_mbus_framefmt *mf = v4l2_subdev_get_try_format(fh, 0); + struct v4l2_mbus_framefmt *mf = v4l2_subdev_get_try_format(sd, fh->pad, 0); ov965x_get_default_format(mf); return 0; diff --git a/drivers/media/i2c/s5c73m3/s5c73m3-core.c b/drivers/media/i2c/s5c73m3/s5c73m3-core.c index ee0f57e01b56..257a335c0ece 100644 --- a/drivers/media/i2c/s5c73m3/s5c73m3-core.c +++ b/drivers/media/i2c/s5c73m3/s5c73m3-core.c @@ -824,10 +824,11 @@ static const struct s5c73m3_frame_size *s5c73m3_find_frame_size( } static void s5c73m3_oif_try_format(struct s5c73m3 *state, - struct v4l2_subdev_fh *fh, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_format *fmt, const struct s5c73m3_frame_size **fs) { + struct v4l2_subdev *sd = &state->sensor_sd; u32 code; switch (fmt->pad) { @@ -850,7 +851,7 @@ static void s5c73m3_oif_try_format(struct s5c73m3 *state, *fs = state->oif_pix_size[RES_ISP]; else *fs = s5c73m3_find_frame_size( - v4l2_subdev_get_try_format(fh, + v4l2_subdev_get_try_format(sd, cfg, OIF_ISP_PAD), RES_ISP); break; @@ -860,7 +861,7 @@ static void s5c73m3_oif_try_format(struct s5c73m3 *state, } static void s5c73m3_try_format(struct s5c73m3 *state, - struct v4l2_subdev_fh *fh, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_format *fmt, const struct s5c73m3_frame_size **fs) { @@ -952,7 +953,7 @@ static int s5c73m3_oif_s_frame_interval(struct v4l2_subdev *sd, } static int s5c73m3_oif_enum_frame_interval(struct v4l2_subdev *sd, - struct v4l2_subdev_fh *fh, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_frame_interval_enum *fie) { struct s5c73m3 *state = oif_sd_to_s5c73m3(sd); @@ -990,7 +991,7 @@ static int s5c73m3_oif_get_pad_code(int pad, int index) } static int s5c73m3_get_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_fh *fh, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_format *fmt) { struct s5c73m3 *state = sensor_sd_to_s5c73m3(sd); @@ -998,7 +999,7 @@ static int s5c73m3_get_fmt(struct v4l2_subdev *sd, u32 code; if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - fmt->format = *v4l2_subdev_get_try_format(fh, fmt->pad); + fmt->format = *v4l2_subdev_get_try_format(sd, cfg, fmt->pad); return 0; } @@ -1024,7 +1025,7 @@ static int s5c73m3_get_fmt(struct v4l2_subdev *sd, } static int s5c73m3_oif_get_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_fh *fh, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_format *fmt) { struct s5c73m3 *state = oif_sd_to_s5c73m3(sd); @@ -1032,7 +1033,7 @@ static int s5c73m3_oif_get_fmt(struct v4l2_subdev *sd, u32 code; if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - fmt->format = *v4l2_subdev_get_try_format(fh, fmt->pad); + fmt->format = *v4l2_subdev_get_try_format(sd, cfg, fmt->pad); return 0; } @@ -1062,7 +1063,7 @@ static int s5c73m3_oif_get_fmt(struct v4l2_subdev *sd, } static int s5c73m3_set_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_fh *fh, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_format *fmt) { const struct s5c73m3_frame_size *frame_size = NULL; @@ -1072,10 +1073,10 @@ static int s5c73m3_set_fmt(struct v4l2_subdev *sd, mutex_lock(&state->lock); - s5c73m3_try_format(state, fh, fmt, &frame_size); + s5c73m3_try_format(state, cfg, fmt, &frame_size); if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - mf = v4l2_subdev_get_try_format(fh, fmt->pad); + mf = v4l2_subdev_get_try_format(sd, cfg, fmt->pad); *mf = fmt->format; } else { switch (fmt->pad) { @@ -1101,7 +1102,7 @@ static int s5c73m3_set_fmt(struct v4l2_subdev *sd, } static int s5c73m3_oif_set_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_fh *fh, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_format *fmt) { const struct s5c73m3_frame_size *frame_size = NULL; @@ -1111,13 +1112,13 @@ static int s5c73m3_oif_set_fmt(struct v4l2_subdev *sd, mutex_lock(&state->lock); - s5c73m3_oif_try_format(state, fh, fmt, &frame_size); + s5c73m3_oif_try_format(state, cfg, fmt, &frame_size); if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - mf = v4l2_subdev_get_try_format(fh, fmt->pad); + mf = v4l2_subdev_get_try_format(sd, cfg, fmt->pad); *mf = fmt->format; if (fmt->pad == OIF_ISP_PAD) { - mf = v4l2_subdev_get_try_format(fh, OIF_SOURCE_PAD); + mf = v4l2_subdev_get_try_format(sd, cfg, OIF_SOURCE_PAD); mf->width = fmt->format.width; mf->height = fmt->format.height; } @@ -1189,7 +1190,7 @@ static int s5c73m3_oif_set_frame_desc(struct v4l2_subdev *sd, unsigned int pad, } static int s5c73m3_enum_mbus_code(struct v4l2_subdev *sd, - struct v4l2_subdev_fh *fh, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_mbus_code_enum *code) { static const int codes[] = { @@ -1205,7 +1206,7 @@ static int s5c73m3_enum_mbus_code(struct v4l2_subdev *sd, } static int s5c73m3_oif_enum_mbus_code(struct v4l2_subdev *sd, - struct v4l2_subdev_fh *fh, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_mbus_code_enum *code) { int ret; @@ -1220,7 +1221,7 @@ static int s5c73m3_oif_enum_mbus_code(struct v4l2_subdev *sd, } static int s5c73m3_enum_frame_size(struct v4l2_subdev *sd, - struct v4l2_subdev_fh *fh, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_frame_size_enum *fse) { int idx; @@ -1247,7 +1248,7 @@ static int s5c73m3_enum_frame_size(struct v4l2_subdev *sd, } static int s5c73m3_oif_enum_frame_size(struct v4l2_subdev *sd, - struct v4l2_subdev_fh *fh, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_frame_size_enum *fse) { int idx; @@ -1260,7 +1261,7 @@ static int s5c73m3_oif_enum_frame_size(struct v4l2_subdev *sd, case S5C73M3_JPEG_FMT: case S5C73M3_ISP_FMT: { struct v4l2_mbus_framefmt *mf = - v4l2_subdev_get_try_format(fh, OIF_ISP_PAD); + v4l2_subdev_get_try_format(sd, cfg, OIF_ISP_PAD); fse->max_width = fse->min_width = mf->width; fse->max_height = fse->min_height = mf->height; @@ -1306,11 +1307,11 @@ static int s5c73m3_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) { struct v4l2_mbus_framefmt *mf; - mf = v4l2_subdev_get_try_format(fh, S5C73M3_ISP_PAD); + mf = v4l2_subdev_get_try_format(sd, fh->pad, S5C73M3_ISP_PAD); s5c73m3_fill_mbus_fmt(mf, &s5c73m3_isp_resolutions[1], S5C73M3_ISP_FMT); - mf = v4l2_subdev_get_try_format(fh, S5C73M3_JPEG_PAD); + mf = v4l2_subdev_get_try_format(sd, fh->pad, S5C73M3_JPEG_PAD); s5c73m3_fill_mbus_fmt(mf, &s5c73m3_jpeg_resolutions[1], S5C73M3_JPEG_FMT); @@ -1321,15 +1322,15 @@ static int s5c73m3_oif_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) { struct v4l2_mbus_framefmt *mf; - mf = v4l2_subdev_get_try_format(fh, OIF_ISP_PAD); + mf = v4l2_subdev_get_try_format(sd, fh->pad, OIF_ISP_PAD); s5c73m3_fill_mbus_fmt(mf, &s5c73m3_isp_resolutions[1], S5C73M3_ISP_FMT); - mf = v4l2_subdev_get_try_format(fh, OIF_JPEG_PAD); + mf = v4l2_subdev_get_try_format(sd, fh->pad, OIF_JPEG_PAD); s5c73m3_fill_mbus_fmt(mf, &s5c73m3_jpeg_resolutions[1], S5C73M3_JPEG_FMT); - mf = v4l2_subdev_get_try_format(fh, OIF_SOURCE_PAD); + mf = v4l2_subdev_get_try_format(sd, fh->pad, OIF_SOURCE_PAD); s5c73m3_fill_mbus_fmt(mf, &s5c73m3_isp_resolutions[1], S5C73M3_ISP_FMT); return 0; diff --git a/drivers/media/i2c/s5k4ecgx.c b/drivers/media/i2c/s5k4ecgx.c index 70071314789e..97084237275d 100644 --- a/drivers/media/i2c/s5k4ecgx.c +++ b/drivers/media/i2c/s5k4ecgx.c @@ -531,7 +531,7 @@ static int s5k4ecgx_try_frame_size(struct v4l2_mbus_framefmt *mf, } static int s5k4ecgx_enum_mbus_code(struct v4l2_subdev *sd, - struct v4l2_subdev_fh *fh, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_mbus_code_enum *code) { if (code->index >= ARRAY_SIZE(s5k4ecgx_formats)) @@ -541,15 +541,15 @@ static int s5k4ecgx_enum_mbus_code(struct v4l2_subdev *sd, return 0; } -static int s5k4ecgx_get_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, +static int s5k4ecgx_get_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_format *fmt) { struct s5k4ecgx *priv = to_s5k4ecgx(sd); struct v4l2_mbus_framefmt *mf; if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - if (fh) { - mf = v4l2_subdev_get_try_format(fh, 0); + if (cfg) { + mf = v4l2_subdev_get_try_format(sd, cfg, 0); fmt->format = *mf; } return 0; @@ -581,7 +581,7 @@ static const struct s5k4ecgx_pixfmt *s5k4ecgx_try_fmt(struct v4l2_subdev *sd, return &s5k4ecgx_formats[i]; } -static int s5k4ecgx_set_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, +static int s5k4ecgx_set_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_format *fmt) { struct s5k4ecgx *priv = to_s5k4ecgx(sd); @@ -596,8 +596,8 @@ static int s5k4ecgx_set_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, fmt->format.field = V4L2_FIELD_NONE; if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - if (fh) { - mf = v4l2_subdev_get_try_format(fh, 0); + if (cfg) { + mf = v4l2_subdev_get_try_format(sd, cfg, 0); *mf = fmt->format; } return 0; @@ -692,7 +692,7 @@ static int s5k4ecgx_registered(struct v4l2_subdev *sd) */ static int s5k4ecgx_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) { - struct v4l2_mbus_framefmt *mf = v4l2_subdev_get_try_format(fh, 0); + struct v4l2_mbus_framefmt *mf = v4l2_subdev_get_try_format(sd, fh->pad, 0); mf->width = s5k4ecgx_prev_sizes[0].size.width; mf->height = s5k4ecgx_prev_sizes[0].size.height; diff --git a/drivers/media/i2c/s5k5baf.c b/drivers/media/i2c/s5k5baf.c index a3d7d0391302..96e7a4834130 100644 --- a/drivers/media/i2c/s5k5baf.c +++ b/drivers/media/i2c/s5k5baf.c @@ -1182,7 +1182,7 @@ static int s5k5baf_s_frame_interval(struct v4l2_subdev *sd, * V4L2 subdev pad level and video operations */ static int s5k5baf_enum_frame_interval(struct v4l2_subdev *sd, - struct v4l2_subdev_fh *fh, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_frame_interval_enum *fie) { if (fie->index > S5K5BAF_MAX_FR_TIME - S5K5BAF_MIN_FR_TIME || @@ -1201,7 +1201,7 @@ static int s5k5baf_enum_frame_interval(struct v4l2_subdev *sd, } static int s5k5baf_enum_mbus_code(struct v4l2_subdev *sd, - struct v4l2_subdev_fh *fh, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_mbus_code_enum *code) { if (code->pad == PAD_CIS) { @@ -1219,7 +1219,7 @@ static int s5k5baf_enum_mbus_code(struct v4l2_subdev *sd, } static int s5k5baf_enum_frame_size(struct v4l2_subdev *sd, - struct v4l2_subdev_fh *fh, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_frame_size_enum *fse) { int i; @@ -1276,7 +1276,7 @@ static int s5k5baf_try_isp_format(struct v4l2_mbus_framefmt *mf) return pixfmt; } -static int s5k5baf_get_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, +static int s5k5baf_get_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_format *fmt) { struct s5k5baf *state = to_s5k5baf(sd); @@ -1284,7 +1284,7 @@ static int s5k5baf_get_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, struct v4l2_mbus_framefmt *mf; if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - mf = v4l2_subdev_get_try_format(fh, fmt->pad); + mf = v4l2_subdev_get_try_format(sd, cfg, fmt->pad); fmt->format = *mf; return 0; } @@ -1306,7 +1306,7 @@ static int s5k5baf_get_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, return 0; } -static int s5k5baf_set_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, +static int s5k5baf_set_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_format *fmt) { struct v4l2_mbus_framefmt *mf = &fmt->format; @@ -1317,7 +1317,7 @@ static int s5k5baf_set_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, mf->field = V4L2_FIELD_NONE; if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - *v4l2_subdev_get_try_format(fh, fmt->pad) = *mf; + *v4l2_subdev_get_try_format(sd, cfg, fmt->pad) = *mf; return 0; } @@ -1369,7 +1369,7 @@ static int s5k5baf_is_bound_target(u32 target) } static int s5k5baf_get_selection(struct v4l2_subdev *sd, - struct v4l2_subdev_fh *fh, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_selection *sel) { static enum selection_rect rtype; @@ -1389,9 +1389,9 @@ static int s5k5baf_get_selection(struct v4l2_subdev *sd, if (sel->which == V4L2_SUBDEV_FORMAT_TRY) { if (rtype == R_COMPOSE) - sel->r = *v4l2_subdev_get_try_compose(fh, sel->pad); + sel->r = *v4l2_subdev_get_try_compose(sd, cfg, sel->pad); else - sel->r = *v4l2_subdev_get_try_crop(fh, sel->pad); + sel->r = *v4l2_subdev_get_try_crop(sd, cfg, sel->pad); return 0; } @@ -1460,7 +1460,7 @@ static bool s5k5baf_cmp_rect(const struct v4l2_rect *r1, } static int s5k5baf_set_selection(struct v4l2_subdev *sd, - struct v4l2_subdev_fh *fh, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_selection *sel) { static enum selection_rect rtype; @@ -1481,9 +1481,9 @@ static int s5k5baf_set_selection(struct v4l2_subdev *sd, if (sel->which == V4L2_SUBDEV_FORMAT_TRY) { rects = (struct v4l2_rect * []) { &s5k5baf_cis_rect, - v4l2_subdev_get_try_crop(fh, PAD_CIS), - v4l2_subdev_get_try_compose(fh, PAD_CIS), - v4l2_subdev_get_try_crop(fh, PAD_OUT) + v4l2_subdev_get_try_crop(sd, cfg, PAD_CIS), + v4l2_subdev_get_try_compose(sd, cfg, PAD_CIS), + v4l2_subdev_get_try_crop(sd, cfg, PAD_OUT) }; s5k5baf_set_rect_and_adjust(rects, rtype, &sel->r); return 0; @@ -1701,22 +1701,22 @@ static int s5k5baf_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) { struct v4l2_mbus_framefmt *mf; - mf = v4l2_subdev_get_try_format(fh, PAD_CIS); + mf = v4l2_subdev_get_try_format(sd, fh->pad, PAD_CIS); s5k5baf_try_cis_format(mf); if (s5k5baf_is_cis_subdev(sd)) return 0; - mf = v4l2_subdev_get_try_format(fh, PAD_OUT); + mf = v4l2_subdev_get_try_format(sd, fh->pad, PAD_OUT); mf->colorspace = s5k5baf_formats[0].colorspace; mf->code = s5k5baf_formats[0].code; mf->width = s5k5baf_cis_rect.width; mf->height = s5k5baf_cis_rect.height; mf->field = V4L2_FIELD_NONE; - *v4l2_subdev_get_try_crop(fh, PAD_CIS) = s5k5baf_cis_rect; - *v4l2_subdev_get_try_compose(fh, PAD_CIS) = s5k5baf_cis_rect; - *v4l2_subdev_get_try_crop(fh, PAD_OUT) = s5k5baf_cis_rect; + *v4l2_subdev_get_try_crop(sd, fh->pad, PAD_CIS) = s5k5baf_cis_rect; + *v4l2_subdev_get_try_compose(sd, fh->pad, PAD_CIS) = s5k5baf_cis_rect; + *v4l2_subdev_get_try_crop(sd, fh->pad, PAD_OUT) = s5k5baf_cis_rect; return 0; } diff --git a/drivers/media/i2c/s5k6a3.c b/drivers/media/i2c/s5k6a3.c index 91b841a1b850..bc389d5e42ae 100644 --- a/drivers/media/i2c/s5k6a3.c +++ b/drivers/media/i2c/s5k6a3.c @@ -99,7 +99,7 @@ static const struct v4l2_mbus_framefmt *find_sensor_format( } static int s5k6a3_enum_mbus_code(struct v4l2_subdev *sd, - struct v4l2_subdev_fh *fh, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_mbus_code_enum *code) { if (code->index >= ARRAY_SIZE(s5k6a3_formats)) @@ -123,17 +123,17 @@ static void s5k6a3_try_format(struct v4l2_mbus_framefmt *mf) } static struct v4l2_mbus_framefmt *__s5k6a3_get_format( - struct s5k6a3 *sensor, struct v4l2_subdev_fh *fh, + struct s5k6a3 *sensor, struct v4l2_subdev_pad_config *cfg, u32 pad, enum v4l2_subdev_format_whence which) { if (which == V4L2_SUBDEV_FORMAT_TRY) - return fh ? v4l2_subdev_get_try_format(fh, pad) : NULL; + return cfg ? v4l2_subdev_get_try_format(&sensor->subdev, cfg, pad) : NULL; return &sensor->format; } static int s5k6a3_set_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_fh *fh, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_format *fmt) { struct s5k6a3 *sensor = sd_to_s5k6a3(sd); @@ -141,7 +141,7 @@ static int s5k6a3_set_fmt(struct v4l2_subdev *sd, s5k6a3_try_format(&fmt->format); - mf = __s5k6a3_get_format(sensor, fh, fmt->pad, fmt->which); + mf = __s5k6a3_get_format(sensor, cfg, fmt->pad, fmt->which); if (mf) { mutex_lock(&sensor->lock); if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) @@ -152,13 +152,13 @@ static int s5k6a3_set_fmt(struct v4l2_subdev *sd, } static int s5k6a3_get_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_fh *fh, - struct v4l2_subdev_format *fmt) + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *fmt) { struct s5k6a3 *sensor = sd_to_s5k6a3(sd); struct v4l2_mbus_framefmt *mf; - mf = __s5k6a3_get_format(sensor, fh, fmt->pad, fmt->which); + mf = __s5k6a3_get_format(sensor, cfg, fmt->pad, fmt->which); mutex_lock(&sensor->lock); fmt->format = *mf; @@ -174,7 +174,7 @@ static struct v4l2_subdev_pad_ops s5k6a3_pad_ops = { static int s5k6a3_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) { - struct v4l2_mbus_framefmt *format = v4l2_subdev_get_try_format(fh, 0); + struct v4l2_mbus_framefmt *format = v4l2_subdev_get_try_format(sd, fh->pad, 0); *format = s5k6a3_formats[0]; format->width = S5K6A3_DEFAULT_WIDTH; diff --git a/drivers/media/i2c/s5k6aa.c b/drivers/media/i2c/s5k6aa.c index b1c583239dab..de803a11efb4 100644 --- a/drivers/media/i2c/s5k6aa.c +++ b/drivers/media/i2c/s5k6aa.c @@ -996,7 +996,7 @@ static int s5k6aa_s_frame_interval(struct v4l2_subdev *sd, * V4L2 subdev pad level and video operations */ static int s5k6aa_enum_frame_interval(struct v4l2_subdev *sd, - struct v4l2_subdev_fh *fh, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_frame_interval_enum *fie) { struct s5k6aa *s5k6aa = to_s5k6aa(sd); @@ -1023,7 +1023,7 @@ static int s5k6aa_enum_frame_interval(struct v4l2_subdev *sd, } static int s5k6aa_enum_mbus_code(struct v4l2_subdev *sd, - struct v4l2_subdev_fh *fh, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_mbus_code_enum *code) { if (code->index >= ARRAY_SIZE(s5k6aa_formats)) @@ -1034,7 +1034,7 @@ static int s5k6aa_enum_mbus_code(struct v4l2_subdev *sd, } static int s5k6aa_enum_frame_size(struct v4l2_subdev *sd, - struct v4l2_subdev_fh *fh, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_frame_size_enum *fse) { int i = ARRAY_SIZE(s5k6aa_formats); @@ -1056,14 +1056,14 @@ static int s5k6aa_enum_frame_size(struct v4l2_subdev *sd, } static struct v4l2_rect * -__s5k6aa_get_crop_rect(struct s5k6aa *s5k6aa, struct v4l2_subdev_fh *fh, +__s5k6aa_get_crop_rect(struct s5k6aa *s5k6aa, struct v4l2_subdev_pad_config *cfg, enum v4l2_subdev_format_whence which) { if (which == V4L2_SUBDEV_FORMAT_ACTIVE) return &s5k6aa->ccd_rect; WARN_ON(which != V4L2_SUBDEV_FORMAT_TRY); - return v4l2_subdev_get_try_crop(fh, 0); + return v4l2_subdev_get_try_crop(&s5k6aa->sd, cfg, 0); } static void s5k6aa_try_format(struct s5k6aa *s5k6aa, @@ -1087,7 +1087,7 @@ static void s5k6aa_try_format(struct s5k6aa *s5k6aa, mf->field = V4L2_FIELD_NONE; } -static int s5k6aa_get_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, +static int s5k6aa_get_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_format *fmt) { struct s5k6aa *s5k6aa = to_s5k6aa(sd); @@ -1096,7 +1096,7 @@ static int s5k6aa_get_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, memset(fmt->reserved, 0, sizeof(fmt->reserved)); if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - mf = v4l2_subdev_get_try_format(fh, 0); + mf = v4l2_subdev_get_try_format(sd, cfg, 0); fmt->format = *mf; return 0; } @@ -1108,7 +1108,7 @@ static int s5k6aa_get_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, return 0; } -static int s5k6aa_set_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, +static int s5k6aa_set_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_format *fmt) { struct s5k6aa *s5k6aa = to_s5k6aa(sd); @@ -1121,8 +1121,8 @@ static int s5k6aa_set_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, s5k6aa_try_format(s5k6aa, &fmt->format); if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - mf = v4l2_subdev_get_try_format(fh, fmt->pad); - crop = v4l2_subdev_get_try_crop(fh, 0); + mf = v4l2_subdev_get_try_format(sd, cfg, fmt->pad); + crop = v4l2_subdev_get_try_crop(sd, cfg, 0); } else { if (s5k6aa->streaming) { ret = -EBUSY; @@ -1162,7 +1162,7 @@ static int s5k6aa_set_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, } static int s5k6aa_get_selection(struct v4l2_subdev *sd, - struct v4l2_subdev_fh *fh, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_selection *sel) { struct s5k6aa *s5k6aa = to_s5k6aa(sd); @@ -1174,7 +1174,7 @@ static int s5k6aa_get_selection(struct v4l2_subdev *sd, memset(sel->reserved, 0, sizeof(sel->reserved)); mutex_lock(&s5k6aa->lock); - rect = __s5k6aa_get_crop_rect(s5k6aa, fh, sel->which); + rect = __s5k6aa_get_crop_rect(s5k6aa, cfg, sel->which); sel->r = *rect; mutex_unlock(&s5k6aa->lock); @@ -1185,7 +1185,7 @@ static int s5k6aa_get_selection(struct v4l2_subdev *sd, } static int s5k6aa_set_selection(struct v4l2_subdev *sd, - struct v4l2_subdev_fh *fh, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_selection *sel) { struct s5k6aa *s5k6aa = to_s5k6aa(sd); @@ -1197,13 +1197,13 @@ static int s5k6aa_set_selection(struct v4l2_subdev *sd, return -EINVAL; mutex_lock(&s5k6aa->lock); - crop_r = __s5k6aa_get_crop_rect(s5k6aa, fh, sel->which); + crop_r = __s5k6aa_get_crop_rect(s5k6aa, cfg, sel->which); if (sel->which == V4L2_SUBDEV_FORMAT_ACTIVE) { mf = &s5k6aa->preset->mbus_fmt; s5k6aa->apply_crop = 1; } else { - mf = v4l2_subdev_get_try_format(fh, 0); + mf = v4l2_subdev_get_try_format(sd, cfg, 0); } v4l_bound_align_image(&sel->r.width, mf->width, S5K6AA_WIN_WIDTH_MAX, 1, @@ -1424,8 +1424,8 @@ static int s5k6aa_initialize_ctrls(struct s5k6aa *s5k6aa) */ static int s5k6aa_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) { - struct v4l2_mbus_framefmt *format = v4l2_subdev_get_try_format(fh, 0); - struct v4l2_rect *crop = v4l2_subdev_get_try_crop(fh, 0); + struct v4l2_mbus_framefmt *format = v4l2_subdev_get_try_format(sd, fh->pad, 0); + struct v4l2_rect *crop = v4l2_subdev_get_try_crop(sd, fh->pad, 0); format->colorspace = s5k6aa_formats[0].colorspace; format->code = s5k6aa_formats[0].code; diff --git a/drivers/media/i2c/smiapp/smiapp-core.c b/drivers/media/i2c/smiapp/smiapp-core.c index d47eff5d3101..c73deb0432d7 100644 --- a/drivers/media/i2c/smiapp/smiapp-core.c +++ b/drivers/media/i2c/smiapp/smiapp-core.c @@ -1557,7 +1557,7 @@ static int smiapp_set_stream(struct v4l2_subdev *subdev, int enable) } static int smiapp_enum_mbus_code(struct v4l2_subdev *subdev, - struct v4l2_subdev_fh *fh, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_mbus_code_enum *code) { struct i2c_client *client = v4l2_get_subdevdata(subdev); @@ -1611,13 +1611,13 @@ static u32 __smiapp_get_mbus_code(struct v4l2_subdev *subdev, } static int __smiapp_get_format(struct v4l2_subdev *subdev, - struct v4l2_subdev_fh *fh, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_format *fmt) { struct smiapp_subdev *ssd = to_smiapp_subdev(subdev); if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - fmt->format = *v4l2_subdev_get_try_format(fh, fmt->pad); + fmt->format = *v4l2_subdev_get_try_format(subdev, cfg, fmt->pad); } else { struct v4l2_rect *r; @@ -1636,21 +1636,21 @@ static int __smiapp_get_format(struct v4l2_subdev *subdev, } static int smiapp_get_format(struct v4l2_subdev *subdev, - struct v4l2_subdev_fh *fh, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_format *fmt) { struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); int rval; mutex_lock(&sensor->mutex); - rval = __smiapp_get_format(subdev, fh, fmt); + rval = __smiapp_get_format(subdev, cfg, fmt); mutex_unlock(&sensor->mutex); return rval; } static void smiapp_get_crop_compose(struct v4l2_subdev *subdev, - struct v4l2_subdev_fh *fh, + struct v4l2_subdev_pad_config *cfg, struct v4l2_rect **crops, struct v4l2_rect **comps, int which) { @@ -1666,12 +1666,12 @@ static void smiapp_get_crop_compose(struct v4l2_subdev *subdev, } else { if (crops) { for (i = 0; i < subdev->entity.num_pads; i++) { - crops[i] = v4l2_subdev_get_try_crop(fh, i); + crops[i] = v4l2_subdev_get_try_crop(subdev, cfg, i); BUG_ON(!crops[i]); } } if (comps) { - *comps = v4l2_subdev_get_try_compose(fh, + *comps = v4l2_subdev_get_try_compose(subdev, cfg, SMIAPP_PAD_SINK); BUG_ON(!*comps); } @@ -1680,14 +1680,14 @@ static void smiapp_get_crop_compose(struct v4l2_subdev *subdev, /* Changes require propagation only on sink pad. */ static void smiapp_propagate(struct v4l2_subdev *subdev, - struct v4l2_subdev_fh *fh, int which, + struct v4l2_subdev_pad_config *cfg, int which, int target) { struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); struct smiapp_subdev *ssd = to_smiapp_subdev(subdev); struct v4l2_rect *comp, *crops[SMIAPP_PADS]; - smiapp_get_crop_compose(subdev, fh, crops, &comp, which); + smiapp_get_crop_compose(subdev, cfg, crops, &comp, which); switch (target) { case V4L2_SEL_TGT_CROP: @@ -1730,7 +1730,7 @@ static const struct smiapp_csi_data_format } static int smiapp_set_format_source(struct v4l2_subdev *subdev, - struct v4l2_subdev_fh *fh, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_format *fmt) { struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); @@ -1741,7 +1741,7 @@ static int smiapp_set_format_source(struct v4l2_subdev *subdev, unsigned int i; int rval; - rval = __smiapp_get_format(subdev, fh, fmt); + rval = __smiapp_get_format(subdev, cfg, fmt); if (rval) return rval; @@ -1783,7 +1783,7 @@ static int smiapp_set_format_source(struct v4l2_subdev *subdev, } static int smiapp_set_format(struct v4l2_subdev *subdev, - struct v4l2_subdev_fh *fh, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_format *fmt) { struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); @@ -1795,7 +1795,7 @@ static int smiapp_set_format(struct v4l2_subdev *subdev, if (fmt->pad == ssd->source_pad) { int rval; - rval = smiapp_set_format_source(subdev, fh, fmt); + rval = smiapp_set_format_source(subdev, cfg, fmt); mutex_unlock(&sensor->mutex); @@ -1817,7 +1817,7 @@ static int smiapp_set_format(struct v4l2_subdev *subdev, sensor->limits[SMIAPP_LIMIT_MIN_Y_OUTPUT_SIZE], sensor->limits[SMIAPP_LIMIT_MAX_Y_OUTPUT_SIZE]); - smiapp_get_crop_compose(subdev, fh, crops, NULL, fmt->which); + smiapp_get_crop_compose(subdev, cfg, crops, NULL, fmt->which); crops[ssd->sink_pad]->left = 0; crops[ssd->sink_pad]->top = 0; @@ -1825,7 +1825,7 @@ static int smiapp_set_format(struct v4l2_subdev *subdev, crops[ssd->sink_pad]->height = fmt->format.height; if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) ssd->sink_fmt = *crops[ssd->sink_pad]; - smiapp_propagate(subdev, fh, fmt->which, + smiapp_propagate(subdev, cfg, fmt->which, V4L2_SEL_TGT_CROP); mutex_unlock(&sensor->mutex); @@ -1878,7 +1878,7 @@ static int scaling_goodness(struct v4l2_subdev *subdev, int w, int ask_w, } static void smiapp_set_compose_binner(struct v4l2_subdev *subdev, - struct v4l2_subdev_fh *fh, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_selection *sel, struct v4l2_rect **crops, struct v4l2_rect *comp) @@ -1926,7 +1926,7 @@ static void smiapp_set_compose_binner(struct v4l2_subdev *subdev, * result. */ static void smiapp_set_compose_scaler(struct v4l2_subdev *subdev, - struct v4l2_subdev_fh *fh, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_selection *sel, struct v4l2_rect **crops, struct v4l2_rect *comp) @@ -2042,25 +2042,25 @@ static void smiapp_set_compose_scaler(struct v4l2_subdev *subdev, } /* We're only called on source pads. This function sets scaling. */ static int smiapp_set_compose(struct v4l2_subdev *subdev, - struct v4l2_subdev_fh *fh, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_selection *sel) { struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); struct smiapp_subdev *ssd = to_smiapp_subdev(subdev); struct v4l2_rect *comp, *crops[SMIAPP_PADS]; - smiapp_get_crop_compose(subdev, fh, crops, &comp, sel->which); + smiapp_get_crop_compose(subdev, cfg, crops, &comp, sel->which); sel->r.top = 0; sel->r.left = 0; if (ssd == sensor->binner) - smiapp_set_compose_binner(subdev, fh, sel, crops, comp); + smiapp_set_compose_binner(subdev, cfg, sel, crops, comp); else - smiapp_set_compose_scaler(subdev, fh, sel, crops, comp); + smiapp_set_compose_scaler(subdev, cfg, sel, crops, comp); *comp = sel->r; - smiapp_propagate(subdev, fh, sel->which, + smiapp_propagate(subdev, cfg, sel->which, V4L2_SEL_TGT_COMPOSE); if (sel->which == V4L2_SUBDEV_FORMAT_ACTIVE) @@ -2113,7 +2113,7 @@ static int __smiapp_sel_supported(struct v4l2_subdev *subdev, } static int smiapp_set_crop(struct v4l2_subdev *subdev, - struct v4l2_subdev_fh *fh, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_selection *sel) { struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); @@ -2121,7 +2121,7 @@ static int smiapp_set_crop(struct v4l2_subdev *subdev, struct v4l2_rect *src_size, *crops[SMIAPP_PADS]; struct v4l2_rect _r; - smiapp_get_crop_compose(subdev, fh, crops, NULL, sel->which); + smiapp_get_crop_compose(subdev, cfg, crops, NULL, sel->which); if (sel->which == V4L2_SUBDEV_FORMAT_ACTIVE) { if (sel->pad == ssd->sink_pad) @@ -2132,15 +2132,15 @@ static int smiapp_set_crop(struct v4l2_subdev *subdev, if (sel->pad == ssd->sink_pad) { _r.left = 0; _r.top = 0; - _r.width = v4l2_subdev_get_try_format(fh, sel->pad) + _r.width = v4l2_subdev_get_try_format(subdev, cfg, sel->pad) ->width; - _r.height = v4l2_subdev_get_try_format(fh, sel->pad) + _r.height = v4l2_subdev_get_try_format(subdev, cfg, sel->pad) ->height; src_size = &_r; } else { src_size = v4l2_subdev_get_try_compose( - fh, ssd->sink_pad); + subdev, cfg, ssd->sink_pad); } } @@ -2158,14 +2158,14 @@ static int smiapp_set_crop(struct v4l2_subdev *subdev, *crops[sel->pad] = sel->r; if (ssd != sensor->pixel_array && sel->pad == SMIAPP_PAD_SINK) - smiapp_propagate(subdev, fh, sel->which, + smiapp_propagate(subdev, cfg, sel->which, V4L2_SEL_TGT_CROP); return 0; } static int __smiapp_get_selection(struct v4l2_subdev *subdev, - struct v4l2_subdev_fh *fh, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_selection *sel) { struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); @@ -2178,13 +2178,13 @@ static int __smiapp_get_selection(struct v4l2_subdev *subdev, if (ret) return ret; - smiapp_get_crop_compose(subdev, fh, crops, &comp, sel->which); + smiapp_get_crop_compose(subdev, cfg, crops, &comp, sel->which); if (sel->which == V4L2_SUBDEV_FORMAT_ACTIVE) { sink_fmt = ssd->sink_fmt; } else { struct v4l2_mbus_framefmt *fmt = - v4l2_subdev_get_try_format(fh, ssd->sink_pad); + v4l2_subdev_get_try_format(subdev, cfg, ssd->sink_pad); sink_fmt.left = 0; sink_fmt.top = 0; @@ -2220,20 +2220,20 @@ static int __smiapp_get_selection(struct v4l2_subdev *subdev, } static int smiapp_get_selection(struct v4l2_subdev *subdev, - struct v4l2_subdev_fh *fh, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_selection *sel) { struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); int rval; mutex_lock(&sensor->mutex); - rval = __smiapp_get_selection(subdev, fh, sel); + rval = __smiapp_get_selection(subdev, cfg, sel); mutex_unlock(&sensor->mutex); return rval; } static int smiapp_set_selection(struct v4l2_subdev *subdev, - struct v4l2_subdev_fh *fh, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_selection *sel) { struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); @@ -2259,10 +2259,10 @@ static int smiapp_set_selection(struct v4l2_subdev *subdev, switch (sel->target) { case V4L2_SEL_TGT_CROP: - ret = smiapp_set_crop(subdev, fh, sel); + ret = smiapp_set_crop(subdev, cfg, sel); break; case V4L2_SEL_TGT_COMPOSE: - ret = smiapp_set_compose(subdev, fh, sel); + ret = smiapp_set_compose(subdev, cfg, sel); break; default: ret = -EINVAL; @@ -2841,8 +2841,8 @@ static int smiapp_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) for (i = 0; i < ssd->npads; i++) { struct v4l2_mbus_framefmt *try_fmt = - v4l2_subdev_get_try_format(fh, i); - struct v4l2_rect *try_crop = v4l2_subdev_get_try_crop(fh, i); + v4l2_subdev_get_try_format(sd, fh->pad, i); + struct v4l2_rect *try_crop = v4l2_subdev_get_try_crop(sd, fh->pad, i); struct v4l2_rect *try_comp; try_fmt->width = sensor->limits[SMIAPP_LIMIT_X_ADDR_MAX] + 1; @@ -2858,7 +2858,7 @@ static int smiapp_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) if (ssd != sensor->pixel_array) continue; - try_comp = v4l2_subdev_get_try_compose(fh, i); + try_comp = v4l2_subdev_get_try_compose(sd, fh->pad, i); *try_comp = *try_crop; } diff --git a/drivers/media/i2c/tvp514x.c b/drivers/media/i2c/tvp514x.c index c6b3dc541cc2..1c6bc306ecdc 100644 --- a/drivers/media/i2c/tvp514x.c +++ b/drivers/media/i2c/tvp514x.c @@ -923,13 +923,13 @@ static const struct v4l2_ctrl_ops tvp514x_ctrl_ops = { /** * tvp514x_enum_mbus_code() - V4L2 decoder interface handler for enum_mbus_code * @sd: pointer to standard V4L2 sub-device structure - * @fh: file handle + * @cfg: pad configuration * @code: pointer to v4l2_subdev_mbus_code_enum structure * * Enumertaes mbus codes supported */ static int tvp514x_enum_mbus_code(struct v4l2_subdev *sd, - struct v4l2_subdev_fh *fh, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_mbus_code_enum *code) { u32 pad = code->pad; @@ -950,13 +950,13 @@ static int tvp514x_enum_mbus_code(struct v4l2_subdev *sd, /** * tvp514x_get_pad_format() - V4L2 decoder interface handler for get pad format * @sd: pointer to standard V4L2 sub-device structure - * @fh: file handle + * @cfg: pad configuration * @format: pointer to v4l2_subdev_format structure * * Retrieves pad format which is active or tried based on requirement */ static int tvp514x_get_pad_format(struct v4l2_subdev *sd, - struct v4l2_subdev_fh *fh, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_format *format) { struct tvp514x_decoder *decoder = to_decoder(sd); @@ -979,13 +979,13 @@ static int tvp514x_get_pad_format(struct v4l2_subdev *sd, /** * tvp514x_set_pad_format() - V4L2 decoder interface handler for set pad format * @sd: pointer to standard V4L2 sub-device structure - * @fh: file handle + * @cfg: pad configuration * @format: pointer to v4l2_subdev_format structure * * Set pad format for the output pad */ static int tvp514x_set_pad_format(struct v4l2_subdev *sd, - struct v4l2_subdev_fh *fh, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_format *fmt) { struct tvp514x_decoder *decoder = to_decoder(sd); diff --git a/drivers/media/i2c/tvp7002.c b/drivers/media/i2c/tvp7002.c index 9233194233eb..787cdfb08749 100644 --- a/drivers/media/i2c/tvp7002.c +++ b/drivers/media/i2c/tvp7002.c @@ -846,13 +846,13 @@ static const struct v4l2_ctrl_ops tvp7002_ctrl_ops = { /* * tvp7002_enum_mbus_code() - Enum supported digital video format on pad * @sd: pointer to standard V4L2 sub-device structure - * @fh: file handle for the subdev + * @cfg: pad configuration * @code: pointer to subdev enum mbus code struct * * Enumerate supported digital video formats for pad. */ static int -tvp7002_enum_mbus_code(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, +tvp7002_enum_mbus_code(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_mbus_code_enum *code) { /* Check requested format index is within range */ @@ -867,13 +867,13 @@ tvp7002_enum_mbus_code(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, /* * tvp7002_get_pad_format() - get video format on pad * @sd: pointer to standard V4L2 sub-device structure - * @fh: file handle for the subdev + * @cfg: pad configuration * @fmt: pointer to subdev format struct * * get video format for pad. */ static int -tvp7002_get_pad_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, +tvp7002_get_pad_format(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_format *fmt) { struct tvp7002 *tvp7002 = to_tvp7002(sd); @@ -890,16 +890,16 @@ tvp7002_get_pad_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, /* * tvp7002_set_pad_format() - set video format on pad * @sd: pointer to standard V4L2 sub-device structure - * @fh: file handle for the subdev + * @cfg: pad configuration * @fmt: pointer to subdev format struct * * set video format for pad. */ static int -tvp7002_set_pad_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, +tvp7002_set_pad_format(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_format *fmt) { - return tvp7002_get_pad_format(sd, fh, fmt); + return tvp7002_get_pad_format(sd, cfg, fmt); } /* V4L2 core operation handlers */ diff --git a/drivers/media/platform/exynos4-is/fimc-capture.c b/drivers/media/platform/exynos4-is/fimc-capture.c index 8a2fd8c33d42..cfebf292e15a 100644 --- a/drivers/media/platform/exynos4-is/fimc-capture.c +++ b/drivers/media/platform/exynos4-is/fimc-capture.c @@ -1482,7 +1482,7 @@ void fimc_sensor_notify(struct v4l2_subdev *sd, unsigned int notification, } static int fimc_subdev_enum_mbus_code(struct v4l2_subdev *sd, - struct v4l2_subdev_fh *fh, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_mbus_code_enum *code) { struct fimc_fmt *fmt; @@ -1495,7 +1495,7 @@ static int fimc_subdev_enum_mbus_code(struct v4l2_subdev *sd, } static int fimc_subdev_get_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_fh *fh, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_format *fmt) { struct fimc_dev *fimc = v4l2_get_subdevdata(sd); @@ -1504,7 +1504,7 @@ static int fimc_subdev_get_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *mf; if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - mf = v4l2_subdev_get_try_format(fh, fmt->pad); + mf = v4l2_subdev_get_try_format(sd, cfg, fmt->pad); fmt->format = *mf; return 0; } @@ -1536,7 +1536,7 @@ static int fimc_subdev_get_fmt(struct v4l2_subdev *sd, } static int fimc_subdev_set_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_fh *fh, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_format *fmt) { struct fimc_dev *fimc = v4l2_get_subdevdata(sd); @@ -1559,7 +1559,7 @@ static int fimc_subdev_set_fmt(struct v4l2_subdev *sd, mf->colorspace = V4L2_COLORSPACE_JPEG; if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - mf = v4l2_subdev_get_try_format(fh, fmt->pad); + mf = v4l2_subdev_get_try_format(sd, cfg, fmt->pad); *mf = fmt->format; return 0; } @@ -1602,7 +1602,7 @@ static int fimc_subdev_set_fmt(struct v4l2_subdev *sd, } static int fimc_subdev_get_selection(struct v4l2_subdev *sd, - struct v4l2_subdev_fh *fh, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_selection *sel) { struct fimc_dev *fimc = v4l2_get_subdevdata(sd); @@ -1628,10 +1628,10 @@ static int fimc_subdev_get_selection(struct v4l2_subdev *sd, return 0; case V4L2_SEL_TGT_CROP: - try_sel = v4l2_subdev_get_try_crop(fh, sel->pad); + try_sel = v4l2_subdev_get_try_crop(sd, cfg, sel->pad); break; case V4L2_SEL_TGT_COMPOSE: - try_sel = v4l2_subdev_get_try_compose(fh, sel->pad); + try_sel = v4l2_subdev_get_try_compose(sd, cfg, sel->pad); f = &ctx->d_frame; break; default: @@ -1657,7 +1657,7 @@ static int fimc_subdev_get_selection(struct v4l2_subdev *sd, } static int fimc_subdev_set_selection(struct v4l2_subdev *sd, - struct v4l2_subdev_fh *fh, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_selection *sel) { struct fimc_dev *fimc = v4l2_get_subdevdata(sd); @@ -1675,10 +1675,10 @@ static int fimc_subdev_set_selection(struct v4l2_subdev *sd, switch (sel->target) { case V4L2_SEL_TGT_CROP: - try_sel = v4l2_subdev_get_try_crop(fh, sel->pad); + try_sel = v4l2_subdev_get_try_crop(sd, cfg, sel->pad); break; case V4L2_SEL_TGT_COMPOSE: - try_sel = v4l2_subdev_get_try_compose(fh, sel->pad); + try_sel = v4l2_subdev_get_try_compose(sd, cfg, sel->pad); f = &ctx->d_frame; break; default: diff --git a/drivers/media/platform/exynos4-is/fimc-isp.c b/drivers/media/platform/exynos4-is/fimc-isp.c index 60c744915549..5d78f5716f3b 100644 --- a/drivers/media/platform/exynos4-is/fimc-isp.c +++ b/drivers/media/platform/exynos4-is/fimc-isp.c @@ -112,7 +112,7 @@ static const struct media_entity_operations fimc_is_subdev_media_ops = { }; static int fimc_is_subdev_enum_mbus_code(struct v4l2_subdev *sd, - struct v4l2_subdev_fh *fh, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_mbus_code_enum *code) { const struct fimc_fmt *fmt; @@ -125,14 +125,14 @@ static int fimc_is_subdev_enum_mbus_code(struct v4l2_subdev *sd, } static int fimc_isp_subdev_get_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_fh *fh, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_format *fmt) { struct fimc_isp *isp = v4l2_get_subdevdata(sd); struct v4l2_mbus_framefmt *mf = &fmt->format; if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - *mf = *v4l2_subdev_get_try_format(fh, fmt->pad); + *mf = *v4l2_subdev_get_try_format(sd, cfg, fmt->pad); return 0; } @@ -162,7 +162,7 @@ static int fimc_isp_subdev_get_fmt(struct v4l2_subdev *sd, } static void __isp_subdev_try_format(struct fimc_isp *isp, - struct v4l2_subdev_fh *fh, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_format *fmt) { struct v4l2_mbus_framefmt *mf = &fmt->format; @@ -178,7 +178,7 @@ static void __isp_subdev_try_format(struct fimc_isp *isp, mf->code = MEDIA_BUS_FMT_SGRBG10_1X10; } else { if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) - format = v4l2_subdev_get_try_format(fh, + format = v4l2_subdev_get_try_format(&isp->subdev, cfg, FIMC_ISP_SD_PAD_SINK); else format = &isp->sink_fmt; @@ -197,7 +197,7 @@ static void __isp_subdev_try_format(struct fimc_isp *isp, } static int fimc_isp_subdev_set_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_fh *fh, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_format *fmt) { struct fimc_isp *isp = v4l2_get_subdevdata(sd); @@ -209,10 +209,10 @@ static int fimc_isp_subdev_set_fmt(struct v4l2_subdev *sd, __func__, fmt->pad, mf->code, mf->width, mf->height); mutex_lock(&isp->subdev_lock); - __isp_subdev_try_format(isp, fh, fmt); + __isp_subdev_try_format(isp, cfg, fmt); if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - mf = v4l2_subdev_get_try_format(fh, fmt->pad); + mf = v4l2_subdev_get_try_format(sd, cfg, fmt->pad); *mf = fmt->format; /* Propagate format to the source pads */ @@ -223,8 +223,8 @@ static int fimc_isp_subdev_set_fmt(struct v4l2_subdev *sd, for (pad = FIMC_ISP_SD_PAD_SRC_FIFO; pad < FIMC_ISP_SD_PADS_NUM; pad++) { format.pad = pad; - __isp_subdev_try_format(isp, fh, &format); - mf = v4l2_subdev_get_try_format(fh, pad); + __isp_subdev_try_format(isp, cfg, &format); + mf = v4l2_subdev_get_try_format(sd, cfg, pad); *mf = format.format; } } @@ -236,7 +236,7 @@ static int fimc_isp_subdev_set_fmt(struct v4l2_subdev *sd, isp->sink_fmt = *mf; format.pad = FIMC_ISP_SD_PAD_SRC_DMA; - __isp_subdev_try_format(isp, fh, &format); + __isp_subdev_try_format(isp, cfg, &format); isp->src_fmt = format.format; __is_set_frame_size(is, &isp->src_fmt); @@ -369,7 +369,7 @@ static int fimc_isp_subdev_open(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt fmt; struct v4l2_mbus_framefmt *format; - format = v4l2_subdev_get_try_format(fh, FIMC_ISP_SD_PAD_SINK); + format = v4l2_subdev_get_try_format(sd, fh->pad, FIMC_ISP_SD_PAD_SINK); fmt.colorspace = V4L2_COLORSPACE_SRGB; fmt.code = fimc_isp_formats[0].mbus_code; @@ -378,12 +378,12 @@ static int fimc_isp_subdev_open(struct v4l2_subdev *sd, fmt.field = V4L2_FIELD_NONE; *format = fmt; - format = v4l2_subdev_get_try_format(fh, FIMC_ISP_SD_PAD_SRC_FIFO); + format = v4l2_subdev_get_try_format(sd, fh->pad, FIMC_ISP_SD_PAD_SRC_FIFO); fmt.width = DEFAULT_PREVIEW_STILL_WIDTH; fmt.height = DEFAULT_PREVIEW_STILL_HEIGHT; *format = fmt; - format = v4l2_subdev_get_try_format(fh, FIMC_ISP_SD_PAD_SRC_DMA); + format = v4l2_subdev_get_try_format(sd, fh->pad, FIMC_ISP_SD_PAD_SRC_DMA); *format = fmt; return 0; diff --git a/drivers/media/platform/exynos4-is/fimc-lite.c b/drivers/media/platform/exynos4-is/fimc-lite.c index 2510f189e242..ca6261a86a5f 100644 --- a/drivers/media/platform/exynos4-is/fimc-lite.c +++ b/drivers/media/platform/exynos4-is/fimc-lite.c @@ -568,7 +568,7 @@ static const struct v4l2_file_operations fimc_lite_fops = { */ static const struct fimc_fmt *fimc_lite_subdev_try_fmt(struct fimc_lite *fimc, - struct v4l2_subdev_fh *fh, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_format *format) { struct flite_drvdata *dd = fimc->dd; @@ -592,13 +592,13 @@ static const struct fimc_fmt *fimc_lite_subdev_try_fmt(struct fimc_lite *fimc, struct v4l2_rect *rect; if (format->which == V4L2_SUBDEV_FORMAT_TRY) { - sink_fmt = v4l2_subdev_get_try_format(fh, + sink_fmt = v4l2_subdev_get_try_format(&fimc->subdev, cfg, FLITE_SD_PAD_SINK); mf->code = sink_fmt->code; mf->colorspace = sink_fmt->colorspace; - rect = v4l2_subdev_get_try_crop(fh, + rect = v4l2_subdev_get_try_crop(&fimc->subdev, cfg, FLITE_SD_PAD_SINK); } else { mf->code = sink->fmt->mbus_code; @@ -1047,7 +1047,7 @@ static const struct media_entity_operations fimc_lite_subdev_media_ops = { }; static int fimc_lite_subdev_enum_mbus_code(struct v4l2_subdev *sd, - struct v4l2_subdev_fh *fh, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_mbus_code_enum *code) { const struct fimc_fmt *fmt; @@ -1060,16 +1060,17 @@ static int fimc_lite_subdev_enum_mbus_code(struct v4l2_subdev *sd, } static struct v4l2_mbus_framefmt *__fimc_lite_subdev_get_try_fmt( - struct v4l2_subdev_fh *fh, unsigned int pad) + struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, unsigned int pad) { if (pad != FLITE_SD_PAD_SINK) pad = FLITE_SD_PAD_SOURCE_DMA; - return v4l2_subdev_get_try_format(fh, pad); + return v4l2_subdev_get_try_format(sd, cfg, pad); } static int fimc_lite_subdev_get_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_fh *fh, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_format *fmt) { struct fimc_lite *fimc = v4l2_get_subdevdata(sd); @@ -1077,7 +1078,7 @@ static int fimc_lite_subdev_get_fmt(struct v4l2_subdev *sd, struct flite_frame *f = &fimc->inp_frame; if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - mf = __fimc_lite_subdev_get_try_fmt(fh, fmt->pad); + mf = __fimc_lite_subdev_get_try_fmt(sd, cfg, fmt->pad); fmt->format = *mf; return 0; } @@ -1100,7 +1101,7 @@ static int fimc_lite_subdev_get_fmt(struct v4l2_subdev *sd, } static int fimc_lite_subdev_set_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_fh *fh, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_format *fmt) { struct fimc_lite *fimc = v4l2_get_subdevdata(sd); @@ -1122,17 +1123,17 @@ static int fimc_lite_subdev_set_fmt(struct v4l2_subdev *sd, return -EBUSY; } - ffmt = fimc_lite_subdev_try_fmt(fimc, fh, fmt); + ffmt = fimc_lite_subdev_try_fmt(fimc, cfg, fmt); if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { struct v4l2_mbus_framefmt *src_fmt; - mf = __fimc_lite_subdev_get_try_fmt(fh, fmt->pad); + mf = __fimc_lite_subdev_get_try_fmt(sd, cfg, fmt->pad); *mf = fmt->format; if (fmt->pad == FLITE_SD_PAD_SINK) { unsigned int pad = FLITE_SD_PAD_SOURCE_DMA; - src_fmt = __fimc_lite_subdev_get_try_fmt(fh, pad); + src_fmt = __fimc_lite_subdev_get_try_fmt(sd, cfg, pad); *src_fmt = *mf; } @@ -1160,7 +1161,7 @@ static int fimc_lite_subdev_set_fmt(struct v4l2_subdev *sd, } static int fimc_lite_subdev_get_selection(struct v4l2_subdev *sd, - struct v4l2_subdev_fh *fh, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_selection *sel) { struct fimc_lite *fimc = v4l2_get_subdevdata(sd); @@ -1172,7 +1173,7 @@ static int fimc_lite_subdev_get_selection(struct v4l2_subdev *sd, return -EINVAL; if (sel->which == V4L2_SUBDEV_FORMAT_TRY) { - sel->r = *v4l2_subdev_get_try_crop(fh, sel->pad); + sel->r = *v4l2_subdev_get_try_crop(sd, cfg, sel->pad); return 0; } @@ -1195,7 +1196,7 @@ static int fimc_lite_subdev_get_selection(struct v4l2_subdev *sd, } static int fimc_lite_subdev_set_selection(struct v4l2_subdev *sd, - struct v4l2_subdev_fh *fh, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_selection *sel) { struct fimc_lite *fimc = v4l2_get_subdevdata(sd); @@ -1209,7 +1210,7 @@ static int fimc_lite_subdev_set_selection(struct v4l2_subdev *sd, fimc_lite_try_crop(fimc, &sel->r); if (sel->which == V4L2_SUBDEV_FORMAT_TRY) { - *v4l2_subdev_get_try_crop(fh, sel->pad) = sel->r; + *v4l2_subdev_get_try_crop(sd, cfg, sel->pad) = sel->r; } else { unsigned long flags; spin_lock_irqsave(&fimc->slock, flags); diff --git a/drivers/media/platform/exynos4-is/mipi-csis.c b/drivers/media/platform/exynos4-is/mipi-csis.c index 2504aa89a6f4..d74e1bec3d86 100644 --- a/drivers/media/platform/exynos4-is/mipi-csis.c +++ b/drivers/media/platform/exynos4-is/mipi-csis.c @@ -540,7 +540,7 @@ unlock: } static int s5pcsis_enum_mbus_code(struct v4l2_subdev *sd, - struct v4l2_subdev_fh *fh, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_mbus_code_enum *code) { if (code->index >= ARRAY_SIZE(s5pcsis_formats)) @@ -568,23 +568,23 @@ static struct csis_pix_format const *s5pcsis_try_format( } static struct v4l2_mbus_framefmt *__s5pcsis_get_format( - struct csis_state *state, struct v4l2_subdev_fh *fh, + struct csis_state *state, struct v4l2_subdev_pad_config *cfg, enum v4l2_subdev_format_whence which) { if (which == V4L2_SUBDEV_FORMAT_TRY) - return fh ? v4l2_subdev_get_try_format(fh, 0) : NULL; + return cfg ? v4l2_subdev_get_try_format(&state->sd, cfg, 0) : NULL; return &state->format; } -static int s5pcsis_set_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, +static int s5pcsis_set_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_format *fmt) { struct csis_state *state = sd_to_csis_state(sd); struct csis_pix_format const *csis_fmt; struct v4l2_mbus_framefmt *mf; - mf = __s5pcsis_get_format(state, fh, fmt->which); + mf = __s5pcsis_get_format(state, cfg, fmt->which); if (fmt->pad == CSIS_PAD_SOURCE) { if (mf) { @@ -605,13 +605,13 @@ static int s5pcsis_set_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, return 0; } -static int s5pcsis_get_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, +static int s5pcsis_get_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_format *fmt) { struct csis_state *state = sd_to_csis_state(sd); struct v4l2_mbus_framefmt *mf; - mf = __s5pcsis_get_format(state, fh, fmt->which); + mf = __s5pcsis_get_format(state, cfg, fmt->which); if (!mf) return -EINVAL; @@ -651,7 +651,7 @@ static int s5pcsis_log_status(struct v4l2_subdev *sd) static int s5pcsis_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) { - struct v4l2_mbus_framefmt *format = v4l2_subdev_get_try_format(fh, 0); + struct v4l2_mbus_framefmt *format = v4l2_subdev_get_try_format(sd, fh->pad, 0); format->colorspace = V4L2_COLORSPACE_JPEG; format->code = s5pcsis_formats[0].code; diff --git a/drivers/media/platform/omap3isp/ispccdc.c b/drivers/media/platform/omap3isp/ispccdc.c index 587489a072d5..b0431a988d47 100644 --- a/drivers/media/platform/omap3isp/ispccdc.c +++ b/drivers/media/platform/omap3isp/ispccdc.c @@ -32,7 +32,7 @@ #define CCDC_MIN_HEIGHT 32 static struct v4l2_mbus_framefmt * -__ccdc_get_format(struct isp_ccdc_device *ccdc, struct v4l2_subdev_fh *fh, +__ccdc_get_format(struct isp_ccdc_device *ccdc, struct v4l2_subdev_pad_config *cfg, unsigned int pad, enum v4l2_subdev_format_whence which); static const unsigned int ccdc_fmts[] = { @@ -1935,21 +1935,21 @@ static int ccdc_set_stream(struct v4l2_subdev *sd, int enable) } static struct v4l2_mbus_framefmt * -__ccdc_get_format(struct isp_ccdc_device *ccdc, struct v4l2_subdev_fh *fh, +__ccdc_get_format(struct isp_ccdc_device *ccdc, struct v4l2_subdev_pad_config *cfg, unsigned int pad, enum v4l2_subdev_format_whence which) { if (which == V4L2_SUBDEV_FORMAT_TRY) - return v4l2_subdev_get_try_format(fh, pad); + return v4l2_subdev_get_try_format(&ccdc->subdev, cfg, pad); else return &ccdc->formats[pad]; } static struct v4l2_rect * -__ccdc_get_crop(struct isp_ccdc_device *ccdc, struct v4l2_subdev_fh *fh, +__ccdc_get_crop(struct isp_ccdc_device *ccdc, struct v4l2_subdev_pad_config *cfg, enum v4l2_subdev_format_whence which) { if (which == V4L2_SUBDEV_FORMAT_TRY) - return v4l2_subdev_get_try_crop(fh, CCDC_PAD_SOURCE_OF); + return v4l2_subdev_get_try_crop(&ccdc->subdev, cfg, CCDC_PAD_SOURCE_OF); else return &ccdc->crop; } @@ -1957,12 +1957,12 @@ __ccdc_get_crop(struct isp_ccdc_device *ccdc, struct v4l2_subdev_fh *fh, /* * ccdc_try_format - Try video format on a pad * @ccdc: ISP CCDC device - * @fh : V4L2 subdev file handle + * @cfg : V4L2 subdev pad configuration * @pad: Pad number * @fmt: Format */ static void -ccdc_try_format(struct isp_ccdc_device *ccdc, struct v4l2_subdev_fh *fh, +ccdc_try_format(struct isp_ccdc_device *ccdc, struct v4l2_subdev_pad_config *cfg, unsigned int pad, struct v4l2_mbus_framefmt *fmt, enum v4l2_subdev_format_whence which) { @@ -1998,7 +1998,7 @@ ccdc_try_format(struct isp_ccdc_device *ccdc, struct v4l2_subdev_fh *fh, case CCDC_PAD_SOURCE_OF: pixelcode = fmt->code; field = fmt->field; - *fmt = *__ccdc_get_format(ccdc, fh, CCDC_PAD_SINK, which); + *fmt = *__ccdc_get_format(ccdc, cfg, CCDC_PAD_SINK, which); /* In SYNC mode the bridge converts YUV formats from 2X8 to * 1X16. In BT.656 no such conversion occurs. As we don't know @@ -2023,7 +2023,7 @@ ccdc_try_format(struct isp_ccdc_device *ccdc, struct v4l2_subdev_fh *fh, } /* Hardcode the output size to the crop rectangle size. */ - crop = __ccdc_get_crop(ccdc, fh, which); + crop = __ccdc_get_crop(ccdc, cfg, which); fmt->width = crop->width; fmt->height = crop->height; @@ -2040,7 +2040,7 @@ ccdc_try_format(struct isp_ccdc_device *ccdc, struct v4l2_subdev_fh *fh, break; case CCDC_PAD_SOURCE_VP: - *fmt = *__ccdc_get_format(ccdc, fh, CCDC_PAD_SINK, which); + *fmt = *__ccdc_get_format(ccdc, cfg, CCDC_PAD_SINK, which); /* The video port interface truncates the data to 10 bits. */ info = omap3isp_video_format_info(fmt->code); @@ -2112,12 +2112,12 @@ static void ccdc_try_crop(struct isp_ccdc_device *ccdc, /* * ccdc_enum_mbus_code - Handle pixel format enumeration * @sd : pointer to v4l2 subdev structure - * @fh : V4L2 subdev file handle + * @cfg : V4L2 subdev pad configuration * @code : pointer to v4l2_subdev_mbus_code_enum structure * return -EINVAL or zero on success */ static int ccdc_enum_mbus_code(struct v4l2_subdev *sd, - struct v4l2_subdev_fh *fh, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_mbus_code_enum *code) { struct isp_ccdc_device *ccdc = v4l2_get_subdevdata(sd); @@ -2132,7 +2132,7 @@ static int ccdc_enum_mbus_code(struct v4l2_subdev *sd, break; case CCDC_PAD_SOURCE_OF: - format = __ccdc_get_format(ccdc, fh, code->pad, + format = __ccdc_get_format(ccdc, cfg, code->pad, V4L2_SUBDEV_FORMAT_TRY); if (format->code == MEDIA_BUS_FMT_YUYV8_2X8 || @@ -2163,7 +2163,7 @@ static int ccdc_enum_mbus_code(struct v4l2_subdev *sd, if (code->index != 0) return -EINVAL; - format = __ccdc_get_format(ccdc, fh, code->pad, + format = __ccdc_get_format(ccdc, cfg, code->pad, V4L2_SUBDEV_FORMAT_TRY); /* A pixel code equal to 0 means that the video port doesn't @@ -2183,7 +2183,7 @@ static int ccdc_enum_mbus_code(struct v4l2_subdev *sd, } static int ccdc_enum_frame_size(struct v4l2_subdev *sd, - struct v4l2_subdev_fh *fh, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_frame_size_enum *fse) { struct isp_ccdc_device *ccdc = v4l2_get_subdevdata(sd); @@ -2195,7 +2195,7 @@ static int ccdc_enum_frame_size(struct v4l2_subdev *sd, format.code = fse->code; format.width = 1; format.height = 1; - ccdc_try_format(ccdc, fh, fse->pad, &format, V4L2_SUBDEV_FORMAT_TRY); + ccdc_try_format(ccdc, cfg, fse->pad, &format, V4L2_SUBDEV_FORMAT_TRY); fse->min_width = format.width; fse->min_height = format.height; @@ -2205,7 +2205,7 @@ static int ccdc_enum_frame_size(struct v4l2_subdev *sd, format.code = fse->code; format.width = -1; format.height = -1; - ccdc_try_format(ccdc, fh, fse->pad, &format, V4L2_SUBDEV_FORMAT_TRY); + ccdc_try_format(ccdc, cfg, fse->pad, &format, V4L2_SUBDEV_FORMAT_TRY); fse->max_width = format.width; fse->max_height = format.height; @@ -2215,7 +2215,7 @@ static int ccdc_enum_frame_size(struct v4l2_subdev *sd, /* * ccdc_get_selection - Retrieve a selection rectangle on a pad * @sd: ISP CCDC V4L2 subdevice - * @fh: V4L2 subdev file handle + * @cfg: V4L2 subdev pad configuration * @sel: Selection rectangle * * The only supported rectangles are the crop rectangles on the output formatter @@ -2223,7 +2223,7 @@ static int ccdc_enum_frame_size(struct v4l2_subdev *sd, * * Return 0 on success or a negative error code otherwise. */ -static int ccdc_get_selection(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, +static int ccdc_get_selection(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_selection *sel) { struct isp_ccdc_device *ccdc = v4l2_get_subdevdata(sd); @@ -2239,12 +2239,12 @@ static int ccdc_get_selection(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, sel->r.width = INT_MAX; sel->r.height = INT_MAX; - format = __ccdc_get_format(ccdc, fh, CCDC_PAD_SINK, sel->which); + format = __ccdc_get_format(ccdc, cfg, CCDC_PAD_SINK, sel->which); ccdc_try_crop(ccdc, format, &sel->r); break; case V4L2_SEL_TGT_CROP: - sel->r = *__ccdc_get_crop(ccdc, fh, sel->which); + sel->r = *__ccdc_get_crop(ccdc, cfg, sel->which); break; default: @@ -2257,7 +2257,7 @@ static int ccdc_get_selection(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, /* * ccdc_set_selection - Set a selection rectangle on a pad * @sd: ISP CCDC V4L2 subdevice - * @fh: V4L2 subdev file handle + * @cfg: V4L2 subdev pad configuration * @sel: Selection rectangle * * The only supported rectangle is the actual crop rectangle on the output @@ -2265,7 +2265,7 @@ static int ccdc_get_selection(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, * * Return 0 on success or a negative error code otherwise. */ -static int ccdc_set_selection(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, +static int ccdc_set_selection(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_selection *sel) { struct isp_ccdc_device *ccdc = v4l2_get_subdevdata(sd); @@ -2284,17 +2284,17 @@ static int ccdc_set_selection(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, * rectangle. */ if (sel->flags & V4L2_SEL_FLAG_KEEP_CONFIG) { - sel->r = *__ccdc_get_crop(ccdc, fh, sel->which); + sel->r = *__ccdc_get_crop(ccdc, cfg, sel->which); return 0; } - format = __ccdc_get_format(ccdc, fh, CCDC_PAD_SINK, sel->which); + format = __ccdc_get_format(ccdc, cfg, CCDC_PAD_SINK, sel->which); ccdc_try_crop(ccdc, format, &sel->r); - *__ccdc_get_crop(ccdc, fh, sel->which) = sel->r; + *__ccdc_get_crop(ccdc, cfg, sel->which) = sel->r; /* Update the source format. */ - format = __ccdc_get_format(ccdc, fh, CCDC_PAD_SOURCE_OF, sel->which); - ccdc_try_format(ccdc, fh, CCDC_PAD_SOURCE_OF, format, sel->which); + format = __ccdc_get_format(ccdc, cfg, CCDC_PAD_SOURCE_OF, sel->which); + ccdc_try_format(ccdc, cfg, CCDC_PAD_SOURCE_OF, format, sel->which); return 0; } @@ -2302,19 +2302,19 @@ static int ccdc_set_selection(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, /* * ccdc_get_format - Retrieve the video format on a pad * @sd : ISP CCDC V4L2 subdevice - * @fh : V4L2 subdev file handle + * @cfg: V4L2 subdev pad configuration * @fmt: Format * * Return 0 on success or -EINVAL if the pad is invalid or doesn't correspond * to the format type. */ -static int ccdc_get_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, +static int ccdc_get_format(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_format *fmt) { struct isp_ccdc_device *ccdc = v4l2_get_subdevdata(sd); struct v4l2_mbus_framefmt *format; - format = __ccdc_get_format(ccdc, fh, fmt->pad, fmt->which); + format = __ccdc_get_format(ccdc, cfg, fmt->pad, fmt->which); if (format == NULL) return -EINVAL; @@ -2325,30 +2325,30 @@ static int ccdc_get_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, /* * ccdc_set_format - Set the video format on a pad * @sd : ISP CCDC V4L2 subdevice - * @fh : V4L2 subdev file handle + * @cfg: V4L2 subdev pad configuration * @fmt: Format * * Return 0 on success or -EINVAL if the pad is invalid or doesn't correspond * to the format type. */ -static int ccdc_set_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, +static int ccdc_set_format(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_format *fmt) { struct isp_ccdc_device *ccdc = v4l2_get_subdevdata(sd); struct v4l2_mbus_framefmt *format; struct v4l2_rect *crop; - format = __ccdc_get_format(ccdc, fh, fmt->pad, fmt->which); + format = __ccdc_get_format(ccdc, cfg, fmt->pad, fmt->which); if (format == NULL) return -EINVAL; - ccdc_try_format(ccdc, fh, fmt->pad, &fmt->format, fmt->which); + ccdc_try_format(ccdc, cfg, fmt->pad, &fmt->format, fmt->which); *format = fmt->format; /* Propagate the format from sink to source */ if (fmt->pad == CCDC_PAD_SINK) { /* Reset the crop rectangle. */ - crop = __ccdc_get_crop(ccdc, fh, fmt->which); + crop = __ccdc_get_crop(ccdc, cfg, fmt->which); crop->left = 0; crop->top = 0; crop->width = fmt->format.width; @@ -2357,16 +2357,16 @@ static int ccdc_set_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, ccdc_try_crop(ccdc, &fmt->format, crop); /* Update the source formats. */ - format = __ccdc_get_format(ccdc, fh, CCDC_PAD_SOURCE_OF, + format = __ccdc_get_format(ccdc, cfg, CCDC_PAD_SOURCE_OF, fmt->which); *format = fmt->format; - ccdc_try_format(ccdc, fh, CCDC_PAD_SOURCE_OF, format, + ccdc_try_format(ccdc, cfg, CCDC_PAD_SOURCE_OF, format, fmt->which); - format = __ccdc_get_format(ccdc, fh, CCDC_PAD_SOURCE_VP, + format = __ccdc_get_format(ccdc, cfg, CCDC_PAD_SOURCE_VP, fmt->which); *format = fmt->format; - ccdc_try_format(ccdc, fh, CCDC_PAD_SOURCE_VP, format, + ccdc_try_format(ccdc, cfg, CCDC_PAD_SOURCE_VP, format, fmt->which); } @@ -2453,7 +2453,7 @@ static int ccdc_init_formats(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) format.format.code = MEDIA_BUS_FMT_SGRBG10_1X10; format.format.width = 4096; format.format.height = 4096; - ccdc_set_format(sd, fh, &format); + ccdc_set_format(sd, fh ? fh->pad : NULL, &format); return 0; } diff --git a/drivers/media/platform/omap3isp/ispccp2.c b/drivers/media/platform/omap3isp/ispccp2.c index f4aedb37e41e..3f10c3af3a0a 100644 --- a/drivers/media/platform/omap3isp/ispccp2.c +++ b/drivers/media/platform/omap3isp/ispccp2.c @@ -611,17 +611,17 @@ static const unsigned int ccp2_fmts[] = { /* * __ccp2_get_format - helper function for getting ccp2 format * @ccp2 : Pointer to ISP CCP2 device - * @fh : V4L2 subdev file handle + * @cfg: V4L2 subdev pad configuration * @pad : pad number * @which : wanted subdev format * return format structure or NULL on error */ static struct v4l2_mbus_framefmt * -__ccp2_get_format(struct isp_ccp2_device *ccp2, struct v4l2_subdev_fh *fh, +__ccp2_get_format(struct isp_ccp2_device *ccp2, struct v4l2_subdev_pad_config *cfg, unsigned int pad, enum v4l2_subdev_format_whence which) { if (which == V4L2_SUBDEV_FORMAT_TRY) - return v4l2_subdev_get_try_format(fh, pad); + return v4l2_subdev_get_try_format(&ccp2->subdev, cfg, pad); else return &ccp2->formats[pad]; } @@ -629,13 +629,13 @@ __ccp2_get_format(struct isp_ccp2_device *ccp2, struct v4l2_subdev_fh *fh, /* * ccp2_try_format - Handle try format by pad subdev method * @ccp2 : Pointer to ISP CCP2 device - * @fh : V4L2 subdev file handle + * @cfg: V4L2 subdev pad configuration * @pad : pad num * @fmt : pointer to v4l2 mbus format structure * @which : wanted subdev format */ static void ccp2_try_format(struct isp_ccp2_device *ccp2, - struct v4l2_subdev_fh *fh, unsigned int pad, + struct v4l2_subdev_pad_config *cfg, unsigned int pad, struct v4l2_mbus_framefmt *fmt, enum v4l2_subdev_format_whence which) { @@ -669,7 +669,7 @@ static void ccp2_try_format(struct isp_ccp2_device *ccp2, * When CCP2 write to memory feature will be added this * should be changed properly. */ - format = __ccp2_get_format(ccp2, fh, CCP2_PAD_SINK, which); + format = __ccp2_get_format(ccp2, cfg, CCP2_PAD_SINK, which); memcpy(fmt, format, sizeof(*fmt)); fmt->code = MEDIA_BUS_FMT_SGRBG10_1X10; break; @@ -682,12 +682,12 @@ static void ccp2_try_format(struct isp_ccp2_device *ccp2, /* * ccp2_enum_mbus_code - Handle pixel format enumeration * @sd : pointer to v4l2 subdev structure - * @fh : V4L2 subdev file handle + * @cfg: V4L2 subdev pad configuration * @code : pointer to v4l2_subdev_mbus_code_enum structure * return -EINVAL or zero on success */ static int ccp2_enum_mbus_code(struct v4l2_subdev *sd, - struct v4l2_subdev_fh *fh, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_mbus_code_enum *code) { struct isp_ccp2_device *ccp2 = v4l2_get_subdevdata(sd); @@ -702,7 +702,7 @@ static int ccp2_enum_mbus_code(struct v4l2_subdev *sd, if (code->index != 0) return -EINVAL; - format = __ccp2_get_format(ccp2, fh, CCP2_PAD_SINK, + format = __ccp2_get_format(ccp2, cfg, CCP2_PAD_SINK, V4L2_SUBDEV_FORMAT_TRY); code->code = format->code; } @@ -711,7 +711,7 @@ static int ccp2_enum_mbus_code(struct v4l2_subdev *sd, } static int ccp2_enum_frame_size(struct v4l2_subdev *sd, - struct v4l2_subdev_fh *fh, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_frame_size_enum *fse) { struct isp_ccp2_device *ccp2 = v4l2_get_subdevdata(sd); @@ -723,7 +723,7 @@ static int ccp2_enum_frame_size(struct v4l2_subdev *sd, format.code = fse->code; format.width = 1; format.height = 1; - ccp2_try_format(ccp2, fh, fse->pad, &format, V4L2_SUBDEV_FORMAT_TRY); + ccp2_try_format(ccp2, cfg, fse->pad, &format, V4L2_SUBDEV_FORMAT_TRY); fse->min_width = format.width; fse->min_height = format.height; @@ -733,7 +733,7 @@ static int ccp2_enum_frame_size(struct v4l2_subdev *sd, format.code = fse->code; format.width = -1; format.height = -1; - ccp2_try_format(ccp2, fh, fse->pad, &format, V4L2_SUBDEV_FORMAT_TRY); + ccp2_try_format(ccp2, cfg, fse->pad, &format, V4L2_SUBDEV_FORMAT_TRY); fse->max_width = format.width; fse->max_height = format.height; @@ -743,17 +743,17 @@ static int ccp2_enum_frame_size(struct v4l2_subdev *sd, /* * ccp2_get_format - Handle get format by pads subdev method * @sd : pointer to v4l2 subdev structure - * @fh : V4L2 subdev file handle + * @cfg: V4L2 subdev pad configuration * @fmt : pointer to v4l2 subdev format structure * return -EINVAL or zero on success */ -static int ccp2_get_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, +static int ccp2_get_format(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_format *fmt) { struct isp_ccp2_device *ccp2 = v4l2_get_subdevdata(sd); struct v4l2_mbus_framefmt *format; - format = __ccp2_get_format(ccp2, fh, fmt->pad, fmt->which); + format = __ccp2_get_format(ccp2, cfg, fmt->pad, fmt->which); if (format == NULL) return -EINVAL; @@ -764,29 +764,29 @@ static int ccp2_get_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, /* * ccp2_set_format - Handle set format by pads subdev method * @sd : pointer to v4l2 subdev structure - * @fh : V4L2 subdev file handle + * @cfg: V4L2 subdev pad configuration * @fmt : pointer to v4l2 subdev format structure * returns zero */ -static int ccp2_set_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, +static int ccp2_set_format(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_format *fmt) { struct isp_ccp2_device *ccp2 = v4l2_get_subdevdata(sd); struct v4l2_mbus_framefmt *format; - format = __ccp2_get_format(ccp2, fh, fmt->pad, fmt->which); + format = __ccp2_get_format(ccp2, cfg, fmt->pad, fmt->which); if (format == NULL) return -EINVAL; - ccp2_try_format(ccp2, fh, fmt->pad, &fmt->format, fmt->which); + ccp2_try_format(ccp2, cfg, fmt->pad, &fmt->format, fmt->which); *format = fmt->format; /* Propagate the format from sink to source */ if (fmt->pad == CCP2_PAD_SINK) { - format = __ccp2_get_format(ccp2, fh, CCP2_PAD_SOURCE, + format = __ccp2_get_format(ccp2, cfg, CCP2_PAD_SOURCE, fmt->which); *format = fmt->format; - ccp2_try_format(ccp2, fh, CCP2_PAD_SOURCE, format, fmt->which); + ccp2_try_format(ccp2, cfg, CCP2_PAD_SOURCE, format, fmt->which); } return 0; @@ -811,7 +811,7 @@ static int ccp2_init_formats(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) format.format.code = MEDIA_BUS_FMT_SGRBG10_1X10; format.format.width = 4096; format.format.height = 4096; - ccp2_set_format(sd, fh, &format); + ccp2_set_format(sd, fh ? fh->pad : NULL, &format); return 0; } diff --git a/drivers/media/platform/omap3isp/ispcsi2.c b/drivers/media/platform/omap3isp/ispcsi2.c index 09c686d96ae8..12ca63f2bf53 100644 --- a/drivers/media/platform/omap3isp/ispcsi2.c +++ b/drivers/media/platform/omap3isp/ispcsi2.c @@ -829,17 +829,17 @@ static const struct isp_video_operations csi2_ispvideo_ops = { */ static struct v4l2_mbus_framefmt * -__csi2_get_format(struct isp_csi2_device *csi2, struct v4l2_subdev_fh *fh, +__csi2_get_format(struct isp_csi2_device *csi2, struct v4l2_subdev_pad_config *cfg, unsigned int pad, enum v4l2_subdev_format_whence which) { if (which == V4L2_SUBDEV_FORMAT_TRY) - return v4l2_subdev_get_try_format(fh, pad); + return v4l2_subdev_get_try_format(&csi2->subdev, cfg, pad); else return &csi2->formats[pad]; } static void -csi2_try_format(struct isp_csi2_device *csi2, struct v4l2_subdev_fh *fh, +csi2_try_format(struct isp_csi2_device *csi2, struct v4l2_subdev_pad_config *cfg, unsigned int pad, struct v4l2_mbus_framefmt *fmt, enum v4l2_subdev_format_whence which) { @@ -869,7 +869,7 @@ csi2_try_format(struct isp_csi2_device *csi2, struct v4l2_subdev_fh *fh, * compression. */ pixelcode = fmt->code; - format = __csi2_get_format(csi2, fh, CSI2_PAD_SINK, which); + format = __csi2_get_format(csi2, cfg, CSI2_PAD_SINK, which); memcpy(fmt, format, sizeof(*fmt)); /* @@ -890,12 +890,12 @@ csi2_try_format(struct isp_csi2_device *csi2, struct v4l2_subdev_fh *fh, /* * csi2_enum_mbus_code - Handle pixel format enumeration * @sd : pointer to v4l2 subdev structure - * @fh : V4L2 subdev file handle + * @cfg: V4L2 subdev pad configuration * @code : pointer to v4l2_subdev_mbus_code_enum structure * return -EINVAL or zero on success */ static int csi2_enum_mbus_code(struct v4l2_subdev *sd, - struct v4l2_subdev_fh *fh, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_mbus_code_enum *code) { struct isp_csi2_device *csi2 = v4l2_get_subdevdata(sd); @@ -908,7 +908,7 @@ static int csi2_enum_mbus_code(struct v4l2_subdev *sd, code->code = csi2_input_fmts[code->index]; } else { - format = __csi2_get_format(csi2, fh, CSI2_PAD_SINK, + format = __csi2_get_format(csi2, cfg, CSI2_PAD_SINK, V4L2_SUBDEV_FORMAT_TRY); switch (code->index) { case 0: @@ -932,7 +932,7 @@ static int csi2_enum_mbus_code(struct v4l2_subdev *sd, } static int csi2_enum_frame_size(struct v4l2_subdev *sd, - struct v4l2_subdev_fh *fh, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_frame_size_enum *fse) { struct isp_csi2_device *csi2 = v4l2_get_subdevdata(sd); @@ -944,7 +944,7 @@ static int csi2_enum_frame_size(struct v4l2_subdev *sd, format.code = fse->code; format.width = 1; format.height = 1; - csi2_try_format(csi2, fh, fse->pad, &format, V4L2_SUBDEV_FORMAT_TRY); + csi2_try_format(csi2, cfg, fse->pad, &format, V4L2_SUBDEV_FORMAT_TRY); fse->min_width = format.width; fse->min_height = format.height; @@ -954,7 +954,7 @@ static int csi2_enum_frame_size(struct v4l2_subdev *sd, format.code = fse->code; format.width = -1; format.height = -1; - csi2_try_format(csi2, fh, fse->pad, &format, V4L2_SUBDEV_FORMAT_TRY); + csi2_try_format(csi2, cfg, fse->pad, &format, V4L2_SUBDEV_FORMAT_TRY); fse->max_width = format.width; fse->max_height = format.height; @@ -964,17 +964,17 @@ static int csi2_enum_frame_size(struct v4l2_subdev *sd, /* * csi2_get_format - Handle get format by pads subdev method * @sd : pointer to v4l2 subdev structure - * @fh : V4L2 subdev file handle + * @cfg: V4L2 subdev pad configuration * @fmt: pointer to v4l2 subdev format structure * return -EINVAL or zero on success */ -static int csi2_get_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, +static int csi2_get_format(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_format *fmt) { struct isp_csi2_device *csi2 = v4l2_get_subdevdata(sd); struct v4l2_mbus_framefmt *format; - format = __csi2_get_format(csi2, fh, fmt->pad, fmt->which); + format = __csi2_get_format(csi2, cfg, fmt->pad, fmt->which); if (format == NULL) return -EINVAL; @@ -985,29 +985,29 @@ static int csi2_get_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, /* * csi2_set_format - Handle set format by pads subdev method * @sd : pointer to v4l2 subdev structure - * @fh : V4L2 subdev file handle + * @cfg: V4L2 subdev pad configuration * @fmt: pointer to v4l2 subdev format structure * return -EINVAL or zero on success */ -static int csi2_set_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, +static int csi2_set_format(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_format *fmt) { struct isp_csi2_device *csi2 = v4l2_get_subdevdata(sd); struct v4l2_mbus_framefmt *format; - format = __csi2_get_format(csi2, fh, fmt->pad, fmt->which); + format = __csi2_get_format(csi2, cfg, fmt->pad, fmt->which); if (format == NULL) return -EINVAL; - csi2_try_format(csi2, fh, fmt->pad, &fmt->format, fmt->which); + csi2_try_format(csi2, cfg, fmt->pad, &fmt->format, fmt->which); *format = fmt->format; /* Propagate the format from sink to source */ if (fmt->pad == CSI2_PAD_SINK) { - format = __csi2_get_format(csi2, fh, CSI2_PAD_SOURCE, + format = __csi2_get_format(csi2, cfg, CSI2_PAD_SOURCE, fmt->which); *format = fmt->format; - csi2_try_format(csi2, fh, CSI2_PAD_SOURCE, format, fmt->which); + csi2_try_format(csi2, cfg, CSI2_PAD_SOURCE, format, fmt->which); } return 0; @@ -1032,7 +1032,7 @@ static int csi2_init_formats(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) format.format.code = MEDIA_BUS_FMT_SGRBG10_1X10; format.format.width = 4096; format.format.height = 4096; - csi2_set_format(sd, fh, &format); + csi2_set_format(sd, fh ? fh->pad : NULL, &format); return 0; } diff --git a/drivers/media/platform/omap3isp/isppreview.c b/drivers/media/platform/omap3isp/isppreview.c index dd9eed45d853..0571c57dbae0 100644 --- a/drivers/media/platform/omap3isp/isppreview.c +++ b/drivers/media/platform/omap3isp/isppreview.c @@ -1686,21 +1686,21 @@ static int preview_set_stream(struct v4l2_subdev *sd, int enable) } static struct v4l2_mbus_framefmt * -__preview_get_format(struct isp_prev_device *prev, struct v4l2_subdev_fh *fh, +__preview_get_format(struct isp_prev_device *prev, struct v4l2_subdev_pad_config *cfg, unsigned int pad, enum v4l2_subdev_format_whence which) { if (which == V4L2_SUBDEV_FORMAT_TRY) - return v4l2_subdev_get_try_format(fh, pad); + return v4l2_subdev_get_try_format(&prev->subdev, cfg, pad); else return &prev->formats[pad]; } static struct v4l2_rect * -__preview_get_crop(struct isp_prev_device *prev, struct v4l2_subdev_fh *fh, +__preview_get_crop(struct isp_prev_device *prev, struct v4l2_subdev_pad_config *cfg, enum v4l2_subdev_format_whence which) { if (which == V4L2_SUBDEV_FORMAT_TRY) - return v4l2_subdev_get_try_crop(fh, PREV_PAD_SINK); + return v4l2_subdev_get_try_crop(&prev->subdev, cfg, PREV_PAD_SINK); else return &prev->crop; } @@ -1727,7 +1727,7 @@ static const unsigned int preview_output_fmts[] = { /* * preview_try_format - Validate a format * @prev: ISP preview engine - * @fh: V4L2 subdev file handle + * @cfg: V4L2 subdev pad configuration * @pad: pad number * @fmt: format to be validated * @which: try/active format selector @@ -1736,7 +1736,7 @@ static const unsigned int preview_output_fmts[] = { * engine limits and the format and crop rectangles on other pads. */ static void preview_try_format(struct isp_prev_device *prev, - struct v4l2_subdev_fh *fh, unsigned int pad, + struct v4l2_subdev_pad_config *cfg, unsigned int pad, struct v4l2_mbus_framefmt *fmt, enum v4l2_subdev_format_whence which) { @@ -1777,7 +1777,7 @@ static void preview_try_format(struct isp_prev_device *prev, case PREV_PAD_SOURCE: pixelcode = fmt->code; - *fmt = *__preview_get_format(prev, fh, PREV_PAD_SINK, which); + *fmt = *__preview_get_format(prev, cfg, PREV_PAD_SINK, which); switch (pixelcode) { case MEDIA_BUS_FMT_YUYV8_1X16: @@ -1795,7 +1795,7 @@ static void preview_try_format(struct isp_prev_device *prev, * is not supported yet, hardcode the output size to the crop * rectangle size. */ - crop = __preview_get_crop(prev, fh, which); + crop = __preview_get_crop(prev, cfg, which); fmt->width = crop->width; fmt->height = crop->height; @@ -1864,12 +1864,12 @@ static void preview_try_crop(struct isp_prev_device *prev, /* * preview_enum_mbus_code - Handle pixel format enumeration * @sd : pointer to v4l2 subdev structure - * @fh : V4L2 subdev file handle + * @cfg: V4L2 subdev pad configuration * @code : pointer to v4l2_subdev_mbus_code_enum structure * return -EINVAL or zero on success */ static int preview_enum_mbus_code(struct v4l2_subdev *sd, - struct v4l2_subdev_fh *fh, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_mbus_code_enum *code) { switch (code->pad) { @@ -1893,7 +1893,7 @@ static int preview_enum_mbus_code(struct v4l2_subdev *sd, } static int preview_enum_frame_size(struct v4l2_subdev *sd, - struct v4l2_subdev_fh *fh, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_frame_size_enum *fse) { struct isp_prev_device *prev = v4l2_get_subdevdata(sd); @@ -1905,7 +1905,7 @@ static int preview_enum_frame_size(struct v4l2_subdev *sd, format.code = fse->code; format.width = 1; format.height = 1; - preview_try_format(prev, fh, fse->pad, &format, V4L2_SUBDEV_FORMAT_TRY); + preview_try_format(prev, cfg, fse->pad, &format, V4L2_SUBDEV_FORMAT_TRY); fse->min_width = format.width; fse->min_height = format.height; @@ -1915,7 +1915,7 @@ static int preview_enum_frame_size(struct v4l2_subdev *sd, format.code = fse->code; format.width = -1; format.height = -1; - preview_try_format(prev, fh, fse->pad, &format, V4L2_SUBDEV_FORMAT_TRY); + preview_try_format(prev, cfg, fse->pad, &format, V4L2_SUBDEV_FORMAT_TRY); fse->max_width = format.width; fse->max_height = format.height; @@ -1925,7 +1925,7 @@ static int preview_enum_frame_size(struct v4l2_subdev *sd, /* * preview_get_selection - Retrieve a selection rectangle on a pad * @sd: ISP preview V4L2 subdevice - * @fh: V4L2 subdev file handle + * @cfg: V4L2 subdev pad configuration * @sel: Selection rectangle * * The only supported rectangles are the crop rectangles on the sink pad. @@ -1933,7 +1933,7 @@ static int preview_enum_frame_size(struct v4l2_subdev *sd, * Return 0 on success or a negative error code otherwise. */ static int preview_get_selection(struct v4l2_subdev *sd, - struct v4l2_subdev_fh *fh, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_selection *sel) { struct isp_prev_device *prev = v4l2_get_subdevdata(sd); @@ -1949,13 +1949,13 @@ static int preview_get_selection(struct v4l2_subdev *sd, sel->r.width = INT_MAX; sel->r.height = INT_MAX; - format = __preview_get_format(prev, fh, PREV_PAD_SINK, + format = __preview_get_format(prev, cfg, PREV_PAD_SINK, sel->which); preview_try_crop(prev, format, &sel->r); break; case V4L2_SEL_TGT_CROP: - sel->r = *__preview_get_crop(prev, fh, sel->which); + sel->r = *__preview_get_crop(prev, cfg, sel->which); break; default: @@ -1968,7 +1968,7 @@ static int preview_get_selection(struct v4l2_subdev *sd, /* * preview_set_selection - Set a selection rectangle on a pad * @sd: ISP preview V4L2 subdevice - * @fh: V4L2 subdev file handle + * @cfg: V4L2 subdev pad configuration * @sel: Selection rectangle * * The only supported rectangle is the actual crop rectangle on the sink pad. @@ -1976,7 +1976,7 @@ static int preview_get_selection(struct v4l2_subdev *sd, * Return 0 on success or a negative error code otherwise. */ static int preview_set_selection(struct v4l2_subdev *sd, - struct v4l2_subdev_fh *fh, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_selection *sel) { struct isp_prev_device *prev = v4l2_get_subdevdata(sd); @@ -1995,17 +1995,17 @@ static int preview_set_selection(struct v4l2_subdev *sd, * rectangle. */ if (sel->flags & V4L2_SEL_FLAG_KEEP_CONFIG) { - sel->r = *__preview_get_crop(prev, fh, sel->which); + sel->r = *__preview_get_crop(prev, cfg, sel->which); return 0; } - format = __preview_get_format(prev, fh, PREV_PAD_SINK, sel->which); + format = __preview_get_format(prev, cfg, PREV_PAD_SINK, sel->which); preview_try_crop(prev, format, &sel->r); - *__preview_get_crop(prev, fh, sel->which) = sel->r; + *__preview_get_crop(prev, cfg, sel->which) = sel->r; /* Update the source format. */ - format = __preview_get_format(prev, fh, PREV_PAD_SOURCE, sel->which); - preview_try_format(prev, fh, PREV_PAD_SOURCE, format, sel->which); + format = __preview_get_format(prev, cfg, PREV_PAD_SOURCE, sel->which); + preview_try_format(prev, cfg, PREV_PAD_SOURCE, format, sel->which); return 0; } @@ -2013,17 +2013,17 @@ static int preview_set_selection(struct v4l2_subdev *sd, /* * preview_get_format - Handle get format by pads subdev method * @sd : pointer to v4l2 subdev structure - * @fh : V4L2 subdev file handle + * @cfg: V4L2 subdev pad configuration * @fmt: pointer to v4l2 subdev format structure * return -EINVAL or zero on success */ -static int preview_get_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, +static int preview_get_format(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_format *fmt) { struct isp_prev_device *prev = v4l2_get_subdevdata(sd); struct v4l2_mbus_framefmt *format; - format = __preview_get_format(prev, fh, fmt->pad, fmt->which); + format = __preview_get_format(prev, cfg, fmt->pad, fmt->which); if (format == NULL) return -EINVAL; @@ -2034,28 +2034,28 @@ static int preview_get_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, /* * preview_set_format - Handle set format by pads subdev method * @sd : pointer to v4l2 subdev structure - * @fh : V4L2 subdev file handle + * @cfg: V4L2 subdev pad configuration * @fmt: pointer to v4l2 subdev format structure * return -EINVAL or zero on success */ -static int preview_set_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, +static int preview_set_format(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_format *fmt) { struct isp_prev_device *prev = v4l2_get_subdevdata(sd); struct v4l2_mbus_framefmt *format; struct v4l2_rect *crop; - format = __preview_get_format(prev, fh, fmt->pad, fmt->which); + format = __preview_get_format(prev, cfg, fmt->pad, fmt->which); if (format == NULL) return -EINVAL; - preview_try_format(prev, fh, fmt->pad, &fmt->format, fmt->which); + preview_try_format(prev, cfg, fmt->pad, &fmt->format, fmt->which); *format = fmt->format; /* Propagate the format from sink to source */ if (fmt->pad == PREV_PAD_SINK) { /* Reset the crop rectangle. */ - crop = __preview_get_crop(prev, fh, fmt->which); + crop = __preview_get_crop(prev, cfg, fmt->which); crop->left = 0; crop->top = 0; crop->width = fmt->format.width; @@ -2064,9 +2064,9 @@ static int preview_set_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, preview_try_crop(prev, &fmt->format, crop); /* Update the source format. */ - format = __preview_get_format(prev, fh, PREV_PAD_SOURCE, + format = __preview_get_format(prev, cfg, PREV_PAD_SOURCE, fmt->which); - preview_try_format(prev, fh, PREV_PAD_SOURCE, format, + preview_try_format(prev, cfg, PREV_PAD_SOURCE, format, fmt->which); } @@ -2093,7 +2093,7 @@ static int preview_init_formats(struct v4l2_subdev *sd, format.format.code = MEDIA_BUS_FMT_SGRBG10_1X10; format.format.width = 4096; format.format.height = 4096; - preview_set_format(sd, fh, &format); + preview_set_format(sd, fh ? fh->pad : NULL, &format); return 0; } diff --git a/drivers/media/platform/omap3isp/ispresizer.c b/drivers/media/platform/omap3isp/ispresizer.c index 2b9bc4839876..3ede27bc0a45 100644 --- a/drivers/media/platform/omap3isp/ispresizer.c +++ b/drivers/media/platform/omap3isp/ispresizer.c @@ -112,16 +112,16 @@ static const struct isprsz_coef filter_coefs = { * __resizer_get_format - helper function for getting resizer format * @res : pointer to resizer private structure * @pad : pad number - * @fh : V4L2 subdev file handle + * @cfg: V4L2 subdev pad configuration * @which : wanted subdev format * return zero */ static struct v4l2_mbus_framefmt * -__resizer_get_format(struct isp_res_device *res, struct v4l2_subdev_fh *fh, +__resizer_get_format(struct isp_res_device *res, struct v4l2_subdev_pad_config *cfg, unsigned int pad, enum v4l2_subdev_format_whence which) { if (which == V4L2_SUBDEV_FORMAT_TRY) - return v4l2_subdev_get_try_format(fh, pad); + return v4l2_subdev_get_try_format(&res->subdev, cfg, pad); else return &res->formats[pad]; } @@ -129,15 +129,15 @@ __resizer_get_format(struct isp_res_device *res, struct v4l2_subdev_fh *fh, /* * __resizer_get_crop - helper function for getting resizer crop rectangle * @res : pointer to resizer private structure - * @fh : V4L2 subdev file handle + * @cfg: V4L2 subdev pad configuration * @which : wanted subdev crop rectangle */ static struct v4l2_rect * -__resizer_get_crop(struct isp_res_device *res, struct v4l2_subdev_fh *fh, +__resizer_get_crop(struct isp_res_device *res, struct v4l2_subdev_pad_config *cfg, enum v4l2_subdev_format_whence which) { if (which == V4L2_SUBDEV_FORMAT_TRY) - return v4l2_subdev_get_try_crop(fh, RESZ_PAD_SINK); + return v4l2_subdev_get_try_crop(&res->subdev, cfg, RESZ_PAD_SINK); else return &res->crop.request; } @@ -1215,7 +1215,7 @@ static void resizer_try_crop(const struct v4l2_mbus_framefmt *sink, /* * resizer_get_selection - Retrieve a selection rectangle on a pad * @sd: ISP resizer V4L2 subdevice - * @fh: V4L2 subdev file handle + * @cfg: V4L2 subdev pad configuration * @sel: Selection rectangle * * The only supported rectangles are the crop rectangles on the sink pad. @@ -1223,7 +1223,7 @@ static void resizer_try_crop(const struct v4l2_mbus_framefmt *sink, * Return 0 on success or a negative error code otherwise. */ static int resizer_get_selection(struct v4l2_subdev *sd, - struct v4l2_subdev_fh *fh, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_selection *sel) { struct isp_res_device *res = v4l2_get_subdevdata(sd); @@ -1234,9 +1234,9 @@ static int resizer_get_selection(struct v4l2_subdev *sd, if (sel->pad != RESZ_PAD_SINK) return -EINVAL; - format_sink = __resizer_get_format(res, fh, RESZ_PAD_SINK, + format_sink = __resizer_get_format(res, cfg, RESZ_PAD_SINK, sel->which); - format_source = __resizer_get_format(res, fh, RESZ_PAD_SOURCE, + format_source = __resizer_get_format(res, cfg, RESZ_PAD_SOURCE, sel->which); switch (sel->target) { @@ -1251,7 +1251,7 @@ static int resizer_get_selection(struct v4l2_subdev *sd, break; case V4L2_SEL_TGT_CROP: - sel->r = *__resizer_get_crop(res, fh, sel->which); + sel->r = *__resizer_get_crop(res, cfg, sel->which); resizer_calc_ratios(res, &sel->r, format_source, &ratio); break; @@ -1265,7 +1265,7 @@ static int resizer_get_selection(struct v4l2_subdev *sd, /* * resizer_set_selection - Set a selection rectangle on a pad * @sd: ISP resizer V4L2 subdevice - * @fh: V4L2 subdev file handle + * @cfg: V4L2 subdev pad configuration * @sel: Selection rectangle * * The only supported rectangle is the actual crop rectangle on the sink pad. @@ -1276,7 +1276,7 @@ static int resizer_get_selection(struct v4l2_subdev *sd, * Return 0 on success or a negative error code otherwise. */ static int resizer_set_selection(struct v4l2_subdev *sd, - struct v4l2_subdev_fh *fh, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_selection *sel) { struct isp_res_device *res = v4l2_get_subdevdata(sd); @@ -1290,9 +1290,9 @@ static int resizer_set_selection(struct v4l2_subdev *sd, sel->pad != RESZ_PAD_SINK) return -EINVAL; - format_sink = __resizer_get_format(res, fh, RESZ_PAD_SINK, + format_sink = __resizer_get_format(res, cfg, RESZ_PAD_SINK, sel->which); - format_source = *__resizer_get_format(res, fh, RESZ_PAD_SOURCE, + format_source = *__resizer_get_format(res, cfg, RESZ_PAD_SOURCE, sel->which); dev_dbg(isp->dev, "%s(%s): req %ux%u -> (%d,%d)/%ux%u -> %ux%u\n", @@ -1310,7 +1310,7 @@ static int resizer_set_selection(struct v4l2_subdev *sd, * stored the mangled rectangle. */ resizer_try_crop(format_sink, &format_source, &sel->r); - *__resizer_get_crop(res, fh, sel->which) = sel->r; + *__resizer_get_crop(res, cfg, sel->which) = sel->r; resizer_calc_ratios(res, &sel->r, &format_source, &ratio); dev_dbg(isp->dev, "%s(%s): got %ux%u -> (%d,%d)/%ux%u -> %ux%u\n", @@ -1320,7 +1320,7 @@ static int resizer_set_selection(struct v4l2_subdev *sd, format_source.width, format_source.height); if (sel->which == V4L2_SUBDEV_FORMAT_TRY) { - *__resizer_get_format(res, fh, RESZ_PAD_SOURCE, sel->which) = + *__resizer_get_format(res, cfg, RESZ_PAD_SOURCE, sel->which) = format_source; return 0; } @@ -1331,7 +1331,7 @@ static int resizer_set_selection(struct v4l2_subdev *sd, */ spin_lock_irqsave(&res->lock, flags); - *__resizer_get_format(res, fh, RESZ_PAD_SOURCE, sel->which) = + *__resizer_get_format(res, cfg, RESZ_PAD_SOURCE, sel->which) = format_source; res->ratio = ratio; @@ -1368,13 +1368,13 @@ static unsigned int resizer_max_in_width(struct isp_res_device *res) /* * resizer_try_format - Handle try format by pad subdev method * @res : ISP resizer device - * @fh : V4L2 subdev file handle + * @cfg: V4L2 subdev pad configuration * @pad : pad num * @fmt : pointer to v4l2 format structure * @which : wanted subdev format */ static void resizer_try_format(struct isp_res_device *res, - struct v4l2_subdev_fh *fh, unsigned int pad, + struct v4l2_subdev_pad_config *cfg, unsigned int pad, struct v4l2_mbus_framefmt *fmt, enum v4l2_subdev_format_whence which) { @@ -1395,10 +1395,10 @@ static void resizer_try_format(struct isp_res_device *res, break; case RESZ_PAD_SOURCE: - format = __resizer_get_format(res, fh, RESZ_PAD_SINK, which); + format = __resizer_get_format(res, cfg, RESZ_PAD_SINK, which); fmt->code = format->code; - crop = *__resizer_get_crop(res, fh, which); + crop = *__resizer_get_crop(res, cfg, which); resizer_calc_ratios(res, &crop, fmt, &ratio); break; } @@ -1410,12 +1410,12 @@ static void resizer_try_format(struct isp_res_device *res, /* * resizer_enum_mbus_code - Handle pixel format enumeration * @sd : pointer to v4l2 subdev structure - * @fh : V4L2 subdev file handle + * @cfg: V4L2 subdev pad configuration * @code : pointer to v4l2_subdev_mbus_code_enum structure * return -EINVAL or zero on success */ static int resizer_enum_mbus_code(struct v4l2_subdev *sd, - struct v4l2_subdev_fh *fh, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_mbus_code_enum *code) { struct isp_res_device *res = v4l2_get_subdevdata(sd); @@ -1430,7 +1430,7 @@ static int resizer_enum_mbus_code(struct v4l2_subdev *sd, if (code->index != 0) return -EINVAL; - format = __resizer_get_format(res, fh, RESZ_PAD_SINK, + format = __resizer_get_format(res, cfg, RESZ_PAD_SINK, V4L2_SUBDEV_FORMAT_TRY); code->code = format->code; } @@ -1439,7 +1439,7 @@ static int resizer_enum_mbus_code(struct v4l2_subdev *sd, } static int resizer_enum_frame_size(struct v4l2_subdev *sd, - struct v4l2_subdev_fh *fh, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_frame_size_enum *fse) { struct isp_res_device *res = v4l2_get_subdevdata(sd); @@ -1451,7 +1451,7 @@ static int resizer_enum_frame_size(struct v4l2_subdev *sd, format.code = fse->code; format.width = 1; format.height = 1; - resizer_try_format(res, fh, fse->pad, &format, V4L2_SUBDEV_FORMAT_TRY); + resizer_try_format(res, cfg, fse->pad, &format, V4L2_SUBDEV_FORMAT_TRY); fse->min_width = format.width; fse->min_height = format.height; @@ -1461,7 +1461,7 @@ static int resizer_enum_frame_size(struct v4l2_subdev *sd, format.code = fse->code; format.width = -1; format.height = -1; - resizer_try_format(res, fh, fse->pad, &format, V4L2_SUBDEV_FORMAT_TRY); + resizer_try_format(res, cfg, fse->pad, &format, V4L2_SUBDEV_FORMAT_TRY); fse->max_width = format.width; fse->max_height = format.height; @@ -1471,17 +1471,17 @@ static int resizer_enum_frame_size(struct v4l2_subdev *sd, /* * resizer_get_format - Handle get format by pads subdev method * @sd : pointer to v4l2 subdev structure - * @fh : V4L2 subdev file handle + * @cfg: V4L2 subdev pad configuration * @fmt : pointer to v4l2 subdev format structure * return -EINVAL or zero on success */ -static int resizer_get_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, +static int resizer_get_format(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_format *fmt) { struct isp_res_device *res = v4l2_get_subdevdata(sd); struct v4l2_mbus_framefmt *format; - format = __resizer_get_format(res, fh, fmt->pad, fmt->which); + format = __resizer_get_format(res, cfg, fmt->pad, fmt->which); if (format == NULL) return -EINVAL; @@ -1492,37 +1492,37 @@ static int resizer_get_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, /* * resizer_set_format - Handle set format by pads subdev method * @sd : pointer to v4l2 subdev structure - * @fh : V4L2 subdev file handle + * @cfg: V4L2 subdev pad configuration * @fmt : pointer to v4l2 subdev format structure * return -EINVAL or zero on success */ -static int resizer_set_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, +static int resizer_set_format(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_format *fmt) { struct isp_res_device *res = v4l2_get_subdevdata(sd); struct v4l2_mbus_framefmt *format; struct v4l2_rect *crop; - format = __resizer_get_format(res, fh, fmt->pad, fmt->which); + format = __resizer_get_format(res, cfg, fmt->pad, fmt->which); if (format == NULL) return -EINVAL; - resizer_try_format(res, fh, fmt->pad, &fmt->format, fmt->which); + resizer_try_format(res, cfg, fmt->pad, &fmt->format, fmt->which); *format = fmt->format; if (fmt->pad == RESZ_PAD_SINK) { /* reset crop rectangle */ - crop = __resizer_get_crop(res, fh, fmt->which); + crop = __resizer_get_crop(res, cfg, fmt->which); crop->left = 0; crop->top = 0; crop->width = fmt->format.width; crop->height = fmt->format.height; /* Propagate the format from sink to source */ - format = __resizer_get_format(res, fh, RESZ_PAD_SOURCE, + format = __resizer_get_format(res, cfg, RESZ_PAD_SOURCE, fmt->which); *format = fmt->format; - resizer_try_format(res, fh, RESZ_PAD_SOURCE, format, + resizer_try_format(res, cfg, RESZ_PAD_SOURCE, format, fmt->which); } @@ -1573,7 +1573,7 @@ static int resizer_init_formats(struct v4l2_subdev *sd, format.format.code = MEDIA_BUS_FMT_YUYV8_1X16; format.format.width = 4096; format.format.height = 4096; - resizer_set_format(sd, fh, &format); + resizer_set_format(sd, fh ? fh->pad : NULL, &format); return 0; } diff --git a/drivers/media/platform/s3c-camif/camif-capture.c b/drivers/media/platform/s3c-camif/camif-capture.c index 54479d60cc0d..f6a61b9ceff4 100644 --- a/drivers/media/platform/s3c-camif/camif-capture.c +++ b/drivers/media/platform/s3c-camif/camif-capture.c @@ -1219,7 +1219,7 @@ static const u32 camif_mbus_formats[] = { */ static int s3c_camif_subdev_enum_mbus_code(struct v4l2_subdev *sd, - struct v4l2_subdev_fh *fh, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_mbus_code_enum *code) { if (code->index >= ARRAY_SIZE(camif_mbus_formats)) @@ -1230,14 +1230,14 @@ static int s3c_camif_subdev_enum_mbus_code(struct v4l2_subdev *sd, } static int s3c_camif_subdev_get_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_fh *fh, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_format *fmt) { struct camif_dev *camif = v4l2_get_subdevdata(sd); struct v4l2_mbus_framefmt *mf = &fmt->format; if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - mf = v4l2_subdev_get_try_format(fh, fmt->pad); + mf = v4l2_subdev_get_try_format(sd, cfg, fmt->pad); fmt->format = *mf; return 0; } @@ -1297,7 +1297,7 @@ static void __camif_subdev_try_format(struct camif_dev *camif, } static int s3c_camif_subdev_set_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_fh *fh, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_format *fmt) { struct camif_dev *camif = v4l2_get_subdevdata(sd); @@ -1325,7 +1325,7 @@ static int s3c_camif_subdev_set_fmt(struct v4l2_subdev *sd, __camif_subdev_try_format(camif, mf, fmt->pad); if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - mf = v4l2_subdev_get_try_format(fh, fmt->pad); + mf = v4l2_subdev_get_try_format(sd, cfg, fmt->pad); *mf = fmt->format; mutex_unlock(&camif->lock); return 0; @@ -1364,7 +1364,7 @@ static int s3c_camif_subdev_set_fmt(struct v4l2_subdev *sd, } static int s3c_camif_subdev_get_selection(struct v4l2_subdev *sd, - struct v4l2_subdev_fh *fh, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_selection *sel) { struct camif_dev *camif = v4l2_get_subdevdata(sd); @@ -1377,7 +1377,7 @@ static int s3c_camif_subdev_get_selection(struct v4l2_subdev *sd, return -EINVAL; if (sel->which == V4L2_SUBDEV_FORMAT_TRY) { - sel->r = *v4l2_subdev_get_try_crop(fh, sel->pad); + sel->r = *v4l2_subdev_get_try_crop(sd, cfg, sel->pad); return 0; } @@ -1451,7 +1451,7 @@ static void __camif_try_crop(struct camif_dev *camif, struct v4l2_rect *r) } static int s3c_camif_subdev_set_selection(struct v4l2_subdev *sd, - struct v4l2_subdev_fh *fh, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_selection *sel) { struct camif_dev *camif = v4l2_get_subdevdata(sd); @@ -1465,7 +1465,7 @@ static int s3c_camif_subdev_set_selection(struct v4l2_subdev *sd, __camif_try_crop(camif, &sel->r); if (sel->which == V4L2_SUBDEV_FORMAT_TRY) { - *v4l2_subdev_get_try_crop(fh, sel->pad) = sel->r; + *v4l2_subdev_get_try_crop(sd, cfg, sel->pad) = sel->r; } else { unsigned long flags; unsigned int i; diff --git a/drivers/media/platform/vsp1/vsp1_bru.c b/drivers/media/platform/vsp1/vsp1_bru.c index 401e2b77a0b6..31ad0b634e0c 100644 --- a/drivers/media/platform/vsp1/vsp1_bru.c +++ b/drivers/media/platform/vsp1/vsp1_bru.c @@ -183,7 +183,7 @@ static int bru_s_stream(struct v4l2_subdev *subdev, int enable) */ static int bru_enum_mbus_code(struct v4l2_subdev *subdev, - struct v4l2_subdev_fh *fh, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_mbus_code_enum *code) { static const unsigned int codes[] = { @@ -201,7 +201,7 @@ static int bru_enum_mbus_code(struct v4l2_subdev *subdev, if (code->index) return -EINVAL; - format = v4l2_subdev_get_try_format(fh, BRU_PAD_SINK(0)); + format = v4l2_subdev_get_try_format(subdev, cfg, BRU_PAD_SINK(0)); code->code = format->code; } @@ -209,7 +209,7 @@ static int bru_enum_mbus_code(struct v4l2_subdev *subdev, } static int bru_enum_frame_size(struct v4l2_subdev *subdev, - struct v4l2_subdev_fh *fh, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_frame_size_enum *fse) { if (fse->index) @@ -228,12 +228,12 @@ static int bru_enum_frame_size(struct v4l2_subdev *subdev, } static struct v4l2_rect *bru_get_compose(struct vsp1_bru *bru, - struct v4l2_subdev_fh *fh, + struct v4l2_subdev_pad_config *cfg, unsigned int pad, u32 which) { switch (which) { case V4L2_SUBDEV_FORMAT_TRY: - return v4l2_subdev_get_try_crop(fh, pad); + return v4l2_subdev_get_try_crop(&bru->entity.subdev, cfg, pad); case V4L2_SUBDEV_FORMAT_ACTIVE: return &bru->inputs[pad].compose; default: @@ -241,18 +241,18 @@ static struct v4l2_rect *bru_get_compose(struct vsp1_bru *bru, } } -static int bru_get_format(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh, +static int bru_get_format(struct v4l2_subdev *subdev, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_format *fmt) { struct vsp1_bru *bru = to_bru(subdev); - fmt->format = *vsp1_entity_get_pad_format(&bru->entity, fh, fmt->pad, + fmt->format = *vsp1_entity_get_pad_format(&bru->entity, cfg, fmt->pad, fmt->which); return 0; } -static void bru_try_format(struct vsp1_bru *bru, struct v4l2_subdev_fh *fh, +static void bru_try_format(struct vsp1_bru *bru, struct v4l2_subdev_pad_config *cfg, unsigned int pad, struct v4l2_mbus_framefmt *fmt, enum v4l2_subdev_format_whence which) { @@ -268,7 +268,7 @@ static void bru_try_format(struct vsp1_bru *bru, struct v4l2_subdev_fh *fh, default: /* The BRU can't perform format conversion. */ - format = vsp1_entity_get_pad_format(&bru->entity, fh, + format = vsp1_entity_get_pad_format(&bru->entity, cfg, BRU_PAD_SINK(0), which); fmt->code = format->code; break; @@ -280,15 +280,15 @@ static void bru_try_format(struct vsp1_bru *bru, struct v4l2_subdev_fh *fh, fmt->colorspace = V4L2_COLORSPACE_SRGB; } -static int bru_set_format(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh, +static int bru_set_format(struct v4l2_subdev *subdev, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_format *fmt) { struct vsp1_bru *bru = to_bru(subdev); struct v4l2_mbus_framefmt *format; - bru_try_format(bru, fh, fmt->pad, &fmt->format, fmt->which); + bru_try_format(bru, cfg, fmt->pad, &fmt->format, fmt->which); - format = vsp1_entity_get_pad_format(&bru->entity, fh, fmt->pad, + format = vsp1_entity_get_pad_format(&bru->entity, cfg, fmt->pad, fmt->which); *format = fmt->format; @@ -296,7 +296,7 @@ static int bru_set_format(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh, if (fmt->pad != BRU_PAD_SOURCE) { struct v4l2_rect *compose; - compose = bru_get_compose(bru, fh, fmt->pad, fmt->which); + compose = bru_get_compose(bru, cfg, fmt->pad, fmt->which); compose->left = 0; compose->top = 0; compose->width = format->width; @@ -308,7 +308,7 @@ static int bru_set_format(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh, unsigned int i; for (i = 0; i <= BRU_PAD_SOURCE; ++i) { - format = vsp1_entity_get_pad_format(&bru->entity, fh, + format = vsp1_entity_get_pad_format(&bru->entity, cfg, i, fmt->which); format->code = fmt->format.code; } @@ -318,7 +318,7 @@ static int bru_set_format(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh, } static int bru_get_selection(struct v4l2_subdev *subdev, - struct v4l2_subdev_fh *fh, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_selection *sel) { struct vsp1_bru *bru = to_bru(subdev); @@ -335,7 +335,7 @@ static int bru_get_selection(struct v4l2_subdev *subdev, return 0; case V4L2_SEL_TGT_COMPOSE: - sel->r = *bru_get_compose(bru, fh, sel->pad, sel->which); + sel->r = *bru_get_compose(bru, cfg, sel->pad, sel->which); return 0; default: @@ -344,7 +344,7 @@ static int bru_get_selection(struct v4l2_subdev *subdev, } static int bru_set_selection(struct v4l2_subdev *subdev, - struct v4l2_subdev_fh *fh, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_selection *sel) { struct vsp1_bru *bru = to_bru(subdev); @@ -360,7 +360,7 @@ static int bru_set_selection(struct v4l2_subdev *subdev, /* The compose rectangle top left corner must be inside the output * frame. */ - format = vsp1_entity_get_pad_format(&bru->entity, fh, BRU_PAD_SOURCE, + format = vsp1_entity_get_pad_format(&bru->entity, cfg, BRU_PAD_SOURCE, sel->which); sel->r.left = clamp_t(unsigned int, sel->r.left, 0, format->width - 1); sel->r.top = clamp_t(unsigned int, sel->r.top, 0, format->height - 1); @@ -368,12 +368,12 @@ static int bru_set_selection(struct v4l2_subdev *subdev, /* Scaling isn't supported, the compose rectangle size must be identical * to the sink format size. */ - format = vsp1_entity_get_pad_format(&bru->entity, fh, sel->pad, + format = vsp1_entity_get_pad_format(&bru->entity, cfg, sel->pad, sel->which); sel->r.width = format->width; sel->r.height = format->height; - compose = bru_get_compose(bru, fh, sel->pad, sel->which); + compose = bru_get_compose(bru, cfg, sel->pad, sel->which); *compose = sel->r; return 0; diff --git a/drivers/media/platform/vsp1/vsp1_entity.c b/drivers/media/platform/vsp1/vsp1_entity.c index 79af71d5e270..a453bb4ddd37 100644 --- a/drivers/media/platform/vsp1/vsp1_entity.c +++ b/drivers/media/platform/vsp1/vsp1_entity.c @@ -63,12 +63,12 @@ int vsp1_entity_set_streaming(struct vsp1_entity *entity, bool streaming) struct v4l2_mbus_framefmt * vsp1_entity_get_pad_format(struct vsp1_entity *entity, - struct v4l2_subdev_fh *fh, + struct v4l2_subdev_pad_config *cfg, unsigned int pad, u32 which) { switch (which) { case V4L2_SUBDEV_FORMAT_TRY: - return v4l2_subdev_get_try_format(fh, pad); + return v4l2_subdev_get_try_format(&entity->subdev, cfg, pad); case V4L2_SUBDEV_FORMAT_ACTIVE: return &entity->formats[pad]; default: @@ -79,14 +79,14 @@ vsp1_entity_get_pad_format(struct vsp1_entity *entity, /* * vsp1_entity_init_formats - Initialize formats on all pads * @subdev: V4L2 subdevice - * @fh: V4L2 subdev file handle + * @cfg: V4L2 subdev pad configuration * - * Initialize all pad formats with default values. If fh is not NULL, try + * Initialize all pad formats with default values. If cfg is not NULL, try * formats are initialized on the file handle. Otherwise active formats are * initialized on the device. */ void vsp1_entity_init_formats(struct v4l2_subdev *subdev, - struct v4l2_subdev_fh *fh) + struct v4l2_subdev_pad_config *cfg) { struct v4l2_subdev_format format; unsigned int pad; @@ -95,17 +95,17 @@ void vsp1_entity_init_formats(struct v4l2_subdev *subdev, memset(&format, 0, sizeof(format)); format.pad = pad; - format.which = fh ? V4L2_SUBDEV_FORMAT_TRY + format.which = cfg ? V4L2_SUBDEV_FORMAT_TRY : V4L2_SUBDEV_FORMAT_ACTIVE; - v4l2_subdev_call(subdev, pad, set_fmt, fh, &format); + v4l2_subdev_call(subdev, pad, set_fmt, cfg, &format); } } static int vsp1_entity_open(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh) { - vsp1_entity_init_formats(subdev, fh); + vsp1_entity_init_formats(subdev, fh->pad); return 0; } diff --git a/drivers/media/platform/vsp1/vsp1_entity.h b/drivers/media/platform/vsp1/vsp1_entity.h index aa20aaa58208..62c768d1c6aa 100644 --- a/drivers/media/platform/vsp1/vsp1_entity.h +++ b/drivers/media/platform/vsp1/vsp1_entity.h @@ -91,10 +91,10 @@ extern const struct media_entity_operations vsp1_media_ops; struct v4l2_mbus_framefmt * vsp1_entity_get_pad_format(struct vsp1_entity *entity, - struct v4l2_subdev_fh *fh, + struct v4l2_subdev_pad_config *cfg, unsigned int pad, u32 which); void vsp1_entity_init_formats(struct v4l2_subdev *subdev, - struct v4l2_subdev_fh *fh); + struct v4l2_subdev_pad_config *cfg); bool vsp1_entity_is_streaming(struct vsp1_entity *entity); int vsp1_entity_set_streaming(struct vsp1_entity *entity, bool streaming); diff --git a/drivers/media/platform/vsp1/vsp1_hsit.c b/drivers/media/platform/vsp1/vsp1_hsit.c index 0bc0471746c9..d226b3ff5e30 100644 --- a/drivers/media/platform/vsp1/vsp1_hsit.c +++ b/drivers/media/platform/vsp1/vsp1_hsit.c @@ -55,7 +55,7 @@ static int hsit_s_stream(struct v4l2_subdev *subdev, int enable) */ static int hsit_enum_mbus_code(struct v4l2_subdev *subdev, - struct v4l2_subdev_fh *fh, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_mbus_code_enum *code) { struct vsp1_hsit *hsit = to_hsit(subdev); @@ -73,12 +73,12 @@ static int hsit_enum_mbus_code(struct v4l2_subdev *subdev, } static int hsit_enum_frame_size(struct v4l2_subdev *subdev, - struct v4l2_subdev_fh *fh, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_frame_size_enum *fse) { struct v4l2_mbus_framefmt *format; - format = v4l2_subdev_get_try_format(fh, fse->pad); + format = v4l2_subdev_get_try_format(subdev, cfg, fse->pad); if (fse->index || fse->code != format->code) return -EINVAL; @@ -102,25 +102,25 @@ static int hsit_enum_frame_size(struct v4l2_subdev *subdev, } static int hsit_get_format(struct v4l2_subdev *subdev, - struct v4l2_subdev_fh *fh, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_format *fmt) { struct vsp1_hsit *hsit = to_hsit(subdev); - fmt->format = *vsp1_entity_get_pad_format(&hsit->entity, fh, fmt->pad, + fmt->format = *vsp1_entity_get_pad_format(&hsit->entity, cfg, fmt->pad, fmt->which); return 0; } static int hsit_set_format(struct v4l2_subdev *subdev, - struct v4l2_subdev_fh *fh, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_format *fmt) { struct vsp1_hsit *hsit = to_hsit(subdev); struct v4l2_mbus_framefmt *format; - format = vsp1_entity_get_pad_format(&hsit->entity, fh, fmt->pad, + format = vsp1_entity_get_pad_format(&hsit->entity, cfg, fmt->pad, fmt->which); if (fmt->pad == HSIT_PAD_SOURCE) { @@ -143,7 +143,7 @@ static int hsit_set_format(struct v4l2_subdev *subdev, fmt->format = *format; /* Propagate the format to the source pad. */ - format = vsp1_entity_get_pad_format(&hsit->entity, fh, HSIT_PAD_SOURCE, + format = vsp1_entity_get_pad_format(&hsit->entity, cfg, HSIT_PAD_SOURCE, fmt->which); *format = fmt->format; format->code = hsit->inverse ? MEDIA_BUS_FMT_ARGB8888_1X32 diff --git a/drivers/media/platform/vsp1/vsp1_lif.c b/drivers/media/platform/vsp1/vsp1_lif.c index 17a6ca7dafe6..b91c925a9887 100644 --- a/drivers/media/platform/vsp1/vsp1_lif.c +++ b/drivers/media/platform/vsp1/vsp1_lif.c @@ -74,7 +74,7 @@ static int lif_s_stream(struct v4l2_subdev *subdev, int enable) */ static int lif_enum_mbus_code(struct v4l2_subdev *subdev, - struct v4l2_subdev_fh *fh, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_mbus_code_enum *code) { static const unsigned int codes[] = { @@ -96,7 +96,7 @@ static int lif_enum_mbus_code(struct v4l2_subdev *subdev, if (code->index) return -EINVAL; - format = v4l2_subdev_get_try_format(fh, LIF_PAD_SINK); + format = v4l2_subdev_get_try_format(subdev, cfg, LIF_PAD_SINK); code->code = format->code; } @@ -104,12 +104,12 @@ static int lif_enum_mbus_code(struct v4l2_subdev *subdev, } static int lif_enum_frame_size(struct v4l2_subdev *subdev, - struct v4l2_subdev_fh *fh, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_frame_size_enum *fse) { struct v4l2_mbus_framefmt *format; - format = v4l2_subdev_get_try_format(fh, LIF_PAD_SINK); + format = v4l2_subdev_get_try_format(subdev, cfg, LIF_PAD_SINK); if (fse->index || fse->code != format->code) return -EINVAL; @@ -129,18 +129,18 @@ static int lif_enum_frame_size(struct v4l2_subdev *subdev, return 0; } -static int lif_get_format(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh, +static int lif_get_format(struct v4l2_subdev *subdev, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_format *fmt) { struct vsp1_lif *lif = to_lif(subdev); - fmt->format = *vsp1_entity_get_pad_format(&lif->entity, fh, fmt->pad, + fmt->format = *vsp1_entity_get_pad_format(&lif->entity, cfg, fmt->pad, fmt->which); return 0; } -static int lif_set_format(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh, +static int lif_set_format(struct v4l2_subdev *subdev, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_format *fmt) { struct vsp1_lif *lif = to_lif(subdev); @@ -151,7 +151,7 @@ static int lif_set_format(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh, fmt->format.code != MEDIA_BUS_FMT_AYUV8_1X32) fmt->format.code = MEDIA_BUS_FMT_AYUV8_1X32; - format = vsp1_entity_get_pad_format(&lif->entity, fh, fmt->pad, + format = vsp1_entity_get_pad_format(&lif->entity, cfg, fmt->pad, fmt->which); if (fmt->pad == LIF_PAD_SOURCE) { @@ -173,7 +173,7 @@ static int lif_set_format(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh, fmt->format = *format; /* Propagate the format to the source pad. */ - format = vsp1_entity_get_pad_format(&lif->entity, fh, LIF_PAD_SOURCE, + format = vsp1_entity_get_pad_format(&lif->entity, cfg, LIF_PAD_SOURCE, fmt->which); *format = fmt->format; diff --git a/drivers/media/platform/vsp1/vsp1_lut.c b/drivers/media/platform/vsp1/vsp1_lut.c index 6f185c3621fe..003363d9a5d8 100644 --- a/drivers/media/platform/vsp1/vsp1_lut.c +++ b/drivers/media/platform/vsp1/vsp1_lut.c @@ -82,7 +82,7 @@ static int lut_s_stream(struct v4l2_subdev *subdev, int enable) */ static int lut_enum_mbus_code(struct v4l2_subdev *subdev, - struct v4l2_subdev_fh *fh, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_mbus_code_enum *code) { static const unsigned int codes[] = { @@ -104,7 +104,7 @@ static int lut_enum_mbus_code(struct v4l2_subdev *subdev, if (code->index) return -EINVAL; - format = v4l2_subdev_get_try_format(fh, LUT_PAD_SINK); + format = v4l2_subdev_get_try_format(subdev, cfg, LUT_PAD_SINK); code->code = format->code; } @@ -112,12 +112,12 @@ static int lut_enum_mbus_code(struct v4l2_subdev *subdev, } static int lut_enum_frame_size(struct v4l2_subdev *subdev, - struct v4l2_subdev_fh *fh, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_frame_size_enum *fse) { struct v4l2_mbus_framefmt *format; - format = v4l2_subdev_get_try_format(fh, fse->pad); + format = v4l2_subdev_get_try_format(subdev, cfg, fse->pad); if (fse->index || fse->code != format->code) return -EINVAL; @@ -140,18 +140,18 @@ static int lut_enum_frame_size(struct v4l2_subdev *subdev, return 0; } -static int lut_get_format(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh, +static int lut_get_format(struct v4l2_subdev *subdev, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_format *fmt) { struct vsp1_lut *lut = to_lut(subdev); - fmt->format = *vsp1_entity_get_pad_format(&lut->entity, fh, fmt->pad, + fmt->format = *vsp1_entity_get_pad_format(&lut->entity, cfg, fmt->pad, fmt->which); return 0; } -static int lut_set_format(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh, +static int lut_set_format(struct v4l2_subdev *subdev, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_format *fmt) { struct vsp1_lut *lut = to_lut(subdev); @@ -163,7 +163,7 @@ static int lut_set_format(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh, fmt->format.code != MEDIA_BUS_FMT_AYUV8_1X32) fmt->format.code = MEDIA_BUS_FMT_AYUV8_1X32; - format = vsp1_entity_get_pad_format(&lut->entity, fh, fmt->pad, + format = vsp1_entity_get_pad_format(&lut->entity, cfg, fmt->pad, fmt->which); if (fmt->pad == LUT_PAD_SOURCE) { @@ -182,7 +182,7 @@ static int lut_set_format(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh, fmt->format = *format; /* Propagate the format to the source pad. */ - format = vsp1_entity_get_pad_format(&lut->entity, fh, LUT_PAD_SOURCE, + format = vsp1_entity_get_pad_format(&lut->entity, cfg, LUT_PAD_SOURCE, fmt->which); *format = fmt->format; diff --git a/drivers/media/platform/vsp1/vsp1_rwpf.c b/drivers/media/platform/vsp1/vsp1_rwpf.c index 1f1ba26a834a..a083d85e84b4 100644 --- a/drivers/media/platform/vsp1/vsp1_rwpf.c +++ b/drivers/media/platform/vsp1/vsp1_rwpf.c @@ -25,7 +25,7 @@ */ int vsp1_rwpf_enum_mbus_code(struct v4l2_subdev *subdev, - struct v4l2_subdev_fh *fh, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_mbus_code_enum *code) { static const unsigned int codes[] = { @@ -42,13 +42,13 @@ int vsp1_rwpf_enum_mbus_code(struct v4l2_subdev *subdev, } int vsp1_rwpf_enum_frame_size(struct v4l2_subdev *subdev, - struct v4l2_subdev_fh *fh, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_frame_size_enum *fse) { struct vsp1_rwpf *rwpf = to_rwpf(subdev); struct v4l2_mbus_framefmt *format; - format = v4l2_subdev_get_try_format(fh, fse->pad); + format = v4l2_subdev_get_try_format(subdev, cfg, fse->pad); if (fse->index || fse->code != format->code) return -EINVAL; @@ -72,11 +72,11 @@ int vsp1_rwpf_enum_frame_size(struct v4l2_subdev *subdev, } static struct v4l2_rect * -vsp1_rwpf_get_crop(struct vsp1_rwpf *rwpf, struct v4l2_subdev_fh *fh, u32 which) +vsp1_rwpf_get_crop(struct vsp1_rwpf *rwpf, struct v4l2_subdev_pad_config *cfg, u32 which) { switch (which) { case V4L2_SUBDEV_FORMAT_TRY: - return v4l2_subdev_get_try_crop(fh, RWPF_PAD_SINK); + return v4l2_subdev_get_try_crop(&rwpf->entity.subdev, cfg, RWPF_PAD_SINK); case V4L2_SUBDEV_FORMAT_ACTIVE: return &rwpf->crop; default: @@ -84,18 +84,18 @@ vsp1_rwpf_get_crop(struct vsp1_rwpf *rwpf, struct v4l2_subdev_fh *fh, u32 which) } } -int vsp1_rwpf_get_format(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh, +int vsp1_rwpf_get_format(struct v4l2_subdev *subdev, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_format *fmt) { struct vsp1_rwpf *rwpf = to_rwpf(subdev); - fmt->format = *vsp1_entity_get_pad_format(&rwpf->entity, fh, fmt->pad, + fmt->format = *vsp1_entity_get_pad_format(&rwpf->entity, cfg, fmt->pad, fmt->which); return 0; } -int vsp1_rwpf_set_format(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh, +int vsp1_rwpf_set_format(struct v4l2_subdev *subdev, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_format *fmt) { struct vsp1_rwpf *rwpf = to_rwpf(subdev); @@ -107,7 +107,7 @@ int vsp1_rwpf_set_format(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh, fmt->format.code != MEDIA_BUS_FMT_AYUV8_1X32) fmt->format.code = MEDIA_BUS_FMT_AYUV8_1X32; - format = vsp1_entity_get_pad_format(&rwpf->entity, fh, fmt->pad, + format = vsp1_entity_get_pad_format(&rwpf->entity, cfg, fmt->pad, fmt->which); if (fmt->pad == RWPF_PAD_SOURCE) { @@ -130,14 +130,14 @@ int vsp1_rwpf_set_format(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh, fmt->format = *format; /* Update the sink crop rectangle. */ - crop = vsp1_rwpf_get_crop(rwpf, fh, fmt->which); + crop = vsp1_rwpf_get_crop(rwpf, cfg, fmt->which); crop->left = 0; crop->top = 0; crop->width = fmt->format.width; crop->height = fmt->format.height; /* Propagate the format to the source pad. */ - format = vsp1_entity_get_pad_format(&rwpf->entity, fh, RWPF_PAD_SOURCE, + format = vsp1_entity_get_pad_format(&rwpf->entity, cfg, RWPF_PAD_SOURCE, fmt->which); *format = fmt->format; @@ -145,7 +145,7 @@ int vsp1_rwpf_set_format(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh, } int vsp1_rwpf_get_selection(struct v4l2_subdev *subdev, - struct v4l2_subdev_fh *fh, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_selection *sel) { struct vsp1_rwpf *rwpf = to_rwpf(subdev); @@ -157,11 +157,11 @@ int vsp1_rwpf_get_selection(struct v4l2_subdev *subdev, switch (sel->target) { case V4L2_SEL_TGT_CROP: - sel->r = *vsp1_rwpf_get_crop(rwpf, fh, sel->which); + sel->r = *vsp1_rwpf_get_crop(rwpf, cfg, sel->which); break; case V4L2_SEL_TGT_CROP_BOUNDS: - format = vsp1_entity_get_pad_format(&rwpf->entity, fh, + format = vsp1_entity_get_pad_format(&rwpf->entity, cfg, RWPF_PAD_SINK, sel->which); sel->r.left = 0; sel->r.top = 0; @@ -177,7 +177,7 @@ int vsp1_rwpf_get_selection(struct v4l2_subdev *subdev, } int vsp1_rwpf_set_selection(struct v4l2_subdev *subdev, - struct v4l2_subdev_fh *fh, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_selection *sel) { struct vsp1_rwpf *rwpf = to_rwpf(subdev); @@ -194,7 +194,7 @@ int vsp1_rwpf_set_selection(struct v4l2_subdev *subdev, /* Make sure the crop rectangle is entirely contained in the image. The * WPF top and left offsets are limited to 255. */ - format = vsp1_entity_get_pad_format(&rwpf->entity, fh, RWPF_PAD_SINK, + format = vsp1_entity_get_pad_format(&rwpf->entity, cfg, RWPF_PAD_SINK, sel->which); sel->r.left = min_t(unsigned int, sel->r.left, format->width - 2); sel->r.top = min_t(unsigned int, sel->r.top, format->height - 2); @@ -207,11 +207,11 @@ int vsp1_rwpf_set_selection(struct v4l2_subdev *subdev, sel->r.height = min_t(unsigned int, sel->r.height, format->height - sel->r.top); - crop = vsp1_rwpf_get_crop(rwpf, fh, sel->which); + crop = vsp1_rwpf_get_crop(rwpf, cfg, sel->which); *crop = sel->r; /* Propagate the format to the source pad. */ - format = vsp1_entity_get_pad_format(&rwpf->entity, fh, RWPF_PAD_SOURCE, + format = vsp1_entity_get_pad_format(&rwpf->entity, cfg, RWPF_PAD_SOURCE, sel->which); format->width = crop->width; format->height = crop->height; diff --git a/drivers/media/platform/vsp1/vsp1_rwpf.h b/drivers/media/platform/vsp1/vsp1_rwpf.h index 2cf1f13d3bf9..f452dce1a931 100644 --- a/drivers/media/platform/vsp1/vsp1_rwpf.h +++ b/drivers/media/platform/vsp1/vsp1_rwpf.h @@ -51,20 +51,20 @@ struct vsp1_rwpf *vsp1_rpf_create(struct vsp1_device *vsp1, unsigned int index); struct vsp1_rwpf *vsp1_wpf_create(struct vsp1_device *vsp1, unsigned int index); int vsp1_rwpf_enum_mbus_code(struct v4l2_subdev *subdev, - struct v4l2_subdev_fh *fh, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_mbus_code_enum *code); int vsp1_rwpf_enum_frame_size(struct v4l2_subdev *subdev, - struct v4l2_subdev_fh *fh, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_frame_size_enum *fse); -int vsp1_rwpf_get_format(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh, +int vsp1_rwpf_get_format(struct v4l2_subdev *subdev, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_format *fmt); -int vsp1_rwpf_set_format(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh, +int vsp1_rwpf_set_format(struct v4l2_subdev *subdev, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_format *fmt); int vsp1_rwpf_get_selection(struct v4l2_subdev *subdev, - struct v4l2_subdev_fh *fh, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_selection *sel); int vsp1_rwpf_set_selection(struct v4l2_subdev *subdev, - struct v4l2_subdev_fh *fh, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_selection *sel); #endif /* __VSP1_RWPF_H__ */ diff --git a/drivers/media/platform/vsp1/vsp1_sru.c b/drivers/media/platform/vsp1/vsp1_sru.c index 1129494c7cfc..c51dcee228bc 100644 --- a/drivers/media/platform/vsp1/vsp1_sru.c +++ b/drivers/media/platform/vsp1/vsp1_sru.c @@ -166,7 +166,7 @@ static int sru_s_stream(struct v4l2_subdev *subdev, int enable) */ static int sru_enum_mbus_code(struct v4l2_subdev *subdev, - struct v4l2_subdev_fh *fh, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_mbus_code_enum *code) { static const unsigned int codes[] = { @@ -187,7 +187,7 @@ static int sru_enum_mbus_code(struct v4l2_subdev *subdev, if (code->index) return -EINVAL; - format = v4l2_subdev_get_try_format(fh, SRU_PAD_SINK); + format = v4l2_subdev_get_try_format(subdev, cfg, SRU_PAD_SINK); code->code = format->code; } @@ -195,12 +195,12 @@ static int sru_enum_mbus_code(struct v4l2_subdev *subdev, } static int sru_enum_frame_size(struct v4l2_subdev *subdev, - struct v4l2_subdev_fh *fh, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_frame_size_enum *fse) { struct v4l2_mbus_framefmt *format; - format = v4l2_subdev_get_try_format(fh, SRU_PAD_SINK); + format = v4l2_subdev_get_try_format(subdev, cfg, SRU_PAD_SINK); if (fse->index || fse->code != format->code) return -EINVAL; @@ -226,18 +226,18 @@ static int sru_enum_frame_size(struct v4l2_subdev *subdev, return 0; } -static int sru_get_format(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh, +static int sru_get_format(struct v4l2_subdev *subdev, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_format *fmt) { struct vsp1_sru *sru = to_sru(subdev); - fmt->format = *vsp1_entity_get_pad_format(&sru->entity, fh, fmt->pad, + fmt->format = *vsp1_entity_get_pad_format(&sru->entity, cfg, fmt->pad, fmt->which); return 0; } -static void sru_try_format(struct vsp1_sru *sru, struct v4l2_subdev_fh *fh, +static void sru_try_format(struct vsp1_sru *sru, struct v4l2_subdev_pad_config *cfg, unsigned int pad, struct v4l2_mbus_framefmt *fmt, enum v4l2_subdev_format_whence which) { @@ -258,7 +258,7 @@ static void sru_try_format(struct vsp1_sru *sru, struct v4l2_subdev_fh *fh, case SRU_PAD_SOURCE: /* The SRU can't perform format conversion. */ - format = vsp1_entity_get_pad_format(&sru->entity, fh, + format = vsp1_entity_get_pad_format(&sru->entity, cfg, SRU_PAD_SINK, which); fmt->code = format->code; @@ -288,25 +288,25 @@ static void sru_try_format(struct vsp1_sru *sru, struct v4l2_subdev_fh *fh, fmt->colorspace = V4L2_COLORSPACE_SRGB; } -static int sru_set_format(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh, +static int sru_set_format(struct v4l2_subdev *subdev, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_format *fmt) { struct vsp1_sru *sru = to_sru(subdev); struct v4l2_mbus_framefmt *format; - sru_try_format(sru, fh, fmt->pad, &fmt->format, fmt->which); + sru_try_format(sru, cfg, fmt->pad, &fmt->format, fmt->which); - format = vsp1_entity_get_pad_format(&sru->entity, fh, fmt->pad, + format = vsp1_entity_get_pad_format(&sru->entity, cfg, fmt->pad, fmt->which); *format = fmt->format; if (fmt->pad == SRU_PAD_SINK) { /* Propagate the format to the source pad. */ - format = vsp1_entity_get_pad_format(&sru->entity, fh, + format = vsp1_entity_get_pad_format(&sru->entity, cfg, SRU_PAD_SOURCE, fmt->which); *format = fmt->format; - sru_try_format(sru, fh, SRU_PAD_SOURCE, format, fmt->which); + sru_try_format(sru, cfg, SRU_PAD_SOURCE, format, fmt->which); } return 0; diff --git a/drivers/media/platform/vsp1/vsp1_uds.c b/drivers/media/platform/vsp1/vsp1_uds.c index a4afec133800..08d916d5d1ac 100644 --- a/drivers/media/platform/vsp1/vsp1_uds.c +++ b/drivers/media/platform/vsp1/vsp1_uds.c @@ -169,7 +169,7 @@ static int uds_s_stream(struct v4l2_subdev *subdev, int enable) */ static int uds_enum_mbus_code(struct v4l2_subdev *subdev, - struct v4l2_subdev_fh *fh, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_mbus_code_enum *code) { static const unsigned int codes[] = { @@ -191,7 +191,7 @@ static int uds_enum_mbus_code(struct v4l2_subdev *subdev, if (code->index) return -EINVAL; - format = v4l2_subdev_get_try_format(fh, UDS_PAD_SINK); + format = v4l2_subdev_get_try_format(subdev, cfg, UDS_PAD_SINK); code->code = format->code; } @@ -199,12 +199,12 @@ static int uds_enum_mbus_code(struct v4l2_subdev *subdev, } static int uds_enum_frame_size(struct v4l2_subdev *subdev, - struct v4l2_subdev_fh *fh, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_frame_size_enum *fse) { struct v4l2_mbus_framefmt *format; - format = v4l2_subdev_get_try_format(fh, UDS_PAD_SINK); + format = v4l2_subdev_get_try_format(subdev, cfg, UDS_PAD_SINK); if (fse->index || fse->code != format->code) return -EINVAL; @@ -224,18 +224,18 @@ static int uds_enum_frame_size(struct v4l2_subdev *subdev, return 0; } -static int uds_get_format(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh, +static int uds_get_format(struct v4l2_subdev *subdev, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_format *fmt) { struct vsp1_uds *uds = to_uds(subdev); - fmt->format = *vsp1_entity_get_pad_format(&uds->entity, fh, fmt->pad, + fmt->format = *vsp1_entity_get_pad_format(&uds->entity, cfg, fmt->pad, fmt->which); return 0; } -static void uds_try_format(struct vsp1_uds *uds, struct v4l2_subdev_fh *fh, +static void uds_try_format(struct vsp1_uds *uds, struct v4l2_subdev_pad_config *cfg, unsigned int pad, struct v4l2_mbus_framefmt *fmt, enum v4l2_subdev_format_whence which) { @@ -256,7 +256,7 @@ static void uds_try_format(struct vsp1_uds *uds, struct v4l2_subdev_fh *fh, case UDS_PAD_SOURCE: /* The UDS scales but can't perform format conversion. */ - format = vsp1_entity_get_pad_format(&uds->entity, fh, + format = vsp1_entity_get_pad_format(&uds->entity, cfg, UDS_PAD_SINK, which); fmt->code = format->code; @@ -271,25 +271,25 @@ static void uds_try_format(struct vsp1_uds *uds, struct v4l2_subdev_fh *fh, fmt->colorspace = V4L2_COLORSPACE_SRGB; } -static int uds_set_format(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh, +static int uds_set_format(struct v4l2_subdev *subdev, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_format *fmt) { struct vsp1_uds *uds = to_uds(subdev); struct v4l2_mbus_framefmt *format; - uds_try_format(uds, fh, fmt->pad, &fmt->format, fmt->which); + uds_try_format(uds, cfg, fmt->pad, &fmt->format, fmt->which); - format = vsp1_entity_get_pad_format(&uds->entity, fh, fmt->pad, + format = vsp1_entity_get_pad_format(&uds->entity, cfg, fmt->pad, fmt->which); *format = fmt->format; if (fmt->pad == UDS_PAD_SINK) { /* Propagate the format to the source pad. */ - format = vsp1_entity_get_pad_format(&uds->entity, fh, + format = vsp1_entity_get_pad_format(&uds->entity, cfg, UDS_PAD_SOURCE, fmt->which); *format = fmt->format; - uds_try_format(uds, fh, UDS_PAD_SOURCE, format, fmt->which); + uds_try_format(uds, cfg, UDS_PAD_SOURCE, format, fmt->which); } return 0; diff --git a/drivers/media/v4l2-core/v4l2-subdev.c b/drivers/media/v4l2-core/v4l2-subdev.c index 19a034e79be4..3c8b198a0622 100644 --- a/drivers/media/v4l2-core/v4l2-subdev.c +++ b/drivers/media/v4l2-core/v4l2-subdev.c @@ -262,7 +262,7 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg) if (rval) return rval; - return v4l2_subdev_call(sd, pad, get_fmt, subdev_fh, format); + return v4l2_subdev_call(sd, pad, get_fmt, subdev_fh->pad, format); } case VIDIOC_SUBDEV_S_FMT: { @@ -272,7 +272,7 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg) if (rval) return rval; - return v4l2_subdev_call(sd, pad, set_fmt, subdev_fh, format); + return v4l2_subdev_call(sd, pad, set_fmt, subdev_fh->pad, format); } case VIDIOC_SUBDEV_G_CROP: { @@ -289,7 +289,7 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg) sel.target = V4L2_SEL_TGT_CROP; rval = v4l2_subdev_call( - sd, pad, get_selection, subdev_fh, &sel); + sd, pad, get_selection, subdev_fh->pad, &sel); crop->rect = sel.r; @@ -311,7 +311,7 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg) sel.r = crop->rect; rval = v4l2_subdev_call( - sd, pad, set_selection, subdev_fh, &sel); + sd, pad, set_selection, subdev_fh->pad, &sel); crop->rect = sel.r; @@ -324,7 +324,7 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg) if (code->pad >= sd->entity.num_pads) return -EINVAL; - return v4l2_subdev_call(sd, pad, enum_mbus_code, subdev_fh, + return v4l2_subdev_call(sd, pad, enum_mbus_code, subdev_fh->pad, code); } @@ -334,7 +334,7 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg) if (fse->pad >= sd->entity.num_pads) return -EINVAL; - return v4l2_subdev_call(sd, pad, enum_frame_size, subdev_fh, + return v4l2_subdev_call(sd, pad, enum_frame_size, subdev_fh->pad, fse); } @@ -362,7 +362,7 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg) if (fie->pad >= sd->entity.num_pads) return -EINVAL; - return v4l2_subdev_call(sd, pad, enum_frame_interval, subdev_fh, + return v4l2_subdev_call(sd, pad, enum_frame_interval, subdev_fh->pad, fie); } @@ -374,7 +374,7 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg) return rval; return v4l2_subdev_call( - sd, pad, get_selection, subdev_fh, sel); + sd, pad, get_selection, subdev_fh->pad, sel); } case VIDIOC_SUBDEV_S_SELECTION: { @@ -385,7 +385,7 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg) return rval; return v4l2_subdev_call( - sd, pad, set_selection, subdev_fh, sel); + sd, pad, set_selection, subdev_fh->pad, sel); } case VIDIOC_G_EDID: { diff --git a/drivers/staging/media/davinci_vpfe/dm365_ipipe.c b/drivers/staging/media/davinci_vpfe/dm365_ipipe.c index a425f71dfb97..715f1e6b8fe2 100644 --- a/drivers/staging/media/davinci_vpfe/dm365_ipipe.c +++ b/drivers/staging/media/davinci_vpfe/dm365_ipipe.c @@ -1414,17 +1414,17 @@ static int ipipe_set_stream(struct v4l2_subdev *sd, int enable) * __ipipe_get_format() - helper function for getting ipipe format * @ipipe: pointer to ipipe private structure. * @pad: pad number. - * @fh: V4L2 subdev file handle. + * @cfg: V4L2 subdev pad config * @which: wanted subdev format. * */ static struct v4l2_mbus_framefmt * __ipipe_get_format(struct vpfe_ipipe_device *ipipe, - struct v4l2_subdev_fh *fh, unsigned int pad, + struct v4l2_subdev_pad_config *cfg, unsigned int pad, enum v4l2_subdev_format_whence which) { if (which == V4L2_SUBDEV_FORMAT_TRY) - return v4l2_subdev_get_try_format(fh, pad); + return v4l2_subdev_get_try_format(&ipipe->subdev, cfg, pad); return &ipipe->formats[pad]; } @@ -1432,14 +1432,14 @@ __ipipe_get_format(struct vpfe_ipipe_device *ipipe, /* * ipipe_try_format() - Handle try format by pad subdev method * @ipipe: VPFE ipipe device. - * @fh: V4L2 subdev file handle. + * @cfg: V4L2 subdev pad config * @pad: pad num. * @fmt: pointer to v4l2 format structure. * @which : wanted subdev format */ static void ipipe_try_format(struct vpfe_ipipe_device *ipipe, - struct v4l2_subdev_fh *fh, unsigned int pad, + struct v4l2_subdev_pad_config *cfg, unsigned int pad, struct v4l2_mbus_framefmt *fmt, enum v4l2_subdev_format_whence which) { @@ -1475,22 +1475,22 @@ ipipe_try_format(struct vpfe_ipipe_device *ipipe, /* * ipipe_set_format() - Handle set format by pads subdev method * @sd: pointer to v4l2 subdev structure - * @fh: V4L2 subdev file handle + * @cfg: V4L2 subdev pad config * @fmt: pointer to v4l2 subdev format structure * return -EINVAL or zero on success */ static int -ipipe_set_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, +ipipe_set_format(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_format *fmt) { struct vpfe_ipipe_device *ipipe = v4l2_get_subdevdata(sd); struct v4l2_mbus_framefmt *format; - format = __ipipe_get_format(ipipe, fh, fmt->pad, fmt->which); + format = __ipipe_get_format(ipipe, cfg, fmt->pad, fmt->which); if (format == NULL) return -EINVAL; - ipipe_try_format(ipipe, fh, fmt->pad, &fmt->format, fmt->which); + ipipe_try_format(ipipe, cfg, fmt->pad, &fmt->format, fmt->which); *format = fmt->format; if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) @@ -1512,11 +1512,11 @@ ipipe_set_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, /* * ipipe_get_format() - Handle get format by pads subdev method. * @sd: pointer to v4l2 subdev structure. - * @fh: V4L2 subdev file handle. + * @cfg: V4L2 subdev pad config * @fmt: pointer to v4l2 subdev format structure. */ static int -ipipe_get_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, +ipipe_get_format(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_format *fmt) { struct vpfe_ipipe_device *ipipe = v4l2_get_subdevdata(sd); @@ -1524,7 +1524,7 @@ ipipe_get_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) fmt->format = ipipe->formats[fmt->pad]; else - fmt->format = *(v4l2_subdev_get_try_format(fh, fmt->pad)); + fmt->format = *(v4l2_subdev_get_try_format(sd, cfg, fmt->pad)); return 0; } @@ -1532,11 +1532,11 @@ ipipe_get_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, /* * ipipe_enum_frame_size() - enum frame sizes on pads * @sd: pointer to v4l2 subdev structure. - * @fh: V4L2 subdev file handle. + * @cfg: V4L2 subdev pad config * @fse: pointer to v4l2_subdev_frame_size_enum structure. */ static int -ipipe_enum_frame_size(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, +ipipe_enum_frame_size(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_frame_size_enum *fse) { struct vpfe_ipipe_device *ipipe = v4l2_get_subdevdata(sd); @@ -1548,7 +1548,7 @@ ipipe_enum_frame_size(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, format.code = fse->code; format.width = 1; format.height = 1; - ipipe_try_format(ipipe, fh, fse->pad, &format, + ipipe_try_format(ipipe, cfg, fse->pad, &format, V4L2_SUBDEV_FORMAT_TRY); fse->min_width = format.width; fse->min_height = format.height; @@ -1559,7 +1559,7 @@ ipipe_enum_frame_size(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, format.code = fse->code; format.width = -1; format.height = -1; - ipipe_try_format(ipipe, fh, fse->pad, &format, + ipipe_try_format(ipipe, cfg, fse->pad, &format, V4L2_SUBDEV_FORMAT_TRY); fse->max_width = format.width; fse->max_height = format.height; @@ -1570,11 +1570,11 @@ ipipe_enum_frame_size(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, /* * ipipe_enum_mbus_code() - enum mbus codes for pads * @sd: pointer to v4l2 subdev structure. - * @fh: V4L2 subdev file handle + * @cfg: V4L2 subdev pad config * @code: pointer to v4l2_subdev_mbus_code_enum structure */ static int -ipipe_enum_mbus_code(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, +ipipe_enum_mbus_code(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_mbus_code_enum *code) { switch (code->pad) { @@ -1630,9 +1630,8 @@ static int ipipe_s_ctrl(struct v4l2_ctrl *ctrl) * @sd: pointer to v4l2 subdev structure. * @fh: V4L2 subdev file handle * - * Initialize all pad formats with default values. If fh is not NULL, try - * formats are initialized on the file handle. Otherwise active formats are - * initialized on the device. + * Initialize all pad formats with default values. Try formats are initialized + * on the file handle. */ static int ipipe_init_formats(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) @@ -1641,19 +1640,19 @@ ipipe_init_formats(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) memset(&format, 0, sizeof(format)); format.pad = IPIPE_PAD_SINK; - format.which = fh ? V4L2_SUBDEV_FORMAT_TRY : V4L2_SUBDEV_FORMAT_ACTIVE; + format.which = V4L2_SUBDEV_FORMAT_TRY; format.format.code = MEDIA_BUS_FMT_SGRBG12_1X12; format.format.width = IPIPE_MAX_OUTPUT_WIDTH_A; format.format.height = IPIPE_MAX_OUTPUT_HEIGHT_A; - ipipe_set_format(sd, fh, &format); + ipipe_set_format(sd, fh->pad, &format); memset(&format, 0, sizeof(format)); format.pad = IPIPE_PAD_SOURCE; - format.which = fh ? V4L2_SUBDEV_FORMAT_TRY : V4L2_SUBDEV_FORMAT_ACTIVE; + format.which = V4L2_SUBDEV_FORMAT_TRY; format.format.code = MEDIA_BUS_FMT_UYVY8_2X8; format.format.width = IPIPE_MAX_OUTPUT_WIDTH_A; format.format.height = IPIPE_MAX_OUTPUT_HEIGHT_A; - ipipe_set_format(sd, fh, &format); + ipipe_set_format(sd, fh->pad, &format); return 0; } diff --git a/drivers/staging/media/davinci_vpfe/dm365_ipipeif.c b/drivers/staging/media/davinci_vpfe/dm365_ipipeif.c index 87d42e18377d..68a9bb04922a 100644 --- a/drivers/staging/media/davinci_vpfe/dm365_ipipeif.c +++ b/drivers/staging/media/davinci_vpfe/dm365_ipipeif.c @@ -544,12 +544,12 @@ static int ipipeif_set_stream(struct v4l2_subdev *sd, int enable) /* * ipipeif_enum_mbus_code() - Handle pixel format enumeration * @sd: pointer to v4l2 subdev structure - * @fh: V4L2 subdev file handle + * @cfg: V4L2 subdev pad config * @code: pointer to v4l2_subdev_mbus_code_enum structure * return -EINVAL or zero on success */ static int ipipeif_enum_mbus_code(struct v4l2_subdev *sd, - struct v4l2_subdev_fh *fh, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_mbus_code_enum *code) { switch (code->pad) { @@ -577,11 +577,11 @@ static int ipipeif_enum_mbus_code(struct v4l2_subdev *sd, /* * ipipeif_get_format() - Handle get format by pads subdev method * @sd: pointer to v4l2 subdev structure - * @fh: V4L2 subdev file handle + * @cfg: V4L2 subdev pad config * @fmt: pointer to v4l2 subdev format structure */ static int -ipipeif_get_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, +ipipeif_get_format(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_format *fmt) { struct vpfe_ipipeif_device *ipipeif = v4l2_get_subdevdata(sd); @@ -589,7 +589,7 @@ ipipeif_get_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) fmt->format = ipipeif->formats[fmt->pad]; else - fmt->format = *(v4l2_subdev_get_try_format(fh, fmt->pad)); + fmt->format = *(v4l2_subdev_get_try_format(sd, cfg, fmt->pad)); return 0; } @@ -600,14 +600,14 @@ ipipeif_get_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, /* * ipipeif_try_format() - Handle try format by pad subdev method * @ipipeif: VPFE ipipeif device. - * @fh: V4L2 subdev file handle. + * @cfg: V4L2 subdev pad config * @pad: pad num. * @fmt: pointer to v4l2 format structure. * @which : wanted subdev format */ static void ipipeif_try_format(struct vpfe_ipipeif_device *ipipeif, - struct v4l2_subdev_fh *fh, unsigned int pad, + struct v4l2_subdev_pad_config *cfg, unsigned int pad, struct v4l2_mbus_framefmt *fmt, enum v4l2_subdev_format_whence which) { @@ -641,7 +641,7 @@ ipipeif_try_format(struct vpfe_ipipeif_device *ipipeif, } static int -ipipeif_enum_frame_size(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, +ipipeif_enum_frame_size(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_frame_size_enum *fse) { struct vpfe_ipipeif_device *ipipeif = v4l2_get_subdevdata(sd); @@ -653,7 +653,7 @@ ipipeif_enum_frame_size(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, format.code = fse->code; format.width = 1; format.height = 1; - ipipeif_try_format(ipipeif, fh, fse->pad, &format, + ipipeif_try_format(ipipeif, cfg, fse->pad, &format, V4L2_SUBDEV_FORMAT_TRY); fse->min_width = format.width; fse->min_height = format.height; @@ -664,7 +664,7 @@ ipipeif_enum_frame_size(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, format.code = fse->code; format.width = -1; format.height = -1; - ipipeif_try_format(ipipeif, fh, fse->pad, &format, + ipipeif_try_format(ipipeif, cfg, fse->pad, &format, V4L2_SUBDEV_FORMAT_TRY); fse->max_width = format.width; fse->max_height = format.height; @@ -675,18 +675,18 @@ ipipeif_enum_frame_size(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, /* * __ipipeif_get_format() - helper function for getting ipipeif format * @ipipeif: pointer to ipipeif private structure. + * @cfg: V4L2 subdev pad config * @pad: pad number. - * @fh: V4L2 subdev file handle. * @which: wanted subdev format. * */ static struct v4l2_mbus_framefmt * __ipipeif_get_format(struct vpfe_ipipeif_device *ipipeif, - struct v4l2_subdev_fh *fh, unsigned int pad, + struct v4l2_subdev_pad_config *cfg, unsigned int pad, enum v4l2_subdev_format_whence which) { if (which == V4L2_SUBDEV_FORMAT_TRY) - return v4l2_subdev_get_try_format(fh, pad); + return v4l2_subdev_get_try_format(&ipipeif->subdev, cfg, pad); return &ipipeif->formats[pad]; } @@ -694,22 +694,22 @@ __ipipeif_get_format(struct vpfe_ipipeif_device *ipipeif, /* * ipipeif_set_format() - Handle set format by pads subdev method * @sd: pointer to v4l2 subdev structure - * @fh: V4L2 subdev file handle + * @cfg: V4L2 subdev pad config * @fmt: pointer to v4l2 subdev format structure * return -EINVAL or zero on success */ static int -ipipeif_set_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, +ipipeif_set_format(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_format *fmt) { struct vpfe_ipipeif_device *ipipeif = v4l2_get_subdevdata(sd); struct v4l2_mbus_framefmt *format; - format = __ipipeif_get_format(ipipeif, fh, fmt->pad, fmt->which); + format = __ipipeif_get_format(ipipeif, cfg, fmt->pad, fmt->which); if (format == NULL) return -EINVAL; - ipipeif_try_format(ipipeif, fh, fmt->pad, &fmt->format, fmt->which); + ipipeif_try_format(ipipeif, cfg, fmt->pad, &fmt->format, fmt->which); *format = fmt->format; if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) @@ -757,9 +757,8 @@ static void ipipeif_set_default_config(struct vpfe_ipipeif_device *ipipeif) * @sd: VPFE ipipeif V4L2 subdevice * @fh: V4L2 subdev file handle * - * Initialize all pad formats with default values. If fh is not NULL, try - * formats are initialized on the file handle. Otherwise active formats are - * initialized on the device. + * Initialize all pad formats with default values. Try formats are initialized + * on the file handle. */ static int ipipeif_init_formats(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) @@ -769,19 +768,19 @@ ipipeif_init_formats(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) memset(&format, 0, sizeof(format)); format.pad = IPIPEIF_PAD_SINK; - format.which = fh ? V4L2_SUBDEV_FORMAT_TRY : V4L2_SUBDEV_FORMAT_ACTIVE; + format.which = V4L2_SUBDEV_FORMAT_TRY; format.format.code = MEDIA_BUS_FMT_SGRBG12_1X12; format.format.width = IPIPE_MAX_OUTPUT_WIDTH_A; format.format.height = IPIPE_MAX_OUTPUT_HEIGHT_A; - ipipeif_set_format(sd, fh, &format); + ipipeif_set_format(sd, fh->pad, &format); memset(&format, 0, sizeof(format)); format.pad = IPIPEIF_PAD_SOURCE; - format.which = fh ? V4L2_SUBDEV_FORMAT_TRY : V4L2_SUBDEV_FORMAT_ACTIVE; + format.which = V4L2_SUBDEV_FORMAT_TRY; format.format.code = MEDIA_BUS_FMT_UYVY8_2X8; format.format.width = IPIPE_MAX_OUTPUT_WIDTH_A; format.format.height = IPIPE_MAX_OUTPUT_HEIGHT_A; - ipipeif_set_format(sd, fh, &format); + ipipeif_set_format(sd, fh->pad, &format); ipipeif_set_default_config(ipipeif); diff --git a/drivers/staging/media/davinci_vpfe/dm365_isif.c b/drivers/staging/media/davinci_vpfe/dm365_isif.c index bcf762bc233d..02b6bdc0d771 100644 --- a/drivers/staging/media/davinci_vpfe/dm365_isif.c +++ b/drivers/staging/media/davinci_vpfe/dm365_isif.c @@ -278,11 +278,11 @@ isif_config_format(struct vpfe_device *vpfe_dev, unsigned int pad) /* * isif_try_format() - Try video format on a pad * @isif: VPFE isif device - * @fh: V4L2 subdev file handle + * @cfg: V4L2 subdev pad config * @fmt: pointer to v4l2 subdev format structure */ static void -isif_try_format(struct vpfe_isif_device *isif, struct v4l2_subdev_fh *fh, +isif_try_format(struct vpfe_isif_device *isif, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_format *fmt) { unsigned int width = fmt->format.width; @@ -1394,11 +1394,11 @@ static int isif_set_stream(struct v4l2_subdev *sd, int enable) * __isif_get_format() - helper function for getting isif format * @isif: pointer to isif private structure. * @pad: pad number. - * @fh: V4L2 subdev file handle. + * @cfg: V4L2 subdev pad config * @which: wanted subdev format. */ static struct v4l2_mbus_framefmt * -__isif_get_format(struct vpfe_isif_device *isif, struct v4l2_subdev_fh *fh, +__isif_get_format(struct vpfe_isif_device *isif, struct v4l2_subdev_pad_config *cfg, unsigned int pad, enum v4l2_subdev_format_whence which) { if (which == V4L2_SUBDEV_FORMAT_TRY) { @@ -1407,32 +1407,32 @@ __isif_get_format(struct vpfe_isif_device *isif, struct v4l2_subdev_fh *fh, fmt.pad = pad; fmt.which = which; - return v4l2_subdev_get_try_format(fh, pad); + return v4l2_subdev_get_try_format(&isif->subdev, cfg, pad); } return &isif->formats[pad]; } /* -* isif_set_format() - set format on pad -* @sd : VPFE ISIF device -* @fh : V4L2 subdev file handle -* @fmt : pointer to v4l2 subdev format structure -* -* Return 0 on success or -EINVAL if format or pad is invalid -*/ + * isif_set_format() - set format on pad + * @sd : VPFE ISIF device + * @cfg : V4L2 subdev pad config + * @fmt : pointer to v4l2 subdev format structure + * + * Return 0 on success or -EINVAL if format or pad is invalid + */ static int -isif_set_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, +isif_set_format(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_format *fmt) { struct vpfe_isif_device *isif = v4l2_get_subdevdata(sd); struct vpfe_device *vpfe_dev = to_vpfe_device(isif); struct v4l2_mbus_framefmt *format; - format = __isif_get_format(isif, fh, fmt->pad, fmt->which); + format = __isif_get_format(isif, cfg, fmt->pad, fmt->which); if (format == NULL) return -EINVAL; - isif_try_format(isif, fh, fmt); + isif_try_format(isif, cfg, fmt); memcpy(format, &fmt->format, sizeof(*format)); if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) @@ -1447,20 +1447,20 @@ isif_set_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, /* * isif_get_format() - Retrieve the video format on a pad * @sd: VPFE ISIF V4L2 subdevice - * @fh: V4L2 subdev file handle + * @cfg: V4L2 subdev pad config * @fmt: pointer to v4l2 subdev format structure * * Return 0 on success or -EINVAL if the pad is invalid or doesn't correspond * to the format type. */ static int -isif_get_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, +isif_get_format(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_format *fmt) { struct vpfe_isif_device *vpfe_isif = v4l2_get_subdevdata(sd); struct v4l2_mbus_framefmt *format; - format = __isif_get_format(vpfe_isif, fh, fmt->pad, fmt->which); + format = __isif_get_format(vpfe_isif, cfg, fmt->pad, fmt->which); if (format == NULL) return -EINVAL; @@ -1472,11 +1472,11 @@ isif_get_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, /* * isif_enum_frame_size() - enum frame sizes on pads * @sd: VPFE isif V4L2 subdevice - * @fh: V4L2 subdev file handle + * @cfg: V4L2 subdev pad config * @code: pointer to v4l2_subdev_frame_size_enum structure */ static int -isif_enum_frame_size(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, +isif_enum_frame_size(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_frame_size_enum *fse) { struct vpfe_isif_device *isif = v4l2_get_subdevdata(sd); @@ -1490,7 +1490,7 @@ isif_enum_frame_size(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, format.format.width = 1; format.format.height = 1; format.which = V4L2_SUBDEV_FORMAT_TRY; - isif_try_format(isif, fh, &format); + isif_try_format(isif, cfg, &format); fse->min_width = format.format.width; fse->min_height = format.format.height; @@ -1502,7 +1502,7 @@ isif_enum_frame_size(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, format.format.width = -1; format.format.height = -1; format.which = V4L2_SUBDEV_FORMAT_TRY; - isif_try_format(isif, fh, &format); + isif_try_format(isif, cfg, &format); fse->max_width = format.format.width; fse->max_height = format.format.height; @@ -1512,11 +1512,11 @@ isif_enum_frame_size(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, /* * isif_enum_mbus_code() - enum mbus codes for pads * @sd: VPFE isif V4L2 subdevice - * @fh: V4L2 subdev file handle + * @cfg: V4L2 subdev pad config * @code: pointer to v4l2_subdev_mbus_code_enum structure */ static int -isif_enum_mbus_code(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, +isif_enum_mbus_code(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_mbus_code_enum *code) { switch (code->pad) { @@ -1537,14 +1537,14 @@ isif_enum_mbus_code(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, /* * isif_pad_set_selection() - set crop rectangle on pad * @sd: VPFE isif V4L2 subdevice - * @fh: V4L2 subdev file handle + * @cfg: V4L2 subdev pad config * @code: pointer to v4l2_subdev_mbus_code_enum structure * * Return 0 on success, -EINVAL if pad is invalid */ static int isif_pad_set_selection(struct v4l2_subdev *sd, - struct v4l2_subdev_fh *fh, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_selection *sel) { struct vpfe_isif_device *vpfe_isif = v4l2_get_subdevdata(sd); @@ -1554,7 +1554,7 @@ isif_pad_set_selection(struct v4l2_subdev *sd, if (sel->pad != ISIF_PAD_SINK || sel->target != V4L2_SEL_TGT_CROP) return -EINVAL; - format = __isif_get_format(vpfe_isif, fh, sel->pad, sel->which); + format = __isif_get_format(vpfe_isif, cfg, sel->pad, sel->which); if (format == NULL) return -EINVAL; @@ -1577,7 +1577,7 @@ isif_pad_set_selection(struct v4l2_subdev *sd, } else { struct v4l2_rect *rect; - rect = v4l2_subdev_get_try_crop(fh, ISIF_PAD_SINK); + rect = v4l2_subdev_get_try_crop(sd, cfg, ISIF_PAD_SINK); memcpy(rect, &vpfe_isif->crop, sizeof(*rect)); } return 0; @@ -1586,14 +1586,14 @@ isif_pad_set_selection(struct v4l2_subdev *sd, /* * isif_pad_get_selection() - get crop rectangle on pad * @sd: VPFE isif V4L2 subdevice - * @fh: V4L2 subdev file handle + * @cfg: V4L2 subdev pad config * @code: pointer to v4l2_subdev_mbus_code_enum structure * * Return 0 on success, -EINVAL if pad is invalid */ static int isif_pad_get_selection(struct v4l2_subdev *sd, - struct v4l2_subdev_fh *fh, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_selection *sel) { struct vpfe_isif_device *vpfe_isif = v4l2_get_subdevdata(sd); @@ -1605,7 +1605,7 @@ isif_pad_get_selection(struct v4l2_subdev *sd, if (sel->which == V4L2_SUBDEV_FORMAT_TRY) { struct v4l2_rect *rect; - rect = v4l2_subdev_get_try_crop(fh, ISIF_PAD_SINK); + rect = v4l2_subdev_get_try_crop(sd, cfg, ISIF_PAD_SINK); memcpy(&sel->r, rect, sizeof(*rect)); } else { sel->r = vpfe_isif->crop; @@ -1619,9 +1619,8 @@ isif_pad_get_selection(struct v4l2_subdev *sd, * @sd: VPFE isif V4L2 subdevice * @fh: V4L2 subdev file handle * - * Initialize all pad formats with default values. If fh is not NULL, try - * formats are initialized on the file handle. Otherwise active formats are - * initialized on the device. + * Initialize all pad formats with default values. Try formats are initialized + * on the file handle. */ static int isif_init_formats(struct v4l2_subdev *sd, @@ -1632,27 +1631,27 @@ isif_init_formats(struct v4l2_subdev *sd, memset(&format, 0, sizeof(format)); format.pad = ISIF_PAD_SINK; - format.which = fh ? V4L2_SUBDEV_FORMAT_TRY : V4L2_SUBDEV_FORMAT_ACTIVE; + format.which = V4L2_SUBDEV_FORMAT_TRY; format.format.code = MEDIA_BUS_FMT_SGRBG12_1X12; format.format.width = MAX_WIDTH; format.format.height = MAX_HEIGHT; - isif_set_format(sd, fh, &format); + isif_set_format(sd, fh->pad, &format); memset(&format, 0, sizeof(format)); format.pad = ISIF_PAD_SOURCE; - format.which = fh ? V4L2_SUBDEV_FORMAT_TRY : V4L2_SUBDEV_FORMAT_ACTIVE; + format.which = V4L2_SUBDEV_FORMAT_TRY; format.format.code = MEDIA_BUS_FMT_SGRBG12_1X12; format.format.width = MAX_WIDTH; format.format.height = MAX_HEIGHT; - isif_set_format(sd, fh, &format); + isif_set_format(sd, fh->pad, &format); memset(&sel, 0, sizeof(sel)); sel.pad = ISIF_PAD_SINK; - sel.which = fh ? V4L2_SUBDEV_FORMAT_TRY : V4L2_SUBDEV_FORMAT_ACTIVE; + sel.which = V4L2_SUBDEV_FORMAT_TRY; sel.target = V4L2_SEL_TGT_CROP; sel.r.width = MAX_WIDTH; sel.r.height = MAX_HEIGHT; - isif_pad_set_selection(sd, fh, &sel); + isif_pad_set_selection(sd, fh->pad, &sel); return 0; } diff --git a/drivers/staging/media/davinci_vpfe/dm365_resizer.c b/drivers/staging/media/davinci_vpfe/dm365_resizer.c index 75e70e14b724..acd9cb56f0f3 100644 --- a/drivers/staging/media/davinci_vpfe/dm365_resizer.c +++ b/drivers/staging/media/davinci_vpfe/dm365_resizer.c @@ -1289,19 +1289,19 @@ static int resizer_set_stream(struct v4l2_subdev *sd, int enable) /* * __resizer_get_format() - helper function for getting resizer format * @sd: pointer to subdev. - * @fh: V4L2 subdev file handle. + * @cfg: V4L2 subdev pad config * @pad: pad number. * @which: wanted subdev format. * Retun wanted mbus frame format. */ static struct v4l2_mbus_framefmt * -__resizer_get_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, +__resizer_get_format(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg, unsigned int pad, enum v4l2_subdev_format_whence which) { struct vpfe_resizer_device *resizer = v4l2_get_subdevdata(sd); if (which == V4L2_SUBDEV_FORMAT_TRY) - return v4l2_subdev_get_try_format(fh, pad); + return v4l2_subdev_get_try_format(sd, cfg, pad); if (&resizer->crop_resizer.subdev == sd) return &resizer->crop_resizer.formats[pad]; if (&resizer->resizer_a.subdev == sd) @@ -1314,13 +1314,13 @@ __resizer_get_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, /* * resizer_try_format() - Handle try format by pad subdev method * @sd: pointer to subdev. - * @fh: V4L2 subdev file handle. + * @cfg: V4L2 subdev pad config * @pad: pad num. * @fmt: pointer to v4l2 format structure. * @which: wanted subdev format. */ static void -resizer_try_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, +resizer_try_format(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg, unsigned int pad, struct v4l2_mbus_framefmt *fmt, enum v4l2_subdev_format_whence which) { @@ -1388,21 +1388,21 @@ resizer_try_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, /* * resizer_set_format() - Handle set format by pads subdev method * @sd: pointer to v4l2 subdev structure - * @fh: V4L2 subdev file handle + * @cfg: V4L2 subdev pad config * @fmt: pointer to v4l2 subdev format structure * return -EINVAL or zero on success */ -static int resizer_set_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, +static int resizer_set_format(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_format *fmt) { struct vpfe_resizer_device *resizer = v4l2_get_subdevdata(sd); struct v4l2_mbus_framefmt *format; - format = __resizer_get_format(sd, fh, fmt->pad, fmt->which); + format = __resizer_get_format(sd, cfg, fmt->pad, fmt->which); if (format == NULL) return -EINVAL; - resizer_try_format(sd, fh, fmt->pad, &fmt->format, fmt->which); + resizer_try_format(sd, cfg, fmt->pad, &fmt->format, fmt->which); *format = fmt->format; if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) @@ -1448,16 +1448,16 @@ static int resizer_set_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, /* * resizer_get_format() - Retrieve the video format on a pad * @sd: pointer to v4l2 subdev structure. - * @fh: V4L2 subdev file handle. + * @cfg: V4L2 subdev pad config * @fmt: pointer to v4l2 subdev format structure * return -EINVAL or zero on success */ -static int resizer_get_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, +static int resizer_get_format(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_format *fmt) { struct v4l2_mbus_framefmt *format; - format = __resizer_get_format(sd, fh, fmt->pad, fmt->which); + format = __resizer_get_format(sd, cfg, fmt->pad, fmt->which); if (format == NULL) return -EINVAL; @@ -1469,11 +1469,11 @@ static int resizer_get_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, /* * resizer_enum_frame_size() - enum frame sizes on pads * @sd: Pointer to subdevice. - * @fh: V4L2 subdev file handle. + * @cfg: V4L2 subdev pad config * @code: pointer to v4l2_subdev_frame_size_enum structure. */ static int resizer_enum_frame_size(struct v4l2_subdev *sd, - struct v4l2_subdev_fh *fh, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_frame_size_enum *fse) { struct v4l2_mbus_framefmt format; @@ -1484,7 +1484,7 @@ static int resizer_enum_frame_size(struct v4l2_subdev *sd, format.code = fse->code; format.width = 1; format.height = 1; - resizer_try_format(sd, fh, fse->pad, &format, + resizer_try_format(sd, cfg, fse->pad, &format, V4L2_SUBDEV_FORMAT_TRY); fse->min_width = format.width; fse->min_height = format.height; @@ -1495,7 +1495,7 @@ static int resizer_enum_frame_size(struct v4l2_subdev *sd, format.code = fse->code; format.width = -1; format.height = -1; - resizer_try_format(sd, fh, fse->pad, &format, + resizer_try_format(sd, cfg, fse->pad, &format, V4L2_SUBDEV_FORMAT_TRY); fse->max_width = format.width; fse->max_height = format.height; @@ -1506,11 +1506,11 @@ static int resizer_enum_frame_size(struct v4l2_subdev *sd, /* * resizer_enum_mbus_code() - enum mbus codes for pads * @sd: Pointer to subdevice. - * @fh: V4L2 subdev file handle + * @cfg: V4L2 subdev pad config * @code: pointer to v4l2_subdev_mbus_code_enum structure */ static int resizer_enum_mbus_code(struct v4l2_subdev *sd, - struct v4l2_subdev_fh *fh, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_mbus_code_enum *code) { if (code->pad == RESIZER_PAD_SINK) { @@ -1533,14 +1533,13 @@ static int resizer_enum_mbus_code(struct v4l2_subdev *sd, * @sd: Pointer to subdevice. * @fh: V4L2 subdev file handle. * - * Initialize all pad formats with default values. If fh is not NULL, try - * formats are initialized on the file handle. Otherwise active formats are - * initialized on the device. + * Initialize all pad formats with default values. Try formats are + * initialized on the file handle. */ static int resizer_init_formats(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) { - __u32 which = fh ? V4L2_SUBDEV_FORMAT_TRY : V4L2_SUBDEV_FORMAT_ACTIVE; + __u32 which = V4L2_SUBDEV_FORMAT_TRY; struct vpfe_resizer_device *resizer = v4l2_get_subdevdata(sd); struct v4l2_subdev_format format; @@ -1551,7 +1550,7 @@ static int resizer_init_formats(struct v4l2_subdev *sd, format.format.code = MEDIA_BUS_FMT_YUYV8_2X8; format.format.width = MAX_IN_WIDTH; format.format.height = MAX_IN_HEIGHT; - resizer_set_format(sd, fh, &format); + resizer_set_format(sd, fh->pad, &format); memset(&format, 0, sizeof(format)); format.pad = RESIZER_CROP_PAD_SOURCE; @@ -1559,7 +1558,7 @@ static int resizer_init_formats(struct v4l2_subdev *sd, format.format.code = MEDIA_BUS_FMT_UYVY8_2X8; format.format.width = MAX_IN_WIDTH; format.format.height = MAX_IN_WIDTH; - resizer_set_format(sd, fh, &format); + resizer_set_format(sd, fh->pad, &format); memset(&format, 0, sizeof(format)); format.pad = RESIZER_CROP_PAD_SOURCE2; @@ -1567,7 +1566,7 @@ static int resizer_init_formats(struct v4l2_subdev *sd, format.format.code = MEDIA_BUS_FMT_UYVY8_2X8; format.format.width = MAX_IN_WIDTH; format.format.height = MAX_IN_WIDTH; - resizer_set_format(sd, fh, &format); + resizer_set_format(sd, fh->pad, &format); } else if (&resizer->resizer_a.subdev == sd) { memset(&format, 0, sizeof(format)); format.pad = RESIZER_PAD_SINK; @@ -1575,7 +1574,7 @@ static int resizer_init_formats(struct v4l2_subdev *sd, format.format.code = MEDIA_BUS_FMT_YUYV8_2X8; format.format.width = MAX_IN_WIDTH; format.format.height = MAX_IN_HEIGHT; - resizer_set_format(sd, fh, &format); + resizer_set_format(sd, fh->pad, &format); memset(&format, 0, sizeof(format)); format.pad = RESIZER_PAD_SOURCE; @@ -1583,7 +1582,7 @@ static int resizer_init_formats(struct v4l2_subdev *sd, format.format.code = MEDIA_BUS_FMT_UYVY8_2X8; format.format.width = IPIPE_MAX_OUTPUT_WIDTH_A; format.format.height = IPIPE_MAX_OUTPUT_HEIGHT_A; - resizer_set_format(sd, fh, &format); + resizer_set_format(sd, fh->pad, &format); } else if (&resizer->resizer_b.subdev == sd) { memset(&format, 0, sizeof(format)); format.pad = RESIZER_PAD_SINK; @@ -1591,7 +1590,7 @@ static int resizer_init_formats(struct v4l2_subdev *sd, format.format.code = MEDIA_BUS_FMT_YUYV8_2X8; format.format.width = MAX_IN_WIDTH; format.format.height = MAX_IN_HEIGHT; - resizer_set_format(sd, fh, &format); + resizer_set_format(sd, fh->pad, &format); memset(&format, 0, sizeof(format)); format.pad = RESIZER_PAD_SOURCE; @@ -1599,7 +1598,7 @@ static int resizer_init_formats(struct v4l2_subdev *sd, format.format.code = MEDIA_BUS_FMT_UYVY8_2X8; format.format.width = IPIPE_MAX_OUTPUT_WIDTH_B; format.format.height = IPIPE_MAX_OUTPUT_HEIGHT_B; - resizer_set_format(sd, fh, &format); + resizer_set_format(sd, fh->pad, &format); } return 0; diff --git a/drivers/staging/media/omap4iss/iss_csi2.c b/drivers/staging/media/omap4iss/iss_csi2.c index 2d96fb3eca53..e404ad4fd987 100644 --- a/drivers/staging/media/omap4iss/iss_csi2.c +++ b/drivers/staging/media/omap4iss/iss_csi2.c @@ -828,17 +828,17 @@ static const struct iss_video_operations csi2_issvideo_ops = { */ static struct v4l2_mbus_framefmt * -__csi2_get_format(struct iss_csi2_device *csi2, struct v4l2_subdev_fh *fh, +__csi2_get_format(struct iss_csi2_device *csi2, struct v4l2_subdev_pad_config *cfg, unsigned int pad, enum v4l2_subdev_format_whence which) { if (which == V4L2_SUBDEV_FORMAT_TRY) - return v4l2_subdev_get_try_format(fh, pad); + return v4l2_subdev_get_try_format(&csi2->subdev, cfg, pad); return &csi2->formats[pad]; } static void -csi2_try_format(struct iss_csi2_device *csi2, struct v4l2_subdev_fh *fh, +csi2_try_format(struct iss_csi2_device *csi2, struct v4l2_subdev_pad_config *cfg, unsigned int pad, struct v4l2_mbus_framefmt *fmt, enum v4l2_subdev_format_whence which) { @@ -868,7 +868,7 @@ csi2_try_format(struct iss_csi2_device *csi2, struct v4l2_subdev_fh *fh, * compression. */ pixelcode = fmt->code; - format = __csi2_get_format(csi2, fh, CSI2_PAD_SINK, which); + format = __csi2_get_format(csi2, cfg, CSI2_PAD_SINK, which); memcpy(fmt, format, sizeof(*fmt)); /* @@ -889,12 +889,12 @@ csi2_try_format(struct iss_csi2_device *csi2, struct v4l2_subdev_fh *fh, /* * csi2_enum_mbus_code - Handle pixel format enumeration * @sd : pointer to v4l2 subdev structure - * @fh : V4L2 subdev file handle + * @cfg : V4L2 subdev pad config * @code : pointer to v4l2_subdev_mbus_code_enum structure * return -EINVAL or zero on success */ static int csi2_enum_mbus_code(struct v4l2_subdev *sd, - struct v4l2_subdev_fh *fh, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_mbus_code_enum *code) { struct iss_csi2_device *csi2 = v4l2_get_subdevdata(sd); @@ -907,7 +907,7 @@ static int csi2_enum_mbus_code(struct v4l2_subdev *sd, code->code = csi2_input_fmts[code->index]; } else { - format = __csi2_get_format(csi2, fh, CSI2_PAD_SINK, + format = __csi2_get_format(csi2, cfg, CSI2_PAD_SINK, V4L2_SUBDEV_FORMAT_TRY); switch (code->index) { case 0: @@ -931,7 +931,7 @@ static int csi2_enum_mbus_code(struct v4l2_subdev *sd, } static int csi2_enum_frame_size(struct v4l2_subdev *sd, - struct v4l2_subdev_fh *fh, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_frame_size_enum *fse) { struct iss_csi2_device *csi2 = v4l2_get_subdevdata(sd); @@ -943,7 +943,7 @@ static int csi2_enum_frame_size(struct v4l2_subdev *sd, format.code = fse->code; format.width = 1; format.height = 1; - csi2_try_format(csi2, fh, fse->pad, &format, V4L2_SUBDEV_FORMAT_TRY); + csi2_try_format(csi2, cfg, fse->pad, &format, V4L2_SUBDEV_FORMAT_TRY); fse->min_width = format.width; fse->min_height = format.height; @@ -953,7 +953,7 @@ static int csi2_enum_frame_size(struct v4l2_subdev *sd, format.code = fse->code; format.width = -1; format.height = -1; - csi2_try_format(csi2, fh, fse->pad, &format, V4L2_SUBDEV_FORMAT_TRY); + csi2_try_format(csi2, cfg, fse->pad, &format, V4L2_SUBDEV_FORMAT_TRY); fse->max_width = format.width; fse->max_height = format.height; @@ -963,17 +963,17 @@ static int csi2_enum_frame_size(struct v4l2_subdev *sd, /* * csi2_get_format - Handle get format by pads subdev method * @sd : pointer to v4l2 subdev structure - * @fh : V4L2 subdev file handle + * @cfg: V4L2 subdev pad config * @fmt: pointer to v4l2 subdev format structure * return -EINVAL or zero on success */ -static int csi2_get_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, +static int csi2_get_format(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_format *fmt) { struct iss_csi2_device *csi2 = v4l2_get_subdevdata(sd); struct v4l2_mbus_framefmt *format; - format = __csi2_get_format(csi2, fh, fmt->pad, fmt->which); + format = __csi2_get_format(csi2, cfg, fmt->pad, fmt->which); if (format == NULL) return -EINVAL; @@ -984,29 +984,29 @@ static int csi2_get_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, /* * csi2_set_format - Handle set format by pads subdev method * @sd : pointer to v4l2 subdev structure - * @fh : V4L2 subdev file handle + * @cfg: V4L2 subdev pad config * @fmt: pointer to v4l2 subdev format structure * return -EINVAL or zero on success */ -static int csi2_set_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, +static int csi2_set_format(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_format *fmt) { struct iss_csi2_device *csi2 = v4l2_get_subdevdata(sd); struct v4l2_mbus_framefmt *format; - format = __csi2_get_format(csi2, fh, fmt->pad, fmt->which); + format = __csi2_get_format(csi2, cfg, fmt->pad, fmt->which); if (format == NULL) return -EINVAL; - csi2_try_format(csi2, fh, fmt->pad, &fmt->format, fmt->which); + csi2_try_format(csi2, cfg, fmt->pad, &fmt->format, fmt->which); *format = fmt->format; /* Propagate the format from sink to source */ if (fmt->pad == CSI2_PAD_SINK) { - format = __csi2_get_format(csi2, fh, CSI2_PAD_SOURCE, + format = __csi2_get_format(csi2, cfg, CSI2_PAD_SOURCE, fmt->which); *format = fmt->format; - csi2_try_format(csi2, fh, CSI2_PAD_SOURCE, format, fmt->which); + csi2_try_format(csi2, cfg, CSI2_PAD_SOURCE, format, fmt->which); } return 0; @@ -1048,7 +1048,7 @@ static int csi2_init_formats(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) format.format.code = MEDIA_BUS_FMT_SGRBG10_1X10; format.format.width = 4096; format.format.height = 4096; - csi2_set_format(sd, fh, &format); + csi2_set_format(sd, fh ? fh->pad : NULL, &format); return 0; } diff --git a/drivers/staging/media/omap4iss/iss_ipipe.c b/drivers/staging/media/omap4iss/iss_ipipe.c index a1a46ef8319b..fc319822a33e 100644 --- a/drivers/staging/media/omap4iss/iss_ipipe.c +++ b/drivers/staging/media/omap4iss/iss_ipipe.c @@ -24,7 +24,7 @@ #include "iss_ipipe.h" static struct v4l2_mbus_framefmt * -__ipipe_get_format(struct iss_ipipe_device *ipipe, struct v4l2_subdev_fh *fh, +__ipipe_get_format(struct iss_ipipe_device *ipipe, struct v4l2_subdev_pad_config *cfg, unsigned int pad, enum v4l2_subdev_format_whence which); static const unsigned int ipipe_fmts[] = { @@ -176,11 +176,11 @@ static int ipipe_set_stream(struct v4l2_subdev *sd, int enable) } static struct v4l2_mbus_framefmt * -__ipipe_get_format(struct iss_ipipe_device *ipipe, struct v4l2_subdev_fh *fh, +__ipipe_get_format(struct iss_ipipe_device *ipipe, struct v4l2_subdev_pad_config *cfg, unsigned int pad, enum v4l2_subdev_format_whence which) { if (which == V4L2_SUBDEV_FORMAT_TRY) - return v4l2_subdev_get_try_format(fh, pad); + return v4l2_subdev_get_try_format(&ipipe->subdev, cfg, pad); return &ipipe->formats[pad]; } @@ -188,12 +188,12 @@ __ipipe_get_format(struct iss_ipipe_device *ipipe, struct v4l2_subdev_fh *fh, /* * ipipe_try_format - Try video format on a pad * @ipipe: ISS IPIPE device - * @fh : V4L2 subdev file handle + * @cfg: V4L2 subdev pad config * @pad: Pad number * @fmt: Format */ static void -ipipe_try_format(struct iss_ipipe_device *ipipe, struct v4l2_subdev_fh *fh, +ipipe_try_format(struct iss_ipipe_device *ipipe, struct v4l2_subdev_pad_config *cfg, unsigned int pad, struct v4l2_mbus_framefmt *fmt, enum v4l2_subdev_format_whence which) { @@ -220,7 +220,7 @@ ipipe_try_format(struct iss_ipipe_device *ipipe, struct v4l2_subdev_fh *fh, break; case IPIPE_PAD_SOURCE_VP: - format = __ipipe_get_format(ipipe, fh, IPIPE_PAD_SINK, which); + format = __ipipe_get_format(ipipe, cfg, IPIPE_PAD_SINK, which); memcpy(fmt, format, sizeof(*fmt)); fmt->code = MEDIA_BUS_FMT_UYVY8_1X16; @@ -236,12 +236,12 @@ ipipe_try_format(struct iss_ipipe_device *ipipe, struct v4l2_subdev_fh *fh, /* * ipipe_enum_mbus_code - Handle pixel format enumeration * @sd : pointer to v4l2 subdev structure - * @fh : V4L2 subdev file handle + * @cfg : V4L2 subdev pad config * @code : pointer to v4l2_subdev_mbus_code_enum structure * return -EINVAL or zero on success */ static int ipipe_enum_mbus_code(struct v4l2_subdev *sd, - struct v4l2_subdev_fh *fh, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_mbus_code_enum *code) { switch (code->pad) { @@ -268,7 +268,7 @@ static int ipipe_enum_mbus_code(struct v4l2_subdev *sd, } static int ipipe_enum_frame_size(struct v4l2_subdev *sd, - struct v4l2_subdev_fh *fh, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_frame_size_enum *fse) { struct iss_ipipe_device *ipipe = v4l2_get_subdevdata(sd); @@ -280,7 +280,7 @@ static int ipipe_enum_frame_size(struct v4l2_subdev *sd, format.code = fse->code; format.width = 1; format.height = 1; - ipipe_try_format(ipipe, fh, fse->pad, &format, V4L2_SUBDEV_FORMAT_TRY); + ipipe_try_format(ipipe, cfg, fse->pad, &format, V4L2_SUBDEV_FORMAT_TRY); fse->min_width = format.width; fse->min_height = format.height; @@ -290,7 +290,7 @@ static int ipipe_enum_frame_size(struct v4l2_subdev *sd, format.code = fse->code; format.width = -1; format.height = -1; - ipipe_try_format(ipipe, fh, fse->pad, &format, V4L2_SUBDEV_FORMAT_TRY); + ipipe_try_format(ipipe, cfg, fse->pad, &format, V4L2_SUBDEV_FORMAT_TRY); fse->max_width = format.width; fse->max_height = format.height; @@ -300,19 +300,19 @@ static int ipipe_enum_frame_size(struct v4l2_subdev *sd, /* * ipipe_get_format - Retrieve the video format on a pad * @sd : ISP IPIPE V4L2 subdevice - * @fh : V4L2 subdev file handle + * @cfg: V4L2 subdev pad config * @fmt: Format * * Return 0 on success or -EINVAL if the pad is invalid or doesn't correspond * to the format type. */ -static int ipipe_get_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, +static int ipipe_get_format(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_format *fmt) { struct iss_ipipe_device *ipipe = v4l2_get_subdevdata(sd); struct v4l2_mbus_framefmt *format; - format = __ipipe_get_format(ipipe, fh, fmt->pad, fmt->which); + format = __ipipe_get_format(ipipe, cfg, fmt->pad, fmt->which); if (format == NULL) return -EINVAL; @@ -323,31 +323,31 @@ static int ipipe_get_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, /* * ipipe_set_format - Set the video format on a pad * @sd : ISP IPIPE V4L2 subdevice - * @fh : V4L2 subdev file handle + * @cfg: V4L2 subdev pad config * @fmt: Format * * Return 0 on success or -EINVAL if the pad is invalid or doesn't correspond * to the format type. */ -static int ipipe_set_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, +static int ipipe_set_format(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_format *fmt) { struct iss_ipipe_device *ipipe = v4l2_get_subdevdata(sd); struct v4l2_mbus_framefmt *format; - format = __ipipe_get_format(ipipe, fh, fmt->pad, fmt->which); + format = __ipipe_get_format(ipipe, cfg, fmt->pad, fmt->which); if (format == NULL) return -EINVAL; - ipipe_try_format(ipipe, fh, fmt->pad, &fmt->format, fmt->which); + ipipe_try_format(ipipe, cfg, fmt->pad, &fmt->format, fmt->which); *format = fmt->format; /* Propagate the format from sink to source */ if (fmt->pad == IPIPE_PAD_SINK) { - format = __ipipe_get_format(ipipe, fh, IPIPE_PAD_SOURCE_VP, + format = __ipipe_get_format(ipipe, cfg, IPIPE_PAD_SOURCE_VP, fmt->which); *format = fmt->format; - ipipe_try_format(ipipe, fh, IPIPE_PAD_SOURCE_VP, format, + ipipe_try_format(ipipe, cfg, IPIPE_PAD_SOURCE_VP, format, fmt->which); } @@ -388,7 +388,7 @@ static int ipipe_init_formats(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) format.format.code = MEDIA_BUS_FMT_SGRBG10_1X10; format.format.width = 4096; format.format.height = 4096; - ipipe_set_format(sd, fh, &format); + ipipe_set_format(sd, fh ? fh->pad : NULL, &format); return 0; } diff --git a/drivers/staging/media/omap4iss/iss_ipipeif.c b/drivers/staging/media/omap4iss/iss_ipipeif.c index 3943fae699ee..948edcca8704 100644 --- a/drivers/staging/media/omap4iss/iss_ipipeif.c +++ b/drivers/staging/media/omap4iss/iss_ipipeif.c @@ -361,24 +361,24 @@ static int ipipeif_set_stream(struct v4l2_subdev *sd, int enable) static struct v4l2_mbus_framefmt * __ipipeif_get_format(struct iss_ipipeif_device *ipipeif, - struct v4l2_subdev_fh *fh, unsigned int pad, + struct v4l2_subdev_pad_config *cfg, unsigned int pad, enum v4l2_subdev_format_whence which) { if (which == V4L2_SUBDEV_FORMAT_TRY) - return v4l2_subdev_get_try_format(fh, pad); + return v4l2_subdev_get_try_format(&ipipeif->subdev, cfg, pad); return &ipipeif->formats[pad]; } /* * ipipeif_try_format - Try video format on a pad * @ipipeif: ISS IPIPEIF device - * @fh : V4L2 subdev file handle + * @cfg: V4L2 subdev pad config * @pad: Pad number * @fmt: Format */ static void ipipeif_try_format(struct iss_ipipeif_device *ipipeif, - struct v4l2_subdev_fh *fh, unsigned int pad, + struct v4l2_subdev_pad_config *cfg, unsigned int pad, struct v4l2_mbus_framefmt *fmt, enum v4l2_subdev_format_whence which) { @@ -407,7 +407,7 @@ ipipeif_try_format(struct iss_ipipeif_device *ipipeif, break; case IPIPEIF_PAD_SOURCE_ISIF_SF: - format = __ipipeif_get_format(ipipeif, fh, IPIPEIF_PAD_SINK, + format = __ipipeif_get_format(ipipeif, cfg, IPIPEIF_PAD_SINK, which); memcpy(fmt, format, sizeof(*fmt)); @@ -422,7 +422,7 @@ ipipeif_try_format(struct iss_ipipeif_device *ipipeif, break; case IPIPEIF_PAD_SOURCE_VP: - format = __ipipeif_get_format(ipipeif, fh, IPIPEIF_PAD_SINK, + format = __ipipeif_get_format(ipipeif, cfg, IPIPEIF_PAD_SINK, which); memcpy(fmt, format, sizeof(*fmt)); @@ -441,12 +441,12 @@ ipipeif_try_format(struct iss_ipipeif_device *ipipeif, /* * ipipeif_enum_mbus_code - Handle pixel format enumeration * @sd : pointer to v4l2 subdev structure - * @fh : V4L2 subdev file handle + * @cfg : V4L2 subdev pad config * @code : pointer to v4l2_subdev_mbus_code_enum structure * return -EINVAL or zero on success */ static int ipipeif_enum_mbus_code(struct v4l2_subdev *sd, - struct v4l2_subdev_fh *fh, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_mbus_code_enum *code) { struct iss_ipipeif_device *ipipeif = v4l2_get_subdevdata(sd); @@ -466,7 +466,7 @@ static int ipipeif_enum_mbus_code(struct v4l2_subdev *sd, if (code->index != 0) return -EINVAL; - format = __ipipeif_get_format(ipipeif, fh, IPIPEIF_PAD_SINK, + format = __ipipeif_get_format(ipipeif, cfg, IPIPEIF_PAD_SINK, V4L2_SUBDEV_FORMAT_TRY); code->code = format->code; @@ -480,7 +480,7 @@ static int ipipeif_enum_mbus_code(struct v4l2_subdev *sd, } static int ipipeif_enum_frame_size(struct v4l2_subdev *sd, - struct v4l2_subdev_fh *fh, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_frame_size_enum *fse) { struct iss_ipipeif_device *ipipeif = v4l2_get_subdevdata(sd); @@ -492,7 +492,7 @@ static int ipipeif_enum_frame_size(struct v4l2_subdev *sd, format.code = fse->code; format.width = 1; format.height = 1; - ipipeif_try_format(ipipeif, fh, fse->pad, &format, + ipipeif_try_format(ipipeif, cfg, fse->pad, &format, V4L2_SUBDEV_FORMAT_TRY); fse->min_width = format.width; fse->min_height = format.height; @@ -503,7 +503,7 @@ static int ipipeif_enum_frame_size(struct v4l2_subdev *sd, format.code = fse->code; format.width = -1; format.height = -1; - ipipeif_try_format(ipipeif, fh, fse->pad, &format, + ipipeif_try_format(ipipeif, cfg, fse->pad, &format, V4L2_SUBDEV_FORMAT_TRY); fse->max_width = format.width; fse->max_height = format.height; @@ -514,19 +514,19 @@ static int ipipeif_enum_frame_size(struct v4l2_subdev *sd, /* * ipipeif_get_format - Retrieve the video format on a pad * @sd : ISP IPIPEIF V4L2 subdevice - * @fh : V4L2 subdev file handle + * @cfg: V4L2 subdev pad config * @fmt: Format * * Return 0 on success or -EINVAL if the pad is invalid or doesn't correspond * to the format type. */ -static int ipipeif_get_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, +static int ipipeif_get_format(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_format *fmt) { struct iss_ipipeif_device *ipipeif = v4l2_get_subdevdata(sd); struct v4l2_mbus_framefmt *format; - format = __ipipeif_get_format(ipipeif, fh, fmt->pad, fmt->which); + format = __ipipeif_get_format(ipipeif, cfg, fmt->pad, fmt->which); if (format == NULL) return -EINVAL; @@ -537,39 +537,39 @@ static int ipipeif_get_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, /* * ipipeif_set_format - Set the video format on a pad * @sd : ISP IPIPEIF V4L2 subdevice - * @fh : V4L2 subdev file handle + * @cfg: V4L2 subdev pad config * @fmt: Format * * Return 0 on success or -EINVAL if the pad is invalid or doesn't correspond * to the format type. */ -static int ipipeif_set_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, +static int ipipeif_set_format(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_format *fmt) { struct iss_ipipeif_device *ipipeif = v4l2_get_subdevdata(sd); struct v4l2_mbus_framefmt *format; - format = __ipipeif_get_format(ipipeif, fh, fmt->pad, fmt->which); + format = __ipipeif_get_format(ipipeif, cfg, fmt->pad, fmt->which); if (format == NULL) return -EINVAL; - ipipeif_try_format(ipipeif, fh, fmt->pad, &fmt->format, fmt->which); + ipipeif_try_format(ipipeif, cfg, fmt->pad, &fmt->format, fmt->which); *format = fmt->format; /* Propagate the format from sink to source */ if (fmt->pad == IPIPEIF_PAD_SINK) { - format = __ipipeif_get_format(ipipeif, fh, + format = __ipipeif_get_format(ipipeif, cfg, IPIPEIF_PAD_SOURCE_ISIF_SF, fmt->which); *format = fmt->format; - ipipeif_try_format(ipipeif, fh, IPIPEIF_PAD_SOURCE_ISIF_SF, + ipipeif_try_format(ipipeif, cfg, IPIPEIF_PAD_SOURCE_ISIF_SF, format, fmt->which); - format = __ipipeif_get_format(ipipeif, fh, + format = __ipipeif_get_format(ipipeif, cfg, IPIPEIF_PAD_SOURCE_VP, fmt->which); *format = fmt->format; - ipipeif_try_format(ipipeif, fh, IPIPEIF_PAD_SOURCE_VP, format, + ipipeif_try_format(ipipeif, cfg, IPIPEIF_PAD_SOURCE_VP, format, fmt->which); } @@ -612,7 +612,7 @@ static int ipipeif_init_formats(struct v4l2_subdev *sd, format.format.code = MEDIA_BUS_FMT_SGRBG10_1X10; format.format.width = 4096; format.format.height = 4096; - ipipeif_set_format(sd, fh, &format); + ipipeif_set_format(sd, fh ? fh->pad : NULL, &format); return 0; } diff --git a/drivers/staging/media/omap4iss/iss_resizer.c b/drivers/staging/media/omap4iss/iss_resizer.c index 3ab972818f1b..f9b0aac91e9d 100644 --- a/drivers/staging/media/omap4iss/iss_resizer.c +++ b/drivers/staging/media/omap4iss/iss_resizer.c @@ -420,24 +420,24 @@ static int resizer_set_stream(struct v4l2_subdev *sd, int enable) static struct v4l2_mbus_framefmt * __resizer_get_format(struct iss_resizer_device *resizer, - struct v4l2_subdev_fh *fh, unsigned int pad, + struct v4l2_subdev_pad_config *cfg, unsigned int pad, enum v4l2_subdev_format_whence which) { if (which == V4L2_SUBDEV_FORMAT_TRY) - return v4l2_subdev_get_try_format(fh, pad); + return v4l2_subdev_get_try_format(&resizer->subdev, cfg, pad); return &resizer->formats[pad]; } /* * resizer_try_format - Try video format on a pad * @resizer: ISS RESIZER device - * @fh : V4L2 subdev file handle + * @cfg: V4L2 subdev pad config * @pad: Pad number * @fmt: Format */ static void resizer_try_format(struct iss_resizer_device *resizer, - struct v4l2_subdev_fh *fh, unsigned int pad, + struct v4l2_subdev_pad_config *cfg, unsigned int pad, struct v4l2_mbus_framefmt *fmt, enum v4l2_subdev_format_whence which) { @@ -465,7 +465,7 @@ resizer_try_format(struct iss_resizer_device *resizer, case RESIZER_PAD_SOURCE_MEM: pixelcode = fmt->code; - format = __resizer_get_format(resizer, fh, RESIZER_PAD_SINK, + format = __resizer_get_format(resizer, cfg, RESIZER_PAD_SINK, which); memcpy(fmt, format, sizeof(*fmt)); @@ -492,12 +492,12 @@ resizer_try_format(struct iss_resizer_device *resizer, /* * resizer_enum_mbus_code - Handle pixel format enumeration * @sd : pointer to v4l2 subdev structure - * @fh : V4L2 subdev file handle + * @cfg: V4L2 subdev pad config * @code : pointer to v4l2_subdev_mbus_code_enum structure * return -EINVAL or zero on success */ static int resizer_enum_mbus_code(struct v4l2_subdev *sd, - struct v4l2_subdev_fh *fh, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_mbus_code_enum *code) { struct iss_resizer_device *resizer = v4l2_get_subdevdata(sd); @@ -512,7 +512,7 @@ static int resizer_enum_mbus_code(struct v4l2_subdev *sd, break; case RESIZER_PAD_SOURCE_MEM: - format = __resizer_get_format(resizer, fh, RESIZER_PAD_SINK, + format = __resizer_get_format(resizer, cfg, RESIZER_PAD_SINK, V4L2_SUBDEV_FORMAT_TRY); if (code->index == 0) { @@ -542,7 +542,7 @@ static int resizer_enum_mbus_code(struct v4l2_subdev *sd, } static int resizer_enum_frame_size(struct v4l2_subdev *sd, - struct v4l2_subdev_fh *fh, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_frame_size_enum *fse) { struct iss_resizer_device *resizer = v4l2_get_subdevdata(sd); @@ -554,7 +554,7 @@ static int resizer_enum_frame_size(struct v4l2_subdev *sd, format.code = fse->code; format.width = 1; format.height = 1; - resizer_try_format(resizer, fh, fse->pad, &format, + resizer_try_format(resizer, cfg, fse->pad, &format, V4L2_SUBDEV_FORMAT_TRY); fse->min_width = format.width; fse->min_height = format.height; @@ -565,7 +565,7 @@ static int resizer_enum_frame_size(struct v4l2_subdev *sd, format.code = fse->code; format.width = -1; format.height = -1; - resizer_try_format(resizer, fh, fse->pad, &format, + resizer_try_format(resizer, cfg, fse->pad, &format, V4L2_SUBDEV_FORMAT_TRY); fse->max_width = format.width; fse->max_height = format.height; @@ -576,19 +576,19 @@ static int resizer_enum_frame_size(struct v4l2_subdev *sd, /* * resizer_get_format - Retrieve the video format on a pad * @sd : ISP RESIZER V4L2 subdevice - * @fh : V4L2 subdev file handle + * @cfg: V4L2 subdev pad config * @fmt: Format * * Return 0 on success or -EINVAL if the pad is invalid or doesn't correspond * to the format type. */ -static int resizer_get_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, +static int resizer_get_format(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_format *fmt) { struct iss_resizer_device *resizer = v4l2_get_subdevdata(sd); struct v4l2_mbus_framefmt *format; - format = __resizer_get_format(resizer, fh, fmt->pad, fmt->which); + format = __resizer_get_format(resizer, cfg, fmt->pad, fmt->which); if (format == NULL) return -EINVAL; @@ -599,32 +599,32 @@ static int resizer_get_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, /* * resizer_set_format - Set the video format on a pad * @sd : ISP RESIZER V4L2 subdevice - * @fh : V4L2 subdev file handle + * @cfg: V4L2 subdev pad config * @fmt: Format * * Return 0 on success or -EINVAL if the pad is invalid or doesn't correspond * to the format type. */ -static int resizer_set_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, +static int resizer_set_format(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_format *fmt) { struct iss_resizer_device *resizer = v4l2_get_subdevdata(sd); struct v4l2_mbus_framefmt *format; - format = __resizer_get_format(resizer, fh, fmt->pad, fmt->which); + format = __resizer_get_format(resizer, cfg, fmt->pad, fmt->which); if (format == NULL) return -EINVAL; - resizer_try_format(resizer, fh, fmt->pad, &fmt->format, fmt->which); + resizer_try_format(resizer, cfg, fmt->pad, &fmt->format, fmt->which); *format = fmt->format; /* Propagate the format from sink to source */ if (fmt->pad == RESIZER_PAD_SINK) { - format = __resizer_get_format(resizer, fh, + format = __resizer_get_format(resizer, cfg, RESIZER_PAD_SOURCE_MEM, fmt->which); *format = fmt->format; - resizer_try_format(resizer, fh, RESIZER_PAD_SOURCE_MEM, format, + resizer_try_format(resizer, cfg, RESIZER_PAD_SOURCE_MEM, format, fmt->which); } @@ -667,7 +667,7 @@ static int resizer_init_formats(struct v4l2_subdev *sd, format.format.code = MEDIA_BUS_FMT_UYVY8_1X16; format.format.width = 4096; format.format.height = 4096; - resizer_set_format(sd, fh, &format); + resizer_set_format(sd, fh ? fh->pad : NULL, &format); return 0; } diff --git a/include/media/v4l2-subdev.h b/include/media/v4l2-subdev.h index 5beeb8744fd1..d9404df80482 100644 --- a/include/media/v4l2-subdev.h +++ b/include/media/v4l2-subdev.h @@ -482,6 +482,18 @@ struct v4l2_subdev_ir_ops { struct v4l2_subdev_ir_parameters *params); }; +/* + * Used for storing subdev pad information. This structure only needs + * to be passed to the pad op if the 'which' field of the main argument + * is set to V4L2_SUBDEV_FORMAT_TRY. For V4L2_SUBDEV_FORMAT_ACTIVE it is + * safe to pass NULL. + */ +struct v4l2_subdev_pad_config { + struct v4l2_mbus_framefmt try_fmt; + struct v4l2_rect try_crop; + struct v4l2_rect try_compose; +}; + /** * struct v4l2_subdev_pad_ops - v4l2-subdev pad level operations * @get_frame_desc: get the current low level media bus frame parameters. @@ -489,21 +501,26 @@ struct v4l2_subdev_ir_ops { * may be adjusted by the subdev driver to device capabilities. */ struct v4l2_subdev_pad_ops { - int (*enum_mbus_code)(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, + int (*enum_mbus_code)(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_mbus_code_enum *code); int (*enum_frame_size)(struct v4l2_subdev *sd, - struct v4l2_subdev_fh *fh, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_frame_size_enum *fse); int (*enum_frame_interval)(struct v4l2_subdev *sd, - struct v4l2_subdev_fh *fh, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_frame_interval_enum *fie); - int (*get_fmt)(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, + int (*get_fmt)(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_format *format); - int (*set_fmt)(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, + int (*set_fmt)(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_format *format); - int (*get_selection)(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, + int (*get_selection)(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_selection *sel); - int (*set_selection)(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, + int (*set_selection)(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_selection *sel); int (*get_edid)(struct v4l2_subdev *sd, struct v4l2_edid *edid); int (*set_edid)(struct v4l2_subdev *sd, struct v4l2_edid *edid); @@ -625,11 +642,7 @@ struct v4l2_subdev { struct v4l2_subdev_fh { struct v4l2_fh vfh; #if defined(CONFIG_VIDEO_V4L2_SUBDEV_API) - struct { - struct v4l2_mbus_framefmt try_fmt; - struct v4l2_rect try_crop; - struct v4l2_rect try_compose; - } *pad; + struct v4l2_subdev_pad_config *pad; #endif }; @@ -639,17 +652,17 @@ struct v4l2_subdev_fh { #if defined(CONFIG_VIDEO_V4L2_SUBDEV_API) #define __V4L2_SUBDEV_MK_GET_TRY(rtype, fun_name, field_name) \ static inline struct rtype * \ - v4l2_subdev_get_try_##fun_name(struct v4l2_subdev_fh *fh, \ - unsigned int pad) \ + fun_name(struct v4l2_subdev *sd, \ + struct v4l2_subdev_pad_config *cfg, \ + unsigned int pad) \ { \ - BUG_ON(pad >= vdev_to_v4l2_subdev( \ - fh->vfh.vdev)->entity.num_pads); \ - return &fh->pad[pad].field_name; \ + BUG_ON(pad >= sd->entity.num_pads); \ + return &cfg[pad].field_name; \ } -__V4L2_SUBDEV_MK_GET_TRY(v4l2_mbus_framefmt, format, try_fmt) -__V4L2_SUBDEV_MK_GET_TRY(v4l2_rect, crop, try_crop) -__V4L2_SUBDEV_MK_GET_TRY(v4l2_rect, compose, try_compose) +__V4L2_SUBDEV_MK_GET_TRY(v4l2_mbus_framefmt, v4l2_subdev_get_try_format, try_fmt) +__V4L2_SUBDEV_MK_GET_TRY(v4l2_rect, v4l2_subdev_get_try_crop, try_crop) +__V4L2_SUBDEV_MK_GET_TRY(v4l2_rect, v4l2_subdev_get_try_compose, try_compose) #endif extern const struct v4l2_file_operations v4l2_subdev_fops; -- cgit v1.2.3 From e1c47e732cbb4211d15ae0c4465dc4ceefcaa398 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Wed, 4 Mar 2015 01:47:55 -0800 Subject: [media] v4l2-subdev.h: add 'which' field for the enum structs While all other pad ops allow you to select whether to use the 'try' or the 'active' formats, the enum ops didn't have that option and always used 'try'. However, this will fail if a simple (e.g. PCI) bridge driver wants to use the enum pad op of a subdev that's also used in a complex platform driver like the omap3. Such a bridge driver generally wants to enum formats based on the active format. So add a new 'which' field to these structs. Note that V4L2_SUBDEV_FORMAT_TRY is 0, so the default remains TRY (applications need to set reserved to 0). Signed-off-by: Hans Verkuil Acked-by: Lad, Prabhakar Tested-by: Lad, Prabhakar Acked-by: Laurent Pinchart Acked-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab --- include/uapi/linux/v4l2-subdev.h | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/include/uapi/linux/v4l2-subdev.h b/include/uapi/linux/v4l2-subdev.h index e0a7e3da498a..dbce2b554e02 100644 --- a/include/uapi/linux/v4l2-subdev.h +++ b/include/uapi/linux/v4l2-subdev.h @@ -69,12 +69,14 @@ struct v4l2_subdev_crop { * @pad: pad number, as reported by the media API * @index: format index during enumeration * @code: format code (MEDIA_BUS_FMT_ definitions) + * @which: format type (from enum v4l2_subdev_format_whence) */ struct v4l2_subdev_mbus_code_enum { __u32 pad; __u32 index; __u32 code; - __u32 reserved[9]; + __u32 which; + __u32 reserved[8]; }; /** @@ -82,6 +84,7 @@ struct v4l2_subdev_mbus_code_enum { * @pad: pad number, as reported by the media API * @index: format index during enumeration * @code: format code (MEDIA_BUS_FMT_ definitions) + * @which: format type (from enum v4l2_subdev_format_whence) */ struct v4l2_subdev_frame_size_enum { __u32 index; @@ -91,7 +94,8 @@ struct v4l2_subdev_frame_size_enum { __u32 max_width; __u32 min_height; __u32 max_height; - __u32 reserved[9]; + __u32 which; + __u32 reserved[8]; }; /** @@ -113,6 +117,7 @@ struct v4l2_subdev_frame_interval { * @width: frame width in pixels * @height: frame height in pixels * @interval: frame interval in seconds + * @which: format type (from enum v4l2_subdev_format_whence) */ struct v4l2_subdev_frame_interval_enum { __u32 index; @@ -121,7 +126,8 @@ struct v4l2_subdev_frame_interval_enum { __u32 width; __u32 height; struct v4l2_fract interval; - __u32 reserved[9]; + __u32 which; + __u32 reserved[8]; }; /** -- cgit v1.2.3 From 8a56b6b5fd6ff92b7e27d870b803b11b751660c2 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Wed, 4 Mar 2015 01:48:01 -0800 Subject: [media] v4l2-subdev: remove enum_framesizes/intervals The video and pad ops are duplicates, so get rid of the more limited video op. The whole point of the subdev API is to allow reuse of subdev drivers by bridge drivers. Having duplicate ops makes that much harder. We should never have allowed duplicate ops in the first place. A lesson for the future. Signed-off-by: Hans Verkuil Acked-by: Laurent Pinchart Acked-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- include/media/v4l2-subdev.h | 2 -- 1 file changed, 2 deletions(-) (limited to 'include') diff --git a/include/media/v4l2-subdev.h b/include/media/v4l2-subdev.h index d9404df80482..2f0a345a7fed 100644 --- a/include/media/v4l2-subdev.h +++ b/include/media/v4l2-subdev.h @@ -332,8 +332,6 @@ struct v4l2_subdev_video_ops { struct v4l2_subdev_frame_interval *interval); int (*s_frame_interval)(struct v4l2_subdev *sd, struct v4l2_subdev_frame_interval *interval); - int (*enum_framesizes)(struct v4l2_subdev *sd, struct v4l2_frmsizeenum *fsize); - int (*enum_frameintervals)(struct v4l2_subdev *sd, struct v4l2_frmivalenum *fival); int (*s_dv_timings)(struct v4l2_subdev *sd, struct v4l2_dv_timings *timings); int (*g_dv_timings)(struct v4l2_subdev *sd, -- cgit v1.2.3 From 0345f93138b2224e0d7ce91fcffdb3dd23f364d7 Mon Sep 17 00:00:00 2001 From: "tadeusz.struk@intel.com" Date: Thu, 19 Mar 2015 12:31:25 -0700 Subject: net: socket: add support for async operations Add support for async operations. Signed-off-by: Tadeusz Struk Signed-off-by: David S. Miller --- include/linux/socket.h | 1 + net/compat.c | 2 ++ net/socket.c | 8 ++++++-- 3 files changed, 9 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/linux/socket.h b/include/linux/socket.h index fab4d0ddf4ed..c9852ef7e317 100644 --- a/include/linux/socket.h +++ b/include/linux/socket.h @@ -51,6 +51,7 @@ struct msghdr { void *msg_control; /* ancillary data */ __kernel_size_t msg_controllen; /* ancillary data buffer length */ unsigned int msg_flags; /* flags on received message */ + struct kiocb *msg_iocb; /* ptr to iocb for async requests */ }; struct user_msghdr { diff --git a/net/compat.c b/net/compat.c index 13c0c9a25cd9..c4b6b0f43d5d 100644 --- a/net/compat.c +++ b/net/compat.c @@ -79,6 +79,8 @@ ssize_t get_compat_msghdr(struct msghdr *kmsg, if (nr_segs > UIO_MAXIOV) return -EMSGSIZE; + kmsg->msg_iocb = NULL; + err = compat_rw_copy_check_uvector(save_addr ? READ : WRITE, compat_ptr(uiov), nr_segs, UIO_FASTIOV, *iov, iov); diff --git a/net/socket.c b/net/socket.c index 3e776776f42c..073809f4125f 100644 --- a/net/socket.c +++ b/net/socket.c @@ -798,7 +798,8 @@ static ssize_t sock_read_iter(struct kiocb *iocb, struct iov_iter *to) { struct file *file = iocb->ki_filp; struct socket *sock = file->private_data; - struct msghdr msg = {.msg_iter = *to}; + struct msghdr msg = {.msg_iter = *to, + .msg_iocb = iocb}; ssize_t res; if (file->f_flags & O_NONBLOCK) @@ -819,7 +820,8 @@ static ssize_t sock_write_iter(struct kiocb *iocb, struct iov_iter *from) { struct file *file = iocb->ki_filp; struct socket *sock = file->private_data; - struct msghdr msg = {.msg_iter = *from}; + struct msghdr msg = {.msg_iter = *from, + .msg_iocb = iocb}; ssize_t res; if (iocb->ki_pos != 0) @@ -1894,6 +1896,8 @@ static ssize_t copy_msghdr_from_user(struct msghdr *kmsg, if (nr_segs > UIO_MAXIOV) return -EMSGSIZE; + kmsg->msg_iocb = NULL; + err = rw_copy_check_uvector(save_addr ? READ : WRITE, uiov, nr_segs, UIO_FASTIOV, *iov, iov); -- cgit v1.2.3 From 66db37391dad834a4f739984eec81af27a68b09c Mon Sep 17 00:00:00 2001 From: Tadeusz Struk Date: Thu, 19 Mar 2015 12:31:30 -0700 Subject: crypto: af_alg - Allow to link sgl Allow to link af_alg sgls. Signed-off-by: Tadeusz Struk Signed-off-by: David S. Miller --- crypto/af_alg.c | 18 +++++++++++++----- include/crypto/if_alg.h | 4 +++- 2 files changed, 16 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/crypto/af_alg.c b/crypto/af_alg.c index 7f8b7edcadca..26089d182cb7 100644 --- a/crypto/af_alg.c +++ b/crypto/af_alg.c @@ -358,8 +358,8 @@ int af_alg_make_sg(struct af_alg_sgl *sgl, struct iov_iter *iter, int len) npages = (off + n + PAGE_SIZE - 1) >> PAGE_SHIFT; if (WARN_ON(npages == 0)) return -EINVAL; - - sg_init_table(sgl->sg, npages); + /* Add one extra for linking */ + sg_init_table(sgl->sg, npages + 1); for (i = 0, len = n; i < npages; i++) { int plen = min_t(int, len, PAGE_SIZE - off); @@ -369,18 +369,26 @@ int af_alg_make_sg(struct af_alg_sgl *sgl, struct iov_iter *iter, int len) off = 0; len -= plen; } + sg_mark_end(sgl->sg + npages - 1); + sgl->npages = npages; + return n; } EXPORT_SYMBOL_GPL(af_alg_make_sg); +void af_alg_link_sg(struct af_alg_sgl *sgl_prev, struct af_alg_sgl *sgl_new) +{ + sg_unmark_end(sgl_prev->sg + sgl_prev->npages - 1); + sg_chain(sgl_prev->sg, sgl_prev->npages + 1, sgl_new->sg); +} +EXPORT_SYMBOL(af_alg_link_sg); + void af_alg_free_sg(struct af_alg_sgl *sgl) { int i; - i = 0; - do { + for (i = 0; i < sgl->npages; i++) put_page(sgl->pages[i]); - } while (!sg_is_last(sgl->sg + (i++))); } EXPORT_SYMBOL_GPL(af_alg_free_sg); diff --git a/include/crypto/if_alg.h b/include/crypto/if_alg.h index 178525e5f430..018afb264ac2 100644 --- a/include/crypto/if_alg.h +++ b/include/crypto/if_alg.h @@ -58,8 +58,9 @@ struct af_alg_type { }; struct af_alg_sgl { - struct scatterlist sg[ALG_MAX_PAGES]; + struct scatterlist sg[ALG_MAX_PAGES + 1]; struct page *pages[ALG_MAX_PAGES]; + unsigned int npages; }; int af_alg_register_type(const struct af_alg_type *type); @@ -70,6 +71,7 @@ int af_alg_accept(struct sock *sk, struct socket *newsock); int af_alg_make_sg(struct af_alg_sgl *sgl, struct iov_iter *iter, int len); void af_alg_free_sg(struct af_alg_sgl *sgl); +void af_alg_link_sg(struct af_alg_sgl *sgl_prev, struct af_alg_sgl *sgl_new); int af_alg_cmsg_send(struct msghdr *msg, struct af_alg_control *con); -- cgit v1.2.3 From 42cb80a2353f42913ae78074ffa1f1b4a49e5436 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Sun, 22 Mar 2015 10:22:19 -0700 Subject: inet: remove sk_listener parameter from syn_ack_timeout() It is not needed, and req->sk_listener points to the listener anyway. request_sock argument can be const. Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- include/linux/dccp.h | 2 +- include/net/request_sock.h | 3 +-- include/net/tcp.h | 2 +- net/dccp/ipv4.c | 2 +- net/ipv4/inet_connection_sock.c | 2 +- net/ipv4/tcp_timer.c | 8 +++++--- 6 files changed, 10 insertions(+), 9 deletions(-) (limited to 'include') diff --git a/include/linux/dccp.h b/include/linux/dccp.h index 439ff698000a..3dca24d3ac67 100644 --- a/include/linux/dccp.h +++ b/include/linux/dccp.h @@ -317,6 +317,6 @@ static inline const char *dccp_role(const struct sock *sk) return NULL; } -extern void dccp_syn_ack_timeout(struct sock *sk, struct request_sock *req); +extern void dccp_syn_ack_timeout(const struct request_sock *req); #endif /* _LINUX_DCCP_H */ diff --git a/include/net/request_sock.h b/include/net/request_sock.h index 6a91261d9b7b..8603c350fad0 100644 --- a/include/net/request_sock.h +++ b/include/net/request_sock.h @@ -39,8 +39,7 @@ struct request_sock_ops { void (*send_reset)(struct sock *sk, struct sk_buff *skb); void (*destructor)(struct request_sock *req); - void (*syn_ack_timeout)(struct sock *sk, - struct request_sock *req); + void (*syn_ack_timeout)(const struct request_sock *req); }; int inet_rtx_syn_ack(struct sock *parent, struct request_sock *req); diff --git a/include/net/tcp.h b/include/net/tcp.h index 082fd79132b7..1876262afd59 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -433,7 +433,7 @@ int compat_tcp_getsockopt(struct sock *sk, int level, int optname, int compat_tcp_setsockopt(struct sock *sk, int level, int optname, char __user *optval, unsigned int optlen); void tcp_set_keepalive(struct sock *sk, int val); -void tcp_syn_ack_timeout(struct sock *sk, struct request_sock *req); +void tcp_syn_ack_timeout(const struct request_sock *req); int tcp_recvmsg(struct sock *sk, struct msghdr *msg, size_t len, int nonblock, int flags, int *addr_len); void tcp_parse_options(const struct sk_buff *skb, diff --git a/net/dccp/ipv4.c b/net/dccp/ipv4.c index 25a9615b3b88..1f7161e05403 100644 --- a/net/dccp/ipv4.c +++ b/net/dccp/ipv4.c @@ -576,7 +576,7 @@ static void dccp_v4_reqsk_destructor(struct request_sock *req) kfree(inet_rsk(req)->opt); } -void dccp_syn_ack_timeout(struct sock *sk, struct request_sock *req) +void dccp_syn_ack_timeout(const struct request_sock *req) { } EXPORT_SYMBOL(dccp_syn_ack_timeout); diff --git a/net/ipv4/inet_connection_sock.c b/net/ipv4/inet_connection_sock.c index 7d011e825c48..a12b973164d0 100644 --- a/net/ipv4/inet_connection_sock.c +++ b/net/ipv4/inet_connection_sock.c @@ -615,7 +615,7 @@ static void reqsk_timer_handler(unsigned long data) max_retries = defer_accept; syn_ack_recalc(req, thresh, max_retries, defer_accept, &expire, &resend); - req->rsk_ops->syn_ack_timeout(sk_listener, req); + req->rsk_ops->syn_ack_timeout(req); if (!expire && (!resend || !inet_rtx_syn_ack(sk_listener, req) || diff --git a/net/ipv4/tcp_timer.c b/net/ipv4/tcp_timer.c index 3daa6b5d766d..2568fd282873 100644 --- a/net/ipv4/tcp_timer.c +++ b/net/ipv4/tcp_timer.c @@ -327,7 +327,7 @@ static void tcp_fastopen_synack_timer(struct sock *sk) struct request_sock *req; req = tcp_sk(sk)->fastopen_rsk; - req->rsk_ops->syn_ack_timeout(sk, req); + req->rsk_ops->syn_ack_timeout(req); if (req->num_timeout >= max_retries) { tcp_write_err(sk); @@ -539,9 +539,11 @@ static void tcp_write_timer(unsigned long data) sock_put(sk); } -void tcp_syn_ack_timeout(struct sock *sk, struct request_sock *req) +void tcp_syn_ack_timeout(const struct request_sock *req) { - NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_TCPTIMEOUTS); + struct net *net = read_pnet(&inet_rsk(req)->ireq_net); + + NET_INC_STATS_BH(net, LINUX_MIB_TCPTIMEOUTS); } EXPORT_SYMBOL(tcp_syn_ack_timeout); -- cgit v1.2.3 From b282705336e03fc7b9377a278939594870a40f96 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Sun, 22 Mar 2015 10:22:21 -0700 Subject: net: convert syn_wait_lock to a spinlock This is a low hanging fruit, as we'll get rid of syn_wait_lock eventually. We hold syn_wait_lock for such small sections, that it makes no sense to use a read/write lock. A spin lock is simply faster. Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- include/net/request_sock.h | 11 +++-------- net/core/request_sock.c | 14 +++++++------- net/ipv4/inet_connection_sock.c | 8 ++++---- net/ipv4/inet_diag.c | 4 ++-- net/ipv4/tcp_ipv4.c | 12 ++++++------ net/ipv6/inet6_connection_sock.c | 4 ++-- 6 files changed, 24 insertions(+), 29 deletions(-) (limited to 'include') diff --git a/include/net/request_sock.h b/include/net/request_sock.h index 8603c350fad0..fe41f3ceb008 100644 --- a/include/net/request_sock.h +++ b/include/net/request_sock.h @@ -173,11 +173,6 @@ struct fastopen_queue { * %syn_wait_lock is necessary only to avoid proc interface having to grab the main * lock sock while browsing the listening hash (otherwise it's deadlock prone). * - * This lock is acquired in read mode only from listening_get_next() seq_file - * op and it's acquired in write mode _only_ from code that is actively - * changing rskq_accept_head. All readers that are holding the master sock lock - * don't need to grab this lock in read mode too as rskq_accept_head. writes - * are always protected from the main sock lock. */ struct request_sock_queue { struct request_sock *rskq_accept_head; @@ -192,7 +187,7 @@ struct request_sock_queue { */ /* temporary alignment, our goal is to get rid of this lock */ - rwlock_t syn_wait_lock ____cacheline_aligned_in_smp; + spinlock_t syn_wait_lock ____cacheline_aligned_in_smp; }; int reqsk_queue_alloc(struct request_sock_queue *queue, @@ -223,14 +218,14 @@ static inline void reqsk_queue_unlink(struct request_sock_queue *queue, struct listen_sock *lopt = queue->listen_opt; struct request_sock **prev; - write_lock(&queue->syn_wait_lock); + spin_lock(&queue->syn_wait_lock); prev = &lopt->syn_table[req->rsk_hash]; while (*prev != req) prev = &(*prev)->dl_next; *prev = req->dl_next; - write_unlock(&queue->syn_wait_lock); + spin_unlock(&queue->syn_wait_lock); if (del_timer(&req->rsk_timer)) reqsk_put(req); } diff --git a/net/core/request_sock.c b/net/core/request_sock.c index cdc0ddd9ac9f..87b22c0bc08c 100644 --- a/net/core/request_sock.c +++ b/net/core/request_sock.c @@ -58,14 +58,14 @@ int reqsk_queue_alloc(struct request_sock_queue *queue, return -ENOMEM; get_random_bytes(&lopt->hash_rnd, sizeof(lopt->hash_rnd)); - rwlock_init(&queue->syn_wait_lock); + spin_lock_init(&queue->syn_wait_lock); queue->rskq_accept_head = NULL; lopt->nr_table_entries = nr_table_entries; lopt->max_qlen_log = ilog2(nr_table_entries); - write_lock_bh(&queue->syn_wait_lock); + spin_lock_bh(&queue->syn_wait_lock); queue->listen_opt = lopt; - write_unlock_bh(&queue->syn_wait_lock); + spin_unlock_bh(&queue->syn_wait_lock); return 0; } @@ -81,10 +81,10 @@ static inline struct listen_sock *reqsk_queue_yank_listen_sk( { struct listen_sock *lopt; - write_lock_bh(&queue->syn_wait_lock); + spin_lock_bh(&queue->syn_wait_lock); lopt = queue->listen_opt; queue->listen_opt = NULL; - write_unlock_bh(&queue->syn_wait_lock); + spin_unlock_bh(&queue->syn_wait_lock); return lopt; } @@ -100,7 +100,7 @@ void reqsk_queue_destroy(struct request_sock_queue *queue) for (i = 0; i < lopt->nr_table_entries; i++) { struct request_sock *req; - write_lock_bh(&queue->syn_wait_lock); + spin_lock_bh(&queue->syn_wait_lock); while ((req = lopt->syn_table[i]) != NULL) { lopt->syn_table[i] = req->dl_next; atomic_inc(&lopt->qlen_dec); @@ -108,7 +108,7 @@ void reqsk_queue_destroy(struct request_sock_queue *queue) reqsk_put(req); reqsk_put(req); } - write_unlock_bh(&queue->syn_wait_lock); + spin_unlock_bh(&queue->syn_wait_lock); } } diff --git a/net/ipv4/inet_connection_sock.c b/net/ipv4/inet_connection_sock.c index 711ab143d4cb..79c0c9439fdc 100644 --- a/net/ipv4/inet_connection_sock.c +++ b/net/ipv4/inet_connection_sock.c @@ -495,7 +495,7 @@ struct request_sock *inet_csk_search_req(struct sock *sk, u32 hash = inet_synq_hash(raddr, rport, lopt->hash_rnd, lopt->nr_table_entries); - write_lock(&icsk->icsk_accept_queue.syn_wait_lock); + spin_lock(&icsk->icsk_accept_queue.syn_wait_lock); for (req = lopt->syn_table[hash]; req != NULL; req = req->dl_next) { const struct inet_request_sock *ireq = inet_rsk(req); @@ -508,7 +508,7 @@ struct request_sock *inet_csk_search_req(struct sock *sk, break; } } - write_unlock(&icsk->icsk_accept_queue.syn_wait_lock); + spin_unlock(&icsk->icsk_accept_queue.syn_wait_lock); return req; } @@ -650,10 +650,10 @@ void reqsk_queue_hash_req(struct request_sock_queue *queue, setup_timer(&req->rsk_timer, reqsk_timer_handler, (unsigned long)req); req->rsk_hash = hash; - write_lock(&queue->syn_wait_lock); + spin_lock(&queue->syn_wait_lock); req->dl_next = lopt->syn_table[hash]; lopt->syn_table[hash] = req; - write_unlock(&queue->syn_wait_lock); + spin_unlock(&queue->syn_wait_lock); mod_timer_pinned(&req->rsk_timer, jiffies + timeout); } diff --git a/net/ipv4/inet_diag.c b/net/ipv4/inet_diag.c index f984b2001d0a..76322c9867d5 100644 --- a/net/ipv4/inet_diag.c +++ b/net/ipv4/inet_diag.c @@ -728,7 +728,7 @@ static int inet_diag_dump_reqs(struct sk_buff *skb, struct sock *sk, entry.family = sk->sk_family; - read_lock_bh(&icsk->icsk_accept_queue.syn_wait_lock); + spin_lock_bh(&icsk->icsk_accept_queue.syn_wait_lock); lopt = icsk->icsk_accept_queue.listen_opt; if (!lopt || !listen_sock_qlen(lopt)) @@ -776,7 +776,7 @@ static int inet_diag_dump_reqs(struct sk_buff *skb, struct sock *sk, } out: - read_unlock_bh(&icsk->icsk_accept_queue.syn_wait_lock); + spin_unlock_bh(&icsk->icsk_accept_queue.syn_wait_lock); return err; } diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index 5554b8f33d41..8028ad5920a4 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -1909,13 +1909,13 @@ get_req: } sk = sk_nulls_next(st->syn_wait_sk); st->state = TCP_SEQ_STATE_LISTENING; - read_unlock_bh(&icsk->icsk_accept_queue.syn_wait_lock); + spin_unlock_bh(&icsk->icsk_accept_queue.syn_wait_lock); } else { icsk = inet_csk(sk); - read_lock_bh(&icsk->icsk_accept_queue.syn_wait_lock); + spin_lock_bh(&icsk->icsk_accept_queue.syn_wait_lock); if (reqsk_queue_len(&icsk->icsk_accept_queue)) goto start_req; - read_unlock_bh(&icsk->icsk_accept_queue.syn_wait_lock); + spin_unlock_bh(&icsk->icsk_accept_queue.syn_wait_lock); sk = sk_nulls_next(sk); } get_sk: @@ -1927,7 +1927,7 @@ get_sk: goto out; } icsk = inet_csk(sk); - read_lock_bh(&icsk->icsk_accept_queue.syn_wait_lock); + spin_lock_bh(&icsk->icsk_accept_queue.syn_wait_lock); if (reqsk_queue_len(&icsk->icsk_accept_queue)) { start_req: st->uid = sock_i_uid(sk); @@ -1936,7 +1936,7 @@ start_req: st->sbucket = 0; goto get_req; } - read_unlock_bh(&icsk->icsk_accept_queue.syn_wait_lock); + spin_unlock_bh(&icsk->icsk_accept_queue.syn_wait_lock); } spin_unlock_bh(&ilb->lock); st->offset = 0; @@ -2155,7 +2155,7 @@ static void tcp_seq_stop(struct seq_file *seq, void *v) case TCP_SEQ_STATE_OPENREQ: if (v) { struct inet_connection_sock *icsk = inet_csk(st->syn_wait_sk); - read_unlock_bh(&icsk->icsk_accept_queue.syn_wait_lock); + spin_unlock_bh(&icsk->icsk_accept_queue.syn_wait_lock); } case TCP_SEQ_STATE_LISTENING: if (v != SEQ_START_TOKEN) diff --git a/net/ipv6/inet6_connection_sock.c b/net/ipv6/inet6_connection_sock.c index 2f3bbe569e8f..6927f3fb5597 100644 --- a/net/ipv6/inet6_connection_sock.c +++ b/net/ipv6/inet6_connection_sock.c @@ -124,7 +124,7 @@ struct request_sock *inet6_csk_search_req(struct sock *sk, u32 hash = inet6_synq_hash(raddr, rport, lopt->hash_rnd, lopt->nr_table_entries); - write_lock(&icsk->icsk_accept_queue.syn_wait_lock); + spin_lock(&icsk->icsk_accept_queue.syn_wait_lock); for (req = lopt->syn_table[hash]; req != NULL; req = req->dl_next) { const struct inet_request_sock *ireq = inet_rsk(req); @@ -138,7 +138,7 @@ struct request_sock *inet6_csk_search_req(struct sock *sk, break; } } - write_unlock(&icsk->icsk_accept_queue.syn_wait_lock); + spin_unlock(&icsk->icsk_accept_queue.syn_wait_lock); return req; } -- cgit v1.2.3 From 26e3736090e1037ac929787df21c05497479b77f Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Sun, 22 Mar 2015 10:22:22 -0700 Subject: ipv4: tcp: handle ICMP messages on TCP_NEW_SYN_RECV request sockets tcp_v4_err() can restrict lookups to ehash table, and not to listeners. Note this patch creates the infrastructure, but this means that ICMP messages for request sockets are ignored until complete conversion. New tcp_req_err() helper is exported so that we can use it in IPv6 in following patch. Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- include/net/tcp.h | 1 + net/ipv4/tcp_ipv4.c | 69 ++++++++++++++++++++++++++--------------------------- 2 files changed, 35 insertions(+), 35 deletions(-) (limited to 'include') diff --git a/include/net/tcp.h b/include/net/tcp.h index 1876262afd59..fe60e00e1919 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -447,6 +447,7 @@ const u8 *tcp_parse_md5sig_option(const struct tcphdr *th); void tcp_v4_send_check(struct sock *sk, struct sk_buff *skb); void tcp_v4_mtu_reduced(struct sock *sk); +void tcp_req_err(struct sock *sk, u32 seq); int tcp_v4_conn_request(struct sock *sk, struct sk_buff *skb); struct sock *tcp_create_openreq_child(struct sock *sk, struct request_sock *req, diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index 8028ad5920a4..a57615062b66 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -310,6 +310,34 @@ static void do_redirect(struct sk_buff *skb, struct sock *sk) dst->ops->redirect(dst, sk, skb); } + +/* handle ICMP messages on TCP_NEW_SYN_RECV request sockets */ +void tcp_req_err(struct sock *sk, u32 seq) +{ + struct request_sock *req = inet_reqsk(sk); + struct net *net = sock_net(sk); + + /* ICMPs are not backlogged, hence we cannot get + * an established socket here. + */ + WARN_ON(req->sk); + + if (seq != tcp_rsk(req)->snt_isn) { + NET_INC_STATS_BH(net, LINUX_MIB_OUTOFWINDOWICMPS); + } else { + /* + * Still in SYN_RECV, just remove it silently. + * There is no good way to pass the error to the newly + * created socket, and POSIX does not want network + * errors returned from accept(). + */ + inet_csk_reqsk_queue_drop(req->rsk_listener, req); + NET_INC_STATS_BH(net, LINUX_MIB_LISTENDROPS); + } + reqsk_put(req); +} +EXPORT_SYMBOL(tcp_req_err); + /* * This routine is called by the ICMP module when it gets some * sort of error condition. If err < 0 then the socket should @@ -343,8 +371,9 @@ void tcp_v4_err(struct sk_buff *icmp_skb, u32 info) int err; struct net *net = dev_net(icmp_skb->dev); - sk = inet_lookup(net, &tcp_hashinfo, iph->daddr, th->dest, - iph->saddr, th->source, inet_iif(icmp_skb)); + sk = __inet_lookup_established(net, &tcp_hashinfo, iph->daddr, + th->dest, iph->saddr, ntohs(th->source), + inet_iif(icmp_skb)); if (!sk) { ICMP_INC_STATS_BH(net, ICMP_MIB_INERRORS); return; @@ -353,6 +382,9 @@ void tcp_v4_err(struct sk_buff *icmp_skb, u32 info) inet_twsk_put(inet_twsk(sk)); return; } + seq = ntohl(th->seq); + if (sk->sk_state == TCP_NEW_SYN_RECV) + return tcp_req_err(sk, seq); bh_lock_sock(sk); /* If too many ICMPs get dropped on busy @@ -374,7 +406,6 @@ void tcp_v4_err(struct sk_buff *icmp_skb, u32 info) icsk = inet_csk(sk); tp = tcp_sk(sk); - seq = ntohl(th->seq); /* XXX (TFO) - tp->snd_una should be ISN (tcp_create_openreq_child() */ fastopen = tp->fastopen_rsk; snd_una = fastopen ? tcp_rsk(fastopen)->snt_isn : tp->snd_una; @@ -458,38 +489,6 @@ void tcp_v4_err(struct sk_buff *icmp_skb, u32 info) } switch (sk->sk_state) { - struct request_sock *req; - case TCP_LISTEN: - if (sock_owned_by_user(sk)) - goto out; - - req = inet_csk_search_req(sk, th->dest, - iph->daddr, iph->saddr); - if (!req) - goto out; - - /* ICMPs are not backlogged, hence we cannot get - an established socket here. - */ - WARN_ON(req->sk); - - if (seq != tcp_rsk(req)->snt_isn) { - NET_INC_STATS_BH(net, LINUX_MIB_OUTOFWINDOWICMPS); - reqsk_put(req); - goto out; - } - - /* - * Still in SYN_RECV, just remove it silently. - * There is no good way to pass the error to the newly - * created socket, and POSIX does not want network - * errors returned from accept(). - */ - inet_csk_reqsk_queue_drop(sk, req); - NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_LISTENDROPS); - reqsk_put(req); - goto out; - case TCP_SYN_SENT: case TCP_SYN_RECV: /* Only in fast or simultaneous open. If a fast open socket is -- cgit v1.2.3 From 85645bab57bfc6b0b43bb96a301c4ef83925c07d Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Sun, 22 Mar 2015 10:22:24 -0700 Subject: ipv4: dccp: handle ICMP messages on DCCP_NEW_SYN_RECV request sockets dccp_v4_err() can restrict lookups to ehash table, and not to listeners. Note this patch creates the infrastructure, but this means that ICMP messages for request sockets are ignored until complete conversion. New dccp_req_err() helper is exported so that we can use it in IPv6 in following patch. Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- include/linux/dccp.h | 2 ++ net/dccp/dccp.h | 1 + net/dccp/ipv4.c | 70 +++++++++++++++++++++++++--------------------------- 3 files changed, 37 insertions(+), 36 deletions(-) (limited to 'include') diff --git a/include/linux/dccp.h b/include/linux/dccp.h index 3dca24d3ac67..221025423e6c 100644 --- a/include/linux/dccp.h +++ b/include/linux/dccp.h @@ -43,6 +43,7 @@ enum dccp_state { DCCP_CLOSING = TCP_CLOSING, DCCP_TIME_WAIT = TCP_TIME_WAIT, DCCP_CLOSED = TCP_CLOSE, + DCCP_NEW_SYN_RECV = TCP_NEW_SYN_RECV, DCCP_PARTOPEN = TCP_MAX_STATES, DCCP_PASSIVE_CLOSEREQ, /* clients receiving CloseReq */ DCCP_MAX_STATES @@ -57,6 +58,7 @@ enum { DCCPF_CLOSING = TCPF_CLOSING, DCCPF_TIME_WAIT = TCPF_TIME_WAIT, DCCPF_CLOSED = TCPF_CLOSE, + DCCPF_NEW_SYN_RECV = TCPF_NEW_SYN_RECV, DCCPF_PARTOPEN = (1 << DCCP_PARTOPEN), }; diff --git a/net/dccp/dccp.h b/net/dccp/dccp.h index 2396f50c5b04..bebc735f5afc 100644 --- a/net/dccp/dccp.h +++ b/net/dccp/dccp.h @@ -317,6 +317,7 @@ int inet_dccp_listen(struct socket *sock, int backlog); unsigned int dccp_poll(struct file *file, struct socket *sock, poll_table *wait); int dccp_v4_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len); +void dccp_req_err(struct sock *sk, u64 seq); struct sk_buff *dccp_ctl_make_reset(struct sock *sk, struct sk_buff *skb); int dccp_send_reset(struct sock *sk, enum dccp_reset_codes code); diff --git a/net/dccp/ipv4.c b/net/dccp/ipv4.c index 1f7161e05403..6310b8b19598 100644 --- a/net/dccp/ipv4.c +++ b/net/dccp/ipv4.c @@ -195,6 +195,32 @@ static void dccp_do_redirect(struct sk_buff *skb, struct sock *sk) dst->ops->redirect(dst, sk, skb); } +void dccp_req_err(struct sock *sk, u64 seq) + { + struct request_sock *req = inet_reqsk(sk); + struct net *net = sock_net(sk); + + /* + * ICMPs are not backlogged, hence we cannot get an established + * socket here. + */ + WARN_ON(req->sk); + + if (!between48(seq, dccp_rsk(req)->dreq_iss, dccp_rsk(req)->dreq_gss)) { + NET_INC_STATS_BH(net, LINUX_MIB_OUTOFWINDOWICMPS); + } else { + /* + * Still in RESPOND, just remove it silently. + * There is no good way to pass the error to the newly + * created socket, and POSIX does not want network + * errors returned from accept(). + */ + inet_csk_reqsk_queue_drop(req->rsk_listener, req); + } + reqsk_put(req); +} +EXPORT_SYMBOL(dccp_req_err); + /* * This routine is called by the ICMP module when it gets some sort of error * condition. If err < 0 then the socket should be closed and the error @@ -227,10 +253,11 @@ static void dccp_v4_err(struct sk_buff *skb, u32 info) return; } - sk = inet_lookup(net, &dccp_hashinfo, - iph->daddr, dh->dccph_dport, - iph->saddr, dh->dccph_sport, inet_iif(skb)); - if (sk == NULL) { + sk = __inet_lookup_established(net, &dccp_hashinfo, + iph->daddr, dh->dccph_dport, + iph->saddr, ntohs(dh->dccph_sport), + inet_iif(skb)); + if (!sk) { ICMP_INC_STATS_BH(net, ICMP_MIB_INERRORS); return; } @@ -239,6 +266,9 @@ static void dccp_v4_err(struct sk_buff *skb, u32 info) inet_twsk_put(inet_twsk(sk)); return; } + seq = dccp_hdr_seq(dh); + if (sk->sk_state == DCCP_NEW_SYN_RECV) + return dccp_req_err(sk, seq); bh_lock_sock(sk); /* If too many ICMPs get dropped on busy @@ -251,7 +281,6 @@ static void dccp_v4_err(struct sk_buff *skb, u32 info) goto out; dp = dccp_sk(sk); - seq = dccp_hdr_seq(dh); if ((1 << sk->sk_state) & ~(DCCPF_REQUESTING | DCCPF_LISTEN) && !between48(seq, dp->dccps_awl, dp->dccps_awh)) { NET_INC_STATS_BH(net, LINUX_MIB_OUTOFWINDOWICMPS); @@ -288,37 +317,6 @@ static void dccp_v4_err(struct sk_buff *skb, u32 info) } switch (sk->sk_state) { - struct request_sock *req; - case DCCP_LISTEN: - if (sock_owned_by_user(sk)) - goto out; - req = inet_csk_search_req(sk, dh->dccph_dport, - iph->daddr, iph->saddr); - if (!req) - goto out; - - /* - * ICMPs are not backlogged, hence we cannot get an established - * socket here. - */ - WARN_ON(req->sk); - - if (!between48(seq, dccp_rsk(req)->dreq_iss, - dccp_rsk(req)->dreq_gss)) { - NET_INC_STATS_BH(net, LINUX_MIB_OUTOFWINDOWICMPS); - reqsk_put(req); - goto out; - } - /* - * Still in RESPOND, just remove it silently. - * There is no good way to pass the error to the newly - * created socket, and POSIX does not want network - * errors returned from accept(). - */ - inet_csk_reqsk_queue_drop(sk, req); - reqsk_put(req); - goto out; - case DCCP_REQUESTING: case DCCP_RESPOND: if (!sock_owned_by_user(sk)) { -- cgit v1.2.3 From a5408ec6057e40d56b1c04608f07d21c5163ced0 Mon Sep 17 00:00:00 2001 From: Georgi Djakov Date: Wed, 18 Mar 2015 16:08:21 +0200 Subject: dt-bindings: Add #defines for MSM8916 clocks and resets Add clocks/resets defines for the global clock controller found on Qualcomm MSM8916 SoCs. Signed-off-by: Georgi Djakov Signed-off-by: Stephen Boyd --- .../devicetree/bindings/clock/qcom,gcc.txt | 1 + include/dt-bindings/clock/qcom,gcc-msm8916.h | 156 +++++++++++++++++++++ include/dt-bindings/reset/qcom,gcc-msm8916.h | 108 ++++++++++++++ 3 files changed, 265 insertions(+) create mode 100644 include/dt-bindings/clock/qcom,gcc-msm8916.h create mode 100644 include/dt-bindings/reset/qcom,gcc-msm8916.h (limited to 'include') diff --git a/Documentation/devicetree/bindings/clock/qcom,gcc.txt b/Documentation/devicetree/bindings/clock/qcom,gcc.txt index aba3d254e037..54c23f34f194 100644 --- a/Documentation/devicetree/bindings/clock/qcom,gcc.txt +++ b/Documentation/devicetree/bindings/clock/qcom,gcc.txt @@ -8,6 +8,7 @@ Required properties : "qcom,gcc-apq8084" "qcom,gcc-ipq8064" "qcom,gcc-msm8660" + "qcom,gcc-msm8916" "qcom,gcc-msm8960" "qcom,gcc-msm8974" "qcom,gcc-msm8974pro" diff --git a/include/dt-bindings/clock/qcom,gcc-msm8916.h b/include/dt-bindings/clock/qcom,gcc-msm8916.h new file mode 100644 index 000000000000..e430f644dd6c --- /dev/null +++ b/include/dt-bindings/clock/qcom,gcc-msm8916.h @@ -0,0 +1,156 @@ +/* + * Copyright 2015 Linaro Limited + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef _DT_BINDINGS_CLK_MSM_GCC_8916_H +#define _DT_BINDINGS_CLK_MSM_GCC_8916_H + +#define GPLL0 0 +#define GPLL0_VOTE 1 +#define BIMC_PLL 2 +#define BIMC_PLL_VOTE 3 +#define GPLL1 4 +#define GPLL1_VOTE 5 +#define GPLL2 6 +#define GPLL2_VOTE 7 +#define PCNOC_BFDCD_CLK_SRC 8 +#define SYSTEM_NOC_BFDCD_CLK_SRC 9 +#define CAMSS_AHB_CLK_SRC 10 +#define APSS_AHB_CLK_SRC 11 +#define CSI0_CLK_SRC 12 +#define CSI1_CLK_SRC 13 +#define GFX3D_CLK_SRC 14 +#define VFE0_CLK_SRC 15 +#define BLSP1_QUP1_I2C_APPS_CLK_SRC 16 +#define BLSP1_QUP1_SPI_APPS_CLK_SRC 17 +#define BLSP1_QUP2_I2C_APPS_CLK_SRC 18 +#define BLSP1_QUP2_SPI_APPS_CLK_SRC 19 +#define BLSP1_QUP3_I2C_APPS_CLK_SRC 20 +#define BLSP1_QUP3_SPI_APPS_CLK_SRC 21 +#define BLSP1_QUP4_I2C_APPS_CLK_SRC 22 +#define BLSP1_QUP4_SPI_APPS_CLK_SRC 23 +#define BLSP1_QUP5_I2C_APPS_CLK_SRC 24 +#define BLSP1_QUP5_SPI_APPS_CLK_SRC 25 +#define BLSP1_QUP6_I2C_APPS_CLK_SRC 26 +#define BLSP1_QUP6_SPI_APPS_CLK_SRC 27 +#define BLSP1_UART1_APPS_CLK_SRC 28 +#define BLSP1_UART2_APPS_CLK_SRC 29 +#define CCI_CLK_SRC 30 +#define CAMSS_GP0_CLK_SRC 31 +#define CAMSS_GP1_CLK_SRC 32 +#define JPEG0_CLK_SRC 33 +#define MCLK0_CLK_SRC 34 +#define MCLK1_CLK_SRC 35 +#define CSI0PHYTIMER_CLK_SRC 36 +#define CSI1PHYTIMER_CLK_SRC 37 +#define CPP_CLK_SRC 38 +#define CRYPTO_CLK_SRC 39 +#define GP1_CLK_SRC 40 +#define GP2_CLK_SRC 41 +#define GP3_CLK_SRC 42 +#define BYTE0_CLK_SRC 43 +#define ESC0_CLK_SRC 44 +#define MDP_CLK_SRC 45 +#define PCLK0_CLK_SRC 46 +#define VSYNC_CLK_SRC 47 +#define PDM2_CLK_SRC 48 +#define SDCC1_APPS_CLK_SRC 49 +#define SDCC2_APPS_CLK_SRC 50 +#define APSS_TCU_CLK_SRC 51 +#define USB_HS_SYSTEM_CLK_SRC 52 +#define VCODEC0_CLK_SRC 53 +#define GCC_BLSP1_AHB_CLK 54 +#define GCC_BLSP1_SLEEP_CLK 55 +#define GCC_BLSP1_QUP1_I2C_APPS_CLK 56 +#define GCC_BLSP1_QUP1_SPI_APPS_CLK 57 +#define GCC_BLSP1_QUP2_I2C_APPS_CLK 58 +#define GCC_BLSP1_QUP2_SPI_APPS_CLK 59 +#define GCC_BLSP1_QUP3_I2C_APPS_CLK 60 +#define GCC_BLSP1_QUP3_SPI_APPS_CLK 61 +#define GCC_BLSP1_QUP4_I2C_APPS_CLK 62 +#define GCC_BLSP1_QUP4_SPI_APPS_CLK 63 +#define GCC_BLSP1_QUP5_I2C_APPS_CLK 64 +#define GCC_BLSP1_QUP5_SPI_APPS_CLK 65 +#define GCC_BLSP1_QUP6_I2C_APPS_CLK 66 +#define GCC_BLSP1_QUP6_SPI_APPS_CLK 67 +#define GCC_BLSP1_UART1_APPS_CLK 68 +#define GCC_BLSP1_UART2_APPS_CLK 69 +#define GCC_BOOT_ROM_AHB_CLK 70 +#define GCC_CAMSS_CCI_AHB_CLK 71 +#define GCC_CAMSS_CCI_CLK 72 +#define GCC_CAMSS_CSI0_AHB_CLK 73 +#define GCC_CAMSS_CSI0_CLK 74 +#define GCC_CAMSS_CSI0PHY_CLK 75 +#define GCC_CAMSS_CSI0PIX_CLK 76 +#define GCC_CAMSS_CSI0RDI_CLK 77 +#define GCC_CAMSS_CSI1_AHB_CLK 78 +#define GCC_CAMSS_CSI1_CLK 79 +#define GCC_CAMSS_CSI1PHY_CLK 80 +#define GCC_CAMSS_CSI1PIX_CLK 81 +#define GCC_CAMSS_CSI1RDI_CLK 82 +#define GCC_CAMSS_CSI_VFE0_CLK 83 +#define GCC_CAMSS_GP0_CLK 84 +#define GCC_CAMSS_GP1_CLK 85 +#define GCC_CAMSS_ISPIF_AHB_CLK 86 +#define GCC_CAMSS_JPEG0_CLK 87 +#define GCC_CAMSS_JPEG_AHB_CLK 88 +#define GCC_CAMSS_JPEG_AXI_CLK 89 +#define GCC_CAMSS_MCLK0_CLK 90 +#define GCC_CAMSS_MCLK1_CLK 91 +#define GCC_CAMSS_MICRO_AHB_CLK 92 +#define GCC_CAMSS_CSI0PHYTIMER_CLK 93 +#define GCC_CAMSS_CSI1PHYTIMER_CLK 94 +#define GCC_CAMSS_AHB_CLK 95 +#define GCC_CAMSS_TOP_AHB_CLK 96 +#define GCC_CAMSS_CPP_AHB_CLK 97 +#define GCC_CAMSS_CPP_CLK 98 +#define GCC_CAMSS_VFE0_CLK 99 +#define GCC_CAMSS_VFE_AHB_CLK 100 +#define GCC_CAMSS_VFE_AXI_CLK 101 +#define GCC_CRYPTO_AHB_CLK 102 +#define GCC_CRYPTO_AXI_CLK 103 +#define GCC_CRYPTO_CLK 104 +#define GCC_OXILI_GMEM_CLK 105 +#define GCC_GP1_CLK 106 +#define GCC_GP2_CLK 107 +#define GCC_GP3_CLK 108 +#define GCC_MDSS_AHB_CLK 109 +#define GCC_MDSS_AXI_CLK 110 +#define GCC_MDSS_BYTE0_CLK 111 +#define GCC_MDSS_ESC0_CLK 112 +#define GCC_MDSS_MDP_CLK 113 +#define GCC_MDSS_PCLK0_CLK 114 +#define GCC_MDSS_VSYNC_CLK 115 +#define GCC_MSS_CFG_AHB_CLK 116 +#define GCC_OXILI_AHB_CLK 117 +#define GCC_OXILI_GFX3D_CLK 118 +#define GCC_PDM2_CLK 119 +#define GCC_PDM_AHB_CLK 120 +#define GCC_PRNG_AHB_CLK 121 +#define GCC_SDCC1_AHB_CLK 122 +#define GCC_SDCC1_APPS_CLK 123 +#define GCC_SDCC2_AHB_CLK 124 +#define GCC_SDCC2_APPS_CLK 125 +#define GCC_GTCU_AHB_CLK 126 +#define GCC_JPEG_TBU_CLK 127 +#define GCC_MDP_TBU_CLK 128 +#define GCC_SMMU_CFG_CLK 129 +#define GCC_VENUS_TBU_CLK 130 +#define GCC_VFE_TBU_CLK 131 +#define GCC_USB2A_PHY_SLEEP_CLK 132 +#define GCC_USB_HS_AHB_CLK 133 +#define GCC_USB_HS_SYSTEM_CLK 134 +#define GCC_VENUS0_AHB_CLK 135 +#define GCC_VENUS0_AXI_CLK 136 +#define GCC_VENUS0_VCODEC0_CLK 137 + +#endif diff --git a/include/dt-bindings/reset/qcom,gcc-msm8916.h b/include/dt-bindings/reset/qcom,gcc-msm8916.h new file mode 100644 index 000000000000..3d90410f09c7 --- /dev/null +++ b/include/dt-bindings/reset/qcom,gcc-msm8916.h @@ -0,0 +1,108 @@ +/* + * Copyright 2015 Linaro Limited + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef _DT_BINDINGS_RESET_MSM_GCC_8916_H +#define _DT_BINDINGS_RESET_MSM_GCC_8916_H + +#define GCC_BLSP1_BCR 0 +#define GCC_BLSP1_QUP1_BCR 1 +#define GCC_BLSP1_UART1_BCR 2 +#define GCC_BLSP1_QUP2_BCR 3 +#define GCC_BLSP1_UART2_BCR 4 +#define GCC_BLSP1_QUP3_BCR 5 +#define GCC_BLSP1_QUP4_BCR 6 +#define GCC_BLSP1_QUP5_BCR 7 +#define GCC_BLSP1_QUP6_BCR 8 +#define GCC_IMEM_BCR 9 +#define GCC_SMMU_BCR 10 +#define GCC_APSS_TCU_BCR 11 +#define GCC_SMMU_XPU_BCR 12 +#define GCC_PCNOC_TBU_BCR 13 +#define GCC_PRNG_BCR 14 +#define GCC_BOOT_ROM_BCR 15 +#define GCC_CRYPTO_BCR 16 +#define GCC_SEC_CTRL_BCR 17 +#define GCC_AUDIO_CORE_BCR 18 +#define GCC_ULT_AUDIO_BCR 19 +#define GCC_DEHR_BCR 20 +#define GCC_SYSTEM_NOC_BCR 21 +#define GCC_PCNOC_BCR 22 +#define GCC_TCSR_BCR 23 +#define GCC_QDSS_BCR 24 +#define GCC_DCD_BCR 25 +#define GCC_MSG_RAM_BCR 26 +#define GCC_MPM_BCR 27 +#define GCC_SPMI_BCR 28 +#define GCC_SPDM_BCR 29 +#define GCC_MM_SPDM_BCR 30 +#define GCC_BIMC_BCR 31 +#define GCC_RBCPR_BCR 32 +#define GCC_TLMM_BCR 33 +#define GCC_USB_HS_BCR 34 +#define GCC_USB2A_PHY_BCR 35 +#define GCC_SDCC1_BCR 36 +#define GCC_SDCC2_BCR 37 +#define GCC_PDM_BCR 38 +#define GCC_SNOC_BUS_TIMEOUT0_BCR 39 +#define GCC_PCNOC_BUS_TIMEOUT0_BCR 40 +#define GCC_PCNOC_BUS_TIMEOUT1_BCR 41 +#define GCC_PCNOC_BUS_TIMEOUT2_BCR 42 +#define GCC_PCNOC_BUS_TIMEOUT3_BCR 43 +#define GCC_PCNOC_BUS_TIMEOUT4_BCR 44 +#define GCC_PCNOC_BUS_TIMEOUT5_BCR 45 +#define GCC_PCNOC_BUS_TIMEOUT6_BCR 46 +#define GCC_PCNOC_BUS_TIMEOUT7_BCR 47 +#define GCC_PCNOC_BUS_TIMEOUT8_BCR 48 +#define GCC_PCNOC_BUS_TIMEOUT9_BCR 49 +#define GCC_MMSS_BCR 50 +#define GCC_VENUS0_BCR 51 +#define GCC_MDSS_BCR 52 +#define GCC_CAMSS_PHY0_BCR 53 +#define GCC_CAMSS_CSI0_BCR 54 +#define GCC_CAMSS_CSI0PHY_BCR 55 +#define GCC_CAMSS_CSI0RDI_BCR 56 +#define GCC_CAMSS_CSI0PIX_BCR 57 +#define GCC_CAMSS_PHY1_BCR 58 +#define GCC_CAMSS_CSI1_BCR 59 +#define GCC_CAMSS_CSI1PHY_BCR 60 +#define GCC_CAMSS_CSI1RDI_BCR 61 +#define GCC_CAMSS_CSI1PIX_BCR 62 +#define GCC_CAMSS_ISPIF_BCR 63 +#define GCC_CAMSS_CCI_BCR 64 +#define GCC_CAMSS_MCLK0_BCR 65 +#define GCC_CAMSS_MCLK1_BCR 66 +#define GCC_CAMSS_GP0_BCR 67 +#define GCC_CAMSS_GP1_BCR 68 +#define GCC_CAMSS_TOP_BCR 69 +#define GCC_CAMSS_MICRO_BCR 70 +#define GCC_CAMSS_JPEG_BCR 71 +#define GCC_CAMSS_VFE_BCR 72 +#define GCC_CAMSS_CSI_VFE0_BCR 73 +#define GCC_OXILI_BCR 74 +#define GCC_GMEM_BCR 75 +#define GCC_CAMSS_AHB_BCR 76 +#define GCC_MDP_TBU_BCR 77 +#define GCC_GFX_TBU_BCR 78 +#define GCC_GFX_TCU_BCR 79 +#define GCC_MSS_TBU_AXI_BCR 80 +#define GCC_MSS_TBU_GSS_AXI_BCR 81 +#define GCC_MSS_TBU_Q6_AXI_BCR 82 +#define GCC_GTCU_AHB_BCR 83 +#define GCC_SMMU_CFG_BCR 84 +#define GCC_VFE_TBU_BCR 85 +#define GCC_VENUS_TBU_BCR 86 +#define GCC_JPEG_TBU_BCR 87 +#define GCC_PRONTO_TBU_BCR 88 +#define GCC_SMMU_CATS_BCR 89 + +#endif -- cgit v1.2.3 From 841a6664f213f76a9bc1bfd07a466d3dbe281a88 Mon Sep 17 00:00:00 2001 From: Arman Uguray Date: Mon, 23 Mar 2015 15:57:09 -0700 Subject: Bluetooth: Add definitions for Add/Remove Advertising API This patch adds definitions for the Add Advertising and Remove Advertising MGMT commands and events. Signed-off-by: Arman Uguray Signed-off-by: Marcel Holtmann --- include/net/bluetooth/mgmt.h | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) (limited to 'include') diff --git a/include/net/bluetooth/mgmt.h b/include/net/bluetooth/mgmt.h index a1a68671bf88..68abd4b0c25d 100644 --- a/include/net/bluetooth/mgmt.h +++ b/include/net/bluetooth/mgmt.h @@ -539,6 +539,30 @@ struct mgmt_rp_read_adv_features { __u8 instance[0]; } __packed; +#define MGMT_OP_ADD_ADVERTISING 0x003E +struct mgmt_cp_add_advertising { + __u8 instance; + __le32 flags; + __le16 duration; + __le16 timeout; + __u8 adv_data_len; + __u8 scan_rsp_len; + __u8 data[0]; +} __packed; +#define MGMT_ADD_ADVERTISING_SIZE 11 +struct mgmt_rp_add_advertising { + __u8 instance; +} __packed; + +#define MGMT_OP_REMOVE_ADVERTISING 0x003F +struct mgmt_cp_remove_advertising { + __u8 instance; +} __packed; +#define MGMT_REMOVE_ADVERTISING_SIZE 1 +struct mgmt_rp_remove_advertising { + __u8 instance; +} __packed; + #define MGMT_EV_CMD_COMPLETE 0x0001 struct mgmt_ev_cmd_complete { __le16 opcode; @@ -742,3 +766,13 @@ struct mgmt_ev_local_oob_data_updated { __le16 eir_len; __u8 eir[0]; } __packed; + +#define MGMT_EV_ADVERTISING_ADDED 0x0023 +struct mgmt_ev_advertising_added { + __u8 instance; +} __packed; + +#define MGMT_EV_ADVERTISING_REMOVED 0x0024 +struct mgmt_ev_advertising_removed { + __u8 instance; +} __packed; -- cgit v1.2.3 From 4453b006538d02ada8294a195bb2dc2ada498436 Mon Sep 17 00:00:00 2001 From: Arman Uguray Date: Mon, 23 Mar 2015 15:57:10 -0700 Subject: Bluetooth: Introduce HCI_ADVERTISING_INSTANCE setting and add AD flags This patch introduces the HCI_ADVERTISING_INSTANCE setting, which is set when an at least one advertising instance has been added using the "Add Advertising" mgmt command. This patch also adds a macro definition for the EIR_APPEARANCE field type. Signed-off-by: Arman Uguray Signed-off-by: Marcel Holtmann --- include/net/bluetooth/hci.h | 2 ++ 1 file changed, 2 insertions(+) (limited to 'include') diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index 06e7eee31ce4..3acecf35420b 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -227,6 +227,7 @@ enum { HCI_LE_ENABLED, HCI_ADVERTISING, HCI_ADVERTISING_CONNECTABLE, + HCI_ADVERTISING_INSTANCE, HCI_CONNECTABLE, HCI_DISCOVERABLE, HCI_LIMITED_DISCOVERABLE, @@ -465,6 +466,7 @@ enum { #define EIR_SSP_HASH_C 0x0E /* Simple Pairing Hash C */ #define EIR_SSP_RAND_R 0x0F /* Simple Pairing Randomizer R */ #define EIR_DEVICE_ID 0x10 /* device ID */ +#define EIR_APPEARANCE 0x19 /* Device appearance */ #define EIR_LE_BDADDR 0x1B /* LE Bluetooth device address */ #define EIR_LE_ROLE 0x1C /* LE role */ #define EIR_LE_SC_CONFIRM 0x22 /* LE SC Confirmation Value */ -- cgit v1.2.3 From 203fea0178d7e165dbe834d1bdd9d243018fd5bf Mon Sep 17 00:00:00 2001 From: Arman Uguray Date: Mon, 23 Mar 2015 15:57:11 -0700 Subject: Bluetooth: Add data structure for advertising instance This patch introduces a new data structure to represent advertising instances that were added using the "Add Advertising" mgmt command. Initially an hci_dev structure will support only one of these instances at a time, so the current instance is simply stored as a direct member of hci_dev. Signed-off-by: Arman Uguray Signed-off-by: Marcel Holtmann --- include/net/bluetooth/hci_core.h | 16 ++++++++++++++++ net/bluetooth/hci_core.c | 1 + 2 files changed, 17 insertions(+) (limited to 'include') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index b65c53de6a69..3a6d4e3d68fe 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -155,6 +155,15 @@ struct oob_data { u8 rand256[16]; }; +struct adv_info { + __u8 instance; + __u32 flags; + __u16 adv_data_len; + __u8 adv_data[HCI_MAX_AD_LENGTH]; + __u16 scan_rsp_len; + __u8 scan_rsp_data[HCI_MAX_AD_LENGTH]; +}; + #define HCI_MAX_SHORT_NAME_LENGTH 10 /* Default LE RPA expiry time, 15 minutes */ @@ -364,6 +373,8 @@ struct hci_dev { __u8 scan_rsp_data[HCI_MAX_AD_LENGTH]; __u8 scan_rsp_data_len; + struct adv_info adv_instance; + __u8 irk[16]; __u32 rpa_timeout; struct delayed_work rpa_expired; @@ -550,6 +561,11 @@ static inline void hci_discovery_filter_clear(struct hci_dev *hdev) hdev->discovery.scan_duration = 0; } +static inline void adv_info_init(struct hci_dev *hdev) +{ + memset(&hdev->adv_instance, 0, sizeof(struct adv_info)); +} + bool hci_discovery_active(struct hci_dev *hdev); void hci_discovery_set_state(struct hci_dev *hdev, int state); diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 17f52a195ba8..e6bfeb7b4415 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -3126,6 +3126,7 @@ struct hci_dev *hci_alloc_dev(void) hci_init_sysfs(hdev); discovery_init(hdev); + adv_info_init(hdev); return hdev; } -- cgit v1.2.3 From 912098a6308e37208b8dcc46c57c66d0778a854b Mon Sep 17 00:00:00 2001 From: Arman Uguray Date: Mon, 23 Mar 2015 15:57:15 -0700 Subject: Bluetooth: Add support for adv instance timeout This patch implements support for the timeout parameter of the Add Advertising command. Signed-off-by: Arman Uguray Signed-off-by: Marcel Holtmann --- include/net/bluetooth/hci_core.h | 2 + net/bluetooth/mgmt.c | 101 +++++++++++++++++++++++++++++++-------- 2 files changed, 83 insertions(+), 20 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 3a6d4e3d68fe..540c07feece7 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -156,8 +156,10 @@ struct oob_data { }; struct adv_info { + struct delayed_work timeout_exp; __u8 instance; __u32 flags; + __u16 timeout; __u16 adv_data_len; __u8 adv_data[HCI_MAX_AD_LENGTH]; __u16 scan_rsp_len; diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 762ca9be9806..eda52397a648 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -1336,6 +1336,49 @@ static bool hci_stop_discovery(struct hci_request *req) return false; } +static void advertising_added(struct sock *sk, struct hci_dev *hdev, + u8 instance) +{ + struct mgmt_ev_advertising_added ev; + + ev.instance = instance; + + mgmt_event(MGMT_EV_ADVERTISING_ADDED, hdev, &ev, sizeof(ev), sk); +} + +static void advertising_removed(struct sock *sk, struct hci_dev *hdev, + u8 instance) +{ + struct mgmt_ev_advertising_removed ev; + + ev.instance = instance; + + mgmt_event(MGMT_EV_ADVERTISING_REMOVED, hdev, &ev, sizeof(ev), sk); +} + +static void clear_adv_instance(struct hci_dev *hdev) +{ + struct hci_request req; + + if (!hci_dev_test_flag(hdev, HCI_ADVERTISING_INSTANCE)) + return; + + if (hdev->adv_instance.timeout) + cancel_delayed_work(&hdev->adv_instance.timeout_exp); + + memset(&hdev->adv_instance, 0, sizeof(hdev->adv_instance)); + advertising_removed(NULL, hdev, 1); + hci_dev_clear_flag(hdev, HCI_ADVERTISING_INSTANCE); + + if (!hdev_is_powered(hdev) || + hci_dev_test_flag(hdev, HCI_ADVERTISING)) + return; + + hci_req_init(&req, hdev); + disable_advertising(&req); + hci_req_run(&req, NULL); +} + static int clean_up_hci_state(struct hci_dev *hdev) { struct hci_request req; @@ -1351,6 +1394,9 @@ static int clean_up_hci_state(struct hci_dev *hdev) hci_req_add(&req, HCI_OP_WRITE_SCAN_ENABLE, 1, &scan); } + if (hdev->adv_instance.timeout) + clear_adv_instance(hdev); + if (hci_dev_test_flag(hdev, HCI_LE_ADV)) disable_advertising(&req); @@ -6468,26 +6514,6 @@ static bool tlv_data_is_valid(struct hci_dev *hdev, u32 adv_flags, u8 *data, return true; } -static void advertising_added(struct sock *sk, struct hci_dev *hdev, - u8 instance) -{ - struct mgmt_ev_advertising_added ev; - - ev.instance = instance; - - mgmt_event(MGMT_EV_ADVERTISING_ADDED, hdev, &ev, sizeof(ev), sk); -} - -static void advertising_removed(struct sock *sk, struct hci_dev *hdev, - u8 instance) -{ - struct mgmt_ev_advertising_removed ev; - - ev.instance = instance; - - mgmt_event(MGMT_EV_ADVERTISING_REMOVED, hdev, &ev, sizeof(ev), sk); -} - static void add_advertising_complete(struct hci_dev *hdev, u8 status, u16 opcode) { @@ -6524,6 +6550,18 @@ unlock: hci_dev_unlock(hdev); } +static void adv_timeout_expired(struct work_struct *work) +{ + struct hci_dev *hdev = container_of(work, struct hci_dev, + adv_instance.timeout_exp.work); + + hdev->adv_instance.timeout = 0; + + hci_dev_lock(hdev); + clear_adv_instance(hdev); + hci_dev_unlock(hdev); +} + static int add_advertising(struct sock *sk, struct hci_dev *hdev, void *data, u16 data_len) { @@ -6531,6 +6569,7 @@ static int add_advertising(struct sock *sk, struct hci_dev *hdev, struct mgmt_rp_add_advertising rp; u32 flags; u8 status; + u16 timeout; int err; struct mgmt_pending_cmd *cmd; struct hci_request req; @@ -6543,6 +6582,7 @@ static int add_advertising(struct sock *sk, struct hci_dev *hdev, status); flags = __le32_to_cpu(cp->flags); + timeout = __le16_to_cpu(cp->timeout); /* The current implementation only supports adding one instance and * doesn't support flags. @@ -6553,6 +6593,12 @@ static int add_advertising(struct sock *sk, struct hci_dev *hdev, hci_dev_lock(hdev); + if (timeout && !hdev_is_powered(hdev)) { + err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_ADD_ADVERTISING, + MGMT_STATUS_REJECTED); + goto unlock; + } + if (pending_find(MGMT_OP_ADD_ADVERTISING, hdev) || pending_find(MGMT_OP_REMOVE_ADVERTISING, hdev) || pending_find(MGMT_OP_SET_LE, hdev)) { @@ -6569,6 +6615,8 @@ static int add_advertising(struct sock *sk, struct hci_dev *hdev, goto unlock; } + INIT_DELAYED_WORK(&hdev->adv_instance.timeout_exp, adv_timeout_expired); + hdev->adv_instance.flags = flags; hdev->adv_instance.adv_data_len = cp->adv_data_len; hdev->adv_instance.scan_rsp_len = cp->scan_rsp_len; @@ -6580,6 +6628,16 @@ static int add_advertising(struct sock *sk, struct hci_dev *hdev, memcpy(hdev->adv_instance.scan_rsp_data, cp->data + cp->adv_data_len, cp->scan_rsp_len); + if (hdev->adv_instance.timeout) + cancel_delayed_work(&hdev->adv_instance.timeout_exp); + + hdev->adv_instance.timeout = timeout; + + if (timeout) + queue_delayed_work(hdev->workqueue, + &hdev->adv_instance.timeout_exp, + msecs_to_jiffies(timeout * 1000)); + if (!hci_dev_test_and_set_flag(hdev, HCI_ADVERTISING_INSTANCE)) advertising_added(sk, hdev, 1); @@ -6682,6 +6740,9 @@ static int remove_advertising(struct sock *sk, struct hci_dev *hdev, goto unlock; } + if (hdev->adv_instance.timeout) + cancel_delayed_work(&hdev->adv_instance.timeout_exp); + memset(&hdev->adv_instance, 0, sizeof(hdev->adv_instance)); advertising_removed(sk, hdev, 1); -- cgit v1.2.3 From 2f921b5bb0511fb698681d8ef35c48be7a9116bf Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Tue, 24 Mar 2015 11:51:39 +1030 Subject: lguest: suppress interrupts for single insn, not range. The last patch reduced our interrupt-suppression region to one address, so simplify the code somewhat. Also, remove the obsolete undefined instruction ranges and the comment which refers to lguest_guest.S instead of head_32.S. Signed-off-by: Rusty Russell --- arch/x86/include/asm/lguest.h | 7 ++----- arch/x86/lguest/boot.c | 3 +-- arch/x86/lguest/head_32.S | 15 ++++++--------- drivers/lguest/hypercalls.c | 5 ++--- drivers/lguest/interrupts_and_traps.c | 8 ++++---- drivers/lguest/lg.h | 2 +- include/linux/lguest.h | 4 ++-- 7 files changed, 18 insertions(+), 26 deletions(-) (limited to 'include') diff --git a/arch/x86/include/asm/lguest.h b/arch/x86/include/asm/lguest.h index e2d4a4afa8c3..3bbc07a57a31 100644 --- a/arch/x86/include/asm/lguest.h +++ b/arch/x86/include/asm/lguest.h @@ -20,13 +20,10 @@ extern unsigned long switcher_addr; /* Found in switcher.S */ extern unsigned long default_idt_entries[]; -/* Declarations for definitions in lguest_guest.S */ -extern char lguest_noirq_start[], lguest_noirq_end[]; +/* Declarations for definitions in arch/x86/lguest/head_32.S */ +extern char lguest_noirq_iret[]; extern const char lgstart_cli[], lgend_cli[]; -extern const char lgstart_sti[], lgend_sti[]; -extern const char lgstart_popf[], lgend_popf[]; extern const char lgstart_pushf[], lgend_pushf[]; -extern const char lgstart_iret[], lgend_iret[]; extern void lguest_iret(void); extern void lguest_init(void); diff --git a/arch/x86/lguest/boot.c b/arch/x86/lguest/boot.c index 543510a2f9e0..13616d708389 100644 --- a/arch/x86/lguest/boot.c +++ b/arch/x86/lguest/boot.c @@ -87,8 +87,7 @@ struct lguest_data lguest_data = { .hcall_status = { [0 ... LHCALL_RING_SIZE-1] = 0xFF }, - .noirq_start = (u32)lguest_noirq_start, - .noirq_end = (u32)lguest_noirq_end, + .noirq_iret = (u32)lguest_noirq_iret, .kernel_address = PAGE_OFFSET, .blocked_interrupts = { 1 }, /* Block timer interrupts */ .syscall_vec = SYSCALL_VECTOR, diff --git a/arch/x86/lguest/head_32.S b/arch/x86/lguest/head_32.S index 81678bf0fcb7..d5ae63f5ec5d 100644 --- a/arch/x86/lguest/head_32.S +++ b/arch/x86/lguest/head_32.S @@ -133,9 +133,8 @@ ENTRY(lg_restore_fl) ret /*:*/ -/* These demark the EIP range where host should never deliver interrupts. */ -.global lguest_noirq_start -.global lguest_noirq_end +/* These demark the EIP where host should never deliver interrupts. */ +.global lguest_noirq_iret /*M:004 * When the Host reflects a trap or injects an interrupt into the Guest, it @@ -174,12 +173,11 @@ ENTRY(lg_restore_fl) * * The second is harder: copying eflags to lguest_data.irq_enabled will turn * interrupts on before we're finished, so we could be interrupted before we - * return to userspace or wherever. Our solution to this is to surround the - * code with lguest_noirq_start: and lguest_noirq_end: labels. We tell the + * return to userspace or wherever. Our solution to this is to tell the * Host that it is *never* to interrupt us there, even if interrupts seem to be * enabled. (It's not necessary to protect pop instruction, since - * data gets updated only after it completes, so we end up surrounding - * just one instruction, iret). + * data gets updated only after it completes, so we only need to protect + * one instruction, iret). */ ENTRY(lguest_iret) pushl 2*4(%esp) @@ -190,6 +188,5 @@ ENTRY(lguest_iret) * prefix makes sure we use the stack segment, which is still valid. */ popl %ss:lguest_data+LGUEST_DATA_irq_enabled -lguest_noirq_start: +lguest_noirq_iret: iret -lguest_noirq_end: diff --git a/drivers/lguest/hypercalls.c b/drivers/lguest/hypercalls.c index 1219af493c0f..19a32280731d 100644 --- a/drivers/lguest/hypercalls.c +++ b/drivers/lguest/hypercalls.c @@ -211,10 +211,9 @@ static void initialize(struct lg_cpu *cpu) /* * The Guest tells us where we're not to deliver interrupts by putting - * the range of addresses into "struct lguest_data". + * the instruction address into "struct lguest_data". */ - if (get_user(cpu->lg->noirq_start, &cpu->lg->lguest_data->noirq_start) - || get_user(cpu->lg->noirq_end, &cpu->lg->lguest_data->noirq_end)) + if (get_user(cpu->lg->noirq_iret, &cpu->lg->lguest_data->noirq_iret)) kill_guest(cpu, "bad guest page %p", cpu->lg->lguest_data); /* diff --git a/drivers/lguest/interrupts_and_traps.c b/drivers/lguest/interrupts_and_traps.c index 70dfcdc29f1f..6d4c072b61e1 100644 --- a/drivers/lguest/interrupts_and_traps.c +++ b/drivers/lguest/interrupts_and_traps.c @@ -204,8 +204,7 @@ void try_deliver_interrupt(struct lg_cpu *cpu, unsigned int irq, bool more) * They may be in the middle of an iret, where they asked us never to * deliver interrupts. */ - if (cpu->regs->eip >= cpu->lg->noirq_start && - (cpu->regs->eip < cpu->lg->noirq_end)) + if (cpu->regs->eip == cpu->lg->noirq_iret) return; /* If they're halted, interrupts restart them. */ @@ -395,8 +394,9 @@ static bool direct_trap(unsigned int num) * The Guest has the ability to turn its interrupt gates into trap gates, * if it is careful. The Host will let trap gates can go directly to the * Guest, but the Guest needs the interrupts atomically disabled for an - * interrupt gate. It can do this by pointing the trap gate at instructions - * within noirq_start and noirq_end, where it can safely disable interrupts. + * interrupt gate. The Host could provide a mechanism to register more + * "no-interrupt" regions, and the Guest could point the trap gate at + * instructions within that region, where it can safely disable interrupts. */ /*M:006 diff --git a/drivers/lguest/lg.h b/drivers/lguest/lg.h index 307e8b39e7d1..ac8ad0461e80 100644 --- a/drivers/lguest/lg.h +++ b/drivers/lguest/lg.h @@ -102,7 +102,7 @@ struct lguest { struct pgdir pgdirs[4]; - unsigned long noirq_start, noirq_end; + unsigned long noirq_iret; unsigned int stack_pages; u32 tsc_khz; diff --git a/include/linux/lguest.h b/include/linux/lguest.h index 9962c6bb1311..6db19f35f7c5 100644 --- a/include/linux/lguest.h +++ b/include/linux/lguest.h @@ -61,8 +61,8 @@ struct lguest_data { u32 tsc_khz; /* Fields initialized by the Guest at boot: */ - /* Instruction range to suppress interrupts even if enabled */ - unsigned long noirq_start, noirq_end; + /* Instruction to suppress interrupts even if enabled */ + unsigned long noirq_iret; /* Address above which page tables are all identical. */ unsigned long kernel_address; /* The vector to try to use for system calls (0x40 or 0x80). */ -- cgit v1.2.3 From 682f048bd49449f4ab978664a7f69a44a74e3caa Mon Sep 17 00:00:00 2001 From: Alexander Drozdov Date: Mon, 23 Mar 2015 09:11:13 +0300 Subject: af_packet: pass checksum validation status to the user Introduce TP_STATUS_CSUM_VALID tp_status flag to tell the af_packet user that at least the transport header checksum has been already validated. For now, the flag may be set for incoming packets only. Signed-off-by: Alexander Drozdov Cc: Willem de Bruijn Signed-off-by: David S. Miller --- Documentation/networking/packet_mmap.txt | 13 ++++++++++--- include/uapi/linux/if_packet.h | 1 + net/packet/af_packet.c | 9 +++++++++ 3 files changed, 20 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/Documentation/networking/packet_mmap.txt b/Documentation/networking/packet_mmap.txt index a6d7cb91069e..daa015af16a0 100644 --- a/Documentation/networking/packet_mmap.txt +++ b/Documentation/networking/packet_mmap.txt @@ -440,9 +440,10 @@ and the following flags apply: +++ Capture process: from include/linux/if_packet.h - #define TP_STATUS_COPY 2 - #define TP_STATUS_LOSING 4 - #define TP_STATUS_CSUMNOTREADY 8 + #define TP_STATUS_COPY (1 << 1) + #define TP_STATUS_LOSING (1 << 2) + #define TP_STATUS_CSUMNOTREADY (1 << 3) + #define TP_STATUS_CSUM_VALID (1 << 7) TP_STATUS_COPY : This flag indicates that the frame (and associated meta information) has been truncated because it's @@ -466,6 +467,12 @@ TP_STATUS_CSUMNOTREADY: currently it's used for outgoing IP packets which reading the packet we should not try to check the checksum. +TP_STATUS_CSUM_VALID : This flag indicates that at least the transport + header checksum of the packet has been already + validated on the kernel side. If the flag is not set + then we are free to check the checksum by ourselves + provided that TP_STATUS_CSUMNOTREADY is also not set. + for convenience there are also the following defines: #define TP_STATUS_KERNEL 0 diff --git a/include/uapi/linux/if_packet.h b/include/uapi/linux/if_packet.h index da2d668b8cf1..053bd102fbe0 100644 --- a/include/uapi/linux/if_packet.h +++ b/include/uapi/linux/if_packet.h @@ -99,6 +99,7 @@ struct tpacket_auxdata { #define TP_STATUS_VLAN_VALID (1 << 4) /* auxdata has valid tp_vlan_tci */ #define TP_STATUS_BLK_TMO (1 << 5) #define TP_STATUS_VLAN_TPID_VALID (1 << 6) /* auxdata has valid tp_vlan_tpid */ +#define TP_STATUS_CSUM_VALID (1 << 7) /* Tx ring - header status */ #define TP_STATUS_AVAILABLE 0 diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c index 9d854c5ce0b5..5102c3cc4eec 100644 --- a/net/packet/af_packet.c +++ b/net/packet/af_packet.c @@ -1924,6 +1924,10 @@ static int tpacket_rcv(struct sk_buff *skb, struct net_device *dev, if (skb->ip_summed == CHECKSUM_PARTIAL) status |= TP_STATUS_CSUMNOTREADY; + else if (skb->pkt_type != PACKET_OUTGOING && + (skb->ip_summed == CHECKSUM_COMPLETE || + skb_csum_unnecessary(skb))) + status |= TP_STATUS_CSUM_VALID; if (snaplen > res) snaplen = res; @@ -3031,6 +3035,11 @@ static int packet_recvmsg(struct socket *sock, struct msghdr *msg, size_t len, aux.tp_status = TP_STATUS_USER; if (skb->ip_summed == CHECKSUM_PARTIAL) aux.tp_status |= TP_STATUS_CSUMNOTREADY; + else if (skb->pkt_type != PACKET_OUTGOING && + (skb->ip_summed == CHECKSUM_COMPLETE || + skb_csum_unnecessary(skb))) + aux.tp_status |= TP_STATUS_CSUM_VALID; + aux.tp_len = origlen; aux.tp_snaplen = skb->len; aux.tp_mac = 0; -- cgit v1.2.3 From de91b25c8011089f5dd99b9d24743db1f550ca4b Mon Sep 17 00:00:00 2001 From: Herbert Xu Date: Tue, 24 Mar 2015 00:50:20 +1100 Subject: rhashtable: Eliminate unnecessary branch in rht_key_hashfn When rht_key_hashfn is called from rhashtable itself and params is equal to ht->p, there is no point in checking params.key_len and falling back to ht->p.key_len. For some reason gcc couldn't figure out that params is the same as ht->p. So let's help it by only checking params.key_len when it's a constant. Signed-off-by: Herbert Xu Acked-by: Thomas Graf Signed-off-by: David S. Miller --- include/linux/rhashtable.h | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/linux/rhashtable.h b/include/linux/rhashtable.h index 89d102270570..3851952781d7 100644 --- a/include/linux/rhashtable.h +++ b/include/linux/rhashtable.h @@ -199,8 +199,12 @@ static inline unsigned int rht_key_hashfn( struct rhashtable *ht, const struct bucket_table *tbl, const void *key, const struct rhashtable_params params) { - return rht_bucket_index(tbl, params.hashfn(key, params.key_len ?: - ht->p.key_len, + /* params must be equal to ht->p if it isn't constant. */ + unsigned key_len = __builtin_constant_p(params.key_len) ? + (params.key_len ?: ht->p.key_len) : + params.key_len; + + return rht_bucket_index(tbl, params.hashfn(key, key_len, tbl->hash_rnd)); } -- cgit v1.2.3 From 31ccde2dacea8375c3a7d6fffbf0060ee0d40214 Mon Sep 17 00:00:00 2001 From: Herbert Xu Date: Tue, 24 Mar 2015 00:50:21 +1100 Subject: rhashtable: Allow hashfn to be unset Since every current rhashtable user uses jhash as their hash function, the fact that jhash is an inline function causes each user to generate a copy of its code. This function provides a solution to this problem by allowing hashfn to be unset. In which case rhashtable will automatically set it to jhash. Furthermore, if the key length is a multiple of 4, we will switch over to jhash2. Signed-off-by: Herbert Xu Acked-by: Thomas Graf Signed-off-by: David S. Miller --- include/linux/rhashtable.h | 33 +++++++++++++++++++++++++++------ lib/rhashtable.c | 17 ++++++++++++++++- 2 files changed, 43 insertions(+), 7 deletions(-) (limited to 'include') diff --git a/include/linux/rhashtable.h b/include/linux/rhashtable.h index 3851952781d7..bc2488b98321 100644 --- a/include/linux/rhashtable.h +++ b/include/linux/rhashtable.h @@ -19,6 +19,7 @@ #include #include +#include #include #include #include @@ -103,7 +104,7 @@ struct rhashtable; * @min_size: Minimum size while shrinking * @nulls_base: Base value to generate nulls marker * @locks_mul: Number of bucket locks to allocate per cpu (default: 128) - * @hashfn: Function to hash key + * @hashfn: Hash function (default: jhash2 if !(key_len % 4), or jhash) * @obj_hashfn: Function to hash object * @obj_cmpfn: Function to compare key with object */ @@ -125,6 +126,7 @@ struct rhashtable_params { * struct rhashtable - Hash table handle * @tbl: Bucket table * @nelems: Number of elements in table + * @key_len: Key length for hashfn * @p: Configuration parameters * @run_work: Deferred worker to expand/shrink asynchronously * @mutex: Mutex to protect current/future table swapping @@ -134,6 +136,7 @@ struct rhashtable { struct bucket_table __rcu *tbl; atomic_t nelems; bool being_destroyed; + unsigned int key_len; struct rhashtable_params p; struct work_struct run_work; struct mutex mutex; @@ -199,13 +202,31 @@ static inline unsigned int rht_key_hashfn( struct rhashtable *ht, const struct bucket_table *tbl, const void *key, const struct rhashtable_params params) { + unsigned hash; + /* params must be equal to ht->p if it isn't constant. */ - unsigned key_len = __builtin_constant_p(params.key_len) ? - (params.key_len ?: ht->p.key_len) : - params.key_len; + if (!__builtin_constant_p(params.key_len)) + hash = ht->p.hashfn(key, ht->key_len, tbl->hash_rnd); + else if (params.key_len) { + unsigned key_len = params.key_len; + + if (params.hashfn) + hash = params.hashfn(key, key_len, tbl->hash_rnd); + else if (key_len & (sizeof(u32) - 1)) + hash = jhash(key, key_len, tbl->hash_rnd); + else + hash = jhash2(key, key_len / sizeof(u32), + tbl->hash_rnd); + } else { + unsigned key_len = ht->p.key_len; + + if (params.hashfn) + hash = params.hashfn(key, key_len, tbl->hash_rnd); + else + hash = jhash(key, key_len, tbl->hash_rnd); + } - return rht_bucket_index(tbl, params.hashfn(key, key_len, - tbl->hash_rnd)); + return rht_bucket_index(tbl, hash); } static inline unsigned int rht_head_hashfn( diff --git a/lib/rhashtable.c b/lib/rhashtable.c index 618a3f00d712..798f01d64ab0 100644 --- a/lib/rhashtable.c +++ b/lib/rhashtable.c @@ -532,6 +532,11 @@ static size_t rounded_hashtable_size(const struct rhashtable_params *params) (unsigned long)params->min_size); } +static u32 rhashtable_jhash2(const void *key, u32 length, u32 seed) +{ + return jhash2(key, length, seed); +} + /** * rhashtable_init - initialize a new hash table * @ht: hash table to be initialized @@ -583,7 +588,7 @@ int rhashtable_init(struct rhashtable *ht, size = HASH_DEFAULT_SIZE; - if ((!(params->key_len && params->hashfn) && !params->obj_hashfn) || + if ((!params->key_len && !params->obj_hashfn) || (params->obj_hashfn && !params->obj_cmpfn)) return -EINVAL; @@ -610,6 +615,16 @@ int rhashtable_init(struct rhashtable *ht, else ht->p.locks_mul = BUCKET_LOCKS_PER_CPU; + ht->key_len = ht->p.key_len; + if (!params->hashfn) { + ht->p.hashfn = jhash; + + if (!(ht->key_len & (sizeof(u32) - 1))) { + ht->key_len /= sizeof(u32); + ht->p.hashfn = rhashtable_jhash2; + } + } + tbl = bucket_table_alloc(ht, size); if (tbl == NULL) return -ENOMEM; -- cgit v1.2.3 From b824478b2145be78ac19e1cf44e2b9036c7a9608 Mon Sep 17 00:00:00 2001 From: Herbert Xu Date: Tue, 24 Mar 2015 00:50:26 +1100 Subject: rhashtable: Add multiple rehash support This patch adds the missing bits to allow multiple rehashes. The read-side as well as remove already handle this correctly. So it's only the rehasher and insertion that need modification to handle this. Note that this patch doesn't actually enable it so for now rehashing is still only performed by the worker thread. This patch also disables the explicit expand/shrink interface because the table is meant to expand and shrink automatically, and continuing to export these interfaces unnecessarily complicates the life of the rehasher since the rehash process is now composed of two parts. Signed-off-by: Herbert Xu Acked-by: Thomas Graf Signed-off-by: David S. Miller --- include/linux/rhashtable.h | 26 +++++++------- lib/rhashtable.c | 87 ++++++++++++++++++++++++++++++++++++++-------- lib/test_rhashtable.c | 24 ------------- 3 files changed, 86 insertions(+), 51 deletions(-) (limited to 'include') diff --git a/include/linux/rhashtable.h b/include/linux/rhashtable.h index bc2488b98321..e8ffcdb5e239 100644 --- a/include/linux/rhashtable.h +++ b/include/linux/rhashtable.h @@ -308,9 +308,6 @@ int rhashtable_insert_slow(struct rhashtable *ht, const void *key, struct rhash_head *obj, struct bucket_table *old_tbl); -int rhashtable_expand(struct rhashtable *ht); -int rhashtable_shrink(struct rhashtable *ht); - int rhashtable_walk_init(struct rhashtable *ht, struct rhashtable_iter *iter); void rhashtable_walk_exit(struct rhashtable_iter *iter); int rhashtable_walk_start(struct rhashtable_iter *iter) __acquires(RCU); @@ -541,17 +538,22 @@ static inline int __rhashtable_insert_fast( rcu_read_lock(); tbl = rht_dereference_rcu(ht->tbl, ht); - hash = rht_head_hashfn(ht, tbl, obj, params); - lock = rht_bucket_lock(tbl, hash); - - spin_lock_bh(lock); - /* Because we have already taken the bucket lock in tbl, - * if we find that future_tbl is not yet visible then - * that guarantees all other insertions of the same entry - * will also grab the bucket lock in tbl because until - * the rehash completes ht->tbl won't be changed. + /* All insertions must grab the oldest table containing + * the hashed bucket that is yet to be rehashed. */ + for (;;) { + hash = rht_head_hashfn(ht, tbl, obj, params); + lock = rht_bucket_lock(tbl, hash); + spin_lock_bh(lock); + + if (tbl->rehash <= hash) + break; + + spin_unlock_bh(lock); + tbl = rht_dereference_rcu(tbl->future_tbl, ht); + } + new_tbl = rht_dereference_rcu(tbl->future_tbl, ht); if (unlikely(new_tbl)) { err = rhashtable_insert_slow(ht, key, obj, new_tbl); diff --git a/lib/rhashtable.c b/lib/rhashtable.c index 9623be345d9c..5e04403e25f5 100644 --- a/lib/rhashtable.c +++ b/lib/rhashtable.c @@ -136,11 +136,24 @@ static struct bucket_table *bucket_table_alloc(struct rhashtable *ht, return tbl; } +static struct bucket_table *rhashtable_last_table(struct rhashtable *ht, + struct bucket_table *tbl) +{ + struct bucket_table *new_tbl; + + do { + new_tbl = tbl; + tbl = rht_dereference_rcu(tbl->future_tbl, ht); + } while (tbl); + + return new_tbl; +} + static int rhashtable_rehash_one(struct rhashtable *ht, unsigned old_hash) { struct bucket_table *old_tbl = rht_dereference(ht->tbl, ht); - struct bucket_table *new_tbl = - rht_dereference(old_tbl->future_tbl, ht) ?: old_tbl; + struct bucket_table *new_tbl = rhashtable_last_table(ht, + rht_dereference_rcu(old_tbl->future_tbl, ht)); struct rhash_head __rcu **pprev = &old_tbl->buckets[old_hash]; int err = -ENOENT; struct rhash_head *head, *next, *entry; @@ -196,12 +209,18 @@ static void rhashtable_rehash_chain(struct rhashtable *ht, unsigned old_hash) spin_unlock_bh(old_bucket_lock); } -static void rhashtable_rehash(struct rhashtable *ht, - struct bucket_table *new_tbl) +static int rhashtable_rehash_attach(struct rhashtable *ht, + struct bucket_table *old_tbl, + struct bucket_table *new_tbl) { - struct bucket_table *old_tbl = rht_dereference(ht->tbl, ht); - struct rhashtable_walker *walker; - unsigned old_hash; + /* Protect future_tbl using the first bucket lock. */ + spin_lock_bh(old_tbl->locks); + + /* Did somebody beat us to it? */ + if (rcu_access_pointer(old_tbl->future_tbl)) { + spin_unlock_bh(old_tbl->locks); + return -EEXIST; + } /* Make insertions go into the new, empty table right away. Deletions * and lookups will be attempted in both tables until we synchronize. @@ -211,6 +230,22 @@ static void rhashtable_rehash(struct rhashtable *ht, /* Ensure the new table is visible to readers. */ smp_wmb(); + spin_unlock_bh(old_tbl->locks); + + return 0; +} + +static int rhashtable_rehash_table(struct rhashtable *ht) +{ + struct bucket_table *old_tbl = rht_dereference(ht->tbl, ht); + struct bucket_table *new_tbl; + struct rhashtable_walker *walker; + unsigned old_hash; + + new_tbl = rht_dereference(old_tbl->future_tbl, ht); + if (!new_tbl) + return 0; + for (old_hash = 0; old_hash < old_tbl->size; old_hash++) rhashtable_rehash_chain(ht, old_hash); @@ -225,6 +260,8 @@ static void rhashtable_rehash(struct rhashtable *ht, * remain. */ call_rcu(&old_tbl->rcu, bucket_table_free_rcu); + + return rht_dereference(new_tbl->future_tbl, ht) ? -EAGAIN : 0; } /** @@ -242,20 +279,25 @@ static void rhashtable_rehash(struct rhashtable *ht, * It is valid to have concurrent insertions and deletions protected by per * bucket locks or concurrent RCU protected lookups and traversals. */ -int rhashtable_expand(struct rhashtable *ht) +static int rhashtable_expand(struct rhashtable *ht) { struct bucket_table *new_tbl, *old_tbl = rht_dereference(ht->tbl, ht); + int err; ASSERT_RHT_MUTEX(ht); + old_tbl = rhashtable_last_table(ht, old_tbl); + new_tbl = bucket_table_alloc(ht, old_tbl->size * 2); if (new_tbl == NULL) return -ENOMEM; - rhashtable_rehash(ht, new_tbl); - return 0; + err = rhashtable_rehash_attach(ht, old_tbl, new_tbl); + if (err) + bucket_table_free(new_tbl); + + return err; } -EXPORT_SYMBOL_GPL(rhashtable_expand); /** * rhashtable_shrink - Shrink hash table while allowing concurrent lookups @@ -273,10 +315,11 @@ EXPORT_SYMBOL_GPL(rhashtable_expand); * It is valid to have concurrent insertions and deletions protected by per * bucket locks or concurrent RCU protected lookups and traversals. */ -int rhashtable_shrink(struct rhashtable *ht) +static int rhashtable_shrink(struct rhashtable *ht) { struct bucket_table *new_tbl, *old_tbl = rht_dereference(ht->tbl, ht); unsigned size = roundup_pow_of_two(atomic_read(&ht->nelems) * 3 / 2); + int err; ASSERT_RHT_MUTEX(ht); @@ -286,19 +329,25 @@ int rhashtable_shrink(struct rhashtable *ht) if (old_tbl->size <= size) return 0; + if (rht_dereference(old_tbl->future_tbl, ht)) + return -EEXIST; + new_tbl = bucket_table_alloc(ht, size); if (new_tbl == NULL) return -ENOMEM; - rhashtable_rehash(ht, new_tbl); - return 0; + err = rhashtable_rehash_attach(ht, old_tbl, new_tbl); + if (err) + bucket_table_free(new_tbl); + + return err; } -EXPORT_SYMBOL_GPL(rhashtable_shrink); static void rht_deferred_worker(struct work_struct *work) { struct rhashtable *ht; struct bucket_table *tbl; + int err = 0; ht = container_of(work, struct rhashtable, run_work); mutex_lock(&ht->mutex); @@ -306,13 +355,20 @@ static void rht_deferred_worker(struct work_struct *work) goto unlock; tbl = rht_dereference(ht->tbl, ht); + tbl = rhashtable_last_table(ht, tbl); if (rht_grow_above_75(ht, tbl)) rhashtable_expand(ht); else if (rht_shrink_below_30(ht, tbl)) rhashtable_shrink(ht); + + err = rhashtable_rehash_table(ht); + unlock: mutex_unlock(&ht->mutex); + + if (err) + schedule_work(&ht->run_work); } int rhashtable_insert_slow(struct rhashtable *ht, const void *key, @@ -323,6 +379,7 @@ int rhashtable_insert_slow(struct rhashtable *ht, const void *key, unsigned hash; int err = -EEXIST; + tbl = rhashtable_last_table(ht, tbl); hash = head_hashfn(ht, tbl, obj); spin_lock_nested(rht_bucket_lock(tbl, hash), SINGLE_DEPTH_NESTING); diff --git a/lib/test_rhashtable.c b/lib/test_rhashtable.c index a2ba6adb60a2..a42a0d44e818 100644 --- a/lib/test_rhashtable.c +++ b/lib/test_rhashtable.c @@ -155,30 +155,6 @@ static int __init test_rhashtable(struct rhashtable *ht) test_rht_lookup(ht); rcu_read_unlock(); - for (i = 0; i < TEST_NEXPANDS; i++) { - pr_info(" Table expansion iteration %u...\n", i); - mutex_lock(&ht->mutex); - rhashtable_expand(ht); - mutex_unlock(&ht->mutex); - - rcu_read_lock(); - pr_info(" Verifying lookups...\n"); - test_rht_lookup(ht); - rcu_read_unlock(); - } - - for (i = 0; i < TEST_NEXPANDS; i++) { - pr_info(" Table shrinkage iteration %u...\n", i); - mutex_lock(&ht->mutex); - rhashtable_shrink(ht); - mutex_unlock(&ht->mutex); - - rcu_read_lock(); - pr_info(" Verifying lookups...\n"); - test_rht_lookup(ht); - rcu_read_unlock(); - } - rcu_read_lock(); test_bucket_stats(ht, true); rcu_read_unlock(); -- cgit v1.2.3 From ccd57b1bd32460d27bbb9c599e795628a3c66983 Mon Sep 17 00:00:00 2001 From: Herbert Xu Date: Tue, 24 Mar 2015 00:50:28 +1100 Subject: rhashtable: Add immediate rehash during insertion This patch reintroduces immediate rehash during insertion. If we find during insertion that the table is full or the chain length exceeds a set limit (currently 16 but may be disabled with insecure_elasticity) then we will force an immediate rehash. The rehash will contain an expansion if the table utilisation exceeds 75%. If this rehash fails then the insertion will fail. Otherwise the insertion will be reattempted in the new hash table. Signed-off-by: Herbert Xu Acked-by: Thomas Graf Signed-off-by: David S. Miller --- include/linux/rhashtable.h | 42 ++++++++++++++++++++++++++++---- lib/rhashtable.c | 60 +++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 96 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/include/linux/rhashtable.h b/include/linux/rhashtable.h index e8ffcdb5e239..f9ecf32bce55 100644 --- a/include/linux/rhashtable.h +++ b/include/linux/rhashtable.h @@ -103,6 +103,7 @@ struct rhashtable; * @max_size: Maximum size while expanding * @min_size: Minimum size while shrinking * @nulls_base: Base value to generate nulls marker + * @insecure_elasticity: Set to true to disable chain length checks * @locks_mul: Number of bucket locks to allocate per cpu (default: 128) * @hashfn: Hash function (default: jhash2 if !(key_len % 4), or jhash) * @obj_hashfn: Function to hash object @@ -116,6 +117,7 @@ struct rhashtable_params { unsigned int max_size; unsigned int min_size; u32 nulls_base; + bool insecure_elasticity; size_t locks_mul; rht_hashfn_t hashfn; rht_obj_hashfn_t obj_hashfn; @@ -127,6 +129,7 @@ struct rhashtable_params { * @tbl: Bucket table * @nelems: Number of elements in table * @key_len: Key length for hashfn + * @elasticity: Maximum chain length before rehash * @p: Configuration parameters * @run_work: Deferred worker to expand/shrink asynchronously * @mutex: Mutex to protect current/future table swapping @@ -137,6 +140,7 @@ struct rhashtable { atomic_t nelems; bool being_destroyed; unsigned int key_len; + unsigned int elasticity; struct rhashtable_params p; struct work_struct run_work; struct mutex mutex; @@ -266,6 +270,17 @@ static inline bool rht_shrink_below_30(const struct rhashtable *ht, tbl->size > ht->p.min_size; } +/** + * rht_grow_above_100 - returns true if nelems > table-size + * @ht: hash table + * @tbl: current table + */ +static inline bool rht_grow_above_100(const struct rhashtable *ht, + const struct bucket_table *tbl) +{ + return atomic_read(&ht->nelems) > tbl->size; +} + /* The bucket lock is selected based on the hash and protects mutations * on a group of hash buckets. * @@ -307,6 +322,7 @@ int rhashtable_init(struct rhashtable *ht, int rhashtable_insert_slow(struct rhashtable *ht, const void *key, struct rhash_head *obj, struct bucket_table *old_tbl); +int rhashtable_insert_rehash(struct rhashtable *ht); int rhashtable_walk_init(struct rhashtable *ht, struct rhashtable_iter *iter); void rhashtable_walk_exit(struct rhashtable_iter *iter); @@ -529,12 +545,14 @@ static inline int __rhashtable_insert_fast( .ht = ht, .key = key, }; - int err = -EEXIST; struct bucket_table *tbl, *new_tbl; struct rhash_head *head; spinlock_t *lock; + unsigned elasticity; unsigned hash; + int err; +restart: rcu_read_lock(); tbl = rht_dereference_rcu(ht->tbl, ht); @@ -557,20 +575,34 @@ static inline int __rhashtable_insert_fast( new_tbl = rht_dereference_rcu(tbl->future_tbl, ht); if (unlikely(new_tbl)) { err = rhashtable_insert_slow(ht, key, obj, new_tbl); + if (err == -EAGAIN) + goto slow_path; goto out; } - if (!key) - goto skip_lookup; + if (unlikely(rht_grow_above_100(ht, tbl))) { +slow_path: + spin_unlock_bh(lock); + rcu_read_unlock(); + err = rhashtable_insert_rehash(ht); + if (err) + return err; + + goto restart; + } + err = -EEXIST; + elasticity = ht->elasticity; rht_for_each(head, tbl, hash) { - if (unlikely(!(params.obj_cmpfn ? + if (key && + unlikely(!(params.obj_cmpfn ? params.obj_cmpfn(&arg, rht_obj(ht, head)) : rhashtable_compare(&arg, rht_obj(ht, head))))) goto out; + if (!--elasticity) + goto slow_path; } -skip_lookup: err = 0; head = rht_dereference_bucket(tbl->buckets[hash], tbl, hash); diff --git a/lib/rhashtable.c b/lib/rhashtable.c index 220a11a13d40..7686c1e9934a 100644 --- a/lib/rhashtable.c +++ b/lib/rhashtable.c @@ -375,21 +375,76 @@ unlock: schedule_work(&ht->run_work); } +static bool rhashtable_check_elasticity(struct rhashtable *ht, + struct bucket_table *tbl, + unsigned hash) +{ + unsigned elasticity = ht->elasticity; + struct rhash_head *head; + + rht_for_each(head, tbl, hash) + if (!--elasticity) + return true; + + return false; +} + +int rhashtable_insert_rehash(struct rhashtable *ht) +{ + struct bucket_table *old_tbl; + struct bucket_table *new_tbl; + struct bucket_table *tbl; + unsigned int size; + int err; + + old_tbl = rht_dereference_rcu(ht->tbl, ht); + tbl = rhashtable_last_table(ht, old_tbl); + + size = tbl->size; + + if (rht_grow_above_75(ht, tbl)) + size *= 2; + /* More than two rehashes (not resizes) detected. */ + else if (WARN_ON(old_tbl != tbl && old_tbl->size == size)) + return -EBUSY; + + new_tbl = bucket_table_alloc(ht, size, GFP_ATOMIC); + if (new_tbl == NULL) + return -ENOMEM; + + err = rhashtable_rehash_attach(ht, tbl, new_tbl); + if (err) { + bucket_table_free(new_tbl); + if (err == -EEXIST) + err = 0; + } else + schedule_work(&ht->run_work); + + return err; +} +EXPORT_SYMBOL_GPL(rhashtable_insert_rehash); + int rhashtable_insert_slow(struct rhashtable *ht, const void *key, struct rhash_head *obj, struct bucket_table *tbl) { struct rhash_head *head; unsigned hash; - int err = -EEXIST; + int err; tbl = rhashtable_last_table(ht, tbl); hash = head_hashfn(ht, tbl, obj); spin_lock_nested(rht_bucket_lock(tbl, hash), SINGLE_DEPTH_NESTING); + err = -EEXIST; if (key && rhashtable_lookup_fast(ht, key, ht->p)) goto exit; + err = -EAGAIN; + if (rhashtable_check_elasticity(ht, tbl, hash) || + rht_grow_above_100(ht, tbl)) + goto exit; + err = 0; head = rht_dereference_bucket(tbl->buckets[hash], tbl, hash); @@ -678,6 +733,9 @@ int rhashtable_init(struct rhashtable *ht, ht->p.min_size = max(ht->p.min_size, HASH_MIN_SIZE); + if (!params->insecure_elasticity) + ht->elasticity = 16; + if (params->locks_mul) ht->p.locks_mul = roundup_pow_of_two(params->locks_mul); else -- cgit v1.2.3 From 3d1bec99320d4e96897805440f8cf4f68eff226b Mon Sep 17 00:00:00 2001 From: Hannes Frederic Sowa Date: Mon, 23 Mar 2015 23:36:00 +0100 Subject: ipv6: introduce secret_stable to ipv6_devconf MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch implements the procfs logic for the stable_address knob: The secret is formatted as an ipv6 address and will be stored per interface and per namespace. We track initialized flag and return EIO errors until the secret is set. We don't inherit the secret to newly created namespaces. Cc: Erik Kline Cc: Fernando Gont Cc: Lorenzo Colitti Cc: YOSHIFUJI Hideaki/吉藤英明 Signed-off-by: Hannes Frederic Sowa Signed-off-by: David S. Miller --- include/linux/ipv6.h | 4 +++ include/uapi/linux/ipv6.h | 1 + net/ipv6/addrconf.c | 68 +++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 73 insertions(+) (limited to 'include') diff --git a/include/linux/ipv6.h b/include/linux/ipv6.h index 4d5169f5d7d1..82806c60aa42 100644 --- a/include/linux/ipv6.h +++ b/include/linux/ipv6.h @@ -53,6 +53,10 @@ struct ipv6_devconf { __s32 ndisc_notify; __s32 suppress_frag_ndisc; __s32 accept_ra_mtu; + struct ipv6_stable_secret { + bool initialized; + struct in6_addr secret; + } stable_secret; void *sysctl; }; diff --git a/include/uapi/linux/ipv6.h b/include/uapi/linux/ipv6.h index 437a6a4b125a..5efa54ae567c 100644 --- a/include/uapi/linux/ipv6.h +++ b/include/uapi/linux/ipv6.h @@ -170,6 +170,7 @@ enum { DEVCONF_ACCEPT_RA_FROM_LOCAL, DEVCONF_USE_OPTIMISTIC, DEVCONF_ACCEPT_RA_MTU, + DEVCONF_STABLE_SECRET, DEVCONF_MAX }; diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c index 158378e73f0a..5b967c8a617a 100644 --- a/net/ipv6/addrconf.c +++ b/net/ipv6/addrconf.c @@ -46,6 +46,7 @@ #include #include #include +#include #include #include #include @@ -102,6 +103,9 @@ #define INFINITY_LIFE_TIME 0xFFFFFFFF +#define IPV6_MAX_STRLEN \ + sizeof("ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255") + static inline u32 cstamp_delta(unsigned long cstamp) { return (cstamp - INITIAL_JIFFIES) * 100UL / HZ; @@ -202,6 +206,9 @@ static struct ipv6_devconf ipv6_devconf __read_mostly = { .accept_dad = 1, .suppress_frag_ndisc = 1, .accept_ra_mtu = 1, + .stable_secret = { + .initialized = false, + } }; static struct ipv6_devconf ipv6_devconf_dflt __read_mostly = { @@ -240,6 +247,9 @@ static struct ipv6_devconf ipv6_devconf_dflt __read_mostly = { .accept_dad = 1, .suppress_frag_ndisc = 1, .accept_ra_mtu = 1, + .stable_secret = { + .initialized = false, + }, }; /* Check if a valid qdisc is available */ @@ -4430,6 +4440,7 @@ static inline void ipv6_store_devconf(struct ipv6_devconf *cnf, array[DEVCONF_SUPPRESS_FRAG_NDISC] = cnf->suppress_frag_ndisc; array[DEVCONF_ACCEPT_RA_FROM_LOCAL] = cnf->accept_ra_from_local; array[DEVCONF_ACCEPT_RA_MTU] = cnf->accept_ra_mtu; + /* we omit DEVCONF_STABLE_SECRET for now */ } static inline size_t inet6_ifla6_size(void) @@ -5074,6 +5085,53 @@ int addrconf_sysctl_proxy_ndp(struct ctl_table *ctl, int write, return ret; } +static int addrconf_sysctl_stable_secret(struct ctl_table *ctl, int write, + void __user *buffer, size_t *lenp, + loff_t *ppos) +{ + int err; + struct in6_addr addr; + char str[IPV6_MAX_STRLEN]; + struct ctl_table lctl = *ctl; + struct ipv6_stable_secret *secret = ctl->data; + + lctl.maxlen = IPV6_MAX_STRLEN; + lctl.data = str; + + if (!rtnl_trylock()) + return restart_syscall(); + + if (!write && !secret->initialized) { + err = -EIO; + goto out; + } + + if (!write) { + err = snprintf(str, sizeof(str), "%pI6", + &secret->secret); + if (err >= sizeof(str)) { + err = -EIO; + goto out; + } + } + + err = proc_dostring(&lctl, write, buffer, lenp, ppos); + if (err || !write) + goto out; + + if (in6_pton(str, -1, addr.in6_u.u6_addr8, -1, NULL) != 1) { + err = -EIO; + goto out; + } + + secret->initialized = true; + secret->secret = addr; + +out: + rtnl_unlock(); + + return err; +} static struct addrconf_sysctl_table { @@ -5346,6 +5404,13 @@ static struct addrconf_sysctl_table .mode = 0644, .proc_handler = proc_dointvec, }, + { + .procname = "stable_secret", + .data = &ipv6_devconf.stable_secret, + .maxlen = IPV6_MAX_STRLEN, + .mode = 0600, + .proc_handler = addrconf_sysctl_stable_secret, + }, { /* sentinel */ } @@ -5442,6 +5507,9 @@ static int __net_init addrconf_init_net(struct net *net) dflt->autoconf = ipv6_defaults.autoconf; dflt->disable_ipv6 = ipv6_defaults.disable_ipv6; + dflt->stable_secret.initialized = false; + all->stable_secret.initialized = false; + net->ipv6.devconf_all = all; net->ipv6.devconf_dflt = dflt; -- cgit v1.2.3 From 622c81d57b392cc9be836670eb464a4dfaa9adfe Mon Sep 17 00:00:00 2001 From: Hannes Frederic Sowa Date: Mon, 23 Mar 2015 23:36:01 +0100 Subject: ipv6: generation of stable privacy addresses for link-local and autoconf MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch implements the stable privacy address generation for link-local and autoconf addresses as specified in RFC7217. RID = F(Prefix, Net_Iface, Network_ID, DAD_Counter, secret_key) is the RID (random identifier). As the hash function F we chose one round of sha1. Prefix will be either the link-local prefix or the router advertised one. As Net_Iface we use the MAC address of the device. DAD_Counter and secret_key are implemented as specified. We don't use Network_ID, as it couples the code too closely to other subsystems. It is specified as optional in the RFC. As Net_Iface we only use the MAC address: we simply have no stable identifier in the kernel we could possibly use: because this code might run very early, we cannot depend on names, as they might be changed by user space early on during the boot process. A new address generation mode is introduced, IN6_ADDR_GEN_MODE_STABLE_PRIVACY. With iproute2 one can switch back to none or eui64 address configuration mode although the stable_secret is already set. We refuse writes to ipv6/conf/all/stable_secret but only allow ipv6/conf/default/stable_secret and the interface specific file to be written to. The default stable_secret is used as the parameter for the namespace, the interface specific can overwrite the secret, e.g. when switching a network configuration from one system to another while inheriting the secret. Cc: Erik Kline Cc: Fernando Gont Cc: Lorenzo Colitti Cc: YOSHIFUJI Hideaki/吉藤英明 Signed-off-by: Hannes Frederic Sowa Signed-off-by: David S. Miller --- include/uapi/linux/if_link.h | 1 + net/ipv6/addrconf.c | 130 +++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 127 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/include/uapi/linux/if_link.h b/include/uapi/linux/if_link.h index f5f5edd5ae5f..7ffb18df01ca 100644 --- a/include/uapi/linux/if_link.h +++ b/include/uapi/linux/if_link.h @@ -216,6 +216,7 @@ enum { enum in6_addr_gen_mode { IN6_ADDR_GEN_MODE_EUI64, IN6_ADDR_GEN_MODE_NONE, + IN6_ADDR_GEN_MODE_STABLE_PRIVACY, }; /* Bridge section */ diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c index 5b967c8a617a..6813268ce8b8 100644 --- a/net/ipv6/addrconf.c +++ b/net/ipv6/addrconf.c @@ -131,6 +131,9 @@ static void ipv6_regen_rndid(unsigned long data); static int ipv6_generate_eui64(u8 *eui, struct net_device *dev); static int ipv6_count_addresses(struct inet6_dev *idev); +static int ipv6_generate_stable_address(struct in6_addr *addr, + u8 dad_count, + const struct inet6_dev *idev); /* * Configured unicast address hash table @@ -2302,6 +2305,11 @@ void addrconf_prefix_rcv(struct net_device *dev, u8 *opt, int len, bool sllao) in6_dev->token.s6_addr + 8, 8); read_unlock_bh(&in6_dev->lock); tokenized = true; + } else if (in6_dev->addr_gen_mode == + IN6_ADDR_GEN_MODE_STABLE_PRIVACY && + !ipv6_generate_stable_address(&addr, 0, + in6_dev)) { + goto ok; } else if (ipv6_generate_eui64(addr.s6_addr + 8, dev) && ipv6_inherit_eui64(addr.s6_addr + 8, in6_dev)) { in6_dev_put(in6_dev); @@ -2820,12 +2828,98 @@ static void addrconf_add_linklocal(struct inet6_dev *idev, const struct in6_addr } } +static bool ipv6_reserved_interfaceid(struct in6_addr address) +{ + if ((address.s6_addr32[2] | address.s6_addr32[3]) == 0) + return true; + + if (address.s6_addr32[2] == htonl(0x02005eff) && + ((address.s6_addr32[3] & htonl(0xfe000000)) == htonl(0xfe000000))) + return true; + + if (address.s6_addr32[2] == htonl(0xfdffffff) && + ((address.s6_addr32[3] & htonl(0xffffff80)) == htonl(0xffffff80))) + return true; + + return false; +} + +static int ipv6_generate_stable_address(struct in6_addr *address, + u8 dad_count, + const struct inet6_dev *idev) +{ + static const int idgen_retries = 3; + + static DEFINE_SPINLOCK(lock); + static __u32 digest[SHA_DIGEST_WORDS]; + static __u32 workspace[SHA_WORKSPACE_WORDS]; + + static union { + char __data[SHA_MESSAGE_BYTES]; + struct { + struct in6_addr secret; + __be64 prefix; + unsigned char hwaddr[MAX_ADDR_LEN]; + u8 dad_count; + } __packed; + } data; + + struct in6_addr secret; + struct in6_addr temp; + struct net *net = dev_net(idev->dev); + + BUILD_BUG_ON(sizeof(data.__data) != sizeof(data)); + + if (idev->cnf.stable_secret.initialized) + secret = idev->cnf.stable_secret.secret; + else if (net->ipv6.devconf_dflt->stable_secret.initialized) + secret = net->ipv6.devconf_dflt->stable_secret.secret; + else + return -1; + +retry: + spin_lock_bh(&lock); + + sha_init(digest); + memset(&data, 0, sizeof(data)); + memset(workspace, 0, sizeof(workspace)); + memcpy(data.hwaddr, idev->dev->perm_addr, idev->dev->addr_len); + data.prefix = ((__be64)address->s6_addr32[0] << 32) | + (__be64)address->s6_addr32[1]; + data.secret = secret; + data.dad_count = dad_count; + + sha_transform(digest, data.__data, workspace); + + temp = *address; + temp.s6_addr32[2] = digest[0]; + temp.s6_addr32[3] = digest[1]; + + spin_unlock_bh(&lock); + + if (ipv6_reserved_interfaceid(temp)) { + dad_count++; + if (dad_count > idgen_retries) + return -1; + goto retry; + } + + *address = temp; + return 0; +} + static void addrconf_addr_gen(struct inet6_dev *idev, bool prefix_route) { - if (idev->addr_gen_mode == IN6_ADDR_GEN_MODE_EUI64) { - struct in6_addr addr; + struct in6_addr addr; + + ipv6_addr_set(&addr, htonl(0xFE800000), 0, 0, 0); - ipv6_addr_set(&addr, htonl(0xFE800000), 0, 0, 0); + if (idev->addr_gen_mode == IN6_ADDR_GEN_MODE_STABLE_PRIVACY) { + if (!ipv6_generate_stable_address(&addr, 0, idev)) + addrconf_add_linklocal(idev, &addr); + else if (prefix_route) + addrconf_prefix_route(&addr, 64, idev->dev, 0, 0); + } else if (idev->addr_gen_mode == IN6_ADDR_GEN_MODE_EUI64) { /* addrconf_add_linklocal also adds a prefix_route and we * only need to care about prefix routes if ipv6_generate_eui64 * couldn't generate one. @@ -4675,8 +4769,15 @@ static int inet6_set_link_af(struct net_device *dev, const struct nlattr *nla) u8 mode = nla_get_u8(tb[IFLA_INET6_ADDR_GEN_MODE]); if (mode != IN6_ADDR_GEN_MODE_EUI64 && - mode != IN6_ADDR_GEN_MODE_NONE) + mode != IN6_ADDR_GEN_MODE_NONE && + mode != IN6_ADDR_GEN_MODE_STABLE_PRIVACY) + return -EINVAL; + + if (mode == IN6_ADDR_GEN_MODE_STABLE_PRIVACY && + !idev->cnf.stable_secret.initialized && + !dev_net(dev)->ipv6.devconf_dflt->stable_secret.initialized) return -EINVAL; + idev->addr_gen_mode = mode; err = 0; } @@ -5093,8 +5194,12 @@ static int addrconf_sysctl_stable_secret(struct ctl_table *ctl, int write, struct in6_addr addr; char str[IPV6_MAX_STRLEN]; struct ctl_table lctl = *ctl; + struct net *net = ctl->extra2; struct ipv6_stable_secret *secret = ctl->data; + if (&net->ipv6.devconf_all->stable_secret == ctl->data) + return -EIO; + lctl.maxlen = IPV6_MAX_STRLEN; lctl.data = str; @@ -5127,6 +5232,23 @@ static int addrconf_sysctl_stable_secret(struct ctl_table *ctl, int write, secret->initialized = true; secret->secret = addr; + if (&net->ipv6.devconf_dflt->stable_secret == ctl->data) { + struct net_device *dev; + + for_each_netdev(net, dev) { + struct inet6_dev *idev = __in6_dev_get(dev); + + if (idev) { + idev->addr_gen_mode = + IN6_ADDR_GEN_MODE_STABLE_PRIVACY; + } + } + } else { + struct inet6_dev *idev = ctl->extra1; + + idev->addr_gen_mode = IN6_ADDR_GEN_MODE_STABLE_PRIVACY; + } + out: rtnl_unlock(); -- cgit v1.2.3 From 64236f3f3d742469e4027b83a9515e84e9ab21b4 Mon Sep 17 00:00:00 2001 From: Hannes Frederic Sowa Date: Mon, 23 Mar 2015 23:36:02 +0100 Subject: ipv6: introduce IFA_F_STABLE_PRIVACY flag MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We need to mark appropriate addresses so we can do retries in case their DAD failed. Cc: Erik Kline Cc: Fernando Gont Cc: Lorenzo Colitti Cc: YOSHIFUJI Hideaki/吉藤英明 Signed-off-by: Hannes Frederic Sowa Signed-off-by: David S. Miller --- include/uapi/linux/if_addr.h | 1 + net/ipv6/addrconf.c | 14 ++++++++------ 2 files changed, 9 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/include/uapi/linux/if_addr.h b/include/uapi/linux/if_addr.h index 40fdfea39714..4318ab1635ce 100644 --- a/include/uapi/linux/if_addr.h +++ b/include/uapi/linux/if_addr.h @@ -51,6 +51,7 @@ enum { #define IFA_F_MANAGETEMPADDR 0x100 #define IFA_F_NOPREFIXROUTE 0x200 #define IFA_F_MCAUTOJOIN 0x400 +#define IFA_F_STABLE_PRIVACY 0x800 struct ifa_cacheinfo { __u32 ifa_prefered; diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c index 6813268ce8b8..c2357b6f62dd 100644 --- a/net/ipv6/addrconf.c +++ b/net/ipv6/addrconf.c @@ -2199,6 +2199,7 @@ void addrconf_prefix_rcv(struct net_device *dev, u8 *opt, int len, bool sllao) __u32 valid_lft; __u32 prefered_lft; int addr_type; + u32 addr_flags = 0; struct inet6_dev *in6_dev; struct net *net = dev_net(dev); @@ -2309,6 +2310,7 @@ void addrconf_prefix_rcv(struct net_device *dev, u8 *opt, int len, bool sllao) IN6_ADDR_GEN_MODE_STABLE_PRIVACY && !ipv6_generate_stable_address(&addr, 0, in6_dev)) { + addr_flags |= IFA_F_STABLE_PRIVACY; goto ok; } else if (ipv6_generate_eui64(addr.s6_addr + 8, dev) && ipv6_inherit_eui64(addr.s6_addr + 8, in6_dev)) { @@ -2328,7 +2330,6 @@ ok: if (ifp == NULL && valid_lft) { int max_addresses = in6_dev->cnf.max_addresses; - u32 addr_flags = 0; #ifdef CONFIG_IPV6_OPTIMISTIC_DAD if (in6_dev->cnf.optimistic_dad && @@ -2807,10 +2808,11 @@ static void init_loopback(struct net_device *dev) } } -static void addrconf_add_linklocal(struct inet6_dev *idev, const struct in6_addr *addr) +static void addrconf_add_linklocal(struct inet6_dev *idev, + const struct in6_addr *addr, u32 flags) { struct inet6_ifaddr *ifp; - u32 addr_flags = IFA_F_PERMANENT; + u32 addr_flags = flags | IFA_F_PERMANENT; #ifdef CONFIG_IPV6_OPTIMISTIC_DAD if (idev->cnf.optimistic_dad && @@ -2818,7 +2820,6 @@ static void addrconf_add_linklocal(struct inet6_dev *idev, const struct in6_addr addr_flags |= IFA_F_OPTIMISTIC; #endif - ifp = ipv6_add_addr(idev, addr, NULL, 64, IFA_LINK, addr_flags, INFINITY_LIFE_TIME, INFINITY_LIFE_TIME); if (!IS_ERR(ifp)) { @@ -2916,7 +2917,8 @@ static void addrconf_addr_gen(struct inet6_dev *idev, bool prefix_route) if (idev->addr_gen_mode == IN6_ADDR_GEN_MODE_STABLE_PRIVACY) { if (!ipv6_generate_stable_address(&addr, 0, idev)) - addrconf_add_linklocal(idev, &addr); + addrconf_add_linklocal(idev, &addr, + IFA_F_STABLE_PRIVACY); else if (prefix_route) addrconf_prefix_route(&addr, 64, idev->dev, 0, 0); } else if (idev->addr_gen_mode == IN6_ADDR_GEN_MODE_EUI64) { @@ -2925,7 +2927,7 @@ static void addrconf_addr_gen(struct inet6_dev *idev, bool prefix_route) * couldn't generate one. */ if (ipv6_generate_eui64(addr.s6_addr + 8, idev->dev) == 0) - addrconf_add_linklocal(idev, &addr); + addrconf_add_linklocal(idev, &addr, 0); else if (prefix_route) addrconf_prefix_route(&addr, 64, idev->dev, 0, 0); } -- cgit v1.2.3 From 8e8e676d0b3c7f074c719c7c05b20296b9b0b0b1 Mon Sep 17 00:00:00 2001 From: Hannes Frederic Sowa Date: Mon, 23 Mar 2015 23:36:03 +0100 Subject: ipv6: collapse state_lock and lock MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Cc: Erik Kline Cc: Fernando Gont Cc: Lorenzo Colitti Cc: YOSHIFUJI Hideaki/吉藤英明 Signed-off-by: Hannes Frederic Sowa Signed-off-by: David S. Miller --- include/net/if_inet6.h | 3 +-- net/ipv6/addrconf.c | 31 +++++++++++++++---------------- 2 files changed, 16 insertions(+), 18 deletions(-) (limited to 'include') diff --git a/include/net/if_inet6.h b/include/net/if_inet6.h index 98e5f9578f86..d89397a22903 100644 --- a/include/net/if_inet6.h +++ b/include/net/if_inet6.h @@ -41,13 +41,12 @@ enum { struct inet6_ifaddr { struct in6_addr addr; __u32 prefix_len; - + /* In seconds, relative to tstamp. Expiry is at tstamp + HZ * lft. */ __u32 valid_lft; __u32 prefered_lft; atomic_t refcnt; spinlock_t lock; - spinlock_t state_lock; int state; diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c index c2357b6f62dd..1cc5320e510f 100644 --- a/net/ipv6/addrconf.c +++ b/net/ipv6/addrconf.c @@ -873,7 +873,6 @@ ipv6_add_addr(struct inet6_dev *idev, const struct in6_addr *addr, ifa->peer_addr = *peer_addr; spin_lock_init(&ifa->lock); - spin_lock_init(&ifa->state_lock); INIT_DELAYED_WORK(&ifa->dad_work, addrconf_dad_work); INIT_HLIST_NODE(&ifa->addr_lst); ifa->scope = scope; @@ -1016,10 +1015,10 @@ static void ipv6_del_addr(struct inet6_ifaddr *ifp) ASSERT_RTNL(); - spin_lock_bh(&ifp->state_lock); + spin_lock_bh(&ifp->lock); state = ifp->state; ifp->state = INET6_IFADDR_STATE_DEAD; - spin_unlock_bh(&ifp->state_lock); + spin_unlock_bh(&ifp->lock); if (state == INET6_IFADDR_STATE_DEAD) goto out; @@ -1699,12 +1698,12 @@ static int addrconf_dad_end(struct inet6_ifaddr *ifp) { int err = -ENOENT; - spin_lock_bh(&ifp->state_lock); + spin_lock_bh(&ifp->lock); if (ifp->state == INET6_IFADDR_STATE_DAD) { ifp->state = INET6_IFADDR_STATE_POSTDAD; err = 0; } - spin_unlock_bh(&ifp->state_lock); + spin_unlock_bh(&ifp->lock); return err; } @@ -1737,10 +1736,10 @@ void addrconf_dad_failure(struct inet6_ifaddr *ifp) } } - spin_lock_bh(&ifp->state_lock); + spin_lock_bh(&ifp->lock); /* transition from _POSTDAD to _ERRDAD */ ifp->state = INET6_IFADDR_STATE_ERRDAD; - spin_unlock_bh(&ifp->state_lock); + spin_unlock_bh(&ifp->lock); addrconf_mod_dad_work(ifp, 0); } @@ -2369,7 +2368,7 @@ ok: u32 stored_lft; /* update lifetime (RFC2462 5.5.3 e) */ - spin_lock(&ifp->lock); + spin_lock_bh(&ifp->lock); now = jiffies; if (ifp->valid_lft > (now - ifp->tstamp) / HZ) stored_lft = ifp->valid_lft - (now - ifp->tstamp) / HZ; @@ -2399,12 +2398,12 @@ ok: ifp->tstamp = now; flags = ifp->flags; ifp->flags &= ~IFA_F_DEPRECATED; - spin_unlock(&ifp->lock); + spin_unlock_bh(&ifp->lock); if (!(flags&IFA_F_TENTATIVE)) ipv6_ifa_notify(0, ifp); } else - spin_unlock(&ifp->lock); + spin_unlock_bh(&ifp->lock); manage_tempaddrs(in6_dev, ifp, valid_lft, prefered_lft, create, now); @@ -3265,10 +3264,10 @@ restart: write_unlock_bh(&idev->lock); - spin_lock_bh(&ifa->state_lock); + spin_lock_bh(&ifa->lock); state = ifa->state; ifa->state = INET6_IFADDR_STATE_DEAD; - spin_unlock_bh(&ifa->state_lock); + spin_unlock_bh(&ifa->lock); if (state != INET6_IFADDR_STATE_DEAD) { __ipv6_ifa_notify(RTM_DELADDR, ifa); @@ -3426,12 +3425,12 @@ static void addrconf_dad_start(struct inet6_ifaddr *ifp) { bool begin_dad = false; - spin_lock_bh(&ifp->state_lock); + spin_lock_bh(&ifp->lock); if (ifp->state != INET6_IFADDR_STATE_DEAD) { ifp->state = INET6_IFADDR_STATE_PREDAD; begin_dad = true; } - spin_unlock_bh(&ifp->state_lock); + spin_unlock_bh(&ifp->lock); if (begin_dad) addrconf_mod_dad_work(ifp, 0); @@ -3453,7 +3452,7 @@ static void addrconf_dad_work(struct work_struct *w) rtnl_lock(); - spin_lock_bh(&ifp->state_lock); + spin_lock_bh(&ifp->lock); if (ifp->state == INET6_IFADDR_STATE_PREDAD) { action = DAD_BEGIN; ifp->state = INET6_IFADDR_STATE_DAD; @@ -3461,7 +3460,7 @@ static void addrconf_dad_work(struct work_struct *w) action = DAD_ABORT; ifp->state = INET6_IFADDR_STATE_POSTDAD; } - spin_unlock_bh(&ifp->state_lock); + spin_unlock_bh(&ifp->lock); if (action == DAD_BEGIN) { addrconf_dad_begin(ifp); -- cgit v1.2.3 From 5f40ef77adb237954d615a76621df1b80a329b31 Mon Sep 17 00:00:00 2001 From: Hannes Frederic Sowa Date: Mon, 23 Mar 2015 23:36:04 +0100 Subject: ipv6: do retries on stable privacy addresses MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If a DAD conflict is detected, we want to retry privacy stable address generation up to idgen_retries (= 3) times with a delay of idgen_delay (= 1 second). Add the logic to addrconf_dad_failure. By design, we don't clean up dad failed permanent addresses. Cc: Erik Kline Cc: Fernando Gont Cc: Lorenzo Colitti Cc: YOSHIFUJI Hideaki/吉藤英明 Signed-off-by: Hannes Frederic Sowa Signed-off-by: David S. Miller --- include/net/if_inet6.h | 1 + net/ipv6/addrconf.c | 57 +++++++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 55 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/include/net/if_inet6.h b/include/net/if_inet6.h index d89397a22903..1c8b6820b694 100644 --- a/include/net/if_inet6.h +++ b/include/net/if_inet6.h @@ -52,6 +52,7 @@ struct inet6_ifaddr { __u32 flags; __u8 dad_probes; + __u8 stable_privacy_retry; __u16 scope; diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c index 1cc5320e510f..9b51fdb42ba9 100644 --- a/net/ipv6/addrconf.c +++ b/net/ipv6/addrconf.c @@ -1710,6 +1710,7 @@ static int addrconf_dad_end(struct inet6_ifaddr *ifp) void addrconf_dad_failure(struct inet6_ifaddr *ifp) { + struct in6_addr addr; struct inet6_dev *idev = ifp->idev; if (addrconf_dad_end(ifp)) { @@ -1720,9 +1721,59 @@ void addrconf_dad_failure(struct inet6_ifaddr *ifp) net_info_ratelimited("%s: IPv6 duplicate address %pI6c detected!\n", ifp->idev->dev->name, &ifp->addr); - if (idev->cnf.accept_dad > 1 && !idev->cnf.disable_ipv6) { - struct in6_addr addr; + spin_lock_bh(&ifp->lock); + + if (ifp->flags & IFA_F_STABLE_PRIVACY) { + int scope = ifp->scope; + u32 flags = ifp->flags; + struct in6_addr new_addr; + struct inet6_ifaddr *ifp2; + u32 valid_lft, preferred_lft; + int pfxlen = ifp->prefix_len; + const unsigned int idgen_retries = 3; + const unsigned int idgen_delay = 1 * HZ; + int retries = ifp->stable_privacy_retry + 1; + + if (retries > idgen_retries) { + net_info_ratelimited("%s: privacy stable address generation failed because of DAD conflicts!\n", + ifp->idev->dev->name); + goto errdad; + } + + new_addr = ifp->addr; + if (ipv6_generate_stable_address(&new_addr, retries, + idev)) + goto errdad; + valid_lft = ifp->valid_lft; + preferred_lft = ifp->prefered_lft; + + spin_unlock_bh(&ifp->lock); + + if (idev->cnf.max_addresses && + ipv6_count_addresses(idev) >= + idev->cnf.max_addresses) + goto lock_errdad; + + net_info_ratelimited("%s: generating new stable privacy address because of DAD conflict\n", + ifp->idev->dev->name); + + ifp2 = ipv6_add_addr(idev, &new_addr, NULL, pfxlen, + scope, flags, valid_lft, + preferred_lft); + if (IS_ERR(ifp2)) + goto lock_errdad; + + spin_lock_bh(&ifp2->lock); + ifp2->stable_privacy_retry = retries; + ifp2->state = INET6_IFADDR_STATE_PREDAD; + spin_unlock_bh(&ifp2->lock); + + addrconf_mod_dad_work(ifp2, idgen_delay); + in6_ifa_put(ifp2); +lock_errdad: + spin_lock_bh(&ifp->lock); + } else if (idev->cnf.accept_dad > 1 && !idev->cnf.disable_ipv6) { addr.s6_addr32[0] = htonl(0xfe800000); addr.s6_addr32[1] = 0; @@ -1736,7 +1787,7 @@ void addrconf_dad_failure(struct inet6_ifaddr *ifp) } } - spin_lock_bh(&ifp->lock); +errdad: /* transition from _POSTDAD to _ERRDAD */ ifp->state = INET6_IFADDR_STATE_ERRDAD; spin_unlock_bh(&ifp->lock); -- cgit v1.2.3 From 1855b7c3e8537c2a4f5a53c797624713bb3becb4 Mon Sep 17 00:00:00 2001 From: Hannes Frederic Sowa Date: Mon, 23 Mar 2015 23:36:05 +0100 Subject: ipv6: introduce idgen_delay and idgen_retries knobs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is specified by RFC 7217. Cc: Erik Kline Cc: Fernando Gont Cc: Lorenzo Colitti Cc: YOSHIFUJI Hideaki/吉藤英明 Signed-off-by: Hannes Frederic Sowa Signed-off-by: David S. Miller --- include/net/netns/ipv6.h | 2 ++ net/ipv6/addrconf.c | 11 ++++------- net/ipv6/af_inet6.c | 2 ++ net/ipv6/sysctl_net_ipv6.c | 16 ++++++++++++++++ 4 files changed, 24 insertions(+), 7 deletions(-) (limited to 'include') diff --git a/include/net/netns/ipv6.h b/include/net/netns/ipv6.h index ca0db12cd089..d2527bf81142 100644 --- a/include/net/netns/ipv6.h +++ b/include/net/netns/ipv6.h @@ -32,6 +32,8 @@ struct netns_sysctl_ipv6 { int icmpv6_time; int anycast_src_echo_reply; int fwmark_reflect; + int idgen_retries; + int idgen_delay; }; struct netns_ipv6 { diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c index 9b51fdb42ba9..d2d238334a11 100644 --- a/net/ipv6/addrconf.c +++ b/net/ipv6/addrconf.c @@ -1712,6 +1712,7 @@ void addrconf_dad_failure(struct inet6_ifaddr *ifp) { struct in6_addr addr; struct inet6_dev *idev = ifp->idev; + struct net *net = dev_net(ifp->idev->dev); if (addrconf_dad_end(ifp)) { in6_ifa_put(ifp); @@ -1730,11 +1731,9 @@ void addrconf_dad_failure(struct inet6_ifaddr *ifp) struct inet6_ifaddr *ifp2; u32 valid_lft, preferred_lft; int pfxlen = ifp->prefix_len; - const unsigned int idgen_retries = 3; - const unsigned int idgen_delay = 1 * HZ; int retries = ifp->stable_privacy_retry + 1; - if (retries > idgen_retries) { + if (retries > net->ipv6.sysctl.idgen_retries) { net_info_ratelimited("%s: privacy stable address generation failed because of DAD conflicts!\n", ifp->idev->dev->name); goto errdad; @@ -1769,7 +1768,7 @@ void addrconf_dad_failure(struct inet6_ifaddr *ifp) ifp2->state = INET6_IFADDR_STATE_PREDAD; spin_unlock_bh(&ifp2->lock); - addrconf_mod_dad_work(ifp2, idgen_delay); + addrconf_mod_dad_work(ifp2, net->ipv6.sysctl.idgen_delay); in6_ifa_put(ifp2); lock_errdad: spin_lock_bh(&ifp->lock); @@ -2899,8 +2898,6 @@ static int ipv6_generate_stable_address(struct in6_addr *address, u8 dad_count, const struct inet6_dev *idev) { - static const int idgen_retries = 3; - static DEFINE_SPINLOCK(lock); static __u32 digest[SHA_DIGEST_WORDS]; static __u32 workspace[SHA_WORKSPACE_WORDS]; @@ -2950,7 +2947,7 @@ retry: if (ipv6_reserved_interfaceid(temp)) { dad_count++; - if (dad_count > idgen_retries) + if (dad_count > dev_net(idev->dev)->ipv6.sysctl.idgen_retries) return -1; goto retry; } diff --git a/net/ipv6/af_inet6.c b/net/ipv6/af_inet6.c index 6bafcc2c79e3..d8dcc526339e 100644 --- a/net/ipv6/af_inet6.c +++ b/net/ipv6/af_inet6.c @@ -766,6 +766,8 @@ static int __net_init inet6_net_init(struct net *net) net->ipv6.sysctl.icmpv6_time = 1*HZ; net->ipv6.sysctl.flowlabel_consistency = 1; net->ipv6.sysctl.auto_flowlabels = 0; + net->ipv6.sysctl.idgen_retries = 3; + net->ipv6.sysctl.idgen_delay = 1 * HZ; atomic_set(&net->ipv6.fib6_sernum, 1); err = ipv6_init_mibs(net); diff --git a/net/ipv6/sysctl_net_ipv6.c b/net/ipv6/sysctl_net_ipv6.c index c5c10fafcfe2..30f5a4ad04eb 100644 --- a/net/ipv6/sysctl_net_ipv6.c +++ b/net/ipv6/sysctl_net_ipv6.c @@ -54,6 +54,20 @@ static struct ctl_table ipv6_table_template[] = { .mode = 0644, .proc_handler = proc_dointvec }, + { + .procname = "idgen_retries", + .data = &init_net.ipv6.sysctl.idgen_retries, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = proc_dointvec, + }, + { + .procname = "idgen_delay", + .data = &init_net.ipv6.sysctl.idgen_delay, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = proc_dointvec_jiffies, + }, { } }; @@ -93,6 +107,8 @@ static int __net_init ipv6_sysctl_net_init(struct net *net) ipv6_table[2].data = &net->ipv6.sysctl.flowlabel_consistency; ipv6_table[3].data = &net->ipv6.sysctl.auto_flowlabels; ipv6_table[4].data = &net->ipv6.sysctl.fwmark_reflect; + ipv6_table[5].data = &net->ipv6.sysctl.idgen_retries; + ipv6_table[6].data = &net->ipv6.sysctl.idgen_delay; ipv6_route_table = ipv6_route_sysctl_init(net); if (!ipv6_route_table) -- cgit v1.2.3 From ba7c95ea3870fe7b847466d39a049ab6f156aa2c Mon Sep 17 00:00:00 2001 From: Herbert Xu Date: Tue, 24 Mar 2015 09:53:17 +1100 Subject: rhashtable: Fix sleeping inside RCU critical section in walk_stop The commit 963ecbd41a1026d99ec7537c050867428c397b89 ("rhashtable: Fix use-after-free in rhashtable_walk_stop") fixed a real bug but created another one because we may end up sleeping inside an RCU critical section. This patch fixes it properly by replacing the mutex with a spin lock that specifically protects the walker lists. Reported-by: Sasha Levin Signed-off-by: Herbert Xu Signed-off-by: David S. Miller --- include/linux/rhashtable.h | 2 ++ lib/rhashtable.c | 7 +++++-- 2 files changed, 7 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/linux/rhashtable.h b/include/linux/rhashtable.h index f9ecf32bce55..d7be9cb0e91f 100644 --- a/include/linux/rhashtable.h +++ b/include/linux/rhashtable.h @@ -133,6 +133,7 @@ struct rhashtable_params { * @p: Configuration parameters * @run_work: Deferred worker to expand/shrink asynchronously * @mutex: Mutex to protect current/future table swapping + * @lock: Spin lock to protect walker list * @being_destroyed: True if table is set up for destruction */ struct rhashtable { @@ -144,6 +145,7 @@ struct rhashtable { struct rhashtable_params p; struct work_struct run_work; struct mutex mutex; + spinlock_t lock; }; /** diff --git a/lib/rhashtable.c b/lib/rhashtable.c index 7686c1e9934a..e96ad1a52c90 100644 --- a/lib/rhashtable.c +++ b/lib/rhashtable.c @@ -256,8 +256,10 @@ static int rhashtable_rehash_table(struct rhashtable *ht) /* Publish the new table pointer. */ rcu_assign_pointer(ht->tbl, new_tbl); + spin_lock(&ht->lock); list_for_each_entry(walker, &old_tbl->walkers, list) walker->tbl = NULL; + spin_unlock(&ht->lock); /* Wait for readers. All new readers will see the new * table, and thus no references to the old table will @@ -635,12 +637,12 @@ void rhashtable_walk_stop(struct rhashtable_iter *iter) ht = iter->ht; - mutex_lock(&ht->mutex); + spin_lock(&ht->lock); if (tbl->rehash < tbl->size) list_add(&iter->walker->list, &tbl->walkers); else iter->walker->tbl = NULL; - mutex_unlock(&ht->mutex); + spin_unlock(&ht->lock); iter->p = NULL; @@ -723,6 +725,7 @@ int rhashtable_init(struct rhashtable *ht, memset(ht, 0, sizeof(*ht)); mutex_init(&ht->mutex); + spin_lock_init(&ht->lock); memcpy(&ht->p, params, sizeof(*params)); if (params->min_size) -- cgit v1.2.3 From 449e056c76cc8c777f3f5c3fb51c197ba2300c0c Mon Sep 17 00:00:00 2001 From: Daniel Lezcano Date: Mon, 2 Feb 2015 16:32:45 +0100 Subject: ARM: cpuidle: Add a cpuidle ops structure to be used for DT The current state of the different cpuidle drivers is the different PM operations are passed via the platform_data using the platform driver paradigm. This approach allowed to split the low level PM code from the arch specific and the generic cpuidle code. Unfortunately there are complaints about this approach as, in the context of the single kernel image, we have multiple drivers loaded in memory for nothing and the platform driver is not adequate for cpuidle. This patch provides a common interface via cpuidle ops for all new cpuidle driver and a definition for the device tree. It will allow with the next patches to a have a common definition with ARM64 and share the same cpuidle driver. The code is optimized to use the __init section intensively in order to reduce the memory footprint after the driver is initialized and unify the function names with ARM64. Signed-off-by: Daniel Lezcano Acked-by: Kevin Hilman Acked-by: Rob Herring Acked-by: Catalin Marinas Tested-by: Lorenzo Pieralisi --- arch/arm/include/asm/cpuidle.h | 21 ++++++++++++ arch/arm/kernel/cpuidle.c | 72 +++++++++++++++++++++++++++++++++++++++ arch/arm64/include/asm/cpuidle.h | 5 ++- include/asm-generic/vmlinux.lds.h | 2 ++ 4 files changed, 99 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/arch/arm/include/asm/cpuidle.h b/arch/arm/include/asm/cpuidle.h index 348dc817b9f3..0f8424924902 100644 --- a/arch/arm/include/asm/cpuidle.h +++ b/arch/arm/include/asm/cpuidle.h @@ -27,4 +27,25 @@ static inline int arm_cpuidle_simple_enter(struct cpuidle_device *dev, */ #define ARM_CPUIDLE_WFI_STATE ARM_CPUIDLE_WFI_STATE_PWR(UINT_MAX) +struct device_node; + +struct cpuidle_ops { + int (*suspend)(int cpu, unsigned long arg); + int (*init)(struct device_node *, int cpu); +}; + +struct of_cpuidle_method { + const char *method; + struct cpuidle_ops *ops; +}; + +#define CPUIDLE_METHOD_OF_DECLARE(name, _method, _ops) \ + static const struct of_cpuidle_method __cpuidle_method_of_table_##name \ + __used __section(__cpuidle_method_of_table) \ + = { .method = _method, .ops = _ops } + +extern int arm_cpuidle_suspend(int index); + +extern int arm_cpuidle_init(int cpu); + #endif diff --git a/arch/arm/kernel/cpuidle.c b/arch/arm/kernel/cpuidle.c index 45969f89f05c..2b0dae3cd058 100644 --- a/arch/arm/kernel/cpuidle.c +++ b/arch/arm/kernel/cpuidle.c @@ -10,8 +10,17 @@ */ #include +#include +#include #include +extern struct of_cpuidle_method __cpuidle_method_of_table[]; + +static const struct of_cpuidle_method __cpuidle_method_of_table_sentinel + __used __section(__cpuidle_method_of_table_end); + +static struct cpuidle_ops cpuidle_ops[NR_CPUS]; + int arm_cpuidle_simple_enter(struct cpuidle_device *dev, struct cpuidle_driver *drv, int index) { @@ -19,3 +28,66 @@ int arm_cpuidle_simple_enter(struct cpuidle_device *dev, return index; } + +int arm_cpuidle_suspend(int index) +{ + int ret = -EOPNOTSUPP; + int cpu = smp_processor_id(); + + if (cpuidle_ops[cpu].suspend) + ret = cpuidle_ops[cpu].suspend(cpu, index); + + return ret; +} + +static struct cpuidle_ops *__init arm_cpuidle_get_ops(const char *method) +{ + struct of_cpuidle_method *m = __cpuidle_method_of_table; + + for (; m->method; m++) + if (!strcmp(m->method, method)) + return m->ops; + + return NULL; +} + +static int __init arm_cpuidle_read_ops(struct device_node *dn, int cpu) +{ + const char *enable_method; + struct cpuidle_ops *ops; + + enable_method = of_get_property(dn, "enable-method", NULL); + if (!enable_method) + return -ENOENT; + + ops = arm_cpuidle_get_ops(enable_method); + if (!ops) { + pr_warn("%s: unsupported enable-method property: %s\n", + dn->full_name, enable_method); + return -EOPNOTSUPP; + } + + cpuidle_ops[cpu] = *ops; /* structure copy */ + + pr_notice("cpuidle: enable-method property '%s'" + " found operations\n", enable_method); + + return 0; +} + +int __init arm_cpuidle_init(int cpu) +{ + struct device_node *cpu_node = of_cpu_device_node_get(cpu); + int ret; + + if (!cpu_node) + return -ENODEV; + + ret = arm_cpuidle_read_ops(cpu_node, cpu); + if (!ret && cpuidle_ops[cpu].init) + ret = cpuidle_ops[cpu].init(cpu_node, cpu); + + of_node_put(cpu_node); + + return ret; +} diff --git a/arch/arm64/include/asm/cpuidle.h b/arch/arm64/include/asm/cpuidle.h index c60643f14cda..460a38bb84b9 100644 --- a/arch/arm64/include/asm/cpuidle.h +++ b/arch/arm64/include/asm/cpuidle.h @@ -17,5 +17,8 @@ static inline int cpu_suspend(unsigned long arg) return -EOPNOTSUPP; } #endif - +static inline int arm_cpuidle_suspend(int index) +{ + return cpu_suspend(index); +} #endif diff --git a/include/asm-generic/vmlinux.lds.h b/include/asm-generic/vmlinux.lds.h index ac78910d7416..91c09305106d 100644 --- a/include/asm-generic/vmlinux.lds.h +++ b/include/asm-generic/vmlinux.lds.h @@ -167,6 +167,7 @@ #define IOMMU_OF_TABLES() OF_TABLE(CONFIG_OF_IOMMU, iommu) #define RESERVEDMEM_OF_TABLES() OF_TABLE(CONFIG_OF_RESERVED_MEM, reservedmem) #define CPU_METHOD_OF_TABLES() OF_TABLE(CONFIG_SMP, cpu_method) +#define CPUIDLE_METHOD_OF_TABLES() OF_TABLE(CONFIG_CPU_IDLE, cpuidle_method) #define EARLYCON_OF_TABLES() OF_TABLE(CONFIG_SERIAL_EARLYCON, earlycon) #define KERNEL_DTB() \ @@ -501,6 +502,7 @@ CLKSRC_OF_TABLES() \ IOMMU_OF_TABLES() \ CPU_METHOD_OF_TABLES() \ + CPUIDLE_METHOD_OF_TABLES() \ KERNEL_DTB() \ IRQCHIP_OF_MATCH_TABLE() \ EARLYCON_OF_TABLES() -- cgit v1.2.3 From 6321fd6b6f0942e986b297e8192febfc860b83b3 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Fri, 30 Jan 2015 12:36:12 +0100 Subject: drm: Remove unused DRM_MODE_OBJECT_BRIDGE With bridges now moving to a separate registry they are no longer DRM objects, hence this define is no longer needed. Signed-off-by: Thierry Reding --- include/drm/drm_crtc.h | 1 - 1 file changed, 1 deletion(-) (limited to 'include') diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index 920e21a8f3fd..f924b81d48ff 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h @@ -53,7 +53,6 @@ struct fence; #define DRM_MODE_OBJECT_FB 0xfbfbfbfb #define DRM_MODE_OBJECT_BLOB 0xbbbbbbbb #define DRM_MODE_OBJECT_PLANE 0xeeeeeeee -#define DRM_MODE_OBJECT_BRIDGE 0xbdbdbdbd #define DRM_MODE_OBJECT_ANY 0 struct drm_mode_object { -- cgit v1.2.3 From 0117ec1970c5fa9c566045e7df8db76acc8f150e Mon Sep 17 00:00:00 2001 From: Hannes Frederic Sowa Date: Mon, 23 Mar 2015 18:40:02 +0100 Subject: net: remove never used forwarding_accel_ops pointer from net_device Cc: John Fastabend Signed-off-by: Hannes Frederic Sowa Signed-off-by: David S. Miller --- include/linux/netdevice.h | 2 -- 1 file changed, 2 deletions(-) (limited to 'include') diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 5ae69e7df867..08c4ab37189f 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -1342,7 +1342,6 @@ enum netdev_priv_flags { * @netdev_ops: Includes several pointers to callbacks, * if one wants to override the ndo_*() functions * @ethtool_ops: Management operations - * @fwd_ops: Management operations * @header_ops: Includes callbacks for creating,parsing,caching,etc * of Layer 2 headers. * @@ -1551,7 +1550,6 @@ struct net_device { #endif const struct net_device_ops *netdev_ops; const struct ethtool_ops *ethtool_ops; - const struct forwarding_accel_ops *fwd_ops; #ifdef CONFIG_NET_SWITCHDEV const struct swdev_ops *swdev_ops; #endif -- cgit v1.2.3 From 44486b48b066330e0ed0a66478cc49f5975ec6c1 Mon Sep 17 00:00:00 2001 From: Luciano Coelho Date: Wed, 18 Mar 2015 18:38:26 +0200 Subject: wl12xx: use frequency instead of enumerations for pdata clocks Instead of defining an enumeration with the FW specific values for the different clock rates, use the actual frequency instead. Also add a boolean to specify whether the clock is XTAL or not. Change all board files to reflect this. Signed-off-by: Luciano Coelho [Eliad - small fixes, update board file changes] Signed-off-by: Eliad Peller Tested-by: Nikita Kiryanov Acked-by: Kalle Valo Acked-by: Sekhar Nori Signed-off-by: Tony Lindgren --- arch/arm/mach-davinci/board-da850-evm.c | 3 +- arch/arm/mach-omap2/pdata-quirks.c | 29 ++++++++-------- drivers/net/wireless/ti/wl12xx/main.c | 60 ++++++++++++++++++++++++++++++--- drivers/net/wireless/ti/wl12xx/wl12xx.h | 28 +++++++++++++++ include/linux/wl12xx.h | 27 ++------------- 5 files changed, 103 insertions(+), 44 deletions(-) (limited to 'include') diff --git a/arch/arm/mach-davinci/board-da850-evm.c b/arch/arm/mach-davinci/board-da850-evm.c index 916589ca8d44..853b941b710e 100644 --- a/arch/arm/mach-davinci/board-da850-evm.c +++ b/arch/arm/mach-davinci/board-da850-evm.c @@ -1386,7 +1386,8 @@ static const short da850_wl12xx_pins[] __initconst = { static struct wl12xx_platform_data da850_wl12xx_wlan_data __initdata = { .irq = -1, .irq_trigger = IRQ_TYPE_EDGE_RISING, - .board_ref_clock = WL12XX_REFCLOCK_38, + .ref_clock_freq = 38400000, + .ref_clock_xtal = false, }; static __init int da850_wl12xx_init(void) diff --git a/arch/arm/mach-omap2/pdata-quirks.c b/arch/arm/mach-omap2/pdata-quirks.c index e86fb0d55c59..cebdf8d0dc08 100644 --- a/arch/arm/mach-omap2/pdata-quirks.c +++ b/arch/arm/mach-omap2/pdata-quirks.c @@ -39,14 +39,14 @@ static struct twl4030_gpio_platform_data twl_gpio_auxdata; static struct wl12xx_platform_data wl12xx __initdata; -static void __init __used legacy_init_wl12xx(unsigned ref_clock, - unsigned tcxo_clock, +static void __init __used legacy_init_wl12xx(u32 ref_clock_freq, + u32 tcxo_clock_freq, int gpio) { int res; - wl12xx.board_ref_clock = ref_clock; - wl12xx.board_tcxo_clock = tcxo_clock; + wl12xx.ref_clock_freq = ref_clock_freq; + wl12xx.tcxo_clock_freq = tcxo_clock_freq; wl12xx.irq = gpio_to_irq(gpio); wl12xx.irq_trigger = IRQ_TYPE_LEVEL_HIGH; @@ -57,8 +57,8 @@ static void __init __used legacy_init_wl12xx(unsigned ref_clock, } } #else -static inline void legacy_init_wl12xx(unsigned ref_clock, - unsigned tcxo_clock, +static inline void legacy_init_wl12xx(u32 ref_clock_freq, + u32 tcxo_clock_freq, int gpio) { } @@ -130,7 +130,7 @@ static void __init omap3_sbc_t3730_twl_init(void) static void __init omap3_sbc_t3730_legacy_init(void) { omap3_sbc_t3x_usb_hub_init(167, "sb-t35 usb hub"); - legacy_init_wl12xx(WL12XX_REFCLOCK_38, 0, 136); + legacy_init_wl12xx(38400000, 0, 136); } static void __init omap3_sbc_t3530_legacy_init(void) @@ -175,12 +175,12 @@ static void __init omap3_igep0030_rev_g_legacy_init(void) static void __init omap3_evm_legacy_init(void) { hsmmc2_internal_input_clk(); - legacy_init_wl12xx(WL12XX_REFCLOCK_38, 0, 149); + legacy_init_wl12xx(38400000, 0, 149); } static void __init omap3_zoom_legacy_init(void) { - legacy_init_wl12xx(WL12XX_REFCLOCK_26, 0, 162); + legacy_init_wl12xx(26000000, 0, 162); } static void am35xx_enable_emac_int(void) @@ -247,7 +247,7 @@ static void __init omap3_sbc_t3517_legacy_init(void) am35xx_emac_reset(); hsmmc2_internal_input_clk(); omap3_sbc_t3517_wifi_init(); - legacy_init_wl12xx(WL12XX_REFCLOCK_38, 0, 145); + legacy_init_wl12xx(38400000, 0, 145); } static void __init am3517_evm_legacy_init(void) @@ -292,18 +292,17 @@ static void __init omap3_tao3530_legacy_init(void) #ifdef CONFIG_ARCH_OMAP4 static void __init omap4_sdp_legacy_init(void) { - legacy_init_wl12xx(WL12XX_REFCLOCK_26, - WL12XX_TCXOCLOCK_26, 53); + legacy_init_wl12xx(26000000, 26000000, 53); } static void __init omap4_panda_legacy_init(void) { - legacy_init_wl12xx(WL12XX_REFCLOCK_38, 0, 53); + legacy_init_wl12xx(38400000, 0, 53); } static void __init var_som_om44_legacy_init(void) { - legacy_init_wl12xx(WL12XX_REFCLOCK_38, 0, 41); + legacy_init_wl12xx(38400000, 0, 41); } #endif @@ -318,7 +317,7 @@ static struct iommu_platform_data omap4_iommu_pdata = { #ifdef CONFIG_SOC_AM33XX static void __init am335x_evmsk_legacy_init(void) { - legacy_init_wl12xx(WL12XX_REFCLOCK_38, 0, 31); + legacy_init_wl12xx(38400000, 0, 31); } #endif diff --git a/drivers/net/wireless/ti/wl12xx/main.c b/drivers/net/wireless/ti/wl12xx/main.c index 144d1f8ba473..b3f751f1e08f 100644 --- a/drivers/net/wireless/ti/wl12xx/main.c +++ b/drivers/net/wireless/ti/wl12xx/main.c @@ -1770,6 +1770,40 @@ wl12xx_iface_combinations[] = { }, }; +static const struct wl12xx_clock wl12xx_refclock_table[] = { + { 19200000, false, WL12XX_REFCLOCK_19 }, + { 26000000, false, WL12XX_REFCLOCK_26 }, + { 26000000, true, WL12XX_REFCLOCK_26_XTAL }, + { 38400000, false, WL12XX_REFCLOCK_38 }, + { 38400000, true, WL12XX_REFCLOCK_38_XTAL }, + { 52000000, false, WL12XX_REFCLOCK_52 }, + { 0, false, 0 } +}; + +static const struct wl12xx_clock wl12xx_tcxoclock_table[] = { + { 16368000, true, WL12XX_TCXOCLOCK_16_368 }, + { 16800000, true, WL12XX_TCXOCLOCK_16_8 }, + { 19200000, true, WL12XX_TCXOCLOCK_19_2 }, + { 26000000, true, WL12XX_TCXOCLOCK_26 }, + { 32736000, true, WL12XX_TCXOCLOCK_32_736 }, + { 33600000, true, WL12XX_TCXOCLOCK_33_6 }, + { 38400000, true, WL12XX_TCXOCLOCK_38_4 }, + { 52000000, true, WL12XX_TCXOCLOCK_52 }, + { 0, false, 0 } +}; + +static int wl12xx_get_clock_idx(const struct wl12xx_clock *table, + u32 freq, bool xtal) +{ + int i; + + for (i = 0; table[i].freq != 0; i++) + if ((table[i].freq == freq) && (table[i].xtal == xtal)) + return table[i].hw_idx; + + return -EINVAL; +} + static int wl12xx_setup(struct wl1271 *wl) { struct wl12xx_priv *priv = wl->priv; @@ -1799,7 +1833,17 @@ static int wl12xx_setup(struct wl1271 *wl) wl12xx_conf_init(wl); if (!fref_param) { - priv->ref_clock = pdata->board_ref_clock; + priv->ref_clock = wl12xx_get_clock_idx(wl12xx_refclock_table, + pdata->ref_clock_freq, + pdata->ref_clock_xtal); + if (priv->ref_clock < 0) { + wl1271_error("Invalid ref_clock frequency (%d Hz, %s)", + pdata->ref_clock_freq, + pdata->ref_clock_xtal ? + "XTAL" : "not XTAL"); + + return priv->ref_clock; + } } else { if (!strcmp(fref_param, "19.2")) priv->ref_clock = WL12XX_REFCLOCK_19; @@ -1817,9 +1861,17 @@ static int wl12xx_setup(struct wl1271 *wl) wl1271_error("Invalid fref parameter %s", fref_param); } - if (!tcxo_param) { - priv->tcxo_clock = pdata->board_tcxo_clock; - } else { + if (!tcxo_param && pdata->tcxo_clock_freq) { + priv->tcxo_clock = wl12xx_get_clock_idx(wl12xx_tcxoclock_table, + pdata->tcxo_clock_freq, + true); + if (priv->tcxo_clock < 0) { + wl1271_error("Invalid tcxo_clock frequency (%d Hz)", + pdata->tcxo_clock_freq); + + return priv->tcxo_clock; + } + } else if (tcxo_param) { if (!strcmp(tcxo_param, "19.2")) priv->tcxo_clock = WL12XX_TCXOCLOCK_19_2; else if (!strcmp(tcxo_param, "26")) diff --git a/drivers/net/wireless/ti/wl12xx/wl12xx.h b/drivers/net/wireless/ti/wl12xx/wl12xx.h index 75c92658bfea..5952e99ace1b 100644 --- a/drivers/net/wireless/ti/wl12xx/wl12xx.h +++ b/drivers/net/wireless/ti/wl12xx/wl12xx.h @@ -82,6 +82,34 @@ struct wl12xx_priv { struct wl127x_rx_mem_pool_addr *rx_mem_addr; }; +/* Reference clock values */ +enum { + WL12XX_REFCLOCK_19 = 0, /* 19.2 MHz */ + WL12XX_REFCLOCK_26 = 1, /* 26 MHz */ + WL12XX_REFCLOCK_38 = 2, /* 38.4 MHz */ + WL12XX_REFCLOCK_52 = 3, /* 52 MHz */ + WL12XX_REFCLOCK_38_XTAL = 4, /* 38.4 MHz, XTAL */ + WL12XX_REFCLOCK_26_XTAL = 5, /* 26 MHz, XTAL */ +}; + +/* TCXO clock values */ +enum { + WL12XX_TCXOCLOCK_19_2 = 0, /* 19.2MHz */ + WL12XX_TCXOCLOCK_26 = 1, /* 26 MHz */ + WL12XX_TCXOCLOCK_38_4 = 2, /* 38.4MHz */ + WL12XX_TCXOCLOCK_52 = 3, /* 52 MHz */ + WL12XX_TCXOCLOCK_16_368 = 4, /* 16.368 MHz */ + WL12XX_TCXOCLOCK_32_736 = 5, /* 32.736 MHz */ + WL12XX_TCXOCLOCK_16_8 = 6, /* 16.8 MHz */ + WL12XX_TCXOCLOCK_33_6 = 7, /* 33.6 MHz */ +}; + +struct wl12xx_clock { + u32 freq; + bool xtal; + u8 hw_idx; +}; + struct wl12xx_fw_packet_counters { /* Cumulative counter of released packets per AC */ u8 tx_released_pkts[NUM_TX_QUEUES]; diff --git a/include/linux/wl12xx.h b/include/linux/wl12xx.h index 3876b67dbcbc..eea1e6dac4be 100644 --- a/include/linux/wl12xx.h +++ b/include/linux/wl12xx.h @@ -26,28 +26,6 @@ #include -/* Reference clock values */ -enum { - WL12XX_REFCLOCK_19 = 0, /* 19.2 MHz */ - WL12XX_REFCLOCK_26 = 1, /* 26 MHz */ - WL12XX_REFCLOCK_38 = 2, /* 38.4 MHz */ - WL12XX_REFCLOCK_52 = 3, /* 52 MHz */ - WL12XX_REFCLOCK_38_XTAL = 4, /* 38.4 MHz, XTAL */ - WL12XX_REFCLOCK_26_XTAL = 5, /* 26 MHz, XTAL */ -}; - -/* TCXO clock values */ -enum { - WL12XX_TCXOCLOCK_19_2 = 0, /* 19.2MHz */ - WL12XX_TCXOCLOCK_26 = 1, /* 26 MHz */ - WL12XX_TCXOCLOCK_38_4 = 2, /* 38.4MHz */ - WL12XX_TCXOCLOCK_52 = 3, /* 52 MHz */ - WL12XX_TCXOCLOCK_16_368 = 4, /* 16.368 MHz */ - WL12XX_TCXOCLOCK_32_736 = 5, /* 32.736 MHz */ - WL12XX_TCXOCLOCK_16_8 = 6, /* 16.8 MHz */ - WL12XX_TCXOCLOCK_33_6 = 7, /* 33.6 MHz */ -}; - struct wl1251_platform_data { int power_gpio; /* SDIO only: IRQ number if WLAN_IRQ line is used, 0 for SDIO IRQs */ @@ -58,8 +36,9 @@ struct wl1251_platform_data { struct wl12xx_platform_data { int irq; u32 irq_trigger; - int board_ref_clock; - int board_tcxo_clock; + bool ref_clock_xtal; /* specify whether the clock is XTAL or not */ + u32 ref_clock_freq; /* in Hertz */ + u32 tcxo_clock_freq; /* in Hertz, tcxo is always XTAL */ bool pwr_in_suspend; }; -- cgit v1.2.3 From 83c3a7d4ac7fdc29a64bf9a5467a36b4c72a1eed Mon Sep 17 00:00:00 2001 From: Eliad Peller Date: Wed, 18 Mar 2015 18:38:30 +0200 Subject: wlcore: remove wl12xx_platform_data Now that we have wlcore device-tree bindings in place (for both wl12xx and wl18xx), remove the legacy wl12xx_platform_data struct, and move its members into the platform device data (that is passed to wlcore) Davinci 850 is the only platform that still set the platform data in the legacy way (and doesn't have DT bindings), so remove the relevant code/Kconfig option from the board file (as suggested by Sekhar Nori) Since no one currently uses wlcore_spi, simply remove its platform data support (DT bindings will have to be added if someone actually needs it) Signed-off-by: Luciano Coelho Signed-off-by: Eliad Peller Tested-by: Nikita Kiryanov Acked-by: Kalle Valo Acked-by: Sekhar Nori Signed-off-by: Tony Lindgren --- arch/arm/mach-davinci/Kconfig | 11 --- arch/arm/mach-davinci/board-da850-evm.c | 113 ------------------------- drivers/net/wireless/ti/wilink_platform_data.c | 25 ------ drivers/net/wireless/ti/wl12xx/main.c | 19 ++--- drivers/net/wireless/ti/wlcore/boot.c | 1 - drivers/net/wireless/ti/wlcore/main.c | 4 +- drivers/net/wireless/ti/wlcore/sdio.c | 76 +++++------------ drivers/net/wireless/ti/wlcore/spi.c | 6 +- drivers/net/wireless/ti/wlcore/wlcore_i.h | 6 +- include/linux/wl12xx.h | 25 ------ 10 files changed, 35 insertions(+), 251 deletions(-) (limited to 'include') diff --git a/arch/arm/mach-davinci/Kconfig b/arch/arm/mach-davinci/Kconfig index cd30f6f5f2ff..dd8f5312b2c0 100644 --- a/arch/arm/mach-davinci/Kconfig +++ b/arch/arm/mach-davinci/Kconfig @@ -200,17 +200,6 @@ config DA850_UI_SD_VIDEO_PORT endchoice -config DA850_WL12XX - bool "AM18x wl1271 daughter board" - depends on MACH_DAVINCI_DA850_EVM - help - The wl1271 daughter card for AM18x EVMs is a combo wireless - connectivity add-on card, based on the LS Research TiWi module with - Texas Instruments' wl1271 solution. - Say Y if you want to use a wl1271 expansion card connected to the - AM18x EVM. - - config MACH_MITYOMAPL138 bool "Critical Link MityDSP-L138/MityARM-1808 SoM" depends on ARCH_DAVINCI_DA850 diff --git a/arch/arm/mach-davinci/board-da850-evm.c b/arch/arm/mach-davinci/board-da850-evm.c index 853b941b710e..1ed545cc2b83 100644 --- a/arch/arm/mach-davinci/board-da850-evm.c +++ b/arch/arm/mach-davinci/board-da850-evm.c @@ -38,7 +38,6 @@ #include #include #include -#include #include #include @@ -60,9 +59,6 @@ #define DA850_MMCSD_CD_PIN GPIO_TO_PIN(4, 0) #define DA850_MMCSD_WP_PIN GPIO_TO_PIN(4, 1) -#define DA850_WLAN_EN GPIO_TO_PIN(6, 9) -#define DA850_WLAN_IRQ GPIO_TO_PIN(6, 10) - #define DA850_MII_MDIO_CLKEN_PIN GPIO_TO_PIN(2, 6) static struct mtd_partition da850evm_spiflash_part[] = { @@ -1343,110 +1339,6 @@ static __init void da850_vpif_init(void) static __init void da850_vpif_init(void) {} #endif -#ifdef CONFIG_DA850_WL12XX - -static void wl12xx_set_power(int index, bool power_on) -{ - static bool power_state; - - pr_debug("Powering %s wl12xx", power_on ? "on" : "off"); - - if (power_on == power_state) - return; - power_state = power_on; - - if (power_on) { - /* Power up sequence required for wl127x devices */ - gpio_set_value(DA850_WLAN_EN, 1); - usleep_range(15000, 15000); - gpio_set_value(DA850_WLAN_EN, 0); - usleep_range(1000, 1000); - gpio_set_value(DA850_WLAN_EN, 1); - msleep(70); - } else { - gpio_set_value(DA850_WLAN_EN, 0); - } -} - -static struct davinci_mmc_config da850_wl12xx_mmc_config = { - .set_power = wl12xx_set_power, - .wires = 4, - .max_freq = 25000000, - .caps = MMC_CAP_4_BIT_DATA | MMC_CAP_NONREMOVABLE | - MMC_CAP_POWER_OFF_CARD, -}; - -static const short da850_wl12xx_pins[] __initconst = { - DA850_MMCSD1_DAT_0, DA850_MMCSD1_DAT_1, DA850_MMCSD1_DAT_2, - DA850_MMCSD1_DAT_3, DA850_MMCSD1_CLK, DA850_MMCSD1_CMD, - DA850_GPIO6_9, DA850_GPIO6_10, - -1 -}; - -static struct wl12xx_platform_data da850_wl12xx_wlan_data __initdata = { - .irq = -1, - .irq_trigger = IRQ_TYPE_EDGE_RISING, - .ref_clock_freq = 38400000, - .ref_clock_xtal = false, -}; - -static __init int da850_wl12xx_init(void) -{ - int ret; - - ret = davinci_cfg_reg_list(da850_wl12xx_pins); - if (ret) { - pr_err("wl12xx/mmc mux setup failed: %d\n", ret); - goto exit; - } - - ret = da850_register_mmcsd1(&da850_wl12xx_mmc_config); - if (ret) { - pr_err("wl12xx/mmc registration failed: %d\n", ret); - goto exit; - } - - ret = gpio_request_one(DA850_WLAN_EN, GPIOF_OUT_INIT_LOW, "wl12xx_en"); - if (ret) { - pr_err("Could not request wl12xx enable gpio: %d\n", ret); - goto exit; - } - - ret = gpio_request_one(DA850_WLAN_IRQ, GPIOF_IN, "wl12xx_irq"); - if (ret) { - pr_err("Could not request wl12xx irq gpio: %d\n", ret); - goto free_wlan_en; - } - - da850_wl12xx_wlan_data.irq = gpio_to_irq(DA850_WLAN_IRQ); - - ret = wl12xx_set_platform_data(&da850_wl12xx_wlan_data); - if (ret) { - pr_err("Could not set wl12xx data: %d\n", ret); - goto free_wlan_irq; - } - - return 0; - -free_wlan_irq: - gpio_free(DA850_WLAN_IRQ); - -free_wlan_en: - gpio_free(DA850_WLAN_EN); - -exit: - return ret; -} - -#else /* CONFIG_DA850_WL12XX */ - -static __init int da850_wl12xx_init(void) -{ - return 0; -} - -#endif /* CONFIG_DA850_WL12XX */ - #define DA850EVM_SATA_REFCLKPN_RATE (100 * 1000 * 1000) static __init void da850_evm_init(void) @@ -1503,11 +1395,6 @@ static __init void da850_evm_init(void) if (ret) pr_warn("%s: MMCSD0 registration failed: %d\n", __func__, ret); - - ret = da850_wl12xx_init(); - if (ret) - pr_warn("%s: WL12xx initialization failed: %d\n", - __func__, ret); } davinci_serial_init(da8xx_serial_device); diff --git a/drivers/net/wireless/ti/wilink_platform_data.c b/drivers/net/wireless/ti/wilink_platform_data.c index a92bd3e89796..ea0e359bdb43 100644 --- a/drivers/net/wireless/ti/wilink_platform_data.c +++ b/drivers/net/wireless/ti/wilink_platform_data.c @@ -23,31 +23,6 @@ #include #include -static struct wl12xx_platform_data *wl12xx_platform_data; - -int __init wl12xx_set_platform_data(const struct wl12xx_platform_data *data) -{ - if (wl12xx_platform_data) - return -EBUSY; - if (!data) - return -EINVAL; - - wl12xx_platform_data = kmemdup(data, sizeof(*data), GFP_KERNEL); - if (!wl12xx_platform_data) - return -ENOMEM; - - return 0; -} - -struct wl12xx_platform_data *wl12xx_get_platform_data(void) -{ - if (!wl12xx_platform_data) - return ERR_PTR(-ENODEV); - - return wl12xx_platform_data; -} -EXPORT_SYMBOL(wl12xx_get_platform_data); - static struct wl1251_platform_data *wl1251_platform_data; int __init wl1251_set_platform_data(const struct wl1251_platform_data *data) diff --git a/drivers/net/wireless/ti/wl12xx/main.c b/drivers/net/wireless/ti/wl12xx/main.c index b3f751f1e08f..af0fe2e17151 100644 --- a/drivers/net/wireless/ti/wl12xx/main.c +++ b/drivers/net/wireless/ti/wl12xx/main.c @@ -24,8 +24,6 @@ #include -#include - #include "../wlcore/wlcore.h" #include "../wlcore/debug.h" #include "../wlcore/io.h" @@ -1808,7 +1806,6 @@ static int wl12xx_setup(struct wl1271 *wl) { struct wl12xx_priv *priv = wl->priv; struct wlcore_platdev_data *pdev_data = dev_get_platdata(&wl->pdev->dev); - struct wl12xx_platform_data *pdata = pdev_data->pdata; BUILD_BUG_ON(WL12XX_MAX_LINKS > WLCORE_MAX_LINKS); BUILD_BUG_ON(WL12XX_MAX_AP_STATIONS > WL12XX_MAX_LINKS); @@ -1834,12 +1831,12 @@ static int wl12xx_setup(struct wl1271 *wl) if (!fref_param) { priv->ref_clock = wl12xx_get_clock_idx(wl12xx_refclock_table, - pdata->ref_clock_freq, - pdata->ref_clock_xtal); + pdev_data->ref_clock_freq, + pdev_data->ref_clock_xtal); if (priv->ref_clock < 0) { wl1271_error("Invalid ref_clock frequency (%d Hz, %s)", - pdata->ref_clock_freq, - pdata->ref_clock_xtal ? + pdev_data->ref_clock_freq, + pdev_data->ref_clock_xtal ? "XTAL" : "not XTAL"); return priv->ref_clock; @@ -1861,13 +1858,13 @@ static int wl12xx_setup(struct wl1271 *wl) wl1271_error("Invalid fref parameter %s", fref_param); } - if (!tcxo_param && pdata->tcxo_clock_freq) { + if (!tcxo_param && pdev_data->tcxo_clock_freq) { priv->tcxo_clock = wl12xx_get_clock_idx(wl12xx_tcxoclock_table, - pdata->tcxo_clock_freq, - true); + pdev_data->tcxo_clock_freq, + true); if (priv->tcxo_clock < 0) { wl1271_error("Invalid tcxo_clock frequency (%d Hz)", - pdata->tcxo_clock_freq); + pdev_data->tcxo_clock_freq); return priv->tcxo_clock; } diff --git a/drivers/net/wireless/ti/wlcore/boot.c b/drivers/net/wireless/ti/wlcore/boot.c index 77752b03f189..19b7ec7b69c2 100644 --- a/drivers/net/wireless/ti/wlcore/boot.c +++ b/drivers/net/wireless/ti/wlcore/boot.c @@ -22,7 +22,6 @@ */ #include -#include #include #include "debug.h" diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c index 67518f692cfc..0be807951afe 100644 --- a/drivers/net/wireless/ti/wlcore/main.c +++ b/drivers/net/wireless/ti/wlcore/main.c @@ -25,7 +25,6 @@ #include #include #include -#include #include #include @@ -6390,7 +6389,6 @@ static void wlcore_nvs_cb(const struct firmware *fw, void *context) struct wl1271 *wl = context; struct platform_device *pdev = wl->pdev; struct wlcore_platdev_data *pdev_data = dev_get_platdata(&pdev->dev); - struct wl12xx_platform_data *pdata = pdev_data->pdata; struct resource *res; int ret; @@ -6446,7 +6444,7 @@ static void wlcore_nvs_cb(const struct firmware *fw, void *context) if (!ret) { wl->irq_wake_enabled = true; device_init_wakeup(wl->dev, 1); - if (pdata->pwr_in_suspend) + if (pdev_data->pwr_in_suspend) wl->hw->wiphy->wowlan = &wlcore_wowlan_support; } #endif diff --git a/drivers/net/wireless/ti/wlcore/sdio.c b/drivers/net/wireless/ti/wlcore/sdio.c index b55dc0ede513..ea7e07abca4e 100644 --- a/drivers/net/wireless/ti/wlcore/sdio.c +++ b/drivers/net/wireless/ti/wlcore/sdio.c @@ -31,7 +31,6 @@ #include #include #include -#include #include #include #include @@ -231,67 +230,37 @@ static const struct of_device_id wlcore_sdio_of_match_table[] = { { } }; -static struct wl12xx_platform_data *wlcore_probe_of(struct device *dev) +static int wlcore_probe_of(struct device *dev, int *irq, + struct wlcore_platdev_data *pdev_data) { struct device_node *np = dev->of_node; - struct wl12xx_platform_data *pdata; if (!np || !of_match_node(wlcore_sdio_of_match_table, np)) - return NULL; + return -ENODATA; - pdata = kzalloc(sizeof(*pdata), GFP_KERNEL); - if (!pdata) - return NULL; - - pdata->irq = irq_of_parse_and_map(np, 0); - if (!pdata->irq) { + *irq = irq_of_parse_and_map(np, 0); + if (!*irq) { dev_err(dev, "No irq in platform data\n"); - kfree(pdata); - return NULL; + kfree(pdev_data); + return -EINVAL; } - pdata->irq_trigger = - irqd_get_trigger_type(irq_get_irq_data(pdata->irq)); - /* optional clock frequency params */ of_property_read_u32(np, "ref-clock-frequency", - &pdata->ref_clock_freq); + &pdev_data->ref_clock_freq); of_property_read_u32(np, "tcxo-clock-frequency", - &pdata->tcxo_clock_freq); + &pdev_data->tcxo_clock_freq); - return pdata; + return 0; } #else -static struct wl12xx_platform_data *wlcore_probe_of(struct device *dev) +static int wlcore_probe_of(struct device *dev, int *irq, + struct wlcore_platdev_data *pdev_data) { - return NULL; + return -ENODATA; } #endif -static struct wl12xx_platform_data * -wlcore_get_platform_data(struct device *dev) -{ - struct wl12xx_platform_data *pdata; - - /* first, look for DT data */ - pdata = wlcore_probe_of(dev); - if (pdata) - return pdata; - - /* if not found - fallback to static platform data */ - pdata = wl12xx_get_platform_data(); - if (!IS_ERR(pdata)) - return kmemdup(pdata, sizeof(*pdata), GFP_KERNEL); - - dev_err(dev, "No platform data set\n"); - return NULL; -} - -static void wlcore_del_platform_data(struct wl12xx_platform_data *pdata) -{ - kfree(pdata); -} - static int wl1271_probe(struct sdio_func *func, const struct sdio_device_id *id) { @@ -300,6 +269,7 @@ static int wl1271_probe(struct sdio_func *func, struct resource res[1]; mmc_pm_flag_t mmcflags; int ret = -ENOMEM; + int irq; const char *chip_family; /* We are only able to handle the wlan function */ @@ -323,8 +293,7 @@ static int wl1271_probe(struct sdio_func *func, /* Use block mode for transferring over one block size of data */ func->card->quirks |= MMC_QUIRK_BLKSZ_FOR_BYTE_MODE; - pdev_data.pdata = wlcore_get_platform_data(&func->dev); - if (!pdev_data.pdata) + if (wlcore_probe_of(&func->dev, &irq, &pdev_data)) goto out_free_glue; /* if sdio can keep power while host is suspended, enable wow */ @@ -332,7 +301,7 @@ static int wl1271_probe(struct sdio_func *func, dev_dbg(glue->dev, "sdio PM caps = 0x%x\n", mmcflags); if (mmcflags & MMC_PM_KEEP_POWER) - pdev_data.pdata->pwr_in_suspend = true; + pdev_data.pwr_in_suspend = true; sdio_set_drvdata(func, glue); @@ -354,15 +323,16 @@ static int wl1271_probe(struct sdio_func *func, if (!glue->core) { dev_err(glue->dev, "can't allocate platform_device"); ret = -ENOMEM; - goto out_free_pdata; + goto out_free_glue; } glue->core->dev.parent = &func->dev; memset(res, 0x00, sizeof(res)); - res[0].start = pdev_data.pdata->irq; - res[0].flags = IORESOURCE_IRQ | pdev_data.pdata->irq_trigger; + res[0].start = irq; + res[0].flags = IORESOURCE_IRQ | + irqd_get_trigger_type(irq_get_irq_data(irq)); res[0].name = "irq"; ret = platform_device_add_resources(glue->core, res, ARRAY_SIZE(res)); @@ -388,9 +358,6 @@ static int wl1271_probe(struct sdio_func *func, out_dev_put: platform_device_put(glue->core); -out_free_pdata: - wlcore_del_platform_data(pdev_data.pdata); - out_free_glue: kfree(glue); @@ -401,14 +368,11 @@ out: static void wl1271_remove(struct sdio_func *func) { struct wl12xx_sdio_glue *glue = sdio_get_drvdata(func); - struct wlcore_platdev_data *pdev_data = glue->core->dev.platform_data; - struct wl12xx_platform_data *pdata = pdev_data->pdata; /* Undo decrement done above in wl1271_probe */ pm_runtime_get_noresume(&func->dev); platform_device_unregister(glue->core); - wlcore_del_platform_data(pdata); kfree(glue); } diff --git a/drivers/net/wireless/ti/wlcore/spi.c b/drivers/net/wireless/ti/wlcore/spi.c index 69601f6741d9..f1ac2839d97c 100644 --- a/drivers/net/wireless/ti/wlcore/spi.c +++ b/drivers/net/wireless/ti/wlcore/spi.c @@ -331,11 +331,7 @@ static int wl1271_probe(struct spi_device *spi) memset(&pdev_data, 0x00, sizeof(pdev_data)); - pdev_data.pdata = dev_get_platdata(&spi->dev); - if (!pdev_data.pdata) { - dev_err(&spi->dev, "no platform data\n"); - return -ENODEV; - } + /* TODO: add DT parsing when needed */ pdev_data.if_ops = &spi_ops; diff --git a/drivers/net/wireless/ti/wlcore/wlcore_i.h b/drivers/net/wireless/ti/wlcore/wlcore_i.h index 3396ce5a934d..39efc6d78b10 100644 --- a/drivers/net/wireless/ti/wlcore/wlcore_i.h +++ b/drivers/net/wireless/ti/wlcore/wlcore_i.h @@ -201,8 +201,12 @@ struct wl1271_if_operations { }; struct wlcore_platdev_data { - struct wl12xx_platform_data *pdata; struct wl1271_if_operations *if_ops; + + bool ref_clock_xtal; /* specify whether the clock is XTAL or not */ + u32 ref_clock_freq; /* in Hertz */ + u32 tcxo_clock_freq; /* in Hertz, tcxo is always XTAL */ + bool pwr_in_suspend; }; #define MAX_NUM_KEYS 14 diff --git a/include/linux/wl12xx.h b/include/linux/wl12xx.h index eea1e6dac4be..95704cd4cfab 100644 --- a/include/linux/wl12xx.h +++ b/include/linux/wl12xx.h @@ -33,39 +33,14 @@ struct wl1251_platform_data { bool use_eeprom; }; -struct wl12xx_platform_data { - int irq; - u32 irq_trigger; - bool ref_clock_xtal; /* specify whether the clock is XTAL or not */ - u32 ref_clock_freq; /* in Hertz */ - u32 tcxo_clock_freq; /* in Hertz, tcxo is always XTAL */ - bool pwr_in_suspend; -}; - #ifdef CONFIG_WILINK_PLATFORM_DATA -int wl12xx_set_platform_data(const struct wl12xx_platform_data *data); - -struct wl12xx_platform_data *wl12xx_get_platform_data(void); - int wl1251_set_platform_data(const struct wl1251_platform_data *data); struct wl1251_platform_data *wl1251_get_platform_data(void); #else -static inline -int wl12xx_set_platform_data(const struct wl12xx_platform_data *data) -{ - return -ENOSYS; -} - -static inline -struct wl12xx_platform_data *wl12xx_get_platform_data(void) -{ - return ERR_PTR(-ENODATA); -} - static inline int wl1251_set_platform_data(const struct wl1251_platform_data *data) { -- cgit v1.2.3 From 27cd5452476978283decb19e429e81fc6c71e74b Mon Sep 17 00:00:00 2001 From: Michal Sekletar Date: Tue, 24 Mar 2015 14:48:41 +0100 Subject: filter: introduce SKF_AD_VLAN_TPID BPF extension If vlan offloading takes place then vlan header is removed from frame and its contents, both vlan_tci and vlan_proto, is available to user space via TPACKET interface. However, only vlan_tci can be used in BPF filters. This commit introduces a new BPF extension. It makes possible to load the value of vlan_proto (vlan TPID) to register A. Support for classic BPF and eBPF is being added, analogous to skb->protocol. Cc: Daniel Borkmann Cc: Alexei Starovoitov Cc: Jiri Pirko Signed-off-by: Michal Sekletar Acked-by: Daniel Borkmann Acked-by: Alexei Starovoitov Reviewed-by: Jiri Pirko Signed-off-by: David S. Miller --- Documentation/networking/filter.txt | 3 ++- include/linux/filter.h | 1 + include/uapi/linux/bpf.h | 1 + include/uapi/linux/filter.h | 3 ++- net/core/filter.c | 17 +++++++++++++++++ tools/net/bpf_exp.l | 2 ++ tools/net/bpf_exp.y | 11 ++++++++++- 7 files changed, 35 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/Documentation/networking/filter.txt b/Documentation/networking/filter.txt index 9930ecfbb465..135581f015e1 100644 --- a/Documentation/networking/filter.txt +++ b/Documentation/networking/filter.txt @@ -280,7 +280,8 @@ Possible BPF extensions are shown in the following table: rxhash skb->hash cpu raw_smp_processor_id() vlan_tci skb_vlan_tag_get(skb) - vlan_pr skb_vlan_tag_present(skb) + vlan_avail skb_vlan_tag_present(skb) + vlan_tpid skb->vlan_proto rand prandom_u32() These extensions can also be prefixed with '#'. diff --git a/include/linux/filter.h b/include/linux/filter.h index 9ee8c67ea249..fa11b3a367be 100644 --- a/include/linux/filter.h +++ b/include/linux/filter.h @@ -454,6 +454,7 @@ static inline u16 bpf_anc_helper(const struct sock_filter *ftest) BPF_ANCILLARY(VLAN_TAG_PRESENT); BPF_ANCILLARY(PAY_OFFSET); BPF_ANCILLARY(RANDOM); + BPF_ANCILLARY(VLAN_TPID); } /* Fallthrough. */ default: diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h index 3dd314a45d0d..27dc4ec58840 100644 --- a/include/uapi/linux/bpf.h +++ b/include/uapi/linux/bpf.h @@ -182,6 +182,7 @@ struct __sk_buff { __u32 protocol; __u32 vlan_present; __u32 vlan_tci; + __u32 vlan_proto; }; #endif /* _UAPI__LINUX_BPF_H__ */ diff --git a/include/uapi/linux/filter.h b/include/uapi/linux/filter.h index 47785d5ecf17..34c7936ca114 100644 --- a/include/uapi/linux/filter.h +++ b/include/uapi/linux/filter.h @@ -77,7 +77,8 @@ struct sock_fprog { /* Required for SO_ATTACH_FILTER. */ #define SKF_AD_VLAN_TAG_PRESENT 48 #define SKF_AD_PAY_OFFSET 52 #define SKF_AD_RANDOM 56 -#define SKF_AD_MAX 60 +#define SKF_AD_VLAN_TPID 60 +#define SKF_AD_MAX 64 #define SKF_NET_OFF (-0x100000) #define SKF_LL_OFF (-0x200000) diff --git a/net/core/filter.c b/net/core/filter.c index 084eacc4d1d4..32f43c59908c 100644 --- a/net/core/filter.c +++ b/net/core/filter.c @@ -272,6 +272,16 @@ static bool convert_bpf_extensions(struct sock_filter *fp, insn += cnt - 1; break; + case SKF_AD_OFF + SKF_AD_VLAN_TPID: + BUILD_BUG_ON(FIELD_SIZEOF(struct sk_buff, vlan_proto) != 2); + + /* A = *(u16 *) (CTX + offsetof(vlan_proto)) */ + *insn++ = BPF_LDX_MEM(BPF_H, BPF_REG_A, BPF_REG_CTX, + offsetof(struct sk_buff, vlan_proto)); + /* A = ntohs(A) [emitting a nop or swap16] */ + *insn = BPF_ENDIAN(BPF_FROM_BE, BPF_REG_A, 16); + break; + case SKF_AD_OFF + SKF_AD_PAY_OFFSET: case SKF_AD_OFF + SKF_AD_NLATTR: case SKF_AD_OFF + SKF_AD_NLATTR_NEST: @@ -1226,6 +1236,13 @@ static u32 sk_filter_convert_ctx_access(int dst_reg, int src_reg, int ctx_off, offsetof(struct sk_buff, protocol)); break; + case offsetof(struct __sk_buff, vlan_proto): + BUILD_BUG_ON(FIELD_SIZEOF(struct sk_buff, vlan_proto) != 2); + + *insn++ = BPF_LDX_MEM(BPF_H, dst_reg, src_reg, + offsetof(struct sk_buff, vlan_proto)); + break; + case offsetof(struct __sk_buff, mark): return convert_skb_access(SKF_AD_MARK, dst_reg, src_reg, insn); diff --git a/tools/net/bpf_exp.l b/tools/net/bpf_exp.l index 833a96611da6..c83af3fb77de 100644 --- a/tools/net/bpf_exp.l +++ b/tools/net/bpf_exp.l @@ -92,6 +92,8 @@ extern void yyerror(const char *str); "#"?("cpu") { return K_CPU; } "#"?("vlan_tci") { return K_VLANT; } "#"?("vlan_pr") { return K_VLANP; } +"#"?("vlan_avail") { return K_VLANP; } +"#"?("vlan_tpid") { return K_VLANTPID; } "#"?("rand") { return K_RAND; } ":" { return ':'; } diff --git a/tools/net/bpf_exp.y b/tools/net/bpf_exp.y index e6306c51c26f..f8332749b44c 100644 --- a/tools/net/bpf_exp.y +++ b/tools/net/bpf_exp.y @@ -56,7 +56,7 @@ static void bpf_set_jmp_label(char *label, enum jmp_type type); %token OP_LDXI %token K_PKT_LEN K_PROTO K_TYPE K_NLATTR K_NLATTR_NEST K_MARK K_QUEUE K_HATYPE -%token K_RXHASH K_CPU K_IFIDX K_VLANT K_VLANP K_POFF K_RAND +%token K_RXHASH K_CPU K_IFIDX K_VLANT K_VLANP K_VLANTPID K_POFF K_RAND %token ':' ',' '[' ']' '(' ')' 'x' 'a' '+' 'M' '*' '&' '#' '%' @@ -167,6 +167,9 @@ ldb | OP_LDB K_RAND { bpf_set_curr_instr(BPF_LD | BPF_B | BPF_ABS, 0, 0, SKF_AD_OFF + SKF_AD_RANDOM); } + | OP_LDB K_VLANTPID { + bpf_set_curr_instr(BPF_LD | BPF_B | BPF_ABS, 0, 0, + SKF_AD_OFF + SKF_AD_VLAN_TPID); } ; ldh @@ -218,6 +221,9 @@ ldh | OP_LDH K_RAND { bpf_set_curr_instr(BPF_LD | BPF_H | BPF_ABS, 0, 0, SKF_AD_OFF + SKF_AD_RANDOM); } + | OP_LDH K_VLANTPID { + bpf_set_curr_instr(BPF_LD | BPF_H | BPF_ABS, 0, 0, + SKF_AD_OFF + SKF_AD_VLAN_TPID); } ; ldi @@ -274,6 +280,9 @@ ld | OP_LD K_RAND { bpf_set_curr_instr(BPF_LD | BPF_W | BPF_ABS, 0, 0, SKF_AD_OFF + SKF_AD_RANDOM); } + | OP_LD K_VLANTPID { + bpf_set_curr_instr(BPF_LD | BPF_W | BPF_ABS, 0, 0, + SKF_AD_OFF + SKF_AD_VLAN_TPID); } | OP_LD 'M' '[' number ']' { bpf_set_curr_instr(BPF_LD | BPF_MEM, 0, 0, $4); } | OP_LD '[' 'x' '+' number ']' { -- cgit v1.2.3 From 58be8a583d8d316448bafa5926414cfb83c02dec Mon Sep 17 00:00:00 2001 From: Thomas Graf Date: Tue, 24 Mar 2015 14:18:16 +0100 Subject: rhashtable: Extend RCU read lock into rhashtable_insert_rehash() rhashtable_insert_rehash() requires RCU locks to be held in order to access ht->tbl and traverse to the last table. Fixes: ccd57b1bd324 ("rhashtable: Add immediate rehash during insertion") Signed-off-by: Thomas Graf Signed-off-by: David S. Miller --- include/linux/rhashtable.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/rhashtable.h b/include/linux/rhashtable.h index d7be9cb0e91f..5976ab59b88f 100644 --- a/include/linux/rhashtable.h +++ b/include/linux/rhashtable.h @@ -585,8 +585,8 @@ restart: if (unlikely(rht_grow_above_100(ht, tbl))) { slow_path: spin_unlock_bh(lock); - rcu_read_unlock(); err = rhashtable_insert_rehash(ht); + rcu_read_unlock(); if (err) return err; -- cgit v1.2.3 From 299e5c32a37a6bca8175db177117467bd1ce970a Mon Sep 17 00:00:00 2001 From: Thomas Graf Date: Tue, 24 Mar 2015 14:18:17 +0100 Subject: rhashtable: Use 'unsigned int' consistently Signed-off-by: Thomas Graf Signed-off-by: David S. Miller --- include/linux/rhashtable.h | 14 +++++++------- lib/rhashtable.c | 18 ++++++++++-------- 2 files changed, 17 insertions(+), 15 deletions(-) (limited to 'include') diff --git a/include/linux/rhashtable.h b/include/linux/rhashtable.h index 5976ab59b88f..f89cda067cb9 100644 --- a/include/linux/rhashtable.h +++ b/include/linux/rhashtable.h @@ -208,13 +208,13 @@ static inline unsigned int rht_key_hashfn( struct rhashtable *ht, const struct bucket_table *tbl, const void *key, const struct rhashtable_params params) { - unsigned hash; + unsigned int hash; /* params must be equal to ht->p if it isn't constant. */ if (!__builtin_constant_p(params.key_len)) hash = ht->p.hashfn(key, ht->key_len, tbl->hash_rnd); else if (params.key_len) { - unsigned key_len = params.key_len; + unsigned int key_len = params.key_len; if (params.hashfn) hash = params.hashfn(key, key_len, tbl->hash_rnd); @@ -224,7 +224,7 @@ static inline unsigned int rht_key_hashfn( hash = jhash2(key, key_len / sizeof(u32), tbl->hash_rnd); } else { - unsigned key_len = ht->p.key_len; + unsigned int key_len = ht->p.key_len; if (params.hashfn) hash = params.hashfn(key, key_len, tbl->hash_rnd); @@ -512,7 +512,7 @@ static inline void *rhashtable_lookup_fast( }; const struct bucket_table *tbl; struct rhash_head *he; - unsigned hash; + unsigned int hash; rcu_read_lock(); @@ -550,8 +550,8 @@ static inline int __rhashtable_insert_fast( struct bucket_table *tbl, *new_tbl; struct rhash_head *head; spinlock_t *lock; - unsigned elasticity; - unsigned hash; + unsigned int elasticity; + unsigned int hash; int err; restart: @@ -718,7 +718,7 @@ static inline int __rhashtable_remove_fast( struct rhash_head __rcu **pprev; struct rhash_head *he; spinlock_t * lock; - unsigned hash; + unsigned int hash; int err = -ENOENT; hash = rht_head_hashfn(ht, tbl, obj, params); diff --git a/lib/rhashtable.c b/lib/rhashtable.c index 8514f7c5f029..50abe4fec4b8 100644 --- a/lib/rhashtable.c +++ b/lib/rhashtable.c @@ -153,7 +153,7 @@ static struct bucket_table *rhashtable_last_table(struct rhashtable *ht, return new_tbl; } -static int rhashtable_rehash_one(struct rhashtable *ht, unsigned old_hash) +static int rhashtable_rehash_one(struct rhashtable *ht, unsigned int old_hash) { struct bucket_table *old_tbl = rht_dereference(ht->tbl, ht); struct bucket_table *new_tbl = rhashtable_last_table(ht, @@ -162,7 +162,7 @@ static int rhashtable_rehash_one(struct rhashtable *ht, unsigned old_hash) int err = -ENOENT; struct rhash_head *head, *next, *entry; spinlock_t *new_bucket_lock; - unsigned new_hash; + unsigned int new_hash; rht_for_each(entry, old_tbl, old_hash) { err = 0; @@ -199,7 +199,8 @@ out: return err; } -static void rhashtable_rehash_chain(struct rhashtable *ht, unsigned old_hash) +static void rhashtable_rehash_chain(struct rhashtable *ht, + unsigned int old_hash) { struct bucket_table *old_tbl = rht_dereference(ht->tbl, ht); spinlock_t *old_bucket_lock; @@ -244,7 +245,7 @@ static int rhashtable_rehash_table(struct rhashtable *ht) struct bucket_table *old_tbl = rht_dereference(ht->tbl, ht); struct bucket_table *new_tbl; struct rhashtable_walker *walker; - unsigned old_hash; + unsigned int old_hash; new_tbl = rht_dereference(old_tbl->future_tbl, ht); if (!new_tbl) @@ -324,11 +325,12 @@ static int rhashtable_expand(struct rhashtable *ht) static int rhashtable_shrink(struct rhashtable *ht) { struct bucket_table *new_tbl, *old_tbl = rht_dereference(ht->tbl, ht); - unsigned size = roundup_pow_of_two(atomic_read(&ht->nelems) * 3 / 2); + unsigned int size; int err; ASSERT_RHT_MUTEX(ht); + size = roundup_pow_of_two(atomic_read(&ht->nelems) * 3 / 2); if (size < ht->p.min_size) size = ht->p.min_size; @@ -379,9 +381,9 @@ unlock: static bool rhashtable_check_elasticity(struct rhashtable *ht, struct bucket_table *tbl, - unsigned hash) + unsigned int hash) { - unsigned elasticity = ht->elasticity; + unsigned int elasticity = ht->elasticity; struct rhash_head *head; rht_for_each(head, tbl, hash) @@ -431,7 +433,7 @@ int rhashtable_insert_slow(struct rhashtable *ht, const void *key, struct bucket_table *tbl) { struct rhash_head *head; - unsigned hash; + unsigned int hash; int err; tbl = rhashtable_last_table(ht, tbl); -- cgit v1.2.3 From ac833bddb591b9c7a0609f440f196375be184044 Mon Sep 17 00:00:00 2001 From: Thomas Graf Date: Tue, 24 Mar 2015 14:18:18 +0100 Subject: rhashtable: Mark internal/private inline functions as such Signed-off-by: Thomas Graf Signed-off-by: David S. Miller --- include/linux/rhashtable.h | 2 ++ 1 file changed, 2 insertions(+) (limited to 'include') diff --git a/include/linux/rhashtable.h b/include/linux/rhashtable.h index f89cda067cb9..0e1f975ad101 100644 --- a/include/linux/rhashtable.h +++ b/include/linux/rhashtable.h @@ -539,6 +539,7 @@ restart: return NULL; } +/* Internal function, please use rhashtable_insert_fast() instead */ static inline int __rhashtable_insert_fast( struct rhashtable *ht, const void *key, struct rhash_head *obj, const struct rhashtable_params params) @@ -711,6 +712,7 @@ static inline int rhashtable_lookup_insert_key( return __rhashtable_insert_fast(ht, key, obj, params); } +/* Internal function, please use rhashtable_remove_fast() instead */ static inline int __rhashtable_remove_fast( struct rhashtable *ht, struct bucket_table *tbl, struct rhash_head *obj, const struct rhashtable_params params) -- cgit v1.2.3 From b5e2c150ac914f28a28833b57397bec0b0a2bd5f Mon Sep 17 00:00:00 2001 From: Thomas Graf Date: Tue, 24 Mar 2015 20:42:19 +0000 Subject: rhashtable: Disable automatic shrinking by default Introduce a new bool automatic_shrinking to require the user to explicitly opt-in to automatic shrinking of tables. Signed-off-by: Thomas Graf Signed-off-by: David S. Miller --- include/linux/rhashtable.h | 7 +++++-- lib/rhashtable.c | 2 +- net/netfilter/nft_hash.c | 1 + net/netlink/af_netlink.c | 1 + net/tipc/socket.c | 1 + 5 files changed, 9 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/include/linux/rhashtable.h b/include/linux/rhashtable.h index 0e1f975ad101..ae26c494e230 100644 --- a/include/linux/rhashtable.h +++ b/include/linux/rhashtable.h @@ -2,7 +2,7 @@ * Resizable, Scalable, Concurrent Hash Table * * Copyright (c) 2015 Herbert Xu - * Copyright (c) 2014 Thomas Graf + * Copyright (c) 2014-2015 Thomas Graf * Copyright (c) 2008-2014 Patrick McHardy * * Code partially derived from nft_hash @@ -104,6 +104,7 @@ struct rhashtable; * @min_size: Minimum size while shrinking * @nulls_base: Base value to generate nulls marker * @insecure_elasticity: Set to true to disable chain length checks + * @automatic_shrinking: Enable automatic shrinking of tables * @locks_mul: Number of bucket locks to allocate per cpu (default: 128) * @hashfn: Hash function (default: jhash2 if !(key_len % 4), or jhash) * @obj_hashfn: Function to hash object @@ -118,6 +119,7 @@ struct rhashtable_params { unsigned int min_size; u32 nulls_base; bool insecure_elasticity; + bool automatic_shrinking; size_t locks_mul; rht_hashfn_t hashfn; rht_obj_hashfn_t obj_hashfn; @@ -784,7 +786,8 @@ static inline int rhashtable_remove_fast( goto out; atomic_dec(&ht->nelems); - if (rht_shrink_below_30(ht, tbl)) + if (unlikely(ht->p.automatic_shrinking && + rht_shrink_below_30(ht, tbl))) schedule_work(&ht->run_work); out: diff --git a/lib/rhashtable.c b/lib/rhashtable.c index 50abe4fec4b8..50374d181148 100644 --- a/lib/rhashtable.c +++ b/lib/rhashtable.c @@ -367,7 +367,7 @@ static void rht_deferred_worker(struct work_struct *work) if (rht_grow_above_75(ht, tbl)) rhashtable_expand(ht); - else if (rht_shrink_below_30(ht, tbl)) + else if (ht->p.automatic_shrinking && rht_shrink_below_30(ht, tbl)) rhashtable_shrink(ht); err = rhashtable_rehash_table(ht); diff --git a/net/netfilter/nft_hash.c b/net/netfilter/nft_hash.c index ad3966976cf5..8577a37af18b 100644 --- a/net/netfilter/nft_hash.c +++ b/net/netfilter/nft_hash.c @@ -172,6 +172,7 @@ static const struct rhashtable_params nft_hash_params = { .head_offset = offsetof(struct nft_hash_elem, node), .key_offset = offsetof(struct nft_hash_elem, key), .hashfn = jhash, + .automatic_shrinking = true, }; static int nft_hash_init(const struct nft_set *set, diff --git a/net/netlink/af_netlink.c b/net/netlink/af_netlink.c index e2f7f28148e0..4caa809dbbe0 100644 --- a/net/netlink/af_netlink.c +++ b/net/netlink/af_netlink.c @@ -3142,6 +3142,7 @@ static const struct rhashtable_params netlink_rhashtable_params = { .obj_hashfn = netlink_hash, .obj_cmpfn = netlink_compare, .max_size = 65536, + .automatic_shrinking = true, }; static int __init netlink_proto_init(void) diff --git a/net/tipc/socket.c b/net/tipc/socket.c index 094710519477..ee90d74d7516 100644 --- a/net/tipc/socket.c +++ b/net/tipc/socket.c @@ -2297,6 +2297,7 @@ static const struct rhashtable_params tsk_rht_params = { .key_len = sizeof(u32), /* portid */ .max_size = 1048576, .min_size = 256, + .automatic_shrinking = true, }; int tipc_sk_rht_init(struct net *net) -- cgit v1.2.3 From 6b6f302ceda7a052dab545d6c69abf5f0d4a6cab Mon Sep 17 00:00:00 2001 From: Thomas Graf Date: Tue, 24 Mar 2015 14:18:20 +0100 Subject: rhashtable: Add rhashtable_free_and_destroy() rhashtable_destroy() variant which stops rehashes, iterates over the table and calls a callback to release resources. Avoids need for nft_hash to embed rhashtable internals and allows to get rid of the being_destroyed flag. It also saves a 2nd mutex lock upon destruction. Also fixes an RCU lockdep splash on nft set destruction due to calling rht_for_each_entry_safe() without holding bucket locks. Open code this loop as we need know that no mutations may occur in parallel. Signed-off-by: Thomas Graf Signed-off-by: David S. Miller --- include/linux/rhashtable.h | 5 +++-- lib/rhashtable.c | 49 ++++++++++++++++++++++++++++++++++++---------- net/netfilter/nft_hash.c | 25 +++++++---------------- 3 files changed, 49 insertions(+), 30 deletions(-) (limited to 'include') diff --git a/include/linux/rhashtable.h b/include/linux/rhashtable.h index ae26c494e230..99f2e49a8a07 100644 --- a/include/linux/rhashtable.h +++ b/include/linux/rhashtable.h @@ -136,12 +136,10 @@ struct rhashtable_params { * @run_work: Deferred worker to expand/shrink asynchronously * @mutex: Mutex to protect current/future table swapping * @lock: Spin lock to protect walker list - * @being_destroyed: True if table is set up for destruction */ struct rhashtable { struct bucket_table __rcu *tbl; atomic_t nelems; - bool being_destroyed; unsigned int key_len; unsigned int elasticity; struct rhashtable_params p; @@ -334,6 +332,9 @@ int rhashtable_walk_start(struct rhashtable_iter *iter) __acquires(RCU); void *rhashtable_walk_next(struct rhashtable_iter *iter); void rhashtable_walk_stop(struct rhashtable_iter *iter) __releases(RCU); +void rhashtable_free_and_destroy(struct rhashtable *ht, + void (*free_fn)(void *ptr, void *arg), + void *arg); void rhashtable_destroy(struct rhashtable *ht); #define rht_dereference(p, ht) \ diff --git a/lib/rhashtable.c b/lib/rhashtable.c index 50374d181148..4b7b7e672b93 100644 --- a/lib/rhashtable.c +++ b/lib/rhashtable.c @@ -359,8 +359,6 @@ static void rht_deferred_worker(struct work_struct *work) ht = container_of(work, struct rhashtable, run_work); mutex_lock(&ht->mutex); - if (ht->being_destroyed) - goto unlock; tbl = rht_dereference(ht->tbl, ht); tbl = rhashtable_last_table(ht, tbl); @@ -372,7 +370,6 @@ static void rht_deferred_worker(struct work_struct *work) err = rhashtable_rehash_table(ht); -unlock: mutex_unlock(&ht->mutex); if (err) @@ -783,21 +780,53 @@ int rhashtable_init(struct rhashtable *ht, EXPORT_SYMBOL_GPL(rhashtable_init); /** - * rhashtable_destroy - destroy hash table + * rhashtable_free_and_destroy - free elements and destroy hash table * @ht: the hash table to destroy + * @free_fn: callback to release resources of element + * @arg: pointer passed to free_fn * - * Frees the bucket array. This function is not rcu safe, therefore the caller - * has to make sure that no resizing may happen by unpublishing the hashtable - * and waiting for the quiescent cycle before releasing the bucket array. + * Stops an eventual async resize. If defined, invokes free_fn for each + * element to releasal resources. Please note that RCU protected + * readers may still be accessing the elements. Releasing of resources + * must occur in a compatible manner. Then frees the bucket array. + * + * This function will eventually sleep to wait for an async resize + * to complete. The caller is responsible that no further write operations + * occurs in parallel. */ -void rhashtable_destroy(struct rhashtable *ht) +void rhashtable_free_and_destroy(struct rhashtable *ht, + void (*free_fn)(void *ptr, void *arg), + void *arg) { - ht->being_destroyed = true; + const struct bucket_table *tbl; + unsigned int i; cancel_work_sync(&ht->run_work); mutex_lock(&ht->mutex); - bucket_table_free(rht_dereference(ht->tbl, ht)); + tbl = rht_dereference(ht->tbl, ht); + if (free_fn) { + for (i = 0; i < tbl->size; i++) { + struct rhash_head *pos, *next; + + for (pos = rht_dereference(tbl->buckets[i], ht), + next = !rht_is_a_nulls(pos) ? + rht_dereference(pos->next, ht) : NULL; + !rht_is_a_nulls(pos); + pos = next, + next = !rht_is_a_nulls(pos) ? + rht_dereference(pos->next, ht) : NULL) + free_fn(rht_obj(ht, pos), arg); + } + } + + bucket_table_free(tbl); mutex_unlock(&ht->mutex); } +EXPORT_SYMBOL_GPL(rhashtable_free_and_destroy); + +void rhashtable_destroy(struct rhashtable *ht) +{ + return rhashtable_free_and_destroy(ht, NULL, NULL); +} EXPORT_SYMBOL_GPL(rhashtable_destroy); diff --git a/net/netfilter/nft_hash.c b/net/netfilter/nft_hash.c index 8577a37af18b..f9ce2195fd63 100644 --- a/net/netfilter/nft_hash.c +++ b/net/netfilter/nft_hash.c @@ -188,26 +188,15 @@ static int nft_hash_init(const struct nft_set *set, return rhashtable_init(priv, ¶ms); } -static void nft_hash_destroy(const struct nft_set *set) +static void nft_free_element(void *ptr, void *arg) { - struct rhashtable *priv = nft_set_priv(set); - const struct bucket_table *tbl; - struct nft_hash_elem *he; - struct rhash_head *pos, *next; - unsigned int i; - - /* Stop an eventual async resizing */ - priv->being_destroyed = true; - mutex_lock(&priv->mutex); - - tbl = rht_dereference(priv->tbl, priv); - for (i = 0; i < tbl->size; i++) { - rht_for_each_entry_safe(he, pos, next, tbl, i, node) - nft_hash_elem_destroy(set, he); - } - mutex_unlock(&priv->mutex); + nft_hash_elem_destroy((const struct nft_set *)arg, ptr); +} - rhashtable_destroy(priv); +static void nft_hash_destroy(const struct nft_set *set) +{ + rhashtable_free_and_destroy(nft_set_priv(set), nft_free_element, + (void *)set); } static bool nft_hash_estimate(const struct nft_set_desc *desc, u32 features, -- cgit v1.2.3 From 39f8e58e53be32ab758d30536e0bd2e6ce766462 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Tue, 24 Mar 2015 15:58:55 -0700 Subject: tcp: md5: remove request sock argument of calc_md5_hash() Since request and established sockets now have same base, there is no need to pass two pointers to tcp_v4_md5_hash_skb() or tcp_v6_md5_hash_skb() Also add a const qualifier to their struct tcp_md5sig_key argument. Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- include/net/tcp.h | 29 +++++++++++++---------------- net/ipv4/tcp_ipv4.c | 17 +++++++---------- net/ipv4/tcp_output.c | 4 ++-- net/ipv6/tcp_ipv6.c | 15 ++++++--------- 4 files changed, 28 insertions(+), 37 deletions(-) (limited to 'include') diff --git a/include/net/tcp.h b/include/net/tcp.h index fe60e00e1919..992be858c370 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -1296,9 +1296,8 @@ struct tcp_md5sig_pool { }; /* - functions */ -int tcp_v4_md5_hash_skb(char *md5_hash, struct tcp_md5sig_key *key, - const struct sock *sk, const struct request_sock *req, - const struct sk_buff *skb); +int tcp_v4_md5_hash_skb(char *md5_hash, const struct tcp_md5sig_key *key, + const struct sock *sk, const struct sk_buff *skb); int tcp_md5_do_add(struct sock *sk, const union tcp_md5_addr *addr, int family, const u8 *newkey, u8 newkeylen, gfp_t gfp); int tcp_md5_do_del(struct sock *sk, const union tcp_md5_addr *addr, @@ -1616,14 +1615,13 @@ struct tcp_sock_af_ops { #ifdef CONFIG_TCP_MD5SIG struct tcp_md5sig_key *(*md5_lookup) (struct sock *sk, struct sock *addr_sk); - int (*calc_md5_hash) (char *location, - struct tcp_md5sig_key *md5, - const struct sock *sk, - const struct request_sock *req, - const struct sk_buff *skb); - int (*md5_parse) (struct sock *sk, - char __user *optval, - int optlen); + int (*calc_md5_hash)(char *location, + const struct tcp_md5sig_key *md5, + const struct sock *sk, + const struct sk_buff *skb); + int (*md5_parse)(struct sock *sk, + char __user *optval, + int optlen); #endif }; @@ -1632,11 +1630,10 @@ struct tcp_request_sock_ops { #ifdef CONFIG_TCP_MD5SIG struct tcp_md5sig_key *(*md5_lookup) (struct sock *sk, struct request_sock *req); - int (*calc_md5_hash) (char *location, - struct tcp_md5sig_key *md5, - const struct sock *sk, - const struct request_sock *req, - const struct sk_buff *skb); + int (*calc_md5_hash) (char *location, + const struct tcp_md5sig_key *md5, + const struct sock *sk, + const struct sk_buff *skb); #endif void (*init_req)(struct request_sock *req, struct sock *sk, struct sk_buff *skb); diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index d339a0488f51..79d5c641688c 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -648,7 +648,7 @@ static void tcp_v4_send_reset(struct sock *sk, struct sk_buff *skb) if (!key) goto release_sk1; - genhash = tcp_v4_md5_hash_skb(newhash, key, NULL, NULL, skb); + genhash = tcp_v4_md5_hash_skb(newhash, key, NULL, skb); if (genhash || memcmp(hash_location, newhash, 16) != 0) goto release_sk1; } else { @@ -1102,8 +1102,8 @@ clear_hash_noput: return 1; } -int tcp_v4_md5_hash_skb(char *md5_hash, struct tcp_md5sig_key *key, - const struct sock *sk, const struct request_sock *req, +int tcp_v4_md5_hash_skb(char *md5_hash, const struct tcp_md5sig_key *key, + const struct sock *sk, const struct sk_buff *skb) { struct tcp_md5sig_pool *hp; @@ -1111,12 +1111,9 @@ int tcp_v4_md5_hash_skb(char *md5_hash, struct tcp_md5sig_key *key, const struct tcphdr *th = tcp_hdr(skb); __be32 saddr, daddr; - if (sk) { - saddr = inet_sk(sk)->inet_saddr; - daddr = inet_sk(sk)->inet_daddr; - } else if (req) { - saddr = inet_rsk(req)->ir_loc_addr; - daddr = inet_rsk(req)->ir_rmt_addr; + if (sk) { /* valid for establish/request sockets */ + saddr = sk->sk_rcv_saddr; + daddr = sk->sk_daddr; } else { const struct iphdr *iph = ip_hdr(skb); saddr = iph->saddr; @@ -1195,7 +1192,7 @@ static bool tcp_v4_inbound_md5_hash(struct sock *sk, */ genhash = tcp_v4_md5_hash_skb(newhash, hash_expected, - NULL, NULL, skb); + NULL, skb); if (genhash || memcmp(hash_location, newhash, 16) != 0) { net_info_ratelimited("MD5 Hash failed for (%pI4, %d)->(%pI4, %d)%s\n", diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c index 5b7fad4b314c..501cf9d401c3 100644 --- a/net/ipv4/tcp_output.c +++ b/net/ipv4/tcp_output.c @@ -986,7 +986,7 @@ static int tcp_transmit_skb(struct sock *sk, struct sk_buff *skb, int clone_it, if (md5) { sk_nocaps_add(sk, NETIF_F_GSO_MASK); tp->af_specific->calc_md5_hash(opts.hash_location, - md5, sk, NULL, skb); + md5, sk, skb); } #endif @@ -2973,7 +2973,7 @@ struct sk_buff *tcp_make_synack(struct sock *sk, struct dst_entry *dst, /* Okay, we have all we need - do the md5 hash if needed */ if (md5) tcp_rsk(req)->af_specific->calc_md5_hash(opts.hash_location, - md5, NULL, req, skb); + md5, req_to_sk(req), skb); rcu_read_unlock(); #endif diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c index 078e7d0f4cd8..57d1c41404ec 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c @@ -582,9 +582,9 @@ clear_hash_noput: return 1; } -static int tcp_v6_md5_hash_skb(char *md5_hash, struct tcp_md5sig_key *key, +static int tcp_v6_md5_hash_skb(char *md5_hash, + const struct tcp_md5sig_key *key, const struct sock *sk, - const struct request_sock *req, const struct sk_buff *skb) { const struct in6_addr *saddr, *daddr; @@ -592,12 +592,9 @@ static int tcp_v6_md5_hash_skb(char *md5_hash, struct tcp_md5sig_key *key, struct hash_desc *desc; const struct tcphdr *th = tcp_hdr(skb); - if (sk) { - saddr = &inet6_sk(sk)->saddr; + if (sk) { /* valid for establish/request sockets */ + saddr = &sk->sk_v6_rcv_saddr; daddr = &sk->sk_v6_daddr; - } else if (req) { - saddr = &inet_rsk(req)->ir_v6_loc_addr; - daddr = &inet_rsk(req)->ir_v6_rmt_addr; } else { const struct ipv6hdr *ip6h = ipv6_hdr(skb); saddr = &ip6h->saddr; @@ -662,7 +659,7 @@ static bool tcp_v6_inbound_md5_hash(struct sock *sk, const struct sk_buff *skb) /* check the signature */ genhash = tcp_v6_md5_hash_skb(newhash, hash_expected, - NULL, NULL, skb); + NULL, skb); if (genhash || memcmp(hash_location, newhash, 16) != 0) { net_info_ratelimited("MD5 Hash %s for [%pI6c]:%u->[%pI6c]:%u\n", @@ -880,7 +877,7 @@ static void tcp_v6_send_reset(struct sock *sk, struct sk_buff *skb) if (!key) goto release_sk1; - genhash = tcp_v6_md5_hash_skb(newhash, key, NULL, NULL, skb); + genhash = tcp_v6_md5_hash_skb(newhash, key, NULL, skb); if (genhash || memcmp(hash_location, newhash, 16) != 0) goto release_sk1; } else { -- cgit v1.2.3 From fd3a154a00fb991872680f19021f5edbb40b4dbe Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Tue, 24 Mar 2015 15:58:56 -0700 Subject: tcp: md5: get rid of tcp_v[46]_reqsk_md5_lookup() With request socks convergence, we no longer need different lookup methods. A request socket can use generic lookup function. Add const qualifier to 2nd tcp_v[46]_md5_lookup() parameter. Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- include/net/tcp.h | 8 ++++---- net/ipv4/tcp_ipv4.c | 19 +++++-------------- net/ipv4/tcp_output.c | 2 +- net/ipv6/tcp_ipv6.c | 10 ++-------- 4 files changed, 12 insertions(+), 27 deletions(-) (limited to 'include') diff --git a/include/net/tcp.h b/include/net/tcp.h index 992be858c370..42690daa924e 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -1303,7 +1303,7 @@ int tcp_md5_do_add(struct sock *sk, const union tcp_md5_addr *addr, int tcp_md5_do_del(struct sock *sk, const union tcp_md5_addr *addr, int family); struct tcp_md5sig_key *tcp_v4_md5_lookup(struct sock *sk, - struct sock *addr_sk); + const struct sock *addr_sk); #ifdef CONFIG_TCP_MD5SIG struct tcp_md5sig_key *tcp_md5_do_lookup(struct sock *sk, @@ -1614,7 +1614,7 @@ int tcp_conn_request(struct request_sock_ops *rsk_ops, struct tcp_sock_af_ops { #ifdef CONFIG_TCP_MD5SIG struct tcp_md5sig_key *(*md5_lookup) (struct sock *sk, - struct sock *addr_sk); + const struct sock *addr_sk); int (*calc_md5_hash)(char *location, const struct tcp_md5sig_key *md5, const struct sock *sk, @@ -1628,8 +1628,8 @@ struct tcp_sock_af_ops { struct tcp_request_sock_ops { u16 mss_clamp; #ifdef CONFIG_TCP_MD5SIG - struct tcp_md5sig_key *(*md5_lookup) (struct sock *sk, - struct request_sock *req); + struct tcp_md5sig_key *(*req_md5_lookup)(struct sock *sk, + const struct sock *addr_sk); int (*calc_md5_hash) (char *location, const struct tcp_md5sig_key *md5, const struct sock *sk, diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index 79d5c641688c..fc8995a702a6 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -898,10 +898,10 @@ struct tcp_md5sig_key *tcp_md5_do_lookup(struct sock *sk, const union tcp_md5_addr *addr, int family) { - struct tcp_sock *tp = tcp_sk(sk); + const struct tcp_sock *tp = tcp_sk(sk); struct tcp_md5sig_key *key; unsigned int size = sizeof(struct in_addr); - struct tcp_md5sig_info *md5sig; + const struct tcp_md5sig_info *md5sig; /* caller either holds rcu_read_lock() or socket lock */ md5sig = rcu_dereference_check(tp->md5sig_info, @@ -924,24 +924,15 @@ struct tcp_md5sig_key *tcp_md5_do_lookup(struct sock *sk, EXPORT_SYMBOL(tcp_md5_do_lookup); struct tcp_md5sig_key *tcp_v4_md5_lookup(struct sock *sk, - struct sock *addr_sk) + const struct sock *addr_sk) { union tcp_md5_addr *addr; - addr = (union tcp_md5_addr *)&inet_sk(addr_sk)->inet_daddr; + addr = (union tcp_md5_addr *)&sk->sk_daddr; return tcp_md5_do_lookup(sk, addr, AF_INET); } EXPORT_SYMBOL(tcp_v4_md5_lookup); -static struct tcp_md5sig_key *tcp_v4_reqsk_md5_lookup(struct sock *sk, - struct request_sock *req) -{ - union tcp_md5_addr *addr; - - addr = (union tcp_md5_addr *)&inet_rsk(req)->ir_rmt_addr; - return tcp_md5_do_lookup(sk, addr, AF_INET); -} - /* This can be called on a newly created socket, from other files */ int tcp_md5_do_add(struct sock *sk, const union tcp_md5_addr *addr, int family, const u8 *newkey, u8 newkeylen, gfp_t gfp) @@ -1247,7 +1238,7 @@ struct request_sock_ops tcp_request_sock_ops __read_mostly = { static const struct tcp_request_sock_ops tcp_request_sock_ipv4_ops = { .mss_clamp = TCP_MSS_DEFAULT, #ifdef CONFIG_TCP_MD5SIG - .md5_lookup = tcp_v4_reqsk_md5_lookup, + .req_md5_lookup = tcp_v4_md5_lookup, .calc_md5_hash = tcp_v4_md5_hash_skb, #endif .init_req = tcp_v4_init_req, diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c index 501cf9d401c3..2e69b8d16e68 100644 --- a/net/ipv4/tcp_output.c +++ b/net/ipv4/tcp_output.c @@ -2938,7 +2938,7 @@ struct sk_buff *tcp_make_synack(struct sock *sk, struct dst_entry *dst, #ifdef CONFIG_TCP_MD5SIG rcu_read_lock(); - md5 = tcp_rsk(req)->af_specific->md5_lookup(sk, req); + md5 = tcp_rsk(req)->af_specific->req_md5_lookup(sk, req_to_sk(req)); #endif tcp_header_size = tcp_synack_options(sk, req, mss, skb, &opts, md5, foc) + sizeof(*th); diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c index 57d1c41404ec..a9568caf4675 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c @@ -486,17 +486,11 @@ static struct tcp_md5sig_key *tcp_v6_md5_do_lookup(struct sock *sk, } static struct tcp_md5sig_key *tcp_v6_md5_lookup(struct sock *sk, - struct sock *addr_sk) + const struct sock *addr_sk) { return tcp_v6_md5_do_lookup(sk, &addr_sk->sk_v6_daddr); } -static struct tcp_md5sig_key *tcp_v6_reqsk_md5_lookup(struct sock *sk, - struct request_sock *req) -{ - return tcp_v6_md5_do_lookup(sk, &inet_rsk(req)->ir_v6_rmt_addr); -} - static int tcp_v6_parse_md5_keys(struct sock *sk, char __user *optval, int optlen) { @@ -720,7 +714,7 @@ static const struct tcp_request_sock_ops tcp_request_sock_ipv6_ops = { .mss_clamp = IPV6_MIN_MTU - sizeof(struct tcphdr) - sizeof(struct ipv6hdr), #ifdef CONFIG_TCP_MD5SIG - .md5_lookup = tcp_v6_reqsk_md5_lookup, + .req_md5_lookup = tcp_v6_md5_lookup, .calc_md5_hash = tcp_v6_md5_hash_skb, #endif .init_req = tcp_v6_init_req, -- cgit v1.2.3 From dd46c787788d5bf5b974729d43e4c405814a4c7d Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Wed, 25 Mar 2015 15:07:05 +1100 Subject: fs: Add support FALLOC_FL_INSERT_RANGE for fallocate FALLOC_FL_INSERT_RANGE command is the opposite command of FALLOC_FL_COLLAPSE_RANGE that is needed for someone who wants to add some data in the middle of file. FALLOC_FL_INSERT_RANGE will create space for writing new data within a file after shifting extents to right as given length. This command also has same limitations as FALLOC_FL_COLLAPSE_RANGE in that operations need to be filesystem block boundary aligned and cannot cross the current EOF. Signed-off-by: Namjae Jeon Signed-off-by: Ashish Sangwan Reviewed-by: Dave Chinner Signed-off-by: Dave Chinner --- fs/open.c | 8 ++++++-- include/linux/falloc.h | 6 ++++++ include/uapi/linux/falloc.h | 17 +++++++++++++++++ 3 files changed, 29 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/fs/open.c b/fs/open.c index 33f9cbf2610b..b724cc0e0228 100644 --- a/fs/open.c +++ b/fs/open.c @@ -231,8 +231,7 @@ int vfs_fallocate(struct file *file, int mode, loff_t offset, loff_t len) return -EINVAL; /* Return error if mode is not supported */ - if (mode & ~(FALLOC_FL_KEEP_SIZE | FALLOC_FL_PUNCH_HOLE | - FALLOC_FL_COLLAPSE_RANGE | FALLOC_FL_ZERO_RANGE)) + if (mode & ~FALLOC_FL_SUPPORTED_MASK) return -EOPNOTSUPP; /* Punch hole and zero range are mutually exclusive */ @@ -250,6 +249,11 @@ int vfs_fallocate(struct file *file, int mode, loff_t offset, loff_t len) (mode & ~FALLOC_FL_COLLAPSE_RANGE)) return -EINVAL; + /* Insert range should only be used exclusively. */ + if ((mode & FALLOC_FL_INSERT_RANGE) && + (mode & ~FALLOC_FL_INSERT_RANGE)) + return -EINVAL; + if (!(file->f_mode & FMODE_WRITE)) return -EBADF; diff --git a/include/linux/falloc.h b/include/linux/falloc.h index 31591686ac2d..996111000a8c 100644 --- a/include/linux/falloc.h +++ b/include/linux/falloc.h @@ -21,4 +21,10 @@ struct space_resv { #define FS_IOC_RESVSP _IOW('X', 40, struct space_resv) #define FS_IOC_RESVSP64 _IOW('X', 42, struct space_resv) +#define FALLOC_FL_SUPPORTED_MASK (FALLOC_FL_KEEP_SIZE | \ + FALLOC_FL_PUNCH_HOLE | \ + FALLOC_FL_COLLAPSE_RANGE | \ + FALLOC_FL_ZERO_RANGE | \ + FALLOC_FL_INSERT_RANGE) + #endif /* _FALLOC_H_ */ diff --git a/include/uapi/linux/falloc.h b/include/uapi/linux/falloc.h index d1197ae3723c..3e445a760f14 100644 --- a/include/uapi/linux/falloc.h +++ b/include/uapi/linux/falloc.h @@ -41,4 +41,21 @@ */ #define FALLOC_FL_ZERO_RANGE 0x10 +/* + * FALLOC_FL_INSERT_RANGE is use to insert space within the file size without + * overwriting any existing data. The contents of the file beyond offset are + * shifted towards right by len bytes to create a hole. As such, this + * operation will increase the size of the file by len bytes. + * + * Different filesystems may implement different limitations on the granularity + * of the operation. Most will limit operations to filesystem block size + * boundaries, but this boundary may be larger or smaller depending on + * the filesystem and/or the configuration of the filesystem or file. + * + * Attempting to insert space using this flag at OR beyond the end of + * the file is considered an illegal operation - just use ftruncate(2) or + * fallocate(2) with mode 0 for such type of operations. + */ +#define FALLOC_FL_INSERT_RANGE 0x20 + #endif /* _UAPI_FALLOC_H_ */ -- cgit v1.2.3 From 3a3e1c88362429ca3a6ef84d232e56b197294ce0 Mon Sep 17 00:00:00 2001 From: Tero Kristo Date: Mon, 23 Feb 2015 15:57:32 +0200 Subject: ARM: OMAP2+: PRCM: add support for static clock memmap indices All clock provider related drivers will now register their iomaps with a static index. This makes it easier to split up the individual drivers to their own files in subsequent patches. Signed-off-by: Tero Kristo --- arch/arm/mach-omap2/prcm-common.h | 8 ++++ arch/arm/mach-omap2/prm_common.c | 79 +++++++++++++++++++++++++-------------- include/linux/clk/ti.h | 1 + 3 files changed, 59 insertions(+), 29 deletions(-) (limited to 'include') diff --git a/arch/arm/mach-omap2/prcm-common.h b/arch/arm/mach-omap2/prcm-common.h index 6163d66102a3..ee38356b3601 100644 --- a/arch/arm/mach-omap2/prcm-common.h +++ b/arch/arm/mach-omap2/prcm-common.h @@ -518,6 +518,14 @@ struct omap_prcm_irq_setup { .priority = _priority \ } +/** + * struct omap_prcm_init_data - PRCM driver init data + * @index: clock memory mapping index to be used + */ +struct omap_prcm_init_data { + int index; +}; + extern void omap_prcm_irq_cleanup(void); extern int omap_prcm_register_chain_handler( struct omap_prcm_irq_setup *irq_setup); diff --git a/arch/arm/mach-omap2/prm_common.c b/arch/arm/mach-omap2/prm_common.c index 79cee117f971..8ec52012f85d 100644 --- a/arch/arm/mach-omap2/prm_common.c +++ b/arch/arm/mach-omap2/prm_common.c @@ -633,31 +633,47 @@ int prm_unregister(struct prm_ll_data *pld) return 0; } +static struct omap_prcm_init_data prm_data = { + .index = TI_CLKM_PRM, +}; + +static struct omap_prcm_init_data cm_data = { + .index = TI_CLKM_CM, +}; + +static struct omap_prcm_init_data cm2_data = { + .index = TI_CLKM_CM2, +}; + +static struct omap_prcm_init_data scrm_data = { + .index = TI_CLKM_SCRM, +}; + static const struct of_device_id omap_prcm_dt_match_table[] = { - { .compatible = "ti,am3-prcm" }, - { .compatible = "ti,am3-scrm" }, - { .compatible = "ti,am4-prcm" }, - { .compatible = "ti,am4-scrm" }, - { .compatible = "ti,dm814-prcm" }, - { .compatible = "ti,dm814-scrm" }, - { .compatible = "ti,dm816-prcm" }, - { .compatible = "ti,dm816-scrm" }, - { .compatible = "ti,omap2-prcm" }, - { .compatible = "ti,omap2-scrm" }, - { .compatible = "ti,omap3-prm" }, - { .compatible = "ti,omap3-cm" }, - { .compatible = "ti,omap3-scrm" }, - { .compatible = "ti,omap4-cm1" }, - { .compatible = "ti,omap4-prm" }, - { .compatible = "ti,omap4-cm2" }, - { .compatible = "ti,omap4-scrm" }, - { .compatible = "ti,omap5-prm" }, - { .compatible = "ti,omap5-cm-core-aon" }, - { .compatible = "ti,omap5-scrm" }, - { .compatible = "ti,omap5-cm-core" }, - { .compatible = "ti,dra7-prm" }, - { .compatible = "ti,dra7-cm-core-aon" }, - { .compatible = "ti,dra7-cm-core" }, + { .compatible = "ti,am3-prcm", .data = &prm_data }, + { .compatible = "ti,am3-scrm", .data = &scrm_data }, + { .compatible = "ti,am4-prcm", .data = &prm_data }, + { .compatible = "ti,am4-scrm", .data = &scrm_data }, + { .compatible = "ti,dm814-prcm", .data = &prm_data }, + { .compatible = "ti,dm814-scrm", .data = &scrm_data }, + { .compatible = "ti,dm816-prcm", .data = &prm_data }, + { .compatible = "ti,dm816-scrm", .data = &scrm_data }, + { .compatible = "ti,omap2-prcm", .data = &prm_data }, + { .compatible = "ti,omap2-scrm", .data = &scrm_data }, + { .compatible = "ti,omap3-prm", .data = &prm_data }, + { .compatible = "ti,omap3-cm", .data = &cm_data }, + { .compatible = "ti,omap3-scrm", .data = &scrm_data }, + { .compatible = "ti,omap4-cm1", .data = &cm_data }, + { .compatible = "ti,omap4-prm", .data = &prm_data }, + { .compatible = "ti,omap4-cm2", .data = &cm2_data }, + { .compatible = "ti,omap4-scrm", .data = &scrm_data }, + { .compatible = "ti,omap5-prm", .data = &prm_data }, + { .compatible = "ti,omap5-cm-core-aon", .data = &cm_data }, + { .compatible = "ti,omap5-scrm", .data = &scrm_data }, + { .compatible = "ti,omap5-cm-core", .data = &cm2_data }, + { .compatible = "ti,dra7-prm", .data = &prm_data }, + { .compatible = "ti,dra7-cm-core-aon", .data = &cm_data }, + { .compatible = "ti,dra7-cm-core", .data = &cm2_data }, { } }; @@ -690,15 +706,20 @@ int __init omap_prcm_init(void) { struct device_node *np; void __iomem *mem; - int memmap_index = 0; + const struct of_device_id *match; + const struct omap_prcm_init_data *data; ti_clk_ll_ops = &omap_clk_ll_ops; - for_each_matching_node(np, omap_prcm_dt_match_table) { + for_each_matching_node_and_match(np, omap_prcm_dt_match_table, &match) { + data = match->data; + mem = of_iomap(np, 0); - clk_memmaps[memmap_index] = mem; - ti_dt_clk_init_provider(np, memmap_index); - memmap_index++; + if (!mem) + return -ENOMEM; + + clk_memmaps[data->index] = mem; + ti_dt_clk_init_provider(np, data->index); } return 0; diff --git a/include/linux/clk/ti.h b/include/linux/clk/ti.h index 67844003493d..19895a3f48b7 100644 --- a/include/linux/clk/ti.h +++ b/include/linux/clk/ti.h @@ -221,6 +221,7 @@ struct ti_dt_clk { /* Static memmap indices */ enum { TI_CLKM_CM = 0, + TI_CLKM_CM2, TI_CLKM_PRM, TI_CLKM_SCRM, }; -- cgit v1.2.3 From bd735995308b553cc3c7f6a975aa284b270c7e2c Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 2 Feb 2015 15:44:54 +0100 Subject: misc: Add attribute groups Add groups field to struct miscdevice for passing the attribute groups at device creation. In this way, the driver can avoid the manual call of device_create_file() after the device registration, which is basically a racy operation, in addition to the reduction of manual device_remove_file() calls. Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- drivers/char/misc.c | 5 +++-- include/linux/miscdevice.h | 2 ++ 2 files changed, 5 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/drivers/char/misc.c b/drivers/char/misc.c index c892c296a4de..5bb3a2109ab7 100644 --- a/drivers/char/misc.c +++ b/drivers/char/misc.c @@ -207,8 +207,9 @@ int misc_register(struct miscdevice * misc) dev = MKDEV(MISC_MAJOR, misc->minor); - misc->this_device = device_create(misc_class, misc->parent, dev, - misc, "%s", misc->name); + misc->this_device = + device_create_with_groups(misc_class, misc->parent, dev, + misc, misc->groups, "%s", misc->name); if (IS_ERR(misc->this_device)) { int i = DYNAMIC_MINORS - misc->minor - 1; if (i < DYNAMIC_MINORS && i >= 0) diff --git a/include/linux/miscdevice.h b/include/linux/miscdevice.h index ee80dd7d9f60..819077c32690 100644 --- a/include/linux/miscdevice.h +++ b/include/linux/miscdevice.h @@ -52,6 +52,7 @@ #define MISC_DYNAMIC_MINOR 255 struct device; +struct attribute_group; struct miscdevice { int minor; @@ -60,6 +61,7 @@ struct miscdevice { struct list_head list; struct device *parent; struct device *this_device; + const struct attribute_group **groups; const char *nodename; umode_t mode; }; -- cgit v1.2.3 From 5ebb335dcbe63470c88c4f80f2d571089543b638 Mon Sep 17 00:00:00 2001 From: Patrick McHardy Date: Sat, 21 Mar 2015 15:19:15 +0000 Subject: netfilter: nf_tables: move struct net pointer to base chain The network namespace is only needed for base chains to get at the gencursor. Also convert to possible_net_t. Signed-off-by: Patrick McHardy Signed-off-by: Pablo Neira Ayuso --- include/net/netfilter/nf_tables.h | 4 ++-- net/netfilter/nf_tables_api.c | 2 +- net/netfilter/nf_tables_core.c | 3 ++- 3 files changed, 5 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/include/net/netfilter/nf_tables.h b/include/net/netfilter/nf_tables.h index d756af559977..ace67a549b30 100644 --- a/include/net/netfilter/nf_tables.h +++ b/include/net/netfilter/nf_tables.h @@ -449,7 +449,6 @@ enum nft_chain_flags { * * @rules: list of rules in the chain * @list: used internally - * @net: net namespace that this chain belongs to * @table: table that this chain belongs to * @handle: chain handle * @use: number of jump references to this chain @@ -460,7 +459,6 @@ enum nft_chain_flags { struct nft_chain { struct list_head rules; struct list_head list; - struct net *net; struct nft_table *table; u64 handle; u32 use; @@ -512,6 +510,7 @@ struct nft_stats { * struct nft_base_chain - nf_tables base chain * * @ops: netfilter hook ops + * @pnet: net namespace that this chain belongs to * @type: chain type * @policy: default policy * @stats: per-cpu chain stats @@ -519,6 +518,7 @@ struct nft_stats { */ struct nft_base_chain { struct nf_hook_ops ops[NFT_HOOK_OPS_MAX]; + possible_net_t pnet; const struct nf_chain_type *type; u8 policy; struct nft_stats __percpu *stats; diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c index 363a39a6c286..0b969b66cb77 100644 --- a/net/netfilter/nf_tables_api.c +++ b/net/netfilter/nf_tables_api.c @@ -1354,6 +1354,7 @@ static int nf_tables_newchain(struct sock *nlsk, struct sk_buff *skb, rcu_assign_pointer(basechain->stats, stats); } + write_pnet(&basechain->pnet, net); basechain->type = type; chain = &basechain->chain; @@ -1381,7 +1382,6 @@ static int nf_tables_newchain(struct sock *nlsk, struct sk_buff *skb, INIT_LIST_HEAD(&chain->rules); chain->handle = nf_tables_alloc_handle(table); - chain->net = net; chain->table = table; nla_strlcpy(chain->name, name, NFT_CHAIN_MAXNAMELEN); diff --git a/net/netfilter/nf_tables_core.c b/net/netfilter/nf_tables_core.c index 77165bf023f3..4c921a302cfd 100644 --- a/net/netfilter/nf_tables_core.c +++ b/net/netfilter/nf_tables_core.c @@ -112,6 +112,7 @@ unsigned int nft_do_chain(struct nft_pktinfo *pkt, const struct nf_hook_ops *ops) { const struct nft_chain *chain = ops->priv, *basechain = chain; + const struct net *net = read_pnet(&nft_base_chain(basechain)->pnet); const struct nft_rule *rule; const struct nft_expr *expr, *last; struct nft_data data[NFT_REG_MAX + 1]; @@ -123,7 +124,7 @@ nft_do_chain(struct nft_pktinfo *pkt, const struct nf_hook_ops *ops) * Cache cursor to avoid problems in case that the cursor is updated * while traversing the ruleset. */ - unsigned int gencursor = ACCESS_ONCE(chain->net->nft.gencursor); + unsigned int gencursor = ACCESS_ONCE(net->nft.gencursor); do_chain: rulenum = 0; -- cgit v1.2.3 From 652261a7a86c884147930fa1e5ddd82a8a5748da Mon Sep 17 00:00:00 2001 From: Mark Salter Date: Tue, 24 Mar 2015 14:02:38 +0000 Subject: ACPI: fix acpi_os_ioremap for arm64 The acpi_os_ioremap() function may be used to map normal RAM or IO regions. The current implementation simply uses ioremap_cache(). This will work for some architectures, but arm64 ioremap_cache() cannot be used to map IO regions which don't support caching. So for arm64, use ioremap() for non-RAM regions. CC: Rafael J Wysocki CC: Catalin Marinas Tested-by: Robert Richter Tested-by: Timur Tabi Acked-by: Robert Richter Acked-by: Rafael J. Wysocki Reviewed-by: Grant Likely Signed-off-by: Mark Salter Signed-off-by: Hanjun Guo Signed-off-by: Will Deacon --- arch/arm64/include/asm/acpi.h | 13 +++++++++++++ include/acpi/acpi_io.h | 4 ++++ 2 files changed, 17 insertions(+) (limited to 'include') diff --git a/arch/arm64/include/asm/acpi.h b/arch/arm64/include/asm/acpi.h index 8b837ab59988..40e092483e96 100644 --- a/arch/arm64/include/asm/acpi.h +++ b/arch/arm64/include/asm/acpi.h @@ -12,8 +12,21 @@ #ifndef _ASM_ACPI_H #define _ASM_ACPI_H +#include + /* Basic configuration for ACPI */ #ifdef CONFIG_ACPI +/* ACPI table mapping after acpi_gbl_permanent_mmap is set */ +static inline void __iomem *acpi_os_ioremap(acpi_physical_address phys, + acpi_size size) +{ + if (!page_is_ram(phys >> PAGE_SHIFT)) + return ioremap(phys, size); + + return ioremap_cache(phys, size); +} +#define acpi_os_ioremap acpi_os_ioremap + #define acpi_strict 1 /* No out-of-spec workarounds on ARM64 */ extern int acpi_disabled; extern int acpi_noirq; diff --git a/include/acpi/acpi_io.h b/include/acpi/acpi_io.h index 444671e9c65d..dd86c5fc102d 100644 --- a/include/acpi/acpi_io.h +++ b/include/acpi/acpi_io.h @@ -3,11 +3,15 @@ #include +#include + +#ifndef acpi_os_ioremap static inline void __iomem *acpi_os_ioremap(acpi_physical_address phys, acpi_size size) { return ioremap_cache(phys, size); } +#endif void __iomem *__init_refok acpi_os_map_iomem(acpi_physical_address phys, acpi_size size); -- cgit v1.2.3 From bbedb179944c29e5e449603163eec9951116fe39 Mon Sep 17 00:00:00 2001 From: Scott Wood Date: Wed, 11 Mar 2015 22:13:57 -0500 Subject: tracing: %pF is only for function pointers Use %pS for actual addresses, otherwise you'll get bad output on arches like ppc64 where %pF expects a function descriptor. Link: http://lkml.kernel.org/r/1426130037-17956-22-git-send-email-scottwood@freescale.com Signed-off-by: Scott Wood Signed-off-by: Steven Rostedt --- include/trace/events/btrfs.h | 4 ++-- include/trace/events/ext3.h | 2 +- include/trace/events/ext4.h | 6 +++--- include/trace/events/module.h | 4 ++-- include/trace/events/random.h | 10 +++++----- kernel/trace/trace_entries.h | 6 +++--- tools/lib/traceevent/event-parse.c | 2 +- 7 files changed, 17 insertions(+), 17 deletions(-) (limited to 'include') diff --git a/include/trace/events/btrfs.h b/include/trace/events/btrfs.h index 1faecea101f3..572e6503394a 100644 --- a/include/trace/events/btrfs.h +++ b/include/trace/events/btrfs.h @@ -962,7 +962,7 @@ TRACE_EVENT(alloc_extent_state, __entry->ip = IP ), - TP_printk("state=%p; mask = %s; caller = %pF", __entry->state, + TP_printk("state=%p; mask = %s; caller = %pS", __entry->state, show_gfp_flags(__entry->mask), (void *)__entry->ip) ); @@ -982,7 +982,7 @@ TRACE_EVENT(free_extent_state, __entry->ip = IP ), - TP_printk(" state=%p; caller = %pF", __entry->state, + TP_printk(" state=%p; caller = %pS", __entry->state, (void *)__entry->ip) ); diff --git a/include/trace/events/ext3.h b/include/trace/events/ext3.h index 6797b9de90ed..7f20707849bb 100644 --- a/include/trace/events/ext3.h +++ b/include/trace/events/ext3.h @@ -144,7 +144,7 @@ TRACE_EVENT(ext3_mark_inode_dirty, __entry->ip = IP; ), - TP_printk("dev %d,%d ino %lu caller %pF", + TP_printk("dev %d,%d ino %lu caller %pS", MAJOR(__entry->dev), MINOR(__entry->dev), (unsigned long) __entry->ino, (void *)__entry->ip) ); diff --git a/include/trace/events/ext4.h b/include/trace/events/ext4.h index 6e5abd6d38a2..47fca36ee426 100644 --- a/include/trace/events/ext4.h +++ b/include/trace/events/ext4.h @@ -240,7 +240,7 @@ TRACE_EVENT(ext4_mark_inode_dirty, __entry->ip = IP; ), - TP_printk("dev %d,%d ino %lu caller %pF", + TP_printk("dev %d,%d ino %lu caller %pS", MAJOR(__entry->dev), MINOR(__entry->dev), (unsigned long) __entry->ino, (void *)__entry->ip) ); @@ -1762,7 +1762,7 @@ TRACE_EVENT(ext4_journal_start, __entry->rsv_blocks = rsv_blocks; ), - TP_printk("dev %d,%d blocks, %d rsv_blocks, %d caller %pF", + TP_printk("dev %d,%d blocks, %d rsv_blocks, %d caller %pS", MAJOR(__entry->dev), MINOR(__entry->dev), __entry->blocks, __entry->rsv_blocks, (void *)__entry->ip) ); @@ -1784,7 +1784,7 @@ TRACE_EVENT(ext4_journal_start_reserved, __entry->blocks = blocks; ), - TP_printk("dev %d,%d blocks, %d caller %pF", + TP_printk("dev %d,%d blocks, %d caller %pS", MAJOR(__entry->dev), MINOR(__entry->dev), __entry->blocks, (void *)__entry->ip) ); diff --git a/include/trace/events/module.h b/include/trace/events/module.h index 81c4c183d348..28c45997e451 100644 --- a/include/trace/events/module.h +++ b/include/trace/events/module.h @@ -84,7 +84,7 @@ DECLARE_EVENT_CLASS(module_refcnt, __assign_str(name, mod->name); ), - TP_printk("%s call_site=%pf refcnt=%d", + TP_printk("%s call_site=%ps refcnt=%d", __get_str(name), (void *)__entry->ip, __entry->refcnt) ); @@ -121,7 +121,7 @@ TRACE_EVENT(module_request, __assign_str(name, name); ), - TP_printk("%s wait=%d call_site=%pf", + TP_printk("%s wait=%d call_site=%ps", __get_str(name), (int)__entry->wait, (void *)__entry->ip) ); diff --git a/include/trace/events/random.h b/include/trace/events/random.h index 805af6db41cc..4684de344c5d 100644 --- a/include/trace/events/random.h +++ b/include/trace/events/random.h @@ -22,7 +22,7 @@ TRACE_EVENT(add_device_randomness, __entry->IP = IP; ), - TP_printk("bytes %d caller %pF", + TP_printk("bytes %d caller %pS", __entry->bytes, (void *)__entry->IP) ); @@ -43,7 +43,7 @@ DECLARE_EVENT_CLASS(random__mix_pool_bytes, __entry->IP = IP; ), - TP_printk("%s pool: bytes %d caller %pF", + TP_printk("%s pool: bytes %d caller %pS", __entry->pool_name, __entry->bytes, (void *)__entry->IP) ); @@ -82,7 +82,7 @@ TRACE_EVENT(credit_entropy_bits, ), TP_printk("%s pool: bits %d entropy_count %d entropy_total %d " - "caller %pF", __entry->pool_name, __entry->bits, + "caller %pS", __entry->pool_name, __entry->bits, __entry->entropy_count, __entry->entropy_total, (void *)__entry->IP) ); @@ -207,7 +207,7 @@ DECLARE_EVENT_CLASS(random__get_random_bytes, __entry->IP = IP; ), - TP_printk("nbytes %d caller %pF", __entry->nbytes, (void *)__entry->IP) + TP_printk("nbytes %d caller %pS", __entry->nbytes, (void *)__entry->IP) ); DEFINE_EVENT(random__get_random_bytes, get_random_bytes, @@ -242,7 +242,7 @@ DECLARE_EVENT_CLASS(random__extract_entropy, __entry->IP = IP; ), - TP_printk("%s pool: nbytes %d entropy_count %d caller %pF", + TP_printk("%s pool: nbytes %d entropy_count %d caller %pS", __entry->pool_name, __entry->nbytes, __entry->entropy_count, (void *)__entry->IP) ); diff --git a/kernel/trace/trace_entries.h b/kernel/trace/trace_entries.h index e2d027ac66a2..ee7b94a4810a 100644 --- a/kernel/trace/trace_entries.h +++ b/kernel/trace/trace_entries.h @@ -223,7 +223,7 @@ FTRACE_ENTRY(bprint, bprint_entry, __dynamic_array( u32, buf ) ), - F_printk("%pf: %s", + F_printk("%ps: %s", (void *)__entry->ip, __entry->fmt), FILTER_OTHER @@ -238,7 +238,7 @@ FTRACE_ENTRY(print, print_entry, __dynamic_array( char, buf ) ), - F_printk("%pf: %s", + F_printk("%ps: %s", (void *)__entry->ip, __entry->buf), FILTER_OTHER @@ -253,7 +253,7 @@ FTRACE_ENTRY(bputs, bputs_entry, __field( const char *, str ) ), - F_printk("%pf: %s", + F_printk("%ps: %s", (void *)__entry->ip, __entry->str), FILTER_OTHER diff --git a/tools/lib/traceevent/event-parse.c b/tools/lib/traceevent/event-parse.c index afe20ed9fac8..2c0bd8f2aad0 100644 --- a/tools/lib/traceevent/event-parse.c +++ b/tools/lib/traceevent/event-parse.c @@ -3976,7 +3976,7 @@ static struct print_arg *make_bprint_args(char *fmt, void *data, int size, struc if (asprintf(&arg->atom.atom, "%lld", ip) < 0) goto out_free; - /* skip the first "%pf: " */ + /* skip the first "%ps: " */ for (ptr = fmt + 5, bptr = data + field->offset; bptr < data + size && *ptr; ptr++) { int ls = 0; -- cgit v1.2.3 From 44caf2f37f009b2affa743073fa935826b6ab2fd Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Fri, 13 Feb 2015 14:25:24 +0000 Subject: iommu/vt-d: kill bogus ecap_niotlb_iunits() As far back as I can see (which right now is a draft of the v1.2 spec dating from September 2008), bits 24-31 of the Extended Capability Register have already been reserved. I have no idea why anyone ever thought there would be multiple sets of IOTLB registers, but we've never supported them and all we do is make sure we map enough MMIO space for them. Kill it dead. Those bits do actually have a different meaning now. Signed-off-by: David Woodhouse --- include/linux/intel-iommu.h | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'include') diff --git a/include/linux/intel-iommu.h b/include/linux/intel-iommu.h index a65208a8fe18..ee24ada20428 100644 --- a/include/linux/intel-iommu.h +++ b/include/linux/intel-iommu.h @@ -115,10 +115,8 @@ static inline void dmar_writeq(void __iomem *addr, u64 val) * Extended Capability Register */ -#define ecap_niotlb_iunits(e) ((((e) >> 24) & 0xff) + 1) #define ecap_iotlb_offset(e) ((((e) >> 8) & 0x3ff) * 16) -#define ecap_max_iotlb_offset(e) \ - (ecap_iotlb_offset(e) + ecap_niotlb_iunits(e) * 16) +#define ecap_max_iotlb_offset(e) (ecap_iotlb_offset(e) + 16) #define ecap_coherent(e) ((e) & 0x1) #define ecap_qis(e) ((e) & 0x2) #define ecap_pass_through(e) ((e >> 6) & 0x1) -- cgit v1.2.3 From 7e3ea6d5c4f4880dbdf85f4083f40d8a3dc9b906 Mon Sep 17 00:00:00 2001 From: Ying Xue Date: Wed, 25 Mar 2015 14:13:01 +0800 Subject: sctp: avoid to repeatedly declare external variables Move the declaration for external variables to sctp.h file avoiding to repeatedly declare them with extern keyword. Signed-off-by: Ying Xue Signed-off-by: David S. Miller --- include/net/sctp/sctp.h | 3 +++ net/sctp/socket.c | 5 ----- net/sctp/sysctl.c | 4 ---- 3 files changed, 3 insertions(+), 9 deletions(-) (limited to 'include') diff --git a/include/net/sctp/sctp.h b/include/net/sctp/sctp.h index 856f01cb51dd..c56a438c3a1e 100644 --- a/include/net/sctp/sctp.h +++ b/include/net/sctp/sctp.h @@ -166,6 +166,9 @@ void sctp_remaddr_proc_exit(struct net *net); */ extern struct kmem_cache *sctp_chunk_cachep __read_mostly; extern struct kmem_cache *sctp_bucket_cachep __read_mostly; +extern long sysctl_sctp_mem[3]; +extern int sysctl_sctp_rmem[3]; +extern int sysctl_sctp_wmem[3]; /* * Section: Macros, externs, and inlines diff --git a/net/sctp/socket.c b/net/sctp/socket.c index f1a65398f311..f09de7fac2e6 100644 --- a/net/sctp/socket.c +++ b/net/sctp/socket.c @@ -102,11 +102,6 @@ static int sctp_autobind(struct sock *sk); static void sctp_sock_migrate(struct sock *, struct sock *, struct sctp_association *, sctp_socket_type_t); -extern struct kmem_cache *sctp_bucket_cachep; -extern long sysctl_sctp_mem[3]; -extern int sysctl_sctp_rmem[3]; -extern int sysctl_sctp_wmem[3]; - static int sctp_memory_pressure; static atomic_long_t sctp_memory_allocated; struct percpu_counter sctp_sockets_allocated; diff --git a/net/sctp/sysctl.c b/net/sctp/sysctl.c index 2e9ada10fd84..26d50c565f54 100644 --- a/net/sctp/sysctl.c +++ b/net/sctp/sysctl.c @@ -58,10 +58,6 @@ static unsigned long max_autoclose_max = (MAX_SCHEDULE_TIMEOUT / HZ > UINT_MAX) ? UINT_MAX : MAX_SCHEDULE_TIMEOUT / HZ; -extern long sysctl_sctp_mem[3]; -extern int sysctl_sctp_rmem[3]; -extern int sysctl_sctp_wmem[3]; - static int proc_sctp_do_hmac_alg(struct ctl_table *ctl, int write, void __user *buffer, size_t *lenp, loff_t *ppos); -- cgit v1.2.3 From 4423f5e7d28c26af31df711c5c21eeacfac737b4 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Wed, 25 Mar 2015 15:43:39 +0000 Subject: iommu/vt-d: Add new extended capabilities from v2.3 VT-d specification Signed-off-by: David Woodhouse --- include/linux/intel-iommu.h | 14 ++++++++++++++ 1 file changed, 14 insertions(+) (limited to 'include') diff --git a/include/linux/intel-iommu.h b/include/linux/intel-iommu.h index ee24ada20428..796ef9645827 100644 --- a/include/linux/intel-iommu.h +++ b/include/linux/intel-iommu.h @@ -115,6 +115,17 @@ static inline void dmar_writeq(void __iomem *addr, u64 val) * Extended Capability Register */ +#define ecap_pss(e) ((e >> 35) & 0x1f) +#define ecap_eafs(e) ((e >> 34) & 0x1) +#define ecap_nwfs(e) ((e >> 33) & 0x1) +#define ecap_srs(e) ((e >> 31) & 0x1) +#define ecap_ers(e) ((e >> 30) & 0x1) +#define ecap_prs(e) ((e >> 29) & 0x1) +#define ecap_pasid(e) ((e >> 28) & 0x1) +#define ecap_dis(e) ((e >> 27) & 0x1) +#define ecap_nest(e) ((e >> 26) & 0x1) +#define ecap_mts(e) ((e >> 25) & 0x1) +#define ecap_ecs(e) ((e >> 24) & 0x1) #define ecap_iotlb_offset(e) ((((e) >> 8) & 0x3ff) * 16) #define ecap_max_iotlb_offset(e) (ecap_iotlb_offset(e) + 16) #define ecap_coherent(e) ((e) & 0x1) @@ -178,6 +189,9 @@ static inline void dmar_writeq(void __iomem *addr, u64 val) #define DMA_GSTS_IRES (((u32)1) << 25) #define DMA_GSTS_CFIS (((u32)1) << 23) +/* DMA_RTADDR_REG */ +#define DMA_RTADDR_RTT (((u64)1) << 11) + /* CCMD_REG */ #define DMA_CCMD_ICC (((u64)1) << 63) #define DMA_CCMD_GLOBAL_INVL (((u64)1) << 61) -- cgit v1.2.3 From 49f7b33e63fec9d16e7ee62ba8f8ab4159cbdc26 Mon Sep 17 00:00:00 2001 From: Patrick McHardy Date: Wed, 25 Mar 2015 13:07:45 +0000 Subject: rhashtable: provide len to obj_hashfn nftables sets will be converted to use so called setextensions, moving the key to a non-fixed position. To hash it, the obj_hashfn must be used, however it so far doesn't receive the length parameter. Pass the key length to obj_hashfn() and convert existing users. Signed-off-by: Patrick McHardy Signed-off-by: Pablo Neira Ayuso --- include/linux/rhashtable.h | 6 ++++-- lib/rhashtable.c | 2 +- net/netlink/af_netlink.c | 2 +- 3 files changed, 6 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/include/linux/rhashtable.h b/include/linux/rhashtable.h index 99f2e49a8a07..e23d242d1230 100644 --- a/include/linux/rhashtable.h +++ b/include/linux/rhashtable.h @@ -88,7 +88,7 @@ struct rhashtable_compare_arg { }; typedef u32 (*rht_hashfn_t)(const void *data, u32 len, u32 seed); -typedef u32 (*rht_obj_hashfn_t)(const void *data, u32 seed); +typedef u32 (*rht_obj_hashfn_t)(const void *data, u32 len, u32 seed); typedef int (*rht_obj_cmpfn_t)(struct rhashtable_compare_arg *arg, const void *obj); @@ -242,7 +242,9 @@ static inline unsigned int rht_head_hashfn( const char *ptr = rht_obj(ht, he); return likely(params.obj_hashfn) ? - rht_bucket_index(tbl, params.obj_hashfn(ptr, tbl->hash_rnd)) : + rht_bucket_index(tbl, params.obj_hashfn(ptr, params.key_len ?: + ht->p.key_len, + tbl->hash_rnd)) : rht_key_hashfn(ht, tbl, ptr + params.key_offset, params); } diff --git a/lib/rhashtable.c b/lib/rhashtable.c index 4b7b7e672b93..4898442b837f 100644 --- a/lib/rhashtable.c +++ b/lib/rhashtable.c @@ -691,7 +691,7 @@ static u32 rhashtable_jhash2(const void *key, u32 length, u32 seed) * struct rhash_head node; * }; * - * u32 my_hash_fn(const void *data, u32 seed) + * u32 my_hash_fn(const void *data, u32 len, u32 seed) * { * struct test_obj *obj = data; * diff --git a/net/netlink/af_netlink.c b/net/netlink/af_netlink.c index 4caa809dbbe0..19909d0786a2 100644 --- a/net/netlink/af_netlink.c +++ b/net/netlink/af_netlink.c @@ -3127,7 +3127,7 @@ static struct pernet_operations __net_initdata netlink_net_ops = { .exit = netlink_net_exit, }; -static inline u32 netlink_hash(const void *data, u32 seed) +static inline u32 netlink_hash(const void *data, u32 len, u32 seed) { const struct netlink_sock *nlk = data; struct netlink_compare_arg arg; -- cgit v1.2.3 From 3ac4c07a24007f0f45d2082b745508768a8e21cf Mon Sep 17 00:00:00 2001 From: Patrick McHardy Date: Wed, 25 Mar 2015 13:07:49 +0000 Subject: netfilter: nf_tables: add set extensions Add simple set extension infrastructure for maintaining variable sized and optional per element data. Signed-off-by: Patrick McHardy Signed-off-by: Pablo Neira Ayuso --- include/net/netfilter/nf_tables.h | 105 ++++++++++++++++++++++++++++++++++++++ net/netfilter/nf_tables_api.c | 16 ++++++ 2 files changed, 121 insertions(+) (limited to 'include') diff --git a/include/net/netfilter/nf_tables.h b/include/net/netfilter/nf_tables.h index ace67a549b30..038f8a67ca1f 100644 --- a/include/net/netfilter/nf_tables.h +++ b/include/net/netfilter/nf_tables.h @@ -311,6 +311,111 @@ int nf_tables_bind_set(const struct nft_ctx *ctx, struct nft_set *set, void nf_tables_unbind_set(const struct nft_ctx *ctx, struct nft_set *set, struct nft_set_binding *binding); +/** + * enum nft_set_extensions - set extension type IDs + * + * @NFT_SET_EXT_KEY: element key + * @NFT_SET_EXT_DATA: mapping data + * @NFT_SET_EXT_FLAGS: element flags + * @NFT_SET_EXT_NUM: number of extension types + */ +enum nft_set_extensions { + NFT_SET_EXT_KEY, + NFT_SET_EXT_DATA, + NFT_SET_EXT_FLAGS, + NFT_SET_EXT_NUM +}; + +/** + * struct nft_set_ext_type - set extension type + * + * @len: fixed part length of the extension + * @align: alignment requirements of the extension + */ +struct nft_set_ext_type { + u8 len; + u8 align; +}; + +extern const struct nft_set_ext_type nft_set_ext_types[]; + +/** + * struct nft_set_ext_tmpl - set extension template + * + * @len: length of extension area + * @offset: offsets of individual extension types + */ +struct nft_set_ext_tmpl { + u16 len; + u8 offset[NFT_SET_EXT_NUM]; +}; + +/** + * struct nft_set_ext - set extensions + * + * @offset: offsets of individual extension types + * @data: beginning of extension data + */ +struct nft_set_ext { + u8 offset[NFT_SET_EXT_NUM]; + char data[0]; +}; + +static inline void nft_set_ext_prepare(struct nft_set_ext_tmpl *tmpl) +{ + memset(tmpl, 0, sizeof(*tmpl)); + tmpl->len = sizeof(struct nft_set_ext); +} + +static inline void nft_set_ext_add_length(struct nft_set_ext_tmpl *tmpl, u8 id, + unsigned int len) +{ + tmpl->len = ALIGN(tmpl->len, nft_set_ext_types[id].align); + BUG_ON(tmpl->len > U8_MAX); + tmpl->offset[id] = tmpl->len; + tmpl->len += nft_set_ext_types[id].len + len; +} + +static inline void nft_set_ext_add(struct nft_set_ext_tmpl *tmpl, u8 id) +{ + nft_set_ext_add_length(tmpl, id, 0); +} + +static inline void nft_set_ext_init(struct nft_set_ext *ext, + const struct nft_set_ext_tmpl *tmpl) +{ + memcpy(ext->offset, tmpl->offset, sizeof(ext->offset)); +} + +static inline bool __nft_set_ext_exists(const struct nft_set_ext *ext, u8 id) +{ + return !!ext->offset[id]; +} + +static inline bool nft_set_ext_exists(const struct nft_set_ext *ext, u8 id) +{ + return ext && __nft_set_ext_exists(ext, id); +} + +static inline void *nft_set_ext(const struct nft_set_ext *ext, u8 id) +{ + return (void *)ext + ext->offset[id]; +} + +static inline struct nft_data *nft_set_ext_key(const struct nft_set_ext *ext) +{ + return nft_set_ext(ext, NFT_SET_EXT_KEY); +} + +static inline struct nft_data *nft_set_ext_data(const struct nft_set_ext *ext) +{ + return nft_set_ext(ext, NFT_SET_EXT_DATA); +} + +static inline u8 *nft_set_ext_flags(const struct nft_set_ext *ext) +{ + return nft_set_ext(ext, NFT_SET_EXT_FLAGS); +} /** * struct nft_expr_type - nf_tables expression type diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c index 0b969b66cb77..972c47f6e823 100644 --- a/net/netfilter/nf_tables_api.c +++ b/net/netfilter/nf_tables_api.c @@ -2827,6 +2827,22 @@ void nf_tables_unbind_set(const struct nft_ctx *ctx, struct nft_set *set, nf_tables_set_destroy(ctx, set); } +const struct nft_set_ext_type nft_set_ext_types[] = { + [NFT_SET_EXT_KEY] = { + .len = sizeof(struct nft_data), + .align = __alignof__(struct nft_data), + }, + [NFT_SET_EXT_DATA] = { + .len = sizeof(struct nft_data), + .align = __alignof__(struct nft_data), + }, + [NFT_SET_EXT_FLAGS] = { + .len = sizeof(u8), + .align = __alignof__(u8), + }, +}; +EXPORT_SYMBOL_GPL(nft_set_ext_types); + /* * Set elements */ -- cgit v1.2.3 From fe2811ebeb97a7a76de0b2b35f13600169508393 Mon Sep 17 00:00:00 2001 From: Patrick McHardy Date: Wed, 25 Mar 2015 13:07:50 +0000 Subject: netfilter: nf_tables: convert hash and rbtree to set extensions The set implementations' private struct will only contain the elements needed to maintain the search structure, all other elements are moved to the set extensions. Element allocation and initialization is performed centrally by nf_tables_api instead of by the different set implementations' ->insert() functions. A new "elemsize" member in the set ops specifies the amount of memory to reserve for internal usage. Destruction will also be moved out of the set implementations by a following patch. Except for element allocation, the patch is a simple conversion to using data from the extension area. Signed-off-by: Patrick McHardy Signed-off-by: Pablo Neira Ayuso --- include/net/netfilter/nf_tables.h | 14 +++-- net/netfilter/nf_tables_api.c | 119 ++++++++++++++++++++++++++++---------- net/netfilter/nft_hash.c | 56 +++++------------- net/netfilter/nft_rbtree.c | 64 +++++++------------- 4 files changed, 132 insertions(+), 121 deletions(-) (limited to 'include') diff --git a/include/net/netfilter/nf_tables.h b/include/net/netfilter/nf_tables.h index 038f8a67ca1f..ef3457c1cb62 100644 --- a/include/net/netfilter/nf_tables.h +++ b/include/net/netfilter/nf_tables.h @@ -140,8 +140,7 @@ struct nft_userdata { * * @cookie: implementation specific element cookie * @key: element key - * @data: element data (maps only) - * @flags: element flags (end of interval) + * @priv: element private data and extensions * * The cookie can be used to store a handle to the element for subsequent * removal. @@ -149,8 +148,7 @@ struct nft_userdata { struct nft_set_elem { void *cookie; struct nft_data key; - struct nft_data data; - u32 flags; + void *priv; }; struct nft_set; @@ -214,6 +212,7 @@ struct nft_set_estimate { * @destroy: destroy private data of set instance * @list: nf_tables_set_ops list node * @owner: module reference + * @elemsize: element private size * @features: features supported by the implementation */ struct nft_set_ops { @@ -241,6 +240,7 @@ struct nft_set_ops { struct list_head list; struct module *owner; + unsigned int elemsize; u32 features; }; @@ -417,6 +417,12 @@ static inline u8 *nft_set_ext_flags(const struct nft_set_ext *ext) return nft_set_ext(ext, NFT_SET_EXT_FLAGS); } +static inline struct nft_set_ext *nft_set_elem_ext(const struct nft_set *set, + void *elem) +{ + return elem + set->ops->elemsize; +} + /** * struct nft_expr_type - nf_tables expression type * diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c index 972c47f6e823..99cb884b985f 100644 --- a/net/netfilter/nf_tables_api.c +++ b/net/netfilter/nf_tables_api.c @@ -2771,10 +2771,11 @@ static int nf_tables_bind_check_setelem(const struct nft_ctx *ctx, const struct nft_set_iter *iter, const struct nft_set_elem *elem) { + const struct nft_set_ext *ext = nft_set_elem_ext(set, elem->priv); enum nft_registers dreg; dreg = nft_type_to_reg(set->dtype); - return nft_validate_data_load(ctx, dreg, &elem->data, + return nft_validate_data_load(ctx, dreg, nft_set_ext_data(ext), set->dtype == NFT_DATA_VERDICT ? NFT_DATA_VERDICT : NFT_DATA_VALUE); } @@ -2889,6 +2890,7 @@ static int nf_tables_fill_setelem(struct sk_buff *skb, const struct nft_set *set, const struct nft_set_elem *elem) { + const struct nft_set_ext *ext = nft_set_elem_ext(set, elem->priv); unsigned char *b = skb_tail_pointer(skb); struct nlattr *nest; @@ -2896,20 +2898,20 @@ static int nf_tables_fill_setelem(struct sk_buff *skb, if (nest == NULL) goto nla_put_failure; - if (nft_data_dump(skb, NFTA_SET_ELEM_KEY, &elem->key, NFT_DATA_VALUE, - set->klen) < 0) + if (nft_data_dump(skb, NFTA_SET_ELEM_KEY, nft_set_ext_key(ext), + NFT_DATA_VALUE, set->klen) < 0) goto nla_put_failure; - if (set->flags & NFT_SET_MAP && - !(elem->flags & NFT_SET_ELEM_INTERVAL_END) && - nft_data_dump(skb, NFTA_SET_ELEM_DATA, &elem->data, + if (nft_set_ext_exists(ext, NFT_SET_EXT_DATA) && + nft_data_dump(skb, NFTA_SET_ELEM_DATA, nft_set_ext_data(ext), set->dtype == NFT_DATA_VERDICT ? NFT_DATA_VERDICT : NFT_DATA_VALUE, set->dlen) < 0) goto nla_put_failure; - if (elem->flags != 0) - if (nla_put_be32(skb, NFTA_SET_ELEM_FLAGS, htonl(elem->flags))) - goto nla_put_failure; + if (nft_set_ext_exists(ext, NFT_SET_EXT_FLAGS) && + nla_put_be32(skb, NFTA_SET_ELEM_FLAGS, + htonl(*nft_set_ext_flags(ext)))) + goto nla_put_failure; nla_nest_end(skb, nest); return 0; @@ -3130,15 +3132,42 @@ static struct nft_trans *nft_trans_elem_alloc(struct nft_ctx *ctx, return trans; } +static void *nft_set_elem_init(const struct nft_set *set, + const struct nft_set_ext_tmpl *tmpl, + const struct nft_data *key, + const struct nft_data *data, + gfp_t gfp) +{ + struct nft_set_ext *ext; + void *elem; + + elem = kzalloc(set->ops->elemsize + tmpl->len, gfp); + if (elem == NULL) + return NULL; + + ext = nft_set_elem_ext(set, elem); + nft_set_ext_init(ext, tmpl); + + memcpy(nft_set_ext_key(ext), key, set->klen); + if (nft_set_ext_exists(ext, NFT_SET_EXT_DATA)) + memcpy(nft_set_ext_data(ext), data, set->dlen); + + return elem; +} + static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set, const struct nlattr *attr) { struct nlattr *nla[NFTA_SET_ELEM_MAX + 1]; struct nft_data_desc d1, d2; + struct nft_set_ext_tmpl tmpl; + struct nft_set_ext *ext; struct nft_set_elem elem; struct nft_set_binding *binding; + struct nft_data data; enum nft_registers dreg; struct nft_trans *trans; + u32 flags; int err; if (set->size && set->nelems == set->size) @@ -3152,22 +3181,26 @@ static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set, if (nla[NFTA_SET_ELEM_KEY] == NULL) return -EINVAL; - elem.flags = 0; + nft_set_ext_prepare(&tmpl); + + flags = 0; if (nla[NFTA_SET_ELEM_FLAGS] != NULL) { - elem.flags = ntohl(nla_get_be32(nla[NFTA_SET_ELEM_FLAGS])); - if (elem.flags & ~NFT_SET_ELEM_INTERVAL_END) + flags = ntohl(nla_get_be32(nla[NFTA_SET_ELEM_FLAGS])); + if (flags & ~NFT_SET_ELEM_INTERVAL_END) return -EINVAL; if (!(set->flags & NFT_SET_INTERVAL) && - elem.flags & NFT_SET_ELEM_INTERVAL_END) + flags & NFT_SET_ELEM_INTERVAL_END) return -EINVAL; + if (flags != 0) + nft_set_ext_add(&tmpl, NFT_SET_EXT_FLAGS); } if (set->flags & NFT_SET_MAP) { if (nla[NFTA_SET_ELEM_DATA] == NULL && - !(elem.flags & NFT_SET_ELEM_INTERVAL_END)) + !(flags & NFT_SET_ELEM_INTERVAL_END)) return -EINVAL; if (nla[NFTA_SET_ELEM_DATA] != NULL && - elem.flags & NFT_SET_ELEM_INTERVAL_END) + flags & NFT_SET_ELEM_INTERVAL_END) return -EINVAL; } else { if (nla[NFTA_SET_ELEM_DATA] != NULL) @@ -3185,8 +3218,10 @@ static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set, if (set->ops->get(set, &elem) == 0) goto err2; + nft_set_ext_add(&tmpl, NFT_SET_EXT_KEY); + if (nla[NFTA_SET_ELEM_DATA] != NULL) { - err = nft_data_init(ctx, &elem.data, &d2, nla[NFTA_SET_ELEM_DATA]); + err = nft_data_init(ctx, &data, &d2, nla[NFTA_SET_ELEM_DATA]); if (err < 0) goto err2; @@ -3203,29 +3238,42 @@ static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set, }; err = nft_validate_data_load(&bind_ctx, dreg, - &elem.data, d2.type); + &data, d2.type); if (err < 0) goto err3; } + + nft_set_ext_add(&tmpl, NFT_SET_EXT_DATA); } + err = -ENOMEM; + elem.priv = nft_set_elem_init(set, &tmpl, &elem.key, &data, GFP_KERNEL); + if (elem.priv == NULL) + goto err3; + + ext = nft_set_elem_ext(set, elem.priv); + if (flags) + *nft_set_ext_flags(ext) = flags; + trans = nft_trans_elem_alloc(ctx, NFT_MSG_NEWSETELEM, set); if (trans == NULL) - goto err3; + goto err4; err = set->ops->insert(set, &elem); if (err < 0) - goto err4; + goto err5; nft_trans_elem(trans) = elem; list_add_tail(&trans->list, &ctx->net->nft.commit_list); return 0; -err4: +err5: kfree(trans); +err4: + kfree(elem.priv); err3: if (nla[NFTA_SET_ELEM_DATA] != NULL) - nft_data_uninit(&elem.data, d2.type); + nft_data_uninit(&data, d2.type); err2: nft_data_uninit(&elem.key, d1.type); err1: @@ -3557,6 +3605,7 @@ static int nf_tables_commit(struct sk_buff *skb) struct net *net = sock_net(skb->sk); struct nft_trans *trans, *next; struct nft_trans_elem *te; + struct nft_set_ext *ext; /* Bump generation counter, invalidate any dump in progress */ while (++net->nft.base_seq == 0); @@ -3641,14 +3690,16 @@ static int nf_tables_commit(struct sk_buff *skb) break; case NFT_MSG_DELSETELEM: te = (struct nft_trans_elem *)trans->data; + ext = nft_set_elem_ext(te->set, te->elem.priv); + nf_tables_setelem_notify(&trans->ctx, te->set, &te->elem, NFT_MSG_DELSETELEM, 0); te->set->ops->get(te->set, &te->elem); nft_data_uninit(&te->elem.key, NFT_DATA_VALUE); - if (te->set->flags & NFT_SET_MAP && - !(te->elem.flags & NFT_SET_ELEM_INTERVAL_END)) - nft_data_uninit(&te->elem.data, te->set->dtype); + if (nft_set_ext_exists(ext, NFT_SET_EXT_DATA)) + nft_data_uninit(nft_set_ext_data(ext), + te->set->dtype); te->set->ops->remove(te->set, &te->elem); nft_trans_destroy(trans); break; @@ -3691,6 +3742,7 @@ static int nf_tables_abort(struct sk_buff *skb) struct net *net = sock_net(skb->sk); struct nft_trans *trans, *next; struct nft_trans_elem *te; + struct nft_set_ext *ext; list_for_each_entry_safe(trans, next, &net->nft.commit_list, list) { switch (trans->msg_type) { @@ -3752,11 +3804,13 @@ static int nf_tables_abort(struct sk_buff *skb) case NFT_MSG_NEWSETELEM: nft_trans_elem_set(trans)->nelems--; te = (struct nft_trans_elem *)trans->data; + ext = nft_set_elem_ext(te->set, te->elem.priv); + te->set->ops->get(te->set, &te->elem); nft_data_uninit(&te->elem.key, NFT_DATA_VALUE); - if (te->set->flags & NFT_SET_MAP && - !(te->elem.flags & NFT_SET_ELEM_INTERVAL_END)) - nft_data_uninit(&te->elem.data, te->set->dtype); + if (nft_set_ext_exists(ext, NFT_SET_EXT_DATA)) + nft_data_uninit(nft_set_ext_data(ext), + te->set->dtype); te->set->ops->remove(te->set, &te->elem); nft_trans_destroy(trans); break; @@ -3836,13 +3890,18 @@ static int nf_tables_loop_check_setelem(const struct nft_ctx *ctx, const struct nft_set_iter *iter, const struct nft_set_elem *elem) { - if (elem->flags & NFT_SET_ELEM_INTERVAL_END) + const struct nft_set_ext *ext = nft_set_elem_ext(set, elem->priv); + const struct nft_data *data; + + if (nft_set_ext_exists(ext, NFT_SET_EXT_FLAGS) && + *nft_set_ext_flags(ext) & NFT_SET_ELEM_INTERVAL_END) return 0; - switch (elem->data.verdict) { + data = nft_set_ext_data(ext); + switch (data->verdict) { case NFT_JUMP: case NFT_GOTO: - return nf_tables_check_loops(ctx, elem->data.chain); + return nf_tables_check_loops(ctx, data->chain); default: return 0; } diff --git a/net/netfilter/nft_hash.c b/net/netfilter/nft_hash.c index dc96a7e94f80..15951a823d1d 100644 --- a/net/netfilter/nft_hash.c +++ b/net/netfilter/nft_hash.c @@ -29,8 +29,7 @@ struct nft_hash { struct nft_hash_elem { struct rhash_head node; - struct nft_data key; - struct nft_data data[]; + struct nft_set_ext ext; }; struct nft_hash_cmp_arg { @@ -51,7 +50,7 @@ static inline u32 nft_hash_obj(const void *data, u32 len, u32 seed) { const struct nft_hash_elem *he = data; - return jhash(&he->key, len, seed); + return jhash(nft_set_ext_key(&he->ext), len, seed); } static inline int nft_hash_cmp(struct rhashtable_compare_arg *arg, @@ -60,7 +59,7 @@ static inline int nft_hash_cmp(struct rhashtable_compare_arg *arg, const struct nft_hash_cmp_arg *x = arg->key; const struct nft_hash_elem *he = ptr; - if (nft_data_cmp(&he->key, x->key, x->set->klen)) + if (nft_data_cmp(nft_set_ext_key(&he->ext), x->key, x->set->klen)) return 1; return 0; } @@ -78,7 +77,7 @@ static bool nft_hash_lookup(const struct nft_set *set, he = rhashtable_lookup_fast(&priv->ht, &arg, nft_hash_params); if (he && set->flags & NFT_SET_MAP) - nft_data_copy(data, he->data); + nft_data_copy(data, nft_set_ext_data(&he->ext)); return !!he; } @@ -87,43 +86,22 @@ static int nft_hash_insert(const struct nft_set *set, const struct nft_set_elem *elem) { struct nft_hash *priv = nft_set_priv(set); - struct nft_hash_elem *he; + struct nft_hash_elem *he = elem->priv; struct nft_hash_cmp_arg arg = { .set = set, .key = &elem->key, }; - unsigned int size; - int err; - - if (elem->flags != 0) - return -EINVAL; - - size = sizeof(*he); - if (set->flags & NFT_SET_MAP) - size += sizeof(he->data[0]); - - he = kzalloc(size, GFP_KERNEL); - if (he == NULL) - return -ENOMEM; - - nft_data_copy(&he->key, &elem->key); - if (set->flags & NFT_SET_MAP) - nft_data_copy(he->data, &elem->data); - - err = rhashtable_lookup_insert_key(&priv->ht, &arg, &he->node, - nft_hash_params); - if (err) - kfree(he); - return err; + return rhashtable_lookup_insert_key(&priv->ht, &arg, &he->node, + nft_hash_params); } static void nft_hash_elem_destroy(const struct nft_set *set, struct nft_hash_elem *he) { - nft_data_uninit(&he->key, NFT_DATA_VALUE); + nft_data_uninit(nft_set_ext_key(&he->ext), NFT_DATA_VALUE); if (set->flags & NFT_SET_MAP) - nft_data_uninit(he->data, set->dtype); + nft_data_uninit(nft_set_ext_data(&he->ext), set->dtype); kfree(he); } @@ -150,10 +128,7 @@ static int nft_hash_get(const struct nft_set *set, struct nft_set_elem *elem) if (!he) return -ENOENT; - elem->cookie = he; - elem->flags = 0; - if (set->flags & NFT_SET_MAP) - nft_data_copy(&elem->data, he->data); + elem->priv = he; return 0; } @@ -162,7 +137,7 @@ static void nft_hash_walk(const struct nft_ctx *ctx, const struct nft_set *set, struct nft_set_iter *iter) { struct nft_hash *priv = nft_set_priv(set); - const struct nft_hash_elem *he; + struct nft_hash_elem *he; struct rhashtable_iter hti; struct nft_set_elem elem; int err; @@ -192,10 +167,7 @@ static void nft_hash_walk(const struct nft_ctx *ctx, const struct nft_set *set, if (iter->count < iter->skip) goto cont; - memcpy(&elem.key, &he->key, sizeof(elem.key)); - if (set->flags & NFT_SET_MAP) - memcpy(&elem.data, he->data, sizeof(elem.data)); - elem.flags = 0; + elem.priv = he; iter->err = iter->fn(ctx, set, iter, &elem); if (iter->err < 0) @@ -254,9 +226,6 @@ static bool nft_hash_estimate(const struct nft_set_desc *desc, u32 features, unsigned int esize; esize = sizeof(struct nft_hash_elem); - if (features & NFT_SET_MAP) - esize += FIELD_SIZEOF(struct nft_hash_elem, data[0]); - if (desc->size) { est->size = sizeof(struct nft_hash) + roundup_pow_of_two(desc->size * 4 / 3) * @@ -278,6 +247,7 @@ static bool nft_hash_estimate(const struct nft_set_desc *desc, u32 features, static struct nft_set_ops nft_hash_ops __read_mostly = { .privsize = nft_hash_privsize, + .elemsize = offsetof(struct nft_hash_elem, ext), .estimate = nft_hash_estimate, .init = nft_hash_init, .destroy = nft_hash_destroy, diff --git a/net/netfilter/nft_rbtree.c b/net/netfilter/nft_rbtree.c index 2c75361077f7..ebf6e60df41c 100644 --- a/net/netfilter/nft_rbtree.c +++ b/net/netfilter/nft_rbtree.c @@ -26,9 +26,7 @@ struct nft_rbtree { struct nft_rbtree_elem { struct rb_node node; - u16 flags; - struct nft_data key; - struct nft_data data[]; + struct nft_set_ext ext; }; static bool nft_rbtree_lookup(const struct nft_set *set, @@ -45,7 +43,7 @@ static bool nft_rbtree_lookup(const struct nft_set *set, while (parent != NULL) { rbe = rb_entry(parent, struct nft_rbtree_elem, node); - d = nft_data_cmp(&rbe->key, key, set->klen); + d = nft_data_cmp(nft_set_ext_key(&rbe->ext), key, set->klen); if (d < 0) { parent = parent->rb_left; interval = rbe; @@ -53,10 +51,12 @@ static bool nft_rbtree_lookup(const struct nft_set *set, parent = parent->rb_right; else { found: - if (rbe->flags & NFT_SET_ELEM_INTERVAL_END) + if (nft_set_ext_exists(&rbe->ext, NFT_SET_EXT_FLAGS) && + *nft_set_ext_flags(&rbe->ext) & + NFT_SET_ELEM_INTERVAL_END) goto out; if (set->flags & NFT_SET_MAP) - nft_data_copy(data, rbe->data); + nft_data_copy(data, nft_set_ext_data(&rbe->ext)); spin_unlock_bh(&nft_rbtree_lock); return true; @@ -75,10 +75,10 @@ out: static void nft_rbtree_elem_destroy(const struct nft_set *set, struct nft_rbtree_elem *rbe) { - nft_data_uninit(&rbe->key, NFT_DATA_VALUE); + nft_data_uninit(nft_set_ext_key(&rbe->ext), NFT_DATA_VALUE); if (set->flags & NFT_SET_MAP && - !(rbe->flags & NFT_SET_ELEM_INTERVAL_END)) - nft_data_uninit(rbe->data, set->dtype); + nft_set_ext_exists(&rbe->ext, NFT_SET_EXT_DATA)) + nft_data_uninit(nft_set_ext_data(&rbe->ext), set->dtype); kfree(rbe); } @@ -96,7 +96,9 @@ static int __nft_rbtree_insert(const struct nft_set *set, while (*p != NULL) { parent = *p; rbe = rb_entry(parent, struct nft_rbtree_elem, node); - d = nft_data_cmp(&rbe->key, &new->key, set->klen); + d = nft_data_cmp(nft_set_ext_key(&rbe->ext), + nft_set_ext_key(&new->ext), + set->klen); if (d < 0) p = &parent->rb_left; else if (d > 0) @@ -112,31 +114,13 @@ static int __nft_rbtree_insert(const struct nft_set *set, static int nft_rbtree_insert(const struct nft_set *set, const struct nft_set_elem *elem) { - struct nft_rbtree_elem *rbe; - unsigned int size; + struct nft_rbtree_elem *rbe = elem->priv; int err; - size = sizeof(*rbe); - if (set->flags & NFT_SET_MAP && - !(elem->flags & NFT_SET_ELEM_INTERVAL_END)) - size += sizeof(rbe->data[0]); - - rbe = kzalloc(size, GFP_KERNEL); - if (rbe == NULL) - return -ENOMEM; - - rbe->flags = elem->flags; - nft_data_copy(&rbe->key, &elem->key); - if (set->flags & NFT_SET_MAP && - !(rbe->flags & NFT_SET_ELEM_INTERVAL_END)) - nft_data_copy(rbe->data, &elem->data); - spin_lock_bh(&nft_rbtree_lock); err = __nft_rbtree_insert(set, rbe); - if (err < 0) - kfree(rbe); - spin_unlock_bh(&nft_rbtree_lock); + return err; } @@ -162,17 +146,15 @@ static int nft_rbtree_get(const struct nft_set *set, struct nft_set_elem *elem) while (parent != NULL) { rbe = rb_entry(parent, struct nft_rbtree_elem, node); - d = nft_data_cmp(&rbe->key, &elem->key, set->klen); + d = nft_data_cmp(nft_set_ext_key(&rbe->ext), &elem->key, + set->klen); if (d < 0) parent = parent->rb_left; else if (d > 0) parent = parent->rb_right; else { elem->cookie = rbe; - if (set->flags & NFT_SET_MAP && - !(rbe->flags & NFT_SET_ELEM_INTERVAL_END)) - nft_data_copy(&elem->data, rbe->data); - elem->flags = rbe->flags; + elem->priv = rbe; return 0; } } @@ -184,7 +166,7 @@ static void nft_rbtree_walk(const struct nft_ctx *ctx, struct nft_set_iter *iter) { const struct nft_rbtree *priv = nft_set_priv(set); - const struct nft_rbtree_elem *rbe; + struct nft_rbtree_elem *rbe; struct nft_set_elem elem; struct rb_node *node; @@ -194,11 +176,7 @@ static void nft_rbtree_walk(const struct nft_ctx *ctx, goto cont; rbe = rb_entry(node, struct nft_rbtree_elem, node); - nft_data_copy(&elem.key, &rbe->key); - if (set->flags & NFT_SET_MAP && - !(rbe->flags & NFT_SET_ELEM_INTERVAL_END)) - nft_data_copy(&elem.data, rbe->data); - elem.flags = rbe->flags; + elem.priv = rbe; iter->err = iter->fn(ctx, set, iter, &elem); if (iter->err < 0) { @@ -245,9 +223,6 @@ static bool nft_rbtree_estimate(const struct nft_set_desc *desc, u32 features, unsigned int nsize; nsize = sizeof(struct nft_rbtree_elem); - if (features & NFT_SET_MAP) - nsize += FIELD_SIZEOF(struct nft_rbtree_elem, data[0]); - if (desc->size) est->size = sizeof(struct nft_rbtree) + desc->size * nsize; else @@ -260,6 +235,7 @@ static bool nft_rbtree_estimate(const struct nft_set_desc *desc, u32 features, static struct nft_set_ops nft_rbtree_ops __read_mostly = { .privsize = nft_rbtree_privsize, + .elemsize = offsetof(struct nft_rbtree_elem, ext), .estimate = nft_rbtree_estimate, .init = nft_rbtree_init, .destroy = nft_rbtree_destroy, -- cgit v1.2.3 From b6a7719aedd7e5c0f2df7641aa47386111682df4 Mon Sep 17 00:00:00 2001 From: Hannes Frederic Sowa Date: Wed, 25 Mar 2015 17:07:44 +0100 Subject: ipv4: hash net ptr into fragmentation bucket selection As namespaces are sometimes used with overlapping ip address ranges, we should also use the namespace as input to the hash to select the ip fragmentation counter bucket. Cc: Eric Dumazet Cc: Flavio Leitner Signed-off-by: Hannes Frederic Sowa Signed-off-by: David S. Miller --- drivers/net/ppp/pptp.c | 2 +- include/net/ip.h | 12 +++++++----- net/ipv4/igmp.c | 4 ++-- net/ipv4/ip_output.c | 7 ++++--- net/ipv4/ip_tunnel_core.c | 2 +- net/ipv4/ipmr.c | 7 ++++--- net/ipv4/raw.c | 2 +- net/ipv4/route.c | 4 ++-- net/ipv4/xfrm4_mode_tunnel.c | 2 +- net/netfilter/ipvs/ip_vs_xmit.c | 5 +++-- 10 files changed, 26 insertions(+), 21 deletions(-) (limited to 'include') diff --git a/drivers/net/ppp/pptp.c b/drivers/net/ppp/pptp.c index 1dc628ffce2b..e3bfbd4d0136 100644 --- a/drivers/net/ppp/pptp.c +++ b/drivers/net/ppp/pptp.c @@ -281,7 +281,7 @@ static int pptp_xmit(struct ppp_channel *chan, struct sk_buff *skb) nf_reset(skb); skb->ip_summed = CHECKSUM_NONE; - ip_select_ident(skb, NULL); + ip_select_ident(sock_net(sk), skb, NULL); ip_send_check(iph); ip_local_out(skb); diff --git a/include/net/ip.h b/include/net/ip.h index 025c61c0dffb..d0808a323763 100644 --- a/include/net/ip.h +++ b/include/net/ip.h @@ -318,9 +318,10 @@ static inline unsigned int ip_skb_dst_mtu(const struct sk_buff *skb) } u32 ip_idents_reserve(u32 hash, int segs); -void __ip_select_ident(struct iphdr *iph, int segs); +void __ip_select_ident(struct net *net, struct iphdr *iph, int segs); -static inline void ip_select_ident_segs(struct sk_buff *skb, struct sock *sk, int segs) +static inline void ip_select_ident_segs(struct net *net, struct sk_buff *skb, + struct sock *sk, int segs) { struct iphdr *iph = ip_hdr(skb); @@ -337,13 +338,14 @@ static inline void ip_select_ident_segs(struct sk_buff *skb, struct sock *sk, in iph->id = 0; } } else { - __ip_select_ident(iph, segs); + __ip_select_ident(net, iph, segs); } } -static inline void ip_select_ident(struct sk_buff *skb, struct sock *sk) +static inline void ip_select_ident(struct net *net, struct sk_buff *skb, + struct sock *sk) { - ip_select_ident_segs(skb, sk, 1); + ip_select_ident_segs(net, skb, sk, 1); } static inline __wsum inet_compute_pseudo(struct sk_buff *skb, int proto) diff --git a/net/ipv4/igmp.c b/net/ipv4/igmp.c index ad3f866085de..ad09213ac5b2 100644 --- a/net/ipv4/igmp.c +++ b/net/ipv4/igmp.c @@ -370,7 +370,7 @@ static struct sk_buff *igmpv3_newpack(struct net_device *dev, unsigned int mtu) pip->saddr = fl4.saddr; pip->protocol = IPPROTO_IGMP; pip->tot_len = 0; /* filled in later */ - ip_select_ident(skb, NULL); + ip_select_ident(net, skb, NULL); ((u8 *)&pip[1])[0] = IPOPT_RA; ((u8 *)&pip[1])[1] = 4; ((u8 *)&pip[1])[2] = 0; @@ -714,7 +714,7 @@ static int igmp_send_report(struct in_device *in_dev, struct ip_mc_list *pmc, iph->daddr = dst; iph->saddr = fl4.saddr; iph->protocol = IPPROTO_IGMP; - ip_select_ident(skb, NULL); + ip_select_ident(net, skb, NULL); ((u8 *)&iph[1])[0] = IPOPT_RA; ((u8 *)&iph[1])[1] = 4; ((u8 *)&iph[1])[2] = 0; diff --git a/net/ipv4/ip_output.c b/net/ipv4/ip_output.c index 90b49e88e84a..8259e777b249 100644 --- a/net/ipv4/ip_output.c +++ b/net/ipv4/ip_output.c @@ -148,7 +148,7 @@ int ip_build_and_send_pkt(struct sk_buff *skb, struct sock *sk, iph->daddr = (opt && opt->opt.srr ? opt->opt.faddr : daddr); iph->saddr = saddr; iph->protocol = sk->sk_protocol; - ip_select_ident(skb, sk); + ip_select_ident(sock_net(sk), skb, sk); if (opt && opt->opt.optlen) { iph->ihl += opt->opt.optlen>>2; @@ -430,7 +430,8 @@ packet_routed: ip_options_build(skb, &inet_opt->opt, inet->inet_daddr, rt, 0); } - ip_select_ident_segs(skb, sk, skb_shinfo(skb)->gso_segs ?: 1); + ip_select_ident_segs(sock_net(sk), skb, sk, + skb_shinfo(skb)->gso_segs ?: 1); /* TODO : should we use skb->sk here instead of sk ? */ skb->priority = sk->sk_priority; @@ -1379,7 +1380,7 @@ struct sk_buff *__ip_make_skb(struct sock *sk, iph->ttl = ttl; iph->protocol = sk->sk_protocol; ip_copy_addrs(iph, fl4); - ip_select_ident(skb, sk); + ip_select_ident(net, skb, sk); if (opt) { iph->ihl += opt->optlen>>2; diff --git a/net/ipv4/ip_tunnel_core.c b/net/ipv4/ip_tunnel_core.c index 88c386cf7d85..8c4dcc46acd2 100644 --- a/net/ipv4/ip_tunnel_core.c +++ b/net/ipv4/ip_tunnel_core.c @@ -74,7 +74,7 @@ int iptunnel_xmit(struct sock *sk, struct rtable *rt, struct sk_buff *skb, iph->daddr = dst; iph->saddr = src; iph->ttl = ttl; - __ip_select_ident(iph, skb_shinfo(skb)->gso_segs ?: 1); + __ip_select_ident(sock_net(sk), iph, skb_shinfo(skb)->gso_segs ?: 1); err = ip_local_out_sk(sk, skb); if (unlikely(net_xmit_eval(err))) diff --git a/net/ipv4/ipmr.c b/net/ipv4/ipmr.c index 5b188832800f..c688cd1b2110 100644 --- a/net/ipv4/ipmr.c +++ b/net/ipv4/ipmr.c @@ -1642,7 +1642,8 @@ static struct notifier_block ip_mr_notifier = { * important for multicast video. */ -static void ip_encap(struct sk_buff *skb, __be32 saddr, __be32 daddr) +static void ip_encap(struct net *net, struct sk_buff *skb, + __be32 saddr, __be32 daddr) { struct iphdr *iph; const struct iphdr *old_iph = ip_hdr(skb); @@ -1661,7 +1662,7 @@ static void ip_encap(struct sk_buff *skb, __be32 saddr, __be32 daddr) iph->protocol = IPPROTO_IPIP; iph->ihl = 5; iph->tot_len = htons(skb->len); - ip_select_ident(skb, NULL); + ip_select_ident(net, skb, NULL); ip_send_check(iph); memset(&(IPCB(skb)->opt), 0, sizeof(IPCB(skb)->opt)); @@ -1758,7 +1759,7 @@ static void ipmr_queue_xmit(struct net *net, struct mr_table *mrt, * What do we do with netfilter? -- RR */ if (vif->flags & VIFF_TUNNEL) { - ip_encap(skb, vif->local, vif->remote); + ip_encap(net, skb, vif->local, vif->remote); /* FIXME: extra output firewall step used to be here. --RR */ vif->dev->stats.tx_packets++; vif->dev->stats.tx_bytes += skb->len; diff --git a/net/ipv4/raw.c b/net/ipv4/raw.c index 923cf538fce1..56946f47d446 100644 --- a/net/ipv4/raw.c +++ b/net/ipv4/raw.c @@ -404,7 +404,7 @@ static int raw_send_hdrinc(struct sock *sk, struct flowi4 *fl4, iph->check = 0; iph->tot_len = htons(length); if (!iph->id) - ip_select_ident(skb, NULL); + ip_select_ident(net, skb, NULL); iph->check = ip_fast_csum((unsigned char *)iph, iph->ihl); } diff --git a/net/ipv4/route.c b/net/ipv4/route.c index 649c8a3f0189..be8703d02ef0 100644 --- a/net/ipv4/route.c +++ b/net/ipv4/route.c @@ -482,7 +482,7 @@ u32 ip_idents_reserve(u32 hash, int segs) } EXPORT_SYMBOL(ip_idents_reserve); -void __ip_select_ident(struct iphdr *iph, int segs) +void __ip_select_ident(struct net *net, struct iphdr *iph, int segs) { static u32 ip_idents_hashrnd __read_mostly; u32 hash, id; @@ -491,7 +491,7 @@ void __ip_select_ident(struct iphdr *iph, int segs) hash = jhash_3words((__force u32)iph->daddr, (__force u32)iph->saddr, - iph->protocol, + iph->protocol ^ net_hash_mix(net), ip_idents_hashrnd); id = ip_idents_reserve(hash, segs); iph->id = htons(id); diff --git a/net/ipv4/xfrm4_mode_tunnel.c b/net/ipv4/xfrm4_mode_tunnel.c index 91771a7c802f..35feda676464 100644 --- a/net/ipv4/xfrm4_mode_tunnel.c +++ b/net/ipv4/xfrm4_mode_tunnel.c @@ -63,7 +63,7 @@ static int xfrm4_mode_tunnel_output(struct xfrm_state *x, struct sk_buff *skb) top_iph->saddr = x->props.saddr.a4; top_iph->daddr = x->id.daddr.a4; - ip_select_ident(skb, NULL); + ip_select_ident(dev_net(dst->dev), skb, NULL); return 0; } diff --git a/net/netfilter/ipvs/ip_vs_xmit.c b/net/netfilter/ipvs/ip_vs_xmit.c index f35c15b0de6b..bf02932b7188 100644 --- a/net/netfilter/ipvs/ip_vs_xmit.c +++ b/net/netfilter/ipvs/ip_vs_xmit.c @@ -924,7 +924,8 @@ int ip_vs_tunnel_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, struct ip_vs_protocol *pp, struct ip_vs_iphdr *ipvsh) { - struct netns_ipvs *ipvs = net_ipvs(skb_net(skb)); + struct net *net = skb_net(skb); + struct netns_ipvs *ipvs = net_ipvs(net); struct rtable *rt; /* Route to the other host */ __be32 saddr; /* Source for tunnel */ struct net_device *tdev; /* Device to other host */ @@ -991,7 +992,7 @@ ip_vs_tunnel_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, iph->daddr = cp->daddr.ip; iph->saddr = saddr; iph->ttl = ttl; - ip_select_ident(skb, NULL); + ip_select_ident(net, skb, NULL); /* Another hack: avoid icmp_send in ip_fragment */ skb->ignore_df = 1; -- cgit v1.2.3 From 5a352dd0a3aac03b443c94828dfd7144261c8636 Mon Sep 17 00:00:00 2001 From: Hannes Frederic Sowa Date: Wed, 25 Mar 2015 17:07:45 +0100 Subject: ipv6: hash net ptr into fragmentation bucket selection As namespaces are sometimes used with overlapping ip address ranges, we should also use the namespace as input to the hash to select the ip fragmentation counter bucket. Cc: Eric Dumazet Cc: Flavio Leitner Signed-off-by: Hannes Frederic Sowa Signed-off-by: David S. Miller --- include/net/ipv6.h | 5 +++-- net/ipv6/ip6_output.c | 6 +++--- net/ipv6/output_core.c | 14 ++++++++------ net/ipv6/udp_offload.c | 4 ++-- 4 files changed, 16 insertions(+), 13 deletions(-) (limited to 'include') diff --git a/include/net/ipv6.h b/include/net/ipv6.h index e7ba9758a345..65142e6af440 100644 --- a/include/net/ipv6.h +++ b/include/net/ipv6.h @@ -671,8 +671,9 @@ static inline int ipv6_addr_diff(const struct in6_addr *a1, const struct in6_add return __ipv6_addr_diff(a1, a2, sizeof(struct in6_addr)); } -void ipv6_select_ident(struct frag_hdr *fhdr, struct rt6_info *rt); -void ipv6_proxy_select_ident(struct sk_buff *skb); +void ipv6_select_ident(struct net *net, struct frag_hdr *fhdr, + struct rt6_info *rt); +void ipv6_proxy_select_ident(struct net *net, struct sk_buff *skb); int ip6_dst_hoplimit(struct dst_entry *dst); diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c index 7e80b61b51ff..b06ad00048d5 100644 --- a/net/ipv6/ip6_output.c +++ b/net/ipv6/ip6_output.c @@ -628,7 +628,7 @@ int ip6_fragment(struct sk_buff *skb, int (*output)(struct sk_buff *)) skb_reset_network_header(skb); memcpy(skb_network_header(skb), tmp_hdr, hlen); - ipv6_select_ident(fh, rt); + ipv6_select_ident(net, fh, rt); fh->nexthdr = nexthdr; fh->reserved = 0; fh->frag_off = htons(IP6_MF); @@ -775,7 +775,7 @@ slow_path: fh->nexthdr = nexthdr; fh->reserved = 0; if (!frag_id) { - ipv6_select_ident(fh, rt); + ipv6_select_ident(net, fh, rt); frag_id = fh->identification; } else fh->identification = frag_id; @@ -1079,7 +1079,7 @@ static inline int ip6_ufo_append_data(struct sock *sk, skb_shinfo(skb)->gso_size = (mtu - fragheaderlen - sizeof(struct frag_hdr)) & ~7; skb_shinfo(skb)->gso_type = SKB_GSO_UDP; - ipv6_select_ident(&fhdr, rt); + ipv6_select_ident(sock_net(sk), &fhdr, rt); skb_shinfo(skb)->ip6_frag_id = fhdr.identification; append: diff --git a/net/ipv6/output_core.c b/net/ipv6/output_core.c index 74581f706c4d..4016a6ef9d61 100644 --- a/net/ipv6/output_core.c +++ b/net/ipv6/output_core.c @@ -9,13 +9,14 @@ #include #include -static u32 __ipv6_select_ident(u32 hashrnd, struct in6_addr *dst, - struct in6_addr *src) +static u32 __ipv6_select_ident(struct net *net, u32 hashrnd, + struct in6_addr *dst, struct in6_addr *src) { u32 hash, id; hash = __ipv6_addr_jhash(dst, hashrnd); hash = __ipv6_addr_jhash(src, hash); + hash ^= net_hash_mix(net); /* Treat id of 0 as unset and if we get 0 back from ip_idents_reserve, * set the hight order instead thus minimizing possible future @@ -36,7 +37,7 @@ static u32 __ipv6_select_ident(u32 hashrnd, struct in6_addr *dst, * * The network header must be set before calling this. */ -void ipv6_proxy_select_ident(struct sk_buff *skb) +void ipv6_proxy_select_ident(struct net *net, struct sk_buff *skb) { static u32 ip6_proxy_idents_hashrnd __read_mostly; struct in6_addr buf[2]; @@ -53,20 +54,21 @@ void ipv6_proxy_select_ident(struct sk_buff *skb) net_get_random_once(&ip6_proxy_idents_hashrnd, sizeof(ip6_proxy_idents_hashrnd)); - id = __ipv6_select_ident(ip6_proxy_idents_hashrnd, + id = __ipv6_select_ident(net, ip6_proxy_idents_hashrnd, &addrs[1], &addrs[0]); skb_shinfo(skb)->ip6_frag_id = htonl(id); } EXPORT_SYMBOL_GPL(ipv6_proxy_select_ident); -void ipv6_select_ident(struct frag_hdr *fhdr, struct rt6_info *rt) +void ipv6_select_ident(struct net *net, struct frag_hdr *fhdr, + struct rt6_info *rt) { static u32 ip6_idents_hashrnd __read_mostly; u32 id; net_get_random_once(&ip6_idents_hashrnd, sizeof(ip6_idents_hashrnd)); - id = __ipv6_select_ident(ip6_idents_hashrnd, &rt->rt6i_dst.addr, + id = __ipv6_select_ident(net, ip6_idents_hashrnd, &rt->rt6i_dst.addr, &rt->rt6i_src.addr); fhdr->identification = htonl(id); } diff --git a/net/ipv6/udp_offload.c b/net/ipv6/udp_offload.c index be2c0ba82c85..7441e1e63893 100644 --- a/net/ipv6/udp_offload.c +++ b/net/ipv6/udp_offload.c @@ -54,7 +54,7 @@ static struct sk_buff *udp6_ufo_fragment(struct sk_buff *skb, /* Set the IPv6 fragment id if not set yet */ if (!skb_shinfo(skb)->ip6_frag_id) - ipv6_proxy_select_ident(skb); + ipv6_proxy_select_ident(dev_net(skb->dev), skb); segs = NULL; goto out; @@ -113,7 +113,7 @@ static struct sk_buff *udp6_ufo_fragment(struct sk_buff *skb, fptr->nexthdr = nexthdr; fptr->reserved = 0; if (!skb_shinfo(skb)->ip6_frag_id) - ipv6_proxy_select_ident(skb); + ipv6_proxy_select_ident(dev_net(skb->dev), skb); fptr->identification = skb_shinfo(skb)->ip6_frag_id; /* Fragment the skb. ipv6 header and the remaining fields of the -- cgit v1.2.3 From e2e40f2c1ed433c5e224525c8c862fd32e5d3df2 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Sun, 22 Feb 2015 08:58:50 -0800 Subject: fs: move struct kiocb to fs.h struct kiocb now is a generic I/O container, so move it to fs.h. Also do a #include diet for aio.h while we're at it. Signed-off-by: Christoph Hellwig Signed-off-by: Al Viro --- arch/s390/hypfs/inode.c | 2 +- drivers/char/mem.c | 2 +- drivers/char/tile-srom.c | 1 - drivers/infiniband/hw/ipath/ipath_file_ops.c | 1 - drivers/infiniband/hw/qib/qib_file_ops.c | 1 - drivers/misc/mei/amthif.c | 1 - drivers/misc/mei/main.c | 1 - drivers/misc/mei/pci-me.c | 1 - drivers/scsi/sg.c | 2 +- drivers/staging/unisys/include/timskmod.h | 1 - drivers/usb/gadget/function/f_fs.c | 1 + drivers/usb/gadget/legacy/inode.c | 1 + fs/9p/vfs_addr.c | 2 +- fs/affs/file.c | 2 +- fs/afs/write.c | 1 - fs/bfs/inode.c | 1 + fs/block_dev.c | 1 - fs/btrfs/file.c | 2 +- fs/btrfs/inode.c | 2 +- fs/ceph/file.c | 1 - fs/direct-io.c | 1 - fs/ecryptfs/file.c | 1 - fs/ext2/inode.c | 2 +- fs/ext3/inode.c | 2 +- fs/ext4/file.c | 2 +- fs/ext4/indirect.c | 2 +- fs/ext4/inode.c | 1 - fs/ext4/page-io.c | 1 - fs/f2fs/data.c | 2 +- fs/fat/inode.c | 1 - fs/fuse/cuse.c | 2 +- fs/fuse/dev.c | 1 - fs/fuse/file.c | 2 +- fs/gfs2/aops.c | 2 +- fs/gfs2/file.c | 1 - fs/hfs/inode.c | 2 +- fs/hfsplus/inode.c | 2 +- fs/jfs/inode.c | 2 +- fs/nfs/file.c | 1 - fs/nilfs2/inode.c | 2 +- fs/ntfs/file.c | 1 - fs/ntfs/inode.c | 1 - fs/ocfs2/aops.c | 1 + fs/ocfs2/aops.h | 2 +- fs/pipe.c | 1 - fs/read_write.c | 1 - fs/reiserfs/inode.c | 2 +- fs/splice.c | 1 - fs/ubifs/file.c | 1 - fs/udf/file.c | 2 +- fs/udf/inode.c | 2 +- fs/xfs/xfs_aops.c | 1 - fs/xfs/xfs_file.c | 1 - include/linux/aio.h | 31 +--------------------------- include/linux/fs.h | 22 ++++++++++++++++++++ include/net/sock.h | 1 - kernel/printk/printk.c | 2 +- kernel/sysctl.c | 1 + mm/filemap.c | 1 - mm/page_io.c | 2 +- mm/shmem.c | 2 +- net/ipv4/raw.c | 1 - sound/core/pcm_native.c | 2 +- 63 files changed, 55 insertions(+), 86 deletions(-) (limited to 'include') diff --git a/arch/s390/hypfs/inode.c b/arch/s390/hypfs/inode.c index 4c8008dd938e..ad66b07f742e 100644 --- a/arch/s390/hypfs/inode.c +++ b/arch/s390/hypfs/inode.c @@ -21,7 +21,7 @@ #include #include #include -#include +#include #include #include "hypfs.h" diff --git a/drivers/char/mem.c b/drivers/char/mem.c index 297110c12635..9c4fd7a8e2e5 100644 --- a/drivers/char/mem.c +++ b/drivers/char/mem.c @@ -26,7 +26,7 @@ #include #include #include -#include +#include #include diff --git a/drivers/char/tile-srom.c b/drivers/char/tile-srom.c index 02e76ac6d282..69f6b4acc377 100644 --- a/drivers/char/tile-srom.c +++ b/drivers/char/tile-srom.c @@ -27,7 +27,6 @@ #include /* size_t */ #include #include /* O_ACCMODE */ -#include #include #include #include diff --git a/drivers/infiniband/hw/ipath/ipath_file_ops.c b/drivers/infiniband/hw/ipath/ipath_file_ops.c index 6d7f453b4d05..aed8afee56da 100644 --- a/drivers/infiniband/hw/ipath/ipath_file_ops.c +++ b/drivers/infiniband/hw/ipath/ipath_file_ops.c @@ -40,7 +40,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/infiniband/hw/qib/qib_file_ops.c b/drivers/infiniband/hw/qib/qib_file_ops.c index b15e34eeef68..826c17ee4c7d 100644 --- a/drivers/infiniband/hw/qib/qib_file_ops.c +++ b/drivers/infiniband/hw/qib/qib_file_ops.c @@ -39,7 +39,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/misc/mei/amthif.c b/drivers/misc/mei/amthif.c index c4cb9a984a5f..40ea639fa413 100644 --- a/drivers/misc/mei/amthif.c +++ b/drivers/misc/mei/amthif.c @@ -19,7 +19,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/misc/mei/main.c b/drivers/misc/mei/main.c index 3c019c0e60eb..47680c84801c 100644 --- a/drivers/misc/mei/main.c +++ b/drivers/misc/mei/main.c @@ -22,7 +22,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/misc/mei/pci-me.c b/drivers/misc/mei/pci-me.c index bd3039ab8f98..af44ee26075d 100644 --- a/drivers/misc/mei/pci-me.c +++ b/drivers/misc/mei/pci-me.c @@ -21,7 +21,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/scsi/sg.c b/drivers/scsi/sg.c index 0cbc1fb45f10..c78a6f7bbc20 100644 --- a/drivers/scsi/sg.c +++ b/drivers/scsi/sg.c @@ -33,7 +33,6 @@ static int sg_version_num = 30536; /* 2 digits for each component */ #include #include #include -#include #include #include #include @@ -51,6 +50,7 @@ static int sg_version_num = 30536; /* 2 digits for each component */ #include #include #include +#include #include "scsi.h" #include diff --git a/drivers/staging/unisys/include/timskmod.h b/drivers/staging/unisys/include/timskmod.h index 4019a0d63645..52648d4d9922 100644 --- a/drivers/staging/unisys/include/timskmod.h +++ b/drivers/staging/unisys/include/timskmod.h @@ -46,7 +46,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/usb/gadget/function/f_fs.c b/drivers/usb/gadget/function/f_fs.c index b64538b498dc..a12315a78248 100644 --- a/drivers/usb/gadget/function/f_fs.c +++ b/drivers/usb/gadget/function/f_fs.c @@ -23,6 +23,7 @@ #include #include #include +#include #include #include diff --git a/drivers/usb/gadget/legacy/inode.c b/drivers/usb/gadget/legacy/inode.c index a4a80694f607..662ef2c1c62b 100644 --- a/drivers/usb/gadget/legacy/inode.c +++ b/drivers/usb/gadget/legacy/inode.c @@ -26,6 +26,7 @@ #include #include #include +#include #include #include diff --git a/fs/9p/vfs_addr.c b/fs/9p/vfs_addr.c index eb14e055ea83..ff1a5bac4200 100644 --- a/fs/9p/vfs_addr.c +++ b/fs/9p/vfs_addr.c @@ -33,7 +33,7 @@ #include #include #include -#include +#include #include #include diff --git a/fs/affs/file.c b/fs/affs/file.c index d2468bf95669..33eaa67bb026 100644 --- a/fs/affs/file.c +++ b/fs/affs/file.c @@ -12,7 +12,7 @@ * affs regular file handling primitives */ -#include +#include #include "affs.h" static struct buffer_head *affs_get_extblock_slow(struct inode *inode, u32 ext); diff --git a/fs/afs/write.c b/fs/afs/write.c index c13cb08964ed..0714abcd7f32 100644 --- a/fs/afs/write.c +++ b/fs/afs/write.c @@ -14,7 +14,6 @@ #include #include #include -#include #include "internal.h" static int afs_write_back_from_locked_page(struct afs_writeback *wb, diff --git a/fs/bfs/inode.c b/fs/bfs/inode.c index 90bc079d9982..fdcb4d69f430 100644 --- a/fs/bfs/inode.c +++ b/fs/bfs/inode.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include "bfs.h" diff --git a/fs/block_dev.c b/fs/block_dev.c index 975266be67d3..2e522aed6584 100644 --- a/fs/block_dev.c +++ b/fs/block_dev.c @@ -27,7 +27,6 @@ #include #include #include -#include #include #include "internal.h" diff --git a/fs/btrfs/file.c b/fs/btrfs/file.c index b78bbbac900d..69c9508d2c7e 100644 --- a/fs/btrfs/file.c +++ b/fs/btrfs/file.c @@ -24,7 +24,6 @@ #include #include #include -#include #include #include #include @@ -32,6 +31,7 @@ #include #include #include +#include #include "ctree.h" #include "disk-io.h" #include "transaction.h" diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index 54bcf639d1cf..b214ab178f3a 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -32,7 +32,6 @@ #include #include #include -#include #include #include #include @@ -43,6 +42,7 @@ #include #include #include +#include #include "ctree.h" #include "disk-io.h" #include "transaction.h" diff --git a/fs/ceph/file.c b/fs/ceph/file.c index 081c4e3f9e49..98e257c1b5b1 100644 --- a/fs/ceph/file.c +++ b/fs/ceph/file.c @@ -7,7 +7,6 @@ #include #include #include -#include #include #include "super.h" diff --git a/fs/direct-io.c b/fs/direct-io.c index c38b460776e6..6fb00e3f1059 100644 --- a/fs/direct-io.c +++ b/fs/direct-io.c @@ -37,7 +37,6 @@ #include #include #include -#include /* * How many user pages to map in one call to get_user_pages(). This determines diff --git a/fs/ecryptfs/file.c b/fs/ecryptfs/file.c index a36da8841e0c..273d36e3f0c0 100644 --- a/fs/ecryptfs/file.c +++ b/fs/ecryptfs/file.c @@ -31,7 +31,6 @@ #include #include #include -#include #include "ecryptfs_kernel.h" /** diff --git a/fs/ext2/inode.c b/fs/ext2/inode.c index 6434bc000125..df9d6afbc5d5 100644 --- a/fs/ext2/inode.c +++ b/fs/ext2/inode.c @@ -31,7 +31,7 @@ #include #include #include -#include +#include #include "ext2.h" #include "acl.h" #include "xattr.h" diff --git a/fs/ext3/inode.c b/fs/ext3/inode.c index 2c6ccc49ba27..db07ffbe7c85 100644 --- a/fs/ext3/inode.c +++ b/fs/ext3/inode.c @@ -27,7 +27,7 @@ #include #include #include -#include +#include #include "ext3.h" #include "xattr.h" #include "acl.h" diff --git a/fs/ext4/file.c b/fs/ext4/file.c index 33a09da16c9c..598abbbe6786 100644 --- a/fs/ext4/file.c +++ b/fs/ext4/file.c @@ -23,9 +23,9 @@ #include #include #include -#include #include #include +#include #include "ext4.h" #include "ext4_jbd2.h" #include "xattr.h" diff --git a/fs/ext4/indirect.c b/fs/ext4/indirect.c index 6b9878a24182..8611640856d3 100644 --- a/fs/ext4/indirect.c +++ b/fs/ext4/indirect.c @@ -20,9 +20,9 @@ * (sct@redhat.com), 1993, 1998 */ -#include #include "ext4_jbd2.h" #include "truncate.h" +#include #include diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c index 85404f15e53a..6325d2c1a65c 100644 --- a/fs/ext4/inode.c +++ b/fs/ext4/inode.c @@ -37,7 +37,6 @@ #include #include #include -#include #include #include "ext4_jbd2.h" diff --git a/fs/ext4/page-io.c b/fs/ext4/page-io.c index b24a2541a9ba..464984261e69 100644 --- a/fs/ext4/page-io.c +++ b/fs/ext4/page-io.c @@ -18,7 +18,6 @@ #include #include #include -#include #include #include #include diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index 985ed023a750..497f8515d205 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -12,12 +12,12 @@ #include #include #include -#include #include #include #include #include #include +#include #include "f2fs.h" #include "node.h" diff --git a/fs/fat/inode.c b/fs/fat/inode.c index 497c7c5263c7..8521207de229 100644 --- a/fs/fat/inode.c +++ b/fs/fat/inode.c @@ -19,7 +19,6 @@ #include #include #include -#include #include #include #include diff --git a/fs/fuse/cuse.c b/fs/fuse/cuse.c index 28d0c7abba1c..b3fa05032234 100644 --- a/fs/fuse/cuse.c +++ b/fs/fuse/cuse.c @@ -38,7 +38,6 @@ #include #include #include -#include #include #include #include @@ -48,6 +47,7 @@ #include #include #include +#include #include "fuse_i.h" diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c index ed19a7d622fa..8c92c727ddd6 100644 --- a/fs/fuse/dev.c +++ b/fs/fuse/dev.c @@ -19,7 +19,6 @@ #include #include #include -#include MODULE_ALIAS_MISCDEV(FUSE_MINOR); MODULE_ALIAS("devname:fuse"); diff --git a/fs/fuse/file.c b/fs/fuse/file.c index a5c5e38b3ff8..ff102cbf16ea 100644 --- a/fs/fuse/file.c +++ b/fs/fuse/file.c @@ -15,8 +15,8 @@ #include #include #include -#include #include +#include static const struct file_operations fuse_direct_io_file_operations; diff --git a/fs/gfs2/aops.c b/fs/gfs2/aops.c index 4ad4f94edebe..fe6634d25d1d 100644 --- a/fs/gfs2/aops.c +++ b/fs/gfs2/aops.c @@ -20,7 +20,7 @@ #include #include #include -#include +#include #include #include "gfs2.h" diff --git a/fs/gfs2/file.c b/fs/gfs2/file.c index 3e32bb8e2d7e..f6fc412b1100 100644 --- a/fs/gfs2/file.c +++ b/fs/gfs2/file.c @@ -25,7 +25,6 @@ #include #include #include -#include #include #include "gfs2.h" diff --git a/fs/hfs/inode.c b/fs/hfs/inode.c index d0929bc81782..98d4ea45bb70 100644 --- a/fs/hfs/inode.c +++ b/fs/hfs/inode.c @@ -14,7 +14,7 @@ #include #include #include -#include +#include #include "hfs_fs.h" #include "btree.h" diff --git a/fs/hfsplus/inode.c b/fs/hfsplus/inode.c index 0cf786f2d046..f541196d4ee9 100644 --- a/fs/hfsplus/inode.c +++ b/fs/hfsplus/inode.c @@ -14,7 +14,7 @@ #include #include #include -#include +#include #include "hfsplus_fs.h" #include "hfsplus_raw.h" diff --git a/fs/jfs/inode.c b/fs/jfs/inode.c index bd3df1ca3c9b..3197aed10614 100644 --- a/fs/jfs/inode.c +++ b/fs/jfs/inode.c @@ -22,8 +22,8 @@ #include #include #include +#include #include -#include #include "jfs_incore.h" #include "jfs_inode.h" #include "jfs_filsys.h" diff --git a/fs/nfs/file.c b/fs/nfs/file.c index 94712fc781fa..5d8b89cb13c0 100644 --- a/fs/nfs/file.c +++ b/fs/nfs/file.c @@ -26,7 +26,6 @@ #include #include #include -#include #include #include diff --git a/fs/nilfs2/inode.c b/fs/nilfs2/inode.c index 8b5969538f39..ab4987bc637f 100644 --- a/fs/nilfs2/inode.c +++ b/fs/nilfs2/inode.c @@ -26,7 +26,7 @@ #include #include #include -#include +#include #include "nilfs.h" #include "btnode.h" #include "segment.h" diff --git a/fs/ntfs/file.c b/fs/ntfs/file.c index 1da9b2d184dc..f16f2d8401fe 100644 --- a/fs/ntfs/file.c +++ b/fs/ntfs/file.c @@ -28,7 +28,6 @@ #include #include #include -#include #include #include diff --git a/fs/ntfs/inode.c b/fs/ntfs/inode.c index 898b9949d363..1d0c21df0d80 100644 --- a/fs/ntfs/inode.c +++ b/fs/ntfs/inode.c @@ -28,7 +28,6 @@ #include #include #include -#include #include "aops.h" #include "attrib.h" diff --git a/fs/ocfs2/aops.c b/fs/ocfs2/aops.c index 44db1808cdb5..e1bf18c5d25e 100644 --- a/fs/ocfs2/aops.c +++ b/fs/ocfs2/aops.c @@ -29,6 +29,7 @@ #include #include #include +#include #include diff --git a/fs/ocfs2/aops.h b/fs/ocfs2/aops.h index 6cae155d54df..dd59599b022d 100644 --- a/fs/ocfs2/aops.h +++ b/fs/ocfs2/aops.h @@ -22,7 +22,7 @@ #ifndef OCFS2_AOPS_H #define OCFS2_AOPS_H -#include +#include handle_t *ocfs2_start_walk_page_trans(struct inode *inode, struct page *page, diff --git a/fs/pipe.c b/fs/pipe.c index 21981e58e2a6..2d084f2d0b83 100644 --- a/fs/pipe.c +++ b/fs/pipe.c @@ -21,7 +21,6 @@ #include #include #include -#include #include #include diff --git a/fs/read_write.c b/fs/read_write.c index 76e324e8ce8d..99a6ef946d01 100644 --- a/fs/read_write.c +++ b/fs/read_write.c @@ -9,7 +9,6 @@ #include #include #include -#include #include #include #include diff --git a/fs/reiserfs/inode.c b/fs/reiserfs/inode.c index e72401e1f995..9312b7842e03 100644 --- a/fs/reiserfs/inode.c +++ b/fs/reiserfs/inode.c @@ -18,7 +18,7 @@ #include #include #include -#include +#include int reiserfs_commit_write(struct file *f, struct page *page, unsigned from, unsigned to); diff --git a/fs/splice.c b/fs/splice.c index 7968da96bebb..4bbfa95b5bfe 100644 --- a/fs/splice.c +++ b/fs/splice.c @@ -32,7 +32,6 @@ #include #include #include -#include #include "internal.h" /* diff --git a/fs/ubifs/file.c b/fs/ubifs/file.c index e627c0acf626..c3d15fe83403 100644 --- a/fs/ubifs/file.c +++ b/fs/ubifs/file.c @@ -50,7 +50,6 @@ */ #include "ubifs.h" -#include #include #include #include diff --git a/fs/udf/file.c b/fs/udf/file.c index 9c0b6da9dbb3..7f885cc8b0b7 100644 --- a/fs/udf/file.c +++ b/fs/udf/file.c @@ -34,7 +34,7 @@ #include #include #include -#include +#include #include "udf_i.h" #include "udf_sb.h" diff --git a/fs/udf/inode.c b/fs/udf/inode.c index a445d599098d..9c1fbd23913d 100644 --- a/fs/udf/inode.c +++ b/fs/udf/inode.c @@ -38,7 +38,7 @@ #include #include #include -#include +#include #include "udf_i.h" #include "udf_sb.h" diff --git a/fs/xfs/xfs_aops.c b/fs/xfs/xfs_aops.c index 3a9b7a1b8704..4f8cdc59bc38 100644 --- a/fs/xfs/xfs_aops.c +++ b/fs/xfs/xfs_aops.c @@ -31,7 +31,6 @@ #include "xfs_bmap.h" #include "xfs_bmap_util.h" #include "xfs_bmap_btree.h" -#include #include #include #include diff --git a/fs/xfs/xfs_file.c b/fs/xfs/xfs_file.c index 1cdba95c78cb..f527618cb42b 100644 --- a/fs/xfs/xfs_file.c +++ b/fs/xfs/xfs_file.c @@ -37,7 +37,6 @@ #include "xfs_log.h" #include "xfs_icache.h" -#include #include #include #include diff --git a/include/linux/aio.h b/include/linux/aio.h index 5c40b61285ac..9eb42dbc5582 100644 --- a/include/linux/aio.h +++ b/include/linux/aio.h @@ -1,52 +1,23 @@ #ifndef __LINUX__AIO_H #define __LINUX__AIO_H -#include -#include #include -#include -#include - -#include struct kioctx; struct kiocb; +struct mm_struct; #define KIOCB_KEY 0 typedef int (kiocb_cancel_fn)(struct kiocb *); -#define IOCB_EVENTFD (1 << 0) - -struct kiocb { - struct file *ki_filp; - loff_t ki_pos; - void (*ki_complete)(struct kiocb *iocb, long ret, long ret2); - void *private; - int ki_flags; -}; - -static inline bool is_sync_kiocb(struct kiocb *kiocb) -{ - return kiocb->ki_complete == NULL; -} - -static inline void init_sync_kiocb(struct kiocb *kiocb, struct file *filp) -{ - *kiocb = (struct kiocb) { - .ki_filp = filp, - }; -} - /* prototypes */ #ifdef CONFIG_AIO -struct mm_struct; extern void exit_aio(struct mm_struct *mm); extern long do_io_submit(aio_context_t ctx_id, long nr, struct iocb __user *__user *iocbpp, bool compat); void kiocb_set_cancel_fn(struct kiocb *req, kiocb_cancel_fn *cancel); #else -struct mm_struct; static inline void exit_aio(struct mm_struct *mm) { } static inline long do_io_submit(aio_context_t ctx_id, long nr, struct iocb __user * __user *iocbpp, diff --git a/include/linux/fs.h b/include/linux/fs.h index 447932aed1e1..48c1472bde4a 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -314,6 +314,28 @@ struct page; struct address_space; struct writeback_control; +#define IOCB_EVENTFD (1 << 0) + +struct kiocb { + struct file *ki_filp; + loff_t ki_pos; + void (*ki_complete)(struct kiocb *iocb, long ret, long ret2); + void *private; + int ki_flags; +}; + +static inline bool is_sync_kiocb(struct kiocb *kiocb) +{ + return kiocb->ki_complete == NULL; +} + +static inline void init_sync_kiocb(struct kiocb *kiocb, struct file *filp) +{ + *kiocb = (struct kiocb) { + .ki_filp = filp, + }; +} + /* * "descriptor" for what we're up to with a read. * This allows us to use the same read code yet diff --git a/include/net/sock.h b/include/net/sock.h index ab186b1d31ff..71c1300025e2 100644 --- a/include/net/sock.h +++ b/include/net/sock.h @@ -57,7 +57,6 @@ #include #include #include -#include #include #include diff --git a/kernel/printk/printk.c b/kernel/printk/printk.c index 60b2aa2a2da9..40d50cc4c686 100644 --- a/kernel/printk/printk.c +++ b/kernel/printk/printk.c @@ -32,7 +32,6 @@ #include #include #include -#include #include #include #include @@ -46,6 +45,7 @@ #include #include #include +#include #include diff --git a/kernel/sysctl.c b/kernel/sysctl.c index 88ea2d6e0031..83d907afb4a6 100644 --- a/kernel/sysctl.c +++ b/kernel/sysctl.c @@ -19,6 +19,7 @@ */ #include +#include #include #include #include diff --git a/mm/filemap.c b/mm/filemap.c index ad7242043bdb..876f4e6f3ed6 100644 --- a/mm/filemap.c +++ b/mm/filemap.c @@ -13,7 +13,6 @@ #include #include #include -#include #include #include #include diff --git a/mm/page_io.c b/mm/page_io.c index 7ef21577856c..a96c8562d835 100644 --- a/mm/page_io.c +++ b/mm/page_io.c @@ -20,8 +20,8 @@ #include #include #include -#include #include +#include #include static struct bio *get_swap_bio(gfp_t gfp_flags, diff --git a/mm/shmem.c b/mm/shmem.c index a63031fa3e0c..944b94079bb0 100644 --- a/mm/shmem.c +++ b/mm/shmem.c @@ -31,7 +31,7 @@ #include #include #include -#include +#include static struct vfsmount *shm_mnt; diff --git a/net/ipv4/raw.c b/net/ipv4/raw.c index f027a708b7e0..4a356b7c081b 100644 --- a/net/ipv4/raw.c +++ b/net/ipv4/raw.c @@ -46,7 +46,6 @@ #include #include #include -#include #include #include #include diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c index b03a638b420c..9ecff240a39b 100644 --- a/sound/core/pcm_native.c +++ b/sound/core/pcm_native.c @@ -25,7 +25,6 @@ #include #include #include -#include #include #include #include @@ -35,6 +34,7 @@ #include #include #include +#include /* * Compatibility -- cgit v1.2.3 From 495099962138732c28449d07f3b1988836659851 Mon Sep 17 00:00:00 2001 From: Arman Uguray Date: Wed, 25 Mar 2015 18:53:39 -0700 Subject: Bluetooth: Add macros for advertising instance flags This patch adds macro definitions for possible advertising instance flags that can be passed to the "Add Advertising" command. Signed-off-by: Arman Uguray Signed-off-by: Marcel Holtmann --- include/net/bluetooth/mgmt.h | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'include') diff --git a/include/net/bluetooth/mgmt.h b/include/net/bluetooth/mgmt.h index 68abd4b0c25d..b831242d48a4 100644 --- a/include/net/bluetooth/mgmt.h +++ b/include/net/bluetooth/mgmt.h @@ -554,6 +554,14 @@ struct mgmt_rp_add_advertising { __u8 instance; } __packed; +#define MGMT_ADV_FLAG_CONNECTABLE BIT(0) +#define MGMT_ADV_FLAG_DISCOV BIT(1) +#define MGMT_ADV_FLAG_LIMITED_DISCOV BIT(2) +#define MGMT_ADV_FLAG_MANAGED_FLAGS BIT(3) +#define MGMT_ADV_FLAG_TX_POWER BIT(4) +#define MGMT_ADV_FLAG_APPEARANCE BIT(5) +#define MGMT_ADV_FLAG_LOCAL_NAME BIT(6) + #define MGMT_OP_REMOVE_ADVERTISING 0x003F struct mgmt_cp_remove_advertising { __u8 instance; -- cgit v1.2.3 From 25af01ed18a282e7d324e5694fe67b85ac412994 Mon Sep 17 00:00:00 2001 From: Clément Perrochaud Date: Mon, 9 Mar 2015 11:12:03 +0100 Subject: NFC: nci: Add firmware download support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A simple forward for firmware download (i.e. sending a new firmware to the NFC adapter) from the NFC subsystem to the drivers. This feature is required to update the firmware of NXP-NCI NFC controllers but can be used by any NCI driver. This feature has been present in the HCI subsystem since 9a695d. Signed-off-by: Clément Perrochaud Signed-off-by: Samuel Ortiz --- include/net/nfc/nci_core.h | 1 + net/nfc/nci/core.c | 11 +++++++++++ 2 files changed, 12 insertions(+) (limited to 'include') diff --git a/include/net/nfc/nci_core.h b/include/net/nfc/nci_core.h index ff87f8611fa3..34a6e09b3f7f 100644 --- a/include/net/nfc/nci_core.h +++ b/include/net/nfc/nci_core.h @@ -71,6 +71,7 @@ struct nci_ops { int (*close)(struct nci_dev *ndev); int (*send)(struct nci_dev *ndev, struct sk_buff *skb); int (*setup)(struct nci_dev *ndev); + int (*fw_download)(struct nci_dev *ndev, const char *firmware_name); __u32 (*get_rfprotocol)(struct nci_dev *ndev, __u8 rf_protocol); int (*discover_se)(struct nci_dev *ndev); int (*disable_se)(struct nci_dev *ndev, u32 se_idx); diff --git a/net/nfc/nci/core.c b/net/nfc/nci/core.c index 9575a1892607..49ff32106080 100644 --- a/net/nfc/nci/core.c +++ b/net/nfc/nci/core.c @@ -907,6 +907,16 @@ static int nci_se_io(struct nfc_dev *nfc_dev, u32 se_idx, return 0; } +static int nci_fw_download(struct nfc_dev *nfc_dev, const char *firmware_name) +{ + struct nci_dev *ndev = nfc_get_drvdata(nfc_dev); + + if (!ndev->ops->fw_download) + return -ENOTSUPP; + + return ndev->ops->fw_download(ndev, firmware_name); +} + static struct nfc_ops nci_nfc_ops = { .dev_up = nci_dev_up, .dev_down = nci_dev_down, @@ -922,6 +932,7 @@ static struct nfc_ops nci_nfc_ops = { .disable_se = nci_disable_se, .discover_se = nci_discover_se, .se_io = nci_se_io, + .fw_download = nci_fw_download, }; /* ---- Interface to NCI drivers ---- */ -- cgit v1.2.3 From dece45855a8b0d1dcf48eb01d0822070ded6a4c8 Mon Sep 17 00:00:00 2001 From: Clément Perrochaud Date: Mon, 9 Mar 2015 11:12:04 +0100 Subject: NFC: nxp-nci: Add support for NXP NCI chips MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add support for NXP NCI NFC controllers such as the NPC100 or PN7150 families. Signed-off-by: Clément Perrochaud Signed-off-by: Samuel Ortiz --- MAINTAINERS | 7 + drivers/nfc/Kconfig | 1 + drivers/nfc/Makefile | 1 + drivers/nfc/nxp-nci/Kconfig | 13 ++ drivers/nfc/nxp-nci/Makefile | 9 + drivers/nfc/nxp-nci/core.c | 186 +++++++++++++++++++ drivers/nfc/nxp-nci/firmware.c | 324 ++++++++++++++++++++++++++++++++++ drivers/nfc/nxp-nci/nxp-nci.h | 89 ++++++++++ include/linux/platform_data/nxp-nci.h | 27 +++ 9 files changed, 657 insertions(+) create mode 100644 drivers/nfc/nxp-nci/Kconfig create mode 100644 drivers/nfc/nxp-nci/Makefile create mode 100644 drivers/nfc/nxp-nci/core.c create mode 100644 drivers/nfc/nxp-nci/firmware.c create mode 100644 drivers/nfc/nxp-nci/nxp-nci.h create mode 100644 include/linux/platform_data/nxp-nci.h (limited to 'include') diff --git a/MAINTAINERS b/MAINTAINERS index c2016557b294..12fdf22a77b0 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -6937,6 +6937,13 @@ S: Supported F: drivers/block/nvme* F: include/linux/nvme.h +NXP-NCI NFC DRIVER +M: Clément Perrochaud +R: Charles Gorand +L: linux-nfc@lists.01.org (moderated for non-subscribers) +S: Supported +F: drivers/nfc/nxp-nci + NXP TDA998X DRM DRIVER M: Russell King S: Supported diff --git a/drivers/nfc/Kconfig b/drivers/nfc/Kconfig index 7929fac13e1c..107714e4405f 100644 --- a/drivers/nfc/Kconfig +++ b/drivers/nfc/Kconfig @@ -73,4 +73,5 @@ source "drivers/nfc/microread/Kconfig" source "drivers/nfc/nfcmrvl/Kconfig" source "drivers/nfc/st21nfca/Kconfig" source "drivers/nfc/st21nfcb/Kconfig" +source "drivers/nfc/nxp-nci/Kconfig" endmenu diff --git a/drivers/nfc/Makefile b/drivers/nfc/Makefile index 6b23a2c6e34a..a4292d790f9b 100644 --- a/drivers/nfc/Makefile +++ b/drivers/nfc/Makefile @@ -13,5 +13,6 @@ obj-$(CONFIG_NFC_MRVL) += nfcmrvl/ obj-$(CONFIG_NFC_TRF7970A) += trf7970a.o obj-$(CONFIG_NFC_ST21NFCA) += st21nfca/ obj-$(CONFIG_NFC_ST21NFCB) += st21nfcb/ +obj-$(CONFIG_NFC_NXP_NCI) += nxp-nci/ ccflags-$(CONFIG_NFC_DEBUG) := -DDEBUG diff --git a/drivers/nfc/nxp-nci/Kconfig b/drivers/nfc/nxp-nci/Kconfig new file mode 100644 index 000000000000..5f60c7cf02e8 --- /dev/null +++ b/drivers/nfc/nxp-nci/Kconfig @@ -0,0 +1,13 @@ +config NFC_NXP_NCI + tristate "NXP-NCI NFC driver" + depends on NFC_NCI + default n + ---help--- + Generic core driver for NXP NCI chips such as the NPC100 + or PN7150 families. + This is a driver based on the NCI NFC kernel layers and + will thus not work with NXP libnfc library. + + To compile this driver as a module, choose m here. The module will + be called nxp_nci. + Say N if unsure. diff --git a/drivers/nfc/nxp-nci/Makefile b/drivers/nfc/nxp-nci/Makefile new file mode 100644 index 000000000000..8f1e32826961 --- /dev/null +++ b/drivers/nfc/nxp-nci/Makefile @@ -0,0 +1,9 @@ +# +# Makefile for NXP-NCI NFC driver +# + +nxp-nci-objs = core.o firmware.o + +obj-$(CONFIG_NFC_NXP_NCI) += nxp-nci.o + +ccflags-$(CONFIG_NFC_DEBUG) := -DDEBUG diff --git a/drivers/nfc/nxp-nci/core.c b/drivers/nfc/nxp-nci/core.c new file mode 100644 index 000000000000..8979636d48ea --- /dev/null +++ b/drivers/nfc/nxp-nci/core.c @@ -0,0 +1,186 @@ +/* + * Generic driver for NXP NCI NFC chips + * + * Copyright (C) 2014 NXP Semiconductors All rights reserved. + * + * Authors: Clément Perrochaud + * + * Derived from PN544 device driver: + * Copyright (C) 2012 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ + +#include +#include +#include +#include +#include + +#include + +#include "nxp-nci.h" + +#define NXP_NCI_HDR_LEN 4 + +#define NXP_NCI_NFC_PROTOCOLS (NFC_PROTO_JEWEL_MASK | \ + NFC_PROTO_MIFARE_MASK | \ + NFC_PROTO_FELICA_MASK | \ + NFC_PROTO_ISO14443_MASK | \ + NFC_PROTO_ISO14443_B_MASK | \ + NFC_PROTO_NFC_DEP_MASK) + +static int nxp_nci_open(struct nci_dev *ndev) +{ + struct nxp_nci_info *info = nci_get_drvdata(ndev); + int r = 0; + + mutex_lock(&info->info_lock); + + if (info->mode != NXP_NCI_MODE_COLD) { + r = -EBUSY; + goto open_exit; + } + + if (info->phy_ops->set_mode) + r = info->phy_ops->set_mode(info->phy_id, NXP_NCI_MODE_NCI); + + info->mode = NXP_NCI_MODE_NCI; + +open_exit: + mutex_unlock(&info->info_lock); + return r; +} + +static int nxp_nci_close(struct nci_dev *ndev) +{ + struct nxp_nci_info *info = nci_get_drvdata(ndev); + int r = 0; + + mutex_lock(&info->info_lock); + + if (info->phy_ops->set_mode) + r = info->phy_ops->set_mode(info->phy_id, NXP_NCI_MODE_COLD); + + info->mode = NXP_NCI_MODE_COLD; + + mutex_unlock(&info->info_lock); + return r; +} + +static int nxp_nci_send(struct nci_dev *ndev, struct sk_buff *skb) +{ + struct nxp_nci_info *info = nci_get_drvdata(ndev); + int r; + + if (!info->phy_ops->write) { + r = -ENOTSUPP; + goto send_exit; + } + + if (info->mode != NXP_NCI_MODE_NCI) { + r = -EINVAL; + goto send_exit; + } + + r = info->phy_ops->write(info->phy_id, skb); + if (r < 0) + kfree_skb(skb); + +send_exit: + return r; +} + +static struct nci_ops nxp_nci_ops = { + .open = nxp_nci_open, + .close = nxp_nci_close, + .send = nxp_nci_send, + .fw_download = nxp_nci_fw_download, +}; + +int nxp_nci_probe(void *phy_id, struct device *pdev, + struct nxp_nci_phy_ops *phy_ops, unsigned int max_payload, + struct nci_dev **ndev) +{ + struct nxp_nci_info *info; + int r; + + info = devm_kzalloc(pdev, sizeof(struct nxp_nci_info), GFP_KERNEL); + if (!info) { + r = -ENOMEM; + goto probe_exit; + } + + info->phy_id = phy_id; + info->pdev = pdev; + info->phy_ops = phy_ops; + info->max_payload = max_payload; + INIT_WORK(&info->fw_info.work, nxp_nci_fw_work); + init_completion(&info->fw_info.cmd_completion); + mutex_init(&info->info_lock); + + if (info->phy_ops->set_mode) { + r = info->phy_ops->set_mode(info->phy_id, NXP_NCI_MODE_COLD); + if (r < 0) + goto probe_exit; + } + + info->mode = NXP_NCI_MODE_COLD; + + info->ndev = nci_allocate_device(&nxp_nci_ops, NXP_NCI_NFC_PROTOCOLS, + NXP_NCI_HDR_LEN, 0); + if (!info->ndev) { + r = -ENOMEM; + goto probe_exit; + } + + nci_set_parent_dev(info->ndev, pdev); + nci_set_drvdata(info->ndev, info); + r = nci_register_device(info->ndev); + if (r < 0) + goto probe_exit_free_nci; + + *ndev = info->ndev; + + goto probe_exit; + +probe_exit_free_nci: + nci_free_device(info->ndev); +probe_exit: + return r; +} +EXPORT_SYMBOL(nxp_nci_probe); + +void nxp_nci_remove(struct nci_dev *ndev) +{ + struct nxp_nci_info *info = nci_get_drvdata(ndev); + + if (info->mode == NXP_NCI_MODE_FW) + nxp_nci_fw_work_complete(info, -ESHUTDOWN); + cancel_work_sync(&info->fw_info.work); + + mutex_lock(&info->info_lock); + + if (info->phy_ops->set_mode) + info->phy_ops->set_mode(info->phy_id, NXP_NCI_MODE_COLD); + + nci_unregister_device(ndev); + nci_free_device(ndev); + + mutex_unlock(&info->info_lock); +} +EXPORT_SYMBOL(nxp_nci_remove); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("NXP NCI NFC driver"); +MODULE_AUTHOR("Clément Perrochaud "); diff --git a/drivers/nfc/nxp-nci/firmware.c b/drivers/nfc/nxp-nci/firmware.c new file mode 100644 index 000000000000..08573f97975f --- /dev/null +++ b/drivers/nfc/nxp-nci/firmware.c @@ -0,0 +1,324 @@ +/* + * Generic driver for NXP NCI NFC chips + * + * Copyright (C) 2014 NXP Semiconductors All rights reserved. + * + * Author: Clément Perrochaud + * + * Derived from PN544 device driver: + * Copyright (C) 2012 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ + +#include +#include +#include +#include + +#include "nxp-nci.h" + +/* Crypto operations can take up to 30 seconds */ +#define NXP_NCI_FW_ANSWER_TIMEOUT msecs_to_jiffies(30000) + +#define NXP_NCI_FW_CMD_RESET 0xF0 +#define NXP_NCI_FW_CMD_GETVERSION 0xF1 +#define NXP_NCI_FW_CMD_CHECKINTEGRITY 0xE0 +#define NXP_NCI_FW_CMD_WRITE 0xC0 +#define NXP_NCI_FW_CMD_READ 0xA2 +#define NXP_NCI_FW_CMD_GETSESSIONSTATE 0xF2 +#define NXP_NCI_FW_CMD_LOG 0xA7 +#define NXP_NCI_FW_CMD_FORCE 0xD0 +#define NXP_NCI_FW_CMD_GET_DIE_ID 0xF4 + +#define NXP_NCI_FW_CHUNK_FLAG 0x0400 + +#define NXP_NCI_FW_RESULT_OK 0x00 +#define NXP_NCI_FW_RESULT_INVALID_ADDR 0x01 +#define NXP_NCI_FW_RESULT_GENERIC_ERROR 0x02 +#define NXP_NCI_FW_RESULT_UNKNOWN_CMD 0x0B +#define NXP_NCI_FW_RESULT_ABORTED_CMD 0x0C +#define NXP_NCI_FW_RESULT_PLL_ERROR 0x0D +#define NXP_NCI_FW_RESULT_ADDR_RANGE_OFL_ERROR 0x1E +#define NXP_NCI_FW_RESULT_BUFFER_OFL_ERROR 0x1F +#define NXP_NCI_FW_RESULT_MEM_BSY 0x20 +#define NXP_NCI_FW_RESULT_SIGNATURE_ERROR 0x21 +#define NXP_NCI_FW_RESULT_FIRMWARE_VERSION_ERROR 0x24 +#define NXP_NCI_FW_RESULT_PROTOCOL_ERROR 0x28 +#define NXP_NCI_FW_RESULT_SFWU_DEGRADED 0x2A +#define NXP_NCI_FW_RESULT_PH_STATUS_FIRST_CHUNK 0x2D +#define NXP_NCI_FW_RESULT_PH_STATUS_NEXT_CHUNK 0x2E +#define NXP_NCI_FW_RESULT_PH_STATUS_INTERNAL_ERROR_5 0xC5 + +void nxp_nci_fw_work_complete(struct nxp_nci_info *info, int result) +{ + struct nxp_nci_fw_info *fw_info = &info->fw_info; + int r; + + if (info->phy_ops->set_mode) { + r = info->phy_ops->set_mode(info->phy_id, NXP_NCI_MODE_COLD); + if (r < 0 && result == 0) + result = -r; + } + + info->mode = NXP_NCI_MODE_COLD; + + if (fw_info->fw) { + release_firmware(fw_info->fw); + fw_info->fw = NULL; + } + + nfc_fw_download_done(info->ndev->nfc_dev, fw_info->name, (u32) -result); +} + +/* crc_ccitt cannot be used since it is computed MSB first and not LSB first */ +static u16 nxp_nci_fw_crc(u8 const *buffer, size_t len) +{ + u16 crc = 0xffff; + + while (len--) { + crc = ((crc >> 8) | (crc << 8)) ^ *buffer++; + crc ^= (crc & 0xff) >> 4; + crc ^= (crc & 0xff) << 12; + crc ^= (crc & 0xff) << 5; + } + + return crc; +} + +static int nxp_nci_fw_send_chunk(struct nxp_nci_info *info) +{ + struct nxp_nci_fw_info *fw_info = &info->fw_info; + u16 header, crc; + struct sk_buff *skb; + size_t chunk_len; + size_t remaining_len; + int r; + + skb = nci_skb_alloc(info->ndev, info->max_payload, GFP_KERNEL); + if (!skb) { + r = -ENOMEM; + goto chunk_exit; + } + + chunk_len = info->max_payload - NXP_NCI_FW_HDR_LEN - NXP_NCI_FW_CRC_LEN; + remaining_len = fw_info->frame_size - fw_info->written; + + if (remaining_len > chunk_len) { + header = NXP_NCI_FW_CHUNK_FLAG; + } else { + chunk_len = remaining_len; + header = 0x0000; + } + + header |= chunk_len & NXP_NCI_FW_FRAME_LEN_MASK; + put_unaligned_be16(header, skb_put(skb, NXP_NCI_FW_HDR_LEN)); + + memcpy(skb_put(skb, chunk_len), fw_info->data + fw_info->written, + chunk_len); + + crc = nxp_nci_fw_crc(skb->data, chunk_len + NXP_NCI_FW_HDR_LEN); + put_unaligned_be16(crc, skb_put(skb, NXP_NCI_FW_CRC_LEN)); + + r = info->phy_ops->write(info->phy_id, skb); + if (r >= 0) + r = chunk_len; + + kfree_skb(skb); + +chunk_exit: + return r; +} + +static int nxp_nci_fw_send(struct nxp_nci_info *info) +{ + struct nxp_nci_fw_info *fw_info = &info->fw_info; + long completion_rc; + int r; + + reinit_completion(&fw_info->cmd_completion); + + if (fw_info->written == 0) { + fw_info->frame_size = get_unaligned_be16(fw_info->data) & + NXP_NCI_FW_FRAME_LEN_MASK; + fw_info->data += NXP_NCI_FW_HDR_LEN; + fw_info->size -= NXP_NCI_FW_HDR_LEN; + } + + if (fw_info->frame_size > fw_info->size) + return -EMSGSIZE; + + r = nxp_nci_fw_send_chunk(info); + if (r < 0) + return r; + + fw_info->written += r; + + if (*fw_info->data == NXP_NCI_FW_CMD_RESET) { + fw_info->cmd_result = 0; + if (fw_info->fw) + schedule_work(&fw_info->work); + } else { + completion_rc = wait_for_completion_interruptible_timeout( + &fw_info->cmd_completion, NXP_NCI_FW_ANSWER_TIMEOUT); + if (completion_rc == 0) + return -ETIMEDOUT; + } + + return 0; +} + +void nxp_nci_fw_work(struct work_struct *work) +{ + struct nxp_nci_info *info; + struct nxp_nci_fw_info *fw_info; + int r; + + fw_info = container_of(work, struct nxp_nci_fw_info, work); + info = container_of(fw_info, struct nxp_nci_info, fw_info); + + mutex_lock(&info->info_lock); + + r = fw_info->cmd_result; + if (r < 0) + goto exit_work; + + if (fw_info->written == fw_info->frame_size) { + fw_info->data += fw_info->frame_size; + fw_info->size -= fw_info->frame_size; + fw_info->written = 0; + } + + if (fw_info->size > 0) + r = nxp_nci_fw_send(info); + +exit_work: + if (r < 0 || fw_info->size == 0) + nxp_nci_fw_work_complete(info, r); + mutex_unlock(&info->info_lock); +} + +int nxp_nci_fw_download(struct nci_dev *ndev, const char *firmware_name) +{ + struct nxp_nci_info *info = nci_get_drvdata(ndev); + struct nxp_nci_fw_info *fw_info = &info->fw_info; + int r; + + mutex_lock(&info->info_lock); + + if (!info->phy_ops->set_mode || !info->phy_ops->write) { + r = -ENOTSUPP; + goto fw_download_exit; + } + + if (!firmware_name || firmware_name[0] == '\0') { + r = -EINVAL; + goto fw_download_exit; + } + + strcpy(fw_info->name, firmware_name); + + r = request_firmware(&fw_info->fw, firmware_name, + ndev->nfc_dev->dev.parent); + if (r < 0) + goto fw_download_exit; + + r = info->phy_ops->set_mode(info->phy_id, NXP_NCI_MODE_FW); + if (r < 0) + goto fw_download_exit; + + info->mode = NXP_NCI_MODE_FW; + + fw_info->data = fw_info->fw->data; + fw_info->size = fw_info->fw->size; + fw_info->written = 0; + fw_info->frame_size = 0; + fw_info->cmd_result = 0; + + if (fw_info->fw) + schedule_work(&fw_info->work); + +fw_download_exit: + mutex_unlock(&info->info_lock); + return r; +} + +static int nxp_nci_fw_read_status(u8 stat) +{ + switch (stat) { + case NXP_NCI_FW_RESULT_OK: + return 0; + case NXP_NCI_FW_RESULT_INVALID_ADDR: + return -EINVAL; + case NXP_NCI_FW_RESULT_UNKNOWN_CMD: + return -EINVAL; + case NXP_NCI_FW_RESULT_ABORTED_CMD: + return -EMSGSIZE; + case NXP_NCI_FW_RESULT_ADDR_RANGE_OFL_ERROR: + return -EADDRNOTAVAIL; + case NXP_NCI_FW_RESULT_BUFFER_OFL_ERROR: + return -ENOBUFS; + case NXP_NCI_FW_RESULT_MEM_BSY: + return -ENOKEY; + case NXP_NCI_FW_RESULT_SIGNATURE_ERROR: + return -EKEYREJECTED; + case NXP_NCI_FW_RESULT_FIRMWARE_VERSION_ERROR: + return -EALREADY; + case NXP_NCI_FW_RESULT_PROTOCOL_ERROR: + return -EPROTO; + case NXP_NCI_FW_RESULT_SFWU_DEGRADED: + return -EHWPOISON; + case NXP_NCI_FW_RESULT_PH_STATUS_FIRST_CHUNK: + return 0; + case NXP_NCI_FW_RESULT_PH_STATUS_NEXT_CHUNK: + return 0; + case NXP_NCI_FW_RESULT_PH_STATUS_INTERNAL_ERROR_5: + return -EINVAL; + default: + return -EIO; + } +} + +static u16 nxp_nci_fw_check_crc(struct sk_buff *skb) +{ + u16 crc, frame_crc; + size_t len = skb->len - NXP_NCI_FW_CRC_LEN; + + crc = nxp_nci_fw_crc(skb->data, len); + frame_crc = get_unaligned_be16(skb->data + len); + + return (crc ^ frame_crc); +} + +void nxp_nci_fw_recv_frame(struct nci_dev *ndev, struct sk_buff *skb) +{ + struct nxp_nci_info *info = nci_get_drvdata(ndev); + struct nxp_nci_fw_info *fw_info = &info->fw_info; + + complete(&fw_info->cmd_completion); + + if (skb) { + if (nxp_nci_fw_check_crc(skb) != 0x00) + fw_info->cmd_result = -EBADMSG; + else + fw_info->cmd_result = nxp_nci_fw_read_status( + *skb_pull(skb, NXP_NCI_FW_HDR_LEN)); + kfree_skb(skb); + } else { + fw_info->cmd_result = -EIO; + } + + if (fw_info->fw) + schedule_work(&fw_info->work); +} +EXPORT_SYMBOL(nxp_nci_fw_recv_frame); diff --git a/drivers/nfc/nxp-nci/nxp-nci.h b/drivers/nfc/nxp-nci/nxp-nci.h new file mode 100644 index 000000000000..f1fecc4e2457 --- /dev/null +++ b/drivers/nfc/nxp-nci/nxp-nci.h @@ -0,0 +1,89 @@ +/* + * Copyright (C) 2014 NXP Semiconductors All rights reserved. + * + * Authors: Clément Perrochaud + * + * Derived from PN544 device driver: + * Copyright (C) 2012 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . +*/ + +#ifndef __LOCAL_NXP_NCI_H_ +#define __LOCAL_NXP_NCI_H_ + +#include +#include +#include +#include + +#include + +#define NXP_NCI_FW_HDR_LEN 2 +#define NXP_NCI_FW_CRC_LEN 2 + +#define NXP_NCI_FW_FRAME_LEN_MASK 0x03FF + +enum nxp_nci_mode { + NXP_NCI_MODE_COLD, + NXP_NCI_MODE_NCI, + NXP_NCI_MODE_FW +}; + +struct nxp_nci_phy_ops { + int (*set_mode)(void *id, enum nxp_nci_mode mode); + int (*write)(void *id, struct sk_buff *skb); +}; + +struct nxp_nci_fw_info { + char name[NFC_FIRMWARE_NAME_MAXSIZE + 1]; + const struct firmware *fw; + + size_t size; + size_t written; + + const u8 *data; + size_t frame_size; + + struct work_struct work; + struct completion cmd_completion; + + int cmd_result; +}; + +struct nxp_nci_info { + struct nci_dev *ndev; + void *phy_id; + struct device *pdev; + + enum nxp_nci_mode mode; + + struct nxp_nci_phy_ops *phy_ops; + unsigned int max_payload; + + struct mutex info_lock; + + struct nxp_nci_fw_info fw_info; +}; + +int nxp_nci_fw_download(struct nci_dev *ndev, const char *firmware_name); +void nxp_nci_fw_work(struct work_struct *work); +void nxp_nci_fw_recv_frame(struct nci_dev *ndev, struct sk_buff *skb); +void nxp_nci_fw_work_complete(struct nxp_nci_info *info, int result); + +int nxp_nci_probe(void *phy_id, struct device *pdev, + struct nxp_nci_phy_ops *phy_ops, unsigned int max_payload, + struct nci_dev **ndev); +void nxp_nci_remove(struct nci_dev *ndev); + +#endif /* __LOCAL_NXP_NCI_H_ */ diff --git a/include/linux/platform_data/nxp-nci.h b/include/linux/platform_data/nxp-nci.h new file mode 100644 index 000000000000..d6ed28679bb2 --- /dev/null +++ b/include/linux/platform_data/nxp-nci.h @@ -0,0 +1,27 @@ +/* + * Generic platform data for the NXP NCI NFC chips. + * + * Copyright (C) 2014 NXP Semiconductors All rights reserved. + * + * Authors: Clément Perrochaud + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef _NXP_NCI_H_ +#define _NXP_NCI_H_ + +struct nxp_nci_nfc_platform_data { + unsigned int gpio_en; + unsigned int gpio_fw; + unsigned int irq; +}; + +#endif /* _NXP_NCI_H_ */ -- cgit v1.2.3 From 61edafbb47e9f46fb850035b1f8f062564445704 Mon Sep 17 00:00:00 2001 From: Patrick McHardy Date: Wed, 25 Mar 2015 14:08:47 +0000 Subject: netfilter: nf_tables: consolide set element destruction With the conversion to set extensions, it is now possible to consolidate the different set element destruction functions. The set implementations' ->remove() functions are changed to only take the element out of their internal data structures. Elements will be freed in a batched fashion after the global transaction's completion RCU grace period. This reduces the amount of grace periods required for nft_hash from N to zero additional ones, additionally this guarantees that the set elements' extensions of all implementations can be used under RCU protection. Signed-off-by: Patrick McHardy Signed-off-by: Pablo Neira Ayuso --- include/net/netfilter/nf_tables.h | 2 ++ net/netfilter/nf_tables_api.c | 34 ++++++++++++++++++++-------------- net/netfilter/nft_hash.c | 18 ++++-------------- net/netfilter/nft_rbtree.c | 14 +------------- 4 files changed, 27 insertions(+), 41 deletions(-) (limited to 'include') diff --git a/include/net/netfilter/nf_tables.h b/include/net/netfilter/nf_tables.h index ef3457c1cb62..6ac63323afd2 100644 --- a/include/net/netfilter/nf_tables.h +++ b/include/net/netfilter/nf_tables.h @@ -423,6 +423,8 @@ static inline struct nft_set_ext *nft_set_elem_ext(const struct nft_set *set, return elem + set->ops->elemsize; } +void nft_set_elem_destroy(const struct nft_set *set, void *elem); + /** * struct nft_expr_type - nf_tables expression type * diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c index 99cb884b985f..b35512f1934c 100644 --- a/net/netfilter/nf_tables_api.c +++ b/net/netfilter/nf_tables_api.c @@ -3155,6 +3155,18 @@ static void *nft_set_elem_init(const struct nft_set *set, return elem; } +void nft_set_elem_destroy(const struct nft_set *set, void *elem) +{ + struct nft_set_ext *ext = nft_set_elem_ext(set, elem); + + nft_data_uninit(nft_set_ext_key(ext), NFT_DATA_VALUE); + if (nft_set_ext_exists(ext, NFT_SET_EXT_DATA)) + nft_data_uninit(nft_set_ext_data(ext), set->dtype); + + kfree(elem); +} +EXPORT_SYMBOL_GPL(nft_set_elem_destroy); + static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set, const struct nlattr *attr) { @@ -3596,6 +3608,10 @@ static void nf_tables_commit_release(struct nft_trans *trans) case NFT_MSG_DELSET: nft_set_destroy(nft_trans_set(trans)); break; + case NFT_MSG_DELSETELEM: + nft_set_elem_destroy(nft_trans_elem_set(trans), + nft_trans_elem(trans).priv); + break; } kfree(trans); } @@ -3605,7 +3621,6 @@ static int nf_tables_commit(struct sk_buff *skb) struct net *net = sock_net(skb->sk); struct nft_trans *trans, *next; struct nft_trans_elem *te; - struct nft_set_ext *ext; /* Bump generation counter, invalidate any dump in progress */ while (++net->nft.base_seq == 0); @@ -3690,18 +3705,12 @@ static int nf_tables_commit(struct sk_buff *skb) break; case NFT_MSG_DELSETELEM: te = (struct nft_trans_elem *)trans->data; - ext = nft_set_elem_ext(te->set, te->elem.priv); nf_tables_setelem_notify(&trans->ctx, te->set, &te->elem, NFT_MSG_DELSETELEM, 0); te->set->ops->get(te->set, &te->elem); - nft_data_uninit(&te->elem.key, NFT_DATA_VALUE); - if (nft_set_ext_exists(ext, NFT_SET_EXT_DATA)) - nft_data_uninit(nft_set_ext_data(ext), - te->set->dtype); te->set->ops->remove(te->set, &te->elem); - nft_trans_destroy(trans); break; } } @@ -3733,6 +3742,10 @@ static void nf_tables_abort_release(struct nft_trans *trans) case NFT_MSG_NEWSET: nft_set_destroy(nft_trans_set(trans)); break; + case NFT_MSG_NEWSETELEM: + nft_set_elem_destroy(nft_trans_elem_set(trans), + nft_trans_elem(trans).priv); + break; } kfree(trans); } @@ -3742,7 +3755,6 @@ static int nf_tables_abort(struct sk_buff *skb) struct net *net = sock_net(skb->sk); struct nft_trans *trans, *next; struct nft_trans_elem *te; - struct nft_set_ext *ext; list_for_each_entry_safe(trans, next, &net->nft.commit_list, list) { switch (trans->msg_type) { @@ -3804,15 +3816,9 @@ static int nf_tables_abort(struct sk_buff *skb) case NFT_MSG_NEWSETELEM: nft_trans_elem_set(trans)->nelems--; te = (struct nft_trans_elem *)trans->data; - ext = nft_set_elem_ext(te->set, te->elem.priv); te->set->ops->get(te->set, &te->elem); - nft_data_uninit(&te->elem.key, NFT_DATA_VALUE); - if (nft_set_ext_exists(ext, NFT_SET_EXT_DATA)) - nft_data_uninit(nft_set_ext_data(ext), - te->set->dtype); te->set->ops->remove(te->set, &te->elem); - nft_trans_destroy(trans); break; case NFT_MSG_DELSETELEM: nft_trans_elem_set(trans)->nelems++; diff --git a/net/netfilter/nft_hash.c b/net/netfilter/nft_hash.c index 15951a823d1d..94bf25def37f 100644 --- a/net/netfilter/nft_hash.c +++ b/net/netfilter/nft_hash.c @@ -96,23 +96,12 @@ static int nft_hash_insert(const struct nft_set *set, nft_hash_params); } -static void nft_hash_elem_destroy(const struct nft_set *set, - struct nft_hash_elem *he) -{ - nft_data_uninit(nft_set_ext_key(&he->ext), NFT_DATA_VALUE); - if (set->flags & NFT_SET_MAP) - nft_data_uninit(nft_set_ext_data(&he->ext), set->dtype); - kfree(he); -} - static void nft_hash_remove(const struct nft_set *set, const struct nft_set_elem *elem) { struct nft_hash *priv = nft_set_priv(set); rhashtable_remove_fast(&priv->ht, elem->cookie, nft_hash_params); - synchronize_rcu(); - kfree(elem->cookie); } static int nft_hash_get(const struct nft_set *set, struct nft_set_elem *elem) @@ -208,16 +197,17 @@ static int nft_hash_init(const struct nft_set *set, return rhashtable_init(&priv->ht, ¶ms); } -static void nft_free_element(void *ptr, void *arg) +static void nft_hash_elem_destroy(void *ptr, void *arg) { - nft_hash_elem_destroy((const struct nft_set *)arg, ptr); + nft_set_elem_destroy((const struct nft_set *)arg, ptr); } static void nft_hash_destroy(const struct nft_set *set) { struct nft_hash *priv = nft_set_priv(set); - rhashtable_free_and_destroy(&priv->ht, nft_free_element, (void *)set); + rhashtable_free_and_destroy(&priv->ht, nft_hash_elem_destroy, + (void *)set); } static bool nft_hash_estimate(const struct nft_set_desc *desc, u32 features, diff --git a/net/netfilter/nft_rbtree.c b/net/netfilter/nft_rbtree.c index ebf6e60df41c..332c6afc77e9 100644 --- a/net/netfilter/nft_rbtree.c +++ b/net/netfilter/nft_rbtree.c @@ -72,17 +72,6 @@ out: return false; } -static void nft_rbtree_elem_destroy(const struct nft_set *set, - struct nft_rbtree_elem *rbe) -{ - nft_data_uninit(nft_set_ext_key(&rbe->ext), NFT_DATA_VALUE); - if (set->flags & NFT_SET_MAP && - nft_set_ext_exists(&rbe->ext, NFT_SET_EXT_DATA)) - nft_data_uninit(nft_set_ext_data(&rbe->ext), set->dtype); - - kfree(rbe); -} - static int __nft_rbtree_insert(const struct nft_set *set, struct nft_rbtree_elem *new) { @@ -133,7 +122,6 @@ static void nft_rbtree_remove(const struct nft_set *set, spin_lock_bh(&nft_rbtree_lock); rb_erase(&rbe->node, &priv->root); spin_unlock_bh(&nft_rbtree_lock); - kfree(rbe); } static int nft_rbtree_get(const struct nft_set *set, struct nft_set_elem *elem) @@ -213,7 +201,7 @@ static void nft_rbtree_destroy(const struct nft_set *set) while ((node = priv->root.rb_node) != NULL) { rb_erase(node, &priv->root); rbe = rb_entry(node, struct nft_rbtree_elem, node); - nft_rbtree_elem_destroy(set, rbe); + nft_set_elem_destroy(set, rbe); } } -- cgit v1.2.3 From b2832dd6621bf73eb8ad38389a94bd83a5983886 Mon Sep 17 00:00:00 2001 From: Patrick McHardy Date: Wed, 25 Mar 2015 14:08:48 +0000 Subject: netfilter: nf_tables: return set extensions from ->lookup() Return the extension area from the ->lookup() function to allow to consolidate common actions. Signed-off-by: Patrick McHardy Signed-off-by: Pablo Neira Ayuso --- include/net/netfilter/nf_tables.h | 4 +++- net/netfilter/nft_hash.c | 6 +++--- net/netfilter/nft_lookup.c | 6 +++++- net/netfilter/nft_rbtree.c | 7 +++---- 4 files changed, 14 insertions(+), 9 deletions(-) (limited to 'include') diff --git a/include/net/netfilter/nf_tables.h b/include/net/netfilter/nf_tables.h index 6ac63323afd2..f190d26bda7d 100644 --- a/include/net/netfilter/nf_tables.h +++ b/include/net/netfilter/nf_tables.h @@ -200,6 +200,8 @@ struct nft_set_estimate { enum nft_set_class class; }; +struct nft_set_ext; + /** * struct nft_set_ops - nf_tables set operations * @@ -218,7 +220,7 @@ struct nft_set_estimate { struct nft_set_ops { bool (*lookup)(const struct nft_set *set, const struct nft_data *key, - struct nft_data *data); + const struct nft_set_ext **ext); int (*get)(const struct nft_set *set, struct nft_set_elem *elem); int (*insert)(const struct nft_set *set, diff --git a/net/netfilter/nft_hash.c b/net/netfilter/nft_hash.c index 94bf25def37f..5bee82195ef5 100644 --- a/net/netfilter/nft_hash.c +++ b/net/netfilter/nft_hash.c @@ -66,7 +66,7 @@ static inline int nft_hash_cmp(struct rhashtable_compare_arg *arg, static bool nft_hash_lookup(const struct nft_set *set, const struct nft_data *key, - struct nft_data *data) + const struct nft_set_ext **ext) { struct nft_hash *priv = nft_set_priv(set); const struct nft_hash_elem *he; @@ -76,8 +76,8 @@ static bool nft_hash_lookup(const struct nft_set *set, }; he = rhashtable_lookup_fast(&priv->ht, &arg, nft_hash_params); - if (he && set->flags & NFT_SET_MAP) - nft_data_copy(data, nft_set_ext_data(&he->ext)); + if (he != NULL) + *ext = &he->ext; return !!he; } diff --git a/net/netfilter/nft_lookup.c b/net/netfilter/nft_lookup.c index 9615b8b9fb37..a5f30b8760ea 100644 --- a/net/netfilter/nft_lookup.c +++ b/net/netfilter/nft_lookup.c @@ -31,9 +31,13 @@ static void nft_lookup_eval(const struct nft_expr *expr, { const struct nft_lookup *priv = nft_expr_priv(expr); const struct nft_set *set = priv->set; + const struct nft_set_ext *ext; - if (set->ops->lookup(set, &data[priv->sreg], &data[priv->dreg])) + if (set->ops->lookup(set, &data[priv->sreg], &ext)) { + if (set->flags & NFT_SET_MAP) + nft_data_copy(&data[priv->dreg], nft_set_ext_data(ext)); return; + } data[NFT_REG_VERDICT].verdict = NFT_BREAK; } diff --git a/net/netfilter/nft_rbtree.c b/net/netfilter/nft_rbtree.c index 332c6afc77e9..cbba755ebebc 100644 --- a/net/netfilter/nft_rbtree.c +++ b/net/netfilter/nft_rbtree.c @@ -31,7 +31,7 @@ struct nft_rbtree_elem { static bool nft_rbtree_lookup(const struct nft_set *set, const struct nft_data *key, - struct nft_data *data) + const struct nft_set_ext **ext) { const struct nft_rbtree *priv = nft_set_priv(set); const struct nft_rbtree_elem *rbe, *interval = NULL; @@ -55,10 +55,9 @@ found: *nft_set_ext_flags(&rbe->ext) & NFT_SET_ELEM_INTERVAL_END) goto out; - if (set->flags & NFT_SET_MAP) - nft_data_copy(data, nft_set_ext_data(&rbe->ext)); - spin_unlock_bh(&nft_rbtree_lock); + + *ext = &rbe->ext; return true; } } -- cgit v1.2.3 From ea4bd995b0f2fc5677ff8085e92a5d2544b9937c Mon Sep 17 00:00:00 2001 From: Patrick McHardy Date: Wed, 25 Mar 2015 14:08:49 +0000 Subject: netfilter: nf_tables: add transaction helper functions Add some helper functions for building the genmask as preparation for set transactions. Also add a little documentation how this stuff actually works. Signed-off-by: Patrick McHardy Signed-off-by: Pablo Neira Ayuso --- include/net/netfilter/nf_tables.h | 28 ++++++++++++++++++++++++++++ net/netfilter/nf_tables_api.c | 17 ++++++----------- net/netfilter/nf_tables_core.c | 6 +----- 3 files changed, 35 insertions(+), 16 deletions(-) (limited to 'include') diff --git a/include/net/netfilter/nf_tables.h b/include/net/netfilter/nf_tables.h index f190d26bda7d..4c46a325874e 100644 --- a/include/net/netfilter/nf_tables.h +++ b/include/net/netfilter/nf_tables.h @@ -720,6 +720,34 @@ void nft_unregister_expr(struct nft_expr_type *); #define MODULE_ALIAS_NFT_SET() \ MODULE_ALIAS("nft-set") +/* + * The gencursor defines two generations, the currently active and the + * next one. Objects contain a bitmask of 2 bits specifying the generations + * they're active in. A set bit means they're inactive in the generation + * represented by that bit. + * + * New objects start out as inactive in the current and active in the + * next generation. When committing the ruleset the bitmask is cleared, + * meaning they're active in all generations. When removing an object, + * it is set inactive in the next generation. After committing the ruleset, + * the objects are removed. + */ +static inline unsigned int nft_gencursor_next(const struct net *net) +{ + return net->nft.gencursor + 1 == 1 ? 1 : 0; +} + +static inline u8 nft_genmask_next(const struct net *net) +{ + return 1 << nft_gencursor_next(net); +} + +static inline u8 nft_genmask_cur(const struct net *net) +{ + /* Use ACCESS_ONCE() to prevent refetching the value for atomicity */ + return 1 << ACCESS_ONCE(net->nft.gencursor); +} + /** * struct nft_trans - nf_tables object update in transaction * diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c index b35512f1934c..66fa5e935a55 100644 --- a/net/netfilter/nf_tables_api.c +++ b/net/netfilter/nf_tables_api.c @@ -198,36 +198,31 @@ static int nft_delchain(struct nft_ctx *ctx) static inline bool nft_rule_is_active(struct net *net, const struct nft_rule *rule) { - return (rule->genmask & (1 << net->nft.gencursor)) == 0; -} - -static inline int gencursor_next(struct net *net) -{ - return net->nft.gencursor+1 == 1 ? 1 : 0; + return (rule->genmask & nft_genmask_cur(net)) == 0; } static inline int nft_rule_is_active_next(struct net *net, const struct nft_rule *rule) { - return (rule->genmask & (1 << gencursor_next(net))) == 0; + return (rule->genmask & nft_genmask_next(net)) == 0; } static inline void nft_rule_activate_next(struct net *net, struct nft_rule *rule) { /* Now inactive, will be active in the future */ - rule->genmask = (1 << net->nft.gencursor); + rule->genmask = nft_genmask_cur(net); } static inline void nft_rule_deactivate_next(struct net *net, struct nft_rule *rule) { - rule->genmask = (1 << gencursor_next(net)); + rule->genmask = nft_genmask_next(net); } static inline void nft_rule_clear(struct net *net, struct nft_rule *rule) { - rule->genmask &= ~(1 << gencursor_next(net)); + rule->genmask &= ~nft_genmask_next(net); } static int @@ -3626,7 +3621,7 @@ static int nf_tables_commit(struct sk_buff *skb) while (++net->nft.base_seq == 0); /* A new generation has just started */ - net->nft.gencursor = gencursor_next(net); + net->nft.gencursor = nft_gencursor_next(net); /* Make sure all packets have left the previous generation before * purging old rules. diff --git a/net/netfilter/nf_tables_core.c b/net/netfilter/nf_tables_core.c index 4429008fe99d..ef4dfcbaf149 100644 --- a/net/netfilter/nf_tables_core.c +++ b/net/netfilter/nf_tables_core.c @@ -121,11 +121,7 @@ nft_do_chain(struct nft_pktinfo *pkt, const struct nf_hook_ops *ops) struct nft_jumpstack jumpstack[NFT_JUMP_STACK_SIZE]; struct nft_stats *stats; int rulenum; - /* - * Cache cursor to avoid problems in case that the cursor is updated - * while traversing the ruleset. - */ - unsigned int gencursor = ACCESS_ONCE(net->nft.gencursor); + unsigned int gencursor = nft_genmask_cur(net); do_chain: rulenum = 0; -- cgit v1.2.3 From cc02e457bb86f7b6ffee3651bab22d104b60effb Mon Sep 17 00:00:00 2001 From: Patrick McHardy Date: Wed, 25 Mar 2015 14:08:50 +0000 Subject: netfilter: nf_tables: implement set transaction support Set elements are the last object type not supporting transaction support. Implement similar to the existing rule transactions: The global transaction counter keeps track of two generations, current and next. Each element contains a bitmask specifying in which generations it is inactive. New elements start out as inactive in the current generation and active in the next. On commit, the previous next generation becomes the current generation and the element becomes active. The bitmask is then cleared to indicate that the element is active in all future generations. If the transaction is aborted, the element is removed from the set before it becomes active. When removing an element, it gets marked as inactive in the next generation. On commit the next generation becomes active and the therefor the element inactive. It is then taken out of then set and released. On abort, the element is marked as active for the next generation again. Lookups ignore elements not active in the current generation. The current set types (hash/rbtree) both use a field in the extension area to store the generation mask. This (currently) does not require any additional memory since we have some free space in there. Signed-off-by: Patrick McHardy Signed-off-by: Pablo Neira Ayuso --- include/net/netfilter/nf_tables.h | 33 +++++++++++++++++++++------ net/netfilter/nf_tables_api.c | 33 ++++++++++++++++----------- net/netfilter/nft_hash.c | 38 +++++++++++++++++++++++-------- net/netfilter/nft_rbtree.c | 48 +++++++++++++++++++++++++++++++-------- 4 files changed, 112 insertions(+), 40 deletions(-) (limited to 'include') diff --git a/include/net/netfilter/nf_tables.h b/include/net/netfilter/nf_tables.h index 4c46a325874e..b8cd60dcb4e1 100644 --- a/include/net/netfilter/nf_tables.h +++ b/include/net/netfilter/nf_tables.h @@ -138,15 +138,10 @@ struct nft_userdata { /** * struct nft_set_elem - generic representation of set elements * - * @cookie: implementation specific element cookie * @key: element key * @priv: element private data and extensions - * - * The cookie can be used to store a handle to the element for subsequent - * removal. */ struct nft_set_elem { - void *cookie; struct nft_data key; void *priv; }; @@ -207,6 +202,8 @@ struct nft_set_ext; * * @lookup: look up an element within the set * @insert: insert new element into set + * @activate: activate new element in the next generation + * @deactivate: deactivate element in the next generation * @remove: remove element from set * @walk: iterate over all set elemeennts * @privsize: function to return size of set private data @@ -221,10 +218,12 @@ struct nft_set_ops { bool (*lookup)(const struct nft_set *set, const struct nft_data *key, const struct nft_set_ext **ext); - int (*get)(const struct nft_set *set, - struct nft_set_elem *elem); int (*insert)(const struct nft_set *set, const struct nft_set_elem *elem); + void (*activate)(const struct nft_set *set, + const struct nft_set_elem *elem); + void * (*deactivate)(const struct nft_set *set, + const struct nft_set_elem *elem); void (*remove)(const struct nft_set *set, const struct nft_set_elem *elem); void (*walk)(const struct nft_ctx *ctx, @@ -261,6 +260,7 @@ void nft_unregister_set(struct nft_set_ops *ops); * @nelems: number of elements * @policy: set parameterization (see enum nft_set_policies) * @ops: set ops + * @pnet: network namespace * @flags: set flags * @klen: key length * @dlen: data length @@ -277,6 +277,7 @@ struct nft_set { u16 policy; /* runtime data below here */ const struct nft_set_ops *ops ____cacheline_aligned; + possible_net_t pnet; u16 flags; u8 klen; u8 dlen; @@ -355,10 +356,12 @@ struct nft_set_ext_tmpl { /** * struct nft_set_ext - set extensions * + * @genmask: generation mask * @offset: offsets of individual extension types * @data: beginning of extension data */ struct nft_set_ext { + u8 genmask; u8 offset[NFT_SET_EXT_NUM]; char data[0]; }; @@ -748,6 +751,22 @@ static inline u8 nft_genmask_cur(const struct net *net) return 1 << ACCESS_ONCE(net->nft.gencursor); } +/* + * Set element transaction helpers + */ + +static inline bool nft_set_elem_active(const struct nft_set_ext *ext, + u8 genmask) +{ + return !(ext->genmask & genmask); +} + +static inline void nft_set_elem_change_active(const struct nft_set *set, + struct nft_set_ext *ext) +{ + ext->genmask ^= nft_genmask_next(read_pnet(&set->pnet)); +} + /** * struct nft_trans - nf_tables object update in transaction * diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c index 66fa5e935a55..5604c2df05d1 100644 --- a/net/netfilter/nf_tables_api.c +++ b/net/netfilter/nf_tables_api.c @@ -2690,6 +2690,7 @@ static int nf_tables_newset(struct sock *nlsk, struct sk_buff *skb, goto err2; INIT_LIST_HEAD(&set->bindings); + write_pnet(&set->pnet, net); set->ops = ops; set->ktype = ktype; set->klen = desc.klen; @@ -3221,10 +3222,6 @@ static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set, if (d1.type != NFT_DATA_VALUE || d1.len != set->klen) goto err2; - err = -EEXIST; - if (set->ops->get(set, &elem) == 0) - goto err2; - nft_set_ext_add(&tmpl, NFT_SET_EXT_KEY); if (nla[NFTA_SET_ELEM_DATA] != NULL) { @@ -3266,6 +3263,7 @@ static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set, if (trans == NULL) goto err4; + ext->genmask = nft_genmask_cur(ctx->net); err = set->ops->insert(set, &elem); if (err < 0) goto err5; @@ -3353,19 +3351,24 @@ static int nft_del_setelem(struct nft_ctx *ctx, struct nft_set *set, if (desc.type != NFT_DATA_VALUE || desc.len != set->klen) goto err2; - err = set->ops->get(set, &elem); - if (err < 0) - goto err2; - trans = nft_trans_elem_alloc(ctx, NFT_MSG_DELSETELEM, set); if (trans == NULL) { err = -ENOMEM; goto err2; } + elem.priv = set->ops->deactivate(set, &elem); + if (elem.priv == NULL) { + err = -ENOENT; + goto err3; + } + nft_trans_elem(trans) = elem; list_add_tail(&trans->list, &ctx->net->nft.commit_list); return 0; + +err3: + kfree(trans); err2: nft_data_uninit(&elem.key, desc.type); err1: @@ -3692,9 +3695,11 @@ static int nf_tables_commit(struct sk_buff *skb) NFT_MSG_DELSET, GFP_KERNEL); break; case NFT_MSG_NEWSETELEM: - nf_tables_setelem_notify(&trans->ctx, - nft_trans_elem_set(trans), - &nft_trans_elem(trans), + te = (struct nft_trans_elem *)trans->data; + + te->set->ops->activate(te->set, &te->elem); + nf_tables_setelem_notify(&trans->ctx, te->set, + &te->elem, NFT_MSG_NEWSETELEM, 0); nft_trans_destroy(trans); break; @@ -3704,7 +3709,6 @@ static int nf_tables_commit(struct sk_buff *skb) nf_tables_setelem_notify(&trans->ctx, te->set, &te->elem, NFT_MSG_DELSETELEM, 0); - te->set->ops->get(te->set, &te->elem); te->set->ops->remove(te->set, &te->elem); break; } @@ -3812,11 +3816,14 @@ static int nf_tables_abort(struct sk_buff *skb) nft_trans_elem_set(trans)->nelems--; te = (struct nft_trans_elem *)trans->data; - te->set->ops->get(te->set, &te->elem); te->set->ops->remove(te->set, &te->elem); break; case NFT_MSG_DELSETELEM: + te = (struct nft_trans_elem *)trans->data; + nft_trans_elem_set(trans)->nelems++; + te->set->ops->activate(te->set, &te->elem); + nft_trans_destroy(trans); break; } diff --git a/net/netfilter/nft_hash.c b/net/netfilter/nft_hash.c index 5bee82195ef5..c7e1a9d7d46f 100644 --- a/net/netfilter/nft_hash.c +++ b/net/netfilter/nft_hash.c @@ -35,6 +35,7 @@ struct nft_hash_elem { struct nft_hash_cmp_arg { const struct nft_set *set; const struct nft_data *key; + u8 genmask; }; static const struct rhashtable_params nft_hash_params; @@ -61,6 +62,8 @@ static inline int nft_hash_cmp(struct rhashtable_compare_arg *arg, if (nft_data_cmp(nft_set_ext_key(&he->ext), x->key, x->set->klen)) return 1; + if (!nft_set_elem_active(&he->ext, x->genmask)) + return 1; return 0; } @@ -71,6 +74,7 @@ static bool nft_hash_lookup(const struct nft_set *set, struct nft_hash *priv = nft_set_priv(set); const struct nft_hash_elem *he; struct nft_hash_cmp_arg arg = { + .genmask = nft_genmask_cur(read_pnet(&set->pnet)), .set = set, .key = key, }; @@ -88,6 +92,7 @@ static int nft_hash_insert(const struct nft_set *set, struct nft_hash *priv = nft_set_priv(set); struct nft_hash_elem *he = elem->priv; struct nft_hash_cmp_arg arg = { + .genmask = nft_genmask_next(read_pnet(&set->pnet)), .set = set, .key = &elem->key, }; @@ -96,30 +101,39 @@ static int nft_hash_insert(const struct nft_set *set, nft_hash_params); } -static void nft_hash_remove(const struct nft_set *set, - const struct nft_set_elem *elem) +static void nft_hash_activate(const struct nft_set *set, + const struct nft_set_elem *elem) { - struct nft_hash *priv = nft_set_priv(set); + struct nft_hash_elem *he = elem->priv; - rhashtable_remove_fast(&priv->ht, elem->cookie, nft_hash_params); + nft_set_elem_change_active(set, &he->ext); } -static int nft_hash_get(const struct nft_set *set, struct nft_set_elem *elem) +static void *nft_hash_deactivate(const struct nft_set *set, + const struct nft_set_elem *elem) { struct nft_hash *priv = nft_set_priv(set); struct nft_hash_elem *he; struct nft_hash_cmp_arg arg = { + .genmask = nft_genmask_next(read_pnet(&set->pnet)), .set = set, .key = &elem->key, }; he = rhashtable_lookup_fast(&priv->ht, &arg, nft_hash_params); - if (!he) - return -ENOENT; + if (he != NULL) + nft_set_elem_change_active(set, &he->ext); - elem->priv = he; + return he; +} - return 0; +static void nft_hash_remove(const struct nft_set *set, + const struct nft_set_elem *elem) +{ + struct nft_hash *priv = nft_set_priv(set); + struct nft_hash_elem *he = elem->priv; + + rhashtable_remove_fast(&priv->ht, &he->node, nft_hash_params); } static void nft_hash_walk(const struct nft_ctx *ctx, const struct nft_set *set, @@ -129,6 +143,7 @@ static void nft_hash_walk(const struct nft_ctx *ctx, const struct nft_set *set, struct nft_hash_elem *he; struct rhashtable_iter hti; struct nft_set_elem elem; + u8 genmask = nft_genmask_cur(read_pnet(&set->pnet)); int err; err = rhashtable_walk_init(&priv->ht, &hti); @@ -155,6 +170,8 @@ static void nft_hash_walk(const struct nft_ctx *ctx, const struct nft_set *set, if (iter->count < iter->skip) goto cont; + if (!nft_set_elem_active(&he->ext, genmask)) + goto cont; elem.priv = he; @@ -241,8 +258,9 @@ static struct nft_set_ops nft_hash_ops __read_mostly = { .estimate = nft_hash_estimate, .init = nft_hash_init, .destroy = nft_hash_destroy, - .get = nft_hash_get, .insert = nft_hash_insert, + .activate = nft_hash_activate, + .deactivate = nft_hash_deactivate, .remove = nft_hash_remove, .lookup = nft_hash_lookup, .walk = nft_hash_walk, diff --git a/net/netfilter/nft_rbtree.c b/net/netfilter/nft_rbtree.c index cbba755ebebc..42d0ca45fb9e 100644 --- a/net/netfilter/nft_rbtree.c +++ b/net/netfilter/nft_rbtree.c @@ -29,6 +29,7 @@ struct nft_rbtree_elem { struct nft_set_ext ext; }; + static bool nft_rbtree_lookup(const struct nft_set *set, const struct nft_data *key, const struct nft_set_ext **ext) @@ -36,6 +37,7 @@ static bool nft_rbtree_lookup(const struct nft_set *set, const struct nft_rbtree *priv = nft_set_priv(set); const struct nft_rbtree_elem *rbe, *interval = NULL; const struct rb_node *parent; + u8 genmask = nft_genmask_cur(read_pnet(&set->pnet)); int d; spin_lock_bh(&nft_rbtree_lock); @@ -51,6 +53,10 @@ static bool nft_rbtree_lookup(const struct nft_set *set, parent = parent->rb_right; else { found: + if (!nft_set_elem_active(&rbe->ext, genmask)) { + parent = parent->rb_left; + continue; + } if (nft_set_ext_exists(&rbe->ext, NFT_SET_EXT_FLAGS) && *nft_set_ext_flags(&rbe->ext) & NFT_SET_ELEM_INTERVAL_END) @@ -77,6 +83,7 @@ static int __nft_rbtree_insert(const struct nft_set *set, struct nft_rbtree *priv = nft_set_priv(set); struct nft_rbtree_elem *rbe; struct rb_node *parent, **p; + u8 genmask = nft_genmask_next(read_pnet(&set->pnet)); int d; parent = NULL; @@ -91,8 +98,11 @@ static int __nft_rbtree_insert(const struct nft_set *set, p = &parent->rb_left; else if (d > 0) p = &parent->rb_right; - else - return -EEXIST; + else { + if (nft_set_elem_active(&rbe->ext, genmask)) + return -EEXIST; + p = &parent->rb_left; + } } rb_link_node(&new->node, parent, p); rb_insert_color(&new->node, &priv->root); @@ -116,18 +126,28 @@ static void nft_rbtree_remove(const struct nft_set *set, const struct nft_set_elem *elem) { struct nft_rbtree *priv = nft_set_priv(set); - struct nft_rbtree_elem *rbe = elem->cookie; + struct nft_rbtree_elem *rbe = elem->priv; spin_lock_bh(&nft_rbtree_lock); rb_erase(&rbe->node, &priv->root); spin_unlock_bh(&nft_rbtree_lock); } -static int nft_rbtree_get(const struct nft_set *set, struct nft_set_elem *elem) +static void nft_rbtree_activate(const struct nft_set *set, + const struct nft_set_elem *elem) +{ + struct nft_rbtree_elem *rbe = elem->priv; + + nft_set_elem_change_active(set, &rbe->ext); +} + +static void *nft_rbtree_deactivate(const struct nft_set *set, + const struct nft_set_elem *elem) { const struct nft_rbtree *priv = nft_set_priv(set); const struct rb_node *parent = priv->root.rb_node; struct nft_rbtree_elem *rbe; + u8 genmask = nft_genmask_cur(read_pnet(&set->pnet)); int d; while (parent != NULL) { @@ -140,12 +160,15 @@ static int nft_rbtree_get(const struct nft_set *set, struct nft_set_elem *elem) else if (d > 0) parent = parent->rb_right; else { - elem->cookie = rbe; - elem->priv = rbe; - return 0; + if (!nft_set_elem_active(&rbe->ext, genmask)) { + parent = parent->rb_left; + continue; + } + nft_set_elem_change_active(set, &rbe->ext); + return rbe; } } - return -ENOENT; + return NULL; } static void nft_rbtree_walk(const struct nft_ctx *ctx, @@ -156,13 +179,17 @@ static void nft_rbtree_walk(const struct nft_ctx *ctx, struct nft_rbtree_elem *rbe; struct nft_set_elem elem; struct rb_node *node; + u8 genmask = nft_genmask_cur(read_pnet(&set->pnet)); spin_lock_bh(&nft_rbtree_lock); for (node = rb_first(&priv->root); node != NULL; node = rb_next(node)) { + rbe = rb_entry(node, struct nft_rbtree_elem, node); + if (iter->count < iter->skip) goto cont; + if (!nft_set_elem_active(&rbe->ext, genmask)) + goto cont; - rbe = rb_entry(node, struct nft_rbtree_elem, node); elem.priv = rbe; iter->err = iter->fn(ctx, set, iter, &elem); @@ -228,7 +255,8 @@ static struct nft_set_ops nft_rbtree_ops __read_mostly = { .destroy = nft_rbtree_destroy, .insert = nft_rbtree_insert, .remove = nft_rbtree_remove, - .get = nft_rbtree_get, + .deactivate = nft_rbtree_deactivate, + .activate = nft_rbtree_activate, .lookup = nft_rbtree_lookup, .walk = nft_rbtree_walk, .features = NFT_SET_INTERVAL | NFT_SET_MAP, -- cgit v1.2.3 From 959801fef94b7ee66ea2c713229637a7e1770890 Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Tue, 24 Feb 2015 14:25:00 -0500 Subject: serial: core: Add minor field to uart_port UART drivers that share ttyS namespace cannot trivially compute the ttyS index from the port->line value since the minor_start may be offset from minor 64. Further, to do so requires a pointer to the uart driver since there is no back pointer from uart_port to uart_driver. Rather than have UART drivers computing the minor value by themselves, encapsulate within the serial core at port registration time. Signed-off-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/serial_core.c | 1 + include/linux/serial_core.h | 1 + 2 files changed, 2 insertions(+) (limited to 'include') diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c index 8379e3fa0162..a373eff44ae8 100644 --- a/drivers/tty/serial/serial_core.c +++ b/drivers/tty/serial/serial_core.c @@ -2682,6 +2682,7 @@ int uart_add_one_port(struct uart_driver *drv, struct uart_port *uport) state->pm_state = UART_PM_STATE_UNDEFINED; uport->cons = drv->cons; + uport->minor = drv->tty_driver->minor_start + uport->line; /* * If this port is a console, then the spinlock is already diff --git a/include/linux/serial_core.h b/include/linux/serial_core.h index b0148e7bcbfa..980170e5a982 100644 --- a/include/linux/serial_core.h +++ b/include/linux/serial_core.h @@ -235,6 +235,7 @@ struct uart_port { const struct uart_ops *ops; unsigned int custom_divisor; unsigned int line; /* port index */ + unsigned int minor; resource_size_t mapbase; /* for ioremap */ struct device *dev; /* parent device */ unsigned char hub6; /* this should be in the 8250 driver */ -- cgit v1.2.3 From 828aef376d7a129547bc4ebb949965040177e3da Mon Sep 17 00:00:00 2001 From: Catalin Marinas Date: Tue, 24 Mar 2015 14:02:46 +0000 Subject: ACPI / processor: Introduce phys_cpuid_t for CPU hardware ID CPU hardware ID (phys_id) is defined as u32 in structure acpi_processor, but phys_id is used as int in acpi processor driver, so it will lead to some inconsistence for the drivers. Furthermore, to cater for ACPI arch ports that implement 64 bits CPU ids a generic CPU physical id type is required. So introduce typedef u32 phys_cpuid_t in a common file, and introduce a macro PHYS_CPUID_INVALID as (phys_cpuid_t)(-1) if it's not defined by other archs, this will solve the inconsistence in acpi processor driver, and will prepare for the ACPI on ARM64 for the 64 bit CPU hardware ID in the following patch. CC: Rafael J Wysocki Suggested-by: Lorenzo Pieralisi Reviewed-by: Grant Likely Acked-by: Sudeep Holla Acked-by: Lorenzo Pieralisi Acked-by: Rafael J. Wysocki Signed-off-by: Catalin Marinas [hj: reworked cpu physid map return codes] Signed-off-by: Hanjun Guo Signed-off-by: Will Deacon --- arch/ia64/kernel/acpi.c | 2 +- arch/x86/kernel/acpi/boot.c | 2 +- drivers/acpi/acpi_processor.c | 7 ++++--- drivers/acpi/processor_core.c | 30 +++++++++++++++--------------- include/acpi/processor.h | 6 +++--- include/linux/acpi.h | 7 ++++++- 6 files changed, 30 insertions(+), 24 deletions(-) (limited to 'include') diff --git a/arch/ia64/kernel/acpi.c b/arch/ia64/kernel/acpi.c index 2c4498919d3c..067ef4439fa4 100644 --- a/arch/ia64/kernel/acpi.c +++ b/arch/ia64/kernel/acpi.c @@ -887,7 +887,7 @@ static int _acpi_map_lsapic(acpi_handle handle, int physid, int *pcpu) } /* wrapper to silence section mismatch warning */ -int __ref acpi_map_cpu(acpi_handle handle, int physid, int *pcpu) +int __ref acpi_map_cpu(acpi_handle handle, phys_cpuid_t physid, int *pcpu) { return _acpi_map_lsapic(handle, physid, pcpu); } diff --git a/arch/x86/kernel/acpi/boot.c b/arch/x86/kernel/acpi/boot.c index 3d525c6124f6..e4f8582eb756 100644 --- a/arch/x86/kernel/acpi/boot.c +++ b/arch/x86/kernel/acpi/boot.c @@ -757,7 +757,7 @@ static int _acpi_map_lsapic(acpi_handle handle, int physid, int *pcpu) } /* wrapper to silence section mismatch warning */ -int __ref acpi_map_cpu(acpi_handle handle, int physid, int *pcpu) +int __ref acpi_map_cpu(acpi_handle handle, phys_cpuid_t physid, int *pcpu) { return _acpi_map_lsapic(handle, physid, pcpu); } diff --git a/drivers/acpi/acpi_processor.c b/drivers/acpi/acpi_processor.c index 1020b1b53a17..58f335ca2e75 100644 --- a/drivers/acpi/acpi_processor.c +++ b/drivers/acpi/acpi_processor.c @@ -170,7 +170,7 @@ static int acpi_processor_hotadd_init(struct acpi_processor *pr) acpi_status status; int ret; - if (pr->phys_id == -1) + if (pr->phys_id == PHYS_CPUID_INVALID) return -ENODEV; status = acpi_evaluate_integer(pr->handle, "_STA", NULL, &sta); @@ -215,7 +215,8 @@ static int acpi_processor_get_info(struct acpi_device *device) union acpi_object object = { 0 }; struct acpi_buffer buffer = { sizeof(union acpi_object), &object }; struct acpi_processor *pr = acpi_driver_data(device); - int phys_id, cpu_index, device_declaration = 0; + phys_cpuid_t phys_id; + int cpu_index, device_declaration = 0; acpi_status status = AE_OK; static int cpu0_initialized; unsigned long long value; @@ -263,7 +264,7 @@ static int acpi_processor_get_info(struct acpi_device *device) } phys_id = acpi_get_phys_id(pr->handle, device_declaration, pr->acpi_id); - if (phys_id < 0) + if (phys_id == PHYS_CPUID_INVALID) acpi_handle_debug(pr->handle, "failed to get CPU physical ID.\n"); pr->phys_id = phys_id; diff --git a/drivers/acpi/processor_core.c b/drivers/acpi/processor_core.c index 7962651cdbd4..51cc29909e08 100644 --- a/drivers/acpi/processor_core.c +++ b/drivers/acpi/processor_core.c @@ -32,7 +32,7 @@ static struct acpi_table_madt *get_madt_table(void) } static int map_lapic_id(struct acpi_subtable_header *entry, - u32 acpi_id, int *apic_id) + u32 acpi_id, phys_cpuid_t *apic_id) { struct acpi_madt_local_apic *lapic = container_of(entry, struct acpi_madt_local_apic, header); @@ -48,7 +48,7 @@ static int map_lapic_id(struct acpi_subtable_header *entry, } static int map_x2apic_id(struct acpi_subtable_header *entry, - int device_declaration, u32 acpi_id, int *apic_id) + int device_declaration, u32 acpi_id, phys_cpuid_t *apic_id) { struct acpi_madt_local_x2apic *apic = container_of(entry, struct acpi_madt_local_x2apic, header); @@ -65,7 +65,7 @@ static int map_x2apic_id(struct acpi_subtable_header *entry, } static int map_lsapic_id(struct acpi_subtable_header *entry, - int device_declaration, u32 acpi_id, int *apic_id) + int device_declaration, u32 acpi_id, phys_cpuid_t *apic_id) { struct acpi_madt_local_sapic *lsapic = container_of(entry, struct acpi_madt_local_sapic, header); @@ -83,10 +83,10 @@ static int map_lsapic_id(struct acpi_subtable_header *entry, return 0; } -static int map_madt_entry(int type, u32 acpi_id) +static phys_cpuid_t map_madt_entry(int type, u32 acpi_id) { unsigned long madt_end, entry; - int phys_id = -1; /* CPU hardware ID */ + phys_cpuid_t phys_id = PHYS_CPUID_INVALID; /* CPU hardware ID */ struct acpi_table_madt *madt; madt = get_madt_table(); @@ -117,12 +117,12 @@ static int map_madt_entry(int type, u32 acpi_id) return phys_id; } -static int map_mat_entry(acpi_handle handle, int type, u32 acpi_id) +static phys_cpuid_t map_mat_entry(acpi_handle handle, int type, u32 acpi_id) { struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; union acpi_object *obj; struct acpi_subtable_header *header; - int phys_id = -1; + phys_cpuid_t phys_id = PHYS_CPUID_INVALID; if (ACPI_FAILURE(acpi_evaluate_object(handle, "_MAT", NULL, &buffer))) goto exit; @@ -149,27 +149,27 @@ exit: return phys_id; } -int acpi_get_phys_id(acpi_handle handle, int type, u32 acpi_id) +phys_cpuid_t acpi_get_phys_id(acpi_handle handle, int type, u32 acpi_id) { - int phys_id; + phys_cpuid_t phys_id; phys_id = map_mat_entry(handle, type, acpi_id); - if (phys_id == -1) + if (phys_id == PHYS_CPUID_INVALID) phys_id = map_madt_entry(type, acpi_id); return phys_id; } -int acpi_map_cpuid(int phys_id, u32 acpi_id) +int acpi_map_cpuid(phys_cpuid_t phys_id, u32 acpi_id) { #ifdef CONFIG_SMP int i; #endif - if (phys_id == -1) { + if (phys_id == PHYS_CPUID_INVALID) { /* * On UP processor, there is no _MAT or MADT table. - * So above phys_id is always set to -1. + * So above phys_id is always set to PHYS_CPUID_INVALID. * * BIOS may define multiple CPU handles even for UP processor. * For example, @@ -190,7 +190,7 @@ int acpi_map_cpuid(int phys_id, u32 acpi_id) if (nr_cpu_ids <= 1 && acpi_id == 0) return acpi_id; else - return phys_id; + return -1; } #ifdef CONFIG_SMP @@ -208,7 +208,7 @@ int acpi_map_cpuid(int phys_id, u32 acpi_id) int acpi_get_cpuid(acpi_handle handle, int type, u32 acpi_id) { - int phys_id; + phys_cpuid_t phys_id; phys_id = acpi_get_phys_id(handle, type, acpi_id); diff --git a/include/acpi/processor.h b/include/acpi/processor.h index b95dc32a6e6b..4188a4d3b597 100644 --- a/include/acpi/processor.h +++ b/include/acpi/processor.h @@ -196,7 +196,7 @@ struct acpi_processor_flags { struct acpi_processor { acpi_handle handle; u32 acpi_id; - u32 phys_id; /* CPU hardware ID such as APIC ID for x86 */ + phys_cpuid_t phys_id; /* CPU hardware ID such as APIC ID for x86 */ u32 id; /* CPU logical ID allocated by OS */ u32 pblk; int performance_platform_limit; @@ -310,8 +310,8 @@ static inline int acpi_processor_get_bios_limit(int cpu, unsigned int *limit) #endif /* CONFIG_CPU_FREQ */ /* in processor_core.c */ -int acpi_get_phys_id(acpi_handle, int type, u32 acpi_id); -int acpi_map_cpuid(int phys_id, u32 acpi_id); +phys_cpuid_t acpi_get_phys_id(acpi_handle, int type, u32 acpi_id); +int acpi_map_cpuid(phys_cpuid_t phys_id, u32 acpi_id); int acpi_get_cpuid(acpi_handle, int type, u32 acpi_id); /* in processor_pdc.c */ diff --git a/include/linux/acpi.h b/include/linux/acpi.h index 24c7aa8b1d20..6ec33c595aea 100644 --- a/include/linux/acpi.h +++ b/include/linux/acpi.h @@ -146,9 +146,14 @@ void acpi_numa_x2apic_affinity_init(struct acpi_srat_x2apic_cpu_affinity *pa); int acpi_numa_memory_affinity_init (struct acpi_srat_mem_affinity *ma); void acpi_numa_arch_fixup(void); +#ifndef PHYS_CPUID_INVALID +typedef u32 phys_cpuid_t; +#define PHYS_CPUID_INVALID (phys_cpuid_t)(-1) +#endif + #ifdef CONFIG_ACPI_HOTPLUG_CPU /* Arch dependent functions for cpu hotplug support */ -int acpi_map_cpu(acpi_handle handle, int physid, int *pcpu); +int acpi_map_cpu(acpi_handle handle, phys_cpuid_t physid, int *pcpu); int acpi_unmap_cpu(int cpu); #endif /* CONFIG_ACPI_HOTPLUG_CPU */ -- cgit v1.2.3 From fbe61ec71ac975279cd47b6c299d5e33f63aac4e Mon Sep 17 00:00:00 2001 From: Hanjun Guo Date: Tue, 24 Mar 2015 14:02:48 +0000 Subject: ARM64 / ACPI: Introduce ACPI_IRQ_MODEL_GIC and register device's gsi Introduce ACPI_IRQ_MODEL_GIC which is needed for ARM64 as GIC is used, and then register device's gsi with the core IRQ subsystem. acpi_register_gsi() is similar to DT based irq_of_parse_and_map(), since gsi is unique in the system, so use hwirq number directly for the mapping. We are going to implement stacked domains when GICv2m, GICv3, ITS support are added. CC: Marc Zyngier Originally-by: Amit Daniel Kachhap Tested-by: Suravee Suthikulpanit Tested-by: Yijing Wang Tested-by: Mark Langsdorf Tested-by: Jon Masters Tested-by: Timur Tabi Tested-by: Robert Richter Acked-by: Robert Richter Acked-by: Rafael J. Wysocki Reviewed-by: Grant Likely Signed-off-by: Hanjun Guo Signed-off-by: Will Deacon --- arch/arm64/kernel/acpi.c | 73 ++++++++++++++++++++++++++++++++++++++++++++++++ drivers/acpi/bus.c | 3 ++ include/linux/acpi.h | 1 + 3 files changed, 77 insertions(+) (limited to 'include') diff --git a/arch/arm64/kernel/acpi.c b/arch/arm64/kernel/acpi.c index c9203c0a1179..dec6f8aed3dc 100644 --- a/arch/arm64/kernel/acpi.c +++ b/arch/arm64/kernel/acpi.c @@ -75,6 +75,12 @@ static int __init dt_scan_depth1_nodes(unsigned long node, return 0; } +/* + * Since we're on ARM, the default interrupt routing model + * clearly has to be GIC. + */ +enum acpi_irq_model_id acpi_irq_model = ACPI_IRQ_MODEL_GIC; + /* * __acpi_map_table() will be called before page_init(), so early_ioremap() * or early_memremap() should be called here to for ACPI table mapping. @@ -218,6 +224,73 @@ void __init acpi_init_cpus(void) pr_info("%d CPUs enabled, %d CPUs total\n", enabled_cpus, total_cpus); } +int acpi_gsi_to_irq(u32 gsi, unsigned int *irq) +{ + *irq = irq_find_mapping(NULL, gsi); + + return 0; +} +EXPORT_SYMBOL_GPL(acpi_gsi_to_irq); + +/* + * success: return IRQ number (>0) + * failure: return =< 0 + */ +int acpi_register_gsi(struct device *dev, u32 gsi, int trigger, int polarity) +{ + unsigned int irq; + unsigned int irq_type; + + /* + * ACPI have no bindings to indicate SPI or PPI, so we + * use different mappings from DT in ACPI. + * + * For FDT + * PPI interrupt: in the range [0, 15]; + * SPI interrupt: in the range [0, 987]; + * + * For ACPI, GSI should be unique so using + * the hwirq directly for the mapping: + * PPI interrupt: in the range [16, 31]; + * SPI interrupt: in the range [32, 1019]; + */ + + if (trigger == ACPI_EDGE_SENSITIVE && + polarity == ACPI_ACTIVE_LOW) + irq_type = IRQ_TYPE_EDGE_FALLING; + else if (trigger == ACPI_EDGE_SENSITIVE && + polarity == ACPI_ACTIVE_HIGH) + irq_type = IRQ_TYPE_EDGE_RISING; + else if (trigger == ACPI_LEVEL_SENSITIVE && + polarity == ACPI_ACTIVE_LOW) + irq_type = IRQ_TYPE_LEVEL_LOW; + else if (trigger == ACPI_LEVEL_SENSITIVE && + polarity == ACPI_ACTIVE_HIGH) + irq_type = IRQ_TYPE_LEVEL_HIGH; + else + irq_type = IRQ_TYPE_NONE; + + /* + * Since only one GIC is supported in ACPI 5.0, we can + * create mapping refer to the default domain + */ + irq = irq_create_mapping(NULL, gsi); + if (!irq) + return irq; + + /* Set irq type if specified and different than the current one */ + if (irq_type != IRQ_TYPE_NONE && + irq_type != irq_get_trigger_type(irq)) + irq_set_irq_type(irq, irq_type); + return irq; +} +EXPORT_SYMBOL_GPL(acpi_register_gsi); + +void acpi_unregister_gsi(u32 gsi) +{ +} +EXPORT_SYMBOL_GPL(acpi_unregister_gsi); + static int __init acpi_parse_fadt(struct acpi_table_header *table) { struct acpi_table_fadt *fadt = (struct acpi_table_fadt *)table; diff --git a/drivers/acpi/bus.c b/drivers/acpi/bus.c index 8b67bd0f6bb5..c412fdb28d34 100644 --- a/drivers/acpi/bus.c +++ b/drivers/acpi/bus.c @@ -448,6 +448,9 @@ static int __init acpi_bus_init_irq(void) case ACPI_IRQ_MODEL_IOSAPIC: message = "IOSAPIC"; break; + case ACPI_IRQ_MODEL_GIC: + message = "GIC"; + break; case ACPI_IRQ_MODEL_PLATFORM: message = "platform specific model"; break; diff --git a/include/linux/acpi.h b/include/linux/acpi.h index 6ec33c595aea..de4e86f89c83 100644 --- a/include/linux/acpi.h +++ b/include/linux/acpi.h @@ -73,6 +73,7 @@ enum acpi_irq_model_id { ACPI_IRQ_MODEL_IOAPIC, ACPI_IRQ_MODEL_IOSAPIC, ACPI_IRQ_MODEL_PLATFORM, + ACPI_IRQ_MODEL_GIC, ACPI_IRQ_MODEL_COUNT }; -- cgit v1.2.3 From d60fc3892c4de4a25658786f941690462c5a5bab Mon Sep 17 00:00:00 2001 From: Tomasz Nowicki Date: Tue, 24 Mar 2015 14:02:49 +0000 Subject: irqchip: Add GICv2 specific ACPI boot support ACPI kernel uses MADT table for proper GIC initialization. It needs to parse GIC related subtables, collect CPU interface and distributor addresses and call driver initialization function (which is hardware abstraction agnostic). In a similar way, FDT initialize GICv1/2. NOTE: This commit allow to initialize GICv1/2 basic functionality. While now simple GICv2 init call is used, any further GIC features require generic infrastructure for proper ACPI irqchip initialization. That mechanism and stacked irqdomains to support GICv2 MSI/virtualization extension, GICv3/4 and its ITS are considered as next steps. CC: Jason Cooper CC: Marc Zyngier CC: Thomas Gleixner Tested-by: Suravee Suthikulpanit Tested-by: Yijing Wang Tested-by: Mark Langsdorf Tested-by: Jon Masters Tested-by: Timur Tabi Tested-by: Robert Richter Acked-by: Robert Richter Acked-by: Marc Zyngier Acked-by: Jason Cooper Reviewed-by: Grant Likely Signed-off-by: Tomasz Nowicki Signed-off-by: Hanjun Guo Signed-off-by: Will Deacon --- arch/arm64/include/asm/acpi.h | 2 + arch/arm64/include/asm/irq.h | 13 +++++ arch/arm64/kernel/acpi.c | 25 +++++++++ drivers/irqchip/irq-gic.c | 102 +++++++++++++++++++++++++++++++++++ drivers/irqchip/irqchip.c | 3 ++ include/linux/acpi_irq.h | 10 ++++ include/linux/irqchip/arm-gic-acpi.h | 31 +++++++++++ 7 files changed, 186 insertions(+) create mode 100644 include/linux/acpi_irq.h create mode 100644 include/linux/irqchip/arm-gic-acpi.h (limited to 'include') diff --git a/arch/arm64/include/asm/acpi.h b/arch/arm64/include/asm/acpi.h index eea0bc3b09d5..59c05d8ea4a0 100644 --- a/arch/arm64/include/asm/acpi.h +++ b/arch/arm64/include/asm/acpi.h @@ -13,6 +13,8 @@ #define _ASM_ACPI_H #include +#include + #include #include diff --git a/arch/arm64/include/asm/irq.h b/arch/arm64/include/asm/irq.h index 94c53674a31d..bbb251b14746 100644 --- a/arch/arm64/include/asm/irq.h +++ b/arch/arm64/include/asm/irq.h @@ -1,6 +1,8 @@ #ifndef __ASM_IRQ_H #define __ASM_IRQ_H +#include + #include struct pt_regs; @@ -8,4 +10,15 @@ struct pt_regs; extern void migrate_irqs(void); extern void set_handle_irq(void (*handle_irq)(struct pt_regs *)); +static inline void acpi_irq_init(void) +{ + /* + * Hardcode ACPI IRQ chip initialization to GICv2 for now. + * Proper irqchip infrastructure will be implemented along with + * incoming GICv2m|GICv3|ITS bits. + */ + acpi_gic_init(); +} +#define acpi_irq_init acpi_irq_init + #endif diff --git a/arch/arm64/kernel/acpi.c b/arch/arm64/kernel/acpi.c index dec6f8aed3dc..6468f8898530 100644 --- a/arch/arm64/kernel/acpi.c +++ b/arch/arm64/kernel/acpi.c @@ -359,3 +359,28 @@ void __init acpi_boot_table_init(void) pr_err("Can't find FADT\n"); } } + +void __init acpi_gic_init(void) +{ + struct acpi_table_header *table; + acpi_status status; + acpi_size tbl_size; + int err; + + if (acpi_disabled) + return; + + status = acpi_get_table_with_size(ACPI_SIG_MADT, 0, &table, &tbl_size); + if (ACPI_FAILURE(status)) { + const char *msg = acpi_format_exception(status); + + pr_err("Failed to get MADT table, %s\n", msg); + return; + } + + err = gic_v2_acpi_init(table); + if (err) + pr_err("Failed to initialize GIC IRQ controller"); + + early_acpi_os_unmap_memory((char *)table, tbl_size); +} diff --git a/drivers/irqchip/irq-gic.c b/drivers/irqchip/irq-gic.c index 471e1cdc1933..d15a36a93c52 100644 --- a/drivers/irqchip/irq-gic.c +++ b/drivers/irqchip/irq-gic.c @@ -33,12 +33,14 @@ #include #include #include +#include #include #include #include #include #include #include +#include #include #include @@ -1090,3 +1092,103 @@ IRQCHIP_DECLARE(msm_8660_qgic, "qcom,msm-8660-qgic", gic_of_init); IRQCHIP_DECLARE(msm_qgic2, "qcom,msm-qgic2", gic_of_init); #endif + +#ifdef CONFIG_ACPI +static phys_addr_t dist_phy_base, cpu_phy_base __initdata; + +static int __init +gic_acpi_parse_madt_cpu(struct acpi_subtable_header *header, + const unsigned long end) +{ + struct acpi_madt_generic_interrupt *processor; + phys_addr_t gic_cpu_base; + static int cpu_base_assigned; + + processor = (struct acpi_madt_generic_interrupt *)header; + + if (BAD_MADT_ENTRY(processor, end)) + return -EINVAL; + + /* + * There is no support for non-banked GICv1/2 register in ACPI spec. + * All CPU interface addresses have to be the same. + */ + gic_cpu_base = processor->base_address; + if (cpu_base_assigned && gic_cpu_base != cpu_phy_base) + return -EINVAL; + + cpu_phy_base = gic_cpu_base; + cpu_base_assigned = 1; + return 0; +} + +static int __init +gic_acpi_parse_madt_distributor(struct acpi_subtable_header *header, + const unsigned long end) +{ + struct acpi_madt_generic_distributor *dist; + + dist = (struct acpi_madt_generic_distributor *)header; + + if (BAD_MADT_ENTRY(dist, end)) + return -EINVAL; + + dist_phy_base = dist->base_address; + return 0; +} + +int __init +gic_v2_acpi_init(struct acpi_table_header *table) +{ + void __iomem *cpu_base, *dist_base; + int count; + + /* Collect CPU base addresses */ + count = acpi_parse_entries(ACPI_SIG_MADT, + sizeof(struct acpi_table_madt), + gic_acpi_parse_madt_cpu, table, + ACPI_MADT_TYPE_GENERIC_INTERRUPT, 0); + if (count <= 0) { + pr_err("No valid GICC entries exist\n"); + return -EINVAL; + } + + /* + * Find distributor base address. We expect one distributor entry since + * ACPI 5.1 spec neither support multi-GIC instances nor GIC cascade. + */ + count = acpi_parse_entries(ACPI_SIG_MADT, + sizeof(struct acpi_table_madt), + gic_acpi_parse_madt_distributor, table, + ACPI_MADT_TYPE_GENERIC_DISTRIBUTOR, 0); + if (count <= 0) { + pr_err("No valid GICD entries exist\n"); + return -EINVAL; + } else if (count > 1) { + pr_err("More than one GICD entry detected\n"); + return -EINVAL; + } + + cpu_base = ioremap(cpu_phy_base, ACPI_GIC_CPU_IF_MEM_SIZE); + if (!cpu_base) { + pr_err("Unable to map GICC registers\n"); + return -ENOMEM; + } + + dist_base = ioremap(dist_phy_base, ACPI_GICV2_DIST_MEM_SIZE); + if (!dist_base) { + pr_err("Unable to map GICD registers\n"); + iounmap(cpu_base); + return -ENOMEM; + } + + /* + * Initialize zero GIC instance (no multi-GIC support). Also, set GIC + * as default IRQ domain to allow for GSI registration and GSI to IRQ + * number translation (see acpi_register_gsi() and acpi_gsi_to_irq()). + */ + gic_init_bases(0, -1, dist_base, cpu_base, 0, NULL); + irq_set_default_host(gic_data[0].domain); + return 0; +} +#endif diff --git a/drivers/irqchip/irqchip.c b/drivers/irqchip/irqchip.c index 0fe2f718d81c..afd1af3dfe5a 100644 --- a/drivers/irqchip/irqchip.c +++ b/drivers/irqchip/irqchip.c @@ -8,6 +8,7 @@ * warranty of any kind, whether express or implied. */ +#include #include #include #include @@ -26,4 +27,6 @@ extern struct of_device_id __irqchip_of_table[]; void __init irqchip_init(void) { of_irq_init(__irqchip_of_table); + + acpi_irq_init(); } diff --git a/include/linux/acpi_irq.h b/include/linux/acpi_irq.h new file mode 100644 index 000000000000..f10c87265855 --- /dev/null +++ b/include/linux/acpi_irq.h @@ -0,0 +1,10 @@ +#ifndef _LINUX_ACPI_IRQ_H +#define _LINUX_ACPI_IRQ_H + +#include + +#ifndef acpi_irq_init +static inline void acpi_irq_init(void) { } +#endif + +#endif /* _LINUX_ACPI_IRQ_H */ diff --git a/include/linux/irqchip/arm-gic-acpi.h b/include/linux/irqchip/arm-gic-acpi.h new file mode 100644 index 000000000000..de3419ed3937 --- /dev/null +++ b/include/linux/irqchip/arm-gic-acpi.h @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2014, Linaro Ltd. + * Author: Tomasz Nowicki + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef ARM_GIC_ACPI_H_ +#define ARM_GIC_ACPI_H_ + +#ifdef CONFIG_ACPI + +/* + * Hard code here, we can not get memory size from MADT (but FDT does), + * Actually no need to do that, because this size can be inferred + * from GIC spec. + */ +#define ACPI_GICV2_DIST_MEM_SIZE (SZ_4K) +#define ACPI_GIC_CPU_IF_MEM_SIZE (SZ_8K) + +struct acpi_table_header; + +int gic_v2_acpi_init(struct acpi_table_header *table); +void acpi_gic_init(void); +#else +static inline void acpi_gic_init(void) { } +#endif + +#endif /* ARM_GIC_ACPI_H_ */ -- cgit v1.2.3 From b09ca1ecf6d499d5a33f978c905d2fbcc79b55d9 Mon Sep 17 00:00:00 2001 From: Hanjun Guo Date: Tue, 24 Mar 2015 14:02:50 +0000 Subject: clocksource / arch_timer: Parse GTDT to initialize arch timer Using the information presented by GTDT (Generic Timer Description Table) to initialize the arch timer (not memory-mapped). CC: Daniel Lezcano CC: Thomas Gleixner Originally-by: Amit Daniel Kachhap Tested-by: Suravee Suthikulpanit Tested-by: Yijing Wang Tested-by: Mark Langsdorf Tested-by: Jon Masters Tested-by: Timur Tabi Tested-by: Robert Richter Acked-by: Robert Richter Acked-by: Daniel Lezcano Reviewed-by: Grant Likely Signed-off-by: Hanjun Guo Signed-off-by: Will Deacon --- arch/arm64/kernel/time.c | 7 ++ drivers/clocksource/arm_arch_timer.c | 132 ++++++++++++++++++++++++++++------- include/linux/clocksource.h | 6 ++ 3 files changed, 118 insertions(+), 27 deletions(-) (limited to 'include') diff --git a/arch/arm64/kernel/time.c b/arch/arm64/kernel/time.c index 1a7125c3099b..42f9195cf2f8 100644 --- a/arch/arm64/kernel/time.c +++ b/arch/arm64/kernel/time.c @@ -35,6 +35,7 @@ #include #include #include +#include #include @@ -72,6 +73,12 @@ void __init time_init(void) tick_setup_hrtimer_broadcast(); + /* + * Since ACPI or FDT will only one be available in the system, + * we can use acpi_generic_timer_init() here safely + */ + acpi_generic_timer_init(); + arch_timer_rate = arch_timer_get_rate(); if (!arch_timer_rate) panic("Unable to initialise architected timer.\n"); diff --git a/drivers/clocksource/arm_arch_timer.c b/drivers/clocksource/arm_arch_timer.c index a3025e7ae35f..ea62fc79e1e8 100644 --- a/drivers/clocksource/arm_arch_timer.c +++ b/drivers/clocksource/arm_arch_timer.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include @@ -371,8 +372,12 @@ arch_timer_detect_rate(void __iomem *cntbase, struct device_node *np) if (arch_timer_rate) return; - /* Try to determine the frequency from the device tree or CNTFRQ */ - if (of_property_read_u32(np, "clock-frequency", &arch_timer_rate)) { + /* + * Try to determine the frequency from the device tree or CNTFRQ, + * if ACPI is enabled, get the frequency from CNTFRQ ONLY. + */ + if (!acpi_disabled || + of_property_read_u32(np, "clock-frequency", &arch_timer_rate)) { if (cntbase) arch_timer_rate = readl_relaxed(cntbase + CNTFRQ); else @@ -691,28 +696,8 @@ static void __init arch_timer_common_init(void) arch_timer_arch_init(); } -static void __init arch_timer_init(struct device_node *np) +static void __init arch_timer_init(void) { - int i; - - if (arch_timers_present & ARCH_CP15_TIMER) { - pr_warn("arch_timer: multiple nodes in dt, skipping\n"); - return; - } - - arch_timers_present |= ARCH_CP15_TIMER; - for (i = PHYS_SECURE_PPI; i < MAX_TIMER_PPI; i++) - arch_timer_ppi[i] = irq_of_parse_and_map(np, i); - arch_timer_detect_rate(NULL, np); - - /* - * If we cannot rely on firmware initializing the timer registers then - * we should use the physical timers instead. - */ - if (IS_ENABLED(CONFIG_ARM) && - of_property_read_bool(np, "arm,cpu-registers-not-fw-configured")) - arch_timer_use_virtual = false; - /* * If HYP mode is available, we know that the physical timer * has been configured to be accessible from PL1. Use it, so @@ -731,13 +716,39 @@ static void __init arch_timer_init(struct device_node *np) } } - arch_timer_c3stop = !of_property_read_bool(np, "always-on"); - arch_timer_register(); arch_timer_common_init(); } -CLOCKSOURCE_OF_DECLARE(armv7_arch_timer, "arm,armv7-timer", arch_timer_init); -CLOCKSOURCE_OF_DECLARE(armv8_arch_timer, "arm,armv8-timer", arch_timer_init); + +static void __init arch_timer_of_init(struct device_node *np) +{ + int i; + + if (arch_timers_present & ARCH_CP15_TIMER) { + pr_warn("arch_timer: multiple nodes in dt, skipping\n"); + return; + } + + arch_timers_present |= ARCH_CP15_TIMER; + for (i = PHYS_SECURE_PPI; i < MAX_TIMER_PPI; i++) + arch_timer_ppi[i] = irq_of_parse_and_map(np, i); + + arch_timer_detect_rate(NULL, np); + + arch_timer_c3stop = !of_property_read_bool(np, "always-on"); + + /* + * If we cannot rely on firmware initializing the timer registers then + * we should use the physical timers instead. + */ + if (IS_ENABLED(CONFIG_ARM) && + of_property_read_bool(np, "arm,cpu-registers-not-fw-configured")) + arch_timer_use_virtual = false; + + arch_timer_init(); +} +CLOCKSOURCE_OF_DECLARE(armv7_arch_timer, "arm,armv7-timer", arch_timer_of_init); +CLOCKSOURCE_OF_DECLARE(armv8_arch_timer, "arm,armv8-timer", arch_timer_of_init); static void __init arch_timer_mem_init(struct device_node *np) { @@ -804,3 +815,70 @@ static void __init arch_timer_mem_init(struct device_node *np) } CLOCKSOURCE_OF_DECLARE(armv7_arch_timer_mem, "arm,armv7-timer-mem", arch_timer_mem_init); + +#ifdef CONFIG_ACPI +static int __init map_generic_timer_interrupt(u32 interrupt, u32 flags) +{ + int trigger, polarity; + + if (!interrupt) + return 0; + + trigger = (flags & ACPI_GTDT_INTERRUPT_MODE) ? ACPI_EDGE_SENSITIVE + : ACPI_LEVEL_SENSITIVE; + + polarity = (flags & ACPI_GTDT_INTERRUPT_POLARITY) ? ACPI_ACTIVE_LOW + : ACPI_ACTIVE_HIGH; + + return acpi_register_gsi(NULL, interrupt, trigger, polarity); +} + +/* Initialize per-processor generic timer */ +static int __init arch_timer_acpi_init(struct acpi_table_header *table) +{ + struct acpi_table_gtdt *gtdt; + + if (arch_timers_present & ARCH_CP15_TIMER) { + pr_warn("arch_timer: already initialized, skipping\n"); + return -EINVAL; + } + + gtdt = container_of(table, struct acpi_table_gtdt, header); + + arch_timers_present |= ARCH_CP15_TIMER; + + arch_timer_ppi[PHYS_SECURE_PPI] = + map_generic_timer_interrupt(gtdt->secure_el1_interrupt, + gtdt->secure_el1_flags); + + arch_timer_ppi[PHYS_NONSECURE_PPI] = + map_generic_timer_interrupt(gtdt->non_secure_el1_interrupt, + gtdt->non_secure_el1_flags); + + arch_timer_ppi[VIRT_PPI] = + map_generic_timer_interrupt(gtdt->virtual_timer_interrupt, + gtdt->virtual_timer_flags); + + arch_timer_ppi[HYP_PPI] = + map_generic_timer_interrupt(gtdt->non_secure_el2_interrupt, + gtdt->non_secure_el2_flags); + + /* Get the frequency from CNTFRQ */ + arch_timer_detect_rate(NULL, NULL); + + /* Always-on capability */ + arch_timer_c3stop = !(gtdt->non_secure_el1_flags & ACPI_GTDT_ALWAYS_ON); + + arch_timer_init(); + return 0; +} + +/* Initialize all the generic timers presented in GTDT */ +void __init acpi_generic_timer_init(void) +{ + if (acpi_disabled) + return; + + acpi_table_parse(ACPI_SIG_GTDT, arch_timer_acpi_init); +} +#endif diff --git a/include/linux/clocksource.h b/include/linux/clocksource.h index 9c78d15d33e4..2b2e1f80c519 100644 --- a/include/linux/clocksource.h +++ b/include/linux/clocksource.h @@ -244,4 +244,10 @@ extern void clocksource_of_init(void); static inline void clocksource_of_init(void) {} #endif +#ifdef CONFIG_ACPI +void acpi_generic_timer_init(void); +#else +static inline void acpi_generic_timer_init(void) { } +#endif + #endif /* _LINUX_CLOCKSOURCE_H */ -- cgit v1.2.3 From c7cef0a84912cab3c9df8949b034e4aa62982ec9 Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Mon, 9 Mar 2015 16:27:12 -0400 Subject: console: Add extensible console matching Add match() method to struct console which allows the console to perform console command line matching instead of (or in addition to) default console matching (ie., by fixed name and index). The match() method returns 0 to indicate a successful match; normal console matching occurs if no match() method is defined or the match() method returns non-zero. The match() method is expected to set the console index if required. Re-implement earlycon-to-console-handoff with direct matching of "console=uart|uart8250,..." to the 8250 ttyS console. Acked-by: Rob Herring Signed-off-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_core.c | 64 +++++++++++++++++++++++++++--------- drivers/tty/serial/8250/8250_early.c | 23 ------------- include/linux/console.h | 3 +- include/linux/serial_8250.h | 2 -- kernel/printk/printk.c | 52 ++++++++++------------------- 5 files changed, 67 insertions(+), 77 deletions(-) (limited to 'include') diff --git a/drivers/tty/serial/8250/8250_core.c b/drivers/tty/serial/8250/8250_core.c index bd06ab790c64..924ee1e13828 100644 --- a/drivers/tty/serial/8250/8250_core.c +++ b/drivers/tty/serial/8250/8250_core.c @@ -3322,9 +3322,54 @@ static int serial8250_console_setup(struct console *co, char *options) return uart_set_options(port, co, baud, parity, bits, flow); } -static int serial8250_console_early_setup(void) +/** + * serial8250_console_match - non-standard console matching + * @co: registering console + * @name: name from console command line + * @idx: index from console command line + * @options: ptr to option string from console command line + * + * Only attempts to match console command lines of the form: + * console=uart<>,io|mmio|mmio32,, + * console=uart<>,,options + * This form is used to register an initial earlycon boot console and + * replace it with the serial8250_console at 8250 driver init. + * + * Performs console setup for a match (as required by interface) + * + * Returns 0 if console matches; otherwise non-zero to use default matching + */ +static int serial8250_console_match(struct console *co, char *name, int idx, + char *options) { - return serial8250_find_port_for_earlycon(); + char match[] = "uart"; /* 8250-specific earlycon name */ + unsigned char iotype; + unsigned long addr; + int i; + + if (strncmp(name, match, 4) != 0) + return -ENODEV; + + if (uart_parse_earlycon(options, &iotype, &addr, &options)) + return -ENODEV; + + /* try to match the port specified on the command line */ + for (i = 0; i < nr_uarts; i++) { + struct uart_port *port = &serial8250_ports[i].port; + + if (port->iotype != iotype) + continue; + if ((iotype == UPIO_MEM || iotype == UPIO_MEM32) && + (port->mapbase != addr)) + continue; + if (iotype == UPIO_PORT && port->iobase != addr) + continue; + + co->index = i; + return serial8250_console_setup(co, options); + } + + return -ENODEV; } static struct console serial8250_console = { @@ -3332,7 +3377,7 @@ static struct console serial8250_console = { .write = serial8250_console_write, .device = uart_console_device, .setup = serial8250_console_setup, - .early_setup = serial8250_console_early_setup, + .match = serial8250_console_match, .flags = CON_PRINTBUFFER | CON_ANYTIME, .index = -1, .data = &serial8250_reg, @@ -3346,19 +3391,6 @@ static int __init serial8250_console_init(void) } console_initcall(serial8250_console_init); -int serial8250_find_port(struct uart_port *p) -{ - int line; - struct uart_port *port; - - for (line = 0; line < nr_uarts; line++) { - port = &serial8250_ports[line].port; - if (uart_match_port(p, port)) - return line; - } - return -ENODEV; -} - #define SERIAL8250_CONSOLE &serial8250_console #else #define SERIAL8250_CONSOLE NULL diff --git a/drivers/tty/serial/8250/8250_early.c b/drivers/tty/serial/8250/8250_early.c index c31a22b4f845..49bca65057e6 100644 --- a/drivers/tty/serial/8250/8250_early.c +++ b/drivers/tty/serial/8250/8250_early.c @@ -173,26 +173,3 @@ int __init setup_early_serial8250_console(char *cmdline) return setup_earlycon(cmdline, match, early_serial8250_setup); } - -int serial8250_find_port_for_earlycon(void) -{ - struct earlycon_device *device = early_device; - struct uart_port *port = device ? &device->port : NULL; - int line; - int ret; - - if (!port || (!port->membase && !port->iobase)) - return -ENODEV; - - line = serial8250_find_port(port); - if (line < 0) - return -ENODEV; - - ret = update_console_cmdline("uart", 8250, - "ttyS", line, device->options); - if (ret < 0) - ret = update_console_cmdline("uart", 0, - "ttyS", line, device->options); - - return ret; -} diff --git a/include/linux/console.h b/include/linux/console.h index 7571a16bd653..9f50fb413c11 100644 --- a/include/linux/console.h +++ b/include/linux/console.h @@ -123,7 +123,7 @@ struct console { struct tty_driver *(*device)(struct console *, int *); void (*unblank)(void); int (*setup)(struct console *, char *); - int (*early_setup)(void); + int (*match)(struct console *, char *name, int idx, char *options); short flags; short index; int cflag; @@ -141,7 +141,6 @@ extern int console_set_on_cmdline; extern struct console *early_console; extern int add_preferred_console(char *name, int idx, char *options); -extern int update_console_cmdline(char *name, int idx, char *name_new, int idx_new, char *options); extern void register_console(struct console *); extern int unregister_console(struct console *); extern struct console *console_drivers; diff --git a/include/linux/serial_8250.h b/include/linux/serial_8250.h index a8efa235b7c1..f26ae7fa30ae 100644 --- a/include/linux/serial_8250.h +++ b/include/linux/serial_8250.h @@ -118,8 +118,6 @@ void serial8250_resume_port(int line); extern int early_serial_setup(struct uart_port *port); -extern int serial8250_find_port(struct uart_port *p); -extern int serial8250_find_port_for_earlycon(void); extern unsigned int serial8250_early_in(struct uart_port *port, int offset); extern void serial8250_early_out(struct uart_port *port, int offset, int value); extern int setup_early_serial8250_console(char *cmdline); diff --git a/kernel/printk/printk.c b/kernel/printk/printk.c index 26f899809539..dda959221086 100644 --- a/kernel/printk/printk.c +++ b/kernel/printk/printk.c @@ -2017,24 +2017,6 @@ int add_preferred_console(char *name, int idx, char *options) return __add_preferred_console(name, idx, options, NULL); } -int update_console_cmdline(char *name, int idx, char *name_new, int idx_new, char *options) -{ - struct console_cmdline *c; - int i; - - for (i = 0, c = console_cmdline; - i < MAX_CMDLINECONSOLES && c->name[0]; - i++, c++) - if (strcmp(c->name, name) == 0 && c->index == idx) { - strlcpy(c->name, name_new, sizeof(c->name)); - c->options = options; - c->index = idx_new; - return i; - } - /* not found */ - return -1; -} - bool console_suspend_enabled = true; EXPORT_SYMBOL(console_suspend_enabled); @@ -2436,9 +2418,6 @@ void register_console(struct console *newcon) if (preferred_console < 0 || bcon || !console_drivers) preferred_console = selected_console; - if (newcon->early_setup) - newcon->early_setup(); - /* * See if we want to use this console driver. If we * didn't select a console we take the first one @@ -2464,21 +2443,26 @@ void register_console(struct console *newcon) for (i = 0, c = console_cmdline; i < MAX_CMDLINECONSOLES && c->name[0]; i++, c++) { - BUILD_BUG_ON(sizeof(c->name) != sizeof(newcon->name)); - if (strcmp(c->name, newcon->name) != 0) - continue; - if (newcon->index >= 0 && - newcon->index != c->index) - continue; - if (newcon->index < 0) - newcon->index = c->index; + if (!newcon->match || + newcon->match(newcon, c->name, c->index, c->options) != 0) { + /* default matching */ + BUILD_BUG_ON(sizeof(c->name) != sizeof(newcon->name)); + if (strcmp(c->name, newcon->name) != 0) + continue; + if (newcon->index >= 0 && + newcon->index != c->index) + continue; + if (newcon->index < 0) + newcon->index = c->index; - if (_braille_register_console(newcon, c)) - return; + if (_braille_register_console(newcon, c)) + return; + + if (newcon->setup && + newcon->setup(newcon, c->options) != 0) + break; + } - if (newcon->setup && - newcon->setup(newcon, console_cmdline[i].options) != 0) - break; newcon->flags |= CON_ENABLED; if (i == selected_console) { newcon->flags |= CON_CONSDEV; -- cgit v1.2.3 From 470ca0de69feaba5df215ad804cec1859883a5ed Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Mon, 9 Mar 2015 16:27:21 -0400 Subject: serial: earlycon: Enable earlycon without command line param Earlycon matching can only be triggered if 'earlycon=...' has been specified on the kernel command line. To workaround this limitation requires tight coupling between arches and specific serial drivers in order to start an earlycon. Devicetree avoids this limitation with a link table that contains the required data to match earlycons. Mirror this approach for earlycon match by name. Re-purpose EARLYCON_DECLARE to generate a table entry which associates name with setup() function. Re-purpose setup_earlycon() to scan this table for an earlycon match, which is registered if found. Declare one "earlycon" early_param, which calls setup_earlycon(). This design allows setup_earlycon() to be called directly with a param string (as if 'earlycon=...' had been set on the command line). Re-registration (either directly or by early_param) is prevented. Acked-by: Rob Herring Signed-off-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_early.c | 7 +-- drivers/tty/serial/earlycon.c | 92 ++++++++++++++++++++++++++++-------- include/asm-generic/vmlinux.lds.h | 9 ++++ include/linux/serial_core.h | 19 ++++---- 4 files changed, 94 insertions(+), 33 deletions(-) (limited to 'include') diff --git a/drivers/tty/serial/8250/8250_early.c b/drivers/tty/serial/8250/8250_early.c index b199c10689f4..d272139a5729 100644 --- a/drivers/tty/serial/8250/8250_early.c +++ b/drivers/tty/serial/8250/8250_early.c @@ -170,10 +170,5 @@ EARLYCON_DECLARE(uart, early_serial8250_setup); int __init setup_early_serial8250_console(char *cmdline) { - char match[] = "uart8250"; - - if (cmdline && cmdline[4] == ',') - match[4] = '\0'; - - return setup_earlycon(cmdline, match, early_serial8250_setup); + return setup_earlycon(cmdline); } diff --git a/drivers/tty/serial/earlycon.c b/drivers/tty/serial/earlycon.c index 9fb76b66c545..5fdc9f3ecd64 100644 --- a/drivers/tty/serial/earlycon.c +++ b/drivers/tty/serial/earlycon.c @@ -10,6 +10,9 @@ * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include #include #include @@ -34,6 +37,10 @@ static struct earlycon_device early_console_dev = { .con = &early_con, }; +extern struct earlycon_id __earlycon_table[]; +static const struct earlycon_id __earlycon_table_sentinel + __used __section(__earlycon_table_end); + static const struct of_device_id __earlycon_of_table_sentinel __used __section(__earlycon_of_table_end); @@ -96,9 +103,7 @@ static int __init parse_options(struct earlycon_device *device, char *options) return 0; } - -static int __init -register_earlycon(char *buf, int (*setup)(struct earlycon_device *, const char *)) +static int __init register_earlycon(char *buf, const struct earlycon_id *match) { int err; struct uart_port *port = &early_console_dev.port; @@ -112,7 +117,7 @@ register_earlycon(char *buf, int (*setup)(struct earlycon_device *, const char * port->membase = earlycon_map(port->mapbase, 64); early_console_dev.con->data = &early_console_dev; - err = setup(&early_console_dev, buf); + err = match->setup(&early_console_dev, buf); if (err < 0) return err; if (!early_console_dev.con->write) @@ -122,27 +127,76 @@ register_earlycon(char *buf, int (*setup)(struct earlycon_device *, const char * return 0; } -int __init setup_earlycon(char *buf, const char *match, - int (*setup)(struct earlycon_device *, const char *)) +/** + * setup_earlycon - match and register earlycon console + * @buf: earlycon param string + * + * Registers the earlycon console matching the earlycon specified + * in the param string @buf. Acceptable param strings are of the form + * ,io|mmio|mmio32,, + * ,0x, + * , + * + * + * Only for the third form does the earlycon setup() method receive the + * string in the 'options' parameter; all other forms set + * the parameter to NULL. + * + * Returns 0 if an attempt to register the earlycon was made, + * otherwise negative error code + */ +int __init setup_earlycon(char *buf) { - size_t len; + const struct earlycon_id *match; - if (!buf || !match || !setup) - return 0; + if (!buf || !buf[0]) + return -EINVAL; - len = strlen(match); - if (strncmp(buf, match, len)) - return 0; + if (early_con.flags & CON_ENABLED) + return -EALREADY; - if (buf[len]) { - if (buf[len] != ',') - return 0; - buf += len + 1; - } else - buf = NULL; + for (match = __earlycon_table; match->name[0]; match++) { + size_t len = strlen(match->name); - return register_earlycon(buf, setup); + if (strncmp(buf, match->name, len)) + continue; + + if (buf[len]) { + if (buf[len] != ',') + continue; + buf += len + 1; + } else + buf = NULL; + + return register_earlycon(buf, match); + } + + return -ENOENT; +} + +/* early_param wrapper for setup_earlycon() */ +static int __init param_setup_earlycon(char *buf) +{ + int err; + + /* + * Just 'earlycon' is a valid param for devicetree earlycons; + * don't generate a warning from parse_early_params() in that case + */ + if (!buf || !buf[0]) + return 0; + + err = setup_earlycon(buf); + if (err == -ENOENT) { + pr_warn("no match for %s\n", buf); + err = 0; + } else if (err == -EALREADY) { + pr_warn("already registered\n"); + err = 0; + } + return err; } +early_param("earlycon", param_setup_earlycon); int __init of_setup_earlycon(unsigned long addr, int (*setup)(struct earlycon_device *, const char *)) diff --git a/include/asm-generic/vmlinux.lds.h b/include/asm-generic/vmlinux.lds.h index ac78910d7416..87e5b6f8f4fc 100644 --- a/include/asm-generic/vmlinux.lds.h +++ b/include/asm-generic/vmlinux.lds.h @@ -150,6 +150,14 @@ #define TRACE_SYSCALLS() #endif +#ifdef CONFIG_SERIAL_EARLYCON +#define EARLYCON_TABLE() . = ALIGN(8); \ + VMLINUX_SYMBOL(__earlycon_table) = .; \ + *(__earlycon_table) \ + *(__earlycon_table_end) +#else +#define EARLYCON_TABLE() +#endif #define ___OF_TABLE(cfg, name) _OF_TABLE_##cfg(name) #define __OF_TABLE(cfg, name) ___OF_TABLE(cfg, name) @@ -503,6 +511,7 @@ CPU_METHOD_OF_TABLES() \ KERNEL_DTB() \ IRQCHIP_OF_MATCH_TABLE() \ + EARLYCON_TABLE() \ EARLYCON_OF_TABLES() #define INIT_TEXT \ diff --git a/include/linux/serial_core.h b/include/linux/serial_core.h index 980170e5a982..8aeec4913a9c 100644 --- a/include/linux/serial_core.h +++ b/include/linux/serial_core.h @@ -337,18 +337,21 @@ struct earlycon_device { char options[16]; /* e.g., 115200n8 */ unsigned int baud; }; -int setup_earlycon(char *buf, const char *match, - int (*setup)(struct earlycon_device *, const char *)); +struct earlycon_id { + char name[16]; + int (*setup)(struct earlycon_device *, const char *options); +}; + +extern int setup_earlycon(char *buf); extern int of_setup_earlycon(unsigned long addr, int (*setup)(struct earlycon_device *, const char *)); -#define EARLYCON_DECLARE(name, func) \ -static int __init name ## _setup_earlycon(char *buf) \ -{ \ - return setup_earlycon(buf, __stringify(name), func); \ -} \ -early_param("earlycon", name ## _setup_earlycon); +#define EARLYCON_DECLARE(_name, func) \ + static const struct earlycon_id __earlycon_##_name \ + __used __section(__earlycon_table) \ + = { .name = __stringify(_name), \ + .setup = func } #define OF_EARLYCON_DECLARE(name, compat, fn) \ _OF_DECLARE(earlycon, name, compat, fn, void *) -- cgit v1.2.3 From df519e7bd33cf56d8a5ce357dfb94248d427b688 Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Mon, 9 Mar 2015 16:27:22 -0400 Subject: serial: 8250_early: Remove setup_early_serial8250_console() setup_earlycon() will now match and register the desired earlycon from the param string (as if 'earlycon=...' had been set on the command line). Use setup_earlycon() from existing arch call sites which start an earlycon directly. Acked-by: Rob Herring Signed-off-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman --- arch/mips/mti-malta/malta-init.c | 4 ++-- drivers/firmware/pcdp.c | 4 ++-- drivers/tty/serial/8250/8250_early.c | 5 ----- include/linux/serial_8250.h | 1 - 4 files changed, 4 insertions(+), 10 deletions(-) (limited to 'include') diff --git a/arch/mips/mti-malta/malta-init.c b/arch/mips/mti-malta/malta-init.c index 6849f533154f..cec3e187c48f 100644 --- a/arch/mips/mti-malta/malta-init.c +++ b/arch/mips/mti-malta/malta-init.c @@ -14,7 +14,7 @@ #include #include #include -#include +#include #include #include @@ -75,7 +75,7 @@ static void __init console_config(void) if ((strstr(fw_getcmdline(), "earlycon=")) == NULL) { sprintf(console_string, "uart8250,io,0x3f8,%d%c%c", baud, parity, bits); - setup_early_serial8250_console(console_string); + setup_earlycon(console_string); } if ((strstr(fw_getcmdline(), "console=")) == NULL) { diff --git a/drivers/firmware/pcdp.c b/drivers/firmware/pcdp.c index a330492e06f9..75273a251603 100644 --- a/drivers/firmware/pcdp.c +++ b/drivers/firmware/pcdp.c @@ -15,7 +15,7 @@ #include #include #include -#include +#include #include #include "pcdp.h" @@ -43,7 +43,7 @@ setup_serial_console(struct pcdp_uart *uart) } add_preferred_console("uart", 8250, &options[9]); - return setup_early_serial8250_console(options); + return setup_earlycon(options); #else return -ENODEV; #endif diff --git a/drivers/tty/serial/8250/8250_early.c b/drivers/tty/serial/8250/8250_early.c index d272139a5729..a2b4e58690ba 100644 --- a/drivers/tty/serial/8250/8250_early.c +++ b/drivers/tty/serial/8250/8250_early.c @@ -167,8 +167,3 @@ static int __init early_serial8250_setup(struct earlycon_device *device, } EARLYCON_DECLARE(uart8250, early_serial8250_setup); EARLYCON_DECLARE(uart, early_serial8250_setup); - -int __init setup_early_serial8250_console(char *cmdline) -{ - return setup_earlycon(cmdline); -} diff --git a/include/linux/serial_8250.h b/include/linux/serial_8250.h index f26ae7fa30ae..ca9f87beac63 100644 --- a/include/linux/serial_8250.h +++ b/include/linux/serial_8250.h @@ -120,7 +120,6 @@ extern int early_serial_setup(struct uart_port *port); extern unsigned int serial8250_early_in(struct uart_port *port, int offset); extern void serial8250_early_out(struct uart_port *port, int offset, int value); -extern int setup_early_serial8250_console(char *cmdline); extern void serial8250_do_set_termios(struct uart_port *port, struct ktermios *termios, struct ktermios *old); extern int serial8250_do_startup(struct uart_port *port); -- cgit v1.2.3 From 01218bf14ee60d4a2d6c667ebdbba3ae9a7a1d66 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Thu, 26 Mar 2015 09:47:55 -0700 Subject: of: Explicitly include linux/types.h in of_graph.h In current -next of_graph.h fails to build due to it relying on linux/types.h without explicitly including it: ../include/linux/of_graph.h:43:71: error: unknown type name 'u32' caused by bfe446e37c4e (of: Add of_graph_get_port_by_id function). Add an explicit inclusion to fix this. Signed-off-by: Mark Brown Acked-by: Laurent Pinchart Acked-by: Philipp Zabel Signed-off-by: Rob Herring --- include/linux/of_graph.h | 2 ++ 1 file changed, 2 insertions(+) (limited to 'include') diff --git a/include/linux/of_graph.h b/include/linux/of_graph.h index 3c1c95a39e0c..7bc92e050608 100644 --- a/include/linux/of_graph.h +++ b/include/linux/of_graph.h @@ -14,6 +14,8 @@ #ifndef __LINUX_OF_GRAPH_H #define __LINUX_OF_GRAPH_H +#include + /** * struct of_endpoint - the OF graph endpoint data structure * @port: identifier (value of reg property) of a port this endpoint belongs to -- cgit v1.2.3 From a4416cd1ac7b48988f0f41a17769d65c71ffc504 Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Tue, 24 Feb 2015 14:25:07 -0500 Subject: serial: 8250: Separate legacy irq handling from core port operations Prepare for 8250 split; decouple irq setup/teardown and handler from core port operations. Introduce setup_irq() and release_irq() 8250 driver methods; the 8250 core will use these methods to install and remove irq handling for the given 8250 port. Refactor irq chain linking/unlinking from 8250 core into univ8250_setup_irq()/univ8250_release_irq() for the universal 8250 driver. Signed-off-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_core.c | 83 +++++++++++++++++++++++-------------- include/linux/serial_8250.h | 15 +++++++ 2 files changed, 68 insertions(+), 30 deletions(-) (limited to 'include') diff --git a/drivers/tty/serial/8250/8250_core.c b/drivers/tty/serial/8250/8250_core.c index 625f81c9b55e..06bb2ced2c94 100644 --- a/drivers/tty/serial/8250/8250_core.c +++ b/drivers/tty/serial/8250/8250_core.c @@ -1890,6 +1890,48 @@ static void serial8250_backup_timeout(unsigned long data) jiffies + uart_poll_timeout(&up->port) + HZ / 5); } +static int univ8250_setup_irq(struct uart_8250_port *up) +{ + struct uart_port *port = &up->port; + int retval = 0; + + /* + * The above check will only give an accurate result the first time + * the port is opened so this value needs to be preserved. + */ + if (up->bugs & UART_BUG_THRE) { + pr_debug("ttyS%d - using backup timer\n", serial_index(port)); + + up->timer.function = serial8250_backup_timeout; + up->timer.data = (unsigned long)up; + mod_timer(&up->timer, jiffies + + uart_poll_timeout(port) + HZ / 5); + } + + /* + * If the "interrupt" for this port doesn't correspond with any + * hardware interrupt, we use a timer-based system. The original + * driver used to do this with IRQ0. + */ + if (!port->irq) { + up->timer.data = (unsigned long)up; + mod_timer(&up->timer, jiffies + uart_poll_timeout(port)); + } else + retval = serial_link_irq_chain(up); + + return retval; +} + +static void univ8250_release_irq(struct uart_8250_port *up) +{ + struct uart_port *port = &up->port; + + del_timer_sync(&up->timer); + up->timer.function = serial8250_timeout; + if (port->irq) + serial_unlink_irq_chain(up); +} + static unsigned int serial8250_tx_empty(struct uart_port *port) { struct uart_8250_port *up = up_to_u8250p(port); @@ -2194,35 +2236,12 @@ int serial8250_do_startup(struct uart_port *port) if ((!(iir1 & UART_IIR_NO_INT) && (iir & UART_IIR_NO_INT)) || up->port.flags & UPF_BUG_THRE) { up->bugs |= UART_BUG_THRE; - pr_debug("ttyS%d - using backup timer\n", - serial_index(port)); } } - /* - * The above check will only give an accurate result the first time - * the port is opened so this value needs to be preserved. - */ - if (up->bugs & UART_BUG_THRE) { - up->timer.function = serial8250_backup_timeout; - up->timer.data = (unsigned long)up; - mod_timer(&up->timer, jiffies + - uart_poll_timeout(port) + HZ / 5); - } - - /* - * If the "interrupt" for this port doesn't correspond with any - * hardware interrupt, we use a timer-based system. The original - * driver used to do this with IRQ0. - */ - if (!port->irq) { - up->timer.data = (unsigned long)up; - mod_timer(&up->timer, jiffies + uart_poll_timeout(port)); - } else { - retval = serial_link_irq_chain(up); - if (retval) - goto out; - } + retval = up->ops->setup_irq(up); + if (retval) + goto out; /* * Now, initialize the UART @@ -2380,10 +2399,7 @@ void serial8250_do_shutdown(struct uart_port *port) serial_port_in(port, UART_RX); serial8250_rpm_put(up); - del_timer_sync(&up->timer); - up->timer.function = serial8250_timeout; - if (port->irq) - serial_unlink_irq_chain(up); + up->ops->release_irq(up); } EXPORT_SYMBOL_GPL(serial8250_do_shutdown); @@ -3083,6 +3099,11 @@ static struct uart_ops serial8250_pops = { #endif }; +static const struct uart_8250_ops univ8250_driver_ops = { + .setup_irq = univ8250_setup_irq, + .release_irq = univ8250_release_irq, +}; + static struct uart_8250_port serial8250_ports[UART_NR]; /** @@ -3137,6 +3158,8 @@ static void __init serial8250_isa_init_ports(void) up->timer.function = serial8250_timeout; up->cur_iotype = 0xFF; + up->ops = &univ8250_driver_ops; + /* * ALPHA_KLUDGE_MCR needs to be killed. */ diff --git a/include/linux/serial_8250.h b/include/linux/serial_8250.h index ca9f87beac63..50735a9ad598 100644 --- a/include/linux/serial_8250.h +++ b/include/linux/serial_8250.h @@ -60,6 +60,20 @@ enum { }; struct uart_8250_dma; +struct uart_8250_port; + +/** + * 8250 core driver operations + * + * @setup_irq() Setup irq handling. The universal 8250 driver links this + * port to the irq chain. Other drivers may @request_irq(). + * @release_irq() Undo irq handling. The universal 8250 driver unlinks + * the port from the irq chain. + */ +struct uart_8250_ops { + int (*setup_irq)(struct uart_8250_port *); + void (*release_irq)(struct uart_8250_port *); +}; /* * This should be used by drivers which want to register @@ -100,6 +114,7 @@ struct uart_8250_port { unsigned char msr_saved_flags; struct uart_8250_dma *dma; + const struct uart_8250_ops *ops; /* 8250 specific callbacks */ int (*dl_read)(struct uart_8250_port *); -- cgit v1.2.3 From 403753937020549e4bb0d8ef6e915f00a338a096 Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Tue, 24 Feb 2015 14:25:14 -0500 Subject: serial: 8250: Decouple RSA probe Prepare for 8250 split; separate RSA probe and resource management from base port operations. Override base port operations for the config_port(), request_port() and release_port() methods to implement the optional RSA probe and resource management only in the universal/legacy 8250 driver. Introduce 'probe' flags for 8250 ports, which allows drivers higher up the driver stack to enable optional probes. Signed-off-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250.h | 3 - drivers/tty/serial/8250/8250_core.c | 112 ++++++++++++++++++++++++------------ include/linux/serial_8250.h | 2 + 3 files changed, 78 insertions(+), 39 deletions(-) (limited to 'include') diff --git a/drivers/tty/serial/8250/8250.h b/drivers/tty/serial/8250/8250.h index 23dc9f9b9839..c43f74c53cd9 100644 --- a/drivers/tty/serial/8250/8250.h +++ b/drivers/tty/serial/8250/8250.h @@ -84,9 +84,6 @@ struct serial8250_config { #define UART_BUG_THRE (1 << 3) /* UART has buggy THRE reassertion */ #define UART_BUG_PARITY (1 << 4) /* UART mishandles parity if FIFO enabled */ -#define PROBE_RSA (1 << 0) -#define PROBE_ANY (~0) - #define HIGH_BITS_OFFSET ((sizeof(long)-sizeof(int))*8) #ifdef CONFIG_SERIAL_8250_SHARE_IRQ diff --git a/drivers/tty/serial/8250/8250_core.c b/drivers/tty/serial/8250/8250_core.c index 257e373bfccd..a4ed84a4f8db 100644 --- a/drivers/tty/serial/8250/8250_core.c +++ b/drivers/tty/serial/8250/8250_core.c @@ -1105,7 +1105,7 @@ static void autoconfig_16550a(struct uart_8250_port *up) * whether or not this UART is a 16550A or not, since this will * determine whether or not we can use its FIFO features or not. */ -static void autoconfig(struct uart_8250_port *up, unsigned int probeflags) +static void autoconfig(struct uart_8250_port *up) { unsigned char status1, scratch, scratch2, scratch3; unsigned char save_lcr, save_mcr; @@ -1228,7 +1228,7 @@ static void autoconfig(struct uart_8250_port *up, unsigned int probeflags) /* * Only probe for RSA ports if we got the region. */ - if (port->type == PORT_16550A && probeflags & PROBE_RSA && + if (port->type == PORT_16550A && up->probe & UART_PROBE_RSA && __enable_rsa(up)) port->type = PORT_RSA; #endif @@ -2832,10 +2832,6 @@ static void serial8250_release_port(struct uart_port *port) struct uart_8250_port *up = up_to_u8250p(port); serial8250_release_std_resource(up); -#ifdef CONFIG_SERIAL_8250_RSA - if (port->type == PORT_RSA) - serial8250_release_rsa_resource(up); -#endif } static int serial8250_request_port(struct uart_port *port) @@ -2847,13 +2843,6 @@ static int serial8250_request_port(struct uart_port *port) return -ENODEV; ret = serial8250_request_std_resource(up); -#ifdef CONFIG_SERIAL_8250_RSA - if (ret == 0 && port->type == PORT_RSA) { - ret = serial8250_request_rsa_resource(up); - if (ret < 0) - serial8250_release_std_resource(up); - } -#endif return ret; } @@ -3001,7 +2990,6 @@ static void register_dev_spec_attr_grp(struct uart_8250_port *up) static void serial8250_config_port(struct uart_port *port, int flags) { struct uart_8250_port *up = up_to_u8250p(port); - int probeflags = 0; int ret; if (port->type == PORT_8250_CIR) @@ -3015,28 +3003,11 @@ static void serial8250_config_port(struct uart_port *port, int flags) if (ret < 0) return; -#ifdef CONFIG_SERIAL_8250_RSA - if (port->type == PORT_RSA) { - if (serial8250_request_rsa_resource(up) == 0) - probeflags |= PROBE_RSA; - } else if (flags & UART_CONFIG_TYPE) { - int i; - - for (i = 0 ; i < probe_rsa_count; ++i) { - if (probe_rsa[i] == port->iobase) { - if (serial8250_request_rsa_resource(up) == 0) - probeflags |= PROBE_RSA; - break; - } - } - } -#endif - if (port->iotype != up->cur_iotype) set_io_from_upio(port); if (flags & UART_CONFIG_TYPE) - autoconfig(up, probeflags); + autoconfig(up); /* if access method is AU, it is a 16550 with a quirk */ if (port->type == PORT_16550A && port->iotype == UPIO_AU) @@ -3049,10 +3020,6 @@ static void serial8250_config_port(struct uart_port *port, int flags) if (port->type != PORT_UNKNOWN && flags & UART_CONFIG_IRQ) autoconfig_irq(up); -#ifdef CONFIG_SERIAL_8250_RSA - if (port->type != PORT_RSA && probeflags & PROBE_RSA) - serial8250_release_rsa_resource(up); -#endif if (port->type == PORT_UNKNOWN) serial8250_release_std_resource(up); @@ -3113,6 +3080,9 @@ static struct uart_ops serial8250_pops = { #endif }; +static const struct uart_ops *base_ops; +static struct uart_ops univ8250_port_ops; + static const struct uart_8250_ops univ8250_driver_ops = { .setup_irq = univ8250_setup_irq, .release_irq = univ8250_release_irq, @@ -3184,6 +3154,69 @@ static void serial8250_set_defaults(struct uart_8250_port *up) } } +#ifdef CONFIG_SERIAL_8250_RSA + +static void univ8250_config_port(struct uart_port *port, int flags) +{ + struct uart_8250_port *up = up_to_u8250p(port); + + up->probe &= ~UART_PROBE_RSA; + if (port->type == PORT_RSA) { + if (serial8250_request_rsa_resource(up) == 0) + up->probe |= UART_PROBE_RSA; + } else if (flags & UART_CONFIG_TYPE) { + int i; + + for (i = 0; i < probe_rsa_count; i++) { + if (probe_rsa[i] == up->port.iobase) { + if (serial8250_request_rsa_resource(up) == 0) + up->probe |= UART_PROBE_RSA; + break; + } + } + } + + base_ops->config_port(port, flags); + + if (port->type != PORT_RSA && up->probe & UART_PROBE_RSA) + serial8250_release_rsa_resource(up); +} + +static int univ8250_request_port(struct uart_port *port) +{ + struct uart_8250_port *up = up_to_u8250p(port); + int ret; + + ret = base_ops->request_port(port); + if (ret == 0 && port->type == PORT_RSA) { + ret = serial8250_request_rsa_resource(up); + if (ret < 0) + base_ops->release_port(port); + } + + return ret; +} + +static void univ8250_release_port(struct uart_port *port) +{ + struct uart_8250_port *up = up_to_u8250p(port); + + if (port->type == PORT_RSA) + serial8250_release_rsa_resource(up); + base_ops->release_port(port); +} + +static void univ8250_rsa_support(struct uart_ops *ops) +{ + ops->config_port = univ8250_config_port; + ops->request_port = univ8250_request_port; + ops->release_port = univ8250_release_port; +} + +#else +#define univ8250_rsa_support(x) do { } while (0) +#endif /* CONFIG_SERIAL_8250_RSA */ + static void __init serial8250_isa_init_ports(void) { struct uart_8250_port *up; @@ -3203,6 +3236,9 @@ static void __init serial8250_isa_init_ports(void) port->line = i; serial8250_init_port(up); + if (!base_ops) + base_ops = port->ops; + port->ops = &univ8250_port_ops; init_timer(&up->timer); up->timer.function = serial8250_timeout; @@ -3216,6 +3252,10 @@ static void __init serial8250_isa_init_ports(void) up->mcr_force = ALPHA_KLUDGE_MCR; } + /* chain base port ops to support Remote Supervisor Adapter */ + univ8250_port_ops = *base_ops; + univ8250_rsa_support(&univ8250_port_ops); + if (share_irqs) irqflag = IRQF_SHARED; diff --git a/include/linux/serial_8250.h b/include/linux/serial_8250.h index 50735a9ad598..78097e7a330a 100644 --- a/include/linux/serial_8250.h +++ b/include/linux/serial_8250.h @@ -102,6 +102,8 @@ struct uart_8250_port { unsigned char canary; /* non-zero during system sleep * if no_console_suspend */ + unsigned char probe; +#define UART_PROBE_RSA (1 << 0) /* * Some bits in registers are cleared on a read, so they must -- cgit v1.2.3 From 0fea53e255eaa889fb6cf8e10d11fbea2921eac8 Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Fri, 13 Mar 2015 11:09:36 -0700 Subject: tty: serial: Remove orphaned serial driver This driver is orphaned now that mach-msm has been removed. Delete it. Cc: Greg Kroah-Hartman Cc: David Brown Cc: Bryan Huntsman Cc: Daniel Walker Signed-off-by: Stephen Boyd Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/Kconfig | 12 - drivers/tty/serial/Makefile | 1 - drivers/tty/serial/msm_serial_hs.c | 1874 --------------------------- include/linux/platform_data/msm_serial_hs.h | 49 - 4 files changed, 1936 deletions(-) delete mode 100644 drivers/tty/serial/msm_serial_hs.c delete mode 100644 include/linux/platform_data/msm_serial_hs.h (limited to 'include') diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig index 04d7a96c7cf9..79ae58682adc 100644 --- a/drivers/tty/serial/Kconfig +++ b/drivers/tty/serial/Kconfig @@ -1053,18 +1053,6 @@ config SERIAL_MSM_CONSOLE select SERIAL_CORE_CONSOLE select SERIAL_EARLYCON -config SERIAL_MSM_HS - tristate "MSM UART High Speed: Serial Driver" - depends on ARCH_MSM7X00A || ARCH_MSM7X30 || ARCH_QSD8X50 - select SERIAL_CORE - help - If you have a machine based on MSM family of SoCs, you - can enable its onboard high speed serial port by enabling - this option. - - Choose M here to compile it as a module. The module will be - called msm_serial_hs. - config SERIAL_VT8500 bool "VIA VT8500 on-chip serial port support" depends on ARCH_VT8500 diff --git a/drivers/tty/serial/Makefile b/drivers/tty/serial/Makefile index f42b4f9845df..c3ac3d930b33 100644 --- a/drivers/tty/serial/Makefile +++ b/drivers/tty/serial/Makefile @@ -62,7 +62,6 @@ obj-$(CONFIG_SERIAL_SGI_IOC3) += ioc3_serial.o obj-$(CONFIG_SERIAL_ATMEL) += atmel_serial.o obj-$(CONFIG_SERIAL_UARTLITE) += uartlite.o obj-$(CONFIG_SERIAL_MSM) += msm_serial.o -obj-$(CONFIG_SERIAL_MSM_HS) += msm_serial_hs.o obj-$(CONFIG_SERIAL_NETX) += netx-serial.o obj-$(CONFIG_SERIAL_OF_PLATFORM) += of_serial.o obj-$(CONFIG_SERIAL_OF_PLATFORM_NWPSERIAL) += nwpserial.o diff --git a/drivers/tty/serial/msm_serial_hs.c b/drivers/tty/serial/msm_serial_hs.c deleted file mode 100644 index 62da8534ba75..000000000000 --- a/drivers/tty/serial/msm_serial_hs.c +++ /dev/null @@ -1,1874 +0,0 @@ -/* - * MSM 7k/8k High speed uart driver - * - * Copyright (c) 2007-2011, Code Aurora Forum. All rights reserved. - * Copyright (c) 2008 Google Inc. - * Modified: Nick Pelly - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License for more details. - * - * Has optional support for uart power management independent of linux - * suspend/resume: - * - * RX wakeup. - * UART wakeup can be triggered by RX activity (using a wakeup GPIO on the - * UART RX pin). This should only be used if there is not a wakeup - * GPIO on the UART CTS, and the first RX byte is known (for example, with the - * Bluetooth Texas Instruments HCILL protocol), since the first RX byte will - * always be lost. RTS will be asserted even while the UART is off in this mode - * of operation. See msm_serial_hs_platform_data.rx_wakeup_irq. - */ - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include -#include -#include - -/* HSUART Registers */ -#define UARTDM_MR1_ADDR 0x0 -#define UARTDM_MR2_ADDR 0x4 - -/* Data Mover result codes */ -#define RSLT_FIFO_CNTR_BMSK (0xE << 28) -#define RSLT_VLD BIT(1) - -/* write only register */ -#define UARTDM_CSR_ADDR 0x8 -#define UARTDM_CSR_115200 0xFF -#define UARTDM_CSR_57600 0xEE -#define UARTDM_CSR_38400 0xDD -#define UARTDM_CSR_28800 0xCC -#define UARTDM_CSR_19200 0xBB -#define UARTDM_CSR_14400 0xAA -#define UARTDM_CSR_9600 0x99 -#define UARTDM_CSR_7200 0x88 -#define UARTDM_CSR_4800 0x77 -#define UARTDM_CSR_3600 0x66 -#define UARTDM_CSR_2400 0x55 -#define UARTDM_CSR_1200 0x44 -#define UARTDM_CSR_600 0x33 -#define UARTDM_CSR_300 0x22 -#define UARTDM_CSR_150 0x11 -#define UARTDM_CSR_75 0x00 - -/* write only register */ -#define UARTDM_TF_ADDR 0x70 -#define UARTDM_TF2_ADDR 0x74 -#define UARTDM_TF3_ADDR 0x78 -#define UARTDM_TF4_ADDR 0x7C - -/* write only register */ -#define UARTDM_CR_ADDR 0x10 -#define UARTDM_IMR_ADDR 0x14 - -#define UARTDM_IPR_ADDR 0x18 -#define UARTDM_TFWR_ADDR 0x1c -#define UARTDM_RFWR_ADDR 0x20 -#define UARTDM_HCR_ADDR 0x24 -#define UARTDM_DMRX_ADDR 0x34 -#define UARTDM_IRDA_ADDR 0x38 -#define UARTDM_DMEN_ADDR 0x3c - -/* UART_DM_NO_CHARS_FOR_TX */ -#define UARTDM_NCF_TX_ADDR 0x40 - -#define UARTDM_BADR_ADDR 0x44 - -#define UARTDM_SIM_CFG_ADDR 0x80 -/* Read Only register */ -#define UARTDM_SR_ADDR 0x8 - -/* Read Only register */ -#define UARTDM_RF_ADDR 0x70 -#define UARTDM_RF2_ADDR 0x74 -#define UARTDM_RF3_ADDR 0x78 -#define UARTDM_RF4_ADDR 0x7C - -/* Read Only register */ -#define UARTDM_MISR_ADDR 0x10 - -/* Read Only register */ -#define UARTDM_ISR_ADDR 0x14 -#define UARTDM_RX_TOTAL_SNAP_ADDR 0x38 - -#define UARTDM_RXFS_ADDR 0x50 - -/* Register field Mask Mapping */ -#define UARTDM_SR_PAR_FRAME_BMSK BIT(5) -#define UARTDM_SR_OVERRUN_BMSK BIT(4) -#define UARTDM_SR_TXEMT_BMSK BIT(3) -#define UARTDM_SR_TXRDY_BMSK BIT(2) -#define UARTDM_SR_RXRDY_BMSK BIT(0) - -#define UARTDM_CR_TX_DISABLE_BMSK BIT(3) -#define UARTDM_CR_RX_DISABLE_BMSK BIT(1) -#define UARTDM_CR_TX_EN_BMSK BIT(2) -#define UARTDM_CR_RX_EN_BMSK BIT(0) - -/* UARTDM_CR channel_comman bit value (register field is bits 8:4) */ -#define RESET_RX 0x10 -#define RESET_TX 0x20 -#define RESET_ERROR_STATUS 0x30 -#define RESET_BREAK_INT 0x40 -#define START_BREAK 0x50 -#define STOP_BREAK 0x60 -#define RESET_CTS 0x70 -#define RESET_STALE_INT 0x80 -#define RFR_LOW 0xD0 -#define RFR_HIGH 0xE0 -#define CR_PROTECTION_EN 0x100 -#define STALE_EVENT_ENABLE 0x500 -#define STALE_EVENT_DISABLE 0x600 -#define FORCE_STALE_EVENT 0x400 -#define CLEAR_TX_READY 0x300 -#define RESET_TX_ERROR 0x800 -#define RESET_TX_DONE 0x810 - -#define UARTDM_MR1_AUTO_RFR_LEVEL1_BMSK 0xffffff00 -#define UARTDM_MR1_AUTO_RFR_LEVEL0_BMSK 0x3f -#define UARTDM_MR1_CTS_CTL_BMSK 0x40 -#define UARTDM_MR1_RX_RDY_CTL_BMSK 0x80 - -#define UARTDM_MR2_ERROR_MODE_BMSK 0x40 -#define UARTDM_MR2_BITS_PER_CHAR_BMSK 0x30 - -/* bits per character configuration */ -#define FIVE_BPC (0 << 4) -#define SIX_BPC (1 << 4) -#define SEVEN_BPC (2 << 4) -#define EIGHT_BPC (3 << 4) - -#define UARTDM_MR2_STOP_BIT_LEN_BMSK 0xc -#define STOP_BIT_ONE (1 << 2) -#define STOP_BIT_TWO (3 << 2) - -#define UARTDM_MR2_PARITY_MODE_BMSK 0x3 - -/* Parity configuration */ -#define NO_PARITY 0x0 -#define EVEN_PARITY 0x1 -#define ODD_PARITY 0x2 -#define SPACE_PARITY 0x3 - -#define UARTDM_IPR_STALE_TIMEOUT_MSB_BMSK 0xffffff80 -#define UARTDM_IPR_STALE_LSB_BMSK 0x1f - -/* These can be used for both ISR and IMR register */ -#define UARTDM_ISR_TX_READY_BMSK BIT(7) -#define UARTDM_ISR_CURRENT_CTS_BMSK BIT(6) -#define UARTDM_ISR_DELTA_CTS_BMSK BIT(5) -#define UARTDM_ISR_RXLEV_BMSK BIT(4) -#define UARTDM_ISR_RXSTALE_BMSK BIT(3) -#define UARTDM_ISR_RXBREAK_BMSK BIT(2) -#define UARTDM_ISR_RXHUNT_BMSK BIT(1) -#define UARTDM_ISR_TXLEV_BMSK BIT(0) - -/* Field definitions for UART_DM_DMEN*/ -#define UARTDM_TX_DM_EN_BMSK 0x1 -#define UARTDM_RX_DM_EN_BMSK 0x2 - -#define UART_FIFOSIZE 64 -#define UARTCLK 7372800 - -/* Rx DMA request states */ -enum flush_reason { - FLUSH_NONE, - FLUSH_DATA_READY, - FLUSH_DATA_INVALID, /* values after this indicate invalid data */ - FLUSH_IGNORE = FLUSH_DATA_INVALID, - FLUSH_STOP, - FLUSH_SHUTDOWN, -}; - -/* UART clock states */ -enum msm_hs_clk_states_e { - MSM_HS_CLK_PORT_OFF, /* port not in use */ - MSM_HS_CLK_OFF, /* clock disabled */ - MSM_HS_CLK_REQUEST_OFF, /* disable after TX and RX flushed */ - MSM_HS_CLK_ON, /* clock enabled */ -}; - -/* Track the forced RXSTALE flush during clock off sequence. - * These states are only valid during MSM_HS_CLK_REQUEST_OFF */ -enum msm_hs_clk_req_off_state_e { - CLK_REQ_OFF_START, - CLK_REQ_OFF_RXSTALE_ISSUED, - CLK_REQ_OFF_FLUSH_ISSUED, - CLK_REQ_OFF_RXSTALE_FLUSHED, -}; - -/** - * struct msm_hs_tx - * @tx_ready_int_en: ok to dma more tx? - * @dma_in_flight: tx dma in progress - * @xfer: top level DMA command pointer structure - * @command_ptr: third level command struct pointer - * @command_ptr_ptr: second level command list struct pointer - * @mapped_cmd_ptr: DMA view of third level command struct - * @mapped_cmd_ptr_ptr: DMA view of second level command list struct - * @tx_count: number of bytes to transfer in DMA transfer - * @dma_base: DMA view of UART xmit buffer - * - * This structure describes a single Tx DMA transaction. MSM DMA - * commands have two levels of indirection. The top level command - * ptr points to a list of command ptr which in turn points to a - * single DMA 'command'. In our case each Tx transaction consists - * of a single second level pointer pointing to a 'box type' command. - */ -struct msm_hs_tx { - unsigned int tx_ready_int_en; - unsigned int dma_in_flight; - struct msm_dmov_cmd xfer; - dmov_box *command_ptr; - u32 *command_ptr_ptr; - dma_addr_t mapped_cmd_ptr; - dma_addr_t mapped_cmd_ptr_ptr; - int tx_count; - dma_addr_t dma_base; -}; - -/** - * struct msm_hs_rx - * @flush: Rx DMA request state - * @xfer: top level DMA command pointer structure - * @cmdptr_dmaaddr: DMA view of second level command structure - * @command_ptr: third level DMA command pointer structure - * @command_ptr_ptr: second level DMA command list pointer - * @mapped_cmd_ptr: DMA view of the third level command structure - * @wait: wait for DMA completion before shutdown - * @buffer: destination buffer for RX DMA - * @rbuffer: DMA view of buffer - * @pool: dma pool out of which coherent rx buffer is allocated - * @tty_work: private work-queue for tty flip buffer push task - * - * This structure describes a single Rx DMA transaction. Rx DMA - * transactions use box mode DMA commands. - */ -struct msm_hs_rx { - enum flush_reason flush; - struct msm_dmov_cmd xfer; - dma_addr_t cmdptr_dmaaddr; - dmov_box *command_ptr; - u32 *command_ptr_ptr; - dma_addr_t mapped_cmd_ptr; - wait_queue_head_t wait; - dma_addr_t rbuffer; - unsigned char *buffer; - struct dma_pool *pool; - struct work_struct tty_work; -}; - -/** - * struct msm_hs_rx_wakeup - * @irq: IRQ line to be configured as interrupt source on Rx activity - * @ignore: boolean value. 1 = ignore the wakeup interrupt - * @rx_to_inject: extra character to be inserted to Rx tty on wakeup - * @inject_rx: 1 = insert rx_to_inject. 0 = do not insert extra character - * - * This is an optional structure required for UART Rx GPIO IRQ based - * wakeup from low power state. UART wakeup can be triggered by RX activity - * (using a wakeup GPIO on the UART RX pin). This should only be used if - * there is not a wakeup GPIO on the UART CTS, and the first RX byte is - * known (eg., with the Bluetooth Texas Instruments HCILL protocol), - * since the first RX byte will always be lost. RTS will be asserted even - * while the UART is clocked off in this mode of operation. - */ -struct msm_hs_rx_wakeup { - int irq; /* < 0 indicates low power wakeup disabled */ - unsigned char ignore; - unsigned char inject_rx; - char rx_to_inject; -}; - -/** - * struct msm_hs_port - * @uport: embedded uart port structure - * @imr_reg: shadow value of UARTDM_IMR - * @clk: uart input clock handle - * @tx: Tx transaction related data structure - * @rx: Rx transaction related data structure - * @dma_tx_channel: Tx DMA command channel - * @dma_rx_channel Rx DMA command channel - * @dma_tx_crci: Tx channel rate control interface number - * @dma_rx_crci: Rx channel rate control interface number - * @clk_off_timer: Timer to poll DMA event completion before clock off - * @clk_off_delay: clk_off_timer poll interval - * @clk_state: overall clock state - * @clk_req_off_state: post flush clock states - * @rx_wakeup: optional rx_wakeup feature related data - * @exit_lpm_cb: optional callback to exit low power mode - * - * Low level serial port structure. - */ -struct msm_hs_port { - struct uart_port uport; - unsigned long imr_reg; - struct clk *clk; - struct msm_hs_tx tx; - struct msm_hs_rx rx; - - int dma_tx_channel; - int dma_rx_channel; - int dma_tx_crci; - int dma_rx_crci; - - struct hrtimer clk_off_timer; - ktime_t clk_off_delay; - enum msm_hs_clk_states_e clk_state; - enum msm_hs_clk_req_off_state_e clk_req_off_state; - - struct msm_hs_rx_wakeup rx_wakeup; - void (*exit_lpm_cb)(struct uart_port *); -}; - -#define MSM_UARTDM_BURST_SIZE 16 /* DM burst size (in bytes) */ -#define UARTDM_TX_BUF_SIZE UART_XMIT_SIZE -#define UARTDM_RX_BUF_SIZE 512 - -#define UARTDM_NR 2 - -static struct msm_hs_port q_uart_port[UARTDM_NR]; -static struct platform_driver msm_serial_hs_platform_driver; -static struct uart_driver msm_hs_driver; -static struct uart_ops msm_hs_ops; -static struct workqueue_struct *msm_hs_workqueue; - -#define UARTDM_TO_MSM(uart_port) \ - container_of((uart_port), struct msm_hs_port, uport) - -static unsigned int use_low_power_rx_wakeup(struct msm_hs_port - *msm_uport) -{ - return (msm_uport->rx_wakeup.irq >= 0); -} - -static unsigned int msm_hs_read(struct uart_port *uport, - unsigned int offset) -{ - return ioread32(uport->membase + offset); -} - -static void msm_hs_write(struct uart_port *uport, unsigned int offset, - unsigned int value) -{ - iowrite32(value, uport->membase + offset); -} - -static void msm_hs_release_port(struct uart_port *port) -{ - iounmap(port->membase); -} - -static int msm_hs_request_port(struct uart_port *port) -{ - port->membase = ioremap(port->mapbase, PAGE_SIZE); - if (unlikely(!port->membase)) - return -ENOMEM; - - /* configure the CR Protection to Enable */ - msm_hs_write(port, UARTDM_CR_ADDR, CR_PROTECTION_EN); - return 0; -} - -static int msm_hs_remove(struct platform_device *pdev) -{ - - struct msm_hs_port *msm_uport; - struct device *dev; - - if (pdev->id < 0 || pdev->id >= UARTDM_NR) { - printk(KERN_ERR "Invalid plaform device ID = %d\n", pdev->id); - return -EINVAL; - } - - msm_uport = &q_uart_port[pdev->id]; - dev = msm_uport->uport.dev; - - dma_unmap_single(dev, msm_uport->rx.mapped_cmd_ptr, sizeof(dmov_box), - DMA_TO_DEVICE); - dma_pool_free(msm_uport->rx.pool, msm_uport->rx.buffer, - msm_uport->rx.rbuffer); - dma_pool_destroy(msm_uport->rx.pool); - - dma_unmap_single(dev, msm_uport->rx.cmdptr_dmaaddr, sizeof(u32), - DMA_TO_DEVICE); - dma_unmap_single(dev, msm_uport->tx.mapped_cmd_ptr_ptr, sizeof(u32), - DMA_TO_DEVICE); - dma_unmap_single(dev, msm_uport->tx.mapped_cmd_ptr, sizeof(dmov_box), - DMA_TO_DEVICE); - - uart_remove_one_port(&msm_hs_driver, &msm_uport->uport); - clk_put(msm_uport->clk); - - /* Free the tx resources */ - kfree(msm_uport->tx.command_ptr); - kfree(msm_uport->tx.command_ptr_ptr); - - /* Free the rx resources */ - kfree(msm_uport->rx.command_ptr); - kfree(msm_uport->rx.command_ptr_ptr); - - iounmap(msm_uport->uport.membase); - - return 0; -} - -static int msm_hs_init_clk_locked(struct uart_port *uport) -{ - int ret; - struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport); - - ret = clk_enable(msm_uport->clk); - if (ret) { - printk(KERN_ERR "Error could not turn on UART clk\n"); - return ret; - } - - /* Set up the MREG/NREG/DREG/MNDREG */ - ret = clk_set_rate(msm_uport->clk, uport->uartclk); - if (ret) { - printk(KERN_WARNING "Error setting clock rate on UART\n"); - clk_disable(msm_uport->clk); - return ret; - } - - msm_uport->clk_state = MSM_HS_CLK_ON; - return 0; -} - -/* Enable and Disable clocks (Used for power management) */ -static void msm_hs_pm(struct uart_port *uport, unsigned int state, - unsigned int oldstate) -{ - struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport); - - if (use_low_power_rx_wakeup(msm_uport) || - msm_uport->exit_lpm_cb) - return; /* ignore linux PM states, - use msm_hs_request_clock API */ - - switch (state) { - case 0: - clk_enable(msm_uport->clk); - break; - case 3: - clk_disable(msm_uport->clk); - break; - default: - dev_err(uport->dev, "msm_serial: Unknown PM state %d\n", - state); - } -} - -/* - * programs the UARTDM_CSR register with correct bit rates - * - * Interrupts should be disabled before we are called, as - * we modify Set Baud rate - * Set receive stale interrupt level, dependent on Bit Rate - * Goal is to have around 8 ms before indicate stale. - * roundup (((Bit Rate * .008) / 10) + 1 - */ -static void msm_hs_set_bps_locked(struct uart_port *uport, - unsigned int bps) -{ - unsigned long rxstale; - unsigned long data; - struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport); - - switch (bps) { - case 300: - msm_hs_write(uport, UARTDM_CSR_ADDR, UARTDM_CSR_75); - rxstale = 1; - break; - case 600: - msm_hs_write(uport, UARTDM_CSR_ADDR, UARTDM_CSR_150); - rxstale = 1; - break; - case 1200: - msm_hs_write(uport, UARTDM_CSR_ADDR, UARTDM_CSR_300); - rxstale = 1; - break; - case 2400: - msm_hs_write(uport, UARTDM_CSR_ADDR, UARTDM_CSR_600); - rxstale = 1; - break; - case 4800: - msm_hs_write(uport, UARTDM_CSR_ADDR, UARTDM_CSR_1200); - rxstale = 1; - break; - case 9600: - msm_hs_write(uport, UARTDM_CSR_ADDR, UARTDM_CSR_2400); - rxstale = 2; - break; - case 14400: - msm_hs_write(uport, UARTDM_CSR_ADDR, UARTDM_CSR_3600); - rxstale = 3; - break; - case 19200: - msm_hs_write(uport, UARTDM_CSR_ADDR, UARTDM_CSR_4800); - rxstale = 4; - break; - case 28800: - msm_hs_write(uport, UARTDM_CSR_ADDR, UARTDM_CSR_7200); - rxstale = 6; - break; - case 38400: - msm_hs_write(uport, UARTDM_CSR_ADDR, UARTDM_CSR_9600); - rxstale = 8; - break; - case 57600: - msm_hs_write(uport, UARTDM_CSR_ADDR, UARTDM_CSR_14400); - rxstale = 16; - break; - case 76800: - msm_hs_write(uport, UARTDM_CSR_ADDR, UARTDM_CSR_19200); - rxstale = 16; - break; - case 115200: - msm_hs_write(uport, UARTDM_CSR_ADDR, UARTDM_CSR_28800); - rxstale = 31; - break; - case 230400: - msm_hs_write(uport, UARTDM_CSR_ADDR, UARTDM_CSR_57600); - rxstale = 31; - break; - case 460800: - msm_hs_write(uport, UARTDM_CSR_ADDR, UARTDM_CSR_115200); - rxstale = 31; - break; - case 4000000: - case 3686400: - case 3200000: - case 3500000: - case 3000000: - case 2500000: - case 1500000: - case 1152000: - case 1000000: - case 921600: - msm_hs_write(uport, UARTDM_CSR_ADDR, UARTDM_CSR_115200); - rxstale = 31; - break; - default: - msm_hs_write(uport, UARTDM_CSR_ADDR, UARTDM_CSR_2400); - /* default to 9600 */ - bps = 9600; - rxstale = 2; - break; - } - if (bps > 460800) - uport->uartclk = bps * 16; - else - uport->uartclk = UARTCLK; - - if (clk_set_rate(msm_uport->clk, uport->uartclk)) { - printk(KERN_WARNING "Error setting clock rate on UART\n"); - return; - } - - data = rxstale & UARTDM_IPR_STALE_LSB_BMSK; - data |= UARTDM_IPR_STALE_TIMEOUT_MSB_BMSK & (rxstale << 2); - - msm_hs_write(uport, UARTDM_IPR_ADDR, data); -} - -/* - * termios : new ktermios - * oldtermios: old ktermios previous setting - * - * Configure the serial port - */ -static void msm_hs_set_termios(struct uart_port *uport, - struct ktermios *termios, - struct ktermios *oldtermios) -{ - unsigned int bps; - unsigned long data; - unsigned long flags; - unsigned int c_cflag = termios->c_cflag; - struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport); - - spin_lock_irqsave(&uport->lock, flags); - clk_enable(msm_uport->clk); - - /* 300 is the minimum baud support by the driver */ - bps = uart_get_baud_rate(uport, termios, oldtermios, 200, 4000000); - - /* Temporary remapping 200 BAUD to 3.2 mbps */ - if (bps == 200) - bps = 3200000; - - msm_hs_set_bps_locked(uport, bps); - - data = msm_hs_read(uport, UARTDM_MR2_ADDR); - data &= ~UARTDM_MR2_PARITY_MODE_BMSK; - /* set parity */ - if (PARENB == (c_cflag & PARENB)) { - if (PARODD == (c_cflag & PARODD)) - data |= ODD_PARITY; - else if (CMSPAR == (c_cflag & CMSPAR)) - data |= SPACE_PARITY; - else - data |= EVEN_PARITY; - } - - /* Set bits per char */ - data &= ~UARTDM_MR2_BITS_PER_CHAR_BMSK; - - switch (c_cflag & CSIZE) { - case CS5: - data |= FIVE_BPC; - break; - case CS6: - data |= SIX_BPC; - break; - case CS7: - data |= SEVEN_BPC; - break; - default: - data |= EIGHT_BPC; - break; - } - /* stop bits */ - if (c_cflag & CSTOPB) { - data |= STOP_BIT_TWO; - } else { - /* otherwise 1 stop bit */ - data |= STOP_BIT_ONE; - } - data |= UARTDM_MR2_ERROR_MODE_BMSK; - /* write parity/bits per char/stop bit configuration */ - msm_hs_write(uport, UARTDM_MR2_ADDR, data); - - /* Configure HW flow control */ - data = msm_hs_read(uport, UARTDM_MR1_ADDR); - - data &= ~(UARTDM_MR1_CTS_CTL_BMSK | UARTDM_MR1_RX_RDY_CTL_BMSK); - - if (c_cflag & CRTSCTS) { - data |= UARTDM_MR1_CTS_CTL_BMSK; - data |= UARTDM_MR1_RX_RDY_CTL_BMSK; - } - - msm_hs_write(uport, UARTDM_MR1_ADDR, data); - - uport->ignore_status_mask = termios->c_iflag & INPCK; - uport->ignore_status_mask |= termios->c_iflag & IGNPAR; - uport->read_status_mask = (termios->c_cflag & CREAD); - - msm_hs_write(uport, UARTDM_IMR_ADDR, 0); - - /* Set Transmit software time out */ - uart_update_timeout(uport, c_cflag, bps); - - msm_hs_write(uport, UARTDM_CR_ADDR, RESET_RX); - msm_hs_write(uport, UARTDM_CR_ADDR, RESET_TX); - - if (msm_uport->rx.flush == FLUSH_NONE) { - msm_uport->rx.flush = FLUSH_IGNORE; - msm_dmov_stop_cmd(msm_uport->dma_rx_channel, NULL, 1); - } - - msm_hs_write(uport, UARTDM_IMR_ADDR, msm_uport->imr_reg); - - clk_disable(msm_uport->clk); - spin_unlock_irqrestore(&uport->lock, flags); -} - -/* - * Standard API, Transmitter - * Any character in the transmit shift register is sent - */ -static unsigned int msm_hs_tx_empty(struct uart_port *uport) -{ - unsigned int data; - unsigned int ret = 0; - struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport); - - clk_enable(msm_uport->clk); - - data = msm_hs_read(uport, UARTDM_SR_ADDR); - if (data & UARTDM_SR_TXEMT_BMSK) - ret = TIOCSER_TEMT; - - clk_disable(msm_uport->clk); - - return ret; -} - -/* - * Standard API, Stop transmitter. - * Any character in the transmit shift register is sent as - * well as the current data mover transfer . - */ -static void msm_hs_stop_tx_locked(struct uart_port *uport) -{ - struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport); - - msm_uport->tx.tx_ready_int_en = 0; -} - -/* - * Standard API, Stop receiver as soon as possible. - * - * Function immediately terminates the operation of the - * channel receiver and any incoming characters are lost. None - * of the receiver status bits are affected by this command and - * characters that are already in the receive FIFO there. - */ -static void msm_hs_stop_rx_locked(struct uart_port *uport) -{ - struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport); - unsigned int data; - - clk_enable(msm_uport->clk); - - /* disable dlink */ - data = msm_hs_read(uport, UARTDM_DMEN_ADDR); - data &= ~UARTDM_RX_DM_EN_BMSK; - msm_hs_write(uport, UARTDM_DMEN_ADDR, data); - - /* Disable the receiver */ - if (msm_uport->rx.flush == FLUSH_NONE) - msm_dmov_stop_cmd(msm_uport->dma_rx_channel, NULL, 1); - - if (msm_uport->rx.flush != FLUSH_SHUTDOWN) - msm_uport->rx.flush = FLUSH_STOP; - - clk_disable(msm_uport->clk); -} - -/* Transmit the next chunk of data */ -static void msm_hs_submit_tx_locked(struct uart_port *uport) -{ - int left; - int tx_count; - dma_addr_t src_addr; - struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport); - struct msm_hs_tx *tx = &msm_uport->tx; - struct circ_buf *tx_buf = &msm_uport->uport.state->xmit; - - if (uart_circ_empty(tx_buf) || uport->state->port.tty->stopped) { - msm_hs_stop_tx_locked(uport); - return; - } - - tx->dma_in_flight = 1; - - tx_count = uart_circ_chars_pending(tx_buf); - - if (UARTDM_TX_BUF_SIZE < tx_count) - tx_count = UARTDM_TX_BUF_SIZE; - - left = UART_XMIT_SIZE - tx_buf->tail; - - if (tx_count > left) - tx_count = left; - - src_addr = tx->dma_base + tx_buf->tail; - dma_sync_single_for_device(uport->dev, src_addr, tx_count, - DMA_TO_DEVICE); - - tx->command_ptr->num_rows = (((tx_count + 15) >> 4) << 16) | - ((tx_count + 15) >> 4); - tx->command_ptr->src_row_addr = src_addr; - - dma_sync_single_for_device(uport->dev, tx->mapped_cmd_ptr, - sizeof(dmov_box), DMA_TO_DEVICE); - - *tx->command_ptr_ptr = CMD_PTR_LP | DMOV_CMD_ADDR(tx->mapped_cmd_ptr); - - dma_sync_single_for_device(uport->dev, tx->mapped_cmd_ptr_ptr, - sizeof(u32), DMA_TO_DEVICE); - - /* Save tx_count to use in Callback */ - tx->tx_count = tx_count; - msm_hs_write(uport, UARTDM_NCF_TX_ADDR, tx_count); - - /* Disable the tx_ready interrupt */ - msm_uport->imr_reg &= ~UARTDM_ISR_TX_READY_BMSK; - msm_hs_write(uport, UARTDM_IMR_ADDR, msm_uport->imr_reg); - msm_dmov_enqueue_cmd(msm_uport->dma_tx_channel, &tx->xfer); -} - -/* Start to receive the next chunk of data */ -static void msm_hs_start_rx_locked(struct uart_port *uport) -{ - struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport); - - msm_hs_write(uport, UARTDM_CR_ADDR, RESET_STALE_INT); - msm_hs_write(uport, UARTDM_DMRX_ADDR, UARTDM_RX_BUF_SIZE); - msm_hs_write(uport, UARTDM_CR_ADDR, STALE_EVENT_ENABLE); - msm_uport->imr_reg |= UARTDM_ISR_RXLEV_BMSK; - msm_hs_write(uport, UARTDM_IMR_ADDR, msm_uport->imr_reg); - - msm_uport->rx.flush = FLUSH_NONE; - msm_dmov_enqueue_cmd(msm_uport->dma_rx_channel, &msm_uport->rx.xfer); - - /* might have finished RX and be ready to clock off */ - hrtimer_start(&msm_uport->clk_off_timer, msm_uport->clk_off_delay, - HRTIMER_MODE_REL); -} - -/* Enable the transmitter Interrupt */ -static void msm_hs_start_tx_locked(struct uart_port *uport) -{ - struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport); - - clk_enable(msm_uport->clk); - - if (msm_uport->exit_lpm_cb) - msm_uport->exit_lpm_cb(uport); - - if (msm_uport->tx.tx_ready_int_en == 0) { - msm_uport->tx.tx_ready_int_en = 1; - msm_hs_submit_tx_locked(uport); - } - - clk_disable(msm_uport->clk); -} - -/* - * This routine is called when we are done with a DMA transfer - * - * This routine is registered with Data mover when we set - * up a Data Mover transfer. It is called from Data mover ISR - * when the DMA transfer is done. - */ -static void msm_hs_dmov_tx_callback(struct msm_dmov_cmd *cmd_ptr, - unsigned int result, - struct msm_dmov_errdata *err) -{ - unsigned long flags; - struct msm_hs_port *msm_uport; - - /* DMA did not finish properly */ - WARN_ON((((result & RSLT_FIFO_CNTR_BMSK) >> 28) == 1) && - !(result & RSLT_VLD)); - - msm_uport = container_of(cmd_ptr, struct msm_hs_port, tx.xfer); - - spin_lock_irqsave(&msm_uport->uport.lock, flags); - clk_enable(msm_uport->clk); - - msm_uport->imr_reg |= UARTDM_ISR_TX_READY_BMSK; - msm_hs_write(&msm_uport->uport, UARTDM_IMR_ADDR, msm_uport->imr_reg); - - clk_disable(msm_uport->clk); - spin_unlock_irqrestore(&msm_uport->uport.lock, flags); -} - -/* - * This routine is called when we are done with a DMA transfer or the - * a flush has been sent to the data mover driver. - * - * This routine is registered with Data mover when we set up a Data Mover - * transfer. It is called from Data mover ISR when the DMA transfer is done. - */ -static void msm_hs_dmov_rx_callback(struct msm_dmov_cmd *cmd_ptr, - unsigned int result, - struct msm_dmov_errdata *err) -{ - int retval; - int rx_count; - unsigned long status; - unsigned int error_f = 0; - unsigned long flags; - unsigned int flush; - struct tty_port *port; - struct uart_port *uport; - struct msm_hs_port *msm_uport; - - msm_uport = container_of(cmd_ptr, struct msm_hs_port, rx.xfer); - uport = &msm_uport->uport; - - spin_lock_irqsave(&uport->lock, flags); - clk_enable(msm_uport->clk); - - port = &uport->state->port; - - msm_hs_write(uport, UARTDM_CR_ADDR, STALE_EVENT_DISABLE); - - status = msm_hs_read(uport, UARTDM_SR_ADDR); - - /* overflow is not connect to data in a FIFO */ - if (unlikely((status & UARTDM_SR_OVERRUN_BMSK) && - (uport->read_status_mask & CREAD))) { - tty_insert_flip_char(port, 0, TTY_OVERRUN); - uport->icount.buf_overrun++; - error_f = 1; - } - - if (!(uport->ignore_status_mask & INPCK)) - status = status & ~(UARTDM_SR_PAR_FRAME_BMSK); - - if (unlikely(status & UARTDM_SR_PAR_FRAME_BMSK)) { - /* Can not tell difference between parity & frame error */ - uport->icount.parity++; - error_f = 1; - if (uport->ignore_status_mask & IGNPAR) - tty_insert_flip_char(port, 0, TTY_PARITY); - } - - if (error_f) - msm_hs_write(uport, UARTDM_CR_ADDR, RESET_ERROR_STATUS); - - if (msm_uport->clk_req_off_state == CLK_REQ_OFF_FLUSH_ISSUED) - msm_uport->clk_req_off_state = CLK_REQ_OFF_RXSTALE_FLUSHED; - - flush = msm_uport->rx.flush; - if (flush == FLUSH_IGNORE) - msm_hs_start_rx_locked(uport); - if (flush == FLUSH_STOP) - msm_uport->rx.flush = FLUSH_SHUTDOWN; - if (flush >= FLUSH_DATA_INVALID) - goto out; - - rx_count = msm_hs_read(uport, UARTDM_RX_TOTAL_SNAP_ADDR); - - if (0 != (uport->read_status_mask & CREAD)) { - retval = tty_insert_flip_string(port, msm_uport->rx.buffer, - rx_count); - BUG_ON(retval != rx_count); - } - - msm_hs_start_rx_locked(uport); - -out: - clk_disable(msm_uport->clk); - - spin_unlock_irqrestore(&uport->lock, flags); - - if (flush < FLUSH_DATA_INVALID) - queue_work(msm_hs_workqueue, &msm_uport->rx.tty_work); -} - -static void msm_hs_tty_flip_buffer_work(struct work_struct *work) -{ - struct msm_hs_port *msm_uport = - container_of(work, struct msm_hs_port, rx.tty_work); - - tty_flip_buffer_push(&msm_uport->uport.state->port); -} - -/* - * Standard API, Current states of modem control inputs - * - * Since CTS can be handled entirely by HARDWARE we always - * indicate clear to send and count on the TX FIFO to block when - * it fills up. - * - * - TIOCM_DCD - * - TIOCM_CTS - * - TIOCM_DSR - * - TIOCM_RI - * (Unsupported) DCD and DSR will return them high. RI will return low. - */ -static unsigned int msm_hs_get_mctrl_locked(struct uart_port *uport) -{ - return TIOCM_DSR | TIOCM_CAR | TIOCM_CTS; -} - -/* - * True enables UART auto RFR, which indicates we are ready for data if the RX - * buffer is not full. False disables auto RFR, and deasserts RFR to indicate - * we are not ready for data. Must be called with UART clock on. - */ -static void set_rfr_locked(struct uart_port *uport, int auto_rfr) -{ - unsigned int data; - - data = msm_hs_read(uport, UARTDM_MR1_ADDR); - - if (auto_rfr) { - /* enable auto ready-for-receiving */ - data |= UARTDM_MR1_RX_RDY_CTL_BMSK; - msm_hs_write(uport, UARTDM_MR1_ADDR, data); - } else { - /* disable auto ready-for-receiving */ - data &= ~UARTDM_MR1_RX_RDY_CTL_BMSK; - msm_hs_write(uport, UARTDM_MR1_ADDR, data); - /* RFR is active low, set high */ - msm_hs_write(uport, UARTDM_CR_ADDR, RFR_HIGH); - } -} - -/* - * Standard API, used to set or clear RFR - */ -static void msm_hs_set_mctrl_locked(struct uart_port *uport, - unsigned int mctrl) -{ - unsigned int auto_rfr; - struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport); - - clk_enable(msm_uport->clk); - - auto_rfr = TIOCM_RTS & mctrl ? 1 : 0; - set_rfr_locked(uport, auto_rfr); - - clk_disable(msm_uport->clk); -} - -/* Standard API, Enable modem status (CTS) interrupt */ -static void msm_hs_enable_ms_locked(struct uart_port *uport) -{ - struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport); - - clk_enable(msm_uport->clk); - - /* Enable DELTA_CTS Interrupt */ - msm_uport->imr_reg |= UARTDM_ISR_DELTA_CTS_BMSK; - msm_hs_write(uport, UARTDM_IMR_ADDR, msm_uport->imr_reg); - - clk_disable(msm_uport->clk); - -} - -/* - * Standard API, Break Signal - * - * Control the transmission of a break signal. ctl eq 0 => break - * signal terminate ctl ne 0 => start break signal - */ -static void msm_hs_break_ctl(struct uart_port *uport, int ctl) -{ - struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport); - - clk_enable(msm_uport->clk); - msm_hs_write(uport, UARTDM_CR_ADDR, ctl ? START_BREAK : STOP_BREAK); - clk_disable(msm_uport->clk); -} - -static void msm_hs_config_port(struct uart_port *uport, int cfg_flags) -{ - unsigned long flags; - - spin_lock_irqsave(&uport->lock, flags); - if (cfg_flags & UART_CONFIG_TYPE) { - uport->type = PORT_MSM; - msm_hs_request_port(uport); - } - spin_unlock_irqrestore(&uport->lock, flags); -} - -/* Handle CTS changes (Called from interrupt handler) */ -static void msm_hs_handle_delta_cts_locked(struct uart_port *uport) -{ - struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport); - - clk_enable(msm_uport->clk); - - /* clear interrupt */ - msm_hs_write(uport, UARTDM_CR_ADDR, RESET_CTS); - uport->icount.cts++; - - clk_disable(msm_uport->clk); - - /* clear the IOCTL TIOCMIWAIT if called */ - wake_up_interruptible(&uport->state->port.delta_msr_wait); -} - -/* check if the TX path is flushed, and if so clock off - * returns 0 did not clock off, need to retry (still sending final byte) - * -1 did not clock off, do not retry - * 1 if we clocked off - */ -static int msm_hs_check_clock_off_locked(struct uart_port *uport) -{ - unsigned long sr_status; - struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport); - struct circ_buf *tx_buf = &uport->state->xmit; - - /* Cancel if tx tty buffer is not empty, dma is in flight, - * or tx fifo is not empty, or rx fifo is not empty */ - if (msm_uport->clk_state != MSM_HS_CLK_REQUEST_OFF || - !uart_circ_empty(tx_buf) || msm_uport->tx.dma_in_flight || - (msm_uport->imr_reg & UARTDM_ISR_TXLEV_BMSK) || - !(msm_uport->imr_reg & UARTDM_ISR_RXLEV_BMSK)) { - return -1; - } - - /* Make sure the uart is finished with the last byte */ - sr_status = msm_hs_read(uport, UARTDM_SR_ADDR); - if (!(sr_status & UARTDM_SR_TXEMT_BMSK)) - return 0; /* retry */ - - /* Make sure forced RXSTALE flush complete */ - switch (msm_uport->clk_req_off_state) { - case CLK_REQ_OFF_START: - msm_uport->clk_req_off_state = CLK_REQ_OFF_RXSTALE_ISSUED; - msm_hs_write(uport, UARTDM_CR_ADDR, FORCE_STALE_EVENT); - return 0; /* RXSTALE flush not complete - retry */ - case CLK_REQ_OFF_RXSTALE_ISSUED: - case CLK_REQ_OFF_FLUSH_ISSUED: - return 0; /* RXSTALE flush not complete - retry */ - case CLK_REQ_OFF_RXSTALE_FLUSHED: - break; /* continue */ - } - - if (msm_uport->rx.flush != FLUSH_SHUTDOWN) { - if (msm_uport->rx.flush == FLUSH_NONE) - msm_hs_stop_rx_locked(uport); - return 0; /* come back later to really clock off */ - } - - /* we really want to clock off */ - clk_disable(msm_uport->clk); - msm_uport->clk_state = MSM_HS_CLK_OFF; - - if (use_low_power_rx_wakeup(msm_uport)) { - msm_uport->rx_wakeup.ignore = 1; - enable_irq(msm_uport->rx_wakeup.irq); - } - return 1; -} - -static enum hrtimer_restart msm_hs_clk_off_retry(struct hrtimer *timer) -{ - unsigned long flags; - int ret = HRTIMER_NORESTART; - struct msm_hs_port *msm_uport = container_of(timer, struct msm_hs_port, - clk_off_timer); - struct uart_port *uport = &msm_uport->uport; - - spin_lock_irqsave(&uport->lock, flags); - - if (!msm_hs_check_clock_off_locked(uport)) { - hrtimer_forward_now(timer, msm_uport->clk_off_delay); - ret = HRTIMER_RESTART; - } - - spin_unlock_irqrestore(&uport->lock, flags); - - return ret; -} - -static irqreturn_t msm_hs_isr(int irq, void *dev) -{ - unsigned long flags; - unsigned long isr_status; - struct msm_hs_port *msm_uport = dev; - struct uart_port *uport = &msm_uport->uport; - struct circ_buf *tx_buf = &uport->state->xmit; - struct msm_hs_tx *tx = &msm_uport->tx; - struct msm_hs_rx *rx = &msm_uport->rx; - - spin_lock_irqsave(&uport->lock, flags); - - isr_status = msm_hs_read(uport, UARTDM_MISR_ADDR); - - /* Uart RX starting */ - if (isr_status & UARTDM_ISR_RXLEV_BMSK) { - msm_uport->imr_reg &= ~UARTDM_ISR_RXLEV_BMSK; - msm_hs_write(uport, UARTDM_IMR_ADDR, msm_uport->imr_reg); - } - /* Stale rx interrupt */ - if (isr_status & UARTDM_ISR_RXSTALE_BMSK) { - msm_hs_write(uport, UARTDM_CR_ADDR, STALE_EVENT_DISABLE); - msm_hs_write(uport, UARTDM_CR_ADDR, RESET_STALE_INT); - - if (msm_uport->clk_req_off_state == CLK_REQ_OFF_RXSTALE_ISSUED) - msm_uport->clk_req_off_state = - CLK_REQ_OFF_FLUSH_ISSUED; - if (rx->flush == FLUSH_NONE) { - rx->flush = FLUSH_DATA_READY; - msm_dmov_stop_cmd(msm_uport->dma_rx_channel, NULL, 1); - } - } - /* tx ready interrupt */ - if (isr_status & UARTDM_ISR_TX_READY_BMSK) { - /* Clear TX Ready */ - msm_hs_write(uport, UARTDM_CR_ADDR, CLEAR_TX_READY); - - if (msm_uport->clk_state == MSM_HS_CLK_REQUEST_OFF) { - msm_uport->imr_reg |= UARTDM_ISR_TXLEV_BMSK; - msm_hs_write(uport, UARTDM_IMR_ADDR, - msm_uport->imr_reg); - } - - /* Complete DMA TX transactions and submit new transactions */ - tx_buf->tail = (tx_buf->tail + tx->tx_count) & ~UART_XMIT_SIZE; - - tx->dma_in_flight = 0; - - uport->icount.tx += tx->tx_count; - if (tx->tx_ready_int_en) - msm_hs_submit_tx_locked(uport); - - if (uart_circ_chars_pending(tx_buf) < WAKEUP_CHARS) - uart_write_wakeup(uport); - } - if (isr_status & UARTDM_ISR_TXLEV_BMSK) { - /* TX FIFO is empty */ - msm_uport->imr_reg &= ~UARTDM_ISR_TXLEV_BMSK; - msm_hs_write(uport, UARTDM_IMR_ADDR, msm_uport->imr_reg); - if (!msm_hs_check_clock_off_locked(uport)) - hrtimer_start(&msm_uport->clk_off_timer, - msm_uport->clk_off_delay, - HRTIMER_MODE_REL); - } - - /* Change in CTS interrupt */ - if (isr_status & UARTDM_ISR_DELTA_CTS_BMSK) - msm_hs_handle_delta_cts_locked(uport); - - spin_unlock_irqrestore(&uport->lock, flags); - - return IRQ_HANDLED; -} - -void msm_hs_request_clock_off_locked(struct uart_port *uport) -{ - struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport); - - if (msm_uport->clk_state == MSM_HS_CLK_ON) { - msm_uport->clk_state = MSM_HS_CLK_REQUEST_OFF; - msm_uport->clk_req_off_state = CLK_REQ_OFF_START; - if (!use_low_power_rx_wakeup(msm_uport)) - set_rfr_locked(uport, 0); - msm_uport->imr_reg |= UARTDM_ISR_TXLEV_BMSK; - msm_hs_write(uport, UARTDM_IMR_ADDR, msm_uport->imr_reg); - } -} - -/** - * msm_hs_request_clock_off - request to (i.e. asynchronously) turn off uart - * clock once pending TX is flushed and Rx DMA command is terminated. - * @uport: uart_port structure for the device instance. - * - * This functions puts the device into a partially active low power mode. It - * waits to complete all pending tx transactions, flushes ongoing Rx DMA - * command and terminates UART side Rx transaction, puts UART HW in non DMA - * mode and then clocks off the device. A client calls this when no UART - * data is expected. msm_request_clock_on() must be called before any further - * UART can be sent or received. - */ -void msm_hs_request_clock_off(struct uart_port *uport) -{ - unsigned long flags; - - spin_lock_irqsave(&uport->lock, flags); - msm_hs_request_clock_off_locked(uport); - spin_unlock_irqrestore(&uport->lock, flags); -} - -void msm_hs_request_clock_on_locked(struct uart_port *uport) -{ - struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport); - unsigned int data; - - switch (msm_uport->clk_state) { - case MSM_HS_CLK_OFF: - clk_enable(msm_uport->clk); - disable_irq_nosync(msm_uport->rx_wakeup.irq); - /* fall-through */ - case MSM_HS_CLK_REQUEST_OFF: - if (msm_uport->rx.flush == FLUSH_STOP || - msm_uport->rx.flush == FLUSH_SHUTDOWN) { - msm_hs_write(uport, UARTDM_CR_ADDR, RESET_RX); - data = msm_hs_read(uport, UARTDM_DMEN_ADDR); - data |= UARTDM_RX_DM_EN_BMSK; - msm_hs_write(uport, UARTDM_DMEN_ADDR, data); - } - hrtimer_try_to_cancel(&msm_uport->clk_off_timer); - if (msm_uport->rx.flush == FLUSH_SHUTDOWN) - msm_hs_start_rx_locked(uport); - if (!use_low_power_rx_wakeup(msm_uport)) - set_rfr_locked(uport, 1); - if (msm_uport->rx.flush == FLUSH_STOP) - msm_uport->rx.flush = FLUSH_IGNORE; - msm_uport->clk_state = MSM_HS_CLK_ON; - break; - case MSM_HS_CLK_ON: - break; - case MSM_HS_CLK_PORT_OFF: - break; - } -} - -/** - * msm_hs_request_clock_on - Switch the device from partially active low - * power mode to fully active (i.e. clock on) mode. - * @uport: uart_port structure for the device. - * - * This function switches on the input clock, puts UART HW into DMA mode - * and enqueues an Rx DMA command if the device was in partially active - * mode. It has no effect if called with the device in inactive state. - */ -void msm_hs_request_clock_on(struct uart_port *uport) -{ - unsigned long flags; - - spin_lock_irqsave(&uport->lock, flags); - msm_hs_request_clock_on_locked(uport); - spin_unlock_irqrestore(&uport->lock, flags); -} - -static irqreturn_t msm_hs_rx_wakeup_isr(int irq, void *dev) -{ - unsigned int wakeup = 0; - unsigned long flags; - struct msm_hs_port *msm_uport = dev; - struct uart_port *uport = &msm_uport->uport; - - spin_lock_irqsave(&uport->lock, flags); - if (msm_uport->clk_state == MSM_HS_CLK_OFF) { - /* ignore the first irq - it is a pending irq that occurred - * before enable_irq() */ - if (msm_uport->rx_wakeup.ignore) - msm_uport->rx_wakeup.ignore = 0; - else - wakeup = 1; - } - - if (wakeup) { - /* the uart was clocked off during an rx, wake up and - * optionally inject char into tty rx */ - msm_hs_request_clock_on_locked(uport); - if (msm_uport->rx_wakeup.inject_rx) { - tty_insert_flip_char(&uport->state->port, - msm_uport->rx_wakeup.rx_to_inject, - TTY_NORMAL); - queue_work(msm_hs_workqueue, &msm_uport->rx.tty_work); - } - } - - spin_unlock_irqrestore(&uport->lock, flags); - - return IRQ_HANDLED; -} - -static const char *msm_hs_type(struct uart_port *port) -{ - return (port->type == PORT_MSM) ? "MSM_HS_UART" : NULL; -} - -/* Called when port is opened */ -static int msm_hs_startup(struct uart_port *uport) -{ - int ret; - int rfr_level; - unsigned long flags; - unsigned int data; - struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport); - struct circ_buf *tx_buf = &uport->state->xmit; - struct msm_hs_tx *tx = &msm_uport->tx; - struct msm_hs_rx *rx = &msm_uport->rx; - - rfr_level = uport->fifosize; - if (rfr_level > 16) - rfr_level -= 16; - - tx->dma_base = dma_map_single(uport->dev, tx_buf->buf, UART_XMIT_SIZE, - DMA_TO_DEVICE); - - /* do not let tty layer execute RX in global workqueue, use a - * dedicated workqueue managed by this driver */ - uport->state->port.low_latency = 1; - - /* turn on uart clk */ - ret = msm_hs_init_clk_locked(uport); - if (unlikely(ret)) { - printk(KERN_ERR "Turning uartclk failed!\n"); - goto err_msm_hs_init_clk; - } - - /* Set auto RFR Level */ - data = msm_hs_read(uport, UARTDM_MR1_ADDR); - data &= ~UARTDM_MR1_AUTO_RFR_LEVEL1_BMSK; - data &= ~UARTDM_MR1_AUTO_RFR_LEVEL0_BMSK; - data |= (UARTDM_MR1_AUTO_RFR_LEVEL1_BMSK & (rfr_level << 2)); - data |= (UARTDM_MR1_AUTO_RFR_LEVEL0_BMSK & rfr_level); - msm_hs_write(uport, UARTDM_MR1_ADDR, data); - - /* Make sure RXSTALE count is non-zero */ - data = msm_hs_read(uport, UARTDM_IPR_ADDR); - if (!data) { - data |= 0x1f & UARTDM_IPR_STALE_LSB_BMSK; - msm_hs_write(uport, UARTDM_IPR_ADDR, data); - } - - /* Enable Data Mover Mode */ - data = UARTDM_TX_DM_EN_BMSK | UARTDM_RX_DM_EN_BMSK; - msm_hs_write(uport, UARTDM_DMEN_ADDR, data); - - /* Reset TX */ - msm_hs_write(uport, UARTDM_CR_ADDR, RESET_TX); - msm_hs_write(uport, UARTDM_CR_ADDR, RESET_RX); - msm_hs_write(uport, UARTDM_CR_ADDR, RESET_ERROR_STATUS); - msm_hs_write(uport, UARTDM_CR_ADDR, RESET_BREAK_INT); - msm_hs_write(uport, UARTDM_CR_ADDR, RESET_STALE_INT); - msm_hs_write(uport, UARTDM_CR_ADDR, RESET_CTS); - msm_hs_write(uport, UARTDM_CR_ADDR, RFR_LOW); - /* Turn on Uart Receiver */ - msm_hs_write(uport, UARTDM_CR_ADDR, UARTDM_CR_RX_EN_BMSK); - - /* Turn on Uart Transmitter */ - msm_hs_write(uport, UARTDM_CR_ADDR, UARTDM_CR_TX_EN_BMSK); - - /* Initialize the tx */ - tx->tx_ready_int_en = 0; - tx->dma_in_flight = 0; - - tx->xfer.complete_func = msm_hs_dmov_tx_callback; - tx->xfer.execute_func = NULL; - - tx->command_ptr->cmd = CMD_LC | - CMD_DST_CRCI(msm_uport->dma_tx_crci) | CMD_MODE_BOX; - - tx->command_ptr->src_dst_len = (MSM_UARTDM_BURST_SIZE << 16) - | (MSM_UARTDM_BURST_SIZE); - - tx->command_ptr->row_offset = (MSM_UARTDM_BURST_SIZE << 16); - - tx->command_ptr->dst_row_addr = - msm_uport->uport.mapbase + UARTDM_TF_ADDR; - - - /* Turn on Uart Receive */ - rx->xfer.complete_func = msm_hs_dmov_rx_callback; - rx->xfer.execute_func = NULL; - - rx->command_ptr->cmd = CMD_LC | - CMD_SRC_CRCI(msm_uport->dma_rx_crci) | CMD_MODE_BOX; - - rx->command_ptr->src_dst_len = (MSM_UARTDM_BURST_SIZE << 16) - | (MSM_UARTDM_BURST_SIZE); - rx->command_ptr->row_offset = MSM_UARTDM_BURST_SIZE; - rx->command_ptr->src_row_addr = uport->mapbase + UARTDM_RF_ADDR; - - - msm_uport->imr_reg |= UARTDM_ISR_RXSTALE_BMSK; - /* Enable reading the current CTS, no harm even if CTS is ignored */ - msm_uport->imr_reg |= UARTDM_ISR_CURRENT_CTS_BMSK; - - msm_hs_write(uport, UARTDM_TFWR_ADDR, 0); /* TXLEV on empty TX fifo */ - - - ret = request_irq(uport->irq, msm_hs_isr, IRQF_TRIGGER_HIGH, - "msm_hs_uart", msm_uport); - if (unlikely(ret)) { - printk(KERN_ERR "Request msm_hs_uart IRQ failed!\n"); - goto err_request_irq; - } - if (use_low_power_rx_wakeup(msm_uport)) { - ret = request_irq(msm_uport->rx_wakeup.irq, - msm_hs_rx_wakeup_isr, - IRQF_TRIGGER_FALLING, - "msm_hs_rx_wakeup", msm_uport); - if (unlikely(ret)) { - printk(KERN_ERR "Request msm_hs_rx_wakeup IRQ failed!\n"); - free_irq(uport->irq, msm_uport); - goto err_request_irq; - } - disable_irq(msm_uport->rx_wakeup.irq); - } - - spin_lock_irqsave(&uport->lock, flags); - - msm_hs_write(uport, UARTDM_RFWR_ADDR, 0); - msm_hs_start_rx_locked(uport); - - spin_unlock_irqrestore(&uport->lock, flags); - ret = pm_runtime_set_active(uport->dev); - if (ret) - dev_err(uport->dev, "set active error:%d\n", ret); - pm_runtime_enable(uport->dev); - - return 0; - -err_request_irq: -err_msm_hs_init_clk: - dma_unmap_single(uport->dev, tx->dma_base, - UART_XMIT_SIZE, DMA_TO_DEVICE); - return ret; -} - -/* Initialize tx and rx data structures */ -static int uartdm_init_port(struct uart_port *uport) -{ - int ret = 0; - struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport); - struct msm_hs_tx *tx = &msm_uport->tx; - struct msm_hs_rx *rx = &msm_uport->rx; - - /* Allocate the command pointer. Needs to be 64 bit aligned */ - tx->command_ptr = kmalloc(sizeof(dmov_box), GFP_KERNEL | __GFP_DMA); - if (!tx->command_ptr) - return -ENOMEM; - - tx->command_ptr_ptr = kmalloc(sizeof(u32), GFP_KERNEL | __GFP_DMA); - if (!tx->command_ptr_ptr) { - ret = -ENOMEM; - goto err_tx_command_ptr_ptr; - } - - tx->mapped_cmd_ptr = dma_map_single(uport->dev, tx->command_ptr, - sizeof(dmov_box), DMA_TO_DEVICE); - tx->mapped_cmd_ptr_ptr = dma_map_single(uport->dev, - tx->command_ptr_ptr, - sizeof(u32), DMA_TO_DEVICE); - tx->xfer.cmdptr = DMOV_CMD_ADDR(tx->mapped_cmd_ptr_ptr); - - init_waitqueue_head(&rx->wait); - - rx->pool = dma_pool_create("rx_buffer_pool", uport->dev, - UARTDM_RX_BUF_SIZE, 16, 0); - if (!rx->pool) { - pr_err("%s(): cannot allocate rx_buffer_pool", __func__); - ret = -ENOMEM; - goto err_dma_pool_create; - } - - rx->buffer = dma_pool_alloc(rx->pool, GFP_KERNEL, &rx->rbuffer); - if (!rx->buffer) { - pr_err("%s(): cannot allocate rx->buffer", __func__); - ret = -ENOMEM; - goto err_dma_pool_alloc; - } - - /* Allocate the command pointer. Needs to be 64 bit aligned */ - rx->command_ptr = kmalloc(sizeof(dmov_box), GFP_KERNEL | __GFP_DMA); - if (!rx->command_ptr) { - pr_err("%s(): cannot allocate rx->command_ptr", __func__); - ret = -ENOMEM; - goto err_rx_command_ptr; - } - - rx->command_ptr_ptr = kmalloc(sizeof(u32), GFP_KERNEL | __GFP_DMA); - if (!rx->command_ptr_ptr) { - pr_err("%s(): cannot allocate rx->command_ptr_ptr", __func__); - ret = -ENOMEM; - goto err_rx_command_ptr_ptr; - } - - rx->command_ptr->num_rows = ((UARTDM_RX_BUF_SIZE >> 4) << 16) | - (UARTDM_RX_BUF_SIZE >> 4); - - rx->command_ptr->dst_row_addr = rx->rbuffer; - - rx->mapped_cmd_ptr = dma_map_single(uport->dev, rx->command_ptr, - sizeof(dmov_box), DMA_TO_DEVICE); - - *rx->command_ptr_ptr = CMD_PTR_LP | DMOV_CMD_ADDR(rx->mapped_cmd_ptr); - - rx->cmdptr_dmaaddr = dma_map_single(uport->dev, rx->command_ptr_ptr, - sizeof(u32), DMA_TO_DEVICE); - rx->xfer.cmdptr = DMOV_CMD_ADDR(rx->cmdptr_dmaaddr); - - INIT_WORK(&rx->tty_work, msm_hs_tty_flip_buffer_work); - - return ret; - -err_rx_command_ptr_ptr: - kfree(rx->command_ptr); -err_rx_command_ptr: - dma_pool_free(msm_uport->rx.pool, msm_uport->rx.buffer, - msm_uport->rx.rbuffer); -err_dma_pool_alloc: - dma_pool_destroy(msm_uport->rx.pool); -err_dma_pool_create: - dma_unmap_single(uport->dev, msm_uport->tx.mapped_cmd_ptr_ptr, - sizeof(u32), DMA_TO_DEVICE); - dma_unmap_single(uport->dev, msm_uport->tx.mapped_cmd_ptr, - sizeof(dmov_box), DMA_TO_DEVICE); - kfree(msm_uport->tx.command_ptr_ptr); -err_tx_command_ptr_ptr: - kfree(msm_uport->tx.command_ptr); - return ret; -} - -static int msm_hs_probe(struct platform_device *pdev) -{ - int ret; - struct uart_port *uport; - struct msm_hs_port *msm_uport; - struct resource *resource; - const struct msm_serial_hs_platform_data *pdata = - dev_get_platdata(&pdev->dev); - - if (pdev->id < 0 || pdev->id >= UARTDM_NR) { - printk(KERN_ERR "Invalid plaform device ID = %d\n", pdev->id); - return -EINVAL; - } - - msm_uport = &q_uart_port[pdev->id]; - uport = &msm_uport->uport; - - uport->dev = &pdev->dev; - - resource = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (unlikely(!resource)) - return -ENXIO; - - uport->mapbase = resource->start; - uport->irq = platform_get_irq(pdev, 0); - if (unlikely(uport->irq < 0)) - return -ENXIO; - - if (unlikely(irq_set_irq_wake(uport->irq, 1))) - return -ENXIO; - - if (pdata == NULL || pdata->rx_wakeup_irq < 0) - msm_uport->rx_wakeup.irq = -1; - else { - msm_uport->rx_wakeup.irq = pdata->rx_wakeup_irq; - msm_uport->rx_wakeup.ignore = 1; - msm_uport->rx_wakeup.inject_rx = pdata->inject_rx_on_wakeup; - msm_uport->rx_wakeup.rx_to_inject = pdata->rx_to_inject; - - if (unlikely(msm_uport->rx_wakeup.irq < 0)) - return -ENXIO; - - if (unlikely(irq_set_irq_wake(msm_uport->rx_wakeup.irq, 1))) - return -ENXIO; - } - - if (pdata == NULL) - msm_uport->exit_lpm_cb = NULL; - else - msm_uport->exit_lpm_cb = pdata->exit_lpm_cb; - - resource = platform_get_resource_byname(pdev, IORESOURCE_DMA, - "uartdm_channels"); - if (unlikely(!resource)) - return -ENXIO; - - msm_uport->dma_tx_channel = resource->start; - msm_uport->dma_rx_channel = resource->end; - - resource = platform_get_resource_byname(pdev, IORESOURCE_DMA, - "uartdm_crci"); - if (unlikely(!resource)) - return -ENXIO; - - msm_uport->dma_tx_crci = resource->start; - msm_uport->dma_rx_crci = resource->end; - - uport->iotype = UPIO_MEM; - uport->fifosize = UART_FIFOSIZE; - uport->ops = &msm_hs_ops; - uport->flags = UPF_BOOT_AUTOCONF; - uport->uartclk = UARTCLK; - msm_uport->imr_reg = 0x0; - msm_uport->clk = clk_get(&pdev->dev, "uartdm_clk"); - if (IS_ERR(msm_uport->clk)) - return PTR_ERR(msm_uport->clk); - - ret = uartdm_init_port(uport); - if (unlikely(ret)) - return ret; - - msm_uport->clk_state = MSM_HS_CLK_PORT_OFF; - hrtimer_init(&msm_uport->clk_off_timer, CLOCK_MONOTONIC, - HRTIMER_MODE_REL); - msm_uport->clk_off_timer.function = msm_hs_clk_off_retry; - msm_uport->clk_off_delay = ktime_set(0, 1000000); /* 1ms */ - - uport->line = pdev->id; - return uart_add_one_port(&msm_hs_driver, uport); -} - -static int __init msm_serial_hs_init(void) -{ - int ret, i; - - /* Init all UARTS as non-configured */ - for (i = 0; i < UARTDM_NR; i++) - q_uart_port[i].uport.type = PORT_UNKNOWN; - - msm_hs_workqueue = create_singlethread_workqueue("msm_serial_hs"); - if (unlikely(!msm_hs_workqueue)) - return -ENOMEM; - - ret = uart_register_driver(&msm_hs_driver); - if (unlikely(ret)) { - printk(KERN_ERR "%s failed to load\n", __func__); - goto err_uart_register_driver; - } - - ret = platform_driver_register(&msm_serial_hs_platform_driver); - if (ret) { - printk(KERN_ERR "%s failed to load\n", __func__); - goto err_platform_driver_register; - } - - return ret; - -err_platform_driver_register: - uart_unregister_driver(&msm_hs_driver); -err_uart_register_driver: - destroy_workqueue(msm_hs_workqueue); - return ret; -} -module_init(msm_serial_hs_init); - -/* - * Called by the upper layer when port is closed. - * - Disables the port - * - Unhook the ISR - */ -static void msm_hs_shutdown(struct uart_port *uport) -{ - unsigned long flags; - struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport); - - BUG_ON(msm_uport->rx.flush < FLUSH_STOP); - - spin_lock_irqsave(&uport->lock, flags); - clk_enable(msm_uport->clk); - - /* Disable the transmitter */ - msm_hs_write(uport, UARTDM_CR_ADDR, UARTDM_CR_TX_DISABLE_BMSK); - /* Disable the receiver */ - msm_hs_write(uport, UARTDM_CR_ADDR, UARTDM_CR_RX_DISABLE_BMSK); - - pm_runtime_disable(uport->dev); - pm_runtime_set_suspended(uport->dev); - - /* Free the interrupt */ - free_irq(uport->irq, msm_uport); - if (use_low_power_rx_wakeup(msm_uport)) - free_irq(msm_uport->rx_wakeup.irq, msm_uport); - - msm_uport->imr_reg = 0; - msm_hs_write(uport, UARTDM_IMR_ADDR, msm_uport->imr_reg); - - wait_event(msm_uport->rx.wait, msm_uport->rx.flush == FLUSH_SHUTDOWN); - - clk_disable(msm_uport->clk); /* to balance local clk_enable() */ - if (msm_uport->clk_state != MSM_HS_CLK_OFF) - clk_disable(msm_uport->clk); /* to balance clk_state */ - msm_uport->clk_state = MSM_HS_CLK_PORT_OFF; - - dma_unmap_single(uport->dev, msm_uport->tx.dma_base, - UART_XMIT_SIZE, DMA_TO_DEVICE); - - spin_unlock_irqrestore(&uport->lock, flags); - - if (cancel_work_sync(&msm_uport->rx.tty_work)) - msm_hs_tty_flip_buffer_work(&msm_uport->rx.tty_work); -} - -static void __exit msm_serial_hs_exit(void) -{ - flush_workqueue(msm_hs_workqueue); - destroy_workqueue(msm_hs_workqueue); - platform_driver_unregister(&msm_serial_hs_platform_driver); - uart_unregister_driver(&msm_hs_driver); -} -module_exit(msm_serial_hs_exit); - -#ifdef CONFIG_PM -static int msm_hs_runtime_idle(struct device *dev) -{ - /* - * returning success from idle results in runtime suspend to be - * called - */ - return 0; -} - -static int msm_hs_runtime_resume(struct device *dev) -{ - struct platform_device *pdev = container_of(dev, struct - platform_device, dev); - struct msm_hs_port *msm_uport = &q_uart_port[pdev->id]; - - msm_hs_request_clock_on(&msm_uport->uport); - return 0; -} - -static int msm_hs_runtime_suspend(struct device *dev) -{ - struct platform_device *pdev = container_of(dev, struct - platform_device, dev); - struct msm_hs_port *msm_uport = &q_uart_port[pdev->id]; - - msm_hs_request_clock_off(&msm_uport->uport); - return 0; -} -#else -#define msm_hs_runtime_idle NULL -#define msm_hs_runtime_resume NULL -#define msm_hs_runtime_suspend NULL -#endif - -static const struct dev_pm_ops msm_hs_dev_pm_ops = { - .runtime_suspend = msm_hs_runtime_suspend, - .runtime_resume = msm_hs_runtime_resume, - .runtime_idle = msm_hs_runtime_idle, -}; - -static struct platform_driver msm_serial_hs_platform_driver = { - .probe = msm_hs_probe, - .remove = msm_hs_remove, - .driver = { - .name = "msm_serial_hs", - .pm = &msm_hs_dev_pm_ops, - }, -}; - -static struct uart_driver msm_hs_driver = { - .owner = THIS_MODULE, - .driver_name = "msm_serial_hs", - .dev_name = "ttyHS", - .nr = UARTDM_NR, - .cons = 0, -}; - -static struct uart_ops msm_hs_ops = { - .tx_empty = msm_hs_tx_empty, - .set_mctrl = msm_hs_set_mctrl_locked, - .get_mctrl = msm_hs_get_mctrl_locked, - .stop_tx = msm_hs_stop_tx_locked, - .start_tx = msm_hs_start_tx_locked, - .stop_rx = msm_hs_stop_rx_locked, - .enable_ms = msm_hs_enable_ms_locked, - .break_ctl = msm_hs_break_ctl, - .startup = msm_hs_startup, - .shutdown = msm_hs_shutdown, - .set_termios = msm_hs_set_termios, - .pm = msm_hs_pm, - .type = msm_hs_type, - .config_port = msm_hs_config_port, - .release_port = msm_hs_release_port, - .request_port = msm_hs_request_port, -}; - -MODULE_DESCRIPTION("High Speed UART Driver for the MSM chipset"); -MODULE_VERSION("1.2"); -MODULE_LICENSE("GPL v2"); diff --git a/include/linux/platform_data/msm_serial_hs.h b/include/linux/platform_data/msm_serial_hs.h deleted file mode 100644 index 98a2046f8b31..000000000000 --- a/include/linux/platform_data/msm_serial_hs.h +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright (C) 2008 Google, Inc. - * Author: Nick Pelly - * - * This software is licensed under the terms of the GNU General Public - * License version 2, as published by the Free Software Foundation, and - * may be copied, distributed, and modified under those terms. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#ifndef __ASM_ARCH_MSM_SERIAL_HS_H -#define __ASM_ARCH_MSM_SERIAL_HS_H - -#include - -/* API to request the uart clock off or on for low power management - * Clients should call request_clock_off() when no uart data is expected, - * and must call request_clock_on() before any further uart data can be - * received. */ -extern void msm_hs_request_clock_off(struct uart_port *uport); -extern void msm_hs_request_clock_on(struct uart_port *uport); - -/** - * struct msm_serial_hs_platform_data - * @rx_wakeup_irq: Rx activity irq - * @rx_to_inject: extra character to be inserted to Rx tty on wakeup - * @inject_rx: 1 = insert rx_to_inject. 0 = do not insert extra character - * @exit_lpm_cb: function called before every Tx transaction - * - * This is an optional structure required for UART Rx GPIO IRQ based - * wakeup from low power state. UART wakeup can be triggered by RX activity - * (using a wakeup GPIO on the UART RX pin). This should only be used if - * there is not a wakeup GPIO on the UART CTS, and the first RX byte is - * known (eg., with the Bluetooth Texas Instruments HCILL protocol), - * since the first RX byte will always be lost. RTS will be asserted even - * while the UART is clocked off in this mode of operation. - */ -struct msm_serial_hs_platform_data { - int rx_wakeup_irq; - unsigned char inject_rx_on_wakeup; - char rx_to_inject; - void (*exit_lpm_cb)(struct uart_port *); -}; - -#endif -- cgit v1.2.3 From 88dcd2dab5c23b1c9cfc396246d8f476c872f0ca Mon Sep 17 00:00:00 2001 From: Nicholas Bellinger Date: Thu, 26 Feb 2015 22:19:15 -0800 Subject: iscsi-target: Convert iscsi_thread_set usage to kthread.h This patch converts iscsi-target code to use modern kthread.h API callers for creating RX/TX threads for each new iscsi_conn descriptor, and releasing associated RX/TX threads during connection shutdown. This is done using iscsit_start_kthreads() -> kthread_run() to start new kthreads from within iscsi_post_login_handler(), and invoking kthread_stop() from existing iscsit_close_connection() code. Also, convert iscsit_logout_post_handler_closesession() code to use cmpxchg when determing when iscsit_cause_connection_reinstatement() needs to sleep waiting for completion. Reported-by: Sagi Grimberg Tested-by: Sagi Grimberg Cc: Slava Shwartsman Cc: # v3.10+ Signed-off-by: Nicholas Bellinger --- drivers/target/iscsi/iscsi_target.c | 104 +++++++++++++----------------- drivers/target/iscsi/iscsi_target_erl0.c | 13 ++-- drivers/target/iscsi/iscsi_target_login.c | 59 +++++++++++++++-- include/target/iscsi/iscsi_target_core.h | 7 ++ 4 files changed, 114 insertions(+), 69 deletions(-) (limited to 'include') diff --git a/drivers/target/iscsi/iscsi_target.c b/drivers/target/iscsi/iscsi_target.c index 47af86167b49..163773fb4f84 100644 --- a/drivers/target/iscsi/iscsi_target.c +++ b/drivers/target/iscsi/iscsi_target.c @@ -537,7 +537,7 @@ static struct iscsit_transport iscsi_target_transport = { static int __init iscsi_target_init_module(void) { - int ret = 0; + int ret = 0, size; pr_debug("iSCSI-Target "ISCSIT_VERSION"\n"); @@ -546,6 +546,7 @@ static int __init iscsi_target_init_module(void) pr_err("Unable to allocate memory for iscsit_global\n"); return -1; } + spin_lock_init(&iscsit_global->ts_bitmap_lock); mutex_init(&auth_id_lock); spin_lock_init(&sess_idr_lock); idr_init(&tiqn_idr); @@ -555,15 +556,11 @@ static int __init iscsi_target_init_module(void) if (ret < 0) goto out; - ret = iscsi_thread_set_init(); - if (ret < 0) + size = BITS_TO_LONGS(ISCSIT_BITMAP_BITS) * sizeof(long); + iscsit_global->ts_bitmap = vzalloc(size); + if (!iscsit_global->ts_bitmap) { + pr_err("Unable to allocate iscsit_global->ts_bitmap\n"); goto configfs_out; - - if (iscsi_allocate_thread_sets(TARGET_THREAD_SET_COUNT) != - TARGET_THREAD_SET_COUNT) { - pr_err("iscsi_allocate_thread_sets() returned" - " unexpected value!\n"); - goto ts_out1; } lio_qr_cache = kmem_cache_create("lio_qr_cache", @@ -572,7 +569,7 @@ static int __init iscsi_target_init_module(void) if (!lio_qr_cache) { pr_err("nable to kmem_cache_create() for" " lio_qr_cache\n"); - goto ts_out2; + goto bitmap_out; } lio_dr_cache = kmem_cache_create("lio_dr_cache", @@ -617,10 +614,8 @@ dr_out: kmem_cache_destroy(lio_dr_cache); qr_out: kmem_cache_destroy(lio_qr_cache); -ts_out2: - iscsi_deallocate_thread_sets(); -ts_out1: - iscsi_thread_set_free(); +bitmap_out: + vfree(iscsit_global->ts_bitmap); configfs_out: iscsi_target_deregister_configfs(); out: @@ -630,8 +625,6 @@ out: static void __exit iscsi_target_cleanup_module(void) { - iscsi_deallocate_thread_sets(); - iscsi_thread_set_free(); iscsit_release_discovery_tpg(); iscsit_unregister_transport(&iscsi_target_transport); kmem_cache_destroy(lio_qr_cache); @@ -641,6 +634,7 @@ static void __exit iscsi_target_cleanup_module(void) iscsi_target_deregister_configfs(); + vfree(iscsit_global->ts_bitmap); kfree(iscsit_global); } @@ -3709,17 +3703,16 @@ static int iscsit_send_reject( void iscsit_thread_get_cpumask(struct iscsi_conn *conn) { - struct iscsi_thread_set *ts = conn->thread_set; int ord, cpu; /* - * thread_id is assigned from iscsit_global->ts_bitmap from - * within iscsi_thread_set.c:iscsi_allocate_thread_sets() + * bitmap_id is assigned from iscsit_global->ts_bitmap from + * within iscsit_start_kthreads() * - * Here we use thread_id to determine which CPU that this - * iSCSI connection's iscsi_thread_set will be scheduled to + * Here we use bitmap_id to determine which CPU that this + * iSCSI connection's RX/TX threads will be scheduled to * execute upon. */ - ord = ts->thread_id % cpumask_weight(cpu_online_mask); + ord = conn->bitmap_id % cpumask_weight(cpu_online_mask); for_each_online_cpu(cpu) { if (ord-- == 0) { cpumask_set_cpu(cpu, conn->conn_cpumask); @@ -3908,7 +3901,7 @@ check_rsp_state: switch (state) { case ISTATE_SEND_LOGOUTRSP: if (!iscsit_logout_post_handler(cmd, conn)) - goto restart; + return -ECONNRESET; /* fall through */ case ISTATE_SEND_STATUS: case ISTATE_SEND_ASYNCMSG: @@ -3936,8 +3929,6 @@ check_rsp_state: err: return -1; -restart: - return -EAGAIN; } static int iscsit_handle_response_queue(struct iscsi_conn *conn) @@ -3964,21 +3955,13 @@ static int iscsit_handle_response_queue(struct iscsi_conn *conn) int iscsi_target_tx_thread(void *arg) { int ret = 0; - struct iscsi_conn *conn; - struct iscsi_thread_set *ts = arg; + struct iscsi_conn *conn = arg; /* * Allow ourselves to be interrupted by SIGINT so that a * connection recovery / failure event can be triggered externally. */ allow_signal(SIGINT); -restart: - conn = iscsi_tx_thread_pre_handler(ts); - if (!conn) - goto out; - - ret = 0; - while (!kthread_should_stop()) { /* * Ensure that both TX and RX per connection kthreads @@ -3987,11 +3970,9 @@ restart: iscsit_thread_check_cpumask(conn, current, 1); wait_event_interruptible(conn->queues_wq, - !iscsit_conn_all_queues_empty(conn) || - ts->status == ISCSI_THREAD_SET_RESET); + !iscsit_conn_all_queues_empty(conn)); - if ((ts->status == ISCSI_THREAD_SET_RESET) || - signal_pending(current)) + if (signal_pending(current)) goto transport_err; get_immediate: @@ -4002,15 +3983,14 @@ get_immediate: ret = iscsit_handle_response_queue(conn); if (ret == 1) goto get_immediate; - else if (ret == -EAGAIN) - goto restart; + else if (ret == -ECONNRESET) + goto out; else if (ret < 0) goto transport_err; } transport_err: iscsit_take_action_for_connection_exit(conn); - goto restart; out: return 0; } @@ -4105,8 +4085,7 @@ int iscsi_target_rx_thread(void *arg) int ret; u8 buffer[ISCSI_HDR_LEN], opcode; u32 checksum = 0, digest = 0; - struct iscsi_conn *conn = NULL; - struct iscsi_thread_set *ts = arg; + struct iscsi_conn *conn = arg; struct kvec iov; /* * Allow ourselves to be interrupted by SIGINT so that a @@ -4114,11 +4093,6 @@ int iscsi_target_rx_thread(void *arg) */ allow_signal(SIGINT); -restart: - conn = iscsi_rx_thread_pre_handler(ts); - if (!conn) - goto out; - if (conn->conn_transport->transport_type == ISCSI_INFINIBAND) { struct completion comp; int rc; @@ -4128,7 +4102,7 @@ restart: if (rc < 0) goto transport_err; - goto out; + goto transport_err; } while (!kthread_should_stop()) { @@ -4204,8 +4178,6 @@ transport_err: if (!signal_pending(current)) atomic_set(&conn->transport_failed, 1); iscsit_take_action_for_connection_exit(conn); - goto restart; -out: return 0; } @@ -4261,7 +4233,24 @@ int iscsit_close_connection( */ complete(&conn->conn_logout_comp); - iscsi_release_thread_set(conn); + if (!strcmp(current->comm, ISCSI_RX_THREAD_NAME)) { + if (conn->tx_thread && + cmpxchg(&conn->tx_thread_active, true, false)) { + send_sig(SIGINT, conn->tx_thread, 1); + kthread_stop(conn->tx_thread); + } + } else if (!strcmp(current->comm, ISCSI_TX_THREAD_NAME)) { + if (conn->rx_thread && + cmpxchg(&conn->rx_thread_active, true, false)) { + send_sig(SIGINT, conn->rx_thread, 1); + kthread_stop(conn->rx_thread); + } + } + + spin_lock(&iscsit_global->ts_bitmap_lock); + bitmap_release_region(iscsit_global->ts_bitmap, conn->bitmap_id, + get_order(1)); + spin_unlock(&iscsit_global->ts_bitmap_lock); iscsit_stop_timers_for_cmds(conn); iscsit_stop_nopin_response_timer(conn); @@ -4539,15 +4528,13 @@ static void iscsit_logout_post_handler_closesession( struct iscsi_conn *conn) { struct iscsi_session *sess = conn->sess; - - iscsi_set_thread_clear(conn, ISCSI_CLEAR_TX_THREAD); - iscsi_set_thread_set_signal(conn, ISCSI_SIGNAL_TX_THREAD); + int sleep = cmpxchg(&conn->tx_thread_active, true, false); atomic_set(&conn->conn_logout_remove, 0); complete(&conn->conn_logout_comp); iscsit_dec_conn_usage_count(conn); - iscsit_stop_session(sess, 1, 1); + iscsit_stop_session(sess, sleep, sleep); iscsit_dec_session_usage_count(sess); target_put_session(sess->se_sess); } @@ -4555,13 +4542,12 @@ static void iscsit_logout_post_handler_closesession( static void iscsit_logout_post_handler_samecid( struct iscsi_conn *conn) { - iscsi_set_thread_clear(conn, ISCSI_CLEAR_TX_THREAD); - iscsi_set_thread_set_signal(conn, ISCSI_SIGNAL_TX_THREAD); + int sleep = cmpxchg(&conn->tx_thread_active, true, false); atomic_set(&conn->conn_logout_remove, 0); complete(&conn->conn_logout_comp); - iscsit_cause_connection_reinstatement(conn, 1); + iscsit_cause_connection_reinstatement(conn, sleep); iscsit_dec_conn_usage_count(conn); } diff --git a/drivers/target/iscsi/iscsi_target_erl0.c b/drivers/target/iscsi/iscsi_target_erl0.c index 1c197bad6132..d4e2159f53c2 100644 --- a/drivers/target/iscsi/iscsi_target_erl0.c +++ b/drivers/target/iscsi/iscsi_target_erl0.c @@ -861,7 +861,10 @@ void iscsit_connection_reinstatement_rcfr(struct iscsi_conn *conn) } spin_unlock_bh(&conn->state_lock); - iscsi_thread_set_force_reinstatement(conn); + if (conn->tx_thread && conn->tx_thread_active) + send_sig(SIGINT, conn->tx_thread, 1); + if (conn->rx_thread && conn->rx_thread_active) + send_sig(SIGINT, conn->rx_thread, 1); sleep: wait_for_completion(&conn->conn_wait_rcfr_comp); @@ -886,10 +889,10 @@ void iscsit_cause_connection_reinstatement(struct iscsi_conn *conn, int sleep) return; } - if (iscsi_thread_set_force_reinstatement(conn) < 0) { - spin_unlock_bh(&conn->state_lock); - return; - } + if (conn->tx_thread && conn->tx_thread_active) + send_sig(SIGINT, conn->tx_thread, 1); + if (conn->rx_thread && conn->rx_thread_active) + send_sig(SIGINT, conn->rx_thread, 1); atomic_set(&conn->connection_reinstatement, 1); if (!sleep) { diff --git a/drivers/target/iscsi/iscsi_target_login.c b/drivers/target/iscsi/iscsi_target_login.c index 153fb66ac1b8..345f073ff6dc 100644 --- a/drivers/target/iscsi/iscsi_target_login.c +++ b/drivers/target/iscsi/iscsi_target_login.c @@ -699,6 +699,51 @@ static void iscsi_post_login_start_timers(struct iscsi_conn *conn) iscsit_start_nopin_timer(conn); } +int iscsit_start_kthreads(struct iscsi_conn *conn) +{ + int ret = 0; + + spin_lock(&iscsit_global->ts_bitmap_lock); + conn->bitmap_id = bitmap_find_free_region(iscsit_global->ts_bitmap, + ISCSIT_BITMAP_BITS, get_order(1)); + spin_unlock(&iscsit_global->ts_bitmap_lock); + + if (conn->bitmap_id < 0) { + pr_err("bitmap_find_free_region() failed for" + " iscsit_start_kthreads()\n"); + return -ENOMEM; + } + + conn->tx_thread = kthread_run(iscsi_target_tx_thread, conn, + "%s", ISCSI_TX_THREAD_NAME); + if (IS_ERR(conn->tx_thread)) { + pr_err("Unable to start iscsi_target_tx_thread\n"); + ret = PTR_ERR(conn->tx_thread); + goto out_bitmap; + } + conn->tx_thread_active = true; + + conn->rx_thread = kthread_run(iscsi_target_rx_thread, conn, + "%s", ISCSI_RX_THREAD_NAME); + if (IS_ERR(conn->rx_thread)) { + pr_err("Unable to start iscsi_target_rx_thread\n"); + ret = PTR_ERR(conn->rx_thread); + goto out_tx; + } + conn->rx_thread_active = true; + + return 0; +out_tx: + kthread_stop(conn->tx_thread); + conn->tx_thread_active = false; +out_bitmap: + spin_lock(&iscsit_global->ts_bitmap_lock); + bitmap_release_region(iscsit_global->ts_bitmap, conn->bitmap_id, + get_order(1)); + spin_unlock(&iscsit_global->ts_bitmap_lock); + return ret; +} + int iscsi_post_login_handler( struct iscsi_np *np, struct iscsi_conn *conn, @@ -709,7 +754,7 @@ int iscsi_post_login_handler( struct se_session *se_sess = sess->se_sess; struct iscsi_portal_group *tpg = sess->tpg; struct se_portal_group *se_tpg = &tpg->tpg_se_tpg; - struct iscsi_thread_set *ts; + int rc; iscsit_inc_conn_usage_count(conn); @@ -724,7 +769,6 @@ int iscsi_post_login_handler( /* * SCSI Initiator -> SCSI Target Port Mapping */ - ts = iscsi_get_thread_set(); if (!zero_tsih) { iscsi_set_session_parameters(sess->sess_ops, conn->param_list, 0); @@ -751,9 +795,11 @@ int iscsi_post_login_handler( sess->sess_ops->InitiatorName); spin_unlock_bh(&sess->conn_lock); - iscsi_post_login_start_timers(conn); + rc = iscsit_start_kthreads(conn); + if (rc) + return rc; - iscsi_activate_thread_set(conn, ts); + iscsi_post_login_start_timers(conn); /* * Determine CPU mask to ensure connection's RX and TX kthreads * are scheduled on the same CPU. @@ -810,8 +856,11 @@ int iscsi_post_login_handler( " iSCSI Target Portal Group: %hu\n", tpg->nsessions, tpg->tpgt); spin_unlock_bh(&se_tpg->session_lock); + rc = iscsit_start_kthreads(conn); + if (rc) + return rc; + iscsi_post_login_start_timers(conn); - iscsi_activate_thread_set(conn, ts); /* * Determine CPU mask to ensure connection's RX and TX kthreads * are scheduled on the same CPU. diff --git a/include/target/iscsi/iscsi_target_core.h b/include/target/iscsi/iscsi_target_core.h index d3583d3ee193..dd0f3abde75d 100644 --- a/include/target/iscsi/iscsi_target_core.h +++ b/include/target/iscsi/iscsi_target_core.h @@ -602,6 +602,11 @@ struct iscsi_conn { struct iscsi_session *sess; /* Pointer to thread_set in use for this conn's threads */ struct iscsi_thread_set *thread_set; + int bitmap_id; + int rx_thread_active; + struct task_struct *rx_thread; + int tx_thread_active; + struct task_struct *tx_thread; /* list_head for session connection list */ struct list_head conn_list; } ____cacheline_aligned; @@ -871,10 +876,12 @@ struct iscsit_global { /* Unique identifier used for the authentication daemon */ u32 auth_id; u32 inactive_ts; +#define ISCSIT_BITMAP_BITS 262144 /* Thread Set bitmap count */ int ts_bitmap_count; /* Thread Set bitmap pointer */ unsigned long *ts_bitmap; + spinlock_t ts_bitmap_lock; /* Used for iSCSI discovery session authentication */ struct iscsi_node_acl discovery_acl; struct iscsi_portal_group *discovery_tpg; -- cgit v1.2.3 From 073900bdb4e34109a647c7cb871856a771634460 Mon Sep 17 00:00:00 2001 From: Nicholas Bellinger Date: Fri, 27 Feb 2015 10:21:06 +0000 Subject: iscsi-target: Drop legacy iscsi_target_tq.c logic Now that iscsi_conn allocates new [rx,tx] threads using kthread.h primitives on the fly, and kthread_stop() is called directly during connection shutdown, it's time to go ahead and drop iscsi_target_tq.c legacy code. The use of multiple struct completion in iscsi_activate_thread_set() has been proven to cause issues during repeated iser login/logout. Tested-by: Sagi Grimberg Cc: Slava Shwartsman Signed-off-by: Nicholas Bellinger --- drivers/target/iscsi/Makefile | 1 - drivers/target/iscsi/iscsi_target.c | 3 - drivers/target/iscsi/iscsi_target_erl0.c | 1 - drivers/target/iscsi/iscsi_target_login.c | 1 - drivers/target/iscsi/iscsi_target_tq.c | 495 ------------------------------ drivers/target/iscsi/iscsi_target_tq.h | 84 ----- drivers/target/iscsi/iscsi_target_util.c | 1 - include/target/iscsi/iscsi_target_core.h | 6 +- 8 files changed, 2 insertions(+), 590 deletions(-) delete mode 100644 drivers/target/iscsi/iscsi_target_tq.c delete mode 100644 drivers/target/iscsi/iscsi_target_tq.h (limited to 'include') diff --git a/drivers/target/iscsi/Makefile b/drivers/target/iscsi/Makefile index 13a92403fe3e..0f43be9c3453 100644 --- a/drivers/target/iscsi/Makefile +++ b/drivers/target/iscsi/Makefile @@ -1,6 +1,5 @@ iscsi_target_mod-y += iscsi_target_parameters.o \ iscsi_target_seq_pdu_list.o \ - iscsi_target_tq.o \ iscsi_target_auth.o \ iscsi_target_datain_values.o \ iscsi_target_device.o \ diff --git a/drivers/target/iscsi/iscsi_target.c b/drivers/target/iscsi/iscsi_target.c index 163773fb4f84..cd611e740de7 100644 --- a/drivers/target/iscsi/iscsi_target.c +++ b/drivers/target/iscsi/iscsi_target.c @@ -33,7 +33,6 @@ #include #include "iscsi_target_parameters.h" #include "iscsi_target_seq_pdu_list.h" -#include "iscsi_target_tq.h" #include "iscsi_target_configfs.h" #include "iscsi_target_datain_values.h" #include "iscsi_target_erl0.h" @@ -4360,8 +4359,6 @@ int iscsit_close_connection( iscsit_put_transport(conn->conn_transport); - conn->thread_set = NULL; - pr_debug("Moving to TARG_CONN_STATE_FREE.\n"); conn->conn_state = TARG_CONN_STATE_FREE; kfree(conn); diff --git a/drivers/target/iscsi/iscsi_target_erl0.c b/drivers/target/iscsi/iscsi_target_erl0.c index d4e2159f53c2..e1f4c7eedb08 100644 --- a/drivers/target/iscsi/iscsi_target_erl0.c +++ b/drivers/target/iscsi/iscsi_target_erl0.c @@ -24,7 +24,6 @@ #include #include #include "iscsi_target_seq_pdu_list.h" -#include "iscsi_target_tq.h" #include "iscsi_target_erl0.h" #include "iscsi_target_erl1.h" #include "iscsi_target_erl2.h" diff --git a/drivers/target/iscsi/iscsi_target_login.c b/drivers/target/iscsi/iscsi_target_login.c index 345f073ff6dc..af20ddf2bbb4 100644 --- a/drivers/target/iscsi/iscsi_target_login.c +++ b/drivers/target/iscsi/iscsi_target_login.c @@ -26,7 +26,6 @@ #include #include -#include "iscsi_target_tq.h" #include "iscsi_target_device.h" #include "iscsi_target_nego.h" #include "iscsi_target_erl0.h" diff --git a/drivers/target/iscsi/iscsi_target_tq.c b/drivers/target/iscsi/iscsi_target_tq.c deleted file mode 100644 index 26aa50996473..000000000000 --- a/drivers/target/iscsi/iscsi_target_tq.c +++ /dev/null @@ -1,495 +0,0 @@ -/******************************************************************************* - * This file contains the iSCSI Login Thread and Thread Queue functions. - * - * (c) Copyright 2007-2013 Datera, Inc. - * - * Author: Nicholas A. Bellinger - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - ******************************************************************************/ - -#include -#include -#include - -#include -#include "iscsi_target_tq.h" -#include "iscsi_target.h" - -static LIST_HEAD(inactive_ts_list); -static DEFINE_SPINLOCK(inactive_ts_lock); -static DEFINE_SPINLOCK(ts_bitmap_lock); - -static void iscsi_add_ts_to_inactive_list(struct iscsi_thread_set *ts) -{ - if (!list_empty(&ts->ts_list)) { - WARN_ON(1); - return; - } - spin_lock(&inactive_ts_lock); - list_add_tail(&ts->ts_list, &inactive_ts_list); - iscsit_global->inactive_ts++; - spin_unlock(&inactive_ts_lock); -} - -static struct iscsi_thread_set *iscsi_get_ts_from_inactive_list(void) -{ - struct iscsi_thread_set *ts; - - spin_lock(&inactive_ts_lock); - if (list_empty(&inactive_ts_list)) { - spin_unlock(&inactive_ts_lock); - return NULL; - } - - ts = list_first_entry(&inactive_ts_list, struct iscsi_thread_set, ts_list); - - list_del_init(&ts->ts_list); - iscsit_global->inactive_ts--; - spin_unlock(&inactive_ts_lock); - - return ts; -} - -int iscsi_allocate_thread_sets(u32 thread_pair_count) -{ - int allocated_thread_pair_count = 0, i, thread_id; - struct iscsi_thread_set *ts = NULL; - - for (i = 0; i < thread_pair_count; i++) { - ts = kzalloc(sizeof(struct iscsi_thread_set), GFP_KERNEL); - if (!ts) { - pr_err("Unable to allocate memory for" - " thread set.\n"); - return allocated_thread_pair_count; - } - /* - * Locate the next available regision in the thread_set_bitmap - */ - spin_lock(&ts_bitmap_lock); - thread_id = bitmap_find_free_region(iscsit_global->ts_bitmap, - iscsit_global->ts_bitmap_count, get_order(1)); - spin_unlock(&ts_bitmap_lock); - if (thread_id < 0) { - pr_err("bitmap_find_free_region() failed for" - " thread_set_bitmap\n"); - kfree(ts); - return allocated_thread_pair_count; - } - - ts->thread_id = thread_id; - ts->status = ISCSI_THREAD_SET_FREE; - INIT_LIST_HEAD(&ts->ts_list); - spin_lock_init(&ts->ts_state_lock); - init_completion(&ts->rx_restart_comp); - init_completion(&ts->tx_restart_comp); - init_completion(&ts->rx_start_comp); - init_completion(&ts->tx_start_comp); - sema_init(&ts->ts_activate_sem, 0); - - ts->create_threads = 1; - ts->tx_thread = kthread_run(iscsi_target_tx_thread, ts, "%s", - ISCSI_TX_THREAD_NAME); - if (IS_ERR(ts->tx_thread)) { - dump_stack(); - pr_err("Unable to start iscsi_target_tx_thread\n"); - break; - } - - ts->rx_thread = kthread_run(iscsi_target_rx_thread, ts, "%s", - ISCSI_RX_THREAD_NAME); - if (IS_ERR(ts->rx_thread)) { - kthread_stop(ts->tx_thread); - pr_err("Unable to start iscsi_target_rx_thread\n"); - break; - } - ts->create_threads = 0; - - iscsi_add_ts_to_inactive_list(ts); - allocated_thread_pair_count++; - } - - pr_debug("Spawned %d thread set(s) (%d total threads).\n", - allocated_thread_pair_count, allocated_thread_pair_count * 2); - return allocated_thread_pair_count; -} - -static void iscsi_deallocate_thread_one(struct iscsi_thread_set *ts) -{ - spin_lock_bh(&ts->ts_state_lock); - ts->status = ISCSI_THREAD_SET_DIE; - - if (ts->rx_thread) { - complete(&ts->rx_start_comp); - spin_unlock_bh(&ts->ts_state_lock); - kthread_stop(ts->rx_thread); - spin_lock_bh(&ts->ts_state_lock); - } - if (ts->tx_thread) { - complete(&ts->tx_start_comp); - spin_unlock_bh(&ts->ts_state_lock); - kthread_stop(ts->tx_thread); - spin_lock_bh(&ts->ts_state_lock); - } - spin_unlock_bh(&ts->ts_state_lock); - /* - * Release this thread_id in the thread_set_bitmap - */ - spin_lock(&ts_bitmap_lock); - bitmap_release_region(iscsit_global->ts_bitmap, - ts->thread_id, get_order(1)); - spin_unlock(&ts_bitmap_lock); - - kfree(ts); -} - -void iscsi_deallocate_thread_sets(void) -{ - struct iscsi_thread_set *ts = NULL; - u32 released_count = 0; - - while ((ts = iscsi_get_ts_from_inactive_list())) { - - iscsi_deallocate_thread_one(ts); - released_count++; - } - - if (released_count) - pr_debug("Stopped %d thread set(s) (%d total threads)." - "\n", released_count, released_count * 2); -} - -static void iscsi_deallocate_extra_thread_sets(void) -{ - u32 orig_count, released_count = 0; - struct iscsi_thread_set *ts = NULL; - - orig_count = TARGET_THREAD_SET_COUNT; - - while ((iscsit_global->inactive_ts + 1) > orig_count) { - ts = iscsi_get_ts_from_inactive_list(); - if (!ts) - break; - - iscsi_deallocate_thread_one(ts); - released_count++; - } - - if (released_count) - pr_debug("Stopped %d thread set(s) (%d total threads)." - "\n", released_count, released_count * 2); -} - -void iscsi_activate_thread_set(struct iscsi_conn *conn, struct iscsi_thread_set *ts) -{ - spin_lock_bh(&ts->ts_state_lock); - conn->thread_set = ts; - ts->conn = conn; - ts->status = ISCSI_THREAD_SET_ACTIVE; - spin_unlock_bh(&ts->ts_state_lock); - - complete(&ts->rx_start_comp); - complete(&ts->tx_start_comp); - - down(&ts->ts_activate_sem); -} - -struct iscsi_thread_set *iscsi_get_thread_set(void) -{ - struct iscsi_thread_set *ts; - -get_set: - ts = iscsi_get_ts_from_inactive_list(); - if (!ts) { - iscsi_allocate_thread_sets(1); - goto get_set; - } - - ts->delay_inactive = 1; - ts->signal_sent = 0; - ts->thread_count = 2; - init_completion(&ts->rx_restart_comp); - init_completion(&ts->tx_restart_comp); - sema_init(&ts->ts_activate_sem, 0); - - return ts; -} - -void iscsi_set_thread_clear(struct iscsi_conn *conn, u8 thread_clear) -{ - struct iscsi_thread_set *ts = NULL; - - if (!conn->thread_set) { - pr_err("struct iscsi_conn->thread_set is NULL\n"); - return; - } - ts = conn->thread_set; - - spin_lock_bh(&ts->ts_state_lock); - ts->thread_clear &= ~thread_clear; - - if ((thread_clear & ISCSI_CLEAR_RX_THREAD) && - (ts->blocked_threads & ISCSI_BLOCK_RX_THREAD)) - complete(&ts->rx_restart_comp); - else if ((thread_clear & ISCSI_CLEAR_TX_THREAD) && - (ts->blocked_threads & ISCSI_BLOCK_TX_THREAD)) - complete(&ts->tx_restart_comp); - spin_unlock_bh(&ts->ts_state_lock); -} - -void iscsi_set_thread_set_signal(struct iscsi_conn *conn, u8 signal_sent) -{ - struct iscsi_thread_set *ts = NULL; - - if (!conn->thread_set) { - pr_err("struct iscsi_conn->thread_set is NULL\n"); - return; - } - ts = conn->thread_set; - - spin_lock_bh(&ts->ts_state_lock); - ts->signal_sent |= signal_sent; - spin_unlock_bh(&ts->ts_state_lock); -} - -int iscsi_release_thread_set(struct iscsi_conn *conn) -{ - int thread_called = 0; - struct iscsi_thread_set *ts = NULL; - - if (!conn || !conn->thread_set) { - pr_err("connection or thread set pointer is NULL\n"); - BUG(); - } - ts = conn->thread_set; - - spin_lock_bh(&ts->ts_state_lock); - ts->status = ISCSI_THREAD_SET_RESET; - - if (!strncmp(current->comm, ISCSI_RX_THREAD_NAME, - strlen(ISCSI_RX_THREAD_NAME))) - thread_called = ISCSI_RX_THREAD; - else if (!strncmp(current->comm, ISCSI_TX_THREAD_NAME, - strlen(ISCSI_TX_THREAD_NAME))) - thread_called = ISCSI_TX_THREAD; - - if (ts->rx_thread && (thread_called == ISCSI_TX_THREAD) && - (ts->thread_clear & ISCSI_CLEAR_RX_THREAD)) { - - if (!(ts->signal_sent & ISCSI_SIGNAL_RX_THREAD)) { - send_sig(SIGINT, ts->rx_thread, 1); - ts->signal_sent |= ISCSI_SIGNAL_RX_THREAD; - } - ts->blocked_threads |= ISCSI_BLOCK_RX_THREAD; - spin_unlock_bh(&ts->ts_state_lock); - wait_for_completion(&ts->rx_restart_comp); - spin_lock_bh(&ts->ts_state_lock); - ts->blocked_threads &= ~ISCSI_BLOCK_RX_THREAD; - } - if (ts->tx_thread && (thread_called == ISCSI_RX_THREAD) && - (ts->thread_clear & ISCSI_CLEAR_TX_THREAD)) { - - if (!(ts->signal_sent & ISCSI_SIGNAL_TX_THREAD)) { - send_sig(SIGINT, ts->tx_thread, 1); - ts->signal_sent |= ISCSI_SIGNAL_TX_THREAD; - } - ts->blocked_threads |= ISCSI_BLOCK_TX_THREAD; - spin_unlock_bh(&ts->ts_state_lock); - wait_for_completion(&ts->tx_restart_comp); - spin_lock_bh(&ts->ts_state_lock); - ts->blocked_threads &= ~ISCSI_BLOCK_TX_THREAD; - } - - ts->conn = NULL; - ts->status = ISCSI_THREAD_SET_FREE; - spin_unlock_bh(&ts->ts_state_lock); - - return 0; -} - -int iscsi_thread_set_force_reinstatement(struct iscsi_conn *conn) -{ - struct iscsi_thread_set *ts; - - if (!conn->thread_set) - return -1; - ts = conn->thread_set; - - spin_lock_bh(&ts->ts_state_lock); - if (ts->status != ISCSI_THREAD_SET_ACTIVE) { - spin_unlock_bh(&ts->ts_state_lock); - return -1; - } - - if (ts->tx_thread && (!(ts->signal_sent & ISCSI_SIGNAL_TX_THREAD))) { - send_sig(SIGINT, ts->tx_thread, 1); - ts->signal_sent |= ISCSI_SIGNAL_TX_THREAD; - } - if (ts->rx_thread && (!(ts->signal_sent & ISCSI_SIGNAL_RX_THREAD))) { - send_sig(SIGINT, ts->rx_thread, 1); - ts->signal_sent |= ISCSI_SIGNAL_RX_THREAD; - } - spin_unlock_bh(&ts->ts_state_lock); - - return 0; -} - -static void iscsi_check_to_add_additional_sets(void) -{ - int thread_sets_add; - - spin_lock(&inactive_ts_lock); - thread_sets_add = iscsit_global->inactive_ts; - spin_unlock(&inactive_ts_lock); - if (thread_sets_add == 1) - iscsi_allocate_thread_sets(1); -} - -static int iscsi_signal_thread_pre_handler(struct iscsi_thread_set *ts) -{ - spin_lock_bh(&ts->ts_state_lock); - if (ts->status == ISCSI_THREAD_SET_DIE || kthread_should_stop() || - signal_pending(current)) { - spin_unlock_bh(&ts->ts_state_lock); - return -1; - } - spin_unlock_bh(&ts->ts_state_lock); - - return 0; -} - -struct iscsi_conn *iscsi_rx_thread_pre_handler(struct iscsi_thread_set *ts) -{ - int ret; - - spin_lock_bh(&ts->ts_state_lock); - if (ts->create_threads) { - spin_unlock_bh(&ts->ts_state_lock); - goto sleep; - } - - if (ts->status != ISCSI_THREAD_SET_DIE) - flush_signals(current); - - if (ts->delay_inactive && (--ts->thread_count == 0)) { - spin_unlock_bh(&ts->ts_state_lock); - - if (!iscsit_global->in_shutdown) - iscsi_deallocate_extra_thread_sets(); - - iscsi_add_ts_to_inactive_list(ts); - spin_lock_bh(&ts->ts_state_lock); - } - - if ((ts->status == ISCSI_THREAD_SET_RESET) && - (ts->thread_clear & ISCSI_CLEAR_RX_THREAD)) - complete(&ts->rx_restart_comp); - - ts->thread_clear &= ~ISCSI_CLEAR_RX_THREAD; - spin_unlock_bh(&ts->ts_state_lock); -sleep: - ret = wait_for_completion_interruptible(&ts->rx_start_comp); - if (ret != 0) - return NULL; - - if (iscsi_signal_thread_pre_handler(ts) < 0) - return NULL; - - iscsi_check_to_add_additional_sets(); - - spin_lock_bh(&ts->ts_state_lock); - if (!ts->conn) { - pr_err("struct iscsi_thread_set->conn is NULL for" - " RX thread_id: %s/%d\n", current->comm, current->pid); - spin_unlock_bh(&ts->ts_state_lock); - return NULL; - } - ts->thread_clear |= ISCSI_CLEAR_RX_THREAD; - spin_unlock_bh(&ts->ts_state_lock); - - up(&ts->ts_activate_sem); - - return ts->conn; -} - -struct iscsi_conn *iscsi_tx_thread_pre_handler(struct iscsi_thread_set *ts) -{ - int ret; - - spin_lock_bh(&ts->ts_state_lock); - if (ts->create_threads) { - spin_unlock_bh(&ts->ts_state_lock); - goto sleep; - } - - if (ts->status != ISCSI_THREAD_SET_DIE) - flush_signals(current); - - if (ts->delay_inactive && (--ts->thread_count == 0)) { - spin_unlock_bh(&ts->ts_state_lock); - - if (!iscsit_global->in_shutdown) - iscsi_deallocate_extra_thread_sets(); - - iscsi_add_ts_to_inactive_list(ts); - spin_lock_bh(&ts->ts_state_lock); - } - if ((ts->status == ISCSI_THREAD_SET_RESET) && - (ts->thread_clear & ISCSI_CLEAR_TX_THREAD)) - complete(&ts->tx_restart_comp); - - ts->thread_clear &= ~ISCSI_CLEAR_TX_THREAD; - spin_unlock_bh(&ts->ts_state_lock); -sleep: - ret = wait_for_completion_interruptible(&ts->tx_start_comp); - if (ret != 0) - return NULL; - - if (iscsi_signal_thread_pre_handler(ts) < 0) - return NULL; - - iscsi_check_to_add_additional_sets(); - - spin_lock_bh(&ts->ts_state_lock); - if (!ts->conn) { - pr_err("struct iscsi_thread_set->conn is NULL for" - " TX thread_id: %s/%d\n", current->comm, current->pid); - spin_unlock_bh(&ts->ts_state_lock); - return NULL; - } - ts->thread_clear |= ISCSI_CLEAR_TX_THREAD; - spin_unlock_bh(&ts->ts_state_lock); - - up(&ts->ts_activate_sem); - - return ts->conn; -} - -int iscsi_thread_set_init(void) -{ - int size; - - iscsit_global->ts_bitmap_count = ISCSI_TS_BITMAP_BITS; - - size = BITS_TO_LONGS(iscsit_global->ts_bitmap_count) * sizeof(long); - iscsit_global->ts_bitmap = kzalloc(size, GFP_KERNEL); - if (!iscsit_global->ts_bitmap) { - pr_err("Unable to allocate iscsit_global->ts_bitmap\n"); - return -ENOMEM; - } - - return 0; -} - -void iscsi_thread_set_free(void) -{ - kfree(iscsit_global->ts_bitmap); -} diff --git a/drivers/target/iscsi/iscsi_target_tq.h b/drivers/target/iscsi/iscsi_target_tq.h deleted file mode 100644 index cc1eede5ab3a..000000000000 --- a/drivers/target/iscsi/iscsi_target_tq.h +++ /dev/null @@ -1,84 +0,0 @@ -#ifndef ISCSI_THREAD_QUEUE_H -#define ISCSI_THREAD_QUEUE_H - -/* - * Defines for thread sets. - */ -extern int iscsi_thread_set_force_reinstatement(struct iscsi_conn *); -extern int iscsi_allocate_thread_sets(u32); -extern void iscsi_deallocate_thread_sets(void); -extern void iscsi_activate_thread_set(struct iscsi_conn *, struct iscsi_thread_set *); -extern struct iscsi_thread_set *iscsi_get_thread_set(void); -extern void iscsi_set_thread_clear(struct iscsi_conn *, u8); -extern void iscsi_set_thread_set_signal(struct iscsi_conn *, u8); -extern int iscsi_release_thread_set(struct iscsi_conn *); -extern struct iscsi_conn *iscsi_rx_thread_pre_handler(struct iscsi_thread_set *); -extern struct iscsi_conn *iscsi_tx_thread_pre_handler(struct iscsi_thread_set *); -extern int iscsi_thread_set_init(void); -extern void iscsi_thread_set_free(void); - -extern int iscsi_target_tx_thread(void *); -extern int iscsi_target_rx_thread(void *); - -#define TARGET_THREAD_SET_COUNT 4 - -#define ISCSI_RX_THREAD 1 -#define ISCSI_TX_THREAD 2 -#define ISCSI_RX_THREAD_NAME "iscsi_trx" -#define ISCSI_TX_THREAD_NAME "iscsi_ttx" -#define ISCSI_BLOCK_RX_THREAD 0x1 -#define ISCSI_BLOCK_TX_THREAD 0x2 -#define ISCSI_CLEAR_RX_THREAD 0x1 -#define ISCSI_CLEAR_TX_THREAD 0x2 -#define ISCSI_SIGNAL_RX_THREAD 0x1 -#define ISCSI_SIGNAL_TX_THREAD 0x2 - -/* struct iscsi_thread_set->status */ -#define ISCSI_THREAD_SET_FREE 1 -#define ISCSI_THREAD_SET_ACTIVE 2 -#define ISCSI_THREAD_SET_DIE 3 -#define ISCSI_THREAD_SET_RESET 4 -#define ISCSI_THREAD_SET_DEALLOCATE_THREADS 5 - -/* By default allow a maximum of 32K iSCSI connections */ -#define ISCSI_TS_BITMAP_BITS 32768 - -struct iscsi_thread_set { - /* flags used for blocking and restarting sets */ - int blocked_threads; - /* flag for creating threads */ - int create_threads; - /* flag for delaying readding to inactive list */ - int delay_inactive; - /* status for thread set */ - int status; - /* which threads have had signals sent */ - int signal_sent; - /* flag for which threads exited first */ - int thread_clear; - /* Active threads in the thread set */ - int thread_count; - /* Unique thread ID */ - u32 thread_id; - /* pointer to connection if set is active */ - struct iscsi_conn *conn; - /* used for controlling ts state accesses */ - spinlock_t ts_state_lock; - /* used for restarting thread queue */ - struct completion rx_restart_comp; - /* used for restarting thread queue */ - struct completion tx_restart_comp; - /* used for normal unused blocking */ - struct completion rx_start_comp; - /* used for normal unused blocking */ - struct completion tx_start_comp; - /* OS descriptor for rx thread */ - struct task_struct *rx_thread; - /* OS descriptor for tx thread */ - struct task_struct *tx_thread; - /* struct iscsi_thread_set in list list head*/ - struct list_head ts_list; - struct semaphore ts_activate_sem; -}; - -#endif /*** ISCSI_THREAD_QUEUE_H ***/ diff --git a/drivers/target/iscsi/iscsi_target_util.c b/drivers/target/iscsi/iscsi_target_util.c index 390df8ed72b2..b18edda3e8af 100644 --- a/drivers/target/iscsi/iscsi_target_util.c +++ b/drivers/target/iscsi/iscsi_target_util.c @@ -33,7 +33,6 @@ #include "iscsi_target_erl1.h" #include "iscsi_target_erl2.h" #include "iscsi_target_tpg.h" -#include "iscsi_target_tq.h" #include "iscsi_target_util.h" #include "iscsi_target.h" diff --git a/include/target/iscsi/iscsi_target_core.h b/include/target/iscsi/iscsi_target_core.h index dd0f3abde75d..0e394a031c28 100644 --- a/include/target/iscsi/iscsi_target_core.h +++ b/include/target/iscsi/iscsi_target_core.h @@ -20,6 +20,8 @@ #define ISCSIT_MIN_TAGS 16 #define ISCSIT_EXTRA_TAGS 8 #define ISCSIT_TCP_BACKLOG 256 +#define ISCSI_RX_THREAD_NAME "iscsi_trx" +#define ISCSI_TX_THREAD_NAME "iscsi_ttx" /* struct iscsi_node_attrib sanity values */ #define NA_DATAOUT_TIMEOUT 3 @@ -600,8 +602,6 @@ struct iscsi_conn { struct iscsi_tpg_np *tpg_np; /* Pointer to parent session */ struct iscsi_session *sess; - /* Pointer to thread_set in use for this conn's threads */ - struct iscsi_thread_set *thread_set; int bitmap_id; int rx_thread_active; struct task_struct *rx_thread; @@ -877,8 +877,6 @@ struct iscsit_global { u32 auth_id; u32 inactive_ts; #define ISCSIT_BITMAP_BITS 262144 - /* Thread Set bitmap count */ - int ts_bitmap_count; /* Thread Set bitmap pointer */ unsigned long *ts_bitmap; spinlock_t ts_bitmap_lock; -- cgit v1.2.3 From ee97d0e3f06498487671c23cad4230bf9aa5fd88 Mon Sep 17 00:00:00 2001 From: Mans Rullgard Date: Sun, 8 Mar 2015 14:30:04 +0000 Subject: serial: 8250: allow specifying iomem size in addition to address This adds a mapsize field to struct uart_port to be used in conjunction with mapbase. If set, it overrides whatever value serial8250_port_size() would otherwise report. Signed-off-by: Mans Rullgard Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_core.c | 4 ++++ include/linux/serial_core.h | 1 + 2 files changed, 5 insertions(+) (limited to 'include') diff --git a/drivers/tty/serial/8250/8250_core.c b/drivers/tty/serial/8250/8250_core.c index 9b2de25359d1..e0fb5f053d09 100644 --- a/drivers/tty/serial/8250/8250_core.c +++ b/drivers/tty/serial/8250/8250_core.c @@ -2722,6 +2722,8 @@ serial8250_pm(struct uart_port *port, unsigned int state, static unsigned int serial8250_port_size(struct uart_8250_port *pt) { + if (pt->port.mapsize) + return pt->port.mapsize; if (pt->port.iotype == UPIO_AU) { if (pt->port.type == PORT_RT2880) return 0x100; @@ -3553,6 +3555,7 @@ int __init early_serial_setup(struct uart_port *port) p->iotype = port->iotype; p->flags = port->flags; p->mapbase = port->mapbase; + p->mapsize = port->mapsize; p->private_data = port->private_data; p->type = port->type; p->line = port->line; @@ -3807,6 +3810,7 @@ int serial8250_register_8250_port(struct uart_8250_port *up) uart->port.flags = up->port.flags | UPF_BOOT_AUTOCONF; uart->bugs = up->bugs; uart->port.mapbase = up->port.mapbase; + uart->port.mapsize = up->port.mapsize; uart->port.private_data = up->port.private_data; uart->port.fifosize = up->port.fifosize; uart->tx_loadsz = up->tx_loadsz; diff --git a/include/linux/serial_core.h b/include/linux/serial_core.h index 8aeec4913a9c..34de16840152 100644 --- a/include/linux/serial_core.h +++ b/include/linux/serial_core.h @@ -237,6 +237,7 @@ struct uart_port { unsigned int line; /* port index */ unsigned int minor; resource_size_t mapbase; /* for ioremap */ + resource_size_t mapsize; struct device *dev; /* parent device */ unsigned char hub6; /* this should be in the 8250 driver */ unsigned char suspended; -- cgit v1.2.3 From 911a88829725572820dad9a168e735c606a2fdcb Mon Sep 17 00:00:00 2001 From: Alex Smith Date: Mon, 9 Mar 2015 14:29:04 +0000 Subject: memory: jz4780-nemc: driver for the NEMC on JZ4780 SoCs Add a driver for the NAND/External Memory Controller (NEMC) on JZ4780 and later SoCs. The primary function of this driver is to configure parameters, such as timings, for external memory devices using data supplied in the device tree. Devices connected to the NEMC are represented in the DT as children of the NEMC node, the driver uses optional properties specified in these child nodes to configure the parameters of each bank. Signed-off-by: Alex Smith Signed-off-by: Zubair Lutfullah Kakakhel Signed-off-by: Greg Kroah-Hartman --- drivers/memory/Kconfig | 9 + drivers/memory/Makefile | 1 + drivers/memory/jz4780-nemc.c | 391 +++++++++++++++++++++++++++++++++++++++++++ include/linux/jz4780-nemc.h | 43 +++++ 4 files changed, 444 insertions(+) create mode 100644 drivers/memory/jz4780-nemc.c create mode 100644 include/linux/jz4780-nemc.h (limited to 'include') diff --git a/drivers/memory/Kconfig b/drivers/memory/Kconfig index 191383d8c94d..868036f70f8f 100644 --- a/drivers/memory/Kconfig +++ b/drivers/memory/Kconfig @@ -83,6 +83,15 @@ config FSL_IFC bool depends on FSL_SOC +config JZ4780_NEMC + bool "Ingenic JZ4780 SoC NEMC driver" + default y + depends on MACH_JZ4780 + help + This driver is for the NAND/External Memory Controller (NEMC) in + the Ingenic JZ4780. This controller is used to handle external + memory devices such as NAND and SRAM. + source "drivers/memory/tegra/Kconfig" endif diff --git a/drivers/memory/Makefile b/drivers/memory/Makefile index 6b6548124473..b670441e3cdf 100644 --- a/drivers/memory/Makefile +++ b/drivers/memory/Makefile @@ -13,5 +13,6 @@ obj-$(CONFIG_FSL_CORENET_CF) += fsl-corenet-cf.o obj-$(CONFIG_FSL_IFC) += fsl_ifc.o obj-$(CONFIG_MVEBU_DEVBUS) += mvebu-devbus.o obj-$(CONFIG_TEGRA20_MC) += tegra20-mc.o +obj-$(CONFIG_JZ4780_NEMC) += jz4780-nemc.o obj-$(CONFIG_TEGRA_MC) += tegra/ diff --git a/drivers/memory/jz4780-nemc.c b/drivers/memory/jz4780-nemc.c new file mode 100644 index 000000000000..919d1925acb9 --- /dev/null +++ b/drivers/memory/jz4780-nemc.c @@ -0,0 +1,391 @@ +/* + * JZ4780 NAND/external memory controller (NEMC) + * + * Copyright (c) 2015 Imagination Technologies + * Author: Alex Smith + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define NEMC_SMCRn(n) (0x14 + (((n) - 1) * 4)) +#define NEMC_NFCSR 0x50 + +#define NEMC_SMCR_SMT BIT(0) +#define NEMC_SMCR_BW_SHIFT 6 +#define NEMC_SMCR_BW_MASK (0x3 << NEMC_SMCR_BW_SHIFT) +#define NEMC_SMCR_BW_8 (0 << 6) +#define NEMC_SMCR_TAS_SHIFT 8 +#define NEMC_SMCR_TAS_MASK (0xf << NEMC_SMCR_TAS_SHIFT) +#define NEMC_SMCR_TAH_SHIFT 12 +#define NEMC_SMCR_TAH_MASK (0xf << NEMC_SMCR_TAH_SHIFT) +#define NEMC_SMCR_TBP_SHIFT 16 +#define NEMC_SMCR_TBP_MASK (0xf << NEMC_SMCR_TBP_SHIFT) +#define NEMC_SMCR_TAW_SHIFT 20 +#define NEMC_SMCR_TAW_MASK (0xf << NEMC_SMCR_TAW_SHIFT) +#define NEMC_SMCR_TSTRV_SHIFT 24 +#define NEMC_SMCR_TSTRV_MASK (0x3f << NEMC_SMCR_TSTRV_SHIFT) + +#define NEMC_NFCSR_NFEn(n) BIT(((n) - 1) << 1) +#define NEMC_NFCSR_NFCEn(n) BIT((((n) - 1) << 1) + 1) +#define NEMC_NFCSR_TNFEn(n) BIT(16 + (n) - 1) + +struct jz4780_nemc { + spinlock_t lock; + struct device *dev; + void __iomem *base; + struct clk *clk; + uint32_t clk_period; + unsigned long banks_present; +}; + +/** + * jz4780_nemc_num_banks() - count the number of banks referenced by a device + * @dev: device to count banks for, must be a child of the NEMC. + * + * Return: The number of unique NEMC banks referred to by the specified NEMC + * child device. Unique here means that a device that references the same bank + * multiple times in the its "reg" property will only count once. + */ +unsigned int jz4780_nemc_num_banks(struct device *dev) +{ + const __be32 *prop; + unsigned int bank, count = 0; + unsigned long referenced = 0; + int i = 0; + + while ((prop = of_get_address(dev->of_node, i++, NULL, NULL))) { + bank = of_read_number(prop, 1); + if (!(referenced & BIT(bank))) { + referenced |= BIT(bank); + count++; + } + } + + return count; +} +EXPORT_SYMBOL(jz4780_nemc_num_banks); + +/** + * jz4780_nemc_set_type() - set the type of device connected to a bank + * @dev: child device of the NEMC. + * @bank: bank number to configure. + * @type: type of device connected to the bank. + */ +void jz4780_nemc_set_type(struct device *dev, unsigned int bank, + enum jz4780_nemc_bank_type type) +{ + struct jz4780_nemc *nemc = dev_get_drvdata(dev->parent); + uint32_t nfcsr; + + nfcsr = readl(nemc->base + NEMC_NFCSR); + + /* TODO: Support toggle NAND devices. */ + switch (type) { + case JZ4780_NEMC_BANK_SRAM: + nfcsr &= ~(NEMC_NFCSR_TNFEn(bank) | NEMC_NFCSR_NFEn(bank)); + break; + case JZ4780_NEMC_BANK_NAND: + nfcsr &= ~NEMC_NFCSR_TNFEn(bank); + nfcsr |= NEMC_NFCSR_NFEn(bank); + break; + } + + writel(nfcsr, nemc->base + NEMC_NFCSR); +} +EXPORT_SYMBOL(jz4780_nemc_set_type); + +/** + * jz4780_nemc_assert() - (de-)assert a NAND device's chip enable pin + * @dev: child device of the NEMC. + * @bank: bank number of device. + * @assert: whether the chip enable pin should be asserted. + * + * (De-)asserts the chip enable pin for the NAND device connected to the + * specified bank. + */ +void jz4780_nemc_assert(struct device *dev, unsigned int bank, bool assert) +{ + struct jz4780_nemc *nemc = dev_get_drvdata(dev->parent); + uint32_t nfcsr; + + nfcsr = readl(nemc->base + NEMC_NFCSR); + + if (assert) + nfcsr |= NEMC_NFCSR_NFCEn(bank); + else + nfcsr &= ~NEMC_NFCSR_NFCEn(bank); + + writel(nfcsr, nemc->base + NEMC_NFCSR); +} +EXPORT_SYMBOL(jz4780_nemc_assert); + +static uint32_t jz4780_nemc_clk_period(struct jz4780_nemc *nemc) +{ + unsigned long rate; + + rate = clk_get_rate(nemc->clk); + if (!rate) + return 0; + + /* Return in picoseconds. */ + return div64_ul(1000000000000ull, rate); +} + +static uint32_t jz4780_nemc_ns_to_cycles(struct jz4780_nemc *nemc, uint32_t ns) +{ + return ((ns * 1000) + nemc->clk_period - 1) / nemc->clk_period; +} + +static bool jz4780_nemc_configure_bank(struct jz4780_nemc *nemc, + unsigned int bank, + struct device_node *node) +{ + uint32_t smcr, val, cycles; + + /* + * Conversion of tBP and tAW cycle counts to values supported by the + * hardware (round up to the next supported value). + */ + static const uint32_t convert_tBP_tAW[] = { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, + + /* 11 - 12 -> 12 cycles */ + 11, 11, + + /* 13 - 15 -> 15 cycles */ + 12, 12, 12, + + /* 16 - 20 -> 20 cycles */ + 13, 13, 13, 13, 13, + + /* 21 - 25 -> 25 cycles */ + 14, 14, 14, 14, 14, + + /* 26 - 31 -> 31 cycles */ + 15, 15, 15, 15, 15, 15 + }; + + smcr = readl(nemc->base + NEMC_SMCRn(bank)); + smcr &= ~NEMC_SMCR_SMT; + + if (!of_property_read_u32(node, "ingenic,nemc-bus-width", &val)) { + smcr &= ~NEMC_SMCR_BW_MASK; + switch (val) { + case 8: + smcr |= NEMC_SMCR_BW_8; + break; + default: + /* + * Earlier SoCs support a 16 bit bus width (the 4780 + * does not), until those are properly supported, error. + */ + dev_err(nemc->dev, "unsupported bus width: %u\n", val); + return false; + } + } + + if (of_property_read_u32(node, "ingenic,nemc-tAS", &val) == 0) { + smcr &= ~NEMC_SMCR_TAS_MASK; + cycles = jz4780_nemc_ns_to_cycles(nemc, val); + if (cycles > 15) { + dev_err(nemc->dev, "tAS %u is too high (%u cycles)\n", + val, cycles); + return false; + } + + smcr |= cycles << NEMC_SMCR_TAS_SHIFT; + } + + if (of_property_read_u32(node, "ingenic,nemc-tAH", &val) == 0) { + smcr &= ~NEMC_SMCR_TAH_MASK; + cycles = jz4780_nemc_ns_to_cycles(nemc, val); + if (cycles > 15) { + dev_err(nemc->dev, "tAH %u is too high (%u cycles)\n", + val, cycles); + return false; + } + + smcr |= cycles << NEMC_SMCR_TAH_SHIFT; + } + + if (of_property_read_u32(node, "ingenic,nemc-tBP", &val) == 0) { + smcr &= ~NEMC_SMCR_TBP_MASK; + cycles = jz4780_nemc_ns_to_cycles(nemc, val); + if (cycles > 31) { + dev_err(nemc->dev, "tBP %u is too high (%u cycles)\n", + val, cycles); + return false; + } + + smcr |= convert_tBP_tAW[cycles] << NEMC_SMCR_TBP_SHIFT; + } + + if (of_property_read_u32(node, "ingenic,nemc-tAW", &val) == 0) { + smcr &= ~NEMC_SMCR_TAW_MASK; + cycles = jz4780_nemc_ns_to_cycles(nemc, val); + if (cycles > 31) { + dev_err(nemc->dev, "tAW %u is too high (%u cycles)\n", + val, cycles); + return false; + } + + smcr |= convert_tBP_tAW[cycles] << NEMC_SMCR_TAW_SHIFT; + } + + if (of_property_read_u32(node, "ingenic,nemc-tSTRV", &val) == 0) { + smcr &= ~NEMC_SMCR_TSTRV_MASK; + cycles = jz4780_nemc_ns_to_cycles(nemc, val); + if (cycles > 63) { + dev_err(nemc->dev, "tSTRV %u is too high (%u cycles)\n", + val, cycles); + return false; + } + + smcr |= cycles << NEMC_SMCR_TSTRV_SHIFT; + } + + writel(smcr, nemc->base + NEMC_SMCRn(bank)); + return true; +} + +static int jz4780_nemc_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct jz4780_nemc *nemc; + struct resource *res; + struct device_node *child; + const __be32 *prop; + unsigned int bank; + unsigned long referenced; + int i, ret; + + nemc = devm_kzalloc(dev, sizeof(*nemc), GFP_KERNEL); + if (!nemc) + return -ENOMEM; + + spin_lock_init(&nemc->lock); + nemc->dev = dev; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + nemc->base = devm_ioremap_resource(dev, res); + if (IS_ERR(nemc->base)) { + dev_err(dev, "failed to get I/O memory\n"); + return PTR_ERR(nemc->base); + } + + writel(0, nemc->base + NEMC_NFCSR); + + nemc->clk = devm_clk_get(dev, NULL); + if (IS_ERR(nemc->clk)) { + dev_err(dev, "failed to get clock\n"); + return PTR_ERR(nemc->clk); + } + + ret = clk_prepare_enable(nemc->clk); + if (ret) { + dev_err(dev, "failed to enable clock: %d\n", ret); + return ret; + } + + nemc->clk_period = jz4780_nemc_clk_period(nemc); + if (!nemc->clk_period) { + dev_err(dev, "failed to calculate clock period\n"); + clk_disable_unprepare(nemc->clk); + return -EINVAL; + } + + /* + * Iterate over child devices, check that they do not conflict with + * each other, and register child devices for them. If a child device + * has invalid properties, it is ignored and no platform device is + * registered for it. + */ + for_each_child_of_node(nemc->dev->of_node, child) { + referenced = 0; + i = 0; + while ((prop = of_get_address(child, i++, NULL, NULL))) { + bank = of_read_number(prop, 1); + if (bank < 1 || bank >= JZ4780_NEMC_NUM_BANKS) { + dev_err(nemc->dev, + "%s requests invalid bank %u\n", + child->full_name, bank); + + /* Will continue the outer loop below. */ + referenced = 0; + break; + } + + referenced |= BIT(bank); + } + + if (!referenced) { + dev_err(nemc->dev, "%s has no addresses\n", + child->full_name); + continue; + } else if (nemc->banks_present & referenced) { + dev_err(nemc->dev, "%s conflicts with another node\n", + child->full_name); + continue; + } + + /* Configure bank parameters. */ + for_each_set_bit(bank, &referenced, JZ4780_NEMC_NUM_BANKS) { + if (!jz4780_nemc_configure_bank(nemc, bank, child)) { + referenced = 0; + break; + } + } + + if (referenced) { + if (of_platform_device_create(child, NULL, nemc->dev)) + nemc->banks_present |= referenced; + } + } + + platform_set_drvdata(pdev, nemc); + dev_info(dev, "JZ4780 NEMC initialised\n"); + return 0; +} + +static int jz4780_nemc_remove(struct platform_device *pdev) +{ + struct jz4780_nemc *nemc = platform_get_drvdata(pdev); + + clk_disable_unprepare(nemc->clk); + return 0; +} + +static const struct of_device_id jz4780_nemc_dt_match[] = { + { .compatible = "ingenic,jz4780-nemc" }, + {}, +}; + +static struct platform_driver jz4780_nemc_driver = { + .probe = jz4780_nemc_probe, + .remove = jz4780_nemc_remove, + .driver = { + .name = "jz4780-nemc", + .of_match_table = of_match_ptr(jz4780_nemc_dt_match), + }, +}; + +static int __init jz4780_nemc_init(void) +{ + return platform_driver_register(&jz4780_nemc_driver); +} +subsys_initcall(jz4780_nemc_init); diff --git a/include/linux/jz4780-nemc.h b/include/linux/jz4780-nemc.h new file mode 100644 index 000000000000..e7f1cc7a2284 --- /dev/null +++ b/include/linux/jz4780-nemc.h @@ -0,0 +1,43 @@ +/* + * JZ4780 NAND/external memory controller (NEMC) + * + * Copyright (c) 2015 Imagination Technologies + * Author: Alex Smith + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#ifndef __LINUX_JZ4780_NEMC_H__ +#define __LINUX_JZ4780_NEMC_H__ + +#include + +struct device; + +/* + * Number of NEMC banks. Note that there are actually 6, but they are numbered + * from 1. + */ +#define JZ4780_NEMC_NUM_BANKS 7 + +/** + * enum jz4780_nemc_bank_type - device types which can be connected to a bank + * @JZ4780_NEMC_BANK_SRAM: SRAM + * @JZ4780_NEMC_BANK_NAND: NAND + */ +enum jz4780_nemc_bank_type { + JZ4780_NEMC_BANK_SRAM, + JZ4780_NEMC_BANK_NAND, +}; + +extern unsigned int jz4780_nemc_num_banks(struct device *dev); + +extern void jz4780_nemc_set_type(struct device *dev, unsigned int bank, + enum jz4780_nemc_bank_type type); +extern void jz4780_nemc_assert(struct device *dev, unsigned int bank, + bool assert); + +#endif /* __LINUX_JZ4780_NEMC_H__ */ -- cgit v1.2.3 From 1efb53a220b78fdfdbb97b726a2156713e75bdab Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Tue, 24 Mar 2015 01:07:08 +0000 Subject: ASoC: simple-card: Remove support for setting differing DAI formats Having to set different formats on the CPU side and the CODEC side of a DAI link is usually indication that something is terribly wrong and in most cases is a result of a broken driver that implements a set_fmt() callback which does not follow the specification. In the past this feature has been used to work around broken drivers, rather than fixing them. We don't really want to encourage this, so remove support for setting different formats on both ends of the link. Along the way switch to static DAI format setup by setting the the dai_fmt field of the snd_soc_dai_link rather than calling snd_soc_dai_fmt(). Signed-off-by: Lars-Peter Clausen Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- include/sound/simple_card.h | 1 - sound/soc/generic/simple-card.c | 30 ++++++++---------------------- 2 files changed, 8 insertions(+), 23 deletions(-) (limited to 'include') diff --git a/include/sound/simple_card.h b/include/sound/simple_card.h index 1255ddb1d3e2..b9b4f289fe6b 100644 --- a/include/sound/simple_card.h +++ b/include/sound/simple_card.h @@ -16,7 +16,6 @@ struct asoc_simple_dai { const char *name; - unsigned int fmt; unsigned int sysclk; int slots; int slot_width; diff --git a/sound/soc/generic/simple-card.c b/sound/soc/generic/simple-card.c index f7c6734bd5da..3efd9472b8a3 100644 --- a/sound/soc/generic/simple-card.c +++ b/sound/soc/generic/simple-card.c @@ -125,14 +125,6 @@ static int __asoc_simple_card_dai_init(struct snd_soc_dai *dai, { int ret; - if (set->fmt) { - ret = snd_soc_dai_set_fmt(dai, set->fmt); - if (ret && ret != -ENOTSUPP) { - dev_err(dai->dev, "simple-card: set_fmt error\n"); - goto err; - } - } - if (set->sysclk) { ret = snd_soc_dai_set_sysclk(dai, 0, set->sysclk, 0); if (ret && ret != -ENOTSUPP) { @@ -269,12 +261,10 @@ static int asoc_simple_card_parse_daifmt(struct device_node *node, struct device_node *codec, char *prefix, int idx) { + struct snd_soc_dai_link *dai_link = simple_priv_to_link(priv, idx); struct device *dev = simple_priv_to_dev(priv); struct device_node *bitclkmaster = NULL; struct device_node *framemaster = NULL; - struct simple_dai_props *dai_props = simple_priv_to_props(priv, idx); - struct asoc_simple_dai *cpu_dai = &dai_props->cpu_dai; - struct asoc_simple_dai *codec_dai = &dai_props->codec_dai; unsigned int daifmt; daifmt = snd_soc_of_parse_daifmt(node, prefix, @@ -289,8 +279,7 @@ static int asoc_simple_card_parse_daifmt(struct device_node *node, */ dev_dbg(dev, "Revert to legacy daifmt parsing\n"); - cpu_dai->fmt = codec_dai->fmt = - snd_soc_of_parse_daifmt(codec, NULL, NULL, NULL) | + daifmt = snd_soc_of_parse_daifmt(codec, NULL, NULL, NULL) | (daifmt & ~SND_SOC_DAIFMT_CLOCK_MASK); } else { if (codec == bitclkmaster) @@ -299,11 +288,10 @@ static int asoc_simple_card_parse_daifmt(struct device_node *node, else daifmt |= (codec == framemaster) ? SND_SOC_DAIFMT_CBS_CFM : SND_SOC_DAIFMT_CBS_CFS; - - cpu_dai->fmt = daifmt; - codec_dai->fmt = daifmt; } + dai_link->dai_fmt = daifmt; + of_node_put(bitclkmaster); of_node_put(framemaster); @@ -379,13 +367,12 @@ static int asoc_simple_card_dai_link_of(struct device_node *node, dai_link->init = asoc_simple_card_dai_init; dev_dbg(dev, "\tname : %s\n", dai_link->stream_name); - dev_dbg(dev, "\tcpu : %s / %04x / %d\n", + dev_dbg(dev, "\tformat : %04x\n", dai_link->dai_fmt); + dev_dbg(dev, "\tcpu : %s / %d\n", dai_link->cpu_dai_name, - dai_props->cpu_dai.fmt, dai_props->cpu_dai.sysclk); - dev_dbg(dev, "\tcodec : %s / %04x / %d\n", + dev_dbg(dev, "\tcodec : %s / %d\n", dai_link->codec_dai_name, - dai_props->codec_dai.fmt, dai_props->codec_dai.sysclk); /* @@ -572,14 +559,13 @@ static int asoc_simple_card_probe(struct platform_device *pdev) dai_link->codec_name = cinfo->codec; dai_link->cpu_dai_name = cinfo->cpu_dai.name; dai_link->codec_dai_name = cinfo->codec_dai.name; + dai_link->dai_fmt = cinfo->daifmt; dai_link->init = asoc_simple_card_dai_init; memcpy(&priv->dai_props->cpu_dai, &cinfo->cpu_dai, sizeof(priv->dai_props->cpu_dai)); memcpy(&priv->dai_props->codec_dai, &cinfo->codec_dai, sizeof(priv->dai_props->codec_dai)); - priv->dai_props->cpu_dai.fmt |= cinfo->daifmt; - priv->dai_props->codec_dai.fmt |= cinfo->daifmt; } snd_soc_card_set_drvdata(&priv->snd_card, priv); -- cgit v1.2.3 From 2c60fae1489c70206e66c28d72b69a3e496c313d Mon Sep 17 00:00:00 2001 From: Tommi Rantala Date: Thu, 26 Mar 2015 21:47:16 +0200 Subject: drm/i915: fix definition of the DRM_IOCTL_I915_GET_SPRITE_COLORKEY ioctl Fix definition of the DRM_IOCTL_I915_GET_SPRITE_COLORKEY ioctl, so that it is different from the DRM_IOCTL_I915_SET_SPRITE_COLORKEY ioctl. Note that this is just for accuracy, the ioctl implementation itself is totally unused and already ripped out. Signed-off-by: Tommi Rantala [danvet: Add note that this is a dead ioctl.] Signed-off-by: Daniel Vetter --- include/uapi/drm/i915_drm.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/uapi/drm/i915_drm.h b/include/uapi/drm/i915_drm.h index 8d1be9073380..551b6737f5df 100644 --- a/include/uapi/drm/i915_drm.h +++ b/include/uapi/drm/i915_drm.h @@ -270,7 +270,7 @@ typedef struct _drm_i915_sarea { #define DRM_IOCTL_I915_OVERLAY_PUT_IMAGE DRM_IOW(DRM_COMMAND_BASE + DRM_I915_OVERLAY_PUT_IMAGE, struct drm_intel_overlay_put_image) #define DRM_IOCTL_I915_OVERLAY_ATTRS DRM_IOWR(DRM_COMMAND_BASE + DRM_I915_OVERLAY_ATTRS, struct drm_intel_overlay_attrs) #define DRM_IOCTL_I915_SET_SPRITE_COLORKEY DRM_IOWR(DRM_COMMAND_BASE + DRM_I915_SET_SPRITE_COLORKEY, struct drm_intel_sprite_colorkey) -#define DRM_IOCTL_I915_GET_SPRITE_COLORKEY DRM_IOWR(DRM_COMMAND_BASE + DRM_I915_SET_SPRITE_COLORKEY, struct drm_intel_sprite_colorkey) +#define DRM_IOCTL_I915_GET_SPRITE_COLORKEY DRM_IOWR(DRM_COMMAND_BASE + DRM_I915_GET_SPRITE_COLORKEY, struct drm_intel_sprite_colorkey) #define DRM_IOCTL_I915_GEM_WAIT DRM_IOWR(DRM_COMMAND_BASE + DRM_I915_GEM_WAIT, struct drm_i915_gem_wait) #define DRM_IOCTL_I915_GEM_CONTEXT_CREATE DRM_IOWR (DRM_COMMAND_BASE + DRM_I915_GEM_CONTEXT_CREATE, struct drm_i915_gem_context_create) #define DRM_IOCTL_I915_GEM_CONTEXT_DESTROY DRM_IOW (DRM_COMMAND_BASE + DRM_I915_GEM_CONTEXT_DESTROY, struct drm_i915_gem_context_destroy) -- cgit v1.2.3 From fe87414f71d0035756cf91a80ac256557d16b488 Mon Sep 17 00:00:00 2001 From: Tero Kristo Date: Wed, 12 Mar 2014 18:33:45 +0200 Subject: ARM: OMAP2+: PRCM: split PRCM module init to their own driver files Splits the clock related provider module inits under their own driver files. Previously this was done for all modules under the common PRM driver. Signed-off-by: Tero Kristo --- arch/arm/mach-omap2/cm.h | 1 + arch/arm/mach-omap2/cm_common.c | 51 ++++++++++++++++++++++++++++++++++++++++ arch/arm/mach-omap2/control.c | 47 ++++++++++++++++++++++++++++++++++++ arch/arm/mach-omap2/control.h | 1 + arch/arm/mach-omap2/io.c | 4 ++++ arch/arm/mach-omap2/prm_common.c | 23 ++---------------- include/linux/clk/ti.h | 5 ++-- 7 files changed, 108 insertions(+), 24 deletions(-) (limited to 'include') diff --git a/arch/arm/mach-omap2/cm.h b/arch/arm/mach-omap2/cm.h index 6222e87a79b6..748ac338812b 100644 --- a/arch/arm/mach-omap2/cm.h +++ b/arch/arm/mach-omap2/cm.h @@ -70,6 +70,7 @@ int omap_cm_module_enable(u8 mode, u8 part, u16 inst, u16 clkctrl_offs); int omap_cm_module_disable(u8 part, u16 inst, u16 clkctrl_offs); extern int cm_register(struct cm_ll_data *cld); extern int cm_unregister(struct cm_ll_data *cld); +int omap_cm_init(void); # endif diff --git a/arch/arm/mach-omap2/cm_common.c b/arch/arm/mach-omap2/cm_common.c index 8fe02fcedc48..f3d578be3272 100644 --- a/arch/arm/mach-omap2/cm_common.c +++ b/arch/arm/mach-omap2/cm_common.c @@ -15,10 +15,13 @@ #include #include #include +#include +#include #include "cm2xxx.h" #include "cm3xxx.h" #include "cm44xx.h" +#include "clock.h" /* * cm_ll_data: function pointers to SoC-specific implementations of @@ -212,3 +215,51 @@ int cm_unregister(struct cm_ll_data *cld) return 0; } + +static struct omap_prcm_init_data cm_data = { + .index = TI_CLKM_CM, +}; + +static struct omap_prcm_init_data cm2_data = { + .index = TI_CLKM_CM2, +}; + +static const struct of_device_id omap_cm_dt_match_table[] = { + { .compatible = "ti,omap3-cm", .data = &cm_data }, + { .compatible = "ti,omap4-cm1", .data = &cm_data }, + { .compatible = "ti,omap4-cm2", .data = &cm2_data }, + { .compatible = "ti,omap5-cm-core-aon", .data = &cm_data }, + { .compatible = "ti,omap5-cm-core", .data = &cm2_data }, + { .compatible = "ti,dra7-cm-core-aon", .data = &cm_data }, + { .compatible = "ti,dra7-cm-core", .data = &cm2_data }, + { } +}; + +/** + * omap_cm_init - low level init for the CM drivers + * + * Initializes the low level clock infrastructure for CM drivers. + * Returns 0 in success, negative error value in failure. + */ +int __init omap_cm_init(void) +{ + struct device_node *np; + void __iomem *mem; + const struct of_device_id *match; + const struct omap_prcm_init_data *data; + int ret; + + for_each_matching_node_and_match(np, omap_cm_dt_match_table, &match) { + data = match->data; + + mem = of_iomap(np, 0); + if (!mem) + return -ENOMEM; + + ret = omap2_clk_provider_init(np, data->index, mem); + if (ret) + return ret; + } + + return 0; +} diff --git a/arch/arm/mach-omap2/control.c b/arch/arm/mach-omap2/control.c index da041b4ab29c..e8818242f968 100644 --- a/arch/arm/mach-omap2/control.c +++ b/arch/arm/mach-omap2/control.c @@ -14,6 +14,7 @@ #include #include +#include #include "soc.h" #include "iomap.h" @@ -25,6 +26,7 @@ #include "sdrc.h" #include "pm.h" #include "control.h" +#include "clock.h" /* Used by omap3_ctrl_save_padconf() */ #define START_PADCONF_SAVE 0x2 @@ -611,3 +613,48 @@ void __init omap3_ctrl_init(void) omap3_ctrl_setup_d2d_padconf(); } #endif /* CONFIG_ARCH_OMAP3 && CONFIG_PM */ + +struct control_init_data { + int index; +}; + +static struct control_init_data ctrl_data = { + .index = TI_CLKM_CTRL, +}; + +static const struct of_device_id omap_scrm_dt_match_table[] = { + { .compatible = "ti,am3-scrm", .data = &ctrl_data }, + { .compatible = "ti,am4-scrm", .data = &ctrl_data }, + { .compatible = "ti,omap2-scrm", .data = &ctrl_data }, + { .compatible = "ti,omap3-scrm", .data = &ctrl_data }, + { } +}; + +/** + * omap_control_init - low level init for the control driver + * + * Initializes the low level clock infrastructure for control driver. + * Returns 0 in success, negative error value in failure. + */ +int __init omap_control_init(void) +{ + struct device_node *np; + void __iomem *mem; + const struct of_device_id *match; + const struct omap_prcm_init_data *data; + int ret; + + for_each_matching_node_and_match(np, omap_scrm_dt_match_table, &match) { + data = match->data; + + mem = of_iomap(np, 0); + if (!mem) + return -ENOMEM; + + ret = omap2_clk_provider_init(np, data->index, mem); + if (ret) + return ret; + } + + return 0; +} diff --git a/arch/arm/mach-omap2/control.h b/arch/arm/mach-omap2/control.h index b8a487181210..baf5783cb05d 100644 --- a/arch/arm/mach-omap2/control.h +++ b/arch/arm/mach-omap2/control.h @@ -464,6 +464,7 @@ extern void omap_ctrl_write_dsp_boot_mode(u8 bootmode); extern void omap3630_ctrl_disable_rta(void); extern int omap3_ctrl_save_padconf(void); void omap3_ctrl_init(void); +int omap_control_init(void); extern void omap2_set_globals_control(void __iomem *ctrl, void __iomem *ctrl_pad); #else diff --git a/arch/arm/mach-omap2/io.c b/arch/arm/mach-omap2/io.c index 460da22a005c..46640c0ddc0f 100644 --- a/arch/arm/mach-omap2/io.c +++ b/arch/arm/mach-omap2/io.c @@ -756,6 +756,10 @@ int __init omap_clk_init(void) ti_clk_init_features(); if (of_have_populated_dt()) { + ret = omap_control_init(); + if (ret) + return ret; + ret = omap_prcm_init(); if (ret) return ret; diff --git a/arch/arm/mach-omap2/prm_common.c b/arch/arm/mach-omap2/prm_common.c index 1bfd00e10f76..6cbebbe252c4 100644 --- a/arch/arm/mach-omap2/prm_common.c +++ b/arch/arm/mach-omap2/prm_common.c @@ -637,43 +637,22 @@ static struct omap_prcm_init_data prm_data = { .index = TI_CLKM_PRM, }; -static struct omap_prcm_init_data cm_data = { - .index = TI_CLKM_CM, -}; - -static struct omap_prcm_init_data cm2_data = { - .index = TI_CLKM_CM2, -}; - static struct omap_prcm_init_data scrm_data = { .index = TI_CLKM_SCRM, }; static const struct of_device_id omap_prcm_dt_match_table[] = { { .compatible = "ti,am3-prcm", .data = &prm_data }, - { .compatible = "ti,am3-scrm", .data = &scrm_data }, { .compatible = "ti,am4-prcm", .data = &prm_data }, - { .compatible = "ti,am4-scrm", .data = &scrm_data }, { .compatible = "ti,dm814-prcm", .data = &prm_data }, - { .compatible = "ti,dm814-scrm", .data = &scrm_data }, { .compatible = "ti,dm816-prcm", .data = &prm_data }, - { .compatible = "ti,dm816-scrm", .data = &scrm_data }, { .compatible = "ti,omap2-prcm", .data = &prm_data }, - { .compatible = "ti,omap2-scrm", .data = &scrm_data }, { .compatible = "ti,omap3-prm", .data = &prm_data }, - { .compatible = "ti,omap3-cm", .data = &cm_data }, - { .compatible = "ti,omap3-scrm", .data = &scrm_data }, - { .compatible = "ti,omap4-cm1", .data = &cm_data }, { .compatible = "ti,omap4-prm", .data = &prm_data }, - { .compatible = "ti,omap4-cm2", .data = &cm2_data }, { .compatible = "ti,omap4-scrm", .data = &scrm_data }, { .compatible = "ti,omap5-prm", .data = &prm_data }, - { .compatible = "ti,omap5-cm-core-aon", .data = &cm_data }, { .compatible = "ti,omap5-scrm", .data = &scrm_data }, - { .compatible = "ti,omap5-cm-core", .data = &cm2_data }, { .compatible = "ti,dra7-prm", .data = &prm_data }, - { .compatible = "ti,dra7-cm-core-aon", .data = &cm_data }, - { .compatible = "ti,dra7-cm-core", .data = &cm2_data }, { } }; @@ -703,6 +682,8 @@ int __init omap_prcm_init(void) return ret; } + omap_cm_init(); + return 0; } diff --git a/include/linux/clk/ti.h b/include/linux/clk/ti.h index 19895a3f48b7..79b76e13d904 100644 --- a/include/linux/clk/ti.h +++ b/include/linux/clk/ti.h @@ -215,15 +215,14 @@ struct ti_dt_clk { .node_name = name, \ } -/* Maximum number of clock memmaps */ -#define CLK_MAX_MEMMAPS 4 - /* Static memmap indices */ enum { TI_CLKM_CM = 0, TI_CLKM_CM2, TI_CLKM_PRM, TI_CLKM_SCRM, + TI_CLKM_CTRL, + CLK_MAX_MEMMAPS }; typedef void (*ti_of_clk_init_cb_t)(struct clk_hw *, struct device_node *); -- cgit v1.2.3 From 34f439278cef7b1177f8ce24f9fc81dfc6221d3b Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Fri, 20 Feb 2015 14:05:38 +0100 Subject: perf: Add per event clockid support While thinking on the whole clock discussion it occurred to me we have two distinct uses of time: 1) the tracking of event/ctx/cgroup enabled/running/stopped times which includes the self-monitoring support in struct perf_event_mmap_page. 2) the actual timestamps visible in the data records. And we've been conflating them. The first is all about tracking time deltas, nobody should really care in what time base that happens, its all relative information, as long as its internally consistent it works. The second however is what people are worried about when having to merge their data with external sources. And here we have the discussion on MONOTONIC vs MONOTONIC_RAW etc.. Where MONOTONIC is good for correlating between machines (static offset), MONOTNIC_RAW is required for correlating against a fixed rate hardware clock. This means configurability; now 1) makes that hard because it needs to be internally consistent across groups of unrelated events; which is why we had to have a global perf_clock(). However, for 2) it doesn't really matter, perf itself doesn't care what it writes into the buffer. The below patch makes the distinction between these two cases by adding perf_event_clock() which is used for the second case. It further makes this configurable on a per-event basis, but adds a few sanity checks such that we cannot combine events with different clocks in confusing ways. And since we then have per-event configurability we might as well retain the 'legacy' behaviour as a default. Signed-off-by: Peter Zijlstra (Intel) Cc: Andrew Morton Cc: Arnaldo Carvalho de Melo Cc: David Ahern Cc: Jiri Olsa Cc: John Stultz Cc: Linus Torvalds Cc: Peter Zijlstra Cc: Stephane Eranian Cc: Thomas Gleixner Signed-off-by: Ingo Molnar --- arch/x86/kernel/cpu/perf_event.c | 14 ++++++-- include/linux/perf_event.h | 2 ++ include/uapi/linux/perf_event.h | 6 ++-- kernel/events/core.c | 77 ++++++++++++++++++++++++++++++++++++++-- 4 files changed, 91 insertions(+), 8 deletions(-) (limited to 'include') diff --git a/arch/x86/kernel/cpu/perf_event.c b/arch/x86/kernel/cpu/perf_event.c index ac41b3ad1fc9..0420ebcac116 100644 --- a/arch/x86/kernel/cpu/perf_event.c +++ b/arch/x86/kernel/cpu/perf_event.c @@ -1978,13 +1978,23 @@ void arch_perf_update_userpage(struct perf_event *event, data = cyc2ns_read_begin(); + /* + * Internal timekeeping for enabled/running/stopped times + * is always in the local_clock domain. + */ userpg->cap_user_time = 1; userpg->time_mult = data->cyc2ns_mul; userpg->time_shift = data->cyc2ns_shift; userpg->time_offset = data->cyc2ns_offset - now; - userpg->cap_user_time_zero = 1; - userpg->time_zero = data->cyc2ns_offset; + /* + * cap_user_time_zero doesn't make sense when we're using a different + * time base for the records. + */ + if (event->clock == &local_clock) { + userpg->cap_user_time_zero = 1; + userpg->time_zero = data->cyc2ns_offset; + } cyc2ns_read_end(data); } diff --git a/include/linux/perf_event.h b/include/linux/perf_event.h index b16eac5f54ce..401554074de9 100644 --- a/include/linux/perf_event.h +++ b/include/linux/perf_event.h @@ -173,6 +173,7 @@ struct perf_event; * pmu::capabilities flags */ #define PERF_PMU_CAP_NO_INTERRUPT 0x01 +#define PERF_PMU_CAP_NO_NMI 0x02 /** * struct pmu - generic performance monitoring unit @@ -457,6 +458,7 @@ struct perf_event { struct pid_namespace *ns; u64 id; + u64 (*clock)(void); perf_overflow_handler_t overflow_handler; void *overflow_handler_context; diff --git a/include/uapi/linux/perf_event.h b/include/uapi/linux/perf_event.h index 1e3cd07cf76e..3bb40ddadbe5 100644 --- a/include/uapi/linux/perf_event.h +++ b/include/uapi/linux/perf_event.h @@ -326,7 +326,8 @@ struct perf_event_attr { exclude_callchain_user : 1, /* exclude user callchains */ mmap2 : 1, /* include mmap with inode data */ comm_exec : 1, /* flag comm events that are due to an exec */ - __reserved_1 : 39; + use_clockid : 1, /* use @clockid for time fields */ + __reserved_1 : 38; union { __u32 wakeup_events; /* wakeup every n events */ @@ -355,8 +356,7 @@ struct perf_event_attr { */ __u32 sample_stack_user; - /* Align to u64. */ - __u32 __reserved_2; + __s32 clockid; /* * Defines set of regs to dump for each sample * state captured on: diff --git a/kernel/events/core.c b/kernel/events/core.c index bb1a7c36e794..c40c2cac2d8e 100644 --- a/kernel/events/core.c +++ b/kernel/events/core.c @@ -327,6 +327,11 @@ static inline u64 perf_clock(void) return local_clock(); } +static inline u64 perf_event_clock(struct perf_event *event) +{ + return event->clock(); +} + static inline struct perf_cpu_context * __get_cpu_context(struct perf_event_context *ctx) { @@ -4762,7 +4767,7 @@ static void __perf_event_header__init_id(struct perf_event_header *header, } if (sample_type & PERF_SAMPLE_TIME) - data->time = perf_clock(); + data->time = perf_event_clock(event); if (sample_type & (PERF_SAMPLE_ID | PERF_SAMPLE_IDENTIFIER)) data->id = primary_event_id(event); @@ -5340,6 +5345,8 @@ static void perf_event_task_output(struct perf_event *event, task_event->event_id.tid = perf_event_tid(event, task); task_event->event_id.ptid = perf_event_tid(event, current); + task_event->event_id.time = perf_event_clock(event); + perf_output_put(&handle, task_event->event_id); perf_event__output_id_sample(event, &handle, &sample); @@ -5373,7 +5380,7 @@ static void perf_event_task(struct task_struct *task, /* .ppid */ /* .tid */ /* .ptid */ - .time = perf_clock(), + /* .time */ }, }; @@ -5749,7 +5756,7 @@ static void perf_log_throttle(struct perf_event *event, int enable) .misc = 0, .size = sizeof(throttle_event), }, - .time = perf_clock(), + .time = perf_event_clock(event), .id = primary_event_id(event), .stream_id = event->id, }; @@ -6293,6 +6300,8 @@ static int perf_swevent_init(struct perf_event *event) static struct pmu perf_swevent = { .task_ctx_nr = perf_sw_context, + .capabilities = PERF_PMU_CAP_NO_NMI, + .event_init = perf_swevent_init, .add = perf_swevent_add, .del = perf_swevent_del, @@ -6636,6 +6645,8 @@ static int cpu_clock_event_init(struct perf_event *event) static struct pmu perf_cpu_clock = { .task_ctx_nr = perf_sw_context, + .capabilities = PERF_PMU_CAP_NO_NMI, + .event_init = cpu_clock_event_init, .add = cpu_clock_event_add, .del = cpu_clock_event_del, @@ -6715,6 +6726,8 @@ static int task_clock_event_init(struct perf_event *event) static struct pmu perf_task_clock = { .task_ctx_nr = perf_sw_context, + .capabilities = PERF_PMU_CAP_NO_NMI, + .event_init = task_clock_event_init, .add = task_clock_event_add, .del = task_clock_event_del, @@ -7200,6 +7213,10 @@ perf_event_alloc(struct perf_event_attr *attr, int cpu, event->hw.target = task; } + event->clock = &local_clock; + if (parent_event) + event->clock = parent_event->clock; + if (!overflow_handler && parent_event) { overflow_handler = parent_event->overflow_handler; context = parent_event->overflow_handler_context; @@ -7422,6 +7439,12 @@ perf_event_set_output(struct perf_event *event, struct perf_event *output_event) if (output_event->cpu == -1 && output_event->ctx != event->ctx) goto out; + /* + * Mixing clocks in the same buffer is trouble you don't need. + */ + if (output_event->clock != event->clock) + goto out; + set: mutex_lock(&event->mmap_mutex); /* Can't redirect output if we've got an active mmap() */ @@ -7454,6 +7477,43 @@ static void mutex_lock_double(struct mutex *a, struct mutex *b) mutex_lock_nested(b, SINGLE_DEPTH_NESTING); } +static int perf_event_set_clock(struct perf_event *event, clockid_t clk_id) +{ + bool nmi_safe = false; + + switch (clk_id) { + case CLOCK_MONOTONIC: + event->clock = &ktime_get_mono_fast_ns; + nmi_safe = true; + break; + + case CLOCK_MONOTONIC_RAW: + event->clock = &ktime_get_raw_fast_ns; + nmi_safe = true; + break; + + case CLOCK_REALTIME: + event->clock = &ktime_get_real_ns; + break; + + case CLOCK_BOOTTIME: + event->clock = &ktime_get_boot_ns; + break; + + case CLOCK_TAI: + event->clock = &ktime_get_tai_ns; + break; + + default: + return -EINVAL; + } + + if (!nmi_safe && !(event->pmu->capabilities & PERF_PMU_CAP_NO_NMI)) + return -EINVAL; + + return 0; +} + /** * sys_perf_event_open - open a performance event, associate it to a task/cpu * @@ -7569,6 +7629,12 @@ SYSCALL_DEFINE5(perf_event_open, */ pmu = event->pmu; + if (attr.use_clockid) { + err = perf_event_set_clock(event, attr.clockid); + if (err) + goto err_alloc; + } + if (group_leader && (is_software_event(event) != is_software_event(group_leader))) { if (is_software_event(event)) { @@ -7618,6 +7684,11 @@ SYSCALL_DEFINE5(perf_event_open, */ if (group_leader->group_leader != group_leader) goto err_context; + + /* All events in a group should have the same clock */ + if (group_leader->clock != event->clock) + goto err_context; + /* * Do not allow to attach to a group in a different * task or CPU context: -- cgit v1.2.3 From 772742a6c7ea4612fe043353531e6435ed33e719 Mon Sep 17 00:00:00 2001 From: "Suzuki K. Poulose" Date: Wed, 18 Mar 2015 12:24:40 +0000 Subject: arm-cci: Get rid of secure transactions for PMU driver Avoid secure transactions while probing the CCI PMU. The existing code makes use of the Peripheral ID2 (PID2) register to determine the revision of the CCI400, which requires a secure transaction. This puts a limitation on the usage of the driver on systems running non-secure Linux(e.g, ARM64). Updated the device-tree binding for cci pmu node to add the explicit revision number for the compatible field. The supported strings are : arm,cci-400-pmu,r0 arm,cci-400-pmu,r1 arm,cci-400-pmu - DEPRECATED. See NOTE below NOTE: If the revision is not mentioned, we need to probe the cci revision, which could be fatal on a platform running non-secure. We need a reliable way to know if we can poke the CCI registers at runtime on ARM32. We depend on 'mcpm_is_available()' when it is available. mcpm_is_available() returns true only when there is a registered driver for mcpm. Otherwise, we assume that we don't have secure access, and skips probing the revision number(ARM64 case). The MCPM should figure out if it is safe to access the CCI. Unfortunately there isn't a reliable way to indicate the same via dtb. This patch doesn't address/change the current situation. It only deals with the CCI-PMU, leaving the assumptions about the secure access as it has been, prior to this patch. Cc: devicetree@vger.kernel.org Cc: Punit Agrawal Tested-by: Sudeep Holla Acked-by: Nicolas Pitre Acked-by: Mark Rutland Signed-off-by: Suzuki K. Poulose Signed-off-by: Will Deacon --- Documentation/devicetree/bindings/arm/cci.txt | 7 +++-- arch/arm/include/asm/arm-cci.h | 42 +++++++++++++++++++++++++++ arch/arm64/include/asm/arm-cci.h | 27 +++++++++++++++++ drivers/bus/arm-cci.c | 17 ++++++++++- include/linux/arm-cci.h | 2 ++ 5 files changed, 92 insertions(+), 3 deletions(-) create mode 100644 arch/arm/include/asm/arm-cci.h create mode 100644 arch/arm64/include/asm/arm-cci.h (limited to 'include') diff --git a/Documentation/devicetree/bindings/arm/cci.txt b/Documentation/devicetree/bindings/arm/cci.txt index f28d82bbbc56..3c5c631328d3 100644 --- a/Documentation/devicetree/bindings/arm/cci.txt +++ b/Documentation/devicetree/bindings/arm/cci.txt @@ -94,8 +94,11 @@ specific to ARM. - compatible Usage: required Value type: - Definition: must be "arm,cci-400-pmu" - + Definition: Must contain one of: + "arm,cci-400-pmu,r0" + "arm,cci-400-pmu,r1" + "arm,cci-400-pmu" - DEPRECATED, permitted only where OS has + secure acces to CCI registers - reg: Usage: required Value type: Integer cells. A register entry, expressed diff --git a/arch/arm/include/asm/arm-cci.h b/arch/arm/include/asm/arm-cci.h new file mode 100644 index 000000000000..fe77f7ab7e6b --- /dev/null +++ b/arch/arm/include/asm/arm-cci.h @@ -0,0 +1,42 @@ +/* + * arch/arm/include/asm/arm-cci.h + * + * Copyright (C) 2015 ARM Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __ASM_ARM_CCI_H +#define __ASM_ARM_CCI_H + +#ifdef CONFIG_MCPM +#include + +/* + * We don't have a reliable way of detecting whether, + * if we have access to secure-only registers, unless + * mcpm is registered. + */ +static inline bool platform_has_secure_cci_access(void) +{ + return mcpm_is_available(); +} + +#else +static inline bool platform_has_secure_cci_access(void) +{ + return false; +} +#endif + +#endif diff --git a/arch/arm64/include/asm/arm-cci.h b/arch/arm64/include/asm/arm-cci.h new file mode 100644 index 000000000000..f0b63712e10e --- /dev/null +++ b/arch/arm64/include/asm/arm-cci.h @@ -0,0 +1,27 @@ +/* + * arch/arm64/include/asm/arm-cci.h + * + * Copyright (C) 2015 ARM Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __ASM_ARM_CCI_H +#define __ASM_ARM_CCI_H + +static inline bool platform_has_secure_cci_access(void) +{ + return false; +} + +#endif diff --git a/drivers/bus/arm-cci.c b/drivers/bus/arm-cci.c index ae3864d95e6c..a23663c7a306 100644 --- a/drivers/bus/arm-cci.c +++ b/drivers/bus/arm-cci.c @@ -217,7 +217,9 @@ static int probe_cci_revision(void) static const struct cci_pmu_model *probe_cci_model(struct platform_device *pdev) { - return &cci_pmu_models[probe_cci_revision()]; + if (platform_has_secure_cci_access()) + return &cci_pmu_models[probe_cci_revision()]; + return NULL; } static int pmu_is_valid_counter(struct cci_pmu *cci_pmu, int idx) @@ -882,6 +884,15 @@ static struct cci_pmu_model cci_pmu_models[] = { static const struct of_device_id arm_cci_pmu_matches[] = { { .compatible = "arm,cci-400-pmu", + .data = NULL, + }, + { + .compatible = "arm,cci-400-pmu,r0", + .data = &cci_pmu_models[CCI_REV_R0], + }, + { + .compatible = "arm,cci-400-pmu,r1", + .data = &cci_pmu_models[CCI_REV_R1], }, {}, }; @@ -892,7 +903,11 @@ static inline const struct cci_pmu_model *get_cci_model(struct platform_device * pdev->dev.of_node); if (!match) return NULL; + if (match->data) + return match->data; + dev_warn(&pdev->dev, "DEPRECATED compatible property," + "requires secure access to CCI registers"); return probe_cci_model(pdev); } diff --git a/include/linux/arm-cci.h b/include/linux/arm-cci.h index 79d6edf446d5..aede5c765eec 100644 --- a/include/linux/arm-cci.h +++ b/include/linux/arm-cci.h @@ -24,6 +24,8 @@ #include #include +#include + struct device_node; #ifdef CONFIG_ARM_CCI -- cgit v1.2.3 From ee8e5d5fbec0e880b18bbdbfe12de53ab1dec21f Mon Sep 17 00:00:00 2001 From: "Suzuki K. Poulose" Date: Wed, 18 Mar 2015 12:24:41 +0000 Subject: arm-cci: Split the code for PMU vs driver support This patch separates the PMU driver code from the low level CCI driver code and enables the PMU driver for ARM64. Introduces config options for both. ARM_CCI400_PORT_CTRL - controls the low level driver code for CCI400 ports. ARM_CCI400_PMU - controls the PMU driver code ARM_CCI400_COMMON - Common defintions for CCI400 This patch also changes: ARM_CCI - common code for probing the CCI devices. This can be used for adding support for newer CCI versions(e.g, CCI-500). Cc: Bartlomiej Zolnierkiewicz Cc: Kukjin Kim Cc: Abhilash Kesavan Cc: Liviu Dudau Cc: Lorenzo Pieralisi Cc: Sudeep Holla Cc: Nicolas Pitre Cc: Punit Agrawal Acked-by: Sudeep Holla Acked-by: Nicolas Pitre Acked-by: Punit Agrawal Signed-off-by: Suzuki K. Poulose Signed-off-by: Will Deacon --- arch/arm/mach-exynos/Kconfig | 2 +- arch/arm/mach-vexpress/Kconfig | 4 ++-- drivers/bus/Kconfig | 28 ++++++++++++++++++++++++---- drivers/bus/arm-cci.c | 24 ++++++++++++++++++++---- include/linux/arm-cci.h | 7 ++++++- 5 files changed, 53 insertions(+), 12 deletions(-) (limited to 'include') diff --git a/arch/arm/mach-exynos/Kconfig b/arch/arm/mach-exynos/Kconfig index 603820e5aba7..81064cd61a0a 100644 --- a/arch/arm/mach-exynos/Kconfig +++ b/arch/arm/mach-exynos/Kconfig @@ -123,7 +123,7 @@ config SOC_EXYNOS5800 config EXYNOS5420_MCPM bool "Exynos5420 Multi-Cluster PM support" depends on MCPM && SOC_EXYNOS5420 - select ARM_CCI + select ARM_CCI400_PORT_CTRL select ARM_CPU_SUSPEND help This is needed to provide CPU and cluster power management diff --git a/arch/arm/mach-vexpress/Kconfig b/arch/arm/mach-vexpress/Kconfig index 3c2509b4b694..daa7ab6cb909 100644 --- a/arch/arm/mach-vexpress/Kconfig +++ b/arch/arm/mach-vexpress/Kconfig @@ -53,7 +53,7 @@ config ARCH_VEXPRESS_CORTEX_A5_A9_ERRATA config ARCH_VEXPRESS_DCSCB bool "Dual Cluster System Control Block (DCSCB) support" depends on MCPM - select ARM_CCI + select ARM_CCI400_PORT_CTRL help Support for the Dual Cluster System Configuration Block (DCSCB). This is needed to provide CPU and cluster power management @@ -71,7 +71,7 @@ config ARCH_VEXPRESS_SPC config ARCH_VEXPRESS_TC2_PM bool "Versatile Express TC2 power management" depends on MCPM - select ARM_CCI + select ARM_CCI400_PORT_CTRL select ARCH_VEXPRESS_SPC select ARM_CPU_SUSPEND help diff --git a/drivers/bus/Kconfig b/drivers/bus/Kconfig index b99729e36860..79e297b1f221 100644 --- a/drivers/bus/Kconfig +++ b/drivers/bus/Kconfig @@ -43,12 +43,32 @@ config OMAP_INTERCONNECT help Driver to enable OMAP interconnect error handling driver. -config ARM_CCI - bool "ARM CCI driver support" +config ARM_CCI400_PORT_CTRL + bool depends on ARM && OF && CPU_V7 + select ARM_CCI400_COMMON + help + Low level power management driver for CCI400 cache coherent + interconnect for ARM platforms. + +config ARM_CCI400_PMU + bool "ARM CCI400 PMU support" + default y + depends on ARM || ARM64 + depends on HW_PERF_EVENTS + select ARM_CCI400_COMMON help - Driver supporting the CCI cache coherent interconnect for ARM - platforms. + Support for PMU events monitoring on the ARM CCI cache coherent + interconnect. + + If unsure, say Y + +config ARM_CCI400_COMMON + bool + select ARM_CCI + +config ARM_CCI + bool config ARM_CCN bool "ARM CCN driver support" diff --git a/drivers/bus/arm-cci.c b/drivers/bus/arm-cci.c index a23663c7a306..054df84562c2 100644 --- a/drivers/bus/arm-cci.c +++ b/drivers/bus/arm-cci.c @@ -32,6 +32,7 @@ static void __iomem *cci_ctrl_base; static unsigned long cci_ctrl_phys; +#ifdef CONFIG_ARM_CCI400_PORT_CTRL struct cci_nb_ports { unsigned int nb_ace; unsigned int nb_ace_lite; @@ -42,12 +43,19 @@ static const struct cci_nb_ports cci400_ports = { .nb_ace_lite = 3 }; +#define CCI400_PORTS_DATA (&cci400_ports) +#else +#define CCI400_PORTS_DATA (NULL) +#endif + static const struct of_device_id arm_cci_matches[] = { - {.compatible = "arm,cci-400", .data = &cci400_ports }, +#ifdef CONFIG_ARM_CCI400_COMMON + {.compatible = "arm,cci-400", .data = CCI400_PORTS_DATA }, +#endif {}, }; -#ifdef CONFIG_HW_PERF_EVENTS +#ifdef CONFIG_ARM_CCI400_PMU #define DRIVER_NAME "CCI-400" #define DRIVER_NAME_PMU DRIVER_NAME " PMU" @@ -1022,14 +1030,16 @@ static int __init cci_platform_init(void) return platform_driver_register(&cci_platform_driver); } -#else /* !CONFIG_HW_PERF_EVENTS */ +#else /* !CONFIG_ARM_CCI400_PMU */ static int __init cci_platform_init(void) { return 0; } -#endif /* CONFIG_HW_PERF_EVENTS */ +#endif /* CONFIG_ARM_CCI400_PMU */ + +#ifdef CONFIG_ARM_CCI400_PORT_CTRL #define CCI_PORT_CTRL 0x0 #define CCI_CTRL_STATUS 0xc @@ -1460,6 +1470,12 @@ static int cci_probe_ports(struct device_node *np) return 0; } +#else /* !CONFIG_ARM_CCI400_PORT_CTRL */ +static inline int cci_probe_ports(struct device_node *np) +{ + return 0; +} +#endif /* CONFIG_ARM_CCI400_PORT_CTRL */ static int cci_probe(void) { diff --git a/include/linux/arm-cci.h b/include/linux/arm-cci.h index aede5c765eec..521ec1f2e6bc 100644 --- a/include/linux/arm-cci.h +++ b/include/linux/arm-cci.h @@ -30,12 +30,16 @@ struct device_node; #ifdef CONFIG_ARM_CCI extern bool cci_probed(void); +#else +static inline bool cci_probed(void) { return false; } +#endif + +#ifdef CONFIG_ARM_CCI400_PORT_CTRL extern int cci_ace_get_port(struct device_node *dn); extern int cci_disable_port_by_cpu(u64 mpidr); extern int __cci_control_port_by_device(struct device_node *dn, bool enable); extern int __cci_control_port_by_index(u32 port, bool enable); #else -static inline bool cci_probed(void) { return false; } static inline int cci_ace_get_port(struct device_node *dn) { return -ENODEV; @@ -51,6 +55,7 @@ static inline int __cci_control_port_by_index(u32 port, bool enable) return -ENODEV; } #endif + #define cci_disable_port_by_device(dev) \ __cci_control_port_by_device(dev, false) #define cci_enable_port_by_device(dev) \ -- cgit v1.2.3 From 9c7d3d3e289c9870ae688780a0f9807bb5ae9faf Mon Sep 17 00:00:00 2001 From: Ville Syrjälä Date: Fri, 27 Mar 2015 15:51:55 +0200 Subject: drm: Fix DRM_IOCTL_DEF_DRV() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently DRM_IOCTL_DEF_DRV does '[DRM_IOCTL_NR(DRM_##ioctl)]' which doesn't make much sense since DRM_##ioctl is already a the raw ioctl number. So change it to 'DRM_IOCTL_NR(DRM_IOCTL_##ioctl) - DRM_COMMAND_BASE' which means the DRM_IOCTL_NR() now makes sense, and also this also means if there's a mistake in the DRM_IOCTL_##ioctl macros we might get a warning about it (eg. we would have gotten a sparse warning about the i915 colorkey get/set ioctl being defined to be the same thing). Signed-off-by: Ville Syrjälä Signed-off-by: Daniel Vetter --- include/drm/drmP.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/drm/drmP.h b/include/drm/drmP.h index 63c0b0131f61..6195ee9b4c2a 100644 --- a/include/drm/drmP.h +++ b/include/drm/drmP.h @@ -263,7 +263,7 @@ struct drm_ioctl_desc { */ #define DRM_IOCTL_DEF_DRV(ioctl, _func, _flags) \ - [DRM_IOCTL_NR(DRM_##ioctl)] = {.cmd = DRM_##ioctl, .func = _func, .flags = _flags, .cmd_drv = DRM_IOCTL_##ioctl, .name = #ioctl} + [DRM_IOCTL_NR(DRM_IOCTL_##ioctl) - DRM_COMMAND_BASE] = {.cmd = DRM_##ioctl, .func = _func, .flags = _flags, .cmd_drv = DRM_IOCTL_##ioctl, .name = #ioctl} /* Event queued up for userspace to read */ struct drm_pending_event { -- cgit v1.2.3 From 7e7392a6e854a29016377b87bebe5ae263f65e5c Mon Sep 17 00:00:00 2001 From: Ville Syrjälä Date: Fri, 27 Mar 2015 15:51:56 +0200 Subject: drm: Drop ioctl->cmd_drv MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ioctl->cmd_drv is pointless and we can just as well stick the full ioctl definition into ioctl->cmd. Cc: Jakob Bornecrantz Cc: Thomas Hellstrom Signed-off-by: Ville Syrjälä Signed-off-by: Daniel Vetter --- drivers/gpu/drm/drm_ioctl.c | 6 +++--- drivers/gpu/drm/vmwgfx/vmwgfx_drv.c | 4 ++-- include/drm/drmP.h | 3 +-- 3 files changed, 6 insertions(+), 7 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/drm_ioctl.c b/drivers/gpu/drm/drm_ioctl.c index a6d773a61c2d..fc0cf4a84011 100644 --- a/drivers/gpu/drm/drm_ioctl.c +++ b/drivers/gpu/drm/drm_ioctl.c @@ -525,7 +525,7 @@ static int drm_ioctl_permit(u32 flags, struct drm_file *file_priv) } #define DRM_IOCTL_DEF(ioctl, _func, _flags) \ - [DRM_IOCTL_NR(ioctl)] = {.cmd = ioctl, .func = _func, .flags = _flags, .cmd_drv = 0, .name = #ioctl} + [DRM_IOCTL_NR(ioctl)] = {.cmd = ioctl, .func = _func, .flags = _flags, .name = #ioctl} /** Ioctl table */ static const struct drm_ioctl_desc drm_ioctls[] = { @@ -677,11 +677,11 @@ long drm_ioctl(struct file *filp, (nr < DRM_COMMAND_BASE + dev->driver->num_ioctls)) { u32 drv_size; ioctl = &dev->driver->ioctls[nr - DRM_COMMAND_BASE]; - drv_size = _IOC_SIZE(ioctl->cmd_drv); + drv_size = _IOC_SIZE(ioctl->cmd); usize = asize = _IOC_SIZE(cmd); if (drv_size > asize) asize = drv_size; - cmd = ioctl->cmd_drv; + cmd = ioctl->cmd; } else if ((nr >= DRM_COMMAND_END) || (nr < DRM_COMMAND_BASE)) { u32 drv_size; diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c index 6c6b655defcf..761b2a767ce0 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c @@ -134,7 +134,7 @@ */ #define VMW_IOCTL_DEF(ioctl, func, flags) \ - [DRM_IOCTL_NR(DRM_IOCTL_##ioctl) - DRM_COMMAND_BASE] = {DRM_##ioctl, flags, func, DRM_IOCTL_##ioctl} + [DRM_IOCTL_NR(DRM_IOCTL_##ioctl) - DRM_COMMAND_BASE] = {DRM_IOCTL_##ioctl, flags, func} /** * Ioctl definitions. @@ -1041,7 +1041,7 @@ static long vmw_generic_ioctl(struct file *filp, unsigned int cmd, const struct drm_ioctl_desc *ioctl = &vmw_ioctls[nr - DRM_COMMAND_BASE]; - if (unlikely(ioctl->cmd_drv != cmd)) { + if (unlikely(ioctl->cmd != cmd)) { DRM_ERROR("Invalid command format, ioctl %d\n", nr - DRM_COMMAND_BASE); return -EINVAL; diff --git a/include/drm/drmP.h b/include/drm/drmP.h index 6195ee9b4c2a..0d501ed20775 100644 --- a/include/drm/drmP.h +++ b/include/drm/drmP.h @@ -253,7 +253,6 @@ struct drm_ioctl_desc { unsigned int cmd; int flags; drm_ioctl_t *func; - unsigned int cmd_drv; const char *name; }; @@ -263,7 +262,7 @@ struct drm_ioctl_desc { */ #define DRM_IOCTL_DEF_DRV(ioctl, _func, _flags) \ - [DRM_IOCTL_NR(DRM_IOCTL_##ioctl) - DRM_COMMAND_BASE] = {.cmd = DRM_##ioctl, .func = _func, .flags = _flags, .cmd_drv = DRM_IOCTL_##ioctl, .name = #ioctl} + [DRM_IOCTL_NR(DRM_IOCTL_##ioctl) - DRM_COMMAND_BASE] = {.cmd = DRM_IOCTL_##ioctl, .func = _func, .flags = _flags, .name = #ioctl} /* Event queued up for userspace to read */ struct drm_pending_event { -- cgit v1.2.3 From 5b77d162a3d7359a8a8d83776720da065bf4e77b Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Mon, 23 Mar 2015 09:26:36 +0100 Subject: i2c: slave: rework the slave API MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit After more discussion, brave users, and additional datasheet evaluation, some API updates for the new I2C slave framework became imminent. The slave events now get some easier to understand naming. Also, the event handling has been simplified to only need a single call to the slave callback when an action by the backend is required. Reported-by: Uwe Kleine-König Signed-off-by: Wolfram Sang Acked-by: Geert Uytterhoeven Acked-by: Uwe Kleine-König Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-rcar.c | 10 ++++------ drivers/i2c/i2c-slave-eeprom.c | 12 ++++++------ include/linux/i2c.h | 8 ++++---- 3 files changed, 14 insertions(+), 16 deletions(-) (limited to 'include') diff --git a/drivers/i2c/busses/i2c-rcar.c b/drivers/i2c/busses/i2c-rcar.c index 71a6e07eb7ab..5a84bea5b845 100644 --- a/drivers/i2c/busses/i2c-rcar.c +++ b/drivers/i2c/busses/i2c-rcar.c @@ -382,11 +382,11 @@ static bool rcar_i2c_slave_irq(struct rcar_i2c_priv *priv) if (ssr_filtered & SAR) { /* read or write request */ if (ssr_raw & STM) { - i2c_slave_event(priv->slave, I2C_SLAVE_REQ_READ_START, &value); + i2c_slave_event(priv->slave, I2C_SLAVE_READ_REQUESTED, &value); rcar_i2c_write(priv, ICRXTX, value); rcar_i2c_write(priv, ICSIER, SDE | SSR | SAR); } else { - i2c_slave_event(priv->slave, I2C_SLAVE_REQ_WRITE_START, &value); + i2c_slave_event(priv->slave, I2C_SLAVE_WRITE_REQUESTED, &value); rcar_i2c_read(priv, ICRXTX); /* dummy read */ rcar_i2c_write(priv, ICSIER, SDR | SSR | SAR); } @@ -406,17 +406,15 @@ static bool rcar_i2c_slave_irq(struct rcar_i2c_priv *priv) int ret; value = rcar_i2c_read(priv, ICRXTX); - ret = i2c_slave_event(priv->slave, I2C_SLAVE_REQ_WRITE_END, &value); + ret = i2c_slave_event(priv->slave, I2C_SLAVE_WRITE_RECEIVED, &value); /* Send NACK in case of error */ rcar_i2c_write(priv, ICSCR, SIE | SDBS | (ret < 0 ? FNA : 0)); - i2c_slave_event(priv->slave, I2C_SLAVE_REQ_WRITE_START, &value); rcar_i2c_write(priv, ICSSR, ~SDR & 0xff); } /* master wants to read from us */ if (ssr_filtered & SDE) { - i2c_slave_event(priv->slave, I2C_SLAVE_REQ_READ_END, &value); - i2c_slave_event(priv->slave, I2C_SLAVE_REQ_READ_START, &value); + i2c_slave_event(priv->slave, I2C_SLAVE_READ_PROCESSED, &value); rcar_i2c_write(priv, ICRXTX, value); rcar_i2c_write(priv, ICSSR, ~SDE & 0xff); } diff --git a/drivers/i2c/i2c-slave-eeprom.c b/drivers/i2c/i2c-slave-eeprom.c index cf9b09db092f..3fb45d894d80 100644 --- a/drivers/i2c/i2c-slave-eeprom.c +++ b/drivers/i2c/i2c-slave-eeprom.c @@ -36,7 +36,7 @@ static int i2c_slave_eeprom_slave_cb(struct i2c_client *client, struct eeprom_data *eeprom = i2c_get_clientdata(client); switch (event) { - case I2C_SLAVE_REQ_WRITE_END: + case I2C_SLAVE_WRITE_RECEIVED: if (eeprom->first_write) { eeprom->buffer_idx = *val; eeprom->first_write = false; @@ -47,17 +47,17 @@ static int i2c_slave_eeprom_slave_cb(struct i2c_client *client, } break; - case I2C_SLAVE_REQ_READ_START: + case I2C_SLAVE_READ_PROCESSED: + eeprom->buffer_idx++; + /* fallthrough */ + case I2C_SLAVE_READ_REQUESTED: spin_lock(&eeprom->buffer_lock); *val = eeprom->buffer[eeprom->buffer_idx]; spin_unlock(&eeprom->buffer_lock); break; - case I2C_SLAVE_REQ_READ_END: - eeprom->buffer_idx++; - break; - case I2C_SLAVE_STOP: + case I2C_SLAVE_WRITE_REQUESTED: eeprom->first_write = true; break; diff --git a/include/linux/i2c.h b/include/linux/i2c.h index 243d1a1d78b2..c5e4bb2c5759 100644 --- a/include/linux/i2c.h +++ b/include/linux/i2c.h @@ -253,10 +253,10 @@ static inline void i2c_set_clientdata(struct i2c_client *dev, void *data) #if IS_ENABLED(CONFIG_I2C_SLAVE) enum i2c_slave_event { - I2C_SLAVE_REQ_READ_START, - I2C_SLAVE_REQ_READ_END, - I2C_SLAVE_REQ_WRITE_START, - I2C_SLAVE_REQ_WRITE_END, + I2C_SLAVE_READ_REQUESTED, + I2C_SLAVE_WRITE_REQUESTED, + I2C_SLAVE_READ_PROCESSED, + I2C_SLAVE_WRITE_RECEIVED, I2C_SLAVE_STOP, }; -- cgit v1.2.3 From 27842bb18b004a2802f4b3221c79ce638c4bf6ee Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Fri, 13 Mar 2015 11:09:39 -0700 Subject: mmc: Remove msm_sdcc driver This driver is orphaned now that mach-msm has been removed. Delete it. Cc: Chris Ball Cc: David Brown Cc: Bryan Huntsman Cc: Daniel Walker Signed-off-by: Stephen Boyd Acked-by: Ulf Hansson Signed-off-by: Kumar Gala --- drivers/mmc/host/Kconfig | 8 - drivers/mmc/host/Makefile | 1 - drivers/mmc/host/msm_sdcc.c | 1474 ---------------------------- drivers/mmc/host/msm_sdcc.h | 256 ----- include/linux/platform_data/mmc-msm_sdcc.h | 27 - 5 files changed, 1766 deletions(-) delete mode 100644 drivers/mmc/host/msm_sdcc.c delete mode 100644 drivers/mmc/host/msm_sdcc.h delete mode 100644 include/linux/platform_data/mmc-msm_sdcc.h (limited to 'include') diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig index 61ac63a3776a..37d1d80fdf04 100644 --- a/drivers/mmc/host/Kconfig +++ b/drivers/mmc/host/Kconfig @@ -393,14 +393,6 @@ config MMC_SDHCI_MSM If unsure, say N. -config MMC_MSM - tristate "Qualcomm SDCC Controller Support" - depends on MMC && (ARCH_MSM7X00A || ARCH_MSM7X30 || ARCH_QSD8X50) - help - This provides support for the SD/MMC cell found in the - MSM and QSD SOCs from Qualcomm. The controller also has - support for SDIO devices. - config MMC_MXC tristate "Freescale i.MX21/27/31 or MPC512x Multimedia Card support" depends on ARCH_MXC || PPC_MPC512x diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile index 6a7cfe0de332..47f9421d0281 100644 --- a/drivers/mmc/host/Makefile +++ b/drivers/mmc/host/Makefile @@ -24,7 +24,6 @@ obj-$(CONFIG_MMC_OMAP) += omap.o obj-$(CONFIG_MMC_OMAP_HS) += omap_hsmmc.o obj-$(CONFIG_MMC_ATMELMCI) += atmel-mci.o obj-$(CONFIG_MMC_TIFM_SD) += tifm_sd.o -obj-$(CONFIG_MMC_MSM) += msm_sdcc.o obj-$(CONFIG_MMC_MVSDIO) += mvsdio.o obj-$(CONFIG_MMC_DAVINCI) += davinci_mmc.o obj-$(CONFIG_MMC_GOLDFISH) += android-goldfish.o diff --git a/drivers/mmc/host/msm_sdcc.c b/drivers/mmc/host/msm_sdcc.c deleted file mode 100644 index 90c60fd4ff6e..000000000000 --- a/drivers/mmc/host/msm_sdcc.c +++ /dev/null @@ -1,1474 +0,0 @@ -/* - * linux/drivers/mmc/host/msm_sdcc.c - Qualcomm MSM 7X00A SDCC Driver - * - * Copyright (C) 2007 Google Inc, - * Copyright (C) 2003 Deep Blue Solutions, Ltd, All Rights Reserved. - * Copyright (C) 2009, Code Aurora Forum. All Rights Reserved. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Based on mmci.c - * - * Author: San Mehat (san@android.com) - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include -#include -#include - -#include "msm_sdcc.h" - -#define DRIVER_NAME "msm-sdcc" - -#define BUSCLK_PWRSAVE 1 -#define BUSCLK_TIMEOUT (HZ) -static unsigned int msmsdcc_fmin = 144000; -static unsigned int msmsdcc_fmax = 50000000; -static unsigned int msmsdcc_4bit = 1; -static unsigned int msmsdcc_pwrsave = 1; -static unsigned int msmsdcc_piopoll = 1; -static unsigned int msmsdcc_sdioirq; - -#define PIO_SPINMAX 30 -#define CMD_SPINMAX 20 - - -static inline void -msmsdcc_disable_clocks(struct msmsdcc_host *host, int deferr) -{ - WARN_ON(!host->clks_on); - - BUG_ON(host->curr.mrq); - - if (deferr) { - mod_timer(&host->busclk_timer, jiffies + BUSCLK_TIMEOUT); - } else { - del_timer_sync(&host->busclk_timer); - /* Need to check clks_on again in case the busclk - * timer fired - */ - if (host->clks_on) { - clk_disable(host->clk); - clk_disable(host->pclk); - host->clks_on = 0; - } - } -} - -static inline int -msmsdcc_enable_clocks(struct msmsdcc_host *host) -{ - int rc; - - del_timer_sync(&host->busclk_timer); - - if (!host->clks_on) { - rc = clk_enable(host->pclk); - if (rc) - return rc; - rc = clk_enable(host->clk); - if (rc) { - clk_disable(host->pclk); - return rc; - } - udelay(1 + ((3 * USEC_PER_SEC) / - (host->clk_rate ? host->clk_rate : msmsdcc_fmin))); - host->clks_on = 1; - } - return 0; -} - -static inline unsigned int -msmsdcc_readl(struct msmsdcc_host *host, unsigned int reg) -{ - return readl(host->base + reg); -} - -static inline void -msmsdcc_writel(struct msmsdcc_host *host, u32 data, unsigned int reg) -{ - writel(data, host->base + reg); - /* 3 clk delay required! */ - udelay(1 + ((3 * USEC_PER_SEC) / - (host->clk_rate ? host->clk_rate : msmsdcc_fmin))); -} - -static void -msmsdcc_start_command(struct msmsdcc_host *host, struct mmc_command *cmd, - u32 c); - -static void msmsdcc_reset_and_restore(struct msmsdcc_host *host) -{ - u32 mci_clk = 0; - u32 mci_mask0 = 0; - int ret = 0; - - /* Save the controller state */ - mci_clk = readl(host->base + MMCICLOCK); - mci_mask0 = readl(host->base + MMCIMASK0); - - /* Reset the controller */ - ret = clk_reset(host->clk, CLK_RESET_ASSERT); - if (ret) - pr_err("%s: Clock assert failed at %u Hz with err %d\n", - mmc_hostname(host->mmc), host->clk_rate, ret); - - ret = clk_reset(host->clk, CLK_RESET_DEASSERT); - if (ret) - pr_err("%s: Clock deassert failed at %u Hz with err %d\n", - mmc_hostname(host->mmc), host->clk_rate, ret); - - pr_info("%s: Controller has been re-initialiazed\n", - mmc_hostname(host->mmc)); - - /* Restore the contoller state */ - writel(host->pwr, host->base + MMCIPOWER); - writel(mci_clk, host->base + MMCICLOCK); - writel(mci_mask0, host->base + MMCIMASK0); - ret = clk_set_rate(host->clk, host->clk_rate); - if (ret) - pr_err("%s: Failed to set clk rate %u Hz (%d)\n", - mmc_hostname(host->mmc), host->clk_rate, ret); -} - -static void -msmsdcc_request_end(struct msmsdcc_host *host, struct mmc_request *mrq) -{ - BUG_ON(host->curr.data); - - host->curr.mrq = NULL; - host->curr.cmd = NULL; - - if (mrq->data) - mrq->data->bytes_xfered = host->curr.data_xfered; - if (mrq->cmd->error == -ETIMEDOUT) - mdelay(5); - -#if BUSCLK_PWRSAVE - msmsdcc_disable_clocks(host, 1); -#endif - /* - * Need to drop the host lock here; mmc_request_done may call - * back into the driver... - */ - spin_unlock(&host->lock); - mmc_request_done(host->mmc, mrq); - spin_lock(&host->lock); -} - -static void -msmsdcc_stop_data(struct msmsdcc_host *host) -{ - host->curr.data = NULL; - host->curr.got_dataend = 0; -} - -uint32_t msmsdcc_fifo_addr(struct msmsdcc_host *host) -{ - return host->memres->start + MMCIFIFO; -} - -static inline void -msmsdcc_start_command_exec(struct msmsdcc_host *host, u32 arg, u32 c) { - msmsdcc_writel(host, arg, MMCIARGUMENT); - msmsdcc_writel(host, c, MMCICOMMAND); -} - -static void -msmsdcc_dma_exec_func(struct msm_dmov_cmd *cmd) -{ - struct msmsdcc_host *host = (struct msmsdcc_host *)cmd->data; - - msmsdcc_writel(host, host->cmd_timeout, MMCIDATATIMER); - msmsdcc_writel(host, (unsigned int)host->curr.xfer_size, - MMCIDATALENGTH); - msmsdcc_writel(host, (msmsdcc_readl(host, MMCIMASK0) & - (~MCI_IRQ_PIO)) | host->cmd_pio_irqmask, MMCIMASK0); - msmsdcc_writel(host, host->cmd_datactrl, MMCIDATACTRL); - - if (host->cmd_cmd) { - msmsdcc_start_command_exec(host, - (u32) host->cmd_cmd->arg, - (u32) host->cmd_c); - } - host->dma.active = 1; -} - -static void -msmsdcc_dma_complete_tlet(unsigned long data) -{ - struct msmsdcc_host *host = (struct msmsdcc_host *)data; - unsigned long flags; - struct mmc_request *mrq; - struct msm_dmov_errdata err; - - spin_lock_irqsave(&host->lock, flags); - host->dma.active = 0; - - err = host->dma.err; - mrq = host->curr.mrq; - BUG_ON(!mrq); - WARN_ON(!mrq->data); - - if (!(host->dma.result & DMOV_RSLT_VALID)) { - pr_err("msmsdcc: Invalid DataMover result\n"); - goto out; - } - - if (host->dma.result & DMOV_RSLT_DONE) { - host->curr.data_xfered = host->curr.xfer_size; - } else { - /* Error or flush */ - if (host->dma.result & DMOV_RSLT_ERROR) - pr_err("%s: DMA error (0x%.8x)\n", - mmc_hostname(host->mmc), host->dma.result); - if (host->dma.result & DMOV_RSLT_FLUSH) - pr_err("%s: DMA channel flushed (0x%.8x)\n", - mmc_hostname(host->mmc), host->dma.result); - - pr_err("Flush data: %.8x %.8x %.8x %.8x %.8x %.8x\n", - err.flush[0], err.flush[1], err.flush[2], - err.flush[3], err.flush[4], err.flush[5]); - - msmsdcc_reset_and_restore(host); - if (!mrq->data->error) - mrq->data->error = -EIO; - } - dma_unmap_sg(mmc_dev(host->mmc), host->dma.sg, host->dma.num_ents, - host->dma.dir); - - host->dma.sg = NULL; - host->dma.busy = 0; - - if (host->curr.got_dataend || mrq->data->error) { - - /* - * If we've already gotten our DATAEND / DATABLKEND - * for this request, then complete it through here. - */ - msmsdcc_stop_data(host); - - if (!mrq->data->error) - host->curr.data_xfered = host->curr.xfer_size; - if (!mrq->data->stop || mrq->cmd->error) { - host->curr.mrq = NULL; - host->curr.cmd = NULL; - mrq->data->bytes_xfered = host->curr.data_xfered; - - spin_unlock_irqrestore(&host->lock, flags); -#if BUSCLK_PWRSAVE - msmsdcc_disable_clocks(host, 1); -#endif - mmc_request_done(host->mmc, mrq); - return; - } else - msmsdcc_start_command(host, mrq->data->stop, 0); - } - -out: - spin_unlock_irqrestore(&host->lock, flags); - return; -} - -static void -msmsdcc_dma_complete_func(struct msm_dmov_cmd *cmd, - unsigned int result, - struct msm_dmov_errdata *err) -{ - struct msmsdcc_dma_data *dma_data = - container_of(cmd, struct msmsdcc_dma_data, hdr); - struct msmsdcc_host *host = dma_data->host; - - dma_data->result = result; - if (err) - memcpy(&dma_data->err, err, sizeof(struct msm_dmov_errdata)); - - tasklet_schedule(&host->dma_tlet); -} - -static int validate_dma(struct msmsdcc_host *host, struct mmc_data *data) -{ - if (host->dma.channel == -1) - return -ENOENT; - - if ((data->blksz * data->blocks) < MCI_FIFOSIZE) - return -EINVAL; - if ((data->blksz * data->blocks) % MCI_FIFOSIZE) - return -EINVAL; - return 0; -} - -static int msmsdcc_config_dma(struct msmsdcc_host *host, struct mmc_data *data) -{ - struct msmsdcc_nc_dmadata *nc; - dmov_box *box; - uint32_t rows; - uint32_t crci; - unsigned int n; - int i, rc; - struct scatterlist *sg = data->sg; - - rc = validate_dma(host, data); - if (rc) - return rc; - - host->dma.sg = data->sg; - host->dma.num_ents = data->sg_len; - - BUG_ON(host->dma.num_ents > NR_SG); /* Prevent memory corruption */ - - nc = host->dma.nc; - - switch (host->pdev_id) { - case 1: - crci = MSMSDCC_CRCI_SDC1; - break; - case 2: - crci = MSMSDCC_CRCI_SDC2; - break; - case 3: - crci = MSMSDCC_CRCI_SDC3; - break; - case 4: - crci = MSMSDCC_CRCI_SDC4; - break; - default: - host->dma.sg = NULL; - host->dma.num_ents = 0; - return -ENOENT; - } - - if (data->flags & MMC_DATA_READ) - host->dma.dir = DMA_FROM_DEVICE; - else - host->dma.dir = DMA_TO_DEVICE; - - host->curr.user_pages = 0; - - box = &nc->cmd[0]; - - /* location of command block must be 64 bit aligned */ - BUG_ON(host->dma.cmd_busaddr & 0x07); - - nc->cmdptr = (host->dma.cmd_busaddr >> 3) | CMD_PTR_LP; - host->dma.hdr.cmdptr = DMOV_CMD_PTR_LIST | - DMOV_CMD_ADDR(host->dma.cmdptr_busaddr); - host->dma.hdr.complete_func = msmsdcc_dma_complete_func; - - n = dma_map_sg(mmc_dev(host->mmc), host->dma.sg, - host->dma.num_ents, host->dma.dir); - if (n == 0) { - pr_err("%s: Unable to map in all sg elements\n", - mmc_hostname(host->mmc)); - host->dma.sg = NULL; - host->dma.num_ents = 0; - return -ENOMEM; - } - - for_each_sg(host->dma.sg, sg, n, i) { - - box->cmd = CMD_MODE_BOX; - - if (i == n - 1) - box->cmd |= CMD_LC; - rows = (sg_dma_len(sg) % MCI_FIFOSIZE) ? - (sg_dma_len(sg) / MCI_FIFOSIZE) + 1 : - (sg_dma_len(sg) / MCI_FIFOSIZE) ; - - if (data->flags & MMC_DATA_READ) { - box->src_row_addr = msmsdcc_fifo_addr(host); - box->dst_row_addr = sg_dma_address(sg); - - box->src_dst_len = (MCI_FIFOSIZE << 16) | - (MCI_FIFOSIZE); - box->row_offset = MCI_FIFOSIZE; - - box->num_rows = rows * ((1 << 16) + 1); - box->cmd |= CMD_SRC_CRCI(crci); - } else { - box->src_row_addr = sg_dma_address(sg); - box->dst_row_addr = msmsdcc_fifo_addr(host); - - box->src_dst_len = (MCI_FIFOSIZE << 16) | - (MCI_FIFOSIZE); - box->row_offset = (MCI_FIFOSIZE << 16); - - box->num_rows = rows * ((1 << 16) + 1); - box->cmd |= CMD_DST_CRCI(crci); - } - box++; - } - - return 0; -} - -static int -snoop_cccr_abort(struct mmc_command *cmd) -{ - if ((cmd->opcode == 52) && - (cmd->arg & 0x80000000) && - (((cmd->arg >> 9) & 0x1ffff) == SDIO_CCCR_ABORT)) - return 1; - return 0; -} - -static void -msmsdcc_start_command_deferred(struct msmsdcc_host *host, - struct mmc_command *cmd, u32 *c) -{ - *c |= (cmd->opcode | MCI_CPSM_ENABLE); - - if (cmd->flags & MMC_RSP_PRESENT) { - if (cmd->flags & MMC_RSP_136) - *c |= MCI_CPSM_LONGRSP; - *c |= MCI_CPSM_RESPONSE; - } - - if (/*interrupt*/0) - *c |= MCI_CPSM_INTERRUPT; - - if ((((cmd->opcode == 17) || (cmd->opcode == 18)) || - ((cmd->opcode == 24) || (cmd->opcode == 25))) || - (cmd->opcode == 53)) - *c |= MCI_CSPM_DATCMD; - - if (host->prog_scan && (cmd->opcode == 12)) { - *c |= MCI_CPSM_PROGENA; - host->prog_enable = true; - } - - if (cmd == cmd->mrq->stop) - *c |= MCI_CSPM_MCIABORT; - - if (snoop_cccr_abort(cmd)) - *c |= MCI_CSPM_MCIABORT; - - if (host->curr.cmd != NULL) { - pr_err("%s: Overlapping command requests\n", - mmc_hostname(host->mmc)); - } - host->curr.cmd = cmd; -} - -static void -msmsdcc_start_data(struct msmsdcc_host *host, struct mmc_data *data, - struct mmc_command *cmd, u32 c) -{ - unsigned int datactrl, timeout; - unsigned long long clks; - unsigned int pio_irqmask = 0; - - host->curr.data = data; - host->curr.xfer_size = data->blksz * data->blocks; - host->curr.xfer_remain = host->curr.xfer_size; - host->curr.data_xfered = 0; - host->curr.got_dataend = 0; - - memset(&host->pio, 0, sizeof(host->pio)); - - datactrl = MCI_DPSM_ENABLE | (data->blksz << 4); - - if (!msmsdcc_config_dma(host, data)) - datactrl |= MCI_DPSM_DMAENABLE; - else { - host->pio.sg = data->sg; - host->pio.sg_len = data->sg_len; - host->pio.sg_off = 0; - - if (data->flags & MMC_DATA_READ) { - pio_irqmask = MCI_RXFIFOHALFFULLMASK; - if (host->curr.xfer_remain < MCI_FIFOSIZE) - pio_irqmask |= MCI_RXDATAAVLBLMASK; - } else - pio_irqmask = MCI_TXFIFOHALFEMPTYMASK; - } - - if (data->flags & MMC_DATA_READ) - datactrl |= MCI_DPSM_DIRECTION; - - clks = (unsigned long long)data->timeout_ns * host->clk_rate; - do_div(clks, NSEC_PER_SEC); - timeout = data->timeout_clks + (unsigned int)clks*2 ; - - if (datactrl & MCI_DPSM_DMAENABLE) { - /* Save parameters for the exec function */ - host->cmd_timeout = timeout; - host->cmd_pio_irqmask = pio_irqmask; - host->cmd_datactrl = datactrl; - host->cmd_cmd = cmd; - - host->dma.hdr.execute_func = msmsdcc_dma_exec_func; - host->dma.hdr.data = (void *)host; - host->dma.busy = 1; - - if (cmd) { - msmsdcc_start_command_deferred(host, cmd, &c); - host->cmd_c = c; - } - msm_dmov_enqueue_cmd(host->dma.channel, &host->dma.hdr); - if (data->flags & MMC_DATA_WRITE) - host->prog_scan = true; - } else { - msmsdcc_writel(host, timeout, MMCIDATATIMER); - - msmsdcc_writel(host, host->curr.xfer_size, MMCIDATALENGTH); - - msmsdcc_writel(host, (msmsdcc_readl(host, MMCIMASK0) & - (~MCI_IRQ_PIO)) | pio_irqmask, MMCIMASK0); - - msmsdcc_writel(host, datactrl, MMCIDATACTRL); - - if (cmd) { - /* Daisy-chain the command if requested */ - msmsdcc_start_command(host, cmd, c); - } - } -} - -static void -msmsdcc_start_command(struct msmsdcc_host *host, struct mmc_command *cmd, u32 c) -{ - if (cmd == cmd->mrq->stop) - c |= MCI_CSPM_MCIABORT; - - host->stats.cmds++; - - msmsdcc_start_command_deferred(host, cmd, &c); - msmsdcc_start_command_exec(host, cmd->arg, c); -} - -static void -msmsdcc_data_err(struct msmsdcc_host *host, struct mmc_data *data, - unsigned int status) -{ - if (status & MCI_DATACRCFAIL) { - pr_err("%s: Data CRC error\n", mmc_hostname(host->mmc)); - pr_err("%s: opcode 0x%.8x\n", __func__, - data->mrq->cmd->opcode); - pr_err("%s: blksz %d, blocks %d\n", __func__, - data->blksz, data->blocks); - data->error = -EILSEQ; - } else if (status & MCI_DATATIMEOUT) { - pr_err("%s: Data timeout\n", mmc_hostname(host->mmc)); - data->error = -ETIMEDOUT; - } else if (status & MCI_RXOVERRUN) { - pr_err("%s: RX overrun\n", mmc_hostname(host->mmc)); - data->error = -EIO; - } else if (status & MCI_TXUNDERRUN) { - pr_err("%s: TX underrun\n", mmc_hostname(host->mmc)); - data->error = -EIO; - } else { - pr_err("%s: Unknown error (0x%.8x)\n", - mmc_hostname(host->mmc), status); - data->error = -EIO; - } -} - - -static int -msmsdcc_pio_read(struct msmsdcc_host *host, char *buffer, unsigned int remain) -{ - uint32_t *ptr = (uint32_t *) buffer; - int count = 0; - - if (remain % 4) - remain = ((remain >> 2) + 1) << 2; - - while (msmsdcc_readl(host, MMCISTATUS) & MCI_RXDATAAVLBL) { - *ptr = msmsdcc_readl(host, MMCIFIFO + (count % MCI_FIFOSIZE)); - ptr++; - count += sizeof(uint32_t); - - remain -= sizeof(uint32_t); - if (remain == 0) - break; - } - return count; -} - -static int -msmsdcc_pio_write(struct msmsdcc_host *host, char *buffer, - unsigned int remain, u32 status) -{ - void __iomem *base = host->base; - char *ptr = buffer; - - do { - unsigned int count, maxcnt, sz; - - maxcnt = status & MCI_TXFIFOEMPTY ? MCI_FIFOSIZE : - MCI_FIFOHALFSIZE; - count = min(remain, maxcnt); - - sz = count % 4 ? (count >> 2) + 1 : (count >> 2); - writesl(base + MMCIFIFO, ptr, sz); - ptr += count; - remain -= count; - - if (remain == 0) - break; - - status = msmsdcc_readl(host, MMCISTATUS); - } while (status & MCI_TXFIFOHALFEMPTY); - - return ptr - buffer; -} - -static int -msmsdcc_spin_on_status(struct msmsdcc_host *host, uint32_t mask, int maxspin) -{ - while (maxspin) { - if ((msmsdcc_readl(host, MMCISTATUS) & mask)) - return 0; - udelay(1); - --maxspin; - } - return -ETIMEDOUT; -} - -static irqreturn_t -msmsdcc_pio_irq(int irq, void *dev_id) -{ - struct msmsdcc_host *host = dev_id; - uint32_t status; - u32 mci_mask0; - - status = msmsdcc_readl(host, MMCISTATUS); - mci_mask0 = msmsdcc_readl(host, MMCIMASK0); - - if (((mci_mask0 & status) & MCI_IRQ_PIO) == 0) - return IRQ_NONE; - - do { - unsigned long flags; - unsigned int remain, len; - char *buffer; - - if (!(status & (MCI_TXFIFOHALFEMPTY | MCI_RXDATAAVLBL))) { - if (host->curr.xfer_remain == 0 || !msmsdcc_piopoll) - break; - - if (msmsdcc_spin_on_status(host, - (MCI_TXFIFOHALFEMPTY | - MCI_RXDATAAVLBL), - PIO_SPINMAX)) { - break; - } - } - - /* Map the current scatter buffer */ - local_irq_save(flags); - buffer = kmap_atomic(sg_page(host->pio.sg)) - + host->pio.sg->offset; - buffer += host->pio.sg_off; - remain = host->pio.sg->length - host->pio.sg_off; - len = 0; - if (status & MCI_RXACTIVE) - len = msmsdcc_pio_read(host, buffer, remain); - if (status & MCI_TXACTIVE) - len = msmsdcc_pio_write(host, buffer, remain, status); - - /* Unmap the buffer */ - kunmap_atomic(buffer); - local_irq_restore(flags); - - host->pio.sg_off += len; - host->curr.xfer_remain -= len; - host->curr.data_xfered += len; - remain -= len; - - if (remain == 0) { - /* This sg page is full - do some housekeeping */ - if (status & MCI_RXACTIVE && host->curr.user_pages) - flush_dcache_page(sg_page(host->pio.sg)); - - if (!--host->pio.sg_len) { - memset(&host->pio, 0, sizeof(host->pio)); - break; - } - - /* Advance to next sg */ - host->pio.sg++; - host->pio.sg_off = 0; - } - - status = msmsdcc_readl(host, MMCISTATUS); - } while (1); - - if (status & MCI_RXACTIVE && host->curr.xfer_remain < MCI_FIFOSIZE) - msmsdcc_writel(host, (mci_mask0 & (~MCI_IRQ_PIO)) | - MCI_RXDATAAVLBLMASK, MMCIMASK0); - - if (!host->curr.xfer_remain) - msmsdcc_writel(host, (mci_mask0 & (~MCI_IRQ_PIO)) | 0, - MMCIMASK0); - - return IRQ_HANDLED; -} - -static void msmsdcc_do_cmdirq(struct msmsdcc_host *host, uint32_t status) -{ - struct mmc_command *cmd = host->curr.cmd; - - host->curr.cmd = NULL; - cmd->resp[0] = msmsdcc_readl(host, MMCIRESPONSE0); - cmd->resp[1] = msmsdcc_readl(host, MMCIRESPONSE1); - cmd->resp[2] = msmsdcc_readl(host, MMCIRESPONSE2); - cmd->resp[3] = msmsdcc_readl(host, MMCIRESPONSE3); - - if (status & MCI_CMDTIMEOUT) { - cmd->error = -ETIMEDOUT; - } else if (status & MCI_CMDCRCFAIL && - cmd->flags & MMC_RSP_CRC) { - pr_err("%s: Command CRC error\n", mmc_hostname(host->mmc)); - cmd->error = -EILSEQ; - } - - if (!cmd->data || cmd->error) { - if (host->curr.data && host->dma.sg) - msm_dmov_stop_cmd(host->dma.channel, - &host->dma.hdr, 0); - else if (host->curr.data) { /* Non DMA */ - msmsdcc_reset_and_restore(host); - msmsdcc_stop_data(host); - msmsdcc_request_end(host, cmd->mrq); - } else { /* host->data == NULL */ - if (!cmd->error && host->prog_enable) { - if (status & MCI_PROGDONE) { - host->prog_scan = false; - host->prog_enable = false; - msmsdcc_request_end(host, cmd->mrq); - } else { - host->curr.cmd = cmd; - } - } else { - if (host->prog_enable) { - host->prog_scan = false; - host->prog_enable = false; - } - msmsdcc_request_end(host, cmd->mrq); - } - } - } else if (cmd->data) - if (!(cmd->data->flags & MMC_DATA_READ)) - msmsdcc_start_data(host, cmd->data, - NULL, 0); -} - -static void -msmsdcc_handle_irq_data(struct msmsdcc_host *host, u32 status, - void __iomem *base) -{ - struct mmc_data *data = host->curr.data; - - if (status & (MCI_CMDSENT | MCI_CMDRESPEND | MCI_CMDCRCFAIL | - MCI_CMDTIMEOUT | MCI_PROGDONE) && host->curr.cmd) { - msmsdcc_do_cmdirq(host, status); - } - - if (!data) - return; - - /* Check for data errors */ - if (status & (MCI_DATACRCFAIL | MCI_DATATIMEOUT | - MCI_TXUNDERRUN | MCI_RXOVERRUN)) { - msmsdcc_data_err(host, data, status); - host->curr.data_xfered = 0; - if (host->dma.sg) - msm_dmov_stop_cmd(host->dma.channel, - &host->dma.hdr, 0); - else { - msmsdcc_reset_and_restore(host); - if (host->curr.data) - msmsdcc_stop_data(host); - if (!data->stop) - msmsdcc_request_end(host, data->mrq); - else - msmsdcc_start_command(host, data->stop, 0); - } - } - - /* Check for data done */ - if (!host->curr.got_dataend && (status & MCI_DATAEND)) - host->curr.got_dataend = 1; - - /* - * If DMA is still in progress, we complete via the completion handler - */ - if (host->curr.got_dataend && !host->dma.busy) { - /* - * There appears to be an issue in the controller where - * if you request a small block transfer (< fifo size), - * you may get your DATAEND/DATABLKEND irq without the - * PIO data irq. - * - * Check to see if there is still data to be read, - * and simulate a PIO irq. - */ - if (readl(base + MMCISTATUS) & MCI_RXDATAAVLBL) - msmsdcc_pio_irq(1, host); - - msmsdcc_stop_data(host); - if (!data->error) - host->curr.data_xfered = host->curr.xfer_size; - - if (!data->stop) - msmsdcc_request_end(host, data->mrq); - else - msmsdcc_start_command(host, data->stop, 0); - } -} - -static irqreturn_t -msmsdcc_irq(int irq, void *dev_id) -{ - struct msmsdcc_host *host = dev_id; - void __iomem *base = host->base; - u32 status; - int ret = 0; - int cardint = 0; - - spin_lock(&host->lock); - - do { - status = msmsdcc_readl(host, MMCISTATUS); - status &= msmsdcc_readl(host, MMCIMASK0); - if ((status & (~MCI_IRQ_PIO)) == 0) - break; - msmsdcc_writel(host, status, MMCICLEAR); - - if (status & MCI_SDIOINTR) - status &= ~MCI_SDIOINTR; - - if (!status) - break; - - msmsdcc_handle_irq_data(host, status, base); - - if (status & MCI_SDIOINTOPER) { - cardint = 1; - status &= ~MCI_SDIOINTOPER; - } - ret = 1; - } while (status); - - spin_unlock(&host->lock); - - /* - * We have to delay handling the card interrupt as it calls - * back into the driver. - */ - if (cardint) - mmc_signal_sdio_irq(host->mmc); - - return IRQ_RETVAL(ret); -} - -static void -msmsdcc_request(struct mmc_host *mmc, struct mmc_request *mrq) -{ - struct msmsdcc_host *host = mmc_priv(mmc); - unsigned long flags; - - WARN_ON(host->curr.mrq != NULL); - WARN_ON(host->pwr == 0); - - spin_lock_irqsave(&host->lock, flags); - - host->stats.reqs++; - - if (host->eject) { - if (mrq->data && !(mrq->data->flags & MMC_DATA_READ)) { - mrq->cmd->error = 0; - mrq->data->bytes_xfered = mrq->data->blksz * - mrq->data->blocks; - } else - mrq->cmd->error = -ENOMEDIUM; - - spin_unlock_irqrestore(&host->lock, flags); - mmc_request_done(mmc, mrq); - return; - } - - msmsdcc_enable_clocks(host); - - host->curr.mrq = mrq; - - if (mrq->data && mrq->data->flags & MMC_DATA_READ) - /* Queue/read data, daisy-chain command when data starts */ - msmsdcc_start_data(host, mrq->data, mrq->cmd, 0); - else - msmsdcc_start_command(host, mrq->cmd, 0); - - if (host->cmdpoll && !msmsdcc_spin_on_status(host, - MCI_CMDRESPEND|MCI_CMDCRCFAIL|MCI_CMDTIMEOUT, - CMD_SPINMAX)) { - uint32_t status = msmsdcc_readl(host, MMCISTATUS); - msmsdcc_do_cmdirq(host, status); - msmsdcc_writel(host, - MCI_CMDRESPEND | MCI_CMDCRCFAIL | MCI_CMDTIMEOUT, - MMCICLEAR); - host->stats.cmdpoll_hits++; - } else { - host->stats.cmdpoll_misses++; - } - spin_unlock_irqrestore(&host->lock, flags); -} - -static void msmsdcc_setup_gpio(struct msmsdcc_host *host, bool enable) -{ - struct msm_mmc_gpio_data *curr; - int i, rc = 0; - - if (!host->plat->gpio_data || host->gpio_config_status == enable) - return; - - curr = host->plat->gpio_data; - for (i = 0; i < curr->size; i++) { - if (enable) { - rc = gpio_request(curr->gpio[i].no, - curr->gpio[i].name); - if (rc) { - pr_err("%s: gpio_request(%d, %s) failed %d\n", - mmc_hostname(host->mmc), - curr->gpio[i].no, - curr->gpio[i].name, rc); - goto free_gpios; - } - } else { - gpio_free(curr->gpio[i].no); - } - } - host->gpio_config_status = enable; - return; - -free_gpios: - for (; i >= 0; i--) - gpio_free(curr->gpio[i].no); -} - -static void -msmsdcc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) -{ - struct msmsdcc_host *host = mmc_priv(mmc); - u32 clk = 0, pwr = 0; - int rc; - unsigned long flags; - - spin_lock_irqsave(&host->lock, flags); - - msmsdcc_enable_clocks(host); - - spin_unlock_irqrestore(&host->lock, flags); - - if (ios->clock) { - if (ios->clock != host->clk_rate) { - rc = clk_set_rate(host->clk, ios->clock); - if (rc < 0) - pr_err("%s: Error setting clock rate (%d)\n", - mmc_hostname(host->mmc), rc); - else - host->clk_rate = ios->clock; - } - clk |= MCI_CLK_ENABLE; - } - - if (ios->bus_width == MMC_BUS_WIDTH_4) - clk |= (2 << 10); /* Set WIDEBUS */ - - if (ios->clock > 400000 && msmsdcc_pwrsave) - clk |= (1 << 9); /* PWRSAVE */ - - clk |= (1 << 12); /* FLOW_ENA */ - clk |= (1 << 15); /* feedback clock */ - - if (host->plat->translate_vdd) - pwr |= host->plat->translate_vdd(mmc_dev(mmc), ios->vdd); - - switch (ios->power_mode) { - case MMC_POWER_OFF: - msmsdcc_setup_gpio(host, false); - break; - case MMC_POWER_UP: - pwr |= MCI_PWR_UP; - msmsdcc_setup_gpio(host, true); - break; - case MMC_POWER_ON: - pwr |= MCI_PWR_ON; - break; - } - - if (ios->bus_mode == MMC_BUSMODE_OPENDRAIN) - pwr |= MCI_OD; - - msmsdcc_writel(host, clk, MMCICLOCK); - - if (host->pwr != pwr) { - host->pwr = pwr; - msmsdcc_writel(host, pwr, MMCIPOWER); - } -#if BUSCLK_PWRSAVE - spin_lock_irqsave(&host->lock, flags); - msmsdcc_disable_clocks(host, 1); - spin_unlock_irqrestore(&host->lock, flags); -#endif -} - -static void msmsdcc_enable_sdio_irq(struct mmc_host *mmc, int enable) -{ - struct msmsdcc_host *host = mmc_priv(mmc); - unsigned long flags; - u32 status; - - spin_lock_irqsave(&host->lock, flags); - if (msmsdcc_sdioirq == 1) { - status = msmsdcc_readl(host, MMCIMASK0); - if (enable) - status |= MCI_SDIOINTOPERMASK; - else - status &= ~MCI_SDIOINTOPERMASK; - host->saved_irq0mask = status; - msmsdcc_writel(host, status, MMCIMASK0); - } - spin_unlock_irqrestore(&host->lock, flags); -} - -static void msmsdcc_init_card(struct mmc_host *mmc, struct mmc_card *card) -{ - struct msmsdcc_host *host = mmc_priv(mmc); - - if (host->plat->init_card) - host->plat->init_card(card); -} - -static const struct mmc_host_ops msmsdcc_ops = { - .request = msmsdcc_request, - .set_ios = msmsdcc_set_ios, - .enable_sdio_irq = msmsdcc_enable_sdio_irq, - .init_card = msmsdcc_init_card, -}; - -static void -msmsdcc_check_status(unsigned long data) -{ - struct msmsdcc_host *host = (struct msmsdcc_host *)data; - unsigned int status; - - if (!host->plat->status) { - mmc_detect_change(host->mmc, 0); - goto out; - } - - status = host->plat->status(mmc_dev(host->mmc)); - host->eject = !status; - if (status ^ host->oldstat) { - pr_info("%s: Slot status change detected (%d -> %d)\n", - mmc_hostname(host->mmc), host->oldstat, status); - if (status) - mmc_detect_change(host->mmc, (5 * HZ) / 2); - else - mmc_detect_change(host->mmc, 0); - } - - host->oldstat = status; - -out: - if (host->timer.function) - mod_timer(&host->timer, jiffies + HZ); -} - -static irqreturn_t -msmsdcc_platform_status_irq(int irq, void *dev_id) -{ - struct msmsdcc_host *host = dev_id; - - pr_debug("%s: %d\n", __func__, irq); - msmsdcc_check_status((unsigned long) host); - return IRQ_HANDLED; -} - -static void -msmsdcc_status_notify_cb(int card_present, void *dev_id) -{ - struct msmsdcc_host *host = dev_id; - - pr_debug("%s: card_present %d\n", mmc_hostname(host->mmc), - card_present); - msmsdcc_check_status((unsigned long) host); -} - -static void -msmsdcc_busclk_expired(unsigned long _data) -{ - struct msmsdcc_host *host = (struct msmsdcc_host *) _data; - - if (host->clks_on) - msmsdcc_disable_clocks(host, 0); -} - -static int -msmsdcc_init_dma(struct msmsdcc_host *host) -{ - memset(&host->dma, 0, sizeof(struct msmsdcc_dma_data)); - host->dma.host = host; - host->dma.channel = -1; - - if (!host->dmares) - return -ENODEV; - - host->dma.nc = dma_alloc_coherent(NULL, - sizeof(struct msmsdcc_nc_dmadata), - &host->dma.nc_busaddr, - GFP_KERNEL); - if (host->dma.nc == NULL) { - pr_err("Unable to allocate DMA buffer\n"); - return -ENOMEM; - } - memset(host->dma.nc, 0x00, sizeof(struct msmsdcc_nc_dmadata)); - host->dma.cmd_busaddr = host->dma.nc_busaddr; - host->dma.cmdptr_busaddr = host->dma.nc_busaddr + - offsetof(struct msmsdcc_nc_dmadata, cmdptr); - host->dma.channel = host->dmares->start; - - return 0; -} - -static int -msmsdcc_probe(struct platform_device *pdev) -{ - struct msm_mmc_platform_data *plat = pdev->dev.platform_data; - struct msmsdcc_host *host; - struct mmc_host *mmc; - struct resource *cmd_irqres = NULL; - struct resource *stat_irqres = NULL; - struct resource *memres = NULL; - struct resource *dmares = NULL; - int ret; - - /* must have platform data */ - if (!plat) { - pr_err("%s: Platform data not available\n", __func__); - ret = -EINVAL; - goto out; - } - - if (pdev->id < 1 || pdev->id > 4) - return -EINVAL; - - if (pdev->resource == NULL || pdev->num_resources < 2) { - pr_err("%s: Invalid resource\n", __func__); - return -ENXIO; - } - - memres = platform_get_resource(pdev, IORESOURCE_MEM, 0); - dmares = platform_get_resource(pdev, IORESOURCE_DMA, 0); - cmd_irqres = platform_get_resource_byname(pdev, IORESOURCE_IRQ, - "cmd_irq"); - stat_irqres = platform_get_resource_byname(pdev, IORESOURCE_IRQ, - "status_irq"); - - if (!cmd_irqres || !memres) { - pr_err("%s: Invalid resource\n", __func__); - return -ENXIO; - } - - /* - * Setup our host structure - */ - - mmc = mmc_alloc_host(sizeof(struct msmsdcc_host), &pdev->dev); - if (!mmc) { - ret = -ENOMEM; - goto out; - } - - host = mmc_priv(mmc); - host->pdev_id = pdev->id; - host->plat = plat; - host->mmc = mmc; - host->curr.cmd = NULL; - init_timer(&host->busclk_timer); - host->busclk_timer.data = (unsigned long) host; - host->busclk_timer.function = msmsdcc_busclk_expired; - - - host->cmdpoll = 1; - - host->base = ioremap(memres->start, PAGE_SIZE); - if (!host->base) { - ret = -ENOMEM; - goto host_free; - } - - host->cmd_irqres = cmd_irqres; - host->memres = memres; - host->dmares = dmares; - spin_lock_init(&host->lock); - - tasklet_init(&host->dma_tlet, msmsdcc_dma_complete_tlet, - (unsigned long)host); - - /* - * Setup DMA - */ - if (host->dmares) { - ret = msmsdcc_init_dma(host); - if (ret) - goto ioremap_free; - } else { - host->dma.channel = -1; - } - - /* Get our clocks */ - host->pclk = clk_get(&pdev->dev, "sdc_pclk"); - if (IS_ERR(host->pclk)) { - ret = PTR_ERR(host->pclk); - goto dma_free; - } - - host->clk = clk_get(&pdev->dev, "sdc_clk"); - if (IS_ERR(host->clk)) { - ret = PTR_ERR(host->clk); - goto pclk_put; - } - - ret = clk_set_rate(host->clk, msmsdcc_fmin); - if (ret) { - pr_err("%s: Clock rate set failed (%d)\n", __func__, ret); - goto clk_put; - } - - ret = clk_prepare(host->pclk); - if (ret) - goto clk_put; - - ret = clk_prepare(host->clk); - if (ret) - goto clk_unprepare_p; - - /* Enable clocks */ - ret = msmsdcc_enable_clocks(host); - if (ret) - goto clk_unprepare; - - host->pclk_rate = clk_get_rate(host->pclk); - host->clk_rate = clk_get_rate(host->clk); - - /* - * Setup MMC host structure - */ - mmc->ops = &msmsdcc_ops; - mmc->f_min = msmsdcc_fmin; - mmc->f_max = msmsdcc_fmax; - mmc->ocr_avail = plat->ocr_mask; - - if (msmsdcc_4bit) - mmc->caps |= MMC_CAP_4_BIT_DATA; - if (msmsdcc_sdioirq) - mmc->caps |= MMC_CAP_SDIO_IRQ; - mmc->caps |= MMC_CAP_MMC_HIGHSPEED | MMC_CAP_SD_HIGHSPEED; - - mmc->max_segs = NR_SG; - mmc->max_blk_size = 4096; /* MCI_DATA_CTL BLOCKSIZE up to 4096 */ - mmc->max_blk_count = 65536; - - mmc->max_req_size = 33554432; /* MCI_DATA_LENGTH is 25 bits */ - mmc->max_seg_size = mmc->max_req_size; - - msmsdcc_writel(host, 0, MMCIMASK0); - msmsdcc_writel(host, 0x5e007ff, MMCICLEAR); - - msmsdcc_writel(host, MCI_IRQENABLE, MMCIMASK0); - host->saved_irq0mask = MCI_IRQENABLE; - - /* - * Setup card detect change - */ - - memset(&host->timer, 0, sizeof(host->timer)); - - if (stat_irqres && !(stat_irqres->flags & IORESOURCE_DISABLED)) { - unsigned long irqflags = IRQF_SHARED | - (stat_irqres->flags & IRQF_TRIGGER_MASK); - - host->stat_irq = stat_irqres->start; - ret = request_irq(host->stat_irq, - msmsdcc_platform_status_irq, - irqflags, - DRIVER_NAME " (slot)", - host); - if (ret) { - pr_err("%s: Unable to get slot IRQ %d (%d)\n", - mmc_hostname(mmc), host->stat_irq, ret); - goto clk_disable; - } - } else if (plat->register_status_notify) { - plat->register_status_notify(msmsdcc_status_notify_cb, host); - } else if (!plat->status) - pr_err("%s: No card detect facilities available\n", - mmc_hostname(mmc)); - else { - init_timer(&host->timer); - host->timer.data = (unsigned long)host; - host->timer.function = msmsdcc_check_status; - host->timer.expires = jiffies + HZ; - add_timer(&host->timer); - } - - if (plat->status) { - host->oldstat = host->plat->status(mmc_dev(host->mmc)); - host->eject = !host->oldstat; - } - - ret = request_irq(cmd_irqres->start, msmsdcc_irq, IRQF_SHARED, - DRIVER_NAME " (cmd)", host); - if (ret) - goto stat_irq_free; - - ret = request_irq(cmd_irqres->start, msmsdcc_pio_irq, IRQF_SHARED, - DRIVER_NAME " (pio)", host); - if (ret) - goto cmd_irq_free; - - platform_set_drvdata(pdev, mmc); - mmc_add_host(mmc); - - pr_info("%s: Qualcomm MSM SDCC at 0x%016llx irq %d,%d dma %d\n", - mmc_hostname(mmc), (unsigned long long)memres->start, - (unsigned int) cmd_irqres->start, - (unsigned int) host->stat_irq, host->dma.channel); - pr_info("%s: 4 bit data mode %s\n", mmc_hostname(mmc), - (mmc->caps & MMC_CAP_4_BIT_DATA ? "enabled" : "disabled")); - pr_info("%s: MMC clock %u -> %u Hz, PCLK %u Hz\n", - mmc_hostname(mmc), msmsdcc_fmin, msmsdcc_fmax, host->pclk_rate); - pr_info("%s: Slot eject status = %d\n", mmc_hostname(mmc), host->eject); - pr_info("%s: Power save feature enable = %d\n", - mmc_hostname(mmc), msmsdcc_pwrsave); - - if (host->dma.channel != -1) { - pr_info("%s: DM non-cached buffer at %p, dma_addr 0x%.8x\n", - mmc_hostname(mmc), host->dma.nc, host->dma.nc_busaddr); - pr_info("%s: DM cmd busaddr 0x%.8x, cmdptr busaddr 0x%.8x\n", - mmc_hostname(mmc), host->dma.cmd_busaddr, - host->dma.cmdptr_busaddr); - } else - pr_info("%s: PIO transfer enabled\n", mmc_hostname(mmc)); - if (host->timer.function) - pr_info("%s: Polling status mode enabled\n", mmc_hostname(mmc)); - - return 0; - cmd_irq_free: - free_irq(cmd_irqres->start, host); - stat_irq_free: - if (host->stat_irq) - free_irq(host->stat_irq, host); - clk_disable: - msmsdcc_disable_clocks(host, 0); - clk_unprepare: - clk_unprepare(host->clk); - clk_unprepare_p: - clk_unprepare(host->pclk); - clk_put: - clk_put(host->clk); - pclk_put: - clk_put(host->pclk); -dma_free: - if (host->dmares) - dma_free_coherent(NULL, sizeof(struct msmsdcc_nc_dmadata), - host->dma.nc, host->dma.nc_busaddr); -ioremap_free: - tasklet_kill(&host->dma_tlet); - iounmap(host->base); - host_free: - mmc_free_host(mmc); - out: - return ret; -} - -#ifdef CONFIG_PM -static int -msmsdcc_suspend(struct platform_device *dev, pm_message_t state) -{ - struct mmc_host *mmc = platform_get_drvdata(dev); - - if (mmc) { - struct msmsdcc_host *host = mmc_priv(mmc); - - if (host->stat_irq) - disable_irq(host->stat_irq); - - msmsdcc_writel(host, 0, MMCIMASK0); - if (host->clks_on) - msmsdcc_disable_clocks(host, 0); - } - return 0; -} - -static int -msmsdcc_resume(struct platform_device *dev) -{ - struct mmc_host *mmc = platform_get_drvdata(dev); - - if (mmc) { - struct msmsdcc_host *host = mmc_priv(mmc); - - msmsdcc_enable_clocks(host); - - msmsdcc_writel(host, host->saved_irq0mask, MMCIMASK0); - - if (host->stat_irq) - enable_irq(host->stat_irq); -#if BUSCLK_PWRSAVE - msmsdcc_disable_clocks(host, 1); -#endif - } - return 0; -} -#else -#define msmsdcc_suspend 0 -#define msmsdcc_resume 0 -#endif - -static struct platform_driver msmsdcc_driver = { - .probe = msmsdcc_probe, - .suspend = msmsdcc_suspend, - .resume = msmsdcc_resume, - .driver = { - .name = "msm_sdcc", - }, -}; - -module_platform_driver(msmsdcc_driver); - -MODULE_DESCRIPTION("Qualcomm MSM 7X00A Multimedia Card Interface driver"); -MODULE_LICENSE("GPL"); diff --git a/drivers/mmc/host/msm_sdcc.h b/drivers/mmc/host/msm_sdcc.h deleted file mode 100644 index 402028d16b86..000000000000 --- a/drivers/mmc/host/msm_sdcc.h +++ /dev/null @@ -1,256 +0,0 @@ -/* - * linux/drivers/mmc/host/msmsdcc.h - QCT MSM7K SDC Controller - * - * Copyright (C) 2008 Google, All Rights Reserved. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * - Based on mmci.h - */ - -#ifndef _MSM_SDCC_H -#define _MSM_SDCC_H - -#define MSMSDCC_CRCI_SDC1 6 -#define MSMSDCC_CRCI_SDC2 7 -#define MSMSDCC_CRCI_SDC3 12 -#define MSMSDCC_CRCI_SDC4 13 - -#define MMCIPOWER 0x000 -#define MCI_PWR_OFF 0x00 -#define MCI_PWR_UP 0x02 -#define MCI_PWR_ON 0x03 -#define MCI_OD (1 << 6) - -#define MMCICLOCK 0x004 -#define MCI_CLK_ENABLE (1 << 8) -#define MCI_CLK_PWRSAVE (1 << 9) -#define MCI_CLK_WIDEBUS (1 << 10) -#define MCI_CLK_FLOWENA (1 << 12) -#define MCI_CLK_INVERTOUT (1 << 13) -#define MCI_CLK_SELECTIN (1 << 14) - -#define MMCIARGUMENT 0x008 -#define MMCICOMMAND 0x00c -#define MCI_CPSM_RESPONSE (1 << 6) -#define MCI_CPSM_LONGRSP (1 << 7) -#define MCI_CPSM_INTERRUPT (1 << 8) -#define MCI_CPSM_PENDING (1 << 9) -#define MCI_CPSM_ENABLE (1 << 10) -#define MCI_CPSM_PROGENA (1 << 11) -#define MCI_CSPM_DATCMD (1 << 12) -#define MCI_CSPM_MCIABORT (1 << 13) -#define MCI_CSPM_CCSENABLE (1 << 14) -#define MCI_CSPM_CCSDISABLE (1 << 15) - - -#define MMCIRESPCMD 0x010 -#define MMCIRESPONSE0 0x014 -#define MMCIRESPONSE1 0x018 -#define MMCIRESPONSE2 0x01c -#define MMCIRESPONSE3 0x020 -#define MMCIDATATIMER 0x024 -#define MMCIDATALENGTH 0x028 - -#define MMCIDATACTRL 0x02c -#define MCI_DPSM_ENABLE (1 << 0) -#define MCI_DPSM_DIRECTION (1 << 1) -#define MCI_DPSM_MODE (1 << 2) -#define MCI_DPSM_DMAENABLE (1 << 3) - -#define MMCIDATACNT 0x030 -#define MMCISTATUS 0x034 -#define MCI_CMDCRCFAIL (1 << 0) -#define MCI_DATACRCFAIL (1 << 1) -#define MCI_CMDTIMEOUT (1 << 2) -#define MCI_DATATIMEOUT (1 << 3) -#define MCI_TXUNDERRUN (1 << 4) -#define MCI_RXOVERRUN (1 << 5) -#define MCI_CMDRESPEND (1 << 6) -#define MCI_CMDSENT (1 << 7) -#define MCI_DATAEND (1 << 8) -#define MCI_DATABLOCKEND (1 << 10) -#define MCI_CMDACTIVE (1 << 11) -#define MCI_TXACTIVE (1 << 12) -#define MCI_RXACTIVE (1 << 13) -#define MCI_TXFIFOHALFEMPTY (1 << 14) -#define MCI_RXFIFOHALFFULL (1 << 15) -#define MCI_TXFIFOFULL (1 << 16) -#define MCI_RXFIFOFULL (1 << 17) -#define MCI_TXFIFOEMPTY (1 << 18) -#define MCI_RXFIFOEMPTY (1 << 19) -#define MCI_TXDATAAVLBL (1 << 20) -#define MCI_RXDATAAVLBL (1 << 21) -#define MCI_SDIOINTR (1 << 22) -#define MCI_PROGDONE (1 << 23) -#define MCI_ATACMDCOMPL (1 << 24) -#define MCI_SDIOINTOPER (1 << 25) -#define MCI_CCSTIMEOUT (1 << 26) - -#define MMCICLEAR 0x038 -#define MCI_CMDCRCFAILCLR (1 << 0) -#define MCI_DATACRCFAILCLR (1 << 1) -#define MCI_CMDTIMEOUTCLR (1 << 2) -#define MCI_DATATIMEOUTCLR (1 << 3) -#define MCI_TXUNDERRUNCLR (1 << 4) -#define MCI_RXOVERRUNCLR (1 << 5) -#define MCI_CMDRESPENDCLR (1 << 6) -#define MCI_CMDSENTCLR (1 << 7) -#define MCI_DATAENDCLR (1 << 8) -#define MCI_DATABLOCKENDCLR (1 << 10) - -#define MMCIMASK0 0x03c -#define MCI_CMDCRCFAILMASK (1 << 0) -#define MCI_DATACRCFAILMASK (1 << 1) -#define MCI_CMDTIMEOUTMASK (1 << 2) -#define MCI_DATATIMEOUTMASK (1 << 3) -#define MCI_TXUNDERRUNMASK (1 << 4) -#define MCI_RXOVERRUNMASK (1 << 5) -#define MCI_CMDRESPENDMASK (1 << 6) -#define MCI_CMDSENTMASK (1 << 7) -#define MCI_DATAENDMASK (1 << 8) -#define MCI_DATABLOCKENDMASK (1 << 10) -#define MCI_CMDACTIVEMASK (1 << 11) -#define MCI_TXACTIVEMASK (1 << 12) -#define MCI_RXACTIVEMASK (1 << 13) -#define MCI_TXFIFOHALFEMPTYMASK (1 << 14) -#define MCI_RXFIFOHALFFULLMASK (1 << 15) -#define MCI_TXFIFOFULLMASK (1 << 16) -#define MCI_RXFIFOFULLMASK (1 << 17) -#define MCI_TXFIFOEMPTYMASK (1 << 18) -#define MCI_RXFIFOEMPTYMASK (1 << 19) -#define MCI_TXDATAAVLBLMASK (1 << 20) -#define MCI_RXDATAAVLBLMASK (1 << 21) -#define MCI_SDIOINTMASK (1 << 22) -#define MCI_PROGDONEMASK (1 << 23) -#define MCI_ATACMDCOMPLMASK (1 << 24) -#define MCI_SDIOINTOPERMASK (1 << 25) -#define MCI_CCSTIMEOUTMASK (1 << 26) - -#define MMCIMASK1 0x040 -#define MMCIFIFOCNT 0x044 -#define MCICCSTIMER 0x058 - -#define MMCIFIFO 0x080 /* to 0x0bc */ - -#define MCI_IRQENABLE \ - (MCI_CMDCRCFAILMASK|MCI_DATACRCFAILMASK|MCI_CMDTIMEOUTMASK| \ - MCI_DATATIMEOUTMASK|MCI_TXUNDERRUNMASK|MCI_RXOVERRUNMASK| \ - MCI_CMDRESPENDMASK|MCI_CMDSENTMASK|MCI_DATAENDMASK|MCI_PROGDONEMASK) - -#define MCI_IRQ_PIO \ - (MCI_RXDATAAVLBLMASK | MCI_TXDATAAVLBLMASK | MCI_RXFIFOEMPTYMASK | \ - MCI_TXFIFOEMPTYMASK | MCI_RXFIFOFULLMASK | MCI_TXFIFOFULLMASK | \ - MCI_RXFIFOHALFFULLMASK | MCI_TXFIFOHALFEMPTYMASK | \ - MCI_RXACTIVEMASK | MCI_TXACTIVEMASK) -/* - * The size of the FIFO in bytes. - */ -#define MCI_FIFOSIZE (16*4) - -#define MCI_FIFOHALFSIZE (MCI_FIFOSIZE / 2) - -#define NR_SG 32 - -struct clk; - -struct msmsdcc_nc_dmadata { - dmov_box cmd[NR_SG]; - uint32_t cmdptr; -}; - -struct msmsdcc_dma_data { - struct msmsdcc_nc_dmadata *nc; - dma_addr_t nc_busaddr; - dma_addr_t cmd_busaddr; - dma_addr_t cmdptr_busaddr; - - struct msm_dmov_cmd hdr; - enum dma_data_direction dir; - - struct scatterlist *sg; - int num_ents; - - int channel; - struct msmsdcc_host *host; - int busy; /* Set if DM is busy */ - int active; - unsigned int result; - struct msm_dmov_errdata err; -}; - -struct msmsdcc_pio_data { - struct scatterlist *sg; - unsigned int sg_len; - unsigned int sg_off; -}; - -struct msmsdcc_curr_req { - struct mmc_request *mrq; - struct mmc_command *cmd; - struct mmc_data *data; - unsigned int xfer_size; /* Total data size */ - unsigned int xfer_remain; /* Bytes remaining to send */ - unsigned int data_xfered; /* Bytes acked by BLKEND irq */ - int got_dataend; - int user_pages; -}; - -struct msmsdcc_stats { - unsigned int reqs; - unsigned int cmds; - unsigned int cmdpoll_hits; - unsigned int cmdpoll_misses; -}; - -struct msmsdcc_host { - struct resource *cmd_irqres; - struct resource *memres; - struct resource *dmares; - void __iomem *base; - int pdev_id; - unsigned int stat_irq; - - struct msmsdcc_curr_req curr; - - struct mmc_host *mmc; - struct clk *clk; /* main MMC bus clock */ - struct clk *pclk; /* SDCC peripheral bus clock */ - unsigned int clks_on; /* set if clocks are enabled */ - struct timer_list busclk_timer; - - unsigned int eject; /* eject state */ - - spinlock_t lock; - - unsigned int clk_rate; /* Current clock rate */ - unsigned int pclk_rate; - - u32 pwr; - u32 saved_irq0mask; /* MMCIMASK0 reg value */ - struct msm_mmc_platform_data *plat; - - struct timer_list timer; - unsigned int oldstat; - - struct msmsdcc_dma_data dma; - struct msmsdcc_pio_data pio; - int cmdpoll; - struct msmsdcc_stats stats; - - struct tasklet_struct dma_tlet; - /* Command parameters */ - unsigned int cmd_timeout; - unsigned int cmd_pio_irqmask; - unsigned int cmd_datactrl; - struct mmc_command *cmd_cmd; - u32 cmd_c; - bool gpio_config_status; - - bool prog_scan; - bool prog_enable; -}; - -#endif diff --git a/include/linux/platform_data/mmc-msm_sdcc.h b/include/linux/platform_data/mmc-msm_sdcc.h deleted file mode 100644 index 55aa873c9396..000000000000 --- a/include/linux/platform_data/mmc-msm_sdcc.h +++ /dev/null @@ -1,27 +0,0 @@ -#ifndef __MMC_MSM_SDCC_H -#define __MMC_MSM_SDCC_H - -#include -#include -#include - -struct msm_mmc_gpio { - unsigned no; - const char *name; -}; - -struct msm_mmc_gpio_data { - struct msm_mmc_gpio *gpio; - u8 size; -}; - -struct msm_mmc_platform_data { - unsigned int ocr_mask; /* available voltages */ - u32 (*translate_vdd)(struct device *, unsigned int); - unsigned int (*status)(struct device *); - int (*register_status_notify)(void (*callback)(int card_present, void *dev_id), void *dev_id); - struct msm_mmc_gpio_data *gpio_data; - void (*init_card)(struct mmc_card *card); -}; - -#endif -- cgit v1.2.3 From 4e59080397faadee59d39ffa2116dc8607adc9c9 Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Mon, 9 Mar 2015 14:01:25 -0400 Subject: NFSv4.1: Allow getdeviceinfo to return notification info back to caller We are only allowed to cache deviceinfo if the server supports notifications and actually promises to call us back when changes occur. Right now, we request those notifications, but then we don't check the server's reply. Signed-off-by: Trond Myklebust --- fs/nfs/nfs4proc.c | 5 +++++ fs/nfs/nfs4xdr.c | 14 +++++--------- include/linux/nfs_xdr.h | 2 ++ 3 files changed, 12 insertions(+), 9 deletions(-) (limited to 'include') diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index 627f37c44456..ba8b2b5e98a1 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -7944,6 +7944,8 @@ _nfs4_proc_getdeviceinfo(struct nfs_server *server, { struct nfs4_getdeviceinfo_args args = { .pdev = pdev, + .notify_types = NOTIFY_DEVICEID4_CHANGE | + NOTIFY_DEVICEID4_DELETE, }; struct nfs4_getdeviceinfo_res res = { .pdev = pdev, @@ -7958,6 +7960,9 @@ _nfs4_proc_getdeviceinfo(struct nfs_server *server, dprintk("--> %s\n", __func__); status = nfs4_call_sync(server->client, server, &msg, &args.seq_args, &res.seq_res, 0); + if (res.notification & ~args.notify_types) + dprintk("%s: unsupported notification\n", __func__); + dprintk("<-- %s status=%d\n", __func__, status); return status; diff --git a/fs/nfs/nfs4xdr.c b/fs/nfs/nfs4xdr.c index 5c399ec41079..6b28a605c697 100644 --- a/fs/nfs/nfs4xdr.c +++ b/fs/nfs/nfs4xdr.c @@ -1920,7 +1920,7 @@ encode_getdeviceinfo(struct xdr_stream *xdr, p = reserve_space(xdr, 4 + 4); *p++ = cpu_to_be32(1); /* bitmap length */ - *p++ = cpu_to_be32(NOTIFY_DEVICEID4_CHANGE | NOTIFY_DEVICEID4_DELETE); + *p++ = cpu_to_be32(args->notify_types); } static void @@ -5753,8 +5753,9 @@ out_overflow: #if defined(CONFIG_NFS_V4_1) static int decode_getdeviceinfo(struct xdr_stream *xdr, - struct pnfs_device *pdev) + struct nfs4_getdeviceinfo_res *res) { + struct pnfs_device *pdev = res->pdev; __be32 *p; uint32_t len, type; int status; @@ -5802,12 +5803,7 @@ static int decode_getdeviceinfo(struct xdr_stream *xdr, if (unlikely(!p)) goto out_overflow; - if (be32_to_cpup(p++) & - ~(NOTIFY_DEVICEID4_CHANGE | NOTIFY_DEVICEID4_DELETE)) { - dprintk("%s: unsupported notification\n", - __func__); - } - + res->notification = be32_to_cpup(p++); for (i = 1; i < len; i++) { if (be32_to_cpup(p++)) { dprintk("%s: unsupported notification\n", @@ -7061,7 +7057,7 @@ static int nfs4_xdr_dec_getdeviceinfo(struct rpc_rqst *rqstp, status = decode_sequence(xdr, &res->seq_res, rqstp); if (status != 0) goto out; - status = decode_getdeviceinfo(xdr, res->pdev); + status = decode_getdeviceinfo(xdr, res); out: return status; } diff --git a/include/linux/nfs_xdr.h b/include/linux/nfs_xdr.h index 4cb3eaa89cf7..3d88908fd140 100644 --- a/include/linux/nfs_xdr.h +++ b/include/linux/nfs_xdr.h @@ -255,11 +255,13 @@ struct nfs4_layoutget { struct nfs4_getdeviceinfo_args { struct nfs4_sequence_args seq_args; struct pnfs_device *pdev; + __u32 notify_types; }; struct nfs4_getdeviceinfo_res { struct nfs4_sequence_res seq_res; struct pnfs_device *pdev; + __u32 notification; }; struct nfs4_layoutcommit_args { -- cgit v1.2.3 From 9e1681c2e74bdd84bad6f74b47a4d88ad2f433df Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Wed, 25 Mar 2015 17:23:31 -0400 Subject: NFSv4: Truncating file opens should also sync O_DIRECT writes We don't just want to sync out buffered writes, but also O_DIRECT ones. Signed-off-by: Trond Myklebust --- fs/nfs/inode.c | 3 ++- fs/nfs/nfs4file.c | 2 +- include/linux/nfs_fs.h | 1 + 3 files changed, 4 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c index 60f907666697..f1fc0e4c1c02 100644 --- a/fs/nfs/inode.c +++ b/fs/nfs/inode.c @@ -133,11 +133,12 @@ void nfs_evict_inode(struct inode *inode) nfs_clear_inode(inode); } -static int nfs_sync_inode(struct inode *inode) +int nfs_sync_inode(struct inode *inode) { nfs_inode_dio_wait(inode); return nfs_wb_all(inode); } +EXPORT_SYMBOL_GPL(nfs_sync_inode); /** * nfs_sync_mapping - helper to flush all mmapped dirty data to disk diff --git a/fs/nfs/nfs4file.c b/fs/nfs/nfs4file.c index 0de62f7cfebf..8da5409e6f1a 100644 --- a/fs/nfs/nfs4file.c +++ b/fs/nfs/nfs4file.c @@ -59,7 +59,7 @@ nfs4_file_open(struct inode *inode, struct file *filp) if (openflags & O_TRUNC) { attr.ia_valid |= ATTR_SIZE; attr.ia_size = 0; - nfs_wb_all(inode); + nfs_sync_inode(inode); } inode = NFS_PROTO(dir)->open_context(dir, ctx, openflags, &attr, &opened); diff --git a/include/linux/nfs_fs.h b/include/linux/nfs_fs.h index b01ccf371fdc..b638eb6727c6 100644 --- a/include/linux/nfs_fs.h +++ b/include/linux/nfs_fs.h @@ -512,6 +512,7 @@ extern int nfs_updatepage(struct file *, struct page *, unsigned int, unsigned * Try to write back everything synchronously (but check the * return value!) */ +extern int nfs_sync_inode(struct inode *inode); extern int nfs_wb_all(struct inode *inode); extern int nfs_wb_page(struct inode *inode, struct page* page); extern int nfs_wb_page_cancel(struct inode *inode, struct page* page); -- cgit v1.2.3 From 8cc7f33aadc8fb37b5a3f4c46f5fa83748a92a01 Mon Sep 17 00:00:00 2001 From: Brian Norris Date: Fri, 13 Mar 2015 00:38:39 -0700 Subject: mtd: spi-nor: factor out replace-able flash_{lock,unlock} Flash lock/unlock is a flash-specific operations. Factor out a callback for it to more readily support other vendors. Signed-off-by: Brian Norris Tested-by: VIET NGA DAO --- drivers/mtd/spi-nor/spi-nor.c | 57 ++++++++++++++++++++++++++++--------------- include/linux/mtd/spi-nor.h | 5 ++++ 2 files changed, 43 insertions(+), 19 deletions(-) (limited to 'include') diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c index b6a5a0c269e1..60bf3caf1ead 100644 --- a/drivers/mtd/spi-nor/spi-nor.c +++ b/drivers/mtd/spi-nor/spi-nor.c @@ -369,17 +369,13 @@ erase_err: return ret; } -static int spi_nor_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len) +static int stm_lock(struct spi_nor *nor, loff_t ofs, uint64_t len) { - struct spi_nor *nor = mtd_to_spi_nor(mtd); + struct mtd_info *mtd = nor->mtd; uint32_t offset = ofs; uint8_t status_old, status_new; int ret = 0; - ret = spi_nor_lock_and_prep(nor, SPI_NOR_OPS_LOCK); - if (ret) - return ret; - status_old = read_sr(nor); if (offset < mtd->size - (mtd->size / 2)) @@ -402,26 +398,18 @@ static int spi_nor_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len) (status_old & (SR_BP2 | SR_BP1 | SR_BP0))) { write_enable(nor); ret = write_sr(nor, status_new); - if (ret) - goto err; } -err: - spi_nor_unlock_and_unprep(nor, SPI_NOR_OPS_LOCK); return ret; } -static int spi_nor_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len) +static int stm_unlock(struct spi_nor *nor, loff_t ofs, uint64_t len) { - struct spi_nor *nor = mtd_to_spi_nor(mtd); + struct mtd_info *mtd = nor->mtd; uint32_t offset = ofs; uint8_t status_old, status_new; int ret = 0; - ret = spi_nor_lock_and_prep(nor, SPI_NOR_OPS_UNLOCK); - if (ret) - return ret; - status_old = read_sr(nor); if (offset+len > mtd->size - (mtd->size / 64)) @@ -444,15 +432,41 @@ static int spi_nor_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len) (status_old & (SR_BP2 | SR_BP1 | SR_BP0))) { write_enable(nor); ret = write_sr(nor, status_new); - if (ret) - goto err; } -err: + return ret; +} + +static int spi_nor_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len) +{ + struct spi_nor *nor = mtd_to_spi_nor(mtd); + int ret; + + ret = spi_nor_lock_and_prep(nor, SPI_NOR_OPS_LOCK); + if (ret) + return ret; + + ret = nor->flash_lock(nor, ofs, len); + spi_nor_unlock_and_unprep(nor, SPI_NOR_OPS_UNLOCK); return ret; } +static int spi_nor_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len) +{ + struct spi_nor *nor = mtd_to_spi_nor(mtd); + int ret; + + ret = spi_nor_lock_and_prep(nor, SPI_NOR_OPS_UNLOCK); + if (ret) + return ret; + + ret = nor->flash_unlock(nor, ofs, len); + + spi_nor_unlock_and_unprep(nor, SPI_NOR_OPS_LOCK); + return ret; +} + /* Used when the "_ext_id" is two bytes at most */ #define INFO(_jedec_id, _ext_id, _sector_size, _n_sectors, _flags) \ ((kernel_ulong_t)&(struct flash_info) { \ @@ -1045,6 +1059,11 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode) /* nor protection support for STmicro chips */ if (JEDEC_MFR(info) == CFI_MFR_ST) { + nor->flash_lock = stm_lock; + nor->flash_unlock = stm_unlock; + } + + if (nor->flash_lock && nor->flash_unlock) { mtd->_lock = spi_nor_lock; mtd->_unlock = spi_nor_unlock; } diff --git a/include/linux/mtd/spi-nor.h b/include/linux/mtd/spi-nor.h index 4720b86ee73d..e5409524bb0a 100644 --- a/include/linux/mtd/spi-nor.h +++ b/include/linux/mtd/spi-nor.h @@ -155,6 +155,8 @@ enum spi_nor_option_flags { * @write: [DRIVER-SPECIFIC] write data to the SPI NOR * @erase: [DRIVER-SPECIFIC] erase a sector of the SPI NOR * at the offset @offs + * @lock: [FLASH-SPECIFIC] lock a region of the SPI NOR + * @unlock: [FLASH-SPECIFIC] unlock a region of the SPI NOR * @priv: the private data */ struct spi_nor { @@ -189,6 +191,9 @@ struct spi_nor { size_t len, size_t *retlen, const u_char *write_buf); int (*erase)(struct spi_nor *nor, loff_t offs); + int (*flash_lock)(struct spi_nor *nor, loff_t ofs, uint64_t len); + int (*flash_unlock)(struct spi_nor *nor, loff_t ofs, uint64_t len); + void *priv; }; -- cgit v1.2.3 From 1006e3c93161431c81135b8a23fbfa69dcd43ab0 Mon Sep 17 00:00:00 2001 From: Andrew Bresticker Date: Tue, 24 Feb 2015 19:56:01 -0800 Subject: CLK: Add binding document for Pistachio clock controllers Add a device-tree binding document describing the four clock controllers present on the IMG Pistachio SoC. Signed-off-by: Damien Horsley Signed-off-by: Andrew Bresticker Cc: Rob Herring Cc: Pawel Moll Cc: Mark Rutland Cc: Ian Campbell Cc: Kumar Gala Cc: Mike Turquette Cc: Stephen Boyd Cc: devicetree@vger.kernel.org Cc: linux-mips@linux-mips.org Cc: linux-kernel@vger.kernel.org Cc: Andrew Bresticker Cc: Ezequiel Garcia Cc: James Hartley Cc: James Hogan Cc: Damien Horsley Acked-by: Stephen Boyd Patchwork: https://patchwork.linux-mips.org/patch/9319/ Signed-off-by: Ralf Baechle --- .../devicetree/bindings/clock/pistachio-clock.txt | 123 ++++++++++++++ include/dt-bindings/clock/pistachio-clk.h | 183 +++++++++++++++++++++ 2 files changed, 306 insertions(+) create mode 100644 Documentation/devicetree/bindings/clock/pistachio-clock.txt create mode 100644 include/dt-bindings/clock/pistachio-clk.h (limited to 'include') diff --git a/Documentation/devicetree/bindings/clock/pistachio-clock.txt b/Documentation/devicetree/bindings/clock/pistachio-clock.txt new file mode 100644 index 000000000000..868db499eed2 --- /dev/null +++ b/Documentation/devicetree/bindings/clock/pistachio-clock.txt @@ -0,0 +1,123 @@ +Imagination Technologies Pistachio SoC clock controllers +======================================================== + +Pistachio has four clock controllers (core clock, peripheral clock, peripheral +general control, and top general control) which are instantiated individually +from the device-tree. + +External clocks: +---------------- + +There are three external inputs to the clock controllers which should be +defined with the following clock-output-names: +- "xtal": External 52Mhz oscillator (required) +- "audio_clk_in": Alternate audio reference clock (optional) +- "enet_clk_in": Alternate ethernet PHY clock (optional) + +Core clock controller: +---------------------- + +The core clock controller generates clocks for the CPU, RPU (WiFi + BT +co-processor), audio, and several peripherals. + +Required properties: +- compatible: Must be "img,pistachio-clk". +- reg: Must contain the base address and length of the core clock controller. +- #clock-cells: Must be 1. The single cell is the clock identifier. + See dt-bindings/clock/pistachio-clk.h for the list of valid identifiers. +- clocks: Must contain an entry for each clock in clock-names. +- clock-names: Must include "xtal" (see "External clocks") and + "audio_clk_in_gate", "enet_clk_in_gate" which are generated by the + top-level general control. + +Example: + clk_core: clock-controller@18144000 { + compatible = "img,pistachio-clk"; + reg = <0x18144000 0x800>; + clocks = <&xtal>, <&cr_top EXT_CLK_AUDIO_IN>, + <&cr_top EXT_CLK_ENET_IN>; + clock-names = "xtal", "audio_clk_in_gate", "enet_clk_in_gate"; + + #clock-cells = <1>; + }; + +Peripheral clock controller: +---------------------------- + +The peripheral clock controller generates clocks for the DDR, ROM, and other +peripherals. The peripheral system clock ("periph_sys") generated by the core +clock controller is the input clock to the peripheral clock controller. + +Required properties: +- compatible: Must be "img,pistachio-periph-clk". +- reg: Must contain the base address and length of the peripheral clock + controller. +- #clock-cells: Must be 1. The single cell is the clock identifier. + See dt-bindings/clock/pistachio-clk.h for the list of valid identifiers. +- clocks: Must contain an entry for each clock in clock-names. +- clock-names: Must include "periph_sys", the peripheral system clock generated + by the core clock controller. + +Example: + clk_periph: clock-controller@18144800 { + compatible = "img,pistachio-clk-periph"; + reg = <0x18144800 0x800>; + clocks = <&clk_core CLK_PERIPH_SYS>; + clock-names = "periph_sys"; + + #clock-cells = <1>; + }; + +Peripheral general control: +--------------------------- + +The peripheral general control block generates system interface clocks and +resets for various peripherals. It also contains miscellaneous peripheral +control registers. The system clock ("sys") generated by the peripheral clock +controller is the input clock to the system clock controller. + +Required properties: +- compatible: Must include "img,pistachio-periph-cr" and "syscon". +- reg: Must contain the base address and length of the peripheral general + control registers. +- #clock-cells: Must be 1. The single cell is the clock identifier. + See dt-bindings/clock/pistachio-clk.h for the list of valid identifiers. +- clocks: Must contain an entry for each clock in clock-names. +- clock-names: Must include "sys", the system clock generated by the peripheral + clock controller. + +Example: + cr_periph: syscon@18144800 { + compatible = "img,pistachio-cr-periph", "syscon"; + reg = <0x18148000 0x1000>; + clocks = <&clock_periph PERIPH_CLK_PERIPH_SYS>; + clock-names = "sys"; + + #clock-cells = <1>; + }; + +Top-level general control: +-------------------------- + +The top-level general control block contains miscellaneous control registers and +gates for the external clocks "audio_clk_in" and "enet_clk_in". + +Required properties: +- compatible: Must include "img,pistachio-cr-top" and "syscon". +- reg: Must contain the base address and length of the top-level + control registers. +- clocks: Must contain an entry for each clock in clock-names. +- clock-names: Two optional clocks, "audio_clk_in" and "enet_clk_in" (see + "External clocks"). +- #clock-cells: Must be 1. The single cell is the clock identifier. + See dt-bindings/clock/pistachio-clk.h for the list of valid identifiers. + +Example: + cr_top: syscon@18144800 { + compatible = "img,pistachio-cr-top", "syscon"; + reg = <0x18149000 0x200>; + clocks = <&audio_refclk>, <&ext_enet_in>; + clock-names = "audio_clk_in", "enet_clk_in"; + + #clock-cells = <1>; + }; diff --git a/include/dt-bindings/clock/pistachio-clk.h b/include/dt-bindings/clock/pistachio-clk.h new file mode 100644 index 000000000000..039f83facb68 --- /dev/null +++ b/include/dt-bindings/clock/pistachio-clk.h @@ -0,0 +1,183 @@ +/* + * Copyright (C) 2014 Google, Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + */ + +#ifndef _DT_BINDINGS_CLOCK_PISTACHIO_H +#define _DT_BINDINGS_CLOCK_PISTACHIO_H + +/* PLLs */ +#define CLK_MIPS_PLL 0 +#define CLK_AUDIO_PLL 1 +#define CLK_RPU_V_PLL 2 +#define CLK_RPU_L_PLL 3 +#define CLK_SYS_PLL 4 +#define CLK_WIFI_PLL 5 +#define CLK_BT_PLL 6 + +/* Fixed-factor clocks */ +#define CLK_WIFI_DIV4 16 +#define CLK_WIFI_DIV8 17 + +/* Gate clocks */ +#define CLK_MIPS 32 +#define CLK_AUDIO_IN 33 +#define CLK_AUDIO 34 +#define CLK_I2S 35 +#define CLK_SPDIF 36 +#define CLK_AUDIO_DAC 37 +#define CLK_RPU_V 38 +#define CLK_RPU_L 39 +#define CLK_RPU_SLEEP 40 +#define CLK_WIFI_PLL_GATE 41 +#define CLK_RPU_CORE 42 +#define CLK_WIFI_ADC 43 +#define CLK_WIFI_DAC 44 +#define CLK_USB_PHY 45 +#define CLK_ENET_IN 46 +#define CLK_ENET 47 +#define CLK_UART0 48 +#define CLK_UART1 49 +#define CLK_PERIPH_SYS 50 +#define CLK_SPI0 51 +#define CLK_SPI1 52 +#define CLK_EVENT_TIMER 53 +#define CLK_AUX_ADC_INTERNAL 54 +#define CLK_AUX_ADC 55 +#define CLK_SD_HOST 56 +#define CLK_BT 57 +#define CLK_BT_DIV4 58 +#define CLK_BT_DIV8 59 +#define CLK_BT_1MHZ 60 + +/* Divider clocks */ +#define CLK_MIPS_INTERNAL_DIV 64 +#define CLK_MIPS_DIV 65 +#define CLK_AUDIO_DIV 66 +#define CLK_I2S_DIV 67 +#define CLK_SPDIF_DIV 68 +#define CLK_AUDIO_DAC_DIV 69 +#define CLK_RPU_V_DIV 70 +#define CLK_RPU_L_DIV 71 +#define CLK_RPU_SLEEP_DIV 72 +#define CLK_RPU_CORE_DIV 73 +#define CLK_USB_PHY_DIV 74 +#define CLK_ENET_DIV 75 +#define CLK_UART0_INTERNAL_DIV 76 +#define CLK_UART0_DIV 77 +#define CLK_UART1_INTERNAL_DIV 78 +#define CLK_UART1_DIV 79 +#define CLK_SYS_INTERNAL_DIV 80 +#define CLK_SPI0_INTERNAL_DIV 81 +#define CLK_SPI0_DIV 82 +#define CLK_SPI1_INTERNAL_DIV 83 +#define CLK_SPI1_DIV 84 +#define CLK_EVENT_TIMER_INTERNAL_DIV 85 +#define CLK_EVENT_TIMER_DIV 86 +#define CLK_AUX_ADC_INTERNAL_DIV 87 +#define CLK_AUX_ADC_DIV 88 +#define CLK_SD_HOST_DIV 89 +#define CLK_BT_DIV 90 +#define CLK_BT_DIV4_DIV 91 +#define CLK_BT_DIV8_DIV 92 +#define CLK_BT_1MHZ_INTERNAL_DIV 93 +#define CLK_BT_1MHZ_DIV 94 + +/* Mux clocks */ +#define CLK_AUDIO_REF_MUX 96 +#define CLK_MIPS_PLL_MUX 97 +#define CLK_AUDIO_PLL_MUX 98 +#define CLK_AUDIO_MUX 99 +#define CLK_RPU_V_PLL_MUX 100 +#define CLK_RPU_L_PLL_MUX 101 +#define CLK_RPU_L_MUX 102 +#define CLK_WIFI_PLL_MUX 103 +#define CLK_WIFI_DIV4_MUX 104 +#define CLK_WIFI_DIV8_MUX 105 +#define CLK_RPU_CORE_MUX 106 +#define CLK_SYS_PLL_MUX 107 +#define CLK_ENET_MUX 108 +#define CLK_EVENT_TIMER_MUX 109 +#define CLK_SD_HOST_MUX 110 +#define CLK_BT_PLL_MUX 111 +#define CLK_DEBUG_MUX 112 + +#define CLK_NR_CLKS 113 + +/* Peripheral gate clocks */ +#define PERIPH_CLK_SYS 0 +#define PERIPH_CLK_SYS_BUS 1 +#define PERIPH_CLK_DDR 2 +#define PERIPH_CLK_ROM 3 +#define PERIPH_CLK_COUNTER_FAST 4 +#define PERIPH_CLK_COUNTER_SLOW 5 +#define PERIPH_CLK_IR 6 +#define PERIPH_CLK_WD 7 +#define PERIPH_CLK_PDM 8 +#define PERIPH_CLK_PWM 9 +#define PERIPH_CLK_I2C0 10 +#define PERIPH_CLK_I2C1 11 +#define PERIPH_CLK_I2C2 12 +#define PERIPH_CLK_I2C3 13 + +/* Peripheral divider clocks */ +#define PERIPH_CLK_ROM_DIV 32 +#define PERIPH_CLK_COUNTER_FAST_DIV 33 +#define PERIPH_CLK_COUNTER_SLOW_PRE_DIV 34 +#define PERIPH_CLK_COUNTER_SLOW_DIV 35 +#define PERIPH_CLK_IR_PRE_DIV 36 +#define PERIPH_CLK_IR_DIV 37 +#define PERIPH_CLK_WD_PRE_DIV 38 +#define PERIPH_CLK_WD_DIV 39 +#define PERIPH_CLK_PDM_PRE_DIV 40 +#define PERIPH_CLK_PDM_DIV 41 +#define PERIPH_CLK_PWM_PRE_DIV 42 +#define PERIPH_CLK_PWM_DIV 43 +#define PERIPH_CLK_I2C0_PRE_DIV 44 +#define PERIPH_CLK_I2C0_DIV 45 +#define PERIPH_CLK_I2C1_PRE_DIV 46 +#define PERIPH_CLK_I2C1_DIV 47 +#define PERIPH_CLK_I2C2_PRE_DIV 48 +#define PERIPH_CLK_I2C2_DIV 49 +#define PERIPH_CLK_I2C3_PRE_DIV 50 +#define PERIPH_CLK_I2C3_DIV 51 + +#define PERIPH_CLK_NR_CLKS 52 + +/* System gate clocks */ +#define SYS_CLK_I2C0 0 +#define SYS_CLK_I2C1 1 +#define SYS_CLK_I2C2 2 +#define SYS_CLK_I2C3 3 +#define SYS_CLK_I2S_IN 4 +#define SYS_CLK_PAUD_OUT 5 +#define SYS_CLK_SPDIF_OUT 6 +#define SYS_CLK_SPI0_MASTER 7 +#define SYS_CLK_SPI0_SLAVE 8 +#define SYS_CLK_PWM 9 +#define SYS_CLK_UART0 10 +#define SYS_CLK_UART1 11 +#define SYS_CLK_SPI1 12 +#define SYS_CLK_MDC 13 +#define SYS_CLK_SD_HOST 14 +#define SYS_CLK_ENET 15 +#define SYS_CLK_IR 16 +#define SYS_CLK_WD 17 +#define SYS_CLK_TIMER 18 +#define SYS_CLK_I2S_OUT 24 +#define SYS_CLK_SPDIF_IN 25 +#define SYS_CLK_EVENT_TIMER 26 +#define SYS_CLK_HASH 27 + +#define SYS_CLK_NR_CLKS 28 + +/* Gates for external input clocks */ +#define EXT_CLK_AUDIO_IN 0 +#define EXT_CLK_ENET_IN 1 + +#define EXT_CLK_NR_CLKS 2 + +#endif /* _DT_BINDINGS_CLOCK_PISTACHIO_H */ -- cgit v1.2.3 From 0384e8c6c6fa49dae44ffad31958e9b897da0160 Mon Sep 17 00:00:00 2001 From: Pantelis Antoniou Date: Wed, 21 Jan 2015 19:05:57 +0200 Subject: of: Empty node & property flag accessors when !OF Introduce empty node and property flag accessors when CONFIG_OF is not defined. This allows us to use them without ifdef'ing them in places where it makes sense to do so. Signed-off-by: Pantelis Antoniou Acked-by: Rob Herring Signed-off-by: Grant Likely --- include/linux/of.h | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) (limited to 'include') diff --git a/include/linux/of.h b/include/linux/of.h index dfde07e77a63..7ede4496bad6 100644 --- a/include/linux/of.h +++ b/include/linux/of.h @@ -616,6 +616,38 @@ static inline const char *of_prop_next_string(struct property *prop, return NULL; } +static inline int of_node_check_flag(struct device_node *n, unsigned long flag) +{ + return 0; +} + +static inline int of_node_test_and_set_flag(struct device_node *n, + unsigned long flag) +{ + return 0; +} + +static inline void of_node_set_flag(struct device_node *n, unsigned long flag) +{ +} + +static inline void of_node_clear_flag(struct device_node *n, unsigned long flag) +{ +} + +static inline int of_property_check_flag(struct property *p, unsigned long flag) +{ + return 0; +} + +static inline void of_property_set_flag(struct property *p, unsigned long flag) +{ +} + +static inline void of_property_clear_flag(struct property *p, unsigned long flag) +{ +} + #define of_match_ptr(_ptr) NULL #define of_match_node(_matches, _node) NULL #endif /* CONFIG_OF */ -- cgit v1.2.3 From 271c865161c57cfabca45b93eaa712b19da365bc Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Fri, 27 Mar 2015 12:46:12 +1030 Subject: Add virtio-input driver. virtio-input is basically evdev-events-over-virtio, so this driver isn't much more than reading configuration from config space and forwarding incoming events to the linux input layer. Signed-off-by: Gerd Hoffmann Acked-by: Michael S. Tsirkin Signed-off-by: Rusty Russell --- MAINTAINERS | 6 + drivers/virtio/Kconfig | 10 + drivers/virtio/Makefile | 1 + drivers/virtio/virtio_input.c | 384 ++++++++++++++++++++++++++++++++++++++ include/uapi/linux/Kbuild | 1 + include/uapi/linux/virtio_ids.h | 1 + include/uapi/linux/virtio_input.h | 76 ++++++++ 7 files changed, 479 insertions(+) create mode 100644 drivers/virtio/virtio_input.c create mode 100644 include/uapi/linux/virtio_input.h (limited to 'include') diff --git a/MAINTAINERS b/MAINTAINERS index 0e1abe8cc684..585e6cdd9186 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -10435,6 +10435,12 @@ S: Maintained F: drivers/vhost/ F: include/uapi/linux/vhost.h +VIRTIO INPUT DRIVER +M: Gerd Hoffmann +S: Maintained +F: drivers/virtio/virtio_input.c +F: include/uapi/linux/virtio_input.h + VIA RHINE NETWORK DRIVER M: Roger Luethi S: Maintained diff --git a/drivers/virtio/Kconfig b/drivers/virtio/Kconfig index b546da5d8ea3..cab9f3f63a38 100644 --- a/drivers/virtio/Kconfig +++ b/drivers/virtio/Kconfig @@ -48,6 +48,16 @@ config VIRTIO_BALLOON If unsure, say M. +config VIRTIO_INPUT + tristate "Virtio input driver" + depends on VIRTIO + depends on INPUT + ---help--- + This driver supports virtio input devices such as + keyboards, mice and tablets. + + If unsure, say M. + config VIRTIO_MMIO tristate "Platform bus driver for memory mapped virtio devices" depends on HAS_IOMEM diff --git a/drivers/virtio/Makefile b/drivers/virtio/Makefile index d85565b8ea46..41e30e3dc842 100644 --- a/drivers/virtio/Makefile +++ b/drivers/virtio/Makefile @@ -4,3 +4,4 @@ obj-$(CONFIG_VIRTIO_PCI) += virtio_pci.o virtio_pci-y := virtio_pci_modern.o virtio_pci_common.o virtio_pci-$(CONFIG_VIRTIO_PCI_LEGACY) += virtio_pci_legacy.o obj-$(CONFIG_VIRTIO_BALLOON) += virtio_balloon.o +obj-$(CONFIG_VIRTIO_INPUT) += virtio_input.o diff --git a/drivers/virtio/virtio_input.c b/drivers/virtio/virtio_input.c new file mode 100644 index 000000000000..60e2a1677563 --- /dev/null +++ b/drivers/virtio/virtio_input.c @@ -0,0 +1,384 @@ +#include +#include +#include +#include + +#include +#include + +struct virtio_input { + struct virtio_device *vdev; + struct input_dev *idev; + char name[64]; + char serial[64]; + char phys[64]; + struct virtqueue *evt, *sts; + struct virtio_input_event evts[64]; + spinlock_t lock; + bool ready; +}; + +static void virtinput_queue_evtbuf(struct virtio_input *vi, + struct virtio_input_event *evtbuf) +{ + struct scatterlist sg[1]; + + sg_init_one(sg, evtbuf, sizeof(*evtbuf)); + virtqueue_add_inbuf(vi->evt, sg, 1, evtbuf, GFP_ATOMIC); +} + +static void virtinput_recv_events(struct virtqueue *vq) +{ + struct virtio_input *vi = vq->vdev->priv; + struct virtio_input_event *event; + unsigned long flags; + unsigned int len; + + spin_lock_irqsave(&vi->lock, flags); + if (vi->ready) { + while ((event = virtqueue_get_buf(vi->evt, &len)) != NULL) { + spin_unlock_irqrestore(&vi->lock, flags); + input_event(vi->idev, + le16_to_cpu(event->type), + le16_to_cpu(event->code), + le32_to_cpu(event->value)); + spin_lock_irqsave(&vi->lock, flags); + virtinput_queue_evtbuf(vi, event); + } + virtqueue_kick(vq); + } + spin_unlock_irqrestore(&vi->lock, flags); +} + +/* + * On error we are losing the status update, which isn't critical as + * this is typically used for stuff like keyboard leds. + */ +static int virtinput_send_status(struct virtio_input *vi, + u16 type, u16 code, s32 value) +{ + struct virtio_input_event *stsbuf; + struct scatterlist sg[1]; + unsigned long flags; + int rc; + + stsbuf = kzalloc(sizeof(*stsbuf), GFP_ATOMIC); + if (!stsbuf) + return -ENOMEM; + + stsbuf->type = cpu_to_le16(type); + stsbuf->code = cpu_to_le16(code); + stsbuf->value = cpu_to_le32(value); + sg_init_one(sg, stsbuf, sizeof(*stsbuf)); + + spin_lock_irqsave(&vi->lock, flags); + if (vi->ready) { + rc = virtqueue_add_outbuf(vi->sts, sg, 1, stsbuf, GFP_ATOMIC); + virtqueue_kick(vi->sts); + } else { + rc = -ENODEV; + } + spin_unlock_irqrestore(&vi->lock, flags); + + if (rc != 0) + kfree(stsbuf); + return rc; +} + +static void virtinput_recv_status(struct virtqueue *vq) +{ + struct virtio_input *vi = vq->vdev->priv; + struct virtio_input_event *stsbuf; + unsigned long flags; + unsigned int len; + + spin_lock_irqsave(&vi->lock, flags); + while ((stsbuf = virtqueue_get_buf(vi->sts, &len)) != NULL) + kfree(stsbuf); + spin_unlock_irqrestore(&vi->lock, flags); +} + +static int virtinput_status(struct input_dev *idev, unsigned int type, + unsigned int code, int value) +{ + struct virtio_input *vi = input_get_drvdata(idev); + + return virtinput_send_status(vi, type, code, value); +} + +static u8 virtinput_cfg_select(struct virtio_input *vi, + u8 select, u8 subsel) +{ + u8 size; + + virtio_cwrite(vi->vdev, struct virtio_input_config, select, &select); + virtio_cwrite(vi->vdev, struct virtio_input_config, subsel, &subsel); + virtio_cread(vi->vdev, struct virtio_input_config, size, &size); + return size; +} + +static void virtinput_cfg_bits(struct virtio_input *vi, int select, int subsel, + unsigned long *bits, unsigned int bitcount) +{ + unsigned int bit; + u8 *virtio_bits; + u8 bytes; + + bytes = virtinput_cfg_select(vi, select, subsel); + if (!bytes) + return; + if (bitcount > bytes * 8) + bitcount = bytes * 8; + + /* + * Bitmap in virtio config space is a simple stream of bytes, + * with the first byte carrying bits 0-7, second bits 8-15 and + * so on. + */ + virtio_bits = kzalloc(bytes, GFP_KERNEL); + if (!virtio_bits) + return; + virtio_cread_bytes(vi->vdev, offsetof(struct virtio_input_config, + u.bitmap), + virtio_bits, bytes); + for (bit = 0; bit < bitcount; bit++) { + if (virtio_bits[bit / 8] & (1 << (bit % 8))) + __set_bit(bit, bits); + } + kfree(virtio_bits); + + if (select == VIRTIO_INPUT_CFG_EV_BITS) + __set_bit(subsel, vi->idev->evbit); +} + +static void virtinput_cfg_abs(struct virtio_input *vi, int abs) +{ + u32 mi, ma, re, fu, fl; + + virtinput_cfg_select(vi, VIRTIO_INPUT_CFG_ABS_INFO, abs); + virtio_cread(vi->vdev, struct virtio_input_config, u.abs.min, &mi); + virtio_cread(vi->vdev, struct virtio_input_config, u.abs.max, &ma); + virtio_cread(vi->vdev, struct virtio_input_config, u.abs.res, &re); + virtio_cread(vi->vdev, struct virtio_input_config, u.abs.fuzz, &fu); + virtio_cread(vi->vdev, struct virtio_input_config, u.abs.flat, &fl); + input_set_abs_params(vi->idev, abs, mi, ma, fu, fl); + input_abs_set_res(vi->idev, abs, re); +} + +static int virtinput_init_vqs(struct virtio_input *vi) +{ + struct virtqueue *vqs[2]; + vq_callback_t *cbs[] = { virtinput_recv_events, + virtinput_recv_status }; + static const char *names[] = { "events", "status" }; + int err; + + err = vi->vdev->config->find_vqs(vi->vdev, 2, vqs, cbs, names); + if (err) + return err; + vi->evt = vqs[0]; + vi->sts = vqs[1]; + + return 0; +} + +static void virtinput_fill_evt(struct virtio_input *vi) +{ + unsigned long flags; + int i, size; + + spin_lock_irqsave(&vi->lock, flags); + size = virtqueue_get_vring_size(vi->evt); + if (size > ARRAY_SIZE(vi->evts)) + size = ARRAY_SIZE(vi->evts); + for (i = 0; i < size; i++) + virtinput_queue_evtbuf(vi, &vi->evts[i]); + virtqueue_kick(vi->evt); + spin_unlock_irqrestore(&vi->lock, flags); +} + +static int virtinput_probe(struct virtio_device *vdev) +{ + struct virtio_input *vi; + unsigned long flags; + size_t size; + int abs, err; + + if (!virtio_has_feature(vdev, VIRTIO_F_VERSION_1)) + return -ENODEV; + + vi = kzalloc(sizeof(*vi), GFP_KERNEL); + if (!vi) + return -ENOMEM; + + vdev->priv = vi; + vi->vdev = vdev; + spin_lock_init(&vi->lock); + + err = virtinput_init_vqs(vi); + if (err) + goto err_init_vq; + + vi->idev = input_allocate_device(); + if (!vi->idev) { + err = -ENOMEM; + goto err_input_alloc; + } + input_set_drvdata(vi->idev, vi); + + size = virtinput_cfg_select(vi, VIRTIO_INPUT_CFG_ID_NAME, 0); + virtio_cread_bytes(vi->vdev, offsetof(struct virtio_input_config, + u.string), + vi->name, min(size, sizeof(vi->name))); + size = virtinput_cfg_select(vi, VIRTIO_INPUT_CFG_ID_SERIAL, 0); + virtio_cread_bytes(vi->vdev, offsetof(struct virtio_input_config, + u.string), + vi->serial, min(size, sizeof(vi->serial))); + snprintf(vi->phys, sizeof(vi->phys), + "virtio%d/input0", vdev->index); + vi->idev->name = vi->name; + vi->idev->phys = vi->phys; + vi->idev->uniq = vi->serial; + + size = virtinput_cfg_select(vi, VIRTIO_INPUT_CFG_ID_DEVIDS, 0); + if (size >= sizeof(struct virtio_input_devids)) { + virtio_cread(vi->vdev, struct virtio_input_config, + u.ids.bustype, &vi->idev->id.bustype); + virtio_cread(vi->vdev, struct virtio_input_config, + u.ids.vendor, &vi->idev->id.vendor); + virtio_cread(vi->vdev, struct virtio_input_config, + u.ids.product, &vi->idev->id.product); + virtio_cread(vi->vdev, struct virtio_input_config, + u.ids.version, &vi->idev->id.version); + } else { + vi->idev->id.bustype = BUS_VIRTUAL; + } + + virtinput_cfg_bits(vi, VIRTIO_INPUT_CFG_PROP_BITS, 0, + vi->idev->propbit, INPUT_PROP_CNT); + size = virtinput_cfg_select(vi, VIRTIO_INPUT_CFG_EV_BITS, EV_REP); + if (size) + __set_bit(EV_REP, vi->idev->evbit); + + vi->idev->dev.parent = &vdev->dev; + vi->idev->event = virtinput_status; + + /* device -> kernel */ + virtinput_cfg_bits(vi, VIRTIO_INPUT_CFG_EV_BITS, EV_KEY, + vi->idev->keybit, KEY_CNT); + virtinput_cfg_bits(vi, VIRTIO_INPUT_CFG_EV_BITS, EV_REL, + vi->idev->relbit, REL_CNT); + virtinput_cfg_bits(vi, VIRTIO_INPUT_CFG_EV_BITS, EV_ABS, + vi->idev->absbit, ABS_CNT); + virtinput_cfg_bits(vi, VIRTIO_INPUT_CFG_EV_BITS, EV_MSC, + vi->idev->mscbit, MSC_CNT); + virtinput_cfg_bits(vi, VIRTIO_INPUT_CFG_EV_BITS, EV_SW, + vi->idev->swbit, SW_CNT); + + /* kernel -> device */ + virtinput_cfg_bits(vi, VIRTIO_INPUT_CFG_EV_BITS, EV_LED, + vi->idev->ledbit, LED_CNT); + virtinput_cfg_bits(vi, VIRTIO_INPUT_CFG_EV_BITS, EV_SND, + vi->idev->sndbit, SND_CNT); + + if (test_bit(EV_ABS, vi->idev->evbit)) { + for (abs = 0; abs < ABS_CNT; abs++) { + if (!test_bit(abs, vi->idev->absbit)) + continue; + virtinput_cfg_abs(vi, abs); + } + } + + virtio_device_ready(vdev); + vi->ready = true; + err = input_register_device(vi->idev); + if (err) + goto err_input_register; + + virtinput_fill_evt(vi); + return 0; + +err_input_register: + spin_lock_irqsave(&vi->lock, flags); + vi->ready = false; + spin_unlock_irqrestore(&vi->lock, flags); + input_free_device(vi->idev); +err_input_alloc: + vdev->config->del_vqs(vdev); +err_init_vq: + kfree(vi); + return err; +} + +static void virtinput_remove(struct virtio_device *vdev) +{ + struct virtio_input *vi = vdev->priv; + unsigned long flags; + + spin_lock_irqsave(&vi->lock, flags); + vi->ready = false; + spin_unlock_irqrestore(&vi->lock, flags); + + input_unregister_device(vi->idev); + vdev->config->del_vqs(vdev); + kfree(vi); +} + +#ifdef CONFIG_PM_SLEEP +static int virtinput_freeze(struct virtio_device *vdev) +{ + struct virtio_input *vi = vdev->priv; + unsigned long flags; + + spin_lock_irqsave(&vi->lock, flags); + vi->ready = false; + spin_unlock_irqrestore(&vi->lock, flags); + + vdev->config->del_vqs(vdev); + return 0; +} + +static int virtinput_restore(struct virtio_device *vdev) +{ + struct virtio_input *vi = vdev->priv; + int err; + + err = virtinput_init_vqs(vi); + if (err) + return err; + + virtio_device_ready(vdev); + vi->ready = true; + virtinput_fill_evt(vi); + return 0; +} +#endif + +static unsigned int features[] = { + /* none */ +}; +static struct virtio_device_id id_table[] = { + { VIRTIO_ID_INPUT, VIRTIO_DEV_ANY_ID }, + { 0 }, +}; + +static struct virtio_driver virtio_input_driver = { + .driver.name = KBUILD_MODNAME, + .driver.owner = THIS_MODULE, + .feature_table = features, + .feature_table_size = ARRAY_SIZE(features), + .id_table = id_table, + .probe = virtinput_probe, + .remove = virtinput_remove, +#ifdef CONFIG_PM_SLEEP + .freeze = virtinput_freeze, + .restore = virtinput_restore, +#endif +}; + +module_virtio_driver(virtio_input_driver); +MODULE_DEVICE_TABLE(virtio, id_table); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Virtio input device driver"); +MODULE_AUTHOR("Gerd Hoffmann "); diff --git a/include/uapi/linux/Kbuild b/include/uapi/linux/Kbuild index 68ceb97c458c..04b829e9ac46 100644 --- a/include/uapi/linux/Kbuild +++ b/include/uapi/linux/Kbuild @@ -430,6 +430,7 @@ header-y += virtio_blk.h header-y += virtio_config.h header-y += virtio_console.h header-y += virtio_ids.h +header-y += virtio_input.h header-y += virtio_net.h header-y += virtio_pci.h header-y += virtio_ring.h diff --git a/include/uapi/linux/virtio_ids.h b/include/uapi/linux/virtio_ids.h index 284fc3a05f7b..5f60aa4be50a 100644 --- a/include/uapi/linux/virtio_ids.h +++ b/include/uapi/linux/virtio_ids.h @@ -39,5 +39,6 @@ #define VIRTIO_ID_9P 9 /* 9p virtio console */ #define VIRTIO_ID_RPROC_SERIAL 11 /* virtio remoteproc serial link */ #define VIRTIO_ID_CAIF 12 /* Virtio caif */ +#define VIRTIO_ID_INPUT 18 /* virtio input */ #endif /* _LINUX_VIRTIO_IDS_H */ diff --git a/include/uapi/linux/virtio_input.h b/include/uapi/linux/virtio_input.h new file mode 100644 index 000000000000..a7fe5c8fb135 --- /dev/null +++ b/include/uapi/linux/virtio_input.h @@ -0,0 +1,76 @@ +#ifndef _LINUX_VIRTIO_INPUT_H +#define _LINUX_VIRTIO_INPUT_H +/* This header is BSD licensed so anyone can use the definitions to implement + * compatible drivers/servers. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of IBM nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL IBM OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. */ + +#include + +enum virtio_input_config_select { + VIRTIO_INPUT_CFG_UNSET = 0x00, + VIRTIO_INPUT_CFG_ID_NAME = 0x01, + VIRTIO_INPUT_CFG_ID_SERIAL = 0x02, + VIRTIO_INPUT_CFG_ID_DEVIDS = 0x03, + VIRTIO_INPUT_CFG_PROP_BITS = 0x10, + VIRTIO_INPUT_CFG_EV_BITS = 0x11, + VIRTIO_INPUT_CFG_ABS_INFO = 0x12, +}; + +struct virtio_input_absinfo { + __u32 min; + __u32 max; + __u32 fuzz; + __u32 flat; + __u32 res; +}; + +struct virtio_input_devids { + __u16 bustype; + __u16 vendor; + __u16 product; + __u16 version; +}; + +struct virtio_input_config { + __u8 select; + __u8 subsel; + __u8 size; + __u8 reserved[5]; + union { + char string[128]; + __u8 bitmap[128]; + struct virtio_input_absinfo abs; + struct virtio_input_devids ids; + } u; +}; + +struct virtio_input_event { + __le16 type; + __le16 code; + __le32 value; +}; + +#endif /* _LINUX_VIRTIO_INPUT_H */ -- cgit v1.2.3 From 41d25fe0927aabb1d4b671871a99a55bcd203257 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Wed, 25 Mar 2015 15:08:47 -0700 Subject: tcp: tcp_syn_flood_action() can be static After commit 1fb6f159fd21 ("tcp: add tcp_conn_request"), tcp_syn_flood_action() is no longer used from IPv6. We can make it static, by moving it above tcp_conn_request() Signed-off-by: Eric Dumazet Reviewed-by: Octavian Purdila Signed-off-by: David S. Miller --- include/net/tcp.h | 2 -- net/ipv4/tcp_input.c | 29 +++++++++++++++++++++++++++++ net/ipv4/tcp_ipv4.c | 29 ----------------------------- 3 files changed, 29 insertions(+), 31 deletions(-) (limited to 'include') diff --git a/include/net/tcp.h b/include/net/tcp.h index 42690daa924e..963303fb96ae 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -529,8 +529,6 @@ int tcp_write_wakeup(struct sock *); void tcp_send_fin(struct sock *sk); void tcp_send_active_reset(struct sock *sk, gfp_t priority); int tcp_send_synack(struct sock *); -bool tcp_syn_flood_action(struct sock *sk, const struct sk_buff *skb, - const char *proto); void tcp_push_one(struct sock *, unsigned int mss_now); void tcp_send_ack(struct sock *sk); void tcp_send_delayed_ack(struct sock *sk); diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index 023196f7ec37..18b80e8bc533 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -5987,6 +5987,35 @@ struct request_sock *inet_reqsk_alloc(const struct request_sock_ops *ops, } EXPORT_SYMBOL(inet_reqsk_alloc); +/* + * Return true if a syncookie should be sent + */ +static bool tcp_syn_flood_action(struct sock *sk, + const struct sk_buff *skb, + const char *proto) +{ + const char *msg = "Dropping request"; + bool want_cookie = false; + struct listen_sock *lopt; + +#ifdef CONFIG_SYN_COOKIES + if (sysctl_tcp_syncookies) { + msg = "Sending cookies"; + want_cookie = true; + NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_TCPREQQFULLDOCOOKIES); + } else +#endif + NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_TCPREQQFULLDROP); + + lopt = inet_csk(sk)->icsk_accept_queue.listen_opt; + if (!lopt->synflood_warned && sysctl_tcp_syncookies != 2) { + lopt->synflood_warned = 1; + pr_info("%s: Possible SYN flooding on port %d. %s. Check SNMP counters.\n", + proto, ntohs(tcp_hdr(skb)->dest), msg); + } + return want_cookie; +} + int tcp_conn_request(struct request_sock_ops *rsk_ops, const struct tcp_request_sock_ops *af_ops, struct sock *sk, struct sk_buff *skb) diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index e073517b2cc7..5aababa20a21 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -856,35 +856,6 @@ static void tcp_v4_reqsk_destructor(struct request_sock *req) kfree(inet_rsk(req)->opt); } -/* - * Return true if a syncookie should be sent - */ -bool tcp_syn_flood_action(struct sock *sk, - const struct sk_buff *skb, - const char *proto) -{ - const char *msg = "Dropping request"; - bool want_cookie = false; - struct listen_sock *lopt; - -#ifdef CONFIG_SYN_COOKIES - if (sysctl_tcp_syncookies) { - msg = "Sending cookies"; - want_cookie = true; - NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_TCPREQQFULLDOCOOKIES); - } else -#endif - NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_TCPREQQFULLDROP); - - lopt = inet_csk(sk)->icsk_accept_queue.listen_opt; - if (!lopt->synflood_warned && sysctl_tcp_syncookies != 2) { - lopt->synflood_warned = 1; - pr_info("%s: Possible SYN flooding on port %d. %s. Check SNMP counters.\n", - proto, ntohs(tcp_hdr(skb)->dest), msg); - } - return want_cookie; -} -EXPORT_SYMBOL(tcp_syn_flood_action); #ifdef CONFIG_TCP_MD5SIG /* -- cgit v1.2.3 From 339d82626d225e9b876665e4e89b7eb123e96b3d Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Thu, 26 Mar 2015 18:36:37 -0700 Subject: net: dsa: Add basic framework to support ndo_fdb functions Provide callbacks for ndo_fdb_add, ndo_fdb_del, and ndo_fdb_dump. Reviewed-by: Andrew Lunn Tested-by: Andrew Lunn Signed-off-by: Guenter Roeck Signed-off-by: David S. Miller --- include/net/dsa.h | 6 ++++ net/dsa/slave.c | 102 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 108 insertions(+) (limited to 'include') diff --git a/include/net/dsa.h b/include/net/dsa.h index 47917e5e1e12..fbca63ba8f73 100644 --- a/include/net/dsa.h +++ b/include/net/dsa.h @@ -296,6 +296,12 @@ struct dsa_switch_driver { u32 br_port_mask); int (*port_stp_update)(struct dsa_switch *ds, int port, u8 state); + int (*fdb_add)(struct dsa_switch *ds, int port, + const unsigned char *addr, u16 vid); + int (*fdb_del)(struct dsa_switch *ds, int port, + const unsigned char *addr, u16 vid); + int (*fdb_getnext)(struct dsa_switch *ds, int port, + unsigned char *addr, bool *is_static); }; void register_switch_driver(struct dsa_switch_driver *type); diff --git a/net/dsa/slave.c b/net/dsa/slave.c index 39555f3f263b..3597724ec3d8 100644 --- a/net/dsa/slave.c +++ b/net/dsa/slave.c @@ -201,6 +201,105 @@ out: return 0; } +static int dsa_slave_fdb_add(struct ndmsg *ndm, struct nlattr *tb[], + struct net_device *dev, + const unsigned char *addr, u16 vid, u16 nlm_flags) +{ + struct dsa_slave_priv *p = netdev_priv(dev); + struct dsa_switch *ds = p->parent; + int ret = -EOPNOTSUPP; + + if (ds->drv->fdb_add) + ret = ds->drv->fdb_add(ds, p->port, addr, vid); + + return ret; +} + +static int dsa_slave_fdb_del(struct ndmsg *ndm, struct nlattr *tb[], + struct net_device *dev, + const unsigned char *addr, u16 vid) +{ + struct dsa_slave_priv *p = netdev_priv(dev); + struct dsa_switch *ds = p->parent; + int ret = -EOPNOTSUPP; + + if (ds->drv->fdb_del) + ret = ds->drv->fdb_del(ds, p->port, addr, vid); + + return ret; +} + +static int dsa_slave_fill_info(struct net_device *dev, struct sk_buff *skb, + const unsigned char *addr, u16 vid, + bool is_static, + u32 portid, u32 seq, int type, + unsigned int flags) +{ + struct nlmsghdr *nlh; + struct ndmsg *ndm; + + nlh = nlmsg_put(skb, portid, seq, type, sizeof(*ndm), flags); + if (!nlh) + return -EMSGSIZE; + + ndm = nlmsg_data(nlh); + ndm->ndm_family = AF_BRIDGE; + ndm->ndm_pad1 = 0; + ndm->ndm_pad2 = 0; + ndm->ndm_flags = NTF_EXT_LEARNED; + ndm->ndm_type = 0; + ndm->ndm_ifindex = dev->ifindex; + ndm->ndm_state = is_static ? NUD_NOARP : NUD_REACHABLE; + + if (nla_put(skb, NDA_LLADDR, ETH_ALEN, addr)) + goto nla_put_failure; + + if (vid && nla_put_u16(skb, NDA_VLAN, vid)) + goto nla_put_failure; + + nlmsg_end(skb, nlh); + return 0; + +nla_put_failure: + nlmsg_cancel(skb, nlh); + return -EMSGSIZE; +} + +/* Dump information about entries, in response to GETNEIGH */ +static int dsa_slave_fdb_dump(struct sk_buff *skb, struct netlink_callback *cb, + struct net_device *dev, + struct net_device *filter_dev, int idx) +{ + struct dsa_slave_priv *p = netdev_priv(dev); + struct dsa_switch *ds = p->parent; + unsigned char addr[ETH_ALEN] = { 0 }; + int ret; + + if (!ds->drv->fdb_getnext) + return -EOPNOTSUPP; + + for (; ; idx++) { + bool is_static; + + ret = ds->drv->fdb_getnext(ds, p->port, addr, &is_static); + if (ret < 0) + break; + + if (idx < cb->args[0]) + continue; + + ret = dsa_slave_fill_info(dev, skb, addr, 0, + is_static, + NETLINK_CB(cb->skb).portid, + cb->nlh->nlmsg_seq, + RTM_NEWNEIGH, NLM_F_MULTI); + if (ret < 0) + break; + } + + return idx; +} + static int dsa_slave_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) { struct dsa_slave_priv *p = netdev_priv(dev); @@ -572,6 +671,9 @@ static const struct net_device_ops dsa_slave_netdev_ops = { .ndo_change_rx_flags = dsa_slave_change_rx_flags, .ndo_set_rx_mode = dsa_slave_set_rx_mode, .ndo_set_mac_address = dsa_slave_set_mac_address, + .ndo_fdb_add = dsa_slave_fdb_add, + .ndo_fdb_del = dsa_slave_fdb_del, + .ndo_fdb_dump = dsa_slave_fdb_dump, .ndo_do_ioctl = dsa_slave_ioctl, }; -- cgit v1.2.3 From 608cd71a9c7c9db76e78a792c5a4101e12fea43f Mon Sep 17 00:00:00 2001 From: Alexei Starovoitov Date: Thu, 26 Mar 2015 19:53:57 -0700 Subject: tc: bpf: generalize pedit action existing TC action 'pedit' can munge any bits of the packet. Generalize it for use in bpf programs attached as cls_bpf and act_bpf via bpf_skb_store_bytes() helper function. Signed-off-by: Alexei Starovoitov Reviewed-by: Jiri Pirko Acked-by: Daniel Borkmann Signed-off-by: David S. Miller --- include/linux/bpf.h | 1 + include/uapi/linux/bpf.h | 1 + kernel/bpf/verifier.c | 2 ++ net/core/filter.c | 71 ++++++++++++++++++++++++++++++++++++++++++++++-- 4 files changed, 73 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/linux/bpf.h b/include/linux/bpf.h index 280a315de8d6..d5cda067115a 100644 --- a/include/linux/bpf.h +++ b/include/linux/bpf.h @@ -59,6 +59,7 @@ enum bpf_arg_type { ARG_PTR_TO_STACK, /* any pointer to eBPF program stack */ ARG_CONST_STACK_SIZE, /* number of bytes accessed from stack */ + ARG_PTR_TO_CTX, /* pointer to context */ ARG_ANYTHING, /* any (initialized) argument is ok */ }; diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h index 27dc4ec58840..74aab6e0d964 100644 --- a/include/uapi/linux/bpf.h +++ b/include/uapi/linux/bpf.h @@ -168,6 +168,7 @@ enum bpf_func_id { BPF_FUNC_map_delete_elem, /* int map_delete_elem(&map, &key) */ BPF_FUNC_get_prandom_u32, /* u32 prandom_u32(void) */ BPF_FUNC_get_smp_processor_id, /* u32 raw_smp_processor_id(void) */ + BPF_FUNC_skb_store_bytes, /* int skb_store_bytes(skb, offset, from, len) */ __BPF_FUNC_MAX_ID, }; diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index 0e714f799ec0..630a7bac1e51 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -773,6 +773,8 @@ static int check_func_arg(struct verifier_env *env, u32 regno, expected_type = CONST_IMM; } else if (arg_type == ARG_CONST_MAP_PTR) { expected_type = CONST_PTR_TO_MAP; + } else if (arg_type == ARG_PTR_TO_CTX) { + expected_type = PTR_TO_CTX; } else { verbose("unsupported arg_type %d\n", arg_type); return -EFAULT; diff --git a/net/core/filter.c b/net/core/filter.c index 32f43c59908c..444a07e4f68d 100644 --- a/net/core/filter.c +++ b/net/core/filter.c @@ -1175,6 +1175,56 @@ int sk_attach_bpf(u32 ufd, struct sock *sk) return 0; } +static u64 bpf_skb_store_bytes(u64 r1, u64 r2, u64 r3, u64 r4, u64 r5) +{ + struct sk_buff *skb = (struct sk_buff *) (long) r1; + unsigned int offset = (unsigned int) r2; + void *from = (void *) (long) r3; + unsigned int len = (unsigned int) r4; + char buf[16]; + void *ptr; + + /* bpf verifier guarantees that: + * 'from' pointer points to bpf program stack + * 'len' bytes of it were initialized + * 'len' > 0 + * 'skb' is a valid pointer to 'struct sk_buff' + * + * so check for invalid 'offset' and too large 'len' + */ + if (offset > 0xffff || len > sizeof(buf)) + return -EFAULT; + + if (skb_cloned(skb) && !skb_clone_writable(skb, offset + len)) + return -EFAULT; + + ptr = skb_header_pointer(skb, offset, len, buf); + if (unlikely(!ptr)) + return -EFAULT; + + skb_postpull_rcsum(skb, ptr, len); + + memcpy(ptr, from, len); + + if (ptr == buf) + /* skb_store_bits cannot return -EFAULT here */ + skb_store_bits(skb, offset, ptr, len); + + if (skb->ip_summed == CHECKSUM_COMPLETE) + skb->csum = csum_add(skb->csum, csum_partial(ptr, len, 0)); + return 0; +} + +const struct bpf_func_proto bpf_skb_store_bytes_proto = { + .func = bpf_skb_store_bytes, + .gpl_only = false, + .ret_type = RET_INTEGER, + .arg1_type = ARG_PTR_TO_CTX, + .arg2_type = ARG_ANYTHING, + .arg3_type = ARG_PTR_TO_STACK, + .arg4_type = ARG_CONST_STACK_SIZE, +}; + static const struct bpf_func_proto * sk_filter_func_proto(enum bpf_func_id func_id) { @@ -1194,6 +1244,17 @@ sk_filter_func_proto(enum bpf_func_id func_id) } } +static const struct bpf_func_proto * +tc_cls_act_func_proto(enum bpf_func_id func_id) +{ + switch (func_id) { + case BPF_FUNC_skb_store_bytes: + return &bpf_skb_store_bytes_proto; + default: + return sk_filter_func_proto(func_id); + } +} + static bool sk_filter_is_valid_access(int off, int size, enum bpf_access_type type) { @@ -1270,18 +1331,24 @@ static const struct bpf_verifier_ops sk_filter_ops = { .convert_ctx_access = sk_filter_convert_ctx_access, }; +static const struct bpf_verifier_ops tc_cls_act_ops = { + .get_func_proto = tc_cls_act_func_proto, + .is_valid_access = sk_filter_is_valid_access, + .convert_ctx_access = sk_filter_convert_ctx_access, +}; + static struct bpf_prog_type_list sk_filter_type __read_mostly = { .ops = &sk_filter_ops, .type = BPF_PROG_TYPE_SOCKET_FILTER, }; static struct bpf_prog_type_list sched_cls_type __read_mostly = { - .ops = &sk_filter_ops, + .ops = &tc_cls_act_ops, .type = BPF_PROG_TYPE_SCHED_CLS, }; static struct bpf_prog_type_list sched_act_type __read_mostly = { - .ops = &sk_filter_ops, + .ops = &tc_cls_act_ops, .type = BPF_PROG_TYPE_SCHED_ACT, }; -- cgit v1.2.3 From f5a7fb88e1f82542ca14ba93a1d4fa35471c60ca Mon Sep 17 00:00:00 2001 From: Toshiaki Makita Date: Fri, 27 Mar 2015 14:31:11 +0900 Subject: vlan: Introduce helper functions to check if skb is tagged Separate the two checks for single vlan and multiple vlans in netif_skb_features(). This allows us to move the check for multiple vlans to another function later. Signed-off-by: Toshiaki Makita Signed-off-by: David S. Miller --- include/linux/if_vlan.h | 45 +++++++++++++++++++++++++++++++++++++++++++++ net/core/dev.c | 24 ++++++++---------------- 2 files changed, 53 insertions(+), 16 deletions(-) (limited to 'include') diff --git a/include/linux/if_vlan.h b/include/linux/if_vlan.h index b11b28a30b9e..4265d440ec4d 100644 --- a/include/linux/if_vlan.h +++ b/include/linux/if_vlan.h @@ -561,4 +561,49 @@ static inline void vlan_set_encap_proto(struct sk_buff *skb, skb->protocol = htons(ETH_P_802_2); } +/** + * skb_vlan_tagged - check if skb is vlan tagged. + * @skb: skbuff to query + * + * Returns true if the skb is tagged, regardless of whether it is hardware + * accelerated or not. + */ +static inline bool skb_vlan_tagged(const struct sk_buff *skb) +{ + if (!skb_vlan_tag_present(skb) && + likely(skb->protocol != htons(ETH_P_8021Q) && + skb->protocol != htons(ETH_P_8021AD))) + return false; + + return true; +} + +/** + * skb_vlan_tagged_multi - check if skb is vlan tagged with multiple headers. + * @skb: skbuff to query + * + * Returns true if the skb is tagged with multiple vlan headers, regardless + * of whether it is hardware accelerated or not. + */ +static inline bool skb_vlan_tagged_multi(const struct sk_buff *skb) +{ + __be16 protocol = skb->protocol; + + if (!skb_vlan_tag_present(skb)) { + struct vlan_ethhdr *veh; + + if (likely(protocol != htons(ETH_P_8021Q) && + protocol != htons(ETH_P_8021AD))) + return false; + + veh = (struct vlan_ethhdr *)skb->data; + protocol = veh->h_vlan_encapsulated_proto; + } + + if (protocol != htons(ETH_P_8021Q) && protocol != htons(ETH_P_8021AD)) + return false; + + return true; +} + #endif /* !(_LINUX_IF_VLAN_H_) */ diff --git a/net/core/dev.c b/net/core/dev.c index a0408d497dae..04bffcd4a48d 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -2567,7 +2567,6 @@ netdev_features_t netif_skb_features(struct sk_buff *skb) struct net_device *dev = skb->dev; netdev_features_t features = dev->features; u16 gso_segs = skb_shinfo(skb)->gso_segs; - __be16 protocol = skb->protocol; if (gso_segs > dev->gso_max_segs || gso_segs < dev->gso_min_segs) features &= ~NETIF_F_GSO_MASK; @@ -2579,22 +2578,15 @@ netdev_features_t netif_skb_features(struct sk_buff *skb) if (skb->encapsulation) features &= dev->hw_enc_features; - if (!skb_vlan_tag_present(skb)) { - if (unlikely(protocol == htons(ETH_P_8021Q) || - protocol == htons(ETH_P_8021AD))) { - struct vlan_ethhdr *veh = (struct vlan_ethhdr *)skb->data; - protocol = veh->h_vlan_encapsulated_proto; - } else { - goto finalize; - } - } - - features = netdev_intersect_features(features, - dev->vlan_features | - NETIF_F_HW_VLAN_CTAG_TX | - NETIF_F_HW_VLAN_STAG_TX); + if (skb_vlan_tagged(skb)) + features = netdev_intersect_features(features, + dev->vlan_features | + NETIF_F_HW_VLAN_CTAG_TX | + NETIF_F_HW_VLAN_STAG_TX); + else + goto finalize; - if (protocol == htons(ETH_P_8021Q) || protocol == htons(ETH_P_8021AD)) + if (skb_vlan_tagged_multi(skb)) features = netdev_intersect_features(features, NETIF_F_SG | NETIF_F_HIGHDMA | -- cgit v1.2.3 From 8cb65d00086bfba22bac87ff18b751432fc74003 Mon Sep 17 00:00:00 2001 From: Toshiaki Makita Date: Fri, 27 Mar 2015 14:31:12 +0900 Subject: net: Move check for multiple vlans to drivers To allow drivers to handle the features check for multiple tags, move the check to ndo_features_check(). As no drivers currently handle multiple tagged TSO, introduce dflt_features_check() and call it if the driver does not have ndo_features_check(). Signed-off-by: Toshiaki Makita Signed-off-by: David S. Miller --- drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c | 1 + drivers/net/ethernet/mellanox/mlx4/en_netdev.c | 1 + drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c | 1 + include/linux/if_vlan.h | 22 ++++++++++++++++++++++ net/core/dev.c | 21 +++++++++------------ 5 files changed, 34 insertions(+), 12 deletions(-) (limited to 'include') diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c index 9677431c582a..039b0c1f480e 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c @@ -12557,6 +12557,7 @@ static netdev_features_t bnx2x_features_check(struct sk_buff *skb, struct net_device *dev, netdev_features_t features) { + features = vlan_features_check(skb, features); return vxlan_features_check(skb, features); } diff --git a/drivers/net/ethernet/mellanox/mlx4/en_netdev.c b/drivers/net/ethernet/mellanox/mlx4/en_netdev.c index a8339e98ad24..ebc93a101c93 100644 --- a/drivers/net/ethernet/mellanox/mlx4/en_netdev.c +++ b/drivers/net/ethernet/mellanox/mlx4/en_netdev.c @@ -2373,6 +2373,7 @@ static netdev_features_t mlx4_en_features_check(struct sk_buff *skb, struct net_device *dev, netdev_features_t features) { + features = vlan_features_check(skb, features); return vxlan_features_check(skb, features); } #endif diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c index a430a34a4434..367f3976df56 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c @@ -507,6 +507,7 @@ static netdev_features_t qlcnic_features_check(struct sk_buff *skb, struct net_device *dev, netdev_features_t features) { + features = vlan_features_check(skb, features); return vxlan_features_check(skb, features); } #endif diff --git a/include/linux/if_vlan.h b/include/linux/if_vlan.h index 4265d440ec4d..920e4457ce6e 100644 --- a/include/linux/if_vlan.h +++ b/include/linux/if_vlan.h @@ -606,4 +606,26 @@ static inline bool skb_vlan_tagged_multi(const struct sk_buff *skb) return true; } +/** + * vlan_features_check - drop unsafe features for skb with multiple tags. + * @skb: skbuff to query + * @features: features to be checked + * + * Returns features without unsafe ones if the skb has multiple tags. + */ +static inline netdev_features_t vlan_features_check(const struct sk_buff *skb, + netdev_features_t features) +{ + if (skb_vlan_tagged_multi(skb)) + features = netdev_intersect_features(features, + NETIF_F_SG | + NETIF_F_HIGHDMA | + NETIF_F_FRAGLIST | + NETIF_F_GEN_CSUM | + NETIF_F_HW_VLAN_CTAG_TX | + NETIF_F_HW_VLAN_STAG_TX); + + return features; +} + #endif /* !(_LINUX_IF_VLAN_H_) */ diff --git a/net/core/dev.c b/net/core/dev.c index 04bffcd4a48d..cb46badbef5a 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -2562,6 +2562,13 @@ static netdev_features_t harmonize_features(struct sk_buff *skb, return features; } +static netdev_features_t dflt_features_check(const struct sk_buff *skb, + struct net_device *dev, + netdev_features_t features) +{ + return vlan_features_check(skb, features); +} + netdev_features_t netif_skb_features(struct sk_buff *skb) { struct net_device *dev = skb->dev; @@ -2583,22 +2590,12 @@ netdev_features_t netif_skb_features(struct sk_buff *skb) dev->vlan_features | NETIF_F_HW_VLAN_CTAG_TX | NETIF_F_HW_VLAN_STAG_TX); - else - goto finalize; - if (skb_vlan_tagged_multi(skb)) - features = netdev_intersect_features(features, - NETIF_F_SG | - NETIF_F_HIGHDMA | - NETIF_F_FRAGLIST | - NETIF_F_GEN_CSUM | - NETIF_F_HW_VLAN_CTAG_TX | - NETIF_F_HW_VLAN_STAG_TX); - -finalize: if (dev->netdev_ops->ndo_features_check) features &= dev->netdev_ops->ndo_features_check(skb, dev, features); + else + features &= dflt_features_check(skb, dev, features); return harmonize_features(skb, features); } -- cgit v1.2.3 From e38f30256b36700aa63aa709dc091bf6eb69c257 Mon Sep 17 00:00:00 2001 From: Toshiaki Makita Date: Fri, 27 Mar 2015 14:31:13 +0900 Subject: net: Introduce passthru_features_check As there are a number of (especially virtual) devices that don't need the multiple vlan check, introduce passthru_features_check() for convenience. Signed-off-by: Toshiaki Makita Signed-off-by: David S. Miller --- include/linux/netdevice.h | 3 +++ net/core/dev.c | 8 ++++++++ 2 files changed, 11 insertions(+) (limited to 'include') diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 08c4ab37189f..967bb4c8caf1 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -3657,6 +3657,9 @@ void netdev_change_features(struct net_device *dev); void netif_stacked_transfer_operstate(const struct net_device *rootdev, struct net_device *dev); +netdev_features_t passthru_features_check(struct sk_buff *skb, + struct net_device *dev, + netdev_features_t features); netdev_features_t netif_skb_features(struct sk_buff *skb); static inline bool net_gso_ok(netdev_features_t features, int gso_type) diff --git a/net/core/dev.c b/net/core/dev.c index cb46badbef5a..3a06003ecafd 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -2562,6 +2562,14 @@ static netdev_features_t harmonize_features(struct sk_buff *skb, return features; } +netdev_features_t passthru_features_check(struct sk_buff *skb, + struct net_device *dev, + netdev_features_t features) +{ + return features; +} +EXPORT_SYMBOL(passthru_features_check); + static netdev_features_t dflt_features_check(const struct sk_buff *skb, struct net_device *dev, netdev_features_t features) -- cgit v1.2.3 From 779c88c94c34bd3b521da97b456a1aa51d870dec Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Tue, 24 Mar 2015 10:39:10 +0100 Subject: ARM: 8321/1: asm-generic: introduce .text.fixup input section This introduces a new .text.fixup input section that gets emitted together with the .text section for each input object file. Note that *(.text) *(.text.fixup) is not the same as *(.text .text.fixup) and we are looking for the latter, to ensure that fixup snippets that are assembled into a separate section in the object file do not end up out of range for the relative branch instructions it contains if the .text section itself grows very large. This helps prevent linker failures on large ARM kernels. Acked-by: Arnd Bergmann Signed-off-by: Ard Biesheuvel Signed-off-by: Russell King --- include/asm-generic/vmlinux.lds.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/asm-generic/vmlinux.lds.h b/include/asm-generic/vmlinux.lds.h index ac78910d7416..463231d5bfc7 100644 --- a/include/asm-generic/vmlinux.lds.h +++ b/include/asm-generic/vmlinux.lds.h @@ -401,7 +401,7 @@ #define TEXT_TEXT \ ALIGN_FUNCTION(); \ *(.text.hot) \ - *(.text) \ + *(.text .text.fixup) \ *(.ref.text) \ MEM_KEEP(init.text) \ MEM_KEEP(exit.text) \ -- cgit v1.2.3 From 30bc3aa5c4ed3072bdff7d915772df1b91307ed4 Mon Sep 17 00:00:00 2001 From: Josh Cartwright Date: Thu, 26 Mar 2015 11:29:25 -0700 Subject: mfd: devicetree: qcom_rpm: Document IPQ8064 resources The IPQ8064 SoC has several RPM-controlled resources, an NSS fabrick clock and four regulator resources. Provide definitions for them. Signed-off-by: Josh Cartwright [sboyd@codeaurora.org: Drop regulator part of binding] Signed-off-by: Stephen Boyd Signed-off-by: Lee Jones --- Documentation/devicetree/bindings/mfd/qcom-rpm.txt | 1 + include/dt-bindings/mfd/qcom-rpm.h | 6 ++++++ 2 files changed, 7 insertions(+) (limited to 'include') diff --git a/Documentation/devicetree/bindings/mfd/qcom-rpm.txt b/Documentation/devicetree/bindings/mfd/qcom-rpm.txt index 85e31980017a..94d9ae8d5168 100644 --- a/Documentation/devicetree/bindings/mfd/qcom-rpm.txt +++ b/Documentation/devicetree/bindings/mfd/qcom-rpm.txt @@ -12,6 +12,7 @@ frequencies. "qcom,rpm-apq8064" "qcom,rpm-msm8660" "qcom,rpm-msm8960" + "qcom,rpm-ipq8064" - reg: Usage: required diff --git a/include/dt-bindings/mfd/qcom-rpm.h b/include/dt-bindings/mfd/qcom-rpm.h index 388a6f3d6165..13a9d4bf2662 100644 --- a/include/dt-bindings/mfd/qcom-rpm.h +++ b/include/dt-bindings/mfd/qcom-rpm.h @@ -141,6 +141,12 @@ #define QCOM_RPM_SYS_FABRIC_MODE 131 #define QCOM_RPM_USB_OTG_SWITCH 132 #define QCOM_RPM_VDDMIN_GPIO 133 +#define QCOM_RPM_NSS_FABRIC_0_CLK 134 +#define QCOM_RPM_NSS_FABRIC_1_CLK 135 +#define QCOM_RPM_SMB208_S1a 136 +#define QCOM_RPM_SMB208_S1b 137 +#define QCOM_RPM_SMB208_S2a 138 +#define QCOM_RPM_SMB208_S2b 139 /* * Constants used to select force mode for regulators. -- cgit v1.2.3 From 6f240fbc474018872f6d9664a3bd350bd22cc8d9 Mon Sep 17 00:00:00 2001 From: Chanwoo Choi Date: Tue, 10 Mar 2015 09:27:35 +0900 Subject: PM / devfreq: event: Add const keyword for devfreq_event_ops structure This patch adds the const keyword for devfreq_event_ops structure because the ops of devfreq_event_desc structure should not be changed after initialization. Cc: Myungjoo Ham Cc: Kyungmin Park Signed-off-by: Chanwoo Choi Signed-off-by: MyungJoo Ham --- drivers/devfreq/event/exynos-ppmu.c | 2 +- include/linux/devfreq-event.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/drivers/devfreq/event/exynos-ppmu.c b/drivers/devfreq/event/exynos-ppmu.c index ad8347385f53..7d99d13bacd8 100644 --- a/drivers/devfreq/event/exynos-ppmu.c +++ b/drivers/devfreq/event/exynos-ppmu.c @@ -194,7 +194,7 @@ static int exynos_ppmu_get_event(struct devfreq_event_dev *edev, return 0; } -static struct devfreq_event_ops exynos_ppmu_ops = { +static const struct devfreq_event_ops exynos_ppmu_ops = { .disable = exynos_ppmu_disable, .set_event = exynos_ppmu_set_event, .get_event = exynos_ppmu_get_event, diff --git a/include/linux/devfreq-event.h b/include/linux/devfreq-event.h index 602fbbfcfeed..0a83a1e648b0 100644 --- a/include/linux/devfreq-event.h +++ b/include/linux/devfreq-event.h @@ -91,7 +91,7 @@ struct devfreq_event_desc { const char *name; void *driver_data; - struct devfreq_event_ops *ops; + const struct devfreq_event_ops *ops; }; #if defined(CONFIG_PM_DEVFREQ_EVENT) -- cgit v1.2.3 From a818292952bbfad12ec5a32ab01330cb1ceed013 Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Mon, 16 Mar 2015 23:23:34 +0200 Subject: mac80211: convert rssi_callback() to event_callback() We will be able to add more events, such as MLME events and others. The low level driver may be interested in knowing about these events to dump firmware data upon failures, or to change parameters in case connection attempts fail etc... Signed-off-by: Emmanuel Grumbach Signed-off-by: Johannes Berg --- drivers/net/wireless/iwlwifi/dvm/mac80211.c | 15 ++++---- drivers/net/wireless/iwlwifi/mvm/coex.c | 2 +- drivers/net/wireless/iwlwifi/mvm/coex_legacy.c | 2 +- drivers/net/wireless/iwlwifi/mvm/mvm.h | 4 +-- include/net/mac80211.h | 48 ++++++++++++++++++++------ net/mac80211/driver-ops.h | 12 +++---- net/mac80211/mlme.c | 9 +++-- net/mac80211/trace.h | 14 ++++---- 8 files changed, 71 insertions(+), 35 deletions(-) (limited to 'include') diff --git a/drivers/net/wireless/iwlwifi/dvm/mac80211.c b/drivers/net/wireless/iwlwifi/dvm/mac80211.c index 47e64e8b9517..5707ba5ce23f 100644 --- a/drivers/net/wireless/iwlwifi/dvm/mac80211.c +++ b/drivers/net/wireless/iwlwifi/dvm/mac80211.c @@ -1129,20 +1129,23 @@ done: IWL_DEBUG_MAC80211(priv, "leave\n"); } -static void iwlagn_mac_rssi_callback(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - enum ieee80211_rssi_event rssi_event) +static void iwlagn_mac_event_callback(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + const struct ieee80211_event *event) { struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); + if (event->type != RSSI_EVENT) + return; + IWL_DEBUG_MAC80211(priv, "enter\n"); mutex_lock(&priv->mutex); if (priv->lib->bt_params && priv->lib->bt_params->advanced_bt_coexist) { - if (rssi_event == RSSI_EVENT_LOW) + if (event->u.rssi.data == RSSI_EVENT_LOW) priv->bt_enable_pspoll = true; - else if (rssi_event == RSSI_EVENT_HIGH) + else if (event->u.rssi.data == RSSI_EVENT_HIGH) priv->bt_enable_pspoll = false; iwlagn_send_advance_bt_config(priv); @@ -1613,7 +1616,7 @@ const struct ieee80211_ops iwlagn_hw_ops = { .channel_switch = iwlagn_mac_channel_switch, .flush = iwlagn_mac_flush, .tx_last_beacon = iwlagn_mac_tx_last_beacon, - .rssi_callback = iwlagn_mac_rssi_callback, + .event_callback = iwlagn_mac_event_callback, .set_tim = iwlagn_mac_set_tim, }; diff --git a/drivers/net/wireless/iwlwifi/mvm/coex.c b/drivers/net/wireless/iwlwifi/mvm/coex.c index 1ec4d55155f7..ba0a596f2d0f 100644 --- a/drivers/net/wireless/iwlwifi/mvm/coex.c +++ b/drivers/net/wireless/iwlwifi/mvm/coex.c @@ -1023,7 +1023,7 @@ static void iwl_mvm_bt_rssi_iterator(void *_data, u8 *mac, } void iwl_mvm_bt_rssi_event(struct iwl_mvm *mvm, struct ieee80211_vif *vif, - enum ieee80211_rssi_event rssi_event) + enum ieee80211_rssi_event_data rssi_event) { struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); struct iwl_bt_iterator_data data = { diff --git a/drivers/net/wireless/iwlwifi/mvm/coex_legacy.c b/drivers/net/wireless/iwlwifi/mvm/coex_legacy.c index d530ef3da107..92c3072bdfff 100644 --- a/drivers/net/wireless/iwlwifi/mvm/coex_legacy.c +++ b/drivers/net/wireless/iwlwifi/mvm/coex_legacy.c @@ -1068,7 +1068,7 @@ static void iwl_mvm_bt_rssi_iterator(void *_data, u8 *mac, } void iwl_mvm_bt_rssi_event_old(struct iwl_mvm *mvm, struct ieee80211_vif *vif, - enum ieee80211_rssi_event rssi_event) + enum ieee80211_rssi_event_data rssi_event) { struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); struct iwl_bt_iterator_data data = { diff --git a/drivers/net/wireless/iwlwifi/mvm/mvm.h b/drivers/net/wireless/iwlwifi/mvm/mvm.h index 6c69d0584f6c..646ef92e23e8 100644 --- a/drivers/net/wireless/iwlwifi/mvm/mvm.h +++ b/drivers/net/wireless/iwlwifi/mvm/mvm.h @@ -1225,7 +1225,7 @@ int iwl_mvm_rx_bt_coex_notif(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb, struct iwl_device_cmd *cmd); void iwl_mvm_bt_rssi_event(struct iwl_mvm *mvm, struct ieee80211_vif *vif, - enum ieee80211_rssi_event rssi_event); + enum ieee80211_rssi_event_data); void iwl_mvm_bt_coex_vif_change(struct iwl_mvm *mvm); u16 iwl_mvm_coex_agg_time_limit(struct iwl_mvm *mvm, struct ieee80211_sta *sta); @@ -1246,7 +1246,7 @@ int iwl_mvm_rx_bt_coex_notif_old(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb, struct iwl_device_cmd *cmd); void iwl_mvm_bt_rssi_event_old(struct iwl_mvm *mvm, struct ieee80211_vif *vif, - enum ieee80211_rssi_event rssi_event); + enum ieee80211_rssi_event_data); u16 iwl_mvm_coex_agg_time_limit_old(struct iwl_mvm *mvm, struct ieee80211_sta *sta); bool iwl_mvm_bt_coex_is_mimo_allowed_old(struct iwl_mvm *mvm, diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 157c0f151766..7a966f3ed67a 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -301,16 +301,43 @@ enum ieee80211_bss_change { #define IEEE80211_BSS_ARP_ADDR_LIST_LEN 4 /** - * enum ieee80211_rssi_event - RSSI threshold event - * An indicator for when RSSI goes below/above a certain threshold. - * @RSSI_EVENT_HIGH: AP's rssi crossed the high threshold set by the driver. - * @RSSI_EVENT_LOW: AP's rssi crossed the low threshold set by the driver. + * enum ieee80211_event_type - event to be notified to the low level driver + * @RSSI_EVENT: AP's rssi crossed the a threshold set by the driver. */ -enum ieee80211_rssi_event { +enum ieee80211_event_type { + RSSI_EVENT, +}; + +/** + * enum ieee80211_rssi_event_data - relevant when event type is %RSSI_EVENT + * @RSSI_EVENT_HIGH: AP's rssi went below the threshold set by the driver. + * @RSSI_EVENT_LOW: AP's rssi went above the threshold set by the driver. + */ +enum ieee80211_rssi_event_data { RSSI_EVENT_HIGH, RSSI_EVENT_LOW, }; +/** + * enum ieee80211_rssi_event - data attached to an %RSSI_EVENT + * @data: See &enum ieee80211_rssi_event_data + */ +struct ieee80211_rssi_event { + enum ieee80211_rssi_event_data data; +}; + +/** + * struct ieee80211_event - event to be sent to the driver + * @type The event itself. See &enum ieee80211_event_type. + * @rssi: relevant if &type is %RSSI_EVENT + */ +struct ieee80211_event { + enum ieee80211_event_type type; + union { + struct ieee80211_rssi_event rssi; + } u; +}; + /** * struct ieee80211_bss_conf - holds the BSS's changing parameters * @@ -2862,8 +2889,9 @@ enum ieee80211_reconfig_type { * @set_bitrate_mask: Set a mask of rates to be used for rate control selection * when transmitting a frame. Currently only legacy rates are handled. * The callback can sleep. - * @rssi_callback: Notify driver when the average RSSI goes above/below - * thresholds that were registered previously. The callback can sleep. + * @event_callback: Notify driver about any event in mac80211. See + * &enum ieee80211_event_type for the different types. + * The callback can sleep. * * @release_buffered_frames: Release buffered frames according to the given * parameters. In the case where the driver buffers some frames for @@ -3159,9 +3187,9 @@ struct ieee80211_ops { bool (*tx_frames_pending)(struct ieee80211_hw *hw); int (*set_bitrate_mask)(struct ieee80211_hw *hw, struct ieee80211_vif *vif, const struct cfg80211_bitrate_mask *mask); - void (*rssi_callback)(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - enum ieee80211_rssi_event rssi_event); + void (*event_callback)(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + const struct ieee80211_event *event); void (*allow_buffered_frames)(struct ieee80211_hw *hw, struct ieee80211_sta *sta, diff --git a/net/mac80211/driver-ops.h b/net/mac80211/driver-ops.h index fdeda17b8dd2..0a39d3db951a 100644 --- a/net/mac80211/driver-ops.h +++ b/net/mac80211/driver-ops.h @@ -941,13 +941,13 @@ static inline void drv_set_rekey_data(struct ieee80211_local *local, trace_drv_return_void(local); } -static inline void drv_rssi_callback(struct ieee80211_local *local, - struct ieee80211_sub_if_data *sdata, - const enum ieee80211_rssi_event event) +static inline void drv_event_callback(struct ieee80211_local *local, + struct ieee80211_sub_if_data *sdata, + const struct ieee80211_event *event) { - trace_drv_rssi_callback(local, sdata, event); - if (local->ops->rssi_callback) - local->ops->rssi_callback(&local->hw, &sdata->vif, event); + trace_drv_event_callback(local, sdata, event); + if (local->ops->event_callback) + local->ops->event_callback(&local->hw, &sdata->vif, event); trace_drv_return_void(local); } diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 1999bc08fdcc..a8c8fe4c9f49 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -3291,6 +3291,9 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata, ifmgd->count_beacon_signal >= IEEE80211_SIGNAL_AVE_MIN_COUNT) { int sig = ifmgd->ave_beacon_signal; int last_sig = ifmgd->last_ave_beacon_signal; + struct ieee80211_event event = { + .type = RSSI_EVENT, + }; /* * if signal crosses either of the boundaries, invoke callback @@ -3299,12 +3302,14 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata, if (sig > ifmgd->rssi_max_thold && (last_sig <= ifmgd->rssi_min_thold || last_sig == 0)) { ifmgd->last_ave_beacon_signal = sig; - drv_rssi_callback(local, sdata, RSSI_EVENT_HIGH); + event.u.rssi.data = RSSI_EVENT_HIGH; + drv_event_callback(local, sdata, &event); } else if (sig < ifmgd->rssi_min_thold && (last_sig >= ifmgd->rssi_max_thold || last_sig == 0)) { ifmgd->last_ave_beacon_signal = sig; - drv_rssi_callback(local, sdata, RSSI_EVENT_LOW); + event.u.rssi.data = RSSI_EVENT_LOW; + drv_event_callback(local, sdata, &event); } } diff --git a/net/mac80211/trace.h b/net/mac80211/trace.h index 263a9561eb26..e9e462b349e5 100644 --- a/net/mac80211/trace.h +++ b/net/mac80211/trace.h @@ -1256,28 +1256,28 @@ TRACE_EVENT(drv_set_rekey_data, LOCAL_PR_ARG, VIF_PR_ARG) ); -TRACE_EVENT(drv_rssi_callback, +TRACE_EVENT(drv_event_callback, TP_PROTO(struct ieee80211_local *local, struct ieee80211_sub_if_data *sdata, - enum ieee80211_rssi_event rssi_event), + const struct ieee80211_event *_event), - TP_ARGS(local, sdata, rssi_event), + TP_ARGS(local, sdata, _event), TP_STRUCT__entry( LOCAL_ENTRY VIF_ENTRY - __field(u32, rssi_event) + __field(u32, type) ), TP_fast_assign( LOCAL_ASSIGN; VIF_ASSIGN; - __entry->rssi_event = rssi_event; + __entry->type = _event->type; ), TP_printk( - LOCAL_PR_FMT VIF_PR_FMT " rssi_event:%d", - LOCAL_PR_ARG, VIF_PR_ARG, __entry->rssi_event + LOCAL_PR_FMT VIF_PR_FMT " event:%d", + LOCAL_PR_ARG, VIF_PR_ARG, __entry->type ) ); -- cgit v1.2.3 From a9409093d23c822d13a73f8d2df7e6fa987ae485 Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Mon, 16 Mar 2015 23:23:35 +0200 Subject: mac80211: notify the driver about authentication status This can allow the driver to take action based on the success / failure of the authentication. Signed-off-by: Emmanuel Grumbach Signed-off-by: Johannes Berg --- include/net/mac80211.h | 36 ++++++++++++++++++++++++++++++++++++ net/mac80211/mlme.c | 15 +++++++++++++++ 2 files changed, 51 insertions(+) (limited to 'include') diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 7a966f3ed67a..6cddf7725bf2 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -303,9 +303,11 @@ enum ieee80211_bss_change { /** * enum ieee80211_event_type - event to be notified to the low level driver * @RSSI_EVENT: AP's rssi crossed the a threshold set by the driver. + * @MLME_EVENT: event related to MLME */ enum ieee80211_event_type { RSSI_EVENT, + MLME_EVENT, }; /** @@ -326,15 +328,49 @@ struct ieee80211_rssi_event { enum ieee80211_rssi_event_data data; }; +/** + * enum ieee80211_mlme_event_data - relevant when event type is %MLME_EVENT + * @AUTH_EVENT: the MLME operation is authentication + */ +enum ieee80211_mlme_event_data { + AUTH_EVENT, +}; + +/** + * enum ieee80211_mlme_event_status - relevant when event type is %MLME_EVENT + * @MLME_SUCCESS: the MLME operation completed successfully. + * @MLME_DENIED: the MLME operation was denied by the peer. + * @MLME_TIMEOUT: the MLME operation timed out. + */ +enum ieee80211_mlme_event_status { + MLME_SUCCESS, + MLME_DENIED, + MLME_TIMEOUT, +}; + +/** + * enum ieee80211_mlme_event - data attached to an %MLME_EVENT + * @data: See &enum ieee80211_mlme_event_data + * @status: See &enum ieee80211_mlme_event_status + * @reason: the reason code if applicable + */ +struct ieee80211_mlme_event { + enum ieee80211_mlme_event_data data; + enum ieee80211_mlme_event_status status; + u16 reason; +}; + /** * struct ieee80211_event - event to be sent to the driver * @type The event itself. See &enum ieee80211_event_type. * @rssi: relevant if &type is %RSSI_EVENT + * @mlme: relevant if &type is %AUTH_EVENT */ struct ieee80211_event { enum ieee80211_event_type type; union { struct ieee80211_rssi_event rssi; + struct ieee80211_mlme_event mlme; } u; }; diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index a8c8fe4c9f49..7865998d69dd 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -2495,6 +2495,10 @@ static void ieee80211_rx_mgmt_auth(struct ieee80211_sub_if_data *sdata, u8 bssid[ETH_ALEN]; u16 auth_alg, auth_transaction, status_code; struct sta_info *sta; + struct ieee80211_event event = { + .type = MLME_EVENT, + .u.mlme.data = AUTH_EVENT, + }; sdata_assert_lock(sdata); @@ -2527,6 +2531,9 @@ static void ieee80211_rx_mgmt_auth(struct ieee80211_sub_if_data *sdata, mgmt->sa, status_code); ieee80211_destroy_auth_data(sdata, false); cfg80211_rx_mlme_mgmt(sdata->dev, (u8 *)mgmt, len); + event.u.mlme.status = MLME_DENIED; + event.u.mlme.reason = status_code; + drv_event_callback(sdata->local, sdata, &event); return; } @@ -2549,6 +2556,8 @@ static void ieee80211_rx_mgmt_auth(struct ieee80211_sub_if_data *sdata, return; } + event.u.mlme.status = MLME_SUCCESS; + drv_event_callback(sdata->local, sdata, &event); sdata_info(sdata, "authenticated\n"); ifmgd->auth_data->done = true; ifmgd->auth_data->timeout = jiffies + IEEE80211_AUTH_WAIT_ASSOC; @@ -3805,12 +3814,18 @@ void ieee80211_sta_work(struct ieee80211_sub_if_data *sdata) ieee80211_destroy_auth_data(sdata, false); } else if (ieee80211_probe_auth(sdata)) { u8 bssid[ETH_ALEN]; + struct ieee80211_event event = { + .type = MLME_EVENT, + .u.mlme.data = AUTH_EVENT, + .u.mlme.status = MLME_TIMEOUT, + }; memcpy(bssid, ifmgd->auth_data->bss->bssid, ETH_ALEN); ieee80211_destroy_auth_data(sdata, false); cfg80211_auth_timeout(sdata->dev, bssid); + drv_event_callback(sdata->local, sdata, &event); } } else if (ifmgd->auth_data && ifmgd->auth_data->timeout_started) run_again(sdata, ifmgd->auth_data->timeout); -- cgit v1.2.3 From d0d1a12f9cc665f3fcac5288ab84429a91abd4e9 Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Mon, 16 Mar 2015 23:23:36 +0200 Subject: mac80211: notify the driver about association status This can allow the driver to take action based on the success / failure of the association. Signed-off-by: Emmanuel Grumbach Signed-off-by: Johannes Berg --- include/net/mac80211.h | 2 ++ net/mac80211/mlme.c | 15 +++++++++++++++ 2 files changed, 17 insertions(+) (limited to 'include') diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 6cddf7725bf2..dcddc4ac4184 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -331,9 +331,11 @@ struct ieee80211_rssi_event { /** * enum ieee80211_mlme_event_data - relevant when event type is %MLME_EVENT * @AUTH_EVENT: the MLME operation is authentication + * @ASSOC_EVENT: the MLME operation is association */ enum ieee80211_mlme_event_data { AUTH_EVENT, + ASSOC_EVENT, }; /** diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 7865998d69dd..b6817c4c4449 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -3041,6 +3041,10 @@ static void ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata, u8 *pos; bool reassoc; struct cfg80211_bss *bss; + struct ieee80211_event event = { + .type = MLME_EVENT, + .u.mlme.data = ASSOC_EVENT, + }; sdata_assert_lock(sdata); @@ -3092,6 +3096,9 @@ static void ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata, sdata_info(sdata, "%pM denied association (code=%d)\n", mgmt->sa, status_code); ieee80211_destroy_assoc_data(sdata, false); + event.u.mlme.status = MLME_DENIED; + event.u.mlme.reason = status_code; + drv_event_callback(sdata->local, sdata, &event); } else { if (!ieee80211_assoc_success(sdata, bss, mgmt, len)) { /* oops -- internal error -- send timeout for now */ @@ -3099,6 +3106,8 @@ static void ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata, cfg80211_assoc_timeout(sdata->dev, bss); return; } + event.u.mlme.status = MLME_SUCCESS; + drv_event_callback(sdata->local, sdata, &event); sdata_info(sdata, "associated\n"); /* @@ -3835,9 +3844,15 @@ void ieee80211_sta_work(struct ieee80211_sub_if_data *sdata) if ((ifmgd->assoc_data->need_beacon && !ifmgd->have_beacon) || ieee80211_do_assoc(sdata)) { struct cfg80211_bss *bss = ifmgd->assoc_data->bss; + struct ieee80211_event event = { + .type = MLME_EVENT, + .u.mlme.data = ASSOC_EVENT, + .u.mlme.status = MLME_TIMEOUT, + }; ieee80211_destroy_assoc_data(sdata, false); cfg80211_assoc_timeout(sdata->dev, bss); + drv_event_callback(sdata->local, sdata, &event); } } else if (ifmgd->assoc_data && ifmgd->assoc_data->timeout_started) run_again(sdata, ifmgd->assoc_data->timeout); -- cgit v1.2.3 From a90faa9d6449f3861428bafeaf4d1fcf2be4ba7f Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Mon, 16 Mar 2015 23:23:37 +0200 Subject: mac80211: notify the driver about deauth This can allow the driver to take action based on the reason of the deauth. Signed-off-by: Emmanuel Grumbach Signed-off-by: Johannes Berg --- include/net/mac80211.h | 4 ++++ net/mac80211/mlme.c | 60 +++++++++++++++++++++++++++++++++++--------------- 2 files changed, 46 insertions(+), 18 deletions(-) (limited to 'include') diff --git a/include/net/mac80211.h b/include/net/mac80211.h index dcddc4ac4184..94c8c62800c3 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -332,10 +332,14 @@ struct ieee80211_rssi_event { * enum ieee80211_mlme_event_data - relevant when event type is %MLME_EVENT * @AUTH_EVENT: the MLME operation is authentication * @ASSOC_EVENT: the MLME operation is association + * @DEAUTH_RX_EVENT: deauth received.. + * @DEAUTH_TX_EVENT: deauth sent. */ enum ieee80211_mlme_event_data { AUTH_EVENT, ASSOC_EVENT, + DEAUTH_RX_EVENT, + DEAUTH_TX_EVENT, }; /** diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index b6817c4c4449..22b125913661 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -2345,6 +2345,24 @@ struct sk_buff *ieee80211_ap_probereq_get(struct ieee80211_hw *hw, } EXPORT_SYMBOL(ieee80211_ap_probereq_get); +static void ieee80211_report_disconnect(struct ieee80211_sub_if_data *sdata, + const u8 *buf, size_t len, bool tx, + u16 reason) +{ + struct ieee80211_event event = { + .type = MLME_EVENT, + .u.mlme.data = tx ? DEAUTH_TX_EVENT : DEAUTH_RX_EVENT, + .u.mlme.reason = reason, + }; + + if (tx) + cfg80211_tx_mlme_mgmt(sdata->dev, buf, len); + else + cfg80211_rx_mlme_mgmt(sdata->dev, buf, len); + + drv_event_callback(sdata->local, sdata, &event); +} + static void __ieee80211_disconnect(struct ieee80211_sub_if_data *sdata) { struct ieee80211_local *local = sdata->local; @@ -2370,8 +2388,9 @@ static void __ieee80211_disconnect(struct ieee80211_sub_if_data *sdata) } mutex_unlock(&local->mtx); - cfg80211_tx_mlme_mgmt(sdata->dev, frame_buf, - IEEE80211_DEAUTH_FRAME_LEN); + ieee80211_report_disconnect(sdata, frame_buf, sizeof(frame_buf), true, + WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY); + sdata_unlock(sdata); } @@ -2676,7 +2695,7 @@ static void ieee80211_rx_mgmt_deauth(struct ieee80211_sub_if_data *sdata, ieee80211_set_disassoc(sdata, 0, 0, false, NULL); - cfg80211_rx_mlme_mgmt(sdata->dev, (u8 *)mgmt, len); + ieee80211_report_disconnect(sdata, (u8 *)mgmt, len, false, reason_code); } @@ -2702,7 +2721,7 @@ static void ieee80211_rx_mgmt_disassoc(struct ieee80211_sub_if_data *sdata, ieee80211_set_disassoc(sdata, 0, 0, false, NULL); - cfg80211_rx_mlme_mgmt(sdata->dev, (u8 *)mgmt, len); + ieee80211_report_disconnect(sdata, (u8 *)mgmt, len, false, reason_code); } static void ieee80211_get_rates(struct ieee80211_supported_band *sband, @@ -3509,8 +3528,9 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata, ieee80211_set_disassoc(sdata, IEEE80211_STYPE_DEAUTH, WLAN_REASON_DEAUTH_LEAVING, true, deauth_buf); - cfg80211_tx_mlme_mgmt(sdata->dev, deauth_buf, - sizeof(deauth_buf)); + ieee80211_report_disconnect(sdata, deauth_buf, + sizeof(deauth_buf), true, + WLAN_REASON_DEAUTH_LEAVING); return; } @@ -3628,8 +3648,8 @@ static void ieee80211_sta_connection_lost(struct ieee80211_sub_if_data *sdata, ieee80211_set_disassoc(sdata, IEEE80211_STYPE_DEAUTH, reason, tx, frame_buf); - cfg80211_tx_mlme_mgmt(sdata->dev, frame_buf, - IEEE80211_DEAUTH_FRAME_LEN); + ieee80211_report_disconnect(sdata, frame_buf, sizeof(frame_buf), true, + reason); } static int ieee80211_probe_auth(struct ieee80211_sub_if_data *sdata) @@ -4507,8 +4527,9 @@ int ieee80211_mgd_auth(struct ieee80211_sub_if_data *sdata, WLAN_REASON_UNSPECIFIED, false, frame_buf); - cfg80211_tx_mlme_mgmt(sdata->dev, frame_buf, - sizeof(frame_buf)); + ieee80211_report_disconnect(sdata, frame_buf, + sizeof(frame_buf), true, + WLAN_REASON_UNSPECIFIED); } sdata_info(sdata, "authenticate with %pM\n", req->bss->bssid); @@ -4608,8 +4629,9 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata, WLAN_REASON_UNSPECIFIED, false, frame_buf); - cfg80211_tx_mlme_mgmt(sdata->dev, frame_buf, - sizeof(frame_buf)); + ieee80211_report_disconnect(sdata, frame_buf, + sizeof(frame_buf), true, + WLAN_REASON_UNSPECIFIED); } if (ifmgd->auth_data && !ifmgd->auth_data->done) { @@ -4899,8 +4921,9 @@ int ieee80211_mgd_deauth(struct ieee80211_sub_if_data *sdata, req->reason_code, tx, frame_buf); ieee80211_destroy_auth_data(sdata, false); - cfg80211_tx_mlme_mgmt(sdata->dev, frame_buf, - IEEE80211_DEAUTH_FRAME_LEN); + ieee80211_report_disconnect(sdata, frame_buf, + sizeof(frame_buf), true, + req->reason_code); return 0; } @@ -4914,8 +4937,9 @@ int ieee80211_mgd_deauth(struct ieee80211_sub_if_data *sdata, ieee80211_set_disassoc(sdata, IEEE80211_STYPE_DEAUTH, req->reason_code, tx, frame_buf); - cfg80211_tx_mlme_mgmt(sdata->dev, frame_buf, - IEEE80211_DEAUTH_FRAME_LEN); + ieee80211_report_disconnect(sdata, frame_buf, + sizeof(frame_buf), true, + req->reason_code); return 0; } @@ -4947,8 +4971,8 @@ int ieee80211_mgd_disassoc(struct ieee80211_sub_if_data *sdata, req->reason_code, !req->local_state_change, frame_buf); - cfg80211_tx_mlme_mgmt(sdata->dev, frame_buf, - IEEE80211_DEAUTH_FRAME_LEN); + ieee80211_report_disconnect(sdata, frame_buf, sizeof(frame_buf), true, + req->reason_code); return 0; } -- cgit v1.2.3 From 3a323d4e17dd5a84f6ad036e6f985d263ca973ed Mon Sep 17 00:00:00 2001 From: Luciano Coelho Date: Wed, 18 Mar 2015 10:47:02 +0200 Subject: nl80211: small clarification of the sched_scan delay attribute Just clarify that the delay is only before the first cycle. Signed-off-by: Luciano Coelho Signed-off-by: Johannes Berg --- include/uapi/linux/nl80211.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index ae16ba9cb1e3..241220c43e86 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h @@ -1754,8 +1754,9 @@ enum nl80211_commands { * should be contained in the result as the sum of the respective counters * over all channels. * - * @NL80211_ATTR_SCHED_SCAN_DELAY: delay before a scheduled scan (or a - * WoWLAN net-detect scan) is started, u32 in seconds. + * @NL80211_ATTR_SCHED_SCAN_DELAY: delay before the first cycle of a + * scheduled scan (or a WoWLAN net-detect scan) is started, u32 + * in seconds. * @NL80211_ATTR_REG_INDOOR: flag attribute, if set indicates that the device * is operating in an indoor environment. -- cgit v1.2.3 From a38700dd486f3def34cef47d00e2d360a04a7bc8 Mon Sep 17 00:00:00 2001 From: Arik Nemtsov Date: Wed, 18 Mar 2015 08:46:08 +0200 Subject: cfg/mac80211: add regulatory classes IE during TDLS setup Seems Broadcom TDLS peers (Nexus 5, Xperia Z3) refuse to allow TDLS connection when channel-switching is supported but the regulatory classes IE is missing from the setup request. Add a chandef to reg-class translation function to cfg80211 and use it to add the required IE during setup. For now add only the current regulatory class as supported - it is enough to resolve the compatibility issue. Signed-off-by: Arik Nemtsov Signed-off-by: Emmanuel Grumbach Signed-off-by: Johannes Berg --- include/net/cfg80211.h | 11 +++++ net/mac80211/tdls.c | 21 ++++++++ net/wireless/util.c | 129 +++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 161 insertions(+) (limited to 'include') diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 12a6121ea76e..c4d873b8f32d 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -4903,6 +4903,17 @@ void cfg80211_ch_switch_started_notify(struct net_device *dev, bool ieee80211_operating_class_to_band(u8 operating_class, enum ieee80211_band *band); +/** + * ieee80211_chandef_to_operating_class - convert chandef to operation class + * + * @chandef: the chandef to convert + * @op_class: a pointer to the resulting operating class + * + * Returns %true if the conversion was successful, %false otherwise. + */ +bool ieee80211_chandef_to_operating_class(struct cfg80211_chan_def *chandef, + u8 *op_class); + /* * cfg80211_tdls_oper_request - request userspace to perform TDLS operation * @dev: the device on which the operation is requested diff --git a/net/mac80211/tdls.c b/net/mac80211/tdls.c index bc7e4049896f..79ed59acf0d4 100644 --- a/net/mac80211/tdls.c +++ b/net/mac80211/tdls.c @@ -136,6 +136,24 @@ ieee80211_tdls_add_supp_channels(struct ieee80211_sub_if_data *sdata, *pos = 2 * subband_cnt; } +static void ieee80211_tdls_add_oper_classes(struct ieee80211_sub_if_data *sdata, + struct sk_buff *skb) +{ + u8 *pos; + u8 op_class; + + if (!ieee80211_chandef_to_operating_class(&sdata->vif.bss_conf.chandef, + &op_class)) + return; + + pos = skb_put(skb, 4); + *pos++ = WLAN_EID_SUPPORTED_REGULATORY_CLASSES; + *pos++ = 2; /* len */ + + *pos++ = op_class; + *pos++ = op_class; /* give current operating class as alternate too */ +} + static void ieee80211_tdls_add_bss_coex_ie(struct sk_buff *skb) { u8 *pos = (void *)skb_put(skb, 3); @@ -350,6 +368,8 @@ ieee80211_tdls_add_setup_start_ies(struct ieee80211_sub_if_data *sdata, } } + ieee80211_tdls_add_oper_classes(sdata, skb); + /* * with TDLS we can switch channels, and HT-caps are not necessarily * the same on all bands. The specification limits the setup to a @@ -786,6 +806,7 @@ ieee80211_tdls_build_mgmt_packet_data(struct ieee80211_sub_if_data *sdata, 50 + /* supported channels */ 3 + /* 40/20 BSS coex */ 4 + /* AID */ + 4 + /* oper classes */ extra_ies_len + sizeof(struct ieee80211_tdls_lnkie)); if (!skb) diff --git a/net/wireless/util.c b/net/wireless/util.c index f7b35980af69..f218b151530a 100644 --- a/net/wireless/util.c +++ b/net/wireless/util.c @@ -1314,6 +1314,135 @@ bool ieee80211_operating_class_to_band(u8 operating_class, } EXPORT_SYMBOL(ieee80211_operating_class_to_band); +bool ieee80211_chandef_to_operating_class(struct cfg80211_chan_def *chandef, + u8 *op_class) +{ + u8 vht_opclass; + u16 freq = chandef->center_freq1; + + if (freq >= 2412 && freq <= 2472) { + if (chandef->width > NL80211_CHAN_WIDTH_40) + return false; + + /* 2.407 GHz, channels 1..13 */ + if (chandef->width == NL80211_CHAN_WIDTH_40) { + if (freq > chandef->chan->center_freq) + *op_class = 83; /* HT40+ */ + else + *op_class = 84; /* HT40- */ + } else { + *op_class = 81; + } + + return true; + } + + if (freq == 2484) { + if (chandef->width > NL80211_CHAN_WIDTH_40) + return false; + + *op_class = 82; /* channel 14 */ + return true; + } + + switch (chandef->width) { + case NL80211_CHAN_WIDTH_80: + vht_opclass = 128; + break; + case NL80211_CHAN_WIDTH_160: + vht_opclass = 129; + break; + case NL80211_CHAN_WIDTH_80P80: + vht_opclass = 130; + break; + case NL80211_CHAN_WIDTH_10: + case NL80211_CHAN_WIDTH_5: + return false; /* unsupported for now */ + default: + vht_opclass = 0; + break; + } + + /* 5 GHz, channels 36..48 */ + if (freq >= 5180 && freq <= 5240) { + if (vht_opclass) { + *op_class = vht_opclass; + } else if (chandef->width == NL80211_CHAN_WIDTH_40) { + if (freq > chandef->chan->center_freq) + *op_class = 116; + else + *op_class = 117; + } else { + *op_class = 115; + } + + return true; + } + + /* 5 GHz, channels 52..64 */ + if (freq >= 5260 && freq <= 5320) { + if (vht_opclass) { + *op_class = vht_opclass; + } else if (chandef->width == NL80211_CHAN_WIDTH_40) { + if (freq > chandef->chan->center_freq) + *op_class = 119; + else + *op_class = 120; + } else { + *op_class = 118; + } + + return true; + } + + /* 5 GHz, channels 100..144 */ + if (freq >= 5500 && freq <= 5720) { + if (vht_opclass) { + *op_class = vht_opclass; + } else if (chandef->width == NL80211_CHAN_WIDTH_40) { + if (freq > chandef->chan->center_freq) + *op_class = 122; + else + *op_class = 123; + } else { + *op_class = 121; + } + + return true; + } + + /* 5 GHz, channels 149..169 */ + if (freq >= 5745 && freq <= 5845) { + if (vht_opclass) { + *op_class = vht_opclass; + } else if (chandef->width == NL80211_CHAN_WIDTH_40) { + if (freq > chandef->chan->center_freq) + *op_class = 126; + else + *op_class = 127; + } else if (freq <= 5805) { + *op_class = 124; + } else { + *op_class = 125; + } + + return true; + } + + /* 56.16 GHz, channel 1..4 */ + if (freq >= 56160 + 2160 * 1 && freq <= 56160 + 2160 * 4) { + if (chandef->width >= NL80211_CHAN_WIDTH_40) + return false; + + *op_class = 180; + return true; + } + + /* not supported yet */ + return false; +} +EXPORT_SYMBOL(ieee80211_chandef_to_operating_class); + int cfg80211_validate_beacon_int(struct cfg80211_registered_device *rdev, u32 beacon_int) { -- cgit v1.2.3 From 6bab2e19c5ffd1f21b28c2cabb3801a37b77ae69 Mon Sep 17 00:00:00 2001 From: Tom Gundersen Date: Wed, 18 Mar 2015 11:13:39 +0100 Subject: cfg80211: pass name_assign_type to rdev_add_virtual_intf() This will expose in /sys whether the ifname of a device is set by userspace or generated by the kernel. The latter kind (wlanX, etc) is not deterministic, so userspace needs to rename these devices to names that are guaranteed to stay the same between reboots. The former, however should never be renamed, so userspace needs to be able to reliably tell the difference. Similar functionality was introduced for the rtnetlink core in commit 5517750f058e ("net: rtnetlink - make create_link take name_assign_type") Signed-off-by: Tom Gundersen Cc: Kalle Valo Cc: Brett Rudley Cc: Arend van Spriel Cc: Franky (Zhenhui) Lin Cc: Hante Meuleman Cc: Johannes Berg [reformat changelog to fit 72 cols] Signed-off-by: Johannes Berg --- drivers/net/wireless/ath/ath6kl/cfg80211.c | 6 ++++-- drivers/net/wireless/ath/ath6kl/cfg80211.h | 1 + drivers/net/wireless/ath/ath6kl/core.c | 4 ++-- drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c | 3 ++- drivers/net/wireless/brcm80211/brcmfmac/p2p.c | 3 +++ drivers/net/wireless/brcm80211/brcmfmac/p2p.h | 1 + drivers/net/wireless/mwifiex/cfg80211.c | 5 +++-- drivers/net/wireless/mwifiex/main.c | 6 +++--- drivers/net/wireless/mwifiex/main.h | 1 + drivers/staging/rtl8723au/os_dep/ioctl_cfg80211.c | 6 +++++- include/net/cfg80211.h | 1 + net/mac80211/cfg.c | 3 ++- net/mac80211/ieee80211_i.h | 1 + net/mac80211/iface.c | 3 ++- net/mac80211/main.c | 2 +- net/wireless/nl80211.c | 3 ++- net/wireless/rdev-ops.h | 5 +++-- 17 files changed, 37 insertions(+), 17 deletions(-) (limited to 'include') diff --git a/drivers/net/wireless/ath/ath6kl/cfg80211.c b/drivers/net/wireless/ath/ath6kl/cfg80211.c index ff7ba5c195c6..d740f14f3539 100644 --- a/drivers/net/wireless/ath/ath6kl/cfg80211.c +++ b/drivers/net/wireless/ath/ath6kl/cfg80211.c @@ -1496,6 +1496,7 @@ static int ath6kl_cfg80211_set_power_mgmt(struct wiphy *wiphy, static struct wireless_dev *ath6kl_cfg80211_add_iface(struct wiphy *wiphy, const char *name, + unsigned char name_assign_type, enum nl80211_iftype type, u32 *flags, struct vif_params *params) @@ -1514,7 +1515,7 @@ static struct wireless_dev *ath6kl_cfg80211_add_iface(struct wiphy *wiphy, return ERR_PTR(-EINVAL); } - wdev = ath6kl_interface_add(ar, name, type, if_idx, nw_type); + wdev = ath6kl_interface_add(ar, name, name_assign_type, type, if_idx, nw_type); if (!wdev) return ERR_PTR(-ENOMEM); @@ -3634,13 +3635,14 @@ void ath6kl_cfg80211_vif_cleanup(struct ath6kl_vif *vif) } struct wireless_dev *ath6kl_interface_add(struct ath6kl *ar, const char *name, + unsigned char name_assign_type, enum nl80211_iftype type, u8 fw_vif_idx, u8 nw_type) { struct net_device *ndev; struct ath6kl_vif *vif; - ndev = alloc_netdev(sizeof(*vif), name, NET_NAME_UNKNOWN, ether_setup); + ndev = alloc_netdev(sizeof(*vif), name, name_assign_type, ether_setup); if (!ndev) return NULL; diff --git a/drivers/net/wireless/ath/ath6kl/cfg80211.h b/drivers/net/wireless/ath/ath6kl/cfg80211.h index b59becd91aea..5aa57a7639bf 100644 --- a/drivers/net/wireless/ath/ath6kl/cfg80211.h +++ b/drivers/net/wireless/ath/ath6kl/cfg80211.h @@ -25,6 +25,7 @@ enum ath6kl_cfg_suspend_mode { }; struct wireless_dev *ath6kl_interface_add(struct ath6kl *ar, const char *name, + unsigned char name_assign_type, enum nl80211_iftype type, u8 fw_vif_idx, u8 nw_type); void ath6kl_cfg80211_ch_switch_notify(struct ath6kl_vif *vif, int freq, diff --git a/drivers/net/wireless/ath/ath6kl/core.c b/drivers/net/wireless/ath/ath6kl/core.c index 0df74b245af4..4ec02cea0f43 100644 --- a/drivers/net/wireless/ath/ath6kl/core.c +++ b/drivers/net/wireless/ath/ath6kl/core.c @@ -211,8 +211,8 @@ int ath6kl_core_init(struct ath6kl *ar, enum ath6kl_htc_type htc_type) rtnl_lock(); /* Add an initial station interface */ - wdev = ath6kl_interface_add(ar, "wlan%d", NL80211_IFTYPE_STATION, 0, - INFRA_NETWORK); + wdev = ath6kl_interface_add(ar, "wlan%d", NET_NAME_ENUM, + NL80211_IFTYPE_STATION, 0, INFRA_NETWORK); rtnl_unlock(); diff --git a/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c b/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c index b59b8c6c42ab..bcbccba4cdbf 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c @@ -625,6 +625,7 @@ static bool brcmf_is_ibssmode(struct brcmf_cfg80211_vif *vif) static struct wireless_dev *brcmf_cfg80211_add_iface(struct wiphy *wiphy, const char *name, + unsigned char name_assign_type, enum nl80211_iftype type, u32 *flags, struct vif_params *params) @@ -648,7 +649,7 @@ static struct wireless_dev *brcmf_cfg80211_add_iface(struct wiphy *wiphy, case NL80211_IFTYPE_P2P_CLIENT: case NL80211_IFTYPE_P2P_GO: case NL80211_IFTYPE_P2P_DEVICE: - wdev = brcmf_p2p_add_vif(wiphy, name, type, flags, params); + wdev = brcmf_p2p_add_vif(wiphy, name, name_assign_type, type, flags, params); if (!IS_ERR(wdev)) brcmf_cfg80211_update_proto_addr_mode(wdev); return wdev; diff --git a/drivers/net/wireless/brcm80211/brcmfmac/p2p.c b/drivers/net/wireless/brcm80211/brcmfmac/p2p.c index effb48ebd864..b44ea85dd9db 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/p2p.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/p2p.c @@ -2246,11 +2246,13 @@ static void brcmf_p2p_delete_p2pdev(struct brcmf_p2p_info *p2p, * * @wiphy: wiphy device of new interface. * @name: name of the new interface. + * @name_assign_type: origin of the interface name * @type: nl80211 interface type. * @flags: not used. * @params: contains mac address for P2P device. */ struct wireless_dev *brcmf_p2p_add_vif(struct wiphy *wiphy, const char *name, + unsigned char name_assign_type, enum nl80211_iftype type, u32 *flags, struct vif_params *params) { @@ -2310,6 +2312,7 @@ struct wireless_dev *brcmf_p2p_add_vif(struct wiphy *wiphy, const char *name, } strncpy(ifp->ndev->name, name, sizeof(ifp->ndev->name) - 1); + ifp->ndev->name_assign_type = name_assign_type; err = brcmf_net_attach(ifp, true); if (err) { brcmf_err("Registering netdevice failed\n"); diff --git a/drivers/net/wireless/brcm80211/brcmfmac/p2p.h b/drivers/net/wireless/brcm80211/brcmfmac/p2p.h index 6821b26224be..872f382d9e49 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/p2p.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/p2p.h @@ -149,6 +149,7 @@ struct brcmf_p2p_info { s32 brcmf_p2p_attach(struct brcmf_cfg80211_info *cfg); void brcmf_p2p_detach(struct brcmf_p2p_info *p2p); struct wireless_dev *brcmf_p2p_add_vif(struct wiphy *wiphy, const char *name, + unsigned char name_assign_type, enum nl80211_iftype type, u32 *flags, struct vif_params *params); int brcmf_p2p_del_vif(struct wiphy *wiphy, struct wireless_dev *wdev); diff --git a/drivers/net/wireless/mwifiex/cfg80211.c b/drivers/net/wireless/mwifiex/cfg80211.c index a47eb55bb6da..2d489bfaea08 100644 --- a/drivers/net/wireless/mwifiex/cfg80211.c +++ b/drivers/net/wireless/mwifiex/cfg80211.c @@ -2399,10 +2399,11 @@ mwifiex_setup_ht_caps(struct ieee80211_sta_ht_cap *ht_info, #define MWIFIEX_MAX_WQ_LEN 30 /* - * create a new virtual interface with the given name + * create a new virtual interface with the given name and name assign type */ struct wireless_dev *mwifiex_add_virtual_intf(struct wiphy *wiphy, const char *name, + unsigned char name_assign_type, enum nl80211_iftype type, u32 *flags, struct vif_params *params) @@ -2523,7 +2524,7 @@ struct wireless_dev *mwifiex_add_virtual_intf(struct wiphy *wiphy, } dev = alloc_netdev_mqs(sizeof(struct mwifiex_private *), name, - NET_NAME_UNKNOWN, ether_setup, + name_assign_type, ether_setup, IEEE80211_NUM_ACS, 1); if (!dev) { wiphy_err(wiphy, "no memory available for netdevice\n"); diff --git a/drivers/net/wireless/mwifiex/main.c b/drivers/net/wireless/mwifiex/main.c index 7e74b4fccddd..6f55e84fcab1 100644 --- a/drivers/net/wireless/mwifiex/main.c +++ b/drivers/net/wireless/mwifiex/main.c @@ -466,7 +466,7 @@ static void mwifiex_fw_dpc(const struct firmware *firmware, void *context) rtnl_lock(); /* Create station interface by default */ - wdev = mwifiex_add_virtual_intf(adapter->wiphy, "mlan%d", + wdev = mwifiex_add_virtual_intf(adapter->wiphy, "mlan%d", NET_NAME_ENUM, NL80211_IFTYPE_STATION, NULL, NULL); if (IS_ERR(wdev)) { dev_err(adapter->dev, "cannot create default STA interface\n"); @@ -475,7 +475,7 @@ static void mwifiex_fw_dpc(const struct firmware *firmware, void *context) } if (driver_mode & MWIFIEX_DRIVER_MODE_UAP) { - wdev = mwifiex_add_virtual_intf(adapter->wiphy, "uap%d", + wdev = mwifiex_add_virtual_intf(adapter->wiphy, "uap%d", NET_NAME_ENUM, NL80211_IFTYPE_AP, NULL, NULL); if (IS_ERR(wdev)) { dev_err(adapter->dev, "cannot create AP interface\n"); @@ -485,7 +485,7 @@ static void mwifiex_fw_dpc(const struct firmware *firmware, void *context) } if (driver_mode & MWIFIEX_DRIVER_MODE_P2P) { - wdev = mwifiex_add_virtual_intf(adapter->wiphy, "p2p%d", + wdev = mwifiex_add_virtual_intf(adapter->wiphy, "p2p%d", NET_NAME_ENUM, NL80211_IFTYPE_P2P_CLIENT, NULL, NULL); if (IS_ERR(wdev)) { diff --git a/drivers/net/wireless/mwifiex/main.h b/drivers/net/wireless/mwifiex/main.h index f0a6af179af0..3f0625f22265 100644 --- a/drivers/net/wireless/mwifiex/main.h +++ b/drivers/net/wireless/mwifiex/main.h @@ -1318,6 +1318,7 @@ u8 mwifiex_chan_type_to_sec_chan_offset(enum nl80211_channel_type chan_type); struct wireless_dev *mwifiex_add_virtual_intf(struct wiphy *wiphy, const char *name, + unsigned char name_assign_type, enum nl80211_iftype type, u32 *flags, struct vif_params *params); diff --git a/drivers/staging/rtl8723au/os_dep/ioctl_cfg80211.c b/drivers/staging/rtl8723au/os_dep/ioctl_cfg80211.c index 537bd8214efe..a6116fdc8678 100644 --- a/drivers/staging/rtl8723au/os_dep/ioctl_cfg80211.c +++ b/drivers/staging/rtl8723au/os_dep/ioctl_cfg80211.c @@ -2580,6 +2580,7 @@ static const struct net_device_ops rtw_cfg80211_monitor_if_ops = { }; static int rtw_cfg80211_add_monitor_if(struct rtw_adapter *padapter, char *name, + unsigned char name_assign_type, struct net_device **ndev) { int ret = 0; @@ -2612,6 +2613,7 @@ static int rtw_cfg80211_add_monitor_if(struct rtw_adapter *padapter, char *name, mon_ndev->type = ARPHRD_IEEE80211_RADIOTAP; strncpy(mon_ndev->name, name, IFNAMSIZ); mon_ndev->name[IFNAMSIZ - 1] = 0; + mon_ndev->name_assign_type = name_assign_type; mon_ndev->destructor = rtw_ndev_destructor; mon_ndev->netdev_ops = &rtw_cfg80211_monitor_if_ops; @@ -2654,6 +2656,7 @@ out: static struct wireless_dev * cfg80211_rtw_add_virtual_intf(struct wiphy *wiphy, const char *name, + unsigned char name_assign_type, enum nl80211_iftype type, u32 *flags, struct vif_params *params) { @@ -2673,7 +2676,8 @@ cfg80211_rtw_add_virtual_intf(struct wiphy *wiphy, const char *name, break; case NL80211_IFTYPE_MONITOR: ret = - rtw_cfg80211_add_monitor_if(padapter, (char *)name, &ndev); + rtw_cfg80211_add_monitor_if(padapter, (char *)name, + name_assign_type, &ndev); break; case NL80211_IFTYPE_P2P_CLIENT: diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index c4d873b8f32d..ab667fbc743d 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -2456,6 +2456,7 @@ struct cfg80211_ops { struct wireless_dev * (*add_virtual_intf)(struct wiphy *wiphy, const char *name, + unsigned char name_assign_type, enum nl80211_iftype type, u32 *flags, struct vif_params *params); diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index e01cea3b9043..49401238ac39 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -24,6 +24,7 @@ static struct wireless_dev *ieee80211_add_iface(struct wiphy *wiphy, const char *name, + unsigned char name_assign_type, enum nl80211_iftype type, u32 *flags, struct vif_params *params) @@ -33,7 +34,7 @@ static struct wireless_dev *ieee80211_add_iface(struct wiphy *wiphy, struct ieee80211_sub_if_data *sdata; int err; - err = ieee80211_if_add(local, name, &wdev, type, params); + err = ieee80211_if_add(local, name, name_assign_type, &wdev, type, params); if (err) return ERR_PTR(err); diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index ebc8135e0aaa..3e3cfe8da4ef 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -1578,6 +1578,7 @@ int ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev, int ieee80211_iface_init(void); void ieee80211_iface_exit(void); int ieee80211_if_add(struct ieee80211_local *local, const char *name, + unsigned char name_assign_type, struct wireless_dev **new_wdev, enum nl80211_iftype type, struct vif_params *params); int ieee80211_if_change_type(struct ieee80211_sub_if_data *sdata, diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c index 9689d3b1133b..a0cd97fd0c49 100644 --- a/net/mac80211/iface.c +++ b/net/mac80211/iface.c @@ -1648,6 +1648,7 @@ static void ieee80211_assign_perm_addr(struct ieee80211_local *local, } int ieee80211_if_add(struct ieee80211_local *local, const char *name, + unsigned char name_assign_type, struct wireless_dev **new_wdev, enum nl80211_iftype type, struct vif_params *params) { @@ -1676,7 +1677,7 @@ int ieee80211_if_add(struct ieee80211_local *local, const char *name, txqs = IEEE80211_NUM_ACS; ndev = alloc_netdev_mqs(sizeof(*sdata) + local->hw.vif_data_size, - name, NET_NAME_UNKNOWN, + name, name_assign_type, ieee80211_if_setup, txqs, 1); if (!ndev) return -ENOMEM; diff --git a/net/mac80211/main.c b/net/mac80211/main.c index 2f51e6d1f2b3..4977967c8b00 100644 --- a/net/mac80211/main.c +++ b/net/mac80211/main.c @@ -1057,7 +1057,7 @@ int ieee80211_register_hw(struct ieee80211_hw *hw) /* add one default STA interface if supported */ if (local->hw.wiphy->interface_modes & BIT(NL80211_IFTYPE_STATION) && !(hw->flags & IEEE80211_HW_NO_AUTO_VIF)) { - result = ieee80211_if_add(local, "wlan%d", NULL, + result = ieee80211_if_add(local, "wlan%d", NET_NAME_ENUM, NULL, NL80211_IFTYPE_STATION, NULL); if (result) wiphy_warn(local->hw.wiphy, diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 543dd51342f0..f60ee5b45c0c 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -2667,7 +2667,8 @@ static int nl80211_new_interface(struct sk_buff *skb, struct genl_info *info) wdev = rdev_add_virtual_intf(rdev, nla_data(info->attrs[NL80211_ATTR_IFNAME]), - type, err ? NULL : &flags, ¶ms); + NET_NAME_USER, type, err ? NULL : &flags, + ¶ms); if (WARN_ON(!wdev)) { nlmsg_free(msg); return -EPROTO; diff --git a/net/wireless/rdev-ops.h b/net/wireless/rdev-ops.h index 35cfb7134bdb..c6e83a7468c0 100644 --- a/net/wireless/rdev-ops.h +++ b/net/wireless/rdev-ops.h @@ -35,13 +35,14 @@ static inline void rdev_set_wakeup(struct cfg80211_registered_device *rdev, static inline struct wireless_dev *rdev_add_virtual_intf(struct cfg80211_registered_device *rdev, char *name, + unsigned char name_assign_type, enum nl80211_iftype type, u32 *flags, struct vif_params *params) { struct wireless_dev *ret; trace_rdev_add_virtual_intf(&rdev->wiphy, name, type); - ret = rdev->ops->add_virtual_intf(&rdev->wiphy, name, type, flags, - params); + ret = rdev->ops->add_virtual_intf(&rdev->wiphy, name, name_assign_type, + type, flags, params); trace_rdev_return_wdev(&rdev->wiphy, ret); return ret; } -- cgit v1.2.3 From 527871d7206dac2733d0bae52f5a328811d299ee Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Sat, 21 Mar 2015 08:09:55 +0100 Subject: mac80211: make sta.wme indicate whether QoS is used Indicating just the peer's capability is fairly pointless if the local device doesn't support it. Make the variable track both combined, and remove the 'local support' check in the TX path. Signed-off-by: Johannes Berg --- include/net/mac80211.h | 3 ++- net/mac80211/cfg.c | 3 ++- net/mac80211/ibss.c | 2 +- net/mac80211/mlme.c | 2 +- net/mac80211/tx.c | 4 ++-- 5 files changed, 8 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 94c8c62800c3..201bc68e0cff 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -1557,7 +1557,8 @@ struct ieee80211_sta_rates { * @supp_rates: Bitmap of supported rates (per band) * @ht_cap: HT capabilities of this STA; restricted to our own capabilities * @vht_cap: VHT capabilities of this STA; restricted to our own capabilities - * @wme: indicates whether the STA supports QoS/WME. + * @wme: indicates whether the STA supports QoS/WME (if local devices does, + * otherwise always false) * @drv_priv: data area for driver use, will always be aligned to * sizeof(void *), size is determined in hw information. * @uapsd_queues: bitmap of queues configured for uapsd. Only valid diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 49401238ac39..301215be59ef 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -1073,7 +1073,8 @@ static int sta_apply_parameters(struct ieee80211_local *local, clear_sta_flag(sta, WLAN_STA_SHORT_PREAMBLE); } - if (mask & BIT(NL80211_STA_FLAG_WME)) + if (mask & BIT(NL80211_STA_FLAG_WME) && + local->hw.queues >= IEEE80211_NUM_ACS) sta->sta.wme = set & BIT(NL80211_STA_FLAG_WME); if (mask & BIT(NL80211_STA_FLAG_MFP)) { diff --git a/net/mac80211/ibss.c b/net/mac80211/ibss.c index 17eda3575d7d..6da4e72f8178 100644 --- a/net/mac80211/ibss.c +++ b/net/mac80211/ibss.c @@ -1016,7 +1016,7 @@ static void ieee80211_update_sta_info(struct ieee80211_sub_if_data *sdata, } } - if (sta && elems->wmm_info) + if (sta && elems->wmm_info && local->hw.queues >= IEEE80211_NUM_ACS) sta->sta.wme = true; if (sta && elems->ht_operation && elems->ht_cap_elem && diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 8a06b1abc47a..0cbcde11fae3 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -2990,7 +2990,7 @@ static bool ieee80211_assoc_success(struct ieee80211_sub_if_data *sdata, sta->sta.mfp = false; } - sta->sta.wme = elems.wmm_param; + sta->sta.wme = elems.wmm_param && local->hw.queues >= IEEE80211_NUM_ACS; err = sta_info_move_state(sta, IEEE80211_STA_ASSOC); if (!err && !(ifmgd->flags & IEEE80211_STA_CONTROL_PORT)) diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index 34b0e7545cc5..018f029d0c95 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -2088,8 +2088,8 @@ static struct sk_buff *ieee80211_build_hdr(struct ieee80211_sub_if_data *sdata, if (ieee80211_vif_is_mesh(&sdata->vif)) wme_sta = true; - /* receiver and we are QoS enabled, use a QoS type frame */ - if (wme_sta && local->hw.queues >= IEEE80211_NUM_ACS) { + /* receiver does QoS (which also means we do) use it */ + if (wme_sta) { fc |= cpu_to_le16(IEEE80211_STYPE_QOS_DATA); hdrlen += 2; } -- cgit v1.2.3 From 6e00ff079354ee72fe95cb61a3993962074a2592 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Sun, 29 Mar 2015 12:45:42 +0100 Subject: mfd: arizona: Correct type of gpio_defaults gpio_defaults needs to be specified as an unsigned int rather than an int, because the intention of the DT binding is that all out of range values for a 16-bit register will cause the defaults to be used, however, if gpio_defaults is an int then values that are larger than INT_MAX will become negative numbers and be written out directly to the hardware. As no where in the code replies on gpio_defaults being an int, the simplest fix is to just change it to unsigned. Signed-off-by: Charles Keepax Signed-off-by: Lee Jones --- include/linux/mfd/arizona/pdata.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/mfd/arizona/pdata.h b/include/linux/mfd/arizona/pdata.h index 48fe31356605..1789cb0f4f17 100644 --- a/include/linux/mfd/arizona/pdata.h +++ b/include/linux/mfd/arizona/pdata.h @@ -94,7 +94,7 @@ struct arizona_pdata { int gpio_base; /** Pin state for GPIO pins */ - int gpio_defaults[ARIZONA_MAX_GPIO]; + unsigned int gpio_defaults[ARIZONA_MAX_GPIO]; /** * Maximum number of channels clocks will be generated for, -- cgit v1.2.3 From 5cdc7f02162aa8b980bd5d89e86fd94de0285ca3 Mon Sep 17 00:00:00 2001 From: Aaro Koskinen Date: Sat, 28 Mar 2015 22:45:35 +0200 Subject: mfd: menelaus: Delete omap_has_menelaus Delete unused macro. Signed-off-by: Aaro Koskinen Signed-off-by: Lee Jones --- include/linux/mfd/menelaus.h | 6 ------ 1 file changed, 6 deletions(-) (limited to 'include') diff --git a/include/linux/mfd/menelaus.h b/include/linux/mfd/menelaus.h index f097e89134cb..a1e12bf393db 100644 --- a/include/linux/mfd/menelaus.h +++ b/include/linux/mfd/menelaus.h @@ -38,10 +38,4 @@ extern int menelaus_set_vcore_hw(unsigned int roof_mV, unsigned int floor_mV); extern int menelaus_set_regulator_sleep(int enable, u32 val); -#if defined(CONFIG_ARCH_OMAP2) && defined(CONFIG_MENELAUS) -#define omap_has_menelaus() 1 -#else -#define omap_has_menelaus() 0 -#endif - #endif -- cgit v1.2.3 From 1ea8684e1e0d7453f65a1ed69c2d0b890bfb9e33 Mon Sep 17 00:00:00 2001 From: Aaro Koskinen Date: Sat, 28 Mar 2015 22:45:36 +0200 Subject: mfd: menelaus: Drop support for SW controller VCORE Drop support for SW controlled VCORE, nobody uses it. Signed-off-by: Aaro Koskinen Signed-off-by: Lee Jones --- drivers/mfd/menelaus.c | 23 ----------------------- include/linux/mfd/menelaus.h | 1 - 2 files changed, 24 deletions(-) (limited to 'include') diff --git a/drivers/mfd/menelaus.c b/drivers/mfd/menelaus.c index 5269ff2f29bc..2b2dc1f6d2ca 100644 --- a/drivers/mfd/menelaus.c +++ b/drivers/mfd/menelaus.c @@ -532,29 +532,6 @@ static const struct menelaus_vtg_value vcore_values[] = { { 1450, 18 }, }; -int menelaus_set_vcore_sw(unsigned int mV) -{ - int val, ret; - struct i2c_client *c = the_menelaus->client; - - val = menelaus_get_vtg_value(mV, vcore_values, - ARRAY_SIZE(vcore_values)); - if (val < 0) - return -EINVAL; - - dev_dbg(&c->dev, "Setting VCORE to %d mV (val 0x%02x)\n", mV, val); - - /* Set SW mode and the voltage in one go. */ - mutex_lock(&the_menelaus->lock); - ret = menelaus_write_reg(MENELAUS_VCORE_CTRL1, val); - if (ret == 0) - the_menelaus->vcore_hw_mode = 0; - mutex_unlock(&the_menelaus->lock); - msleep(1); - - return ret; -} - int menelaus_set_vcore_hw(unsigned int roof_mV, unsigned int floor_mV) { int fval, rval, val, ret; diff --git a/include/linux/mfd/menelaus.h b/include/linux/mfd/menelaus.h index a1e12bf393db..9e85ac06da89 100644 --- a/include/linux/mfd/menelaus.h +++ b/include/linux/mfd/menelaus.h @@ -24,7 +24,6 @@ extern int menelaus_set_vaux(unsigned int mV); extern int menelaus_set_vdcdc(int dcdc, unsigned int mV); extern int menelaus_set_slot_sel(int enable); extern int menelaus_get_slot_pin_states(void); -extern int menelaus_set_vcore_sw(unsigned int mV); extern int menelaus_set_vcore_hw(unsigned int roof_mV, unsigned int floor_mV); #define EN_VPLL_SLEEP (1 << 7) -- cgit v1.2.3 From bc917be8105993c256338ad1189650364a741483 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sat, 21 Mar 2015 17:45:43 -0400 Subject: saner iov_iter initialization primitives iovec-backed iov_iter instances are assumed to satisfy several properties: * no more than UIO_MAXIOV elements in iovec array * total size of all ranges is no more than MAX_RW_COUNT * all ranges pass access_ok(). The problem is, invariants of data structures should be established in the primitives creating those data structures, not in the code using those primitives. And iov_iter_init() violates that principle. For a while we managed to get away with that, but once the use of iov_iter started to spread, it didn't take long for shit to hit the fan - missed check in sys_sendto() had introduced a roothole. We _do_ have primitives for importing and validating iovecs (both native and compat ones) and those primitives are almost always followed by shoving the resulting iovec into iov_iter. Life would be considerably simpler (and safer) if we combined those primitives with initializing iov_iter. That gives us two new primitives - import_iovec() and compat_import_iovec(). Calling conventions: iovec = iov_array; err = import_iovec(direction, uvec, nr_segs, ARRAY_SIZE(iov_array), &iovec, &iter); imports user vector into kernel space (into iov_array if it fits, allocated if it doesn't fit or if iovec was NULL), validates it and sets iter up to refer to it. On success 0 is returned and allocated kernel copy (or NULL if the array had fit into caller-supplied one) is returned via iovec. On failure all allocations are undone and -E... is returned. If the total size of ranges exceeds MAX_RW_COUNT, the excess is silently truncated. compat_import_iovec() expects uvec to be a pointer to user array of compat_iovec; otherwise it's identical to import_iovec(). Finally, import_single_range() sets iov_iter backed by single-element iovec covering a user-supplied range - err = import_single_range(direction, address, size, iovec, &iter); does validation and sets iter up. Again, size in excess of MAX_RW_COUNT gets silently truncated. Next commits will be switching the things up to use of those and reducing the amount of iov_iter_init() instances. Signed-off-by: Al Viro --- include/linux/uio.h | 14 +++++++++++++ lib/iov_iter.c | 57 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 71 insertions(+) (limited to 'include') diff --git a/include/linux/uio.h b/include/linux/uio.h index 71880299ed48..1f4a37f1f025 100644 --- a/include/linux/uio.h +++ b/include/linux/uio.h @@ -139,4 +139,18 @@ static inline void iov_iter_reexpand(struct iov_iter *i, size_t count) size_t csum_and_copy_to_iter(void *addr, size_t bytes, __wsum *csum, struct iov_iter *i); size_t csum_and_copy_from_iter(void *addr, size_t bytes, __wsum *csum, struct iov_iter *i); +int import_iovec(int type, const struct iovec __user * uvector, + unsigned nr_segs, unsigned fast_segs, + struct iovec **iov, struct iov_iter *i); + +#ifdef CONFIG_COMPAT +struct compat_iovec; +int compat_import_iovec(int type, const struct compat_iovec __user * uvector, + unsigned nr_segs, unsigned fast_segs, + struct iovec **iov, struct iov_iter *i); +#endif + +int import_single_range(int type, void __user *buf, size_t len, + struct iovec *iov, struct iov_iter *i); + #endif diff --git a/lib/iov_iter.c b/lib/iov_iter.c index 9d96e283520c..fc6e33f6b7f3 100644 --- a/lib/iov_iter.c +++ b/lib/iov_iter.c @@ -766,3 +766,60 @@ const void *dup_iter(struct iov_iter *new, struct iov_iter *old, gfp_t flags) flags); } EXPORT_SYMBOL(dup_iter); + +int import_iovec(int type, const struct iovec __user * uvector, + unsigned nr_segs, unsigned fast_segs, + struct iovec **iov, struct iov_iter *i) +{ + ssize_t n; + struct iovec *p; + n = rw_copy_check_uvector(type, uvector, nr_segs, fast_segs, + *iov, &p); + if (n < 0) { + if (p != *iov) + kfree(p); + *iov = NULL; + return n; + } + iov_iter_init(i, type, p, nr_segs, n); + *iov = p == *iov ? NULL : p; + return 0; +} +EXPORT_SYMBOL(import_iovec); + +#ifdef CONFIG_COMPAT +#include + +int compat_import_iovec(int type, const struct compat_iovec __user * uvector, + unsigned nr_segs, unsigned fast_segs, + struct iovec **iov, struct iov_iter *i) +{ + ssize_t n; + struct iovec *p; + n = compat_rw_copy_check_uvector(type, uvector, nr_segs, fast_segs, + *iov, &p); + if (n < 0) { + if (p != *iov) + kfree(p); + *iov = NULL; + return n; + } + iov_iter_init(i, type, p, nr_segs, n); + *iov = p == *iov ? NULL : p; + return 0; +} +#endif + +int import_single_range(int rw, void __user *buf, size_t len, + struct iovec *iov, struct iov_iter *i) +{ + if (len > MAX_RW_COUNT) + len = MAX_RW_COUNT; + if (unlikely(!access_ok(!rw, buf, len))) + return -EFAULT; + + iov->iov_base = buf; + iov->iov_len = len; + iov_iter_init(i, rw, iov, 1, len); + return 0; +} -- cgit v1.2.3 From 9647507aff4d885d98a884a634d360b8f2d24c10 Mon Sep 17 00:00:00 2001 From: Jacek Anaszewski Date: Thu, 12 Mar 2015 08:45:02 -0700 Subject: leds: flash: Fix the size of sysfs_groups array LED_FLASH_MAX_SYSFS_GROUPS macro had value that was relevant for previous version of the patches introducing LED Flash class. Currently it is required to reserve the room for maximum 4 sysfs groups. Since the last element of the struct attribute_group array passed to the function device_create_with_groups has to be NULL, the size of the array has to be greater by one than maximum allowed number of groups. Therefore, the name of the macro is being changed to LED_FLASH_SYSFS_GROUPS_SIZE, to make it more accurrate. Signed-off-by: Jacek Anaszewski Acked-by: Kyungmin Park Cc: Bryan Wu Cc: Richard Purdie Signed-off-by: Bryan Wu --- include/linux/led-class-flash.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/linux/led-class-flash.h b/include/linux/led-class-flash.h index 21ec91e13c47..e97966d1fb8d 100644 --- a/include/linux/led-class-flash.h +++ b/include/linux/led-class-flash.h @@ -32,7 +32,7 @@ struct led_classdev_flash; #define LED_FAULT_LED_OVER_TEMPERATURE (1 << 8) #define LED_NUM_FLASH_FAULTS 9 -#define LED_FLASH_MAX_SYSFS_GROUPS 7 +#define LED_FLASH_SYSFS_GROUPS_SIZE 5 struct led_flash_ops { /* set flash brightness */ @@ -80,7 +80,7 @@ struct led_classdev_flash { struct led_flash_setting timeout; /* LED Flash class sysfs groups */ - const struct attribute_group *sysfs_groups[LED_FLASH_MAX_SYSFS_GROUPS]; + const struct attribute_group *sysfs_groups[LED_FLASH_SYSFS_GROUPS_SIZE]; }; static inline struct led_classdev_flash *lcdev_to_flcdev( -- cgit v1.2.3 From 5e23a35cf8b3c5c07111409d589590b9fb4305f6 Mon Sep 17 00:00:00 2001 From: Jacek Anaszewski Date: Thu, 12 Mar 2015 08:45:03 -0700 Subject: dt-binding: leds: Add common LED DT bindings macros Add macros for defining boost mode and trigger type properties of flash LED devices. Signed-off-by: Jacek Anaszewski Acked-by: Kyungmin Park Cc: Richard Purdie Signed-off-by: Bryan Wu --- include/dt-bindings/leds/common.h | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 include/dt-bindings/leds/common.h (limited to 'include') diff --git a/include/dt-bindings/leds/common.h b/include/dt-bindings/leds/common.h new file mode 100644 index 000000000000..79fcef72ef57 --- /dev/null +++ b/include/dt-bindings/leds/common.h @@ -0,0 +1,21 @@ +/* + * This header provides macros for the common LEDs device tree bindings. + * + * Copyright (C) 2015, Samsung Electronics Co., Ltd. + * + * Author: Jacek Anaszewski + */ + +#ifndef __DT_BINDINGS_LEDS_H__ +#define __DT_BINDINGS_LEDS_H + +/* External trigger type */ +#define LEDS_TRIG_TYPE_EDGE 0 +#define LEDS_TRIG_TYPE_LEVEL 1 + +/* Boost modes */ +#define LEDS_BOOST_OFF 0 +#define LEDS_BOOST_ADAPTIVE 1 +#define LEDS_BOOST_FIXED 2 + +#endif /* __DT_BINDINGS_LEDS_H */ -- cgit v1.2.3 From a4368ff3ed3b57e4b5e36d83b75604f68bbcdaad Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Mon, 30 Mar 2015 23:21:01 +0300 Subject: Bluetooth: Refactor L2CAP variables into l2cap_ctrl We're getting very close to the maximum possible size of bt_skb_cb. To prepare to shrink the struct with the help of a union this patch moves all L2CAP related variables into the l2cap_ctrl struct. To later add other 'ctrl' structs the L2CAP one is renamed simple 'l2cap' instead of 'control'. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- include/net/bluetooth/bluetooth.h | 8 +++---- net/bluetooth/l2cap_core.c | 48 +++++++++++++++++++-------------------- net/bluetooth/l2cap_sock.c | 6 ++--- net/bluetooth/smp.c | 2 +- 4 files changed, 32 insertions(+), 32 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/bluetooth.h b/include/net/bluetooth/bluetooth.h index 33a5e00025aa..d8367cc7c76e 100644 --- a/include/net/bluetooth/bluetooth.h +++ b/include/net/bluetooth/bluetooth.h @@ -269,6 +269,9 @@ struct l2cap_ctrl { __u16 reqseq; __u16 txseq; __u8 retries; + __le16 psm; + bdaddr_t bdaddr; + struct l2cap_chan *chan; }; struct hci_dev; @@ -284,10 +287,7 @@ struct bt_skb_cb { __u8 req_start:1; u8 req_event; hci_req_complete_t req_complete; - struct l2cap_chan *chan; - struct l2cap_ctrl control; - bdaddr_t bdaddr; - __le16 psm; + struct l2cap_ctrl l2cap; }; #define bt_cb(skb) ((struct bt_skb_cb *)((skb)->cb)) diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index d69861c89bb5..dad419782a12 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -292,7 +292,7 @@ static struct sk_buff *l2cap_ertm_seq_in_queue(struct sk_buff_head *head, struct sk_buff *skb; skb_queue_walk(head, skb) { - if (bt_cb(skb)->control.txseq == seq) + if (bt_cb(skb)->l2cap.txseq == seq) return skb; } @@ -954,11 +954,11 @@ static inline void __unpack_control(struct l2cap_chan *chan, { if (test_bit(FLAG_EXT_CTRL, &chan->flags)) { __unpack_extended_control(get_unaligned_le32(skb->data), - &bt_cb(skb)->control); + &bt_cb(skb)->l2cap); skb_pull(skb, L2CAP_EXT_CTRL_SIZE); } else { __unpack_enhanced_control(get_unaligned_le16(skb->data), - &bt_cb(skb)->control); + &bt_cb(skb)->l2cap); skb_pull(skb, L2CAP_ENH_CTRL_SIZE); } } @@ -1200,8 +1200,8 @@ static void l2cap_move_setup(struct l2cap_chan *chan) chan->retry_count = 0; skb_queue_walk(&chan->tx_q, skb) { - if (bt_cb(skb)->control.retries) - bt_cb(skb)->control.retries = 1; + if (bt_cb(skb)->l2cap.retries) + bt_cb(skb)->l2cap.retries = 1; else break; } @@ -1846,8 +1846,8 @@ static void l2cap_streaming_send(struct l2cap_chan *chan, skb = skb_dequeue(&chan->tx_q); - bt_cb(skb)->control.retries = 1; - control = &bt_cb(skb)->control; + bt_cb(skb)->l2cap.retries = 1; + control = &bt_cb(skb)->l2cap; control->reqseq = 0; control->txseq = chan->next_tx_seq; @@ -1891,8 +1891,8 @@ static int l2cap_ertm_send(struct l2cap_chan *chan) skb = chan->tx_send_head; - bt_cb(skb)->control.retries = 1; - control = &bt_cb(skb)->control; + bt_cb(skb)->l2cap.retries = 1; + control = &bt_cb(skb)->l2cap; if (test_and_clear_bit(CONN_SEND_FBIT, &chan->conn_state)) control->final = 1; @@ -1963,11 +1963,11 @@ static void l2cap_ertm_resend(struct l2cap_chan *chan) continue; } - bt_cb(skb)->control.retries++; - control = bt_cb(skb)->control; + bt_cb(skb)->l2cap.retries++; + control = bt_cb(skb)->l2cap; if (chan->max_tx != 0 && - bt_cb(skb)->control.retries > chan->max_tx) { + bt_cb(skb)->l2cap.retries > chan->max_tx) { BT_DBG("Retry limit exceeded (%d)", chan->max_tx); l2cap_send_disconn_req(chan, ECONNRESET); l2cap_seq_list_clear(&chan->retrans_list); @@ -2045,7 +2045,7 @@ static void l2cap_retransmit_all(struct l2cap_chan *chan, if (chan->unacked_frames) { skb_queue_walk(&chan->tx_q, skb) { - if (bt_cb(skb)->control.txseq == control->reqseq || + if (bt_cb(skb)->l2cap.txseq == control->reqseq || skb == chan->tx_send_head) break; } @@ -2055,7 +2055,7 @@ static void l2cap_retransmit_all(struct l2cap_chan *chan, break; l2cap_seq_list_append(&chan->retrans_list, - bt_cb(skb)->control.txseq); + bt_cb(skb)->l2cap.txseq); } l2cap_ertm_resend(chan); @@ -2267,8 +2267,8 @@ static struct sk_buff *l2cap_create_iframe_pdu(struct l2cap_chan *chan, return ERR_PTR(err); } - bt_cb(skb)->control.fcs = chan->fcs; - bt_cb(skb)->control.retries = 0; + bt_cb(skb)->l2cap.fcs = chan->fcs; + bt_cb(skb)->l2cap.retries = 0; return skb; } @@ -2321,7 +2321,7 @@ static int l2cap_segment_sdu(struct l2cap_chan *chan, return PTR_ERR(skb); } - bt_cb(skb)->control.sar = sar; + bt_cb(skb)->l2cap.sar = sar; __skb_queue_tail(seg_queue, skb); len -= pdu_len; @@ -2856,7 +2856,7 @@ static void l2cap_raw_recv(struct l2cap_conn *conn, struct sk_buff *skb) continue; /* Don't send frame to the channel it came from */ - if (bt_cb(skb)->chan == chan) + if (bt_cb(skb)->l2cap.chan == chan) continue; nskb = skb_clone(skb, GFP_KERNEL); @@ -5918,7 +5918,7 @@ static int l2cap_rx_queued_iframes(struct l2cap_chan *chan) skb_unlink(skb, &chan->srej_q); chan->buffer_seq = __next_seq(chan, chan->buffer_seq); - err = l2cap_reassemble_sdu(chan, skb, &bt_cb(skb)->control); + err = l2cap_reassemble_sdu(chan, skb, &bt_cb(skb)->l2cap); if (err) break; } @@ -5952,7 +5952,7 @@ static void l2cap_handle_srej(struct l2cap_chan *chan, return; } - if (chan->max_tx != 0 && bt_cb(skb)->control.retries >= chan->max_tx) { + if (chan->max_tx != 0 && bt_cb(skb)->l2cap.retries >= chan->max_tx) { BT_DBG("Retry limit exceeded (%d)", chan->max_tx); l2cap_send_disconn_req(chan, ECONNRESET); return; @@ -6005,7 +6005,7 @@ static void l2cap_handle_rej(struct l2cap_chan *chan, skb = l2cap_ertm_seq_in_queue(&chan->tx_q, control->reqseq); if (chan->max_tx && skb && - bt_cb(skb)->control.retries >= chan->max_tx) { + bt_cb(skb)->l2cap.retries >= chan->max_tx) { BT_DBG("Retry limit exceeded (%d)", chan->max_tx); l2cap_send_disconn_req(chan, ECONNRESET); return; @@ -6565,7 +6565,7 @@ static int l2cap_stream_rx(struct l2cap_chan *chan, struct l2cap_ctrl *control, static int l2cap_data_rcv(struct l2cap_chan *chan, struct sk_buff *skb) { - struct l2cap_ctrl *control = &bt_cb(skb)->control; + struct l2cap_ctrl *control = &bt_cb(skb)->l2cap; u16 len; u8 event; @@ -6864,8 +6864,8 @@ static void l2cap_conless_channel(struct l2cap_conn *conn, __le16 psm, goto drop; /* Store remote BD_ADDR and PSM for msg_name */ - bacpy(&bt_cb(skb)->bdaddr, &hcon->dst); - bt_cb(skb)->psm = psm; + bacpy(&bt_cb(skb)->l2cap.bdaddr, &hcon->dst); + bt_cb(skb)->l2cap.psm = psm; if (!chan->ops->recv(chan, skb)) { l2cap_chan_put(chan); diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c index 9070720eedc8..a7278f05eafb 100644 --- a/net/bluetooth/l2cap_sock.c +++ b/net/bluetooth/l2cap_sock.c @@ -1330,7 +1330,7 @@ static struct sk_buff *l2cap_sock_alloc_skb_cb(struct l2cap_chan *chan, skb->priority = sk->sk_priority; - bt_cb(skb)->chan = chan; + bt_cb(skb)->l2cap.chan = chan; return skb; } @@ -1444,8 +1444,8 @@ static void l2cap_skb_msg_name(struct sk_buff *skb, void *msg_name, memset(la, 0, sizeof(struct sockaddr_l2)); la->l2_family = AF_BLUETOOTH; - la->l2_psm = bt_cb(skb)->psm; - bacpy(&la->l2_bdaddr, &bt_cb(skb)->bdaddr); + la->l2_psm = bt_cb(skb)->l2cap.psm; + bacpy(&la->l2_bdaddr, &bt_cb(skb)->l2cap.bdaddr); *msg_namelen = sizeof(struct sockaddr_l2); } diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c index 1ec3f66b5a74..1910c5806974 100644 --- a/net/bluetooth/smp.c +++ b/net/bluetooth/smp.c @@ -3017,7 +3017,7 @@ static struct sk_buff *smp_alloc_skb_cb(struct l2cap_chan *chan, return ERR_PTR(-ENOMEM); skb->priority = HCI_PRIO_MAX; - bt_cb(skb)->chan = chan; + bt_cb(skb)->l2cap.chan = chan; return skb; } -- cgit v1.2.3 From db6e3e8d016823c6b0f773c70a69ce65807d8a44 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Mon, 30 Mar 2015 23:21:02 +0300 Subject: Bluetooth: Refactor HCI request variables into own struct In order to shrink the size of bt_skb_cb, this patch moves the HCI request related variables into their own req_ctrl struct. Additionall the L2CAP and HCI request structs are placed inside the same union since they will never be used at the same time for the same skb. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- include/net/bluetooth/bluetooth.h | 14 ++++++++++---- net/bluetooth/hci_core.c | 12 ++++++------ net/bluetooth/hci_event.c | 4 ++-- net/bluetooth/hci_request.c | 6 +++--- net/bluetooth/hci_sock.c | 2 +- 5 files changed, 22 insertions(+), 16 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/bluetooth.h b/include/net/bluetooth/bluetooth.h index d8367cc7c76e..eeaff4b5cb62 100644 --- a/include/net/bluetooth/bluetooth.h +++ b/include/net/bluetooth/bluetooth.h @@ -278,16 +278,22 @@ struct hci_dev; typedef void (*hci_req_complete_t)(struct hci_dev *hdev, u8 status, u16 opcode); +struct req_ctrl { + bool start; + u8 event; + hci_req_complete_t complete; +}; + struct bt_skb_cb { __u8 pkt_type; __u8 force_active; __u16 opcode; __u16 expect; __u8 incoming:1; - __u8 req_start:1; - u8 req_event; - hci_req_complete_t req_complete; - struct l2cap_ctrl l2cap; + union { + struct l2cap_ctrl l2cap; + struct req_ctrl req; + }; }; #define bt_cb(skb) ((struct bt_skb_cb *)((skb)->cb)) diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index e6bfeb7b4415..246d7eca5d29 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -3585,7 +3585,7 @@ int hci_send_cmd(struct hci_dev *hdev, __u16 opcode, __u32 plen, /* Stand-alone HCI commands must be flagged as * single-command requests. */ - bt_cb(skb)->req_start = 1; + bt_cb(skb)->req.start = true; skb_queue_tail(&hdev->cmd_q, skb); queue_work(hdev->workqueue, &hdev->cmd_work); @@ -4263,7 +4263,7 @@ static bool hci_req_is_complete(struct hci_dev *hdev) if (!skb) return true; - return bt_cb(skb)->req_start; + return bt_cb(skb)->req.start; } static void hci_resend_last(struct hci_dev *hdev) @@ -4323,14 +4323,14 @@ void hci_req_cmd_complete(struct hci_dev *hdev, u16 opcode, u8 status) * command queue (hdev->cmd_q). */ if (hdev->sent_cmd) { - req_complete = bt_cb(hdev->sent_cmd)->req_complete; + req_complete = bt_cb(hdev->sent_cmd)->req.complete; if (req_complete) { /* We must set the complete callback to NULL to * avoid calling the callback more than once if * this function gets called again. */ - bt_cb(hdev->sent_cmd)->req_complete = NULL; + bt_cb(hdev->sent_cmd)->req.complete = NULL; goto call_complete; } @@ -4339,12 +4339,12 @@ void hci_req_cmd_complete(struct hci_dev *hdev, u16 opcode, u8 status) /* Remove all pending commands belonging to this request */ spin_lock_irqsave(&hdev->cmd_q.lock, flags); while ((skb = __skb_dequeue(&hdev->cmd_q))) { - if (bt_cb(skb)->req_start) { + if (bt_cb(skb)->req.start) { __skb_queue_head(&hdev->cmd_q, skb); break; } - req_complete = bt_cb(skb)->req_complete; + req_complete = bt_cb(skb)->req.complete; kfree_skb(skb); } spin_unlock_irqrestore(&hdev->cmd_q.lock, flags); diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 20f34b861426..7c0f992602f5 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -3125,7 +3125,7 @@ static void hci_cmd_status_evt(struct hci_dev *hdev, struct sk_buff *skb) atomic_set(&hdev->cmd_cnt, 1); if (ev->status || - (hdev->sent_cmd && !bt_cb(hdev->sent_cmd)->req_event)) + (hdev->sent_cmd && !bt_cb(hdev->sent_cmd)->req.event)) hci_req_cmd_complete(hdev, opcode, ev->status); if (atomic_read(&hdev->cmd_cnt) && !skb_queue_empty(&hdev->cmd_q)) @@ -5049,7 +5049,7 @@ void hci_event_packet(struct hci_dev *hdev, struct sk_buff *skb) skb_pull(skb, HCI_EVENT_HDR_SIZE); - if (hdev->sent_cmd && bt_cb(hdev->sent_cmd)->req_event == event) { + if (hdev->sent_cmd && bt_cb(hdev->sent_cmd)->req.event == event) { struct hci_command_hdr *cmd_hdr = (void *) hdev->sent_cmd->data; u16 opcode = __le16_to_cpu(cmd_hdr->opcode); diff --git a/net/bluetooth/hci_request.c b/net/bluetooth/hci_request.c index 55e096d20a0f..7e17907effb3 100644 --- a/net/bluetooth/hci_request.c +++ b/net/bluetooth/hci_request.c @@ -55,7 +55,7 @@ int hci_req_run(struct hci_request *req, hci_req_complete_t complete) return -ENODATA; skb = skb_peek_tail(&req->cmd_q); - bt_cb(skb)->req_complete = complete; + bt_cb(skb)->req.complete = complete; spin_lock_irqsave(&hdev->cmd_q.lock, flags); skb_queue_splice_tail(&req->cmd_q, &hdev->cmd_q); @@ -116,9 +116,9 @@ void hci_req_add_ev(struct hci_request *req, u16 opcode, u32 plen, } if (skb_queue_empty(&req->cmd_q)) - bt_cb(skb)->req_start = 1; + bt_cb(skb)->req.start = true; - bt_cb(skb)->req_event = event; + bt_cb(skb)->req.event = event; skb_queue_tail(&req->cmd_q, skb); } diff --git a/net/bluetooth/hci_sock.c b/net/bluetooth/hci_sock.c index 85a44a7dc150..56f9edbf3d05 100644 --- a/net/bluetooth/hci_sock.c +++ b/net/bluetooth/hci_sock.c @@ -1164,7 +1164,7 @@ static int hci_sock_sendmsg(struct socket *sock, struct msghdr *msg, /* Stand-alone HCI commands must be flagged as * single-command requests. */ - bt_cb(skb)->req_start = 1; + bt_cb(skb)->req.start = true; skb_queue_tail(&hdev->cmd_q, skb); queue_work(hdev->workqueue, &hdev->cmd_work); -- cgit v1.2.3 From 045ecad0fdb23f34a2769fa9d49384a3130f7831 Mon Sep 17 00:00:00 2001 From: Tomasz Figa Date: Mon, 2 Mar 2015 14:17:29 +0100 Subject: clk: samsung: exynos3250: Add driver for CMU_ISP clock domain Add clock controller for CMU ISP clock domain on Exynos3250, providing clocks for FIMC-IS subsystem. [b.michalska: use samsung_cmu_register_one to register the provider; updated DT binding documentation] Signed-off-by: Tomasz Figa Signed-off-by: Chanwoo Choi Signed-off-by: Beata Michalska Acked-by: Kyungmin Park [s.nawrocki: added __init attribute which was missing in function exynos3250_cmu_platform_init() in function, which has been] Reported-by: kbuild test robot Signed-off-by: Sylwester Nawrocki Signed-off-by: Michael Turquette --- .../devicetree/bindings/clock/exynos3250-clock.txt | 8 + drivers/clk/samsung/clk-exynos3250.c | 163 +++++++++++++++++++++ include/dt-bindings/clock/exynos3250.h | 61 ++++++++ 3 files changed, 232 insertions(+) (limited to 'include') diff --git a/Documentation/devicetree/bindings/clock/exynos3250-clock.txt b/Documentation/devicetree/bindings/clock/exynos3250-clock.txt index f57d9dd9ea85..f1738b88c225 100644 --- a/Documentation/devicetree/bindings/clock/exynos3250-clock.txt +++ b/Documentation/devicetree/bindings/clock/exynos3250-clock.txt @@ -9,6 +9,8 @@ Required Properties: - "samsung,exynos3250-cmu" - controller compatible with Exynos3250 SoC. - "samsung,exynos3250-cmu-dmc" - controller compatible with Exynos3250 SoC for Dynamic Memory Controller domain. + - "samsung,exynos3250-cmu-isp" - ISP block clock controller compatible + with Exynos3250 SOC - reg: physical base address of the controller and length of memory mapped region. @@ -36,6 +38,12 @@ Example 1: Examples of clock controller nodes are listed below. #clock-cells = <1>; }; + cmu_isp: clock-controller@10048000 { + compatible = "samsung,exynos3250-cmu-isp"; + reg = <0x10048000 0x1000>; + #clock-cells = <1>; + }; + Example 2: UART controller node that consumes the clock generated by the clock controller. Refer to the standard clock bindings for information about 'clocks' and 'clock-names' property. diff --git a/drivers/clk/samsung/clk-exynos3250.c b/drivers/clk/samsung/clk-exynos3250.c index cc4c348d8a24..538de66a759e 100644 --- a/drivers/clk/samsung/clk-exynos3250.c +++ b/drivers/clk/samsung/clk-exynos3250.c @@ -894,3 +894,166 @@ static void __init exynos3250_cmu_dmc_init(struct device_node *np) } CLK_OF_DECLARE(exynos3250_cmu_dmc, "samsung,exynos3250-cmu-dmc", exynos3250_cmu_dmc_init); + + +/* + * CMU ISP + */ + +#define DIV_ISP0 0x300 +#define DIV_ISP1 0x304 +#define GATE_IP_ISP0 0x800 +#define GATE_IP_ISP1 0x804 +#define GATE_SCLK_ISP 0x900 + +static struct samsung_div_clock isp_div_clks[] __initdata = { + /* + * NOTE: Following table is sorted by register address in ascending + * order and then bitfield shift in descending order, as it is done + * in the User's Manual. When adding new entries, please make sure + * that the order is preserved, to avoid merge conflicts and make + * further work with defined data easier. + */ + /* DIV_ISP0 */ + DIV(CLK_DIV_ISP1, "div_isp1", "mout_aclk_266_sub", DIV_ISP0, 4, 3), + DIV(CLK_DIV_ISP0, "div_isp0", "mout_aclk_266_sub", DIV_ISP0, 0, 3), + + /* DIV_ISP1 */ + DIV(CLK_DIV_MCUISP1, "div_mcuisp1", "mout_aclk_400_mcuisp_sub", + DIV_ISP1, 8, 3), + DIV(CLK_DIV_MCUISP0, "div_mcuisp0", "mout_aclk_400_mcuisp_sub", + DIV_ISP1, 4, 3), + DIV(CLK_DIV_MPWM, "div_mpwm", "div_isp1", DIV_ISP1, 0, 3), +}; + +static struct samsung_gate_clock isp_gate_clks[] __initdata = { + /* + * NOTE: Following table is sorted by register address in ascending + * order and then bitfield shift in descending order, as it is done + * in the User's Manual. When adding new entries, please make sure + * that the order is preserved, to avoid merge conflicts and make + * further work with defined data easier. + */ + + /* GATE_IP_ISP0 */ + GATE(CLK_UART_ISP, "uart_isp", "uart_isp_top", + GATE_IP_ISP0, 31, CLK_IGNORE_UNUSED, 0), + GATE(CLK_WDT_ISP, "wdt_isp", "mout_aclk_266_sub", + GATE_IP_ISP0, 30, CLK_IGNORE_UNUSED, 0), + GATE(CLK_PWM_ISP, "pwm_isp", "mout_aclk_266_sub", + GATE_IP_ISP0, 28, CLK_IGNORE_UNUSED, 0), + GATE(CLK_I2C1_ISP, "i2c1_isp", "mout_aclk_266_sub", + GATE_IP_ISP0, 26, CLK_IGNORE_UNUSED, 0), + GATE(CLK_I2C0_ISP, "i2c0_isp", "mout_aclk_266_sub", + GATE_IP_ISP0, 25, CLK_IGNORE_UNUSED, 0), + GATE(CLK_MPWM_ISP, "mpwm_isp", "mout_aclk_266_sub", + GATE_IP_ISP0, 24, CLK_IGNORE_UNUSED, 0), + GATE(CLK_MCUCTL_ISP, "mcuctl_isp", "mout_aclk_266_sub", + GATE_IP_ISP0, 23, CLK_IGNORE_UNUSED, 0), + GATE(CLK_PPMUISPX, "ppmuispx", "mout_aclk_266_sub", + GATE_IP_ISP0, 21, CLK_IGNORE_UNUSED, 0), + GATE(CLK_PPMUISPMX, "ppmuispmx", "mout_aclk_266_sub", + GATE_IP_ISP0, 20, CLK_IGNORE_UNUSED, 0), + GATE(CLK_QE_LITE1, "qe_lite1", "mout_aclk_266_sub", + GATE_IP_ISP0, 18, CLK_IGNORE_UNUSED, 0), + GATE(CLK_QE_LITE0, "qe_lite0", "mout_aclk_266_sub", + GATE_IP_ISP0, 17, CLK_IGNORE_UNUSED, 0), + GATE(CLK_QE_FD, "qe_fd", "mout_aclk_266_sub", + GATE_IP_ISP0, 16, CLK_IGNORE_UNUSED, 0), + GATE(CLK_QE_DRC, "qe_drc", "mout_aclk_266_sub", + GATE_IP_ISP0, 15, CLK_IGNORE_UNUSED, 0), + GATE(CLK_QE_ISP, "qe_isp", "mout_aclk_266_sub", + GATE_IP_ISP0, 14, CLK_IGNORE_UNUSED, 0), + GATE(CLK_CSIS1, "csis1", "mout_aclk_266_sub", + GATE_IP_ISP0, 13, CLK_IGNORE_UNUSED, 0), + GATE(CLK_SMMU_LITE1, "smmu_lite1", "mout_aclk_266_sub", + GATE_IP_ISP0, 12, CLK_IGNORE_UNUSED, 0), + GATE(CLK_SMMU_LITE0, "smmu_lite0", "mout_aclk_266_sub", + GATE_IP_ISP0, 11, CLK_IGNORE_UNUSED, 0), + GATE(CLK_SMMU_FD, "smmu_fd", "mout_aclk_266_sub", + GATE_IP_ISP0, 10, CLK_IGNORE_UNUSED, 0), + GATE(CLK_SMMU_DRC, "smmu_drc", "mout_aclk_266_sub", + GATE_IP_ISP0, 9, CLK_IGNORE_UNUSED, 0), + GATE(CLK_SMMU_ISP, "smmu_isp", "mout_aclk_266_sub", + GATE_IP_ISP0, 8, CLK_IGNORE_UNUSED, 0), + GATE(CLK_GICISP, "gicisp", "mout_aclk_266_sub", + GATE_IP_ISP0, 7, CLK_IGNORE_UNUSED, 0), + GATE(CLK_CSIS0, "csis0", "mout_aclk_266_sub", + GATE_IP_ISP0, 6, CLK_IGNORE_UNUSED, 0), + GATE(CLK_MCUISP, "mcuisp", "mout_aclk_266_sub", + GATE_IP_ISP0, 5, CLK_IGNORE_UNUSED, 0), + GATE(CLK_LITE1, "lite1", "mout_aclk_266_sub", + GATE_IP_ISP0, 4, CLK_IGNORE_UNUSED, 0), + GATE(CLK_LITE0, "lite0", "mout_aclk_266_sub", + GATE_IP_ISP0, 3, CLK_IGNORE_UNUSED, 0), + GATE(CLK_FD, "fd", "mout_aclk_266_sub", + GATE_IP_ISP0, 2, CLK_IGNORE_UNUSED, 0), + GATE(CLK_DRC, "drc", "mout_aclk_266_sub", + GATE_IP_ISP0, 1, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ISP, "isp", "mout_aclk_266_sub", + GATE_IP_ISP0, 0, CLK_IGNORE_UNUSED, 0), + + /* GATE_IP_ISP1 */ + GATE(CLK_QE_ISPCX, "qe_ispcx", "uart_isp_top", + GATE_IP_ISP0, 21, CLK_IGNORE_UNUSED, 0), + GATE(CLK_QE_SCALERP, "qe_scalerp", "uart_isp_top", + GATE_IP_ISP0, 20, CLK_IGNORE_UNUSED, 0), + GATE(CLK_QE_SCALERC, "qe_scalerc", "uart_isp_top", + GATE_IP_ISP0, 19, CLK_IGNORE_UNUSED, 0), + GATE(CLK_SMMU_SCALERP, "smmu_scalerp", "uart_isp_top", + GATE_IP_ISP0, 18, CLK_IGNORE_UNUSED, 0), + GATE(CLK_SMMU_SCALERC, "smmu_scalerc", "uart_isp_top", + GATE_IP_ISP0, 17, CLK_IGNORE_UNUSED, 0), + GATE(CLK_SCALERP, "scalerp", "uart_isp_top", + GATE_IP_ISP0, 16, CLK_IGNORE_UNUSED, 0), + GATE(CLK_SCALERC, "scalerc", "uart_isp_top", + GATE_IP_ISP0, 15, CLK_IGNORE_UNUSED, 0), + GATE(CLK_SPI1_ISP, "spi1_isp", "uart_isp_top", + GATE_IP_ISP0, 13, CLK_IGNORE_UNUSED, 0), + GATE(CLK_SPI0_ISP, "spi0_isp", "uart_isp_top", + GATE_IP_ISP0, 12, CLK_IGNORE_UNUSED, 0), + GATE(CLK_SMMU_ISPCX, "smmu_ispcx", "uart_isp_top", + GATE_IP_ISP0, 4, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ASYNCAXIM, "asyncaxim", "uart_isp_top", + GATE_IP_ISP0, 0, CLK_IGNORE_UNUSED, 0), + + /* GATE_SCLK_ISP */ + GATE(CLK_SCLK_MPWM_ISP, "sclk_mpwm_isp", "div_mpwm", + GATE_SCLK_ISP, 0, CLK_IGNORE_UNUSED, 0), +}; + +static struct samsung_cmu_info isp_cmu_info __initdata = { + .div_clks = isp_div_clks, + .nr_div_clks = ARRAY_SIZE(isp_div_clks), + .gate_clks = isp_gate_clks, + .nr_gate_clks = ARRAY_SIZE(isp_gate_clks), + .nr_clk_ids = NR_CLKS_ISP, +}; + +static int __init exynos3250_cmu_isp_probe(struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + + samsung_cmu_register_one(np, &isp_cmu_info); + return 0; +} + +static const struct of_device_id exynos3250_cmu_isp_of_match[] = { + { .compatible = "samsung,exynos3250-cmu-isp", }, + { /* sentinel */ } +}; + +static struct platform_driver exynos3250_cmu_isp_driver = { + .driver = { + .name = "exynos3250-cmu-isp", + .of_match_table = exynos3250_cmu_isp_of_match, + }, +}; + +static int __init exynos3250_cmu_platform_init(void) +{ + return platform_driver_probe(&exynos3250_cmu_isp_driver, + exynos3250_cmu_isp_probe); +} +subsys_initcall(exynos3250_cmu_platform_init); + diff --git a/include/dt-bindings/clock/exynos3250.h b/include/dt-bindings/clock/exynos3250.h index 961b9c130ea9..aab088d30199 100644 --- a/include/dt-bindings/clock/exynos3250.h +++ b/include/dt-bindings/clock/exynos3250.h @@ -282,4 +282,65 @@ */ #define NR_CLKS_DMC 21 +/* + * CMU ISP + */ + +/* Dividers */ + +#define CLK_DIV_ISP1 1 +#define CLK_DIV_ISP0 2 +#define CLK_DIV_MCUISP1 3 +#define CLK_DIV_MCUISP0 4 +#define CLK_DIV_MPWM 5 + +/* Gates */ + +#define CLK_UART_ISP 8 +#define CLK_WDT_ISP 9 +#define CLK_PWM_ISP 10 +#define CLK_I2C1_ISP 11 +#define CLK_I2C0_ISP 12 +#define CLK_MPWM_ISP 13 +#define CLK_MCUCTL_ISP 14 +#define CLK_PPMUISPX 15 +#define CLK_PPMUISPMX 16 +#define CLK_QE_LITE1 17 +#define CLK_QE_LITE0 18 +#define CLK_QE_FD 19 +#define CLK_QE_DRC 20 +#define CLK_QE_ISP 21 +#define CLK_CSIS1 22 +#define CLK_SMMU_LITE1 23 +#define CLK_SMMU_LITE0 24 +#define CLK_SMMU_FD 25 +#define CLK_SMMU_DRC 26 +#define CLK_SMMU_ISP 27 +#define CLK_GICISP 28 +#define CLK_CSIS0 29 +#define CLK_MCUISP 30 +#define CLK_LITE1 31 +#define CLK_LITE0 32 +#define CLK_FD 33 +#define CLK_DRC 34 +#define CLK_ISP 35 +#define CLK_QE_ISPCX 36 +#define CLK_QE_SCALERP 37 +#define CLK_QE_SCALERC 38 +#define CLK_SMMU_SCALERP 39 +#define CLK_SMMU_SCALERC 40 +#define CLK_SCALERP 41 +#define CLK_SCALERC 42 +#define CLK_SPI1_ISP 43 +#define CLK_SPI0_ISP 44 +#define CLK_SMMU_ISPCX 45 +#define CLK_ASYNCAXIM 46 +#define CLK_SCLK_MPWM_ISP 47 + +/* + * Total number of clocks of CMU_ISP. + * NOTE: Must be equal to last clock ID increased by one. + */ +#define NR_CLKS_ISP 48 + #endif /* _DT_BINDINGS_CLOCK_SAMSUNG_EXYNOS3250_CLOCK_H */ -- cgit v1.2.3 From 0e6c9122a6ec96d19f1db61e9750287d86b6829c Mon Sep 17 00:00:00 2001 From: Wei Yang Date: Wed, 25 Mar 2015 16:23:44 +0800 Subject: PCI: Keep individual VF BAR size in struct pci_sriov Currently we don't store the individual VF BAR size. We calculate it when needed by dividing the PF's IOV resource size (which contains space for *all* the VFs) by total_VFs or by reading the BAR in the SR-IOV capability again. Keep the individual VF BAR size in struct pci_sriov.barsz[], add pci_iov_resource_size() to retrieve it, and use that instead of doing the division or reading the SR-IOV capability BAR. [bhelgaas: rename to "barsz[]", simplify barsz[] index computation, remove SR-IOV capability BAR sizing] Signed-off-by: Wei Yang Acked-by: Bjorn Helgaas Signed-off-by: Benjamin Herrenschmidt --- drivers/pci/iov.c | 39 ++++++++++++++++++++------------------- drivers/pci/pci.h | 1 + include/linux/pci.h | 3 +++ 3 files changed, 24 insertions(+), 19 deletions(-) (limited to 'include') diff --git a/drivers/pci/iov.c b/drivers/pci/iov.c index 05f9d97e4175..5bca0e1a2799 100644 --- a/drivers/pci/iov.c +++ b/drivers/pci/iov.c @@ -57,6 +57,14 @@ static void virtfn_remove_bus(struct pci_bus *physbus, struct pci_bus *virtbus) pci_remove_bus(virtbus); } +resource_size_t pci_iov_resource_size(struct pci_dev *dev, int resno) +{ + if (!dev->is_physfn) + return 0; + + return dev->sriov->barsz[resno - PCI_IOV_RESOURCES]; +} + static int virtfn_add(struct pci_dev *dev, int id, int reset) { int i; @@ -92,8 +100,7 @@ static int virtfn_add(struct pci_dev *dev, int id, int reset) continue; virtfn->resource[i].name = pci_name(virtfn); virtfn->resource[i].flags = res->flags; - size = resource_size(res); - do_div(size, iov->total_VFs); + size = pci_iov_resource_size(dev, i + PCI_IOV_RESOURCES); virtfn->resource[i].start = res->start + size * id; virtfn->resource[i].end = virtfn->resource[i].start + size - 1; rc = request_resource(res, &virtfn->resource[i]); @@ -311,7 +318,7 @@ static void sriov_disable(struct pci_dev *dev) static int sriov_init(struct pci_dev *dev, int pos) { - int i; + int i, bar64; int rc; int nres; u32 pgsz; @@ -360,29 +367,29 @@ found: pgsz &= ~(pgsz - 1); pci_write_config_dword(dev, pos + PCI_SRIOV_SYS_PGSIZE, pgsz); + iov = kzalloc(sizeof(*iov), GFP_KERNEL); + if (!iov) + return -ENOMEM; + nres = 0; for (i = 0; i < PCI_SRIOV_NUM_BARS; i++) { res = dev->resource + PCI_IOV_RESOURCES + i; - i += __pci_read_base(dev, pci_bar_unknown, res, - pos + PCI_SRIOV_BAR + i * 4); + bar64 = __pci_read_base(dev, pci_bar_unknown, res, + pos + PCI_SRIOV_BAR + i * 4); if (!res->flags) continue; if (resource_size(res) & (PAGE_SIZE - 1)) { rc = -EIO; goto failed; } + iov->barsz[i] = resource_size(res); res->end = res->start + resource_size(res) * total - 1; dev_info(&dev->dev, "VF(n) BAR%d space: %pR (contains BAR%d for %d VFs)\n", i, res, i, total); + i += bar64; nres++; } - iov = kzalloc(sizeof(*iov), GFP_KERNEL); - if (!iov) { - rc = -ENOMEM; - goto failed; - } - iov->pos = pos; iov->nres = nres; iov->ctrl = ctrl; @@ -414,6 +421,7 @@ failed: res->flags = 0; } + kfree(iov); return rc; } @@ -510,14 +518,7 @@ int pci_iov_resource_bar(struct pci_dev *dev, int resno) */ resource_size_t pci_sriov_resource_alignment(struct pci_dev *dev, int resno) { - struct resource tmp; - int reg = pci_iov_resource_bar(dev, resno); - - if (!reg) - return 0; - - __pci_read_base(dev, pci_bar_unknown, &tmp, reg); - return resource_alignment(&tmp); + return pci_iov_resource_size(dev, resno); } /** diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index 4091f82239cd..57329645dd01 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -247,6 +247,7 @@ struct pci_sriov { struct pci_dev *dev; /* lowest numbered PF */ struct pci_dev *self; /* this PF */ struct mutex lock; /* lock for VF bus */ + resource_size_t barsz[PCI_SRIOV_NUM_BARS]; /* VF BAR size */ }; #ifdef CONFIG_PCI_ATS diff --git a/include/linux/pci.h b/include/linux/pci.h index 211e9da8a7d7..15596582e575 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -1675,6 +1675,7 @@ int pci_num_vf(struct pci_dev *dev); int pci_vfs_assigned(struct pci_dev *dev); int pci_sriov_set_totalvfs(struct pci_dev *dev, u16 numvfs); int pci_sriov_get_totalvfs(struct pci_dev *dev); +resource_size_t pci_iov_resource_size(struct pci_dev *dev, int resno); #else static inline int pci_enable_sriov(struct pci_dev *dev, int nr_virtfn) { return -ENODEV; } @@ -1686,6 +1687,8 @@ static inline int pci_sriov_set_totalvfs(struct pci_dev *dev, u16 numvfs) { return 0; } static inline int pci_sriov_get_totalvfs(struct pci_dev *dev) { return 0; } +static inline resource_size_t pci_iov_resource_size(struct pci_dev *dev, int resno) +{ return 0; } #endif #if defined(CONFIG_HOTPLUG_PCI) || defined(CONFIG_HOTPLUG_PCI_MODULE) -- cgit v1.2.3 From b07579c0924eee1543eb6cd2c19544d15a4b5236 Mon Sep 17 00:00:00 2001 From: Wei Yang Date: Wed, 25 Mar 2015 16:23:48 +0800 Subject: PCI: Export pci_iov_virtfn_bus() and pci_iov_virtfn_devfn() On PowerNV, some resource reservation is needed for SR-IOV VFs that don't exist at the bootup stage. To do the match between resources and VFs, the code need to get the VF's BDF in advance. Rename virtfn_bus() and virtfn_devfn() to pci_iov_virtfn_bus() and pci_iov_virtfn_devfn() and export them. [bhelgaas: changelog, make "busnr" int] Signed-off-by: Wei Yang Acked-by: Bjorn Helgaas Signed-off-by: Benjamin Herrenschmidt --- drivers/pci/iov.c | 28 ++++++++++++++++------------ include/linux/pci.h | 11 +++++++++++ 2 files changed, 27 insertions(+), 12 deletions(-) (limited to 'include') diff --git a/drivers/pci/iov.c b/drivers/pci/iov.c index 2ae921f84bd3..5643a1011e23 100644 --- a/drivers/pci/iov.c +++ b/drivers/pci/iov.c @@ -19,16 +19,20 @@ #define VIRTFN_ID_LEN 16 -static inline u8 virtfn_bus(struct pci_dev *dev, int id) +int pci_iov_virtfn_bus(struct pci_dev *dev, int vf_id) { + if (!dev->is_physfn) + return -EINVAL; return dev->bus->number + ((dev->devfn + dev->sriov->offset + - dev->sriov->stride * id) >> 8); + dev->sriov->stride * vf_id) >> 8); } -static inline u8 virtfn_devfn(struct pci_dev *dev, int id) +int pci_iov_virtfn_devfn(struct pci_dev *dev, int vf_id) { + if (!dev->is_physfn) + return -EINVAL; return (dev->devfn + dev->sriov->offset + - dev->sriov->stride * id) & 0xff; + dev->sriov->stride * vf_id) & 0xff; } /* @@ -58,11 +62,11 @@ static inline u8 virtfn_max_buses(struct pci_dev *dev) struct pci_sriov *iov = dev->sriov; int nr_virtfn; u8 max = 0; - u8 busnr; + int busnr; for (nr_virtfn = 1; nr_virtfn <= iov->total_VFs; nr_virtfn++) { pci_iov_set_numvfs(dev, nr_virtfn); - busnr = virtfn_bus(dev, nr_virtfn - 1); + busnr = pci_iov_virtfn_bus(dev, nr_virtfn - 1); if (busnr > max) max = busnr; } @@ -116,7 +120,7 @@ static int virtfn_add(struct pci_dev *dev, int id, int reset) struct pci_bus *bus; mutex_lock(&iov->dev->sriov->lock); - bus = virtfn_add_bus(dev->bus, virtfn_bus(dev, id)); + bus = virtfn_add_bus(dev->bus, pci_iov_virtfn_bus(dev, id)); if (!bus) goto failed; @@ -124,7 +128,7 @@ static int virtfn_add(struct pci_dev *dev, int id, int reset) if (!virtfn) goto failed0; - virtfn->devfn = virtfn_devfn(dev, id); + virtfn->devfn = pci_iov_virtfn_devfn(dev, id); virtfn->vendor = dev->vendor; pci_read_config_word(dev, iov->pos + PCI_SRIOV_VF_DID, &virtfn->device); pci_setup_device(virtfn); @@ -186,8 +190,8 @@ static void virtfn_remove(struct pci_dev *dev, int id, int reset) struct pci_sriov *iov = dev->sriov; virtfn = pci_get_domain_bus_and_slot(pci_domain_nr(dev->bus), - virtfn_bus(dev, id), - virtfn_devfn(dev, id)); + pci_iov_virtfn_bus(dev, id), + pci_iov_virtfn_devfn(dev, id)); if (!virtfn) return; @@ -226,7 +230,7 @@ static int sriov_enable(struct pci_dev *dev, int nr_virtfn) struct pci_dev *pdev; struct pci_sriov *iov = dev->sriov; int bars = 0; - u8 bus; + int bus; if (!nr_virtfn) return 0; @@ -263,7 +267,7 @@ static int sriov_enable(struct pci_dev *dev, int nr_virtfn) iov->offset = offset; iov->stride = stride; - bus = virtfn_bus(dev, nr_virtfn - 1); + bus = pci_iov_virtfn_bus(dev, nr_virtfn - 1); if (bus > dev->bus->busn_res.end) { dev_err(&dev->dev, "can't enable %d VFs (bus %02x out of range of %pR)\n", nr_virtfn, bus, &dev->bus->busn_res); diff --git a/include/linux/pci.h b/include/linux/pci.h index 15596582e575..99ea94835fb6 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -1669,6 +1669,9 @@ int pci_ext_cfg_avail(void); void __iomem *pci_ioremap_bar(struct pci_dev *pdev, int bar); #ifdef CONFIG_PCI_IOV +int pci_iov_virtfn_bus(struct pci_dev *dev, int id); +int pci_iov_virtfn_devfn(struct pci_dev *dev, int id); + int pci_enable_sriov(struct pci_dev *dev, int nr_virtfn); void pci_disable_sriov(struct pci_dev *dev); int pci_num_vf(struct pci_dev *dev); @@ -1677,6 +1680,14 @@ int pci_sriov_set_totalvfs(struct pci_dev *dev, u16 numvfs); int pci_sriov_get_totalvfs(struct pci_dev *dev); resource_size_t pci_iov_resource_size(struct pci_dev *dev, int resno); #else +static inline int pci_iov_virtfn_bus(struct pci_dev *dev, int id) +{ + return -ENOSYS; +} +static inline int pci_iov_virtfn_devfn(struct pci_dev *dev, int id) +{ + return -ENOSYS; +} static inline int pci_enable_sriov(struct pci_dev *dev, int nr_virtfn) { return -ENODEV; } static inline void pci_disable_sriov(struct pci_dev *dev) { } -- cgit v1.2.3 From 978d2d68312326b715a5913aaab1eaf24fe99108 Mon Sep 17 00:00:00 2001 From: Wei Yang Date: Wed, 25 Mar 2015 16:23:50 +0800 Subject: PCI: Add pcibios_iov_resource_alignment() interface Per the SR-IOV spec r1.1, sec 3.3.14, the required alignment of a PF's IOV BAR is the size of an individual VF BAR, and the size consumed is the individual VF BAR size times NumVFs. The PowerNV platform has additional alignment requirements to help support its Partitionable Endpoint device isolation feature (see Documentation/powerpc/pci_iov_resource_on_powernv.txt). Add a pcibios_iov_resource_alignment() interface to allow platforms to request additional alignment. [bhelgaas: changelog, adapt to reworked pci_sriov_resource_alignment(), drop "align" parameter] Signed-off-by: Wei Yang Acked-by: Bjorn Helgaas Signed-off-by: Benjamin Herrenschmidt --- drivers/pci/iov.c | 8 +++++++- include/linux/pci.h | 1 + 2 files changed, 8 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/drivers/pci/iov.c b/drivers/pci/iov.c index 64c46925c62d..ee0ebff103a4 100644 --- a/drivers/pci/iov.c +++ b/drivers/pci/iov.c @@ -569,6 +569,12 @@ int pci_iov_resource_bar(struct pci_dev *dev, int resno) 4 * (resno - PCI_IOV_RESOURCES); } +resource_size_t __weak pcibios_iov_resource_alignment(struct pci_dev *dev, + int resno) +{ + return pci_iov_resource_size(dev, resno); +} + /** * pci_sriov_resource_alignment - get resource alignment for VF BAR * @dev: the PCI device @@ -581,7 +587,7 @@ int pci_iov_resource_bar(struct pci_dev *dev, int resno) */ resource_size_t pci_sriov_resource_alignment(struct pci_dev *dev, int resno) { - return pci_iov_resource_size(dev, resno); + return pcibios_iov_resource_alignment(dev, resno); } /** diff --git a/include/linux/pci.h b/include/linux/pci.h index 99ea94835fb6..4e1f17db1a81 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -1174,6 +1174,7 @@ unsigned char pci_bus_max_busnr(struct pci_bus *bus); void pci_setup_bridge(struct pci_bus *bus); resource_size_t pcibios_window_alignment(struct pci_bus *bus, unsigned long type); +resource_size_t pcibios_iov_resource_alignment(struct pci_dev *dev, int resno); #define PCI_VGA_STATE_CHANGE_BRIDGE (1 << 0) #define PCI_VGA_STATE_CHANGE_DECODES (1 << 1) -- cgit v1.2.3 From 3bbf7f4624856751aa4cf279a472bd14a8eb16fd Mon Sep 17 00:00:00 2001 From: Rasmus Villemoes Date: Tue, 31 Mar 2015 13:25:05 +1030 Subject: linux/cpumask.h: add typechecking to cpumask_test_cpu The Subtlety (1) referred to vanished with 6ba2ef7baac2 ("cpumask: Move deprecated functions to end of header."). That used to mention some suboptimal code generation by a, by now, rather ancient gcc. With gcc 4.7, I don't see any change in the generated code by making it a static inline, so let's add type checking and get rid of the ghost reference. Signed-off-by: Rasmus Villemoes Signed-off-by: Rusty Russell --- include/linux/cpumask.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/include/linux/cpumask.h b/include/linux/cpumask.h index 4ad2d3c8e21f..89558d0b56ac 100644 --- a/include/linux/cpumask.h +++ b/include/linux/cpumask.h @@ -290,11 +290,11 @@ static inline void cpumask_clear_cpu(int cpu, struct cpumask *dstp) * @cpumask: the cpumask pointer * * Returns 1 if @cpu is set in @cpumask, else returns 0 - * - * No static inline type checking - see Subtlety (1) above. */ -#define cpumask_test_cpu(cpu, cpumask) \ - test_bit(cpumask_check(cpu), cpumask_bits((cpumask))) +static inline int cpumask_test_cpu(int cpu, const struct cpumask *cpumask) +{ + return test_bit(cpumask_check(cpu), cpumask_bits((cpumask))); +} /** * cpumask_test_and_set_cpu - atomically test and set a cpu in a cpumask -- cgit v1.2.3 From 066626d5d5548d7ff63772a840b8d40a0d278825 Mon Sep 17 00:00:00 2001 From: Emil Velikov Date: Mon, 30 Mar 2015 17:10:36 +0000 Subject: drm: line wrap DRM_IOCTL_DEF* macros Improve the readability and keeps the lines shorter than 80 columns. Signed-off-by: Emil Velikov Signed-off-by: Daniel Vetter --- drivers/gpu/drm/drm_ioctl.c | 9 +++++++-- include/drm/drmP.h | 9 +++++++-- 2 files changed, 14 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/drm_ioctl.c b/drivers/gpu/drm/drm_ioctl.c index 1f257aecd2b2..266dcd6cdf3b 100644 --- a/drivers/gpu/drm/drm_ioctl.c +++ b/drivers/gpu/drm/drm_ioctl.c @@ -524,8 +524,13 @@ static int drm_ioctl_permit(u32 flags, struct drm_file *file_priv) return 0; } -#define DRM_IOCTL_DEF(ioctl, _func, _flags) \ - [DRM_IOCTL_NR(ioctl)] = {.cmd = ioctl, .func = _func, .flags = _flags, .name = #ioctl} +#define DRM_IOCTL_DEF(ioctl, _func, _flags) \ + [DRM_IOCTL_NR(ioctl)] = { \ + .cmd = ioctl, \ + .func = _func, \ + .flags = _flags, \ + .name = #ioctl \ + } /** Ioctl table */ static const struct drm_ioctl_desc drm_ioctls[] = { diff --git a/include/drm/drmP.h b/include/drm/drmP.h index 0d501ed20775..62c40777c009 100644 --- a/include/drm/drmP.h +++ b/include/drm/drmP.h @@ -261,8 +261,13 @@ struct drm_ioctl_desc { * ioctl, for use by drm_ioctl(). */ -#define DRM_IOCTL_DEF_DRV(ioctl, _func, _flags) \ - [DRM_IOCTL_NR(DRM_IOCTL_##ioctl) - DRM_COMMAND_BASE] = {.cmd = DRM_IOCTL_##ioctl, .func = _func, .flags = _flags, .name = #ioctl} +#define DRM_IOCTL_DEF_DRV(ioctl, _func, _flags) \ + [DRM_IOCTL_NR(DRM_IOCTL_##ioctl) - DRM_COMMAND_BASE] = { \ + .cmd = DRM_IOCTL_##ioctl, \ + .func = _func, \ + .flags = _flags, \ + .name = #ioctl \ + } /* Event queued up for userspace to read */ struct drm_pending_event { -- cgit v1.2.3 From 4b3a81a917a5ef21a4483d699cefd4d9fa35b841 Mon Sep 17 00:00:00 2001 From: Boris Brezillion Date: Wed, 28 Jan 2015 17:49:51 +0100 Subject: Add RGB444_1X12 and RGB565_1X16 media bus formats Add RGB444_1X12 and RGB565_1X16 format definitions and update the documentation. Signed-off-by: Boris Brezillon Acked-by: Mauro Carvalho Chehab Acked-by: Sakari Ailus Acked-by: Laurent Pinchart Acked-by: Hans Verkuil Signed-off-by: Philipp Zabel --- Documentation/DocBook/media/v4l/subdev-formats.xml | 40 ++++++++++++++++++++++ include/uapi/linux/media-bus-format.h | 4 ++- 2 files changed, 43 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/Documentation/DocBook/media/v4l/subdev-formats.xml b/Documentation/DocBook/media/v4l/subdev-formats.xml index c5ea868e3909..29fe60112f4c 100644 --- a/Documentation/DocBook/media/v4l/subdev-formats.xml +++ b/Documentation/DocBook/media/v4l/subdev-formats.xml @@ -192,6 +192,24 @@ see . + + MEDIA_BUS_FMT_RGB444_1X12 + 0x100e + + &dash-ent-20; + r3 + r2 + r1 + r0 + g3 + g2 + g1 + g0 + b3 + b2 + b1 + b0 + MEDIA_BUS_FMT_RGB444_2X8_PADHI_BE 0x1001 @@ -304,6 +322,28 @@ see . g4 g3 + + MEDIA_BUS_FMT_RGB565_1X16 + 0x100f + + &dash-ent-16; + r4 + r3 + r2 + r1 + r0 + g5 + g4 + g3 + g2 + g1 + g0 + b4 + b3 + b2 + b1 + b0 + MEDIA_BUS_FMT_BGR565_2X8_BE 0x1005 diff --git a/include/uapi/linux/media-bus-format.h b/include/uapi/linux/media-bus-format.h index 23b40908be30..37091c668f65 100644 --- a/include/uapi/linux/media-bus-format.h +++ b/include/uapi/linux/media-bus-format.h @@ -33,11 +33,13 @@ #define MEDIA_BUS_FMT_FIXED 0x0001 -/* RGB - next is 0x100e */ +/* RGB - next is 0x1010 */ +#define MEDIA_BUS_FMT_RGB444_1X12 0x100e #define MEDIA_BUS_FMT_RGB444_2X8_PADHI_BE 0x1001 #define MEDIA_BUS_FMT_RGB444_2X8_PADHI_LE 0x1002 #define MEDIA_BUS_FMT_RGB555_2X8_PADHI_BE 0x1003 #define MEDIA_BUS_FMT_RGB555_2X8_PADHI_LE 0x1004 +#define MEDIA_BUS_FMT_RGB565_1X16 0x100f #define MEDIA_BUS_FMT_BGR565_2X8_BE 0x1005 #define MEDIA_BUS_FMT_BGR565_2X8_LE 0x1006 #define MEDIA_BUS_FMT_RGB565_2X8_BE 0x1007 -- cgit v1.2.3 From b295c22978b86fc62019d12f4108b68b7e795610 Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Tue, 2 Dec 2014 17:49:04 +0100 Subject: Add LVDS RGB media bus formats This patch adds three new RGB media bus formats that describe 18-bit or 24-bit samples transferred over an LVDS bus with three or four differential data pairs, serialized into 7 time slots, using standard SPWG/PSWG/VESA or JEIDA data ordering. Signed-off-by: Philipp Zabel Acked-by: Sakari Ailus Acked-by: Hans Verkuil --- Documentation/DocBook/media/v4l/subdev-formats.xml | 255 +++++++++++++++++++++ include/uapi/linux/media-bus-format.h | 5 +- 2 files changed, 259 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/Documentation/DocBook/media/v4l/subdev-formats.xml b/Documentation/DocBook/media/v4l/subdev-formats.xml index 29fe60112f4c..18449b32f240 100644 --- a/Documentation/DocBook/media/v4l/subdev-formats.xml +++ b/Documentation/DocBook/media/v4l/subdev-formats.xml @@ -622,6 +622,261 @@ see . + + On LVDS buses, usually each sample is transferred serialized in + seven time slots per pixel clock, on three (18-bit) or four (24-bit) + differential data pairs at the same time. The remaining bits are used for + control signals as defined by SPWG/PSWG/VESA or JEIDA standards. + The 24-bit RGB format serialized in seven time slots on four lanes using + JEIDA defined bit mapping will be named + MEDIA_BUS_FMT_RGB888_1X7X4_JEIDA, for example. + + + + LVDS RGB formats + + + + + + + + + + + + + Identifier + Code + + + Data organization + + + + + Timeslot + Lane + 3 + 2 + 1 + 0 + + + + + MEDIA_BUS_FMT_RGB666_1X7X3_SPWG + 0x1010 + 0 + + - + d + b1 + g0 + + + + + 1 + + - + d + b0 + r5 + + + + + 2 + + - + d + g5 + r4 + + + + + 3 + + - + b5 + g4 + r3 + + + + + 4 + + - + b4 + g3 + r2 + + + + + 5 + + - + b3 + g2 + r1 + + + + + 6 + + - + b2 + g1 + r0 + + + MEDIA_BUS_FMT_RGB888_1X7X4_SPWG + 0x1011 + 0 + + d + d + b1 + g0 + + + + + 1 + + b7 + d + b0 + r5 + + + + + 2 + + b6 + d + g5 + r4 + + + + + 3 + + g7 + b5 + g4 + r3 + + + + + 4 + + g6 + b4 + g3 + r2 + + + + + 5 + + r7 + b3 + g2 + r1 + + + + + 6 + + r6 + b2 + g1 + r0 + + + MEDIA_BUS_FMT_RGB888_1X7X4_JEIDA + 0x1012 + 0 + + d + d + b3 + g2 + + + + + 1 + + b1 + d + b2 + r7 + + + + + 2 + + b0 + d + g7 + r6 + + + + + 3 + + g1 + b7 + g6 + r5 + + + + + 4 + + g0 + b6 + g5 + r4 + + + + + 5 + + r1 + b5 + g4 + r3 + + + + + 6 + + r0 + b4 + g3 + r2 + + + +
diff --git a/include/uapi/linux/media-bus-format.h b/include/uapi/linux/media-bus-format.h index 37091c668f65..3fb9cbbb603f 100644 --- a/include/uapi/linux/media-bus-format.h +++ b/include/uapi/linux/media-bus-format.h @@ -33,7 +33,7 @@ #define MEDIA_BUS_FMT_FIXED 0x0001 -/* RGB - next is 0x1010 */ +/* RGB - next is 0x1013 */ #define MEDIA_BUS_FMT_RGB444_1X12 0x100e #define MEDIA_BUS_FMT_RGB444_2X8_PADHI_BE 0x1001 #define MEDIA_BUS_FMT_RGB444_2X8_PADHI_LE 0x1002 @@ -45,9 +45,12 @@ #define MEDIA_BUS_FMT_RGB565_2X8_BE 0x1007 #define MEDIA_BUS_FMT_RGB565_2X8_LE 0x1008 #define MEDIA_BUS_FMT_RGB666_1X18 0x1009 +#define MEDIA_BUS_FMT_RGB666_1X7X3_SPWG 0x1010 #define MEDIA_BUS_FMT_RGB888_1X24 0x100a #define MEDIA_BUS_FMT_RGB888_2X12_BE 0x100b #define MEDIA_BUS_FMT_RGB888_2X12_LE 0x100c +#define MEDIA_BUS_FMT_RGB888_1X7X4_SPWG 0x1011 +#define MEDIA_BUS_FMT_RGB888_1X7X4_JEIDA 0x1012 #define MEDIA_BUS_FMT_ARGB8888_1X32 0x100d /* YUV (including grey) - next is 0x2024 */ -- cgit v1.2.3 From 08c38458be7efa36a1d2dffd40500448e46d29c5 Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Tue, 2 Dec 2014 15:45:25 +0100 Subject: Add BGR888_1X24 and GBR888_1X24 media bus formats This patch adds two more 24-bit RGB formats. BGR888 is more or less common, GBR888 is used on the internal connection between the IPU display interface and the TVE (VGA DAC) on i.MX53 SoCs. Signed-off-by: Philipp Zabel Acked-by: Laurent Pinchart Acked-by: Hans Verkuil --- Documentation/DocBook/media/v4l/subdev-formats.xml | 60 ++++++++++++++++++++++ include/uapi/linux/media-bus-format.h | 4 +- 2 files changed, 63 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/Documentation/DocBook/media/v4l/subdev-formats.xml b/Documentation/DocBook/media/v4l/subdev-formats.xml index 18449b32f240..805cbe1acab7 100644 --- a/Documentation/DocBook/media/v4l/subdev-formats.xml +++ b/Documentation/DocBook/media/v4l/subdev-formats.xml @@ -480,6 +480,66 @@ see . b1 b0 + + MEDIA_BUS_FMT_BGR888_1X24 + 0x1013 + + &dash-ent-8; + b7 + b6 + b5 + b4 + b3 + b2 + b1 + b0 + g7 + g6 + g5 + g4 + g3 + g2 + g1 + g0 + r7 + r6 + r5 + r4 + r3 + r2 + r1 + r0 + + + MEDIA_BUS_FMT_GBR888_1X24 + 0x1014 + + &dash-ent-8; + g7 + g6 + g5 + g4 + g3 + g2 + g1 + g0 + b7 + b6 + b5 + b4 + b3 + b2 + b1 + b0 + r7 + r6 + r5 + r4 + r3 + r2 + r1 + r0 + MEDIA_BUS_FMT_RGB888_1X24 0x100a diff --git a/include/uapi/linux/media-bus-format.h b/include/uapi/linux/media-bus-format.h index 3fb9cbbb603f..6f6942e8dae6 100644 --- a/include/uapi/linux/media-bus-format.h +++ b/include/uapi/linux/media-bus-format.h @@ -33,7 +33,7 @@ #define MEDIA_BUS_FMT_FIXED 0x0001 -/* RGB - next is 0x1013 */ +/* RGB - next is 0x1015 */ #define MEDIA_BUS_FMT_RGB444_1X12 0x100e #define MEDIA_BUS_FMT_RGB444_2X8_PADHI_BE 0x1001 #define MEDIA_BUS_FMT_RGB444_2X8_PADHI_LE 0x1002 @@ -46,6 +46,8 @@ #define MEDIA_BUS_FMT_RGB565_2X8_LE 0x1008 #define MEDIA_BUS_FMT_RGB666_1X18 0x1009 #define MEDIA_BUS_FMT_RGB666_1X7X3_SPWG 0x1010 +#define MEDIA_BUS_FMT_BGR888_1X24 0x1013 +#define MEDIA_BUS_FMT_GBR888_1X24 0x1014 #define MEDIA_BUS_FMT_RGB888_1X24 0x100a #define MEDIA_BUS_FMT_RGB888_2X12_BE 0x100b #define MEDIA_BUS_FMT_RGB888_2X12_LE 0x100c -- cgit v1.2.3 From 0fc63eb104d76e20654cd97eeb1bfd0f35bc3d3a Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Wed, 3 Dec 2014 11:01:54 +0100 Subject: Add YUV8_1X24 media bus format This patch adds the media bus format for a 24-bit bus format with three 8-bit YUV components. Signed-off-by: Philipp Zabel Acked-by: Laurent Pinchart Acked-by: Hans Verkuil --- Documentation/DocBook/media/v4l/subdev-formats.xml | 37 ++++++++++++++++++++++ include/uapi/linux/media-bus-format.h | 3 +- 2 files changed, 39 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/Documentation/DocBook/media/v4l/subdev-formats.xml b/Documentation/DocBook/media/v4l/subdev-formats.xml index 805cbe1acab7..8d1f62402f88 100644 --- a/Documentation/DocBook/media/v4l/subdev-formats.xml +++ b/Documentation/DocBook/media/v4l/subdev-formats.xml @@ -3015,6 +3015,43 @@ see . u1 u0 + + MEDIA_BUS_FMT_YUV8_1X24 + 0x2024 + + - + - + - + - + - + - + - + - + y7 + y6 + y5 + y4 + y3 + y2 + y1 + y0 + u7 + u6 + u5 + u4 + u3 + u2 + u1 + u0 + v7 + v6 + v5 + v4 + v3 + v2 + v1 + v0 + MEDIA_BUS_FMT_YUV10_1X30 0x2016 diff --git a/include/uapi/linux/media-bus-format.h b/include/uapi/linux/media-bus-format.h index 6f6942e8dae6..8dbf16cc5d1c 100644 --- a/include/uapi/linux/media-bus-format.h +++ b/include/uapi/linux/media-bus-format.h @@ -55,7 +55,7 @@ #define MEDIA_BUS_FMT_RGB888_1X7X4_JEIDA 0x1012 #define MEDIA_BUS_FMT_ARGB8888_1X32 0x100d -/* YUV (including grey) - next is 0x2024 */ +/* YUV (including grey) - next is 0x2025 */ #define MEDIA_BUS_FMT_Y8_1X8 0x2001 #define MEDIA_BUS_FMT_UV8_1X8 0x2015 #define MEDIA_BUS_FMT_UYVY8_1_5X8 0x2002 @@ -81,6 +81,7 @@ #define MEDIA_BUS_FMT_VYUY10_1X20 0x201b #define MEDIA_BUS_FMT_YUYV10_1X20 0x200d #define MEDIA_BUS_FMT_YVYU10_1X20 0x200e +#define MEDIA_BUS_FMT_YUV8_1X24 0x2024 #define MEDIA_BUS_FMT_YUV10_1X30 0x2016 #define MEDIA_BUS_FMT_AYUV8_1X32 0x2017 #define MEDIA_BUS_FMT_UYVY12_2X12 0x201c -- cgit v1.2.3 From 203508ef52e3fee93b71262928541ecea82c735d Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Tue, 2 Dec 2014 17:56:04 +0100 Subject: Add RGB666_1X24_CPADHI media bus format Commit 9e74d2926a28 ("staging: imx-drm: add LVDS666 support for parallel display") describes a 24-bit bus format where three 6-bit components each take the lower part of 8 bits with the two high bits zero padded. Add a component-wise padded media bus format RGB666_1X24_CPADHI to support this connection. Signed-off-by: Philipp Zabel Acked-by: Hans Verkuil Tested-by: Emil Renner Berthing --- Documentation/DocBook/media/v4l/subdev-formats.xml | 34 +++++++++++++++++++++- include/uapi/linux/media-bus-format.h | 3 +- 2 files changed, 35 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/Documentation/DocBook/media/v4l/subdev-formats.xml b/Documentation/DocBook/media/v4l/subdev-formats.xml index 8d1f62402f88..18b71aff48c9 100644 --- a/Documentation/DocBook/media/v4l/subdev-formats.xml +++ b/Documentation/DocBook/media/v4l/subdev-formats.xml @@ -91,7 +91,9 @@ see . For formats where the total number of bits per pixel is smaller than the number of bus samples per pixel times the bus width, a padding value stating if the bytes are padded in their most high order bits - (PADHI) or low order bits (PADLO). + (PADHI) or low order bits (PADLO). A "C" prefix is used for component-wise + padding in the most high order bits (CPADHI) or low order bits (CPADLO) + of each separate component. For formats where the number of bus samples per pixel is larger than 1, an endianness value stating if the pixel is transferred MSB first (BE) or LSB first (LE). @@ -480,6 +482,36 @@ see . b1 b0 + + MEDIA_BUS_FMT_RGB666_1X24_CPADHI + 0x1015 + + &dash-ent-8; + 0 + 0 + r5 + r4 + r3 + r2 + r1 + r0 + 0 + 0 + g5 + g4 + g3 + g2 + g1 + g0 + 0 + 0 + b5 + b4 + b3 + b2 + b1 + b0 + MEDIA_BUS_FMT_BGR888_1X24 0x1013 diff --git a/include/uapi/linux/media-bus-format.h b/include/uapi/linux/media-bus-format.h index 8dbf16cc5d1c..83ea46f4be51 100644 --- a/include/uapi/linux/media-bus-format.h +++ b/include/uapi/linux/media-bus-format.h @@ -33,7 +33,7 @@ #define MEDIA_BUS_FMT_FIXED 0x0001 -/* RGB - next is 0x1015 */ +/* RGB - next is 0x1016 */ #define MEDIA_BUS_FMT_RGB444_1X12 0x100e #define MEDIA_BUS_FMT_RGB444_2X8_PADHI_BE 0x1001 #define MEDIA_BUS_FMT_RGB444_2X8_PADHI_LE 0x1002 @@ -45,6 +45,7 @@ #define MEDIA_BUS_FMT_RGB565_2X8_BE 0x1007 #define MEDIA_BUS_FMT_RGB565_2X8_LE 0x1008 #define MEDIA_BUS_FMT_RGB666_1X18 0x1009 +#define MEDIA_BUS_FMT_RGB666_1X24_CPADHI 0x1015 #define MEDIA_BUS_FMT_RGB666_1X7X3_SPWG 0x1010 #define MEDIA_BUS_FMT_BGR888_1X24 0x1013 #define MEDIA_BUS_FMT_GBR888_1X24 0x1014 -- cgit v1.2.3 From 2872c8072aae65fa55cafea50e73d69d423df168 Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Mon, 2 Feb 2015 17:25:59 +0100 Subject: drm/imx: consolidate bus format variable names This patch consolidates the different interface_pix_fmt, pixel_fmt, pix_fmt, and pixfmt variables to a common name "bus_format" wherever they describe the pixel format on the bus between display controller and encoder hardware. At the same time, it renames imx_drm_panel_format to imx_drm_set_bus_format. Signed-off-by: Philipp Zabel Tested-by: Emil Renner Berthing --- drivers/gpu/drm/imx/dw_hdmi-imx.c | 2 +- drivers/gpu/drm/imx/imx-drm-core.c | 14 +++++++------- drivers/gpu/drm/imx/imx-drm.h | 10 +++++----- drivers/gpu/drm/imx/imx-ldb.c | 10 +++++----- drivers/gpu/drm/imx/imx-tve.c | 6 +++--- drivers/gpu/drm/imx/ipuv3-crtc.c | 13 +++++-------- drivers/gpu/drm/imx/parallel-display.c | 13 ++++++------- drivers/gpu/ipu-v3/ipu-dc.c | 4 ++-- include/video/imx-ipu-v3.h | 2 +- 9 files changed, 35 insertions(+), 39 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/imx/dw_hdmi-imx.c b/drivers/gpu/drm/imx/dw_hdmi-imx.c index 61ef987ec37b..1834ac8998cc 100644 --- a/drivers/gpu/drm/imx/dw_hdmi-imx.c +++ b/drivers/gpu/drm/imx/dw_hdmi-imx.c @@ -123,7 +123,7 @@ static void dw_hdmi_imx_encoder_commit(struct drm_encoder *encoder) static void dw_hdmi_imx_encoder_prepare(struct drm_encoder *encoder) { - imx_drm_panel_format(encoder, MEDIA_BUS_FMT_RGB888_1X24); + imx_drm_set_bus_format(encoder, MEDIA_BUS_FMT_RGB888_1X24); } static struct drm_encoder_helper_funcs dw_hdmi_imx_encoder_helper_funcs = { diff --git a/drivers/gpu/drm/imx/imx-drm-core.c b/drivers/gpu/drm/imx/imx-drm-core.c index a002f53aab0e..c6f2c4723b1b 100644 --- a/drivers/gpu/drm/imx/imx-drm-core.c +++ b/drivers/gpu/drm/imx/imx-drm-core.c @@ -103,8 +103,8 @@ static struct imx_drm_crtc *imx_drm_find_crtc(struct drm_crtc *crtc) return NULL; } -int imx_drm_panel_format_pins(struct drm_encoder *encoder, - u32 interface_pix_fmt, int hsync_pin, int vsync_pin) +int imx_drm_set_bus_format_pins(struct drm_encoder *encoder, u32 bus_format, + int hsync_pin, int vsync_pin) { struct imx_drm_crtc_helper_funcs *helper; struct imx_drm_crtc *imx_crtc; @@ -116,16 +116,16 @@ int imx_drm_panel_format_pins(struct drm_encoder *encoder, helper = &imx_crtc->imx_drm_helper_funcs; if (helper->set_interface_pix_fmt) return helper->set_interface_pix_fmt(encoder->crtc, - interface_pix_fmt, hsync_pin, vsync_pin); + bus_format, hsync_pin, vsync_pin); return 0; } -EXPORT_SYMBOL_GPL(imx_drm_panel_format_pins); +EXPORT_SYMBOL_GPL(imx_drm_set_bus_format_pins); -int imx_drm_panel_format(struct drm_encoder *encoder, u32 interface_pix_fmt) +int imx_drm_set_bus_format(struct drm_encoder *encoder, u32 bus_format) { - return imx_drm_panel_format_pins(encoder, interface_pix_fmt, 2, 3); + return imx_drm_set_bus_format_pins(encoder, bus_format, 2, 3); } -EXPORT_SYMBOL_GPL(imx_drm_panel_format); +EXPORT_SYMBOL_GPL(imx_drm_set_bus_format); int imx_drm_crtc_vblank_get(struct imx_drm_crtc *imx_drm_crtc) { diff --git a/drivers/gpu/drm/imx/imx-drm.h b/drivers/gpu/drm/imx/imx-drm.h index 3c559ccd6af0..28e776d8d9d2 100644 --- a/drivers/gpu/drm/imx/imx-drm.h +++ b/drivers/gpu/drm/imx/imx-drm.h @@ -18,7 +18,7 @@ struct imx_drm_crtc_helper_funcs { int (*enable_vblank)(struct drm_crtc *crtc); void (*disable_vblank)(struct drm_crtc *crtc); int (*set_interface_pix_fmt)(struct drm_crtc *crtc, - u32 pix_fmt, int hsync_pin, int vsync_pin); + u32 bus_format, int hsync_pin, int vsync_pin); const struct drm_crtc_helper_funcs *crtc_helper_funcs; const struct drm_crtc_funcs *crtc_funcs; }; @@ -40,10 +40,10 @@ void imx_drm_mode_config_init(struct drm_device *drm); struct drm_gem_cma_object *imx_drm_fb_get_obj(struct drm_framebuffer *fb); -int imx_drm_panel_format_pins(struct drm_encoder *encoder, - u32 interface_pix_fmt, int hsync_pin, int vsync_pin); -int imx_drm_panel_format(struct drm_encoder *encoder, - u32 interface_pix_fmt); +int imx_drm_set_bus_format_pins(struct drm_encoder *encoder, + u32 bus_format, int hsync_pin, int vsync_pin); +int imx_drm_set_bus_format(struct drm_encoder *encoder, + u32 bus_format); int imx_drm_encoder_get_mux_id(struct device_node *node, struct drm_encoder *encoder); diff --git a/drivers/gpu/drm/imx/imx-ldb.c b/drivers/gpu/drm/imx/imx-ldb.c index f9ec17a43458..cd062b11a102 100644 --- a/drivers/gpu/drm/imx/imx-ldb.c +++ b/drivers/gpu/drm/imx/imx-ldb.c @@ -163,24 +163,24 @@ static void imx_ldb_encoder_prepare(struct drm_encoder *encoder) { struct imx_ldb_channel *imx_ldb_ch = enc_to_imx_ldb_ch(encoder); struct imx_ldb *ldb = imx_ldb_ch->ldb; - u32 pixel_fmt; + u32 bus_format; switch (imx_ldb_ch->chno) { case 0: - pixel_fmt = (ldb->ldb_ctrl & LDB_DATA_WIDTH_CH0_24) ? + bus_format = (ldb->ldb_ctrl & LDB_DATA_WIDTH_CH0_24) ? MEDIA_BUS_FMT_RGB888_1X24 : MEDIA_BUS_FMT_RGB666_1X18; break; case 1: - pixel_fmt = (ldb->ldb_ctrl & LDB_DATA_WIDTH_CH1_24) ? + bus_format = (ldb->ldb_ctrl & LDB_DATA_WIDTH_CH1_24) ? MEDIA_BUS_FMT_RGB888_1X24 : MEDIA_BUS_FMT_RGB666_1X18; break; default: dev_err(ldb->dev, "unable to config di%d panel format\n", imx_ldb_ch->chno); - pixel_fmt = MEDIA_BUS_FMT_RGB888_1X24; + bus_format = MEDIA_BUS_FMT_RGB888_1X24; } - imx_drm_panel_format(encoder, pixel_fmt); + imx_drm_set_bus_format(encoder, bus_format); } static void imx_ldb_encoder_commit(struct drm_encoder *encoder) diff --git a/drivers/gpu/drm/imx/imx-tve.c b/drivers/gpu/drm/imx/imx-tve.c index bcdcb1b8f0ba..214eceefc981 100644 --- a/drivers/gpu/drm/imx/imx-tve.c +++ b/drivers/gpu/drm/imx/imx-tve.c @@ -301,11 +301,11 @@ static void imx_tve_encoder_prepare(struct drm_encoder *encoder) switch (tve->mode) { case TVE_MODE_VGA: - imx_drm_panel_format_pins(encoder, MEDIA_BUS_FMT_YUV8_1X24, - tve->hsync_pin, tve->vsync_pin); + imx_drm_set_bus_format_pins(encoder, MEDIA_BUS_FMT_YUV8_1X24, + tve->hsync_pin, tve->vsync_pin); break; case TVE_MODE_TVOUT: - imx_drm_panel_format(encoder, MEDIA_BUS_FMT_YUV8_1X24); + imx_drm_set_bus_format(encoder, MEDIA_BUS_FMT_YUV8_1X24); break; } } diff --git a/drivers/gpu/drm/imx/ipuv3-crtc.c b/drivers/gpu/drm/imx/ipuv3-crtc.c index 4a393f609220..7bc8301fafff 100644 --- a/drivers/gpu/drm/imx/ipuv3-crtc.c +++ b/drivers/gpu/drm/imx/ipuv3-crtc.c @@ -45,7 +45,7 @@ struct ipu_crtc { struct drm_pending_vblank_event *page_flip_event; struct drm_framebuffer *newfb; int irq; - u32 interface_pix_fmt; + u32 bus_format; int di_hsync_pin; int di_vsync_pin; }; @@ -145,7 +145,6 @@ static int ipu_crtc_mode_set(struct drm_crtc *crtc, struct ipu_crtc *ipu_crtc = to_ipu_crtc(crtc); struct ipu_di_signal_cfg sig_cfg = {}; unsigned long encoder_types = 0; - u32 out_pixel_fmt; int ret; dev_dbg(ipu_crtc->dev, "%s: mode->hdisplay: %d\n", __func__, @@ -173,11 +172,9 @@ static int ipu_crtc_mode_set(struct drm_crtc *crtc, else sig_cfg.clkflags = 0; - out_pixel_fmt = ipu_crtc->interface_pix_fmt; - sig_cfg.enable_pol = 1; sig_cfg.clk_pol = 0; - sig_cfg.pixel_fmt = out_pixel_fmt; + sig_cfg.bus_format = ipu_crtc->bus_format; sig_cfg.v_to_h_sync = 0; sig_cfg.hsync_pin = ipu_crtc->di_hsync_pin; sig_cfg.vsync_pin = ipu_crtc->di_vsync_pin; @@ -186,7 +183,7 @@ static int ipu_crtc_mode_set(struct drm_crtc *crtc, ret = ipu_dc_init_sync(ipu_crtc->dc, ipu_crtc->di, mode->flags & DRM_MODE_FLAG_INTERLACE, - out_pixel_fmt, mode->hdisplay); + ipu_crtc->bus_format, mode->hdisplay); if (ret) { dev_err(ipu_crtc->dev, "initializing display controller failed with %d\n", @@ -294,11 +291,11 @@ static void ipu_disable_vblank(struct drm_crtc *crtc) } static int ipu_set_interface_pix_fmt(struct drm_crtc *crtc, - u32 pixfmt, int hsync_pin, int vsync_pin) + u32 bus_format, int hsync_pin, int vsync_pin) { struct ipu_crtc *ipu_crtc = to_ipu_crtc(crtc); - ipu_crtc->interface_pix_fmt = pixfmt; + ipu_crtc->bus_format = bus_format; ipu_crtc->di_hsync_pin = hsync_pin; ipu_crtc->di_vsync_pin = vsync_pin; diff --git a/drivers/gpu/drm/imx/parallel-display.c b/drivers/gpu/drm/imx/parallel-display.c index e645fe18c1a7..74a9ce40ddc4 100644 --- a/drivers/gpu/drm/imx/parallel-display.c +++ b/drivers/gpu/drm/imx/parallel-display.c @@ -33,7 +33,7 @@ struct imx_parallel_display { struct device *dev; void *edid; int edid_len; - u32 interface_pix_fmt; + u32 bus_format; int mode_valid; struct drm_display_mode mode; struct drm_panel *panel; @@ -118,7 +118,7 @@ static void imx_pd_encoder_prepare(struct drm_encoder *encoder) { struct imx_parallel_display *imxpd = enc_to_imxpd(encoder); - imx_drm_panel_format(encoder, imxpd->interface_pix_fmt); + imx_drm_set_bus_format(encoder, imxpd->bus_format); } static void imx_pd_encoder_commit(struct drm_encoder *encoder) @@ -225,14 +225,13 @@ static int imx_pd_bind(struct device *dev, struct device *master, void *data) ret = of_property_read_string(np, "interface-pix-fmt", &fmt); if (!ret) { if (!strcmp(fmt, "rgb24")) - imxpd->interface_pix_fmt = MEDIA_BUS_FMT_RGB888_1X24; + imxpd->bus_format = MEDIA_BUS_FMT_RGB888_1X24; else if (!strcmp(fmt, "rgb565")) - imxpd->interface_pix_fmt = MEDIA_BUS_FMT_RGB565_1X16; + imxpd->bus_format = MEDIA_BUS_FMT_RGB565_1X16; else if (!strcmp(fmt, "bgr666")) - imxpd->interface_pix_fmt = MEDIA_BUS_FMT_RGB666_1X18; + imxpd->bus_format = MEDIA_BUS_FMT_RGB666_1X18; else if (!strcmp(fmt, "lvds666")) - imxpd->interface_pix_fmt = - MEDIA_BUS_FMT_RGB666_1X24_CPADHI; + imxpd->bus_format = MEDIA_BUS_FMT_RGB666_1X24_CPADHI; } panel_node = of_parse_phandle(np, "fsl,panel", 0); diff --git a/drivers/gpu/ipu-v3/ipu-dc.c b/drivers/gpu/ipu-v3/ipu-dc.c index 651c20cfe7e1..9ef2e1f54ca4 100644 --- a/drivers/gpu/ipu-v3/ipu-dc.c +++ b/drivers/gpu/ipu-v3/ipu-dc.c @@ -168,7 +168,7 @@ static int ipu_bus_format_to_map(u32 fmt) } int ipu_dc_init_sync(struct ipu_dc *dc, struct ipu_di *di, bool interlaced, - u32 pixel_fmt, u32 width) + u32 bus_format, u32 width) { struct ipu_dc_priv *priv = dc->priv; u32 reg = 0; @@ -176,7 +176,7 @@ int ipu_dc_init_sync(struct ipu_dc *dc, struct ipu_di *di, bool interlaced, dc->di = ipu_di_get_num(di); - map = ipu_bus_format_to_map(pixel_fmt); + map = ipu_bus_format_to_map(bus_format); if (map < 0) { dev_dbg(priv->dev, "IPU_DISP: No MAP\n"); return map; diff --git a/include/video/imx-ipu-v3.h b/include/video/imx-ipu-v3.h index 73390c120cad..85dedca3dcfb 100644 --- a/include/video/imx-ipu-v3.h +++ b/include/video/imx-ipu-v3.h @@ -39,7 +39,7 @@ struct ipu_di_signal_cfg { struct videomode mode; - u32 pixel_fmt; + u32 bus_format; u32 v_to_h_sync; #define IPU_DI_CLKMODE_SYNC (1 << 0) -- cgit v1.2.3 From ea3c023ebf7130d72f9f00a3992c22176ef6703b Mon Sep 17 00:00:00 2001 From: James Hogan Date: Tue, 27 Jan 2015 21:45:51 +0000 Subject: IRQCHIP: mips-gic: Add missing definitions for FDC IRQ Add missing VPE_PEND, VPE_RMASK and VPE_SMASK definitions for the local FDC interrupt. These local interrupt definitions aren't directly used, but if they exist they should be complete. Signed-off-by: James Hogan Cc: Andrew Bresticker Cc: Thomas Gleixner Cc: Jason Cooper Cc: linux-mips@linux-mips.org Reviewed-by: Andrew Bresticker Cc: linux-kernel@vger.kernel.org Patchwork: https://patchwork.linux-mips.org/patch/9127/ Signed-off-by: Ralf Baechle --- include/linux/irqchip/mips-gic.h | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'include') diff --git a/include/linux/irqchip/mips-gic.h b/include/linux/irqchip/mips-gic.h index e6a6aac451db..2e79b4bb2d75 100644 --- a/include/linux/irqchip/mips-gic.h +++ b/include/linux/irqchip/mips-gic.h @@ -165,6 +165,8 @@ #define GIC_VPE_PEND_SWINT0_MSK (MSK(1) << GIC_VPE_PEND_SWINT0_SHF) #define GIC_VPE_PEND_SWINT1_SHF 5 #define GIC_VPE_PEND_SWINT1_MSK (MSK(1) << GIC_VPE_PEND_SWINT1_SHF) +#define GIC_VPE_PEND_FDC_SHF 6 +#define GIC_VPE_PEND_FDC_MSK (MSK(1) << GIC_VPE_PEND_FDC_SHF) /* GIC_VPE_RMASK Masks */ #define GIC_VPE_RMASK_WD_SHF 0 @@ -179,6 +181,8 @@ #define GIC_VPE_RMASK_SWINT0_MSK (MSK(1) << GIC_VPE_RMASK_SWINT0_SHF) #define GIC_VPE_RMASK_SWINT1_SHF 5 #define GIC_VPE_RMASK_SWINT1_MSK (MSK(1) << GIC_VPE_RMASK_SWINT1_SHF) +#define GIC_VPE_RMASK_FDC_SHF 6 +#define GIC_VPE_RMASK_FDC_MSK (MSK(1) << GIC_VPE_RMASK_FDC_SHF) /* GIC_VPE_SMASK Masks */ #define GIC_VPE_SMASK_WD_SHF 0 @@ -193,6 +197,8 @@ #define GIC_VPE_SMASK_SWINT0_MSK (MSK(1) << GIC_VPE_SMASK_SWINT0_SHF) #define GIC_VPE_SMASK_SWINT1_SHF 5 #define GIC_VPE_SMASK_SWINT1_MSK (MSK(1) << GIC_VPE_SMASK_SWINT1_SHF) +#define GIC_VPE_SMASK_FDC_SHF 6 +#define GIC_VPE_SMASK_FDC_MSK (MSK(1) << GIC_VPE_SMASK_FDC_SHF) /* GIC nomenclature for Core Interrupt Pins. */ #define GIC_CPU_INT0 0 /* Core Interrupt 2 */ -- cgit v1.2.3 From 8286ae03308c6f97f346f9f8cb9174b04969add5 Mon Sep 17 00:00:00 2001 From: James Hogan Date: Wed, 25 Mar 2015 15:39:50 +0000 Subject: MIPS: Add CDMM bus support Add MIPS Common Device Memory Map (CDMM) support in the form of a bus in the standard Linux device model. Each device attached via CDMM is discoverable via an 8-bit type identifier and may contain a number of blocks of memory mapped registers in the CDMM region. IRQs are expected to be handled separately. Due to the per-cpu (per-VPE for MT cores) nature of the CDMM devices, all the driver callbacks take place from workqueues which are run on the right CPU for the device in question, so that the driver doesn't need to be as concerned about which CPU it is running on. Callbacks also exist for when CPUs are taken offline, so that any per-CPU resources used by the driver can be disabled so they don't get forcefully migrated. CDMM devices are created as children of the CPU device they are attached to. Any existing CDMM configuration by the bootloader will be inherited, however platforms wishing to enable CDMM should implement the weak mips_cdmm_phys_base() function (see asm/cdmm.h) so that the bus driver knows where it should put the CDMM region in the physical address space if the bootloader hasn't already enabled it. A mips_cdmm_early_probe() function is also provided to allow early boot or particularly low level code to set up the CDMM region and probe for a specific device type, for example early console or KGDB IO drivers for the EJTAG Fast Debug Channel (FDC) CDMM device. Signed-off-by: James Hogan Cc: Greg Kroah-Hartman Cc: linux-mips@linux-mips.org Cc: linux-kernel@vger.kernel.org Patchwork: https://patchwork.linux-mips.org/patch/9599/ Signed-off-by: Ralf Baechle --- arch/mips/include/asm/cdmm.h | 87 +++++ drivers/bus/Kconfig | 13 + drivers/bus/Makefile | 1 + drivers/bus/mips_cdmm.c | 716 ++++++++++++++++++++++++++++++++++++++ include/linux/mod_devicetable.h | 8 + scripts/mod/devicetable-offsets.c | 3 + scripts/mod/file2alias.c | 16 + 7 files changed, 844 insertions(+) create mode 100644 arch/mips/include/asm/cdmm.h create mode 100644 drivers/bus/mips_cdmm.c (limited to 'include') diff --git a/arch/mips/include/asm/cdmm.h b/arch/mips/include/asm/cdmm.h new file mode 100644 index 000000000000..b7d520f28d30 --- /dev/null +++ b/arch/mips/include/asm/cdmm.h @@ -0,0 +1,87 @@ +/* + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + * + * Copyright (C) 2014 Imagination Technologies Ltd. + */ +#ifndef __ASM_CDMM_H +#define __ASM_CDMM_H + +#include +#include + +/** + * struct mips_cdmm_device - Represents a single device on a CDMM bus. + * @dev: Driver model device object. + * @cpu: CPU which can access this device. + * @res: MMIO resource. + * @type: Device type identifier. + * @rev: Device revision number. + */ +struct mips_cdmm_device { + struct device dev; + unsigned int cpu; + struct resource res; + unsigned int type; + unsigned int rev; +}; + +/** + * struct mips_cdmm_driver - Represents a driver for a CDMM device. + * @drv: Driver model driver object. + * @probe Callback for probing newly discovered devices. + * @remove: Callback to remove the device. + * @shutdown: Callback on system shutdown. + * @cpu_down: Callback when the parent CPU is going down. + * Any CPU pinned threads/timers should be disabled. + * @cpu_up: Callback when the parent CPU is coming back up again. + * CPU pinned threads/timers can be restarted. + * @id_table: Table for CDMM IDs to match against. + */ +struct mips_cdmm_driver { + struct device_driver drv; + int (*probe)(struct mips_cdmm_device *); + int (*remove)(struct mips_cdmm_device *); + void (*shutdown)(struct mips_cdmm_device *); + int (*cpu_down)(struct mips_cdmm_device *); + int (*cpu_up)(struct mips_cdmm_device *); + const struct mips_cdmm_device_id *id_table; +}; + +/** + * mips_cdmm_phys_base() - Choose a physical base address for CDMM region. + * + * Picking a suitable physical address at which to map the CDMM region is + * platform specific, so this weak function can be defined by platform code to + * pick a suitable value if none is configured by the bootloader. + * + * This address must be 32kB aligned, and the region occupies a maximum of 32kB + * of physical address space which must not be used for anything else. + * + * Returns: Physical base address for CDMM region, or 0 on failure. + */ +phys_addr_t __weak mips_cdmm_phys_base(void); + +extern struct bus_type mips_cdmm_bustype; +void __iomem *mips_cdmm_early_probe(unsigned int dev_type); + +#define to_mips_cdmm_device(d) container_of(d, struct mips_cdmm_device, dev) + +#define mips_cdmm_get_drvdata(d) dev_get_drvdata(&d->dev) +#define mips_cdmm_set_drvdata(d, p) dev_set_drvdata(&d->dev, p) + +int mips_cdmm_driver_register(struct mips_cdmm_driver *); +void mips_cdmm_driver_unregister(struct mips_cdmm_driver *); + +/* + * module_mips_cdmm_driver() - Helper macro for drivers that don't do + * anything special in module init/exit. This eliminates a lot of + * boilerplate. Each module may only use this macro once, and + * calling it replaces module_init() and module_exit() + */ +#define module_mips_cdmm_driver(__mips_cdmm_driver) \ + module_driver(__mips_cdmm_driver, mips_cdmm_driver_register, \ + mips_cdmm_driver_unregister) + +#endif /* __ASM_CDMM_H */ diff --git a/drivers/bus/Kconfig b/drivers/bus/Kconfig index b99729e36860..cbddbaddb347 100644 --- a/drivers/bus/Kconfig +++ b/drivers/bus/Kconfig @@ -20,6 +20,19 @@ config IMX_WEIM The WEIM(Wireless External Interface Module) works like a bus. You can attach many different devices on it, such as NOR, onenand. +config MIPS_CDMM + bool "MIPS Common Device Memory Map (CDMM) Driver" + depends on CPU_MIPSR2 + help + Driver needed for the MIPS Common Device Memory Map bus in MIPS + cores. This bus is for per-CPU tightly coupled devices such as the + Fast Debug Channel (FDC). + + For this to work, either your bootloader needs to enable the CDMM + region at an unused physical address on the boot CPU, or else your + platform code needs to implement mips_cdmm_phys_base() (see + asm/cdmm.h). + config MVEBU_MBUS bool depends on PLAT_ORION diff --git a/drivers/bus/Makefile b/drivers/bus/Makefile index 2973c18cbcc2..807dd17ef2f8 100644 --- a/drivers/bus/Makefile +++ b/drivers/bus/Makefile @@ -4,6 +4,7 @@ obj-$(CONFIG_BRCMSTB_GISB_ARB) += brcmstb_gisb.o obj-$(CONFIG_IMX_WEIM) += imx-weim.o +obj-$(CONFIG_MIPS_CDMM) += mips_cdmm.o obj-$(CONFIG_MVEBU_MBUS) += mvebu-mbus.o obj-$(CONFIG_OMAP_OCP2SCP) += omap-ocp2scp.o diff --git a/drivers/bus/mips_cdmm.c b/drivers/bus/mips_cdmm.c new file mode 100644 index 000000000000..5bd792c68f9b --- /dev/null +++ b/drivers/bus/mips_cdmm.c @@ -0,0 +1,716 @@ +/* + * Bus driver for MIPS Common Device Memory Map (CDMM). + * + * Copyright (C) 2014-2015 Imagination Technologies Ltd. + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Access control and status register fields */ +#define CDMM_ACSR_DEVTYPE_SHIFT 24 +#define CDMM_ACSR_DEVTYPE (255ul << CDMM_ACSR_DEVTYPE_SHIFT) +#define CDMM_ACSR_DEVSIZE_SHIFT 16 +#define CDMM_ACSR_DEVSIZE (31ul << CDMM_ACSR_DEVSIZE_SHIFT) +#define CDMM_ACSR_DEVREV_SHIFT 12 +#define CDMM_ACSR_DEVREV (15ul << CDMM_ACSR_DEVREV_SHIFT) +#define CDMM_ACSR_UW (1ul << 3) +#define CDMM_ACSR_UR (1ul << 2) +#define CDMM_ACSR_SW (1ul << 1) +#define CDMM_ACSR_SR (1ul << 0) + +/* Each block of device registers is 64 bytes */ +#define CDMM_DRB_SIZE 64 + +#define to_mips_cdmm_driver(d) container_of(d, struct mips_cdmm_driver, drv) + +/* Default physical base address */ +static phys_addr_t mips_cdmm_default_base; + +/* Bus operations */ + +static const struct mips_cdmm_device_id * +mips_cdmm_lookup(const struct mips_cdmm_device_id *table, + struct mips_cdmm_device *dev) +{ + int ret = 0; + + for (; table->type; ++table) { + ret = (dev->type == table->type); + if (ret) + break; + } + + return ret ? table : NULL; +} + +static int mips_cdmm_match(struct device *dev, struct device_driver *drv) +{ + struct mips_cdmm_device *cdev = to_mips_cdmm_device(dev); + struct mips_cdmm_driver *cdrv = to_mips_cdmm_driver(drv); + + return mips_cdmm_lookup(cdrv->id_table, cdev) != NULL; +} + +static int mips_cdmm_uevent(struct device *dev, struct kobj_uevent_env *env) +{ + struct mips_cdmm_device *cdev = to_mips_cdmm_device(dev); + int retval = 0; + + retval = add_uevent_var(env, "CDMM_CPU=%u", cdev->cpu); + if (retval) + return retval; + + retval = add_uevent_var(env, "CDMM_TYPE=0x%02x", cdev->type); + if (retval) + return retval; + + retval = add_uevent_var(env, "CDMM_REV=%u", cdev->rev); + if (retval) + return retval; + + retval = add_uevent_var(env, "MODALIAS=mipscdmm:t%02X", cdev->type); + return retval; +} + +/* Device attributes */ + +#define CDMM_ATTR(name, fmt, arg...) \ +static ssize_t name##_show(struct device *_dev, \ + struct device_attribute *attr, char *buf) \ +{ \ + struct mips_cdmm_device *dev = to_mips_cdmm_device(_dev); \ + return sprintf(buf, fmt, arg); \ +} \ +static DEVICE_ATTR_RO(name); + +CDMM_ATTR(cpu, "%u\n", dev->cpu); +CDMM_ATTR(type, "0x%02x\n", dev->type); +CDMM_ATTR(revision, "%u\n", dev->rev); +CDMM_ATTR(modalias, "mipscdmm:t%02X\n", dev->type); +CDMM_ATTR(resource, "\t%016llx\t%016llx\t%016lx\n", + (unsigned long long)dev->res.start, + (unsigned long long)dev->res.end, + dev->res.flags); + +static struct attribute *mips_cdmm_dev_attrs[] = { + &dev_attr_cpu.attr, + &dev_attr_type.attr, + &dev_attr_revision.attr, + &dev_attr_modalias.attr, + &dev_attr_resource.attr, + NULL, +}; +ATTRIBUTE_GROUPS(mips_cdmm_dev); + +struct bus_type mips_cdmm_bustype = { + .name = "cdmm", + .dev_groups = mips_cdmm_dev_groups, + .match = mips_cdmm_match, + .uevent = mips_cdmm_uevent, +}; +EXPORT_SYMBOL_GPL(mips_cdmm_bustype); + +/* + * Standard driver callback helpers. + * + * All the CDMM driver callbacks need to be executed on the appropriate CPU from + * workqueues. For the standard driver callbacks we need a work function + * (mips_cdmm_{void,int}_work()) to do the actual call from the right CPU, and a + * wrapper function (generated with BUILD_PERCPU_HELPER) to arrange for the work + * function to be called on that CPU. + */ + +/** + * struct mips_cdmm_work_dev - Data for per-device call work. + * @fn: CDMM driver callback function to call for the device. + * @dev: CDMM device to pass to @fn. + */ +struct mips_cdmm_work_dev { + void *fn; + struct mips_cdmm_device *dev; +}; + +/** + * mips_cdmm_void_work() - Call a void returning CDMM driver callback. + * @data: struct mips_cdmm_work_dev pointer. + * + * A work_on_cpu() callback function to call an arbitrary CDMM driver callback + * function which doesn't return a value. + */ +static long mips_cdmm_void_work(void *data) +{ + struct mips_cdmm_work_dev *work = data; + void (*fn)(struct mips_cdmm_device *) = work->fn; + + fn(work->dev); + return 0; +} + +/** + * mips_cdmm_int_work() - Call an int returning CDMM driver callback. + * @data: struct mips_cdmm_work_dev pointer. + * + * A work_on_cpu() callback function to call an arbitrary CDMM driver callback + * function which returns an int. + */ +static long mips_cdmm_int_work(void *data) +{ + struct mips_cdmm_work_dev *work = data; + int (*fn)(struct mips_cdmm_device *) = work->fn; + + return fn(work->dev); +} + +#define _BUILD_RET_void +#define _BUILD_RET_int return + +/** + * BUILD_PERCPU_HELPER() - Helper to call a CDMM driver callback on right CPU. + * @_ret: Return type (void or int). + * @_name: Name of CDMM driver callback function. + * + * Generates a specific device callback function to call a CDMM driver callback + * function on the appropriate CPU for the device, and if applicable return the + * result. + */ +#define BUILD_PERCPU_HELPER(_ret, _name) \ +static _ret mips_cdmm_##_name(struct device *dev) \ +{ \ + struct mips_cdmm_device *cdev = to_mips_cdmm_device(dev); \ + struct mips_cdmm_driver *cdrv = to_mips_cdmm_driver(dev->driver); \ + struct mips_cdmm_work_dev work = { \ + .fn = cdrv->_name, \ + .dev = cdev, \ + }; \ + \ + _BUILD_RET_##_ret work_on_cpu(cdev->cpu, \ + mips_cdmm_##_ret##_work, &work); \ +} + +/* Driver callback functions */ +BUILD_PERCPU_HELPER(int, probe) /* int mips_cdmm_probe(struct device) */ +BUILD_PERCPU_HELPER(int, remove) /* int mips_cdmm_remove(struct device) */ +BUILD_PERCPU_HELPER(void, shutdown) /* void mips_cdmm_shutdown(struct device) */ + + +/* Driver registration */ + +/** + * mips_cdmm_driver_register() - Register a CDMM driver. + * @drv: CDMM driver information. + * + * Register a CDMM driver with the CDMM subsystem. The driver will be informed + * of matching devices which are discovered. + * + * Returns: 0 on success. + */ +int mips_cdmm_driver_register(struct mips_cdmm_driver *drv) +{ + drv->drv.bus = &mips_cdmm_bustype; + + if (drv->probe) + drv->drv.probe = mips_cdmm_probe; + if (drv->remove) + drv->drv.remove = mips_cdmm_remove; + if (drv->shutdown) + drv->drv.shutdown = mips_cdmm_shutdown; + + return driver_register(&drv->drv); +} +EXPORT_SYMBOL_GPL(mips_cdmm_driver_register); + +/** + * mips_cdmm_driver_unregister() - Unregister a CDMM driver. + * @drv: CDMM driver information. + * + * Unregister a CDMM driver from the CDMM subsystem. + */ +void mips_cdmm_driver_unregister(struct mips_cdmm_driver *drv) +{ + driver_unregister(&drv->drv); +} +EXPORT_SYMBOL_GPL(mips_cdmm_driver_unregister); + + +/* CDMM initialisation and bus discovery */ + +/** + * struct mips_cdmm_bus - Info about CDMM bus. + * @phys: Physical address at which it is mapped. + * @regs: Virtual address where registers can be accessed. + * @drbs: Total number of DRBs. + * @drbs_reserved: Number of DRBs reserved. + * @discovered: Whether the devices on the bus have been discovered yet. + * @offline: Whether the CDMM bus is going offline (or very early + * coming back online), in which case it should be + * reconfigured each time. + */ +struct mips_cdmm_bus { + phys_addr_t phys; + void __iomem *regs; + unsigned int drbs; + unsigned int drbs_reserved; + bool discovered; + bool offline; +}; + +static struct mips_cdmm_bus mips_cdmm_boot_bus; +static DEFINE_PER_CPU(struct mips_cdmm_bus *, mips_cdmm_buses); +static atomic_t mips_cdmm_next_id = ATOMIC_INIT(-1); + +/** + * mips_cdmm_get_bus() - Get the per-CPU CDMM bus information. + * + * Get information about the per-CPU CDMM bus, if the bus is present. + * + * The caller must prevent migration to another CPU, either by disabling + * pre-emption or by running from a pinned kernel thread. + * + * Returns: Pointer to CDMM bus information for the current CPU. + * May return ERR_PTR(-errno) in case of error, so check with + * IS_ERR(). + */ +static struct mips_cdmm_bus *mips_cdmm_get_bus(void) +{ + struct mips_cdmm_bus *bus, **bus_p; + unsigned long flags; + unsigned int cpu; + + if (!cpu_has_cdmm) + return ERR_PTR(-ENODEV); + + cpu = smp_processor_id(); + /* Avoid early use of per-cpu primitives before initialised */ + if (cpu == 0) + return &mips_cdmm_boot_bus; + + /* Get bus pointer */ + bus_p = per_cpu_ptr(&mips_cdmm_buses, cpu); + local_irq_save(flags); + bus = *bus_p; + /* Attempt allocation if NULL */ + if (unlikely(!bus)) { + bus = kzalloc(sizeof(*bus), GFP_ATOMIC); + if (unlikely(!bus)) + bus = ERR_PTR(-ENOMEM); + else + *bus_p = bus; + } + local_irq_restore(flags); + return bus; +} + +/** + * mips_cdmm_cur_base() - Find current physical base address of CDMM region. + * + * Returns: Physical base address of CDMM region according to cdmmbase CP0 + * register, or 0 if the CDMM region is disabled. + */ +static phys_addr_t mips_cdmm_cur_base(void) +{ + unsigned long cdmmbase = read_c0_cdmmbase(); + + if (!(cdmmbase & MIPS_CDMMBASE_EN)) + return 0; + + return (cdmmbase >> MIPS_CDMMBASE_ADDR_SHIFT) + << MIPS_CDMMBASE_ADDR_START; +} + +/** + * mips_cdmm_setup() - Ensure the CDMM bus is initialised and usable. + * @bus: Pointer to bus information for current CPU. + * IS_ERR(bus) is checked, so no need for caller to check. + * + * The caller must prevent migration to another CPU, either by disabling + * pre-emption or by running from a pinned kernel thread. + * + * Returns 0 on success, -errno on failure. + */ +static int mips_cdmm_setup(struct mips_cdmm_bus *bus) +{ + unsigned long cdmmbase, flags; + int ret = 0; + + if (IS_ERR(bus)) + return PTR_ERR(bus); + + local_irq_save(flags); + /* Don't set up bus a second time unless marked offline */ + if (bus->offline) { + /* If CDMM region is still set up, nothing to do */ + if (bus->phys == mips_cdmm_cur_base()) + goto out; + /* + * The CDMM region isn't set up as expected, so it needs + * reconfiguring, but then we can stop checking it. + */ + bus->offline = false; + } else if (bus->phys > 1) { + goto out; + } + + /* If the CDMM region is already configured, inherit that setup */ + if (!bus->phys) + bus->phys = mips_cdmm_cur_base(); + /* Otherwise, ask platform code for suggestions */ + if (!bus->phys && mips_cdmm_phys_base) + bus->phys = mips_cdmm_phys_base(); + /* Otherwise, copy what other CPUs have done */ + if (!bus->phys) + bus->phys = mips_cdmm_default_base; + /* Otherwise, complain once */ + if (!bus->phys) { + bus->phys = 1; + /* + * If you hit this, either your bootloader needs to set up the + * CDMM on the boot CPU, or else you need to implement + * mips_cdmm_phys_base() for your platform (see asm/cdmm.h). + */ + pr_err("cdmm%u: Failed to choose a physical base\n", + smp_processor_id()); + } + /* Already complained? */ + if (bus->phys == 1) { + ret = -ENOMEM; + goto out; + } + /* Record our success for other CPUs to copy */ + mips_cdmm_default_base = bus->phys; + + pr_debug("cdmm%u: Enabling CDMM region at %pa\n", + smp_processor_id(), &bus->phys); + + /* Enable CDMM */ + cdmmbase = read_c0_cdmmbase(); + cdmmbase &= (1ul << MIPS_CDMMBASE_ADDR_SHIFT) - 1; + cdmmbase |= (bus->phys >> MIPS_CDMMBASE_ADDR_START) + << MIPS_CDMMBASE_ADDR_SHIFT; + cdmmbase |= MIPS_CDMMBASE_EN; + write_c0_cdmmbase(cdmmbase); + tlbw_use_hazard(); + + bus->regs = (void __iomem *)CKSEG1ADDR(bus->phys); + bus->drbs = 1 + ((cdmmbase & MIPS_CDMMBASE_SIZE) >> + MIPS_CDMMBASE_SIZE_SHIFT); + bus->drbs_reserved = !!(cdmmbase & MIPS_CDMMBASE_CI); + +out: + local_irq_restore(flags); + return ret; +} + +/** + * mips_cdmm_early_probe() - Minimally probe for a specific device on CDMM. + * @dev_type: CDMM type code to look for. + * + * Minimally configure the in-CPU Common Device Memory Map (CDMM) and look for a + * specific device. This can be used to find a device very early in boot for + * example to configure an early FDC console device. + * + * The caller must prevent migration to another CPU, either by disabling + * pre-emption or by running from a pinned kernel thread. + * + * Returns: MMIO pointer to device memory. The caller can read the ACSR + * register to find more information about the device (such as the + * version number or the number of blocks). + * May return IOMEM_ERR_PTR(-errno) in case of error, so check with + * IS_ERR(). + */ +void __iomem *mips_cdmm_early_probe(unsigned int dev_type) +{ + struct mips_cdmm_bus *bus; + void __iomem *cdmm; + u32 acsr; + unsigned int drb, type, size; + int err; + + if (WARN_ON(!dev_type)) + return IOMEM_ERR_PTR(-ENODEV); + + bus = mips_cdmm_get_bus(); + err = mips_cdmm_setup(bus); + if (err) + return IOMEM_ERR_PTR(err); + + /* Skip the first block if it's reserved for more registers */ + drb = bus->drbs_reserved; + cdmm = bus->regs; + + /* Look for a specific device type */ + for (; drb < bus->drbs; drb += size + 1) { + acsr = readl(cdmm + drb * CDMM_DRB_SIZE); + type = (acsr & CDMM_ACSR_DEVTYPE) >> CDMM_ACSR_DEVTYPE_SHIFT; + if (type == dev_type) + return cdmm + drb * CDMM_DRB_SIZE; + size = (acsr & CDMM_ACSR_DEVSIZE) >> CDMM_ACSR_DEVSIZE_SHIFT; + } + + return IOMEM_ERR_PTR(-ENODEV); +} +EXPORT_SYMBOL_GPL(mips_cdmm_early_probe); + +/** + * mips_cdmm_release() - Release a removed CDMM device. + * @dev: Device object + * + * Clean up the struct mips_cdmm_device for an unused CDMM device. This is + * called automatically by the driver core when a device is removed. + */ +static void mips_cdmm_release(struct device *dev) +{ + struct mips_cdmm_device *cdev = to_mips_cdmm_device(dev); + + kfree(cdev); +} + +/** + * mips_cdmm_bus_discover() - Discover the devices on the CDMM bus. + * @bus: CDMM bus information, must already be set up. + */ +static void mips_cdmm_bus_discover(struct mips_cdmm_bus *bus) +{ + void __iomem *cdmm; + u32 acsr; + unsigned int drb, type, size, rev; + struct mips_cdmm_device *dev; + unsigned int cpu = smp_processor_id(); + int ret = 0; + int id = 0; + + /* Skip the first block if it's reserved for more registers */ + drb = bus->drbs_reserved; + cdmm = bus->regs; + + /* Discover devices */ + bus->discovered = true; + pr_info("cdmm%u discovery (%u blocks)\n", cpu, bus->drbs); + for (; drb < bus->drbs; drb += size + 1) { + acsr = readl(cdmm + drb * CDMM_DRB_SIZE); + type = (acsr & CDMM_ACSR_DEVTYPE) >> CDMM_ACSR_DEVTYPE_SHIFT; + size = (acsr & CDMM_ACSR_DEVSIZE) >> CDMM_ACSR_DEVSIZE_SHIFT; + rev = (acsr & CDMM_ACSR_DEVREV) >> CDMM_ACSR_DEVREV_SHIFT; + + if (!type) + continue; + + pr_info("cdmm%u-%u: @%u (%#x..%#x), type 0x%02x, rev %u\n", + cpu, id, drb, drb * CDMM_DRB_SIZE, + (drb + size + 1) * CDMM_DRB_SIZE - 1, + type, rev); + + dev = kzalloc(sizeof(*dev), GFP_KERNEL); + if (!dev) + break; + + dev->cpu = cpu; + dev->res.start = bus->phys + drb * CDMM_DRB_SIZE; + dev->res.end = bus->phys + + (drb + size + 1) * CDMM_DRB_SIZE - 1; + dev->res.flags = IORESOURCE_MEM; + dev->type = type; + dev->rev = rev; + dev->dev.parent = get_cpu_device(cpu); + dev->dev.bus = &mips_cdmm_bustype; + dev->dev.id = atomic_inc_return(&mips_cdmm_next_id); + dev->dev.release = mips_cdmm_release; + + dev_set_name(&dev->dev, "cdmm%u-%u", cpu, id); + ++id; + ret = device_register(&dev->dev); + if (ret) { + put_device(&dev->dev); + kfree(dev); + } + } +} + + +/* + * CPU hotplug and initialisation + * + * All the CDMM driver callbacks need to be executed on the appropriate CPU from + * workqueues. For the CPU callbacks, they need to be called for all devices on + * that CPU, so the work function calls bus_for_each_dev, using a helper + * (generated with BUILD_PERDEV_HELPER) to call the driver callback if the + * device's CPU matches. + */ + +/** + * BUILD_PERDEV_HELPER() - Helper to call a CDMM driver callback if CPU matches. + * @_name: Name of CDMM driver callback function. + * + * Generates a bus_for_each_dev callback function to call a specific CDMM driver + * callback function for the device if the device's CPU matches that pointed to + * by the data argument. + * + * This is used for informing drivers for all devices on a given CPU of some + * event (such as the CPU going online/offline). + * + * It is expected to already be called from the appropriate CPU. + */ +#define BUILD_PERDEV_HELPER(_name) \ +static int mips_cdmm_##_name##_helper(struct device *dev, void *data) \ +{ \ + struct mips_cdmm_device *cdev = to_mips_cdmm_device(dev); \ + struct mips_cdmm_driver *cdrv; \ + unsigned int cpu = *(unsigned int *)data; \ + \ + if (cdev->cpu != cpu || !dev->driver) \ + return 0; \ + \ + cdrv = to_mips_cdmm_driver(dev->driver); \ + if (!cdrv->_name) \ + return 0; \ + return cdrv->_name(cdev); \ +} + +/* bus_for_each_dev callback helper functions */ +BUILD_PERDEV_HELPER(cpu_down) /* int mips_cdmm_cpu_down_helper(...) */ +BUILD_PERDEV_HELPER(cpu_up) /* int mips_cdmm_cpu_up_helper(...) */ + +/** + * mips_cdmm_bus_down() - Tear down the CDMM bus. + * @data: Pointer to unsigned int CPU number. + * + * This work_on_cpu callback function is executed on a given CPU to call the + * CDMM driver cpu_down callback for all devices on that CPU. + */ +static long mips_cdmm_bus_down(void *data) +{ + struct mips_cdmm_bus *bus; + long ret; + + /* Inform all the devices on the bus */ + ret = bus_for_each_dev(&mips_cdmm_bustype, NULL, data, + mips_cdmm_cpu_down_helper); + + /* + * While bus is offline, each use of it should reconfigure it just in + * case it is first use when coming back online again. + */ + bus = mips_cdmm_get_bus(); + if (!IS_ERR(bus)) + bus->offline = true; + + return ret; +} + +/** + * mips_cdmm_bus_up() - Bring up the CDMM bus. + * @data: Pointer to unsigned int CPU number. + * + * This work_on_cpu callback function is executed on a given CPU to discover + * CDMM devices on that CPU, or to call the CDMM driver cpu_up callback for all + * devices already discovered on that CPU. + * + * It is used during initialisation and when CPUs are brought online. + */ +static long mips_cdmm_bus_up(void *data) +{ + struct mips_cdmm_bus *bus; + long ret; + + bus = mips_cdmm_get_bus(); + ret = mips_cdmm_setup(bus); + if (ret) + return ret; + + /* Bus now set up, so we can drop the offline flag if still set */ + bus->offline = false; + + if (!bus->discovered) + mips_cdmm_bus_discover(bus); + else + /* Inform all the devices on the bus */ + ret = bus_for_each_dev(&mips_cdmm_bustype, NULL, data, + mips_cdmm_cpu_up_helper); + + return ret; +} + +/** + * mips_cdmm_cpu_notify() - Take action when a CPU is going online or offline. + * @nb: CPU notifier block . + * @action: Event that has taken place (CPU_*). + * @data: CPU number. + * + * This notifier is used to keep the CDMM buses updated as CPUs are offlined and + * onlined. When CPUs go offline or come back online, so does their CDMM bus, so + * devices must be informed. Also when CPUs come online for the first time the + * devices on the CDMM bus need discovering. + * + * Returns: NOTIFY_OK if event was used. + * NOTIFY_DONE if we didn't care. + */ +static int mips_cdmm_cpu_notify(struct notifier_block *nb, + unsigned long action, void *data) +{ + unsigned int cpu = (unsigned int)data; + + switch (action & ~CPU_TASKS_FROZEN) { + case CPU_ONLINE: + case CPU_DOWN_FAILED: + work_on_cpu(cpu, mips_cdmm_bus_up, &cpu); + break; + case CPU_DOWN_PREPARE: + work_on_cpu(cpu, mips_cdmm_bus_down, &cpu); + break; + default: + return NOTIFY_DONE; + } + + return NOTIFY_OK; +} + +static struct notifier_block mips_cdmm_cpu_nb = { + .notifier_call = mips_cdmm_cpu_notify, +}; + +/** + * mips_cdmm_init() - Initialise CDMM bus. + * + * Initialise CDMM bus, discover CDMM devices for online CPUs, and arrange for + * hotplug notifications so the CDMM drivers can be kept up to date. + */ +static int __init mips_cdmm_init(void) +{ + unsigned int cpu; + int ret; + + /* Register the bus */ + ret = bus_register(&mips_cdmm_bustype); + if (ret) + return ret; + + /* We want to be notified about new CPUs */ + ret = register_cpu_notifier(&mips_cdmm_cpu_nb); + if (ret) { + pr_warn("cdmm: Failed to register CPU notifier\n"); + goto out; + } + + /* Discover devices on CDMM of online CPUs */ + for_each_online_cpu(cpu) + work_on_cpu(cpu, mips_cdmm_bus_up, &cpu); + + return 0; +out: + bus_unregister(&mips_cdmm_bustype); + return ret; +} +subsys_initcall(mips_cdmm_init); diff --git a/include/linux/mod_devicetable.h b/include/linux/mod_devicetable.h index e530533b94be..3bfd56778c29 100644 --- a/include/linux/mod_devicetable.h +++ b/include/linux/mod_devicetable.h @@ -546,6 +546,14 @@ struct amba_id { void *data; }; +/** + * struct mips_cdmm_device_id - identifies devices in MIPS CDMM bus + * @type: Device type identifier. + */ +struct mips_cdmm_device_id { + __u8 type; +}; + /* * Match x86 CPUs for CPU specific drivers. * See documentation of "x86_match_cpu" for details. diff --git a/scripts/mod/devicetable-offsets.c b/scripts/mod/devicetable-offsets.c index f282516acc7b..fce36d0f6898 100644 --- a/scripts/mod/devicetable-offsets.c +++ b/scripts/mod/devicetable-offsets.c @@ -168,6 +168,9 @@ int main(void) DEVID_FIELD(amba_id, id); DEVID_FIELD(amba_id, mask); + DEVID(mips_cdmm_device_id); + DEVID_FIELD(mips_cdmm_device_id, type); + DEVID(x86_cpu_id); DEVID_FIELD(x86_cpu_id, feature); DEVID_FIELD(x86_cpu_id, family); diff --git a/scripts/mod/file2alias.c b/scripts/mod/file2alias.c index e614ef689eee..78691d51a479 100644 --- a/scripts/mod/file2alias.c +++ b/scripts/mod/file2alias.c @@ -1109,6 +1109,22 @@ static int do_amba_entry(const char *filename, } ADD_TO_DEVTABLE("amba", amba_id, do_amba_entry); +/* + * looks like: "mipscdmm:tN" + * + * N is exactly 2 digits, where each is an upper-case hex digit, or + * a ? or [] pattern matching exactly one digit. + */ +static int do_mips_cdmm_entry(const char *filename, + void *symval, char *alias) +{ + DEF_FIELD(symval, mips_cdmm_device_id, type); + + sprintf(alias, "mipscdmm:t%02X*", type); + return 1; +} +ADD_TO_DEVTABLE("mipscdmm", mips_cdmm_device_id, do_mips_cdmm_entry); + /* LOOKS like cpu:type:x86,venVVVVfamFFFFmodMMMM:feature:*,FEAT,* * All fields are numbers. It would be nicer to use strings for vendor * and feature, but getting those out of the build system here is too -- cgit v1.2.3 From 6429e2b6fc05d8640bb94f5b67c047e936707f31 Mon Sep 17 00:00:00 2001 From: James Hogan Date: Thu, 29 Jan 2015 11:14:09 +0000 Subject: IRQCHIP: mips-gic: Add function for retrieving FDC IRQ Add a function to the MIPS GIC driver for retrieving the Fast Debug Channel (FDC) interrupt number, similar to the existing ones for the timer and perf counter interrupts. This will be used by platform implementations of get_c0_fdc_int() if a GIC is present. A workaround exists for interAptiv and proAptiv which claim to be able to route the FDC interrupt but don't seem to be able to in practice (at least on Malta). [ralf@linux-mips.org: Fix conflict.] Signed-off-by: James Hogan Cc: Ralf Baechle Cc: Andrew Bresticker Cc: Thomas Gleixner Cc: Jason Cooper Cc: linux-mips@linux-mips.org Cc: linux-kernel@vger.kernel.org Patchwork: https://patchwork.linux-mips.org/patch/9142/ Signed-off-by: Ralf Baechle --- drivers/irqchip/irq-mips-gic.c | 23 +++++++++++++++++++++++ include/linux/irqchip/mips-gic.h | 1 + 2 files changed, 24 insertions(+) (limited to 'include') diff --git a/drivers/irqchip/irq-mips-gic.c b/drivers/irqchip/irq-mips-gic.c index c8699eec3930..827cf9b9db39 100644 --- a/drivers/irqchip/irq-mips-gic.c +++ b/drivers/irqchip/irq-mips-gic.c @@ -227,6 +227,29 @@ int gic_get_c0_perfcount_int(void) GIC_LOCAL_TO_HWIRQ(GIC_LOCAL_INT_PERFCTR)); } +int gic_get_c0_fdc_int(void) +{ + if (!gic_local_irq_is_routable(GIC_LOCAL_INT_FDC)) { + /* Is the FDC IRQ even present? */ + if (cp0_fdc_irq < 0) + return -1; + return MIPS_CPU_IRQ_BASE + cp0_fdc_irq; + } + + /* + * Some cores claim the FDC is routable but it doesn't actually seem to + * be connected. + */ + switch (current_cpu_type()) { + case CPU_INTERAPTIV: + case CPU_PROAPTIV: + return -1; + } + + return irq_create_mapping(gic_irq_domain, + GIC_LOCAL_TO_HWIRQ(GIC_LOCAL_INT_FDC)); +} + static void gic_handle_shared_int(void) { unsigned int i, intr, virq; diff --git a/include/linux/irqchip/mips-gic.h b/include/linux/irqchip/mips-gic.h index 2e79b4bb2d75..83099e5ab2c4 100644 --- a/include/linux/irqchip/mips-gic.h +++ b/include/linux/irqchip/mips-gic.h @@ -251,4 +251,5 @@ extern unsigned int plat_ipi_call_int_xlate(unsigned int); extern unsigned int plat_ipi_resched_int_xlate(unsigned int); extern int gic_get_c0_compare_int(void); extern int gic_get_c0_perfcount_int(void); +extern int gic_get_c0_fdc_int(void); #endif /* __LINUX_IRQCHIP_MIPS_GIC_H */ -- cgit v1.2.3 From 8fa4b93067b70a87785279a7c60158e58e4f2f20 Mon Sep 17 00:00:00 2001 From: Markos Chandras Date: Mon, 23 Mar 2015 12:32:01 +0000 Subject: IRQCHIP: irq-mips-gic: Add new functions to start/stop the GIC counter We add new functions to start and stop the GIC counter since there are no guarantees the counter will be running after a CPU reset. The GIC counter is stopped by setting the 29th bit on the GIC Config register and it is started by clearing that bit. Signed-off-by: Markos Chandras Cc: Thomas Gleixner Cc: Jason Cooper Cc: Andrew Bresticker Cc: Qais Yousef Cc: linux-kernel@vger.kernel.org Cc: linux-mips@linux-mips.org Patchwork: https://patchwork.linux-mips.org/patch/9594/ Signed-off-by: Ralf Baechle --- drivers/irqchip/irq-mips-gic.c | 21 +++++++++++++++++++++ include/linux/irqchip/mips-gic.h | 2 ++ 2 files changed, 23 insertions(+) (limited to 'include') diff --git a/drivers/irqchip/irq-mips-gic.c b/drivers/irqchip/irq-mips-gic.c index 827cf9b9db39..bc48b7dc89ec 100644 --- a/drivers/irqchip/irq-mips-gic.c +++ b/drivers/irqchip/irq-mips-gic.c @@ -166,6 +166,27 @@ cycle_t gic_read_compare(void) return (((cycle_t) hi) << 32) + lo; } + +void gic_start_count(void) +{ + u32 gicconfig; + + /* Start the counter */ + gicconfig = gic_read(GIC_REG(SHARED, GIC_SH_CONFIG)); + gicconfig &= ~(1 << GIC_SH_CONFIG_COUNTSTOP_SHF); + gic_write(GIC_REG(SHARED, GIC_SH_CONFIG), gicconfig); +} + +void gic_stop_count(void) +{ + u32 gicconfig; + + /* Stop the counter */ + gicconfig = gic_read(GIC_REG(SHARED, GIC_SH_CONFIG)); + gicconfig |= 1 << GIC_SH_CONFIG_COUNTSTOP_SHF; + gic_write(GIC_REG(SHARED, GIC_SH_CONFIG), gicconfig); +} + #endif static bool gic_local_irq_is_routable(int intr) diff --git a/include/linux/irqchip/mips-gic.h b/include/linux/irqchip/mips-gic.h index 83099e5ab2c4..9b1ad3734911 100644 --- a/include/linux/irqchip/mips-gic.h +++ b/include/linux/irqchip/mips-gic.h @@ -246,6 +246,8 @@ extern unsigned int gic_get_count_width(void); extern cycle_t gic_read_compare(void); extern void gic_write_compare(cycle_t cnt); extern void gic_write_cpu_compare(cycle_t cnt, int cpu); +extern void gic_start_count(void); +extern void gic_stop_count(void); extern void gic_send_ipi(unsigned int intr); extern unsigned int plat_ipi_call_int_xlate(unsigned int); extern unsigned int plat_ipi_resched_int_xlate(unsigned int); -- cgit v1.2.3 From 06ca7f68d4c861d549a8deb161e1527065a80bb1 Mon Sep 17 00:00:00 2001 From: Stephan Mueller Date: Mon, 30 Mar 2015 21:55:52 +0200 Subject: crypto: api - prevent helper ciphers from being used Several hardware related cipher implementations are implemented as follows: a "helper" cipher implementation is registered with the kernel crypto API. Such helper ciphers are never intended to be called by normal users. In some cases, calling them via the normal crypto API may even cause failures including kernel crashes. In a normal case, the "wrapping" ciphers that use the helpers ensure that these helpers are invoked such that they cannot cause any calamity. Considering the AF_ALG user space interface, unprivileged users can call all ciphers registered with the crypto API, including these helper ciphers that are not intended to be called directly. That means, with AF_ALG user space may invoke these helper ciphers and may cause undefined states or side effects. To avoid any potential side effects with such helpers, the patch prevents the helpers to be called directly. A new cipher type flag is added: CRYPTO_ALG_INTERNAL. This flag shall be used to mark helper ciphers. These ciphers can only be used if the caller invoke the cipher with CRYPTO_ALG_INTERNAL in the type and mask field. Signed-off-by: Stephan Mueller Signed-off-by: Herbert Xu --- crypto/api.c | 10 ++++++++++ include/linux/crypto.h | 6 ++++++ 2 files changed, 16 insertions(+) (limited to 'include') diff --git a/crypto/api.c b/crypto/api.c index 2a81e98a0021..afe4610afc4b 100644 --- a/crypto/api.c +++ b/crypto/api.c @@ -257,6 +257,16 @@ struct crypto_alg *crypto_alg_mod_lookup(const char *name, u32 type, u32 mask) mask |= CRYPTO_ALG_TESTED; } + /* + * If the internal flag is set for a cipher, require a caller to + * to invoke the cipher with the internal flag to use that cipher. + * Also, if a caller wants to allocate a cipher that may or may + * not be an internal cipher, use type | CRYPTO_ALG_INTERNAL and + * !(mask & CRYPTO_ALG_INTERNAL). + */ + if (!((type | mask) & CRYPTO_ALG_INTERNAL)) + mask |= CRYPTO_ALG_INTERNAL; + larval = crypto_larval_lookup(name, type, mask); if (IS_ERR(larval) || !crypto_is_larval(larval)) return larval; diff --git a/include/linux/crypto.h b/include/linux/crypto.h index fb5ef16d6a12..10df5d2d093a 100644 --- a/include/linux/crypto.h +++ b/include/linux/crypto.h @@ -94,6 +94,12 @@ */ #define CRYPTO_ALG_KERN_DRIVER_ONLY 0x00001000 +/* + * Mark a cipher as a service implementation only usable by another + * cipher and never by a normal user of the kernel crypto API + */ +#define CRYPTO_ALG_INTERNAL 0x00002000 + /* * Transform masks and values (for crt_flags). */ -- cgit v1.2.3 From 938c470976192590b4adc921b2e10fa31237eddc Mon Sep 17 00:00:00 2001 From: Joerg Roedel Date: Thu, 26 Mar 2015 13:43:04 +0100 Subject: iommu: Introduce domain_alloc and domain_free iommu_ops These new call-backs defer the allocation and destruction of 'struct iommu_domain' to the iommu driver. This allows drivers to embed this struct into their private domain structures and to get rid of the domain_init and domain_destroy call-backs when all drivers have been converted. Tested-by: Thierry Reding Tested-by: Heiko Stuebner Reviewed-by: Alex Williamson Acked-by: Will Deacon Signed-off-by: Joerg Roedel --- drivers/iommu/iommu.c | 29 +++++++++++++++++++++-------- include/linux/iommu.h | 5 +++++ 2 files changed, 26 insertions(+), 8 deletions(-) (limited to 'include') diff --git a/drivers/iommu/iommu.c b/drivers/iommu/iommu.c index 72e683df0731..11de2620bbf4 100644 --- a/drivers/iommu/iommu.c +++ b/drivers/iommu/iommu.c @@ -900,26 +900,34 @@ EXPORT_SYMBOL_GPL(iommu_set_fault_handler); struct iommu_domain *iommu_domain_alloc(struct bus_type *bus) { + const struct iommu_ops *ops; struct iommu_domain *domain; - int ret; if (bus == NULL || bus->iommu_ops == NULL) return NULL; - domain = kzalloc(sizeof(*domain), GFP_KERNEL); + ops = bus->iommu_ops; + + if (ops->domain_alloc) + domain = ops->domain_alloc(); + else + domain = kzalloc(sizeof(*domain), GFP_KERNEL); + if (!domain) return NULL; domain->ops = bus->iommu_ops; - ret = domain->ops->domain_init(domain); - if (ret) + if (ops->domain_init && domain->ops->domain_init(domain)) goto out_free; return domain; out_free: - kfree(domain); + if (ops->domain_free) + ops->domain_free(domain); + else + kfree(domain); return NULL; } @@ -927,10 +935,15 @@ EXPORT_SYMBOL_GPL(iommu_domain_alloc); void iommu_domain_free(struct iommu_domain *domain) { - if (likely(domain->ops->domain_destroy != NULL)) - domain->ops->domain_destroy(domain); + const struct iommu_ops *ops = domain->ops; - kfree(domain); + if (likely(ops->domain_destroy != NULL)) + ops->domain_destroy(domain); + + if (ops->domain_free) + ops->domain_free(domain); + else + kfree(domain); } EXPORT_SYMBOL_GPL(iommu_domain_free); diff --git a/include/linux/iommu.h b/include/linux/iommu.h index 38daa453f2e5..69d1d120c0a3 100644 --- a/include/linux/iommu.h +++ b/include/linux/iommu.h @@ -115,6 +115,11 @@ struct iommu_ops { bool (*capable)(enum iommu_cap); int (*domain_init)(struct iommu_domain *domain); void (*domain_destroy)(struct iommu_domain *domain); + + /* Domain allocation and freeing by the iommu driver */ + struct iommu_domain *(*domain_alloc)(void); + void (*domain_free)(struct iommu_domain *); + int (*attach_dev)(struct iommu_domain *domain, struct device *dev); void (*detach_dev)(struct iommu_domain *domain, struct device *dev); int (*map)(struct iommu_domain *domain, unsigned long iova, -- cgit v1.2.3 From 8539c7c16b970258e14761d8a1f7d10fe798031a Mon Sep 17 00:00:00 2001 From: Joerg Roedel Date: Thu, 26 Mar 2015 13:43:05 +0100 Subject: iommu: Introduce iommu domain types This allows to handle domains differently based on their type in the future. An IOMMU driver can implement certain optimizations for DMA-API domains for example. The domain types can be extended later and some of the existing domain attributes can be migrated to become domain flags. Tested-by: Thierry Reding Tested-by: Heiko Stuebner Reviewed-by: Alex Williamson Acked-by: Will Deacon Signed-off-by: Joerg Roedel --- drivers/iommu/iommu.c | 5 +++-- include/linux/iommu.h | 27 ++++++++++++++++++++++++++- 2 files changed, 29 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/drivers/iommu/iommu.c b/drivers/iommu/iommu.c index 11de2620bbf4..4920605892a3 100644 --- a/drivers/iommu/iommu.c +++ b/drivers/iommu/iommu.c @@ -909,14 +909,15 @@ struct iommu_domain *iommu_domain_alloc(struct bus_type *bus) ops = bus->iommu_ops; if (ops->domain_alloc) - domain = ops->domain_alloc(); + domain = ops->domain_alloc(IOMMU_DOMAIN_UNMANAGED); else domain = kzalloc(sizeof(*domain), GFP_KERNEL); if (!domain) return NULL; - domain->ops = bus->iommu_ops; + domain->ops = bus->iommu_ops; + domain->type = IOMMU_DOMAIN_UNMANAGED; if (ops->domain_init && domain->ops->domain_init(domain)) goto out_free; diff --git a/include/linux/iommu.h b/include/linux/iommu.h index 69d1d120c0a3..72d03fe78cf6 100644 --- a/include/linux/iommu.h +++ b/include/linux/iommu.h @@ -51,7 +51,32 @@ struct iommu_domain_geometry { bool force_aperture; /* DMA only allowed in mappable range? */ }; +/* Domain feature flags */ +#define __IOMMU_DOMAIN_PAGING (1U << 0) /* Support for iommu_map/unmap */ +#define __IOMMU_DOMAIN_DMA_API (1U << 1) /* Domain for use in DMA-API + implementation */ +#define __IOMMU_DOMAIN_PT (1U << 2) /* Domain is identity mapped */ + +/* + * This are the possible domain-types + * + * IOMMU_DOMAIN_BLOCKED - All DMA is blocked, can be used to isolate + * devices + * IOMMU_DOMAIN_IDENTITY - DMA addresses are system physical addresses + * IOMMU_DOMAIN_UNMANAGED - DMA mappings managed by IOMMU-API user, used + * for VMs + * IOMMU_DOMAIN_DMA - Internally used for DMA-API implementations. + * This flag allows IOMMU drivers to implement + * certain optimizations for these domains + */ +#define IOMMU_DOMAIN_BLOCKED (0U) +#define IOMMU_DOMAIN_IDENTITY (__IOMMU_DOMAIN_PT) +#define IOMMU_DOMAIN_UNMANAGED (__IOMMU_DOMAIN_PAGING) +#define IOMMU_DOMAIN_DMA (__IOMMU_DOMAIN_PAGING | \ + __IOMMU_DOMAIN_DMA_API) + struct iommu_domain { + unsigned type; const struct iommu_ops *ops; void *priv; iommu_fault_handler_t handler; @@ -117,7 +142,7 @@ struct iommu_ops { void (*domain_destroy)(struct iommu_domain *domain); /* Domain allocation and freeing by the iommu driver */ - struct iommu_domain *(*domain_alloc)(void); + struct iommu_domain *(*domain_alloc)(unsigned iommu_domain_type); void (*domain_free)(struct iommu_domain *); int (*attach_dev)(struct iommu_domain *domain, struct device *dev); -- cgit v1.2.3 From 89be34a1ced886880a3219f9d2ba2192dc738ef2 Mon Sep 17 00:00:00 2001 From: Joerg Roedel Date: Thu, 26 Mar 2015 13:43:19 +0100 Subject: iommu: Remove domain_init and domain_free iommu_ops All drivers have been converted to the new domain_alloc and domain_free iommu-ops. So remove the old ones and get rid of iommu_domain->priv too, as this is no longer needed when the struct iommu_domain is embedded in the private structures of the iommu drivers. Tested-by: Thierry Reding Tested-by: Heiko Stuebner Reviewed-by: Alex Williamson Acked-by: Will Deacon Signed-off-by: Joerg Roedel --- drivers/iommu/iommu.c | 30 ++---------------------------- include/linux/iommu.h | 3 --- 2 files changed, 2 insertions(+), 31 deletions(-) (limited to 'include') diff --git a/drivers/iommu/iommu.c b/drivers/iommu/iommu.c index 656b9499e748..d4f527e56679 100644 --- a/drivers/iommu/iommu.c +++ b/drivers/iommu/iommu.c @@ -900,51 +900,25 @@ EXPORT_SYMBOL_GPL(iommu_set_fault_handler); struct iommu_domain *iommu_domain_alloc(struct bus_type *bus) { - const struct iommu_ops *ops; struct iommu_domain *domain; if (bus == NULL || bus->iommu_ops == NULL) return NULL; - ops = bus->iommu_ops; - - if (ops->domain_alloc) - domain = ops->domain_alloc(IOMMU_DOMAIN_UNMANAGED); - else - domain = kzalloc(sizeof(*domain), GFP_KERNEL); - + domain = bus->iommu_ops->domain_alloc(IOMMU_DOMAIN_UNMANAGED); if (!domain) return NULL; domain->ops = bus->iommu_ops; domain->type = IOMMU_DOMAIN_UNMANAGED; - if (ops->domain_init && domain->ops->domain_init(domain)) - goto out_free; - return domain; - -out_free: - if (ops->domain_free) - ops->domain_free(domain); - else - kfree(domain); - - return NULL; } EXPORT_SYMBOL_GPL(iommu_domain_alloc); void iommu_domain_free(struct iommu_domain *domain) { - const struct iommu_ops *ops = domain->ops; - - if (likely(ops->domain_destroy != NULL)) - ops->domain_destroy(domain); - - if (ops->domain_free) - ops->domain_free(domain); - else - kfree(domain); + domain->ops->domain_free(domain); } EXPORT_SYMBOL_GPL(iommu_domain_free); diff --git a/include/linux/iommu.h b/include/linux/iommu.h index 72d03fe78cf6..0546b8710ce3 100644 --- a/include/linux/iommu.h +++ b/include/linux/iommu.h @@ -78,7 +78,6 @@ struct iommu_domain_geometry { struct iommu_domain { unsigned type; const struct iommu_ops *ops; - void *priv; iommu_fault_handler_t handler; void *handler_token; struct iommu_domain_geometry geometry; @@ -138,8 +137,6 @@ enum iommu_attr { */ struct iommu_ops { bool (*capable)(enum iommu_cap); - int (*domain_init)(struct iommu_domain *domain); - void (*domain_destroy)(struct iommu_domain *domain); /* Domain allocation and freeing by the iommu driver */ struct iommu_domain *(*domain_alloc)(unsigned iommu_domain_type); -- cgit v1.2.3 From dbbc14775ae395a50d91f22904670ca4c9297542 Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Mon, 30 Mar 2015 14:33:33 -0400 Subject: SUNRPC: Introduce missing well-known netids Signed-off-by: Chuck Lever Signed-off-by: Anna Schumaker --- include/linux/sunrpc/msg_prot.h | 8 +++++++- include/linux/sunrpc/xprtrdma.h | 5 ----- 2 files changed, 7 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/include/linux/sunrpc/msg_prot.h b/include/linux/sunrpc/msg_prot.h index aadc6a04e1ac..807371357160 100644 --- a/include/linux/sunrpc/msg_prot.h +++ b/include/linux/sunrpc/msg_prot.h @@ -142,12 +142,18 @@ typedef __be32 rpc_fraghdr; (RPC_REPHDRSIZE + (2 + RPC_MAX_AUTH_SIZE/4)) /* - * RFC1833/RFC3530 rpcbind (v3+) well-known netid's. + * Well-known netids. See: + * + * http://www.iana.org/assignments/rpc-netids/rpc-netids.xhtml */ #define RPCBIND_NETID_UDP "udp" #define RPCBIND_NETID_TCP "tcp" +#define RPCBIND_NETID_RDMA "rdma" +#define RPCBIND_NETID_SCTP "sctp" #define RPCBIND_NETID_UDP6 "udp6" #define RPCBIND_NETID_TCP6 "tcp6" +#define RPCBIND_NETID_RDMA6 "rdma6" +#define RPCBIND_NETID_SCTP6 "sctp6" #define RPCBIND_NETID_LOCAL "local" /* diff --git a/include/linux/sunrpc/xprtrdma.h b/include/linux/sunrpc/xprtrdma.h index 64a0a0a97b23..c984c85981ea 100644 --- a/include/linux/sunrpc/xprtrdma.h +++ b/include/linux/sunrpc/xprtrdma.h @@ -40,11 +40,6 @@ #ifndef _LINUX_SUNRPC_XPRTRDMA_H #define _LINUX_SUNRPC_XPRTRDMA_H -/* - * rpcbind (v3+) RDMA netid. - */ -#define RPCBIND_NETID_RDMA "rdma" - /* * Constants. Max RPC/NFS header is big enough to account for * additional marshaling buffers passed down by Linux client. -- cgit v1.2.3 From 92f1719407b90475b3be0b7b9c983dec2ff8351e Mon Sep 17 00:00:00 2001 From: Richard Cochran Date: Sun, 29 Mar 2015 23:11:51 +0200 Subject: ptp: introduce get/set time methods with explicit 64 bit seconds. Converting the PHC drivers over to the new methods is one step along the way to making them ready for 2038. Once all the drivers are up to date, then the old methods will be removed. Signed-off-by: Richard Cochran Signed-off-by: David S. Miller --- include/linux/ptp_clock_kernel.h | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/linux/ptp_clock_kernel.h b/include/linux/ptp_clock_kernel.h index 0d8ff3fb84ba..7d6f8e66396f 100644 --- a/include/linux/ptp_clock_kernel.h +++ b/include/linux/ptp_clock_kernel.h @@ -64,12 +64,18 @@ struct ptp_clock_request { * @adjtime: Shifts the time of the hardware clock. * parameter delta: Desired change in nanoseconds. * - * @gettime: Reads the current time from the hardware clock. + * @gettime: Reads the current time from the hardware clock. (deprecated) * parameter ts: Holds the result. * - * @settime: Set the current time on the hardware clock. + * @settime: Set the current time on the hardware clock. (deprecated) * parameter ts: Time value to set. * + * @gettime64: Reads the current time from the hardware clock. + * parameter ts: Holds the result. + * + * @settime64: Set the current time on the hardware clock. + * parameter ts: Time value to set. + * * @enable: Request driver to enable or disable an ancillary feature. * parameter request: Desired resource to enable or disable. * parameter on: Caller passes one to enable or zero to disable. @@ -106,6 +112,8 @@ struct ptp_clock_info { int (*adjtime)(struct ptp_clock_info *ptp, s64 delta); int (*gettime)(struct ptp_clock_info *ptp, struct timespec *ts); int (*settime)(struct ptp_clock_info *ptp, const struct timespec *ts); + int (*gettime64)(struct ptp_clock_info *ptp, struct timespec64 *ts); + int (*settime64)(struct ptp_clock_info *p, const struct timespec64 *ts); int (*enable)(struct ptp_clock_info *ptp, struct ptp_clock_request *request, int on); int (*verify)(struct ptp_clock_info *ptp, unsigned int pin, -- cgit v1.2.3 From ed7c6317bc599502e1fdc7f5f95cb9a5550360a4 Mon Sep 17 00:00:00 2001 From: Richard Cochran Date: Sun, 29 Mar 2015 23:12:13 +0200 Subject: ptp: remove 32 bit get/set methods. All of the PHC drivers have been converted to the new methods. This patch converts the three remaining callers within the core code and removes the older methods for good. As a result, the core PHC code is ready for the year 2038. However, some of the PHC drivers are not quite ready yet. Signed-off-by: Richard Cochran Signed-off-by: David S. Miller --- drivers/ptp/ptp_chardev.c | 8 +------- drivers/ptp/ptp_clock.c | 15 ++++----------- include/linux/ptp_clock_kernel.h | 8 -------- 3 files changed, 5 insertions(+), 26 deletions(-) (limited to 'include') diff --git a/drivers/ptp/ptp_chardev.c b/drivers/ptp/ptp_chardev.c index 95bcf1525a84..da7bae991552 100644 --- a/drivers/ptp/ptp_chardev.c +++ b/drivers/ptp/ptp_chardev.c @@ -125,7 +125,6 @@ long ptp_ioctl(struct posix_clock *pc, unsigned int cmd, unsigned long arg) struct ptp_clock_info *ops = ptp->info; struct ptp_clock_time *pct; struct timespec64 ts; - struct timespec t2; int enable, err = 0; unsigned int i, pin_index; @@ -202,12 +201,7 @@ long ptp_ioctl(struct posix_clock *pc, unsigned int cmd, unsigned long arg) pct->sec = ts.tv_sec; pct->nsec = ts.tv_nsec; pct++; - if (ptp->info->gettime64) { - ptp->info->gettime64(ptp->info, &ts); - } else { - ptp->info->gettime(ptp->info, &t2); - ts = timespec_to_timespec64(t2); - } + ptp->info->gettime64(ptp->info, &ts); pct->sec = ts.tv_sec; pct->nsec = ts.tv_nsec; pct++; diff --git a/drivers/ptp/ptp_clock.c b/drivers/ptp/ptp_clock.c index df50d5eeae6f..2e481b9e8ea5 100644 --- a/drivers/ptp/ptp_clock.c +++ b/drivers/ptp/ptp_clock.c @@ -109,9 +109,7 @@ static int ptp_clock_settime(struct posix_clock *pc, const struct timespec *tp) struct ptp_clock *ptp = container_of(pc, struct ptp_clock, clock); struct timespec64 ts = timespec_to_timespec64(*tp); - return ptp->info->settime64 ? - ptp->info->settime64(ptp->info, &ts) : - ptp->info->settime(ptp->info, tp); + return ptp->info->settime64(ptp->info, &ts); } static int ptp_clock_gettime(struct posix_clock *pc, struct timespec *tp) @@ -120,14 +118,9 @@ static int ptp_clock_gettime(struct posix_clock *pc, struct timespec *tp) struct timespec64 ts; int err; - if (ptp->info->gettime64) { - err = ptp->info->gettime64(ptp->info, &ts); - if (!err) - *tp = timespec64_to_timespec(ts); - } else { - err = ptp->info->gettime(ptp->info, tp); - } - + err = ptp->info->gettime64(ptp->info, &ts); + if (!err) + *tp = timespec64_to_timespec(ts); return err; } diff --git a/include/linux/ptp_clock_kernel.h b/include/linux/ptp_clock_kernel.h index 7d6f8e66396f..b8b73066d137 100644 --- a/include/linux/ptp_clock_kernel.h +++ b/include/linux/ptp_clock_kernel.h @@ -64,12 +64,6 @@ struct ptp_clock_request { * @adjtime: Shifts the time of the hardware clock. * parameter delta: Desired change in nanoseconds. * - * @gettime: Reads the current time from the hardware clock. (deprecated) - * parameter ts: Holds the result. - * - * @settime: Set the current time on the hardware clock. (deprecated) - * parameter ts: Time value to set. - * * @gettime64: Reads the current time from the hardware clock. * parameter ts: Holds the result. * @@ -110,8 +104,6 @@ struct ptp_clock_info { struct ptp_pin_desc *pin_config; int (*adjfreq)(struct ptp_clock_info *ptp, s32 delta); int (*adjtime)(struct ptp_clock_info *ptp, s64 delta); - int (*gettime)(struct ptp_clock_info *ptp, struct timespec *ts); - int (*settime)(struct ptp_clock_info *ptp, const struct timespec *ts); int (*gettime64)(struct ptp_clock_info *ptp, struct timespec64 *ts); int (*settime64)(struct ptp_clock_info *p, const struct timespec64 *ts); int (*enable)(struct ptp_clock_info *ptp, -- cgit v1.2.3 From 52b09914af86fa3e728175c1125c91520e437b2f Mon Sep 17 00:00:00 2001 From: Mike Snitzer Date: Mon, 23 Feb 2015 16:36:41 -0500 Subject: dm: remove unnecessary wrapper around blk_lld_busy There is no need for DM to export a wrapper around the already exported blk_lld_busy(). Signed-off-by: Mike Snitzer --- drivers/md/dm-mpath.c | 2 +- drivers/md/dm.c | 6 ------ include/linux/device-mapper.h | 5 ----- 3 files changed, 1 insertion(+), 12 deletions(-) (limited to 'include') diff --git a/drivers/md/dm-mpath.c b/drivers/md/dm-mpath.c index d376dc87716e..add6391f3f8e 100644 --- a/drivers/md/dm-mpath.c +++ b/drivers/md/dm-mpath.c @@ -1627,7 +1627,7 @@ static int __pgpath_busy(struct pgpath *pgpath) { struct request_queue *q = bdev_get_queue(pgpath->path.dev->bdev); - return dm_underlying_device_busy(q); + return blk_lld_busy(q); } /* diff --git a/drivers/md/dm.c b/drivers/md/dm.c index 0e5f3441fcda..e7095ebb8d64 100644 --- a/drivers/md/dm.c +++ b/drivers/md/dm.c @@ -2006,12 +2006,6 @@ out: dm_put_live_table(md, srcu_idx); } -int dm_underlying_device_busy(struct request_queue *q) -{ - return blk_lld_busy(q); -} -EXPORT_SYMBOL_GPL(dm_underlying_device_busy); - static int dm_lld_busy(struct request_queue *q) { int r; diff --git a/include/linux/device-mapper.h b/include/linux/device-mapper.h index fd23978d93fe..51cc1deb7af3 100644 --- a/include/linux/device-mapper.h +++ b/include/linux/device-mapper.h @@ -605,9 +605,4 @@ static inline unsigned long to_bytes(sector_t n) return (n << SECTOR_SHIFT); } -/*----------------------------------------------------------------- - * Helper for block layer and dm core operations - *---------------------------------------------------------------*/ -int dm_underlying_device_busy(struct request_queue *q); - #endif /* _LINUX_DEVICE_MAPPER_H */ -- cgit v1.2.3 From 8f55db48608b109ad8c7ff4b946ad39b3189a540 Mon Sep 17 00:00:00 2001 From: Jiri Benc Date: Sun, 29 Mar 2015 16:59:23 +0200 Subject: tcp: simplify inetpeer_addr_base use In many places, the a6 field is typecasted to struct in6_addr. As the fields are in union anyway, just add in6_addr type to the union and get rid of the typecasting. Signed-off-by: Jiri Benc Signed-off-by: David S. Miller --- include/net/inetpeer.h | 3 ++- net/ipv4/tcp_metrics.c | 22 ++++++++-------------- 2 files changed, 10 insertions(+), 15 deletions(-) (limited to 'include') diff --git a/include/net/inetpeer.h b/include/net/inetpeer.h index 80479abddf73..d5332ddcea3f 100644 --- a/include/net/inetpeer.h +++ b/include/net/inetpeer.h @@ -19,6 +19,7 @@ struct inetpeer_addr_base { union { __be32 a4; __be32 a6[4]; + struct in6_addr in6; }; }; @@ -151,7 +152,7 @@ static inline struct inet_peer *inet_getpeer_v6(struct inet_peer_base *base, { struct inetpeer_addr daddr; - *(struct in6_addr *)daddr.addr.a6 = *v6daddr; + daddr.addr.in6 = *v6daddr; daddr.family = AF_INET6; return inet_getpeer(base, &daddr, create); } diff --git a/net/ipv4/tcp_metrics.c b/net/ipv4/tcp_metrics.c index 5bef3513af77..f62c2c68ced0 100644 --- a/net/ipv4/tcp_metrics.c +++ b/net/ipv4/tcp_metrics.c @@ -80,17 +80,11 @@ static void tcp_metric_set(struct tcp_metrics_block *tm, static bool addr_same(const struct inetpeer_addr *a, const struct inetpeer_addr *b) { - const struct in6_addr *a6, *b6; - if (a->family != b->family) return false; if (a->family == AF_INET) return a->addr.a4 == b->addr.a4; - - a6 = (const struct in6_addr *) &a->addr.a6[0]; - b6 = (const struct in6_addr *) &b->addr.a6[0]; - - return ipv6_addr_equal(a6, b6); + return ipv6_addr_equal(&a->addr.in6, &b->addr.in6); } struct tcpm_hash_bucket { @@ -256,8 +250,8 @@ static struct tcp_metrics_block *__tcp_get_metrics_req(struct request_sock *req, break; #if IS_ENABLED(CONFIG_IPV6) case AF_INET6: - *(struct in6_addr *)saddr.addr.a6 = inet_rsk(req)->ir_v6_loc_addr; - *(struct in6_addr *)daddr.addr.a6 = inet_rsk(req)->ir_v6_rmt_addr; + saddr.addr.in6 = inet_rsk(req)->ir_v6_loc_addr; + daddr.addr.in6 = inet_rsk(req)->ir_v6_rmt_addr; hash = ipv6_addr_hash(&inet_rsk(req)->ir_v6_rmt_addr); break; #endif @@ -304,9 +298,9 @@ static struct tcp_metrics_block *__tcp_get_metrics_tw(struct inet_timewait_sock hash = (__force unsigned int) daddr.addr.a4; } else { saddr.family = AF_INET6; - *(struct in6_addr *)saddr.addr.a6 = tw->tw_v6_rcv_saddr; + saddr.addr.in6 = tw->tw_v6_rcv_saddr; daddr.family = AF_INET6; - *(struct in6_addr *)daddr.addr.a6 = tw->tw_v6_daddr; + daddr.addr.in6 = tw->tw_v6_daddr; hash = ipv6_addr_hash(&tw->tw_v6_daddr); } } @@ -354,9 +348,9 @@ static struct tcp_metrics_block *tcp_get_metrics(struct sock *sk, hash = (__force unsigned int) daddr.addr.a4; } else { saddr.family = AF_INET6; - *(struct in6_addr *)saddr.addr.a6 = sk->sk_v6_rcv_saddr; + saddr.addr.in6 = sk->sk_v6_rcv_saddr; daddr.family = AF_INET6; - *(struct in6_addr *)daddr.addr.a6 = sk->sk_v6_daddr; + daddr.addr.in6 = sk->sk_v6_daddr; hash = ipv6_addr_hash(&sk->sk_v6_daddr); } } @@ -966,7 +960,7 @@ static int __parse_nl_addr(struct genl_info *info, struct inetpeer_addr *addr, addr->family = AF_INET6; memcpy(addr->addr.a6, nla_data(a), sizeof(addr->addr.a6)); if (hash) - *hash = ipv6_addr_hash((struct in6_addr *) addr->addr.a6); + *hash = ipv6_addr_hash(&addr->addr.in6); return 0; } return optional ? 1 : -EAFNOSUPPORT; -- cgit v1.2.3 From 15e318bdc6dfb82914c82fb7ad00badaa8387d8e Mon Sep 17 00:00:00 2001 From: Jiri Benc Date: Sun, 29 Mar 2015 16:59:24 +0200 Subject: xfrm: simplify xfrm_address_t use In many places, the a6 field is typecasted to struct in6_addr. As the fields are in union anyway, just add in6_addr type to the union and get rid of the typecasting. Modifying the uapi header is okay, the union has still the same size. Signed-off-by: Jiri Benc Signed-off-by: David S. Miller --- include/net/xfrm.h | 6 +++--- include/uapi/linux/xfrm.h | 2 ++ net/ipv6/xfrm6_mode_beet.c | 4 ++-- net/ipv6/xfrm6_policy.c | 4 +--- net/key/af_key.c | 2 +- net/xfrm/xfrm_state.c | 8 ++++---- 6 files changed, 13 insertions(+), 13 deletions(-) (limited to 'include') diff --git a/include/net/xfrm.h b/include/net/xfrm.h index d0ac7d7be8a7..461f83539493 100644 --- a/include/net/xfrm.h +++ b/include/net/xfrm.h @@ -1025,7 +1025,7 @@ xfrm_addr_any(const xfrm_address_t *addr, unsigned short family) case AF_INET: return addr->a4 == 0; case AF_INET6: - return ipv6_addr_any((struct in6_addr *)&addr->a6); + return ipv6_addr_any(&addr->in6); } return 0; } @@ -1238,8 +1238,8 @@ void xfrm_flowi_addr_get(const struct flowi *fl, memcpy(&daddr->a4, &fl->u.ip4.daddr, sizeof(daddr->a4)); break; case AF_INET6: - *(struct in6_addr *)saddr->a6 = fl->u.ip6.saddr; - *(struct in6_addr *)daddr->a6 = fl->u.ip6.daddr; + saddr->in6 = fl->u.ip6.saddr; + daddr->in6 = fl->u.ip6.daddr; break; } } diff --git a/include/uapi/linux/xfrm.h b/include/uapi/linux/xfrm.h index 02d5125a5ee8..2cd9e608d0d1 100644 --- a/include/uapi/linux/xfrm.h +++ b/include/uapi/linux/xfrm.h @@ -1,6 +1,7 @@ #ifndef _LINUX_XFRM_H #define _LINUX_XFRM_H +#include #include /* All of the structures in this file may not change size as they are @@ -13,6 +14,7 @@ typedef union { __be32 a4; __be32 a6[4]; + struct in6_addr in6; } xfrm_address_t; /* Ident of a specific xfrm_state. It is used on input to lookup diff --git a/net/ipv6/xfrm6_mode_beet.c b/net/ipv6/xfrm6_mode_beet.c index 9949a356d62c..1e205c3253ac 100644 --- a/net/ipv6/xfrm6_mode_beet.c +++ b/net/ipv6/xfrm6_mode_beet.c @@ -95,8 +95,8 @@ static int xfrm6_beet_input(struct xfrm_state *x, struct sk_buff *skb) ip6h = ipv6_hdr(skb); ip6h->payload_len = htons(skb->len - size); - ip6h->daddr = *(struct in6_addr *)&x->sel.daddr.a6; - ip6h->saddr = *(struct in6_addr *)&x->sel.saddr.a6; + ip6h->daddr = x->sel.daddr.in6; + ip6h->saddr = x->sel.saddr.in6; err = 0; out: return err; diff --git a/net/ipv6/xfrm6_policy.c b/net/ipv6/xfrm6_policy.c index 91d934c22a2a..f337a908a76a 100644 --- a/net/ipv6/xfrm6_policy.c +++ b/net/ipv6/xfrm6_policy.c @@ -61,9 +61,7 @@ static int xfrm6_get_saddr(struct net *net, return -EHOSTUNREACH; dev = ip6_dst_idev(dst)->dev; - ipv6_dev_get_saddr(dev_net(dev), dev, - (struct in6_addr *)&daddr->a6, 0, - (struct in6_addr *)&saddr->a6); + ipv6_dev_get_saddr(dev_net(dev), dev, &daddr->in6, 0, &saddr->in6); dst_release(dst); return 0; } diff --git a/net/key/af_key.c b/net/key/af_key.c index 9255fd9d94bc..f0d52d721b3a 100644 --- a/net/key/af_key.c +++ b/net/key/af_key.c @@ -709,7 +709,7 @@ static unsigned int pfkey_sockaddr_fill(const xfrm_address_t *xaddr, __be16 port sin6->sin6_family = AF_INET6; sin6->sin6_port = port; sin6->sin6_flowinfo = 0; - sin6->sin6_addr = *(struct in6_addr *)xaddr->a6; + sin6->sin6_addr = xaddr->in6; sin6->sin6_scope_id = 0; return 128; } diff --git a/net/xfrm/xfrm_state.c b/net/xfrm/xfrm_state.c index de971b6d38c5..f5e39e35d73a 100644 --- a/net/xfrm/xfrm_state.c +++ b/net/xfrm/xfrm_state.c @@ -1043,12 +1043,12 @@ static struct xfrm_state *__find_acq_core(struct net *net, break; case AF_INET6: - *(struct in6_addr *)x->sel.daddr.a6 = *(struct in6_addr *)daddr; - *(struct in6_addr *)x->sel.saddr.a6 = *(struct in6_addr *)saddr; + x->sel.daddr.in6 = daddr->in6; + x->sel.saddr.in6 = saddr->in6; x->sel.prefixlen_d = 128; x->sel.prefixlen_s = 128; - *(struct in6_addr *)x->props.saddr.a6 = *(struct in6_addr *)saddr; - *(struct in6_addr *)x->id.daddr.a6 = *(struct in6_addr *)daddr; + x->props.saddr.in6 = saddr->in6; + x->id.daddr.in6 = daddr->in6; break; } -- cgit v1.2.3 From 930345ea630405aa6e6f42efcb149c3f360a6b67 Mon Sep 17 00:00:00 2001 From: Jiri Benc Date: Sun, 29 Mar 2015 16:59:25 +0200 Subject: netlink: implement nla_put_in_addr and nla_put_in6_addr IP addresses are often stored in netlink attributes. Add generic functions to do that. For nla_put_in_addr, it would be nicer to pass struct in_addr but this is not used universally throughout the kernel, in way too many places __be32 is used to store IPv4 address. Signed-off-by: Jiri Benc Signed-off-by: David S. Miller --- drivers/net/vxlan.c | 22 +++++++++---------- include/linux/netfilter/ipset/ip_set.h | 5 ++--- include/net/netlink.h | 29 ++++++++++++++++++++++++++ net/ipv4/devinet.c | 6 +++--- net/ipv4/fib_rules.c | 4 ++-- net/ipv4/fib_semantics.c | 8 +++---- net/ipv4/ip_gre.c | 4 ++-- net/ipv4/ip_vti.c | 4 ++-- net/ipv4/ipip.c | 4 ++-- net/ipv4/ipmr.c | 4 ++-- net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c | 4 ++-- net/ipv4/route.c | 8 +++---- net/ipv4/tcp_metrics.c | 16 +++++++------- net/ipv6/addrconf.c | 10 ++++----- net/ipv6/addrlabel.c | 2 +- net/ipv6/fib6_rules.c | 6 ++---- net/ipv6/ip6_gre.c | 4 ++-- net/ipv6/ip6_tunnel.c | 6 ++---- net/ipv6/ip6_vti.c | 6 ++---- net/ipv6/ip6mr.c | 4 ++-- net/ipv6/ndisc.c | 3 +-- net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c | 6 ++---- net/ipv6/route.c | 14 ++++++------- net/ipv6/sit.c | 12 +++++------ net/l2tp/l2tp_netlink.c | 14 +++++++------ net/netlabel/netlabel_mgmt.c | 20 +++++++----------- net/netlabel/netlabel_unlabeled.c | 28 +++++++++++-------------- net/openvswitch/flow_netlink.c | 6 ++++-- net/wireless/nl80211.c | 4 ++-- 29 files changed, 139 insertions(+), 124 deletions(-) (limited to 'include') diff --git a/drivers/net/vxlan.c b/drivers/net/vxlan.c index 1c80b67c688d..86f085f95408 100644 --- a/drivers/net/vxlan.c +++ b/drivers/net/vxlan.c @@ -187,9 +187,9 @@ static int vxlan_nla_put_addr(struct sk_buff *skb, int attr, const union vxlan_addr *ip) { if (ip->sa.sa_family == AF_INET6) - return nla_put(skb, attr, sizeof(struct in6_addr), &ip->sin6.sin6_addr); + return nla_put_in6_addr(skb, attr, &ip->sin6.sin6_addr); else - return nla_put_be32(skb, attr, ip->sin.sin_addr.s_addr); + return nla_put_in_addr(skb, attr, ip->sin.sin_addr.s_addr); } #else /* !CONFIG_IPV6 */ @@ -226,7 +226,7 @@ static int vxlan_nla_get_addr(union vxlan_addr *ip, struct nlattr *nla) static int vxlan_nla_put_addr(struct sk_buff *skb, int attr, const union vxlan_addr *ip) { - return nla_put_be32(skb, attr, ip->sin.sin_addr.s_addr); + return nla_put_in_addr(skb, attr, ip->sin.sin_addr.s_addr); } #endif @@ -2807,13 +2807,13 @@ static int vxlan_fill_info(struct sk_buff *skb, const struct net_device *dev) if (!vxlan_addr_any(&dst->remote_ip)) { if (dst->remote_ip.sa.sa_family == AF_INET) { - if (nla_put_be32(skb, IFLA_VXLAN_GROUP, - dst->remote_ip.sin.sin_addr.s_addr)) + if (nla_put_in_addr(skb, IFLA_VXLAN_GROUP, + dst->remote_ip.sin.sin_addr.s_addr)) goto nla_put_failure; #if IS_ENABLED(CONFIG_IPV6) } else { - if (nla_put(skb, IFLA_VXLAN_GROUP6, sizeof(struct in6_addr), - &dst->remote_ip.sin6.sin6_addr)) + if (nla_put_in6_addr(skb, IFLA_VXLAN_GROUP6, + &dst->remote_ip.sin6.sin6_addr)) goto nla_put_failure; #endif } @@ -2824,13 +2824,13 @@ static int vxlan_fill_info(struct sk_buff *skb, const struct net_device *dev) if (!vxlan_addr_any(&vxlan->saddr)) { if (vxlan->saddr.sa.sa_family == AF_INET) { - if (nla_put_be32(skb, IFLA_VXLAN_LOCAL, - vxlan->saddr.sin.sin_addr.s_addr)) + if (nla_put_in_addr(skb, IFLA_VXLAN_LOCAL, + vxlan->saddr.sin.sin_addr.s_addr)) goto nla_put_failure; #if IS_ENABLED(CONFIG_IPV6) } else { - if (nla_put(skb, IFLA_VXLAN_LOCAL6, sizeof(struct in6_addr), - &vxlan->saddr.sin6.sin6_addr)) + if (nla_put_in6_addr(skb, IFLA_VXLAN_LOCAL6, + &vxlan->saddr.sin6.sin6_addr)) goto nla_put_failure; #endif } diff --git a/include/linux/netfilter/ipset/ip_set.h b/include/linux/netfilter/ipset/ip_set.h index f1606fa6132d..34b172301558 100644 --- a/include/linux/netfilter/ipset/ip_set.h +++ b/include/linux/netfilter/ipset/ip_set.h @@ -483,7 +483,7 @@ static inline int nla_put_ipaddr4(struct sk_buff *skb, int type, __be32 ipaddr) if (!__nested) return -EMSGSIZE; - ret = nla_put_net32(skb, IPSET_ATTR_IPADDR_IPV4, ipaddr); + ret = nla_put_in_addr(skb, IPSET_ATTR_IPADDR_IPV4, ipaddr); if (!ret) ipset_nest_end(skb, __nested); return ret; @@ -497,8 +497,7 @@ static inline int nla_put_ipaddr6(struct sk_buff *skb, int type, if (!__nested) return -EMSGSIZE; - ret = nla_put(skb, IPSET_ATTR_IPADDR_IPV6, - sizeof(struct in6_addr), ipaddrptr); + ret = nla_put_in6_addr(skb, IPSET_ATTR_IPADDR_IPV6, ipaddrptr); if (!ret) ipset_nest_end(skb, __nested); return ret; diff --git a/include/net/netlink.h b/include/net/netlink.h index e010ee8da41d..17fc76e5b05e 100644 --- a/include/net/netlink.h +++ b/include/net/netlink.h @@ -4,6 +4,7 @@ #include #include #include +#include /* ======================================================================== * Netlink Messages and Attributes Interface (As Seen On TV) @@ -105,6 +106,8 @@ * nla_put_string(skb, type, str) add string attribute to skb * nla_put_flag(skb, type) add flag attribute to skb * nla_put_msecs(skb, type, jiffies) add msecs attribute to skb + * nla_put_in_addr(skb, type, addr) add IPv4 address attribute to skb + * nla_put_in6_addr(skb, type, addr) add IPv6 address attribute to skb * * Nested Attributes Construction: * nla_nest_start(skb, type) start a nested attribute @@ -956,6 +959,32 @@ static inline int nla_put_msecs(struct sk_buff *skb, int attrtype, return nla_put(skb, attrtype, sizeof(u64), &tmp); } +/** + * nla_put_in_addr - Add an IPv4 address netlink attribute to a socket + * buffer + * @skb: socket buffer to add attribute to + * @attrtype: attribute type + * @addr: IPv4 address + */ +static inline int nla_put_in_addr(struct sk_buff *skb, int attrtype, + __be32 addr) +{ + return nla_put_be32(skb, attrtype, addr); +} + +/** + * nla_put_in6_addr - Add an IPv6 address netlink attribute to a socket + * buffer + * @skb: socket buffer to add attribute to + * @attrtype: attribute type + * @addr: IPv6 address + */ +static inline int nla_put_in6_addr(struct sk_buff *skb, int attrtype, + const struct in6_addr *addr) +{ + return nla_put(skb, attrtype, sizeof(*addr), addr); +} + /** * nla_get_u32 - return payload of u32 attribute * @nla: u32 netlink attribute diff --git a/net/ipv4/devinet.c b/net/ipv4/devinet.c index 975ee5e30c64..66cd85973056 100644 --- a/net/ipv4/devinet.c +++ b/net/ipv4/devinet.c @@ -1541,11 +1541,11 @@ static int inet_fill_ifaddr(struct sk_buff *skb, struct in_ifaddr *ifa, valid = INFINITY_LIFE_TIME; } if ((ifa->ifa_address && - nla_put_be32(skb, IFA_ADDRESS, ifa->ifa_address)) || + nla_put_in_addr(skb, IFA_ADDRESS, ifa->ifa_address)) || (ifa->ifa_local && - nla_put_be32(skb, IFA_LOCAL, ifa->ifa_local)) || + nla_put_in_addr(skb, IFA_LOCAL, ifa->ifa_local)) || (ifa->ifa_broadcast && - nla_put_be32(skb, IFA_BROADCAST, ifa->ifa_broadcast)) || + nla_put_in_addr(skb, IFA_BROADCAST, ifa->ifa_broadcast)) || (ifa->ifa_label[0] && nla_put_string(skb, IFA_LABEL, ifa->ifa_label)) || nla_put_u32(skb, IFA_FLAGS, ifa->ifa_flags) || diff --git a/net/ipv4/fib_rules.c b/net/ipv4/fib_rules.c index e9bc5e42cf43..edfea0deec43 100644 --- a/net/ipv4/fib_rules.c +++ b/net/ipv4/fib_rules.c @@ -279,9 +279,9 @@ static int fib4_rule_fill(struct fib_rule *rule, struct sk_buff *skb, frh->tos = rule4->tos; if ((rule4->dst_len && - nla_put_be32(skb, FRA_DST, rule4->dst)) || + nla_put_in_addr(skb, FRA_DST, rule4->dst)) || (rule4->src_len && - nla_put_be32(skb, FRA_SRC, rule4->src))) + nla_put_in_addr(skb, FRA_SRC, rule4->src))) goto nla_put_failure; #ifdef CONFIG_IP_ROUTE_CLASSID if (rule4->tclassid && diff --git a/net/ipv4/fib_semantics.c b/net/ipv4/fib_semantics.c index 66c1e4fbf884..453b24e5322c 100644 --- a/net/ipv4/fib_semantics.c +++ b/net/ipv4/fib_semantics.c @@ -1015,7 +1015,7 @@ int fib_dump_info(struct sk_buff *skb, u32 portid, u32 seq, int event, rtm->rtm_protocol = fi->fib_protocol; if (rtm->rtm_dst_len && - nla_put_be32(skb, RTA_DST, dst)) + nla_put_in_addr(skb, RTA_DST, dst)) goto nla_put_failure; if (fi->fib_priority && nla_put_u32(skb, RTA_PRIORITY, fi->fib_priority)) @@ -1024,11 +1024,11 @@ int fib_dump_info(struct sk_buff *skb, u32 portid, u32 seq, int event, goto nla_put_failure; if (fi->fib_prefsrc && - nla_put_be32(skb, RTA_PREFSRC, fi->fib_prefsrc)) + nla_put_in_addr(skb, RTA_PREFSRC, fi->fib_prefsrc)) goto nla_put_failure; if (fi->fib_nhs == 1) { if (fi->fib_nh->nh_gw && - nla_put_be32(skb, RTA_GATEWAY, fi->fib_nh->nh_gw)) + nla_put_in_addr(skb, RTA_GATEWAY, fi->fib_nh->nh_gw)) goto nla_put_failure; if (fi->fib_nh->nh_oif && nla_put_u32(skb, RTA_OIF, fi->fib_nh->nh_oif)) @@ -1058,7 +1058,7 @@ int fib_dump_info(struct sk_buff *skb, u32 portid, u32 seq, int event, rtnh->rtnh_ifindex = nh->nh_oif; if (nh->nh_gw && - nla_put_be32(skb, RTA_GATEWAY, nh->nh_gw)) + nla_put_in_addr(skb, RTA_GATEWAY, nh->nh_gw)) goto nla_put_failure; #ifdef CONFIG_IP_ROUTE_CLASSID if (nh->nh_tclassid && diff --git a/net/ipv4/ip_gre.c b/net/ipv4/ip_gre.c index 6207275fc749..2e878df46075 100644 --- a/net/ipv4/ip_gre.c +++ b/net/ipv4/ip_gre.c @@ -776,8 +776,8 @@ static int ipgre_fill_info(struct sk_buff *skb, const struct net_device *dev) nla_put_be16(skb, IFLA_GRE_OFLAGS, tnl_flags_to_gre_flags(p->o_flags)) || nla_put_be32(skb, IFLA_GRE_IKEY, p->i_key) || nla_put_be32(skb, IFLA_GRE_OKEY, p->o_key) || - nla_put_be32(skb, IFLA_GRE_LOCAL, p->iph.saddr) || - nla_put_be32(skb, IFLA_GRE_REMOTE, p->iph.daddr) || + nla_put_in_addr(skb, IFLA_GRE_LOCAL, p->iph.saddr) || + nla_put_in_addr(skb, IFLA_GRE_REMOTE, p->iph.daddr) || nla_put_u8(skb, IFLA_GRE_TTL, p->iph.ttl) || nla_put_u8(skb, IFLA_GRE_TOS, p->iph.tos) || nla_put_u8(skb, IFLA_GRE_PMTUDISC, diff --git a/net/ipv4/ip_vti.c b/net/ipv4/ip_vti.c index 94efe148181c..f189f2a8aaa5 100644 --- a/net/ipv4/ip_vti.c +++ b/net/ipv4/ip_vti.c @@ -505,8 +505,8 @@ static int vti_fill_info(struct sk_buff *skb, const struct net_device *dev) nla_put_u32(skb, IFLA_VTI_LINK, p->link); nla_put_be32(skb, IFLA_VTI_IKEY, p->i_key); nla_put_be32(skb, IFLA_VTI_OKEY, p->o_key); - nla_put_be32(skb, IFLA_VTI_LOCAL, p->iph.saddr); - nla_put_be32(skb, IFLA_VTI_REMOTE, p->iph.daddr); + nla_put_in_addr(skb, IFLA_VTI_LOCAL, p->iph.saddr); + nla_put_in_addr(skb, IFLA_VTI_REMOTE, p->iph.daddr); return 0; } diff --git a/net/ipv4/ipip.c b/net/ipv4/ipip.c index 915d215a7d14..17df8d38bbbd 100644 --- a/net/ipv4/ipip.c +++ b/net/ipv4/ipip.c @@ -450,8 +450,8 @@ static int ipip_fill_info(struct sk_buff *skb, const struct net_device *dev) struct ip_tunnel_parm *parm = &tunnel->parms; if (nla_put_u32(skb, IFLA_IPTUN_LINK, parm->link) || - nla_put_be32(skb, IFLA_IPTUN_LOCAL, parm->iph.saddr) || - nla_put_be32(skb, IFLA_IPTUN_REMOTE, parm->iph.daddr) || + nla_put_in_addr(skb, IFLA_IPTUN_LOCAL, parm->iph.saddr) || + nla_put_in_addr(skb, IFLA_IPTUN_REMOTE, parm->iph.daddr) || nla_put_u8(skb, IFLA_IPTUN_TTL, parm->iph.ttl) || nla_put_u8(skb, IFLA_IPTUN_TOS, parm->iph.tos) || nla_put_u8(skb, IFLA_IPTUN_PMTUDISC, diff --git a/net/ipv4/ipmr.c b/net/ipv4/ipmr.c index c688cd1b2110..b4a545d24adb 100644 --- a/net/ipv4/ipmr.c +++ b/net/ipv4/ipmr.c @@ -2281,8 +2281,8 @@ static int ipmr_fill_mroute(struct mr_table *mrt, struct sk_buff *skb, rtm->rtm_protocol = RTPROT_MROUTED; rtm->rtm_flags = 0; - if (nla_put_be32(skb, RTA_SRC, c->mfc_origin) || - nla_put_be32(skb, RTA_DST, c->mfc_mcastgrp)) + if (nla_put_in_addr(skb, RTA_SRC, c->mfc_origin) || + nla_put_in_addr(skb, RTA_DST, c->mfc_mcastgrp)) goto nla_put_failure; err = __ipmr_fill_mroute(mrt, skb, c, rtm); /* do not break the dump if cache is unresolved */ diff --git a/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c b/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c index 5c61328b7704..b36ebfc6b812 100644 --- a/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c +++ b/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c @@ -322,8 +322,8 @@ getorigdst(struct sock *sk, int optval, void __user *user, int *len) static int ipv4_tuple_to_nlattr(struct sk_buff *skb, const struct nf_conntrack_tuple *tuple) { - if (nla_put_be32(skb, CTA_IP_V4_SRC, tuple->src.u3.ip) || - nla_put_be32(skb, CTA_IP_V4_DST, tuple->dst.u3.ip)) + if (nla_put_in_addr(skb, CTA_IP_V4_SRC, tuple->src.u3.ip) || + nla_put_in_addr(skb, CTA_IP_V4_DST, tuple->dst.u3.ip)) goto nla_put_failure; return 0; diff --git a/net/ipv4/route.c b/net/ipv4/route.c index be8703d02ef0..1f147204f1f3 100644 --- a/net/ipv4/route.c +++ b/net/ipv4/route.c @@ -2319,11 +2319,11 @@ static int rt_fill_info(struct net *net, __be32 dst, __be32 src, if (IPCB(skb)->flags & IPSKB_DOREDIRECT) r->rtm_flags |= RTCF_DOREDIRECT; - if (nla_put_be32(skb, RTA_DST, dst)) + if (nla_put_in_addr(skb, RTA_DST, dst)) goto nla_put_failure; if (src) { r->rtm_src_len = 32; - if (nla_put_be32(skb, RTA_SRC, src)) + if (nla_put_in_addr(skb, RTA_SRC, src)) goto nla_put_failure; } if (rt->dst.dev && @@ -2336,11 +2336,11 @@ static int rt_fill_info(struct net *net, __be32 dst, __be32 src, #endif if (!rt_is_input_route(rt) && fl4->saddr != src) { - if (nla_put_be32(skb, RTA_PREFSRC, fl4->saddr)) + if (nla_put_in_addr(skb, RTA_PREFSRC, fl4->saddr)) goto nla_put_failure; } if (rt->rt_uses_gateway && - nla_put_be32(skb, RTA_GATEWAY, rt->rt_gateway)) + nla_put_in_addr(skb, RTA_GATEWAY, rt->rt_gateway)) goto nla_put_failure; expires = rt->dst.expires; diff --git a/net/ipv4/tcp_metrics.c b/net/ipv4/tcp_metrics.c index f62c2c68ced0..32e36ea6bc0f 100644 --- a/net/ipv4/tcp_metrics.c +++ b/net/ipv4/tcp_metrics.c @@ -786,19 +786,19 @@ static int tcp_metrics_fill_info(struct sk_buff *msg, switch (tm->tcpm_daddr.family) { case AF_INET: - if (nla_put_be32(msg, TCP_METRICS_ATTR_ADDR_IPV4, - tm->tcpm_daddr.addr.a4) < 0) + if (nla_put_in_addr(msg, TCP_METRICS_ATTR_ADDR_IPV4, + tm->tcpm_daddr.addr.a4) < 0) goto nla_put_failure; - if (nla_put_be32(msg, TCP_METRICS_ATTR_SADDR_IPV4, - tm->tcpm_saddr.addr.a4) < 0) + if (nla_put_in_addr(msg, TCP_METRICS_ATTR_SADDR_IPV4, + tm->tcpm_saddr.addr.a4) < 0) goto nla_put_failure; break; case AF_INET6: - if (nla_put(msg, TCP_METRICS_ATTR_ADDR_IPV6, 16, - tm->tcpm_daddr.addr.a6) < 0) + if (nla_put_in6_addr(msg, TCP_METRICS_ATTR_ADDR_IPV6, + &tm->tcpm_daddr.addr.in6) < 0) goto nla_put_failure; - if (nla_put(msg, TCP_METRICS_ATTR_SADDR_IPV6, 16, - tm->tcpm_saddr.addr.a6) < 0) + if (nla_put_in6_addr(msg, TCP_METRICS_ATTR_SADDR_IPV6, + &tm->tcpm_saddr.addr.in6) < 0) goto nla_put_failure; break; default: diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c index e86f7434e3c3..5c9e94cb1b2c 100644 --- a/net/ipv6/addrconf.c +++ b/net/ipv6/addrconf.c @@ -4237,11 +4237,11 @@ static int inet6_fill_ifaddr(struct sk_buff *skb, struct inet6_ifaddr *ifa, } if (!ipv6_addr_any(&ifa->peer_addr)) { - if (nla_put(skb, IFA_LOCAL, 16, &ifa->addr) < 0 || - nla_put(skb, IFA_ADDRESS, 16, &ifa->peer_addr) < 0) + if (nla_put_in6_addr(skb, IFA_LOCAL, &ifa->addr) < 0 || + nla_put_in6_addr(skb, IFA_ADDRESS, &ifa->peer_addr) < 0) goto error; } else - if (nla_put(skb, IFA_ADDRESS, 16, &ifa->addr) < 0) + if (nla_put_in6_addr(skb, IFA_ADDRESS, &ifa->addr) < 0) goto error; if (put_cacheinfo(skb, ifa->cstamp, ifa->tstamp, preferred, valid) < 0) @@ -4273,7 +4273,7 @@ static int inet6_fill_ifmcaddr(struct sk_buff *skb, struct ifmcaddr6 *ifmca, return -EMSGSIZE; put_ifaddrmsg(nlh, 128, IFA_F_PERMANENT, scope, ifindex); - if (nla_put(skb, IFA_MULTICAST, 16, &ifmca->mca_addr) < 0 || + if (nla_put_in6_addr(skb, IFA_MULTICAST, &ifmca->mca_addr) < 0 || put_cacheinfo(skb, ifmca->mca_cstamp, ifmca->mca_tstamp, INFINITY_LIFE_TIME, INFINITY_LIFE_TIME) < 0) { nlmsg_cancel(skb, nlh); @@ -4299,7 +4299,7 @@ static int inet6_fill_ifacaddr(struct sk_buff *skb, struct ifacaddr6 *ifaca, return -EMSGSIZE; put_ifaddrmsg(nlh, 128, IFA_F_PERMANENT, scope, ifindex); - if (nla_put(skb, IFA_ANYCAST, 16, &ifaca->aca_addr) < 0 || + if (nla_put_in6_addr(skb, IFA_ANYCAST, &ifaca->aca_addr) < 0 || put_cacheinfo(skb, ifaca->aca_cstamp, ifaca->aca_tstamp, INFINITY_LIFE_TIME, INFINITY_LIFE_TIME) < 0) { nlmsg_cancel(skb, nlh); diff --git a/net/ipv6/addrlabel.c b/net/ipv6/addrlabel.c index 3cc50e2d3bf5..882124ebb438 100644 --- a/net/ipv6/addrlabel.c +++ b/net/ipv6/addrlabel.c @@ -477,7 +477,7 @@ static int ip6addrlbl_fill(struct sk_buff *skb, ip6addrlbl_putmsg(nlh, p->prefixlen, p->ifindex, lseq); - if (nla_put(skb, IFAL_ADDRESS, 16, &p->prefix) < 0 || + if (nla_put_in6_addr(skb, IFAL_ADDRESS, &p->prefix) < 0 || nla_put_u32(skb, IFAL_LABEL, p->label) < 0) { nlmsg_cancel(skb, nlh); return -EMSGSIZE; diff --git a/net/ipv6/fib6_rules.c b/net/ipv6/fib6_rules.c index 273eb26cd6d4..d313bfd88512 100644 --- a/net/ipv6/fib6_rules.c +++ b/net/ipv6/fib6_rules.c @@ -250,11 +250,9 @@ static int fib6_rule_fill(struct fib_rule *rule, struct sk_buff *skb, frh->tos = rule6->tclass; if ((rule6->dst.plen && - nla_put(skb, FRA_DST, sizeof(struct in6_addr), - &rule6->dst.addr)) || + nla_put_in6_addr(skb, FRA_DST, &rule6->dst.addr)) || (rule6->src.plen && - nla_put(skb, FRA_SRC, sizeof(struct in6_addr), - &rule6->src.addr))) + nla_put_in6_addr(skb, FRA_SRC, &rule6->src.addr))) goto nla_put_failure; return 0; diff --git a/net/ipv6/ip6_gre.c b/net/ipv6/ip6_gre.c index 67e014d88e55..f61f7ad2d045 100644 --- a/net/ipv6/ip6_gre.c +++ b/net/ipv6/ip6_gre.c @@ -1622,8 +1622,8 @@ static int ip6gre_fill_info(struct sk_buff *skb, const struct net_device *dev) nla_put_be16(skb, IFLA_GRE_OFLAGS, p->o_flags) || nla_put_be32(skb, IFLA_GRE_IKEY, p->i_key) || nla_put_be32(skb, IFLA_GRE_OKEY, p->o_key) || - nla_put(skb, IFLA_GRE_LOCAL, sizeof(struct in6_addr), &p->laddr) || - nla_put(skb, IFLA_GRE_REMOTE, sizeof(struct in6_addr), &p->raddr) || + nla_put_in6_addr(skb, IFLA_GRE_LOCAL, &p->laddr) || + nla_put_in6_addr(skb, IFLA_GRE_REMOTE, &p->raddr) || nla_put_u8(skb, IFLA_GRE_TTL, p->hop_limit) || /*nla_put_u8(skb, IFLA_GRE_TOS, t->priority) ||*/ nla_put_u8(skb, IFLA_GRE_ENCAP_LIMIT, p->encap_limit) || diff --git a/net/ipv6/ip6_tunnel.c b/net/ipv6/ip6_tunnel.c index 0c68012b6d6e..80543d13ea7c 100644 --- a/net/ipv6/ip6_tunnel.c +++ b/net/ipv6/ip6_tunnel.c @@ -1739,10 +1739,8 @@ static int ip6_tnl_fill_info(struct sk_buff *skb, const struct net_device *dev) struct __ip6_tnl_parm *parm = &tunnel->parms; if (nla_put_u32(skb, IFLA_IPTUN_LINK, parm->link) || - nla_put(skb, IFLA_IPTUN_LOCAL, sizeof(struct in6_addr), - &parm->laddr) || - nla_put(skb, IFLA_IPTUN_REMOTE, sizeof(struct in6_addr), - &parm->raddr) || + nla_put_in6_addr(skb, IFLA_IPTUN_LOCAL, &parm->laddr) || + nla_put_in6_addr(skb, IFLA_IPTUN_REMOTE, &parm->raddr) || nla_put_u8(skb, IFLA_IPTUN_TTL, parm->hop_limit) || nla_put_u8(skb, IFLA_IPTUN_ENCAP_LIMIT, parm->encap_limit) || nla_put_be32(skb, IFLA_IPTUN_FLOWINFO, parm->flowinfo) || diff --git a/net/ipv6/ip6_vti.c b/net/ipv6/ip6_vti.c index 1ec5b4a530d0..87a262b0f07b 100644 --- a/net/ipv6/ip6_vti.c +++ b/net/ipv6/ip6_vti.c @@ -983,10 +983,8 @@ static int vti6_fill_info(struct sk_buff *skb, const struct net_device *dev) struct __ip6_tnl_parm *parm = &tunnel->parms; if (nla_put_u32(skb, IFLA_VTI_LINK, parm->link) || - nla_put(skb, IFLA_VTI_LOCAL, sizeof(struct in6_addr), - &parm->laddr) || - nla_put(skb, IFLA_VTI_REMOTE, sizeof(struct in6_addr), - &parm->raddr) || + nla_put_in6_addr(skb, IFLA_VTI_LOCAL, &parm->laddr) || + nla_put_in6_addr(skb, IFLA_VTI_REMOTE, &parm->raddr) || nla_put_be32(skb, IFLA_VTI_IKEY, parm->i_key) || nla_put_be32(skb, IFLA_VTI_OKEY, parm->o_key)) goto nla_put_failure; diff --git a/net/ipv6/ip6mr.c b/net/ipv6/ip6mr.c index ff883c9d0e3c..caf6b99374e6 100644 --- a/net/ipv6/ip6mr.c +++ b/net/ipv6/ip6mr.c @@ -2378,8 +2378,8 @@ static int ip6mr_fill_mroute(struct mr6_table *mrt, struct sk_buff *skb, rtm->rtm_protocol = RTPROT_MROUTED; rtm->rtm_flags = 0; - if (nla_put(skb, RTA_SRC, 16, &c->mf6c_origin) || - nla_put(skb, RTA_DST, 16, &c->mf6c_mcastgrp)) + if (nla_put_in6_addr(skb, RTA_SRC, &c->mf6c_origin) || + nla_put_in6_addr(skb, RTA_DST, &c->mf6c_mcastgrp)) goto nla_put_failure; err = __ip6mr_fill_mroute(mrt, skb, c, rtm); /* do not break the dump if cache is unresolved */ diff --git a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c index 482dfb9f0f7e..c283827d60e2 100644 --- a/net/ipv6/ndisc.c +++ b/net/ipv6/ndisc.c @@ -1049,8 +1049,7 @@ static void ndisc_ra_useropt(struct sk_buff *ra, struct nd_opt_hdr *opt) memcpy(ndmsg + 1, opt, opt->nd_opt_len << 3); - if (nla_put(skb, NDUSEROPT_SRCADDR, sizeof(struct in6_addr), - &ipv6_hdr(ra)->saddr)) + if (nla_put_in6_addr(skb, NDUSEROPT_SRCADDR, &ipv6_hdr(ra)->saddr)) goto nla_put_failure; nlmsg_end(skb, nlh); diff --git a/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c b/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c index b68d0e59c1f8..78284a697439 100644 --- a/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c +++ b/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c @@ -290,10 +290,8 @@ ipv6_getorigdst(struct sock *sk, int optval, void __user *user, int *len) static int ipv6_tuple_to_nlattr(struct sk_buff *skb, const struct nf_conntrack_tuple *tuple) { - if (nla_put(skb, CTA_IP_V6_SRC, sizeof(u_int32_t) * 4, - &tuple->src.u3.ip6) || - nla_put(skb, CTA_IP_V6_DST, sizeof(u_int32_t) * 4, - &tuple->dst.u3.ip6)) + if (nla_put_in6_addr(skb, CTA_IP_V6_SRC, &tuple->src.u3.in6) || + nla_put_in6_addr(skb, CTA_IP_V6_DST, &tuple->dst.u3.in6)) goto nla_put_failure; return 0; diff --git a/net/ipv6/route.c b/net/ipv6/route.c index fe742fa0f7ff..385e9bd4f218 100644 --- a/net/ipv6/route.c +++ b/net/ipv6/route.c @@ -2669,19 +2669,19 @@ static int rt6_fill_node(struct net *net, rtm->rtm_flags |= RTM_F_CLONED; if (dst) { - if (nla_put(skb, RTA_DST, 16, dst)) + if (nla_put_in6_addr(skb, RTA_DST, dst)) goto nla_put_failure; rtm->rtm_dst_len = 128; } else if (rtm->rtm_dst_len) - if (nla_put(skb, RTA_DST, 16, &rt->rt6i_dst.addr)) + if (nla_put_in6_addr(skb, RTA_DST, &rt->rt6i_dst.addr)) goto nla_put_failure; #ifdef CONFIG_IPV6_SUBTREES if (src) { - if (nla_put(skb, RTA_SRC, 16, src)) + if (nla_put_in6_addr(skb, RTA_SRC, src)) goto nla_put_failure; rtm->rtm_src_len = 128; } else if (rtm->rtm_src_len && - nla_put(skb, RTA_SRC, 16, &rt->rt6i_src.addr)) + nla_put_in6_addr(skb, RTA_SRC, &rt->rt6i_src.addr)) goto nla_put_failure; #endif if (iif) { @@ -2705,14 +2705,14 @@ static int rt6_fill_node(struct net *net, } else if (dst) { struct in6_addr saddr_buf; if (ip6_route_get_saddr(net, rt, dst, 0, &saddr_buf) == 0 && - nla_put(skb, RTA_PREFSRC, 16, &saddr_buf)) + nla_put_in6_addr(skb, RTA_PREFSRC, &saddr_buf)) goto nla_put_failure; } if (rt->rt6i_prefsrc.plen) { struct in6_addr saddr_buf; saddr_buf = rt->rt6i_prefsrc.addr; - if (nla_put(skb, RTA_PREFSRC, 16, &saddr_buf)) + if (nla_put_in6_addr(skb, RTA_PREFSRC, &saddr_buf)) goto nla_put_failure; } @@ -2720,7 +2720,7 @@ static int rt6_fill_node(struct net *net, goto nla_put_failure; if (rt->rt6i_flags & RTF_GATEWAY) { - if (nla_put(skb, RTA_GATEWAY, 16, &rt->rt6i_gateway) < 0) + if (nla_put_in6_addr(skb, RTA_GATEWAY, &rt->rt6i_gateway) < 0) goto nla_put_failure; } diff --git a/net/ipv6/sit.c b/net/ipv6/sit.c index 92692a7e8a2b..0e2bb538a556 100644 --- a/net/ipv6/sit.c +++ b/net/ipv6/sit.c @@ -1683,8 +1683,8 @@ static int ipip6_fill_info(struct sk_buff *skb, const struct net_device *dev) struct ip_tunnel_parm *parm = &tunnel->parms; if (nla_put_u32(skb, IFLA_IPTUN_LINK, parm->link) || - nla_put_be32(skb, IFLA_IPTUN_LOCAL, parm->iph.saddr) || - nla_put_be32(skb, IFLA_IPTUN_REMOTE, parm->iph.daddr) || + nla_put_in_addr(skb, IFLA_IPTUN_LOCAL, parm->iph.saddr) || + nla_put_in_addr(skb, IFLA_IPTUN_REMOTE, parm->iph.daddr) || nla_put_u8(skb, IFLA_IPTUN_TTL, parm->iph.ttl) || nla_put_u8(skb, IFLA_IPTUN_TOS, parm->iph.tos) || nla_put_u8(skb, IFLA_IPTUN_PMTUDISC, @@ -1694,10 +1694,10 @@ static int ipip6_fill_info(struct sk_buff *skb, const struct net_device *dev) goto nla_put_failure; #ifdef CONFIG_IPV6_SIT_6RD - if (nla_put(skb, IFLA_IPTUN_6RD_PREFIX, sizeof(struct in6_addr), - &tunnel->ip6rd.prefix) || - nla_put_be32(skb, IFLA_IPTUN_6RD_RELAY_PREFIX, - tunnel->ip6rd.relay_prefix) || + if (nla_put_in6_addr(skb, IFLA_IPTUN_6RD_PREFIX, + &tunnel->ip6rd.prefix) || + nla_put_in_addr(skb, IFLA_IPTUN_6RD_RELAY_PREFIX, + tunnel->ip6rd.relay_prefix) || nla_put_u16(skb, IFLA_IPTUN_6RD_PREFIXLEN, tunnel->ip6rd.prefixlen) || nla_put_u16(skb, IFLA_IPTUN_6RD_RELAY_PREFIXLEN, diff --git a/net/l2tp/l2tp_netlink.c b/net/l2tp/l2tp_netlink.c index b4e923f77954..a4f78d36bace 100644 --- a/net/l2tp/l2tp_netlink.c +++ b/net/l2tp/l2tp_netlink.c @@ -376,15 +376,17 @@ static int l2tp_nl_tunnel_send(struct sk_buff *skb, u32 portid, u32 seq, int fla case L2TP_ENCAPTYPE_IP: #if IS_ENABLED(CONFIG_IPV6) if (np) { - if (nla_put(skb, L2TP_ATTR_IP6_SADDR, sizeof(np->saddr), - &np->saddr) || - nla_put(skb, L2TP_ATTR_IP6_DADDR, sizeof(sk->sk_v6_daddr), - &sk->sk_v6_daddr)) + if (nla_put_in6_addr(skb, L2TP_ATTR_IP6_SADDR, + &np->saddr) || + nla_put_in6_addr(skb, L2TP_ATTR_IP6_DADDR, + &sk->sk_v6_daddr)) goto nla_put_failure; } else #endif - if (nla_put_be32(skb, L2TP_ATTR_IP_SADDR, inet->inet_saddr) || - nla_put_be32(skb, L2TP_ATTR_IP_DADDR, inet->inet_daddr)) + if (nla_put_in_addr(skb, L2TP_ATTR_IP_SADDR, + inet->inet_saddr) || + nla_put_in_addr(skb, L2TP_ATTR_IP_DADDR, + inet->inet_daddr)) goto nla_put_failure; break; } diff --git a/net/netlabel/netlabel_mgmt.c b/net/netlabel/netlabel_mgmt.c index 70440748fe5c..13f777f20995 100644 --- a/net/netlabel/netlabel_mgmt.c +++ b/net/netlabel/netlabel_mgmt.c @@ -293,15 +293,13 @@ static int netlbl_mgmt_listentry(struct sk_buff *skb, return -ENOMEM; addr_struct.s_addr = iter4->addr; - ret_val = nla_put(skb, NLBL_MGMT_A_IPV4ADDR, - sizeof(struct in_addr), - &addr_struct); + ret_val = nla_put_in_addr(skb, NLBL_MGMT_A_IPV4ADDR, + addr_struct.s_addr); if (ret_val != 0) return ret_val; addr_struct.s_addr = iter4->mask; - ret_val = nla_put(skb, NLBL_MGMT_A_IPV4MASK, - sizeof(struct in_addr), - &addr_struct); + ret_val = nla_put_in_addr(skb, NLBL_MGMT_A_IPV4MASK, + addr_struct.s_addr); if (ret_val != 0) return ret_val; map4 = netlbl_domhsh_addr4_entry(iter4); @@ -328,14 +326,12 @@ static int netlbl_mgmt_listentry(struct sk_buff *skb, if (nla_b == NULL) return -ENOMEM; - ret_val = nla_put(skb, NLBL_MGMT_A_IPV6ADDR, - sizeof(struct in6_addr), - &iter6->addr); + ret_val = nla_put_in6_addr(skb, NLBL_MGMT_A_IPV6ADDR, + &iter6->addr); if (ret_val != 0) return ret_val; - ret_val = nla_put(skb, NLBL_MGMT_A_IPV6MASK, - sizeof(struct in6_addr), - &iter6->mask); + ret_val = nla_put_in6_addr(skb, NLBL_MGMT_A_IPV6MASK, + &iter6->mask); if (ret_val != 0) return ret_val; map6 = netlbl_domhsh_addr6_entry(iter6); diff --git a/net/netlabel/netlabel_unlabeled.c b/net/netlabel/netlabel_unlabeled.c index aec7994f78cf..b0380927f05f 100644 --- a/net/netlabel/netlabel_unlabeled.c +++ b/net/netlabel/netlabel_unlabeled.c @@ -1117,34 +1117,30 @@ static int netlbl_unlabel_staticlist_gen(u32 cmd, struct in_addr addr_struct; addr_struct.s_addr = addr4->list.addr; - ret_val = nla_put(cb_arg->skb, - NLBL_UNLABEL_A_IPV4ADDR, - sizeof(struct in_addr), - &addr_struct); + ret_val = nla_put_in_addr(cb_arg->skb, + NLBL_UNLABEL_A_IPV4ADDR, + addr_struct.s_addr); if (ret_val != 0) goto list_cb_failure; addr_struct.s_addr = addr4->list.mask; - ret_val = nla_put(cb_arg->skb, - NLBL_UNLABEL_A_IPV4MASK, - sizeof(struct in_addr), - &addr_struct); + ret_val = nla_put_in_addr(cb_arg->skb, + NLBL_UNLABEL_A_IPV4MASK, + addr_struct.s_addr); if (ret_val != 0) goto list_cb_failure; secid = addr4->secid; } else { - ret_val = nla_put(cb_arg->skb, - NLBL_UNLABEL_A_IPV6ADDR, - sizeof(struct in6_addr), - &addr6->list.addr); + ret_val = nla_put_in6_addr(cb_arg->skb, + NLBL_UNLABEL_A_IPV6ADDR, + &addr6->list.addr); if (ret_val != 0) goto list_cb_failure; - ret_val = nla_put(cb_arg->skb, - NLBL_UNLABEL_A_IPV6MASK, - sizeof(struct in6_addr), - &addr6->list.mask); + ret_val = nla_put_in6_addr(cb_arg->skb, + NLBL_UNLABEL_A_IPV6MASK, + &addr6->list.mask); if (ret_val != 0) goto list_cb_failure; diff --git a/net/openvswitch/flow_netlink.c b/net/openvswitch/flow_netlink.c index 22b18c145c92..c0c5b5519f45 100644 --- a/net/openvswitch/flow_netlink.c +++ b/net/openvswitch/flow_netlink.c @@ -648,10 +648,12 @@ static int __ipv4_tun_to_nlattr(struct sk_buff *skb, nla_put_be64(skb, OVS_TUNNEL_KEY_ATTR_ID, output->tun_id)) return -EMSGSIZE; if (output->ipv4_src && - nla_put_be32(skb, OVS_TUNNEL_KEY_ATTR_IPV4_SRC, output->ipv4_src)) + nla_put_in_addr(skb, OVS_TUNNEL_KEY_ATTR_IPV4_SRC, + output->ipv4_src)) return -EMSGSIZE; if (output->ipv4_dst && - nla_put_be32(skb, OVS_TUNNEL_KEY_ATTR_IPV4_DST, output->ipv4_dst)) + nla_put_in_addr(skb, OVS_TUNNEL_KEY_ATTR_IPV4_DST, + output->ipv4_dst)) return -EMSGSIZE; if (output->ipv4_tos && nla_put_u8(skb, OVS_TUNNEL_KEY_ATTR_TOS, output->ipv4_tos)) diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index d6ba4a6bbff6..6a4a4d7db1fc 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -8761,8 +8761,8 @@ static int nl80211_send_wowlan_tcp(struct sk_buff *msg, if (!nl_tcp) return -ENOBUFS; - if (nla_put_be32(msg, NL80211_WOWLAN_TCP_SRC_IPV4, tcp->src) || - nla_put_be32(msg, NL80211_WOWLAN_TCP_DST_IPV4, tcp->dst) || + if (nla_put_in_addr(msg, NL80211_WOWLAN_TCP_SRC_IPV4, tcp->src) || + nla_put_in_addr(msg, NL80211_WOWLAN_TCP_DST_IPV4, tcp->dst) || nla_put(msg, NL80211_WOWLAN_TCP_DST_MAC, ETH_ALEN, tcp->dst_mac) || nla_put_u16(msg, NL80211_WOWLAN_TCP_SRC_PORT, tcp->src_port) || nla_put_u16(msg, NL80211_WOWLAN_TCP_DST_PORT, tcp->dst_port) || -- cgit v1.2.3 From 67b61f6c130a05b2cd4c3dfded49a751ff42c534 Mon Sep 17 00:00:00 2001 From: Jiri Benc Date: Sun, 29 Mar 2015 16:59:26 +0200 Subject: netlink: implement nla_get_in_addr and nla_get_in6_addr Those are counterparts to nla_put_in_addr and nla_put_in6_addr. Signed-off-by: Jiri Benc Signed-off-by: David S. Miller --- drivers/net/vxlan.c | 16 +++++++--------- include/net/netlink.h | 21 +++++++++++++++++++++ net/ipv4/devinet.c | 10 +++++----- net/ipv4/fib_rules.c | 8 ++++---- net/ipv4/fib_semantics.c | 4 ++-- net/ipv4/ip_gre.c | 4 ++-- net/ipv4/ip_vti.c | 4 ++-- net/ipv4/ipip.c | 4 ++-- net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c | 4 ++-- net/ipv4/route.c | 4 ++-- net/ipv4/tcp_metrics.c | 4 ++-- net/ipv6/fib6_rules.c | 6 ++---- net/ipv6/ip6_gre.c | 6 +++--- net/ipv6/ip6_tunnel.c | 6 ++---- net/ipv6/ip6_vti.c | 6 ++---- net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c | 6 ++---- net/ipv6/route.c | 6 +++--- net/ipv6/sit.c | 3 +-- net/l2tp/l2tp_netlink.c | 4 ++-- net/openvswitch/flow_netlink.c | 4 ++-- net/wireless/nl80211.c | 4 ++-- 21 files changed, 72 insertions(+), 62 deletions(-) (limited to 'include') diff --git a/drivers/net/vxlan.c b/drivers/net/vxlan.c index 86f085f95408..a8d345054d23 100644 --- a/drivers/net/vxlan.c +++ b/drivers/net/vxlan.c @@ -171,11 +171,11 @@ static inline bool vxlan_addr_multicast(const union vxlan_addr *ipa) static int vxlan_nla_get_addr(union vxlan_addr *ip, struct nlattr *nla) { if (nla_len(nla) >= sizeof(struct in6_addr)) { - nla_memcpy(&ip->sin6.sin6_addr, nla, sizeof(struct in6_addr)); + ip->sin6.sin6_addr = nla_get_in6_addr(nla); ip->sa.sa_family = AF_INET6; return 0; } else if (nla_len(nla) >= sizeof(__be32)) { - ip->sin.sin_addr.s_addr = nla_get_be32(nla); + ip->sin.sin_addr.s_addr = nla_get_in_addr(nla); ip->sa.sa_family = AF_INET; return 0; } else { @@ -215,7 +215,7 @@ static int vxlan_nla_get_addr(union vxlan_addr *ip, struct nlattr *nla) if (nla_len(nla) >= sizeof(struct in6_addr)) { return -EAFNOSUPPORT; } else if (nla_len(nla) >= sizeof(__be32)) { - ip->sin.sin_addr.s_addr = nla_get_be32(nla); + ip->sin.sin_addr.s_addr = nla_get_in_addr(nla); ip->sa.sa_family = AF_INET; return 0; } else { @@ -2602,27 +2602,25 @@ static int vxlan_newlink(struct net *src_net, struct net_device *dev, /* Unless IPv6 is explicitly requested, assume IPv4 */ dst->remote_ip.sa.sa_family = AF_INET; if (data[IFLA_VXLAN_GROUP]) { - dst->remote_ip.sin.sin_addr.s_addr = nla_get_be32(data[IFLA_VXLAN_GROUP]); + dst->remote_ip.sin.sin_addr.s_addr = nla_get_in_addr(data[IFLA_VXLAN_GROUP]); } else if (data[IFLA_VXLAN_GROUP6]) { if (!IS_ENABLED(CONFIG_IPV6)) return -EPFNOSUPPORT; - nla_memcpy(&dst->remote_ip.sin6.sin6_addr, data[IFLA_VXLAN_GROUP6], - sizeof(struct in6_addr)); + dst->remote_ip.sin6.sin6_addr = nla_get_in6_addr(data[IFLA_VXLAN_GROUP6]); dst->remote_ip.sa.sa_family = AF_INET6; use_ipv6 = true; } if (data[IFLA_VXLAN_LOCAL]) { - vxlan->saddr.sin.sin_addr.s_addr = nla_get_be32(data[IFLA_VXLAN_LOCAL]); + vxlan->saddr.sin.sin_addr.s_addr = nla_get_in_addr(data[IFLA_VXLAN_LOCAL]); vxlan->saddr.sa.sa_family = AF_INET; } else if (data[IFLA_VXLAN_LOCAL6]) { if (!IS_ENABLED(CONFIG_IPV6)) return -EPFNOSUPPORT; /* TODO: respect scope id */ - nla_memcpy(&vxlan->saddr.sin6.sin6_addr, data[IFLA_VXLAN_LOCAL6], - sizeof(struct in6_addr)); + vxlan->saddr.sin6.sin6_addr = nla_get_in6_addr(data[IFLA_VXLAN_LOCAL6]); vxlan->saddr.sa.sa_family = AF_INET6; use_ipv6 = true; } diff --git a/include/net/netlink.h b/include/net/netlink.h index 17fc76e5b05e..2a5dbcc90d1c 100644 --- a/include/net/netlink.h +++ b/include/net/netlink.h @@ -1127,6 +1127,27 @@ static inline unsigned long nla_get_msecs(const struct nlattr *nla) return msecs_to_jiffies((unsigned long) msecs); } +/** + * nla_get_in_addr - return payload of IPv4 address attribute + * @nla: IPv4 address netlink attribute + */ +static inline __be32 nla_get_in_addr(const struct nlattr *nla) +{ + return *(__be32 *) nla_data(nla); +} + +/** + * nla_get_in6_addr - return payload of IPv6 address attribute + * @nla: IPv6 address netlink attribute + */ +static inline struct in6_addr nla_get_in6_addr(const struct nlattr *nla) +{ + struct in6_addr tmp; + + nla_memcpy(&tmp, nla, sizeof(tmp)); + return tmp; +} + /** * nla_nest_start - Start a new level of nested attributes * @skb: socket buffer to add attributes to diff --git a/net/ipv4/devinet.c b/net/ipv4/devinet.c index 66cd85973056..c6473f365ad1 100644 --- a/net/ipv4/devinet.c +++ b/net/ipv4/devinet.c @@ -593,7 +593,7 @@ static int inet_rtm_deladdr(struct sk_buff *skb, struct nlmsghdr *nlh) for (ifap = &in_dev->ifa_list; (ifa = *ifap) != NULL; ifap = &ifa->ifa_next) { if (tb[IFA_LOCAL] && - ifa->ifa_local != nla_get_be32(tb[IFA_LOCAL])) + ifa->ifa_local != nla_get_in_addr(tb[IFA_LOCAL])) continue; if (tb[IFA_LABEL] && nla_strcmp(tb[IFA_LABEL], ifa->ifa_label)) @@ -601,7 +601,7 @@ static int inet_rtm_deladdr(struct sk_buff *skb, struct nlmsghdr *nlh) if (tb[IFA_ADDRESS] && (ifm->ifa_prefixlen != ifa->ifa_prefixlen || - !inet_ifa_match(nla_get_be32(tb[IFA_ADDRESS]), ifa))) + !inet_ifa_match(nla_get_in_addr(tb[IFA_ADDRESS]), ifa))) continue; if (ipv4_is_multicast(ifa->ifa_address)) @@ -791,11 +791,11 @@ static struct in_ifaddr *rtm_to_ifaddr(struct net *net, struct nlmsghdr *nlh, ifa->ifa_scope = ifm->ifa_scope; ifa->ifa_dev = in_dev; - ifa->ifa_local = nla_get_be32(tb[IFA_LOCAL]); - ifa->ifa_address = nla_get_be32(tb[IFA_ADDRESS]); + ifa->ifa_local = nla_get_in_addr(tb[IFA_LOCAL]); + ifa->ifa_address = nla_get_in_addr(tb[IFA_ADDRESS]); if (tb[IFA_BROADCAST]) - ifa->ifa_broadcast = nla_get_be32(tb[IFA_BROADCAST]); + ifa->ifa_broadcast = nla_get_in_addr(tb[IFA_BROADCAST]); if (tb[IFA_LABEL]) nla_strlcpy(ifa->ifa_label, tb[IFA_LABEL], IFNAMSIZ); diff --git a/net/ipv4/fib_rules.c b/net/ipv4/fib_rules.c index edfea0deec43..8162dd8e86d7 100644 --- a/net/ipv4/fib_rules.c +++ b/net/ipv4/fib_rules.c @@ -194,10 +194,10 @@ static int fib4_rule_configure(struct fib_rule *rule, struct sk_buff *skb, } if (frh->src_len) - rule4->src = nla_get_be32(tb[FRA_SRC]); + rule4->src = nla_get_in_addr(tb[FRA_SRC]); if (frh->dst_len) - rule4->dst = nla_get_be32(tb[FRA_DST]); + rule4->dst = nla_get_in_addr(tb[FRA_DST]); #ifdef CONFIG_IP_ROUTE_CLASSID if (tb[FRA_FLOW]) { @@ -260,10 +260,10 @@ static int fib4_rule_compare(struct fib_rule *rule, struct fib_rule_hdr *frh, return 0; #endif - if (frh->src_len && (rule4->src != nla_get_be32(tb[FRA_SRC]))) + if (frh->src_len && (rule4->src != nla_get_in_addr(tb[FRA_SRC]))) return 0; - if (frh->dst_len && (rule4->dst != nla_get_be32(tb[FRA_DST]))) + if (frh->dst_len && (rule4->dst != nla_get_in_addr(tb[FRA_DST]))) return 0; return 1; diff --git a/net/ipv4/fib_semantics.c b/net/ipv4/fib_semantics.c index 453b24e5322c..eac5aec7772a 100644 --- a/net/ipv4/fib_semantics.c +++ b/net/ipv4/fib_semantics.c @@ -468,7 +468,7 @@ static int fib_get_nhs(struct fib_info *fi, struct rtnexthop *rtnh, struct nlattr *nla, *attrs = rtnh_attrs(rtnh); nla = nla_find(attrs, attrlen, RTA_GATEWAY); - nexthop_nh->nh_gw = nla ? nla_get_be32(nla) : 0; + nexthop_nh->nh_gw = nla ? nla_get_in_addr(nla) : 0; #ifdef CONFIG_IP_ROUTE_CLASSID nla = nla_find(attrs, attrlen, RTA_FLOW); nexthop_nh->nh_tclassid = nla ? nla_get_u32(nla) : 0; @@ -523,7 +523,7 @@ int fib_nh_match(struct fib_config *cfg, struct fib_info *fi) struct nlattr *nla, *attrs = rtnh_attrs(rtnh); nla = nla_find(attrs, attrlen, RTA_GATEWAY); - if (nla && nla_get_be32(nla) != nh->nh_gw) + if (nla && nla_get_in_addr(nla) != nh->nh_gw) return 1; #ifdef CONFIG_IP_ROUTE_CLASSID nla = nla_find(attrs, attrlen, RTA_FLOW); diff --git a/net/ipv4/ip_gre.c b/net/ipv4/ip_gre.c index 2e878df46075..0eb2a040a830 100644 --- a/net/ipv4/ip_gre.c +++ b/net/ipv4/ip_gre.c @@ -621,10 +621,10 @@ static void ipgre_netlink_parms(struct nlattr *data[], struct nlattr *tb[], parms->o_key = nla_get_be32(data[IFLA_GRE_OKEY]); if (data[IFLA_GRE_LOCAL]) - parms->iph.saddr = nla_get_be32(data[IFLA_GRE_LOCAL]); + parms->iph.saddr = nla_get_in_addr(data[IFLA_GRE_LOCAL]); if (data[IFLA_GRE_REMOTE]) - parms->iph.daddr = nla_get_be32(data[IFLA_GRE_REMOTE]); + parms->iph.daddr = nla_get_in_addr(data[IFLA_GRE_REMOTE]); if (data[IFLA_GRE_TTL]) parms->iph.ttl = nla_get_u8(data[IFLA_GRE_TTL]); diff --git a/net/ipv4/ip_vti.c b/net/ipv4/ip_vti.c index f189f2a8aaa5..5a6e27054f0a 100644 --- a/net/ipv4/ip_vti.c +++ b/net/ipv4/ip_vti.c @@ -456,10 +456,10 @@ static void vti_netlink_parms(struct nlattr *data[], parms->o_key = nla_get_be32(data[IFLA_VTI_OKEY]); if (data[IFLA_VTI_LOCAL]) - parms->iph.saddr = nla_get_be32(data[IFLA_VTI_LOCAL]); + parms->iph.saddr = nla_get_in_addr(data[IFLA_VTI_LOCAL]); if (data[IFLA_VTI_REMOTE]) - parms->iph.daddr = nla_get_be32(data[IFLA_VTI_REMOTE]); + parms->iph.daddr = nla_get_in_addr(data[IFLA_VTI_REMOTE]); } diff --git a/net/ipv4/ipip.c b/net/ipv4/ipip.c index 17df8d38bbbd..bfbcc85c02ee 100644 --- a/net/ipv4/ipip.c +++ b/net/ipv4/ipip.c @@ -325,10 +325,10 @@ static void ipip_netlink_parms(struct nlattr *data[], parms->link = nla_get_u32(data[IFLA_IPTUN_LINK]); if (data[IFLA_IPTUN_LOCAL]) - parms->iph.saddr = nla_get_be32(data[IFLA_IPTUN_LOCAL]); + parms->iph.saddr = nla_get_in_addr(data[IFLA_IPTUN_LOCAL]); if (data[IFLA_IPTUN_REMOTE]) - parms->iph.daddr = nla_get_be32(data[IFLA_IPTUN_REMOTE]); + parms->iph.daddr = nla_get_in_addr(data[IFLA_IPTUN_REMOTE]); if (data[IFLA_IPTUN_TTL]) { parms->iph.ttl = nla_get_u8(data[IFLA_IPTUN_TTL]); diff --git a/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c b/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c index b36ebfc6b812..8c8d6642cbb0 100644 --- a/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c +++ b/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c @@ -342,8 +342,8 @@ static int ipv4_nlattr_to_tuple(struct nlattr *tb[], if (!tb[CTA_IP_V4_SRC] || !tb[CTA_IP_V4_DST]) return -EINVAL; - t->src.u3.ip = nla_get_be32(tb[CTA_IP_V4_SRC]); - t->dst.u3.ip = nla_get_be32(tb[CTA_IP_V4_DST]); + t->src.u3.ip = nla_get_in_addr(tb[CTA_IP_V4_SRC]); + t->dst.u3.ip = nla_get_in_addr(tb[CTA_IP_V4_DST]); return 0; } diff --git a/net/ipv4/route.c b/net/ipv4/route.c index 1f147204f1f3..652b92ebd7ba 100644 --- a/net/ipv4/route.c +++ b/net/ipv4/route.c @@ -2436,8 +2436,8 @@ static int inet_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr *nlh) ip_hdr(skb)->protocol = IPPROTO_ICMP; skb_reserve(skb, MAX_HEADER + sizeof(struct iphdr)); - src = tb[RTA_SRC] ? nla_get_be32(tb[RTA_SRC]) : 0; - dst = tb[RTA_DST] ? nla_get_be32(tb[RTA_DST]) : 0; + src = tb[RTA_SRC] ? nla_get_in_addr(tb[RTA_SRC]) : 0; + dst = tb[RTA_DST] ? nla_get_in_addr(tb[RTA_DST]) : 0; iif = tb[RTA_IIF] ? nla_get_u32(tb[RTA_IIF]) : 0; mark = tb[RTA_MARK] ? nla_get_u32(tb[RTA_MARK]) : 0; diff --git a/net/ipv4/tcp_metrics.c b/net/ipv4/tcp_metrics.c index 32e36ea6bc0f..71ec14c87579 100644 --- a/net/ipv4/tcp_metrics.c +++ b/net/ipv4/tcp_metrics.c @@ -948,7 +948,7 @@ static int __parse_nl_addr(struct genl_info *info, struct inetpeer_addr *addr, a = info->attrs[v4]; if (a) { addr->family = AF_INET; - addr->addr.a4 = nla_get_be32(a); + addr->addr.a4 = nla_get_in_addr(a); if (hash) *hash = (__force unsigned int) addr->addr.a4; return 0; @@ -958,7 +958,7 @@ static int __parse_nl_addr(struct genl_info *info, struct inetpeer_addr *addr, if (nla_len(a) != sizeof(struct in6_addr)) return -EINVAL; addr->family = AF_INET6; - memcpy(addr->addr.a6, nla_data(a), sizeof(addr->addr.a6)); + addr->addr.in6 = nla_get_in6_addr(a); if (hash) *hash = ipv6_addr_hash(&addr->addr.in6); return 0; diff --git a/net/ipv6/fib6_rules.c b/net/ipv6/fib6_rules.c index d313bfd88512..61fb184b818d 100644 --- a/net/ipv6/fib6_rules.c +++ b/net/ipv6/fib6_rules.c @@ -199,12 +199,10 @@ static int fib6_rule_configure(struct fib_rule *rule, struct sk_buff *skb, } if (frh->src_len) - nla_memcpy(&rule6->src.addr, tb[FRA_SRC], - sizeof(struct in6_addr)); + rule6->src.addr = nla_get_in6_addr(tb[FRA_SRC]); if (frh->dst_len) - nla_memcpy(&rule6->dst.addr, tb[FRA_DST], - sizeof(struct in6_addr)); + rule6->dst.addr = nla_get_in6_addr(tb[FRA_DST]); rule6->src.plen = frh->src_len; rule6->dst.plen = frh->dst_len; diff --git a/net/ipv6/ip6_gre.c b/net/ipv6/ip6_gre.c index f61f7ad2d045..0f4e73da14e4 100644 --- a/net/ipv6/ip6_gre.c +++ b/net/ipv6/ip6_gre.c @@ -1412,7 +1412,7 @@ static int ip6gre_tap_validate(struct nlattr *tb[], struct nlattr *data[]) goto out; if (data[IFLA_GRE_REMOTE]) { - nla_memcpy(&daddr, data[IFLA_GRE_REMOTE], sizeof(struct in6_addr)); + daddr = nla_get_in6_addr(data[IFLA_GRE_REMOTE]); if (ipv6_addr_any(&daddr)) return -EINVAL; } @@ -1446,10 +1446,10 @@ static void ip6gre_netlink_parms(struct nlattr *data[], parms->o_key = nla_get_be32(data[IFLA_GRE_OKEY]); if (data[IFLA_GRE_LOCAL]) - nla_memcpy(&parms->laddr, data[IFLA_GRE_LOCAL], sizeof(struct in6_addr)); + parms->laddr = nla_get_in6_addr(data[IFLA_GRE_LOCAL]); if (data[IFLA_GRE_REMOTE]) - nla_memcpy(&parms->raddr, data[IFLA_GRE_REMOTE], sizeof(struct in6_addr)); + parms->raddr = nla_get_in6_addr(data[IFLA_GRE_REMOTE]); if (data[IFLA_GRE_TTL]) parms->hop_limit = nla_get_u8(data[IFLA_GRE_TTL]); diff --git a/net/ipv6/ip6_tunnel.c b/net/ipv6/ip6_tunnel.c index 80543d13ea7c..9bd85f0dff69 100644 --- a/net/ipv6/ip6_tunnel.c +++ b/net/ipv6/ip6_tunnel.c @@ -1640,12 +1640,10 @@ static void ip6_tnl_netlink_parms(struct nlattr *data[], parms->link = nla_get_u32(data[IFLA_IPTUN_LINK]); if (data[IFLA_IPTUN_LOCAL]) - nla_memcpy(&parms->laddr, data[IFLA_IPTUN_LOCAL], - sizeof(struct in6_addr)); + parms->laddr = nla_get_in6_addr(data[IFLA_IPTUN_LOCAL]); if (data[IFLA_IPTUN_REMOTE]) - nla_memcpy(&parms->raddr, data[IFLA_IPTUN_REMOTE], - sizeof(struct in6_addr)); + parms->raddr = nla_get_in6_addr(data[IFLA_IPTUN_REMOTE]); if (data[IFLA_IPTUN_TTL]) parms->hop_limit = nla_get_u8(data[IFLA_IPTUN_TTL]); diff --git a/net/ipv6/ip6_vti.c b/net/ipv6/ip6_vti.c index 87a262b0f07b..53d90ed68905 100644 --- a/net/ipv6/ip6_vti.c +++ b/net/ipv6/ip6_vti.c @@ -897,12 +897,10 @@ static void vti6_netlink_parms(struct nlattr *data[], parms->link = nla_get_u32(data[IFLA_VTI_LINK]); if (data[IFLA_VTI_LOCAL]) - nla_memcpy(&parms->laddr, data[IFLA_VTI_LOCAL], - sizeof(struct in6_addr)); + parms->laddr = nla_get_in6_addr(data[IFLA_VTI_LOCAL]); if (data[IFLA_VTI_REMOTE]) - nla_memcpy(&parms->raddr, data[IFLA_VTI_REMOTE], - sizeof(struct in6_addr)); + parms->raddr = nla_get_in6_addr(data[IFLA_VTI_REMOTE]); if (data[IFLA_VTI_IKEY]) parms->i_key = nla_get_be32(data[IFLA_VTI_IKEY]); diff --git a/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c b/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c index 78284a697439..fba91c6fc7ca 100644 --- a/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c +++ b/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c @@ -310,10 +310,8 @@ static int ipv6_nlattr_to_tuple(struct nlattr *tb[], if (!tb[CTA_IP_V6_SRC] || !tb[CTA_IP_V6_DST]) return -EINVAL; - memcpy(&t->src.u3.ip6, nla_data(tb[CTA_IP_V6_SRC]), - sizeof(u_int32_t) * 4); - memcpy(&t->dst.u3.ip6, nla_data(tb[CTA_IP_V6_DST]), - sizeof(u_int32_t) * 4); + t->src.u3.in6 = nla_get_in6_addr(tb[CTA_IP_V6_SRC]); + t->dst.u3.in6 = nla_get_in6_addr(tb[CTA_IP_V6_DST]); return 0; } diff --git a/net/ipv6/route.c b/net/ipv6/route.c index 385e9bd4f218..5c48293ff062 100644 --- a/net/ipv6/route.c +++ b/net/ipv6/route.c @@ -2438,7 +2438,7 @@ static int rtm_to_fib6_config(struct sk_buff *skb, struct nlmsghdr *nlh, cfg->fc_nlinfo.nl_net = sock_net(skb->sk); if (tb[RTA_GATEWAY]) { - nla_memcpy(&cfg->fc_gateway, tb[RTA_GATEWAY], 16); + cfg->fc_gateway = nla_get_in6_addr(tb[RTA_GATEWAY]); cfg->fc_flags |= RTF_GATEWAY; } @@ -2461,7 +2461,7 @@ static int rtm_to_fib6_config(struct sk_buff *skb, struct nlmsghdr *nlh, } if (tb[RTA_PREFSRC]) - nla_memcpy(&cfg->fc_prefsrc, tb[RTA_PREFSRC], 16); + cfg->fc_prefsrc = nla_get_in6_addr(tb[RTA_PREFSRC]); if (tb[RTA_OIF]) cfg->fc_ifindex = nla_get_u32(tb[RTA_OIF]); @@ -2519,7 +2519,7 @@ beginning: nla = nla_find(attrs, attrlen, RTA_GATEWAY); if (nla) { - nla_memcpy(&r_cfg.fc_gateway, nla, 16); + r_cfg.fc_gateway = nla_get_in6_addr(nla); r_cfg.fc_flags |= RTF_GATEWAY; } } diff --git a/net/ipv6/sit.c b/net/ipv6/sit.c index 0e2bb538a556..e6b9f51b15e8 100644 --- a/net/ipv6/sit.c +++ b/net/ipv6/sit.c @@ -1530,8 +1530,7 @@ static bool ipip6_netlink_6rd_parms(struct nlattr *data[], if (data[IFLA_IPTUN_6RD_PREFIX]) { ret = true; - nla_memcpy(&ip6rd->prefix, data[IFLA_IPTUN_6RD_PREFIX], - sizeof(struct in6_addr)); + ip6rd->prefix = nla_get_in6_addr(data[IFLA_IPTUN_6RD_PREFIX]); } if (data[IFLA_IPTUN_6RD_RELAY_PREFIX]) { diff --git a/net/l2tp/l2tp_netlink.c b/net/l2tp/l2tp_netlink.c index a4f78d36bace..9e13c2ff8789 100644 --- a/net/l2tp/l2tp_netlink.c +++ b/net/l2tp/l2tp_netlink.c @@ -205,9 +205,9 @@ static int l2tp_nl_cmd_tunnel_create(struct sk_buff *skb, struct genl_info *info #endif if (info->attrs[L2TP_ATTR_IP_SADDR] && info->attrs[L2TP_ATTR_IP_DADDR]) { - cfg.local_ip.s_addr = nla_get_be32( + cfg.local_ip.s_addr = nla_get_in_addr( info->attrs[L2TP_ATTR_IP_SADDR]); - cfg.peer_ip.s_addr = nla_get_be32( + cfg.peer_ip.s_addr = nla_get_in_addr( info->attrs[L2TP_ATTR_IP_DADDR]); } else { ret = -EINVAL; diff --git a/net/openvswitch/flow_netlink.c b/net/openvswitch/flow_netlink.c index c0c5b5519f45..c691b1a1eee0 100644 --- a/net/openvswitch/flow_netlink.c +++ b/net/openvswitch/flow_netlink.c @@ -535,11 +535,11 @@ static int ipv4_tun_from_nlattr(const struct nlattr *attr, break; case OVS_TUNNEL_KEY_ATTR_IPV4_SRC: SW_FLOW_KEY_PUT(match, tun_key.ipv4_src, - nla_get_be32(a), is_mask); + nla_get_in_addr(a), is_mask); break; case OVS_TUNNEL_KEY_ATTR_IPV4_DST: SW_FLOW_KEY_PUT(match, tun_key.ipv4_dst, - nla_get_be32(a), is_mask); + nla_get_in_addr(a), is_mask); break; case OVS_TUNNEL_KEY_ATTR_TOS: SW_FLOW_KEY_PUT(match, tun_key.ipv4_tos, diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 6a4a4d7db1fc..2fb804bfa361 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -8993,8 +8993,8 @@ static int nl80211_parse_wowlan_tcp(struct cfg80211_registered_device *rdev, cfg = kzalloc(size, GFP_KERNEL); if (!cfg) return -ENOMEM; - cfg->src = nla_get_be32(tb[NL80211_WOWLAN_TCP_SRC_IPV4]); - cfg->dst = nla_get_be32(tb[NL80211_WOWLAN_TCP_DST_IPV4]); + cfg->src = nla_get_in_addr(tb[NL80211_WOWLAN_TCP_SRC_IPV4]); + cfg->dst = nla_get_in_addr(tb[NL80211_WOWLAN_TCP_DST_IPV4]); memcpy(cfg->dst_mac, nla_data(tb[NL80211_WOWLAN_TCP_DST_MAC]), ETH_ALEN); if (tb[NL80211_WOWLAN_TCP_SRC_PORT]) -- cgit v1.2.3 From ffa88f37ffeaac398be68f9678b0e6046a5ba7f6 Mon Sep 17 00:00:00 2001 From: Eran Ben Elisha Date: Mon, 30 Mar 2015 17:45:22 +0300 Subject: net/mlx4_en: Move statistics bitmap setting to the Ethernet driver The statistics bitmap belongs to the Ethernet driver, move it there. Signed-off-by: Eran Ben Elisha Signed-off-by: Hadar Hen Zion Signed-off-by: Or Gerlitz Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlx4/en_netdev.c | 22 +++++++++++++++++++++- drivers/net/ethernet/mellanox/mlx4/mlx4_en.h | 2 ++ drivers/net/ethernet/mellanox/mlx4/port.c | 21 --------------------- include/linux/mlx4/device.h | 1 - 4 files changed, 23 insertions(+), 23 deletions(-) (limited to 'include') diff --git a/drivers/net/ethernet/mellanox/mlx4/en_netdev.c b/drivers/net/ethernet/mellanox/mlx4/en_netdev.c index ebc93a101c93..9556230465f0 100644 --- a/drivers/net/ethernet/mellanox/mlx4/en_netdev.c +++ b/drivers/net/ethernet/mellanox/mlx4/en_netdev.c @@ -49,6 +49,11 @@ #include "mlx4_en.h" #include "en_port.h" +#define MLX4_STATS_TRAFFIC_COUNTERS_MASK 0xfULL +#define MLX4_STATS_TRAFFIC_DROPS_MASK 0xc0ULL +#define MLX4_STATS_ERROR_COUNTERS_MASK 0x1ffc30ULL +#define MLX4_STATS_PORT_COUNTERS_MASK 0x7fe00000ULL + int mlx4_en_setup_tc(struct net_device *dev, u8 up) { struct mlx4_en_priv *priv = netdev_priv(dev); @@ -2648,6 +2653,21 @@ int mlx4_en_netdev_event(struct notifier_block *this, return NOTIFY_DONE; } +void mlx4_en_set_stats_bitmap(struct mlx4_dev *dev, u64 *stats_bitmap) +{ + if (!mlx4_is_mfunc(dev)) { + *stats_bitmap = 0; + return; + } + + *stats_bitmap = (MLX4_STATS_TRAFFIC_COUNTERS_MASK | + MLX4_STATS_TRAFFIC_DROPS_MASK | + MLX4_STATS_PORT_COUNTERS_MASK); + + if (mlx4_is_master(dev)) + *stats_bitmap |= MLX4_STATS_ERROR_COUNTERS_MASK; +} + int mlx4_en_init_netdev(struct mlx4_en_dev *mdev, int port, struct mlx4_en_port_profile *prof) { @@ -2881,7 +2901,7 @@ int mlx4_en_init_netdev(struct mlx4_en_dev *mdev, int port, queue_delayed_work(mdev->workqueue, &priv->service_task, SERVICE_TASK_DELAY); - mlx4_set_stats_bitmap(mdev->dev, &priv->stats_bitmap); + mlx4_en_set_stats_bitmap(mdev->dev, &priv->stats_bitmap); return 0; diff --git a/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h b/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h index 6b379a2df4a0..af3d9b79277c 100644 --- a/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h +++ b/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h @@ -730,6 +730,8 @@ int mlx4_en_init_netdev(struct mlx4_en_dev *mdev, int port, int mlx4_en_start_port(struct net_device *dev); void mlx4_en_stop_port(struct net_device *dev, int detach); +void mlx4_en_set_stats_bitmap(struct mlx4_dev *dev, u64 *stats_bitmap); + void mlx4_en_free_resources(struct mlx4_en_priv *priv); int mlx4_en_alloc_resources(struct mlx4_en_priv *priv); diff --git a/drivers/net/ethernet/mellanox/mlx4/port.c b/drivers/net/ethernet/mellanox/mlx4/port.c index 3f6f77112274..b97f173ab062 100644 --- a/drivers/net/ethernet/mellanox/mlx4/port.c +++ b/drivers/net/ethernet/mellanox/mlx4/port.c @@ -45,11 +45,6 @@ #define MLX4_VLAN_VALID (1u << 31) #define MLX4_VLAN_MASK 0xfff -#define MLX4_STATS_TRAFFIC_COUNTERS_MASK 0xfULL -#define MLX4_STATS_TRAFFIC_DROPS_MASK 0xc0ULL -#define MLX4_STATS_ERROR_COUNTERS_MASK 0x1ffc30ULL -#define MLX4_STATS_PORT_COUNTERS_MASK 0x7fe00000ULL - void mlx4_init_mac_table(struct mlx4_dev *dev, struct mlx4_mac_table *table) { int i; @@ -1185,22 +1180,6 @@ int mlx4_DUMP_ETH_STATS_wrapper(struct mlx4_dev *dev, int slave, vhcr->in_modifier, outbox); } -void mlx4_set_stats_bitmap(struct mlx4_dev *dev, u64 *stats_bitmap) -{ - if (!mlx4_is_mfunc(dev)) { - *stats_bitmap = 0; - return; - } - - *stats_bitmap = (MLX4_STATS_TRAFFIC_COUNTERS_MASK | - MLX4_STATS_TRAFFIC_DROPS_MASK | - MLX4_STATS_PORT_COUNTERS_MASK); - - if (mlx4_is_master(dev)) - *stats_bitmap |= MLX4_STATS_ERROR_COUNTERS_MASK; -} -EXPORT_SYMBOL(mlx4_set_stats_bitmap); - int mlx4_get_slave_from_roce_gid(struct mlx4_dev *dev, int port, u8 *gid, int *slave_id) { diff --git a/include/linux/mlx4/device.h b/include/linux/mlx4/device.h index 4550c67b92e4..49abbe28e230 100644 --- a/include/linux/mlx4/device.h +++ b/include/linux/mlx4/device.h @@ -1300,7 +1300,6 @@ int mlx4_register_mac(struct mlx4_dev *dev, u8 port, u64 mac); void mlx4_unregister_mac(struct mlx4_dev *dev, u8 port, u64 mac); int mlx4_get_base_qpn(struct mlx4_dev *dev, u8 port); int __mlx4_replace_mac(struct mlx4_dev *dev, u8 port, int qpn, u64 new_mac); -void mlx4_set_stats_bitmap(struct mlx4_dev *dev, u64 *stats_bitmap); int mlx4_SET_PORT_general(struct mlx4_dev *dev, u8 port, int mtu, u8 pptx, u8 pfctx, u8 pprx, u8 pfcrx); int mlx4_SET_PORT_qpn_calc(struct mlx4_dev *dev, u8 port, u32 base_qpn, -- cgit v1.2.3 From 0b131561a7d639abb0a194d2d8fae839ce3b99e9 Mon Sep 17 00:00:00 2001 From: Matan Barak Date: Mon, 30 Mar 2015 17:45:25 +0300 Subject: net/mlx4_en: Add Flow control statistics display via ethtool Flow control per priority and Global pause counters are now visible via ethtool. The counters shows statistics regarding pauses in the device. Signed-off-by: Matan Barak Signed-off-by: Shani Michaeli Signed-off-by: Eran Ben Elisha Signed-off-by: Hadar Hen Zion Signed-off-by: Or Gerlitz Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlx4/en_dcb_nl.c | 4 ++ drivers/net/ethernet/mellanox/mlx4/en_ethtool.c | 74 +++++++++++++++++++++++++ drivers/net/ethernet/mellanox/mlx4/en_netdev.c | 57 ++++++++++++++++++- drivers/net/ethernet/mellanox/mlx4/en_port.c | 50 +++++++++++++++++ drivers/net/ethernet/mellanox/mlx4/fw.c | 10 +++- drivers/net/ethernet/mellanox/mlx4/mlx4_en.h | 13 ++++- drivers/net/ethernet/mellanox/mlx4/mlx4_stats.h | 52 ++++++++++++++++- include/linux/mlx4/device.h | 3 +- 8 files changed, 255 insertions(+), 8 deletions(-) (limited to 'include') diff --git a/drivers/net/ethernet/mellanox/mlx4/en_dcb_nl.c b/drivers/net/ethernet/mellanox/mlx4/en_dcb_nl.c index cde14fa2f742..8e3260c0eaa5 100644 --- a/drivers/net/ethernet/mellanox/mlx4/en_dcb_nl.c +++ b/drivers/net/ethernet/mellanox/mlx4/en_dcb_nl.c @@ -226,6 +226,10 @@ static int mlx4_en_dcbnl_ieee_setpfc(struct net_device *dev, prof->rx_ppp); if (err) en_err(priv, "Failed setting pause params\n"); + else + mlx4_en_update_pfc_stats_bitmap(mdev->dev, &priv->stats_bitmap, + prof->rx_ppp, prof->rx_pause, + prof->tx_ppp, prof->tx_pause); return err; } diff --git a/drivers/net/ethernet/mellanox/mlx4/en_ethtool.c b/drivers/net/ethernet/mellanox/mlx4/en_ethtool.c index 3e7ed39e8e76..4fc767461b4f 100644 --- a/drivers/net/ethernet/mellanox/mlx4/en_ethtool.c +++ b/drivers/net/ethernet/mellanox/mlx4/en_ethtool.c @@ -119,6 +119,48 @@ static const char main_strings[][ETH_GSTRING_LEN] = { "queue_stopped", "wake_queue", "tx_timeout", "rx_alloc_failed", "rx_csum_good", "rx_csum_none", "rx_csum_complete", "tx_chksum_offload", + /* priority flow control statistics rx */ + "rx_pause_prio_0", "rx_pause_duration_prio_0", + "rx_pause_transition_prio_0", + "rx_pause_prio_1", "rx_pause_duration_prio_1", + "rx_pause_transition_prio_1", + "rx_pause_prio_2", "rx_pause_duration_prio_2", + "rx_pause_transition_prio_2", + "rx_pause_prio_3", "rx_pause_duration_prio_3", + "rx_pause_transition_prio_3", + "rx_pause_prio_4", "rx_pause_duration_prio_4", + "rx_pause_transition_prio_4", + "rx_pause_prio_5", "rx_pause_duration_prio_5", + "rx_pause_transition_prio_5", + "rx_pause_prio_6", "rx_pause_duration_prio_6", + "rx_pause_transition_prio_6", + "rx_pause_prio_7", "rx_pause_duration_prio_7", + "rx_pause_transition_prio_7", + + /* flow control statistics rx */ + "rx_pause", "rx_pause_duration", "rx_pause_transition", + + /* priority flow control statistics tx */ + "tx_pause_prio_0", "tx_pause_duration_prio_0", + "tx_pause_transition_prio_0", + "tx_pause_prio_1", "tx_pause_duration_prio_1", + "tx_pause_transition_prio_1", + "tx_pause_prio_2", "tx_pause_duration_prio_2", + "tx_pause_transition_prio_2", + "tx_pause_prio_3", "tx_pause_duration_prio_3", + "tx_pause_transition_prio_3", + "tx_pause_prio_4", "tx_pause_duration_prio_4", + "tx_pause_transition_prio_4", + "tx_pause_prio_5", "tx_pause_duration_prio_5", + "tx_pause_transition_prio_5", + "tx_pause_prio_6", "tx_pause_duration_prio_6", + "tx_pause_transition_prio_6", + "tx_pause_prio_7", "tx_pause_duration_prio_7", + "tx_pause_transition_prio_7", + + /* flow control statistics tx */ + "tx_pause", "tx_pause_duration", "tx_pause_transition", + /* packet statistics */ "broadcast", "rx_prio_0", "rx_prio_1", "rx_prio_2", "rx_prio_3", "rx_prio_4", "rx_prio_5", "rx_prio_6", "rx_prio_7", "tx_prio_0", @@ -304,6 +346,26 @@ static void mlx4_en_get_ethtool_stats(struct net_device *dev, if (bitmap_iterator_test(&it)) data[index++] = ((unsigned long *)&priv->port_stats)[i]; + for (i = 0; i < NUM_FLOW_PRIORITY_STATS_RX; + i++, bitmap_iterator_inc(&it)) + if (bitmap_iterator_test(&it)) + data[index++] = + ((u64 *)&priv->rx_priority_flowstats)[i]; + + for (i = 0; i < NUM_FLOW_STATS_RX; i++, bitmap_iterator_inc(&it)) + if (bitmap_iterator_test(&it)) + data[index++] = ((u64 *)&priv->rx_flowstats)[i]; + + for (i = 0; i < NUM_FLOW_PRIORITY_STATS_TX; + i++, bitmap_iterator_inc(&it)) + if (bitmap_iterator_test(&it)) + data[index++] = + ((u64 *)&priv->tx_priority_flowstats)[i]; + + for (i = 0; i < NUM_FLOW_STATS_TX; i++, bitmap_iterator_inc(&it)) + if (bitmap_iterator_test(&it)) + data[index++] = ((u64 *)&priv->tx_flowstats)[i]; + for (i = 0; i < NUM_PKT_STATS; i++, bitmap_iterator_inc(&it)) if (bitmap_iterator_test(&it)) data[index++] = ((unsigned long *)&priv->pkstats)[i]; @@ -364,6 +426,12 @@ static void mlx4_en_get_strings(struct net_device *dev, strcpy(data + (index++) * ETH_GSTRING_LEN, main_strings[strings]); + for (i = 0; i < NUM_FLOW_STATS; i++, strings++, + bitmap_iterator_inc(&it)) + if (bitmap_iterator_test(&it)) + strcpy(data + (index++) * ETH_GSTRING_LEN, + main_strings[strings]); + for (i = 0; i < NUM_PKT_STATS; i++, strings++, bitmap_iterator_inc(&it)) if (bitmap_iterator_test(&it)) @@ -910,6 +978,12 @@ static int mlx4_en_set_pauseparam(struct net_device *dev, priv->prof->rx_ppp); if (err) en_err(priv, "Failed setting pause params\n"); + else + mlx4_en_update_pfc_stats_bitmap(mdev->dev, &priv->stats_bitmap, + priv->prof->rx_ppp, + priv->prof->rx_pause, + priv->prof->tx_ppp, + priv->prof->tx_pause); return err; } diff --git a/drivers/net/ethernet/mellanox/mlx4/en_netdev.c b/drivers/net/ethernet/mellanox/mlx4/en_netdev.c index 4542bab9494b..354e254b53cf 100644 --- a/drivers/net/ethernet/mellanox/mlx4/en_netdev.c +++ b/drivers/net/ethernet/mellanox/mlx4/en_netdev.c @@ -1888,6 +1888,12 @@ static void mlx4_en_clear_stats(struct net_device *dev) memset(&priv->pstats, 0, sizeof(priv->pstats)); memset(&priv->pkstats, 0, sizeof(priv->pkstats)); memset(&priv->port_stats, 0, sizeof(priv->port_stats)); + memset(&priv->rx_flowstats, 0, sizeof(priv->rx_flowstats)); + memset(&priv->tx_flowstats, 0, sizeof(priv->tx_flowstats)); + memset(&priv->rx_priority_flowstats, 0, + sizeof(priv->rx_priority_flowstats)); + memset(&priv->tx_priority_flowstats, 0, + sizeof(priv->tx_priority_flowstats)); for (i = 0; i < priv->tx_ring_num; i++) { priv->tx_ring[i]->bytes = 0; @@ -2648,8 +2654,46 @@ int mlx4_en_netdev_event(struct notifier_block *this, return NOTIFY_DONE; } +void mlx4_en_update_pfc_stats_bitmap(struct mlx4_dev *dev, + struct mlx4_en_stats_bitmap *stats_bitmap, + u8 rx_ppp, u8 rx_pause, + u8 tx_ppp, u8 tx_pause) +{ + int last_i = NUM_MAIN_STATS + NUM_PORT_STATS; + + if (!mlx4_is_slave(dev) && + (dev->caps.flags2 & MLX4_DEV_CAP_FLAG2_FLOWSTATS_EN)) { + mutex_lock(&stats_bitmap->mutex); + bitmap_clear(stats_bitmap->bitmap, last_i, NUM_FLOW_STATS); + + if (rx_ppp) + bitmap_set(stats_bitmap->bitmap, last_i, + NUM_FLOW_PRIORITY_STATS_RX); + last_i += NUM_FLOW_PRIORITY_STATS_RX; + + if (rx_pause && !(rx_ppp)) + bitmap_set(stats_bitmap->bitmap, last_i, + NUM_FLOW_STATS_RX); + last_i += NUM_FLOW_STATS_RX; + + if (tx_ppp) + bitmap_set(stats_bitmap->bitmap, last_i, + NUM_FLOW_PRIORITY_STATS_TX); + last_i += NUM_FLOW_PRIORITY_STATS_TX; + + if (tx_pause && !(tx_ppp)) + bitmap_set(stats_bitmap->bitmap, last_i, + NUM_FLOW_STATS_TX); + last_i += NUM_FLOW_STATS_TX; + + mutex_unlock(&stats_bitmap->mutex); + } +} + void mlx4_en_set_stats_bitmap(struct mlx4_dev *dev, - struct mlx4_en_stats_bitmap *stats_bitmap) + struct mlx4_en_stats_bitmap *stats_bitmap, + u8 rx_ppp, u8 rx_pause, + u8 tx_ppp, u8 tx_pause) { int last_i = 0; @@ -2677,6 +2721,11 @@ void mlx4_en_set_stats_bitmap(struct mlx4_dev *dev, bitmap_set(stats_bitmap->bitmap, last_i, NUM_PORT_STATS); last_i += NUM_PORT_STATS; + mlx4_en_update_pfc_stats_bitmap(dev, stats_bitmap, + rx_ppp, rx_pause, + tx_ppp, tx_pause); + last_i += NUM_FLOW_STATS; + if (!mlx4_is_slave(dev)) bitmap_set(stats_bitmap->bitmap, last_i, NUM_PKT_STATS); } @@ -2914,7 +2963,11 @@ int mlx4_en_init_netdev(struct mlx4_en_dev *mdev, int port, queue_delayed_work(mdev->workqueue, &priv->service_task, SERVICE_TASK_DELAY); - mlx4_en_set_stats_bitmap(mdev->dev, &priv->stats_bitmap); + mlx4_en_set_stats_bitmap(mdev->dev, &priv->stats_bitmap, + mdev->profile.prof[priv->port].rx_ppp, + mdev->profile.prof[priv->port].rx_pause, + mdev->profile.prof[priv->port].tx_ppp, + mdev->profile.prof[priv->port].tx_pause); return 0; diff --git a/drivers/net/ethernet/mellanox/mlx4/en_port.c b/drivers/net/ethernet/mellanox/mlx4/en_port.c index 6cb80072af6c..821ae1278dc9 100644 --- a/drivers/net/ethernet/mellanox/mlx4/en_port.c +++ b/drivers/net/ethernet/mellanox/mlx4/en_port.c @@ -131,6 +131,7 @@ out: int mlx4_en_DUMP_ETH_STATS(struct mlx4_en_dev *mdev, u8 port, u8 reset) { struct mlx4_en_stat_out_mbox *mlx4_en_stats; + struct mlx4_en_stat_out_flow_control_mbox *flowstats; struct mlx4_en_priv *priv = netdev_priv(mdev->pndev[port]); struct net_device_stats *stats = &priv->stats; struct mlx4_cmd_mailbox *mailbox; @@ -239,6 +240,55 @@ int mlx4_en_DUMP_ETH_STATS(struct mlx4_en_dev *mdev, u8 port, u8 reset) priv->pkstats.tx_prio[7] = be64_to_cpu(mlx4_en_stats->TTOT_prio_7); spin_unlock_bh(&priv->stats_lock); + /* 0xffs indicates invalid value */ + memset(mailbox->buf, 0xff, sizeof(*flowstats) * MLX4_NUM_PRIORITIES); + + if (mdev->dev->caps.flags2 & MLX4_DEV_CAP_FLAG2_FLOWSTATS_EN) { + memset(mailbox->buf, 0, + sizeof(*flowstats) * MLX4_NUM_PRIORITIES); + err = mlx4_cmd_box(mdev->dev, 0, mailbox->dma, + in_mod | MLX4_DUMP_ETH_STATS_FLOW_CONTROL, + 0, MLX4_CMD_DUMP_ETH_STATS, + MLX4_CMD_TIME_CLASS_B, MLX4_CMD_WRAPPED); + if (err) + goto out; + } + + flowstats = mailbox->buf; + + spin_lock_bh(&priv->stats_lock); + + for (i = 0; i < MLX4_NUM_PRIORITIES; i++) { + priv->rx_priority_flowstats[i].rx_pause = + be64_to_cpu(flowstats[i].rx_pause); + priv->rx_priority_flowstats[i].rx_pause_duration = + be64_to_cpu(flowstats[i].rx_pause_duration); + priv->rx_priority_flowstats[i].rx_pause_transition = + be64_to_cpu(flowstats[i].rx_pause_transition); + priv->tx_priority_flowstats[i].tx_pause = + be64_to_cpu(flowstats[i].tx_pause); + priv->tx_priority_flowstats[i].tx_pause_duration = + be64_to_cpu(flowstats[i].tx_pause_duration); + priv->tx_priority_flowstats[i].tx_pause_transition = + be64_to_cpu(flowstats[i].tx_pause_transition); + } + + /* if pfc is not in use, all priorities counters have the same value */ + priv->rx_flowstats.rx_pause = + be64_to_cpu(flowstats[0].rx_pause); + priv->rx_flowstats.rx_pause_duration = + be64_to_cpu(flowstats[0].rx_pause_duration); + priv->rx_flowstats.rx_pause_transition = + be64_to_cpu(flowstats[0].rx_pause_transition); + priv->tx_flowstats.tx_pause = + be64_to_cpu(flowstats[0].tx_pause); + priv->tx_flowstats.tx_pause_duration = + be64_to_cpu(flowstats[0].tx_pause_duration); + priv->tx_flowstats.tx_pause_transition = + be64_to_cpu(flowstats[0].tx_pause_transition); + + spin_unlock_bh(&priv->stats_lock); + out: mlx4_free_cmd_mailbox(mdev->dev, mailbox); return err; diff --git a/drivers/net/ethernet/mellanox/mlx4/fw.c b/drivers/net/ethernet/mellanox/mlx4/fw.c index 4a471f5d1b56..209a6171e59b 100644 --- a/drivers/net/ethernet/mellanox/mlx4/fw.c +++ b/drivers/net/ethernet/mellanox/mlx4/fw.c @@ -145,7 +145,8 @@ static void dump_dev_cap_flags2(struct mlx4_dev *dev, u64 flags) [20] = "Recoverable error events support", [21] = "Port Remap support", [22] = "QCN support", - [23] = "QP rate limiting support" + [23] = "QP rate limiting support", + [24] = "Ethernet Flow control statistics support" }; int i; @@ -672,6 +673,7 @@ int mlx4_QUERY_DEV_CAP(struct mlx4_dev *dev, struct mlx4_dev_cap *dev_cap) #define QUERY_DEV_CAP_RSVD_XRC_OFFSET 0x66 #define QUERY_DEV_CAP_MAX_XRC_OFFSET 0x67 #define QUERY_DEV_CAP_MAX_COUNTERS_OFFSET 0x68 +#define QUERY_DEV_CAP_PORT_FLOWSTATS_COUNTERS_OFFSET 0x70 #define QUERY_DEV_CAP_EXT_2_FLAGS_OFFSET 0x70 #define QUERY_DEV_CAP_FLOW_STEERING_IPOIB_OFFSET 0x74 #define QUERY_DEV_CAP_FLOW_STEERING_RANGE_EN_OFFSET 0x76 @@ -773,6 +775,9 @@ int mlx4_QUERY_DEV_CAP(struct mlx4_dev *dev, struct mlx4_dev_cap *dev_cap) MLX4_GET(field, outbox, QUERY_DEV_CAP_VL_PORT_OFFSET); dev_cap->num_ports = field & 0xf; MLX4_GET(field, outbox, QUERY_DEV_CAP_MAX_MSG_SZ_OFFSET); + MLX4_GET(field, outbox, QUERY_DEV_CAP_PORT_FLOWSTATS_COUNTERS_OFFSET); + if (field & 0x10) + dev_cap->flags2 |= MLX4_DEV_CAP_FLAG2_FLOWSTATS_EN; dev_cap->max_msg_sz = 1 << (field & 0x1f); MLX4_GET(field, outbox, QUERY_DEV_CAP_FLOW_STEERING_RANGE_EN_OFFSET); if (field & 0x80) @@ -1088,6 +1093,7 @@ out: return err; } +#define DEV_CAP_EXT_2_FLAG_PFC_COUNTERS (1 << 28) #define DEV_CAP_EXT_2_FLAG_VLAN_CONTROL (1 << 26) #define DEV_CAP_EXT_2_FLAG_80_VFS (1 << 21) #define DEV_CAP_EXT_2_FLAG_FSM (1 << 20) @@ -1177,7 +1183,7 @@ int mlx4_QUERY_DEV_CAP_wrapper(struct mlx4_dev *dev, int slave, /* turn off host side virt features (VST, FSM, etc) for guests */ MLX4_GET(field32, outbox->buf, QUERY_DEV_CAP_EXT_2_FLAGS_OFFSET); field32 &= ~(DEV_CAP_EXT_2_FLAG_VLAN_CONTROL | DEV_CAP_EXT_2_FLAG_80_VFS | - DEV_CAP_EXT_2_FLAG_FSM); + DEV_CAP_EXT_2_FLAG_FSM | DEV_CAP_EXT_2_FLAG_PFC_COUNTERS); MLX4_PUT(outbox->buf, field32, QUERY_DEV_CAP_EXT_2_FLAGS_OFFSET); /* turn off QCN for guests */ diff --git a/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h b/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h index d5d971a408f2..67eeea244eff 100644 --- a/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h +++ b/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h @@ -565,6 +565,10 @@ struct mlx4_en_priv { #endif struct mlx4_en_perf_stats pstats; struct mlx4_en_pkt_stats pkstats; + struct mlx4_en_flow_stats_rx rx_priority_flowstats[MLX4_NUM_PRIORITIES]; + struct mlx4_en_flow_stats_tx tx_priority_flowstats[MLX4_NUM_PRIORITIES]; + struct mlx4_en_flow_stats_rx rx_flowstats; + struct mlx4_en_flow_stats_tx tx_flowstats; struct mlx4_en_port_stats port_stats; struct mlx4_en_stats_bitmap stats_bitmap; struct list_head mc_list; @@ -736,7 +740,9 @@ int mlx4_en_start_port(struct net_device *dev); void mlx4_en_stop_port(struct net_device *dev, int detach); void mlx4_en_set_stats_bitmap(struct mlx4_dev *dev, - struct mlx4_en_stats_bitmap *stats_bitmap); + struct mlx4_en_stats_bitmap *stats_bitmap, + u8 rx_ppp, u8 rx_pause, + u8 tx_ppp, u8 tx_pause); void mlx4_en_free_resources(struct mlx4_en_priv *priv); int mlx4_en_alloc_resources(struct mlx4_en_priv *priv); @@ -823,7 +829,10 @@ void mlx4_en_ptp_overflow_check(struct mlx4_en_dev *mdev); int mlx4_en_reset_config(struct net_device *dev, struct hwtstamp_config ts_config, netdev_features_t new_features); - +void mlx4_en_update_pfc_stats_bitmap(struct mlx4_dev *dev, + struct mlx4_en_stats_bitmap *stats_bitmap, + u8 rx_ppp, u8 rx_pause, + u8 tx_ppp, u8 tx_pause); int mlx4_en_netdev_event(struct notifier_block *this, unsigned long event, void *ptr); diff --git a/drivers/net/ethernet/mellanox/mlx4/mlx4_stats.h b/drivers/net/ethernet/mellanox/mlx4/mlx4_stats.h index d7183e30b40b..e193680fb527 100644 --- a/drivers/net/ethernet/mellanox/mlx4/mlx4_stats.h +++ b/drivers/net/ethernet/mellanox/mlx4/mlx4_stats.h @@ -42,8 +42,58 @@ struct mlx4_en_perf_stats { }; #define NUM_MAIN_STATS 21 + +#define MLX4_NUM_PRIORITIES 8 + +struct mlx4_en_flow_stats_rx { + u64 rx_pause; + u64 rx_pause_duration; + u64 rx_pause_transition; +#define NUM_FLOW_STATS_RX 3 +#define NUM_FLOW_PRIORITY_STATS_RX (NUM_FLOW_STATS_RX * \ + MLX4_NUM_PRIORITIES) +}; + +struct mlx4_en_flow_stats_tx { + u64 tx_pause; + u64 tx_pause_duration; + u64 tx_pause_transition; +#define NUM_FLOW_STATS_TX 3 +#define NUM_FLOW_PRIORITY_STATS_TX (NUM_FLOW_STATS_TX * \ + MLX4_NUM_PRIORITIES) +}; + +#define NUM_FLOW_STATS (NUM_FLOW_STATS_RX + NUM_FLOW_STATS_TX + \ + NUM_FLOW_PRIORITY_STATS_TX + \ + NUM_FLOW_PRIORITY_STATS_RX) + +struct mlx4_en_stat_out_flow_control_mbox { + /* Total number of PAUSE frames received from the far-end port */ + __be64 rx_pause; + /* Total number of microseconds that far-end port requested to pause + * transmission of packets + */ + __be64 rx_pause_duration; + /* Number of received transmission from XOFF state to XON state */ + __be64 rx_pause_transition; + /* Total number of PAUSE frames sent from the far-end port */ + __be64 tx_pause; + /* Total time in microseconds that transmission of packets has been + * paused + */ + __be64 tx_pause_duration; + /* Number of transmitter transitions from XOFF state to XON state */ + __be64 tx_pause_transition; + /* Reserverd */ + __be64 reserved[2]; +}; + +enum { + MLX4_DUMP_ETH_STATS_FLOW_CONTROL = 1 << 12 +}; + #define NUM_ALL_STATS (NUM_MAIN_STATS + NUM_PORT_STATS + NUM_PKT_STATS + \ - NUM_PERF_STATS) + NUM_FLOW_STATS + NUM_PERF_STATS) #define MLX4_FIND_NETDEV_STAT(n) (offsetof(struct net_device_stats, n) / \ sizeof(((struct net_device_stats *)0)->n)) diff --git a/include/linux/mlx4/device.h b/include/linux/mlx4/device.h index 49abbe28e230..ab7ebec943b8 100644 --- a/include/linux/mlx4/device.h +++ b/include/linux/mlx4/device.h @@ -205,7 +205,8 @@ enum { MLX4_DEV_CAP_FLAG2_RECOVERABLE_ERROR_EVENT = 1LL << 20, MLX4_DEV_CAP_FLAG2_PORT_REMAP = 1LL << 21, MLX4_DEV_CAP_FLAG2_QCN = 1LL << 22, - MLX4_DEV_CAP_FLAG2_QP_RATE_LIMIT = 1LL << 23 + MLX4_DEV_CAP_FLAG2_QP_RATE_LIMIT = 1LL << 23, + MLX4_DEV_CAP_FLAG2_FLOWSTATS_EN = 1LL << 24 }; enum { -- cgit v1.2.3 From d894fc6046fecd66b0d8ec35c7d2515781cc030b Mon Sep 17 00:00:00 2001 From: Alex Smith Date: Wed, 18 Mar 2015 16:16:36 +0000 Subject: dmaengine: jz4780: add driver for the Ingenic JZ4780 DMA controller This patch adds a driver for the DMA controller found in the Ingenic JZ4780. It currently does not implement any support for the programmable firmware feature of the controller - this is not necessary for most uses. It also does not take priority into account when allocating channels, it just allocates the first available channel. This can be implemented later. Signed-off-by: Alex Smith Signed-off-by: Zubair Lutfullah Kakakhel [Updated for dmaengine api changes, Add residue support, couple of minor fixes] Signed-off-by: Vinod Koul --- drivers/dma/Kconfig | 10 + drivers/dma/Makefile | 1 + drivers/dma/dma-jz4780.c | 877 +++++++++++++++++++++++++++++++++++ include/dt-bindings/dma/jz4780-dma.h | 49 ++ 4 files changed, 937 insertions(+) create mode 100644 drivers/dma/dma-jz4780.c create mode 100644 include/dt-bindings/dma/jz4780-dma.h (limited to 'include') diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig index a874b6ec6650..ce09734248da 100644 --- a/drivers/dma/Kconfig +++ b/drivers/dma/Kconfig @@ -358,6 +358,16 @@ config DMA_JZ4740 select DMA_ENGINE select DMA_VIRTUAL_CHANNELS +config DMA_JZ4780 + tristate "JZ4780 DMA support" + depends on MACH_JZ4780 + select DMA_ENGINE + select DMA_VIRTUAL_CHANNELS + help + This selects support for the DMA controller in Ingenic JZ4780 SoCs. + If you have a board based on such a SoC and wish to use DMA for + devices which can use the DMA controller, say Y or M here. + config K3_DMA tristate "Hisilicon K3 DMA support" depends on ARCH_HI3xxx diff --git a/drivers/dma/Makefile b/drivers/dma/Makefile index f915f61ec574..af239e765cbb 100644 --- a/drivers/dma/Makefile +++ b/drivers/dma/Makefile @@ -41,6 +41,7 @@ obj-$(CONFIG_DMA_OMAP) += omap-dma.o obj-$(CONFIG_DMA_BCM2835) += bcm2835-dma.o obj-$(CONFIG_MMP_PDMA) += mmp_pdma.o obj-$(CONFIG_DMA_JZ4740) += dma-jz4740.o +obj-$(CONFIG_DMA_JZ4780) += dma-jz4780.o obj-$(CONFIG_TI_CPPI41) += cppi41.o obj-$(CONFIG_K3_DMA) += k3dma.o obj-$(CONFIG_MOXART_DMA) += moxart-dma.o diff --git a/drivers/dma/dma-jz4780.c b/drivers/dma/dma-jz4780.c new file mode 100644 index 000000000000..26d2f0e09ea3 --- /dev/null +++ b/drivers/dma/dma-jz4780.c @@ -0,0 +1,877 @@ +/* + * Ingenic JZ4780 DMA controller + * + * Copyright (c) 2015 Imagination Technologies + * Author: Alex Smith + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "dmaengine.h" +#include "virt-dma.h" + +#define JZ_DMA_NR_CHANNELS 32 + +/* Global registers. */ +#define JZ_DMA_REG_DMAC 0x1000 +#define JZ_DMA_REG_DIRQP 0x1004 +#define JZ_DMA_REG_DDR 0x1008 +#define JZ_DMA_REG_DDRS 0x100c +#define JZ_DMA_REG_DMACP 0x101c +#define JZ_DMA_REG_DSIRQP 0x1020 +#define JZ_DMA_REG_DSIRQM 0x1024 +#define JZ_DMA_REG_DCIRQP 0x1028 +#define JZ_DMA_REG_DCIRQM 0x102c + +/* Per-channel registers. */ +#define JZ_DMA_REG_CHAN(n) (n * 0x20) +#define JZ_DMA_REG_DSA(n) (0x00 + JZ_DMA_REG_CHAN(n)) +#define JZ_DMA_REG_DTA(n) (0x04 + JZ_DMA_REG_CHAN(n)) +#define JZ_DMA_REG_DTC(n) (0x08 + JZ_DMA_REG_CHAN(n)) +#define JZ_DMA_REG_DRT(n) (0x0c + JZ_DMA_REG_CHAN(n)) +#define JZ_DMA_REG_DCS(n) (0x10 + JZ_DMA_REG_CHAN(n)) +#define JZ_DMA_REG_DCM(n) (0x14 + JZ_DMA_REG_CHAN(n)) +#define JZ_DMA_REG_DDA(n) (0x18 + JZ_DMA_REG_CHAN(n)) +#define JZ_DMA_REG_DSD(n) (0x1c + JZ_DMA_REG_CHAN(n)) + +#define JZ_DMA_DMAC_DMAE BIT(0) +#define JZ_DMA_DMAC_AR BIT(2) +#define JZ_DMA_DMAC_HLT BIT(3) +#define JZ_DMA_DMAC_FMSC BIT(31) + +#define JZ_DMA_DRT_AUTO 0x8 + +#define JZ_DMA_DCS_CTE BIT(0) +#define JZ_DMA_DCS_HLT BIT(2) +#define JZ_DMA_DCS_TT BIT(3) +#define JZ_DMA_DCS_AR BIT(4) +#define JZ_DMA_DCS_DES8 BIT(30) + +#define JZ_DMA_DCM_LINK BIT(0) +#define JZ_DMA_DCM_TIE BIT(1) +#define JZ_DMA_DCM_STDE BIT(2) +#define JZ_DMA_DCM_TSZ_SHIFT 8 +#define JZ_DMA_DCM_TSZ_MASK (0x7 << JZ_DMA_DCM_TSZ_SHIFT) +#define JZ_DMA_DCM_DP_SHIFT 12 +#define JZ_DMA_DCM_SP_SHIFT 14 +#define JZ_DMA_DCM_DAI BIT(22) +#define JZ_DMA_DCM_SAI BIT(23) + +#define JZ_DMA_SIZE_4_BYTE 0x0 +#define JZ_DMA_SIZE_1_BYTE 0x1 +#define JZ_DMA_SIZE_2_BYTE 0x2 +#define JZ_DMA_SIZE_16_BYTE 0x3 +#define JZ_DMA_SIZE_32_BYTE 0x4 +#define JZ_DMA_SIZE_64_BYTE 0x5 +#define JZ_DMA_SIZE_128_BYTE 0x6 + +#define JZ_DMA_WIDTH_32_BIT 0x0 +#define JZ_DMA_WIDTH_8_BIT 0x1 +#define JZ_DMA_WIDTH_16_BIT 0x2 + +#define JZ_DMA_BUSWIDTHS (BIT(DMA_SLAVE_BUSWIDTH_1_BYTE) | \ + BIT(DMA_SLAVE_BUSWIDTH_2_BYTES) | \ + BIT(DMA_SLAVE_BUSWIDTH_4_BYTES)) + +/** + * struct jz4780_dma_hwdesc - descriptor structure read by the DMA controller. + * @dcm: value for the DCM (channel command) register + * @dsa: source address + * @dta: target address + * @dtc: transfer count (number of blocks of the transfer size specified in DCM + * to transfer) in the low 24 bits, offset of the next descriptor from the + * descriptor base address in the upper 8 bits. + * @sd: target/source stride difference (in stride transfer mode). + * @drt: request type + */ +struct jz4780_dma_hwdesc { + uint32_t dcm; + uint32_t dsa; + uint32_t dta; + uint32_t dtc; + uint32_t sd; + uint32_t drt; + uint32_t reserved[2]; +}; + +/* Size of allocations for hardware descriptor blocks. */ +#define JZ_DMA_DESC_BLOCK_SIZE PAGE_SIZE +#define JZ_DMA_MAX_DESC \ + (JZ_DMA_DESC_BLOCK_SIZE / sizeof(struct jz4780_dma_hwdesc)) + +struct jz4780_dma_desc { + struct virt_dma_desc vdesc; + + struct jz4780_dma_hwdesc *desc; + dma_addr_t desc_phys; + unsigned int count; + enum dma_transaction_type type; + uint32_t status; +}; + +struct jz4780_dma_chan { + struct virt_dma_chan vchan; + unsigned int id; + struct dma_pool *desc_pool; + + uint32_t transfer_type; + uint32_t transfer_shift; + struct dma_slave_config config; + + struct jz4780_dma_desc *desc; + unsigned int curr_hwdesc; +}; + +struct jz4780_dma_dev { + struct dma_device dma_device; + void __iomem *base; + struct clk *clk; + unsigned int irq; + + uint32_t chan_reserved; + struct jz4780_dma_chan chan[JZ_DMA_NR_CHANNELS]; +}; + +struct jz4780_dma_data { + uint32_t transfer_type; + int channel; +}; + +static inline struct jz4780_dma_chan *to_jz4780_dma_chan(struct dma_chan *chan) +{ + return container_of(chan, struct jz4780_dma_chan, vchan.chan); +} + +static inline struct jz4780_dma_desc *to_jz4780_dma_desc( + struct virt_dma_desc *vdesc) +{ + return container_of(vdesc, struct jz4780_dma_desc, vdesc); +} + +static inline struct jz4780_dma_dev *jz4780_dma_chan_parent( + struct jz4780_dma_chan *jzchan) +{ + return container_of(jzchan->vchan.chan.device, struct jz4780_dma_dev, + dma_device); +} + +static inline uint32_t jz4780_dma_readl(struct jz4780_dma_dev *jzdma, + unsigned int reg) +{ + return readl(jzdma->base + reg); +} + +static inline void jz4780_dma_writel(struct jz4780_dma_dev *jzdma, + unsigned int reg, uint32_t val) +{ + writel(val, jzdma->base + reg); +} + +static struct jz4780_dma_desc *jz4780_dma_desc_alloc( + struct jz4780_dma_chan *jzchan, unsigned int count, + enum dma_transaction_type type) +{ + struct jz4780_dma_desc *desc; + + if (count > JZ_DMA_MAX_DESC) + return NULL; + + desc = kzalloc(sizeof(*desc), GFP_NOWAIT); + if (!desc) + return NULL; + + desc->desc = dma_pool_alloc(jzchan->desc_pool, GFP_NOWAIT, + &desc->desc_phys); + if (!desc->desc) { + kfree(desc); + return NULL; + } + + desc->count = count; + desc->type = type; + return desc; +} + +static void jz4780_dma_desc_free(struct virt_dma_desc *vdesc) +{ + struct jz4780_dma_desc *desc = to_jz4780_dma_desc(vdesc); + struct jz4780_dma_chan *jzchan = to_jz4780_dma_chan(vdesc->tx.chan); + + dma_pool_free(jzchan->desc_pool, desc->desc, desc->desc_phys); + kfree(desc); +} + +static uint32_t jz4780_dma_transfer_size(unsigned long val, int *ord) +{ + *ord = ffs(val) - 1; + + switch (*ord) { + case 0: + return JZ_DMA_SIZE_1_BYTE; + case 1: + return JZ_DMA_SIZE_2_BYTE; + case 2: + return JZ_DMA_SIZE_4_BYTE; + case 4: + return JZ_DMA_SIZE_16_BYTE; + case 5: + return JZ_DMA_SIZE_32_BYTE; + case 6: + return JZ_DMA_SIZE_64_BYTE; + case 7: + return JZ_DMA_SIZE_128_BYTE; + default: + return -EINVAL; + } +} + +static uint32_t jz4780_dma_setup_hwdesc(struct jz4780_dma_chan *jzchan, + struct jz4780_dma_hwdesc *desc, dma_addr_t addr, size_t len, + enum dma_transfer_direction direction) +{ + struct dma_slave_config *config = &jzchan->config; + uint32_t width, maxburst, tsz; + int ord; + + if (direction == DMA_MEM_TO_DEV) { + desc->dcm = JZ_DMA_DCM_SAI; + desc->dsa = addr; + desc->dta = config->dst_addr; + desc->drt = jzchan->transfer_type; + + width = config->dst_addr_width; + maxburst = config->dst_maxburst; + } else { + desc->dcm = JZ_DMA_DCM_DAI; + desc->dsa = config->src_addr; + desc->dta = addr; + desc->drt = jzchan->transfer_type; + + width = config->src_addr_width; + maxburst = config->src_maxburst; + } + + /* + * This calculates the maximum transfer size that can be used with the + * given address, length, width and maximum burst size. The address + * must be aligned to the transfer size, the total length must be + * divisible by the transfer size, and we must not use more than the + * maximum burst specified by the user. + */ + tsz = jz4780_dma_transfer_size(addr | len | (width * maxburst), &ord); + jzchan->transfer_shift = ord; + + switch (width) { + case DMA_SLAVE_BUSWIDTH_1_BYTE: + case DMA_SLAVE_BUSWIDTH_2_BYTES: + break; + case DMA_SLAVE_BUSWIDTH_4_BYTES: + width = JZ_DMA_WIDTH_32_BIT; + break; + default: + return -EINVAL; + } + + desc->dcm |= tsz << JZ_DMA_DCM_TSZ_SHIFT; + desc->dcm |= width << JZ_DMA_DCM_SP_SHIFT; + desc->dcm |= width << JZ_DMA_DCM_DP_SHIFT; + + desc->dtc = len >> ord; +} + +static struct dma_async_tx_descriptor *jz4780_dma_prep_slave_sg( + struct dma_chan *chan, struct scatterlist *sgl, unsigned int sg_len, + enum dma_transfer_direction direction, unsigned long flags) +{ + struct jz4780_dma_chan *jzchan = to_jz4780_dma_chan(chan); + struct jz4780_dma_desc *desc; + unsigned int i; + int err; + + desc = jz4780_dma_desc_alloc(jzchan, sg_len, DMA_SLAVE); + if (!desc) + return NULL; + + for (i = 0; i < sg_len; i++) { + err = jz4780_dma_setup_hwdesc(jzchan, &desc->desc[i], + sg_dma_address(&sgl[i]), + sg_dma_len(&sgl[i]), + direction); + if (err < 0) + return ERR_PTR(err); + + + desc->desc[i].dcm |= JZ_DMA_DCM_TIE; + + if (i != (sg_len - 1)) { + /* Automatically proceeed to the next descriptor. */ + desc->desc[i].dcm |= JZ_DMA_DCM_LINK; + + /* + * The upper 8 bits of the DTC field in the descriptor + * must be set to (offset from descriptor base of next + * descriptor >> 4). + */ + desc->desc[i].dtc |= + (((i + 1) * sizeof(*desc->desc)) >> 4) << 24; + } + } + + return vchan_tx_prep(&jzchan->vchan, &desc->vdesc, flags); +} + +static struct dma_async_tx_descriptor *jz4780_dma_prep_dma_cyclic( + struct dma_chan *chan, dma_addr_t buf_addr, size_t buf_len, + size_t period_len, enum dma_transfer_direction direction, + unsigned long flags) +{ + struct jz4780_dma_chan *jzchan = to_jz4780_dma_chan(chan); + struct jz4780_dma_desc *desc; + unsigned int periods, i; + int err; + + if (buf_len % period_len) + return NULL; + + periods = buf_len / period_len; + + desc = jz4780_dma_desc_alloc(jzchan, periods, DMA_CYCLIC); + if (!desc) + return NULL; + + for (i = 0; i < periods; i++) { + err = jz4780_dma_setup_hwdesc(jzchan, &desc->desc[i], buf_addr, + period_len, direction); + if (err < 0) + return ERR_PTR(err); + + buf_addr += period_len; + + /* + * Set the link bit to indicate that the controller should + * automatically proceed to the next descriptor. In + * jz4780_dma_begin(), this will be cleared if we need to issue + * an interrupt after each period. + */ + desc->desc[i].dcm |= JZ_DMA_DCM_TIE | JZ_DMA_DCM_LINK; + + /* + * The upper 8 bits of the DTC field in the descriptor must be + * set to (offset from descriptor base of next descriptor >> 4). + * If this is the last descriptor, link it back to the first, + * i.e. leave offset set to 0, otherwise point to the next one. + */ + if (i != (periods - 1)) { + desc->desc[i].dtc |= + (((i + 1) * sizeof(*desc->desc)) >> 4) << 24; + } + } + + return vchan_tx_prep(&jzchan->vchan, &desc->vdesc, flags); +} + +struct dma_async_tx_descriptor *jz4780_dma_prep_dma_memcpy( + struct dma_chan *chan, dma_addr_t dest, dma_addr_t src, + size_t len, unsigned long flags) +{ + struct jz4780_dma_chan *jzchan = to_jz4780_dma_chan(chan); + struct jz4780_dma_desc *desc; + uint32_t tsz; + int ord; + + desc = jz4780_dma_desc_alloc(jzchan, 1, DMA_MEMCPY); + if (!desc) + return NULL; + + tsz = jz4780_dma_transfer_size(dest | src | len, &ord); + if (tsz < 0) + return ERR_PTR(tsz); + + desc->desc[0].dsa = src; + desc->desc[0].dta = dest; + desc->desc[0].drt = JZ_DMA_DRT_AUTO; + desc->desc[0].dcm = JZ_DMA_DCM_TIE | JZ_DMA_DCM_SAI | JZ_DMA_DCM_DAI | + tsz << JZ_DMA_DCM_TSZ_SHIFT | + JZ_DMA_WIDTH_32_BIT << JZ_DMA_DCM_SP_SHIFT | + JZ_DMA_WIDTH_32_BIT << JZ_DMA_DCM_DP_SHIFT; + desc->desc[0].dtc = len >> ord; + + return vchan_tx_prep(&jzchan->vchan, &desc->vdesc, flags); +} + +static void jz4780_dma_begin(struct jz4780_dma_chan *jzchan) +{ + struct jz4780_dma_dev *jzdma = jz4780_dma_chan_parent(jzchan); + struct virt_dma_desc *vdesc; + unsigned int i; + dma_addr_t desc_phys; + + if (!jzchan->desc) { + vdesc = vchan_next_desc(&jzchan->vchan); + if (!vdesc) + return; + + list_del(&vdesc->node); + + jzchan->desc = to_jz4780_dma_desc(vdesc); + jzchan->curr_hwdesc = 0; + + if (jzchan->desc->type == DMA_CYCLIC && vdesc->tx.callback) { + /* + * The DMA controller doesn't support triggering an + * interrupt after processing each descriptor, only + * after processing an entire terminated list of + * descriptors. For a cyclic DMA setup the list of + * descriptors is not terminated so we can never get an + * interrupt. + * + * If the user requested a callback for a cyclic DMA + * setup then we workaround this hardware limitation + * here by degrading to a set of unlinked descriptors + * which we will submit in sequence in response to the + * completion of processing the previous descriptor. + */ + for (i = 0; i < jzchan->desc->count; i++) + jzchan->desc->desc[i].dcm &= ~JZ_DMA_DCM_LINK; + } + } else { + /* + * There is an existing transfer, therefore this must be one + * for which we unlinked the descriptors above. Advance to the + * next one in the list. + */ + jzchan->curr_hwdesc = + (jzchan->curr_hwdesc + 1) % jzchan->desc->count; + } + + /* Use 8-word descriptors. */ + jz4780_dma_writel(jzdma, JZ_DMA_REG_DCS(jzchan->id), JZ_DMA_DCS_DES8); + + /* Write descriptor address and initiate descriptor fetch. */ + desc_phys = jzchan->desc->desc_phys + + (jzchan->curr_hwdesc * sizeof(*jzchan->desc->desc)); + jz4780_dma_writel(jzdma, JZ_DMA_REG_DDA(jzchan->id), desc_phys); + jz4780_dma_writel(jzdma, JZ_DMA_REG_DDRS, BIT(jzchan->id)); + + /* Enable the channel. */ + jz4780_dma_writel(jzdma, JZ_DMA_REG_DCS(jzchan->id), + JZ_DMA_DCS_DES8 | JZ_DMA_DCS_CTE); +} + +static void jz4780_dma_issue_pending(struct dma_chan *chan) +{ + struct jz4780_dma_chan *jzchan = to_jz4780_dma_chan(chan); + unsigned long flags; + + spin_lock_irqsave(&jzchan->vchan.lock, flags); + + if (vchan_issue_pending(&jzchan->vchan) && !jzchan->desc) + jz4780_dma_begin(jzchan); + + spin_unlock_irqrestore(&jzchan->vchan.lock, flags); +} + +static int jz4780_dma_terminate_all(struct jz4780_dma_chan *jzchan) +{ + struct jz4780_dma_dev *jzdma = jz4780_dma_chan_parent(jzchan); + unsigned long flags; + LIST_HEAD(head); + + spin_lock_irqsave(&jzchan->vchan.lock, flags); + + /* Clear the DMA status and stop the transfer. */ + jz4780_dma_writel(jzdma, JZ_DMA_REG_DCS(jzchan->id), 0); + if (jzchan->desc) { + jz4780_dma_desc_free(&jzchan->desc->vdesc); + jzchan->desc = NULL; + } + + vchan_get_all_descriptors(&jzchan->vchan, &head); + + spin_unlock_irqrestore(&jzchan->vchan.lock, flags); + + vchan_dma_desc_free_list(&jzchan->vchan, &head); + return 0; +} + +static int jz4780_dma_slave_config(struct jz4780_dma_chan *jzchan, + const struct dma_slave_config *config) +{ + if ((config->src_addr_width == DMA_SLAVE_BUSWIDTH_8_BYTES) + || (config->dst_addr_width == DMA_SLAVE_BUSWIDTH_8_BYTES)) + return -EINVAL; + + /* Copy the reset of the slave configuration, it is used later. */ + memcpy(&jzchan->config, config, sizeof(jzchan->config)); + + return 0; +} + +static size_t jz4780_dma_desc_residue(struct jz4780_dma_chan *jzchan, + struct jz4780_dma_desc *desc, unsigned int next_sg) +{ + struct jz4780_dma_dev *jzdma = jz4780_dma_chan_parent(jzchan); + unsigned int residue, count; + unsigned int i; + + residue = 0; + + for (i = next_sg; i < desc->count; i++) + residue += desc->desc[i].dtc << jzchan->transfer_shift; + + if (next_sg != 0) { + count = jz4780_dma_readl(jzdma, + JZ_DMA_REG_DTC(jzchan->id)); + residue += count << jzchan->transfer_shift; + } + + return residue; +} + +static enum dma_status jz4780_dma_tx_status(struct dma_chan *chan, + dma_cookie_t cookie, struct dma_tx_state *txstate) +{ + struct jz4780_dma_chan *jzchan = to_jz4780_dma_chan(chan); + struct virt_dma_desc *vdesc; + enum dma_status status; + unsigned long flags; + + status = dma_cookie_status(chan, cookie, txstate); + if ((status == DMA_COMPLETE) || (txstate == NULL)) + return status; + + spin_lock_irqsave(&jzchan->vchan.lock, flags); + + vdesc = vchan_find_desc(&jzchan->vchan, cookie); + if (vdesc) { + /* On the issued list, so hasn't been processed yet */ + txstate->residue = jz4780_dma_desc_residue(jzchan, + to_jz4780_dma_desc(vdesc), 0); + } else if (cookie == jzchan->desc->vdesc.tx.cookie) { + txstate->residue = jz4780_dma_desc_residue(jzchan, jzchan->desc, + (jzchan->curr_hwdesc + 1) % jzchan->desc->count); + } else + txstate->residue = 0; + + if (vdesc && jzchan->desc && vdesc == &jzchan->desc->vdesc + && jzchan->desc->status & (JZ_DMA_DCS_AR | JZ_DMA_DCS_HLT)) + status = DMA_ERROR; + + spin_unlock_irqrestore(&jzchan->vchan.lock, flags); + return status; +} + +static void jz4780_dma_chan_irq(struct jz4780_dma_dev *jzdma, + struct jz4780_dma_chan *jzchan) +{ + uint32_t dcs; + + spin_lock(&jzchan->vchan.lock); + + dcs = jz4780_dma_readl(jzdma, JZ_DMA_REG_DCS(jzchan->id)); + jz4780_dma_writel(jzdma, JZ_DMA_REG_DCS(jzchan->id), 0); + + if (dcs & JZ_DMA_DCS_AR) { + dev_warn(&jzchan->vchan.chan.dev->device, + "address error (DCS=0x%x)\n", dcs); + } + + if (dcs & JZ_DMA_DCS_HLT) { + dev_warn(&jzchan->vchan.chan.dev->device, + "channel halt (DCS=0x%x)\n", dcs); + } + + if (jzchan->desc) { + jzchan->desc->status = dcs; + + if ((dcs & (JZ_DMA_DCS_AR | JZ_DMA_DCS_HLT)) == 0) { + if (jzchan->desc->type == DMA_CYCLIC) { + vchan_cyclic_callback(&jzchan->desc->vdesc); + } else { + vchan_cookie_complete(&jzchan->desc->vdesc); + jzchan->desc = NULL; + } + + jz4780_dma_begin(jzchan); + } + } else { + dev_err(&jzchan->vchan.chan.dev->device, + "channel IRQ with no active transfer\n"); + } + + spin_unlock(&jzchan->vchan.lock); +} + +static irqreturn_t jz4780_dma_irq_handler(int irq, void *data) +{ + struct jz4780_dma_dev *jzdma = data; + uint32_t pending, dmac; + int i; + + pending = jz4780_dma_readl(jzdma, JZ_DMA_REG_DIRQP); + + for (i = 0; i < JZ_DMA_NR_CHANNELS; i++) { + if (!(pending & (1<chan[i]); + } + + /* Clear halt and address error status of all channels. */ + dmac = jz4780_dma_readl(jzdma, JZ_DMA_REG_DMAC); + dmac &= ~(JZ_DMA_DMAC_HLT | JZ_DMA_DMAC_AR); + jz4780_dma_writel(jzdma, JZ_DMA_REG_DMAC, dmac); + + /* Clear interrupt pending status. */ + jz4780_dma_writel(jzdma, JZ_DMA_REG_DIRQP, 0); + + return IRQ_HANDLED; +} + +static int jz4780_dma_alloc_chan_resources(struct dma_chan *chan) +{ + struct jz4780_dma_chan *jzchan = to_jz4780_dma_chan(chan); + + jzchan->desc_pool = dma_pool_create(dev_name(&chan->dev->device), + chan->device->dev, + JZ_DMA_DESC_BLOCK_SIZE, + PAGE_SIZE, 0); + if (!jzchan->desc_pool) { + dev_err(&chan->dev->device, + "failed to allocate descriptor pool\n"); + return -ENOMEM; + } + + return 0; +} + +static void jz4780_dma_free_chan_resources(struct dma_chan *chan) +{ + struct jz4780_dma_chan *jzchan = to_jz4780_dma_chan(chan); + + vchan_free_chan_resources(&jzchan->vchan); + dma_pool_destroy(jzchan->desc_pool); + jzchan->desc_pool = NULL; +} + +static bool jz4780_dma_filter_fn(struct dma_chan *chan, void *param) +{ + struct jz4780_dma_chan *jzchan = to_jz4780_dma_chan(chan); + struct jz4780_dma_dev *jzdma = jz4780_dma_chan_parent(jzchan); + struct jz4780_dma_data *data = param; + + if (data->channel > -1) { + if (data->channel != jzchan->id) + return false; + } else if (jzdma->chan_reserved & BIT(jzchan->id)) { + return false; + } + + jzchan->transfer_type = data->transfer_type; + + return true; +} + +static struct dma_chan *jz4780_of_dma_xlate(struct of_phandle_args *dma_spec, + struct of_dma *ofdma) +{ + struct jz4780_dma_dev *jzdma = ofdma->of_dma_data; + dma_cap_mask_t mask = jzdma->dma_device.cap_mask; + struct jz4780_dma_data data; + + if (dma_spec->args_count != 2) + return NULL; + + data.transfer_type = dma_spec->args[0]; + data.channel = dma_spec->args[1]; + + if (data.channel > -1) { + if (data.channel >= JZ_DMA_NR_CHANNELS) { + dev_err(jzdma->dma_device.dev, + "device requested non-existent channel %u\n", + data.channel); + return NULL; + } + + /* Can only select a channel marked as reserved. */ + if (!(jzdma->chan_reserved & BIT(data.channel))) { + dev_err(jzdma->dma_device.dev, + "device requested unreserved channel %u\n", + data.channel); + return NULL; + } + } + + return dma_request_channel(mask, jz4780_dma_filter_fn, &data); +} + +static int jz4780_dma_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct jz4780_dma_dev *jzdma; + struct jz4780_dma_chan *jzchan; + struct dma_device *dd; + struct resource *res; + int i, ret; + + jzdma = devm_kzalloc(dev, sizeof(*jzdma), GFP_KERNEL); + if (!jzdma) + return -ENOMEM; + + platform_set_drvdata(pdev, jzdma); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(dev, "failed to get I/O memory\n"); + return -EINVAL; + } + + jzdma->base = devm_ioremap_resource(dev, res); + if (IS_ERR(jzdma->base)) + return PTR_ERR(jzdma->base); + + jzdma->irq = platform_get_irq(pdev, 0); + if (jzdma->irq < 0) { + dev_err(dev, "failed to get IRQ: %d\n", ret); + return jzdma->irq; + } + + ret = devm_request_irq(dev, jzdma->irq, jz4780_dma_irq_handler, 0, + dev_name(dev), jzdma); + if (ret) { + dev_err(dev, "failed to request IRQ %u!\n", jzdma->irq); + return -EINVAL; + } + + jzdma->clk = devm_clk_get(dev, NULL); + if (IS_ERR(jzdma->clk)) { + dev_err(dev, "failed to get clock\n"); + return PTR_ERR(jzdma->clk); + } + + clk_prepare_enable(jzdma->clk); + + /* Property is optional, if it doesn't exist the value will remain 0. */ + of_property_read_u32_index(dev->of_node, "ingenic,reserved-channels", + 0, &jzdma->chan_reserved); + + dd = &jzdma->dma_device; + + dma_cap_set(DMA_MEMCPY, dd->cap_mask); + dma_cap_set(DMA_SLAVE, dd->cap_mask); + dma_cap_set(DMA_CYCLIC, dd->cap_mask); + + dd->dev = dev; + dd->copy_align = 2; /* 2^2 = 4 byte alignment */ + dd->device_alloc_chan_resources = jz4780_dma_alloc_chan_resources; + dd->device_free_chan_resources = jz4780_dma_free_chan_resources; + dd->device_prep_slave_sg = jz4780_dma_prep_slave_sg; + dd->device_prep_dma_cyclic = jz4780_dma_prep_dma_cyclic; + dd->device_prep_dma_memcpy = jz4780_dma_prep_dma_memcpy; + dd->device_config = jz4780_dma_slave_config; + dd->device_terminate_all = jz4780_dma_terminate_all; + dd->device_tx_status = jz4780_dma_tx_status; + dd->device_issue_pending = jz4780_dma_issue_pending; + dd->src_addr_widths = JZ_DMA_BUSWIDTHS; + dd->dst_addr_widths = JZ_DMA_BUSWIDTHS; + dd->directions = BIT(DMA_DEV_TO_MEM) | BIT(DMA_MEM_TO_DEV); + dd->residue_granularity = DMA_RESIDUE_GRANULARITY_BURST; + + + /* + * Enable DMA controller, mark all channels as not programmable. + * Also set the FMSC bit - it increases MSC performance, so it makes + * little sense not to enable it. + */ + jz4780_dma_writel(jzdma, JZ_DMA_REG_DMAC, + JZ_DMA_DMAC_DMAE | JZ_DMA_DMAC_FMSC); + jz4780_dma_writel(jzdma, JZ_DMA_REG_DMACP, 0); + + INIT_LIST_HEAD(&dd->channels); + + for (i = 0; i < JZ_DMA_NR_CHANNELS; i++) { + jzchan = &jzdma->chan[i]; + jzchan->id = i; + + vchan_init(&jzchan->vchan, dd); + jzchan->vchan.desc_free = jz4780_dma_desc_free; + } + + ret = dma_async_device_register(dd); + if (ret) { + dev_err(dev, "failed to register device\n"); + goto err_disable_clk; + } + + /* Register with OF DMA helpers. */ + ret = of_dma_controller_register(dev->of_node, jz4780_of_dma_xlate, + jzdma); + if (ret) { + dev_err(dev, "failed to register OF DMA controller\n"); + goto err_unregister_dev; + } + + dev_info(dev, "JZ4780 DMA controller initialised\n"); + return 0; + +err_unregister_dev: + dma_async_device_unregister(dd); + +err_disable_clk: + clk_disable_unprepare(jzdma->clk); + return ret; +} + +static int jz4780_dma_remove(struct platform_device *pdev) +{ + struct jz4780_dma_dev *jzdma = platform_get_drvdata(pdev); + + of_dma_controller_free(pdev->dev.of_node); + devm_free_irq(&pdev->dev, jzdma->irq, jzdma); + dma_async_device_unregister(&jzdma->dma_device); + return 0; +} + +static const struct of_device_id jz4780_dma_dt_match[] = { + { .compatible = "ingenic,jz4780-dma", .data = NULL }, + {}, +}; +MODULE_DEVICE_TABLE(of, jz4780_dma_dt_match); + +static struct platform_driver jz4780_dma_driver = { + .probe = jz4780_dma_probe, + .remove = jz4780_dma_remove, + .driver = { + .name = "jz4780-dma", + .of_match_table = of_match_ptr(jz4780_dma_dt_match), + }, +}; + +static int __init jz4780_dma_init(void) +{ + return platform_driver_register(&jz4780_dma_driver); +} +subsys_initcall(jz4780_dma_init); + +static void __exit jz4780_dma_exit(void) +{ + platform_driver_unregister(&jz4780_dma_driver); +} +module_exit(jz4780_dma_exit); + +MODULE_AUTHOR("Alex Smith "); +MODULE_DESCRIPTION("Ingenic JZ4780 DMA controller driver"); +MODULE_LICENSE("GPL"); diff --git a/include/dt-bindings/dma/jz4780-dma.h b/include/dt-bindings/dma/jz4780-dma.h new file mode 100644 index 000000000000..df017fdfb44e --- /dev/null +++ b/include/dt-bindings/dma/jz4780-dma.h @@ -0,0 +1,49 @@ +#ifndef __DT_BINDINGS_DMA_JZ4780_DMA_H__ +#define __DT_BINDINGS_DMA_JZ4780_DMA_H__ + +/* + * Request type numbers for the JZ4780 DMA controller (written to the DRTn + * register for the channel). + */ +#define JZ4780_DMA_I2S1_TX 0x4 +#define JZ4780_DMA_I2S1_RX 0x5 +#define JZ4780_DMA_I2S0_TX 0x6 +#define JZ4780_DMA_I2S0_RX 0x7 +#define JZ4780_DMA_AUTO 0x8 +#define JZ4780_DMA_SADC_RX 0x9 +#define JZ4780_DMA_UART4_TX 0xc +#define JZ4780_DMA_UART4_RX 0xd +#define JZ4780_DMA_UART3_TX 0xe +#define JZ4780_DMA_UART3_RX 0xf +#define JZ4780_DMA_UART2_TX 0x10 +#define JZ4780_DMA_UART2_RX 0x11 +#define JZ4780_DMA_UART1_TX 0x12 +#define JZ4780_DMA_UART1_RX 0x13 +#define JZ4780_DMA_UART0_TX 0x14 +#define JZ4780_DMA_UART0_RX 0x15 +#define JZ4780_DMA_SSI0_TX 0x16 +#define JZ4780_DMA_SSI0_RX 0x17 +#define JZ4780_DMA_SSI1_TX 0x18 +#define JZ4780_DMA_SSI1_RX 0x19 +#define JZ4780_DMA_MSC0_TX 0x1a +#define JZ4780_DMA_MSC0_RX 0x1b +#define JZ4780_DMA_MSC1_TX 0x1c +#define JZ4780_DMA_MSC1_RX 0x1d +#define JZ4780_DMA_MSC2_TX 0x1e +#define JZ4780_DMA_MSC2_RX 0x1f +#define JZ4780_DMA_PCM0_TX 0x20 +#define JZ4780_DMA_PCM0_RX 0x21 +#define JZ4780_DMA_SMB0_TX 0x24 +#define JZ4780_DMA_SMB0_RX 0x25 +#define JZ4780_DMA_SMB1_TX 0x26 +#define JZ4780_DMA_SMB1_RX 0x27 +#define JZ4780_DMA_SMB2_TX 0x28 +#define JZ4780_DMA_SMB2_RX 0x29 +#define JZ4780_DMA_SMB3_TX 0x2a +#define JZ4780_DMA_SMB3_RX 0x2b +#define JZ4780_DMA_SMB4_TX 0x2c +#define JZ4780_DMA_SMB4_RX 0x2d +#define JZ4780_DMA_DES_TX 0x2e +#define JZ4780_DMA_DES_RX 0x2f + +#endif /* __DT_BINDINGS_DMA_JZ4780_DMA_H__ */ -- cgit v1.2.3 From caa0e2d0e331a04cbc1cb9bca3169c1d94b80838 Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" Date: Wed, 1 Apr 2015 08:21:51 +1030 Subject: virtio_config: reorder functions This simply reorders functions in virtio_config so width access wrapper helpers are all together. Drops an extra empty line while we are at it. Signed-off-by: Michael S. Tsirkin Signed-off-by: Rusty Russell --- include/linux/virtio_config.h | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) (limited to 'include') diff --git a/include/linux/virtio_config.h b/include/linux/virtio_config.h index ca3ed78e5ec7..22d33034b578 100644 --- a/include/linux/virtio_config.h +++ b/include/linux/virtio_config.h @@ -298,13 +298,6 @@ static inline __virtio64 cpu_to_virtio64(struct virtio_device *vdev, u64 val) } \ } while(0) -static inline u8 virtio_cread8(struct virtio_device *vdev, unsigned int offset) -{ - u8 ret; - vdev->config->get(vdev, offset, &ret, sizeof(ret)); - return ret; -} - /* Read @count fields, @bytes each. */ static inline void __virtio_cread_many(struct virtio_device *vdev, unsigned int offset, @@ -326,7 +319,6 @@ static inline void __virtio_cread_many(struct virtio_device *vdev, } while (gen != old); } - static inline void virtio_cread_bytes(struct virtio_device *vdev, unsigned int offset, void *buf, size_t len) @@ -334,6 +326,13 @@ static inline void virtio_cread_bytes(struct virtio_device *vdev, __virtio_cread_many(vdev, offset, buf, len, 1); } +static inline u8 virtio_cread8(struct virtio_device *vdev, unsigned int offset) +{ + u8 ret; + vdev->config->get(vdev, offset, &ret, sizeof(ret)); + return ret; +} + static inline void virtio_cwrite8(struct virtio_device *vdev, unsigned int offset, u8 val) { -- cgit v1.2.3 From 012665391dfe12bf8a88d1000e627be012c39dbf Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" Date: Wed, 1 Apr 2015 13:31:20 +1030 Subject: virtio: drop a useless config read "virtio: core support for config generation" fixed reading up 64 bit values, adding generation checks for such reads. By mistake, it left an explicit get call in place as well. the result is that the value is read twice, the first result is discarded. Not a big deal since this only happens with virtio blk and only on boot ATM, so performance isn't affected, but let's clean it up. Signed-off-by: Michael S. Tsirkin Reviewed-by: Cornelia Huck Signed-off-by: Rusty Russell --- include/linux/virtio_config.h | 1 - 1 file changed, 1 deletion(-) (limited to 'include') diff --git a/include/linux/virtio_config.h b/include/linux/virtio_config.h index 22d33034b578..1e306f727edc 100644 --- a/include/linux/virtio_config.h +++ b/include/linux/virtio_config.h @@ -373,7 +373,6 @@ static inline u64 virtio_cread64(struct virtio_device *vdev, unsigned int offset) { u64 ret; - vdev->config->get(vdev, offset, &ret, sizeof(ret)); __virtio_cread_many(vdev, offset, &ret, 1, sizeof(ret)); return virtio64_to_cpu(vdev, (__force __virtio64)ret); } -- cgit v1.2.3 From 761da2935d6e18d178582dbdf315a3a458555505 Mon Sep 17 00:00:00 2001 From: Patrick McHardy Date: Thu, 26 Mar 2015 12:39:36 +0000 Subject: netfilter: nf_tables: add set timeout API support Add set timeout support to the netlink API. Sets with timeout support enabled can have a default timeout value and garbage collection interval specified. Signed-off-by: Patrick McHardy Signed-off-by: Pablo Neira Ayuso --- include/net/netfilter/nf_tables.h | 9 +++++++++ include/uapi/linux/netfilter/nf_tables.h | 6 ++++++ net/netfilter/nf_tables_api.c | 30 ++++++++++++++++++++++++++++-- 3 files changed, 43 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/net/netfilter/nf_tables.h b/include/net/netfilter/nf_tables.h index b8cd60dcb4e1..8936803a2ad5 100644 --- a/include/net/netfilter/nf_tables.h +++ b/include/net/netfilter/nf_tables.h @@ -258,6 +258,8 @@ void nft_unregister_set(struct nft_set_ops *ops); * @dtype: data type (verdict or numeric type defined by userspace) * @size: maximum set size * @nelems: number of elements + * @timeout: default timeout value in msecs + * @gc_int: garbage collection interval in msecs * @policy: set parameterization (see enum nft_set_policies) * @ops: set ops * @pnet: network namespace @@ -274,6 +276,8 @@ struct nft_set { u32 dtype; u32 size; u32 nelems; + u64 timeout; + u32 gc_int; u16 policy; /* runtime data below here */ const struct nft_set_ops *ops ____cacheline_aligned; @@ -295,6 +299,11 @@ struct nft_set *nf_tables_set_lookup(const struct nft_table *table, struct nft_set *nf_tables_set_lookup_byid(const struct net *net, const struct nlattr *nla); +static inline unsigned long nft_set_gc_interval(const struct nft_set *set) +{ + return set->gc_int ? msecs_to_jiffies(set->gc_int) : HZ; +} + /** * struct nft_set_binding - nf_tables set binding * diff --git a/include/uapi/linux/netfilter/nf_tables.h b/include/uapi/linux/netfilter/nf_tables.h index b9783931503b..971d245e7378 100644 --- a/include/uapi/linux/netfilter/nf_tables.h +++ b/include/uapi/linux/netfilter/nf_tables.h @@ -208,12 +208,14 @@ enum nft_rule_compat_attributes { * @NFT_SET_CONSTANT: set contents may not change while bound * @NFT_SET_INTERVAL: set contains intervals * @NFT_SET_MAP: set is used as a dictionary + * @NFT_SET_TIMEOUT: set uses timeouts */ enum nft_set_flags { NFT_SET_ANONYMOUS = 0x1, NFT_SET_CONSTANT = 0x2, NFT_SET_INTERVAL = 0x4, NFT_SET_MAP = 0x8, + NFT_SET_TIMEOUT = 0x10, }; /** @@ -252,6 +254,8 @@ enum nft_set_desc_attributes { * @NFTA_SET_POLICY: selection policy (NLA_U32) * @NFTA_SET_DESC: set description (NLA_NESTED) * @NFTA_SET_ID: uniquely identifies a set in a transaction (NLA_U32) + * @NFTA_SET_TIMEOUT: default timeout value (NLA_U64) + * @NFTA_SET_GC_INTERVAL: garbage collection interval (NLA_U32) */ enum nft_set_attributes { NFTA_SET_UNSPEC, @@ -265,6 +269,8 @@ enum nft_set_attributes { NFTA_SET_POLICY, NFTA_SET_DESC, NFTA_SET_ID, + NFTA_SET_TIMEOUT, + NFTA_SET_GC_INTERVAL, __NFTA_SET_MAX }; #define NFTA_SET_MAX (__NFTA_SET_MAX - 1) diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c index 5604c2df05d1..6320b64e773e 100644 --- a/net/netfilter/nf_tables_api.c +++ b/net/netfilter/nf_tables_api.c @@ -2216,6 +2216,8 @@ static const struct nla_policy nft_set_policy[NFTA_SET_MAX + 1] = { [NFTA_SET_POLICY] = { .type = NLA_U32 }, [NFTA_SET_DESC] = { .type = NLA_NESTED }, [NFTA_SET_ID] = { .type = NLA_U32 }, + [NFTA_SET_TIMEOUT] = { .type = NLA_U64 }, + [NFTA_SET_GC_INTERVAL] = { .type = NLA_U32 }, }; static const struct nla_policy nft_set_desc_policy[NFTA_SET_DESC_MAX + 1] = { @@ -2366,6 +2368,13 @@ static int nf_tables_fill_set(struct sk_buff *skb, const struct nft_ctx *ctx, goto nla_put_failure; } + if (set->timeout && + nla_put_be64(skb, NFTA_SET_TIMEOUT, cpu_to_be64(set->timeout))) + goto nla_put_failure; + if (set->gc_int && + nla_put_be32(skb, NFTA_SET_GC_INTERVAL, htonl(set->gc_int))) + goto nla_put_failure; + if (set->policy != NFT_SET_POL_PERFORMANCE) { if (nla_put_be32(skb, NFTA_SET_POLICY, htonl(set->policy))) goto nla_put_failure; @@ -2578,7 +2587,8 @@ static int nf_tables_newset(struct sock *nlsk, struct sk_buff *skb, char name[IFNAMSIZ]; unsigned int size; bool create; - u32 ktype, dtype, flags, policy; + u64 timeout; + u32 ktype, dtype, flags, policy, gc_int; struct nft_set_desc desc; int err; @@ -2605,7 +2615,8 @@ static int nf_tables_newset(struct sock *nlsk, struct sk_buff *skb, if (nla[NFTA_SET_FLAGS] != NULL) { flags = ntohl(nla_get_be32(nla[NFTA_SET_FLAGS])); if (flags & ~(NFT_SET_ANONYMOUS | NFT_SET_CONSTANT | - NFT_SET_INTERVAL | NFT_SET_MAP)) + NFT_SET_INTERVAL | NFT_SET_MAP | + NFT_SET_TIMEOUT)) return -EINVAL; } @@ -2631,6 +2642,19 @@ static int nf_tables_newset(struct sock *nlsk, struct sk_buff *skb, } else if (flags & NFT_SET_MAP) return -EINVAL; + timeout = 0; + if (nla[NFTA_SET_TIMEOUT] != NULL) { + if (!(flags & NFT_SET_TIMEOUT)) + return -EINVAL; + timeout = be64_to_cpu(nla_get_be64(nla[NFTA_SET_TIMEOUT])); + } + gc_int = 0; + if (nla[NFTA_SET_GC_INTERVAL] != NULL) { + if (!(flags & NFT_SET_TIMEOUT)) + return -EINVAL; + gc_int = ntohl(nla_get_be32(nla[NFTA_SET_GC_INTERVAL])); + } + policy = NFT_SET_POL_PERFORMANCE; if (nla[NFTA_SET_POLICY] != NULL) policy = ntohl(nla_get_be32(nla[NFTA_SET_POLICY])); @@ -2699,6 +2723,8 @@ static int nf_tables_newset(struct sock *nlsk, struct sk_buff *skb, set->flags = flags; set->size = desc.size; set->policy = policy; + set->timeout = timeout; + set->gc_int = gc_int; err = ops->init(set, &desc, nla); if (err < 0) -- cgit v1.2.3 From c3e1b005ed1cc068fc9d454a6e745830d55d251d Mon Sep 17 00:00:00 2001 From: Patrick McHardy Date: Thu, 26 Mar 2015 12:39:37 +0000 Subject: netfilter: nf_tables: add set element timeout support Add API support for set element timeouts. Elements can have a individual timeout value specified, overriding the sets' default. Two new extension types are used for timeouts - the timeout value and the expiration time. The timeout value only exists if it differs from the default value. Signed-off-by: Patrick McHardy Signed-off-by: Pablo Neira Ayuso --- include/net/netfilter/nf_tables.h | 20 ++++++++++++ include/uapi/linux/netfilter/nf_tables.h | 4 +++ net/netfilter/nf_tables_api.c | 53 ++++++++++++++++++++++++++++++-- 3 files changed, 75 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/net/netfilter/nf_tables.h b/include/net/netfilter/nf_tables.h index 8936803a2ad5..f2726c537248 100644 --- a/include/net/netfilter/nf_tables.h +++ b/include/net/netfilter/nf_tables.h @@ -329,12 +329,16 @@ void nf_tables_unbind_set(const struct nft_ctx *ctx, struct nft_set *set, * @NFT_SET_EXT_KEY: element key * @NFT_SET_EXT_DATA: mapping data * @NFT_SET_EXT_FLAGS: element flags + * @NFT_SET_EXT_TIMEOUT: element timeout + * @NFT_SET_EXT_EXPIRATION: element expiration time * @NFT_SET_EXT_NUM: number of extension types */ enum nft_set_extensions { NFT_SET_EXT_KEY, NFT_SET_EXT_DATA, NFT_SET_EXT_FLAGS, + NFT_SET_EXT_TIMEOUT, + NFT_SET_EXT_EXPIRATION, NFT_SET_EXT_NUM }; @@ -431,6 +435,22 @@ static inline u8 *nft_set_ext_flags(const struct nft_set_ext *ext) return nft_set_ext(ext, NFT_SET_EXT_FLAGS); } +static inline u64 *nft_set_ext_timeout(const struct nft_set_ext *ext) +{ + return nft_set_ext(ext, NFT_SET_EXT_TIMEOUT); +} + +static inline unsigned long *nft_set_ext_expiration(const struct nft_set_ext *ext) +{ + return nft_set_ext(ext, NFT_SET_EXT_EXPIRATION); +} + +static inline bool nft_set_elem_expired(const struct nft_set_ext *ext) +{ + return nft_set_ext_exists(ext, NFT_SET_EXT_EXPIRATION) && + time_is_before_eq_jiffies(*nft_set_ext_expiration(ext)); +} + static inline struct nft_set_ext *nft_set_elem_ext(const struct nft_set *set, void *elem) { diff --git a/include/uapi/linux/netfilter/nf_tables.h b/include/uapi/linux/netfilter/nf_tables.h index 971d245e7378..83441cc4594b 100644 --- a/include/uapi/linux/netfilter/nf_tables.h +++ b/include/uapi/linux/netfilter/nf_tables.h @@ -290,12 +290,16 @@ enum nft_set_elem_flags { * @NFTA_SET_ELEM_KEY: key value (NLA_NESTED: nft_data) * @NFTA_SET_ELEM_DATA: data value of mapping (NLA_NESTED: nft_data_attributes) * @NFTA_SET_ELEM_FLAGS: bitmask of nft_set_elem_flags (NLA_U32) + * @NFTA_SET_ELEM_TIMEOUT: timeout value (NLA_U64) + * @NFTA_SET_ELEM_EXPIRATION: expiration time (NLA_U64) */ enum nft_set_elem_attributes { NFTA_SET_ELEM_UNSPEC, NFTA_SET_ELEM_KEY, NFTA_SET_ELEM_DATA, NFTA_SET_ELEM_FLAGS, + NFTA_SET_ELEM_TIMEOUT, + NFTA_SET_ELEM_EXPIRATION, __NFTA_SET_ELEM_MAX }; #define NFTA_SET_ELEM_MAX (__NFTA_SET_ELEM_MAX - 1) diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c index 6320b64e773e..9e032dbc149c 100644 --- a/net/netfilter/nf_tables_api.c +++ b/net/netfilter/nf_tables_api.c @@ -2863,6 +2863,14 @@ const struct nft_set_ext_type nft_set_ext_types[] = { .len = sizeof(u8), .align = __alignof__(u8), }, + [NFT_SET_EXT_TIMEOUT] = { + .len = sizeof(u64), + .align = __alignof__(u64), + }, + [NFT_SET_EXT_EXPIRATION] = { + .len = sizeof(unsigned long), + .align = __alignof__(unsigned long), + }, }; EXPORT_SYMBOL_GPL(nft_set_ext_types); @@ -2874,6 +2882,7 @@ static const struct nla_policy nft_set_elem_policy[NFTA_SET_ELEM_MAX + 1] = { [NFTA_SET_ELEM_KEY] = { .type = NLA_NESTED }, [NFTA_SET_ELEM_DATA] = { .type = NLA_NESTED }, [NFTA_SET_ELEM_FLAGS] = { .type = NLA_U32 }, + [NFTA_SET_ELEM_TIMEOUT] = { .type = NLA_U64 }, }; static const struct nla_policy nft_set_elem_list_policy[NFTA_SET_ELEM_LIST_MAX + 1] = { @@ -2935,6 +2944,25 @@ static int nf_tables_fill_setelem(struct sk_buff *skb, htonl(*nft_set_ext_flags(ext)))) goto nla_put_failure; + if (nft_set_ext_exists(ext, NFT_SET_EXT_TIMEOUT) && + nla_put_be64(skb, NFTA_SET_ELEM_TIMEOUT, + cpu_to_be64(*nft_set_ext_timeout(ext)))) + goto nla_put_failure; + + if (nft_set_ext_exists(ext, NFT_SET_EXT_EXPIRATION)) { + unsigned long expires, now = jiffies; + + expires = *nft_set_ext_expiration(ext); + if (time_before(now, expires)) + expires -= now; + else + expires = 0; + + if (nla_put_be64(skb, NFTA_SET_ELEM_EXPIRATION, + cpu_to_be64(jiffies_to_msecs(expires)))) + goto nla_put_failure; + } + nla_nest_end(skb, nest); return 0; @@ -3158,7 +3186,7 @@ static void *nft_set_elem_init(const struct nft_set *set, const struct nft_set_ext_tmpl *tmpl, const struct nft_data *key, const struct nft_data *data, - gfp_t gfp) + u64 timeout, gfp_t gfp) { struct nft_set_ext *ext; void *elem; @@ -3173,6 +3201,11 @@ static void *nft_set_elem_init(const struct nft_set *set, memcpy(nft_set_ext_key(ext), key, set->klen); if (nft_set_ext_exists(ext, NFT_SET_EXT_DATA)) memcpy(nft_set_ext_data(ext), data, set->dlen); + if (nft_set_ext_exists(ext, NFT_SET_EXT_EXPIRATION)) + *nft_set_ext_expiration(ext) = + jiffies + msecs_to_jiffies(timeout); + if (nft_set_ext_exists(ext, NFT_SET_EXT_TIMEOUT)) + *nft_set_ext_timeout(ext) = timeout; return elem; } @@ -3201,6 +3234,7 @@ static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set, struct nft_data data; enum nft_registers dreg; struct nft_trans *trans; + u64 timeout; u32 flags; int err; @@ -3241,6 +3275,15 @@ static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set, return -EINVAL; } + timeout = 0; + if (nla[NFTA_SET_ELEM_TIMEOUT] != NULL) { + if (!(set->flags & NFT_SET_TIMEOUT)) + return -EINVAL; + timeout = be64_to_cpu(nla_get_be64(nla[NFTA_SET_ELEM_TIMEOUT])); + } else if (set->flags & NFT_SET_TIMEOUT) { + timeout = set->timeout; + } + err = nft_data_init(ctx, &elem.key, &d1, nla[NFTA_SET_ELEM_KEY]); if (err < 0) goto err1; @@ -3249,6 +3292,11 @@ static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set, goto err2; nft_set_ext_add(&tmpl, NFT_SET_EXT_KEY); + if (timeout > 0) { + nft_set_ext_add(&tmpl, NFT_SET_EXT_EXPIRATION); + if (timeout != set->timeout) + nft_set_ext_add(&tmpl, NFT_SET_EXT_TIMEOUT); + } if (nla[NFTA_SET_ELEM_DATA] != NULL) { err = nft_data_init(ctx, &data, &d2, nla[NFTA_SET_ELEM_DATA]); @@ -3277,7 +3325,8 @@ static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set, } err = -ENOMEM; - elem.priv = nft_set_elem_init(set, &tmpl, &elem.key, &data, GFP_KERNEL); + elem.priv = nft_set_elem_init(set, &tmpl, &elem.key, &data, + timeout, GFP_KERNEL); if (elem.priv == NULL) goto err3; -- cgit v1.2.3 From cfed7e1b1f8ed9b3d81ab12203cfb69c3ef24ac6 Mon Sep 17 00:00:00 2001 From: Patrick McHardy Date: Thu, 26 Mar 2015 12:39:38 +0000 Subject: netfilter: nf_tables: add set garbage collection helpers Add helpers for GC batch destruction: since element destruction needs a RCU grace period for all set implementations, add some helper functions for asynchronous batch destruction. Elements are collected in a batch structure, which is asynchronously released using RCU once its full. Signed-off-by: Patrick McHardy Signed-off-by: Pablo Neira Ayuso --- include/net/netfilter/nf_tables.h | 56 +++++++++++++++++++++++++++++++++++++++ net/netfilter/nf_tables_api.c | 25 +++++++++++++++++ 2 files changed, 81 insertions(+) (limited to 'include') diff --git a/include/net/netfilter/nf_tables.h b/include/net/netfilter/nf_tables.h index f2726c537248..6fd44959bf87 100644 --- a/include/net/netfilter/nf_tables.h +++ b/include/net/netfilter/nf_tables.h @@ -459,6 +459,62 @@ static inline struct nft_set_ext *nft_set_elem_ext(const struct nft_set *set, void nft_set_elem_destroy(const struct nft_set *set, void *elem); +/** + * struct nft_set_gc_batch_head - nf_tables set garbage collection batch + * + * @rcu: rcu head + * @set: set the elements belong to + * @cnt: count of elements + */ +struct nft_set_gc_batch_head { + struct rcu_head rcu; + const struct nft_set *set; + unsigned int cnt; +}; + +#define NFT_SET_GC_BATCH_SIZE ((PAGE_SIZE - \ + sizeof(struct nft_set_gc_batch_head)) / \ + sizeof(void *)) + +/** + * struct nft_set_gc_batch - nf_tables set garbage collection batch + * + * @head: GC batch head + * @elems: garbage collection elements + */ +struct nft_set_gc_batch { + struct nft_set_gc_batch_head head; + void *elems[NFT_SET_GC_BATCH_SIZE]; +}; + +struct nft_set_gc_batch *nft_set_gc_batch_alloc(const struct nft_set *set, + gfp_t gfp); +void nft_set_gc_batch_release(struct rcu_head *rcu); + +static inline void nft_set_gc_batch_complete(struct nft_set_gc_batch *gcb) +{ + if (gcb != NULL) + call_rcu(&gcb->head.rcu, nft_set_gc_batch_release); +} + +static inline struct nft_set_gc_batch * +nft_set_gc_batch_check(const struct nft_set *set, struct nft_set_gc_batch *gcb, + gfp_t gfp) +{ + if (gcb != NULL) { + if (gcb->head.cnt + 1 < ARRAY_SIZE(gcb->elems)) + return gcb; + nft_set_gc_batch_complete(gcb); + } + return nft_set_gc_batch_alloc(set, gfp); +} + +static inline void nft_set_gc_batch_add(struct nft_set_gc_batch *gcb, + void *elem) +{ + gcb->elems[gcb->head.cnt++] = elem; +} + /** * struct nft_expr_type - nf_tables expression type * diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c index 9e032dbc149c..138e47fddab7 100644 --- a/net/netfilter/nf_tables_api.c +++ b/net/netfilter/nf_tables_api.c @@ -3482,6 +3482,31 @@ static int nf_tables_delsetelem(struct sock *nlsk, struct sk_buff *skb, return err; } +void nft_set_gc_batch_release(struct rcu_head *rcu) +{ + struct nft_set_gc_batch *gcb; + unsigned int i; + + gcb = container_of(rcu, struct nft_set_gc_batch, head.rcu); + for (i = 0; i < gcb->head.cnt; i++) + nft_set_elem_destroy(gcb->head.set, gcb->elems[i]); + kfree(gcb); +} +EXPORT_SYMBOL_GPL(nft_set_gc_batch_release); + +struct nft_set_gc_batch *nft_set_gc_batch_alloc(const struct nft_set *set, + gfp_t gfp) +{ + struct nft_set_gc_batch *gcb; + + gcb = kzalloc(sizeof(*gcb), gfp); + if (gcb == NULL) + return gcb; + gcb->head.set = set; + return gcb; +} +EXPORT_SYMBOL_GPL(nft_set_gc_batch_alloc); + static int nf_tables_fill_gen_info(struct sk_buff *skb, struct net *net, u32 portid, u32 seq) { -- cgit v1.2.3 From 6908665826d56ddd024f3e131a9ee36f0d140943 Mon Sep 17 00:00:00 2001 From: Patrick McHardy Date: Thu, 26 Mar 2015 12:39:39 +0000 Subject: netfilter: nf_tables: add GC synchronization helpers GC is expected to happen asynchrously to the netlink interface. In the netlink path, both insertion and removal of elements consist of two steps, insertion followed by activation or deactivation followed by removal, during which the element must not be freed by GC. The synchronization helpers use an unused bit in the genmask field to atomically mark an element as "busy", meaning it is either currently being handled through the netlink API or by GC. Elements being processed by GC will never survive, netlink will simply ignore them. Elements being currently processed through netlink will be skipped by GC and reprocessed during the next run. Signed-off-by: Patrick McHardy Signed-off-by: Pablo Neira Ayuso --- include/net/netfilter/nf_tables.h | 35 +++++++++++++++++++++++++++++++++++ net/netfilter/nf_tables_api.c | 2 +- 2 files changed, 36 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/net/netfilter/nf_tables.h b/include/net/netfilter/nf_tables.h index 6fd44959bf87..1ea13fcd388e 100644 --- a/include/net/netfilter/nf_tables.h +++ b/include/net/netfilter/nf_tables.h @@ -852,6 +852,41 @@ static inline void nft_set_elem_change_active(const struct nft_set *set, ext->genmask ^= nft_genmask_next(read_pnet(&set->pnet)); } +/* + * We use a free bit in the genmask field to indicate the element + * is busy, meaning it is currently being processed either by + * the netlink API or GC. + * + * Even though the genmask is only a single byte wide, this works + * because the extension structure if fully constant once initialized, + * so there are no non-atomic write accesses unless it is already + * marked busy. + */ +#define NFT_SET_ELEM_BUSY_MASK (1 << 2) + +#if defined(__LITTLE_ENDIAN_BITFIELD) +#define NFT_SET_ELEM_BUSY_BIT 2 +#elif defined(__BIG_ENDIAN_BITFIELD) +#define NFT_SET_ELEM_BUSY_BIT (BITS_PER_LONG - BITS_PER_BYTE + 2) +#else +#error +#endif + +static inline int nft_set_elem_mark_busy(struct nft_set_ext *ext) +{ + unsigned long *word = (unsigned long *)ext; + + BUILD_BUG_ON(offsetof(struct nft_set_ext, genmask) != 0); + return test_and_set_bit(NFT_SET_ELEM_BUSY_BIT, word); +} + +static inline void nft_set_elem_clear_busy(struct nft_set_ext *ext) +{ + unsigned long *word = (unsigned long *)ext; + + clear_bit(NFT_SET_ELEM_BUSY_BIT, word); +} + /** * struct nft_trans - nf_tables object update in transaction * diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c index 138e47fddab7..3aa92b3f85fd 100644 --- a/net/netfilter/nf_tables_api.c +++ b/net/netfilter/nf_tables_api.c @@ -3338,7 +3338,7 @@ static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set, if (trans == NULL) goto err4; - ext->genmask = nft_genmask_cur(ctx->net); + ext->genmask = nft_genmask_cur(ctx->net) | NFT_SET_ELEM_BUSY_MASK; err = set->ops->insert(set, &elem); if (err < 0) goto err5; -- cgit v1.2.3 From 9d0982927e79049675cb6c6c04a0ebb3dad5a434 Mon Sep 17 00:00:00 2001 From: Patrick McHardy Date: Thu, 26 Mar 2015 12:39:40 +0000 Subject: netfilter: nft_hash: add support for timeouts Add support for element timeouts to nft_hash. The lookup and walking functions are changed to ignore timed out elements, a periodic garbage collection task cleans out expired entries. Signed-off-by: Patrick McHardy Signed-off-by: Pablo Neira Ayuso --- include/net/netfilter/nf_tables.h | 5 +++ net/netfilter/nft_hash.c | 79 +++++++++++++++++++++++++++++++++++++-- 2 files changed, 80 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/include/net/netfilter/nf_tables.h b/include/net/netfilter/nf_tables.h index 1ea13fcd388e..a785699329c9 100644 --- a/include/net/netfilter/nf_tables.h +++ b/include/net/netfilter/nf_tables.h @@ -294,6 +294,11 @@ static inline void *nft_set_priv(const struct nft_set *set) return (void *)set->data; } +static inline struct nft_set *nft_set_container_of(const void *priv) +{ + return (void *)priv - offsetof(struct nft_set, data); +} + struct nft_set *nf_tables_set_lookup(const struct nft_table *table, const struct nlattr *nla); struct nft_set *nf_tables_set_lookup_byid(const struct net *net, diff --git a/net/netfilter/nft_hash.c b/net/netfilter/nft_hash.c index c7e1a9d7d46f..5923ec547268 100644 --- a/net/netfilter/nft_hash.c +++ b/net/netfilter/nft_hash.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -25,6 +26,7 @@ struct nft_hash { struct rhashtable ht; + struct delayed_work gc_work; }; struct nft_hash_elem { @@ -62,6 +64,8 @@ static inline int nft_hash_cmp(struct rhashtable_compare_arg *arg, if (nft_data_cmp(nft_set_ext_key(&he->ext), x->key, x->set->klen)) return 1; + if (nft_set_elem_expired(&he->ext)) + return 1; if (!nft_set_elem_active(&he->ext, x->genmask)) return 1; return 0; @@ -107,6 +111,7 @@ static void nft_hash_activate(const struct nft_set *set, struct nft_hash_elem *he = elem->priv; nft_set_elem_change_active(set, &he->ext); + nft_set_elem_clear_busy(&he->ext); } static void *nft_hash_deactivate(const struct nft_set *set, @@ -120,9 +125,15 @@ static void *nft_hash_deactivate(const struct nft_set *set, .key = &elem->key, }; + rcu_read_lock(); he = rhashtable_lookup_fast(&priv->ht, &arg, nft_hash_params); - if (he != NULL) - nft_set_elem_change_active(set, &he->ext); + if (he != NULL) { + if (!nft_set_elem_mark_busy(&he->ext)) + nft_set_elem_change_active(set, &he->ext); + else + he = NULL; + } + rcu_read_unlock(); return he; } @@ -170,6 +181,8 @@ static void nft_hash_walk(const struct nft_ctx *ctx, const struct nft_set *set, if (iter->count < iter->skip) goto cont; + if (nft_set_elem_expired(&he->ext)) + goto cont; if (!nft_set_elem_active(&he->ext, genmask)) goto cont; @@ -188,6 +201,54 @@ out: rhashtable_walk_exit(&hti); } +static void nft_hash_gc(struct work_struct *work) +{ + const struct nft_set *set; + struct nft_hash_elem *he; + struct nft_hash *priv; + struct nft_set_gc_batch *gcb = NULL; + struct rhashtable_iter hti; + int err; + + priv = container_of(work, struct nft_hash, gc_work.work); + set = nft_set_container_of(priv); + + err = rhashtable_walk_init(&priv->ht, &hti); + if (err) + goto schedule; + + err = rhashtable_walk_start(&hti); + if (err && err != -EAGAIN) + goto out; + + while ((he = rhashtable_walk_next(&hti))) { + if (IS_ERR(he)) { + if (PTR_ERR(he) != -EAGAIN) + goto out; + continue; + } + + if (!nft_set_elem_expired(&he->ext)) + continue; + if (nft_set_elem_mark_busy(&he->ext)) + continue; + + gcb = nft_set_gc_batch_check(set, gcb, GFP_ATOMIC); + if (gcb == NULL) + goto out; + rhashtable_remove_fast(&priv->ht, &he->node, nft_hash_params); + nft_set_gc_batch_add(gcb, he); + } +out: + rhashtable_walk_stop(&hti); + rhashtable_walk_exit(&hti); + + nft_set_gc_batch_complete(gcb); +schedule: + queue_delayed_work(system_power_efficient_wq, &priv->gc_work, + nft_set_gc_interval(set)); +} + static unsigned int nft_hash_privsize(const struct nlattr * const nla[]) { return sizeof(struct nft_hash); @@ -207,11 +268,20 @@ static int nft_hash_init(const struct nft_set *set, { struct nft_hash *priv = nft_set_priv(set); struct rhashtable_params params = nft_hash_params; + int err; params.nelem_hint = desc->size ?: NFT_HASH_ELEMENT_HINT; params.key_len = set->klen; - return rhashtable_init(&priv->ht, ¶ms); + err = rhashtable_init(&priv->ht, ¶ms); + if (err < 0) + return err; + + INIT_DEFERRABLE_WORK(&priv->gc_work, nft_hash_gc); + if (set->flags & NFT_SET_TIMEOUT) + queue_delayed_work(system_power_efficient_wq, &priv->gc_work, + nft_set_gc_interval(set)); + return 0; } static void nft_hash_elem_destroy(void *ptr, void *arg) @@ -223,6 +293,7 @@ static void nft_hash_destroy(const struct nft_set *set) { struct nft_hash *priv = nft_set_priv(set); + cancel_delayed_work_sync(&priv->gc_work); rhashtable_free_and_destroy(&priv->ht, nft_hash_elem_destroy, (void *)set); } @@ -264,7 +335,7 @@ static struct nft_set_ops nft_hash_ops __read_mostly = { .remove = nft_hash_remove, .lookup = nft_hash_lookup, .walk = nft_hash_walk, - .features = NFT_SET_MAP, + .features = NFT_SET_MAP | NFT_SET_TIMEOUT, .owner = THIS_MODULE, }; -- cgit v1.2.3 From a5581ef4c2eac6449188862e903eb46c7233582a Mon Sep 17 00:00:00 2001 From: Oliver Hartkopp Date: Wed, 1 Apr 2015 07:50:29 +0200 Subject: can: introduce new raw socket option to join the given CAN filters The CAN_RAW socket can set multiple CAN identifier specific filters that lead to multiple filters in the af_can.c filter processing. These filters are indenpendent from each other which leads to logical OR'ed filters when applied. This socket option joines the given CAN filters in the way that only CAN frames are passed to user space that matched *all* given CAN filters. The semantic for the applied filters is therefore changed to a logical AND. This is useful especially when the filterset is a combination of filters where the CAN_INV_FILTER flag is set in order to notch single CAN IDs or CAN ID ranges from the incoming traffic. As the raw_rcv() function is executed from NET_RX softirq the introduced variables are implemented as per-CPU variables to avoid extensive locking at CAN frame reception time. Signed-off-by: Oliver Hartkopp Signed-off-by: Marc Kleine-Budde --- Documentation/networking/can.txt | 20 ++++++++++++++++++-- include/uapi/linux/can/raw.h | 1 + net/can/raw.c | 31 ++++++++++++++++++++++++++++++- 3 files changed, 49 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/Documentation/networking/can.txt b/Documentation/networking/can.txt index 0a2859a8ee7e..5abad1e921ca 100644 --- a/Documentation/networking/can.txt +++ b/Documentation/networking/can.txt @@ -22,7 +22,8 @@ This file contains 4.1.3 RAW socket option CAN_RAW_LOOPBACK 4.1.4 RAW socket option CAN_RAW_RECV_OWN_MSGS 4.1.5 RAW socket option CAN_RAW_FD_FRAMES - 4.1.6 RAW socket returned message flags + 4.1.6 RAW socket option CAN_RAW_JOIN_FILTERS + 4.1.7 RAW socket returned message flags 4.2 Broadcast Manager protocol sockets (SOCK_DGRAM) 4.2.1 Broadcast Manager operations 4.2.2 Broadcast Manager message flags @@ -601,7 +602,22 @@ solution for a couple of reasons: CAN FD frames by checking if the device maximum transfer unit is CANFD_MTU. The CAN device MTU can be retrieved e.g. with a SIOCGIFMTU ioctl() syscall. - 4.1.6 RAW socket returned message flags + 4.1.6 RAW socket option CAN_RAW_JOIN_FILTERS + + The CAN_RAW socket can set multiple CAN identifier specific filters that + lead to multiple filters in the af_can.c filter processing. These filters + are indenpendent from each other which leads to logical OR'ed filters when + applied (see 4.1.1). + + This socket option joines the given CAN filters in the way that only CAN + frames are passed to user space that matched *all* given CAN filters. The + semantic for the applied filters is therefore changed to a logical AND. + + This is useful especially when the filterset is a combination of filters + where the CAN_INV_FILTER flag is set in order to notch single CAN IDs or + CAN ID ranges from the incoming traffic. + + 4.1.7 RAW socket returned message flags When using recvmsg() call, the msg->msg_flags may contain following flags: diff --git a/include/uapi/linux/can/raw.h b/include/uapi/linux/can/raw.h index 78ec76fd89a6..8735f1080385 100644 --- a/include/uapi/linux/can/raw.h +++ b/include/uapi/linux/can/raw.h @@ -57,6 +57,7 @@ enum { CAN_RAW_LOOPBACK, /* local loopback (default:on) */ CAN_RAW_RECV_OWN_MSGS, /* receive my own msgs (default:off) */ CAN_RAW_FD_FRAMES, /* allow CAN FD frames (default:off) */ + CAN_RAW_JOIN_FILTERS, /* all filters must match to trigger */ }; #endif /* !_UAPI_CAN_RAW_H */ diff --git a/net/can/raw.c b/net/can/raw.c index 0c8d537b59b8..31b9748cbb4e 100644 --- a/net/can/raw.c +++ b/net/can/raw.c @@ -77,6 +77,7 @@ MODULE_ALIAS("can-proto-1"); struct uniqframe { ktime_t tstamp; const struct sk_buff *skb; + unsigned int join_rx_count; }; struct raw_sock { @@ -87,6 +88,7 @@ struct raw_sock { int loopback; int recv_own_msgs; int fd_frames; + int join_filters; int count; /* number of active filters */ struct can_filter dfilter; /* default/single filter */ struct can_filter *filter; /* pointer to filter(s) */ @@ -132,10 +134,21 @@ static void raw_rcv(struct sk_buff *oskb, void *data) /* eliminate multiple filter matches for the same skb */ if (this_cpu_ptr(ro->uniq)->skb == oskb && ktime_equal(this_cpu_ptr(ro->uniq)->tstamp, oskb->tstamp)) { - return; + if (ro->join_filters) { + this_cpu_inc(ro->uniq->join_rx_count); + /* drop frame until all enabled filters matched */ + if (this_cpu_ptr(ro->uniq)->join_rx_count < ro->count) + return; + } else { + return; + } } else { this_cpu_ptr(ro->uniq)->skb = oskb; this_cpu_ptr(ro->uniq)->tstamp = oskb->tstamp; + this_cpu_ptr(ro->uniq)->join_rx_count = 1; + /* drop first frame to check all enabled filters? */ + if (ro->join_filters && ro->count > 1) + return; } /* clone the given skb to be able to enqueue it into the rcv queue */ @@ -311,6 +324,7 @@ static int raw_init(struct sock *sk) ro->loopback = 1; ro->recv_own_msgs = 0; ro->fd_frames = 0; + ro->join_filters = 0; /* alloc_percpu provides zero'ed memory */ ro->uniq = alloc_percpu(struct uniqframe); @@ -604,6 +618,15 @@ static int raw_setsockopt(struct socket *sock, int level, int optname, break; + case CAN_RAW_JOIN_FILTERS: + if (optlen != sizeof(ro->join_filters)) + return -EINVAL; + + if (copy_from_user(&ro->join_filters, optval, optlen)) + return -EFAULT; + + break; + default: return -ENOPROTOOPT; } @@ -668,6 +691,12 @@ static int raw_getsockopt(struct socket *sock, int level, int optname, val = &ro->fd_frames; break; + case CAN_RAW_JOIN_FILTERS: + if (len > sizeof(int)) + len = sizeof(int); + val = &ro->join_filters; + break; + default: return -ENOPROTOOPT; } -- cgit v1.2.3 From 034705a48bcae9d51661c461b7dd21e0d5b85afc Mon Sep 17 00:00:00 2001 From: Yakir Yang Date: Tue, 31 Mar 2015 23:56:10 -0400 Subject: drm: bridge/dw_hdmi: separate VLEVCTRL settting into platform driver Because of iMX6 & Rockchip have differnet mpll config parameter, the VLEVCTRL parameter would be different. In this case we should separate VLEVCTRL setting from the common dw_hdmi driver, config this parameter in platform driver(dw_hdmi-imx and dw_hdmi-rockchip) Signed-off-by: Yakir Yang Signed-off-by: Russell King --- drivers/gpu/drm/bridge/dw_hdmi.c | 14 +++++++------- drivers/gpu/drm/imx/dw_hdmi-imx.c | 24 ++++++++++++------------ drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c | 14 +++++++------- include/drm/bridge/dw_hdmi.h | 5 +++-- 4 files changed, 29 insertions(+), 28 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/bridge/dw_hdmi.c b/drivers/gpu/drm/bridge/dw_hdmi.c index 7943cedd63ce..49cafb61d290 100644 --- a/drivers/gpu/drm/bridge/dw_hdmi.c +++ b/drivers/gpu/drm/bridge/dw_hdmi.c @@ -758,7 +758,7 @@ static int hdmi_phy_configure(struct dw_hdmi *hdmi, unsigned char prep, const struct dw_hdmi_plat_data *plat_data = hdmi->plat_data; const struct dw_hdmi_mpll_config *mpll_config = plat_data->mpll_cfg; const struct dw_hdmi_curr_ctrl *curr_ctrl = plat_data->cur_ctr; - const struct dw_hdmi_sym_term *sym_term = plat_data->sym_term; + const struct dw_hdmi_phy_config *phy_config = plat_data->phy_config; if (prep) return -EINVAL; @@ -829,18 +829,18 @@ static int hdmi_phy_configure(struct dw_hdmi *hdmi, unsigned char prep, hdmi_phy_i2c_write(hdmi, 0x0000, 0x13); /* PLLPHBYCTRL */ hdmi_phy_i2c_write(hdmi, 0x0006, 0x17); - for (i = 0; sym_term[i].mpixelclock != (~0UL); i++) + for (i = 0; phy_config[i].mpixelclock != (~0UL); i++) if (hdmi->hdmi_data.video_mode.mpixelclock <= - sym_term[i].mpixelclock) + phy_config[i].mpixelclock) break; /* RESISTANCE TERM 133Ohm Cfg */ - hdmi_phy_i2c_write(hdmi, sym_term[i].term, 0x19); /* TXTERM */ + hdmi_phy_i2c_write(hdmi, phy_config[i].term, 0x19); /* TXTERM */ /* PREEMP Cgf 0.00 */ - hdmi_phy_i2c_write(hdmi, sym_term[i].sym_ctr, 0x09); /* CKSYMTXCTRL */ - + hdmi_phy_i2c_write(hdmi, phy_config[i].sym_ctr, 0x09); /* CKSYMTXCTRL */ /* TX/CK LVL 10 */ - hdmi_phy_i2c_write(hdmi, 0x01ad, 0x0E); /* VLEVCTRL */ + hdmi_phy_i2c_write(hdmi, phy_config[i].vlev_ctr, 0x0E); /* VLEVCTRL */ + /* REMOVE CLK TERM */ hdmi_phy_i2c_write(hdmi, 0x8000, 0x05); /* CKCALCTRL */ diff --git a/drivers/gpu/drm/imx/dw_hdmi-imx.c b/drivers/gpu/drm/imx/dw_hdmi-imx.c index 121d30ca2d44..1032c07773e3 100644 --- a/drivers/gpu/drm/imx/dw_hdmi-imx.c +++ b/drivers/gpu/drm/imx/dw_hdmi-imx.c @@ -73,10 +73,10 @@ static const struct dw_hdmi_curr_ctrl imx_cur_ctr[] = { } }; -static const struct dw_hdmi_sym_term imx_sym_term[] = { - /*pixelclk symbol term*/ - { 148500000, 0x800d, 0x0005 }, - { ~0UL, 0x0000, 0x0000 } +static const struct dw_hdmi_phy_config imx_phy_config[] = { + /*pixelclk symbol term vlev */ + { 148500000, 0x800d, 0x0005, 0x01ad}, + { ~0UL, 0x0000, 0x0000, 0x0000} }; static int dw_hdmi_imx_parse_dt(struct imx_hdmi *hdmi) @@ -137,17 +137,17 @@ static struct drm_encoder_funcs dw_hdmi_imx_encoder_funcs = { }; static struct dw_hdmi_plat_data imx6q_hdmi_drv_data = { - .mpll_cfg = imx_mpll_cfg, - .cur_ctr = imx_cur_ctr, - .sym_term = imx_sym_term, - .dev_type = IMX6Q_HDMI, + .mpll_cfg = imx_mpll_cfg, + .cur_ctr = imx_cur_ctr, + .phy_config = imx_phy_config, + .dev_type = IMX6Q_HDMI, }; static struct dw_hdmi_plat_data imx6dl_hdmi_drv_data = { - .mpll_cfg = imx_mpll_cfg, - .cur_ctr = imx_cur_ctr, - .sym_term = imx_sym_term, - .dev_type = IMX6DL_HDMI, + .mpll_cfg = imx_mpll_cfg, + .cur_ctr = imx_cur_ctr, + .phy_config = imx_phy_config, + .dev_type = IMX6DL_HDMI, }; static const struct of_device_id dw_hdmi_imx_dt_ids[] = { diff --git a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c index d236faa05b19..65b0f7bf5b52 100644 --- a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c +++ b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c @@ -133,12 +133,12 @@ static const struct dw_hdmi_curr_ctrl rockchip_cur_ctr[] = { } }; -static const struct dw_hdmi_sym_term rockchip_sym_term[] = { - /*pixelclk symbol term*/ - { 74250000, 0x8009, 0x0004 }, - { 148500000, 0x8029, 0x0004 }, - { 297000000, 0x8039, 0x0005 }, - { ~0UL, 0x0000, 0x0000 } +static const struct dw_hdmi_phy_config rockchip_phy_config[] = { + /*pixelclk symbol term vlev*/ + { 74250000, 0x8009, 0x0004, 0x01ad}, + { 148500000, 0x8029, 0x0004, 0x01ad}, + { 297000000, 0x8039, 0x0005, 0x01ad}, + { ~0UL, 0x0000, 0x0000, 0x0000} }; static int rockchip_hdmi_parse_dt(struct rockchip_hdmi *hdmi) @@ -230,7 +230,7 @@ static const struct dw_hdmi_plat_data rockchip_hdmi_drv_data = { .mode_valid = dw_hdmi_rockchip_mode_valid, .mpll_cfg = rockchip_mpll_cfg, .cur_ctr = rockchip_cur_ctr, - .sym_term = rockchip_sym_term, + .phy_config = rockchip_phy_config, .dev_type = RK3288_HDMI, }; diff --git a/include/drm/bridge/dw_hdmi.h b/include/drm/bridge/dw_hdmi.h index 5a4f49005169..de13bfc35634 100644 --- a/include/drm/bridge/dw_hdmi.h +++ b/include/drm/bridge/dw_hdmi.h @@ -38,17 +38,18 @@ struct dw_hdmi_curr_ctrl { u16 curr[DW_HDMI_RES_MAX]; }; -struct dw_hdmi_sym_term { +struct dw_hdmi_phy_config { unsigned long mpixelclock; u16 sym_ctr; /*clock symbol and transmitter control*/ u16 term; /*transmission termination value*/ + u16 vlev_ctr; /* voltage level control */ }; struct dw_hdmi_plat_data { enum dw_hdmi_devtype dev_type; const struct dw_hdmi_mpll_config *mpll_cfg; const struct dw_hdmi_curr_ctrl *cur_ctr; - const struct dw_hdmi_sym_term *sym_term; + const struct dw_hdmi_phy_config *phy_config; enum drm_mode_status (*mode_valid)(struct drm_connector *connector, struct drm_display_mode *mode); }; -- cgit v1.2.3 From 138173d4e826587da66c7d321da1a91283222536 Mon Sep 17 00:00:00 2001 From: Rafał Miłecki Date: Mon, 1 Dec 2014 07:58:18 +0100 Subject: MIPS: BCM47xx: Move NVRAM header to the include/linux/. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There are two reasons for having this header in the common place: 1) Simplifying drivers that read NVRAM entries. We will be able to safely call bcm47xx_nvram_* functions without #ifdef-s. 2) Getting NVRAM driver out of MIPS arch code. This is needed to support BCM5301X arch which also requires this NVRAM driver. Patch for that will follow once we get is reviewed. Signed-off-by: Rafał Miłecki Acked-by: Hauke Mehrtens Cc: linux-mips@linux-mips.org Cc: Arnd Bergmann Cc: Paul Walmsley Cc: linux-soc@vger.kernel.org Patchwork: https://patchwork.linux-mips.org/patch/8619/ Signed-off-by: Ralf Baechle --- arch/mips/bcm47xx/board.c | 2 +- arch/mips/bcm47xx/nvram.c | 2 +- arch/mips/bcm47xx/setup.c | 1 - arch/mips/bcm47xx/sprom.c | 1 - arch/mips/bcm47xx/time.c | 1 - arch/mips/include/asm/mach-bcm47xx/bcm47xx.h | 1 + arch/mips/include/asm/mach-bcm47xx/bcm47xx_nvram.h | 21 ------------- drivers/bcma/driver_mips.c | 2 +- drivers/net/ethernet/broadcom/b44.c | 2 +- drivers/net/ethernet/broadcom/bgmac.c | 2 +- drivers/ssb/driver_chipcommon_pmu.c | 2 +- drivers/ssb/driver_mipscore.c | 2 +- include/linux/bcm47xx_nvram.h | 34 ++++++++++++++++++++++ 13 files changed, 42 insertions(+), 31 deletions(-) delete mode 100644 arch/mips/include/asm/mach-bcm47xx/bcm47xx_nvram.h create mode 100644 include/linux/bcm47xx_nvram.h (limited to 'include') diff --git a/arch/mips/bcm47xx/board.c b/arch/mips/bcm47xx/board.c index b3ae068ca4fa..6e8513068325 100644 --- a/arch/mips/bcm47xx/board.c +++ b/arch/mips/bcm47xx/board.c @@ -1,8 +1,8 @@ #include #include #include +#include #include -#include struct bcm47xx_board_type { const enum bcm47xx_board board; diff --git a/arch/mips/bcm47xx/nvram.c b/arch/mips/bcm47xx/nvram.c index d805d8af415f..7c77a88eedf9 100644 --- a/arch/mips/bcm47xx/nvram.c +++ b/arch/mips/bcm47xx/nvram.c @@ -16,7 +16,7 @@ #include #include #include -#include +#include #define NVRAM_MAGIC 0x48534C46 /* 'FLSH' */ #define NVRAM_SPACE 0x8000 diff --git a/arch/mips/bcm47xx/setup.c b/arch/mips/bcm47xx/setup.c index e43b5046cb30..b26c9c24275e 100644 --- a/arch/mips/bcm47xx/setup.c +++ b/arch/mips/bcm47xx/setup.c @@ -42,7 +42,6 @@ #include #include #include -#include #include union bcm47xx_bus bcm47xx_bus; diff --git a/arch/mips/bcm47xx/sprom.c b/arch/mips/bcm47xx/sprom.c index eff920560689..c114b0239758 100644 --- a/arch/mips/bcm47xx/sprom.c +++ b/arch/mips/bcm47xx/sprom.c @@ -27,7 +27,6 @@ */ #include -#include #include #include diff --git a/arch/mips/bcm47xx/time.c b/arch/mips/bcm47xx/time.c index 2c85d9254b5e..5b46510daa7b 100644 --- a/arch/mips/bcm47xx/time.c +++ b/arch/mips/bcm47xx/time.c @@ -27,7 +27,6 @@ #include #include #include -#include #include void __init plat_time_init(void) diff --git a/arch/mips/include/asm/mach-bcm47xx/bcm47xx.h b/arch/mips/include/asm/mach-bcm47xx/bcm47xx.h index 7527c1d33d02..8ed77f618940 100644 --- a/arch/mips/include/asm/mach-bcm47xx/bcm47xx.h +++ b/arch/mips/include/asm/mach-bcm47xx/bcm47xx.h @@ -22,6 +22,7 @@ #include #include #include +#include enum bcm47xx_bus_type { #ifdef CONFIG_BCM47XX_SSB diff --git a/arch/mips/include/asm/mach-bcm47xx/bcm47xx_nvram.h b/arch/mips/include/asm/mach-bcm47xx/bcm47xx_nvram.h deleted file mode 100644 index ee59ffe99922..000000000000 --- a/arch/mips/include/asm/mach-bcm47xx/bcm47xx_nvram.h +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright (C) 2005, Broadcom Corporation - * Copyright (C) 2006, Felix Fietkau - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - */ - -#ifndef __BCM47XX_NVRAM_H -#define __BCM47XX_NVRAM_H - -#include -#include - -int bcm47xx_nvram_init_from_mem(u32 base, u32 lim); -int bcm47xx_nvram_getenv(const char *name, char *val, size_t val_len); -int bcm47xx_nvram_gpio_pin(const char *name); - -#endif /* __BCM47XX_NVRAM_H */ diff --git a/drivers/bcma/driver_mips.c b/drivers/bcma/driver_mips.c index 04faf6df959f..24424f3fef96 100644 --- a/drivers/bcma/driver_mips.c +++ b/drivers/bcma/driver_mips.c @@ -21,7 +21,7 @@ #include #include #ifdef CONFIG_BCM47XX -#include +#include #endif enum bcma_boot_dev { diff --git a/drivers/net/ethernet/broadcom/b44.c b/drivers/net/ethernet/broadcom/b44.c index bd5916a60cb5..77363d680532 100644 --- a/drivers/net/ethernet/broadcom/b44.c +++ b/drivers/net/ethernet/broadcom/b44.c @@ -400,7 +400,7 @@ static void b44_set_flow_ctrl(struct b44 *bp, u32 local, u32 remote) } #ifdef CONFIG_BCM47XX -#include +#include static void b44_wap54g10_workaround(struct b44 *bp) { char buf[20]; diff --git a/drivers/net/ethernet/broadcom/bgmac.c b/drivers/net/ethernet/broadcom/bgmac.c index 0469f72c6e7e..be059df8c852 100644 --- a/drivers/net/ethernet/broadcom/bgmac.c +++ b/drivers/net/ethernet/broadcom/bgmac.c @@ -16,7 +16,7 @@ #include #include #include -#include +#include static const struct bcma_device_id bgmac_bcma_tbl[] = { BCMA_CORE(BCMA_MANUF_BCM, BCMA_CORE_4706_MAC_GBIT, BCMA_ANY_REV, BCMA_ANY_CLASS), diff --git a/drivers/ssb/driver_chipcommon_pmu.c b/drivers/ssb/driver_chipcommon_pmu.c index 1173a091b402..09428412139e 100644 --- a/drivers/ssb/driver_chipcommon_pmu.c +++ b/drivers/ssb/driver_chipcommon_pmu.c @@ -14,7 +14,7 @@ #include #include #ifdef CONFIG_BCM47XX -#include +#include #endif #include "ssb_private.h" diff --git a/drivers/ssb/driver_mipscore.c b/drivers/ssb/driver_mipscore.c index 7b986f9f213f..f87efef42252 100644 --- a/drivers/ssb/driver_mipscore.c +++ b/drivers/ssb/driver_mipscore.c @@ -16,7 +16,7 @@ #include #include #ifdef CONFIG_BCM47XX -#include +#include #endif #include "ssb_private.h" diff --git a/include/linux/bcm47xx_nvram.h b/include/linux/bcm47xx_nvram.h new file mode 100644 index 000000000000..b12b07e75929 --- /dev/null +++ b/include/linux/bcm47xx_nvram.h @@ -0,0 +1,34 @@ +/* + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#ifndef __BCM47XX_NVRAM_H +#define __BCM47XX_NVRAM_H + +#include +#include + +#ifdef CONFIG_BCM47XX +int bcm47xx_nvram_init_from_mem(u32 base, u32 lim); +int bcm47xx_nvram_getenv(const char *name, char *val, size_t val_len); +int bcm47xx_nvram_gpio_pin(const char *name); +#else +static inline int bcm47xx_nvram_init_from_mem(u32 base, u32 lim) +{ + return -ENOTSUPP; +}; +static inline int bcm47xx_nvram_getenv(const char *name, char *val, + size_t val_len) +{ + return -ENOTSUPP; +}; +static inline int bcm47xx_nvram_gpio_pin(const char *name) +{ + return -ENOTSUPP; +}; +#endif + +#endif /* __BCM47XX_NVRAM_H */ -- cgit v1.2.3 From 44ba06987c0b10faa998b9324850e8a6564c714d Mon Sep 17 00:00:00 2001 From: David Howells Date: Wed, 1 Apr 2015 16:31:26 +0100 Subject: RxRPC: Handle VERSION Rx protocol packets Handle VERSION Rx protocol packets. We should respond to a VERSION packet with a string indicating the Rx version. This is a maximum of 64 characters and is padded out to 65 chars with NUL bytes. Note that other AFS clients use the version request as a NAT keepalive so we need to handle it rather than returning an abort. The standard formulation seems to be: built --
for example: " OpenAFS 1.6.2 built 2013-05-07 " (note the three extra spaces) as obtained with: rxdebug grand.mit.edu -version from the openafs package. Signed-off-by: David Howells --- include/rxrpc/packet.h | 3 +- net/rxrpc/ar-input.c | 23 +++++++++++- net/rxrpc/ar-internal.h | 2 + net/rxrpc/ar-local.c | 98 +++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 124 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/rxrpc/packet.h b/include/rxrpc/packet.h index f2902ef7ab75..4dce116bfd80 100644 --- a/include/rxrpc/packet.h +++ b/include/rxrpc/packet.h @@ -47,7 +47,8 @@ struct rxrpc_header { #define RXRPC_PACKET_TYPE_CHALLENGE 6 /* connection security challenge (SRVR->CLNT) */ #define RXRPC_PACKET_TYPE_RESPONSE 7 /* connection secutity response (CLNT->SRVR) */ #define RXRPC_PACKET_TYPE_DEBUG 8 /* debug info request */ -#define RXRPC_N_PACKET_TYPES 9 /* number of packet types (incl type 0) */ +#define RXRPC_PACKET_TYPE_VERSION 13 /* version string request */ +#define RXRPC_N_PACKET_TYPES 14 /* number of packet types (incl type 0) */ uint8_t flags; /* packet flags */ #define RXRPC_CLIENT_INITIATED 0x01 /* signifies a packet generated by a client */ diff --git a/net/rxrpc/ar-input.c b/net/rxrpc/ar-input.c index 481f89f93789..4505a691d88c 100644 --- a/net/rxrpc/ar-input.c +++ b/net/rxrpc/ar-input.c @@ -28,7 +28,7 @@ const char *rxrpc_pkts[] = { "?00", "DATA", "ACK", "BUSY", "ABORT", "ACKALL", "CHALL", "RESP", "DEBUG", - "?09", "?10", "?11", "?12", "?13", "?14", "?15" + "?09", "?10", "?11", "?12", "VERSION", "?14", "?15" }; /* @@ -593,6 +593,20 @@ static void rxrpc_post_packet_to_conn(struct rxrpc_connection *conn, rxrpc_queue_conn(conn); } +/* + * post endpoint-level events to the local endpoint + * - this includes debug and version messages + */ +static void rxrpc_post_packet_to_local(struct rxrpc_local *local, + struct sk_buff *skb) +{ + _enter("%p,%p", local, skb); + + atomic_inc(&local->usage); + skb_queue_tail(&local->event_queue, skb); + rxrpc_queue_work(&local->event_processor); +} + static struct rxrpc_connection *rxrpc_conn_from_local(struct rxrpc_local *local, struct sk_buff *skb, struct rxrpc_skb_priv *sp) @@ -699,6 +713,11 @@ void rxrpc_data_ready(struct sock *sk) goto bad_message; } + if (sp->hdr.type == RXRPC_PACKET_TYPE_VERSION) { + rxrpc_post_packet_to_local(local, skb); + goto out; + } + if (sp->hdr.type == RXRPC_PACKET_TYPE_DATA && (sp->hdr.callNumber == 0 || sp->hdr.seq == 0)) goto bad_message; @@ -731,6 +750,8 @@ void rxrpc_data_ready(struct sock *sk) else goto cant_route_call; } + +out: rxrpc_put_local(local); return; diff --git a/net/rxrpc/ar-internal.h b/net/rxrpc/ar-internal.h index ba9fd36d3f15..9a4f7a26adc6 100644 --- a/net/rxrpc/ar-internal.h +++ b/net/rxrpc/ar-internal.h @@ -152,11 +152,13 @@ struct rxrpc_local { struct work_struct destroyer; /* endpoint destroyer */ struct work_struct acceptor; /* incoming call processor */ struct work_struct rejecter; /* packet reject writer */ + struct work_struct event_processor; /* endpoint event processor */ struct list_head services; /* services listening on this endpoint */ struct list_head link; /* link in endpoint list */ struct rw_semaphore defrag_sem; /* control re-enablement of IP DF bit */ struct sk_buff_head accept_queue; /* incoming calls awaiting acceptance */ struct sk_buff_head reject_queue; /* packets awaiting rejection */ + struct sk_buff_head event_queue; /* endpoint event packets awaiting processing */ spinlock_t lock; /* access lock */ rwlock_t services_lock; /* lock for services list */ atomic_t usage; diff --git a/net/rxrpc/ar-local.c b/net/rxrpc/ar-local.c index 87f7135d238b..ca904ed5400a 100644 --- a/net/rxrpc/ar-local.c +++ b/net/rxrpc/ar-local.c @@ -13,16 +13,22 @@ #include #include #include +#include +#include #include #include +#include #include "ar-internal.h" +static const char rxrpc_version_string[65] = "linux-" UTS_RELEASE " AF_RXRPC"; + static LIST_HEAD(rxrpc_locals); DEFINE_RWLOCK(rxrpc_local_lock); static DECLARE_RWSEM(rxrpc_local_sem); static DECLARE_WAIT_QUEUE_HEAD(rxrpc_local_wq); static void rxrpc_destroy_local(struct work_struct *work); +static void rxrpc_process_local_events(struct work_struct *work); /* * allocate a new local @@ -37,11 +43,13 @@ struct rxrpc_local *rxrpc_alloc_local(struct sockaddr_rxrpc *srx) INIT_WORK(&local->destroyer, &rxrpc_destroy_local); INIT_WORK(&local->acceptor, &rxrpc_accept_incoming_calls); INIT_WORK(&local->rejecter, &rxrpc_reject_packets); + INIT_WORK(&local->event_processor, &rxrpc_process_local_events); INIT_LIST_HEAD(&local->services); INIT_LIST_HEAD(&local->link); init_rwsem(&local->defrag_sem); skb_queue_head_init(&local->accept_queue); skb_queue_head_init(&local->reject_queue); + skb_queue_head_init(&local->event_queue); spin_lock_init(&local->lock); rwlock_init(&local->services_lock); atomic_set(&local->usage, 1); @@ -264,10 +272,12 @@ static void rxrpc_destroy_local(struct work_struct *work) ASSERT(list_empty(&local->services)); ASSERT(!work_pending(&local->acceptor)); ASSERT(!work_pending(&local->rejecter)); + ASSERT(!work_pending(&local->event_processor)); /* finish cleaning up the local descriptor */ rxrpc_purge_queue(&local->accept_queue); rxrpc_purge_queue(&local->reject_queue); + rxrpc_purge_queue(&local->event_queue); kernel_sock_shutdown(local->socket, SHUT_RDWR); sock_release(local->socket); @@ -308,3 +318,91 @@ void __exit rxrpc_destroy_all_locals(void) _leave(""); } + +/* + * Reply to a version request + */ +static void rxrpc_send_version_request(struct rxrpc_local *local, + struct rxrpc_header *hdr, + struct sk_buff *skb) +{ + struct sockaddr_in sin; + struct msghdr msg; + struct kvec iov[2]; + size_t len; + int ret; + + _enter(""); + + sin.sin_family = AF_INET; + sin.sin_port = udp_hdr(skb)->source; + sin.sin_addr.s_addr = ip_hdr(skb)->saddr; + + msg.msg_name = &sin; + msg.msg_namelen = sizeof(sin); + msg.msg_control = NULL; + msg.msg_controllen = 0; + msg.msg_flags = 0; + + hdr->seq = 0; + hdr->serial = 0; + hdr->type = RXRPC_PACKET_TYPE_VERSION; + hdr->flags = RXRPC_LAST_PACKET | (~hdr->flags & RXRPC_CLIENT_INITIATED); + hdr->userStatus = 0; + hdr->_rsvd = 0; + + iov[0].iov_base = hdr; + iov[0].iov_len = sizeof(*hdr); + iov[1].iov_base = (char *)rxrpc_version_string; + iov[1].iov_len = sizeof(rxrpc_version_string); + + len = iov[0].iov_len + iov[1].iov_len; + + _proto("Tx VERSION (reply)"); + + ret = kernel_sendmsg(local->socket, &msg, iov, 2, len); + if (ret < 0) + _debug("sendmsg failed: %d", ret); + + _leave(""); +} + +/* + * Process event packets targetted at a local endpoint. + */ +static void rxrpc_process_local_events(struct work_struct *work) +{ + struct rxrpc_local *local = container_of(work, struct rxrpc_local, event_processor); + struct sk_buff *skb; + char v; + + _enter(""); + + atomic_inc(&local->usage); + + while ((skb = skb_dequeue(&local->event_queue))) { + struct rxrpc_skb_priv *sp = rxrpc_skb(skb); + + kdebug("{%d},{%u}", local->debug_id, sp->hdr.type); + + switch (sp->hdr.type) { + case RXRPC_PACKET_TYPE_VERSION: + if (skb_copy_bits(skb, 0, &v, 1) < 0) + return; + _proto("Rx VERSION { %02x }", v); + if (v == 0) + rxrpc_send_version_request(local, &sp->hdr, skb); + break; + + default: + /* Just ignore anything we don't understand */ + break; + } + + rxrpc_put_local(local); + rxrpc_free_skb(skb); + } + + rxrpc_put_local(local); + _leave(""); +} -- cgit v1.2.3 From ba8c3d6f16a1f9305c23ac1d2fd3992508c5ac03 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Fri, 27 Mar 2015 21:30:37 +0100 Subject: mac80211: add an intermediate software queue implementation This allows drivers to request per-vif and per-sta-tid queues from which they can pull frames. This makes it easier to keep the hardware queues short, and to improve fairness between clients and vifs. The task of scheduling packet transmission is left up to the driver - queueing is controlled by mac80211. Drivers can only dequeue packets by calling ieee80211_tx_dequeue. This makes it possible to add active queue management later without changing drivers using this code. This can also be used as a starting point to implement A-MSDU aggregation in a way that does not add artificially induced latency. Signed-off-by: Felix Fietkau [resolved minor context conflict, minor changes, endian annotations] Signed-off-by: Johannes Berg --- include/net/mac80211.h | 83 ++++++++++++++++++++++++++++++++ net/mac80211/agg-tx.c | 44 +++++++++++++++++ net/mac80211/driver-ops.h | 12 +++++ net/mac80211/ieee80211_i.h | 21 +++++++++ net/mac80211/iface.c | 23 ++++++++- net/mac80211/main.c | 3 ++ net/mac80211/rx.c | 13 +++++ net/mac80211/sta_info.c | 83 ++++++++++++++++++++++++++++---- net/mac80211/sta_info.h | 2 + net/mac80211/trace.h | 31 ++++++++++++ net/mac80211/tx.c | 115 +++++++++++++++++++++++++++++++++++++++++---- net/mac80211/util.c | 22 +++++++++ 12 files changed, 433 insertions(+), 19 deletions(-) (limited to 'include') diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 201bc68e0cff..3578da96b41a 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -84,6 +84,39 @@ * */ +/** + * DOC: mac80211 software tx queueing + * + * mac80211 provides an optional intermediate queueing implementation designed + * to allow the driver to keep hardware queues short and provide some fairness + * between different stations/interfaces. + * In this model, the driver pulls data frames from the mac80211 queue instead + * of letting mac80211 push them via drv_tx(). + * Other frames (e.g. control or management) are still pushed using drv_tx(). + * + * Drivers indicate that they use this model by implementing the .wake_tx_queue + * driver operation. + * + * Intermediate queues (struct ieee80211_txq) are kept per-sta per-tid, with a + * single per-vif queue for multicast data frames. + * + * The driver is expected to initialize its private per-queue data for stations + * and interfaces in the .add_interface and .sta_add ops. + * + * The driver can't access the queue directly. To dequeue a frame, it calls + * ieee80211_tx_dequeue(). Whenever mac80211 adds a new frame to a queue, it + * calls the .wake_tx_queue driver op. + * + * For AP powersave TIM handling, the driver only needs to indicate if it has + * buffered packets in the driver specific data structures by calling + * ieee80211_sta_set_buffered(). For frames buffered in the ieee80211_txq + * struct, mac80211 sets the appropriate TIM PVB bits and calls + * .release_buffered_frames(). + * In that callback the driver is therefore expected to release its own + * buffered frames and afterwards also frames from the ieee80211_txq (obtained + * via the usual ieee80211_tx_dequeue). + */ + struct device; /** @@ -1306,6 +1339,7 @@ enum ieee80211_vif_flags { * monitor interface (if that is requested.) * @drv_priv: data area for driver use, will always be aligned to * sizeof(void *). + * @txq: the multicast data TX queue (if driver uses the TXQ abstraction) */ struct ieee80211_vif { enum nl80211_iftype type; @@ -1317,6 +1351,8 @@ struct ieee80211_vif { u8 cab_queue; u8 hw_queue[IEEE80211_NUM_ACS]; + struct ieee80211_txq *txq; + struct ieee80211_chanctx_conf __rcu *chanctx_conf; u32 driver_flags; @@ -1575,6 +1611,7 @@ struct ieee80211_sta_rates { * @tdls_initiator: indicates the STA is an initiator of the TDLS link. Only * valid if the STA is a TDLS peer in the first place. * @mfp: indicates whether the STA uses management frame protection or not. + * @txq: per-TID data TX queues (if driver uses the TXQ abstraction) */ struct ieee80211_sta { u32 supp_rates[IEEE80211_NUM_BANDS]; @@ -1593,6 +1630,8 @@ struct ieee80211_sta { bool tdls_initiator; bool mfp; + struct ieee80211_txq *txq[IEEE80211_NUM_TIDS]; + /* must be last */ u8 drv_priv[0] __aligned(sizeof(void *)); }; @@ -1620,6 +1659,27 @@ struct ieee80211_tx_control { struct ieee80211_sta *sta; }; +/** + * struct ieee80211_txq - Software intermediate tx queue + * + * @vif: &struct ieee80211_vif pointer from the add_interface callback. + * @sta: station table entry, %NULL for per-vif queue + * @tid: the TID for this queue (unused for per-vif queue) + * @ac: the AC for this queue + * + * The driver can obtain packets from this queue by calling + * ieee80211_tx_dequeue(). + */ +struct ieee80211_txq { + struct ieee80211_vif *vif; + struct ieee80211_sta *sta; + u8 tid; + u8 ac; + + /* must be last */ + u8 drv_priv[0] __aligned(sizeof(void *)); +}; + /** * enum ieee80211_hw_flags - hardware flags * @@ -1844,6 +1904,8 @@ enum ieee80211_hw_flags { * within &struct ieee80211_sta. * @chanctx_data_size: size (in bytes) of the drv_priv data area * within &struct ieee80211_chanctx_conf. + * @txq_data_size: size (in bytes) of the drv_priv data area + * within @struct ieee80211_txq. * * @max_rates: maximum number of alternate rate retry stages the hw * can handle. @@ -1892,6 +1954,9 @@ enum ieee80211_hw_flags { * @n_cipher_schemes: a size of an array of cipher schemes definitions. * @cipher_schemes: a pointer to an array of cipher scheme definitions * supported by HW. + * + * @txq_ac_max_pending: maximum number of frames per AC pending in all txq + * entries for a vif. */ struct ieee80211_hw { struct ieee80211_conf conf; @@ -1904,6 +1969,7 @@ struct ieee80211_hw { int vif_data_size; int sta_data_size; int chanctx_data_size; + int txq_data_size; u16 queues; u16 max_listen_interval; s8 max_signal; @@ -1920,6 +1986,7 @@ struct ieee80211_hw { u8 uapsd_max_sp_len; u8 n_cipher_schemes; const struct ieee80211_cipher_scheme *cipher_schemes; + int txq_ac_max_pending; }; /** @@ -3082,6 +3149,8 @@ enum ieee80211_reconfig_type { * response template is provided, together with the location of the * switch-timing IE within the template. The skb can only be used within * the function call. + * + * @wake_tx_queue: Called when new packets have been added to the queue. */ struct ieee80211_ops { void (*tx)(struct ieee80211_hw *hw, @@ -3313,6 +3382,9 @@ struct ieee80211_ops { void (*tdls_recv_channel_switch)(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_tdls_ch_sw_params *params); + + void (*wake_tx_queue)(struct ieee80211_hw *hw, + struct ieee80211_txq *txq); }; /** @@ -5334,4 +5406,15 @@ void ieee80211_unreserve_tid(struct ieee80211_sta *sta, u8 tid); */ size_t ieee80211_ie_split(const u8 *ies, size_t ielen, const u8 *ids, int n_ids, size_t offset); + +/** + * ieee80211_tx_dequeue - dequeue a packet from a software tx queue + * + * @hw: pointer as obtained from ieee80211_alloc_hw() + * @txq: pointer obtained from station or virtual interface + * + * Returns the skb if successful, %NULL if no frame was available. + */ +struct sk_buff *ieee80211_tx_dequeue(struct ieee80211_hw *hw, + struct ieee80211_txq *txq); #endif /* MAC80211_H */ diff --git a/net/mac80211/agg-tx.c b/net/mac80211/agg-tx.c index 20522492d8cc..cce9d425c718 100644 --- a/net/mac80211/agg-tx.c +++ b/net/mac80211/agg-tx.c @@ -188,6 +188,43 @@ ieee80211_wake_queue_agg(struct ieee80211_sub_if_data *sdata, int tid) __release(agg_queue); } +static void +ieee80211_agg_stop_txq(struct sta_info *sta, int tid) +{ + struct ieee80211_txq *txq = sta->sta.txq[tid]; + struct txq_info *txqi; + + if (!txq) + return; + + txqi = to_txq_info(txq); + + /* Lock here to protect against further seqno updates on dequeue */ + spin_lock_bh(&txqi->queue.lock); + set_bit(IEEE80211_TXQ_STOP, &txqi->flags); + spin_unlock_bh(&txqi->queue.lock); +} + +static void +ieee80211_agg_start_txq(struct sta_info *sta, int tid, bool enable) +{ + struct ieee80211_txq *txq = sta->sta.txq[tid]; + struct txq_info *txqi; + + if (!txq) + return; + + txqi = to_txq_info(txq); + + if (enable) + set_bit(IEEE80211_TXQ_AMPDU, &txqi->flags); + else + clear_bit(IEEE80211_TXQ_AMPDU, &txqi->flags); + + clear_bit(IEEE80211_TXQ_STOP, &txqi->flags); + drv_wake_tx_queue(sta->sdata->local, txqi); +} + /* * splice packets from the STA's pending to the local pending, * requires a call to ieee80211_agg_splice_finish later @@ -247,6 +284,7 @@ static void ieee80211_remove_tid_tx(struct sta_info *sta, int tid) ieee80211_assign_tid_tx(sta, tid, NULL); ieee80211_agg_splice_finish(sta->sdata, tid); + ieee80211_agg_start_txq(sta, tid, false); kfree_rcu(tid_tx, rcu_head); } @@ -418,6 +456,8 @@ void ieee80211_tx_ba_session_handle_start(struct sta_info *sta, int tid) */ clear_bit(HT_AGG_STATE_WANT_START, &tid_tx->state); + ieee80211_agg_stop_txq(sta, tid); + /* * Make sure no packets are being processed. This ensures that * we have a valid starting sequence number and that in-flight @@ -440,6 +480,8 @@ void ieee80211_tx_ba_session_handle_start(struct sta_info *sta, int tid) ieee80211_agg_splice_finish(sdata, tid); spin_unlock_bh(&sta->lock); + ieee80211_agg_start_txq(sta, tid, false); + kfree_rcu(tid_tx, rcu_head); return; } @@ -669,6 +711,8 @@ static void ieee80211_agg_tx_operational(struct ieee80211_local *local, ieee80211_agg_splice_finish(sta->sdata, tid); spin_unlock_bh(&sta->lock); + + ieee80211_agg_start_txq(sta, tid, true); } void ieee80211_start_tx_ba_cb(struct ieee80211_vif *vif, u8 *ra, u16 tid) diff --git a/net/mac80211/driver-ops.h b/net/mac80211/driver-ops.h index 0a39d3db951a..26e1ca8a474a 100644 --- a/net/mac80211/driver-ops.h +++ b/net/mac80211/driver-ops.h @@ -1367,4 +1367,16 @@ drv_tdls_recv_channel_switch(struct ieee80211_local *local, trace_drv_return_void(local); } +static inline void drv_wake_tx_queue(struct ieee80211_local *local, + struct txq_info *txq) +{ + struct ieee80211_sub_if_data *sdata = vif_to_sdata(txq->txq.vif); + + if (!check_sdata_in_driver(sdata)) + return; + + trace_drv_wake_tx_queue(local, sdata, txq); + local->ops->wake_tx_queue(&local->hw, &txq->txq); +} + #endif /* __MAC80211_DRIVER_OPS */ diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 3c1512b0442c..04b32f3e0395 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -811,6 +811,19 @@ struct mac80211_qos_map { struct rcu_head rcu_head; }; +enum txq_info_flags { + IEEE80211_TXQ_STOP, + IEEE80211_TXQ_AMPDU, +}; + +struct txq_info { + struct sk_buff_head queue; + unsigned long flags; + + /* keep last! */ + struct ieee80211_txq txq; +}; + struct ieee80211_sub_if_data { struct list_head list; @@ -853,6 +866,7 @@ struct ieee80211_sub_if_data { bool control_port_no_encrypt; int encrypt_headroom; + atomic_t txqs_len[IEEE80211_NUM_ACS]; struct ieee80211_tx_queue_params tx_conf[IEEE80211_NUM_ACS]; struct mac80211_qos_map __rcu *qos_map; @@ -1450,6 +1464,10 @@ static inline struct ieee80211_local *hw_to_local( return container_of(hw, struct ieee80211_local, hw); } +static inline struct txq_info *to_txq_info(struct ieee80211_txq *txq) +{ + return container_of(txq, struct txq_info, txq); +} static inline int ieee80211_bssid_match(const u8 *raddr, const u8 *addr) { @@ -1906,6 +1924,9 @@ static inline bool ieee80211_can_run_worker(struct ieee80211_local *local) return true; } +void ieee80211_init_tx_queue(struct ieee80211_sub_if_data *sdata, + struct sta_info *sta, + struct txq_info *txq, int tid); void ieee80211_send_auth(struct ieee80211_sub_if_data *sdata, u16 transaction, u16 auth_alg, u16 status, const u8 *extra, size_t extra_len, const u8 *bssid, diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c index a0cd97fd0c49..b4ac596a7cb7 100644 --- a/net/mac80211/iface.c +++ b/net/mac80211/iface.c @@ -969,6 +969,13 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata, } spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags); + if (sdata->vif.txq) { + struct txq_info *txqi = to_txq_info(sdata->vif.txq); + + ieee80211_purge_tx_queue(&local->hw, &txqi->queue); + atomic_set(&sdata->txqs_len[txqi->txq.ac], 0); + } + if (local->open_count == 0) ieee80211_clear_tx_pending(local); @@ -1654,6 +1661,7 @@ int ieee80211_if_add(struct ieee80211_local *local, const char *name, { struct net_device *ndev = NULL; struct ieee80211_sub_if_data *sdata = NULL; + struct txq_info *txqi; int ret, i; int txqs = 1; @@ -1673,10 +1681,18 @@ int ieee80211_if_add(struct ieee80211_local *local, const char *name, ieee80211_assign_perm_addr(local, wdev->address, type); memcpy(sdata->vif.addr, wdev->address, ETH_ALEN); } else { + int size = ALIGN(sizeof(*sdata) + local->hw.vif_data_size, + sizeof(void *)); + int txq_size = 0; + + if (local->ops->wake_tx_queue) + txq_size += sizeof(struct txq_info) + + local->hw.txq_data_size; + if (local->hw.queues >= IEEE80211_NUM_ACS) txqs = IEEE80211_NUM_ACS; - ndev = alloc_netdev_mqs(sizeof(*sdata) + local->hw.vif_data_size, + ndev = alloc_netdev_mqs(size + txq_size, name, name_assign_type, ieee80211_if_setup, txqs, 1); if (!ndev) @@ -1711,6 +1727,11 @@ int ieee80211_if_add(struct ieee80211_local *local, const char *name, memcpy(sdata->vif.addr, ndev->dev_addr, ETH_ALEN); memcpy(sdata->name, ndev->name, IFNAMSIZ); + if (txq_size) { + txqi = netdev_priv(ndev) + size; + ieee80211_init_tx_queue(sdata, NULL, txqi, 0); + } + sdata->dev = ndev; } diff --git a/net/mac80211/main.c b/net/mac80211/main.c index 51e0332a4589..df3051d96aff 100644 --- a/net/mac80211/main.c +++ b/net/mac80211/main.c @@ -1039,6 +1039,9 @@ int ieee80211_register_hw(struct ieee80211_hw *hw) local->dynamic_ps_forced_timeout = -1; + if (!local->hw.txq_ac_max_pending) + local->hw.txq_ac_max_pending = 64; + result = ieee80211_wep_init(local); if (result < 0) wiphy_debug(local->hw.wiphy, "Failed to initialize wep: %d\n", diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index 5b60bcf00ec3..bc59c8a20a39 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c @@ -1184,6 +1184,7 @@ static void sta_ps_start(struct sta_info *sta) struct ieee80211_sub_if_data *sdata = sta->sdata; struct ieee80211_local *local = sdata->local; struct ps_data *ps; + int tid; if (sta->sdata->vif.type == NL80211_IFTYPE_AP || sta->sdata->vif.type == NL80211_IFTYPE_AP_VLAN) @@ -1197,6 +1198,18 @@ static void sta_ps_start(struct sta_info *sta) drv_sta_notify(local, sdata, STA_NOTIFY_SLEEP, &sta->sta); ps_dbg(sdata, "STA %pM aid %d enters power save mode\n", sta->sta.addr, sta->sta.aid); + + if (!sta->sta.txq[0]) + return; + + for (tid = 0; tid < ARRAY_SIZE(sta->sta.txq); tid++) { + struct txq_info *txqi = to_txq_info(sta->sta.txq[tid]); + + if (!skb_queue_len(&txqi->queue)) + set_bit(tid, &sta->txq_buffered_tids); + else + clear_bit(tid, &sta->txq_buffered_tids); + } } static void sta_ps_end(struct sta_info *sta) diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c index 81cc499fa4a9..12971b71d0fa 100644 --- a/net/mac80211/sta_info.c +++ b/net/mac80211/sta_info.c @@ -106,6 +106,16 @@ static void __cleanup_single_sta(struct sta_info *sta) atomic_dec(&ps->num_sta_ps); } + if (sta->sta.txq[0]) { + for (i = 0; i < ARRAY_SIZE(sta->sta.txq); i++) { + struct txq_info *txqi = to_txq_info(sta->sta.txq[i]); + int n = skb_queue_len(&txqi->queue); + + ieee80211_purge_tx_queue(&local->hw, &txqi->queue); + atomic_sub(n, &sdata->txqs_len[txqi->txq.ac]); + } + } + for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) { local->total_ps_buffered -= skb_queue_len(&sta->ps_tx_buf[ac]); ieee80211_purge_tx_queue(&local->hw, &sta->ps_tx_buf[ac]); @@ -218,6 +228,8 @@ void sta_info_free(struct ieee80211_local *local, struct sta_info *sta) sta_dbg(sta->sdata, "Destroyed STA %pM\n", sta->sta.addr); + if (sta->sta.txq[0]) + kfree(to_txq_info(sta->sta.txq[0])); kfree(rcu_dereference_raw(sta->sta.rates)); kfree(sta); } @@ -268,11 +280,12 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata, const u8 *addr, gfp_t gfp) { struct ieee80211_local *local = sdata->local; + struct ieee80211_hw *hw = &local->hw; struct sta_info *sta; struct timespec uptime; int i; - sta = kzalloc(sizeof(*sta) + local->hw.sta_data_size, gfp); + sta = kzalloc(sizeof(*sta) + hw->sta_data_size, gfp); if (!sta) return NULL; @@ -304,11 +317,25 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata, for (i = 0; i < ARRAY_SIZE(sta->chain_signal_avg); i++) ewma_init(&sta->chain_signal_avg[i], 1024, 8); - if (sta_prepare_rate_control(local, sta, gfp)) { - kfree(sta); - return NULL; + if (local->ops->wake_tx_queue) { + void *txq_data; + int size = sizeof(struct txq_info) + + ALIGN(hw->txq_data_size, sizeof(void *)); + + txq_data = kcalloc(ARRAY_SIZE(sta->sta.txq), size, gfp); + if (!txq_data) + goto free; + + for (i = 0; i < ARRAY_SIZE(sta->sta.txq); i++) { + struct txq_info *txq = txq_data + i * size; + + ieee80211_init_tx_queue(sdata, sta, txq, i); + } } + if (sta_prepare_rate_control(local, sta, gfp)) + goto free_txq; + for (i = 0; i < IEEE80211_NUM_TIDS; i++) { /* * timer_to_tid must be initialized with identity mapping @@ -329,7 +356,7 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata, if (sdata->vif.type == NL80211_IFTYPE_AP || sdata->vif.type == NL80211_IFTYPE_AP_VLAN) { struct ieee80211_supported_band *sband = - local->hw.wiphy->bands[ieee80211_get_sdata_band(sdata)]; + hw->wiphy->bands[ieee80211_get_sdata_band(sdata)]; u8 smps = (sband->ht_cap.cap & IEEE80211_HT_CAP_SM_PS) >> IEEE80211_HT_CAP_SM_PS_SHIFT; /* @@ -354,6 +381,13 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata, sta_dbg(sdata, "Allocated STA %pM\n", sta->sta.addr); return sta; + +free_txq: + if (sta->sta.txq[0]) + kfree(to_txq_info(sta->sta.txq[0])); +free: + kfree(sta); + return NULL; } static int sta_info_insert_check(struct sta_info *sta) @@ -623,6 +657,8 @@ static void __sta_info_recalc_tim(struct sta_info *sta, bool ignore_pending) indicate_tim |= sta->driver_buffered_tids & tids; + indicate_tim |= + sta->txq_buffered_tids & tids; } done: @@ -1072,7 +1108,7 @@ void ieee80211_sta_ps_deliver_wakeup(struct sta_info *sta) struct ieee80211_sub_if_data *sdata = sta->sdata; struct ieee80211_local *local = sdata->local; struct sk_buff_head pending; - int filtered = 0, buffered = 0, ac; + int filtered = 0, buffered = 0, ac, i; unsigned long flags; struct ps_data *ps; @@ -1091,10 +1127,22 @@ void ieee80211_sta_ps_deliver_wakeup(struct sta_info *sta) BUILD_BUG_ON(BITS_TO_LONGS(IEEE80211_NUM_TIDS) > 1); sta->driver_buffered_tids = 0; + sta->txq_buffered_tids = 0; if (!(local->hw.flags & IEEE80211_HW_AP_LINK_PS)) drv_sta_notify(local, sdata, STA_NOTIFY_AWAKE, &sta->sta); + if (sta->sta.txq[0]) { + for (i = 0; i < ARRAY_SIZE(sta->sta.txq); i++) { + struct txq_info *txqi = to_txq_info(sta->sta.txq[i]); + + if (!skb_queue_len(&txqi->queue)) + continue; + + drv_wake_tx_queue(local, txqi); + } + } + skb_queue_head_init(&pending); /* sync with ieee80211_tx_h_unicast_ps_buf */ @@ -1276,8 +1324,10 @@ ieee80211_sta_ps_deliver_response(struct sta_info *sta, /* if we already have frames from software, then we can't also * release from hardware queues */ - if (skb_queue_empty(&frames)) + if (skb_queue_empty(&frames)) { driver_release_tids |= sta->driver_buffered_tids & tids; + driver_release_tids |= sta->txq_buffered_tids & tids; + } if (driver_release_tids) { /* If the driver has data on more than one TID then @@ -1448,6 +1498,9 @@ ieee80211_sta_ps_deliver_response(struct sta_info *sta, sta_info_recalc_tim(sta); } else { + unsigned long tids = sta->txq_buffered_tids & driver_release_tids; + int tid; + /* * We need to release a frame that is buffered somewhere in the * driver ... it'll have to handle that. @@ -1467,8 +1520,22 @@ ieee80211_sta_ps_deliver_response(struct sta_info *sta, * that the TID(s) became empty before returning here from the * release function. * Either way, however, when the driver tells us that the TID(s) - * became empty we'll do the TIM recalculation. + * became empty or we find that a txq became empty, we'll do the + * TIM recalculation. */ + + if (!sta->sta.txq[0]) + return; + + for (tid = 0; tid < ARRAY_SIZE(sta->sta.txq); tid++) { + struct txq_info *txqi = to_txq_info(sta->sta.txq[tid]); + + if (!(tids & BIT(tid)) || skb_queue_len(&txqi->queue)) + continue; + + sta_info_recalc_tim(sta); + break; + } } } diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h index 97f25b9e52be..691d8a1f94a5 100644 --- a/net/mac80211/sta_info.h +++ b/net/mac80211/sta_info.h @@ -275,6 +275,7 @@ struct sta_ampdu_mlme { * entered power saving state, these are also delivered to * the station when it leaves powersave or polls for frames * @driver_buffered_tids: bitmap of TIDs the driver has data buffered on + * @txq_buffered_tids: bitmap of TIDs that mac80211 has txq data buffered on * @rx_packets: Number of MSDUs received from this STA * @rx_bytes: Number of bytes received from this STA * @last_rx: time (in jiffies) when last frame was received from this STA @@ -369,6 +370,7 @@ struct sta_info { struct sk_buff_head ps_tx_buf[IEEE80211_NUM_ACS]; struct sk_buff_head tx_filtered[IEEE80211_NUM_ACS]; unsigned long driver_buffered_tids; + unsigned long txq_buffered_tids; /* Updated from RX path only, no locking requirements */ unsigned long rx_packets; diff --git a/net/mac80211/trace.h b/net/mac80211/trace.h index e9e462b349e5..790bd45081c4 100644 --- a/net/mac80211/trace.h +++ b/net/mac80211/trace.h @@ -2312,6 +2312,37 @@ TRACE_EVENT(drv_tdls_recv_channel_switch, ) ); +TRACE_EVENT(drv_wake_tx_queue, + TP_PROTO(struct ieee80211_local *local, + struct ieee80211_sub_if_data *sdata, + struct txq_info *txq), + + TP_ARGS(local, sdata, txq), + + TP_STRUCT__entry( + LOCAL_ENTRY + VIF_ENTRY + STA_ENTRY + __field(u8, ac) + __field(u8, tid) + ), + + TP_fast_assign( + struct ieee80211_sta *sta = txq->txq.sta; + + LOCAL_ASSIGN; + VIF_ASSIGN; + STA_ASSIGN; + __entry->ac = txq->txq.ac; + __entry->tid = txq->txq.tid; + ), + + TP_printk( + LOCAL_PR_FMT VIF_PR_FMT STA_PR_FMT " ac:%d tid:%d", + LOCAL_PR_ARG, VIF_PR_ARG, STA_PR_ARG, __entry->ac, __entry->tid + ) +); + #ifdef CONFIG_MAC80211_MESSAGE_TRACING #undef TRACE_SYSTEM #define TRACE_SYSTEM mac80211_msg diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index 9f7fb4eec37b..667111ee6a20 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -767,12 +767,22 @@ ieee80211_tx_h_rate_ctrl(struct ieee80211_tx_data *tx) return TX_CONTINUE; } +static __le16 ieee80211_tx_next_seq(struct sta_info *sta, int tid) +{ + u16 *seq = &sta->tid_seq[tid]; + __le16 ret = cpu_to_le16(*seq); + + /* Increase the sequence number. */ + *seq = (*seq + 0x10) & IEEE80211_SCTL_SEQ; + + return ret; +} + static ieee80211_tx_result debug_noinline ieee80211_tx_h_sequence(struct ieee80211_tx_data *tx) { struct ieee80211_tx_info *info = IEEE80211_SKB_CB(tx->skb); struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)tx->skb->data; - u16 *seq; u8 *qc; int tid; @@ -823,13 +833,10 @@ ieee80211_tx_h_sequence(struct ieee80211_tx_data *tx) qc = ieee80211_get_qos_ctl(hdr); tid = *qc & IEEE80211_QOS_CTL_TID_MASK; - seq = &tx->sta->tid_seq[tid]; tx->sta->tx_msdu[tid]++; - hdr->seq_ctrl = cpu_to_le16(*seq); - - /* Increase the sequence number. */ - *seq = (*seq + 0x10) & IEEE80211_SCTL_SEQ; + if (!tx->sta->sta.txq[0]) + hdr->seq_ctrl = ieee80211_tx_next_seq(tx->sta, tid); return TX_CONTINUE; } @@ -1070,7 +1077,7 @@ static bool ieee80211_tx_prep_agg(struct ieee80211_tx_data *tx, * nothing -- this aggregation session is being started * but that might still fail with the driver */ - } else { + } else if (!tx->sta->sta.txq[tid]) { spin_lock(&tx->sta->lock); /* * Need to re-check now, because we may get here @@ -1211,13 +1218,102 @@ ieee80211_tx_prepare(struct ieee80211_sub_if_data *sdata, return TX_CONTINUE; } +static void ieee80211_drv_tx(struct ieee80211_local *local, + struct ieee80211_vif *vif, + struct ieee80211_sta *pubsta, + struct sk_buff *skb) +{ + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; + struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif); + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + struct ieee80211_tx_control control = { + .sta = pubsta, + }; + struct ieee80211_txq *txq = NULL; + struct txq_info *txqi; + u8 ac; + + if (info->control.flags & IEEE80211_TX_CTRL_PS_RESPONSE) + goto tx_normal; + + if (!ieee80211_is_data(hdr->frame_control)) + goto tx_normal; + + if (pubsta) { + u8 tid = skb->priority & IEEE80211_QOS_CTL_TID_MASK; + + txq = pubsta->txq[tid]; + } else if (vif) { + txq = vif->txq; + } + + if (!txq) + goto tx_normal; + + ac = txq->ac; + txqi = to_txq_info(txq); + atomic_inc(&sdata->txqs_len[ac]); + if (atomic_read(&sdata->txqs_len[ac]) >= local->hw.txq_ac_max_pending) + netif_stop_subqueue(sdata->dev, ac); + + skb_queue_tail(&txqi->queue, skb); + drv_wake_tx_queue(local, txqi); + + return; + +tx_normal: + drv_tx(local, &control, skb); +} + +struct sk_buff *ieee80211_tx_dequeue(struct ieee80211_hw *hw, + struct ieee80211_txq *txq) +{ + struct ieee80211_local *local = hw_to_local(hw); + struct ieee80211_sub_if_data *sdata = vif_to_sdata(txq->vif); + struct txq_info *txqi = container_of(txq, struct txq_info, txq); + struct ieee80211_hdr *hdr; + struct sk_buff *skb = NULL; + u8 ac = txq->ac; + + spin_lock_bh(&txqi->queue.lock); + + if (test_bit(IEEE80211_TXQ_STOP, &txqi->flags)) + goto out; + + skb = __skb_dequeue(&txqi->queue); + if (!skb) + goto out; + + atomic_dec(&sdata->txqs_len[ac]); + if (__netif_subqueue_stopped(sdata->dev, ac)) + ieee80211_propagate_queue_wake(local, sdata->vif.hw_queue[ac]); + + hdr = (struct ieee80211_hdr *)skb->data; + if (txq->sta && ieee80211_is_data_qos(hdr->frame_control)) { + struct sta_info *sta = container_of(txq->sta, struct sta_info, + sta); + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + + hdr->seq_ctrl = ieee80211_tx_next_seq(sta, txq->tid); + if (test_bit(IEEE80211_TXQ_AMPDU, &txqi->flags)) + info->flags |= IEEE80211_TX_CTL_AMPDU; + else + info->flags &= ~IEEE80211_TX_CTL_AMPDU; + } + +out: + spin_unlock_bh(&txqi->queue.lock); + + return skb; +} +EXPORT_SYMBOL(ieee80211_tx_dequeue); + static bool ieee80211_tx_frags(struct ieee80211_local *local, struct ieee80211_vif *vif, struct ieee80211_sta *sta, struct sk_buff_head *skbs, bool txpending) { - struct ieee80211_tx_control control; struct sk_buff *skb, *tmp; unsigned long flags; @@ -1275,10 +1371,9 @@ static bool ieee80211_tx_frags(struct ieee80211_local *local, spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags); info->control.vif = vif; - control.sta = sta; __skb_unlink(skb, skbs); - drv_tx(local, &control, skb); + ieee80211_drv_tx(local, vif, sta, skb); } return true; diff --git a/net/mac80211/util.c b/net/mac80211/util.c index d1742a7d9ea4..482b85c19a36 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -308,6 +308,11 @@ void ieee80211_propagate_queue_wake(struct ieee80211_local *local, int queue) for (ac = 0; ac < n_acs; ac++) { int ac_queue = sdata->vif.hw_queue[ac]; + if (local->ops->wake_tx_queue && + (atomic_read(&sdata->txqs_len[ac]) > + local->hw.txq_ac_max_pending)) + continue; + if (ac_queue == queue || (sdata->vif.cab_queue == queue && local->queue_stop_reasons[ac_queue] == 0 && @@ -3352,3 +3357,20 @@ u8 *ieee80211_add_wmm_info_ie(u8 *buf, u8 qosinfo) return buf; } + +void ieee80211_init_tx_queue(struct ieee80211_sub_if_data *sdata, + struct sta_info *sta, + struct txq_info *txqi, int tid) +{ + skb_queue_head_init(&txqi->queue); + txqi->txq.vif = &sdata->vif; + + if (sta) { + txqi->txq.sta = &sta->sta; + sta->sta.txq[tid] = &txqi->txq; + txqi->txq.ac = ieee802_1d_to_ac[tid & 7]; + } else { + sdata->vif.txq = &txqi->txq; + txqi->txq.ac = IEEE80211_AC_BE; + } +} -- cgit v1.2.3 From 37660b6daf6d28bb2206c95ec75c8063f2db1606 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Mon, 30 Mar 2015 21:04:50 +0200 Subject: ASoC: Remove suspend_bias_level from DAPM context struct The only two users of the suspend_bias_level field were two rather old drivers which weren't exactly doing things by the book. Those drivers have been updated and field is now unused and can be removed. Signed-off-by: Lars-Peter Clausen Acked-by: Charles Keepax Signed-off-by: Mark Brown --- include/sound/soc-dapm.h | 1 - sound/soc/soc-core.c | 10 ++-------- 2 files changed, 2 insertions(+), 9 deletions(-) (limited to 'include') diff --git a/include/sound/soc-dapm.h b/include/sound/soc-dapm.h index 8d7416e46861..9e35203653ff 100644 --- a/include/sound/soc-dapm.h +++ b/include/sound/soc-dapm.h @@ -586,7 +586,6 @@ struct snd_soc_dapm_update { /* DAPM context */ struct snd_soc_dapm_context { enum snd_soc_bias_level bias_level; - enum snd_soc_bias_level suspend_bias_level; struct delayed_work delayed_work; unsigned int idle_bias_off:1; /* Use BIAS_OFF instead of STANDBY */ /* Go to BIAS_OFF in suspend if the DAPM context is idle */ diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 30579ca5bacb..72601a86a748 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -583,15 +583,9 @@ int snd_soc_suspend(struct device *dev) cpu_dai->driver->suspend(cpu_dai); } - /* close any waiting streams and save state */ - for (i = 0; i < card->num_rtd; i++) { - struct snd_soc_dai **codec_dais = card->rtd[i].codec_dais; + /* close any waiting streams */ + for (i = 0; i < card->num_rtd; i++) flush_delayed_work(&card->rtd[i].delayed_work); - for (j = 0; j < card->rtd[i].num_codecs; j++) { - codec_dais[j]->codec->dapm.suspend_bias_level = - codec_dais[j]->codec->dapm.bias_level; - } - } for (i = 0; i < card->num_rtd; i++) { -- cgit v1.2.3 From 7c0e3facf39a68f30c2eae69e969918eca1ff9d6 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Mon, 30 Mar 2015 21:04:52 +0200 Subject: ASoC: dapm: Remove delayed_work from dapm context struct The delayed_work field in the snd_soc_dapm_context struct is now unused and can be removed. Removing it reduces the size of the snd_soc_dapm_context struct by ~50% from 100 bytes to 48 bytes. Signed-off-by: Lars-Peter Clausen Acked-by: Charles Keepax Signed-off-by: Mark Brown --- include/sound/soc-dapm.h | 1 - 1 file changed, 1 deletion(-) (limited to 'include') diff --git a/include/sound/soc-dapm.h b/include/sound/soc-dapm.h index 9e35203653ff..485fc9d1a7bc 100644 --- a/include/sound/soc-dapm.h +++ b/include/sound/soc-dapm.h @@ -586,7 +586,6 @@ struct snd_soc_dapm_update { /* DAPM context */ struct snd_soc_dapm_context { enum snd_soc_bias_level bias_level; - struct delayed_work delayed_work; unsigned int idle_bias_off:1; /* Use BIAS_OFF instead of STANDBY */ /* Go to BIAS_OFF in suspend if the DAPM context is idle */ unsigned int suspend_bias_off:1; -- cgit v1.2.3 From 4e537f7fbdce5e8ae7c33ebaa8a1956c7727d5a7 Mon Sep 17 00:00:00 2001 From: Daniel Borkmann Date: Wed, 25 Mar 2015 12:49:18 -0700 Subject: bpf: Make internal bpf API independent of CONFIG_BPF_SYSCALL #ifdefs Socket filter code and other subsystems with upcoming eBPF support should not need to deal with the fact that we have CONFIG_BPF_SYSCALL defined or not. Having the bpf syscall as a config option is a nice thing and I'd expect it to stay that way for expert users (I presume one day the default setting of it might change, though), but code making use of it should not care if it's actually enabled or not. Instead, hide this via header files and let the rest deal with it. Signed-off-by: Daniel Borkmann Signed-off-by: Alexei Starovoitov Reviewed-by: Masami Hiramatsu Cc: Arnaldo Carvalho de Melo Cc: David S. Miller Cc: Jiri Olsa Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Steven Rostedt Link: http://lkml.kernel.org/r/1427312966-8434-2-git-send-email-ast@plumgrid.com Signed-off-by: Ingo Molnar --- include/linux/bpf.h | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/include/linux/bpf.h b/include/linux/bpf.h index bbfceb756452..c2e21113ecc0 100644 --- a/include/linux/bpf.h +++ b/include/linux/bpf.h @@ -113,8 +113,6 @@ struct bpf_prog_type_list { enum bpf_prog_type type; }; -void bpf_register_prog_type(struct bpf_prog_type_list *tl); - struct bpf_prog; struct bpf_prog_aux { @@ -129,11 +127,25 @@ struct bpf_prog_aux { }; #ifdef CONFIG_BPF_SYSCALL +void bpf_register_prog_type(struct bpf_prog_type_list *tl); + void bpf_prog_put(struct bpf_prog *prog); +struct bpf_prog *bpf_prog_get(u32 ufd); #else -static inline void bpf_prog_put(struct bpf_prog *prog) {} +static inline void bpf_register_prog_type(struct bpf_prog_type_list *tl) +{ +} + +static inline struct bpf_prog *bpf_prog_get(u32 ufd) +{ + return ERR_PTR(-EOPNOTSUPP); +} + +static inline void bpf_prog_put(struct bpf_prog *prog) +{ +} #endif -struct bpf_prog *bpf_prog_get(u32 ufd); + /* verify correctness of eBPF program */ int bpf_check(struct bpf_prog *fp, union bpf_attr *attr); -- cgit v1.2.3 From 72cbbc8994242b5b43753738c01bf07bf29cb70d Mon Sep 17 00:00:00 2001 From: Alexei Starovoitov Date: Wed, 25 Mar 2015 12:49:19 -0700 Subject: tracing: Add kprobe flag add TRACE_EVENT_FL_KPROBE flag to differentiate kprobe type of tracepoints, since bpf programs can only be attached to kprobe type of PERF_TYPE_TRACEPOINT perf events. Signed-off-by: Alexei Starovoitov Reviewed-by: Steven Rostedt Reviewed-by: Masami Hiramatsu Cc: Andrew Morton Cc: Arnaldo Carvalho de Melo Cc: Arnaldo Carvalho de Melo Cc: Daniel Borkmann Cc: David S. Miller Cc: Jiri Olsa Cc: Linus Torvalds Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1427312966-8434-3-git-send-email-ast@plumgrid.com Signed-off-by: Ingo Molnar --- include/linux/ftrace_event.h | 3 +++ kernel/trace/trace_kprobe.c | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/ftrace_event.h b/include/linux/ftrace_event.h index c674ee8f7fca..77325e1a1816 100644 --- a/include/linux/ftrace_event.h +++ b/include/linux/ftrace_event.h @@ -252,6 +252,7 @@ enum { TRACE_EVENT_FL_WAS_ENABLED_BIT, TRACE_EVENT_FL_USE_CALL_FILTER_BIT, TRACE_EVENT_FL_TRACEPOINT_BIT, + TRACE_EVENT_FL_KPROBE_BIT, }; /* @@ -265,6 +266,7 @@ enum { * it is best to clear the buffers that used it). * USE_CALL_FILTER - For ftrace internal events, don't use file filter * TRACEPOINT - Event is a tracepoint + * KPROBE - Event is a kprobe */ enum { TRACE_EVENT_FL_FILTERED = (1 << TRACE_EVENT_FL_FILTERED_BIT), @@ -274,6 +276,7 @@ enum { TRACE_EVENT_FL_WAS_ENABLED = (1 << TRACE_EVENT_FL_WAS_ENABLED_BIT), TRACE_EVENT_FL_USE_CALL_FILTER = (1 << TRACE_EVENT_FL_USE_CALL_FILTER_BIT), TRACE_EVENT_FL_TRACEPOINT = (1 << TRACE_EVENT_FL_TRACEPOINT_BIT), + TRACE_EVENT_FL_KPROBE = (1 << TRACE_EVENT_FL_KPROBE_BIT), }; struct ftrace_event_call { diff --git a/kernel/trace/trace_kprobe.c b/kernel/trace/trace_kprobe.c index d73f565b4e06..8fa549f6f528 100644 --- a/kernel/trace/trace_kprobe.c +++ b/kernel/trace/trace_kprobe.c @@ -1286,7 +1286,7 @@ static int register_kprobe_event(struct trace_kprobe *tk) kfree(call->print_fmt); return -ENODEV; } - call->flags = 0; + call->flags = TRACE_EVENT_FL_KPROBE; call->class->reg = kprobe_register; call->data = tk; ret = trace_add_event_call(call); -- cgit v1.2.3 From 2541517c32be2531e0da59dfd7efc1ce844644f5 Mon Sep 17 00:00:00 2001 From: Alexei Starovoitov Date: Wed, 25 Mar 2015 12:49:20 -0700 Subject: tracing, perf: Implement BPF programs attached to kprobes BPF programs, attached to kprobes, provide a safe way to execute user-defined BPF byte-code programs without being able to crash or hang the kernel in any way. The BPF engine makes sure that such programs have a finite execution time and that they cannot break out of their sandbox. The user interface is to attach to a kprobe via the perf syscall: struct perf_event_attr attr = { .type = PERF_TYPE_TRACEPOINT, .config = event_id, ... }; event_fd = perf_event_open(&attr,...); ioctl(event_fd, PERF_EVENT_IOC_SET_BPF, prog_fd); 'prog_fd' is a file descriptor associated with BPF program previously loaded. 'event_id' is an ID of the kprobe created. Closing 'event_fd': close(event_fd); ... automatically detaches BPF program from it. BPF programs can call in-kernel helper functions to: - lookup/update/delete elements in maps - probe_read - wraper of probe_kernel_read() used to access any kernel data structures BPF programs receive 'struct pt_regs *' as an input ('struct pt_regs' is architecture dependent) and return 0 to ignore the event and 1 to store kprobe event into the ring buffer. Note, kprobes are a fundamentally _not_ a stable kernel ABI, so BPF programs attached to kprobes must be recompiled for every kernel version and user must supply correct LINUX_VERSION_CODE in attr.kern_version during bpf_prog_load() call. Signed-off-by: Alexei Starovoitov Reviewed-by: Steven Rostedt Reviewed-by: Masami Hiramatsu Cc: Andrew Morton Cc: Arnaldo Carvalho de Melo Cc: Arnaldo Carvalho de Melo Cc: Daniel Borkmann Cc: David S. Miller Cc: Jiri Olsa Cc: Linus Torvalds Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1427312966-8434-4-git-send-email-ast@plumgrid.com Signed-off-by: Ingo Molnar --- include/linux/ftrace_event.h | 11 ++++ include/uapi/linux/bpf.h | 3 + include/uapi/linux/perf_event.h | 1 + kernel/bpf/syscall.c | 7 ++- kernel/events/core.c | 59 ++++++++++++++++++ kernel/trace/Makefile | 1 + kernel/trace/bpf_trace.c | 130 ++++++++++++++++++++++++++++++++++++++++ kernel/trace/trace_kprobe.c | 8 +++ 8 files changed, 219 insertions(+), 1 deletion(-) create mode 100644 kernel/trace/bpf_trace.c (limited to 'include') diff --git a/include/linux/ftrace_event.h b/include/linux/ftrace_event.h index 77325e1a1816..0aa535bc9f05 100644 --- a/include/linux/ftrace_event.h +++ b/include/linux/ftrace_event.h @@ -13,6 +13,7 @@ struct trace_array; struct trace_buffer; struct tracer; struct dentry; +struct bpf_prog; struct trace_print_flags { unsigned long mask; @@ -306,6 +307,7 @@ struct ftrace_event_call { #ifdef CONFIG_PERF_EVENTS int perf_refcount; struct hlist_head __percpu *perf_events; + struct bpf_prog *prog; int (*perf_perm)(struct ftrace_event_call *, struct perf_event *); @@ -551,6 +553,15 @@ event_trigger_unlock_commit_regs(struct ftrace_event_file *file, event_triggers_post_call(file, tt); } +#ifdef CONFIG_BPF_SYSCALL +unsigned int trace_call_bpf(struct bpf_prog *prog, void *ctx); +#else +static inline unsigned int trace_call_bpf(struct bpf_prog *prog, void *ctx) +{ + return 1; +} +#endif + enum { FILTER_OTHER = 0, FILTER_STATIC_STRING, diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h index 45da7ec7d274..b2948feeb70b 100644 --- a/include/uapi/linux/bpf.h +++ b/include/uapi/linux/bpf.h @@ -118,6 +118,7 @@ enum bpf_map_type { enum bpf_prog_type { BPF_PROG_TYPE_UNSPEC, BPF_PROG_TYPE_SOCKET_FILTER, + BPF_PROG_TYPE_KPROBE, }; /* flags for BPF_MAP_UPDATE_ELEM command */ @@ -151,6 +152,7 @@ union bpf_attr { __u32 log_level; /* verbosity level of verifier */ __u32 log_size; /* size of user buffer */ __aligned_u64 log_buf; /* user supplied buffer */ + __u32 kern_version; /* checked when prog_type=kprobe */ }; } __attribute__((aligned(8))); @@ -162,6 +164,7 @@ enum bpf_func_id { BPF_FUNC_map_lookup_elem, /* void *map_lookup_elem(&map, &key) */ BPF_FUNC_map_update_elem, /* int map_update_elem(&map, &key, &value, flags) */ BPF_FUNC_map_delete_elem, /* int map_delete_elem(&map, &key) */ + BPF_FUNC_probe_read, /* int bpf_probe_read(void *dst, int size, void *src) */ __BPF_FUNC_MAX_ID, }; diff --git a/include/uapi/linux/perf_event.h b/include/uapi/linux/perf_event.h index 3bb40ddadbe5..91803e54ee73 100644 --- a/include/uapi/linux/perf_event.h +++ b/include/uapi/linux/perf_event.h @@ -381,6 +381,7 @@ struct perf_event_attr { #define PERF_EVENT_IOC_SET_OUTPUT _IO ('$', 5) #define PERF_EVENT_IOC_SET_FILTER _IOW('$', 6, char *) #define PERF_EVENT_IOC_ID _IOR('$', 7, __u64 *) +#define PERF_EVENT_IOC_SET_BPF _IOW('$', 8, __u32) enum perf_event_ioc_flags { PERF_IOC_FLAG_GROUP = 1U << 0, diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c index 536edc2be307..504c10b990ef 100644 --- a/kernel/bpf/syscall.c +++ b/kernel/bpf/syscall.c @@ -16,6 +16,7 @@ #include #include #include +#include static LIST_HEAD(bpf_map_types); @@ -467,7 +468,7 @@ struct bpf_prog *bpf_prog_get(u32 ufd) } /* last field in 'union bpf_attr' used by this command */ -#define BPF_PROG_LOAD_LAST_FIELD log_buf +#define BPF_PROG_LOAD_LAST_FIELD kern_version static int bpf_prog_load(union bpf_attr *attr) { @@ -492,6 +493,10 @@ static int bpf_prog_load(union bpf_attr *attr) if (attr->insn_cnt >= BPF_MAXINSNS) return -EINVAL; + if (type == BPF_PROG_TYPE_KPROBE && + attr->kern_version != LINUX_VERSION_CODE) + return -EINVAL; + /* plain bpf_prog allocation */ prog = bpf_prog_alloc(bpf_prog_size(attr->insn_cnt), GFP_USER); if (!prog) diff --git a/kernel/events/core.c b/kernel/events/core.c index c40c2cac2d8e..5c13862d3e85 100644 --- a/kernel/events/core.c +++ b/kernel/events/core.c @@ -42,6 +42,8 @@ #include #include #include +#include +#include #include "internal.h" @@ -3407,6 +3409,7 @@ errout: } static void perf_event_free_filter(struct perf_event *event); +static void perf_event_free_bpf_prog(struct perf_event *event); static void free_event_rcu(struct rcu_head *head) { @@ -3416,6 +3419,7 @@ static void free_event_rcu(struct rcu_head *head) if (event->ns) put_pid_ns(event->ns); perf_event_free_filter(event); + perf_event_free_bpf_prog(event); kfree(event); } @@ -3928,6 +3932,7 @@ static inline int perf_fget_light(int fd, struct fd *p) static int perf_event_set_output(struct perf_event *event, struct perf_event *output_event); static int perf_event_set_filter(struct perf_event *event, void __user *arg); +static int perf_event_set_bpf_prog(struct perf_event *event, u32 prog_fd); static long _perf_ioctl(struct perf_event *event, unsigned int cmd, unsigned long arg) { @@ -3981,6 +3986,9 @@ static long _perf_ioctl(struct perf_event *event, unsigned int cmd, unsigned lon case PERF_EVENT_IOC_SET_FILTER: return perf_event_set_filter(event, (void __user *)arg); + case PERF_EVENT_IOC_SET_BPF: + return perf_event_set_bpf_prog(event, arg); + default: return -ENOTTY; } @@ -6455,6 +6463,49 @@ static void perf_event_free_filter(struct perf_event *event) ftrace_profile_free_filter(event); } +static int perf_event_set_bpf_prog(struct perf_event *event, u32 prog_fd) +{ + struct bpf_prog *prog; + + if (event->attr.type != PERF_TYPE_TRACEPOINT) + return -EINVAL; + + if (event->tp_event->prog) + return -EEXIST; + + if (!(event->tp_event->flags & TRACE_EVENT_FL_KPROBE)) + /* bpf programs can only be attached to kprobes */ + return -EINVAL; + + prog = bpf_prog_get(prog_fd); + if (IS_ERR(prog)) + return PTR_ERR(prog); + + if (prog->aux->prog_type != BPF_PROG_TYPE_KPROBE) { + /* valid fd, but invalid bpf program type */ + bpf_prog_put(prog); + return -EINVAL; + } + + event->tp_event->prog = prog; + + return 0; +} + +static void perf_event_free_bpf_prog(struct perf_event *event) +{ + struct bpf_prog *prog; + + if (!event->tp_event) + return; + + prog = event->tp_event->prog; + if (prog) { + event->tp_event->prog = NULL; + bpf_prog_put(prog); + } +} + #else static inline void perf_tp_register(void) @@ -6470,6 +6521,14 @@ static void perf_event_free_filter(struct perf_event *event) { } +static int perf_event_set_bpf_prog(struct perf_event *event, u32 prog_fd) +{ + return -ENOENT; +} + +static void perf_event_free_bpf_prog(struct perf_event *event) +{ +} #endif /* CONFIG_EVENT_TRACING */ #ifdef CONFIG_HAVE_HW_BREAKPOINT diff --git a/kernel/trace/Makefile b/kernel/trace/Makefile index 98f26588255e..c575a300103b 100644 --- a/kernel/trace/Makefile +++ b/kernel/trace/Makefile @@ -53,6 +53,7 @@ obj-$(CONFIG_EVENT_TRACING) += trace_event_perf.o endif obj-$(CONFIG_EVENT_TRACING) += trace_events_filter.o obj-$(CONFIG_EVENT_TRACING) += trace_events_trigger.o +obj-$(CONFIG_BPF_SYSCALL) += bpf_trace.o obj-$(CONFIG_KPROBE_EVENT) += trace_kprobe.o obj-$(CONFIG_TRACEPOINTS) += power-traces.o ifeq ($(CONFIG_PM),y) diff --git a/kernel/trace/bpf_trace.c b/kernel/trace/bpf_trace.c new file mode 100644 index 000000000000..f1e87da91da3 --- /dev/null +++ b/kernel/trace/bpf_trace.c @@ -0,0 +1,130 @@ +/* Copyright (c) 2011-2015 PLUMgrid, http://plumgrid.com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU General Public + * License as published by the Free Software Foundation. + */ +#include +#include +#include +#include +#include +#include +#include "trace.h" + +static DEFINE_PER_CPU(int, bpf_prog_active); + +/** + * trace_call_bpf - invoke BPF program + * @prog: BPF program + * @ctx: opaque context pointer + * + * kprobe handlers execute BPF programs via this helper. + * Can be used from static tracepoints in the future. + * + * Return: BPF programs always return an integer which is interpreted by + * kprobe handler as: + * 0 - return from kprobe (event is filtered out) + * 1 - store kprobe event into ring buffer + * Other values are reserved and currently alias to 1 + */ +unsigned int trace_call_bpf(struct bpf_prog *prog, void *ctx) +{ + unsigned int ret; + + if (in_nmi()) /* not supported yet */ + return 1; + + preempt_disable(); + + if (unlikely(__this_cpu_inc_return(bpf_prog_active) != 1)) { + /* + * since some bpf program is already running on this cpu, + * don't call into another bpf program (same or different) + * and don't send kprobe event into ring-buffer, + * so return zero here + */ + ret = 0; + goto out; + } + + rcu_read_lock(); + ret = BPF_PROG_RUN(prog, ctx); + rcu_read_unlock(); + + out: + __this_cpu_dec(bpf_prog_active); + preempt_enable(); + + return ret; +} +EXPORT_SYMBOL_GPL(trace_call_bpf); + +static u64 bpf_probe_read(u64 r1, u64 r2, u64 r3, u64 r4, u64 r5) +{ + void *dst = (void *) (long) r1; + int size = (int) r2; + void *unsafe_ptr = (void *) (long) r3; + + return probe_kernel_read(dst, unsafe_ptr, size); +} + +static const struct bpf_func_proto bpf_probe_read_proto = { + .func = bpf_probe_read, + .gpl_only = true, + .ret_type = RET_INTEGER, + .arg1_type = ARG_PTR_TO_STACK, + .arg2_type = ARG_CONST_STACK_SIZE, + .arg3_type = ARG_ANYTHING, +}; + +static const struct bpf_func_proto *kprobe_prog_func_proto(enum bpf_func_id func_id) +{ + switch (func_id) { + case BPF_FUNC_map_lookup_elem: + return &bpf_map_lookup_elem_proto; + case BPF_FUNC_map_update_elem: + return &bpf_map_update_elem_proto; + case BPF_FUNC_map_delete_elem: + return &bpf_map_delete_elem_proto; + case BPF_FUNC_probe_read: + return &bpf_probe_read_proto; + default: + return NULL; + } +} + +/* bpf+kprobe programs can access fields of 'struct pt_regs' */ +static bool kprobe_prog_is_valid_access(int off, int size, enum bpf_access_type type) +{ + /* check bounds */ + if (off < 0 || off >= sizeof(struct pt_regs)) + return false; + + /* only read is allowed */ + if (type != BPF_READ) + return false; + + /* disallow misaligned access */ + if (off % size != 0) + return false; + + return true; +} + +static struct bpf_verifier_ops kprobe_prog_ops = { + .get_func_proto = kprobe_prog_func_proto, + .is_valid_access = kprobe_prog_is_valid_access, +}; + +static struct bpf_prog_type_list kprobe_tl = { + .ops = &kprobe_prog_ops, + .type = BPF_PROG_TYPE_KPROBE, +}; + +static int __init register_kprobe_prog_ops(void) +{ + bpf_register_prog_type(&kprobe_tl); + return 0; +} +late_initcall(register_kprobe_prog_ops); diff --git a/kernel/trace/trace_kprobe.c b/kernel/trace/trace_kprobe.c index 8fa549f6f528..dc3462507d7c 100644 --- a/kernel/trace/trace_kprobe.c +++ b/kernel/trace/trace_kprobe.c @@ -1134,11 +1134,15 @@ static void kprobe_perf_func(struct trace_kprobe *tk, struct pt_regs *regs) { struct ftrace_event_call *call = &tk->tp.call; + struct bpf_prog *prog = call->prog; struct kprobe_trace_entry_head *entry; struct hlist_head *head; int size, __size, dsize; int rctx; + if (prog && !trace_call_bpf(prog, regs)) + return; + head = this_cpu_ptr(call->perf_events); if (hlist_empty(head)) return; @@ -1165,11 +1169,15 @@ kretprobe_perf_func(struct trace_kprobe *tk, struct kretprobe_instance *ri, struct pt_regs *regs) { struct ftrace_event_call *call = &tk->tp.call; + struct bpf_prog *prog = call->prog; struct kretprobe_trace_entry_head *entry; struct hlist_head *head; int size, __size, dsize; int rctx; + if (prog && !trace_call_bpf(prog, regs)) + return; + head = this_cpu_ptr(call->perf_events); if (hlist_empty(head)) return; -- cgit v1.2.3 From d9847d310ab4003725e6ed1822682e24bd406908 Mon Sep 17 00:00:00 2001 From: Alexei Starovoitov Date: Wed, 25 Mar 2015 12:49:21 -0700 Subject: tracing: Allow BPF programs to call bpf_ktime_get_ns() bpf_ktime_get_ns() is used by programs to compute time delta between events or as a timestamp Signed-off-by: Alexei Starovoitov Reviewed-by: Steven Rostedt Cc: Andrew Morton Cc: Arnaldo Carvalho de Melo Cc: Arnaldo Carvalho de Melo Cc: Daniel Borkmann Cc: David S. Miller Cc: Jiri Olsa Cc: Linus Torvalds Cc: Masami Hiramatsu Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1427312966-8434-5-git-send-email-ast@plumgrid.com Signed-off-by: Ingo Molnar --- include/uapi/linux/bpf.h | 1 + kernel/trace/bpf_trace.c | 14 ++++++++++++++ 2 files changed, 15 insertions(+) (limited to 'include') diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h index b2948feeb70b..238c6883877b 100644 --- a/include/uapi/linux/bpf.h +++ b/include/uapi/linux/bpf.h @@ -165,6 +165,7 @@ enum bpf_func_id { BPF_FUNC_map_update_elem, /* int map_update_elem(&map, &key, &value, flags) */ BPF_FUNC_map_delete_elem, /* int map_delete_elem(&map, &key) */ BPF_FUNC_probe_read, /* int bpf_probe_read(void *dst, int size, void *src) */ + BPF_FUNC_ktime_get_ns, /* u64 bpf_ktime_get_ns(void) */ __BPF_FUNC_MAX_ID, }; diff --git a/kernel/trace/bpf_trace.c b/kernel/trace/bpf_trace.c index f1e87da91da3..8f5787294971 100644 --- a/kernel/trace/bpf_trace.c +++ b/kernel/trace/bpf_trace.c @@ -78,6 +78,18 @@ static const struct bpf_func_proto bpf_probe_read_proto = { .arg3_type = ARG_ANYTHING, }; +static u64 bpf_ktime_get_ns(u64 r1, u64 r2, u64 r3, u64 r4, u64 r5) +{ + /* NMI safe access to clock monotonic */ + return ktime_get_mono_fast_ns(); +} + +static const struct bpf_func_proto bpf_ktime_get_ns_proto = { + .func = bpf_ktime_get_ns, + .gpl_only = true, + .ret_type = RET_INTEGER, +}; + static const struct bpf_func_proto *kprobe_prog_func_proto(enum bpf_func_id func_id) { switch (func_id) { @@ -89,6 +101,8 @@ static const struct bpf_func_proto *kprobe_prog_func_proto(enum bpf_func_id func return &bpf_map_delete_elem_proto; case BPF_FUNC_probe_read: return &bpf_probe_read_proto; + case BPF_FUNC_ktime_get_ns: + return &bpf_ktime_get_ns_proto; default: return NULL; } -- cgit v1.2.3 From 9c959c863f8217a2ff3d7c296e8223654d240569 Mon Sep 17 00:00:00 2001 From: Alexei Starovoitov Date: Wed, 25 Mar 2015 12:49:22 -0700 Subject: tracing: Allow BPF programs to call bpf_trace_printk() Debugging of BPF programs needs some form of printk from the program, so let programs call limited trace_printk() with %d %u %x %p modifiers only. Similar to kernel modules, during program load verifier checks whether program is calling bpf_trace_printk() and if so, kernel allocates trace_printk buffers and emits big 'this is debug only' banner. Signed-off-by: Alexei Starovoitov Reviewed-by: Steven Rostedt Cc: Andrew Morton Cc: Arnaldo Carvalho de Melo Cc: Arnaldo Carvalho de Melo Cc: Daniel Borkmann Cc: David S. Miller Cc: Jiri Olsa Cc: Linus Torvalds Cc: Masami Hiramatsu Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1427312966-8434-6-git-send-email-ast@plumgrid.com Signed-off-by: Ingo Molnar --- include/uapi/linux/bpf.h | 1 + kernel/trace/bpf_trace.c | 78 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 79 insertions(+) (limited to 'include') diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h index 238c6883877b..cc47ef41076a 100644 --- a/include/uapi/linux/bpf.h +++ b/include/uapi/linux/bpf.h @@ -166,6 +166,7 @@ enum bpf_func_id { BPF_FUNC_map_delete_elem, /* int map_delete_elem(&map, &key) */ BPF_FUNC_probe_read, /* int bpf_probe_read(void *dst, int size, void *src) */ BPF_FUNC_ktime_get_ns, /* u64 bpf_ktime_get_ns(void) */ + BPF_FUNC_trace_printk, /* int bpf_trace_printk(const char *fmt, int fmt_size, ...) */ __BPF_FUNC_MAX_ID, }; diff --git a/kernel/trace/bpf_trace.c b/kernel/trace/bpf_trace.c index 8f5787294971..2d56ce501632 100644 --- a/kernel/trace/bpf_trace.c +++ b/kernel/trace/bpf_trace.c @@ -10,6 +10,7 @@ #include #include #include +#include #include "trace.h" static DEFINE_PER_CPU(int, bpf_prog_active); @@ -90,6 +91,74 @@ static const struct bpf_func_proto bpf_ktime_get_ns_proto = { .ret_type = RET_INTEGER, }; +/* + * limited trace_printk() + * only %d %u %x %ld %lu %lx %lld %llu %llx %p conversion specifiers allowed + */ +static u64 bpf_trace_printk(u64 r1, u64 fmt_size, u64 r3, u64 r4, u64 r5) +{ + char *fmt = (char *) (long) r1; + int mod[3] = {}; + int fmt_cnt = 0; + int i; + + /* + * bpf_check()->check_func_arg()->check_stack_boundary() + * guarantees that fmt points to bpf program stack, + * fmt_size bytes of it were initialized and fmt_size > 0 + */ + if (fmt[--fmt_size] != 0) + return -EINVAL; + + /* check format string for allowed specifiers */ + for (i = 0; i < fmt_size; i++) { + if ((!isprint(fmt[i]) && !isspace(fmt[i])) || !isascii(fmt[i])) + return -EINVAL; + + if (fmt[i] != '%') + continue; + + if (fmt_cnt >= 3) + return -EINVAL; + + /* fmt[i] != 0 && fmt[last] == 0, so we can access fmt[i + 1] */ + i++; + if (fmt[i] == 'l') { + mod[fmt_cnt]++; + i++; + } else if (fmt[i] == 'p') { + mod[fmt_cnt]++; + i++; + if (!isspace(fmt[i]) && !ispunct(fmt[i]) && fmt[i] != 0) + return -EINVAL; + fmt_cnt++; + continue; + } + + if (fmt[i] == 'l') { + mod[fmt_cnt]++; + i++; + } + + if (fmt[i] != 'd' && fmt[i] != 'u' && fmt[i] != 'x') + return -EINVAL; + fmt_cnt++; + } + + return __trace_printk(1/* fake ip will not be printed */, fmt, + mod[0] == 2 ? r3 : mod[0] == 1 ? (long) r3 : (u32) r3, + mod[1] == 2 ? r4 : mod[1] == 1 ? (long) r4 : (u32) r4, + mod[2] == 2 ? r5 : mod[2] == 1 ? (long) r5 : (u32) r5); +} + +static const struct bpf_func_proto bpf_trace_printk_proto = { + .func = bpf_trace_printk, + .gpl_only = true, + .ret_type = RET_INTEGER, + .arg1_type = ARG_PTR_TO_STACK, + .arg2_type = ARG_CONST_STACK_SIZE, +}; + static const struct bpf_func_proto *kprobe_prog_func_proto(enum bpf_func_id func_id) { switch (func_id) { @@ -103,6 +172,15 @@ static const struct bpf_func_proto *kprobe_prog_func_proto(enum bpf_func_id func return &bpf_probe_read_proto; case BPF_FUNC_ktime_get_ns: return &bpf_ktime_get_ns_proto; + + case BPF_FUNC_trace_printk: + /* + * this program might be calling bpf_trace_printk, + * so allocate per-cpu printk buffers + */ + trace_printk_init_buffers(); + + return &bpf_trace_printk_proto; default: return NULL; } -- cgit v1.2.3 From e6214487492566b15ff24e97c6747bb2e5d9e040 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Thu, 2 Apr 2015 13:41:08 +0300 Subject: Bluetooth: Add second hci_request callback option for full skb This patch adds a second possible callback for HCI requests where the callback will receive the full skb of the last successfully completed HCI command. This API is useful for cases where we want to use a request to read some data and the existing hci_event.c handlers do not store it e.g. in the hci_dev struct. The reason the patch is a bit bigger than just adding the new API is because the hci_req_cmd_complete() functions required some refactoring to enable it: now hci_req_cmd_complete() is simply used to request the callback pointers if any, and the actual calling of them happens from a single place at the end of hci_event_packet(). The reason for this is that we need to pass the original skb (without any skb_pull, etc modifications done to it) and it's simplest to keep track of it within the hci_event_packet() function. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- include/net/bluetooth/bluetooth.h | 3 ++ net/bluetooth/hci_core.c | 30 +++++++--------- net/bluetooth/hci_event.c | 76 ++++++++++++++++++++++++++------------- net/bluetooth/hci_request.c | 14 +++++++- net/bluetooth/hci_request.h | 5 ++- 5 files changed, 84 insertions(+), 44 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/bluetooth.h b/include/net/bluetooth/bluetooth.h index eeaff4b5cb62..7dba80546f16 100644 --- a/include/net/bluetooth/bluetooth.h +++ b/include/net/bluetooth/bluetooth.h @@ -277,11 +277,14 @@ struct l2cap_ctrl { struct hci_dev; typedef void (*hci_req_complete_t)(struct hci_dev *hdev, u8 status, u16 opcode); +typedef void (*hci_req_complete_skb_t)(struct hci_dev *hdev, u8 status, + u16 opcode, struct sk_buff *skb); struct req_ctrl { bool start; u8 event; hci_req_complete_t complete; + hci_req_complete_skb_t complete_skb; }; struct bt_skb_cb { diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 246d7eca5d29..8af3af324eee 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -4288,9 +4288,10 @@ static void hci_resend_last(struct hci_dev *hdev) queue_work(hdev->workqueue, &hdev->cmd_work); } -void hci_req_cmd_complete(struct hci_dev *hdev, u16 opcode, u8 status) +void hci_req_cmd_complete(struct hci_dev *hdev, u16 opcode, u8 status, + hci_req_complete_t *req_complete, + hci_req_complete_skb_t *req_complete_skb) { - hci_req_complete_t req_complete = NULL; struct sk_buff *skb; unsigned long flags; @@ -4322,18 +4323,14 @@ void hci_req_cmd_complete(struct hci_dev *hdev, u16 opcode, u8 status) * callback would be found in hdev->sent_cmd instead of the * command queue (hdev->cmd_q). */ - if (hdev->sent_cmd) { - req_complete = bt_cb(hdev->sent_cmd)->req.complete; - - if (req_complete) { - /* We must set the complete callback to NULL to - * avoid calling the callback more than once if - * this function gets called again. - */ - bt_cb(hdev->sent_cmd)->req.complete = NULL; + if (bt_cb(hdev->sent_cmd)->req.complete) { + *req_complete = bt_cb(hdev->sent_cmd)->req.complete; + return; + } - goto call_complete; - } + if (bt_cb(hdev->sent_cmd)->req.complete_skb) { + *req_complete_skb = bt_cb(hdev->sent_cmd)->req.complete_skb; + return; } /* Remove all pending commands belonging to this request */ @@ -4344,14 +4341,11 @@ void hci_req_cmd_complete(struct hci_dev *hdev, u16 opcode, u8 status) break; } - req_complete = bt_cb(skb)->req.complete; + *req_complete = bt_cb(skb)->req.complete; + *req_complete_skb = bt_cb(skb)->req.complete_skb; kfree_skb(skb); } spin_unlock_irqrestore(&hdev->cmd_q.lock, flags); - -call_complete: - if (req_complete) - req_complete(hdev, status, status ? opcode : HCI_OP_NOP); } static void hci_rx_work(struct work_struct *work) diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 87e5bee36408..7c69eb3629b7 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -2731,17 +2731,19 @@ unlock: hci_dev_unlock(hdev); } -static void hci_cmd_complete_evt(struct hci_dev *hdev, struct sk_buff *skb) +static void hci_cmd_complete_evt(struct hci_dev *hdev, struct sk_buff *skb, + u16 *opcode, u8 *status, + hci_req_complete_t *req_complete, + hci_req_complete_skb_t *req_complete_skb) { struct hci_ev_cmd_complete *ev = (void *) skb->data; - u8 status = skb->data[sizeof(*ev)]; - __u16 opcode; - skb_pull(skb, sizeof(*ev)); + *opcode = __le16_to_cpu(ev->opcode); + *status = skb->data[sizeof(*ev)]; - opcode = __le16_to_cpu(ev->opcode); + skb_pull(skb, sizeof(*ev)); - switch (opcode) { + switch (*opcode) { case HCI_OP_INQUIRY_CANCEL: hci_cc_inquiry_cancel(hdev, skb); break; @@ -3019,32 +3021,36 @@ static void hci_cmd_complete_evt(struct hci_dev *hdev, struct sk_buff *skb) break; default: - BT_DBG("%s opcode 0x%4.4x", hdev->name, opcode); + BT_DBG("%s opcode 0x%4.4x", hdev->name, *opcode); break; } - if (opcode != HCI_OP_NOP) + if (*opcode != HCI_OP_NOP) cancel_delayed_work(&hdev->cmd_timer); if (ev->ncmd && !test_bit(HCI_RESET, &hdev->flags)) atomic_set(&hdev->cmd_cnt, 1); - hci_req_cmd_complete(hdev, opcode, status); + hci_req_cmd_complete(hdev, *opcode, *status, req_complete, + req_complete_skb); if (atomic_read(&hdev->cmd_cnt) && !skb_queue_empty(&hdev->cmd_q)) queue_work(hdev->workqueue, &hdev->cmd_work); } -static void hci_cmd_status_evt(struct hci_dev *hdev, struct sk_buff *skb) +static void hci_cmd_status_evt(struct hci_dev *hdev, struct sk_buff *skb, + u16 *opcode, u8 *status, + hci_req_complete_t *req_complete, + hci_req_complete_skb_t *req_complete_skb) { struct hci_ev_cmd_status *ev = (void *) skb->data; - __u16 opcode; skb_pull(skb, sizeof(*ev)); - opcode = __le16_to_cpu(ev->opcode); + *opcode = __le16_to_cpu(ev->opcode); + *status = ev->status; - switch (opcode) { + switch (*opcode) { case HCI_OP_INQUIRY: hci_cs_inquiry(hdev, ev->status); break; @@ -3114,11 +3120,11 @@ static void hci_cmd_status_evt(struct hci_dev *hdev, struct sk_buff *skb) break; default: - BT_DBG("%s opcode 0x%4.4x", hdev->name, opcode); + BT_DBG("%s opcode 0x%4.4x", hdev->name, *opcode); break; } - if (opcode != HCI_OP_NOP) + if (*opcode != HCI_OP_NOP) cancel_delayed_work(&hdev->cmd_timer); if (ev->ncmd && !test_bit(HCI_RESET, &hdev->flags)) @@ -3132,7 +3138,8 @@ static void hci_cmd_status_evt(struct hci_dev *hdev, struct sk_buff *skb) */ if (ev->status || (hdev->sent_cmd && !bt_cb(hdev->sent_cmd)->req.event)) - hci_req_cmd_complete(hdev, opcode, ev->status); + hci_req_cmd_complete(hdev, *opcode, ev->status, req_complete, + req_complete_skb); if (atomic_read(&hdev->cmd_cnt) && !skb_queue_empty(&hdev->cmd_q)) queue_work(hdev->workqueue, &hdev->cmd_work); @@ -5039,7 +5046,11 @@ static void hci_chan_selected_evt(struct hci_dev *hdev, struct sk_buff *skb) void hci_event_packet(struct hci_dev *hdev, struct sk_buff *skb) { struct hci_event_hdr *hdr = (void *) skb->data; - __u8 event = hdr->evt; + hci_req_complete_t req_complete = NULL; + hci_req_complete_skb_t req_complete_skb = NULL; + struct sk_buff *orig_skb = NULL; + u8 status = 0, event = hdr->evt; + u16 opcode = HCI_OP_NOP; hci_dev_lock(hdev); @@ -5053,15 +5064,24 @@ void hci_event_packet(struct hci_dev *hdev, struct sk_buff *skb) hci_dev_unlock(hdev); - skb_pull(skb, HCI_EVENT_HDR_SIZE); - if (hdev->sent_cmd && bt_cb(hdev->sent_cmd)->req.event == event) { struct hci_command_hdr *cmd_hdr = (void *) hdev->sent_cmd->data; - u16 opcode = __le16_to_cpu(cmd_hdr->opcode); - - hci_req_cmd_complete(hdev, opcode, 0); + opcode = __le16_to_cpu(cmd_hdr->opcode); + hci_req_cmd_complete(hdev, opcode, status, &req_complete, + &req_complete_skb); } + /* If it looks like we might end up having to call + * req_complete_skb, store a pristine copy of the skb since the + * various handlers may modify the original one through + * skb_pull() calls, etc. + */ + if (req_complete_skb || event == HCI_EV_CMD_STATUS || + event == HCI_EV_CMD_COMPLETE) + orig_skb = skb_clone(skb, GFP_KERNEL); + + skb_pull(skb, HCI_EVENT_HDR_SIZE); + switch (event) { case HCI_EV_INQUIRY_COMPLETE: hci_inquiry_complete_evt(hdev, skb); @@ -5104,11 +5124,13 @@ void hci_event_packet(struct hci_dev *hdev, struct sk_buff *skb) break; case HCI_EV_CMD_COMPLETE: - hci_cmd_complete_evt(hdev, skb); + hci_cmd_complete_evt(hdev, skb, &opcode, &status, + &req_complete, &req_complete_skb); break; case HCI_EV_CMD_STATUS: - hci_cmd_status_evt(hdev, skb); + hci_cmd_status_evt(hdev, skb, &opcode, &status, &req_complete, + &req_complete_skb); break; case HCI_EV_HARDWARE_ERROR: @@ -5240,6 +5262,12 @@ void hci_event_packet(struct hci_dev *hdev, struct sk_buff *skb) break; } + if (req_complete) + req_complete(hdev, status, opcode); + else if (req_complete_skb) + req_complete_skb(hdev, status, opcode, orig_skb); + + kfree_skb(orig_skb); kfree_skb(skb); hdev->stat.evt_rx++; } diff --git a/net/bluetooth/hci_request.c b/net/bluetooth/hci_request.c index 7e17907effb3..d6025d6e6d59 100644 --- a/net/bluetooth/hci_request.c +++ b/net/bluetooth/hci_request.c @@ -34,7 +34,8 @@ void hci_req_init(struct hci_request *req, struct hci_dev *hdev) req->err = 0; } -int hci_req_run(struct hci_request *req, hci_req_complete_t complete) +static int req_run(struct hci_request *req, hci_req_complete_t complete, + hci_req_complete_skb_t complete_skb) { struct hci_dev *hdev = req->hdev; struct sk_buff *skb; @@ -56,6 +57,7 @@ int hci_req_run(struct hci_request *req, hci_req_complete_t complete) skb = skb_peek_tail(&req->cmd_q); bt_cb(skb)->req.complete = complete; + bt_cb(skb)->req.complete_skb = complete_skb; spin_lock_irqsave(&hdev->cmd_q.lock, flags); skb_queue_splice_tail(&req->cmd_q, &hdev->cmd_q); @@ -66,6 +68,16 @@ int hci_req_run(struct hci_request *req, hci_req_complete_t complete) return 0; } +int hci_req_run(struct hci_request *req, hci_req_complete_t complete) +{ + return req_run(req, complete, NULL); +} + +int hci_req_run_skb(struct hci_request *req, hci_req_complete_skb_t complete) +{ + return req_run(req, NULL, complete); +} + struct sk_buff *hci_prepare_cmd(struct hci_dev *hdev, u16 opcode, u32 plen, const void *param) { diff --git a/net/bluetooth/hci_request.h b/net/bluetooth/hci_request.h index adf074d33544..bf6df92f42db 100644 --- a/net/bluetooth/hci_request.h +++ b/net/bluetooth/hci_request.h @@ -32,11 +32,14 @@ struct hci_request { void hci_req_init(struct hci_request *req, struct hci_dev *hdev); int hci_req_run(struct hci_request *req, hci_req_complete_t complete); +int hci_req_run_skb(struct hci_request *req, hci_req_complete_skb_t complete); void hci_req_add(struct hci_request *req, u16 opcode, u32 plen, const void *param); void hci_req_add_ev(struct hci_request *req, u16 opcode, u32 plen, const void *param, u8 event); -void hci_req_cmd_complete(struct hci_dev *hdev, u16 opcode, u8 status); +void hci_req_cmd_complete(struct hci_dev *hdev, u16 opcode, u8 status, + hci_req_complete_t *req_complete, + hci_req_complete_skb_t *req_complete_skb); struct sk_buff *hci_prepare_cmd(struct hci_dev *hdev, u16 opcode, u32 plen, const void *param); -- cgit v1.2.3 From f60cb30579d3401cab1ed36b42df5c0568ae0ba7 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Thu, 2 Apr 2015 13:41:09 +0300 Subject: Bluetooth: Convert hci_req_sync family of function to new request API Now that there's an API in place that allows passing the resulting skb to the request callback we can conveniently convert the hci_req_sync and related functions to use it. Since we still need to get the skb from the async callback into the sleeping _sync() function the patch adds another req_skb variable to hci_dev where the sync request state is tracked. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- include/net/bluetooth/hci_core.h | 1 + net/bluetooth/hci_core.c | 28 ++++++++++++++-------------- 2 files changed, 15 insertions(+), 14 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 540c07feece7..257ac04c00e1 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -334,6 +334,7 @@ struct hci_dev { wait_queue_head_t req_wait_q; __u32 req_status; __u32 req_result; + struct sk_buff *req_skb; void *smp_data; void *smp_bredr_data; diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 8af3af324eee..5cbb0957edc7 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -141,13 +141,16 @@ static const struct file_operations dut_mode_fops = { /* ---- HCI requests ---- */ -static void hci_req_sync_complete(struct hci_dev *hdev, u8 result, u16 opcode) +static void hci_req_sync_complete(struct hci_dev *hdev, u8 result, u16 opcode, + struct sk_buff *skb) { BT_DBG("%s result 0x%2.2x", hdev->name, result); if (hdev->req_status == HCI_REQ_PEND) { hdev->req_result = result; hdev->req_status = HCI_REQ_DONE; + if (skb) + hdev->req_skb = skb_get(skb); wake_up_interruptible(&hdev->req_wait_q); } } @@ -164,18 +167,10 @@ static void hci_req_cancel(struct hci_dev *hdev, int err) } static struct sk_buff *hci_get_cmd_complete(struct hci_dev *hdev, u16 opcode, - u8 event) + u8 event, struct sk_buff *skb) { struct hci_ev_cmd_complete *ev; struct hci_event_hdr *hdr; - struct sk_buff *skb; - - hci_dev_lock(hdev); - - skb = hdev->recv_evt; - hdev->recv_evt = NULL; - - hci_dev_unlock(hdev); if (!skb) return ERR_PTR(-ENODATA); @@ -223,6 +218,7 @@ struct sk_buff *__hci_cmd_sync_ev(struct hci_dev *hdev, u16 opcode, u32 plen, { DECLARE_WAITQUEUE(wait, current); struct hci_request req; + struct sk_buff *skb; int err = 0; BT_DBG("%s", hdev->name); @@ -236,7 +232,7 @@ struct sk_buff *__hci_cmd_sync_ev(struct hci_dev *hdev, u16 opcode, u32 plen, add_wait_queue(&hdev->req_wait_q, &wait); set_current_state(TASK_INTERRUPTIBLE); - err = hci_req_run(&req, hci_req_sync_complete); + err = hci_req_run_skb(&req, hci_req_sync_complete); if (err < 0) { remove_wait_queue(&hdev->req_wait_q, &wait); set_current_state(TASK_RUNNING); @@ -265,13 +261,17 @@ struct sk_buff *__hci_cmd_sync_ev(struct hci_dev *hdev, u16 opcode, u32 plen, } hdev->req_status = hdev->req_result = 0; + skb = hdev->req_skb; + hdev->req_skb = NULL; BT_DBG("%s end: err %d", hdev->name, err); - if (err < 0) + if (err < 0) { + kfree_skb(skb); return ERR_PTR(err); + } - return hci_get_cmd_complete(hdev, opcode, event); + return hci_get_cmd_complete(hdev, opcode, event, skb); } EXPORT_SYMBOL(__hci_cmd_sync_ev); @@ -303,7 +303,7 @@ static int __hci_req_sync(struct hci_dev *hdev, add_wait_queue(&hdev->req_wait_q, &wait); set_current_state(TASK_INTERRUPTIBLE); - err = hci_req_run(&req, hci_req_sync_complete); + err = hci_req_run_skb(&req, hci_req_sync_complete); if (err < 0) { hdev->req_status = 0; -- cgit v1.2.3 From f7d9e97592aeb7742084814c5f37e25571b2d51d Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Thu, 2 Apr 2015 13:41:10 +0300 Subject: Bluetooth: Remove unneeded recv_event variable Now that the synchronous HCI requests use the new API and a new private variable the recv_evt member of hci_dev is no-longer needed. This patch removes it. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- include/net/bluetooth/hci_core.h | 1 - net/bluetooth/hci_core.c | 3 --- net/bluetooth/hci_event.c | 12 ------------ 3 files changed, 16 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 257ac04c00e1..4cefee0b6330 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -326,7 +326,6 @@ struct hci_dev { struct sk_buff_head raw_q; struct sk_buff_head cmd_q; - struct sk_buff *recv_evt; struct sk_buff *sent_cmd; struct sk_buff *reassembly[NUM_REASSEMBLY]; diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 5cbb0957edc7..6192f6e3242f 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -1690,9 +1690,6 @@ static int hci_dev_do_close(struct hci_dev *hdev) hdev->sent_cmd = NULL; } - kfree_skb(hdev->recv_evt); - hdev->recv_evt = NULL; - /* After this point our queues are empty * and no tasks are scheduled. */ hdev->close(hdev); diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 7c69eb3629b7..dc9547c11c45 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -5052,18 +5052,6 @@ void hci_event_packet(struct hci_dev *hdev, struct sk_buff *skb) u8 status = 0, event = hdr->evt; u16 opcode = HCI_OP_NOP; - hci_dev_lock(hdev); - - /* Received events are (currently) only needed when a request is - * ongoing so avoid unnecessary memory allocation. - */ - if (hci_req_pending(hdev)) { - kfree_skb(hdev->recv_evt); - hdev->recv_evt = skb_clone(skb, GFP_KERNEL); - } - - hci_dev_unlock(hdev); - if (hdev->sent_cmd && bt_cb(hdev->sent_cmd)->req.event == event) { struct hci_command_hdr *cmd_hdr = (void *) hdev->sent_cmd->data; opcode = __le16_to_cpu(cmd_hdr->opcode); -- cgit v1.2.3 From abe66a4d036933c7376b40b0d7bb5de0458331aa Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Thu, 2 Apr 2015 13:41:11 +0300 Subject: Bluetooth: Remove unused hci_req_pending() function The hci_req_pending() function has no users anymore, so simply remove it. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- include/net/bluetooth/hci_core.h | 2 -- net/bluetooth/hci_core.c | 5 ----- 2 files changed, 7 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 4cefee0b6330..12686e8e9343 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -1284,8 +1284,6 @@ static inline int hci_check_conn_params(u16 min, u16 max, u16 latency, int hci_register_cb(struct hci_cb *hcb); int hci_unregister_cb(struct hci_cb *hcb); -bool hci_req_pending(struct hci_dev *hdev); - struct sk_buff *__hci_cmd_sync(struct hci_dev *hdev, u16 opcode, u32 plen, const void *param, u32 timeout); struct sk_buff *__hci_cmd_sync_ev(struct hci_dev *hdev, u16 opcode, u32 plen, diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 6192f6e3242f..fda23720e7b8 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -3560,11 +3560,6 @@ static void hci_send_frame(struct hci_dev *hdev, struct sk_buff *skb) } } -bool hci_req_pending(struct hci_dev *hdev) -{ - return (hdev->req_status == HCI_REQ_PEND); -} - /* Send HCI command */ int hci_send_cmd(struct hci_dev *hdev, __u16 opcode, __u32 plen, const void *param) -- cgit v1.2.3 From 1b9441f8ec426223f6f54f2af10ee01c8b743e5b Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Thu, 2 Apr 2015 13:41:13 +0300 Subject: Bluetooth: Convert local OOB data reading to use HCI request Now that there's a HCI request API available where the callback receives the resulting skb, we can convert the local OOB data reading to use this new API. This patch does the necessary update in mgmt.c (which also requires moving the callback higher up since it's now a static function) and removes the custom calls from hci_event.c that are no-longer necessary. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- include/net/bluetooth/hci_core.h | 3 -- net/bluetooth/hci_event.c | 11 ---- net/bluetooth/mgmt.c | 105 ++++++++++++++++++++++++--------------- 3 files changed, 65 insertions(+), 54 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 12686e8e9343..93fd3e756b8a 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -1391,9 +1391,6 @@ void mgmt_ssp_enable_complete(struct hci_dev *hdev, u8 enable, u8 status); void mgmt_set_class_of_dev_complete(struct hci_dev *hdev, u8 *dev_class, u8 status); void mgmt_set_local_name_complete(struct hci_dev *hdev, u8 *name, u8 status); -void mgmt_read_local_oob_data_complete(struct hci_dev *hdev, u8 *hash192, - u8 *rand192, u8 *hash256, u8 *rand256, - u8 status); void mgmt_device_found(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type, u8 addr_type, u8 *dev_class, s8 rssi, u32 flags, u8 *eir, u16 eir_len, u8 *scan_rsp, u8 scan_rsp_len); diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index c2483cb6ffbd..01031038eb0e 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -1045,11 +1045,6 @@ static void hci_cc_read_local_oob_data(struct hci_dev *hdev, struct hci_rp_read_local_oob_data *rp = (void *) skb->data; BT_DBG("%s status 0x%2.2x", hdev->name, rp->status); - - hci_dev_lock(hdev); - mgmt_read_local_oob_data_complete(hdev, rp->hash, rp->rand, NULL, NULL, - rp->status); - hci_dev_unlock(hdev); } static void hci_cc_read_local_oob_ext_data(struct hci_dev *hdev, @@ -1058,12 +1053,6 @@ static void hci_cc_read_local_oob_ext_data(struct hci_dev *hdev, struct hci_rp_read_local_oob_ext_data *rp = (void *) skb->data; BT_DBG("%s status 0x%2.2x", hdev->name, rp->status); - - hci_dev_lock(hdev); - mgmt_read_local_oob_data_complete(hdev, rp->hash192, rp->rand192, - rp->hash256, rp->rand256, - rp->status); - hci_dev_unlock(hdev); } static void hci_cc_le_set_random_addr(struct hci_dev *hdev, struct sk_buff *skb) diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 3048092b1264..bb0c53ac4c66 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -3783,10 +3783,70 @@ failed: return err; } +static void read_local_oob_data_complete(struct hci_dev *hdev, u8 status, + u16 opcode, struct sk_buff *skb) +{ + struct mgmt_rp_read_local_oob_data mgmt_rp; + size_t rp_size = sizeof(mgmt_rp); + struct mgmt_pending_cmd *cmd; + + BT_DBG("%s status %u", hdev->name, status); + + cmd = pending_find(MGMT_OP_READ_LOCAL_OOB_DATA, hdev); + if (!cmd) + return; + + if (status || !skb) { + mgmt_cmd_status(cmd->sk, hdev->id, MGMT_OP_READ_LOCAL_OOB_DATA, + status ? mgmt_status(status) : MGMT_STATUS_FAILED); + goto remove; + } + + memset(&mgmt_rp, 0, sizeof(mgmt_rp)); + + if (opcode == HCI_OP_READ_LOCAL_OOB_DATA) { + struct hci_rp_read_local_oob_data *rp = (void *) skb->data; + + if (skb->len < sizeof(*rp)) { + mgmt_cmd_status(cmd->sk, hdev->id, + MGMT_OP_READ_LOCAL_OOB_DATA, + MGMT_STATUS_FAILED); + goto remove; + } + + memcpy(mgmt_rp.hash192, rp->hash, sizeof(rp->hash)); + memcpy(mgmt_rp.rand192, rp->rand, sizeof(rp->rand)); + + rp_size -= sizeof(mgmt_rp.hash256) + sizeof(mgmt_rp.rand256); + } else { + struct hci_rp_read_local_oob_ext_data *rp = (void *) skb->data; + + if (skb->len < sizeof(*rp)) { + mgmt_cmd_status(cmd->sk, hdev->id, + MGMT_OP_READ_LOCAL_OOB_DATA, + MGMT_STATUS_FAILED); + goto remove; + } + + memcpy(mgmt_rp.hash192, rp->hash192, sizeof(rp->hash192)); + memcpy(mgmt_rp.rand192, rp->rand192, sizeof(rp->rand192)); + + memcpy(mgmt_rp.hash256, rp->hash256, sizeof(rp->hash256)); + memcpy(mgmt_rp.rand256, rp->rand256, sizeof(rp->rand256)); + } + + mgmt_cmd_complete(cmd->sk, hdev->id, MGMT_OP_READ_LOCAL_OOB_DATA, + MGMT_STATUS_SUCCESS, &mgmt_rp, rp_size); + +remove: + mgmt_pending_remove(cmd); +} + static int read_local_oob_data(struct sock *sk, struct hci_dev *hdev, void *data, u16 data_len) { struct mgmt_pending_cmd *cmd; + struct hci_request req; int err; BT_DBG("%s", hdev->name); @@ -3817,12 +3877,14 @@ static int read_local_oob_data(struct sock *sk, struct hci_dev *hdev, goto unlock; } + hci_req_init(&req, hdev); + if (bredr_sc_enabled(hdev)) - err = hci_send_cmd(hdev, HCI_OP_READ_LOCAL_OOB_EXT_DATA, - 0, NULL); + hci_req_add(&req, HCI_OP_READ_LOCAL_OOB_EXT_DATA, 0, NULL); else - err = hci_send_cmd(hdev, HCI_OP_READ_LOCAL_OOB_DATA, 0, NULL); + hci_req_add(&req, HCI_OP_READ_LOCAL_OOB_DATA, 0, NULL); + err = hci_req_run_skb(&req, read_local_oob_data_complete); if (err < 0) mgmt_pending_remove(cmd); @@ -7920,43 +7982,6 @@ void mgmt_set_local_name_complete(struct hci_dev *hdev, u8 *name, u8 status) cmd ? cmd->sk : NULL); } -void mgmt_read_local_oob_data_complete(struct hci_dev *hdev, u8 *hash192, - u8 *rand192, u8 *hash256, u8 *rand256, - u8 status) -{ - struct mgmt_pending_cmd *cmd; - - BT_DBG("%s status %u", hdev->name, status); - - cmd = pending_find(MGMT_OP_READ_LOCAL_OOB_DATA, hdev); - if (!cmd) - return; - - if (status) { - mgmt_cmd_status(cmd->sk, hdev->id, MGMT_OP_READ_LOCAL_OOB_DATA, - mgmt_status(status)); - } else { - struct mgmt_rp_read_local_oob_data rp; - size_t rp_size = sizeof(rp); - - memcpy(rp.hash192, hash192, sizeof(rp.hash192)); - memcpy(rp.rand192, rand192, sizeof(rp.rand192)); - - if (bredr_sc_enabled(hdev) && hash256 && rand256) { - memcpy(rp.hash256, hash256, sizeof(rp.hash256)); - memcpy(rp.rand256, rand256, sizeof(rp.rand256)); - } else { - rp_size -= sizeof(rp.hash256) + sizeof(rp.rand256); - } - - mgmt_cmd_complete(cmd->sk, hdev->id, - MGMT_OP_READ_LOCAL_OOB_DATA, 0, - &rp, rp_size); - } - - mgmt_pending_remove(cmd); -} - static inline bool has_uuid(u8 *uuid, u16 uuid_count, u8 (*uuids)[16]) { int i; -- cgit v1.2.3 From e8c6deac69629c0cb97c3d3272f8631ef17f8f0f Mon Sep 17 00:00:00 2001 From: Alexander Shishkin Date: Wed, 14 Jan 2015 14:18:10 +0200 Subject: perf: Add data_{offset,size} to user_page Currently, the actual perf ring buffer is one page into the mmap area, following the user page and the userspace follows this convention. This patch adds data_{offset,size} fields to user_page that can be used by userspace instead for locating perf data in the mmap area. This is also helpful when mapping existing or shared buffers if their size is not known in advance. Right now, it is made to follow the existing convention that data_offset == PAGE_SIZE and data_offset + data_size == mmap_size. Signed-off-by: Alexander Shishkin Signed-off-by: Peter Zijlstra (Intel) Cc: Borislav Petkov Cc: Frederic Weisbecker Cc: H. Peter Anvin Cc: Kaixu Xia Cc: Linus Torvalds Cc: Mike Galbraith Cc: Paul Mackerras Cc: Robert Richter Cc: Stephane Eranian Cc: Thomas Gleixner Cc: acme@infradead.org Cc: adrian.hunter@intel.com Cc: kan.liang@intel.com Cc: markus.t.metzger@intel.com Cc: mathieu.poirier@linaro.org Link: http://lkml.kernel.org/r/1421237903-181015-2-git-send-email-alexander.shishkin@linux.intel.com Signed-off-by: Ingo Molnar --- include/uapi/linux/perf_event.h | 5 +++++ kernel/events/core.c | 2 ++ 2 files changed, 7 insertions(+) (limited to 'include') diff --git a/include/uapi/linux/perf_event.h b/include/uapi/linux/perf_event.h index 91803e54ee73..86c44ae66d43 100644 --- a/include/uapi/linux/perf_event.h +++ b/include/uapi/linux/perf_event.h @@ -522,9 +522,14 @@ struct perf_event_mmap_page { * In this case the kernel will not over-write unread data. * * See perf_output_put_handle() for the data ordering. + * + * data_{offset,size} indicate the location and size of the perf record + * buffer within the mmapped area. */ __u64 data_head; /* head in the data section */ __u64 data_tail; /* user-space written tail */ + __u64 data_offset; /* where the buffer starts */ + __u64 data_size; /* data buffer size */ }; #define PERF_RECORD_MISC_CPUMODE_MASK (7 << 0) diff --git a/kernel/events/core.c b/kernel/events/core.c index 5c13862d3e85..6efa516f1ab8 100644 --- a/kernel/events/core.c +++ b/kernel/events/core.c @@ -4105,6 +4105,8 @@ static void perf_event_init_userpage(struct perf_event *event) /* Allow new userspace to detect that bit 0 is deprecated */ userpg->cap_bit0_is_deprecated = 1; userpg->size = offsetof(struct perf_event_mmap_page, __reserved); + userpg->data_offset = PAGE_SIZE; + userpg->data_size = perf_data_size(rb); unlock: rcu_read_unlock(); -- cgit v1.2.3 From 45bfb2e50471abbbfd83d40d28c986078b0d24ff Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Wed, 14 Jan 2015 14:18:11 +0200 Subject: perf: Add AUX area to ring buffer for raw data streams This patch introduces "AUX space" in the perf mmap buffer, intended for exporting high bandwidth data streams to userspace, such as instruction flow traces. AUX space is a ring buffer, defined by aux_{offset,size} fields in the user_page structure, and read/write pointers aux_{head,tail}, which abide by the same rules as data_* counterparts of the main perf buffer. In order to allocate/mmap AUX, userspace needs to set up aux_offset to such an offset that will be greater than data_offset+data_size and aux_size to be the desired buffer size. Both need to be page aligned. Then, same aux_offset and aux_size should be passed to mmap() call and if everything adds up, you should have an AUX buffer as a result. Pages that are mapped into this buffer also come out of user's mlock rlimit plus perf_event_mlock_kb allowance. Signed-off-by: Peter Zijlstra (Intel) Acked-by: Alexander Shishkin Cc: Borislav Petkov Cc: Frederic Weisbecker Cc: H. Peter Anvin Cc: Kaixu Xia Cc: Linus Torvalds Cc: Mike Galbraith Cc: Paul Mackerras Cc: Robert Richter Cc: Stephane Eranian Cc: Thomas Gleixner Cc: acme@infradead.org Cc: adrian.hunter@intel.com Cc: kan.liang@intel.com Cc: markus.t.metzger@intel.com Cc: mathieu.poirier@linaro.org Link: http://lkml.kernel.org/r/1421237903-181015-3-git-send-email-alexander.shishkin@linux.intel.com Signed-off-by: Ingo Molnar --- include/linux/perf_event.h | 17 +++++ include/uapi/linux/perf_event.h | 16 +++++ kernel/events/core.c | 141 +++++++++++++++++++++++++++++++++------- kernel/events/internal.h | 23 +++++++ kernel/events/ring_buffer.c | 97 +++++++++++++++++++++++++-- 5 files changed, 265 insertions(+), 29 deletions(-) (limited to 'include') diff --git a/include/linux/perf_event.h b/include/linux/perf_event.h index 401554074de9..5a94f6d6fa91 100644 --- a/include/linux/perf_event.h +++ b/include/linux/perf_event.h @@ -284,6 +284,18 @@ struct pmu { * Return the count value for a counter. */ u64 (*count) (struct perf_event *event); /*optional*/ + + /* + * Set up pmu-private data structures for an AUX area + */ + void *(*setup_aux) (int cpu, void **pages, + int nr_pages, bool overwrite); + /* optional */ + + /* + * Free pmu-private AUX data structures + */ + void (*free_aux) (void *aux); /* optional */ }; /** @@ -862,6 +874,11 @@ static inline bool needs_branch_stack(struct perf_event *event) return event->attr.branch_sample_type != 0; } +static inline bool has_aux(struct perf_event *event) +{ + return event->pmu->setup_aux; +} + extern int perf_output_begin(struct perf_output_handle *handle, struct perf_event *event, unsigned int size); extern void perf_output_end(struct perf_output_handle *handle); diff --git a/include/uapi/linux/perf_event.h b/include/uapi/linux/perf_event.h index 86c44ae66d43..6c5013a71714 100644 --- a/include/uapi/linux/perf_event.h +++ b/include/uapi/linux/perf_event.h @@ -530,6 +530,22 @@ struct perf_event_mmap_page { __u64 data_tail; /* user-space written tail */ __u64 data_offset; /* where the buffer starts */ __u64 data_size; /* data buffer size */ + + /* + * AUX area is defined by aux_{offset,size} fields that should be set + * by the userspace, so that + * + * aux_offset >= data_offset + data_size + * + * prior to mmap()ing it. Size of the mmap()ed area should be aux_size. + * + * Ring buffer pointers aux_{head,tail} have the same semantics as + * data_{head,tail} and same ordering rules apply. + */ + __u64 aux_head; + __u64 aux_tail; + __u64 aux_offset; + __u64 aux_size; }; #define PERF_RECORD_MISC_CPUMODE_MASK (7 << 0) diff --git a/kernel/events/core.c b/kernel/events/core.c index 6efa516f1ab8..da51128c337a 100644 --- a/kernel/events/core.c +++ b/kernel/events/core.c @@ -4306,6 +4306,9 @@ static void perf_mmap_open(struct vm_area_struct *vma) atomic_inc(&event->mmap_count); atomic_inc(&event->rb->mmap_count); + if (vma->vm_pgoff) + atomic_inc(&event->rb->aux_mmap_count); + if (event->pmu->event_mapped) event->pmu->event_mapped(event); } @@ -4330,6 +4333,20 @@ static void perf_mmap_close(struct vm_area_struct *vma) if (event->pmu->event_unmapped) event->pmu->event_unmapped(event); + /* + * rb->aux_mmap_count will always drop before rb->mmap_count and + * event->mmap_count, so it is ok to use event->mmap_mutex to + * serialize with perf_mmap here. + */ + if (rb_has_aux(rb) && vma->vm_pgoff == rb->aux_pgoff && + atomic_dec_and_mutex_lock(&rb->aux_mmap_count, &event->mmap_mutex)) { + atomic_long_sub(rb->aux_nr_pages, &mmap_user->locked_vm); + vma->vm_mm->pinned_vm -= rb->aux_mmap_locked; + + rb_free_aux(rb); + mutex_unlock(&event->mmap_mutex); + } + atomic_dec(&rb->mmap_count); if (!atomic_dec_and_mutex_lock(&event->mmap_count, &event->mmap_mutex)) @@ -4403,7 +4420,7 @@ out_put: static const struct vm_operations_struct perf_mmap_vmops = { .open = perf_mmap_open, - .close = perf_mmap_close, + .close = perf_mmap_close, /* non mergable */ .fault = perf_mmap_fault, .page_mkwrite = perf_mmap_fault, }; @@ -4414,10 +4431,10 @@ static int perf_mmap(struct file *file, struct vm_area_struct *vma) unsigned long user_locked, user_lock_limit; struct user_struct *user = current_user(); unsigned long locked, lock_limit; - struct ring_buffer *rb; + struct ring_buffer *rb = NULL; unsigned long vma_size; unsigned long nr_pages; - long user_extra, extra; + long user_extra = 0, extra = 0; int ret = 0, flags = 0; /* @@ -4432,7 +4449,66 @@ static int perf_mmap(struct file *file, struct vm_area_struct *vma) return -EINVAL; vma_size = vma->vm_end - vma->vm_start; - nr_pages = (vma_size / PAGE_SIZE) - 1; + + if (vma->vm_pgoff == 0) { + nr_pages = (vma_size / PAGE_SIZE) - 1; + } else { + /* + * AUX area mapping: if rb->aux_nr_pages != 0, it's already + * mapped, all subsequent mappings should have the same size + * and offset. Must be above the normal perf buffer. + */ + u64 aux_offset, aux_size; + + if (!event->rb) + return -EINVAL; + + nr_pages = vma_size / PAGE_SIZE; + + mutex_lock(&event->mmap_mutex); + ret = -EINVAL; + + rb = event->rb; + if (!rb) + goto aux_unlock; + + aux_offset = ACCESS_ONCE(rb->user_page->aux_offset); + aux_size = ACCESS_ONCE(rb->user_page->aux_size); + + if (aux_offset < perf_data_size(rb) + PAGE_SIZE) + goto aux_unlock; + + if (aux_offset != vma->vm_pgoff << PAGE_SHIFT) + goto aux_unlock; + + /* already mapped with a different offset */ + if (rb_has_aux(rb) && rb->aux_pgoff != vma->vm_pgoff) + goto aux_unlock; + + if (aux_size != vma_size || aux_size != nr_pages * PAGE_SIZE) + goto aux_unlock; + + /* already mapped with a different size */ + if (rb_has_aux(rb) && rb->aux_nr_pages != nr_pages) + goto aux_unlock; + + if (!is_power_of_2(nr_pages)) + goto aux_unlock; + + if (!atomic_inc_not_zero(&rb->mmap_count)) + goto aux_unlock; + + if (rb_has_aux(rb)) { + atomic_inc(&rb->aux_mmap_count); + ret = 0; + goto unlock; + } + + atomic_set(&rb->aux_mmap_count, 1); + user_extra = nr_pages; + + goto accounting; + } /* * If we have rb pages ensure they're a power-of-two number, so we @@ -4444,9 +4520,6 @@ static int perf_mmap(struct file *file, struct vm_area_struct *vma) if (vma_size != PAGE_SIZE * (1 + nr_pages)) return -EINVAL; - if (vma->vm_pgoff != 0) - return -EINVAL; - WARN_ON_ONCE(event->ctx->parent_ctx); again: mutex_lock(&event->mmap_mutex); @@ -4470,6 +4543,8 @@ again: } user_extra = nr_pages + 1; + +accounting: user_lock_limit = sysctl_perf_event_mlock >> (PAGE_SHIFT - 10); /* @@ -4479,7 +4554,6 @@ again: user_locked = atomic_long_read(&user->locked_vm) + user_extra; - extra = 0; if (user_locked > user_lock_limit) extra = user_locked - user_lock_limit; @@ -4493,35 +4567,45 @@ again: goto unlock; } - WARN_ON(event->rb); + WARN_ON(!rb && event->rb); if (vma->vm_flags & VM_WRITE) flags |= RING_BUFFER_WRITABLE; - rb = rb_alloc(nr_pages, - event->attr.watermark ? event->attr.wakeup_watermark : 0, - event->cpu, flags); - if (!rb) { - ret = -ENOMEM; - goto unlock; - } + rb = rb_alloc(nr_pages, + event->attr.watermark ? event->attr.wakeup_watermark : 0, + event->cpu, flags); - atomic_set(&rb->mmap_count, 1); - rb->mmap_locked = extra; - rb->mmap_user = get_current_user(); + if (!rb) { + ret = -ENOMEM; + goto unlock; + } - atomic_long_add(user_extra, &user->locked_vm); - vma->vm_mm->pinned_vm += extra; + atomic_set(&rb->mmap_count, 1); + rb->mmap_user = get_current_user(); + rb->mmap_locked = extra; - ring_buffer_attach(event, rb); + ring_buffer_attach(event, rb); - perf_event_init_userpage(event); - perf_event_update_userpage(event); + perf_event_init_userpage(event); + perf_event_update_userpage(event); + } else { + ret = rb_alloc_aux(rb, event, vma->vm_pgoff, nr_pages, flags); + if (!ret) + rb->aux_mmap_locked = extra; + } unlock: - if (!ret) + if (!ret) { + atomic_long_add(user_extra, &user->locked_vm); + vma->vm_mm->pinned_vm += extra; + atomic_inc(&event->mmap_count); + } else if (rb) { + atomic_dec(&rb->mmap_count); + } +aux_unlock: mutex_unlock(&event->mmap_mutex); /* @@ -7506,6 +7590,13 @@ perf_event_set_output(struct perf_event *event, struct perf_event *output_event) if (output_event->clock != event->clock) goto out; + /* + * If both events generate aux data, they must be on the same PMU + */ + if (has_aux(event) && has_aux(output_event) && + event->pmu != output_event->pmu) + goto out; + set: mutex_lock(&event->mmap_mutex); /* Can't redirect output if we've got an active mmap() */ diff --git a/kernel/events/internal.h b/kernel/events/internal.h index 569b218782ad..0f6d08015927 100644 --- a/kernel/events/internal.h +++ b/kernel/events/internal.h @@ -35,6 +35,16 @@ struct ring_buffer { unsigned long mmap_locked; struct user_struct *mmap_user; + /* AUX area */ + unsigned long aux_pgoff; + int aux_nr_pages; + atomic_t aux_mmap_count; + unsigned long aux_mmap_locked; + void (*free_aux)(void *); + atomic_t aux_refcount; + void **aux_pages; + void *aux_priv; + struct perf_event_mmap_page *user_page; void *data_pages[0]; }; @@ -43,6 +53,14 @@ extern void rb_free(struct ring_buffer *rb); extern struct ring_buffer * rb_alloc(int nr_pages, long watermark, int cpu, int flags); extern void perf_event_wakeup(struct perf_event *event); +extern int rb_alloc_aux(struct ring_buffer *rb, struct perf_event *event, + pgoff_t pgoff, int nr_pages, int flags); +extern void rb_free_aux(struct ring_buffer *rb); + +static inline bool rb_has_aux(struct ring_buffer *rb) +{ + return !!rb->aux_nr_pages; +} extern void perf_event_header__init_id(struct perf_event_header *header, @@ -81,6 +99,11 @@ static inline unsigned long perf_data_size(struct ring_buffer *rb) return rb->nr_pages << (PAGE_SHIFT + page_order(rb)); } +static inline unsigned long perf_aux_size(struct ring_buffer *rb) +{ + return rb->aux_nr_pages << PAGE_SHIFT; +} + #define DEFINE_OUTPUT_COPY(func_name, memcpy_func) \ static inline unsigned long \ func_name(struct perf_output_handle *handle, \ diff --git a/kernel/events/ring_buffer.c b/kernel/events/ring_buffer.c index eadb95ce7aac..3de9c4e9ea9f 100644 --- a/kernel/events/ring_buffer.c +++ b/kernel/events/ring_buffer.c @@ -243,14 +243,87 @@ ring_buffer_init(struct ring_buffer *rb, long watermark, int flags) spin_lock_init(&rb->event_lock); } +int rb_alloc_aux(struct ring_buffer *rb, struct perf_event *event, + pgoff_t pgoff, int nr_pages, int flags) +{ + bool overwrite = !(flags & RING_BUFFER_WRITABLE); + int node = (event->cpu == -1) ? -1 : cpu_to_node(event->cpu); + int ret = -ENOMEM; + + if (!has_aux(event)) + return -ENOTSUPP; + + rb->aux_pages = kzalloc_node(nr_pages * sizeof(void *), GFP_KERNEL, node); + if (!rb->aux_pages) + return -ENOMEM; + + rb->free_aux = event->pmu->free_aux; + for (rb->aux_nr_pages = 0; rb->aux_nr_pages < nr_pages; + rb->aux_nr_pages++) { + struct page *page; + + page = alloc_pages_node(node, GFP_KERNEL | __GFP_ZERO, 0); + if (!page) + goto out; + + rb->aux_pages[rb->aux_nr_pages] = page_address(page); + } + + rb->aux_priv = event->pmu->setup_aux(event->cpu, rb->aux_pages, nr_pages, + overwrite); + if (!rb->aux_priv) + goto out; + + ret = 0; + + /* + * aux_pages (and pmu driver's private data, aux_priv) will be + * referenced in both producer's and consumer's contexts, thus + * we keep a refcount here to make sure either of the two can + * reference them safely. + */ + atomic_set(&rb->aux_refcount, 1); + +out: + if (!ret) + rb->aux_pgoff = pgoff; + else + rb_free_aux(rb); + + return ret; +} + +static void __rb_free_aux(struct ring_buffer *rb) +{ + int pg; + + if (rb->aux_priv) { + rb->free_aux(rb->aux_priv); + rb->free_aux = NULL; + rb->aux_priv = NULL; + } + + for (pg = 0; pg < rb->aux_nr_pages; pg++) + free_page((unsigned long)rb->aux_pages[pg]); + + kfree(rb->aux_pages); + rb->aux_nr_pages = 0; +} + +void rb_free_aux(struct ring_buffer *rb) +{ + if (atomic_dec_and_test(&rb->aux_refcount)) + __rb_free_aux(rb); +} + #ifndef CONFIG_PERF_USE_VMALLOC /* * Back perf_mmap() with regular GFP_KERNEL-0 pages. */ -struct page * -perf_mmap_to_page(struct ring_buffer *rb, unsigned long pgoff) +static struct page * +__perf_mmap_to_page(struct ring_buffer *rb, unsigned long pgoff) { if (pgoff > rb->nr_pages) return NULL; @@ -340,8 +413,8 @@ static int data_page_nr(struct ring_buffer *rb) return rb->nr_pages << page_order(rb); } -struct page * -perf_mmap_to_page(struct ring_buffer *rb, unsigned long pgoff) +static struct page * +__perf_mmap_to_page(struct ring_buffer *rb, unsigned long pgoff) { /* The '>' counts in the user page. */ if (pgoff > data_page_nr(rb)) @@ -416,3 +489,19 @@ fail: } #endif + +struct page * +perf_mmap_to_page(struct ring_buffer *rb, unsigned long pgoff) +{ + if (rb->aux_nr_pages) { + /* above AUX space */ + if (pgoff > rb->aux_pgoff + rb->aux_nr_pages) + return NULL; + + /* AUX space */ + if (pgoff >= rb->aux_pgoff) + return virt_to_page(rb->aux_pages[pgoff - rb->aux_pgoff]); + } + + return __perf_mmap_to_page(rb, pgoff); +} -- cgit v1.2.3 From 0a4e38e64f5e91ce131cc42ee5bb3925377ec840 Mon Sep 17 00:00:00 2001 From: Alexander Shishkin Date: Wed, 14 Jan 2015 14:18:12 +0200 Subject: perf: Support high-order allocations for AUX space Some pmus (such as BTS or Intel PT without multiple-entry ToPA capability) don't support scatter-gather and will prefer larger contiguous areas for their output regions. This patch adds a new pmu capability to request higher order allocations. Signed-off-by: Alexander Shishkin Signed-off-by: Peter Zijlstra (Intel) Cc: Borislav Petkov Cc: Frederic Weisbecker Cc: H. Peter Anvin Cc: Kaixu Xia Cc: Linus Torvalds Cc: Mike Galbraith Cc: Paul Mackerras Cc: Robert Richter Cc: Stephane Eranian Cc: Thomas Gleixner Cc: acme@infradead.org Cc: adrian.hunter@intel.com Cc: kan.liang@intel.com Cc: markus.t.metzger@intel.com Cc: mathieu.poirier@linaro.org Link: http://lkml.kernel.org/r/1421237903-181015-4-git-send-email-alexander.shishkin@linux.intel.com Signed-off-by: Ingo Molnar --- include/linux/perf_event.h | 1 + kernel/events/ring_buffer.c | 56 ++++++++++++++++++++++++++++++++++++++++----- 2 files changed, 51 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/include/linux/perf_event.h b/include/linux/perf_event.h index 5a94f6d6fa91..d5a4a8e95808 100644 --- a/include/linux/perf_event.h +++ b/include/linux/perf_event.h @@ -174,6 +174,7 @@ struct perf_event; */ #define PERF_PMU_CAP_NO_INTERRUPT 0x01 #define PERF_PMU_CAP_NO_NMI 0x02 +#define PERF_PMU_CAP_AUX_NO_SG 0x04 /** * struct pmu - generic performance monitoring unit diff --git a/kernel/events/ring_buffer.c b/kernel/events/ring_buffer.c index 3de9c4e9ea9f..ed0859e33b2f 100644 --- a/kernel/events/ring_buffer.c +++ b/kernel/events/ring_buffer.c @@ -243,30 +243,74 @@ ring_buffer_init(struct ring_buffer *rb, long watermark, int flags) spin_lock_init(&rb->event_lock); } +#define PERF_AUX_GFP (GFP_KERNEL | __GFP_ZERO | __GFP_NOWARN | __GFP_NORETRY) + +static struct page *rb_alloc_aux_page(int node, int order) +{ + struct page *page; + + if (order > MAX_ORDER) + order = MAX_ORDER; + + do { + page = alloc_pages_node(node, PERF_AUX_GFP, order); + } while (!page && order--); + + if (page && order) { + /* + * Communicate the allocation size to the driver + */ + split_page(page, order); + SetPagePrivate(page); + set_page_private(page, order); + } + + return page; +} + +static void rb_free_aux_page(struct ring_buffer *rb, int idx) +{ + struct page *page = virt_to_page(rb->aux_pages[idx]); + + ClearPagePrivate(page); + page->mapping = NULL; + __free_page(page); +} + int rb_alloc_aux(struct ring_buffer *rb, struct perf_event *event, pgoff_t pgoff, int nr_pages, int flags) { bool overwrite = !(flags & RING_BUFFER_WRITABLE); int node = (event->cpu == -1) ? -1 : cpu_to_node(event->cpu); - int ret = -ENOMEM; + int ret = -ENOMEM, max_order = 0; if (!has_aux(event)) return -ENOTSUPP; + if (event->pmu->capabilities & PERF_PMU_CAP_AUX_NO_SG) + /* + * We need to start with the max_order that fits in nr_pages, + * not the other way around, hence ilog2() and not get_order. + */ + max_order = ilog2(nr_pages); + rb->aux_pages = kzalloc_node(nr_pages * sizeof(void *), GFP_KERNEL, node); if (!rb->aux_pages) return -ENOMEM; rb->free_aux = event->pmu->free_aux; - for (rb->aux_nr_pages = 0; rb->aux_nr_pages < nr_pages; - rb->aux_nr_pages++) { + for (rb->aux_nr_pages = 0; rb->aux_nr_pages < nr_pages;) { struct page *page; + int last, order; - page = alloc_pages_node(node, GFP_KERNEL | __GFP_ZERO, 0); + order = min(max_order, ilog2(nr_pages - rb->aux_nr_pages)); + page = rb_alloc_aux_page(node, order); if (!page) goto out; - rb->aux_pages[rb->aux_nr_pages] = page_address(page); + for (last = rb->aux_nr_pages + (1 << page_private(page)); + last > rb->aux_nr_pages; rb->aux_nr_pages++) + rb->aux_pages[rb->aux_nr_pages] = page_address(page++); } rb->aux_priv = event->pmu->setup_aux(event->cpu, rb->aux_pages, nr_pages, @@ -304,7 +348,7 @@ static void __rb_free_aux(struct ring_buffer *rb) } for (pg = 0; pg < rb->aux_nr_pages; pg++) - free_page((unsigned long)rb->aux_pages[pg]); + rb_free_aux_page(rb, pg); kfree(rb->aux_pages); rb->aux_nr_pages = 0; -- cgit v1.2.3 From 6a279230391b63130070e0219b0ad09d34d28c89 Mon Sep 17 00:00:00 2001 From: Alexander Shishkin Date: Wed, 14 Jan 2015 14:18:13 +0200 Subject: perf: Add a capability for AUX_NO_SG pmus to do software double buffering For pmus that don't support scatter-gather for AUX data in hardware, it might still make sense to implement software double buffering to avoid losing data while the user is reading data out. For this purpose, add a pmu capability that guarantees multiple high-order chunks for AUX buffer, so that the pmu driver can do switchover tricks. To make use of this feature, add PERF_PMU_CAP_AUX_SW_DOUBLEBUF to your pmu's capability mask. This will make the ring buffer AUX allocation code ensure that the biggest high order allocation for the aux buffer pages is no bigger than half of the total requested buffer size, thus making sure that the buffer has at least two high order allocations. Signed-off-by: Alexander Shishkin Signed-off-by: Peter Zijlstra (Intel) Cc: Borislav Petkov Cc: Frederic Weisbecker Cc: H. Peter Anvin Cc: Kaixu Xia Cc: Linus Torvalds Cc: Mike Galbraith Cc: Paul Mackerras Cc: Robert Richter Cc: Stephane Eranian Cc: Thomas Gleixner Cc: acme@infradead.org Cc: adrian.hunter@intel.com Cc: kan.liang@intel.com Cc: markus.t.metzger@intel.com Cc: mathieu.poirier@linaro.org Link: http://lkml.kernel.org/r/1421237903-181015-5-git-send-email-alexander.shishkin@linux.intel.com Signed-off-by: Ingo Molnar --- include/linux/perf_event.h | 1 + kernel/events/ring_buffer.c | 15 ++++++++++++++- 2 files changed, 15 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/perf_event.h b/include/linux/perf_event.h index d5a4a8e95808..13a1eb3a2a2d 100644 --- a/include/linux/perf_event.h +++ b/include/linux/perf_event.h @@ -175,6 +175,7 @@ struct perf_event; #define PERF_PMU_CAP_NO_INTERRUPT 0x01 #define PERF_PMU_CAP_NO_NMI 0x02 #define PERF_PMU_CAP_AUX_NO_SG 0x04 +#define PERF_PMU_CAP_AUX_SW_DOUBLEBUF 0x08 /** * struct pmu - generic performance monitoring unit diff --git a/kernel/events/ring_buffer.c b/kernel/events/ring_buffer.c index ed0859e33b2f..6e3be7a10c50 100644 --- a/kernel/events/ring_buffer.c +++ b/kernel/events/ring_buffer.c @@ -287,13 +287,26 @@ int rb_alloc_aux(struct ring_buffer *rb, struct perf_event *event, if (!has_aux(event)) return -ENOTSUPP; - if (event->pmu->capabilities & PERF_PMU_CAP_AUX_NO_SG) + if (event->pmu->capabilities & PERF_PMU_CAP_AUX_NO_SG) { /* * We need to start with the max_order that fits in nr_pages, * not the other way around, hence ilog2() and not get_order. */ max_order = ilog2(nr_pages); + /* + * PMU requests more than one contiguous chunks of memory + * for SW double buffering + */ + if ((event->pmu->capabilities & PERF_PMU_CAP_AUX_SW_DOUBLEBUF) && + !overwrite) { + if (!max_order) + return -EINVAL; + + max_order--; + } + } + rb->aux_pages = kzalloc_node(nr_pages * sizeof(void *), GFP_KERNEL, node); if (!rb->aux_pages) return -ENOMEM; -- cgit v1.2.3 From bed5b25ad9c8a2f5d735ef0bc746ec870c01c1b0 Mon Sep 17 00:00:00 2001 From: Alexander Shishkin Date: Fri, 30 Jan 2015 12:31:06 +0200 Subject: perf: Add a pmu capability for "exclusive" events Usually, pmus that do, for example, instruction tracing, would only ever be able to have one event per task per cpu (or per perf_event_context). For such pmus it makes sense to disallow creating conflicting events early on, so as to provide consistent behavior for the user. This patch adds a pmu capability that indicates such constraint on event creation. Signed-off-by: Alexander Shishkin Signed-off-by: Peter Zijlstra (Intel) Cc: Borislav Petkov Cc: Frederic Weisbecker Cc: H. Peter Anvin Cc: Kaixu Xia Cc: Linus Torvalds Cc: Mike Galbraith Cc: Paul Mackerras Cc: Robert Richter Cc: Stephane Eranian Cc: Thomas Gleixner Cc: acme@infradead.org Cc: adrian.hunter@intel.com Cc: kan.liang@intel.com Cc: markus.t.metzger@intel.com Cc: mathieu.poirier@linaro.org Link: http://lkml.kernel.org/r/1422613866-113186-1-git-send-email-alexander.shishkin@linux.intel.com Signed-off-by: Ingo Molnar --- include/linux/perf_event.h | 2 + kernel/events/core.c | 119 ++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 119 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/linux/perf_event.h b/include/linux/perf_event.h index 13a1eb3a2a2d..f936a1e51f29 100644 --- a/include/linux/perf_event.h +++ b/include/linux/perf_event.h @@ -176,6 +176,7 @@ struct perf_event; #define PERF_PMU_CAP_NO_NMI 0x02 #define PERF_PMU_CAP_AUX_NO_SG 0x04 #define PERF_PMU_CAP_AUX_SW_DOUBLEBUF 0x08 +#define PERF_PMU_CAP_EXCLUSIVE 0x10 /** * struct pmu - generic performance monitoring unit @@ -196,6 +197,7 @@ struct pmu { int * __percpu pmu_disable_count; struct perf_cpu_context * __percpu pmu_cpu_context; + atomic_t exclusive_cnt; /* < 0: cpu; > 0: tsk */ int task_ctx_nr; int hrtimer_interval_ms; diff --git a/kernel/events/core.c b/kernel/events/core.c index da51128c337a..6d9fdaef7b57 100644 --- a/kernel/events/core.c +++ b/kernel/events/core.c @@ -3459,6 +3459,91 @@ static void unaccount_event(struct perf_event *event) unaccount_event_cpu(event, event->cpu); } +/* + * The following implement mutual exclusion of events on "exclusive" pmus + * (PERF_PMU_CAP_EXCLUSIVE). Such pmus can only have one event scheduled + * at a time, so we disallow creating events that might conflict, namely: + * + * 1) cpu-wide events in the presence of per-task events, + * 2) per-task events in the presence of cpu-wide events, + * 3) two matching events on the same context. + * + * The former two cases are handled in the allocation path (perf_event_alloc(), + * __free_event()), the latter -- before the first perf_install_in_context(). + */ +static int exclusive_event_init(struct perf_event *event) +{ + struct pmu *pmu = event->pmu; + + if (!(pmu->capabilities & PERF_PMU_CAP_EXCLUSIVE)) + return 0; + + /* + * Prevent co-existence of per-task and cpu-wide events on the + * same exclusive pmu. + * + * Negative pmu::exclusive_cnt means there are cpu-wide + * events on this "exclusive" pmu, positive means there are + * per-task events. + * + * Since this is called in perf_event_alloc() path, event::ctx + * doesn't exist yet; it is, however, safe to use PERF_ATTACH_TASK + * to mean "per-task event", because unlike other attach states it + * never gets cleared. + */ + if (event->attach_state & PERF_ATTACH_TASK) { + if (!atomic_inc_unless_negative(&pmu->exclusive_cnt)) + return -EBUSY; + } else { + if (!atomic_dec_unless_positive(&pmu->exclusive_cnt)) + return -EBUSY; + } + + return 0; +} + +static void exclusive_event_destroy(struct perf_event *event) +{ + struct pmu *pmu = event->pmu; + + if (!(pmu->capabilities & PERF_PMU_CAP_EXCLUSIVE)) + return; + + /* see comment in exclusive_event_init() */ + if (event->attach_state & PERF_ATTACH_TASK) + atomic_dec(&pmu->exclusive_cnt); + else + atomic_inc(&pmu->exclusive_cnt); +} + +static bool exclusive_event_match(struct perf_event *e1, struct perf_event *e2) +{ + if ((e1->pmu->capabilities & PERF_PMU_CAP_EXCLUSIVE) && + (e1->cpu == e2->cpu || + e1->cpu == -1 || + e2->cpu == -1)) + return true; + return false; +} + +/* Called under the same ctx::mutex as perf_install_in_context() */ +static bool exclusive_event_installable(struct perf_event *event, + struct perf_event_context *ctx) +{ + struct perf_event *iter_event; + struct pmu *pmu = event->pmu; + + if (!(pmu->capabilities & PERF_PMU_CAP_EXCLUSIVE)) + return true; + + list_for_each_entry(iter_event, &ctx->event_list, event_entry) { + if (exclusive_event_match(iter_event, event)) + return false; + } + + return true; +} + static void __free_event(struct perf_event *event) { if (!event->parent) { @@ -3472,8 +3557,10 @@ static void __free_event(struct perf_event *event) if (event->ctx) put_ctx(event->ctx); - if (event->pmu) + if (event->pmu) { + exclusive_event_destroy(event); module_put(event->pmu->module); + } call_rcu(&event->rcu_head, free_event_rcu); } @@ -7150,6 +7237,7 @@ got_cpu_context: pmu->event_idx = perf_event_idx_default; list_add_rcu(&pmu->entry, &pmus); + atomic_set(&pmu->exclusive_cnt, 0); ret = 0; unlock: mutex_unlock(&pmus_lock); @@ -7405,16 +7493,23 @@ perf_event_alloc(struct perf_event_attr *attr, int cpu, goto err_ns; } + err = exclusive_event_init(event); + if (err) + goto err_pmu; + if (!event->parent) { if (event->attr.sample_type & PERF_SAMPLE_CALLCHAIN) { err = get_callchain_buffers(); if (err) - goto err_pmu; + goto err_per_task; } } return event; +err_per_task: + exclusive_event_destroy(event); + err_pmu: if (event->destroy) event->destroy(event); @@ -7819,6 +7914,11 @@ SYSCALL_DEFINE5(perf_event_open, goto err_alloc; } + if ((pmu->capabilities & PERF_PMU_CAP_EXCLUSIVE) && group_leader) { + err = -EBUSY; + goto err_context; + } + if (task) { put_task_struct(task); task = NULL; @@ -7941,6 +8041,13 @@ SYSCALL_DEFINE5(perf_event_open, get_ctx(ctx); } + if (!exclusive_event_installable(event, ctx)) { + err = -EBUSY; + mutex_unlock(&ctx->mutex); + fput(event_file); + goto err_context; + } + perf_install_in_context(ctx, event, event->cpu); perf_unpin_context(ctx); @@ -8032,6 +8139,14 @@ perf_event_create_kernel_counter(struct perf_event_attr *attr, int cpu, WARN_ON_ONCE(ctx->parent_ctx); mutex_lock(&ctx->mutex); + if (!exclusive_event_installable(event, ctx)) { + mutex_unlock(&ctx->mutex); + perf_unpin_context(ctx); + put_ctx(ctx); + err = -EBUSY; + goto err_free; + } + perf_install_in_context(ctx, event, cpu); perf_unpin_context(ctx); mutex_unlock(&ctx->mutex); -- cgit v1.2.3 From 68db7e98c3a6ebe7284b6cf14906ed7c55f3f7f0 Mon Sep 17 00:00:00 2001 From: Alexander Shishkin Date: Wed, 14 Jan 2015 14:18:15 +0200 Subject: perf: Add AUX record When there's new data in the AUX space, output a record indicating its offset and size and a set of flags, such as PERF_AUX_FLAG_TRUNCATED, to mean the described data was truncated to fit in the ring buffer. Signed-off-by: Alexander Shishkin Signed-off-by: Peter Zijlstra (Intel) Cc: Arnaldo Carvalho de Melo Cc: Borislav Petkov Cc: Frederic Weisbecker Cc: H. Peter Anvin Cc: Kaixu Xia Cc: Linus Torvalds Cc: Mike Galbraith Cc: Paul Mackerras Cc: Robert Richter Cc: Stephane Eranian Cc: Thomas Gleixner Cc: adrian.hunter@intel.com Cc: kan.liang@intel.com Cc: markus.t.metzger@intel.com Cc: mathieu.poirier@linaro.org Link: http://lkml.kernel.org/r/1421237903-181015-7-git-send-email-alexander.shishkin@linux.intel.com Signed-off-by: Ingo Molnar --- include/uapi/linux/perf_event.h | 19 +++++++++++++++++++ kernel/events/core.c | 34 ++++++++++++++++++++++++++++++++++ kernel/events/internal.h | 3 +++ 3 files changed, 56 insertions(+) (limited to 'include') diff --git a/include/uapi/linux/perf_event.h b/include/uapi/linux/perf_event.h index 6c5013a71714..8904ad3a850b 100644 --- a/include/uapi/linux/perf_event.h +++ b/include/uapi/linux/perf_event.h @@ -768,6 +768,20 @@ enum perf_event_type { */ PERF_RECORD_MMAP2 = 10, + /* + * Records that new data landed in the AUX buffer part. + * + * struct { + * struct perf_event_header header; + * + * u64 aux_offset; + * u64 aux_size; + * u64 flags; + * struct sample_id sample_id; + * }; + */ + PERF_RECORD_AUX = 11, + PERF_RECORD_MAX, /* non-ABI */ }; @@ -785,6 +799,11 @@ enum perf_callchain_context { PERF_CONTEXT_MAX = (__u64)-4095, }; +/** + * PERF_RECORD_AUX::flags bits + */ +#define PERF_AUX_FLAG_TRUNCATED 0x01 /* record was truncated to fit */ + #define PERF_FLAG_FD_NO_GROUP (1UL << 0) #define PERF_FLAG_FD_OUTPUT (1UL << 1) #define PERF_FLAG_PID_CGROUP (1UL << 2) /* pid=cgroup id, per-cpu mode only */ diff --git a/kernel/events/core.c b/kernel/events/core.c index 6d9fdaef7b57..dbc2eff32230 100644 --- a/kernel/events/core.c +++ b/kernel/events/core.c @@ -5916,6 +5916,40 @@ void perf_event_mmap(struct vm_area_struct *vma) perf_event_mmap_event(&mmap_event); } +void perf_event_aux_event(struct perf_event *event, unsigned long head, + unsigned long size, u64 flags) +{ + struct perf_output_handle handle; + struct perf_sample_data sample; + struct perf_aux_event { + struct perf_event_header header; + u64 offset; + u64 size; + u64 flags; + } rec = { + .header = { + .type = PERF_RECORD_AUX, + .misc = 0, + .size = sizeof(rec), + }, + .offset = head, + .size = size, + .flags = flags, + }; + int ret; + + perf_event_header__init_id(&rec.header, &sample, event); + ret = perf_output_begin(&handle, event, rec.header.size); + + if (ret) + return; + + perf_output_put(&handle, rec); + perf_event__output_id_sample(event, &handle, &sample); + + perf_output_end(&handle); +} + /* * IRQ throttle logging */ diff --git a/kernel/events/internal.h b/kernel/events/internal.h index 0f6d08015927..4d117a981431 100644 --- a/kernel/events/internal.h +++ b/kernel/events/internal.h @@ -62,6 +62,9 @@ static inline bool rb_has_aux(struct ring_buffer *rb) return !!rb->aux_nr_pages; } +void perf_event_aux_event(struct perf_event *event, unsigned long head, + unsigned long size, u64 flags); + extern void perf_event_header__init_id(struct perf_event_header *header, struct perf_sample_data *data, -- cgit v1.2.3 From fdc2670666f40ab3e03143f04d1ebf4a05e2c24a Mon Sep 17 00:00:00 2001 From: Alexander Shishkin Date: Wed, 14 Jan 2015 14:18:16 +0200 Subject: perf: Add API for PMUs to write to the AUX area For pmus that wish to write data to ring buffer's AUX area, provide perf_aux_output_{begin,end}() calls to initiate/commit data writes, similarly to perf_output_{begin,end}. These also use the same output handle structure. Also, similarly to software counterparts, these will direct inherited events' output to parents' ring buffers. After the perf_aux_output_begin() returns successfully, handle->size is set to the maximum amount of data that can be written wrt aux_tail pointer, so that no data that the user hasn't seen will be overwritten, therefore this should always be called before hardware writing is enabled. On success, this will return the pointer to pmu driver's private structure allocated for this aux area by pmu::setup_aux. Same pointer can also be retrieved using perf_get_aux() while hardware writing is enabled. PMU driver should pass the actual amount of data written as a parameter to perf_aux_output_end(). All hardware writes should be completed and visible before this one is called. Additionally, perf_aux_output_skip() will adjust output handle and aux_head in case some part of the buffer has to be skipped over to maintain hardware's alignment constraints. Nested writers are forbidden and guards are in place to catch such attempts. Signed-off-by: Alexander Shishkin Signed-off-by: Peter Zijlstra (Intel) Cc: Borislav Petkov Cc: Frederic Weisbecker Cc: H. Peter Anvin Cc: Kaixu Xia Cc: Linus Torvalds Cc: Mike Galbraith Cc: Paul Mackerras Cc: Robert Richter Cc: Stephane Eranian Cc: Thomas Gleixner Cc: acme@infradead.org Cc: adrian.hunter@intel.com Cc: kan.liang@intel.com Cc: markus.t.metzger@intel.com Cc: mathieu.poirier@linaro.org Link: http://lkml.kernel.org/r/1421237903-181015-8-git-send-email-alexander.shishkin@linux.intel.com Signed-off-by: Ingo Molnar --- include/linux/perf_event.h | 24 +++++++- kernel/events/core.c | 5 +- kernel/events/internal.h | 4 ++ kernel/events/ring_buffer.c | 139 ++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 168 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/include/linux/perf_event.h b/include/linux/perf_event.h index f936a1e51f29..45c5873ad9b3 100644 --- a/include/linux/perf_event.h +++ b/include/linux/perf_event.h @@ -573,7 +573,10 @@ struct perf_output_handle { struct ring_buffer *rb; unsigned long wakeup; unsigned long size; - void *addr; + union { + void *addr; + unsigned long head; + }; int page; }; @@ -608,6 +611,14 @@ perf_cgroup_from_task(struct task_struct *task) #ifdef CONFIG_PERF_EVENTS +extern void *perf_aux_output_begin(struct perf_output_handle *handle, + struct perf_event *event); +extern void perf_aux_output_end(struct perf_output_handle *handle, + unsigned long size, bool truncated); +extern int perf_aux_output_skip(struct perf_output_handle *handle, + unsigned long size); +extern void *perf_get_aux(struct perf_output_handle *handle); + extern int perf_pmu_register(struct pmu *pmu, const char *name, int type); extern void perf_pmu_unregister(struct pmu *pmu); @@ -898,6 +909,17 @@ extern void perf_event_disable(struct perf_event *event); extern int __perf_event_disable(void *info); extern void perf_event_task_tick(void); #else /* !CONFIG_PERF_EVENTS: */ +static inline void * +perf_aux_output_begin(struct perf_output_handle *handle, + struct perf_event *event) { return NULL; } +static inline void +perf_aux_output_end(struct perf_output_handle *handle, unsigned long size, + bool truncated) { } +static inline int +perf_aux_output_skip(struct perf_output_handle *handle, + unsigned long size) { return -EINVAL; } +static inline void * +perf_get_aux(struct perf_output_handle *handle) { return NULL; } static inline void perf_event_task_sched_in(struct task_struct *prev, struct task_struct *task) { } diff --git a/kernel/events/core.c b/kernel/events/core.c index dbc2eff32230..81e8d14ac59a 100644 --- a/kernel/events/core.c +++ b/kernel/events/core.c @@ -3423,7 +3423,6 @@ static void free_event_rcu(struct rcu_head *head) kfree(event); } -static void ring_buffer_put(struct ring_buffer *rb); static void ring_buffer_attach(struct perf_event *event, struct ring_buffer *rb); @@ -4361,7 +4360,7 @@ static void rb_free_rcu(struct rcu_head *rcu_head) rb_free(rb); } -static struct ring_buffer *ring_buffer_get(struct perf_event *event) +struct ring_buffer *ring_buffer_get(struct perf_event *event) { struct ring_buffer *rb; @@ -4376,7 +4375,7 @@ static struct ring_buffer *ring_buffer_get(struct perf_event *event) return rb; } -static void ring_buffer_put(struct ring_buffer *rb) +void ring_buffer_put(struct ring_buffer *rb) { if (!atomic_dec_and_test(&rb->refcount)) return; diff --git a/kernel/events/internal.h b/kernel/events/internal.h index 4d117a981431..b701ebc32570 100644 --- a/kernel/events/internal.h +++ b/kernel/events/internal.h @@ -36,6 +36,8 @@ struct ring_buffer { struct user_struct *mmap_user; /* AUX area */ + local_t aux_head; + local_t aux_nest; unsigned long aux_pgoff; int aux_nr_pages; atomic_t aux_mmap_count; @@ -56,6 +58,8 @@ extern void perf_event_wakeup(struct perf_event *event); extern int rb_alloc_aux(struct ring_buffer *rb, struct perf_event *event, pgoff_t pgoff, int nr_pages, int flags); extern void rb_free_aux(struct ring_buffer *rb); +extern struct ring_buffer *ring_buffer_get(struct perf_event *event); +extern void ring_buffer_put(struct ring_buffer *rb); static inline bool rb_has_aux(struct ring_buffer *rb) { diff --git a/kernel/events/ring_buffer.c b/kernel/events/ring_buffer.c index 6e3be7a10c50..0cc7b0f39058 100644 --- a/kernel/events/ring_buffer.c +++ b/kernel/events/ring_buffer.c @@ -243,6 +243,145 @@ ring_buffer_init(struct ring_buffer *rb, long watermark, int flags) spin_lock_init(&rb->event_lock); } +/* + * This is called before hardware starts writing to the AUX area to + * obtain an output handle and make sure there's room in the buffer. + * When the capture completes, call perf_aux_output_end() to commit + * the recorded data to the buffer. + * + * The ordering is similar to that of perf_output_{begin,end}, with + * the exception of (B), which should be taken care of by the pmu + * driver, since ordering rules will differ depending on hardware. + */ +void *perf_aux_output_begin(struct perf_output_handle *handle, + struct perf_event *event) +{ + struct perf_event *output_event = event; + unsigned long aux_head, aux_tail; + struct ring_buffer *rb; + + if (output_event->parent) + output_event = output_event->parent; + + /* + * Since this will typically be open across pmu::add/pmu::del, we + * grab ring_buffer's refcount instead of holding rcu read lock + * to make sure it doesn't disappear under us. + */ + rb = ring_buffer_get(output_event); + if (!rb) + return NULL; + + if (!rb_has_aux(rb) || !atomic_inc_not_zero(&rb->aux_refcount)) + goto err; + + /* + * Nesting is not supported for AUX area, make sure nested + * writers are caught early + */ + if (WARN_ON_ONCE(local_xchg(&rb->aux_nest, 1))) + goto err_put; + + aux_head = local_read(&rb->aux_head); + aux_tail = ACCESS_ONCE(rb->user_page->aux_tail); + + handle->rb = rb; + handle->event = event; + handle->head = aux_head; + if (aux_head - aux_tail < perf_aux_size(rb)) + handle->size = CIRC_SPACE(aux_head, aux_tail, perf_aux_size(rb)); + else + handle->size = 0; + + /* + * handle->size computation depends on aux_tail load; this forms a + * control dependency barrier separating aux_tail load from aux data + * store that will be enabled on successful return + */ + if (!handle->size) { /* A, matches D */ + event->pending_disable = 1; + perf_output_wakeup(handle); + local_set(&rb->aux_nest, 0); + goto err_put; + } + + return handle->rb->aux_priv; + +err_put: + rb_free_aux(rb); + +err: + ring_buffer_put(rb); + handle->event = NULL; + + return NULL; +} + +/* + * Commit the data written by hardware into the ring buffer by adjusting + * aux_head and posting a PERF_RECORD_AUX into the perf buffer. It is the + * pmu driver's responsibility to observe ordering rules of the hardware, + * so that all the data is externally visible before this is called. + */ +void perf_aux_output_end(struct perf_output_handle *handle, unsigned long size, + bool truncated) +{ + struct ring_buffer *rb = handle->rb; + unsigned long aux_head = local_read(&rb->aux_head); + u64 flags = 0; + + if (truncated) + flags |= PERF_AUX_FLAG_TRUNCATED; + + local_add(size, &rb->aux_head); + + if (size || flags) { + /* + * Only send RECORD_AUX if we have something useful to communicate + */ + + perf_event_aux_event(handle->event, aux_head, size, flags); + } + + rb->user_page->aux_head = local_read(&rb->aux_head); + + perf_output_wakeup(handle); + handle->event = NULL; + + local_set(&rb->aux_nest, 0); + rb_free_aux(rb); + ring_buffer_put(rb); +} + +/* + * Skip over a given number of bytes in the AUX buffer, due to, for example, + * hardware's alignment constraints. + */ +int perf_aux_output_skip(struct perf_output_handle *handle, unsigned long size) +{ + struct ring_buffer *rb = handle->rb; + unsigned long aux_head; + + if (size > handle->size) + return -ENOSPC; + + local_add(size, &rb->aux_head); + + handle->head = aux_head; + handle->size -= size; + + return 0; +} + +void *perf_get_aux(struct perf_output_handle *handle) +{ + /* this is only valid between perf_aux_output_begin and *_end */ + if (!handle->event) + return NULL; + + return handle->rb->aux_priv; +} + #define PERF_AUX_GFP (GFP_KERNEL | __GFP_ZERO | __GFP_NOWARN | __GFP_NORETRY) static struct page *rb_alloc_aux_page(int node, int order) -- cgit v1.2.3 From 2023a0d2829e521fe6ad6b9907f3f90bfbf57142 Mon Sep 17 00:00:00 2001 From: Alexander Shishkin Date: Wed, 14 Jan 2015 14:18:17 +0200 Subject: perf: Support overwrite mode for the AUX area This adds support for overwrite mode in the AUX area, which means "keep collecting data till you're stopped", turning AUX area into a circular buffer, where new data overwrites old data. It does not depend on data buffer's overwrite mode, so that it doesn't lose sideband data that is instrumental for processing AUX data. Overwrite mode is enabled at mapping AUX area read only. Even though aux_tail in the buffer's user page might be user writable, it will be ignored in this mode. A PERF_RECORD_AUX with PERF_AUX_FLAG_OVERWRITE set is written to the perf data stream every time an event writes new data to the AUX area. The pmu driver might not be able to infer the exact beginning of the new data in each snapshot, some drivers will only provide the tail, which is aux_offset + aux_size in the AUX record. Consumer has to be able to tell the new data from the old one, for example, by means of time stamps if such are provided in the trace. Consumer is also responsible for disabling any events that might write to the AUX area (thus potentially racing with the consumer) before collecting the data. Signed-off-by: Alexander Shishkin Signed-off-by: Peter Zijlstra (Intel) Cc: Borislav Petkov Cc: Frederic Weisbecker Cc: H. Peter Anvin Cc: Kaixu Xia Cc: Linus Torvalds Cc: Mike Galbraith Cc: Paul Mackerras Cc: Robert Richter Cc: Stephane Eranian Cc: Thomas Gleixner Cc: acme@infradead.org Cc: adrian.hunter@intel.com Cc: kan.liang@intel.com Cc: markus.t.metzger@intel.com Cc: mathieu.poirier@linaro.org Link: http://lkml.kernel.org/r/1421237903-181015-9-git-send-email-alexander.shishkin@linux.intel.com Signed-off-by: Ingo Molnar --- include/uapi/linux/perf_event.h | 1 + kernel/events/internal.h | 1 + kernel/events/ring_buffer.c | 48 ++++++++++++++++++++++++++++------------- 3 files changed, 35 insertions(+), 15 deletions(-) (limited to 'include') diff --git a/include/uapi/linux/perf_event.h b/include/uapi/linux/perf_event.h index 8904ad3a850b..29ef2f73bb4a 100644 --- a/include/uapi/linux/perf_event.h +++ b/include/uapi/linux/perf_event.h @@ -803,6 +803,7 @@ enum perf_callchain_context { * PERF_RECORD_AUX::flags bits */ #define PERF_AUX_FLAG_TRUNCATED 0x01 /* record was truncated to fit */ +#define PERF_AUX_FLAG_OVERWRITE 0x02 /* snapshot from overwrite mode */ #define PERF_FLAG_FD_NO_GROUP (1UL << 0) #define PERF_FLAG_FD_OUTPUT (1UL << 1) diff --git a/kernel/events/internal.h b/kernel/events/internal.h index b701ebc32570..ffd51d9f5945 100644 --- a/kernel/events/internal.h +++ b/kernel/events/internal.h @@ -40,6 +40,7 @@ struct ring_buffer { local_t aux_nest; unsigned long aux_pgoff; int aux_nr_pages; + int aux_overwrite; atomic_t aux_mmap_count; unsigned long aux_mmap_locked; void (*free_aux)(void *); diff --git a/kernel/events/ring_buffer.c b/kernel/events/ring_buffer.c index 0cc7b0f39058..67b328337a41 100644 --- a/kernel/events/ring_buffer.c +++ b/kernel/events/ring_buffer.c @@ -283,26 +283,33 @@ void *perf_aux_output_begin(struct perf_output_handle *handle, goto err_put; aux_head = local_read(&rb->aux_head); - aux_tail = ACCESS_ONCE(rb->user_page->aux_tail); handle->rb = rb; handle->event = event; handle->head = aux_head; - if (aux_head - aux_tail < perf_aux_size(rb)) - handle->size = CIRC_SPACE(aux_head, aux_tail, perf_aux_size(rb)); - else - handle->size = 0; + handle->size = 0; /* - * handle->size computation depends on aux_tail load; this forms a - * control dependency barrier separating aux_tail load from aux data - * store that will be enabled on successful return + * In overwrite mode, AUX data stores do not depend on aux_tail, + * therefore (A) control dependency barrier does not exist. The + * (B) <-> (C) ordering is still observed by the pmu driver. */ - if (!handle->size) { /* A, matches D */ - event->pending_disable = 1; - perf_output_wakeup(handle); - local_set(&rb->aux_nest, 0); - goto err_put; + if (!rb->aux_overwrite) { + aux_tail = ACCESS_ONCE(rb->user_page->aux_tail); + if (aux_head - aux_tail < perf_aux_size(rb)) + handle->size = CIRC_SPACE(aux_head, aux_tail, perf_aux_size(rb)); + + /* + * handle->size computation depends on aux_tail load; this forms a + * control dependency barrier separating aux_tail load from aux data + * store that will be enabled on successful return + */ + if (!handle->size) { /* A, matches D */ + event->pending_disable = 1; + perf_output_wakeup(handle); + local_set(&rb->aux_nest, 0); + goto err_put; + } } return handle->rb->aux_priv; @@ -327,13 +334,22 @@ void perf_aux_output_end(struct perf_output_handle *handle, unsigned long size, bool truncated) { struct ring_buffer *rb = handle->rb; - unsigned long aux_head = local_read(&rb->aux_head); + unsigned long aux_head; u64 flags = 0; if (truncated) flags |= PERF_AUX_FLAG_TRUNCATED; - local_add(size, &rb->aux_head); + /* in overwrite mode, driver provides aux_head via handle */ + if (rb->aux_overwrite) { + flags |= PERF_AUX_FLAG_OVERWRITE; + + aux_head = handle->head; + local_set(&rb->aux_head, aux_head); + } else { + aux_head = local_read(&rb->aux_head); + local_add(size, &rb->aux_head); + } if (size || flags) { /* @@ -480,6 +496,8 @@ int rb_alloc_aux(struct ring_buffer *rb, struct perf_event *event, */ atomic_set(&rb->aux_refcount, 1); + rb->aux_overwrite = overwrite; + out: if (!ret) rb->aux_pgoff = pgoff; -- cgit v1.2.3 From 1a5941312414c71dece6717da9a0fa1303127afa Mon Sep 17 00:00:00 2001 From: Alexander Shishkin Date: Wed, 14 Jan 2015 14:18:18 +0200 Subject: perf: Add wakeup watermark control to the AUX area When AUX area gets a certain amount of new data, we want to wake up userspace to collect it. This adds a new control to specify how much data will cause a wakeup. This is then passed down to pmu drivers via output handle's "wakeup" field, so that the driver can find the nearest point where it can generate an interrupt. We repurpose __reserved_2 in the event attribute for this, even though it was never checked to be zero before, aux_watermark will only matter for new AUX-aware code, so the old code should still be fine. Signed-off-by: Alexander Shishkin Signed-off-by: Peter Zijlstra (Intel) Cc: Borislav Petkov Cc: Frederic Weisbecker Cc: H. Peter Anvin Cc: Kaixu Xia Cc: Linus Torvalds Cc: Mike Galbraith Cc: Paul Mackerras Cc: Robert Richter Cc: Stephane Eranian Cc: Thomas Gleixner Cc: acme@infradead.org Cc: adrian.hunter@intel.com Cc: kan.liang@intel.com Cc: markus.t.metzger@intel.com Cc: mathieu.poirier@linaro.org Link: http://lkml.kernel.org/r/1421237903-181015-10-git-send-email-alexander.shishkin@linux.intel.com Signed-off-by: Ingo Molnar --- include/uapi/linux/perf_event.h | 7 +++++++ kernel/events/core.c | 3 ++- kernel/events/internal.h | 4 +++- kernel/events/ring_buffer.c | 22 +++++++++++++++++++--- 4 files changed, 31 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/include/uapi/linux/perf_event.h b/include/uapi/linux/perf_event.h index 29ef2f73bb4a..84819546c8ce 100644 --- a/include/uapi/linux/perf_event.h +++ b/include/uapi/linux/perf_event.h @@ -261,6 +261,7 @@ enum perf_event_read_format { #define PERF_ATTR_SIZE_VER3 96 /* add: sample_regs_user */ /* add: sample_stack_user */ #define PERF_ATTR_SIZE_VER4 104 /* add: sample_regs_intr */ +#define PERF_ATTR_SIZE_VER5 112 /* add: aux_watermark */ /* * Hardware event_id to monitor via a performance monitoring event: @@ -366,6 +367,12 @@ struct perf_event_attr { * See asm/perf_regs.h for details. */ __u64 sample_regs_intr; + + /* + * Wakeup watermark for AUX area + */ + __u32 aux_watermark; + __u32 __reserved_2; /* align to __u64 */ }; #define perf_flags(attr) (*(&(attr)->read_format + 1)) diff --git a/kernel/events/core.c b/kernel/events/core.c index 81e8d14ac59a..31f6b504ad62 100644 --- a/kernel/events/core.c +++ b/kernel/events/core.c @@ -4677,7 +4677,8 @@ accounting: perf_event_init_userpage(event); perf_event_update_userpage(event); } else { - ret = rb_alloc_aux(rb, event, vma->vm_pgoff, nr_pages, flags); + ret = rb_alloc_aux(rb, event, vma->vm_pgoff, nr_pages, + event->attr.aux_watermark, flags); if (!ret) rb->aux_mmap_locked = extra; } diff --git a/kernel/events/internal.h b/kernel/events/internal.h index ffd51d9f5945..9f6ce9ba4a04 100644 --- a/kernel/events/internal.h +++ b/kernel/events/internal.h @@ -27,6 +27,7 @@ struct ring_buffer { local_t lost; /* nr records lost */ long watermark; /* wakeup watermark */ + long aux_watermark; /* poll crap */ spinlock_t event_lock; struct list_head event_list; @@ -38,6 +39,7 @@ struct ring_buffer { /* AUX area */ local_t aux_head; local_t aux_nest; + local_t aux_wakeup; unsigned long aux_pgoff; int aux_nr_pages; int aux_overwrite; @@ -57,7 +59,7 @@ extern struct ring_buffer * rb_alloc(int nr_pages, long watermark, int cpu, int flags); extern void perf_event_wakeup(struct perf_event *event); extern int rb_alloc_aux(struct ring_buffer *rb, struct perf_event *event, - pgoff_t pgoff, int nr_pages, int flags); + pgoff_t pgoff, int nr_pages, long watermark, int flags); extern void rb_free_aux(struct ring_buffer *rb); extern struct ring_buffer *ring_buffer_get(struct perf_event *event); extern void ring_buffer_put(struct ring_buffer *rb); diff --git a/kernel/events/ring_buffer.c b/kernel/events/ring_buffer.c index 67b328337a41..232f00f273cb 100644 --- a/kernel/events/ring_buffer.c +++ b/kernel/events/ring_buffer.c @@ -296,6 +296,7 @@ void *perf_aux_output_begin(struct perf_output_handle *handle, */ if (!rb->aux_overwrite) { aux_tail = ACCESS_ONCE(rb->user_page->aux_tail); + handle->wakeup = local_read(&rb->aux_wakeup) + rb->aux_watermark; if (aux_head - aux_tail < perf_aux_size(rb)) handle->size = CIRC_SPACE(aux_head, aux_tail, perf_aux_size(rb)); @@ -359,9 +360,12 @@ void perf_aux_output_end(struct perf_output_handle *handle, unsigned long size, perf_event_aux_event(handle->event, aux_head, size, flags); } - rb->user_page->aux_head = local_read(&rb->aux_head); + aux_head = rb->user_page->aux_head = local_read(&rb->aux_head); - perf_output_wakeup(handle); + if (aux_head - local_read(&rb->aux_wakeup) >= rb->aux_watermark) { + perf_output_wakeup(handle); + local_add(rb->aux_watermark, &rb->aux_wakeup); + } handle->event = NULL; local_set(&rb->aux_nest, 0); @@ -383,6 +387,14 @@ int perf_aux_output_skip(struct perf_output_handle *handle, unsigned long size) local_add(size, &rb->aux_head); + aux_head = rb->user_page->aux_head = local_read(&rb->aux_head); + if (aux_head - local_read(&rb->aux_wakeup) >= rb->aux_watermark) { + perf_output_wakeup(handle); + local_add(rb->aux_watermark, &rb->aux_wakeup); + handle->wakeup = local_read(&rb->aux_wakeup) + + rb->aux_watermark; + } + handle->head = aux_head; handle->size -= size; @@ -433,7 +445,7 @@ static void rb_free_aux_page(struct ring_buffer *rb, int idx) } int rb_alloc_aux(struct ring_buffer *rb, struct perf_event *event, - pgoff_t pgoff, int nr_pages, int flags) + pgoff_t pgoff, int nr_pages, long watermark, int flags) { bool overwrite = !(flags & RING_BUFFER_WRITABLE); int node = (event->cpu == -1) ? -1 : cpu_to_node(event->cpu); @@ -497,6 +509,10 @@ int rb_alloc_aux(struct ring_buffer *rb, struct perf_event *event, atomic_set(&rb->aux_refcount, 1); rb->aux_overwrite = overwrite; + rb->aux_watermark = watermark; + + if (!rb->aux_watermark && !rb->aux_overwrite) + rb->aux_watermark = nr_pages << (PAGE_SHIFT - 1); out: if (!ret) -- cgit v1.2.3 From ec0d7729bbaed4b9d2d3fada693278e13a3d1368 Mon Sep 17 00:00:00 2001 From: Alexander Shishkin Date: Wed, 14 Jan 2015 14:18:23 +0200 Subject: perf: Add ITRACE_START record to indicate that tracing has started For counters that generate AUX data that is bound to the context of a running task, such as instruction tracing, the decoder needs to know exactly which task is running when the event is first scheduled in, before the first sched_switch. The decoder's need to know this stems from the fact that instruction flow trace decoding will almost always require program's object code in order to reconstruct said flow and for that we need at least its pid/tid in the perf stream. To single out such instruction tracing pmus, this patch introduces ITRACE PMU capability. The reason this is not part of RECORD_AUX record is that not all pmus capable of generating AUX data need this, and the opposite is *probably* also true. While sched_switch covers for most cases, there are two problems with it: the consumer will need to process events out of order (that is, having found RECORD_AUX, it will have to skip forward to the nearest sched_switch to figure out which task it was, then go back to the actual trace to decode it) and it completely misses the case when the tracing is enabled and disabled before sched_switch, for example, via PERF_EVENT_IOC_DISABLE. Signed-off-by: Alexander Shishkin Signed-off-by: Peter Zijlstra (Intel) Cc: Borislav Petkov Cc: Frederic Weisbecker Cc: H. Peter Anvin Cc: Kaixu Xia Cc: Linus Torvalds Cc: Mike Galbraith Cc: Paul Mackerras Cc: Robert Richter Cc: Stephane Eranian Cc: Thomas Gleixner Cc: acme@infradead.org Cc: adrian.hunter@intel.com Cc: kan.liang@intel.com Cc: markus.t.metzger@intel.com Cc: mathieu.poirier@linaro.org Link: http://lkml.kernel.org/r/1421237903-181015-15-git-send-email-alexander.shishkin@linux.intel.com Signed-off-by: Ingo Molnar --- include/linux/perf_event.h | 4 ++++ include/uapi/linux/perf_event.h | 11 +++++++++++ kernel/events/core.c | 41 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 56 insertions(+) (limited to 'include') diff --git a/include/linux/perf_event.h b/include/linux/perf_event.h index 45c5873ad9b3..61992cf2e977 100644 --- a/include/linux/perf_event.h +++ b/include/linux/perf_event.h @@ -129,6 +129,9 @@ struct hw_perf_event { struct list_head cqm_groups_entry; struct list_head cqm_group_entry; }; + struct { /* itrace */ + int itrace_started; + }; #ifdef CONFIG_HAVE_HW_BREAKPOINT struct { /* breakpoint */ /* @@ -177,6 +180,7 @@ struct perf_event; #define PERF_PMU_CAP_AUX_NO_SG 0x04 #define PERF_PMU_CAP_AUX_SW_DOUBLEBUF 0x08 #define PERF_PMU_CAP_EXCLUSIVE 0x10 +#define PERF_PMU_CAP_ITRACE 0x20 /** * struct pmu - generic performance monitoring unit diff --git a/include/uapi/linux/perf_event.h b/include/uapi/linux/perf_event.h index 84819546c8ce..309211b3eb67 100644 --- a/include/uapi/linux/perf_event.h +++ b/include/uapi/linux/perf_event.h @@ -789,6 +789,17 @@ enum perf_event_type { */ PERF_RECORD_AUX = 11, + /* + * Indicates that instruction trace has started + * + * struct { + * struct perf_event_header header; + * u32 pid; + * u32 tid; + * }; + */ + PERF_RECORD_ITRACE_START = 12, + PERF_RECORD_MAX, /* non-ABI */ }; diff --git a/kernel/events/core.c b/kernel/events/core.c index 31f6b504ad62..06917d537302 100644 --- a/kernel/events/core.c +++ b/kernel/events/core.c @@ -1831,6 +1831,7 @@ static void perf_set_shadow_time(struct perf_event *event, #define MAX_INTERRUPTS (~0ULL) static void perf_log_throttle(struct perf_event *event, int enable); +static void perf_log_itrace_start(struct perf_event *event); static int event_sched_in(struct perf_event *event, @@ -1869,6 +1870,8 @@ event_sched_in(struct perf_event *event, perf_set_shadow_time(event, ctx, tstamp); + perf_log_itrace_start(event); + if (event->pmu->add(event, PERF_EF_START)) { event->state = PERF_EVENT_STATE_INACTIVE; event->oncpu = -1; @@ -5991,6 +5994,44 @@ static void perf_log_throttle(struct perf_event *event, int enable) perf_output_end(&handle); } +static void perf_log_itrace_start(struct perf_event *event) +{ + struct perf_output_handle handle; + struct perf_sample_data sample; + struct perf_aux_event { + struct perf_event_header header; + u32 pid; + u32 tid; + } rec; + int ret; + + if (event->parent) + event = event->parent; + + if (!(event->pmu->capabilities & PERF_PMU_CAP_ITRACE) || + event->hw.itrace_started) + return; + + event->hw.itrace_started = 1; + + rec.header.type = PERF_RECORD_ITRACE_START; + rec.header.misc = 0; + rec.header.size = sizeof(rec); + rec.pid = perf_event_pid(event, current); + rec.tid = perf_event_tid(event, current); + + perf_event_header__init_id(&rec.header, &sample, event); + ret = perf_output_begin(&handle, event, rec.header.size); + + if (ret) + return; + + perf_output_put(&handle, rec); + perf_event__output_id_sample(event, &handle, &sample); + + perf_output_end(&handle); +} + /* * Generic event overflow handling, sampling. */ -- cgit v1.2.3 From b3738d29323344da3017a91010530cf3a58590fc Mon Sep 17 00:00:00 2001 From: Stephane Eranian Date: Mon, 17 Nov 2014 20:07:03 +0100 Subject: watchdog: Add watchdog enable/disable all functions This patch adds two new functions to enable/disable the watchdog across all CPUs. This will be used by the HT PMU bug workaround code to disable/enable the NMI watchdog across quirk enablement. Signed-off-by: Stephane Eranian Signed-off-by: Peter Zijlstra (Intel) Cc: bp@alien8.de Cc: jolsa@redhat.com Cc: kan.liang@intel.com Cc: maria.n.dimakopoulou@gmail.com Cc: Frederic Weisbecker Cc: Don Zickus Cc: Andrew Morton Link: http://lkml.kernel.org/r/1416251225-17721-12-git-send-email-eranian@google.com Signed-off-by: Ingo Molnar --- include/linux/watchdog.h | 8 ++++++++ kernel/watchdog.c | 28 ++++++++++++++++++++++++++++ 2 files changed, 36 insertions(+) (limited to 'include') diff --git a/include/linux/watchdog.h b/include/linux/watchdog.h index 395b70e0eccf..a746bf5216f8 100644 --- a/include/linux/watchdog.h +++ b/include/linux/watchdog.h @@ -137,4 +137,12 @@ extern int watchdog_init_timeout(struct watchdog_device *wdd, extern int watchdog_register_device(struct watchdog_device *); extern void watchdog_unregister_device(struct watchdog_device *); +#ifdef CONFIG_HARDLOCKUP_DETECTOR +void watchdog_nmi_disable_all(void); +void watchdog_nmi_enable_all(void); +#else +static inline void watchdog_nmi_disable_all(void) {} +static inline void watchdog_nmi_enable_all(void) {} +#endif + #endif /* ifndef _LINUX_WATCHDOG_H */ diff --git a/kernel/watchdog.c b/kernel/watchdog.c index 3174bf8e3538..9a056f5bc02c 100644 --- a/kernel/watchdog.c +++ b/kernel/watchdog.c @@ -567,9 +567,37 @@ static void watchdog_nmi_disable(unsigned int cpu) cpu0_err = 0; } } + +void watchdog_nmi_enable_all(void) +{ + int cpu; + + if (!watchdog_user_enabled) + return; + + get_online_cpus(); + for_each_online_cpu(cpu) + watchdog_nmi_enable(cpu); + put_online_cpus(); +} + +void watchdog_nmi_disable_all(void) +{ + int cpu; + + if (!watchdog_running) + return; + + get_online_cpus(); + for_each_online_cpu(cpu) + watchdog_nmi_disable(cpu); + put_online_cpus(); +} #else static int watchdog_nmi_enable(unsigned int cpu) { return 0; } static void watchdog_nmi_disable(unsigned int cpu) { return; } +void watchdog_nmi_enable_all(void) {} +void watchdog_nmi_disable_all(void) {} #endif /* CONFIG_HARDLOCKUP_DETECTOR */ static struct smp_hotplug_thread watchdog_threads = { -- cgit v1.2.3 From b4a20144e0c0a45431695fa5968ce2ed8c9ce6ca Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Wed, 28 Jan 2015 14:29:02 +0100 Subject: gpu: host1x: Export host1x_syncpt_read() This function is used to read the current value of the syncpt and is useful in situations where drivers don't schedule work and wait for the syncpoint to increment. One particular use-case is using the syncpoint as a VBLANK counter. Signed-off-by: Thierry Reding --- drivers/gpu/host1x/syncpt.c | 6 ++++++ include/linux/host1x.h | 1 + 2 files changed, 7 insertions(+) (limited to 'include') diff --git a/drivers/gpu/host1x/syncpt.c b/drivers/gpu/host1x/syncpt.c index b10550ee1d89..6b7fdc1e2ed0 100644 --- a/drivers/gpu/host1x/syncpt.c +++ b/drivers/gpu/host1x/syncpt.c @@ -425,6 +425,12 @@ u32 host1x_syncpt_read_min(struct host1x_syncpt *sp) } EXPORT_SYMBOL(host1x_syncpt_read_min); +u32 host1x_syncpt_read(struct host1x_syncpt *sp) +{ + return host1x_syncpt_load(sp); +} +EXPORT_SYMBOL(host1x_syncpt_read); + int host1x_syncpt_nb_pts(struct host1x *host) { return host->info->nb_pts; diff --git a/include/linux/host1x.h b/include/linux/host1x.h index 464f33814a94..d2ba7d334039 100644 --- a/include/linux/host1x.h +++ b/include/linux/host1x.h @@ -135,6 +135,7 @@ struct host1x_syncpt *host1x_syncpt_get(struct host1x *host, u32 id); u32 host1x_syncpt_id(struct host1x_syncpt *sp); u32 host1x_syncpt_read_min(struct host1x_syncpt *sp); u32 host1x_syncpt_read_max(struct host1x_syncpt *sp); +u32 host1x_syncpt_read(struct host1x_syncpt *sp); int host1x_syncpt_incr(struct host1x_syncpt *sp); u32 host1x_syncpt_incr_max(struct host1x_syncpt *sp, u32 incrs); int host1x_syncpt_wait(struct host1x_syncpt *sp, u32 thresh, long timeout, -- cgit v1.2.3 From f5e7840b0c4368f8cdbb055188c2a0eef50c3052 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Wed, 28 Jan 2015 14:54:32 +0100 Subject: drm/atomic: Add helpers for state-subclassing drivers Drivers that subclass CRTC, plane or connector state need to carefully duplicate the code that the atomic helpers have. This is bound to cause breakage eventually because it requires auditing all drivers and update them when code is added to the helpers. In order to avoid that, implement new helpers that perform the required steps when copying and destroying state. These new helpers are exported so that state-subclassing drivers can use them. The default helpers are implemented using them as well, providing a single location that needs to be changed when adding to base atomic states. Reviewed-by: Daniel Vetter Reviewed-by: Eric Anholt Signed-off-by: Thierry Reding --- drivers/gpu/drm/drm_atomic_helper.c | 144 ++++++++++++++++++++++++++++++++---- include/drm/drm_atomic_helper.h | 14 ++++ 2 files changed, 142 insertions(+), 16 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c index 7e3a52b97c7d..60d28a18f0a3 100644 --- a/drivers/gpu/drm/drm_atomic_helper.c +++ b/drivers/gpu/drm/drm_atomic_helper.c @@ -2049,6 +2049,26 @@ void drm_atomic_helper_crtc_reset(struct drm_crtc *crtc) } EXPORT_SYMBOL(drm_atomic_helper_crtc_reset); +/** + * __drm_atomic_helper_crtc_duplicate_state - copy atomic CRTC state + * @crtc: CRTC object + * @state: atomic CRTC state + * + * Copies atomic state from a CRTC's current state and resets inferred values. + * This is useful for drivers that subclass the CRTC state. + */ +void __drm_atomic_helper_crtc_duplicate_state(struct drm_crtc *crtc, + struct drm_crtc_state *state) +{ + memcpy(state, crtc->state, sizeof(*state)); + + state->mode_changed = false; + state->active_changed = false; + state->planes_changed = false; + state->event = NULL; +} +EXPORT_SYMBOL(__drm_atomic_helper_crtc_duplicate_state); + /** * drm_atomic_helper_crtc_duplicate_state - default state duplicate hook * @crtc: drm CRTC @@ -2064,19 +2084,34 @@ drm_atomic_helper_crtc_duplicate_state(struct drm_crtc *crtc) if (WARN_ON(!crtc->state)) return NULL; - state = kmemdup(crtc->state, sizeof(*crtc->state), GFP_KERNEL); - - if (state) { - state->mode_changed = false; - state->active_changed = false; - state->planes_changed = false; - state->event = NULL; - } + state = kmalloc(sizeof(*state), GFP_KERNEL); + if (state) + __drm_atomic_helper_crtc_duplicate_state(crtc, state); return state; } EXPORT_SYMBOL(drm_atomic_helper_crtc_duplicate_state); +/** + * __drm_atomic_helper_crtc_destroy_state - release CRTC state + * @crtc: CRTC object + * @state: CRTC state object to release + * + * Releases all resources stored in the CRTC state without actually freeing + * the memory of the CRTC state. This is useful for drivers that subclass the + * CRTC state. + */ +void __drm_atomic_helper_crtc_destroy_state(struct drm_crtc *crtc, + struct drm_crtc_state *state) +{ + /* + * This is currently a placeholder so that drivers that subclass the + * state will automatically do the right thing if code is ever added + * to this function. + */ +} +EXPORT_SYMBOL(__drm_atomic_helper_crtc_destroy_state); + /** * drm_atomic_helper_crtc_destroy_state - default state destroy hook * @crtc: drm CRTC @@ -2088,6 +2123,7 @@ EXPORT_SYMBOL(drm_atomic_helper_crtc_duplicate_state); void drm_atomic_helper_crtc_destroy_state(struct drm_crtc *crtc, struct drm_crtc_state *state) { + __drm_atomic_helper_crtc_destroy_state(crtc, state); kfree(state); } EXPORT_SYMBOL(drm_atomic_helper_crtc_destroy_state); @@ -2112,6 +2148,24 @@ void drm_atomic_helper_plane_reset(struct drm_plane *plane) } EXPORT_SYMBOL(drm_atomic_helper_plane_reset); +/** + * __drm_atomic_helper_plane_duplicate_state - copy atomic plane state + * @plane: plane object + * @state: atomic plane state + * + * Copies atomic state from a plane's current state. This is useful for + * drivers that subclass the plane state. + */ +void __drm_atomic_helper_plane_duplicate_state(struct drm_plane *plane, + struct drm_plane_state *state) +{ + memcpy(state, plane->state, sizeof(*state)); + + if (state->fb) + drm_framebuffer_reference(state->fb); +} +EXPORT_SYMBOL(__drm_atomic_helper_plane_duplicate_state); + /** * drm_atomic_helper_plane_duplicate_state - default state duplicate hook * @plane: drm plane @@ -2127,15 +2181,31 @@ drm_atomic_helper_plane_duplicate_state(struct drm_plane *plane) if (WARN_ON(!plane->state)) return NULL; - state = kmemdup(plane->state, sizeof(*plane->state), GFP_KERNEL); - - if (state && state->fb) - drm_framebuffer_reference(state->fb); + state = kmalloc(sizeof(*state), GFP_KERNEL); + if (state) + __drm_atomic_helper_plane_duplicate_state(plane, state); return state; } EXPORT_SYMBOL(drm_atomic_helper_plane_duplicate_state); +/** + * __drm_atomic_helper_plane_destroy_state - release plane state + * @plane: plane object + * @state: plane state object to release + * + * Releases all resources stored in the plane state without actually freeing + * the memory of the plane state. This is useful for drivers that subclass the + * plane state. + */ +void __drm_atomic_helper_plane_destroy_state(struct drm_plane *plane, + struct drm_plane_state *state) +{ + if (state->fb) + drm_framebuffer_unreference(state->fb); +} +EXPORT_SYMBOL(__drm_atomic_helper_plane_destroy_state); + /** * drm_atomic_helper_plane_destroy_state - default state destroy hook * @plane: drm plane @@ -2147,9 +2217,7 @@ EXPORT_SYMBOL(drm_atomic_helper_plane_duplicate_state); void drm_atomic_helper_plane_destroy_state(struct drm_plane *plane, struct drm_plane_state *state) { - if (state->fb) - drm_framebuffer_unreference(state->fb); - + __drm_atomic_helper_plane_destroy_state(plane, state); kfree(state); } EXPORT_SYMBOL(drm_atomic_helper_plane_destroy_state); @@ -2172,6 +2240,22 @@ void drm_atomic_helper_connector_reset(struct drm_connector *connector) } EXPORT_SYMBOL(drm_atomic_helper_connector_reset); +/** + * __drm_atomic_helper_connector_duplicate_state - copy atomic connector state + * @connector: connector object + * @state: atomic connector state + * + * Copies atomic state from a connector's current state. This is useful for + * drivers that subclass the connector state. + */ +void +__drm_atomic_helper_connector_duplicate_state(struct drm_connector *connector, + struct drm_connector_state *state) +{ + memcpy(state, connector->state, sizeof(*state)); +} +EXPORT_SYMBOL(__drm_atomic_helper_connector_duplicate_state); + /** * drm_atomic_helper_connector_duplicate_state - default state duplicate hook * @connector: drm connector @@ -2182,13 +2266,40 @@ EXPORT_SYMBOL(drm_atomic_helper_connector_reset); struct drm_connector_state * drm_atomic_helper_connector_duplicate_state(struct drm_connector *connector) { + struct drm_connector_state *state; + if (WARN_ON(!connector->state)) return NULL; - return kmemdup(connector->state, sizeof(*connector->state), GFP_KERNEL); + state = kmalloc(sizeof(*state), GFP_KERNEL); + if (state) + __drm_atomic_helper_connector_duplicate_state(connector, state); + + return state; } EXPORT_SYMBOL(drm_atomic_helper_connector_duplicate_state); +/** + * __drm_atomic_helper_connector_destroy_state - release connector state + * @connector: connector object + * @state: connector state object to release + * + * Releases all resources stored in the connector state without actually + * freeing the memory of the connector state. This is useful for drivers that + * subclass the connector state. + */ +void +__drm_atomic_helper_connector_destroy_state(struct drm_connector *connector, + struct drm_connector_state *state) +{ + /* + * This is currently a placeholder so that drivers that subclass the + * state will automatically do the right thing if code is ever added + * to this function. + */ +} +EXPORT_SYMBOL(__drm_atomic_helper_connector_destroy_state); + /** * drm_atomic_helper_connector_destroy_state - default state destroy hook * @connector: drm connector @@ -2200,6 +2311,7 @@ EXPORT_SYMBOL(drm_atomic_helper_connector_duplicate_state); void drm_atomic_helper_connector_destroy_state(struct drm_connector *connector, struct drm_connector_state *state) { + __drm_atomic_helper_connector_destroy_state(connector, state); kfree(state); } EXPORT_SYMBOL(drm_atomic_helper_connector_destroy_state); diff --git a/include/drm/drm_atomic_helper.h b/include/drm/drm_atomic_helper.h index 8039d54a7441..e2086372ef61 100644 --- a/include/drm/drm_atomic_helper.h +++ b/include/drm/drm_atomic_helper.h @@ -87,20 +87,34 @@ void drm_atomic_helper_connector_dpms(struct drm_connector *connector, /* default implementations for state handling */ void drm_atomic_helper_crtc_reset(struct drm_crtc *crtc); +void __drm_atomic_helper_crtc_duplicate_state(struct drm_crtc *crtc, + struct drm_crtc_state *state); struct drm_crtc_state * drm_atomic_helper_crtc_duplicate_state(struct drm_crtc *crtc); +void __drm_atomic_helper_crtc_destroy_state(struct drm_crtc *crtc, + struct drm_crtc_state *state); void drm_atomic_helper_crtc_destroy_state(struct drm_crtc *crtc, struct drm_crtc_state *state); void drm_atomic_helper_plane_reset(struct drm_plane *plane); +void __drm_atomic_helper_plane_duplicate_state(struct drm_plane *plane, + struct drm_plane_state *state); struct drm_plane_state * drm_atomic_helper_plane_duplicate_state(struct drm_plane *plane); +void __drm_atomic_helper_plane_destroy_state(struct drm_plane *plane, + struct drm_plane_state *state); void drm_atomic_helper_plane_destroy_state(struct drm_plane *plane, struct drm_plane_state *state); void drm_atomic_helper_connector_reset(struct drm_connector *connector); +void +__drm_atomic_helper_connector_duplicate_state(struct drm_connector *connector, + struct drm_connector_state *state); struct drm_connector_state * drm_atomic_helper_connector_duplicate_state(struct drm_connector *connector); +void +__drm_atomic_helper_connector_destroy_state(struct drm_connector *connector, + struct drm_connector_state *state); void drm_atomic_helper_connector_destroy_state(struct drm_connector *connector, struct drm_connector_state *state); -- cgit v1.2.3 From bdf765071a8b573f7b1ba14c02881fa3e623825e Mon Sep 17 00:00:00 2001 From: Sean Paul Date: Fri, 30 Jan 2015 13:57:01 -0500 Subject: drm/tegra: gem: Return 64-bit offset for mmap(2) On 64-bit targets, tegra_gem_mmap() only returns a partial offset to userspace. As such, subsequent calls to mmap(2) may fail. Change the arguments to use a 64-bit offset to fix this. Signed-off-by: Sean Paul Acked-by: Erik Faye-Lund [treding@nvidia.com: tweak commit message] Signed-off-by: Thierry Reding --- include/uapi/drm/tegra_drm.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/uapi/drm/tegra_drm.h b/include/uapi/drm/tegra_drm.h index c15d781ecc0f..5391780c2b05 100644 --- a/include/uapi/drm/tegra_drm.h +++ b/include/uapi/drm/tegra_drm.h @@ -36,7 +36,8 @@ struct drm_tegra_gem_create { struct drm_tegra_gem_mmap { __u32 handle; - __u32 offset; + __u32 pad; + __u64 offset; }; struct drm_tegra_syncpt_read { -- cgit v1.2.3 From 2938931f3732a4ec70c299e221ec8dea01dfd796 Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Thu, 11 Dec 2014 18:32:44 +0100 Subject: drm/panel: Add display timing support Many panel data sheets, additionally to typical values, list allowed ranges for timings such as hsync/vsync lengths, porches, and the pixel clock rate. These can be stored in a struct display_timing, to be used by an encoder mode_fixup callback to clamp user provided timing values or to validate workarounds for clock source limitations. This patch adds a new drm_panel_funcs callback that returns the panel's available display_timing entries. Signed-off-by: Philipp Zabel Signed-off-by: Thierry Reding --- include/drm/drm_panel.h | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'include') diff --git a/include/drm/drm_panel.h b/include/drm/drm_panel.h index 1fbcc96063a7..13ff44b28893 100644 --- a/include/drm/drm_panel.h +++ b/include/drm/drm_panel.h @@ -29,6 +29,7 @@ struct drm_connector; struct drm_device; struct drm_panel; +struct display_timing; /** * struct drm_panel_funcs - perform operations on a given panel @@ -38,6 +39,8 @@ struct drm_panel; * @enable: enable panel (turn on back light, etc.) * @get_modes: add modes to the connector that the panel is attached to and * return the number of modes added + * @get_timings: copy display timings into the provided array and return + * the number of display timings available * * The .prepare() function is typically called before the display controller * starts to transmit video data. Panel drivers can use this to turn the panel @@ -68,6 +71,8 @@ struct drm_panel_funcs { int (*prepare)(struct drm_panel *panel); int (*enable)(struct drm_panel *panel); int (*get_modes)(struct drm_panel *panel); + int (*get_timings)(struct drm_panel *panel, unsigned int num_timings, + struct display_timing *timings); }; struct drm_panel { -- cgit v1.2.3 From a54acb3a6f853e8394c4cb7b6a4d93c88f13eefd Mon Sep 17 00:00:00 2001 From: Nicolas Dichtel Date: Thu, 2 Apr 2015 17:07:00 +0200 Subject: dev: introduce dev_get_iflink() The goal of this patch is to prepare the removal of the iflink field. It introduces a new ndo function, which will be implemented by virtual interfaces. There is no functional change into this patch. All readers of iflink field now call dev_get_iflink(). Signed-off-by: Nicolas Dichtel Signed-off-by: David S. Miller --- drivers/net/ipvlan/ipvlan_core.c | 2 +- include/linux/netdevice.h | 4 ++++ net/batman-adv/hard-interface.c | 5 +++-- net/bridge/br_netlink.c | 4 ++-- net/core/dev.c | 21 +++++++++++++++++++-- net/core/link_watch.c | 4 ++-- net/core/net-sysfs.c | 10 +++++++++- net/core/rtnetlink.c | 8 ++++---- net/ipv4/ipmr.c | 2 +- net/ipv6/addrconf.c | 4 ++-- net/ipv6/ip6mr.c | 2 +- 11 files changed, 48 insertions(+), 18 deletions(-) (limited to 'include') diff --git a/drivers/net/ipvlan/ipvlan_core.c b/drivers/net/ipvlan/ipvlan_core.c index 2a175006028b..131bde98188d 100644 --- a/drivers/net/ipvlan/ipvlan_core.c +++ b/drivers/net/ipvlan/ipvlan_core.c @@ -330,7 +330,7 @@ static int ipvlan_process_v4_outbound(struct sk_buff *skb) struct rtable *rt; int err, ret = NET_XMIT_DROP; struct flowi4 fl4 = { - .flowi4_oif = dev->iflink, + .flowi4_oif = dev_get_iflink(dev), .flowi4_tos = RT_TOS(ip4h->tos), .flowi4_flags = FLOWI_FLAG_ANYSRC, .daddr = ip4h->daddr, diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 967bb4c8caf1..788eb7a622ad 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -1030,6 +1030,8 @@ typedef u16 (*select_queue_fallback_t)(struct net_device *dev, * int queue_index, u32 maxrate); * Called when a user wants to set a max-rate limitation of specific * TX queue. + * int (*ndo_get_iflink)(const struct net_device *dev); + * Called to get the iflink value of this device. */ struct net_device_ops { int (*ndo_init)(struct net_device *dev); @@ -1191,6 +1193,7 @@ struct net_device_ops { int (*ndo_set_tx_maxrate)(struct net_device *dev, int queue_index, u32 maxrate); + int (*ndo_get_iflink)(const struct net_device *dev); }; /** @@ -2149,6 +2152,7 @@ void __dev_remove_pack(struct packet_type *pt); void dev_add_offload(struct packet_offload *po); void dev_remove_offload(struct packet_offload *po); +int dev_get_iflink(const struct net_device *dev); struct net_device *__dev_get_by_flags(struct net *net, unsigned short flags, unsigned short mask); struct net_device *dev_get_by_name(struct net *net, const char *name); diff --git a/net/batman-adv/hard-interface.c b/net/batman-adv/hard-interface.c index fbda6b54baff..baf1f9843f2c 100644 --- a/net/batman-adv/hard-interface.c +++ b/net/batman-adv/hard-interface.c @@ -83,11 +83,12 @@ static bool batadv_is_on_batman_iface(const struct net_device *net_dev) return true; /* no more parents..stop recursion */ - if (net_dev->iflink == 0 || net_dev->iflink == net_dev->ifindex) + if (dev_get_iflink(net_dev) == 0 || + dev_get_iflink(net_dev) == net_dev->ifindex) return false; /* recurse over the parent device */ - parent_dev = __dev_get_by_index(&init_net, net_dev->iflink); + parent_dev = __dev_get_by_index(&init_net, dev_get_iflink(net_dev)); /* if we got a NULL parent_dev there is something broken.. */ if (WARN(!parent_dev, "Cannot find parent device")) return false; diff --git a/net/bridge/br_netlink.c b/net/bridge/br_netlink.c index e1115a224a95..0e4ddb81610d 100644 --- a/net/bridge/br_netlink.c +++ b/net/bridge/br_netlink.c @@ -305,8 +305,8 @@ static int br_fill_ifinfo(struct sk_buff *skb, nla_put_u8(skb, IFLA_OPERSTATE, operstate) || (dev->addr_len && nla_put(skb, IFLA_ADDRESS, dev->addr_len, dev->dev_addr)) || - (dev->ifindex != dev->iflink && - nla_put_u32(skb, IFLA_LINK, dev->iflink))) + (dev->ifindex != dev_get_iflink(dev) && + nla_put_u32(skb, IFLA_LINK, dev_get_iflink(dev)))) goto nla_put_failure; if (event == RTM_NEWLINK && port) { diff --git a/net/core/dev.c b/net/core/dev.c index 65492b0354c0..77172d085760 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -659,6 +659,23 @@ __setup("netdev=", netdev_boot_setup); *******************************************************************************/ +/** + * dev_get_iflink - get 'iflink' value of a interface + * @dev: targeted interface + * + * Indicates the ifindex the interface is linked to. + * Physical interfaces have the same 'ifindex' and 'iflink' values. + */ + +int dev_get_iflink(const struct net_device *dev) +{ + if (dev->netdev_ops && dev->netdev_ops->ndo_get_iflink) + return dev->netdev_ops->ndo_get_iflink(dev); + + return dev->iflink; +} +EXPORT_SYMBOL(dev_get_iflink); + /** * __dev_get_by_name - find a device by its name * @net: the applicable net namespace @@ -6345,7 +6362,7 @@ int register_netdevice(struct net_device *dev) else if (__dev_get_by_index(net, dev->ifindex)) goto err_uninit; - if (dev->iflink == -1) + if (dev_get_iflink(dev) == -1) dev->iflink = dev->ifindex; /* Transfer changeable features to wanted_features and enable @@ -7061,7 +7078,7 @@ int dev_change_net_namespace(struct net_device *dev, struct net *net, const char /* If there is an ifindex conflict assign a new one */ if (__dev_get_by_index(net, dev->ifindex)) { - int iflink = (dev->iflink == dev->ifindex); + int iflink = (dev_get_iflink(dev) == dev->ifindex); dev->ifindex = dev_new_index(net); if (iflink) dev->iflink = dev->ifindex; diff --git a/net/core/link_watch.c b/net/core/link_watch.c index 49a9e3e06c08..982861607f88 100644 --- a/net/core/link_watch.c +++ b/net/core/link_watch.c @@ -40,7 +40,7 @@ static DEFINE_SPINLOCK(lweventlist_lock); static unsigned char default_operstate(const struct net_device *dev) { if (!netif_carrier_ok(dev)) - return (dev->ifindex != dev->iflink ? + return (dev->ifindex != dev_get_iflink(dev) ? IF_OPER_LOWERLAYERDOWN : IF_OPER_DOWN); if (netif_dormant(dev)) @@ -89,7 +89,7 @@ static bool linkwatch_urgent_event(struct net_device *dev) if (!netif_running(dev)) return false; - if (dev->ifindex != dev->iflink) + if (dev->ifindex != dev_get_iflink(dev)) return true; if (dev->priv_flags & IFF_TEAM_PORT) diff --git a/net/core/net-sysfs.c b/net/core/net-sysfs.c index cc5cf689809c..4238d6da5c60 100644 --- a/net/core/net-sysfs.c +++ b/net/core/net-sysfs.c @@ -109,11 +109,19 @@ NETDEVICE_SHOW_RO(dev_id, fmt_hex); NETDEVICE_SHOW_RO(dev_port, fmt_dec); NETDEVICE_SHOW_RO(addr_assign_type, fmt_dec); NETDEVICE_SHOW_RO(addr_len, fmt_dec); -NETDEVICE_SHOW_RO(iflink, fmt_dec); NETDEVICE_SHOW_RO(ifindex, fmt_dec); NETDEVICE_SHOW_RO(type, fmt_dec); NETDEVICE_SHOW_RO(link_mode, fmt_dec); +static ssize_t iflink_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct net_device *ndev = to_net_dev(dev); + + return sprintf(buf, fmt_dec, dev_get_iflink(ndev)); +} +static DEVICE_ATTR_RO(iflink); + static ssize_t format_name_assign_type(const struct net_device *dev, char *buf) { return sprintf(buf, fmt_dec, dev->name_assign_type); diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c index b96ac2109c82..ee0186cdd5cf 100644 --- a/net/core/rtnetlink.c +++ b/net/core/rtnetlink.c @@ -1055,8 +1055,8 @@ static int rtnl_fill_ifinfo(struct sk_buff *skb, struct net_device *dev, #ifdef CONFIG_RPS nla_put_u32(skb, IFLA_NUM_RX_QUEUES, dev->num_rx_queues) || #endif - (dev->ifindex != dev->iflink && - nla_put_u32(skb, IFLA_LINK, dev->iflink)) || + (dev->ifindex != dev_get_iflink(dev) && + nla_put_u32(skb, IFLA_LINK, dev_get_iflink(dev))) || (upper_dev && nla_put_u32(skb, IFLA_MASTER, upper_dev->ifindex)) || nla_put_u8(skb, IFLA_CARRIER, netif_carrier_ok(dev)) || @@ -2863,8 +2863,8 @@ int ndo_dflt_bridge_getlink(struct sk_buff *skb, u32 pid, u32 seq, nla_put_u32(skb, IFLA_MASTER, br_dev->ifindex)) || (dev->addr_len && nla_put(skb, IFLA_ADDRESS, dev->addr_len, dev->dev_addr)) || - (dev->ifindex != dev->iflink && - nla_put_u32(skb, IFLA_LINK, dev->iflink))) + (dev->ifindex != dev_get_iflink(dev) && + nla_put_u32(skb, IFLA_LINK, dev_get_iflink(dev)))) goto nla_put_failure; br_afspec = nla_nest_start(skb, IFLA_AF_SPEC); diff --git a/net/ipv4/ipmr.c b/net/ipv4/ipmr.c index b4a545d24adb..eec68b0c3bc8 100644 --- a/net/ipv4/ipmr.c +++ b/net/ipv4/ipmr.c @@ -801,7 +801,7 @@ static int vif_add(struct net *net, struct mr_table *mrt, v->pkt_out = 0; v->link = dev->ifindex; if (v->flags & (VIFF_TUNNEL | VIFF_REGISTER)) - v->link = dev->iflink; + v->link = dev_get_iflink(dev); /* And finish update writing critical data */ write_lock_bh(&mrt_lock); diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c index 5c9e94cb1b2c..37b70e82bff8 100644 --- a/net/ipv6/addrconf.c +++ b/net/ipv6/addrconf.c @@ -4858,8 +4858,8 @@ static int inet6_fill_ifinfo(struct sk_buff *skb, struct inet6_dev *idev, (dev->addr_len && nla_put(skb, IFLA_ADDRESS, dev->addr_len, dev->dev_addr)) || nla_put_u32(skb, IFLA_MTU, dev->mtu) || - (dev->ifindex != dev->iflink && - nla_put_u32(skb, IFLA_LINK, dev->iflink))) + (dev->ifindex != dev_get_iflink(dev) && + nla_put_u32(skb, IFLA_LINK, dev_get_iflink(dev)))) goto nla_put_failure; protoinfo = nla_nest_start(skb, IFLA_PROTINFO); if (!protoinfo) diff --git a/net/ipv6/ip6mr.c b/net/ipv6/ip6mr.c index caf6b99374e6..18a5ab286420 100644 --- a/net/ipv6/ip6mr.c +++ b/net/ipv6/ip6mr.c @@ -992,7 +992,7 @@ static int mif6_add(struct net *net, struct mr6_table *mrt, v->pkt_out = 0; v->link = dev->ifindex; if (v->flags & MIFF_REGISTER) - v->link = dev->iflink; + v->link = dev_get_iflink(dev); /* And finish update writing critical data */ write_lock_bh(&mrt_lock); -- cgit v1.2.3 From ecf2c06a88d2ed534a87b84b8c1a467ab23352dd Mon Sep 17 00:00:00 2001 From: Nicolas Dichtel Date: Thu, 2 Apr 2015 17:07:01 +0200 Subject: ip6tnl,gre6,vti6: implement ndo_get_iflink Don't use dev->iflink anymore. CC: Steffen Klassert Signed-off-by: Nicolas Dichtel Signed-off-by: David S. Miller --- include/net/ip6_tunnel.h | 1 + net/ipv6/ip6_gre.c | 8 ++------ net/ipv6/ip6_tunnel.c | 10 ++++++++-- net/ipv6/ip6_vti.c | 3 +-- 4 files changed, 12 insertions(+), 10 deletions(-) (limited to 'include') diff --git a/include/net/ip6_tunnel.h b/include/net/ip6_tunnel.h index 76c091b53dae..1668be5937e6 100644 --- a/include/net/ip6_tunnel.h +++ b/include/net/ip6_tunnel.h @@ -71,6 +71,7 @@ __u16 ip6_tnl_parse_tlv_enc_lim(struct sk_buff *skb, __u8 *raw); __u32 ip6_tnl_get_cap(struct ip6_tnl *t, const struct in6_addr *laddr, const struct in6_addr *raddr); struct net *ip6_tnl_get_link_net(const struct net_device *dev); +int ip6_tnl_get_iflink(const struct net_device *dev); static inline void ip6tunnel_xmit(struct sk_buff *skb, struct net_device *dev) { diff --git a/net/ipv6/ip6_gre.c b/net/ipv6/ip6_gre.c index 0f4e73da14e4..f724329d7436 100644 --- a/net/ipv6/ip6_gre.c +++ b/net/ipv6/ip6_gre.c @@ -1216,6 +1216,7 @@ static const struct net_device_ops ip6gre_netdev_ops = { .ndo_do_ioctl = ip6gre_tunnel_ioctl, .ndo_change_mtu = ip6gre_tunnel_change_mtu, .ndo_get_stats64 = ip_tunnel_get_stats64, + .ndo_get_iflink = ip6_tnl_get_iflink, }; static void ip6gre_dev_free(struct net_device *dev) @@ -1238,7 +1239,6 @@ static void ip6gre_tunnel_setup(struct net_device *dev) if (!(t->parms.flags & IP6_TNL_F_IGN_ENCAP_LIMIT)) dev->mtu -= 8; dev->flags |= IFF_NOARP; - dev->iflink = 0; dev->addr_len = sizeof(struct in6_addr); netif_keep_dst(dev); } @@ -1270,8 +1270,6 @@ static int ip6gre_tunnel_init(struct net_device *dev) u64_stats_init(&ip6gre_tunnel_stats->syncp); } - dev->iflink = tunnel->parms.link; - return 0; } @@ -1480,8 +1478,6 @@ static int ip6gre_tap_init(struct net_device *dev) if (!dev->tstats) return -ENOMEM; - dev->iflink = tunnel->parms.link; - return 0; } @@ -1493,6 +1489,7 @@ static const struct net_device_ops ip6gre_tap_netdev_ops = { .ndo_validate_addr = eth_validate_addr, .ndo_change_mtu = ip6gre_tunnel_change_mtu, .ndo_get_stats64 = ip_tunnel_get_stats64, + .ndo_get_iflink = ip6_tnl_get_iflink, }; static void ip6gre_tap_setup(struct net_device *dev) @@ -1503,7 +1500,6 @@ static void ip6gre_tap_setup(struct net_device *dev) dev->netdev_ops = &ip6gre_tap_netdev_ops; dev->destructor = ip6gre_dev_free; - dev->iflink = 0; dev->features |= NETIF_F_NETNS_LOCAL; } diff --git a/net/ipv6/ip6_tunnel.c b/net/ipv6/ip6_tunnel.c index 9bd85f0dff69..b6a211a150b2 100644 --- a/net/ipv6/ip6_tunnel.c +++ b/net/ipv6/ip6_tunnel.c @@ -1264,8 +1264,6 @@ static void ip6_tnl_link_config(struct ip6_tnl *t) else dev->flags &= ~IFF_POINTOPOINT; - dev->iflink = p->link; - if (p->flags & IP6_TNL_F_CAP_XMIT) { int strict = (ipv6_addr_type(&p->raddr) & (IPV6_ADDR_MULTICAST|IPV6_ADDR_LINKLOCAL)); @@ -1517,6 +1515,13 @@ ip6_tnl_change_mtu(struct net_device *dev, int new_mtu) return 0; } +int ip6_tnl_get_iflink(const struct net_device *dev) +{ + struct ip6_tnl *t = netdev_priv(dev); + + return t->parms.link; +} +EXPORT_SYMBOL(ip6_tnl_get_iflink); static const struct net_device_ops ip6_tnl_netdev_ops = { .ndo_init = ip6_tnl_dev_init, @@ -1525,6 +1530,7 @@ static const struct net_device_ops ip6_tnl_netdev_ops = { .ndo_do_ioctl = ip6_tnl_ioctl, .ndo_change_mtu = ip6_tnl_change_mtu, .ndo_get_stats = ip6_get_stats, + .ndo_get_iflink = ip6_tnl_get_iflink, }; diff --git a/net/ipv6/ip6_vti.c b/net/ipv6/ip6_vti.c index 53d90ed68905..b53148444e15 100644 --- a/net/ipv6/ip6_vti.c +++ b/net/ipv6/ip6_vti.c @@ -601,8 +601,6 @@ static void vti6_link_config(struct ip6_tnl *t) dev->flags |= IFF_POINTOPOINT; else dev->flags &= ~IFF_POINTOPOINT; - - dev->iflink = p->link; } /** @@ -808,6 +806,7 @@ static const struct net_device_ops vti6_netdev_ops = { .ndo_do_ioctl = vti6_ioctl, .ndo_change_mtu = vti6_change_mtu, .ndo_get_stats64 = ip_tunnel_get_stats64, + .ndo_get_iflink = ip6_tnl_get_iflink, }; /** -- cgit v1.2.3 From 1e99584b911cb6f3d2a681e2532d8dc3f9339c9c Mon Sep 17 00:00:00 2001 From: Nicolas Dichtel Date: Thu, 2 Apr 2015 17:07:02 +0200 Subject: ipip,gre,vti,sit: implement ndo_get_iflink Don't use dev->iflink anymore. CC: Steffen Klassert Signed-off-by: Nicolas Dichtel Signed-off-by: David S. Miller --- include/net/ip_tunnels.h | 1 + net/ipv4/ip_gre.c | 2 ++ net/ipv4/ip_tunnel.c | 9 ++++++++- net/ipv4/ip_vti.c | 2 +- net/ipv4/ipip.c | 2 +- net/ipv6/sit.c | 3 +-- 6 files changed, 14 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/include/net/ip_tunnels.h b/include/net/ip_tunnels.h index 2c47061a6954..d8214cb88bbc 100644 --- a/include/net/ip_tunnels.h +++ b/include/net/ip_tunnels.h @@ -142,6 +142,7 @@ int ip_tunnel_init(struct net_device *dev); void ip_tunnel_uninit(struct net_device *dev); void ip_tunnel_dellink(struct net_device *dev, struct list_head *head); struct net *ip_tunnel_get_link_net(const struct net_device *dev); +int ip_tunnel_get_iflink(const struct net_device *dev); int ip_tunnel_init_net(struct net *net, int ip_tnl_net_id, struct rtnl_link_ops *ops, char *devname); diff --git a/net/ipv4/ip_gre.c b/net/ipv4/ip_gre.c index 0eb2a040a830..1060ca0bc23a 100644 --- a/net/ipv4/ip_gre.c +++ b/net/ipv4/ip_gre.c @@ -456,6 +456,7 @@ static const struct net_device_ops ipgre_netdev_ops = { .ndo_do_ioctl = ipgre_tunnel_ioctl, .ndo_change_mtu = ip_tunnel_change_mtu, .ndo_get_stats64 = ip_tunnel_get_stats64, + .ndo_get_iflink = ip_tunnel_get_iflink, }; #define GRE_FEATURES (NETIF_F_SG | \ @@ -686,6 +687,7 @@ static const struct net_device_ops gre_tap_netdev_ops = { .ndo_validate_addr = eth_validate_addr, .ndo_change_mtu = ip_tunnel_change_mtu, .ndo_get_stats64 = ip_tunnel_get_stats64, + .ndo_get_iflink = ip_tunnel_get_iflink, }; static void ipgre_tap_setup(struct net_device *dev) diff --git a/net/ipv4/ip_tunnel.c b/net/ipv4/ip_tunnel.c index 2cd08280c77b..4bb7252110a6 100644 --- a/net/ipv4/ip_tunnel.c +++ b/net/ipv4/ip_tunnel.c @@ -389,7 +389,6 @@ static int ip_tunnel_bind_dev(struct net_device *dev) hlen = tdev->hard_header_len + tdev->needed_headroom; mtu = tdev->mtu; } - dev->iflink = tunnel->parms.link; dev->needed_headroom = t_hlen + hlen; mtu -= (dev->hard_header_len + t_hlen); @@ -980,6 +979,14 @@ struct net *ip_tunnel_get_link_net(const struct net_device *dev) } EXPORT_SYMBOL(ip_tunnel_get_link_net); +int ip_tunnel_get_iflink(const struct net_device *dev) +{ + struct ip_tunnel *tunnel = netdev_priv(dev); + + return tunnel->parms.link; +} +EXPORT_SYMBOL(ip_tunnel_get_iflink); + int ip_tunnel_init_net(struct net *net, int ip_tnl_net_id, struct rtnl_link_ops *ops, char *devname) { diff --git a/net/ipv4/ip_vti.c b/net/ipv4/ip_vti.c index 5a6e27054f0a..c4f93c0d1104 100644 --- a/net/ipv4/ip_vti.c +++ b/net/ipv4/ip_vti.c @@ -341,6 +341,7 @@ static const struct net_device_ops vti_netdev_ops = { .ndo_do_ioctl = vti_tunnel_ioctl, .ndo_change_mtu = ip_tunnel_change_mtu, .ndo_get_stats64 = ip_tunnel_get_stats64, + .ndo_get_iflink = ip_tunnel_get_iflink, }; static void vti_tunnel_setup(struct net_device *dev) @@ -361,7 +362,6 @@ static int vti_tunnel_init(struct net_device *dev) dev->hard_header_len = LL_MAX_HEADER + sizeof(struct iphdr); dev->mtu = ETH_DATA_LEN; dev->flags = IFF_NOARP; - dev->iflink = 0; dev->addr_len = 4; dev->features |= NETIF_F_LLTX; netif_keep_dst(dev); diff --git a/net/ipv4/ipip.c b/net/ipv4/ipip.c index bfbcc85c02ee..5c81f6e40842 100644 --- a/net/ipv4/ipip.c +++ b/net/ipv4/ipip.c @@ -272,6 +272,7 @@ static const struct net_device_ops ipip_netdev_ops = { .ndo_do_ioctl = ipip_tunnel_ioctl, .ndo_change_mtu = ip_tunnel_change_mtu, .ndo_get_stats64 = ip_tunnel_get_stats64, + .ndo_get_iflink = ip_tunnel_get_iflink, }; #define IPIP_FEATURES (NETIF_F_SG | \ @@ -286,7 +287,6 @@ static void ipip_tunnel_setup(struct net_device *dev) dev->type = ARPHRD_TUNNEL; dev->flags = IFF_NOARP; - dev->iflink = 0; dev->addr_len = 4; dev->features |= NETIF_F_LLTX; netif_keep_dst(dev); diff --git a/net/ipv6/sit.c b/net/ipv6/sit.c index e6b9f51b15e8..6cf2026a9cea 100644 --- a/net/ipv6/sit.c +++ b/net/ipv6/sit.c @@ -1076,7 +1076,6 @@ static void ipip6_tunnel_bind_dev(struct net_device *dev) if (dev->mtu < IPV6_MIN_MTU) dev->mtu = IPV6_MIN_MTU; } - dev->iflink = tunnel->parms.link; } static void ipip6_tunnel_update(struct ip_tunnel *t, struct ip_tunnel_parm *p) @@ -1336,6 +1335,7 @@ static const struct net_device_ops ipip6_netdev_ops = { .ndo_do_ioctl = ipip6_tunnel_ioctl, .ndo_change_mtu = ipip6_tunnel_change_mtu, .ndo_get_stats64 = ip_tunnel_get_stats64, + .ndo_get_iflink = ip_tunnel_get_iflink, }; static void ipip6_dev_free(struct net_device *dev) @@ -1366,7 +1366,6 @@ static void ipip6_tunnel_setup(struct net_device *dev) dev->mtu = ETH_DATA_LEN - t_hlen; dev->flags = IFF_NOARP; netif_keep_dst(dev); - dev->iflink = 0; dev->addr_len = 4; dev->features |= NETIF_F_LLTX; dev->features |= SIT_FEATURES; -- cgit v1.2.3 From 7a66bbc96ce9ad8261fa5f7f6ae65370eb6866ee Mon Sep 17 00:00:00 2001 From: Nicolas Dichtel Date: Thu, 2 Apr 2015 17:07:09 +0200 Subject: net: remove iflink field from struct net_device Now that all users of iflink have the ndo_get_iflink handler available, it's possible to remove this field. By default, dev_get_iflink() returns the ifindex of the interface. Signed-off-by: Nicolas Dichtel Signed-off-by: David S. Miller --- include/linux/netdevice.h | 3 +-- net/core/dev.c | 13 ++----------- 2 files changed, 3 insertions(+), 13 deletions(-) (limited to 'include') diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 788eb7a622ad..846a1f5bc9db 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -1538,7 +1538,7 @@ struct net_device { netdev_features_t mpls_features; int ifindex; - int iflink; + int group; struct net_device_stats stats; @@ -1741,7 +1741,6 @@ struct net_device { #endif struct phy_device *phydev; struct lock_class_key *qdisc_tx_busylock; - int group; struct pm_qos_request pm_qos_req; }; #define to_net_dev(d) container_of(d, struct net_device, dev) diff --git a/net/core/dev.c b/net/core/dev.c index 77172d085760..3be107e0bc93 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -672,7 +672,7 @@ int dev_get_iflink(const struct net_device *dev) if (dev->netdev_ops && dev->netdev_ops->ndo_get_iflink) return dev->netdev_ops->ndo_get_iflink(dev); - return dev->iflink; + return dev->ifindex; } EXPORT_SYMBOL(dev_get_iflink); @@ -6331,8 +6331,6 @@ int register_netdevice(struct net_device *dev) spin_lock_init(&dev->addr_list_lock); netdev_set_addr_lockdep_class(dev); - dev->iflink = -1; - ret = dev_get_valid_name(net, dev, dev->name); if (ret < 0) goto out; @@ -6362,9 +6360,6 @@ int register_netdevice(struct net_device *dev) else if (__dev_get_by_index(net, dev->ifindex)) goto err_uninit; - if (dev_get_iflink(dev) == -1) - dev->iflink = dev->ifindex; - /* Transfer changeable features to wanted_features and enable * software offloads (GSO and GRO). */ @@ -7077,12 +7072,8 @@ int dev_change_net_namespace(struct net_device *dev, struct net *net, const char dev_net_set(dev, net); /* If there is an ifindex conflict assign a new one */ - if (__dev_get_by_index(net, dev->ifindex)) { - int iflink = (dev_get_iflink(dev) == dev->ifindex); + if (__dev_get_by_index(net, dev->ifindex)) dev->ifindex = dev_new_index(net); - if (iflink) - dev->iflink = dev->ifindex; - } /* Send a netdev-add uevent to the new namespace */ kobject_uevent(&dev->dev.kobj, KOBJ_ADD); -- cgit v1.2.3 From 13e71d69cc7444b7d840bab581dbe831e440fb62 Mon Sep 17 00:00:00 2001 From: Markus Pargmann Date: Thu, 2 Apr 2015 10:11:35 +0200 Subject: nbd: Remove kernel internal header The header is not included anywhere. Remove it and include the private nbd_device struct in nbd.c. Signed-off-by: Markus Pargmann Signed-off-by: Jens Axboe --- drivers/block/nbd.c | 22 ++++++++++++++++++++++ include/linux/nbd.h | 46 ---------------------------------------------- 2 files changed, 22 insertions(+), 46 deletions(-) delete mode 100644 include/linux/nbd.h (limited to 'include') diff --git a/drivers/block/nbd.c b/drivers/block/nbd.c index a98c41f72c63..1aaabcc2af92 100644 --- a/drivers/block/nbd.c +++ b/drivers/block/nbd.c @@ -38,6 +38,28 @@ #include +struct nbd_device { + int flags; + int harderror; /* Code of hard error */ + struct socket * sock; /* If == NULL, device is not ready, yet */ + int magic; + + spinlock_t queue_lock; + struct list_head queue_head; /* Requests waiting result */ + struct request *active_req; + wait_queue_head_t active_wq; + struct list_head waiting_queue; /* Requests to be sent */ + wait_queue_head_t waiting_wq; + + struct mutex tx_lock; + struct gendisk *disk; + int blksize; + u64 bytesize; + pid_t pid; /* pid of nbd-client, if attached */ + int xmit_timeout; + int disconnect; /* a disconnect has been requested by user */ +}; + #define NBD_MAGIC 0x68797548 #ifdef NDEBUG diff --git a/include/linux/nbd.h b/include/linux/nbd.h deleted file mode 100644 index f62f78aef4ac..000000000000 --- a/include/linux/nbd.h +++ /dev/null @@ -1,46 +0,0 @@ -/* - * 1999 Copyright (C) Pavel Machek, pavel@ucw.cz. This code is GPL. - * 1999/11/04 Copyright (C) 1999 VMware, Inc. (Regis "HPReg" Duchesne) - * Made nbd_end_request() use the io_request_lock - * 2001 Copyright (C) Steven Whitehouse - * New nbd_end_request() for compatibility with new linux block - * layer code. - * 2003/06/24 Louis D. Langholtz - * Removed unneeded blksize_bits field from nbd_device struct. - * Cleanup PARANOIA usage & code. - * 2004/02/19 Paul Clements - * Removed PARANOIA, plus various cleanup and comments - */ -#ifndef LINUX_NBD_H -#define LINUX_NBD_H - - -#include -#include -#include - -struct request; - -struct nbd_device { - int flags; - int harderror; /* Code of hard error */ - struct socket * sock; /* If == NULL, device is not ready, yet */ - int magic; - - spinlock_t queue_lock; - struct list_head queue_head; /* Requests waiting result */ - struct request *active_req; - wait_queue_head_t active_wq; - struct list_head waiting_queue; /* Requests to be sent */ - wait_queue_head_t waiting_wq; - - struct mutex tx_lock; - struct gendisk *disk; - int blksize; - u64 bytesize; - pid_t pid; /* pid of nbd-client, if attached */ - int xmit_timeout; - int disconnect; /* a disconnect has been requested by user */ -}; - -#endif -- cgit v1.2.3 From 6890874772e4c5e2925187f262893df0eb0322ba Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Wed, 25 Mar 2015 19:57:30 -0300 Subject: [media] omap3isp: Refactor device configuration structs for Device Tree Make omap3isp configuration data structures more suitable for consumption by the DT by separating the I2C bus information of all the sub-devices in a group and the ISP bus information from each other. The ISP bus information is made a pointer instead of being directly embedded in the struct. In the case of the DT only the sensor specific information on the ISP bus configuration is retained. The structs are renamed to reflect that. After this change the structs needed to describe device configuration can be allocated and accessed separately without those needed only in the case of platform data. The platform data related structs can be later removed once the support for platform data can be removed. Signed-off-by: Sakari Ailus Acked-by: Igor Grinberg (for cm-t35) Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- arch/arm/mach-omap2/board-cm-t35.c | 57 +++++++------------ drivers/media/platform/omap3isp/isp.c | 86 ++++++++++++++--------------- drivers/media/platform/omap3isp/isp.h | 2 +- drivers/media/platform/omap3isp/ispccdc.c | 26 ++++----- drivers/media/platform/omap3isp/ispccp2.c | 22 ++++---- drivers/media/platform/omap3isp/ispcsi2.c | 8 +-- drivers/media/platform/omap3isp/ispcsiphy.c | 21 ++++--- include/media/omap3isp.h | 34 ++++++------ 8 files changed, 119 insertions(+), 137 deletions(-) (limited to 'include') diff --git a/arch/arm/mach-omap2/board-cm-t35.c b/arch/arm/mach-omap2/board-cm-t35.c index 91738a14ecbe..b5dfbc1b1fc6 100644 --- a/arch/arm/mach-omap2/board-cm-t35.c +++ b/arch/arm/mach-omap2/board-cm-t35.c @@ -492,51 +492,36 @@ static struct twl4030_platform_data cm_t35_twldata = { #include #include "devices.h" -static struct i2c_board_info cm_t35_isp_i2c_boardinfo[] = { +static struct isp_platform_subdev cm_t35_isp_subdevs[] = { { - I2C_BOARD_INFO("mt9t001", 0x5d), - }, - { - I2C_BOARD_INFO("tvp5150", 0x5c), - }, -}; - -static struct isp_subdev_i2c_board_info cm_t35_isp_primary_subdevs[] = { - { - .board_info = &cm_t35_isp_i2c_boardinfo[0], - .i2c_adapter_id = 3, - }, - { NULL, 0, }, -}; - -static struct isp_subdev_i2c_board_info cm_t35_isp_secondary_subdevs[] = { - { - .board_info = &cm_t35_isp_i2c_boardinfo[1], + .board_info = &(struct i2c_board_info){ + I2C_BOARD_INFO("mt9t001", 0x5d) + }, .i2c_adapter_id = 3, - }, - { NULL, 0, }, -}; - -static struct isp_v4l2_subdevs_group cm_t35_isp_subdevs[] = { - { - .subdevs = cm_t35_isp_primary_subdevs, - .interface = ISP_INTERFACE_PARALLEL, - .bus = { - .parallel = { - .clk_pol = 1, + .bus = &(struct isp_bus_cfg){ + .interface = ISP_INTERFACE_PARALLEL, + .bus = { + .parallel = { + .clk_pol = 1, + }, }, }, }, { - .subdevs = cm_t35_isp_secondary_subdevs, - .interface = ISP_INTERFACE_PARALLEL, - .bus = { - .parallel = { - .clk_pol = 0, + .board_info = &(struct i2c_board_info){ + I2C_BOARD_INFO("tvp5150", 0x5c), + }, + .i2c_adapter_id = 3, + .bus = &(struct isp_bus_cfg){ + .interface = ISP_INTERFACE_PARALLEL, + .bus = { + .parallel = { + .clk_pol = 0, + }, }, }, }, - { NULL, 0, }, + { 0 }, }; static struct isp_platform_data cm_t35_isp_pdata = { diff --git a/drivers/media/platform/omap3isp/isp.c b/drivers/media/platform/omap3isp/isp.c index 537377b43083..1b5c6df3b645 100644 --- a/drivers/media/platform/omap3isp/isp.c +++ b/drivers/media/platform/omap3isp/isp.c @@ -447,7 +447,7 @@ static void isp_core_init(struct isp_device *isp, int idle) */ void omap3isp_configure_bridge(struct isp_device *isp, enum ccdc_input_entity input, - const struct isp_parallel_platform_data *pdata, + const struct isp_parallel_cfg *parcfg, unsigned int shift, unsigned int bridge) { u32 ispctrl_val; @@ -462,8 +462,8 @@ void omap3isp_configure_bridge(struct isp_device *isp, switch (input) { case CCDC_INPUT_PARALLEL: ispctrl_val |= ISPCTRL_PAR_SER_CLK_SEL_PARALLEL; - ispctrl_val |= pdata->clk_pol << ISPCTRL_PAR_CLK_POL_SHIFT; - shift += pdata->data_lane_shift * 2; + ispctrl_val |= parcfg->clk_pol << ISPCTRL_PAR_CLK_POL_SHIFT; + shift += parcfg->data_lane_shift * 2; break; case CCDC_INPUT_CSI2A: @@ -1809,52 +1809,44 @@ static void isp_unregister_entities(struct isp_device *isp) } /* - * isp_register_subdev_group - Register a group of subdevices + * isp_register_subdev - Register a sub-device * @isp: OMAP3 ISP device - * @board_info: I2C subdevs board information array + * @isp_subdev: platform data related to a sub-device * - * Register all I2C subdevices in the board_info array. The array must be - * terminated by a NULL entry, and the first entry must be the sensor. + * Register an I2C sub-device which has not been registered by other + * means (such as the Device Tree). * - * Return a pointer to the sensor media entity if it has been successfully + * Return a pointer to the sub-device if it has been successfully * registered, or NULL otherwise. */ static struct v4l2_subdev * -isp_register_subdev_group(struct isp_device *isp, - struct isp_subdev_i2c_board_info *board_info) +isp_register_subdev(struct isp_device *isp, + struct isp_platform_subdev *isp_subdev) { - struct v4l2_subdev *sensor = NULL; - unsigned int first; + struct i2c_adapter *adapter; + struct v4l2_subdev *sd; - if (board_info->board_info == NULL) + if (isp_subdev->board_info == NULL) return NULL; - for (first = 1; board_info->board_info; ++board_info, first = 0) { - struct v4l2_subdev *subdev; - struct i2c_adapter *adapter; - - adapter = i2c_get_adapter(board_info->i2c_adapter_id); - if (adapter == NULL) { - dev_err(isp->dev, "%s: Unable to get I2C adapter %d for " - "device %s\n", __func__, - board_info->i2c_adapter_id, - board_info->board_info->type); - continue; - } - - subdev = v4l2_i2c_new_subdev_board(&isp->v4l2_dev, adapter, - board_info->board_info, NULL); - if (subdev == NULL) { - dev_err(isp->dev, "%s: Unable to register subdev %s\n", - __func__, board_info->board_info->type); - continue; - } + adapter = i2c_get_adapter(isp_subdev->i2c_adapter_id); + if (adapter == NULL) { + dev_err(isp->dev, + "%s: Unable to get I2C adapter %d for device %s\n", + __func__, isp_subdev->i2c_adapter_id, + isp_subdev->board_info->type); + return NULL; + } - if (first) - sensor = subdev; + sd = v4l2_i2c_new_subdev_board(&isp->v4l2_dev, adapter, + isp_subdev->board_info, NULL); + if (sd == NULL) { + dev_err(isp->dev, "%s: Unable to register subdev %s\n", + __func__, isp_subdev->board_info->type); + return NULL; } - return sensor; + return sd; } static int isp_link_entity( @@ -1931,7 +1923,7 @@ static int isp_link_entity( static int isp_register_entities(struct isp_device *isp) { struct isp_platform_data *pdata = isp->pdata; - struct isp_v4l2_subdevs_group *subdevs; + struct isp_platform_subdev *isp_subdev; int ret; isp->media_dev.dev = isp->dev; @@ -1989,17 +1981,23 @@ static int isp_register_entities(struct isp_device *isp) goto done; /* Register external entities */ - for (subdevs = pdata ? pdata->subdevs : NULL; - subdevs && subdevs->subdevs; ++subdevs) { - struct v4l2_subdev *sensor; + for (isp_subdev = pdata ? pdata->subdevs : NULL; + isp_subdev && isp_subdev->board_info; isp_subdev++) { + struct v4l2_subdev *sd; - sensor = isp_register_subdev_group(isp, subdevs->subdevs); - if (sensor == NULL) + sd = isp_register_subdev(isp, isp_subdev); + + /* + * No bus information --- this is either a flash or a + * lens subdev. + */ + if (!sd || !isp_subdev->bus) continue; - sensor->host_priv = subdevs; + sd->host_priv = isp_subdev->bus; - ret = isp_link_entity(isp, &sensor->entity, subdevs->interface); + ret = isp_link_entity(isp, &sd->entity, + isp_subdev->bus->interface); if (ret < 0) goto done; } diff --git a/drivers/media/platform/omap3isp/isp.h b/drivers/media/platform/omap3isp/isp.h index cfdfc8714b6b..b932a6f22b52 100644 --- a/drivers/media/platform/omap3isp/isp.h +++ b/drivers/media/platform/omap3isp/isp.h @@ -229,7 +229,7 @@ int omap3isp_pipeline_set_stream(struct isp_pipeline *pipe, void omap3isp_pipeline_cancel_stream(struct isp_pipeline *pipe); void omap3isp_configure_bridge(struct isp_device *isp, enum ccdc_input_entity input, - const struct isp_parallel_platform_data *pdata, + const struct isp_parallel_cfg *buscfg, unsigned int shift, unsigned int bridge); struct isp_device *omap3isp_get(struct isp_device *isp); diff --git a/drivers/media/platform/omap3isp/ispccdc.c b/drivers/media/platform/omap3isp/ispccdc.c index 6e0291bca73f..a6a61cce43dd 100644 --- a/drivers/media/platform/omap3isp/ispccdc.c +++ b/drivers/media/platform/omap3isp/ispccdc.c @@ -958,11 +958,11 @@ void omap3isp_ccdc_max_rate(struct isp_ccdc_device *ccdc, /* * ccdc_config_sync_if - Set CCDC sync interface configuration * @ccdc: Pointer to ISP CCDC device. - * @pdata: Parallel interface platform data (may be NULL) + * @parcfg: Parallel interface platform data (may be NULL) * @data_size: Data size */ static void ccdc_config_sync_if(struct isp_ccdc_device *ccdc, - struct isp_parallel_platform_data *pdata, + struct isp_parallel_cfg *parcfg, unsigned int data_size) { struct isp_device *isp = to_isp_device(ccdc); @@ -1000,19 +1000,19 @@ static void ccdc_config_sync_if(struct isp_ccdc_device *ccdc, break; } - if (pdata && pdata->data_pol) + if (parcfg && parcfg->data_pol) syn_mode |= ISPCCDC_SYN_MODE_DATAPOL; - if (pdata && pdata->hs_pol) + if (parcfg && parcfg->hs_pol) syn_mode |= ISPCCDC_SYN_MODE_HDPOL; /* The polarity of the vertical sync signal output by the BT.656 * decoder is not documented and seems to be active low. */ - if ((pdata && pdata->vs_pol) || ccdc->bt656) + if ((parcfg && parcfg->vs_pol) || ccdc->bt656) syn_mode |= ISPCCDC_SYN_MODE_VDPOL; - if (pdata && pdata->fld_pol) + if (parcfg && parcfg->fld_pol) syn_mode |= ISPCCDC_SYN_MODE_FLDPOL; isp_reg_writel(isp, syn_mode, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_SYN_MODE); @@ -1115,7 +1115,7 @@ static const u32 ccdc_sgbrg_pattern = static void ccdc_configure(struct isp_ccdc_device *ccdc) { struct isp_device *isp = to_isp_device(ccdc); - struct isp_parallel_platform_data *pdata = NULL; + struct isp_parallel_cfg *parcfg = NULL; struct v4l2_subdev *sensor; struct v4l2_mbus_framefmt *format; const struct v4l2_rect *crop; @@ -1145,7 +1145,7 @@ static void ccdc_configure(struct isp_ccdc_device *ccdc) if (!ret) ccdc->bt656 = cfg.type == V4L2_MBUS_BT656; - pdata = &((struct isp_v4l2_subdevs_group *)sensor->host_priv) + parcfg = &((struct isp_bus_cfg *)sensor->host_priv) ->bus.parallel; } @@ -1175,10 +1175,10 @@ static void ccdc_configure(struct isp_ccdc_device *ccdc) else bridge = ISPCTRL_PAR_BRIDGE_DISABLE; - omap3isp_configure_bridge(isp, ccdc->input, pdata, shift, bridge); + omap3isp_configure_bridge(isp, ccdc->input, parcfg, shift, bridge); /* Configure the sync interface. */ - ccdc_config_sync_if(ccdc, pdata, depth_out); + ccdc_config_sync_if(ccdc, parcfg, depth_out); syn_mode = isp_reg_readl(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_SYN_MODE); @@ -2417,11 +2417,11 @@ static int ccdc_link_validate(struct v4l2_subdev *sd, /* We've got a parallel sensor here. */ if (ccdc->input == CCDC_INPUT_PARALLEL) { - struct isp_parallel_platform_data *pdata = - &((struct isp_v4l2_subdevs_group *) + struct isp_parallel_cfg *parcfg = + &((struct isp_bus_cfg *) media_entity_to_v4l2_subdev(link->source->entity) ->host_priv)->bus.parallel; - parallel_shift = pdata->data_lane_shift * 2; + parallel_shift = parcfg->data_lane_shift * 2; } else { parallel_shift = 0; } diff --git a/drivers/media/platform/omap3isp/ispccp2.c b/drivers/media/platform/omap3isp/ispccp2.c index 44c20fa8501a..38e6a974c5b1 100644 --- a/drivers/media/platform/omap3isp/ispccp2.c +++ b/drivers/media/platform/omap3isp/ispccp2.c @@ -201,14 +201,14 @@ static void ccp2_mem_enable(struct isp_ccp2_device *ccp2, u8 enable) /* * ccp2_phyif_config - Initialize CCP2 phy interface config * @ccp2: Pointer to ISP CCP2 device - * @pdata: CCP2 platform data + * @buscfg: CCP2 platform data * * Configure the CCP2 physical interface module from platform data. * * Returns -EIO if strobe is chosen in CSI1 mode, or 0 on success. */ static int ccp2_phyif_config(struct isp_ccp2_device *ccp2, - const struct isp_ccp2_platform_data *pdata) + const struct isp_ccp2_cfg *buscfg) { struct isp_device *isp = to_isp_device(ccp2); u32 val; @@ -218,16 +218,16 @@ static int ccp2_phyif_config(struct isp_ccp2_device *ccp2, ISPCCP2_CTRL_IO_OUT_SEL | ISPCCP2_CTRL_MODE; /* Data/strobe physical layer */ BIT_SET(val, ISPCCP2_CTRL_PHY_SEL_SHIFT, ISPCCP2_CTRL_PHY_SEL_MASK, - pdata->phy_layer); + buscfg->phy_layer); BIT_SET(val, ISPCCP2_CTRL_INV_SHIFT, ISPCCP2_CTRL_INV_MASK, - pdata->strobe_clk_pol); + buscfg->strobe_clk_pol); isp_reg_writel(isp, val, OMAP3_ISP_IOMEM_CCP2, ISPCCP2_CTRL); val = isp_reg_readl(isp, OMAP3_ISP_IOMEM_CCP2, ISPCCP2_CTRL); if (!(val & ISPCCP2_CTRL_MODE)) { - if (pdata->ccp2_mode == ISP_CCP2_MODE_CCP2) + if (buscfg->ccp2_mode == ISP_CCP2_MODE_CCP2) dev_warn(isp->dev, "OMAP3 CCP2 bus not available\n"); - if (pdata->phy_layer == ISP_CCP2_PHY_DATA_STROBE) + if (buscfg->phy_layer == ISP_CCP2_PHY_DATA_STROBE) /* Strobe mode requires CCP2 */ return -EIO; } @@ -347,7 +347,7 @@ static void ccp2_lcx_config(struct isp_ccp2_device *ccp2, */ static int ccp2_if_configure(struct isp_ccp2_device *ccp2) { - const struct isp_v4l2_subdevs_group *pdata; + const struct isp_bus_cfg *buscfg; struct v4l2_mbus_framefmt *format; struct media_pad *pad; struct v4l2_subdev *sensor; @@ -358,20 +358,20 @@ static int ccp2_if_configure(struct isp_ccp2_device *ccp2) pad = media_entity_remote_pad(&ccp2->pads[CCP2_PAD_SINK]); sensor = media_entity_to_v4l2_subdev(pad->entity); - pdata = sensor->host_priv; + buscfg = sensor->host_priv; - ret = ccp2_phyif_config(ccp2, &pdata->bus.ccp2); + ret = ccp2_phyif_config(ccp2, &buscfg->bus.ccp2); if (ret < 0) return ret; - ccp2_vp_config(ccp2, pdata->bus.ccp2.vpclk_div + 1); + ccp2_vp_config(ccp2, buscfg->bus.ccp2.vpclk_div + 1); v4l2_subdev_call(sensor, sensor, g_skip_top_lines, &lines); format = &ccp2->formats[CCP2_PAD_SINK]; ccp2->if_cfg.data_start = lines; - ccp2->if_cfg.crc = pdata->bus.ccp2.crc; + ccp2->if_cfg.crc = buscfg->bus.ccp2.crc; ccp2->if_cfg.format = format->code; ccp2->if_cfg.data_size = format->height; diff --git a/drivers/media/platform/omap3isp/ispcsi2.c b/drivers/media/platform/omap3isp/ispcsi2.c index bbadf6653db7..45ac90a49889 100644 --- a/drivers/media/platform/omap3isp/ispcsi2.c +++ b/drivers/media/platform/omap3isp/ispcsi2.c @@ -548,7 +548,7 @@ int omap3isp_csi2_reset(struct isp_csi2_device *csi2) static int csi2_configure(struct isp_csi2_device *csi2) { - const struct isp_v4l2_subdevs_group *pdata; + const struct isp_bus_cfg *buscfg; struct isp_device *isp = csi2->isp; struct isp_csi2_timing_cfg *timing = &csi2->timing[0]; struct v4l2_subdev *sensor; @@ -565,14 +565,14 @@ static int csi2_configure(struct isp_csi2_device *csi2) pad = media_entity_remote_pad(&csi2->pads[CSI2_PAD_SINK]); sensor = media_entity_to_v4l2_subdev(pad->entity); - pdata = sensor->host_priv; + buscfg = sensor->host_priv; csi2->frame_skip = 0; v4l2_subdev_call(sensor, sensor, g_skip_frames, &csi2->frame_skip); - csi2->ctrl.vp_out_ctrl = pdata->bus.csi2.vpclk_div; + csi2->ctrl.vp_out_ctrl = buscfg->bus.csi2.vpclk_div; csi2->ctrl.frame_mode = ISP_CSI2_FRAME_IMMEDIATE; - csi2->ctrl.ecc_enable = pdata->bus.csi2.crc; + csi2->ctrl.ecc_enable = buscfg->bus.csi2.crc; timing->ionum = 1; timing->force_rx_mode = 1; diff --git a/drivers/media/platform/omap3isp/ispcsiphy.c b/drivers/media/platform/omap3isp/ispcsiphy.c index e033f2237a72..4486e9f492df 100644 --- a/drivers/media/platform/omap3isp/ispcsiphy.c +++ b/drivers/media/platform/omap3isp/ispcsiphy.c @@ -168,18 +168,18 @@ static int omap3isp_csiphy_config(struct isp_csiphy *phy) { struct isp_csi2_device *csi2 = phy->csi2; struct isp_pipeline *pipe = to_isp_pipeline(&csi2->subdev.entity); - struct isp_v4l2_subdevs_group *subdevs = pipe->external->host_priv; + struct isp_bus_cfg *buscfg = pipe->external->host_priv; struct isp_csiphy_lanes_cfg *lanes; int csi2_ddrclk_khz; unsigned int used_lanes = 0; unsigned int i; u32 reg; - if (subdevs->interface == ISP_INTERFACE_CCP2B_PHY1 - || subdevs->interface == ISP_INTERFACE_CCP2B_PHY2) - lanes = &subdevs->bus.ccp2.lanecfg; + if (buscfg->interface == ISP_INTERFACE_CCP2B_PHY1 + || buscfg->interface == ISP_INTERFACE_CCP2B_PHY2) + lanes = &buscfg->bus.ccp2.lanecfg; else - lanes = &subdevs->bus.csi2.lanecfg; + lanes = &buscfg->bus.csi2.lanecfg; /* Clock and data lanes verification */ for (i = 0; i < phy->num_data_lanes; i++) { @@ -203,8 +203,8 @@ static int omap3isp_csiphy_config(struct isp_csiphy *phy) * issue since the MPU power domain is forced on whilst the * ISP is in use. */ - csiphy_routing_cfg(phy, subdevs->interface, true, - subdevs->bus.ccp2.phy_layer); + csiphy_routing_cfg(phy, buscfg->interface, true, + buscfg->bus.ccp2.phy_layer); /* DPHY timing configuration */ /* CSI-2 is DDR and we only count used lanes. */ @@ -302,11 +302,10 @@ void omap3isp_csiphy_release(struct isp_csiphy *phy) struct isp_csi2_device *csi2 = phy->csi2; struct isp_pipeline *pipe = to_isp_pipeline(&csi2->subdev.entity); - struct isp_v4l2_subdevs_group *subdevs = - pipe->external->host_priv; + struct isp_bus_cfg *buscfg = pipe->external->host_priv; - csiphy_routing_cfg(phy, subdevs->interface, false, - subdevs->bus.ccp2.phy_layer); + csiphy_routing_cfg(phy, buscfg->interface, false, + buscfg->bus.ccp2.phy_layer); csiphy_power_autoswitch_enable(phy, false); csiphy_set_power(phy, ISPCSI2_PHY_CFG_PWR_CMD_OFF); regulator_disable(phy->vdd); diff --git a/include/media/omap3isp.h b/include/media/omap3isp.h index 398279dd1922..39e0748b0d31 100644 --- a/include/media/omap3isp.h +++ b/include/media/omap3isp.h @@ -45,7 +45,7 @@ enum { }; /** - * struct isp_parallel_platform_data - Parallel interface platform data + * struct isp_parallel_cfg - Parallel interface configuration * @data_lane_shift: Data lane shifter * ISP_LANE_SHIFT_0 - CAMEXT[13:0] -> CAM[13:0] * ISP_LANE_SHIFT_2 - CAMEXT[13:2] -> CAM[11:0] @@ -62,7 +62,7 @@ enum { * @data_pol: Data polarity * 0 - Normal, 1 - One's complement */ -struct isp_parallel_platform_data { +struct isp_parallel_cfg { unsigned int data_lane_shift:2; unsigned int clk_pol:1; unsigned int hs_pol:1; @@ -105,7 +105,7 @@ struct isp_csiphy_lanes_cfg { }; /** - * struct isp_ccp2_platform_data - CCP2 interface platform data + * struct isp_ccp2_cfg - CCP2 interface configuration * @strobe_clk_pol: Strobe/clock polarity * 0 - Non Inverted, 1 - Inverted * @crc: Enable the cyclic redundancy check @@ -117,7 +117,7 @@ struct isp_csiphy_lanes_cfg { * ISP_CCP2_PHY_DATA_STROBE - Data/strobe physical layer * @vpclk_div: Video port output clock control */ -struct isp_ccp2_platform_data { +struct isp_ccp2_cfg { unsigned int strobe_clk_pol:1; unsigned int crc:1; unsigned int ccp2_mode:1; @@ -127,31 +127,31 @@ struct isp_ccp2_platform_data { }; /** - * struct isp_csi2_platform_data - CSI2 interface platform data + * struct isp_csi2_cfg - CSI2 interface configuration * @crc: Enable the cyclic redundancy check * @vpclk_div: Video port output clock control */ -struct isp_csi2_platform_data { +struct isp_csi2_cfg { unsigned crc:1; unsigned vpclk_div:2; struct isp_csiphy_lanes_cfg lanecfg; }; -struct isp_subdev_i2c_board_info { - struct i2c_board_info *board_info; - int i2c_adapter_id; -}; - -struct isp_v4l2_subdevs_group { - struct isp_subdev_i2c_board_info *subdevs; +struct isp_bus_cfg { enum isp_interface_type interface; union { - struct isp_parallel_platform_data parallel; - struct isp_ccp2_platform_data ccp2; - struct isp_csi2_platform_data csi2; + struct isp_parallel_cfg parallel; + struct isp_ccp2_cfg ccp2; + struct isp_csi2_cfg csi2; } bus; /* gcc < 4.6.0 chokes on anonymous union initializers */ }; +struct isp_platform_subdev { + struct i2c_board_info *board_info; + int i2c_adapter_id; + struct isp_bus_cfg *bus; +}; + struct isp_platform_xclk { const char *dev_id; const char *con_id; @@ -159,7 +159,7 @@ struct isp_platform_xclk { struct isp_platform_data { struct isp_platform_xclk xclks[2]; - struct isp_v4l2_subdevs_group *subdevs; + struct isp_platform_subdev *subdevs; void (*set_constraints)(struct isp_device *isp, bool enable); }; -- cgit v1.2.3 From 6387b75284aa7b78c2e947934fb874444ab427e9 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Wed, 25 Mar 2015 19:57:32 -0300 Subject: [media] omap3isp: Calculate vpclk_div for CSI-2 The video port clock is l3_ick divided by vpclk_div. This clock must be high enough for the external pixel rate. The video port requires two clock cycles to process a pixel. Signed-off-by: Sakari Ailus Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/omap3isp/ispcsi2.c | 8 +++++++- include/media/omap3isp.h | 2 -- 2 files changed, 7 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/drivers/media/platform/omap3isp/ispcsi2.c b/drivers/media/platform/omap3isp/ispcsi2.c index 45ac90a49889..a78338d012b4 100644 --- a/drivers/media/platform/omap3isp/ispcsi2.c +++ b/drivers/media/platform/omap3isp/ispcsi2.c @@ -548,6 +548,7 @@ int omap3isp_csi2_reset(struct isp_csi2_device *csi2) static int csi2_configure(struct isp_csi2_device *csi2) { + struct isp_pipeline *pipe = to_isp_pipeline(&csi2->subdev.entity); const struct isp_bus_cfg *buscfg; struct isp_device *isp = csi2->isp; struct isp_csi2_timing_cfg *timing = &csi2->timing[0]; @@ -570,7 +571,12 @@ static int csi2_configure(struct isp_csi2_device *csi2) csi2->frame_skip = 0; v4l2_subdev_call(sensor, sensor, g_skip_frames, &csi2->frame_skip); - csi2->ctrl.vp_out_ctrl = buscfg->bus.csi2.vpclk_div; + csi2->ctrl.vp_out_ctrl = + clamp_t(unsigned int, pipe->l3_ick / pipe->external_rate - 1, + 1, 3); + dev_dbg(isp->dev, "%s: l3_ick %lu, external_rate %u, vp_out_ctrl %u\n", + __func__, pipe->l3_ick, pipe->external_rate, + csi2->ctrl.vp_out_ctrl); csi2->ctrl.frame_mode = ISP_CSI2_FRAME_IMMEDIATE; csi2->ctrl.ecc_enable = buscfg->bus.csi2.crc; diff --git a/include/media/omap3isp.h b/include/media/omap3isp.h index 39e0748b0d31..0f0c08b48829 100644 --- a/include/media/omap3isp.h +++ b/include/media/omap3isp.h @@ -129,11 +129,9 @@ struct isp_ccp2_cfg { /** * struct isp_csi2_cfg - CSI2 interface configuration * @crc: Enable the cyclic redundancy check - * @vpclk_div: Video port output clock control */ struct isp_csi2_cfg { unsigned crc:1; - unsigned vpclk_div:2; struct isp_csiphy_lanes_cfg lanecfg; }; -- cgit v1.2.3 From b6eec1c4939962838ff51b10a8feb7a49bccc0d2 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Wed, 25 Mar 2015 19:57:37 -0300 Subject: [media] v4l: of: Read lane-polarities endpoint property Add lane_polarities field to struct v4l2_of_bus_mipi_csi2 and write the contents of the lane-polarities property to it. The field tells the polarity of the physical lanes starting from the first one. Any unused lanes are ignored, i.e. only the polarity of the used lanes is specified. Also rework reading the "data-lanes" property a little. Signed-off-by: Sakari Ailus Acked-by: Laurent Pinchart Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-of.c | 41 ++++++++++++++++++++++++++++++--------- include/media/v4l2-of.h | 3 +++ 2 files changed, 35 insertions(+), 9 deletions(-) (limited to 'include') diff --git a/drivers/media/v4l2-core/v4l2-of.c b/drivers/media/v4l2-core/v4l2-of.c index b4ed9a955fbe..58e401f4893a 100644 --- a/drivers/media/v4l2-core/v4l2-of.c +++ b/drivers/media/v4l2-core/v4l2-of.c @@ -19,11 +19,10 @@ #include -static void v4l2_of_parse_csi_bus(const struct device_node *node, - struct v4l2_of_endpoint *endpoint) +static int v4l2_of_parse_csi_bus(const struct device_node *node, + struct v4l2_of_endpoint *endpoint) { struct v4l2_of_bus_mipi_csi2 *bus = &endpoint->bus.mipi_csi2; - u32 data_lanes[ARRAY_SIZE(bus->data_lanes)]; struct property *prop; bool have_clk_lane = false; unsigned int flags = 0; @@ -32,16 +31,34 @@ static void v4l2_of_parse_csi_bus(const struct device_node *node, prop = of_find_property(node, "data-lanes", NULL); if (prop) { const __be32 *lane = NULL; - int i; + unsigned int i; - for (i = 0; i < ARRAY_SIZE(data_lanes); i++) { - lane = of_prop_next_u32(prop, lane, &data_lanes[i]); + for (i = 0; i < ARRAY_SIZE(bus->data_lanes); i++) { + lane = of_prop_next_u32(prop, lane, &v); if (!lane) break; + bus->data_lanes[i] = v; } bus->num_data_lanes = i; - while (i--) - bus->data_lanes[i] = data_lanes[i]; + } + + prop = of_find_property(node, "lane-polarities", NULL); + if (prop) { + const __be32 *polarity = NULL; + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(bus->lane_polarities); i++) { + polarity = of_prop_next_u32(prop, polarity, &v); + if (!polarity) + break; + bus->lane_polarities[i] = v; + } + + if (i < 1 + bus->num_data_lanes /* clock + data */) { + pr_warn("%s: too few lane-polarities entries (need %u, got %u)\n", + node->full_name, 1 + bus->num_data_lanes, i); + return -EINVAL; + } } if (!of_property_read_u32(node, "clock-lanes", &v)) { @@ -56,6 +73,8 @@ static void v4l2_of_parse_csi_bus(const struct device_node *node, bus->flags = flags; endpoint->bus_type = V4L2_MBUS_CSI2; + + return 0; } static void v4l2_of_parse_parallel_bus(const struct device_node *node, @@ -127,11 +146,15 @@ static void v4l2_of_parse_parallel_bus(const struct device_node *node, int v4l2_of_parse_endpoint(const struct device_node *node, struct v4l2_of_endpoint *endpoint) { + int rval; + of_graph_parse_endpoint(node, &endpoint->base); endpoint->bus_type = 0; memset(&endpoint->bus, 0, sizeof(endpoint->bus)); - v4l2_of_parse_csi_bus(node, endpoint); + rval = v4l2_of_parse_csi_bus(node, endpoint); + if (rval) + return rval; /* * Parse the parallel video bus properties only if none * of the MIPI CSI-2 specific properties were found. diff --git a/include/media/v4l2-of.h b/include/media/v4l2-of.h index 70fa7b7b0487..2de42c584eb2 100644 --- a/include/media/v4l2-of.h +++ b/include/media/v4l2-of.h @@ -29,12 +29,15 @@ struct device_node; * @data_lanes: an array of physical data lane indexes * @clock_lane: physical lane index of the clock lane * @num_data_lanes: number of data lanes + * @lane_polarities: polarity of the lanes. The order is the same of + * the physical lanes. */ struct v4l2_of_bus_mipi_csi2 { unsigned int flags; unsigned char data_lanes[4]; unsigned char clock_lane; unsigned short num_data_lanes; + bool lane_polarities[5]; }; /** -- cgit v1.2.3 From 802f42a8d99254b8602bf65cc50e8333bf479f13 Mon Sep 17 00:00:00 2001 From: Ido Shamay Date: Thu, 2 Apr 2015 16:31:06 +0300 Subject: net/mlx4: Add RSS support for fragmented IP datagrams Enable RSS support for fragmented IP packets, when device supports it. Until now, fragmented IP packets were directed only to the default_qpn. Since IP fragments (datagram) have no upper protocols (L3 IP packets), hash is performed on 3-tuple - dst MAC, source IP and dest IP. The HW makes sure that this holds for the 1st fragment too, so all fragments go to the same QP. Signed-off-by: Ido Shamay Signed-off-by: Or Gerlitz Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlx4/fw.c | 12 ++++++++++++ drivers/net/ethernet/mellanox/mlx4/fw.h | 1 + drivers/net/ethernet/mellanox/mlx4/main.c | 2 ++ include/linux/mlx4/device.h | 1 + 4 files changed, 16 insertions(+) (limited to 'include') diff --git a/drivers/net/ethernet/mellanox/mlx4/fw.c b/drivers/net/ethernet/mellanox/mlx4/fw.c index 209a6171e59b..412e019e8b2e 100644 --- a/drivers/net/ethernet/mellanox/mlx4/fw.c +++ b/drivers/net/ethernet/mellanox/mlx4/fw.c @@ -105,6 +105,7 @@ static void dump_dev_cap_flags(struct mlx4_dev *dev, u64 flags) [41] = "Unicast VEP steering support", [42] = "Multicast VEP steering support", [48] = "Counters support", + [52] = "RSS IP fragments support", [53] = "Port ETS Scheduler support", [55] = "Port link type sensing support", [59] = "Port management change event support", @@ -1138,6 +1139,9 @@ int mlx4_QUERY_DEV_CAP_wrapper(struct mlx4_dev *dev, int slave, } for (; slave_port < dev->caps.num_ports; ++slave_port) flags &= ~(MLX4_DEV_CAP_FLAG_WOL_PORT1 << slave_port); + + /* Not exposing RSS IP fragments to guests */ + flags &= ~MLX4_DEV_CAP_FLAG_RSS_IP_FRAG; MLX4_PUT(outbox->buf, flags, QUERY_DEV_CAP_EXT_FLAGS_OFFSET); MLX4_GET(field, outbox->buf, QUERY_DEV_CAP_VL_PORT_OFFSET); @@ -1701,6 +1705,10 @@ int mlx4_INIT_HCA(struct mlx4_dev *dev, struct mlx4_init_hca_param *param) if (dev->caps.flags & MLX4_DEV_CAP_FLAG_COUNTERS) *(inbox + INIT_HCA_FLAGS_OFFSET / 4) |= cpu_to_be32(1 << 4); + /* Enable RSS spread to fragmented IP packets when supported */ + if (dev->caps.flags & MLX4_DEV_CAP_FLAG_RSS_IP_FRAG) + *(inbox + INIT_HCA_FLAGS_OFFSET / 4) |= cpu_to_be32(1 << 13); + /* CX3 is capable of extending CQEs/EQEs from 32 to 64 bytes */ if (dev->caps.flags & MLX4_DEV_CAP_FLAG_64B_EQE) { *(inbox + INIT_HCA_EQE_CQE_OFFSETS / 4) |= cpu_to_be32(1 << 29); @@ -1889,6 +1897,10 @@ int mlx4_QUERY_HCA(struct mlx4_dev *dev, else param->steering_mode = MLX4_STEERING_MODE_A0; } + + if (dword_field & (1 << 13)) + param->rss_ip_frags = 1; + /* steering attributes */ if (param->steering_mode == MLX4_STEERING_MODE_DEVICE_MANAGED) { MLX4_GET(param->mc_base, outbox, INIT_HCA_FS_BASE_OFFSET); diff --git a/drivers/net/ethernet/mellanox/mlx4/fw.h b/drivers/net/ethernet/mellanox/mlx4/fw.h index 863655bd3947..07cb7c2461ad 100644 --- a/drivers/net/ethernet/mellanox/mlx4/fw.h +++ b/drivers/net/ethernet/mellanox/mlx4/fw.h @@ -203,6 +203,7 @@ struct mlx4_init_hca_param { u64 dev_cap_enabled; u16 cqe_size; /* For use only when CQE stride feature enabled */ u16 eqe_size; /* For use only when EQE stride feature enabled */ + u8 rss_ip_frags; }; struct mlx4_init_ib_param { diff --git a/drivers/net/ethernet/mellanox/mlx4/main.c b/drivers/net/ethernet/mellanox/mlx4/main.c index 43aa76775b5f..ee0a67069b57 100644 --- a/drivers/net/ethernet/mellanox/mlx4/main.c +++ b/drivers/net/ethernet/mellanox/mlx4/main.c @@ -885,6 +885,8 @@ static int mlx4_slave_cap(struct mlx4_dev *dev) mlx4_warn(dev, "Timestamping is not supported in slave mode\n"); slave_adjust_steering_mode(dev, &dev_cap, &hca_param); + mlx4_dbg(dev, "RSS support for IP fragments is %s\n", + hca_param.rss_ip_frags ? "on" : "off"); if (func_cap.extra_flags & MLX4_QUERY_FUNC_FLAGS_BF_RES_QP && dev->caps.bf_reg_size) diff --git a/include/linux/mlx4/device.h b/include/linux/mlx4/device.h index ab7ebec943b8..41eaafdfb1cd 100644 --- a/include/linux/mlx4/device.h +++ b/include/linux/mlx4/device.h @@ -174,6 +174,7 @@ enum { MLX4_DEV_CAP_FLAG_VEP_UC_STEER = 1LL << 41, MLX4_DEV_CAP_FLAG_VEP_MC_STEER = 1LL << 42, MLX4_DEV_CAP_FLAG_COUNTERS = 1LL << 48, + MLX4_DEV_CAP_FLAG_RSS_IP_FRAG = 1LL << 52, MLX4_DEV_CAP_FLAG_SET_ETH_SCHED = 1LL << 53, MLX4_DEV_CAP_FLAG_SENSE_SUPPORT = 1LL << 55, MLX4_DEV_CAP_FLAG_PORT_MNG_CHG_EV = 1LL << 59, -- cgit v1.2.3 From fccea6436a5bd16aed6f722efce13ec2c90427d7 Mon Sep 17 00:00:00 2001 From: Ido Shamay Date: Thu, 2 Apr 2015 16:31:08 +0300 Subject: net/mlx4: Make mlx4_is_eth visible inline funcion Currently implemented as static function in resource_tracker.c -- this change will allow other files in mlx4_core to use it as well. Signed-off-by: Ido Shamay Signed-off-by: Or Gerlitz Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlx4/resource_tracker.c | 5 ----- include/linux/mlx4/device.h | 5 +++++ 2 files changed, 5 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c b/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c index 11bcd27e218f..fd2520aa8a8c 100644 --- a/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c +++ b/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c @@ -221,11 +221,6 @@ struct res_fs_rule { int qpn; }; -static int mlx4_is_eth(struct mlx4_dev *dev, int port) -{ - return dev->caps.port_mask[port] == MLX4_PORT_TYPE_IB ? 0 : 1; -} - static void *res_tracker_lookup(struct rb_root *root, u64 res_id) { struct rb_node *node = root->rb_node; diff --git a/include/linux/mlx4/device.h b/include/linux/mlx4/device.h index 41eaafdfb1cd..0f7f13d6a03e 100644 --- a/include/linux/mlx4/device.h +++ b/include/linux/mlx4/device.h @@ -1002,6 +1002,11 @@ static inline int mlx4_is_slave(struct mlx4_dev *dev) return dev->flags & MLX4_FLAG_SLAVE; } +static inline int mlx4_is_eth(struct mlx4_dev *dev, int port) +{ + return dev->caps.port_type[port] == MLX4_PORT_TYPE_IB ? 0 : 1; +} + int mlx4_buf_alloc(struct mlx4_dev *dev, int size, int max_direct, struct mlx4_buf *buf, gfp_t gfp); void mlx4_buf_free(struct mlx4_dev *dev, int size, struct mlx4_buf *buf); -- cgit v1.2.3 From 12a889c057504fbf307dd237aedb87263ef2848a Mon Sep 17 00:00:00 2001 From: Ido Shamay Date: Thu, 2 Apr 2015 16:31:10 +0300 Subject: net/mlx4: New file for QoS related firmware commands Create two new files fw_qos.h and fw_qos.c in mlx4_core module. It gathers all relevant QoS firmware related commands etc, thus improving encapsulation of the mlx4_core module. For now it contains the QoS existing commands: mlx4_SET_PORT_SCHEDULER and mlx4_SET_PORT_PRIO2TC. Signed-off-by: Ido Shamay Signed-off-by: Or Gerlitz Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlx4/Makefile | 5 +- drivers/net/ethernet/mellanox/mlx4/en_dcb_nl.c | 1 + drivers/net/ethernet/mellanox/mlx4/fw_qos.c | 125 +++++++++++++++++++++++++ drivers/net/ethernet/mellanox/mlx4/fw_qos.h | 75 +++++++++++++++ drivers/net/ethernet/mellanox/mlx4/mlx4.h | 15 --- drivers/net/ethernet/mellanox/mlx4/port.c | 71 -------------- include/linux/mlx4/device.h | 5 - 7 files changed, 204 insertions(+), 93 deletions(-) create mode 100644 drivers/net/ethernet/mellanox/mlx4/fw_qos.c create mode 100644 drivers/net/ethernet/mellanox/mlx4/fw_qos.h (limited to 'include') diff --git a/drivers/net/ethernet/mellanox/mlx4/Makefile b/drivers/net/ethernet/mellanox/mlx4/Makefile index 3e9c70f15b42..c82217e0d22d 100644 --- a/drivers/net/ethernet/mellanox/mlx4/Makefile +++ b/drivers/net/ethernet/mellanox/mlx4/Makefile @@ -1,7 +1,8 @@ obj-$(CONFIG_MLX4_CORE) += mlx4_core.o -mlx4_core-y := alloc.o catas.o cmd.o cq.o eq.o fw.o icm.o intf.o main.o mcg.o \ - mr.o pd.o port.o profile.o qp.o reset.o sense.o srq.o resource_tracker.o +mlx4_core-y := alloc.o catas.o cmd.o cq.o eq.o fw.o fw_qos.o icm.o intf.o \ + main.o mcg.o mr.o pd.o port.o profile.o qp.o reset.o sense.o \ + srq.o resource_tracker.o obj-$(CONFIG_MLX4_EN) += mlx4_en.o diff --git a/drivers/net/ethernet/mellanox/mlx4/en_dcb_nl.c b/drivers/net/ethernet/mellanox/mlx4/en_dcb_nl.c index 8e3260c0eaa5..f01918c63f28 100644 --- a/drivers/net/ethernet/mellanox/mlx4/en_dcb_nl.c +++ b/drivers/net/ethernet/mellanox/mlx4/en_dcb_nl.c @@ -35,6 +35,7 @@ #include #include "mlx4_en.h" +#include "fw_qos.h" /* Definitions for QCN */ diff --git a/drivers/net/ethernet/mellanox/mlx4/fw_qos.c b/drivers/net/ethernet/mellanox/mlx4/fw_qos.c new file mode 100644 index 000000000000..0f5af7cf4547 --- /dev/null +++ b/drivers/net/ethernet/mellanox/mlx4/fw_qos.c @@ -0,0 +1,125 @@ +/* + * Copyright (c) 2004, 2005 Topspin Communications. All rights reserved. + * Copyright (c) 2005, 2006, 2007, 2008 Mellanox Technologies. + * All rights reserved. + * + * This software is available to you under a choice of one of two + * licenses. You may choose to be licensed under the terms of the GNU + * General Public License (GPL) Version 2, available from the file + * COPYING in the main directory of this source tree, or the + * OpenIB.org BSD license below: + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include "fw_qos.h" + +struct mlx4_set_port_prio2tc_context { + u8 prio2tc[4]; +}; + +struct mlx4_port_scheduler_tc_cfg_be { + __be16 pg; + __be16 bw_precentage; + __be16 max_bw_units; /* 3-100Mbps, 4-1Gbps, other values - reserved */ + __be16 max_bw_value; +}; + +struct mlx4_set_port_scheduler_context { + struct mlx4_port_scheduler_tc_cfg_be tc[MLX4_NUM_TC]; +}; + +int mlx4_SET_PORT_PRIO2TC(struct mlx4_dev *dev, u8 port, u8 *prio2tc) +{ + struct mlx4_cmd_mailbox *mailbox; + struct mlx4_set_port_prio2tc_context *context; + int err; + u32 in_mod; + int i; + + mailbox = mlx4_alloc_cmd_mailbox(dev); + if (IS_ERR(mailbox)) + return PTR_ERR(mailbox); + + context = mailbox->buf; + + for (i = 0; i < MLX4_NUM_UP; i += 2) + context->prio2tc[i >> 1] = prio2tc[i] << 4 | prio2tc[i + 1]; + + in_mod = MLX4_SET_PORT_PRIO2TC << 8 | port; + err = mlx4_cmd(dev, mailbox->dma, in_mod, 1, MLX4_CMD_SET_PORT, + MLX4_CMD_TIME_CLASS_B, MLX4_CMD_NATIVE); + + mlx4_free_cmd_mailbox(dev, mailbox); + return err; +} +EXPORT_SYMBOL(mlx4_SET_PORT_PRIO2TC); + +int mlx4_SET_PORT_SCHEDULER(struct mlx4_dev *dev, u8 port, u8 *tc_tx_bw, + u8 *pg, u16 *ratelimit) +{ + struct mlx4_cmd_mailbox *mailbox; + struct mlx4_set_port_scheduler_context *context; + int err; + u32 in_mod; + int i; + + mailbox = mlx4_alloc_cmd_mailbox(dev); + if (IS_ERR(mailbox)) + return PTR_ERR(mailbox); + + context = mailbox->buf; + + for (i = 0; i < MLX4_NUM_TC; i++) { + struct mlx4_port_scheduler_tc_cfg_be *tc = &context->tc[i]; + u16 r; + + if (ratelimit && ratelimit[i]) { + if (ratelimit[i] <= MLX4_MAX_100M_UNITS_VAL) { + r = ratelimit[i]; + tc->max_bw_units = + htons(MLX4_RATELIMIT_100M_UNITS); + } else { + r = ratelimit[i] / 10; + tc->max_bw_units = + htons(MLX4_RATELIMIT_1G_UNITS); + } + tc->max_bw_value = htons(r); + } else { + tc->max_bw_value = htons(MLX4_RATELIMIT_DEFAULT); + tc->max_bw_units = htons(MLX4_RATELIMIT_1G_UNITS); + } + + tc->pg = htons(pg[i]); + tc->bw_precentage = htons(tc_tx_bw[i]); + } + + in_mod = MLX4_SET_PORT_SCHEDULER << 8 | port; + err = mlx4_cmd(dev, mailbox->dma, in_mod, 1, MLX4_CMD_SET_PORT, + MLX4_CMD_TIME_CLASS_B, MLX4_CMD_NATIVE); + + mlx4_free_cmd_mailbox(dev, mailbox); + return err; +} +EXPORT_SYMBOL(mlx4_SET_PORT_SCHEDULER); diff --git a/drivers/net/ethernet/mellanox/mlx4/fw_qos.h b/drivers/net/ethernet/mellanox/mlx4/fw_qos.h new file mode 100644 index 000000000000..ab9be0fad9c8 --- /dev/null +++ b/drivers/net/ethernet/mellanox/mlx4/fw_qos.h @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2004, 2005 Topspin Communications. All rights reserved. + * Copyright (c) 2005, 2006, 2007, 2008 Mellanox Technologies. + * All rights reserved. + * + * This software is available to you under a choice of one of two + * licenses. You may choose to be licensed under the terms of the GNU + * General Public License (GPL) Version 2, available from the file + * COPYING in the main directory of this source tree, or the + * OpenIB.org BSD license below: + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef MLX4_FW_QOS_H +#define MLX4_FW_QOS_H + +#include +#include + +#define MLX4_NUM_UP 8 +#define MLX4_NUM_TC 8 + +/** + * mlx4_SET_PORT_PRIO2TC - This routine maps user priorities to traffic + * classes of a given port and device. + * + * @dev: mlx4_dev. + * @port: Physical port number. + * @prio2tc: Array of TC associated with each priorities. + * + * Returns 0 on success or a negative mlx4_core errno code. + **/ +int mlx4_SET_PORT_PRIO2TC(struct mlx4_dev *dev, u8 port, u8 *prio2tc); + +/** + * mlx4_SET_PORT_SCHEDULER - This routine configures the arbitration between + * traffic classes (ETS) and configured rate limit for traffic classes. + * tc_tx_bw, pg and ratelimit are arrays where each index represents a TC. + * The description for those parameters below refers to a single TC. + * + * @dev: mlx4_dev. + * @port: Physical port number. + * @tc_tx_bw: The percentage of the bandwidth allocated for traffic class + * within a TC group. The sum of the bw_percentage of all the traffic + * classes within a TC group must equal 100% for correct operation. + * @pg: The TC group the traffic class is associated with. + * @ratelimit: The maximal bandwidth allowed for the use by this traffic class. + * + * Returns 0 on success or a negative mlx4_core errno code. + **/ +int mlx4_SET_PORT_SCHEDULER(struct mlx4_dev *dev, u8 port, u8 *tc_tx_bw, + u8 *pg, u16 *ratelimit); + +#endif /* MLX4_FW_QOS_H */ diff --git a/drivers/net/ethernet/mellanox/mlx4/mlx4.h b/drivers/net/ethernet/mellanox/mlx4/mlx4.h index 0b16db015745..9e5236b1b5f8 100644 --- a/drivers/net/ethernet/mellanox/mlx4/mlx4.h +++ b/drivers/net/ethernet/mellanox/mlx4/mlx4.h @@ -64,21 +64,6 @@ #define INIT_HCA_TPT_MW_ENABLE (1 << 7) -struct mlx4_set_port_prio2tc_context { - u8 prio2tc[4]; -}; - -struct mlx4_port_scheduler_tc_cfg_be { - __be16 pg; - __be16 bw_precentage; - __be16 max_bw_units; /* 3-100Mbps, 4-1Gbps, other values - reserved */ - __be16 max_bw_value; -}; - -struct mlx4_set_port_scheduler_context { - struct mlx4_port_scheduler_tc_cfg_be tc[MLX4_NUM_TC]; -}; - enum { MLX4_HCR_BASE = 0x80680, MLX4_HCR_SIZE = 0x0001c, diff --git a/drivers/net/ethernet/mellanox/mlx4/port.c b/drivers/net/ethernet/mellanox/mlx4/port.c index b97f173ab062..6a53d42db52f 100644 --- a/drivers/net/ethernet/mellanox/mlx4/port.c +++ b/drivers/net/ethernet/mellanox/mlx4/port.c @@ -1016,77 +1016,6 @@ int mlx4_SET_PORT_qpn_calc(struct mlx4_dev *dev, u8 port, u32 base_qpn, } EXPORT_SYMBOL(mlx4_SET_PORT_qpn_calc); -int mlx4_SET_PORT_PRIO2TC(struct mlx4_dev *dev, u8 port, u8 *prio2tc) -{ - struct mlx4_cmd_mailbox *mailbox; - struct mlx4_set_port_prio2tc_context *context; - int err; - u32 in_mod; - int i; - - mailbox = mlx4_alloc_cmd_mailbox(dev); - if (IS_ERR(mailbox)) - return PTR_ERR(mailbox); - context = mailbox->buf; - for (i = 0; i < MLX4_NUM_UP; i += 2) - context->prio2tc[i >> 1] = prio2tc[i] << 4 | prio2tc[i + 1]; - - in_mod = MLX4_SET_PORT_PRIO2TC << 8 | port; - err = mlx4_cmd(dev, mailbox->dma, in_mod, 1, MLX4_CMD_SET_PORT, - MLX4_CMD_TIME_CLASS_B, MLX4_CMD_NATIVE); - - mlx4_free_cmd_mailbox(dev, mailbox); - return err; -} -EXPORT_SYMBOL(mlx4_SET_PORT_PRIO2TC); - -int mlx4_SET_PORT_SCHEDULER(struct mlx4_dev *dev, u8 port, u8 *tc_tx_bw, - u8 *pg, u16 *ratelimit) -{ - struct mlx4_cmd_mailbox *mailbox; - struct mlx4_set_port_scheduler_context *context; - int err; - u32 in_mod; - int i; - - mailbox = mlx4_alloc_cmd_mailbox(dev); - if (IS_ERR(mailbox)) - return PTR_ERR(mailbox); - context = mailbox->buf; - - for (i = 0; i < MLX4_NUM_TC; i++) { - struct mlx4_port_scheduler_tc_cfg_be *tc = &context->tc[i]; - u16 r; - - if (ratelimit && ratelimit[i]) { - if (ratelimit[i] <= MLX4_MAX_100M_UNITS_VAL) { - r = ratelimit[i]; - tc->max_bw_units = - htons(MLX4_RATELIMIT_100M_UNITS); - } else { - r = ratelimit[i]/10; - tc->max_bw_units = - htons(MLX4_RATELIMIT_1G_UNITS); - } - tc->max_bw_value = htons(r); - } else { - tc->max_bw_value = htons(MLX4_RATELIMIT_DEFAULT); - tc->max_bw_units = htons(MLX4_RATELIMIT_1G_UNITS); - } - - tc->pg = htons(pg[i]); - tc->bw_precentage = htons(tc_tx_bw[i]); - } - - in_mod = MLX4_SET_PORT_SCHEDULER << 8 | port; - err = mlx4_cmd(dev, mailbox->dma, in_mod, 1, MLX4_CMD_SET_PORT, - MLX4_CMD_TIME_CLASS_B, MLX4_CMD_NATIVE); - - mlx4_free_cmd_mailbox(dev, mailbox); - return err; -} -EXPORT_SYMBOL(mlx4_SET_PORT_SCHEDULER); - enum { VXLAN_ENABLE_MODIFY = 1 << 7, VXLAN_STEERING_MODIFY = 1 << 6, diff --git a/include/linux/mlx4/device.h b/include/linux/mlx4/device.h index 0f7f13d6a03e..b676d0c0111a 100644 --- a/include/linux/mlx4/device.h +++ b/include/linux/mlx4/device.h @@ -49,8 +49,6 @@ #define MSIX_LEGACY_SZ 4 #define MIN_MSIX_P_PORT 5 -#define MLX4_NUM_UP 8 -#define MLX4_NUM_TC 8 #define MLX4_MAX_100M_UNITS_VAL 255 /* * work around: can't set values * greater then this value when @@ -1311,9 +1309,6 @@ int mlx4_SET_PORT_general(struct mlx4_dev *dev, u8 port, int mtu, u8 pptx, u8 pfctx, u8 pprx, u8 pfcrx); int mlx4_SET_PORT_qpn_calc(struct mlx4_dev *dev, u8 port, u32 base_qpn, u8 promisc); -int mlx4_SET_PORT_PRIO2TC(struct mlx4_dev *dev, u8 port, u8 *prio2tc); -int mlx4_SET_PORT_SCHEDULER(struct mlx4_dev *dev, u8 port, u8 *tc_tx_bw, - u8 *pg, u16 *ratelimit); int mlx4_SET_PORT_VXLAN(struct mlx4_dev *dev, u8 port, u8 steering, int enable); int mlx4_find_cached_mac(struct mlx4_dev *dev, u8 port, u64 mac, int *idx); int mlx4_find_cached_vlan(struct mlx4_dev *dev, u8 port, u16 vid, int *idx); -- cgit v1.2.3 From 7e95bb99a8f969d4534867452793e44cc4731ca9 Mon Sep 17 00:00:00 2001 From: Ido Shamay Date: Thu, 2 Apr 2015 16:31:11 +0300 Subject: net/mlx4: Add mlx4_ALLOCATE_VPP implementation Implements device ALLOCATE_VPP command, to be used for granular QoS configuration of VFs by the PF device. Defines and queries the amount of VPPs assigned to each port, and the amount of VPPs assigned to each priority of each port. Once the total VPPs are split between the priorities of a port, they may be assigned with a share of the BW or a rate limit. Split into two functions (get/set) whoch are supplied with mlx4_alloc_vpp_context and physical port number. Signed-off-by: Ido Shamay Signed-off-by: Or Gerlitz Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlx4/cmd.c | 9 ++++ drivers/net/ethernet/mellanox/mlx4/fw_qos.c | 75 +++++++++++++++++++++++++++++ drivers/net/ethernet/mellanox/mlx4/fw_qos.h | 28 +++++++++++ include/linux/mlx4/cmd.h | 1 + 4 files changed, 113 insertions(+) (limited to 'include') diff --git a/drivers/net/ethernet/mellanox/mlx4/cmd.c b/drivers/net/ethernet/mellanox/mlx4/cmd.c index 179ca2889434..79cc5ec57dca 100644 --- a/drivers/net/ethernet/mellanox/mlx4/cmd.c +++ b/drivers/net/ethernet/mellanox/mlx4/cmd.c @@ -1454,6 +1454,15 @@ static struct mlx4_cmd_info cmd_info[] = { .verify = NULL, .wrapper = mlx4_CMD_EPERM_wrapper, }, + { + .opcode = MLX4_CMD_ALLOCATE_VPP, + .has_inbox = false, + .has_outbox = true, + .out_is_imm = false, + .encode_slave_id = false, + .verify = NULL, + .wrapper = mlx4_CMD_EPERM_wrapper, + }, { .opcode = MLX4_CMD_CONF_SPECIAL_QP, .has_inbox = false, diff --git a/drivers/net/ethernet/mellanox/mlx4/fw_qos.c b/drivers/net/ethernet/mellanox/mlx4/fw_qos.c index 0f5af7cf4547..5ce3440b99c7 100644 --- a/drivers/net/ethernet/mellanox/mlx4/fw_qos.c +++ b/drivers/net/ethernet/mellanox/mlx4/fw_qos.c @@ -34,6 +34,13 @@ #include #include "fw_qos.h" +#include "fw.h" + +enum { + /* allocate vpp opcode modifiers */ + MLX4_ALLOCATE_VPP_ALLOCATE = 0x0, + MLX4_ALLOCATE_VPP_QUERY = 0x1 +}; struct mlx4_set_port_prio2tc_context { u8 prio2tc[4]; @@ -50,6 +57,12 @@ struct mlx4_set_port_scheduler_context { struct mlx4_port_scheduler_tc_cfg_be tc[MLX4_NUM_TC]; }; +/* Granular Qos (per VF) section */ +struct mlx4_alloc_vpp_param { + __be32 availible_vpp; + __be32 vpp_p_up[MLX4_NUM_UP]; +}; + int mlx4_SET_PORT_PRIO2TC(struct mlx4_dev *dev, u8 port, u8 *prio2tc) { struct mlx4_cmd_mailbox *mailbox; @@ -123,3 +136,65 @@ int mlx4_SET_PORT_SCHEDULER(struct mlx4_dev *dev, u8 port, u8 *tc_tx_bw, return err; } EXPORT_SYMBOL(mlx4_SET_PORT_SCHEDULER); + +int mlx4_ALLOCATE_VPP_get(struct mlx4_dev *dev, u8 port, + u16 *availible_vpp, u8 *vpp_p_up) +{ + int i; + int err; + struct mlx4_cmd_mailbox *mailbox; + struct mlx4_alloc_vpp_param *out_param; + + mailbox = mlx4_alloc_cmd_mailbox(dev); + if (IS_ERR(mailbox)) + return PTR_ERR(mailbox); + + out_param = mailbox->buf; + + err = mlx4_cmd_box(dev, 0, mailbox->dma, port, + MLX4_ALLOCATE_VPP_QUERY, + MLX4_CMD_ALLOCATE_VPP, + MLX4_CMD_TIME_CLASS_A, + MLX4_CMD_NATIVE); + if (err) + goto out; + + /* Total number of supported VPPs */ + *availible_vpp = (u16)be32_to_cpu(out_param->availible_vpp); + + for (i = 0; i < MLX4_NUM_UP; i++) + vpp_p_up[i] = (u8)be32_to_cpu(out_param->vpp_p_up[i]); + +out: + mlx4_free_cmd_mailbox(dev, mailbox); + + return err; +} +EXPORT_SYMBOL(mlx4_ALLOCATE_VPP_get); + +int mlx4_ALLOCATE_VPP_set(struct mlx4_dev *dev, u8 port, u8 *vpp_p_up) +{ + int i; + int err; + struct mlx4_cmd_mailbox *mailbox; + struct mlx4_alloc_vpp_param *in_param; + + mailbox = mlx4_alloc_cmd_mailbox(dev); + if (IS_ERR(mailbox)) + return PTR_ERR(mailbox); + + in_param = mailbox->buf; + + for (i = 0; i < MLX4_NUM_UP; i++) + in_param->vpp_p_up[i] = cpu_to_be32(vpp_p_up[i]); + + err = mlx4_cmd(dev, mailbox->dma, port, + MLX4_ALLOCATE_VPP_ALLOCATE, + MLX4_CMD_ALLOCATE_VPP, + MLX4_CMD_TIME_CLASS_A, + MLX4_CMD_NATIVE); + + mlx4_free_cmd_mailbox(dev, mailbox); + return err; +} +EXPORT_SYMBOL(mlx4_ALLOCATE_VPP_set); diff --git a/drivers/net/ethernet/mellanox/mlx4/fw_qos.h b/drivers/net/ethernet/mellanox/mlx4/fw_qos.h index ab9be0fad9c8..be79951c44ef 100644 --- a/drivers/net/ethernet/mellanox/mlx4/fw_qos.h +++ b/drivers/net/ethernet/mellanox/mlx4/fw_qos.h @@ -71,5 +71,33 @@ int mlx4_SET_PORT_PRIO2TC(struct mlx4_dev *dev, u8 port, u8 *prio2tc); **/ int mlx4_SET_PORT_SCHEDULER(struct mlx4_dev *dev, u8 port, u8 *tc_tx_bw, u8 *pg, u16 *ratelimit); +/** + * mlx4_ALLOCATE_VPP_get - Query port VPP availible resources and allocation. + * Before distribution of VPPs to priorities, only availible_vpp is returned. + * After initialization it returns the distribution of VPPs among priorities. + * + * @dev: mlx4_dev. + * @port: Physical port number. + * @availible_vpp: Pointer to variable where number of availible VPPs is stored + * @vpp_p_up: Distribution of VPPs to priorities is stored in this array + * + * Returns 0 on success or a negative mlx4_core errno code. + **/ +int mlx4_ALLOCATE_VPP_get(struct mlx4_dev *dev, u8 port, + u16 *availible_vpp, u8 *vpp_p_up); +/** + * mlx4_ALLOCATE_VPP_set - Distribution of VPPs among differnt priorities. + * The total number of VPPs assigned to all for a port must not exceed + * the value reported by availible_vpp in mlx4_ALLOCATE_VPP_get. + * VPP allocation is allowed only after the port type has been set, + * and while no QPs are open for this port. + * + * @dev: mlx4_dev. + * @port: Physical port number. + * @vpp_p_up: Allocation of VPPs to different priorities. + * + * Returns 0 on success or a negative mlx4_core errno code. + **/ +int mlx4_ALLOCATE_VPP_set(struct mlx4_dev *dev, u8 port, u8 *vpp_p_up); #endif /* MLX4_FW_QOS_H */ diff --git a/include/linux/mlx4/cmd.h b/include/linux/mlx4/cmd.h index 7299e9548906..e0f88a098fb8 100644 --- a/include/linux/mlx4/cmd.h +++ b/include/linux/mlx4/cmd.h @@ -68,6 +68,7 @@ enum { MLX4_CMD_UNMAP_ICM_AUX = 0xffb, MLX4_CMD_SET_ICM_SIZE = 0xffd, MLX4_CMD_ACCESS_REG = 0x3b, + MLX4_CMD_ALLOCATE_VPP = 0x80, /*master notify fw on finish for slave's flr*/ MLX4_CMD_INFORM_FLR_DONE = 0x5b, -- cgit v1.2.3 From 1c29146d3821be984030b49cf9509a269edc3b8b Mon Sep 17 00:00:00 2001 From: Ido Shamay Date: Thu, 2 Apr 2015 16:31:12 +0300 Subject: net/mlx4: Add mlx4_SET_VPORT_QOS implementation Add the SET_VPORT_QOS device command, which is ntended for virtual granular QoS configuration per VF in SRIOV mode. The SET_VPORT_QOS command sets and queries QoS parameters of a VPort. Each priority allowed for a VPort is assigned with a share of the BW, and a BW limitation. QoS parameters can be modified at any time, but must be initialized before any QP is associated with the VPort. Signed-off-by: Ido Shamay Signed-off-by: Or Gerlitz Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlx4/cmd.c | 9 +++ drivers/net/ethernet/mellanox/mlx4/fw_qos.c | 89 +++++++++++++++++++++++++++++ drivers/net/ethernet/mellanox/mlx4/fw_qos.h | 36 ++++++++++++ include/linux/mlx4/cmd.h | 1 + 4 files changed, 135 insertions(+) (limited to 'include') diff --git a/drivers/net/ethernet/mellanox/mlx4/cmd.c b/drivers/net/ethernet/mellanox/mlx4/cmd.c index 79cc5ec57dca..af95231ba3e2 100644 --- a/drivers/net/ethernet/mellanox/mlx4/cmd.c +++ b/drivers/net/ethernet/mellanox/mlx4/cmd.c @@ -1463,6 +1463,15 @@ static struct mlx4_cmd_info cmd_info[] = { .verify = NULL, .wrapper = mlx4_CMD_EPERM_wrapper, }, + { + .opcode = MLX4_CMD_SET_VPORT_QOS, + .has_inbox = false, + .has_outbox = true, + .out_is_imm = false, + .encode_slave_id = false, + .verify = NULL, + .wrapper = mlx4_CMD_EPERM_wrapper, + }, { .opcode = MLX4_CMD_CONF_SPECIAL_QP, .has_inbox = false, diff --git a/drivers/net/ethernet/mellanox/mlx4/fw_qos.c b/drivers/net/ethernet/mellanox/mlx4/fw_qos.c index 5ce3440b99c7..8f2fde0487c4 100644 --- a/drivers/net/ethernet/mellanox/mlx4/fw_qos.c +++ b/drivers/net/ethernet/mellanox/mlx4/fw_qos.c @@ -42,6 +42,12 @@ enum { MLX4_ALLOCATE_VPP_QUERY = 0x1 }; +enum { + /* set vport qos opcode modifiers */ + MLX4_SET_VPORT_QOS_SET = 0x0, + MLX4_SET_VPORT_QOS_QUERY = 0x1 +}; + struct mlx4_set_port_prio2tc_context { u8 prio2tc[4]; }; @@ -63,6 +69,19 @@ struct mlx4_alloc_vpp_param { __be32 vpp_p_up[MLX4_NUM_UP]; }; +struct mlx4_prio_qos_param { + __be32 bw_share; + __be32 max_avg_bw; + __be32 reserved; + __be32 enable; + __be32 reserved1[4]; +}; + +struct mlx4_set_vport_context { + __be32 reserved[8]; + struct mlx4_prio_qos_param qos_p_up[MLX4_NUM_UP]; +}; + int mlx4_SET_PORT_PRIO2TC(struct mlx4_dev *dev, u8 port, u8 *prio2tc) { struct mlx4_cmd_mailbox *mailbox; @@ -198,3 +217,73 @@ int mlx4_ALLOCATE_VPP_set(struct mlx4_dev *dev, u8 port, u8 *vpp_p_up) return err; } EXPORT_SYMBOL(mlx4_ALLOCATE_VPP_set); + +int mlx4_SET_VPORT_QOS_get(struct mlx4_dev *dev, u8 port, u8 vport, + struct mlx4_vport_qos_param *out_param) +{ + int i; + int err; + struct mlx4_cmd_mailbox *mailbox; + struct mlx4_set_vport_context *ctx; + + mailbox = mlx4_alloc_cmd_mailbox(dev); + if (IS_ERR(mailbox)) + return PTR_ERR(mailbox); + + ctx = mailbox->buf; + + err = mlx4_cmd_box(dev, 0, mailbox->dma, (vport << 8) | port, + MLX4_SET_VPORT_QOS_QUERY, + MLX4_CMD_SET_VPORT_QOS, + MLX4_CMD_TIME_CLASS_A, + MLX4_CMD_NATIVE); + if (err) + goto out; + + for (i = 0; i < MLX4_NUM_UP; i++) { + out_param[i].bw_share = be32_to_cpu(ctx->qos_p_up[i].bw_share); + out_param[i].max_avg_bw = + be32_to_cpu(ctx->qos_p_up[i].max_avg_bw); + out_param[i].enable = + !!(be32_to_cpu(ctx->qos_p_up[i].enable) & 31); + } + +out: + mlx4_free_cmd_mailbox(dev, mailbox); + + return err; +} +EXPORT_SYMBOL(mlx4_SET_VPORT_QOS_get); + +int mlx4_SET_VPORT_QOS_set(struct mlx4_dev *dev, u8 port, u8 vport, + struct mlx4_vport_qos_param *in_param) +{ + int i; + int err; + struct mlx4_cmd_mailbox *mailbox; + struct mlx4_set_vport_context *ctx; + + mailbox = mlx4_alloc_cmd_mailbox(dev); + if (IS_ERR(mailbox)) + return PTR_ERR(mailbox); + + ctx = mailbox->buf; + + for (i = 0; i < MLX4_NUM_UP; i++) { + ctx->qos_p_up[i].bw_share = cpu_to_be32(in_param[i].bw_share); + ctx->qos_p_up[i].max_avg_bw = + cpu_to_be32(in_param[i].max_avg_bw); + ctx->qos_p_up[i].enable = + cpu_to_be32(in_param[i].enable << 31); + } + + err = mlx4_cmd(dev, mailbox->dma, (vport << 8) | port, + MLX4_SET_VPORT_QOS_SET, + MLX4_CMD_SET_VPORT_QOS, + MLX4_CMD_TIME_CLASS_A, + MLX4_CMD_NATIVE); + + mlx4_free_cmd_mailbox(dev, mailbox); + return err; +} +EXPORT_SYMBOL(mlx4_SET_VPORT_QOS_set); diff --git a/drivers/net/ethernet/mellanox/mlx4/fw_qos.h b/drivers/net/ethernet/mellanox/mlx4/fw_qos.h index be79951c44ef..b3fffafff062 100644 --- a/drivers/net/ethernet/mellanox/mlx4/fw_qos.h +++ b/drivers/net/ethernet/mellanox/mlx4/fw_qos.h @@ -41,6 +41,12 @@ #define MLX4_NUM_UP 8 #define MLX4_NUM_TC 8 +struct mlx4_vport_qos_param { + u32 bw_share; + u32 max_avg_bw; + u8 enable; +}; + /** * mlx4_SET_PORT_PRIO2TC - This routine maps user priorities to traffic * classes of a given port and device. @@ -100,4 +106,34 @@ int mlx4_ALLOCATE_VPP_get(struct mlx4_dev *dev, u8 port, **/ int mlx4_ALLOCATE_VPP_set(struct mlx4_dev *dev, u8 port, u8 *vpp_p_up); +/** + * mlx4_SET_VPORT_QOS_get - Query QoS proporties of a Vport. + * Each priority allowed for the Vport is assigned with a share of the BW, + * and a BW limitation. This commands query the current QoS values. + * + * @dev: mlx4_dev. + * @port: Physical port number. + * @vport: Vport id. + * @out_param: Array of mlx4_vport_qos_param that will contain the values. + * + * Returns 0 on success or a negative mlx4_core errno code. + **/ +int mlx4_SET_VPORT_QOS_get(struct mlx4_dev *dev, u8 port, u8 vport, + struct mlx4_vport_qos_param *out_param); + +/** + * mlx4_SET_VPORT_QOS_set - Set QoS proporties of a Vport. + * QoS parameters can be modified at any time, but must be initialized + * before any QP is associated with the VPort. + * + * @dev: mlx4_dev. + * @port: Physical port number. + * @vport: Vport id. + * @out_param: Array of mlx4_vport_qos_param which holds the requested values. + * + * Returns 0 on success or a negative mlx4_core errno code. + **/ +int mlx4_SET_VPORT_QOS_set(struct mlx4_dev *dev, u8 port, u8 vport, + struct mlx4_vport_qos_param *in_param); + #endif /* MLX4_FW_QOS_H */ diff --git a/include/linux/mlx4/cmd.h b/include/linux/mlx4/cmd.h index e0f88a098fb8..88326ed0033a 100644 --- a/include/linux/mlx4/cmd.h +++ b/include/linux/mlx4/cmd.h @@ -69,6 +69,7 @@ enum { MLX4_CMD_SET_ICM_SIZE = 0xffd, MLX4_CMD_ACCESS_REG = 0x3b, MLX4_CMD_ALLOCATE_VPP = 0x80, + MLX4_CMD_SET_VPORT_QOS = 0x81, /*master notify fw on finish for slave's flr*/ MLX4_CMD_INFORM_FLR_DONE = 0x5b, -- cgit v1.2.3 From d019fcb2244519d453694bfce868f3e717bfcebb Mon Sep 17 00:00:00 2001 From: Ido Shamay Date: Thu, 2 Apr 2015 16:31:13 +0300 Subject: net/mlx4: Query device for QoS per VF support Checks in QUERY_DEV_CAP if the granular QoS per VF feature is supported by the device. Disabled for guests. Signed-off-by: Ido Shamay Signed-off-by: Or Gerlitz Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlx4/fw.c | 10 +++++++++- include/linux/mlx4/device.h | 3 ++- 2 files changed, 11 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/drivers/net/ethernet/mellanox/mlx4/fw.c b/drivers/net/ethernet/mellanox/mlx4/fw.c index 412e019e8b2e..94e857e39063 100644 --- a/drivers/net/ethernet/mellanox/mlx4/fw.c +++ b/drivers/net/ethernet/mellanox/mlx4/fw.c @@ -147,7 +147,8 @@ static void dump_dev_cap_flags2(struct mlx4_dev *dev, u64 flags) [21] = "Port Remap support", [22] = "QCN support", [23] = "QP rate limiting support", - [24] = "Ethernet Flow control statistics support" + [24] = "Ethernet Flow control statistics support", + [25] = "Granular QoS per VF support", }; int i; @@ -871,6 +872,8 @@ int mlx4_QUERY_DEV_CAP(struct mlx4_dev *dev, struct mlx4_dev_cap *dev_cap) MLX4_GET(size, outbox, QUERY_DEV_CAP_MAX_DESC_SZ_RQ_OFFSET); dev_cap->max_rq_desc_sz = size; MLX4_GET(field, outbox, QUERY_DEV_CAP_CQ_EQ_CACHE_LINE_STRIDE); + if (field & (1 << 4)) + dev_cap->flags2 |= MLX4_DEV_CAP_FLAG2_QOS_VPP; if (field & (1 << 5)) dev_cap->flags2 |= MLX4_DEV_CAP_FLAG2_ETH_PROT_CTRL; if (field & (1 << 6)) @@ -1199,6 +1202,11 @@ int mlx4_QUERY_DEV_CAP_wrapper(struct mlx4_dev *dev, int slave, field16 = 0; MLX4_PUT(outbox->buf, field16, QUERY_DEV_CAP_QP_RATE_LIMIT_NUM_OFFSET); + /* turn off QoS per VF support for guests */ + MLX4_GET(field, outbox->buf, QUERY_DEV_CAP_CQ_EQ_CACHE_LINE_STRIDE); + field &= 0xef; + MLX4_PUT(outbox->buf, field, QUERY_DEV_CAP_CQ_EQ_CACHE_LINE_STRIDE); + return 0; } diff --git a/include/linux/mlx4/device.h b/include/linux/mlx4/device.h index b676d0c0111a..c37208f7869f 100644 --- a/include/linux/mlx4/device.h +++ b/include/linux/mlx4/device.h @@ -205,7 +205,8 @@ enum { MLX4_DEV_CAP_FLAG2_PORT_REMAP = 1LL << 21, MLX4_DEV_CAP_FLAG2_QCN = 1LL << 22, MLX4_DEV_CAP_FLAG2_QP_RATE_LIMIT = 1LL << 23, - MLX4_DEV_CAP_FLAG2_FLOWSTATS_EN = 1LL << 24 + MLX4_DEV_CAP_FLAG2_FLOWSTATS_EN = 1LL << 24, + MLX4_DEV_CAP_FLAG2_QOS_VPP = 1LL << 25, }; enum { -- cgit v1.2.3 From 08068cd5683f11e4505aa9c8cc6ed5942f8ad299 Mon Sep 17 00:00:00 2001 From: Ido Shamay Date: Thu, 2 Apr 2015 16:31:15 +0300 Subject: net/mlx4: Added qos_vport QP configuration in VST mode Granular QoS per VF feature introduce a new QP field, qos_vport. PF administrator can connect VF QPs to a certain QoS Vport, to inherit its proporties. Connecting QPs to the default QoS Vport (defined as 0) is always allowed, even when there are no allocated VPPs. At this point, only the default vport is connected to QPs. Signed-off-by: Ido Shamay Signed-off-by: Or Gerlitz Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlx4/cmd.c | 9 ++++++++- drivers/net/ethernet/mellanox/mlx4/fw_qos.h | 3 +++ drivers/net/ethernet/mellanox/mlx4/mlx4.h | 2 ++ drivers/net/ethernet/mellanox/mlx4/qp.c | 5 +++++ drivers/net/ethernet/mellanox/mlx4/resource_tracker.c | 6 ++++++ include/linux/mlx4/qp.h | 8 ++++++-- 6 files changed, 30 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/drivers/net/ethernet/mellanox/mlx4/cmd.c b/drivers/net/ethernet/mellanox/mlx4/cmd.c index 7d3c64275b75..4c0c6c7e63e7 100644 --- a/drivers/net/ethernet/mellanox/mlx4/cmd.c +++ b/drivers/net/ethernet/mellanox/mlx4/cmd.c @@ -48,6 +48,7 @@ #include "mlx4.h" #include "fw.h" +#include "fw_qos.h" #define CMD_POLL_TOKEN 0xffff #define INBOX_MASK 0xffffffffffffff00ULL @@ -1808,7 +1809,8 @@ static int mlx4_master_immediate_activate_vlan_qos(struct mlx4_priv *priv, if (vp_oper->state.default_vlan == vp_admin->default_vlan && vp_oper->state.default_qos == vp_admin->default_qos && - vp_oper->state.link_state == vp_admin->link_state) + vp_oper->state.link_state == vp_admin->link_state && + vp_oper->state.qos_vport == vp_admin->qos_vport) return 0; if (!(priv->mfunc.master.slave_state[slave].active && @@ -1866,6 +1868,7 @@ static int mlx4_master_immediate_activate_vlan_qos(struct mlx4_priv *priv, vp_oper->state.default_vlan = vp_admin->default_vlan; vp_oper->state.default_qos = vp_admin->default_qos; vp_oper->state.link_state = vp_admin->link_state; + vp_oper->state.qos_vport = vp_admin->qos_vport; if (vp_admin->link_state == IFLA_VF_LINK_STATE_DISABLE) work->flags |= MLX4_VF_IMMED_VLAN_FLAG_LINK_DISABLE; @@ -1874,6 +1877,7 @@ static int mlx4_master_immediate_activate_vlan_qos(struct mlx4_priv *priv, work->port = port; work->slave = slave; work->qos = vp_oper->state.default_qos; + work->qos_vport = vp_oper->state.qos_vport; work->vlan_id = vp_oper->state.default_vlan; work->vlan_ix = vp_oper->vlan_idx; work->priv = priv; @@ -2339,6 +2343,9 @@ int mlx4_multi_func_init(struct mlx4_dev *dev) INIT_LIST_HEAD(&s_state->mcast_filters[port]); admin_vport->default_vlan = MLX4_VGT; oper_vport->default_vlan = MLX4_VGT; + admin_vport->qos_vport = + MLX4_VPP_DEFAULT_VPORT; + oper_vport->qos_vport = MLX4_VPP_DEFAULT_VPORT; vf_oper->vport[port].vlan_idx = NO_INDX; vf_oper->vport[port].mac_idx = NO_INDX; } diff --git a/drivers/net/ethernet/mellanox/mlx4/fw_qos.h b/drivers/net/ethernet/mellanox/mlx4/fw_qos.h index 4a5320f79094..ac1f331878e6 100644 --- a/drivers/net/ethernet/mellanox/mlx4/fw_qos.h +++ b/drivers/net/ethernet/mellanox/mlx4/fw_qos.h @@ -44,6 +44,9 @@ /* Default supported priorities for VPP allocation */ #define MLX4_DEFAULT_QOS_PRIO (0) +/* Derived from FW feature definition, 0 is the default vport fo all QPs */ +#define MLX4_VPP_DEFAULT_VPORT (0) + struct mlx4_vport_qos_param { u32 bw_share; u32 max_avg_bw; diff --git a/drivers/net/ethernet/mellanox/mlx4/mlx4.h b/drivers/net/ethernet/mellanox/mlx4/mlx4.h index 1add698dba5b..32469c64d580 100644 --- a/drivers/net/ethernet/mellanox/mlx4/mlx4.h +++ b/drivers/net/ethernet/mellanox/mlx4/mlx4.h @@ -498,6 +498,7 @@ struct mlx4_vport_state { u32 tx_rate; bool spoofchk; u32 link_state; + u8 qos_vport; }; struct mlx4_vf_admin_state { @@ -636,6 +637,7 @@ struct mlx4_vf_immed_vlan_work { int orig_vlan_ix; u8 port; u8 qos; + u8 qos_vport; u16 vlan_id; u16 orig_vlan_id; }; diff --git a/drivers/net/ethernet/mellanox/mlx4/qp.c b/drivers/net/ethernet/mellanox/mlx4/qp.c index 69e4462e4ee4..b75214a80d0e 100644 --- a/drivers/net/ethernet/mellanox/mlx4/qp.c +++ b/drivers/net/ethernet/mellanox/mlx4/qp.c @@ -447,6 +447,11 @@ int mlx4_update_qp(struct mlx4_dev *dev, u32 qpn, cmd->qp_context.rate_limit_params = cpu_to_be16((params->rate_unit << 14) | params->rate_val); } + if (attr & MLX4_UPDATE_QP_QOS_VPORT) { + qp_mask |= 1ULL << MLX4_UPD_QP_MASK_QOS_VPP; + cmd->qp_context.qos_vport = params->qos_vport; + } + cmd->primary_addr_path_mask = cpu_to_be64(pri_addr_path_mask); cmd->qp_mask = cpu_to_be64(qp_mask); diff --git a/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c b/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c index fd2520aa8a8c..c7f28bf4b8e2 100644 --- a/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c +++ b/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c @@ -765,6 +765,7 @@ static int update_vport_qp_param(struct mlx4_dev *dev, qpc->pri_path.feup |= MLX4_FEUP_FORCE_ETH_UP | MLX4_FVL_FORCE_ETH_VLAN; qpc->pri_path.sched_queue &= 0xC7; qpc->pri_path.sched_queue |= (vp_oper->state.default_qos) << 3; + qpc->qos_vport = vp_oper->state.qos_vport; } if (vp_oper->state.spoofchk) { qpc->pri_path.feup |= MLX4_FSM_FORCE_ETH_SRC_MAC; @@ -4917,6 +4918,11 @@ void mlx4_vf_immed_vlan_work_handler(struct work_struct *_work) qp->sched_queue & 0xC7; upd_context->qp_context.pri_path.sched_queue |= ((work->qos & 0x7) << 3); + upd_context->qp_mask |= + cpu_to_be64(1ULL << + MLX4_UPD_QP_MASK_QOS_VPP); + upd_context->qp_context.qos_vport = + work->qos_vport; } err = mlx4_cmd(dev, mailbox->dma, diff --git a/include/linux/mlx4/qp.h b/include/linux/mlx4/qp.h index 1023ebe035b7..6fed539e5456 100644 --- a/include/linux/mlx4/qp.h +++ b/include/linux/mlx4/qp.h @@ -209,7 +209,8 @@ struct mlx4_qp_context { __be16 sq_wqe_counter; u32 reserved3; __be16 rate_limit_params; - __be16 reserved4; + u8 reserved4; + u8 qos_vport; __be32 param3; __be32 nummmcpeers_basemkey; u8 log_page_size; @@ -231,6 +232,7 @@ struct mlx4_update_qp_context { enum { MLX4_UPD_QP_MASK_PM_STATE = 32, MLX4_UPD_QP_MASK_VSD = 33, + MLX4_UPD_QP_MASK_QOS_VPP = 34, MLX4_UPD_QP_MASK_RATE_LIMIT = 35, }; @@ -432,7 +434,8 @@ enum mlx4_update_qp_attr { MLX4_UPDATE_QP_SMAC = 1 << 0, MLX4_UPDATE_QP_VSD = 1 << 1, MLX4_UPDATE_QP_RATE_LIMIT = 1 << 2, - MLX4_UPDATE_QP_SUPPORTED_ATTRS = (1 << 3) - 1 + MLX4_UPDATE_QP_QOS_VPORT = 1 << 3, + MLX4_UPDATE_QP_SUPPORTED_ATTRS = (1 << 4) - 1 }; enum mlx4_update_qp_params_flags { @@ -441,6 +444,7 @@ enum mlx4_update_qp_params_flags { struct mlx4_update_qp_params { u8 smac_index; + u8 qos_vport; u32 flags; u16 rate_unit; u16 rate_val; -- cgit v1.2.3 From cda373f4849d5dd6fedceb4aeba35682a0e1a833 Mon Sep 17 00:00:00 2001 From: Ido Shamay Date: Thu, 2 Apr 2015 16:31:16 +0300 Subject: net/mlx4_en: Enable TX rate limit per VF Support granular QoS per VF, by implementing the ndo_set_vf_rate. Enforce a rate limit per VF when called, and enabled only for VFs in VST mode with user priority supported by the device. We don't enforce VFs to be in VST mode at the moment of configuration, but rather save the given rate limit and enforce it when the VF is moved to VST with user priority which is supported (currently 0). VST<->VGT or VST qos value state changes are disallowed when a rate limit is configured. Minimum BW share is not supported yet. Signed-off-by: Ido Shamay Signed-off-by: Or Gerlitz Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlx4/cmd.c | 177 ++++++++++++++++++++++++- drivers/net/ethernet/mellanox/mlx4/en_netdev.c | 11 ++ include/linux/mlx4/cmd.h | 2 + 3 files changed, 189 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/drivers/net/ethernet/mellanox/mlx4/cmd.c b/drivers/net/ethernet/mellanox/mlx4/cmd.c index 4c0c6c7e63e7..bc6e4c9b6bb5 100644 --- a/drivers/net/ethernet/mellanox/mlx4/cmd.c +++ b/drivers/net/ethernet/mellanox/mlx4/cmd.c @@ -2781,6 +2781,103 @@ static int mlx4_slaves_closest_port(struct mlx4_dev *dev, int slave, int port) return port; } +static int mlx4_set_vport_qos(struct mlx4_priv *priv, int slave, int port, + int max_tx_rate) +{ + int i; + int err; + struct mlx4_qos_manager *port_qos; + struct mlx4_dev *dev = &priv->dev; + struct mlx4_vport_qos_param vpp_qos[MLX4_NUM_UP]; + + port_qos = &priv->mfunc.master.qos_ctl[port]; + memset(vpp_qos, 0, sizeof(struct mlx4_vport_qos_param) * MLX4_NUM_UP); + + if (slave > port_qos->num_of_qos_vfs) { + mlx4_info(dev, "No availible VPP resources for this VF\n"); + return -EINVAL; + } + + /* Query for default QoS values from Vport 0 is needed */ + err = mlx4_SET_VPORT_QOS_get(dev, port, 0, vpp_qos); + if (err) { + mlx4_info(dev, "Failed to query Vport 0 QoS values\n"); + return err; + } + + for (i = 0; i < MLX4_NUM_UP; i++) { + if (test_bit(i, port_qos->priority_bm) && max_tx_rate) { + vpp_qos[i].max_avg_bw = max_tx_rate; + vpp_qos[i].enable = 1; + } else { + /* if user supplied tx_rate == 0, meaning no rate limit + * configuration is required. so we are leaving the + * value of max_avg_bw as queried from Vport 0. + */ + vpp_qos[i].enable = 0; + } + } + + err = mlx4_SET_VPORT_QOS_set(dev, port, slave, vpp_qos); + if (err) { + mlx4_info(dev, "Failed to set Vport %d QoS values\n", slave); + return err; + } + + return 0; +} + +static bool mlx4_is_vf_vst_and_prio_qos(struct mlx4_dev *dev, int port, + struct mlx4_vport_state *vf_admin) +{ + struct mlx4_qos_manager *info; + struct mlx4_priv *priv = mlx4_priv(dev); + + if (!mlx4_is_master(dev) || + !(dev->caps.flags2 & MLX4_DEV_CAP_FLAG2_QOS_VPP)) + return false; + + info = &priv->mfunc.master.qos_ctl[port]; + + if (vf_admin->default_vlan != MLX4_VGT && + test_bit(vf_admin->default_qos, info->priority_bm)) + return true; + + return false; +} + +static bool mlx4_valid_vf_state_change(struct mlx4_dev *dev, int port, + struct mlx4_vport_state *vf_admin, + int vlan, int qos) +{ + struct mlx4_vport_state dummy_admin = {0}; + + if (!mlx4_is_vf_vst_and_prio_qos(dev, port, vf_admin) || + !vf_admin->tx_rate) + return true; + + dummy_admin.default_qos = qos; + dummy_admin.default_vlan = vlan; + + /* VF wants to move to other VST state which is valid with current + * rate limit. Either differnt default vlan in VST or other + * supported QoS priority. Otherwise we don't allow this change when + * the TX rate is still configured. + */ + if (mlx4_is_vf_vst_and_prio_qos(dev, port, &dummy_admin)) + return true; + + mlx4_info(dev, "Cannot change VF state to %s while rate is set\n", + (vlan == MLX4_VGT) ? "VGT" : "VST"); + + if (vlan != MLX4_VGT) + mlx4_info(dev, "VST priority %d not supported for QoS\n", qos); + + mlx4_info(dev, "Please set rate to 0 prior to this VF state change\n"); + + return false; +} + int mlx4_set_vf_mac(struct mlx4_dev *dev, int port, int vf, u64 mac) { struct mlx4_priv *priv = mlx4_priv(dev); @@ -2824,12 +2921,22 @@ int mlx4_set_vf_vlan(struct mlx4_dev *dev, int port, int vf, u16 vlan, u8 qos) port = mlx4_slaves_closest_port(dev, slave, port); vf_admin = &priv->mfunc.master.vf_admin[slave].vport[port]; + if (!mlx4_valid_vf_state_change(dev, port, vf_admin, vlan, qos)) + return -EPERM; + if ((0 == vlan) && (0 == qos)) vf_admin->default_vlan = MLX4_VGT; else vf_admin->default_vlan = vlan; vf_admin->default_qos = qos; + /* If rate was configured prior to VST, we saved the configured rate + * in vf_admin->rate and now, if priority supported we enforce the QoS + */ + if (mlx4_is_vf_vst_and_prio_qos(dev, port, vf_admin) && + vf_admin->tx_rate) + vf_admin->qos_vport = slave; + if (mlx4_master_immediate_activate_vlan_qos(priv, slave, port)) mlx4_info(dev, "updating vf %d port %d config will take effect on next VF restart\n", @@ -2838,6 +2945,69 @@ int mlx4_set_vf_vlan(struct mlx4_dev *dev, int port, int vf, u16 vlan, u8 qos) } EXPORT_SYMBOL_GPL(mlx4_set_vf_vlan); +int mlx4_set_vf_rate(struct mlx4_dev *dev, int port, int vf, int min_tx_rate, + int max_tx_rate) +{ + int err; + int slave; + struct mlx4_vport_state *vf_admin; + struct mlx4_priv *priv = mlx4_priv(dev); + + if (!mlx4_is_master(dev) || + !(dev->caps.flags2 & MLX4_DEV_CAP_FLAG2_QOS_VPP)) + return -EPROTONOSUPPORT; + + if (min_tx_rate) { + mlx4_info(dev, "Minimum BW share not supported\n"); + return -EPROTONOSUPPORT; + } + + slave = mlx4_get_slave_indx(dev, vf); + if (slave < 0) + return -EINVAL; + + port = mlx4_slaves_closest_port(dev, slave, port); + vf_admin = &priv->mfunc.master.vf_admin[slave].vport[port]; + + err = mlx4_set_vport_qos(priv, slave, port, max_tx_rate); + if (err) { + mlx4_info(dev, "vf %d failed to set rate %d\n", vf, + max_tx_rate); + return err; + } + + vf_admin->tx_rate = max_tx_rate; + /* if VF is not in supported mode (VST with supported prio), + * we do not change vport configuration for its QPs, but save + * the rate, so it will be enforced when it moves to supported + * mode next time. + */ + if (!mlx4_is_vf_vst_and_prio_qos(dev, port, vf_admin)) { + mlx4_info(dev, + "rate set for VF %d when not in valid state\n", vf); + + if (vf_admin->default_vlan != MLX4_VGT) + mlx4_info(dev, "VST priority not supported by QoS\n"); + else + mlx4_info(dev, "VF in VGT mode (needed VST)\n"); + + mlx4_info(dev, + "rate %d take affect when VF moves to valid state\n", + max_tx_rate); + return 0; + } + + /* If user sets rate 0 assigning default vport for its QPs */ + vf_admin->qos_vport = max_tx_rate ? slave : MLX4_VPP_DEFAULT_VPORT; + + if (priv->mfunc.master.slave_state[slave].active && + dev->caps.flags2 & MLX4_DEV_CAP_FLAG2_UPDATE_QP) + mlx4_master_immediate_activate_vlan_qos(priv, slave, port); + + return 0; +} +EXPORT_SYMBOL_GPL(mlx4_set_vf_rate); + /* mlx4_get_slave_default_vlan - * return true if VST ( default vlan) * if VST, will return vlan & qos (if not NULL) @@ -2911,7 +3081,12 @@ int mlx4_get_vf_config(struct mlx4_dev *dev, int port, int vf, struct ifla_vf_in ivf->vlan = s_info->default_vlan; ivf->qos = s_info->default_qos; - ivf->max_tx_rate = s_info->tx_rate; + + if (mlx4_is_vf_vst_and_prio_qos(dev, port, s_info)) + ivf->max_tx_rate = s_info->tx_rate; + else + ivf->max_tx_rate = 0; + ivf->min_tx_rate = 0; ivf->spoofchk = s_info->spoofchk; ivf->linkstate = s_info->link_state; diff --git a/drivers/net/ethernet/mellanox/mlx4/en_netdev.c b/drivers/net/ethernet/mellanox/mlx4/en_netdev.c index d73ab1ba5a78..2a5df0aa2228 100644 --- a/drivers/net/ethernet/mellanox/mlx4/en_netdev.c +++ b/drivers/net/ethernet/mellanox/mlx4/en_netdev.c @@ -2240,6 +2240,16 @@ static int mlx4_en_set_vf_vlan(struct net_device *dev, int vf, u16 vlan, u8 qos) return mlx4_set_vf_vlan(mdev->dev, en_priv->port, vf, vlan, qos); } +static int mlx4_en_set_vf_rate(struct net_device *dev, int vf, int min_tx_rate, + int max_tx_rate) +{ + struct mlx4_en_priv *en_priv = netdev_priv(dev); + struct mlx4_en_dev *mdev = en_priv->mdev; + + return mlx4_set_vf_rate(mdev->dev, en_priv->port, vf, min_tx_rate, + max_tx_rate); +} + static int mlx4_en_set_vf_spoofchk(struct net_device *dev, int vf, bool setting) { struct mlx4_en_priv *en_priv = netdev_priv(dev); @@ -2458,6 +2468,7 @@ static const struct net_device_ops mlx4_netdev_ops_master = { .ndo_vlan_rx_kill_vid = mlx4_en_vlan_rx_kill_vid, .ndo_set_vf_mac = mlx4_en_set_vf_mac, .ndo_set_vf_vlan = mlx4_en_set_vf_vlan, + .ndo_set_vf_rate = mlx4_en_set_vf_rate, .ndo_set_vf_spoofchk = mlx4_en_set_vf_spoofchk, .ndo_set_vf_link_state = mlx4_en_set_vf_link_state, .ndo_get_vf_config = mlx4_en_get_vf_config, diff --git a/include/linux/mlx4/cmd.h b/include/linux/mlx4/cmd.h index 88326ed0033a..a788839f54bb 100644 --- a/include/linux/mlx4/cmd.h +++ b/include/linux/mlx4/cmd.h @@ -296,6 +296,8 @@ void mlx4_free_cmd_mailbox(struct mlx4_dev *dev, struct mlx4_cmd_mailbox *mailbo u32 mlx4_comm_get_version(void); int mlx4_set_vf_mac(struct mlx4_dev *dev, int port, int vf, u64 mac); int mlx4_set_vf_vlan(struct mlx4_dev *dev, int port, int vf, u16 vlan, u8 qos); +int mlx4_set_vf_rate(struct mlx4_dev *dev, int port, int vf, int min_tx_rate, + int max_tx_rate); int mlx4_set_vf_spoofchk(struct mlx4_dev *dev, int port, int vf, bool setting); int mlx4_get_vf_config(struct mlx4_dev *dev, int port, int vf, struct ifla_vf_info *ivf); int mlx4_set_vf_link_state(struct mlx4_dev *dev, int port, int vf, int link_state); -- cgit v1.2.3 From 3742cc65512cd4897a63dce94104f9a6e74997a0 Mon Sep 17 00:00:00 2001 From: Ido Shamay Date: Thu, 2 Apr 2015 16:31:17 +0300 Subject: net/mlx4: Warn users of depracated QoS Firmware A new capability bit was introduced in the past to to differ devices using the QoS ETS feature. The old was deprecated since then. If driver sees device which set only the old capabilty, it will print warning to user suggesting to upgrade the FW. Signed-off-by: Ido Shamay Signed-off-by: Or Gerlitz Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlx4/en_netdev.c | 2 +- drivers/net/ethernet/mellanox/mlx4/fw.c | 7 +++++-- drivers/net/ethernet/mellanox/mlx4/main.c | 8 ++++++++ include/linux/mlx4/device.h | 1 + 4 files changed, 15 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/drivers/net/ethernet/mellanox/mlx4/en_netdev.c b/drivers/net/ethernet/mellanox/mlx4/en_netdev.c index 2a5df0aa2228..be9e07a72193 100644 --- a/drivers/net/ethernet/mellanox/mlx4/en_netdev.c +++ b/drivers/net/ethernet/mellanox/mlx4/en_netdev.c @@ -2814,7 +2814,7 @@ int mlx4_en_init_netdev(struct mlx4_en_dev *mdev, int port, priv->msg_enable = MLX4_EN_MSG_LEVEL; #ifdef CONFIG_MLX4_EN_DCB if (!mlx4_is_slave(priv->mdev->dev)) { - if (mdev->dev->caps.flags & MLX4_DEV_CAP_FLAG_SET_ETH_SCHED) { + if (mdev->dev->caps.flags2 & MLX4_DEV_CAP_FLAG2_ETS_CFG) { dev->dcbnl_ops = &mlx4_en_dcbnl_ops; } else { en_info(priv, "enabling only PFC DCB ops\n"); diff --git a/drivers/net/ethernet/mellanox/mlx4/fw.c b/drivers/net/ethernet/mellanox/mlx4/fw.c index 94e857e39063..16bbd6cdff6c 100644 --- a/drivers/net/ethernet/mellanox/mlx4/fw.c +++ b/drivers/net/ethernet/mellanox/mlx4/fw.c @@ -149,6 +149,7 @@ static void dump_dev_cap_flags2(struct mlx4_dev *dev, u64 flags) [23] = "QP rate limiting support", [24] = "Ethernet Flow control statistics support", [25] = "Granular QoS per VF support", + [26] = "Port ETS Scheduler support", }; int i; @@ -900,6 +901,8 @@ int mlx4_QUERY_DEV_CAP(struct mlx4_dev *dev, struct mlx4_dev_cap *dev_cap) MLX4_GET(field, outbox, QUERY_DEV_CAP_VXLAN); if (field & 1<<3) dev_cap->flags2 |= MLX4_DEV_CAP_FLAG2_VXLAN_OFFLOADS; + if (field & (1 << 5)) + dev_cap->flags2 |= MLX4_DEV_CAP_FLAG2_ETS_CFG; MLX4_GET(dev_cap->max_icm_sz, outbox, QUERY_DEV_CAP_MAX_ICM_SZ_OFFSET); if (dev_cap->flags & MLX4_DEV_CAP_FLAG_COUNTERS) @@ -1157,9 +1160,9 @@ int mlx4_QUERY_DEV_CAP_wrapper(struct mlx4_dev *dev, int slave, field &= 0x7f; MLX4_PUT(outbox->buf, field, QUERY_DEV_CAP_CQ_TS_SUPPORT_OFFSET); - /* For guests, disable vxlan tunneling */ + /* For guests, disable vxlan tunneling and QoS support */ MLX4_GET(field, outbox->buf, QUERY_DEV_CAP_VXLAN); - field &= 0xf7; + field &= 0xd7; MLX4_PUT(outbox->buf, field, QUERY_DEV_CAP_VXLAN); /* For guests, report Blueflame disabled */ diff --git a/drivers/net/ethernet/mellanox/mlx4/main.c b/drivers/net/ethernet/mellanox/mlx4/main.c index ee0a67069b57..3becad37aec8 100644 --- a/drivers/net/ethernet/mellanox/mlx4/main.c +++ b/drivers/net/ethernet/mellanox/mlx4/main.c @@ -528,6 +528,14 @@ static int mlx4_dev_cap(struct mlx4_dev *dev, struct mlx4_dev_cap *dev_cap) dev->caps.alloc_res_qp_mask = (dev->caps.bf_reg_size ? MLX4_RESERVE_ETH_BF_QP : 0) | MLX4_RESERVE_A0_QP; + + if (!(dev->caps.flags2 & MLX4_DEV_CAP_FLAG2_ETS_CFG) && + dev->caps.flags & MLX4_DEV_CAP_FLAG_SET_ETH_SCHED) { + mlx4_warn(dev, "Old device ETS support detected\n"); + mlx4_warn(dev, "Consider upgrading device FW.\n"); + dev->caps.flags2 |= MLX4_DEV_CAP_FLAG2_ETS_CFG; + } + } else { dev->caps.alloc_res_qp_mask = 0; } diff --git a/include/linux/mlx4/device.h b/include/linux/mlx4/device.h index c37208f7869f..15f46767342e 100644 --- a/include/linux/mlx4/device.h +++ b/include/linux/mlx4/device.h @@ -207,6 +207,7 @@ enum { MLX4_DEV_CAP_FLAG2_QP_RATE_LIMIT = 1LL << 23, MLX4_DEV_CAP_FLAG2_FLOWSTATS_EN = 1LL << 24, MLX4_DEV_CAP_FLAG2_QOS_VPP = 1LL << 25, + MLX4_DEV_CAP_FLAG2_ETS_CFG = 1LL << 26, }; enum { -- cgit v1.2.3 From a130b59057320192b4b00ed0ba4bc8a38f66f289 Mon Sep 17 00:00:00 2001 From: Ido Shamay Date: Thu, 2 Apr 2015 16:31:19 +0300 Subject: net/mlx4: Add SET_PORT opcode modifiers enumeration The calls to SET_PORT used hard-code numbers, when supplying command's opcode modifiers, fix that to use well defined constants. Signed-off-by: Ido Shamay Signed-off-by: Or Gerlitz Signed-off-by: David S. Miller --- drivers/infiniband/hw/mlx4/main.c | 11 +++++----- drivers/net/ethernet/mellanox/mlx4/cmd.c | 3 ++- drivers/net/ethernet/mellanox/mlx4/port.c | 36 ++++++++++++++++++------------- include/linux/mlx4/cmd.h | 8 ++++++- 4 files changed, 36 insertions(+), 22 deletions(-) (limited to 'include') diff --git a/drivers/infiniband/hw/mlx4/main.c b/drivers/infiniband/hw/mlx4/main.c index b972c0b41799..976bea794b5f 100644 --- a/drivers/infiniband/hw/mlx4/main.c +++ b/drivers/infiniband/hw/mlx4/main.c @@ -587,8 +587,9 @@ static int mlx4_ib_SET_PORT(struct mlx4_ib_dev *dev, u8 port, int reset_qkey_vio ((__be32 *) mailbox->buf)[1] = cpu_to_be32(cap_mask); } - err = mlx4_cmd(dev->dev, mailbox->dma, port, 0, MLX4_CMD_SET_PORT, - MLX4_CMD_TIME_CLASS_B, MLX4_CMD_WRAPPED); + err = mlx4_cmd(dev->dev, mailbox->dma, port, MLX4_SET_PORT_IB_OPCODE, + MLX4_CMD_SET_PORT, MLX4_CMD_TIME_CLASS_B, + MLX4_CMD_WRAPPED); mlx4_free_cmd_mailbox(dev->dev, mailbox); return err; @@ -1525,8 +1526,8 @@ static void update_gids_task(struct work_struct *work) memcpy(gids, gw->gids, sizeof gw->gids); err = mlx4_cmd(dev, mailbox->dma, MLX4_SET_PORT_GID_TABLE << 8 | gw->port, - 1, MLX4_CMD_SET_PORT, MLX4_CMD_TIME_CLASS_B, - MLX4_CMD_WRAPPED); + MLX4_SET_PORT_ETH_OPCODE, MLX4_CMD_SET_PORT, + MLX4_CMD_TIME_CLASS_B, MLX4_CMD_WRAPPED); if (err) pr_warn("set port command failed\n"); else @@ -1564,7 +1565,7 @@ static void reset_gids_task(struct work_struct *work) IB_LINK_LAYER_ETHERNET) { err = mlx4_cmd(dev, mailbox->dma, MLX4_SET_PORT_GID_TABLE << 8 | gw->port, - 1, MLX4_CMD_SET_PORT, + MLX4_SET_PORT_ETH_OPCODE, MLX4_CMD_SET_PORT, MLX4_CMD_TIME_CLASS_B, MLX4_CMD_WRAPPED); if (err) diff --git a/drivers/net/ethernet/mellanox/mlx4/cmd.c b/drivers/net/ethernet/mellanox/mlx4/cmd.c index bc6e4c9b6bb5..06993ea9e6ba 100644 --- a/drivers/net/ethernet/mellanox/mlx4/cmd.c +++ b/drivers/net/ethernet/mellanox/mlx4/cmd.c @@ -726,7 +726,8 @@ static int mlx4_cmd_wait(struct mlx4_dev *dev, u64 in_param, u64 *out_param, * specific command/input_mod/opcode_mod/fw-status to be debug. */ if (op == MLX4_CMD_SET_PORT && in_modifier == 1 && - op_modifier == 0 && context->fw_status == CMD_STAT_BAD_SIZE) + op_modifier == MLX4_SET_PORT_IB_OPCODE && + context->fw_status == CMD_STAT_BAD_SIZE) mlx4_dbg(dev, "command 0x%x failed: fw status = 0x%x\n", op, context->fw_status); else diff --git a/drivers/net/ethernet/mellanox/mlx4/port.c b/drivers/net/ethernet/mellanox/mlx4/port.c index 6a53d42db52f..ca4488ed561c 100644 --- a/drivers/net/ethernet/mellanox/mlx4/port.c +++ b/drivers/net/ethernet/mellanox/mlx4/port.c @@ -123,8 +123,9 @@ static int mlx4_set_port_mac_table(struct mlx4_dev *dev, u8 port, in_mod = MLX4_SET_PORT_MAC_TABLE << 8 | port; - err = mlx4_cmd(dev, mailbox->dma, in_mod, 1, MLX4_CMD_SET_PORT, - MLX4_CMD_TIME_CLASS_B, MLX4_CMD_NATIVE); + err = mlx4_cmd(dev, mailbox->dma, in_mod, MLX4_SET_PORT_ETH_OPCODE, + MLX4_CMD_SET_PORT, MLX4_CMD_TIME_CLASS_B, + MLX4_CMD_NATIVE); mlx4_free_cmd_mailbox(dev, mailbox); return err; @@ -337,8 +338,9 @@ static int mlx4_set_port_vlan_table(struct mlx4_dev *dev, u8 port, memcpy(mailbox->buf, entries, MLX4_VLAN_TABLE_SIZE); in_mod = MLX4_SET_PORT_VLAN_TABLE << 8 | port; - err = mlx4_cmd(dev, mailbox->dma, in_mod, 1, MLX4_CMD_SET_PORT, - MLX4_CMD_TIME_CLASS_B, MLX4_CMD_NATIVE); + err = mlx4_cmd(dev, mailbox->dma, in_mod, MLX4_SET_PORT_ETH_OPCODE, + MLX4_CMD_SET_PORT, MLX4_CMD_TIME_CLASS_B, + MLX4_CMD_NATIVE); mlx4_free_cmd_mailbox(dev, mailbox); @@ -625,9 +627,9 @@ static int mlx4_reset_roce_port_gids(struct mlx4_dev *dev, int slave, MLX4_ROCE_GID_ENTRY_SIZE); err = mlx4_cmd(dev, mailbox->dma, - ((u32)port) | (MLX4_SET_PORT_GID_TABLE << 8), 1, - MLX4_CMD_SET_PORT, MLX4_CMD_TIME_CLASS_B, - MLX4_CMD_NATIVE); + ((u32)port) | (MLX4_SET_PORT_GID_TABLE << 8), + MLX4_SET_PORT_ETH_OPCODE, MLX4_CMD_SET_PORT, + MLX4_CMD_TIME_CLASS_B, MLX4_CMD_NATIVE); mutex_unlock(&(priv->port[port].gid_table.mutex)); return err; } @@ -941,8 +943,9 @@ int mlx4_SET_PORT(struct mlx4_dev *dev, u8 port, int pkey_tbl_sz) (pkey_tbl_flag << MLX4_CHANGE_PORT_PKEY_TBL_SZ) | (dev->caps.port_ib_mtu[port] << MLX4_SET_PORT_MTU_CAP) | (vl_cap << MLX4_SET_PORT_VL_CAP)); - err = mlx4_cmd(dev, mailbox->dma, port, 0, MLX4_CMD_SET_PORT, - MLX4_CMD_TIME_CLASS_B, MLX4_CMD_WRAPPED); + err = mlx4_cmd(dev, mailbox->dma, port, + MLX4_SET_PORT_IB_OPCODE, MLX4_CMD_SET_PORT, + MLX4_CMD_TIME_CLASS_B, MLX4_CMD_WRAPPED); if (err != -ENOMEM) break; } @@ -971,8 +974,9 @@ int mlx4_SET_PORT_general(struct mlx4_dev *dev, u8 port, int mtu, context->pfcrx = pfcrx; in_mod = MLX4_SET_PORT_GENERAL << 8 | port; - err = mlx4_cmd(dev, mailbox->dma, in_mod, 1, MLX4_CMD_SET_PORT, - MLX4_CMD_TIME_CLASS_B, MLX4_CMD_WRAPPED); + err = mlx4_cmd(dev, mailbox->dma, in_mod, MLX4_SET_PORT_ETH_OPCODE, + MLX4_CMD_SET_PORT, MLX4_CMD_TIME_CLASS_B, + MLX4_CMD_WRAPPED); mlx4_free_cmd_mailbox(dev, mailbox); return err; @@ -1008,8 +1012,9 @@ int mlx4_SET_PORT_qpn_calc(struct mlx4_dev *dev, u8 port, u32 base_qpn, context->vlan_miss = MLX4_VLAN_MISS_IDX; in_mod = MLX4_SET_PORT_RQP_CALC << 8 | port; - err = mlx4_cmd(dev, mailbox->dma, in_mod, 1, MLX4_CMD_SET_PORT, - MLX4_CMD_TIME_CLASS_B, MLX4_CMD_WRAPPED); + err = mlx4_cmd(dev, mailbox->dma, in_mod, MLX4_SET_PORT_ETH_OPCODE, + MLX4_CMD_SET_PORT, MLX4_CMD_TIME_CLASS_B, + MLX4_CMD_WRAPPED); mlx4_free_cmd_mailbox(dev, mailbox); return err; @@ -1050,8 +1055,9 @@ int mlx4_SET_PORT_VXLAN(struct mlx4_dev *dev, u8 port, u8 steering, int enable) context->steering = steering; in_mod = MLX4_SET_PORT_VXLAN << 8 | port; - err = mlx4_cmd(dev, mailbox->dma, in_mod, 1, MLX4_CMD_SET_PORT, - MLX4_CMD_TIME_CLASS_B, MLX4_CMD_NATIVE); + err = mlx4_cmd(dev, mailbox->dma, in_mod, MLX4_SET_PORT_ETH_OPCODE, + MLX4_CMD_SET_PORT, MLX4_CMD_TIME_CLASS_B, + MLX4_CMD_NATIVE); mlx4_free_cmd_mailbox(dev, mailbox); return err; diff --git a/include/linux/mlx4/cmd.h b/include/linux/mlx4/cmd.h index a788839f54bb..62c4d4def474 100644 --- a/include/linux/mlx4/cmd.h +++ b/include/linux/mlx4/cmd.h @@ -188,7 +188,13 @@ enum { }; enum { - /* set port opcode modifiers */ + /* Set port opcode modifiers */ + MLX4_SET_PORT_IB_OPCODE = 0x0, + MLX4_SET_PORT_ETH_OPCODE = 0x1, +}; + +enum { + /* Set port Ethernet input modifiers */ MLX4_SET_PORT_GENERAL = 0x0, MLX4_SET_PORT_RQP_CALC = 0x1, MLX4_SET_PORT_MAC_TABLE = 0x2, -- cgit v1.2.3 From 51af33cfed248dc8f36fa82df06b85e10038a01e Mon Sep 17 00:00:00 2001 From: Ido Shamay Date: Thu, 2 Apr 2015 16:31:20 +0300 Subject: net/mlx4_en: Add interface identify support Add support for the interface ethtool identify feature. Make the physical port LED to blink with green and yellow colors. The device handles the LED blink by itself (synchrous use of set_phys_id), by returning 0 to ETHTOOL_ID_ACTIVE command. Signed-off-by: Eyal Grossman Signed-off-by: Ido Shamay Signed-off-by: Or Gerlitz Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlx4/en_ethtool.c | 27 +++++++++++++++++++++++++ drivers/net/ethernet/mellanox/mlx4/fw.c | 10 +++++++++ drivers/net/ethernet/mellanox/mlx4/mlx4_en.h | 1 + drivers/net/ethernet/mellanox/mlx4/port.c | 26 ++++++++++++++++++++++++ include/linux/mlx4/cmd.h | 1 + include/linux/mlx4/device.h | 2 ++ 6 files changed, 67 insertions(+) (limited to 'include') diff --git a/drivers/net/ethernet/mellanox/mlx4/en_ethtool.c b/drivers/net/ethernet/mellanox/mlx4/en_ethtool.c index eba969b08dd1..3f44e2bbb982 100644 --- a/drivers/net/ethernet/mellanox/mlx4/en_ethtool.c +++ b/drivers/net/ethernet/mellanox/mlx4/en_ethtool.c @@ -1939,6 +1939,32 @@ static int mlx4_en_get_module_eeprom(struct net_device *dev, return 0; } +static int mlx4_en_set_phys_id(struct net_device *dev, + enum ethtool_phys_id_state state) +{ + int err; + u16 beacon_duration; + struct mlx4_en_priv *priv = netdev_priv(dev); + struct mlx4_en_dev *mdev = priv->mdev; + + if (!(mdev->dev->caps.flags2 & MLX4_DEV_CAP_FLAG2_PORT_BEACON)) + return -EOPNOTSUPP; + + switch (state) { + case ETHTOOL_ID_ACTIVE: + beacon_duration = PORT_BEACON_MAX_LIMIT; + break; + case ETHTOOL_ID_INACTIVE: + beacon_duration = 0; + break; + default: + return -EOPNOTSUPP; + } + + err = mlx4_SET_PORT_BEACON(mdev->dev, priv->port, beacon_duration); + return err; +} + const struct ethtool_ops mlx4_en_ethtool_ops = { .get_drvinfo = mlx4_en_get_drvinfo, .get_settings = mlx4_en_get_settings, @@ -1948,6 +1974,7 @@ const struct ethtool_ops mlx4_en_ethtool_ops = { .get_sset_count = mlx4_en_get_sset_count, .get_ethtool_stats = mlx4_en_get_ethtool_stats, .self_test = mlx4_en_self_test, + .set_phys_id = mlx4_en_set_phys_id, .get_wol = mlx4_en_get_wol, .set_wol = mlx4_en_set_wol, .get_msglevel = mlx4_en_get_msglevel, diff --git a/drivers/net/ethernet/mellanox/mlx4/fw.c b/drivers/net/ethernet/mellanox/mlx4/fw.c index b93e23bef6c9..6317844b291f 100644 --- a/drivers/net/ethernet/mellanox/mlx4/fw.c +++ b/drivers/net/ethernet/mellanox/mlx4/fw.c @@ -150,6 +150,7 @@ static void dump_dev_cap_flags2(struct mlx4_dev *dev, u64 flags) [24] = "Ethernet Flow control statistics support", [25] = "Granular QoS per VF support", [26] = "Port ETS Scheduler support", + [27] = "Port beacon support", }; int i; @@ -647,6 +648,7 @@ int mlx4_QUERY_DEV_CAP(struct mlx4_dev *dev, struct mlx4_dev_cap *dev_cap) #define QUERY_DEV_CAP_RSS_OFFSET 0x2e #define QUERY_DEV_CAP_MAX_RDMA_OFFSET 0x2f #define QUERY_DEV_CAP_RSZ_SRQ_OFFSET 0x33 +#define QUERY_DEV_CAP_PORT_BEACON_OFFSET 0x34 #define QUERY_DEV_CAP_ACK_DELAY_OFFSET 0x35 #define QUERY_DEV_CAP_MTU_WIDTH_OFFSET 0x36 #define QUERY_DEV_CAP_VL_PORT_OFFSET 0x37 @@ -786,6 +788,9 @@ int mlx4_QUERY_DEV_CAP(struct mlx4_dev *dev, struct mlx4_dev_cap *dev_cap) if (field & 0x80) dev_cap->flags2 |= MLX4_DEV_CAP_FLAG2_FS_EN; dev_cap->fs_log_max_ucast_qp_range_size = field & 0x1f; + MLX4_GET(field, outbox, QUERY_DEV_CAP_PORT_BEACON_OFFSET); + if (field & 0x80) + dev_cap->flags2 |= MLX4_DEV_CAP_FLAG2_PORT_BEACON; MLX4_GET(field, outbox, QUERY_DEV_CAP_FLOW_STEERING_IPOIB_OFFSET); if (field & 0x80) dev_cap->flags2 |= MLX4_DEV_CAP_FLAG2_DMFS_IPOIB; @@ -1165,6 +1170,11 @@ int mlx4_QUERY_DEV_CAP_wrapper(struct mlx4_dev *dev, int slave, field &= 0xd7; MLX4_PUT(outbox->buf, field, QUERY_DEV_CAP_VXLAN); + /* For guests, disable port BEACON */ + MLX4_GET(field, outbox->buf, QUERY_DEV_CAP_PORT_BEACON_OFFSET); + field &= 0x7f; + MLX4_PUT(outbox->buf, field, QUERY_DEV_CAP_PORT_BEACON_OFFSET); + /* For guests, report Blueflame disabled */ MLX4_GET(field, outbox->buf, QUERY_DEV_CAP_BF_OFFSET); field &= 0x7f; diff --git a/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h b/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h index 67eeea244eff..9de30216b146 100644 --- a/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h +++ b/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h @@ -482,6 +482,7 @@ enum { MLX4_EN_FLAG_RX_CSUM_NON_TCP_UDP = (1 << 5), }; +#define PORT_BEACON_MAX_LIMIT (65535) #define MLX4_EN_MAC_HASH_SIZE (1 << BITS_PER_BYTE) #define MLX4_EN_MAC_HASH_IDX 5 diff --git a/drivers/net/ethernet/mellanox/mlx4/port.c b/drivers/net/ethernet/mellanox/mlx4/port.c index ca4488ed561c..e8d716dfd5cb 100644 --- a/drivers/net/ethernet/mellanox/mlx4/port.c +++ b/drivers/net/ethernet/mellanox/mlx4/port.c @@ -835,6 +835,12 @@ static int mlx4_common_set_port(struct mlx4_dev *dev, int slave, u32 in_mod, MLX4_CMD_NATIVE); } + /* Slaves are not allowed to SET_PORT beacon (LED) blink */ + if (op_mod == MLX4_SET_PORT_BEACON_OPCODE) { + mlx4_warn(dev, "denying SET_PORT Beacon slave:%d\n", slave); + return -EPERM; + } + /* For IB, we only consider: * - The capability mask, which is set to the aggregate of all * slave function capabilities @@ -1064,6 +1070,26 @@ int mlx4_SET_PORT_VXLAN(struct mlx4_dev *dev, u8 port, u8 steering, int enable) } EXPORT_SYMBOL(mlx4_SET_PORT_VXLAN); +int mlx4_SET_PORT_BEACON(struct mlx4_dev *dev, u8 port, u16 time) +{ + int err; + struct mlx4_cmd_mailbox *mailbox; + + mailbox = mlx4_alloc_cmd_mailbox(dev); + if (IS_ERR(mailbox)) + return PTR_ERR(mailbox); + + *((__be32 *)mailbox->buf) = cpu_to_be32(time); + + err = mlx4_cmd(dev, mailbox->dma, port, MLX4_SET_PORT_BEACON_OPCODE, + MLX4_CMD_SET_PORT, MLX4_CMD_TIME_CLASS_B, + MLX4_CMD_NATIVE); + + mlx4_free_cmd_mailbox(dev, mailbox); + return err; +} +EXPORT_SYMBOL(mlx4_SET_PORT_BEACON); + int mlx4_SET_MCAST_FLTR_wrapper(struct mlx4_dev *dev, int slave, struct mlx4_vhcr *vhcr, struct mlx4_cmd_mailbox *inbox, diff --git a/include/linux/mlx4/cmd.h b/include/linux/mlx4/cmd.h index 62c4d4def474..f62e7cf227c6 100644 --- a/include/linux/mlx4/cmd.h +++ b/include/linux/mlx4/cmd.h @@ -191,6 +191,7 @@ enum { /* Set port opcode modifiers */ MLX4_SET_PORT_IB_OPCODE = 0x0, MLX4_SET_PORT_ETH_OPCODE = 0x1, + MLX4_SET_PORT_BEACON_OPCODE = 0x4, }; enum { diff --git a/include/linux/mlx4/device.h b/include/linux/mlx4/device.h index 15f46767342e..b761be7c88c8 100644 --- a/include/linux/mlx4/device.h +++ b/include/linux/mlx4/device.h @@ -208,6 +208,7 @@ enum { MLX4_DEV_CAP_FLAG2_FLOWSTATS_EN = 1LL << 24, MLX4_DEV_CAP_FLAG2_QOS_VPP = 1LL << 25, MLX4_DEV_CAP_FLAG2_ETS_CFG = 1LL << 26, + MLX4_DEV_CAP_FLAG2_PORT_BEACON = 1LL << 27, }; enum { @@ -1311,6 +1312,7 @@ int mlx4_SET_PORT_general(struct mlx4_dev *dev, u8 port, int mtu, u8 pptx, u8 pfctx, u8 pprx, u8 pfcrx); int mlx4_SET_PORT_qpn_calc(struct mlx4_dev *dev, u8 port, u32 base_qpn, u8 promisc); +int mlx4_SET_PORT_BEACON(struct mlx4_dev *dev, u8 port, u16 time); int mlx4_SET_PORT_VXLAN(struct mlx4_dev *dev, u8 port, u8 steering, int enable); int mlx4_find_cached_mac(struct mlx4_dev *dev, u8 port, u64 mac, int *idx); int mlx4_find_cached_vlan(struct mlx4_dev *dev, u8 port, u16 vid, int *idx); -- cgit v1.2.3 From 78500b8c03236a18d454c9cc8a24cccca506b200 Mon Sep 17 00:00:00 2001 From: Muhammad Mahajna Date: Thu, 2 Apr 2015 16:31:22 +0300 Subject: net/mlx4_en: Add RX-ALL support Enabled when the device supports KEEP FCS and IGNORE FCS. When the flag is set, pass all received frames up the stack, even ones with invalid FCS, controlled by ethtool. Signed-off-by: Muhammad Mahajna Signed-off-by: Ido Shamay Signed-off-by: Or Gerlitz Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlx4/en_netdev.c | 14 +++++++++++ drivers/net/ethernet/mellanox/mlx4/fw.c | 8 ++++++ drivers/net/ethernet/mellanox/mlx4/main.c | 21 ++++++++++++++++ drivers/net/ethernet/mellanox/mlx4/mlx4.h | 6 +++-- drivers/net/ethernet/mellanox/mlx4/port.c | 34 ++++++++++++++++++++++++++ include/linux/mlx4/device.h | 3 +++ 6 files changed, 84 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/drivers/net/ethernet/mellanox/mlx4/en_netdev.c b/drivers/net/ethernet/mellanox/mlx4/en_netdev.c index 333af3405a80..0f1afc085d58 100644 --- a/drivers/net/ethernet/mellanox/mlx4/en_netdev.c +++ b/drivers/net/ethernet/mellanox/mlx4/en_netdev.c @@ -2204,6 +2204,17 @@ static int mlx4_en_set_features(struct net_device *netdev, reset = true; } + if (DEV_FEATURE_CHANGED(netdev, features, NETIF_F_RXALL)) { + u8 ignore_fcs_value = (features & NETIF_F_RXALL) ? 1 : 0; + + en_info(priv, "Turn %s RX-ALL\n", + ignore_fcs_value ? "ON" : "OFF"); + ret = mlx4_SET_PORT_fcs_check(priv->mdev->dev, + priv->port, ignore_fcs_value); + if (ret) + return ret; + } + if (DEV_FEATURE_CHANGED(netdev, features, NETIF_F_HW_VLAN_CTAG_RX)) { en_info(priv, "Turn %s RX vlan strip offload\n", (features & NETIF_F_HW_VLAN_CTAG_RX) ? "ON" : "OFF"); @@ -2914,6 +2925,9 @@ int mlx4_en_init_netdev(struct mlx4_en_dev *mdev, int port, if (mdev->dev->caps.flags & MLX4_DEV_CAP_FLAG_FCS_KEEP) dev->hw_features |= NETIF_F_RXFCS; + if (mdev->dev->caps.flags2 & MLX4_DEV_CAP_FLAG2_IGNORE_FCS) + dev->hw_features |= NETIF_F_RXALL; + if (mdev->dev->caps.steering_mode == MLX4_STEERING_MODE_DEVICE_MANAGED && mdev->dev->caps.dmfs_high_steer_mode != MLX4_STEERING_DMFS_A0_STATIC) diff --git a/drivers/net/ethernet/mellanox/mlx4/fw.c b/drivers/net/ethernet/mellanox/mlx4/fw.c index 6317844b291f..b9881fc1252f 100644 --- a/drivers/net/ethernet/mellanox/mlx4/fw.c +++ b/drivers/net/ethernet/mellanox/mlx4/fw.c @@ -151,6 +151,7 @@ static void dump_dev_cap_flags2(struct mlx4_dev *dev, u64 flags) [25] = "Granular QoS per VF support", [26] = "Port ETS Scheduler support", [27] = "Port beacon support", + [28] = "RX-ALL support", }; int i; @@ -893,6 +894,8 @@ int mlx4_QUERY_DEV_CAP(struct mlx4_dev *dev, struct mlx4_dev_cap *dev_cap) MLX4_GET(field, outbox, QUERY_DEV_CAP_CONFIG_DEV_OFFSET); if (field & 0x20) dev_cap->flags2 |= MLX4_DEV_CAP_FLAG2_CONFIG_DEV; + if (field & (1 << 2)) + dev_cap->flags2 |= MLX4_DEV_CAP_FLAG2_IGNORE_FCS; MLX4_GET(dev_cap->reserved_lkey, outbox, QUERY_DEV_CAP_RSVD_LKEY_OFFSET); MLX4_GET(field32, outbox, QUERY_DEV_CAP_ETH_BACKPL_OFFSET); @@ -1220,6 +1223,11 @@ int mlx4_QUERY_DEV_CAP_wrapper(struct mlx4_dev *dev, int slave, field &= 0xef; MLX4_PUT(outbox->buf, field, QUERY_DEV_CAP_CQ_EQ_CACHE_LINE_STRIDE); + /* turn off ignore FCS feature for guests */ + MLX4_GET(field, outbox->buf, QUERY_DEV_CAP_CONFIG_DEV_OFFSET); + field &= 0xfb; + MLX4_PUT(outbox->buf, field, QUERY_DEV_CAP_CONFIG_DEV_OFFSET); + return 0; } diff --git a/drivers/net/ethernet/mellanox/mlx4/main.c b/drivers/net/ethernet/mellanox/mlx4/main.c index 3becad37aec8..acceb75e8c44 100644 --- a/drivers/net/ethernet/mellanox/mlx4/main.c +++ b/drivers/net/ethernet/mellanox/mlx4/main.c @@ -297,6 +297,25 @@ static int mlx4_dev_port(struct mlx4_dev *dev, int port, return err; } +static inline void mlx4_enable_ignore_fcs(struct mlx4_dev *dev) +{ + if (!(dev->caps.flags2 & MLX4_DEV_CAP_FLAG2_IGNORE_FCS)) + return; + + if (mlx4_is_mfunc(dev)) { + mlx4_dbg(dev, "SRIOV mode - Disabling Ignore FCS"); + dev->caps.flags2 &= ~MLX4_DEV_CAP_FLAG2_IGNORE_FCS; + return; + } + + if (!(dev->caps.flags & MLX4_DEV_CAP_FLAG_FCS_KEEP)) { + mlx4_dbg(dev, + "Keep FCS is not supported - Disabling Ignore FCS"); + dev->caps.flags2 &= ~MLX4_DEV_CAP_FLAG2_IGNORE_FCS; + return; + } +} + #define MLX4_A0_STEERING_TABLE_SIZE 256 static int mlx4_dev_cap(struct mlx4_dev *dev, struct mlx4_dev_cap *dev_cap) { @@ -540,6 +559,8 @@ static int mlx4_dev_cap(struct mlx4_dev *dev, struct mlx4_dev_cap *dev_cap) dev->caps.alloc_res_qp_mask = 0; } + mlx4_enable_ignore_fcs(dev); + return 0; } diff --git a/drivers/net/ethernet/mellanox/mlx4/mlx4.h b/drivers/net/ethernet/mellanox/mlx4/mlx4.h index 32469c64d580..f30eeb730a86 100644 --- a/drivers/net/ethernet/mellanox/mlx4/mlx4.h +++ b/drivers/net/ethernet/mellanox/mlx4/mlx4.h @@ -763,9 +763,11 @@ enum { struct mlx4_set_port_general_context { - u8 reserved[3]; + u16 reserved1; + u8 v_ignore_fcs; u8 flags; - u16 reserved2; + u8 ignore_fcs; + u8 reserved2; __be16 mtu; u8 pptx; u8 pfctx; diff --git a/drivers/net/ethernet/mellanox/mlx4/port.c b/drivers/net/ethernet/mellanox/mlx4/port.c index e8d716dfd5cb..c2b21313dba7 100644 --- a/drivers/net/ethernet/mellanox/mlx4/port.c +++ b/drivers/net/ethernet/mellanox/mlx4/port.c @@ -45,6 +45,14 @@ #define MLX4_VLAN_VALID (1u << 31) #define MLX4_VLAN_MASK 0xfff +#define MLX4_STATS_TRAFFIC_COUNTERS_MASK 0xfULL +#define MLX4_STATS_TRAFFIC_DROPS_MASK 0xc0ULL +#define MLX4_STATS_ERROR_COUNTERS_MASK 0x1ffc30ULL +#define MLX4_STATS_PORT_COUNTERS_MASK 0x1fe00000ULL + +#define MLX4_FLAG_V_IGNORE_FCS_MASK 0x2 +#define MLX4_IGNORE_FCS_MASK 0x1 + void mlx4_init_mac_table(struct mlx4_dev *dev, struct mlx4_mac_table *table) { int i; @@ -1027,6 +1035,32 @@ int mlx4_SET_PORT_qpn_calc(struct mlx4_dev *dev, u8 port, u32 base_qpn, } EXPORT_SYMBOL(mlx4_SET_PORT_qpn_calc); +int mlx4_SET_PORT_fcs_check(struct mlx4_dev *dev, u8 port, u8 ignore_fcs_value) +{ + struct mlx4_cmd_mailbox *mailbox; + struct mlx4_set_port_general_context *context; + u32 in_mod; + int err; + + mailbox = mlx4_alloc_cmd_mailbox(dev); + if (IS_ERR(mailbox)) + return PTR_ERR(mailbox); + context = mailbox->buf; + context->v_ignore_fcs |= MLX4_FLAG_V_IGNORE_FCS_MASK; + if (ignore_fcs_value) + context->ignore_fcs |= MLX4_IGNORE_FCS_MASK; + else + context->ignore_fcs &= ~MLX4_IGNORE_FCS_MASK; + + in_mod = MLX4_SET_PORT_GENERAL << 8 | port; + err = mlx4_cmd(dev, mailbox->dma, in_mod, 1, MLX4_CMD_SET_PORT, + MLX4_CMD_TIME_CLASS_B, MLX4_CMD_NATIVE); + + mlx4_free_cmd_mailbox(dev, mailbox); + return err; +} +EXPORT_SYMBOL(mlx4_SET_PORT_fcs_check); + enum { VXLAN_ENABLE_MODIFY = 1 << 7, VXLAN_STEERING_MODIFY = 1 << 6, diff --git a/include/linux/mlx4/device.h b/include/linux/mlx4/device.h index b761be7c88c8..f9ce34bec45b 100644 --- a/include/linux/mlx4/device.h +++ b/include/linux/mlx4/device.h @@ -209,6 +209,7 @@ enum { MLX4_DEV_CAP_FLAG2_QOS_VPP = 1LL << 25, MLX4_DEV_CAP_FLAG2_ETS_CFG = 1LL << 26, MLX4_DEV_CAP_FLAG2_PORT_BEACON = 1LL << 27, + MLX4_DEV_CAP_FLAG2_IGNORE_FCS = 1LL << 28, }; enum { @@ -1313,6 +1314,8 @@ int mlx4_SET_PORT_general(struct mlx4_dev *dev, u8 port, int mtu, int mlx4_SET_PORT_qpn_calc(struct mlx4_dev *dev, u8 port, u32 base_qpn, u8 promisc); int mlx4_SET_PORT_BEACON(struct mlx4_dev *dev, u8 port, u16 time); +int mlx4_SET_PORT_fcs_check(struct mlx4_dev *dev, u8 port, + u8 ignore_fcs_value); int mlx4_SET_PORT_VXLAN(struct mlx4_dev *dev, u8 port, u8 steering, int enable); int mlx4_find_cached_mac(struct mlx4_dev *dev, u8 port, u64 mac, int *idx); int mlx4_find_cached_vlan(struct mlx4_dev *dev, u8 port, u16 vid, int *idx); -- cgit v1.2.3 From 64599cca51de08cef94bc13a0f98351e5bb01f41 Mon Sep 17 00:00:00 2001 From: Eli Cohen Date: Thu, 2 Apr 2015 17:07:25 +0300 Subject: net/mlx5_core: Use coherent memory for command interface page Use coherent memory for the commands descriptor page. Take measures to make sure the page is aligned to MLX5_ADAPTER_PAGE_SIZE as required by the hardware. Reported-by: Yevgeny Kliteynik Signed-off-by: Eli Cohen Signed-off-by: Saeed Mahameed Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlx5/core/cmd.c | 72 ++++++++++++++++++--------- include/linux/mlx5/driver.h | 3 ++ 2 files changed, 52 insertions(+), 23 deletions(-) (limited to 'include') diff --git a/drivers/net/ethernet/mellanox/mlx5/core/cmd.c b/drivers/net/ethernet/mellanox/mlx5/core/cmd.c index a2853057c779..43a73d36b01d 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/cmd.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/cmd.c @@ -1319,6 +1319,45 @@ ex_err: return err; } +static int alloc_cmd_page(struct mlx5_core_dev *dev, struct mlx5_cmd *cmd) +{ + struct device *ddev = &dev->pdev->dev; + + cmd->cmd_alloc_buf = dma_zalloc_coherent(ddev, MLX5_ADAPTER_PAGE_SIZE, + &cmd->alloc_dma, GFP_KERNEL); + if (!cmd->cmd_alloc_buf) + return -ENOMEM; + + /* make sure it is aligned to 4K */ + if (!((uintptr_t)cmd->cmd_alloc_buf & (MLX5_ADAPTER_PAGE_SIZE - 1))) { + cmd->cmd_buf = cmd->cmd_alloc_buf; + cmd->dma = cmd->alloc_dma; + cmd->alloc_size = MLX5_ADAPTER_PAGE_SIZE; + return 0; + } + + dma_free_coherent(ddev, MLX5_ADAPTER_PAGE_SIZE, cmd->cmd_alloc_buf, + cmd->alloc_dma); + cmd->cmd_alloc_buf = dma_zalloc_coherent(ddev, + 2 * MLX5_ADAPTER_PAGE_SIZE - 1, + &cmd->alloc_dma, GFP_KERNEL); + if (!cmd->cmd_alloc_buf) + return -ENOMEM; + + cmd->cmd_buf = PTR_ALIGN(cmd->cmd_alloc_buf, MLX5_ADAPTER_PAGE_SIZE); + cmd->dma = ALIGN(cmd->alloc_dma, MLX5_ADAPTER_PAGE_SIZE); + cmd->alloc_size = 2 * MLX5_ADAPTER_PAGE_SIZE - 1; + return 0; +} + +static void free_cmd_page(struct mlx5_core_dev *dev, struct mlx5_cmd *cmd) +{ + struct device *ddev = &dev->pdev->dev; + + dma_free_coherent(ddev, cmd->alloc_size, cmd->cmd_alloc_buf, + cmd->alloc_dma); +} + int mlx5_cmd_init(struct mlx5_core_dev *dev) { int size = sizeof(struct mlx5_cmd_prot_block); @@ -1341,17 +1380,9 @@ int mlx5_cmd_init(struct mlx5_core_dev *dev) if (!cmd->pool) return -ENOMEM; - cmd->cmd_buf = (void *)__get_free_pages(GFP_ATOMIC, 0); - if (!cmd->cmd_buf) { - err = -ENOMEM; + err = alloc_cmd_page(dev, cmd); + if (err) goto err_free_pool; - } - cmd->dma = dma_map_single(&dev->pdev->dev, cmd->cmd_buf, PAGE_SIZE, - DMA_BIDIRECTIONAL); - if (dma_mapping_error(&dev->pdev->dev, cmd->dma)) { - err = -ENOMEM; - goto err_free; - } cmd_l = ioread32be(&dev->iseg->cmdq_addr_l_sz) & 0xff; cmd->log_sz = cmd_l >> 4 & 0xf; @@ -1360,13 +1391,13 @@ int mlx5_cmd_init(struct mlx5_core_dev *dev) dev_err(&dev->pdev->dev, "firmware reports too many outstanding commands %d\n", 1 << cmd->log_sz); err = -EINVAL; - goto err_map; + goto err_free_page; } if (cmd->log_sz + cmd->log_stride > MLX5_ADAPTER_PAGE_SHIFT) { dev_err(&dev->pdev->dev, "command queue size overflow\n"); err = -EINVAL; - goto err_map; + goto err_free_page; } cmd->checksum_disabled = 1; @@ -1378,7 +1409,7 @@ int mlx5_cmd_init(struct mlx5_core_dev *dev) dev_err(&dev->pdev->dev, "driver does not support command interface version. driver %d, firmware %d\n", CMD_IF_REV, cmd->cmdif_rev); err = -ENOTSUPP; - goto err_map; + goto err_free_page; } spin_lock_init(&cmd->alloc_lock); @@ -1394,7 +1425,7 @@ int mlx5_cmd_init(struct mlx5_core_dev *dev) if (cmd_l & 0xfff) { dev_err(&dev->pdev->dev, "invalid command queue address\n"); err = -ENOMEM; - goto err_map; + goto err_free_page; } iowrite32be(cmd_h, &dev->iseg->cmdq_addr_h); @@ -1410,7 +1441,7 @@ int mlx5_cmd_init(struct mlx5_core_dev *dev) err = create_msg_cache(dev); if (err) { dev_err(&dev->pdev->dev, "failed to create command cache\n"); - goto err_map; + goto err_free_page; } set_wqname(dev); @@ -1435,11 +1466,8 @@ err_wq: err_cache: destroy_msg_cache(dev); -err_map: - dma_unmap_single(&dev->pdev->dev, cmd->dma, PAGE_SIZE, - DMA_BIDIRECTIONAL); -err_free: - free_pages((unsigned long)cmd->cmd_buf, 0); +err_free_page: + free_cmd_page(dev, cmd); err_free_pool: pci_pool_destroy(cmd->pool); @@ -1455,9 +1483,7 @@ void mlx5_cmd_cleanup(struct mlx5_core_dev *dev) clean_debug_files(dev); destroy_workqueue(cmd->wq); destroy_msg_cache(dev); - dma_unmap_single(&dev->pdev->dev, cmd->dma, PAGE_SIZE, - DMA_BIDIRECTIONAL); - free_pages((unsigned long)cmd->cmd_buf, 0); + free_cmd_page(dev, cmd); pci_pool_destroy(cmd->pool); } EXPORT_SYMBOL(mlx5_cmd_cleanup); diff --git a/include/linux/mlx5/driver.h b/include/linux/mlx5/driver.h index 166d9315fe4b..8d8ca6d9b03b 100644 --- a/include/linux/mlx5/driver.h +++ b/include/linux/mlx5/driver.h @@ -232,6 +232,9 @@ struct mlx5_cmd_stats { }; struct mlx5_cmd { + void *cmd_alloc_buf; + dma_addr_t alloc_dma; + int alloc_size; void *cmd_buf; dma_addr_t dma; u16 cmdif_rev; -- cgit v1.2.3 From 302bdf68fc56a6330bc6b10ce435b4d466417537 Mon Sep 17 00:00:00 2001 From: Saeed Mahameed Date: Thu, 2 Apr 2015 17:07:29 +0300 Subject: net/mlx5_core: Fix Mellanox copyright note Signed-off-by: Achiad Shochat Signed-off-by: Saeed Mahameed Signed-off-by: Eli Cohen Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlx5/core/alloc.c | 2 +- drivers/net/ethernet/mellanox/mlx5/core/cmd.c | 2 +- drivers/net/ethernet/mellanox/mlx5/core/cq.c | 2 +- drivers/net/ethernet/mellanox/mlx5/core/debugfs.c | 2 +- drivers/net/ethernet/mellanox/mlx5/core/eq.c | 2 +- drivers/net/ethernet/mellanox/mlx5/core/fw.c | 2 +- drivers/net/ethernet/mellanox/mlx5/core/health.c | 2 +- drivers/net/ethernet/mellanox/mlx5/core/mad.c | 2 +- drivers/net/ethernet/mellanox/mlx5/core/main.c | 2 +- drivers/net/ethernet/mellanox/mlx5/core/mcg.c | 2 +- drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.h | 2 +- drivers/net/ethernet/mellanox/mlx5/core/mr.c | 2 +- drivers/net/ethernet/mellanox/mlx5/core/pagealloc.c | 2 +- drivers/net/ethernet/mellanox/mlx5/core/pd.c | 2 +- drivers/net/ethernet/mellanox/mlx5/core/port.c | 2 +- drivers/net/ethernet/mellanox/mlx5/core/qp.c | 2 +- drivers/net/ethernet/mellanox/mlx5/core/srq.c | 2 +- drivers/net/ethernet/mellanox/mlx5/core/uar.c | 2 +- include/linux/mlx5/cmd.h | 2 +- include/linux/mlx5/cq.h | 2 +- include/linux/mlx5/device.h | 2 +- include/linux/mlx5/doorbell.h | 2 +- include/linux/mlx5/driver.h | 2 +- include/linux/mlx5/mlx5_ifc.h | 2 +- include/linux/mlx5/qp.h | 2 +- include/linux/mlx5/srq.h | 2 +- 26 files changed, 26 insertions(+), 26 deletions(-) (limited to 'include') diff --git a/drivers/net/ethernet/mellanox/mlx5/core/alloc.c b/drivers/net/ethernet/mellanox/mlx5/core/alloc.c index 63dd75485204..ac0f7bf4be95 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/alloc.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/alloc.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, Mellanox Technologies inc. All rights reserved. + * Copyright (c) 2013-2015, Mellanox Technologies. All rights reserved. * * This software is available to you under a choice of one of two * licenses. You may choose to be licensed under the terms of the GNU diff --git a/drivers/net/ethernet/mellanox/mlx5/core/cmd.c b/drivers/net/ethernet/mellanox/mlx5/core/cmd.c index 697d538163f2..e3273faf4568 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/cmd.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/cmd.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, Mellanox Technologies inc. All rights reserved. + * Copyright (c) 2013-2015, Mellanox Technologies. All rights reserved. * * This software is available to you under a choice of one of two * licenses. You may choose to be licensed under the terms of the GNU diff --git a/drivers/net/ethernet/mellanox/mlx5/core/cq.c b/drivers/net/ethernet/mellanox/mlx5/core/cq.c index 43c5f4809526..eb0cf81f5f45 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/cq.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/cq.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, Mellanox Technologies inc. All rights reserved. + * Copyright (c) 2013-2015, Mellanox Technologies. All rights reserved. * * This software is available to you under a choice of one of two * licenses. You may choose to be licensed under the terms of the GNU diff --git a/drivers/net/ethernet/mellanox/mlx5/core/debugfs.c b/drivers/net/ethernet/mellanox/mlx5/core/debugfs.c index 4878025e231c..5210d92e6bc7 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/debugfs.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/debugfs.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, Mellanox Technologies inc. All rights reserved. + * Copyright (c) 2013-2015, Mellanox Technologies. All rights reserved. * * This software is available to you under a choice of one of two * licenses. You may choose to be licensed under the terms of the GNU diff --git a/drivers/net/ethernet/mellanox/mlx5/core/eq.c b/drivers/net/ethernet/mellanox/mlx5/core/eq.c index da82991239a8..dbf190d9b9ad 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/eq.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/eq.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, Mellanox Technologies inc. All rights reserved. + * Copyright (c) 2013-2015, Mellanox Technologies. All rights reserved. * * This software is available to you under a choice of one of two * licenses. You may choose to be licensed under the terms of the GNU diff --git a/drivers/net/ethernet/mellanox/mlx5/core/fw.c b/drivers/net/ethernet/mellanox/mlx5/core/fw.c index 06f9036acd83..4b4cda3bcc5f 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/fw.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/fw.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, Mellanox Technologies inc. All rights reserved. + * Copyright (c) 2013-2015, Mellanox Technologies. All rights reserved. * * This software is available to you under a choice of one of two * licenses. You may choose to be licensed under the terms of the GNU diff --git a/drivers/net/ethernet/mellanox/mlx5/core/health.c b/drivers/net/ethernet/mellanox/mlx5/core/health.c index 3e6670c4a7cd..292d76f2a904 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/health.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/health.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, Mellanox Technologies inc. All rights reserved. + * Copyright (c) 2013-2015, Mellanox Technologies. All rights reserved. * * This software is available to you under a choice of one of two * licenses. You may choose to be licensed under the terms of the GNU diff --git a/drivers/net/ethernet/mellanox/mlx5/core/mad.c b/drivers/net/ethernet/mellanox/mlx5/core/mad.c index fd80ecfa7195..ee1b0b965f34 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/mad.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/mad.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, Mellanox Technologies inc. All rights reserved. + * Copyright (c) 2013-2015, Mellanox Technologies. All rights reserved. * * This software is available to you under a choice of one of two * licenses. You may choose to be licensed under the terms of the GNU diff --git a/drivers/net/ethernet/mellanox/mlx5/core/main.c b/drivers/net/ethernet/mellanox/mlx5/core/main.c index f29fd5592305..78dfdab1ead4 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/main.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/main.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, Mellanox Technologies inc. All rights reserved. + * Copyright (c) 2013-2015, Mellanox Technologies. All rights reserved. * * This software is available to you under a choice of one of two * licenses. You may choose to be licensed under the terms of the GNU diff --git a/drivers/net/ethernet/mellanox/mlx5/core/mcg.c b/drivers/net/ethernet/mellanox/mlx5/core/mcg.c index 44837640bd7c..d79fd85d1dd5 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/mcg.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/mcg.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, Mellanox Technologies inc. All rights reserved. + * Copyright (c) 2013-2015, Mellanox Technologies. All rights reserved. * * This software is available to you under a choice of one of two * licenses. You may choose to be licensed under the terms of the GNU diff --git a/drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.h b/drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.h index f0c9f9a7a361..a051b906afdf 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, Mellanox Technologies inc. All rights reserved. + * Copyright (c) 2013-2015, Mellanox Technologies. All rights reserved. * * This software is available to you under a choice of one of two * licenses. You may choose to be licensed under the terms of the GNU diff --git a/drivers/net/ethernet/mellanox/mlx5/core/mr.c b/drivers/net/ethernet/mellanox/mlx5/core/mr.c index 1a9b75139c2d..1adb300dd850 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/mr.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/mr.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, Mellanox Technologies inc. All rights reserved. + * Copyright (c) 2013-2015, Mellanox Technologies. All rights reserved. * * This software is available to you under a choice of one of two * licenses. You may choose to be licensed under the terms of the GNU diff --git a/drivers/net/ethernet/mellanox/mlx5/core/pagealloc.c b/drivers/net/ethernet/mellanox/mlx5/core/pagealloc.c index cf8591a0c48d..df2238372ea7 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/pagealloc.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/pagealloc.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, Mellanox Technologies inc. All rights reserved. + * Copyright (c) 2013-2015, Mellanox Technologies. All rights reserved. * * This software is available to you under a choice of one of two * licenses. You may choose to be licensed under the terms of the GNU diff --git a/drivers/net/ethernet/mellanox/mlx5/core/pd.c b/drivers/net/ethernet/mellanox/mlx5/core/pd.c index 790da5c4ca4f..f2d3aee909e8 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/pd.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/pd.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, Mellanox Technologies inc. All rights reserved. + * Copyright (c) 2013-2015, Mellanox Technologies. All rights reserved. * * This software is available to you under a choice of one of two * licenses. You may choose to be licensed under the terms of the GNU diff --git a/drivers/net/ethernet/mellanox/mlx5/core/port.c b/drivers/net/ethernet/mellanox/mlx5/core/port.c index 72c2d002c3b8..49e90f2612d8 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/port.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/port.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, Mellanox Technologies inc. All rights reserved. + * Copyright (c) 2013-2015, Mellanox Technologies. All rights reserved. * * This software is available to you under a choice of one of two * licenses. You may choose to be licensed under the terms of the GNU diff --git a/drivers/net/ethernet/mellanox/mlx5/core/qp.c b/drivers/net/ethernet/mellanox/mlx5/core/qp.c index 575d853dbe05..dc7dbf7e9d98 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/qp.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/qp.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, Mellanox Technologies inc. All rights reserved. + * Copyright (c) 2013-2015, Mellanox Technologies. All rights reserved. * * This software is available to you under a choice of one of two * licenses. You may choose to be licensed under the terms of the GNU diff --git a/drivers/net/ethernet/mellanox/mlx5/core/srq.c b/drivers/net/ethernet/mellanox/mlx5/core/srq.c index 38bce93f8314..f9d25dcd03c1 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/srq.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/srq.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, Mellanox Technologies inc. All rights reserved. + * Copyright (c) 2013-2015, Mellanox Technologies. All rights reserved. * * This software is available to you under a choice of one of two * licenses. You may choose to be licensed under the terms of the GNU diff --git a/drivers/net/ethernet/mellanox/mlx5/core/uar.c b/drivers/net/ethernet/mellanox/mlx5/core/uar.c index 06801d6f595e..5a89bb1d678a 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/uar.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/uar.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, Mellanox Technologies inc. All rights reserved. + * Copyright (c) 2013-2015, Mellanox Technologies. All rights reserved. * * This software is available to you under a choice of one of two * licenses. You may choose to be licensed under the terms of the GNU diff --git a/include/linux/mlx5/cmd.h b/include/linux/mlx5/cmd.h index 2826a4b6071e..68cd08f02c2f 100644 --- a/include/linux/mlx5/cmd.h +++ b/include/linux/mlx5/cmd.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, Mellanox Technologies inc. All rights reserved. + * Copyright (c) 2013-2015, Mellanox Technologies. All rights reserved. * * This software is available to you under a choice of one of two * licenses. You may choose to be licensed under the terms of the GNU diff --git a/include/linux/mlx5/cq.h b/include/linux/mlx5/cq.h index f6b17ac601bd..72ee0d732a26 100644 --- a/include/linux/mlx5/cq.h +++ b/include/linux/mlx5/cq.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, Mellanox Technologies inc. All rights reserved. + * Copyright (c) 2013-2015, Mellanox Technologies. All rights reserved. * * This software is available to you under a choice of one of two * licenses. You may choose to be licensed under the terms of the GNU diff --git a/include/linux/mlx5/device.h b/include/linux/mlx5/device.h index 4e5bd813bb9a..abf65c790421 100644 --- a/include/linux/mlx5/device.h +++ b/include/linux/mlx5/device.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, Mellanox Technologies inc. All rights reserved. + * Copyright (c) 2013-2015, Mellanox Technologies. All rights reserved. * * This software is available to you under a choice of one of two * licenses. You may choose to be licensed under the terms of the GNU diff --git a/include/linux/mlx5/doorbell.h b/include/linux/mlx5/doorbell.h index 163a818411e7..afc78a3f4462 100644 --- a/include/linux/mlx5/doorbell.h +++ b/include/linux/mlx5/doorbell.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, Mellanox Technologies inc. All rights reserved. + * Copyright (c) 2013-2015, Mellanox Technologies. All rights reserved. * * This software is available to you under a choice of one of two * licenses. You may choose to be licensed under the terms of the GNU diff --git a/include/linux/mlx5/driver.h b/include/linux/mlx5/driver.h index 8d8ca6d9b03b..8fedd39a9a60 100644 --- a/include/linux/mlx5/driver.h +++ b/include/linux/mlx5/driver.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, Mellanox Technologies inc. All rights reserved. + * Copyright (c) 2013-2015, Mellanox Technologies. All rights reserved. * * This software is available to you under a choice of one of two * licenses. You may choose to be licensed under the terms of the GNU diff --git a/include/linux/mlx5/mlx5_ifc.h b/include/linux/mlx5/mlx5_ifc.h index 5f48b8f592c5..cb3ad17edd1f 100644 --- a/include/linux/mlx5/mlx5_ifc.h +++ b/include/linux/mlx5/mlx5_ifc.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, Mellanox Technologies inc. All rights reserved. + * Copyright (c) 2013-2015, Mellanox Technologies. All rights reserved. * * This software is available to you under a choice of one of two * licenses. You may choose to be licensed under the terms of the GNU diff --git a/include/linux/mlx5/qp.h b/include/linux/mlx5/qp.h index 61f7a342d1bf..310b5f7fd6ae 100644 --- a/include/linux/mlx5/qp.h +++ b/include/linux/mlx5/qp.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, Mellanox Technologies inc. All rights reserved. + * Copyright (c) 2013-2015, Mellanox Technologies. All rights reserved. * * This software is available to you under a choice of one of two * licenses. You may choose to be licensed under the terms of the GNU diff --git a/include/linux/mlx5/srq.h b/include/linux/mlx5/srq.h index e1a363a33663..f43ed054a3e0 100644 --- a/include/linux/mlx5/srq.h +++ b/include/linux/mlx5/srq.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, Mellanox Technologies inc. All rights reserved. + * Copyright (c) 2013-2015, Mellanox Technologies. All rights reserved. * * This software is available to you under a choice of one of two * licenses. You may choose to be licensed under the terms of the GNU -- cgit v1.2.3 From 233d05d28ad942929b6b4fbc48aa8dd083c16484 Mon Sep 17 00:00:00 2001 From: Saeed Mahameed Date: Thu, 2 Apr 2015 17:07:32 +0300 Subject: net/mlx5_core: Move completion eqs from mlx5_ib to mlx5_core Preparation for ethernet driver. These functions will be used in drivers other than mlx5_ib. Signed-off-by: Achiad Shochat Signed-off-by: Saeed Mahameed Signed-off-by: Eli Cohen Signed-off-by: David S. Miller --- drivers/infiniband/hw/mlx5/cq.c | 2 +- drivers/infiniband/hw/mlx5/main.c | 104 +------------------------ drivers/infiniband/hw/mlx5/mlx5_ib.h | 3 - drivers/net/ethernet/mellanox/mlx5/core/main.c | 91 ++++++++++++++++++++++ include/linux/mlx5/driver.h | 3 +- 5 files changed, 98 insertions(+), 105 deletions(-) (limited to 'include') diff --git a/drivers/infiniband/hw/mlx5/cq.c b/drivers/infiniband/hw/mlx5/cq.c index 6efd9f73ff60..c94e2651820d 100644 --- a/drivers/infiniband/hw/mlx5/cq.c +++ b/drivers/infiniband/hw/mlx5/cq.c @@ -780,7 +780,7 @@ struct ib_cq *mlx5_ib_create_cq(struct ib_device *ibdev, int entries, cq->cqe_size = cqe_size; cqb->ctx.cqe_sz_flags = cqe_sz_to_mlx_sz(cqe_size) << 5; cqb->ctx.log_sz_usr_page = cpu_to_be32((ilog2(entries) << 24) | index); - err = mlx5_vector2eqn(dev, vector, &eqn, &irqn); + err = mlx5_vector2eqn(dev->mdev, vector, &eqn, &irqn); if (err) goto err_cqb; diff --git a/drivers/infiniband/hw/mlx5/main.c b/drivers/infiniband/hw/mlx5/main.c index 45816b10af5e..e59e2a1f8726 100644 --- a/drivers/infiniband/hw/mlx5/main.c +++ b/drivers/infiniband/hw/mlx5/main.c @@ -62,95 +62,6 @@ static char mlx5_version[] = DRIVER_NAME ": Mellanox Connect-IB Infiniband driver v" DRIVER_VERSION " (" DRIVER_RELDATE ")\n"; -int mlx5_vector2eqn(struct mlx5_ib_dev *dev, int vector, int *eqn, int *irqn) -{ - struct mlx5_eq_table *table = &dev->mdev->priv.eq_table; - struct mlx5_eq *eq, *n; - int err = -ENOENT; - - spin_lock(&table->lock); - list_for_each_entry_safe(eq, n, &dev->eqs_list, list) { - if (eq->index == vector) { - *eqn = eq->eqn; - *irqn = eq->irqn; - err = 0; - break; - } - } - spin_unlock(&table->lock); - - return err; -} - -static int alloc_comp_eqs(struct mlx5_ib_dev *dev) -{ - struct mlx5_eq_table *table = &dev->mdev->priv.eq_table; - char name[MLX5_MAX_EQ_NAME]; - struct mlx5_eq *eq, *n; - int ncomp_vec; - int nent; - int err; - int i; - - INIT_LIST_HEAD(&dev->eqs_list); - ncomp_vec = table->num_comp_vectors; - nent = MLX5_COMP_EQ_SIZE; - for (i = 0; i < ncomp_vec; i++) { - eq = kzalloc(sizeof(*eq), GFP_KERNEL); - if (!eq) { - err = -ENOMEM; - goto clean; - } - - snprintf(name, MLX5_MAX_EQ_NAME, "mlx5_comp%d", i); - err = mlx5_create_map_eq(dev->mdev, eq, - i + MLX5_EQ_VEC_COMP_BASE, nent, 0, - name, &dev->mdev->priv.uuari.uars[0]); - if (err) { - kfree(eq); - goto clean; - } - mlx5_ib_dbg(dev, "allocated completion EQN %d\n", eq->eqn); - eq->index = i; - spin_lock(&table->lock); - list_add_tail(&eq->list, &dev->eqs_list); - spin_unlock(&table->lock); - } - - dev->num_comp_vectors = ncomp_vec; - return 0; - -clean: - spin_lock(&table->lock); - list_for_each_entry_safe(eq, n, &dev->eqs_list, list) { - list_del(&eq->list); - spin_unlock(&table->lock); - if (mlx5_destroy_unmap_eq(dev->mdev, eq)) - mlx5_ib_warn(dev, "failed to destroy EQ 0x%x\n", eq->eqn); - kfree(eq); - spin_lock(&table->lock); - } - spin_unlock(&table->lock); - return err; -} - -static void free_comp_eqs(struct mlx5_ib_dev *dev) -{ - struct mlx5_eq_table *table = &dev->mdev->priv.eq_table; - struct mlx5_eq *eq, *n; - - spin_lock(&table->lock); - list_for_each_entry_safe(eq, n, &dev->eqs_list, list) { - list_del(&eq->list); - spin_unlock(&table->lock); - if (mlx5_destroy_unmap_eq(dev->mdev, eq)) - mlx5_ib_warn(dev, "failed to destroy EQ 0x%x\n", eq->eqn); - kfree(eq); - spin_lock(&table->lock); - } - spin_unlock(&table->lock); -} - static int mlx5_ib_query_device(struct ib_device *ibdev, struct ib_device_attr *props) { @@ -1291,10 +1202,6 @@ static void *mlx5_ib_add(struct mlx5_core_dev *mdev) get_ext_port_caps(dev); - err = alloc_comp_eqs(dev); - if (err) - goto err_dealloc; - MLX5_INIT_DOORBELL_LOCK(&dev->uar_lock); strlcpy(dev->ib_dev.name, "mlx5_%d", IB_DEVICE_NAME_MAX); @@ -1303,7 +1210,8 @@ static void *mlx5_ib_add(struct mlx5_core_dev *mdev) dev->ib_dev.local_dma_lkey = mdev->caps.gen.reserved_lkey; dev->num_ports = mdev->caps.gen.num_ports; dev->ib_dev.phys_port_cnt = dev->num_ports; - dev->ib_dev.num_comp_vectors = dev->num_comp_vectors; + dev->ib_dev.num_comp_vectors = + dev->mdev->priv.eq_table.num_comp_vectors; dev->ib_dev.dma_device = &mdev->pdev->dev; dev->ib_dev.uverbs_abi_ver = MLX5_IB_UVERBS_ABI_VERSION; @@ -1390,13 +1298,13 @@ static void *mlx5_ib_add(struct mlx5_core_dev *mdev) err = init_node_data(dev); if (err) - goto err_eqs; + goto err_dealloc; mutex_init(&dev->cap_mask_mutex); err = create_dev_resources(&dev->devr); if (err) - goto err_eqs; + goto err_dealloc; err = mlx5_ib_odp_init_one(dev); if (err) @@ -1433,9 +1341,6 @@ err_odp: err_rsrc: destroy_dev_resources(&dev->devr); -err_eqs: - free_comp_eqs(dev); - err_dealloc: ib_dealloc_device((struct ib_device *)dev); @@ -1450,7 +1355,6 @@ static void mlx5_ib_remove(struct mlx5_core_dev *mdev, void *context) destroy_umrc_res(dev); mlx5_ib_odp_remove_one(dev); destroy_dev_resources(&dev->devr); - free_comp_eqs(dev); ib_dealloc_device(&dev->ib_dev); } diff --git a/drivers/infiniband/hw/mlx5/mlx5_ib.h b/drivers/infiniband/hw/mlx5/mlx5_ib.h index 8f94f69fbea5..dff1cfcdf476 100644 --- a/drivers/infiniband/hw/mlx5/mlx5_ib.h +++ b/drivers/infiniband/hw/mlx5/mlx5_ib.h @@ -421,9 +421,7 @@ struct mlx5_ib_dev { struct ib_device ib_dev; struct mlx5_core_dev *mdev; MLX5_DECLARE_DOORBELL_LOCK(uar_lock); - struct list_head eqs_list; int num_ports; - int num_comp_vectors; /* serialize update of capability mask */ struct mutex cap_mask_mutex; @@ -594,7 +592,6 @@ struct ib_xrcd *mlx5_ib_alloc_xrcd(struct ib_device *ibdev, struct ib_ucontext *context, struct ib_udata *udata); int mlx5_ib_dealloc_xrcd(struct ib_xrcd *xrcd); -int mlx5_vector2eqn(struct mlx5_ib_dev *dev, int vector, int *eqn, int *irqn); int mlx5_ib_get_buf_offset(u64 addr, int page_shift, u32 *offset); int mlx5_query_ext_port_caps(struct mlx5_ib_dev *dev, u8 port); int mlx5_ib_query_port(struct ib_device *ibdev, u8 port, diff --git a/drivers/net/ethernet/mellanox/mlx5/core/main.c b/drivers/net/ethernet/mellanox/mlx5/core/main.c index fe41a7c2c629..cfc29027f07a 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/main.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/main.c @@ -507,6 +507,87 @@ static int mlx5_core_disable_hca(struct mlx5_core_dev *dev) return 0; } +int mlx5_vector2eqn(struct mlx5_core_dev *dev, int vector, int *eqn, int *irqn) +{ + struct mlx5_eq_table *table = &dev->priv.eq_table; + struct mlx5_eq *eq, *n; + int err = -ENOENT; + + spin_lock(&table->lock); + list_for_each_entry_safe(eq, n, &table->comp_eqs_list, list) { + if (eq->index == vector) { + *eqn = eq->eqn; + *irqn = eq->irqn; + err = 0; + break; + } + } + spin_unlock(&table->lock); + + return err; +} +EXPORT_SYMBOL(mlx5_vector2eqn); + +static void free_comp_eqs(struct mlx5_core_dev *dev) +{ + struct mlx5_eq_table *table = &dev->priv.eq_table; + struct mlx5_eq *eq, *n; + + spin_lock(&table->lock); + list_for_each_entry_safe(eq, n, &table->comp_eqs_list, list) { + list_del(&eq->list); + spin_unlock(&table->lock); + if (mlx5_destroy_unmap_eq(dev, eq)) + mlx5_core_warn(dev, "failed to destroy EQ 0x%x\n", + eq->eqn); + kfree(eq); + spin_lock(&table->lock); + } + spin_unlock(&table->lock); +} + +static int alloc_comp_eqs(struct mlx5_core_dev *dev) +{ + struct mlx5_eq_table *table = &dev->priv.eq_table; + char name[MLX5_MAX_EQ_NAME]; + struct mlx5_eq *eq; + int ncomp_vec; + int nent; + int err; + int i; + + INIT_LIST_HEAD(&table->comp_eqs_list); + ncomp_vec = table->num_comp_vectors; + nent = MLX5_COMP_EQ_SIZE; + for (i = 0; i < ncomp_vec; i++) { + eq = kzalloc(sizeof(*eq), GFP_KERNEL); + if (!eq) { + err = -ENOMEM; + goto clean; + } + + snprintf(name, MLX5_MAX_EQ_NAME, "mlx5_comp%d", i); + err = mlx5_create_map_eq(dev, eq, + i + MLX5_EQ_VEC_COMP_BASE, nent, 0, + name, &dev->priv.uuari.uars[0]); + if (err) { + kfree(eq); + goto clean; + } + mlx5_core_dbg(dev, "allocated completion EQN %d\n", eq->eqn); + eq->index = i; + spin_lock(&table->lock); + list_add_tail(&eq->list, &table->comp_eqs_list); + spin_unlock(&table->lock); + } + + return 0; + +clean: + free_comp_eqs(dev); + return err; +} + static int mlx5_dev_init(struct mlx5_core_dev *dev, struct pci_dev *pdev) { struct mlx5_priv *priv = &dev->priv; @@ -643,6 +724,12 @@ static int mlx5_dev_init(struct mlx5_core_dev *dev, struct pci_dev *pdev) goto err_free_uar; } + err = alloc_comp_eqs(dev); + if (err) { + dev_err(&pdev->dev, "Failed to alloc completion EQs\n"); + goto err_stop_eqs; + } + MLX5_INIT_DOORBELL_LOCK(&priv->cq_uar_lock); mlx5_init_cq_table(dev); @@ -652,6 +739,9 @@ static int mlx5_dev_init(struct mlx5_core_dev *dev, struct pci_dev *pdev) return 0; +err_stop_eqs: + mlx5_stop_eqs(dev); + err_free_uar: mlx5_free_uuars(dev, &priv->uuari); @@ -703,6 +793,7 @@ static void mlx5_dev_cleanup(struct mlx5_core_dev *dev) mlx5_cleanup_srq_table(dev); mlx5_cleanup_qp_table(dev); mlx5_cleanup_cq_table(dev); + free_comp_eqs(dev); mlx5_stop_eqs(dev); mlx5_free_uuars(dev, &priv->uuari); mlx5_eq_cleanup(dev); diff --git a/include/linux/mlx5/driver.h b/include/linux/mlx5/driver.h index 8fedd39a9a60..f250f6580dad 100644 --- a/include/linux/mlx5/driver.h +++ b/include/linux/mlx5/driver.h @@ -410,7 +410,7 @@ struct mlx5_core_srq { struct mlx5_eq_table { void __iomem *update_ci; void __iomem *update_arm_ci; - struct list_head *comp_eq_head; + struct list_head comp_eqs_list; struct mlx5_eq pages_eq; struct mlx5_eq async_eq; struct mlx5_eq cmd_eq; @@ -725,6 +725,7 @@ int mlx5_create_map_eq(struct mlx5_core_dev *dev, struct mlx5_eq *eq, u8 vecidx, int mlx5_destroy_unmap_eq(struct mlx5_core_dev *dev, struct mlx5_eq *eq); int mlx5_start_eqs(struct mlx5_core_dev *dev); int mlx5_stop_eqs(struct mlx5_core_dev *dev); +int mlx5_vector2eqn(struct mlx5_core_dev *dev, int vector, int *eqn, int *irqn); int mlx5_core_attach_mcg(struct mlx5_core_dev *dev, union ib_gid *mgid, u32 qpn); int mlx5_core_detach_mcg(struct mlx5_core_dev *dev, union ib_gid *mgid, u32 qpn); -- cgit v1.2.3 From ce0f75093282c5dca1e79ae3e3e893deaea86166 Mon Sep 17 00:00:00 2001 From: Saeed Mahameed Date: Thu, 2 Apr 2015 17:07:33 +0300 Subject: net/mlx5_core: Modify arm CQ in preparation for upcoming Ethernet driver Pass consumer index as a parameter to arm CQ Signed-off-by: Saeed Mahameed Signed-off-by: Eli Cohen Signed-off-by: David S. Miller --- drivers/infiniband/hw/mlx5/cq.c | 8 ++++++-- include/linux/mlx5/cq.h | 5 +++-- 2 files changed, 9 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/drivers/infiniband/hw/mlx5/cq.c b/drivers/infiniband/hw/mlx5/cq.c index c94e2651820d..2ee6b1051975 100644 --- a/drivers/infiniband/hw/mlx5/cq.c +++ b/drivers/infiniband/hw/mlx5/cq.c @@ -572,11 +572,15 @@ int mlx5_ib_poll_cq(struct ib_cq *ibcq, int num_entries, struct ib_wc *wc) int mlx5_ib_arm_cq(struct ib_cq *ibcq, enum ib_cq_notify_flags flags) { + struct mlx5_core_dev *mdev = to_mdev(ibcq->device)->mdev; + void __iomem *uar_page = mdev->priv.uuari.uars[0].map; + mlx5_cq_arm(&to_mcq(ibcq)->mcq, (flags & IB_CQ_SOLICITED_MASK) == IB_CQ_SOLICITED ? MLX5_CQ_DB_REQ_NOT_SOL : MLX5_CQ_DB_REQ_NOT, - to_mdev(ibcq->device)->mdev->priv.uuari.uars[0].map, - MLX5_GET_DOORBELL_LOCK(&to_mdev(ibcq->device)->mdev->priv.cq_uar_lock)); + uar_page, + MLX5_GET_DOORBELL_LOCK(&mdev->priv.cq_uar_lock), + to_mcq(ibcq)->mcq.cons_index); return 0; } diff --git a/include/linux/mlx5/cq.h b/include/linux/mlx5/cq.h index 72ee0d732a26..2695ced222df 100644 --- a/include/linux/mlx5/cq.h +++ b/include/linux/mlx5/cq.h @@ -137,14 +137,15 @@ enum { static inline void mlx5_cq_arm(struct mlx5_core_cq *cq, u32 cmd, void __iomem *uar_page, - spinlock_t *doorbell_lock) + spinlock_t *doorbell_lock, + u32 cons_index) { __be32 doorbell[2]; u32 sn; u32 ci; sn = cq->arm_sn & 3; - ci = cq->cons_index & 0xffffff; + ci = cons_index & 0xffffff; *cq->arm_db = cpu_to_be32(sn << 28 | cmd | ci); -- cgit v1.2.3 From 64613d9499c4887485d4350387919ea507330d90 Mon Sep 17 00:00:00 2001 From: Saeed Mahameed Date: Thu, 2 Apr 2015 17:07:34 +0300 Subject: net/mlx5_core: Extend struct mlx5_interface to support multiple protocols Preparation for ethernet driver. Signed-off-by: Achiad Shochat Signed-off-by: Saeed Mahameed Signed-off-by: Eli Cohen Signed-off-by: David S. Miller --- drivers/infiniband/hw/mlx5/main.c | 1 + drivers/net/ethernet/mellanox/mlx5/core/main.c | 22 ++++++++++++++++++++++ include/linux/mlx5/driver.h | 8 ++++++++ 3 files changed, 31 insertions(+) (limited to 'include') diff --git a/drivers/infiniband/hw/mlx5/main.c b/drivers/infiniband/hw/mlx5/main.c index e59e2a1f8726..57c9809e8b87 100644 --- a/drivers/infiniband/hw/mlx5/main.c +++ b/drivers/infiniband/hw/mlx5/main.c @@ -1362,6 +1362,7 @@ static struct mlx5_interface mlx5_ib_interface = { .add = mlx5_ib_add, .remove = mlx5_ib_remove, .event = mlx5_ib_event, + .protocol = MLX5_INTERFACE_PROTOCOL_IB, }; static int __init mlx5_ib_init(void) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/main.c b/drivers/net/ethernet/mellanox/mlx5/core/main.c index cfc29027f07a..28425e5ea91f 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/main.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/main.c @@ -908,6 +908,28 @@ void mlx5_unregister_interface(struct mlx5_interface *intf) } EXPORT_SYMBOL(mlx5_unregister_interface); +void *mlx5_get_protocol_dev(struct mlx5_core_dev *mdev, int protocol) +{ + struct mlx5_priv *priv = &mdev->priv; + struct mlx5_device_context *dev_ctx; + unsigned long flags; + void *result = NULL; + + spin_lock_irqsave(&priv->ctx_lock, flags); + + list_for_each_entry(dev_ctx, &mdev->priv.ctx_list, list) + if ((dev_ctx->intf->protocol == protocol) && + dev_ctx->intf->get_dev) { + result = dev_ctx->intf->get_dev(dev_ctx->context); + break; + } + + spin_unlock_irqrestore(&priv->ctx_lock, flags); + + return result; +} +EXPORT_SYMBOL(mlx5_get_protocol_dev); + static void mlx5_core_event(struct mlx5_core_dev *dev, enum mlx5_dev_event event, unsigned long param) { diff --git a/include/linux/mlx5/driver.h b/include/linux/mlx5/driver.h index f250f6580dad..9a90e7523dc2 100644 --- a/include/linux/mlx5/driver.h +++ b/include/linux/mlx5/driver.h @@ -781,14 +781,22 @@ enum { MAX_MR_CACHE_ENTRIES = 16, }; +enum { + MLX5_INTERFACE_PROTOCOL_IB = 0, + MLX5_INTERFACE_PROTOCOL_ETH = 1, +}; + struct mlx5_interface { void * (*add)(struct mlx5_core_dev *dev); void (*remove)(struct mlx5_core_dev *dev, void *context); void (*event)(struct mlx5_core_dev *dev, void *context, enum mlx5_dev_event event, unsigned long param); + void * (*get_dev)(void *context); + int protocol; struct list_head list; }; +void *mlx5_get_protocol_dev(struct mlx5_core_dev *mdev, int protocol); int mlx5_register_interface(struct mlx5_interface *intf); void mlx5_unregister_interface(struct mlx5_interface *intf); -- cgit v1.2.3 From 64bf8049a2ec1e61e1475eb02b5514d7061d443e Mon Sep 17 00:00:00 2001 From: Masatake YAMATO Date: Wed, 4 Mar 2015 17:13:24 -0300 Subject: [media] am437x: include linux/videodev2.h for expanding BASE_VIDIOC_PRIVATE In am437x-vpfe.h BASE_VIDIOC_PRIVATE is used for making the name of ioctl command(VIDIOC_AM437X_CCDC_CFG). The definition of BASE_VIDIOC_PRIVATE is in linux/videodev2.h. However, linux/videodev2.h is not included in am437x-vpfe.h. As the result an application using has to include both am437x-vpfe.h and linux/videodev2.h. With this patch, the application can include just am437x-vpfe.h. Signed-off-by: Masatake YAMATO Acked-by: Lad, Prabhakar Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- include/uapi/linux/am437x-vpfe.h | 2 ++ 1 file changed, 2 insertions(+) (limited to 'include') diff --git a/include/uapi/linux/am437x-vpfe.h b/include/uapi/linux/am437x-vpfe.h index 9b03033f9cd6..d75774317b9b 100644 --- a/include/uapi/linux/am437x-vpfe.h +++ b/include/uapi/linux/am437x-vpfe.h @@ -21,6 +21,8 @@ #ifndef AM437X_VPFE_USER_H #define AM437X_VPFE_USER_H +#include + enum vpfe_ccdc_data_size { VPFE_CCDC_DATA_16BITS = 0, VPFE_CCDC_DATA_15BITS, -- cgit v1.2.3 From aa05b979f14998fec16fa38f1c91eaa197e73148 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sun, 8 Mar 2015 04:53:32 -0300 Subject: [media] videodev2.h: fix comment The quantization comment in the header was incorrect w.r.t. BT.2020. Fix this. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- include/uapi/linux/videodev2.h | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/include/uapi/linux/videodev2.h b/include/uapi/linux/videodev2.h index fbdc3602ee27..15b21e481246 100644 --- a/include/uapi/linux/videodev2.h +++ b/include/uapi/linux/videodev2.h @@ -268,9 +268,10 @@ enum v4l2_ycbcr_encoding { enum v4l2_quantization { /* - * The default for R'G'B' quantization is always full range. For - * Y'CbCr the quantization is always limited range, except for - * SYCC, XV601, XV709 or JPEG: those are full range. + * The default for R'G'B' quantization is always full range, except + * for the BT2020 colorspace. For Y'CbCr the quantization is always + * limited range, except for COLORSPACE_JPEG, SYCC, XV601 or XV709: + * those are full range. */ V4L2_QUANTIZATION_DEFAULT = 0, V4L2_QUANTIZATION_FULL_RANGE = 1, -- cgit v1.2.3 From a37462b919e1368ea3cf4bb0cbdb00ca8e76959c Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Sat, 31 Jan 2015 20:21:32 -0300 Subject: [media] V4L: remove clock name from v4l2_clk API All uses of the v4l2_clk API so far only register one clock with a fixed name. This allows us to get rid of it, which also will make CCF and DT integration easier. Signed-off-by: Guennadi Liakhovetski Acked-by: Laurent Pinchart Tested-by: Josh Wu Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/soc_camera/soc_camera.c | 6 ++--- drivers/media/usb/em28xx/em28xx-camera.c | 2 +- drivers/media/v4l2-core/v4l2-clk.c | 33 ++++++++++---------------- include/media/v4l2-clk.h | 8 +++---- 4 files changed, 20 insertions(+), 29 deletions(-) (limited to 'include') diff --git a/drivers/media/platform/soc_camera/soc_camera.c b/drivers/media/platform/soc_camera/soc_camera.c index 1ed0a0bc8d44..2049037149a1 100644 --- a/drivers/media/platform/soc_camera/soc_camera.c +++ b/drivers/media/platform/soc_camera/soc_camera.c @@ -1364,7 +1364,7 @@ static int soc_camera_i2c_init(struct soc_camera_device *icd, snprintf(clk_name, sizeof(clk_name), "%d-%04x", shd->i2c_adapter_id, shd->board_info->addr); - icd->clk = v4l2_clk_register(&soc_camera_clk_ops, clk_name, "mclk", icd); + icd->clk = v4l2_clk_register(&soc_camera_clk_ops, clk_name, icd); if (IS_ERR(icd->clk)) { ret = PTR_ERR(icd->clk); goto eclkreg; @@ -1545,7 +1545,7 @@ static int scan_async_group(struct soc_camera_host *ici, snprintf(clk_name, sizeof(clk_name), "%d-%04x", sasd->asd.match.i2c.adapter_id, sasd->asd.match.i2c.address); - icd->clk = v4l2_clk_register(&soc_camera_clk_ops, clk_name, "mclk", icd); + icd->clk = v4l2_clk_register(&soc_camera_clk_ops, clk_name, icd); if (IS_ERR(icd->clk)) { ret = PTR_ERR(icd->clk); goto eclkreg; @@ -1650,7 +1650,7 @@ static int soc_of_bind(struct soc_camera_host *ici, snprintf(clk_name, sizeof(clk_name), "of-%s", of_node_full_name(remote)); - icd->clk = v4l2_clk_register(&soc_camera_clk_ops, clk_name, "mclk", icd); + icd->clk = v4l2_clk_register(&soc_camera_clk_ops, clk_name, icd); if (IS_ERR(icd->clk)) { ret = PTR_ERR(icd->clk); goto eclkreg; diff --git a/drivers/media/usb/em28xx/em28xx-camera.c b/drivers/media/usb/em28xx/em28xx-camera.c index 7be661f73930..a4b22c2c3ba7 100644 --- a/drivers/media/usb/em28xx/em28xx-camera.c +++ b/drivers/media/usb/em28xx/em28xx-camera.c @@ -330,7 +330,7 @@ int em28xx_init_camera(struct em28xx *dev) v4l2_clk_name_i2c(clk_name, sizeof(clk_name), i2c_adapter_id(adap), client->addr); - v4l2->clk = v4l2_clk_register_fixed(clk_name, "mclk", -EINVAL); + v4l2->clk = v4l2_clk_register_fixed(clk_name, -EINVAL); if (IS_ERR(v4l2->clk)) return PTR_ERR(v4l2->clk); diff --git a/drivers/media/v4l2-core/v4l2-clk.c b/drivers/media/v4l2-core/v4l2-clk.c index e18cc0469cf8..3ff0b0093b0e 100644 --- a/drivers/media/v4l2-core/v4l2-clk.c +++ b/drivers/media/v4l2-core/v4l2-clk.c @@ -23,17 +23,13 @@ static DEFINE_MUTEX(clk_lock); static LIST_HEAD(clk_list); -static struct v4l2_clk *v4l2_clk_find(const char *dev_id, const char *id) +static struct v4l2_clk *v4l2_clk_find(const char *dev_id) { struct v4l2_clk *clk; - list_for_each_entry(clk, &clk_list, list) { - if (strcmp(dev_id, clk->dev_id)) - continue; - - if (!id || !clk->id || !strcmp(clk->id, id)) + list_for_each_entry(clk, &clk_list, list) + if (!strcmp(dev_id, clk->dev_id)) return clk; - } return ERR_PTR(-ENODEV); } @@ -43,7 +39,7 @@ struct v4l2_clk *v4l2_clk_get(struct device *dev, const char *id) struct v4l2_clk *clk; mutex_lock(&clk_lock); - clk = v4l2_clk_find(dev_name(dev), id); + clk = v4l2_clk_find(dev_name(dev)); if (!IS_ERR(clk)) atomic_inc(&clk->use_count); @@ -127,8 +123,8 @@ void v4l2_clk_disable(struct v4l2_clk *clk) mutex_lock(&clk->lock); enable = --clk->enable; - if (WARN(enable < 0, "Unbalanced %s() on %s:%s!\n", __func__, - clk->dev_id, clk->id)) + if (WARN(enable < 0, "Unbalanced %s() on %s!\n", __func__, + clk->dev_id)) clk->enable++; else if (!enable && clk->ops->disable) clk->ops->disable(clk); @@ -181,7 +177,7 @@ EXPORT_SYMBOL(v4l2_clk_set_rate); struct v4l2_clk *v4l2_clk_register(const struct v4l2_clk_ops *ops, const char *dev_id, - const char *id, void *priv) + void *priv) { struct v4l2_clk *clk; int ret; @@ -193,9 +189,8 @@ struct v4l2_clk *v4l2_clk_register(const struct v4l2_clk_ops *ops, if (!clk) return ERR_PTR(-ENOMEM); - clk->id = kstrdup(id, GFP_KERNEL); clk->dev_id = kstrdup(dev_id, GFP_KERNEL); - if ((id && !clk->id) || !clk->dev_id) { + if (!clk->dev_id) { ret = -ENOMEM; goto ealloc; } @@ -205,7 +200,7 @@ struct v4l2_clk *v4l2_clk_register(const struct v4l2_clk_ops *ops, mutex_init(&clk->lock); mutex_lock(&clk_lock); - if (!IS_ERR(v4l2_clk_find(dev_id, id))) { + if (!IS_ERR(v4l2_clk_find(dev_id))) { mutex_unlock(&clk_lock); ret = -EEXIST; goto eexist; @@ -217,7 +212,6 @@ struct v4l2_clk *v4l2_clk_register(const struct v4l2_clk_ops *ops, eexist: ealloc: - kfree(clk->id); kfree(clk->dev_id); kfree(clk); return ERR_PTR(ret); @@ -227,15 +221,14 @@ EXPORT_SYMBOL(v4l2_clk_register); void v4l2_clk_unregister(struct v4l2_clk *clk) { if (WARN(atomic_read(&clk->use_count), - "%s(): Refusing to unregister ref-counted %s:%s clock!\n", - __func__, clk->dev_id, clk->id)) + "%s(): Refusing to unregister ref-counted %s clock!\n", + __func__, clk->dev_id)) return; mutex_lock(&clk_lock); list_del(&clk->list); mutex_unlock(&clk_lock); - kfree(clk->id); kfree(clk->dev_id); kfree(clk); } @@ -253,7 +246,7 @@ static unsigned long fixed_get_rate(struct v4l2_clk *clk) } struct v4l2_clk *__v4l2_clk_register_fixed(const char *dev_id, - const char *id, unsigned long rate, struct module *owner) + unsigned long rate, struct module *owner) { struct v4l2_clk *clk; struct v4l2_clk_fixed *priv = kzalloc(sizeof(*priv), GFP_KERNEL); @@ -265,7 +258,7 @@ struct v4l2_clk *__v4l2_clk_register_fixed(const char *dev_id, priv->ops.get_rate = fixed_get_rate; priv->ops.owner = owner; - clk = v4l2_clk_register(&priv->ops, dev_id, id, priv); + clk = v4l2_clk_register(&priv->ops, dev_id, priv); if (IS_ERR(clk)) kfree(priv); diff --git a/include/media/v4l2-clk.h b/include/media/v4l2-clk.h index 0b36cc138304..928045f4e70c 100644 --- a/include/media/v4l2-clk.h +++ b/include/media/v4l2-clk.h @@ -26,7 +26,6 @@ struct v4l2_clk { struct list_head list; const struct v4l2_clk_ops *ops; const char *dev_id; - const char *id; int enable; struct mutex lock; /* Protect the enable count */ atomic_t use_count; @@ -43,7 +42,7 @@ struct v4l2_clk_ops { struct v4l2_clk *v4l2_clk_register(const struct v4l2_clk_ops *ops, const char *dev_name, - const char *name, void *priv); + void *priv); void v4l2_clk_unregister(struct v4l2_clk *clk); struct v4l2_clk *v4l2_clk_get(struct device *dev, const char *id); void v4l2_clk_put(struct v4l2_clk *clk); @@ -55,14 +54,13 @@ int v4l2_clk_set_rate(struct v4l2_clk *clk, unsigned long rate); struct module; struct v4l2_clk *__v4l2_clk_register_fixed(const char *dev_id, - const char *id, unsigned long rate, struct module *owner); + unsigned long rate, struct module *owner); void v4l2_clk_unregister_fixed(struct v4l2_clk *clk); static inline struct v4l2_clk *v4l2_clk_register_fixed(const char *dev_id, - const char *id, unsigned long rate) { - return __v4l2_clk_register_fixed(dev_id, id, rate, THIS_MODULE); + return __v4l2_clk_register_fixed(dev_id, rate, THIS_MODULE); } #define v4l2_clk_name_i2c(name, size, adap, client) snprintf(name, size, \ -- cgit v1.2.3 From 4f528afcfbcac540c8690b41307cac5c22088ff1 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Sun, 1 Feb 2015 08:12:33 -0300 Subject: [media] V4L: add CCF support to the v4l2_clk API V4L2 clocks, e.g. used by camera sensors for their master clock, do not have to be supplied by a different V4L2 driver, they can also be supplied by an independent source. In this case the standart kernel clock API should be used to handle such clocks. This patch adds support for such cases. Signed-off-by: Guennadi Liakhovetski Acked-by: Laurent Pinchart Tested-by: Josh Wu Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-clk.c | 48 +++++++++++++++++++++++++++++++++++--- include/media/v4l2-clk.h | 2 ++ 2 files changed, 47 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/drivers/media/v4l2-core/v4l2-clk.c b/drivers/media/v4l2-core/v4l2-clk.c index 3ff0b0093b0e..34e416a554f6 100644 --- a/drivers/media/v4l2-core/v4l2-clk.c +++ b/drivers/media/v4l2-core/v4l2-clk.c @@ -9,6 +9,7 @@ */ #include +#include #include #include #include @@ -37,6 +38,21 @@ static struct v4l2_clk *v4l2_clk_find(const char *dev_id) struct v4l2_clk *v4l2_clk_get(struct device *dev, const char *id) { struct v4l2_clk *clk; + struct clk *ccf_clk = clk_get(dev, id); + + if (PTR_ERR(ccf_clk) == -EPROBE_DEFER) + return ERR_PTR(-EPROBE_DEFER); + + if (!IS_ERR_OR_NULL(ccf_clk)) { + clk = kzalloc(sizeof(*clk), GFP_KERNEL); + if (!clk) { + clk_put(ccf_clk); + return ERR_PTR(-ENOMEM); + } + clk->clk = ccf_clk; + + return clk; + } mutex_lock(&clk_lock); clk = v4l2_clk_find(dev_name(dev)); @@ -56,6 +72,12 @@ void v4l2_clk_put(struct v4l2_clk *clk) if (IS_ERR(clk)) return; + if (clk->clk) { + clk_put(clk->clk); + kfree(clk); + return; + } + mutex_lock(&clk_lock); list_for_each_entry(tmp, &clk_list, list) @@ -93,8 +115,12 @@ static void v4l2_clk_unlock_driver(struct v4l2_clk *clk) int v4l2_clk_enable(struct v4l2_clk *clk) { - int ret = v4l2_clk_lock_driver(clk); + int ret; + if (clk->clk) + return clk_prepare_enable(clk->clk); + + ret = v4l2_clk_lock_driver(clk); if (ret < 0) return ret; @@ -120,6 +146,9 @@ void v4l2_clk_disable(struct v4l2_clk *clk) { int enable; + if (clk->clk) + return clk_disable_unprepare(clk->clk); + mutex_lock(&clk->lock); enable = --clk->enable; @@ -137,8 +166,12 @@ EXPORT_SYMBOL(v4l2_clk_disable); unsigned long v4l2_clk_get_rate(struct v4l2_clk *clk) { - int ret = v4l2_clk_lock_driver(clk); + int ret; + + if (clk->clk) + return clk_get_rate(clk->clk); + ret = v4l2_clk_lock_driver(clk); if (ret < 0) return ret; @@ -157,7 +190,16 @@ EXPORT_SYMBOL(v4l2_clk_get_rate); int v4l2_clk_set_rate(struct v4l2_clk *clk, unsigned long rate) { - int ret = v4l2_clk_lock_driver(clk); + int ret; + + if (clk->clk) { + long r = clk_round_rate(clk->clk, rate); + if (r < 0) + return r; + return clk_set_rate(clk->clk, r); + } + + ret = v4l2_clk_lock_driver(clk); if (ret < 0) return ret; diff --git a/include/media/v4l2-clk.h b/include/media/v4l2-clk.h index 928045f4e70c..3ef6e3d5ed6c 100644 --- a/include/media/v4l2-clk.h +++ b/include/media/v4l2-clk.h @@ -22,6 +22,7 @@ struct module; struct device; +struct clk; struct v4l2_clk { struct list_head list; const struct v4l2_clk_ops *ops; @@ -29,6 +30,7 @@ struct v4l2_clk { int enable; struct mutex lock; /* Protect the enable count */ atomic_t use_count; + struct clk *clk; void *priv; }; -- cgit v1.2.3 From 7c3be9f812be6c2466ecb705200fc13cc34caa9c Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Sat, 20 Sep 2014 18:38:56 -0300 Subject: [media] v4l: mt9p031: Convert to the gpiod API This simplifies platform data and DT integration. Signed-off-by: Laurent Pinchart Reviewed-by: Lad, Prabhakar Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/mt9p031.c | 31 +++++++++++-------------------- include/media/mt9p031.h | 2 -- 2 files changed, 11 insertions(+), 22 deletions(-) (limited to 'include') diff --git a/drivers/media/i2c/mt9p031.c b/drivers/media/i2c/mt9p031.c index 9258d048a1e3..0db15f528ac1 100644 --- a/drivers/media/i2c/mt9p031.c +++ b/drivers/media/i2c/mt9p031.c @@ -15,12 +15,11 @@ #include #include #include -#include +#include #include #include #include #include -#include #include #include #include @@ -136,7 +135,7 @@ struct mt9p031 { struct aptina_pll pll; unsigned int clk_div; bool use_pll; - int reset; + struct gpio_desc *reset; struct v4l2_ctrl_handler ctrls; struct v4l2_ctrl *blc_auto; @@ -309,9 +308,9 @@ static int mt9p031_power_on(struct mt9p031 *mt9p031) { int ret; - /* Ensure RESET_BAR is low */ - if (gpio_is_valid(mt9p031->reset)) { - gpio_set_value(mt9p031->reset, 0); + /* Ensure RESET_BAR is active */ + if (mt9p031->reset) { + gpiod_set_value(mt9p031->reset, 1); usleep_range(1000, 2000); } @@ -332,8 +331,8 @@ static int mt9p031_power_on(struct mt9p031 *mt9p031) } /* Now RESET_BAR must be high */ - if (gpio_is_valid(mt9p031->reset)) { - gpio_set_value(mt9p031->reset, 1); + if (mt9p031->reset) { + gpiod_set_value(mt9p031->reset, 0); usleep_range(1000, 2000); } @@ -342,8 +341,8 @@ static int mt9p031_power_on(struct mt9p031 *mt9p031) static void mt9p031_power_off(struct mt9p031 *mt9p031) { - if (gpio_is_valid(mt9p031->reset)) { - gpio_set_value(mt9p031->reset, 0); + if (mt9p031->reset) { + gpiod_set_value(mt9p031->reset, 1); usleep_range(1000, 2000); } @@ -1023,7 +1022,6 @@ mt9p031_get_pdata(struct i2c_client *client) if (!pdata) goto done; - pdata->reset = of_get_named_gpio(client->dev.of_node, "reset-gpios", 0); of_property_read_u32(np, "input-clock-frequency", &pdata->ext_freq); of_property_read_u32(np, "pixel-clock-frequency", &pdata->target_freq); @@ -1060,7 +1058,6 @@ static int mt9p031_probe(struct i2c_client *client, mt9p031->output_control = MT9P031_OUTPUT_CONTROL_DEF; mt9p031->mode2 = MT9P031_READ_MODE_2_ROW_BLC; mt9p031->model = did->driver_data; - mt9p031->reset = -1; mt9p031->regulators[0].supply = "vdd"; mt9p031->regulators[1].supply = "vdd_io"; @@ -1136,14 +1133,8 @@ static int mt9p031_probe(struct i2c_client *client, mt9p031->format.field = V4L2_FIELD_NONE; mt9p031->format.colorspace = V4L2_COLORSPACE_SRGB; - if (gpio_is_valid(pdata->reset)) { - ret = devm_gpio_request_one(&client->dev, pdata->reset, - GPIOF_OUT_INIT_LOW, "mt9p031_rst"); - if (ret < 0) - goto done; - - mt9p031->reset = pdata->reset; - } + mt9p031->reset = devm_gpiod_get_optional(&client->dev, "reset", + GPIOD_OUT_HIGH); ret = mt9p031_clk_setup(mt9p031); if (ret) diff --git a/include/media/mt9p031.h b/include/media/mt9p031.h index b1e63f2b72bd..1ba361205af1 100644 --- a/include/media/mt9p031.h +++ b/include/media/mt9p031.h @@ -5,12 +5,10 @@ struct v4l2_subdev; /* * struct mt9p031_platform_data - MT9P031 platform data - * @reset: Chip reset GPIO (set to -1 if not used) * @ext_freq: Input clock frequency * @target_freq: Pixel clock frequency */ struct mt9p031_platform_data { - int reset; int ext_freq; int target_freq; }; -- cgit v1.2.3 From c4c0283ab3cd78f8c53e708f8e75e8ed80468a31 Mon Sep 17 00:00:00 2001 From: Benoit Parrot Date: Fri, 20 Mar 2015 18:03:52 -0300 Subject: [media] media: i2c: add support for omnivision's ov2659 sensor this patch adds support for omnivision's ov2659 sensor, the driver supports following features: 1: Asynchronous probing 2: DT support 3: Media controller support Signed-off-by: Benoit Parrot Signed-off-by: Lad, Prabhakar Acked-by: Sakari Ailus Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- .../devicetree/bindings/media/i2c/ov2659.txt | 38 + MAINTAINERS | 10 + drivers/media/i2c/Kconfig | 11 + drivers/media/i2c/Makefile | 1 + drivers/media/i2c/ov2659.c | 1509 ++++++++++++++++++++ include/media/ov2659.h | 34 + 6 files changed, 1603 insertions(+) create mode 100644 Documentation/devicetree/bindings/media/i2c/ov2659.txt create mode 100644 drivers/media/i2c/ov2659.c create mode 100644 include/media/ov2659.h (limited to 'include') diff --git a/Documentation/devicetree/bindings/media/i2c/ov2659.txt b/Documentation/devicetree/bindings/media/i2c/ov2659.txt new file mode 100644 index 000000000000..cabc7d827dfb --- /dev/null +++ b/Documentation/devicetree/bindings/media/i2c/ov2659.txt @@ -0,0 +1,38 @@ +* OV2659 1/5-Inch 2Mp SOC Camera + +The Omnivision OV2659 is a 1/5-inch SOC camera, with an active array size of +1632H x 1212V. It is programmable through a SCCB. The OV2659 sensor supports +multiple resolutions output, such as UXGA, SVGA, 720p. It also can support +YUV422, RGB565/555 or raw RGB output formats. + +Required Properties: +- compatible: Must be "ovti,ov2659" +- reg: I2C slave address +- clocks: reference to the xvclk input clock. +- clock-names: should be "xvclk". +- link-frequencies: target pixel clock frequency. + +For further reading on port node refer to +Documentation/devicetree/bindings/media/video-interfaces.txt. + +Example: + + i2c0@1c22000 { + ... + ... + ov2659@30 { + compatible = "ovti,ov2659"; + reg = <0x30>; + + clocks = <&clk_ov2659 0>; + clock-names = "xvclk"; + + port { + ov2659_0: endpoint { + remote-endpoint = <&vpfe_ep>; + link-frequencies = /bits/ 64 <70000000>; + }; + }; + }; + ... + }; diff --git a/MAINTAINERS b/MAINTAINERS index 97c174b544a4..460194ca7927 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -8911,6 +8911,16 @@ T: git git://linuxtv.org/mhadli/v4l-dvb-davinci_devices.git S: Maintained F: drivers/media/platform/am437x/ +OV2659 OMNIVISION SENSOR DRIVER +M: Lad, Prabhakar +L: linux-media@vger.kernel.org +W: http://linuxtv.org/ +Q: http://patchwork.linuxtv.org/project/linux-media/list/ +T: git git://linuxtv.org/mhadli/v4l-dvb-davinci_devices.git +S: Maintained +F: drivers/media/i2c/ov2659.c +F: include/media/ov2659.h + SIS 190 ETHERNET DRIVER M: Francois Romieu L: netdev@vger.kernel.org diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig index da58c9bb67c2..6f30ea76151a 100644 --- a/drivers/media/i2c/Kconfig +++ b/drivers/media/i2c/Kconfig @@ -466,6 +466,17 @@ config VIDEO_APTINA_PLL config VIDEO_SMIAPP_PLL tristate +config VIDEO_OV2659 + tristate "OmniVision OV2659 sensor support" + depends on VIDEO_V4L2 && I2C + depends on MEDIA_CAMERA_SUPPORT + ---help--- + This is a Video4Linux2 sensor-level driver for the OmniVision + OV2659 camera. + + To compile this driver as a module, choose M here: the + module will be called ov2659. + config VIDEO_OV7640 tristate "OmniVision OV7640 sensor support" depends on I2C && VIDEO_V4L2 diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile index 9858900168bf..f165faea5b3f 100644 --- a/drivers/media/i2c/Makefile +++ b/drivers/media/i2c/Makefile @@ -77,3 +77,4 @@ obj-$(CONFIG_VIDEO_SMIAPP_PLL) += smiapp-pll.o obj-$(CONFIG_VIDEO_AK881X) += ak881x.o obj-$(CONFIG_VIDEO_IR_I2C) += ir-kbd-i2c.o obj-$(CONFIG_VIDEO_ML86V7667) += ml86v7667.o +obj-$(CONFIG_VIDEO_OV2659) += ov2659.o diff --git a/drivers/media/i2c/ov2659.c b/drivers/media/i2c/ov2659.c new file mode 100644 index 000000000000..edebd114279d --- /dev/null +++ b/drivers/media/i2c/ov2659.c @@ -0,0 +1,1509 @@ +/* + * Omnivision OV2659 CMOS Image Sensor driver + * + * Copyright (C) 2015 Texas Instruments, Inc. + * + * Benoit Parrot + * Lad, Prabhakar + * + * This program is free software; you may redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DRIVER_NAME "ov2659" + +/* + * OV2659 register definitions + */ +#define REG_SOFTWARE_STANDBY 0x0100 +#define REG_SOFTWARE_RESET 0x0103 +#define REG_IO_CTRL00 0x3000 +#define REG_IO_CTRL01 0x3001 +#define REG_IO_CTRL02 0x3002 +#define REG_OUTPUT_VALUE00 0x3008 +#define REG_OUTPUT_VALUE01 0x3009 +#define REG_OUTPUT_VALUE02 0x300d +#define REG_OUTPUT_SELECT00 0x300e +#define REG_OUTPUT_SELECT01 0x300f +#define REG_OUTPUT_SELECT02 0x3010 +#define REG_OUTPUT_DRIVE 0x3011 +#define REG_INPUT_READOUT00 0x302d +#define REG_INPUT_READOUT01 0x302e +#define REG_INPUT_READOUT02 0x302f + +#define REG_SC_PLL_CTRL0 0x3003 +#define REG_SC_PLL_CTRL1 0x3004 +#define REG_SC_PLL_CTRL2 0x3005 +#define REG_SC_PLL_CTRL3 0x3006 +#define REG_SC_CHIP_ID_H 0x300a +#define REG_SC_CHIP_ID_L 0x300b +#define REG_SC_PWC 0x3014 +#define REG_SC_CLKRST0 0x301a +#define REG_SC_CLKRST1 0x301b +#define REG_SC_CLKRST2 0x301c +#define REG_SC_CLKRST3 0x301d +#define REG_SC_SUB_ID 0x302a +#define REG_SC_SCCB_ID 0x302b + +#define REG_GROUP_ADDRESS_00 0x3200 +#define REG_GROUP_ADDRESS_01 0x3201 +#define REG_GROUP_ADDRESS_02 0x3202 +#define REG_GROUP_ADDRESS_03 0x3203 +#define REG_GROUP_ACCESS 0x3208 + +#define REG_AWB_R_GAIN_H 0x3400 +#define REG_AWB_R_GAIN_L 0x3401 +#define REG_AWB_G_GAIN_H 0x3402 +#define REG_AWB_G_GAIN_L 0x3403 +#define REG_AWB_B_GAIN_H 0x3404 +#define REG_AWB_B_GAIN_L 0x3405 +#define REG_AWB_MANUAL_CONTROL 0x3406 + +#define REG_TIMING_HS_H 0x3800 +#define REG_TIMING_HS_L 0x3801 +#define REG_TIMING_VS_H 0x3802 +#define REG_TIMING_VS_L 0x3803 +#define REG_TIMING_HW_H 0x3804 +#define REG_TIMING_HW_L 0x3805 +#define REG_TIMING_VH_H 0x3806 +#define REG_TIMING_VH_L 0x3807 +#define REG_TIMING_DVPHO_H 0x3808 +#define REG_TIMING_DVPHO_L 0x3809 +#define REG_TIMING_DVPVO_H 0x380a +#define REG_TIMING_DVPVO_L 0x380b +#define REG_TIMING_HTS_H 0x380c +#define REG_TIMING_HTS_L 0x380d +#define REG_TIMING_VTS_H 0x380e +#define REG_TIMING_VTS_L 0x380f +#define REG_TIMING_HOFFS_H 0x3810 +#define REG_TIMING_HOFFS_L 0x3811 +#define REG_TIMING_VOFFS_H 0x3812 +#define REG_TIMING_VOFFS_L 0x3813 +#define REG_TIMING_XINC 0x3814 +#define REG_TIMING_YINC 0x3815 +#define REG_TIMING_VERT_FORMAT 0x3820 +#define REG_TIMING_HORIZ_FORMAT 0x3821 + +#define REG_FORMAT_CTRL00 0x4300 + +#define REG_VFIFO_READ_START_H 0x4608 +#define REG_VFIFO_READ_START_L 0x4609 + +#define REG_DVP_CTRL02 0x4708 + +#define REG_ISP_CTRL00 0x5000 +#define REG_ISP_CTRL01 0x5001 +#define REG_ISP_CTRL02 0x5002 + +#define REG_LENC_RED_X0_H 0x500c +#define REG_LENC_RED_X0_L 0x500d +#define REG_LENC_RED_Y0_H 0x500e +#define REG_LENC_RED_Y0_L 0x500f +#define REG_LENC_RED_A1 0x5010 +#define REG_LENC_RED_B1 0x5011 +#define REG_LENC_RED_A2_B2 0x5012 +#define REG_LENC_GREEN_X0_H 0x5013 +#define REG_LENC_GREEN_X0_L 0x5014 +#define REG_LENC_GREEN_Y0_H 0x5015 +#define REG_LENC_GREEN_Y0_L 0x5016 +#define REG_LENC_GREEN_A1 0x5017 +#define REG_LENC_GREEN_B1 0x5018 +#define REG_LENC_GREEN_A2_B2 0x5019 +#define REG_LENC_BLUE_X0_H 0x501a +#define REG_LENC_BLUE_X0_L 0x501b +#define REG_LENC_BLUE_Y0_H 0x501c +#define REG_LENC_BLUE_Y0_L 0x501d +#define REG_LENC_BLUE_A1 0x501e +#define REG_LENC_BLUE_B1 0x501f +#define REG_LENC_BLUE_A2_B2 0x5020 + +#define REG_AWB_CTRL00 0x5035 +#define REG_AWB_CTRL01 0x5036 +#define REG_AWB_CTRL02 0x5037 +#define REG_AWB_CTRL03 0x5038 +#define REG_AWB_CTRL04 0x5039 +#define REG_AWB_LOCAL_LIMIT 0x503a +#define REG_AWB_CTRL12 0x5049 +#define REG_AWB_CTRL13 0x504a +#define REG_AWB_CTRL14 0x504b + +#define REG_SHARPENMT_THRESH1 0x5064 +#define REG_SHARPENMT_THRESH2 0x5065 +#define REG_SHARPENMT_OFFSET1 0x5066 +#define REG_SHARPENMT_OFFSET2 0x5067 +#define REG_DENOISE_THRESH1 0x5068 +#define REG_DENOISE_THRESH2 0x5069 +#define REG_DENOISE_OFFSET1 0x506a +#define REG_DENOISE_OFFSET2 0x506b +#define REG_SHARPEN_THRESH1 0x506c +#define REG_SHARPEN_THRESH2 0x506d +#define REG_CIP_CTRL00 0x506e +#define REG_CIP_CTRL01 0x506f + +#define REG_CMX_SIGN 0x5079 +#define REG_CMX_MISC_CTRL 0x507a + +#define REG_PRE_ISP_CTRL00 0x50a0 +#define TEST_PATTERN_ENABLE BIT(7) +#define VERTICAL_COLOR_BAR_MASK 0x53 + +#define REG_NULL 0x0000 /* Array end token */ + +#define OV265X_ID(_msb, _lsb) ((_msb) << 8 | (_lsb)) +#define OV2659_ID 0x2656 + +struct sensor_register { + u16 addr; + u8 value; +}; + +struct ov2659_framesize { + u16 width; + u16 height; + u16 max_exp_lines; + const struct sensor_register *regs; +}; + +struct ov2659_pll_ctrl { + u8 ctrl1; + u8 ctrl2; + u8 ctrl3; +}; + +struct ov2659_pixfmt { + u32 code; + /* Output format Register Value (REG_FORMAT_CTRL00) */ + struct sensor_register *format_ctrl_regs; +}; + +struct pll_ctrl_reg { + unsigned int div; + unsigned char reg; +}; + +struct ov2659 { + struct v4l2_subdev sd; + struct media_pad pad; + struct v4l2_mbus_framefmt format; + unsigned int xvclk_frequency; + const struct ov2659_platform_data *pdata; + struct mutex lock; + struct i2c_client *client; + struct v4l2_ctrl_handler ctrls; + struct v4l2_ctrl *link_frequency; + const struct ov2659_framesize *frame_size; + struct sensor_register *format_ctrl_regs; + struct ov2659_pll_ctrl pll; + int streaming; +}; + +static const struct sensor_register ov2659_init_regs[] = { + { REG_IO_CTRL00, 0x03 }, + { REG_IO_CTRL01, 0xff }, + { REG_IO_CTRL02, 0xe0 }, + { 0x3633, 0x3d }, + { 0x3620, 0x02 }, + { 0x3631, 0x11 }, + { 0x3612, 0x04 }, + { 0x3630, 0x20 }, + { 0x4702, 0x02 }, + { 0x370c, 0x34 }, + { REG_TIMING_HS_H, 0x00 }, + { REG_TIMING_HS_L, 0x00 }, + { REG_TIMING_VS_H, 0x00 }, + { REG_TIMING_VS_L, 0x00 }, + { REG_TIMING_HW_H, 0x06 }, + { REG_TIMING_HW_L, 0x5f }, + { REG_TIMING_VH_H, 0x04 }, + { REG_TIMING_VH_L, 0xb7 }, + { REG_TIMING_DVPHO_H, 0x03 }, + { REG_TIMING_DVPHO_L, 0x20 }, + { REG_TIMING_DVPVO_H, 0x02 }, + { REG_TIMING_DVPVO_L, 0x58 }, + { REG_TIMING_HTS_H, 0x05 }, + { REG_TIMING_HTS_L, 0x14 }, + { REG_TIMING_VTS_H, 0x02 }, + { REG_TIMING_VTS_L, 0x68 }, + { REG_TIMING_HOFFS_L, 0x08 }, + { REG_TIMING_VOFFS_L, 0x02 }, + { REG_TIMING_XINC, 0x31 }, + { REG_TIMING_YINC, 0x31 }, + { 0x3a02, 0x02 }, + { 0x3a03, 0x68 }, + { 0x3a08, 0x00 }, + { 0x3a09, 0x5c }, + { 0x3a0a, 0x00 }, + { 0x3a0b, 0x4d }, + { 0x3a0d, 0x08 }, + { 0x3a0e, 0x06 }, + { 0x3a14, 0x02 }, + { 0x3a15, 0x28 }, + { REG_DVP_CTRL02, 0x01 }, + { 0x3623, 0x00 }, + { 0x3634, 0x76 }, + { 0x3701, 0x44 }, + { 0x3702, 0x18 }, + { 0x3703, 0x24 }, + { 0x3704, 0x24 }, + { 0x3705, 0x0c }, + { REG_TIMING_VERT_FORMAT, 0x81 }, + { REG_TIMING_HORIZ_FORMAT, 0x01 }, + { 0x370a, 0x52 }, + { REG_VFIFO_READ_START_H, 0x00 }, + { REG_VFIFO_READ_START_L, 0x80 }, + { REG_FORMAT_CTRL00, 0x30 }, + { 0x5086, 0x02 }, + { REG_ISP_CTRL00, 0xfb }, + { REG_ISP_CTRL01, 0x1f }, + { REG_ISP_CTRL02, 0x00 }, + { 0x5025, 0x0e }, + { 0x5026, 0x18 }, + { 0x5027, 0x34 }, + { 0x5028, 0x4c }, + { 0x5029, 0x62 }, + { 0x502a, 0x74 }, + { 0x502b, 0x85 }, + { 0x502c, 0x92 }, + { 0x502d, 0x9e }, + { 0x502e, 0xb2 }, + { 0x502f, 0xc0 }, + { 0x5030, 0xcc }, + { 0x5031, 0xe0 }, + { 0x5032, 0xee }, + { 0x5033, 0xf6 }, + { 0x5034, 0x11 }, + { 0x5070, 0x1c }, + { 0x5071, 0x5b }, + { 0x5072, 0x05 }, + { 0x5073, 0x20 }, + { 0x5074, 0x94 }, + { 0x5075, 0xb4 }, + { 0x5076, 0xb4 }, + { 0x5077, 0xaf }, + { 0x5078, 0x05 }, + { REG_CMX_SIGN, 0x98 }, + { REG_CMX_MISC_CTRL, 0x21 }, + { REG_AWB_CTRL00, 0x6a }, + { REG_AWB_CTRL01, 0x11 }, + { REG_AWB_CTRL02, 0x92 }, + { REG_AWB_CTRL03, 0x21 }, + { REG_AWB_CTRL04, 0xe1 }, + { REG_AWB_LOCAL_LIMIT, 0x01 }, + { 0x503c, 0x05 }, + { 0x503d, 0x08 }, + { 0x503e, 0x08 }, + { 0x503f, 0x64 }, + { 0x5040, 0x58 }, + { 0x5041, 0x2a }, + { 0x5042, 0xc5 }, + { 0x5043, 0x2e }, + { 0x5044, 0x3a }, + { 0x5045, 0x3c }, + { 0x5046, 0x44 }, + { 0x5047, 0xf8 }, + { 0x5048, 0x08 }, + { REG_AWB_CTRL12, 0x70 }, + { REG_AWB_CTRL13, 0xf0 }, + { REG_AWB_CTRL14, 0xf0 }, + { REG_LENC_RED_X0_H, 0x03 }, + { REG_LENC_RED_X0_L, 0x20 }, + { REG_LENC_RED_Y0_H, 0x02 }, + { REG_LENC_RED_Y0_L, 0x5c }, + { REG_LENC_RED_A1, 0x48 }, + { REG_LENC_RED_B1, 0x00 }, + { REG_LENC_RED_A2_B2, 0x66 }, + { REG_LENC_GREEN_X0_H, 0x03 }, + { REG_LENC_GREEN_X0_L, 0x30 }, + { REG_LENC_GREEN_Y0_H, 0x02 }, + { REG_LENC_GREEN_Y0_L, 0x7c }, + { REG_LENC_GREEN_A1, 0x40 }, + { REG_LENC_GREEN_B1, 0x00 }, + { REG_LENC_GREEN_A2_B2, 0x66 }, + { REG_LENC_BLUE_X0_H, 0x03 }, + { REG_LENC_BLUE_X0_L, 0x10 }, + { REG_LENC_BLUE_Y0_H, 0x02 }, + { REG_LENC_BLUE_Y0_L, 0x7c }, + { REG_LENC_BLUE_A1, 0x3a }, + { REG_LENC_BLUE_B1, 0x00 }, + { REG_LENC_BLUE_A2_B2, 0x66 }, + { REG_CIP_CTRL00, 0x44 }, + { REG_SHARPENMT_THRESH1, 0x08 }, + { REG_SHARPENMT_THRESH2, 0x10 }, + { REG_SHARPENMT_OFFSET1, 0x12 }, + { REG_SHARPENMT_OFFSET2, 0x02 }, + { REG_SHARPEN_THRESH1, 0x08 }, + { REG_SHARPEN_THRESH2, 0x10 }, + { REG_CIP_CTRL01, 0xa6 }, + { REG_DENOISE_THRESH1, 0x08 }, + { REG_DENOISE_THRESH2, 0x10 }, + { REG_DENOISE_OFFSET1, 0x04 }, + { REG_DENOISE_OFFSET2, 0x12 }, + { 0x507e, 0x40 }, + { 0x507f, 0x20 }, + { 0x507b, 0x02 }, + { REG_CMX_MISC_CTRL, 0x01 }, + { 0x5084, 0x0c }, + { 0x5085, 0x3e }, + { 0x5005, 0x80 }, + { 0x3a0f, 0x30 }, + { 0x3a10, 0x28 }, + { 0x3a1b, 0x32 }, + { 0x3a1e, 0x26 }, + { 0x3a11, 0x60 }, + { 0x3a1f, 0x14 }, + { 0x5060, 0x69 }, + { 0x5061, 0x7d }, + { 0x5062, 0x7d }, + { 0x5063, 0x69 }, + { REG_NULL, 0x00 }, +}; + +/* 1280X720 720p */ +static struct sensor_register ov2659_720p[] = { + { REG_TIMING_HS_H, 0x00 }, + { REG_TIMING_HS_L, 0xa0 }, + { REG_TIMING_VS_H, 0x00 }, + { REG_TIMING_VS_L, 0xf0 }, + { REG_TIMING_HW_H, 0x05 }, + { REG_TIMING_HW_L, 0xbf }, + { REG_TIMING_VH_H, 0x03 }, + { REG_TIMING_VH_L, 0xcb }, + { REG_TIMING_DVPHO_H, 0x05 }, + { REG_TIMING_DVPHO_L, 0x00 }, + { REG_TIMING_DVPVO_H, 0x02 }, + { REG_TIMING_DVPVO_L, 0xd0 }, + { REG_TIMING_HTS_H, 0x06 }, + { REG_TIMING_HTS_L, 0x4c }, + { REG_TIMING_VTS_H, 0x02 }, + { REG_TIMING_VTS_L, 0xe8 }, + { REG_TIMING_HOFFS_L, 0x10 }, + { REG_TIMING_VOFFS_L, 0x06 }, + { REG_TIMING_XINC, 0x11 }, + { REG_TIMING_YINC, 0x11 }, + { REG_TIMING_VERT_FORMAT, 0x80 }, + { REG_TIMING_HORIZ_FORMAT, 0x00 }, + { 0x3a03, 0xe8 }, + { 0x3a09, 0x6f }, + { 0x3a0b, 0x5d }, + { 0x3a15, 0x9a }, + { REG_NULL, 0x00 }, +}; + +/* 1600X1200 UXGA */ +static struct sensor_register ov2659_uxga[] = { + { REG_TIMING_HS_H, 0x00 }, + { REG_TIMING_HS_L, 0x00 }, + { REG_TIMING_VS_H, 0x00 }, + { REG_TIMING_VS_L, 0x00 }, + { REG_TIMING_HW_H, 0x06 }, + { REG_TIMING_HW_L, 0x5f }, + { REG_TIMING_VH_H, 0x04 }, + { REG_TIMING_VH_L, 0xbb }, + { REG_TIMING_DVPHO_H, 0x06 }, + { REG_TIMING_DVPHO_L, 0x40 }, + { REG_TIMING_DVPVO_H, 0x04 }, + { REG_TIMING_DVPVO_L, 0xb0 }, + { REG_TIMING_HTS_H, 0x07 }, + { REG_TIMING_HTS_L, 0x9f }, + { REG_TIMING_VTS_H, 0x04 }, + { REG_TIMING_VTS_L, 0xd0 }, + { REG_TIMING_HOFFS_L, 0x10 }, + { REG_TIMING_VOFFS_L, 0x06 }, + { REG_TIMING_XINC, 0x11 }, + { REG_TIMING_YINC, 0x11 }, + { 0x3a02, 0x04 }, + { 0x3a03, 0xd0 }, + { 0x3a08, 0x00 }, + { 0x3a09, 0xb8 }, + { 0x3a0a, 0x00 }, + { 0x3a0b, 0x9a }, + { 0x3a0d, 0x08 }, + { 0x3a0e, 0x06 }, + { 0x3a14, 0x04 }, + { 0x3a15, 0x50 }, + { 0x3623, 0x00 }, + { 0x3634, 0x44 }, + { 0x3701, 0x44 }, + { 0x3702, 0x30 }, + { 0x3703, 0x48 }, + { 0x3704, 0x48 }, + { 0x3705, 0x18 }, + { REG_TIMING_VERT_FORMAT, 0x80 }, + { REG_TIMING_HORIZ_FORMAT, 0x00 }, + { 0x370a, 0x12 }, + { REG_VFIFO_READ_START_H, 0x00 }, + { REG_VFIFO_READ_START_L, 0x80 }, + { REG_ISP_CTRL02, 0x00 }, + { REG_NULL, 0x00 }, +}; + +/* 1280X1024 SXGA */ +static struct sensor_register ov2659_sxga[] = { + { REG_TIMING_HS_H, 0x00 }, + { REG_TIMING_HS_L, 0x00 }, + { REG_TIMING_VS_H, 0x00 }, + { REG_TIMING_VS_L, 0x00 }, + { REG_TIMING_HW_H, 0x06 }, + { REG_TIMING_HW_L, 0x5f }, + { REG_TIMING_VH_H, 0x04 }, + { REG_TIMING_VH_L, 0xb7 }, + { REG_TIMING_DVPHO_H, 0x05 }, + { REG_TIMING_DVPHO_L, 0x00 }, + { REG_TIMING_DVPVO_H, 0x04 }, + { REG_TIMING_DVPVO_L, 0x00 }, + { REG_TIMING_HTS_H, 0x07 }, + { REG_TIMING_HTS_L, 0x9c }, + { REG_TIMING_VTS_H, 0x04 }, + { REG_TIMING_VTS_L, 0xd0 }, + { REG_TIMING_HOFFS_L, 0x10 }, + { REG_TIMING_VOFFS_L, 0x06 }, + { REG_TIMING_XINC, 0x11 }, + { REG_TIMING_YINC, 0x11 }, + { 0x3a02, 0x02 }, + { 0x3a03, 0x68 }, + { 0x3a08, 0x00 }, + { 0x3a09, 0x5c }, + { 0x3a0a, 0x00 }, + { 0x3a0b, 0x4d }, + { 0x3a0d, 0x08 }, + { 0x3a0e, 0x06 }, + { 0x3a14, 0x02 }, + { 0x3a15, 0x28 }, + { 0x3623, 0x00 }, + { 0x3634, 0x76 }, + { 0x3701, 0x44 }, + { 0x3702, 0x18 }, + { 0x3703, 0x24 }, + { 0x3704, 0x24 }, + { 0x3705, 0x0c }, + { REG_TIMING_VERT_FORMAT, 0x80 }, + { REG_TIMING_HORIZ_FORMAT, 0x00 }, + { 0x370a, 0x52 }, + { REG_VFIFO_READ_START_H, 0x00 }, + { REG_VFIFO_READ_START_L, 0x80 }, + { REG_ISP_CTRL02, 0x00 }, + { REG_NULL, 0x00 }, +}; + +/* 1024X768 SXGA */ +static struct sensor_register ov2659_xga[] = { + { REG_TIMING_HS_H, 0x00 }, + { REG_TIMING_HS_L, 0x00 }, + { REG_TIMING_VS_H, 0x00 }, + { REG_TIMING_VS_L, 0x00 }, + { REG_TIMING_HW_H, 0x06 }, + { REG_TIMING_HW_L, 0x5f }, + { REG_TIMING_VH_H, 0x04 }, + { REG_TIMING_VH_L, 0xb7 }, + { REG_TIMING_DVPHO_H, 0x04 }, + { REG_TIMING_DVPHO_L, 0x00 }, + { REG_TIMING_DVPVO_H, 0x03 }, + { REG_TIMING_DVPVO_L, 0x00 }, + { REG_TIMING_HTS_H, 0x07 }, + { REG_TIMING_HTS_L, 0x9c }, + { REG_TIMING_VTS_H, 0x04 }, + { REG_TIMING_VTS_L, 0xd0 }, + { REG_TIMING_HOFFS_L, 0x10 }, + { REG_TIMING_VOFFS_L, 0x06 }, + { REG_TIMING_XINC, 0x11 }, + { REG_TIMING_YINC, 0x11 }, + { 0x3a02, 0x02 }, + { 0x3a03, 0x68 }, + { 0x3a08, 0x00 }, + { 0x3a09, 0x5c }, + { 0x3a0a, 0x00 }, + { 0x3a0b, 0x4d }, + { 0x3a0d, 0x08 }, + { 0x3a0e, 0x06 }, + { 0x3a14, 0x02 }, + { 0x3a15, 0x28 }, + { 0x3623, 0x00 }, + { 0x3634, 0x76 }, + { 0x3701, 0x44 }, + { 0x3702, 0x18 }, + { 0x3703, 0x24 }, + { 0x3704, 0x24 }, + { 0x3705, 0x0c }, + { REG_TIMING_VERT_FORMAT, 0x80 }, + { REG_TIMING_HORIZ_FORMAT, 0x00 }, + { 0x370a, 0x52 }, + { REG_VFIFO_READ_START_H, 0x00 }, + { REG_VFIFO_READ_START_L, 0x80 }, + { REG_ISP_CTRL02, 0x00 }, + { REG_NULL, 0x00 }, +}; + +/* 800X600 SVGA */ +static struct sensor_register ov2659_svga[] = { + { REG_TIMING_HS_H, 0x00 }, + { REG_TIMING_HS_L, 0x00 }, + { REG_TIMING_VS_H, 0x00 }, + { REG_TIMING_VS_L, 0x00 }, + { REG_TIMING_HW_H, 0x06 }, + { REG_TIMING_HW_L, 0x5f }, + { REG_TIMING_VH_H, 0x04 }, + { REG_TIMING_VH_L, 0xb7 }, + { REG_TIMING_DVPHO_H, 0x03 }, + { REG_TIMING_DVPHO_L, 0x20 }, + { REG_TIMING_DVPVO_H, 0x02 }, + { REG_TIMING_DVPVO_L, 0x58 }, + { REG_TIMING_HTS_H, 0x05 }, + { REG_TIMING_HTS_L, 0x14 }, + { REG_TIMING_VTS_H, 0x02 }, + { REG_TIMING_VTS_L, 0x68 }, + { REG_TIMING_HOFFS_L, 0x08 }, + { REG_TIMING_VOFFS_L, 0x02 }, + { REG_TIMING_XINC, 0x31 }, + { REG_TIMING_YINC, 0x31 }, + { 0x3a02, 0x02 }, + { 0x3a03, 0x68 }, + { 0x3a08, 0x00 }, + { 0x3a09, 0x5c }, + { 0x3a0a, 0x00 }, + { 0x3a0b, 0x4d }, + { 0x3a0d, 0x08 }, + { 0x3a0e, 0x06 }, + { 0x3a14, 0x02 }, + { 0x3a15, 0x28 }, + { 0x3623, 0x00 }, + { 0x3634, 0x76 }, + { 0x3701, 0x44 }, + { 0x3702, 0x18 }, + { 0x3703, 0x24 }, + { 0x3704, 0x24 }, + { 0x3705, 0x0c }, + { REG_TIMING_VERT_FORMAT, 0x81 }, + { REG_TIMING_HORIZ_FORMAT, 0x01 }, + { 0x370a, 0x52 }, + { REG_VFIFO_READ_START_H, 0x00 }, + { REG_VFIFO_READ_START_L, 0x80 }, + { REG_ISP_CTRL02, 0x00 }, + { REG_NULL, 0x00 }, +}; + +/* 640X480 VGA */ +static struct sensor_register ov2659_vga[] = { + { REG_TIMING_HS_H, 0x00 }, + { REG_TIMING_HS_L, 0x00 }, + { REG_TIMING_VS_H, 0x00 }, + { REG_TIMING_VS_L, 0x00 }, + { REG_TIMING_HW_H, 0x06 }, + { REG_TIMING_HW_L, 0x5f }, + { REG_TIMING_VH_H, 0x04 }, + { REG_TIMING_VH_L, 0xb7 }, + { REG_TIMING_DVPHO_H, 0x02 }, + { REG_TIMING_DVPHO_L, 0x80 }, + { REG_TIMING_DVPVO_H, 0x01 }, + { REG_TIMING_DVPVO_L, 0xe0 }, + { REG_TIMING_HTS_H, 0x05 }, + { REG_TIMING_HTS_L, 0x14 }, + { REG_TIMING_VTS_H, 0x02 }, + { REG_TIMING_VTS_L, 0x68 }, + { REG_TIMING_HOFFS_L, 0x08 }, + { REG_TIMING_VOFFS_L, 0x02 }, + { REG_TIMING_XINC, 0x31 }, + { REG_TIMING_YINC, 0x31 }, + { 0x3a02, 0x02 }, + { 0x3a03, 0x68 }, + { 0x3a08, 0x00 }, + { 0x3a09, 0x5c }, + { 0x3a0a, 0x00 }, + { 0x3a0b, 0x4d }, + { 0x3a0d, 0x08 }, + { 0x3a0e, 0x06 }, + { 0x3a14, 0x02 }, + { 0x3a15, 0x28 }, + { 0x3623, 0x00 }, + { 0x3634, 0x76 }, + { 0x3701, 0x44 }, + { 0x3702, 0x18 }, + { 0x3703, 0x24 }, + { 0x3704, 0x24 }, + { 0x3705, 0x0c }, + { REG_TIMING_VERT_FORMAT, 0x81 }, + { REG_TIMING_HORIZ_FORMAT, 0x01 }, + { 0x370a, 0x52 }, + { REG_VFIFO_READ_START_H, 0x00 }, + { REG_VFIFO_READ_START_L, 0x80 }, + { REG_ISP_CTRL02, 0x10 }, + { REG_NULL, 0x00 }, +}; + +/* 320X240 QVGA */ +static struct sensor_register ov2659_qvga[] = { + { REG_TIMING_HS_H, 0x00 }, + { REG_TIMING_HS_L, 0x00 }, + { REG_TIMING_VS_H, 0x00 }, + { REG_TIMING_VS_L, 0x00 }, + { REG_TIMING_HW_H, 0x06 }, + { REG_TIMING_HW_L, 0x5f }, + { REG_TIMING_VH_H, 0x04 }, + { REG_TIMING_VH_L, 0xb7 }, + { REG_TIMING_DVPHO_H, 0x01 }, + { REG_TIMING_DVPHO_L, 0x40 }, + { REG_TIMING_DVPVO_H, 0x00 }, + { REG_TIMING_DVPVO_L, 0xf0 }, + { REG_TIMING_HTS_H, 0x05 }, + { REG_TIMING_HTS_L, 0x14 }, + { REG_TIMING_VTS_H, 0x02 }, + { REG_TIMING_VTS_L, 0x68 }, + { REG_TIMING_HOFFS_L, 0x08 }, + { REG_TIMING_VOFFS_L, 0x02 }, + { REG_TIMING_XINC, 0x31 }, + { REG_TIMING_YINC, 0x31 }, + { 0x3a02, 0x02 }, + { 0x3a03, 0x68 }, + { 0x3a08, 0x00 }, + { 0x3a09, 0x5c }, + { 0x3a0a, 0x00 }, + { 0x3a0b, 0x4d }, + { 0x3a0d, 0x08 }, + { 0x3a0e, 0x06 }, + { 0x3a14, 0x02 }, + { 0x3a15, 0x28 }, + { 0x3623, 0x00 }, + { 0x3634, 0x76 }, + { 0x3701, 0x44 }, + { 0x3702, 0x18 }, + { 0x3703, 0x24 }, + { 0x3704, 0x24 }, + { 0x3705, 0x0c }, + { REG_TIMING_VERT_FORMAT, 0x81 }, + { REG_TIMING_HORIZ_FORMAT, 0x01 }, + { 0x370a, 0x52 }, + { REG_VFIFO_READ_START_H, 0x00 }, + { REG_VFIFO_READ_START_L, 0x80 }, + { REG_ISP_CTRL02, 0x10 }, + { REG_NULL, 0x00 }, +}; + +static const struct pll_ctrl_reg ctrl3[] = { + { 1, 0x00 }, + { 2, 0x02 }, + { 3, 0x03 }, + { 4, 0x06 }, + { 6, 0x0d }, + { 8, 0x0e }, + { 12, 0x0f }, + { 16, 0x12 }, + { 24, 0x13 }, + { 32, 0x16 }, + { 48, 0x1b }, + { 64, 0x1e }, + { 96, 0x1f }, + { 0, 0x00 }, +}; + +static const struct pll_ctrl_reg ctrl1[] = { + { 2, 0x10 }, + { 4, 0x20 }, + { 6, 0x30 }, + { 8, 0x40 }, + { 10, 0x50 }, + { 12, 0x60 }, + { 14, 0x70 }, + { 16, 0x80 }, + { 18, 0x90 }, + { 20, 0xa0 }, + { 22, 0xb0 }, + { 24, 0xc0 }, + { 26, 0xd0 }, + { 28, 0xe0 }, + { 30, 0xf0 }, + { 0, 0x00 }, +}; + +static const struct ov2659_framesize ov2659_framesizes[] = { + { /* QVGA */ + .width = 320, + .height = 240, + .regs = ov2659_qvga, + .max_exp_lines = 248, + }, { /* VGA */ + .width = 640, + .height = 480, + .regs = ov2659_vga, + .max_exp_lines = 498, + }, { /* SVGA */ + .width = 800, + .height = 600, + .regs = ov2659_svga, + .max_exp_lines = 498, + }, { /* XGA */ + .width = 1024, + .height = 768, + .regs = ov2659_xga, + .max_exp_lines = 498, + }, { /* 720P */ + .width = 1280, + .height = 720, + .regs = ov2659_720p, + .max_exp_lines = 498, + }, { /* SXGA */ + .width = 1280, + .height = 1024, + .regs = ov2659_sxga, + .max_exp_lines = 1048, + }, { /* UXGA */ + .width = 1600, + .height = 1200, + .regs = ov2659_uxga, + .max_exp_lines = 498, + }, +}; + +/* YUV422 YUYV*/ +static struct sensor_register ov2659_format_yuyv[] = { + { REG_FORMAT_CTRL00, 0x30 }, + { REG_NULL, 0x0 }, +}; + +/* YUV422 UYVY */ +static struct sensor_register ov2659_format_uyvy[] = { + { REG_FORMAT_CTRL00, 0x32 }, + { REG_NULL, 0x0 }, +}; + +/* Raw Bayer BGGR */ +static struct sensor_register ov2659_format_bggr[] = { + { REG_FORMAT_CTRL00, 0x00 }, + { REG_NULL, 0x0 }, +}; + +/* RGB565 */ +static struct sensor_register ov2659_format_rgb565[] = { + { REG_FORMAT_CTRL00, 0x60 }, + { REG_NULL, 0x0 }, +}; + +static const struct ov2659_pixfmt ov2659_formats[] = { + { + .code = MEDIA_BUS_FMT_YUYV8_2X8, + .format_ctrl_regs = ov2659_format_yuyv, + }, { + .code = MEDIA_BUS_FMT_UYVY8_2X8, + .format_ctrl_regs = ov2659_format_uyvy, + }, { + .code = MEDIA_BUS_FMT_RGB565_2X8_BE, + .format_ctrl_regs = ov2659_format_rgb565, + }, { + .code = MEDIA_BUS_FMT_SBGGR8_1X8, + .format_ctrl_regs = ov2659_format_bggr, + }, +}; + +static inline struct ov2659 *to_ov2659(struct v4l2_subdev *sd) +{ + return container_of(sd, struct ov2659, sd); +} + +/* sensor register write */ +static int ov2659_write(struct i2c_client *client, u16 reg, u8 val) +{ + struct i2c_msg msg; + u8 buf[3]; + int ret; + + buf[0] = reg >> 8; + buf[1] = reg & 0xFF; + buf[2] = val; + + msg.addr = client->addr; + msg.flags = client->flags; + msg.buf = buf; + msg.len = sizeof(buf); + + ret = i2c_transfer(client->adapter, &msg, 1); + if (ret >= 0) + return 0; + + dev_dbg(&client->dev, + "ov2659 write reg(0x%x val:0x%x) failed !\n", reg, val); + + return ret; +} + +/* sensor register read */ +static int ov2659_read(struct i2c_client *client, u16 reg, u8 *val) +{ + struct i2c_msg msg[2]; + u8 buf[2]; + int ret; + + buf[0] = reg >> 8; + buf[1] = reg & 0xFF; + + msg[0].addr = client->addr; + msg[0].flags = client->flags; + msg[0].buf = buf; + msg[0].len = sizeof(buf); + + msg[1].addr = client->addr; + msg[1].flags = client->flags | I2C_M_RD; + msg[1].buf = buf; + msg[1].len = 1; + + ret = i2c_transfer(client->adapter, msg, 2); + if (ret >= 0) { + *val = buf[0]; + return 0; + } + + dev_dbg(&client->dev, + "ov2659 read reg(0x%x val:0x%x) failed !\n", reg, *val); + + return ret; +} + +static int ov2659_write_array(struct i2c_client *client, + const struct sensor_register *regs) +{ + int i, ret = 0; + + for (i = 0; ret == 0 && regs[i].addr; i++) + ret = ov2659_write(client, regs[i].addr, regs[i].value); + + return ret; +} + +static void ov2659_pll_calc_params(struct ov2659 *ov2659) +{ + const struct ov2659_platform_data *pdata = ov2659->pdata; + u8 ctrl1_reg = 0, ctrl2_reg = 0, ctrl3_reg = 0; + struct i2c_client *client = ov2659->client; + unsigned int desired = pdata->link_frequency; + u32 s_prediv = 1, s_postdiv = 1, s_mult = 1; + u32 prediv, postdiv, mult; + u32 bestdelta = -1; + u32 delta, actual; + int i, j; + + for (i = 0; ctrl1[i].div != 0; i++) { + postdiv = ctrl1[i].div; + for (j = 0; ctrl3[j].div != 0; j++) { + prediv = ctrl3[j].div; + for (mult = 1; mult <= 63; mult++) { + actual = ov2659->xvclk_frequency; + actual *= mult; + actual /= prediv; + actual /= postdiv; + delta = actual - desired; + delta = abs(delta); + + if ((delta < bestdelta) || (bestdelta == -1)) { + bestdelta = delta; + s_mult = mult; + s_prediv = prediv; + s_postdiv = postdiv; + ctrl1_reg = ctrl1[i].reg; + ctrl2_reg = mult; + ctrl3_reg = ctrl3[j].reg; + } + } + } + } + + ov2659->pll.ctrl1 = ctrl1_reg; + ov2659->pll.ctrl2 = ctrl2_reg; + ov2659->pll.ctrl3 = ctrl3_reg; + + dev_dbg(&client->dev, + "Actual reg config: ctrl1_reg: %02x ctrl2_reg: %02x ctrl3_reg: %02x\n", + ctrl1_reg, ctrl2_reg, ctrl3_reg); +} + +static int ov2659_set_pixel_clock(struct ov2659 *ov2659) +{ + struct i2c_client *client = ov2659->client; + struct sensor_register pll_regs[] = { + {REG_SC_PLL_CTRL1, ov2659->pll.ctrl1}, + {REG_SC_PLL_CTRL2, ov2659->pll.ctrl2}, + {REG_SC_PLL_CTRL3, ov2659->pll.ctrl3}, + {REG_NULL, 0x00}, + }; + + dev_dbg(&client->dev, "%s\n", __func__); + + return ov2659_write_array(client, pll_regs); +}; + +static void ov2659_get_default_format(struct v4l2_mbus_framefmt *format) +{ + format->width = ov2659_framesizes[2].width; + format->height = ov2659_framesizes[2].height; + format->colorspace = V4L2_COLORSPACE_SRGB; + format->code = ov2659_formats[0].code; + format->field = V4L2_FIELD_NONE; +} + +static void ov2659_set_streaming(struct ov2659 *ov2659, int on) +{ + struct i2c_client *client = ov2659->client; + int ret; + + on = !!on; + + dev_dbg(&client->dev, "%s: on: %d\n", __func__, on); + + ret = ov2659_write(client, REG_SOFTWARE_STANDBY, on); + if (ret) + dev_err(&client->dev, "ov2659 soft standby failed\n"); +} + +static int ov2659_init(struct v4l2_subdev *sd, u32 val) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + return ov2659_write_array(client, ov2659_init_regs); +} + +/* + * V4L2 subdev video and pad level operations + */ + +static int ov2659_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_mbus_code_enum *code) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + dev_dbg(&client->dev, "%s:\n", __func__); + + if (code->index >= ARRAY_SIZE(ov2659_formats)) + return -EINVAL; + + code->code = ov2659_formats[code->index].code; + + return 0; +} + +static int ov2659_enum_frame_sizes(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_frame_size_enum *fse) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + int i = ARRAY_SIZE(ov2659_formats); + + dev_dbg(&client->dev, "%s:\n", __func__); + + if (fse->index >= ARRAY_SIZE(ov2659_framesizes)) + return -EINVAL; + + while (--i) + if (fse->code == ov2659_formats[i].code) + break; + + fse->code = ov2659_formats[i].code; + + fse->min_width = ov2659_framesizes[fse->index].width; + fse->max_width = fse->min_width; + fse->max_height = ov2659_framesizes[fse->index].height; + fse->min_height = fse->max_height; + + return 0; +} + +static int ov2659_get_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *fmt) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct ov2659 *ov2659 = to_ov2659(sd); + struct v4l2_mbus_framefmt *mf; + + dev_dbg(&client->dev, "ov2659_get_fmt\n"); + + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { + mf = v4l2_subdev_get_try_format(sd, cfg, 0); + mutex_lock(&ov2659->lock); + fmt->format = *mf; + mutex_unlock(&ov2659->lock); + return 0; + } + + mutex_lock(&ov2659->lock); + fmt->format = ov2659->format; + mutex_unlock(&ov2659->lock); + + dev_dbg(&client->dev, "ov2659_get_fmt: %x %dx%d\n", + ov2659->format.code, ov2659->format.width, + ov2659->format.height); + + return 0; +} + +static void __ov2659_try_frame_size(struct v4l2_mbus_framefmt *mf, + const struct ov2659_framesize **size) +{ + const struct ov2659_framesize *fsize = &ov2659_framesizes[0]; + const struct ov2659_framesize *match = NULL; + int i = ARRAY_SIZE(ov2659_framesizes); + unsigned int min_err = UINT_MAX; + + while (i--) { + int err = abs(fsize->width - mf->width) + + abs(fsize->height - mf->height); + if ((err < min_err) && (fsize->regs[0].addr)) { + min_err = err; + match = fsize; + } + fsize++; + } + + if (!match) + match = &ov2659_framesizes[2]; + + mf->width = match->width; + mf->height = match->height; + + if (size) + *size = match; +} + +static int ov2659_set_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *fmt) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + unsigned int index = ARRAY_SIZE(ov2659_formats); + struct v4l2_mbus_framefmt *mf = &fmt->format; + const struct ov2659_framesize *size = NULL; + struct ov2659 *ov2659 = to_ov2659(sd); + int ret = 0; + + dev_dbg(&client->dev, "ov2659_set_fmt\n"); + + __ov2659_try_frame_size(mf, &size); + + while (--index >= 0) + if (ov2659_formats[index].code == mf->code) + break; + + if (index < 0) + return -EINVAL; + + mf->colorspace = V4L2_COLORSPACE_SRGB; + mf->code = ov2659_formats[index].code; + mf->field = V4L2_FIELD_NONE; + + mutex_lock(&ov2659->lock); + + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { + mf = v4l2_subdev_get_try_format(sd, cfg, fmt->pad); + *mf = fmt->format; + } else { + s64 val; + + if (ov2659->streaming) { + mutex_unlock(&ov2659->lock); + return -EBUSY; + } + + ov2659->frame_size = size; + ov2659->format = fmt->format; + ov2659->format_ctrl_regs = + ov2659_formats[index].format_ctrl_regs; + + if (ov2659->format.code != MEDIA_BUS_FMT_SBGGR8_1X8) + val = ov2659->pdata->link_frequency / 2; + else + val = ov2659->pdata->link_frequency; + + ret = v4l2_ctrl_s_ctrl_int64(ov2659->link_frequency, val); + if (ret < 0) + dev_warn(&client->dev, + "failed to set link_frequency rate (%d)\n", + ret); + } + + mutex_unlock(&ov2659->lock); + return ret; +} + +static int ov2659_set_frame_size(struct ov2659 *ov2659) +{ + struct i2c_client *client = ov2659->client; + + dev_dbg(&client->dev, "%s\n", __func__); + + return ov2659_write_array(ov2659->client, ov2659->frame_size->regs); +} + +static int ov2659_set_format(struct ov2659 *ov2659) +{ + struct i2c_client *client = ov2659->client; + + dev_dbg(&client->dev, "%s\n", __func__); + + return ov2659_write_array(ov2659->client, ov2659->format_ctrl_regs); +} + +static int ov2659_s_stream(struct v4l2_subdev *sd, int on) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct ov2659 *ov2659 = to_ov2659(sd); + int ret = 0; + + dev_dbg(&client->dev, "%s: on: %d\n", __func__, on); + + mutex_lock(&ov2659->lock); + + on = !!on; + + if (ov2659->streaming == on) + goto unlock; + + if (!on) { + /* Stop Streaming Sequence */ + ov2659_set_streaming(ov2659, 0); + ov2659->streaming = on; + goto unlock; + } + + ov2659_set_pixel_clock(ov2659); + ov2659_set_frame_size(ov2659); + ov2659_set_format(ov2659); + ov2659_set_streaming(ov2659, 1); + ov2659->streaming = on; + +unlock: + mutex_unlock(&ov2659->lock); + return ret; +} + +static int ov2659_set_test_pattern(struct ov2659 *ov2659, int value) +{ + struct i2c_client *client = v4l2_get_subdevdata(&ov2659->sd); + int ret; + u8 val; + + ret = ov2659_read(client, REG_PRE_ISP_CTRL00, &val); + if (ret < 0) + return ret; + + switch (value) { + case 0: + val &= ~TEST_PATTERN_ENABLE; + break; + case 1: + val &= VERTICAL_COLOR_BAR_MASK; + val |= TEST_PATTERN_ENABLE; + break; + } + + return ov2659_write(client, REG_PRE_ISP_CTRL00, val); +} + +static int ov2659_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct ov2659 *ov2659 = + container_of(ctrl->handler, struct ov2659, ctrls); + + switch (ctrl->id) { + case V4L2_CID_TEST_PATTERN: + return ov2659_set_test_pattern(ov2659, ctrl->val); + } + + return 0; +} + +static struct v4l2_ctrl_ops ov2659_ctrl_ops = { + .s_ctrl = ov2659_s_ctrl, +}; + +static const char * const ov2659_test_pattern_menu[] = { + "Disabled", + "Vertical Color Bars", +}; + +/* ----------------------------------------------------------------------------- + * V4L2 subdev internal operations + */ + +static int ov2659_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct v4l2_mbus_framefmt *format = + v4l2_subdev_get_try_format(sd, fh->pad, 0); + + dev_dbg(&client->dev, "%s:\n", __func__); + + ov2659_get_default_format(format); + + return 0; +} + +static const struct v4l2_subdev_core_ops ov2659_subdev_core_ops = { + .log_status = v4l2_ctrl_subdev_log_status, + .subscribe_event = v4l2_ctrl_subdev_subscribe_event, + .unsubscribe_event = v4l2_event_subdev_unsubscribe, +}; + +static const struct v4l2_subdev_video_ops ov2659_subdev_video_ops = { + .s_stream = ov2659_s_stream, +}; + +static const struct v4l2_subdev_pad_ops ov2659_subdev_pad_ops = { + .enum_mbus_code = ov2659_enum_mbus_code, + .enum_frame_size = ov2659_enum_frame_sizes, + .get_fmt = ov2659_get_fmt, + .set_fmt = ov2659_set_fmt, +}; + +static const struct v4l2_subdev_ops ov2659_subdev_ops = { + .core = &ov2659_subdev_core_ops, + .video = &ov2659_subdev_video_ops, + .pad = &ov2659_subdev_pad_ops, +}; + +static const struct v4l2_subdev_internal_ops ov2659_subdev_internal_ops = { + .open = ov2659_open, +}; + +static int ov2659_detect(struct v4l2_subdev *sd) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + u8 pid, ver; + int ret; + + dev_dbg(&client->dev, "%s:\n", __func__); + + ret = ov2659_write(client, REG_SOFTWARE_RESET, 0x01); + if (ret != 0) { + dev_err(&client->dev, "Sensor soft reset failed\n"); + return -ENODEV; + } + usleep_range(1000, 2000); + + ret = ov2659_init(sd, 0); + if (ret < 0) + return ret; + + /* Check sensor revision */ + ret = ov2659_read(client, REG_SC_CHIP_ID_H, &pid); + if (!ret) + ret = ov2659_read(client, REG_SC_CHIP_ID_L, &ver); + + if (!ret) { + unsigned short id; + + id = OV265X_ID(pid, ver); + if (id != OV2659_ID) + dev_err(&client->dev, + "Sensor detection failed (%04X, %d)\n", + id, ret); + else + dev_info(&client->dev, "Found OV%04X sensor\n", id); + } + + return ret; +} + +static struct ov2659_platform_data * +ov2659_get_pdata(struct i2c_client *client) +{ + struct ov2659_platform_data *pdata; + struct device_node *endpoint; + int ret; + + if (!IS_ENABLED(CONFIG_OF) || !client->dev.of_node) + return client->dev.platform_data; + + endpoint = of_graph_get_next_endpoint(client->dev.of_node, NULL); + if (!endpoint) + return NULL; + + pdata = devm_kzalloc(&client->dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) + goto done; + + ret = of_property_read_u64(endpoint, "link-frequencies", + &pdata->link_frequency); + if (ret) { + dev_err(&client->dev, "link-frequencies property not found\n"); + pdata = NULL; + } + +done: + of_node_put(endpoint); + return pdata; +} + +static int ov2659_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + const struct ov2659_platform_data *pdata = ov2659_get_pdata(client); + struct v4l2_subdev *sd; + struct ov2659 *ov2659; + struct clk *clk; + int ret; + + if (!pdata) { + dev_err(&client->dev, "platform data not specified\n"); + return -EINVAL; + } + + ov2659 = devm_kzalloc(&client->dev, sizeof(*ov2659), GFP_KERNEL); + if (!ov2659) + return -ENOMEM; + + ov2659->pdata = pdata; + ov2659->client = client; + + clk = devm_clk_get(&client->dev, "xvclk"); + if (IS_ERR(clk)) + return PTR_ERR(clk); + + ov2659->xvclk_frequency = clk_get_rate(clk); + if (ov2659->xvclk_frequency < 6000000 || + ov2659->xvclk_frequency > 27000000) + return -EINVAL; + + v4l2_ctrl_handler_init(&ov2659->ctrls, 2); + ov2659->link_frequency = + v4l2_ctrl_new_std(&ov2659->ctrls, &ov2659_ctrl_ops, + V4L2_CID_PIXEL_RATE, + pdata->link_frequency / 2, + pdata->link_frequency, 1, + pdata->link_frequency); + v4l2_ctrl_new_std_menu_items(&ov2659->ctrls, &ov2659_ctrl_ops, + V4L2_CID_TEST_PATTERN, + ARRAY_SIZE(ov2659_test_pattern_menu) - 1, + 0, 0, ov2659_test_pattern_menu); + ov2659->sd.ctrl_handler = &ov2659->ctrls; + + if (ov2659->ctrls.error) { + dev_err(&client->dev, "%s: control initialization error %d\n", + __func__, ov2659->ctrls.error); + return ov2659->ctrls.error; + } + + sd = &ov2659->sd; + client->flags |= I2C_CLIENT_SCCB; + v4l2_i2c_subdev_init(sd, client, &ov2659_subdev_ops); + + sd->internal_ops = &ov2659_subdev_internal_ops; + sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | + V4L2_SUBDEV_FL_HAS_EVENTS; + +#if defined(CONFIG_MEDIA_CONTROLLER) + ov2659->pad.flags = MEDIA_PAD_FL_SOURCE; + sd->entity.type = MEDIA_ENT_T_V4L2_SUBDEV_SENSOR; + ret = media_entity_init(&sd->entity, 1, &ov2659->pad, 0); + if (ret < 0) { + v4l2_ctrl_handler_free(&ov2659->ctrls); + return ret; + } +#endif + + mutex_init(&ov2659->lock); + + ov2659_get_default_format(&ov2659->format); + ov2659->frame_size = &ov2659_framesizes[2]; + ov2659->format_ctrl_regs = ov2659_formats[0].format_ctrl_regs; + + ret = ov2659_detect(sd); + if (ret < 0) + goto error; + + /* Calculate the PLL register value needed */ + ov2659_pll_calc_params(ov2659); + + ret = v4l2_async_register_subdev(&ov2659->sd); + if (ret) + goto error; + + dev_info(&client->dev, "%s sensor driver registered !!\n", sd->name); + + return 0; + +error: + v4l2_ctrl_handler_free(&ov2659->ctrls); +#if defined(CONFIG_MEDIA_CONTROLLER) + media_entity_cleanup(&sd->entity); +#endif + mutex_destroy(&ov2659->lock); + return ret; +} + +static int ov2659_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct ov2659 *ov2659 = to_ov2659(sd); + + v4l2_ctrl_handler_free(&ov2659->ctrls); + v4l2_async_unregister_subdev(sd); +#if defined(CONFIG_MEDIA_CONTROLLER) + media_entity_cleanup(&sd->entity); +#endif + mutex_destroy(&ov2659->lock); + + return 0; +} + +static const struct i2c_device_id ov2659_id[] = { + { "ov2659", 0 }, + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(i2c, ov2659_id); + +#if IS_ENABLED(CONFIG_OF) +static const struct of_device_id ov2659_of_match[] = { + { .compatible = "ovti,ov2659", }, + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(of, ov2659_of_match); +#endif + +static struct i2c_driver ov2659_i2c_driver = { + .driver = { + .name = DRIVER_NAME, + .of_match_table = of_match_ptr(ov2659_of_match), + }, + .probe = ov2659_probe, + .remove = ov2659_remove, + .id_table = ov2659_id, +}; + +module_i2c_driver(ov2659_i2c_driver); + +MODULE_AUTHOR("Benoit Parrot "); +MODULE_DESCRIPTION("OV2659 CMOS Image Sensor driver"); +MODULE_LICENSE("GPL v2"); diff --git a/include/media/ov2659.h b/include/media/ov2659.h new file mode 100644 index 000000000000..4216adc1ede2 --- /dev/null +++ b/include/media/ov2659.h @@ -0,0 +1,34 @@ +/* + * Omnivision OV2659 CMOS Image Sensor driver + * + * Copyright (C) 2015 Texas Instruments, Inc. + * + * Benoit Parrot + * Lad, Prabhakar + * + * This program is free software; you may redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef OV2659_H +#define OV2659_H + +/** + * struct ov2659_platform_data - ov2659 driver platform data + * @link_frequency: target pixel clock frequency + */ +struct ov2659_platform_data { + s64 link_frequency; +}; + +#endif /* OV2659_H */ -- cgit v1.2.3 From 590ce4bcbfb4e0462a720a4ad901e84416080bba Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Mon, 22 Dec 2014 18:30:08 -0600 Subject: mnt: Add MNT_UMOUNT flag In some instances it is necessary to know if the the unmounting process has begun on a mount. Add MNT_UMOUNT to make that reliably testable. This fix gets used in fixing locked mounts in MNT_DETACH Cc: stable@vger.kernel.org Signed-off-by: "Eric W. Biederman" --- fs/namespace.c | 4 +++- fs/pnode.c | 1 + include/linux/mount.h | 1 + 3 files changed, 5 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/fs/namespace.c b/fs/namespace.c index 54cbef129f4a..d1708147eb45 100644 --- a/fs/namespace.c +++ b/fs/namespace.c @@ -1331,8 +1331,10 @@ static void umount_tree(struct mount *mnt, enum umount_tree_flags how) struct mount *p; /* Gather the mounts to umount */ - for (p = mnt; p; p = next_mnt(p, mnt)) + for (p = mnt; p; p = next_mnt(p, mnt)) { + p->mnt.mnt_flags |= MNT_UMOUNT; list_move(&p->mnt_list, &tmp_list); + } /* Hide the mounts from lookup_mnt and mnt_mounts */ list_for_each_entry(p, &tmp_list, mnt_list) { diff --git a/fs/pnode.c b/fs/pnode.c index bf012af709dd..ac3aa0d43b90 100644 --- a/fs/pnode.c +++ b/fs/pnode.c @@ -384,6 +384,7 @@ static void __propagate_umount(struct mount *mnt) if (child && list_empty(&child->mnt_mounts)) { list_del_init(&child->mnt_child); hlist_del_init_rcu(&child->mnt_hash); + child->mnt.mnt_flags |= MNT_UMOUNT; list_move_tail(&child->mnt_list, &mnt->mnt_list); } } diff --git a/include/linux/mount.h b/include/linux/mount.h index c2c561dc0114..564beeec5d83 100644 --- a/include/linux/mount.h +++ b/include/linux/mount.h @@ -61,6 +61,7 @@ struct mnt_namespace; #define MNT_DOOMED 0x1000000 #define MNT_SYNC_UMOUNT 0x2000000 #define MNT_MARKED 0x4000000 +#define MNT_UMOUNT 0x8000000 struct vfsmount { struct dentry *mnt_root; /* root of the mounted tree */ -- cgit v1.2.3 From 3ae863e0db47ae7815f9e52975e1fddfca59520a Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 9 Mar 2015 13:33:57 -0300 Subject: [media] saa7146: embed video_device Embed the video_device struct to simplify the error handling and in order to (eventually) get rid of video_device_alloc/release. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/common/saa7146/saa7146_fops.c | 19 ++++--------------- drivers/media/pci/saa7146/hexium_gemini.c | 2 +- drivers/media/pci/saa7146/hexium_orion.c | 2 +- drivers/media/pci/saa7146/mxb.c | 4 ++-- drivers/media/pci/ttpci/av7110.h | 4 ++-- drivers/media/pci/ttpci/budget-av.c | 2 +- include/media/saa7146_vv.h | 4 ++-- 7 files changed, 13 insertions(+), 24 deletions(-) (limited to 'include') diff --git a/drivers/media/common/saa7146/saa7146_fops.c b/drivers/media/common/saa7146/saa7146_fops.c index b7d63933dae6..df1e8c975cd8 100644 --- a/drivers/media/common/saa7146/saa7146_fops.c +++ b/drivers/media/common/saa7146/saa7146_fops.c @@ -587,26 +587,20 @@ int saa7146_vv_release(struct saa7146_dev* dev) } EXPORT_SYMBOL_GPL(saa7146_vv_release); -int saa7146_register_device(struct video_device **vid, struct saa7146_dev* dev, +int saa7146_register_device(struct video_device *vfd, struct saa7146_dev *dev, char *name, int type) { - struct video_device *vfd; int err; int i; DEB_EE("dev:%p, name:'%s', type:%d\n", dev, name, type); - // released by vfd->release - vfd = video_device_alloc(); - if (vfd == NULL) - return -ENOMEM; - vfd->fops = &video_fops; if (type == VFL_TYPE_GRABBER) vfd->ioctl_ops = &dev->ext_vv_data->vid_ops; else vfd->ioctl_ops = &dev->ext_vv_data->vbi_ops; - vfd->release = video_device_release; + vfd->release = video_device_release_empty; vfd->lock = &dev->v4l2_lock; vfd->v4l2_dev = &dev->v4l2_dev; vfd->tvnorms = 0; @@ -618,25 +612,20 @@ int saa7146_register_device(struct video_device **vid, struct saa7146_dev* dev, err = video_register_device(vfd, type, -1); if (err < 0) { ERR("cannot register v4l2 device. skipping.\n"); - video_device_release(vfd); return err; } pr_info("%s: registered device %s [v4l2]\n", dev->name, video_device_node_name(vfd)); - - *vid = vfd; return 0; } EXPORT_SYMBOL_GPL(saa7146_register_device); -int saa7146_unregister_device(struct video_device **vid, struct saa7146_dev* dev) +int saa7146_unregister_device(struct video_device *vfd, struct saa7146_dev *dev) { DEB_EE("dev:%p\n", dev); - video_unregister_device(*vid); - *vid = NULL; - + video_unregister_device(vfd); return 0; } EXPORT_SYMBOL_GPL(saa7146_unregister_device); diff --git a/drivers/media/pci/saa7146/hexium_gemini.c b/drivers/media/pci/saa7146/hexium_gemini.c index 366434f5647e..03cbcd2095c6 100644 --- a/drivers/media/pci/saa7146/hexium_gemini.c +++ b/drivers/media/pci/saa7146/hexium_gemini.c @@ -66,7 +66,7 @@ struct hexium { int type; - struct video_device *video_dev; + struct video_device video_dev; struct i2c_adapter i2c_adapter; int cur_input; /* current input */ diff --git a/drivers/media/pci/saa7146/hexium_orion.c b/drivers/media/pci/saa7146/hexium_orion.c index a1eb26d11070..15f0d66ff78a 100644 --- a/drivers/media/pci/saa7146/hexium_orion.c +++ b/drivers/media/pci/saa7146/hexium_orion.c @@ -63,7 +63,7 @@ struct hexium_data struct hexium { int type; - struct video_device *video_dev; + struct video_device video_dev; struct i2c_adapter i2c_adapter; int cur_input; /* current input */ diff --git a/drivers/media/pci/saa7146/mxb.c b/drivers/media/pci/saa7146/mxb.c index c4c8fce8f2b4..0ca1e07ae783 100644 --- a/drivers/media/pci/saa7146/mxb.c +++ b/drivers/media/pci/saa7146/mxb.c @@ -151,8 +151,8 @@ static struct mxb_routing TEA6420_line[MXB_AUDIOS + 1][2] = { struct mxb { - struct video_device *video_dev; - struct video_device *vbi_dev; + struct video_device video_dev; + struct video_device vbi_dev; struct i2c_adapter i2c_adapter; diff --git a/drivers/media/pci/ttpci/av7110.h b/drivers/media/pci/ttpci/av7110.h index ef3d9606b269..835635b0c712 100644 --- a/drivers/media/pci/ttpci/av7110.h +++ b/drivers/media/pci/ttpci/av7110.h @@ -102,8 +102,8 @@ struct av7110 { struct dvb_device dvb_dev; struct dvb_net dvb_net; - struct video_device *v4l_dev; - struct video_device *vbi_dev; + struct video_device v4l_dev; + struct video_device vbi_dev; struct saa7146_dev *dev; diff --git a/drivers/media/pci/ttpci/budget-av.c b/drivers/media/pci/ttpci/budget-av.c index 0ba3875af22e..54c9910256f8 100644 --- a/drivers/media/pci/ttpci/budget-av.c +++ b/drivers/media/pci/ttpci/budget-av.c @@ -68,7 +68,7 @@ DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); struct budget_av { struct budget budget; - struct video_device *vd; + struct video_device vd; int cur_input; int has_saa7113; struct tasklet_struct ciintf_irq_tasklet; diff --git a/include/media/saa7146_vv.h b/include/media/saa7146_vv.h index 944ecdf3530f..92766f77a5de 100644 --- a/include/media/saa7146_vv.h +++ b/include/media/saa7146_vv.h @@ -178,8 +178,8 @@ struct saa7146_use_ops { }; /* from saa7146_fops.c */ -int saa7146_register_device(struct video_device **vid, struct saa7146_dev* dev, char *name, int type); -int saa7146_unregister_device(struct video_device **vid, struct saa7146_dev* dev); +int saa7146_register_device(struct video_device *vid, struct saa7146_dev *dev, char *name, int type); +int saa7146_unregister_device(struct video_device *vid, struct saa7146_dev *dev); void saa7146_buffer_finish(struct saa7146_dev *dev, struct saa7146_dmaqueue *q, int state); void saa7146_buffer_next(struct saa7146_dev *dev, struct saa7146_dmaqueue *q,int vbi); int saa7146_buffer_queue(struct saa7146_dev *dev, struct saa7146_dmaqueue *q, struct saa7146_buf *buf); -- cgit v1.2.3 From 03c278f01d70ce168a24f85a08e11636df30f580 Mon Sep 17 00:00:00 2001 From: "Lad, Prabhakar" Date: Tue, 10 Mar 2015 14:53:05 -0300 Subject: [media] media: davinci: vpfe_capture: embed video_device Embed the video_device struct to simplify the error handling and in order to (eventually) get rid of video_device_alloc/release. Signed-off-by: Lad, Prabhakar Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/davinci/vpfe_capture.c | 26 +++++++------------------- include/media/davinci/vpfe_capture.h | 2 +- 2 files changed, 8 insertions(+), 20 deletions(-) (limited to 'include') diff --git a/drivers/media/platform/davinci/vpfe_capture.c b/drivers/media/platform/davinci/vpfe_capture.c index b41bf7e822c8..ccfcf3f528d3 100644 --- a/drivers/media/platform/davinci/vpfe_capture.c +++ b/drivers/media/platform/davinci/vpfe_capture.c @@ -1871,16 +1871,9 @@ static int vpfe_probe(struct platform_device *pdev) goto probe_free_ccdc_cfg_mem; } - /* Allocate memory for video device */ - vfd = video_device_alloc(); - if (NULL == vfd) { - ret = -ENOMEM; - v4l2_err(pdev->dev.driver, "Unable to alloc video device\n"); - goto probe_out_release_irq; - } - + vfd = &vpfe_dev->video_dev; /* Initialize field of video device */ - vfd->release = video_device_release; + vfd->release = video_device_release_empty; vfd->fops = &vpfe_fops; vfd->ioctl_ops = &vpfe_ioctl_ops; vfd->tvnorms = 0; @@ -1891,14 +1884,12 @@ static int vpfe_probe(struct platform_device *pdev) (VPFE_CAPTURE_VERSION_CODE >> 16) & 0xff, (VPFE_CAPTURE_VERSION_CODE >> 8) & 0xff, (VPFE_CAPTURE_VERSION_CODE) & 0xff); - /* Set video_dev to the video device */ - vpfe_dev->video_dev = vfd; ret = v4l2_device_register(&pdev->dev, &vpfe_dev->v4l2_dev); if (ret) { v4l2_err(pdev->dev.driver, "Unable to register v4l2 device.\n"); - goto probe_out_video_release; + goto probe_out_release_irq; } v4l2_info(&vpfe_dev->v4l2_dev, "v4l2 device registered\n"); spin_lock_init(&vpfe_dev->irqlock); @@ -1914,7 +1905,7 @@ static int vpfe_probe(struct platform_device *pdev) v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "video_dev=%p\n", &vpfe_dev->video_dev); vpfe_dev->fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - ret = video_register_device(vpfe_dev->video_dev, + ret = video_register_device(&vpfe_dev->video_dev, VFL_TYPE_GRABBER, -1); if (ret) { @@ -1927,7 +1918,7 @@ static int vpfe_probe(struct platform_device *pdev) /* set the driver data in platform device */ platform_set_drvdata(pdev, vpfe_dev); /* set driver private data */ - video_set_drvdata(vpfe_dev->video_dev, vpfe_dev); + video_set_drvdata(&vpfe_dev->video_dev, vpfe_dev); i2c_adap = i2c_get_adapter(vpfe_cfg->i2c_adapter_id); num_subdevs = vpfe_cfg->num_subdevs; vpfe_dev->sd = kmalloc(sizeof(struct v4l2_subdev *) * num_subdevs, @@ -1979,12 +1970,9 @@ static int vpfe_probe(struct platform_device *pdev) probe_sd_out: kfree(vpfe_dev->sd); probe_out_video_unregister: - video_unregister_device(vpfe_dev->video_dev); + video_unregister_device(&vpfe_dev->video_dev); probe_out_v4l2_unregister: v4l2_device_unregister(&vpfe_dev->v4l2_dev); -probe_out_video_release: - if (!video_is_registered(vpfe_dev->video_dev)) - video_device_release(vpfe_dev->video_dev); probe_out_release_irq: free_irq(vpfe_dev->ccdc_irq0, vpfe_dev); probe_free_ccdc_cfg_mem: @@ -2007,7 +1995,7 @@ static int vpfe_remove(struct platform_device *pdev) free_irq(vpfe_dev->ccdc_irq0, vpfe_dev); kfree(vpfe_dev->sd); v4l2_device_unregister(&vpfe_dev->v4l2_dev); - video_unregister_device(vpfe_dev->video_dev); + video_unregister_device(&vpfe_dev->video_dev); kfree(vpfe_dev); kfree(ccdc_cfg); return 0; diff --git a/include/media/davinci/vpfe_capture.h b/include/media/davinci/vpfe_capture.h index 288772e6900a..28bcd71cdd26 100644 --- a/include/media/davinci/vpfe_capture.h +++ b/include/media/davinci/vpfe_capture.h @@ -102,7 +102,7 @@ struct vpfe_config { struct vpfe_device { /* V4l2 specific parameters */ /* Identifies video device for this channel */ - struct video_device *video_dev; + struct video_device video_dev; /* sub devices */ struct v4l2_subdev **sd; /* vpfe cfg */ -- cgit v1.2.3 From cc7d2dfb75b3ac0f248801ceed65f69465eb0389 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sun, 15 Mar 2015 14:30:25 -0300 Subject: [media] v4l2_plane_pix_format: use __u32 bytesperline instead of __u16 While running v4l2-compliance tests on vivid I suddenly got errors due to a call to vmalloc_user with size 0 from vb2. Digging deeper into the cause I discovered that this was due to the fact that struct v4l2_plane_pix_format defines bytesperline as a __u16 instead of a __u32. The test I was running selected a format of 4 * 4096 by 4 * 2048 with a 32 bit pixelformat. So bytesperline was 4 * 4 * 4096 = 65536, which becomes 0 in a __u16. And bytesperline * height is suddenly 0 as well. While the vivid driver may be a virtual driver, it is to be expected that this limit will be hit for real hardware as well in the near future: 8k deep-color video will already reach it. The solution is to change the type to __u32. The only drivers besides vivid that use the multiplanar API are little-endian ARM and SH platforms (exynos, ti-vpe, vsp1), so this is safe. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- Documentation/DocBook/media/v4l/pixfmt.xml | 4 ++-- drivers/media/platform/s5p-tv/mixer_video.c | 2 +- include/uapi/linux/videodev2.h | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/Documentation/DocBook/media/v4l/pixfmt.xml b/Documentation/DocBook/media/v4l/pixfmt.xml index 0a1528f3e891..fcde4e20205e 100644 --- a/Documentation/DocBook/media/v4l/pixfmt.xml +++ b/Documentation/DocBook/media/v4l/pixfmt.xml @@ -182,14 +182,14 @@ see . - __u16 + __u32 bytesperline Distance in bytes between the leftmost pixels in two adjacent lines. See &v4l2-pix-format;. __u16 - reserved[7] + reserved[6] Reserved for future extensions. Should be zeroed by the application. diff --git a/drivers/media/platform/s5p-tv/mixer_video.c b/drivers/media/platform/s5p-tv/mixer_video.c index 72d4f2e1efc0..751f3b618337 100644 --- a/drivers/media/platform/s5p-tv/mixer_video.c +++ b/drivers/media/platform/s5p-tv/mixer_video.c @@ -287,7 +287,7 @@ static void mxr_mplane_fill(struct v4l2_plane_pix_format *planes, u32 bl_width = divup(width, blk->width); u32 bl_height = divup(height, blk->height); u32 sizeimage = bl_width * bl_height * blk->size; - u16 bytesperline = bl_width * blk->size / blk->height; + u32 bytesperline = bl_width * blk->size / blk->height; plane->sizeimage += sizeimage; plane->bytesperline = max(plane->bytesperline, bytesperline); diff --git a/include/uapi/linux/videodev2.h b/include/uapi/linux/videodev2.h index 15b21e481246..47df18f36c4b 100644 --- a/include/uapi/linux/videodev2.h +++ b/include/uapi/linux/videodev2.h @@ -1842,8 +1842,8 @@ struct v4l2_mpeg_vbi_fmt_ivtv { */ struct v4l2_plane_pix_format { __u32 sizeimage; - __u16 bytesperline; - __u16 reserved[7]; + __u32 bytesperline; + __u16 reserved[6]; } __attribute__ ((packed)); /** -- cgit v1.2.3 From 5a5394be30dd9725351cdd0f923c1e374dc140cc Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 26 Mar 2014 00:01:44 -0300 Subject: [media] media: entity: Document the media_entity_ops structure Currently, there's no documentation for the structure. Add a kernel-doc nano documentation to it. Signed-off-by: Laurent Pinchart Acked-by: Hans Verkuil Acked-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- include/media/media-entity.h | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'include') diff --git a/include/media/media-entity.h b/include/media/media-entity.h index d6d74bcfe183..0c003d817493 100644 --- a/include/media/media-entity.h +++ b/include/media/media-entity.h @@ -44,6 +44,15 @@ struct media_pad { unsigned long flags; /* Pad flags (MEDIA_PAD_FL_*) */ }; +/** + * struct media_entity_operations - Media entity operations + * @link_setup: Notify the entity of link changes. The operation can + * return an error, in which case link setup will be + * cancelled. Optional. + * @link_validate: Return whether a link is valid from the entity point of + * view. The media_entity_pipeline_start() function + * validates all links by calling this operation. Optional. + */ struct media_entity_operations { int (*link_setup)(struct media_entity *entity, const struct media_pad *local, -- cgit v1.2.3 From 7b0fd4568bee379474eb0d989ef1125064f19fa7 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 15 May 2013 11:34:26 -0300 Subject: [media] v4l: Add RBG and RGB 8:8:8 media bus formats on 24 and 32 bit busses Add support and documentation for two media bus formats: MEDIA_BUS_FMT_RBG888_1X24 and MEDIA_BUS_FMT_RGB888_1X32_PADHI Signed-off-by: Laurent Pinchart Acked-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- Documentation/DocBook/media/v4l/subdev-formats.xml | 67 ++++++++++++++++++++++ include/uapi/linux/media-bus-format.h | 4 +- 2 files changed, 70 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/Documentation/DocBook/media/v4l/subdev-formats.xml b/Documentation/DocBook/media/v4l/subdev-formats.xml index c5ea868e3909..d253e8f85f4d 100644 --- a/Documentation/DocBook/media/v4l/subdev-formats.xml +++ b/Documentation/DocBook/media/v4l/subdev-formats.xml @@ -440,6 +440,36 @@ see . b1 b0 + + MEDIA_BUS_FMT_RBG888_1X24 + 0x100e + + &dash-ent-8; + r7 + r6 + r5 + r4 + r3 + r2 + r1 + r0 + b7 + b6 + b5 + b4 + b3 + b2 + b1 + b0 + g7 + g6 + g5 + g4 + g3 + g2 + g1 + g0 + MEDIA_BUS_FMT_RGB888_1X24 0x100a @@ -579,6 +609,43 @@ see . b1 b0 + + MEDIA_BUS_FMT_RGB888_1X32_PADHI + 0x100f + + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + r7 + r6 + r5 + r4 + r3 + r2 + r1 + r0 + g7 + g6 + g5 + g4 + g3 + g2 + g1 + g0 + b7 + b6 + b5 + b4 + b3 + b2 + b1 + b0 + diff --git a/include/uapi/linux/media-bus-format.h b/include/uapi/linux/media-bus-format.h index 23b40908be30..b585bb32d25e 100644 --- a/include/uapi/linux/media-bus-format.h +++ b/include/uapi/linux/media-bus-format.h @@ -33,7 +33,7 @@ #define MEDIA_BUS_FMT_FIXED 0x0001 -/* RGB - next is 0x100e */ +/* RGB - next is 0x1010 */ #define MEDIA_BUS_FMT_RGB444_2X8_PADHI_BE 0x1001 #define MEDIA_BUS_FMT_RGB444_2X8_PADHI_LE 0x1002 #define MEDIA_BUS_FMT_RGB555_2X8_PADHI_BE 0x1003 @@ -43,10 +43,12 @@ #define MEDIA_BUS_FMT_RGB565_2X8_BE 0x1007 #define MEDIA_BUS_FMT_RGB565_2X8_LE 0x1008 #define MEDIA_BUS_FMT_RGB666_1X18 0x1009 +#define MEDIA_BUS_FMT_RBG888_1X24 0x100e #define MEDIA_BUS_FMT_RGB888_1X24 0x100a #define MEDIA_BUS_FMT_RGB888_2X12_BE 0x100b #define MEDIA_BUS_FMT_RGB888_2X12_LE 0x100c #define MEDIA_BUS_FMT_ARGB8888_1X32 0x100d +#define MEDIA_BUS_FMT_RGB888_1X32_PADHI 0x100f /* YUV (including grey) - next is 0x2024 */ #define MEDIA_BUS_FMT_Y8_1X8 0x2001 -- cgit v1.2.3 From e8b2d7a565ae4fc8627ffe35b953062ff6119533 Mon Sep 17 00:00:00 2001 From: Hyun Kwon Date: Tue, 18 Mar 2014 13:18:14 -0300 Subject: [media] v4l: Sort YUV formats of v4l2_mbus_pixelcode Keep the formats sorted by type, bus_width, bits per component, samples per pixel and order of subsamples, in that order. Signed-off-by: Hyun Kwon Signed-off-by: Laurent Pinchart Acked-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- Documentation/DocBook/media/v4l/subdev-formats.xml | 600 ++++++++++----------- include/uapi/linux/media-bus-format.h | 12 +- 2 files changed, 306 insertions(+), 306 deletions(-) (limited to 'include') diff --git a/Documentation/DocBook/media/v4l/subdev-formats.xml b/Documentation/DocBook/media/v4l/subdev-formats.xml index d253e8f85f4d..9bfd468cd524 100644 --- a/Documentation/DocBook/media/v4l/subdev-formats.xml +++ b/Documentation/DocBook/media/v4l/subdev-formats.xml @@ -2255,11 +2255,15 @@ see . y1 y0 - - MEDIA_BUS_FMT_UYVY8_1X16 - 0x200f + + MEDIA_BUS_FMT_UYVY12_2X12 + 0x201c - &dash-ent-16; + &dash-ent-20; + u11 + u10 + u9 + u8 u7 u6 u5 @@ -2268,6 +2272,16 @@ see . u2 u1 u0 + + + + + + &dash-ent-20; + y11 + y10 + y9 + y8 y7 y6 y5 @@ -2281,7 +2295,11 @@ see . - &dash-ent-16; + &dash-ent-20; + v11 + v10 + v9 + v8 v7 v6 v5 @@ -2290,6 +2308,16 @@ see . v2 v1 v0 + + + + + + &dash-ent-20; + y11 + y10 + y9 + y8 y7 y6 y5 @@ -2299,11 +2327,15 @@ see . y1 y0 - - MEDIA_BUS_FMT_VYUY8_1X16 - 0x2010 + + MEDIA_BUS_FMT_VYUY12_2X12 + 0x201d - &dash-ent-16; + &dash-ent-20; + v11 + v10 + v9 + v8 v7 v6 v5 @@ -2312,6 +2344,16 @@ see . v2 v1 v0 + + + + + + &dash-ent-20; + y11 + y10 + y9 + y8 y7 y6 y5 @@ -2325,7 +2367,11 @@ see . - &dash-ent-16; + &dash-ent-20; + u11 + u10 + u9 + u8 u7 u6 u5 @@ -2334,6 +2380,16 @@ see . u2 u1 u0 + + + + + + &dash-ent-20; + y11 + y10 + y9 + y8 y7 y6 y5 @@ -2343,11 +2399,15 @@ see . y1 y0 - - MEDIA_BUS_FMT_YUYV8_1X16 - 0x2011 + + MEDIA_BUS_FMT_YUYV12_2X12 + 0x201e - &dash-ent-16; + &dash-ent-20; + y11 + y10 + y9 + y8 y7 y6 y5 @@ -2356,6 +2416,16 @@ see . y2 y1 y0 + + + + + + &dash-ent-20; + u11 + u10 + u9 + u8 u7 u6 u5 @@ -2369,7 +2439,11 @@ see . - &dash-ent-16; + &dash-ent-20; + y11 + y10 + y9 + y8 y7 y6 y5 @@ -2378,6 +2452,16 @@ see . y2 y1 y0 + + + + + + &dash-ent-20; + v11 + v10 + v9 + v8 v7 v6 v5 @@ -2387,11 +2471,15 @@ see . v1 v0 - - MEDIA_BUS_FMT_YVYU8_1X16 - 0x2012 + + MEDIA_BUS_FMT_YVYU12_2X12 + 0x201f - &dash-ent-16; + &dash-ent-20; + y11 + y10 + y9 + y8 y7 y6 y5 @@ -2400,6 +2488,16 @@ see . y2 y1 y0 + + + + + + &dash-ent-20; + v11 + v10 + v9 + v8 v7 v6 v5 @@ -2413,29 +2511,11 @@ see . - &dash-ent-16; - y7 - y6 - y5 - y4 - y3 - y2 - y1 - y0 - u7 - u6 - u5 - u4 - u3 - u2 - u1 - u0 - - - MEDIA_BUS_FMT_YDYUYDYV8_1X16 - 0x2014 - - &dash-ent-16; + &dash-ent-20; + y11 + y10 + y9 + y8 y7 y6 y5 @@ -2444,28 +2524,16 @@ see . y2 y1 y0 - d - d - d - d - d - d - d - d - &dash-ent-16; - y7 - y6 - y5 - y4 - y3 - y2 - y1 - y0 + &dash-ent-20; + u11 + u10 + u9 + u8 u7 u6 u5 @@ -2475,57 +2543,11 @@ see . u1 u0 - - - - - &dash-ent-16; - y7 - y6 - y5 - y4 - y3 - y2 - y1 - y0 - d - d - d - d - d - d - d - d - - - - + + MEDIA_BUS_FMT_UYVY8_1X16 + 0x200f &dash-ent-16; - y7 - y6 - y5 - y4 - y3 - y2 - y1 - y0 - v7 - v6 - v5 - v4 - v3 - v2 - v1 - v0 - - - MEDIA_BUS_FMT_UYVY10_1X20 - 0x201a - - &dash-ent-12; - u9 - u8 u7 u6 u5 @@ -2534,8 +2556,6 @@ see . u2 u1 u0 - y9 - y8 y7 y6 y5 @@ -2549,9 +2569,7 @@ see . - &dash-ent-12; - v9 - v8 + &dash-ent-16; v7 v6 v5 @@ -2560,8 +2578,6 @@ see . v2 v1 v0 - y9 - y8 y7 y6 y5 @@ -2571,13 +2587,11 @@ see . y1 y0 - - MEDIA_BUS_FMT_VYUY10_1X20 - 0x201b + + MEDIA_BUS_FMT_VYUY8_1X16 + 0x2010 - &dash-ent-12; - v9 - v8 + &dash-ent-16; v7 v6 v5 @@ -2586,8 +2600,6 @@ see . v2 v1 v0 - y9 - y8 y7 y6 y5 @@ -2601,9 +2613,7 @@ see . - &dash-ent-12; - u9 - u8 + &dash-ent-16; u7 u6 u5 @@ -2612,8 +2622,6 @@ see . u2 u1 u0 - y9 - y8 y7 y6 y5 @@ -2623,13 +2631,11 @@ see . y1 y0 - - MEDIA_BUS_FMT_YUYV10_1X20 - 0x200d + + MEDIA_BUS_FMT_YUYV8_1X16 + 0x2011 - &dash-ent-12; - y9 - y8 + &dash-ent-16; y7 y6 y5 @@ -2638,8 +2644,6 @@ see . y2 y1 y0 - u9 - u8 u7 u6 u5 @@ -2653,9 +2657,7 @@ see . - &dash-ent-12; - y9 - y8 + &dash-ent-16; y7 y6 y5 @@ -2664,8 +2666,6 @@ see . y2 y1 y0 - v9 - v8 v7 v6 v5 @@ -2675,13 +2675,11 @@ see . v1 v0 - - MEDIA_BUS_FMT_YVYU10_1X20 - 0x200e + + MEDIA_BUS_FMT_YVYU8_1X16 + 0x2012 - &dash-ent-12; - y9 - y8 + &dash-ent-16; y7 y6 y5 @@ -2690,8 +2688,6 @@ see . y2 y1 y0 - v9 - v8 v7 v6 v5 @@ -2705,9 +2701,51 @@ see . - &dash-ent-12; - y9 - y8 + &dash-ent-16; + y7 + y6 + y5 + y4 + y3 + y2 + y1 + y0 + u7 + u6 + u5 + u4 + u3 + u2 + u1 + u0 + + + MEDIA_BUS_FMT_YDYUYDYV8_1X16 + 0x2014 + + &dash-ent-16; + y7 + y6 + y5 + y4 + y3 + y2 + y1 + y0 + d + d + d + d + d + d + d + d + + + + + + &dash-ent-16; y7 y6 y5 @@ -2716,8 +2754,6 @@ see . y2 y1 y0 - u9 - u8 u7 u6 u5 @@ -2727,14 +2763,11 @@ see . u1 u0 - - MEDIA_BUS_FMT_YUV10_1X30 - 0x2016 + - - - - - y9 - y8 + + + &dash-ent-16; y7 y6 y5 @@ -2743,39 +2776,20 @@ see . y2 y1 y0 - u9 - u8 - u7 - u6 - u5 - u4 - u3 - u2 - u1 - u0 - v9 - v8 - v7 - v6 - v5 - v4 - v3 - v2 - v1 - v0 + d + d + d + d + d + d + d + d - - MEDIA_BUS_FMT_AYUV8_1X32 - 0x2017 + - a7 - a6 - a5 - a4 - a3 - a2 - a1 - a0 + + + &dash-ent-16; y7 y6 y5 @@ -2784,14 +2798,6 @@ see . y2 y1 y0 - u7 - u6 - u5 - u4 - u3 - u2 - u1 - u0 v7 v6 v5 @@ -2801,13 +2807,11 @@ see . v1 v0 - - MEDIA_BUS_FMT_UYVY12_2X12 - 0x201c + + MEDIA_BUS_FMT_UYVY10_1X20 + 0x201a - &dash-ent-20; - u11 - u10 + &dash-ent-12; u9 u8 u7 @@ -2818,14 +2822,6 @@ see . u2 u1 u0 - - - - - - &dash-ent-20; - y11 - y10 y9 y8 y7 @@ -2841,9 +2837,7 @@ see . - &dash-ent-20; - v11 - v10 + &dash-ent-12; v9 v8 v7 @@ -2854,14 +2848,6 @@ see . v2 v1 v0 - - - - - - &dash-ent-20; - y11 - y10 y9 y8 y7 @@ -2873,13 +2859,11 @@ see . y1 y0 - - MEDIA_BUS_FMT_VYUY12_2X12 - 0x201d + + MEDIA_BUS_FMT_VYUY10_1X20 + 0x201b - &dash-ent-20; - v11 - v10 + &dash-ent-12; v9 v8 v7 @@ -2890,14 +2874,6 @@ see . v2 v1 v0 - - - - - - &dash-ent-20; - y11 - y10 y9 y8 y7 @@ -2913,9 +2889,7 @@ see . - &dash-ent-20; - u11 - u10 + &dash-ent-12; u9 u8 u7 @@ -2926,14 +2900,6 @@ see . u2 u1 u0 - - - - - - &dash-ent-20; - y11 - y10 y9 y8 y7 @@ -2945,13 +2911,11 @@ see . y1 y0 - - MEDIA_BUS_FMT_YUYV12_2X12 - 0x201e + + MEDIA_BUS_FMT_YUYV10_1X20 + 0x200d - &dash-ent-20; - y11 - y10 + &dash-ent-12; y9 y8 y7 @@ -2962,14 +2926,6 @@ see . y2 y1 y0 - - - - - - &dash-ent-20; - u11 - u10 u9 u8 u7 @@ -2985,9 +2941,7 @@ see . - &dash-ent-20; - y11 - y10 + &dash-ent-12; y9 y8 y7 @@ -2998,14 +2952,6 @@ see . y2 y1 y0 - - - - - - &dash-ent-20; - v11 - v10 v9 v8 v7 @@ -3017,13 +2963,11 @@ see . v1 v0 - - MEDIA_BUS_FMT_YVYU12_2X12 - 0x201f + + MEDIA_BUS_FMT_YVYU10_1X20 + 0x200e - &dash-ent-20; - y11 - y10 + &dash-ent-12; y9 y8 y7 @@ -3034,14 +2978,6 @@ see . y2 y1 y0 - - - - - - &dash-ent-20; - v11 - v10 v9 v8 v7 @@ -3057,9 +2993,7 @@ see . - &dash-ent-20; - y11 - y10 + &dash-ent-12; y9 y8 y7 @@ -3070,14 +3004,6 @@ see . y2 y1 y0 - - - - - - &dash-ent-20; - u11 - u10 u9 u8 u7 @@ -3329,6 +3255,80 @@ see . u1 u0 + + MEDIA_BUS_FMT_YUV10_1X30 + 0x2016 + + - + - + y9 + y8 + y7 + y6 + y5 + y4 + y3 + y2 + y1 + y0 + u9 + u8 + u7 + u6 + u5 + u4 + u3 + u2 + u1 + u0 + v9 + v8 + v7 + v6 + v5 + v4 + v3 + v2 + v1 + v0 + + + MEDIA_BUS_FMT_AYUV8_1X32 + 0x2017 + + a7 + a6 + a5 + a4 + a3 + a2 + a1 + a0 + y7 + y6 + y5 + y4 + y3 + y2 + y1 + y0 + u7 + u6 + u5 + u4 + u3 + u2 + u1 + u0 + v7 + v6 + v5 + v4 + v3 + v2 + v1 + v0 + diff --git a/include/uapi/linux/media-bus-format.h b/include/uapi/linux/media-bus-format.h index b585bb32d25e..363a30fd8a21 100644 --- a/include/uapi/linux/media-bus-format.h +++ b/include/uapi/linux/media-bus-format.h @@ -67,6 +67,10 @@ #define MEDIA_BUS_FMT_YUYV10_2X10 0x200b #define MEDIA_BUS_FMT_YVYU10_2X10 0x200c #define MEDIA_BUS_FMT_Y12_1X12 0x2013 +#define MEDIA_BUS_FMT_UYVY12_2X12 0x201c +#define MEDIA_BUS_FMT_VYUY12_2X12 0x201d +#define MEDIA_BUS_FMT_YUYV12_2X12 0x201e +#define MEDIA_BUS_FMT_YVYU12_2X12 0x201f #define MEDIA_BUS_FMT_UYVY8_1X16 0x200f #define MEDIA_BUS_FMT_VYUY8_1X16 0x2010 #define MEDIA_BUS_FMT_YUYV8_1X16 0x2011 @@ -76,16 +80,12 @@ #define MEDIA_BUS_FMT_VYUY10_1X20 0x201b #define MEDIA_BUS_FMT_YUYV10_1X20 0x200d #define MEDIA_BUS_FMT_YVYU10_1X20 0x200e -#define MEDIA_BUS_FMT_YUV10_1X30 0x2016 -#define MEDIA_BUS_FMT_AYUV8_1X32 0x2017 -#define MEDIA_BUS_FMT_UYVY12_2X12 0x201c -#define MEDIA_BUS_FMT_VYUY12_2X12 0x201d -#define MEDIA_BUS_FMT_YUYV12_2X12 0x201e -#define MEDIA_BUS_FMT_YVYU12_2X12 0x201f #define MEDIA_BUS_FMT_UYVY12_1X24 0x2020 #define MEDIA_BUS_FMT_VYUY12_1X24 0x2021 #define MEDIA_BUS_FMT_YUYV12_1X24 0x2022 #define MEDIA_BUS_FMT_YVYU12_1X24 0x2023 +#define MEDIA_BUS_FMT_YUV10_1X30 0x2016 +#define MEDIA_BUS_FMT_AYUV8_1X32 0x2017 /* Bayer - next is 0x3019 */ #define MEDIA_BUS_FMT_SBGGR8_1X8 0x3001 -- cgit v1.2.3 From 2dca0551e4e2f3fc2b9beee784c7071e1e79c14b Mon Sep 17 00:00:00 2001 From: Hyun Kwon Date: Tue, 18 Mar 2014 13:18:15 -0300 Subject: [media] v4l: Add VUY8 24 bits bus format Add VUY8 24 bits bus format, V4L2_MBUS_FMT_VUY8_1X24. Signed-off-by: Hyun Kwon Signed-off-by: Laurent Pinchart Acked-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- Documentation/DocBook/media/v4l/subdev-formats.xml | 30 ++++++++++++++++++++++ include/uapi/linux/media-bus-format.h | 3 ++- 2 files changed, 32 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/Documentation/DocBook/media/v4l/subdev-formats.xml b/Documentation/DocBook/media/v4l/subdev-formats.xml index 9bfd468cd524..bc8d3fb9e4a9 100644 --- a/Documentation/DocBook/media/v4l/subdev-formats.xml +++ b/Documentation/DocBook/media/v4l/subdev-formats.xml @@ -3015,6 +3015,36 @@ see . u1 u0 + + MEDIA_BUS_FMT_VUY8_1X24 + 0x201a + + &dash-ent-8; + v7 + v6 + v5 + v4 + v3 + v2 + v1 + v0 + u7 + u6 + u5 + u4 + u3 + u2 + u1 + u0 + y7 + y6 + y5 + y4 + y3 + y2 + y1 + y0 + MEDIA_BUS_FMT_UYVY12_1X24 0x2020 diff --git a/include/uapi/linux/media-bus-format.h b/include/uapi/linux/media-bus-format.h index 363a30fd8a21..d391893064a0 100644 --- a/include/uapi/linux/media-bus-format.h +++ b/include/uapi/linux/media-bus-format.h @@ -50,7 +50,7 @@ #define MEDIA_BUS_FMT_ARGB8888_1X32 0x100d #define MEDIA_BUS_FMT_RGB888_1X32_PADHI 0x100f -/* YUV (including grey) - next is 0x2024 */ +/* YUV (including grey) - next is 0x2025 */ #define MEDIA_BUS_FMT_Y8_1X8 0x2001 #define MEDIA_BUS_FMT_UV8_1X8 0x2015 #define MEDIA_BUS_FMT_UYVY8_1_5X8 0x2002 @@ -80,6 +80,7 @@ #define MEDIA_BUS_FMT_VYUY10_1X20 0x201b #define MEDIA_BUS_FMT_YUYV10_1X20 0x200d #define MEDIA_BUS_FMT_YVYU10_1X20 0x200e +#define MEDIA_BUS_FMT_VUY8_1X24 0x2024 #define MEDIA_BUS_FMT_UYVY12_1X24 0x2020 #define MEDIA_BUS_FMT_VYUY12_1X24 0x2021 #define MEDIA_BUS_FMT_YUYV12_1X24 0x2022 -- cgit v1.2.3 From c9bca8b33118573da9b7ac2ea21947a8e4d287dd Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Fri, 17 May 2013 07:31:04 -0300 Subject: [media] v4l: of: Add v4l2_of_parse_link() function The function fills a link data structure with the device node and port number at both the local and remote ends of a link defined by one of its endpoint nodes. Signed-off-by: Laurent Pinchart Acked-by: Hans Verkuil Acked-by: Sakari Ailus Acked-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-of.c | 61 +++++++++++++++++++++++++++++++++++++++ include/media/v4l2-of.h | 27 +++++++++++++++++ 2 files changed, 88 insertions(+) (limited to 'include') diff --git a/drivers/media/v4l2-core/v4l2-of.c b/drivers/media/v4l2-core/v4l2-of.c index 58e401f4893a..83143d39dea7 100644 --- a/drivers/media/v4l2-core/v4l2-of.c +++ b/drivers/media/v4l2-core/v4l2-of.c @@ -165,3 +165,64 @@ int v4l2_of_parse_endpoint(const struct device_node *node, return 0; } EXPORT_SYMBOL(v4l2_of_parse_endpoint); + +/** + * v4l2_of_parse_link() - parse a link between two endpoints + * @node: pointer to the endpoint at the local end of the link + * @link: pointer to the V4L2 OF link data structure + * + * Fill the link structure with the local and remote nodes and port numbers. + * The local_node and remote_node fields are set to point to the local and + * remote port's parent nodes respectively (the port parent node being the + * parent node of the port node if that node isn't a 'ports' node, or the + * grand-parent node of the port node otherwise). + * + * A reference is taken to both the local and remote nodes, the caller must use + * v4l2_of_put_link() to drop the references when done with the link. + * + * Return: 0 on success, or -ENOLINK if the remote endpoint can't be found. + */ +int v4l2_of_parse_link(const struct device_node *node, + struct v4l2_of_link *link) +{ + struct device_node *np; + + memset(link, 0, sizeof(*link)); + + np = of_get_parent(node); + of_property_read_u32(np, "reg", &link->local_port); + np = of_get_next_parent(np); + if (of_node_cmp(np->name, "ports") == 0) + np = of_get_next_parent(np); + link->local_node = np; + + np = of_parse_phandle(node, "remote-endpoint", 0); + if (!np) { + of_node_put(link->local_node); + return -ENOLINK; + } + + np = of_get_parent(np); + of_property_read_u32(np, "reg", &link->remote_port); + np = of_get_next_parent(np); + if (of_node_cmp(np->name, "ports") == 0) + np = of_get_next_parent(np); + link->remote_node = np; + + return 0; +} +EXPORT_SYMBOL(v4l2_of_parse_link); + +/** + * v4l2_of_put_link() - drop references to nodes in a link + * @link: pointer to the V4L2 OF link data structure + * + * Drop references to the local and remote nodes in the link. This function must + * be called on every link parsed with v4l2_of_parse_link(). + */ +void v4l2_of_put_link(struct v4l2_of_link *link) +{ + of_node_put(link->local_node); + of_node_put(link->remote_node); +} +EXPORT_SYMBOL(v4l2_of_put_link); diff --git a/include/media/v4l2-of.h b/include/media/v4l2-of.h index 2de42c584eb2..f831c9c225b6 100644 --- a/include/media/v4l2-of.h +++ b/include/media/v4l2-of.h @@ -69,9 +69,26 @@ struct v4l2_of_endpoint { struct list_head head; }; +/** + * struct v4l2_of_link - a link between two endpoints + * @local_node: pointer to device_node of this endpoint + * @local_port: identifier of the port this endpoint belongs to + * @remote_node: pointer to device_node of the remote endpoint + * @remote_port: identifier of the port the remote endpoint belongs to + */ +struct v4l2_of_link { + struct device_node *local_node; + unsigned int local_port; + struct device_node *remote_node; + unsigned int remote_port; +}; + #ifdef CONFIG_OF int v4l2_of_parse_endpoint(const struct device_node *node, struct v4l2_of_endpoint *endpoint); +int v4l2_of_parse_link(const struct device_node *node, + struct v4l2_of_link *link); +void v4l2_of_put_link(struct v4l2_of_link *link); #else /* CONFIG_OF */ static inline int v4l2_of_parse_endpoint(const struct device_node *node, @@ -80,6 +97,16 @@ static inline int v4l2_of_parse_endpoint(const struct device_node *node, return -ENOSYS; } +static inline int v4l2_of_parse_link(const struct device_node *node, + struct v4l2_of_link *link) +{ + return -ENOSYS; +} + +static inline void v4l2_of_put_link(struct v4l2_of_link *link) +{ +} + #endif /* CONFIG_OF */ #endif /* _V4L2_OF_H */ -- cgit v1.2.3 From df3305156f989339529b3d6744b898d498fb1f7b Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 15 May 2013 11:36:19 -0300 Subject: [media] v4l: xilinx: Add Xilinx Video IP core Xilinx platforms have no hardwired video capture or video processing interface. Users create capture and memory to memory processing pipelines in the FPGA fabric to suit their particular needs, by instantiating video IP cores from a large library. The Xilinx Video IP core is a framework that models a video pipeline described in the device tree and expose the pipeline to userspace through the media controller and V4L2 APIs. Signed-off-by: Laurent Pinchart Signed-off-by: Hyun Kwon Signed-off-by: Radhey Shyam Pandey Signed-off-by: Michal Simek Acked-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- .../devicetree/bindings/media/xilinx/video.txt | 35 + .../bindings/media/xilinx/xlnx,video.txt | 55 ++ MAINTAINERS | 9 + drivers/media/platform/Kconfig | 1 + drivers/media/platform/Makefile | 2 + drivers/media/platform/xilinx/Kconfig | 10 + drivers/media/platform/xilinx/Makefile | 3 + drivers/media/platform/xilinx/xilinx-dma.c | 766 +++++++++++++++++++++ drivers/media/platform/xilinx/xilinx-dma.h | 109 +++ drivers/media/platform/xilinx/xilinx-vip.c | 323 +++++++++ drivers/media/platform/xilinx/xilinx-vip.h | 238 +++++++ drivers/media/platform/xilinx/xilinx-vipp.c | 669 ++++++++++++++++++ drivers/media/platform/xilinx/xilinx-vipp.h | 49 ++ include/dt-bindings/media/xilinx-vip.h | 39 ++ 14 files changed, 2308 insertions(+) create mode 100644 Documentation/devicetree/bindings/media/xilinx/video.txt create mode 100644 Documentation/devicetree/bindings/media/xilinx/xlnx,video.txt create mode 100644 drivers/media/platform/xilinx/Kconfig create mode 100644 drivers/media/platform/xilinx/Makefile create mode 100644 drivers/media/platform/xilinx/xilinx-dma.c create mode 100644 drivers/media/platform/xilinx/xilinx-dma.h create mode 100644 drivers/media/platform/xilinx/xilinx-vip.c create mode 100644 drivers/media/platform/xilinx/xilinx-vip.h create mode 100644 drivers/media/platform/xilinx/xilinx-vipp.c create mode 100644 drivers/media/platform/xilinx/xilinx-vipp.h create mode 100644 include/dt-bindings/media/xilinx-vip.h (limited to 'include') diff --git a/Documentation/devicetree/bindings/media/xilinx/video.txt b/Documentation/devicetree/bindings/media/xilinx/video.txt new file mode 100644 index 000000000000..cbd46fa0988f --- /dev/null +++ b/Documentation/devicetree/bindings/media/xilinx/video.txt @@ -0,0 +1,35 @@ +DT bindings for Xilinx video IP cores +------------------------------------- + +Xilinx video IP cores process video streams by acting as video sinks and/or +sources. They are connected by links through their input and output ports, +creating a video pipeline. + +Each video IP core is represented by an AMBA bus child node in the device +tree using bindings documented in this directory. Connections between the IP +cores are represented as defined in ../video-interfaces.txt. + +The whole pipeline is represented by an AMBA bus child node in the device +tree using bindings documented in ./xlnx,video.txt. + +Common properties +----------------- + +The following properties are common to all Xilinx video IP cores. + +- xlnx,video-format: This property represents a video format transmitted on an + AXI bus between video IP cores, using its VF code as defined in "AXI4-Stream + Video IP and System Design Guide" [UG934]. How the format relates to the IP + core is decribed in the IP core bindings documentation. + +- xlnx,video-width: This property qualifies the video format with the sample + width expressed as a number of bits per pixel component. All components must + use the same width. + +- xlnx,cfa-pattern: When the video format is set to Mono/Sensor, this property + describes the sensor's color filter array pattern. Supported values are + "bggr", "gbrg", "grbg", "rggb" and "mono". If not specified, the pattern + defaults to "mono". + + +[UG934] http://www.xilinx.com/support/documentation/ip_documentation/axi_videoip/v1_0/ug934_axi_videoIP.pdf diff --git a/Documentation/devicetree/bindings/media/xilinx/xlnx,video.txt b/Documentation/devicetree/bindings/media/xilinx/xlnx,video.txt new file mode 100644 index 000000000000..5a0227023608 --- /dev/null +++ b/Documentation/devicetree/bindings/media/xilinx/xlnx,video.txt @@ -0,0 +1,55 @@ +Xilinx Video IP Pipeline (VIPP) +------------------------------- + +General concept +--------------- + +Xilinx video IP pipeline processes video streams through one or more Xilinx +video IP cores. Each video IP core is represented as documented in video.txt +and IP core specific documentation, xlnx,v-*.txt, in this directory. The DT +node of the VIPP represents as a top level node of the pipeline and defines +mappings between DMAs and the video IP cores. + +Required properties: + +- compatible: Must be "xlnx,video". + +- dmas, dma-names: List of one DMA specifier and identifier string (as defined + in Documentation/devicetree/bindings/dma/dma.txt) per port. Each port + requires a DMA channel with the identifier string set to "port" followed by + the port index. + +- ports: Video port, using the DT bindings defined in ../video-interfaces.txt. + +Required port properties: + +- direction: should be either "input" or "output" depending on the direction + of stream. + +Example: + + video_cap { + compatible = "xlnx,video"; + dmas = <&vdma_1 1>, <&vdma_3 1>; + dma-names = "port0", "port1"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + direction = "input"; + vcap0_in0: endpoint { + remote-endpoint = <&scaler0_out>; + }; + }; + port@1 { + reg = <1>; + direction = "input"; + vcap0_in1: endpoint { + remote-endpoint = <&switch_out1>; + }; + }; + }; + }; diff --git a/MAINTAINERS b/MAINTAINERS index ec9bf9f54c97..ca3b2638fcc6 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -10818,6 +10818,15 @@ L: linux-serial@vger.kernel.org S: Maintained F: drivers/tty/serial/uartlite.c +XILINX VIDEO IP CORES +M: Hyun Kwon +M: Laurent Pinchart +L: linux-media@vger.kernel.org +T: git git://linuxtv.org/media_tree.git +S: Supported +F: Documentation/devicetree/bindings/media/xilinx/ +F: drivers/media/platform/xilinx/ + XILLYBUS DRIVER M: Eli Billauer L: linux-kernel@vger.kernel.org diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig index 272dc8c40f57..421f53188c6c 100644 --- a/drivers/media/platform/Kconfig +++ b/drivers/media/platform/Kconfig @@ -118,6 +118,7 @@ source "drivers/media/platform/soc_camera/Kconfig" source "drivers/media/platform/exynos4-is/Kconfig" source "drivers/media/platform/s5p-tv/Kconfig" source "drivers/media/platform/am437x/Kconfig" +source "drivers/media/platform/xilinx/Kconfig" endif # V4L_PLATFORM_DRIVERS diff --git a/drivers/media/platform/Makefile b/drivers/media/platform/Makefile index 3ec154742083..8f855616c237 100644 --- a/drivers/media/platform/Makefile +++ b/drivers/media/platform/Makefile @@ -48,4 +48,6 @@ obj-y += omap/ obj-$(CONFIG_VIDEO_AM437X_VPFE) += am437x/ +obj-$(CONFIG_VIDEO_XILINX) += xilinx/ + ccflags-y += -I$(srctree)/drivers/media/i2c diff --git a/drivers/media/platform/xilinx/Kconfig b/drivers/media/platform/xilinx/Kconfig new file mode 100644 index 000000000000..f4347e9af252 --- /dev/null +++ b/drivers/media/platform/xilinx/Kconfig @@ -0,0 +1,10 @@ +config VIDEO_XILINX + tristate "Xilinx Video IP (EXPERIMENTAL)" + depends on VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API && OF + select VIDEOBUF2_DMA_CONTIG + ---help--- + Driver for Xilinx Video IP Pipelines + +if VIDEO_XILINX + +endif #VIDEO_XILINX diff --git a/drivers/media/platform/xilinx/Makefile b/drivers/media/platform/xilinx/Makefile new file mode 100644 index 000000000000..3ef9c8e3cc8d --- /dev/null +++ b/drivers/media/platform/xilinx/Makefile @@ -0,0 +1,3 @@ +xilinx-video-objs += xilinx-dma.o xilinx-vip.o xilinx-vipp.o + +obj-$(CONFIG_VIDEO_XILINX) += xilinx-video.o diff --git a/drivers/media/platform/xilinx/xilinx-dma.c b/drivers/media/platform/xilinx/xilinx-dma.c new file mode 100644 index 000000000000..10209c294168 --- /dev/null +++ b/drivers/media/platform/xilinx/xilinx-dma.c @@ -0,0 +1,766 @@ +/* + * Xilinx Video DMA + * + * Copyright (C) 2013-2015 Ideas on Board + * Copyright (C) 2013-2015 Xilinx, Inc. + * + * Contacts: Hyun Kwon + * Laurent Pinchart + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "xilinx-dma.h" +#include "xilinx-vip.h" +#include "xilinx-vipp.h" + +#define XVIP_DMA_DEF_FORMAT V4L2_PIX_FMT_YUYV +#define XVIP_DMA_DEF_WIDTH 1920 +#define XVIP_DMA_DEF_HEIGHT 1080 + +/* Minimum and maximum widths are expressed in bytes */ +#define XVIP_DMA_MIN_WIDTH 1U +#define XVIP_DMA_MAX_WIDTH 65535U +#define XVIP_DMA_MIN_HEIGHT 1U +#define XVIP_DMA_MAX_HEIGHT 8191U + +/* ----------------------------------------------------------------------------- + * Helper functions + */ + +static struct v4l2_subdev * +xvip_dma_remote_subdev(struct media_pad *local, u32 *pad) +{ + struct media_pad *remote; + + remote = media_entity_remote_pad(local); + if (remote == NULL || + media_entity_type(remote->entity) != MEDIA_ENT_T_V4L2_SUBDEV) + return NULL; + + if (pad) + *pad = remote->index; + + return media_entity_to_v4l2_subdev(remote->entity); +} + +static int xvip_dma_verify_format(struct xvip_dma *dma) +{ + struct v4l2_subdev_format fmt; + struct v4l2_subdev *subdev; + int ret; + + subdev = xvip_dma_remote_subdev(&dma->pad, &fmt.pad); + if (subdev == NULL) + return -EPIPE; + + fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE; + ret = v4l2_subdev_call(subdev, pad, get_fmt, NULL, &fmt); + if (ret < 0) + return ret == -ENOIOCTLCMD ? -EINVAL : ret; + + if (dma->fmtinfo->code != fmt.format.code || + dma->format.height != fmt.format.height || + dma->format.width != fmt.format.width || + dma->format.colorspace != fmt.format.colorspace) + return -EINVAL; + + return 0; +} + +/* ----------------------------------------------------------------------------- + * Pipeline Stream Management + */ + +/** + * xvip_pipeline_start_stop - Start ot stop streaming on a pipeline + * @pipe: The pipeline + * @start: Start (when true) or stop (when false) the pipeline + * + * Walk the entities chain starting at the pipeline output video node and start + * or stop all of them. + * + * Return: 0 if successful, or the return value of the failed video::s_stream + * operation otherwise. + */ +static int xvip_pipeline_start_stop(struct xvip_pipeline *pipe, bool start) +{ + struct xvip_dma *dma = pipe->output; + struct media_entity *entity; + struct media_pad *pad; + struct v4l2_subdev *subdev; + int ret; + + entity = &dma->video.entity; + while (1) { + pad = &entity->pads[0]; + if (!(pad->flags & MEDIA_PAD_FL_SINK)) + break; + + pad = media_entity_remote_pad(pad); + if (pad == NULL || + media_entity_type(pad->entity) != MEDIA_ENT_T_V4L2_SUBDEV) + break; + + entity = pad->entity; + subdev = media_entity_to_v4l2_subdev(entity); + + ret = v4l2_subdev_call(subdev, video, s_stream, start); + if (start && ret < 0 && ret != -ENOIOCTLCMD) + return ret; + } + + return 0; +} + +/** + * xvip_pipeline_set_stream - Enable/disable streaming on a pipeline + * @pipe: The pipeline + * @on: Turn the stream on when true or off when false + * + * The pipeline is shared between all DMA engines connect at its input and + * output. While the stream state of DMA engines can be controlled + * independently, pipelines have a shared stream state that enable or disable + * all entities in the pipeline. For this reason the pipeline uses a streaming + * counter that tracks the number of DMA engines that have requested the stream + * to be enabled. + * + * When called with the @on argument set to true, this function will increment + * the pipeline streaming count. If the streaming count reaches the number of + * DMA engines in the pipeline it will enable all entities that belong to the + * pipeline. + * + * Similarly, when called with the @on argument set to false, this function will + * decrement the pipeline streaming count and disable all entities in the + * pipeline when the streaming count reaches zero. + * + * Return: 0 if successful, or the return value of the failed video::s_stream + * operation otherwise. Stopping the pipeline never fails. The pipeline state is + * not updated when the operation fails. + */ +static int xvip_pipeline_set_stream(struct xvip_pipeline *pipe, bool on) +{ + int ret = 0; + + mutex_lock(&pipe->lock); + + if (on) { + if (pipe->stream_count == pipe->num_dmas - 1) { + ret = xvip_pipeline_start_stop(pipe, true); + if (ret < 0) + goto done; + } + pipe->stream_count++; + } else { + if (--pipe->stream_count == 0) + xvip_pipeline_start_stop(pipe, false); + } + +done: + mutex_unlock(&pipe->lock); + return ret; +} + +static int xvip_pipeline_validate(struct xvip_pipeline *pipe, + struct xvip_dma *start) +{ + struct media_entity_graph graph; + struct media_entity *entity = &start->video.entity; + struct media_device *mdev = entity->parent; + unsigned int num_inputs = 0; + unsigned int num_outputs = 0; + + mutex_lock(&mdev->graph_mutex); + + /* Walk the graph to locate the video nodes. */ + media_entity_graph_walk_start(&graph, entity); + + while ((entity = media_entity_graph_walk_next(&graph))) { + struct xvip_dma *dma; + + if (entity->type != MEDIA_ENT_T_DEVNODE_V4L) + continue; + + dma = to_xvip_dma(media_entity_to_video_device(entity)); + + if (dma->pad.flags & MEDIA_PAD_FL_SINK) { + pipe->output = dma; + num_outputs++; + } else { + num_inputs++; + } + } + + mutex_unlock(&mdev->graph_mutex); + + /* We need exactly one output and zero or one input. */ + if (num_outputs != 1 || num_inputs > 1) + return -EPIPE; + + pipe->num_dmas = num_inputs + num_outputs; + + return 0; +} + +static void __xvip_pipeline_cleanup(struct xvip_pipeline *pipe) +{ + pipe->num_dmas = 0; + pipe->output = NULL; +} + +/** + * xvip_pipeline_cleanup - Cleanup the pipeline after streaming + * @pipe: the pipeline + * + * Decrease the pipeline use count and clean it up if we were the last user. + */ +static void xvip_pipeline_cleanup(struct xvip_pipeline *pipe) +{ + mutex_lock(&pipe->lock); + + /* If we're the last user clean up the pipeline. */ + if (--pipe->use_count == 0) + __xvip_pipeline_cleanup(pipe); + + mutex_unlock(&pipe->lock); +} + +/** + * xvip_pipeline_prepare - Prepare the pipeline for streaming + * @pipe: the pipeline + * @dma: DMA engine at one end of the pipeline + * + * Validate the pipeline if no user exists yet, otherwise just increase the use + * count. + * + * Return: 0 if successful or -EPIPE if the pipeline is not valid. + */ +static int xvip_pipeline_prepare(struct xvip_pipeline *pipe, + struct xvip_dma *dma) +{ + int ret; + + mutex_lock(&pipe->lock); + + /* If we're the first user validate and initialize the pipeline. */ + if (pipe->use_count == 0) { + ret = xvip_pipeline_validate(pipe, dma); + if (ret < 0) { + __xvip_pipeline_cleanup(pipe); + goto done; + } + } + + pipe->use_count++; + ret = 0; + +done: + mutex_unlock(&pipe->lock); + return ret; +} + +/* ----------------------------------------------------------------------------- + * videobuf2 queue operations + */ + +/** + * struct xvip_dma_buffer - Video DMA buffer + * @buf: vb2 buffer base object + * @queue: buffer list entry in the DMA engine queued buffers list + * @dma: DMA channel that uses the buffer + */ +struct xvip_dma_buffer { + struct vb2_buffer buf; + struct list_head queue; + struct xvip_dma *dma; +}; + +#define to_xvip_dma_buffer(vb) container_of(vb, struct xvip_dma_buffer, buf) + +static void xvip_dma_complete(void *param) +{ + struct xvip_dma_buffer *buf = param; + struct xvip_dma *dma = buf->dma; + + spin_lock(&dma->queued_lock); + list_del(&buf->queue); + spin_unlock(&dma->queued_lock); + + buf->buf.v4l2_buf.field = V4L2_FIELD_NONE; + buf->buf.v4l2_buf.sequence = dma->sequence++; + v4l2_get_timestamp(&buf->buf.v4l2_buf.timestamp); + vb2_set_plane_payload(&buf->buf, 0, dma->format.sizeimage); + vb2_buffer_done(&buf->buf, VB2_BUF_STATE_DONE); +} + +static int +xvip_dma_queue_setup(struct vb2_queue *vq, const struct v4l2_format *fmt, + unsigned int *nbuffers, unsigned int *nplanes, + unsigned int sizes[], void *alloc_ctxs[]) +{ + struct xvip_dma *dma = vb2_get_drv_priv(vq); + + /* Make sure the image size is large enough. */ + if (fmt && fmt->fmt.pix.sizeimage < dma->format.sizeimage) + return -EINVAL; + + *nplanes = 1; + + sizes[0] = fmt ? fmt->fmt.pix.sizeimage : dma->format.sizeimage; + alloc_ctxs[0] = dma->alloc_ctx; + + return 0; +} + +static int xvip_dma_buffer_prepare(struct vb2_buffer *vb) +{ + struct xvip_dma *dma = vb2_get_drv_priv(vb->vb2_queue); + struct xvip_dma_buffer *buf = to_xvip_dma_buffer(vb); + + buf->dma = dma; + + return 0; +} + +static void xvip_dma_buffer_queue(struct vb2_buffer *vb) +{ + struct xvip_dma *dma = vb2_get_drv_priv(vb->vb2_queue); + struct xvip_dma_buffer *buf = to_xvip_dma_buffer(vb); + struct dma_async_tx_descriptor *desc; + dma_addr_t addr = vb2_dma_contig_plane_dma_addr(vb, 0); + u32 flags; + + if (dma->queue.type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { + flags = DMA_PREP_INTERRUPT | DMA_CTRL_ACK; + dma->xt.dir = DMA_DEV_TO_MEM; + dma->xt.src_sgl = false; + dma->xt.dst_sgl = true; + dma->xt.dst_start = addr; + } else { + flags = DMA_PREP_INTERRUPT | DMA_CTRL_ACK; + dma->xt.dir = DMA_MEM_TO_DEV; + dma->xt.src_sgl = true; + dma->xt.dst_sgl = false; + dma->xt.src_start = addr; + } + + dma->xt.frame_size = 1; + dma->sgl[0].size = dma->format.width * dma->fmtinfo->bpp; + dma->sgl[0].icg = dma->format.bytesperline - dma->sgl[0].size; + dma->xt.numf = dma->format.height; + + desc = dmaengine_prep_interleaved_dma(dma->dma, &dma->xt, flags); + if (!desc) { + dev_err(dma->xdev->dev, "Failed to prepare DMA transfer\n"); + vb2_buffer_done(&buf->buf, VB2_BUF_STATE_ERROR); + return; + } + desc->callback = xvip_dma_complete; + desc->callback_param = buf; + + spin_lock_irq(&dma->queued_lock); + list_add_tail(&buf->queue, &dma->queued_bufs); + spin_unlock_irq(&dma->queued_lock); + + dmaengine_submit(desc); + + if (vb2_is_streaming(&dma->queue)) + dma_async_issue_pending(dma->dma); +} + +static int xvip_dma_start_streaming(struct vb2_queue *vq, unsigned int count) +{ + struct xvip_dma *dma = vb2_get_drv_priv(vq); + struct xvip_dma_buffer *buf, *nbuf; + struct xvip_pipeline *pipe; + int ret; + + dma->sequence = 0; + + /* + * Start streaming on the pipeline. No link touching an entity in the + * pipeline can be activated or deactivated once streaming is started. + * + * Use the pipeline object embedded in the first DMA object that starts + * streaming. + */ + pipe = dma->video.entity.pipe + ? to_xvip_pipeline(&dma->video.entity) : &dma->pipe; + + ret = media_entity_pipeline_start(&dma->video.entity, &pipe->pipe); + if (ret < 0) + goto error; + + /* Verify that the configured format matches the output of the + * connected subdev. + */ + ret = xvip_dma_verify_format(dma); + if (ret < 0) + goto error_stop; + + ret = xvip_pipeline_prepare(pipe, dma); + if (ret < 0) + goto error_stop; + + /* Start the DMA engine. This must be done before starting the blocks + * in the pipeline to avoid DMA synchronization issues. + */ + dma_async_issue_pending(dma->dma); + + /* Start the pipeline. */ + xvip_pipeline_set_stream(pipe, true); + + return 0; + +error_stop: + media_entity_pipeline_stop(&dma->video.entity); + +error: + /* Give back all queued buffers to videobuf2. */ + spin_lock_irq(&dma->queued_lock); + list_for_each_entry_safe(buf, nbuf, &dma->queued_bufs, queue) { + vb2_buffer_done(&buf->buf, VB2_BUF_STATE_QUEUED); + list_del(&buf->queue); + } + spin_unlock_irq(&dma->queued_lock); + + return ret; +} + +static void xvip_dma_stop_streaming(struct vb2_queue *vq) +{ + struct xvip_dma *dma = vb2_get_drv_priv(vq); + struct xvip_pipeline *pipe = to_xvip_pipeline(&dma->video.entity); + struct xvip_dma_buffer *buf, *nbuf; + + /* Stop the pipeline. */ + xvip_pipeline_set_stream(pipe, false); + + /* Stop and reset the DMA engine. */ + dmaengine_terminate_all(dma->dma); + + /* Cleanup the pipeline and mark it as being stopped. */ + xvip_pipeline_cleanup(pipe); + media_entity_pipeline_stop(&dma->video.entity); + + /* Give back all queued buffers to videobuf2. */ + spin_lock_irq(&dma->queued_lock); + list_for_each_entry_safe(buf, nbuf, &dma->queued_bufs, queue) { + vb2_buffer_done(&buf->buf, VB2_BUF_STATE_ERROR); + list_del(&buf->queue); + } + spin_unlock_irq(&dma->queued_lock); +} + +static struct vb2_ops xvip_dma_queue_qops = { + .queue_setup = xvip_dma_queue_setup, + .buf_prepare = xvip_dma_buffer_prepare, + .buf_queue = xvip_dma_buffer_queue, + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, + .start_streaming = xvip_dma_start_streaming, + .stop_streaming = xvip_dma_stop_streaming, +}; + +/* ----------------------------------------------------------------------------- + * V4L2 ioctls + */ + +static int +xvip_dma_querycap(struct file *file, void *fh, struct v4l2_capability *cap) +{ + struct v4l2_fh *vfh = file->private_data; + struct xvip_dma *dma = to_xvip_dma(vfh->vdev); + + cap->capabilities = V4L2_CAP_DEVICE_CAPS | V4L2_CAP_STREAMING + | dma->xdev->v4l2_caps; + + if (dma->queue.type == V4L2_BUF_TYPE_VIDEO_CAPTURE) + cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING; + else + cap->device_caps = V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_STREAMING; + + strlcpy(cap->driver, "xilinx-vipp", sizeof(cap->driver)); + strlcpy(cap->card, dma->video.name, sizeof(cap->card)); + snprintf(cap->bus_info, sizeof(cap->bus_info), "platform:%s:%u", + dma->xdev->dev->of_node->name, dma->port); + + return 0; +} + +/* FIXME: without this callback function, some applications are not configured + * with correct formats, and it results in frames in wrong format. Whether this + * callback needs to be required is not clearly defined, so it should be + * clarified through the mailing list. + */ +static int +xvip_dma_enum_format(struct file *file, void *fh, struct v4l2_fmtdesc *f) +{ + struct v4l2_fh *vfh = file->private_data; + struct xvip_dma *dma = to_xvip_dma(vfh->vdev); + + if (f->index > 0) + return -EINVAL; + + f->pixelformat = dma->format.pixelformat; + strlcpy(f->description, dma->fmtinfo->description, + sizeof(f->description)); + + return 0; +} + +static int +xvip_dma_get_format(struct file *file, void *fh, struct v4l2_format *format) +{ + struct v4l2_fh *vfh = file->private_data; + struct xvip_dma *dma = to_xvip_dma(vfh->vdev); + + format->fmt.pix = dma->format; + + return 0; +} + +static void +__xvip_dma_try_format(struct xvip_dma *dma, struct v4l2_pix_format *pix, + const struct xvip_video_format **fmtinfo) +{ + const struct xvip_video_format *info; + unsigned int min_width; + unsigned int max_width; + unsigned int min_bpl; + unsigned int max_bpl; + unsigned int width; + unsigned int align; + unsigned int bpl; + + /* Retrieve format information and select the default format if the + * requested format isn't supported. + */ + info = xvip_get_format_by_fourcc(pix->pixelformat); + if (IS_ERR(info)) + info = xvip_get_format_by_fourcc(XVIP_DMA_DEF_FORMAT); + + pix->pixelformat = info->fourcc; + pix->field = V4L2_FIELD_NONE; + + /* The transfer alignment requirements are expressed in bytes. Compute + * the minimum and maximum values, clamp the requested width and convert + * it back to pixels. + */ + align = lcm(dma->align, info->bpp); + min_width = roundup(XVIP_DMA_MIN_WIDTH, align); + max_width = rounddown(XVIP_DMA_MAX_WIDTH, align); + width = rounddown(pix->width * info->bpp, align); + + pix->width = clamp(width, min_width, max_width) / info->bpp; + pix->height = clamp(pix->height, XVIP_DMA_MIN_HEIGHT, + XVIP_DMA_MAX_HEIGHT); + + /* Clamp the requested bytes per line value. If the maximum bytes per + * line value is zero, the module doesn't support user configurable line + * sizes. Override the requested value with the minimum in that case. + */ + min_bpl = pix->width * info->bpp; + max_bpl = rounddown(XVIP_DMA_MAX_WIDTH, dma->align); + bpl = rounddown(pix->bytesperline, dma->align); + + pix->bytesperline = clamp(bpl, min_bpl, max_bpl); + pix->sizeimage = pix->bytesperline * pix->height; + + if (fmtinfo) + *fmtinfo = info; +} + +static int +xvip_dma_try_format(struct file *file, void *fh, struct v4l2_format *format) +{ + struct v4l2_fh *vfh = file->private_data; + struct xvip_dma *dma = to_xvip_dma(vfh->vdev); + + __xvip_dma_try_format(dma, &format->fmt.pix, NULL); + return 0; +} + +static int +xvip_dma_set_format(struct file *file, void *fh, struct v4l2_format *format) +{ + struct v4l2_fh *vfh = file->private_data; + struct xvip_dma *dma = to_xvip_dma(vfh->vdev); + const struct xvip_video_format *info; + + __xvip_dma_try_format(dma, &format->fmt.pix, &info); + + if (vb2_is_busy(&dma->queue)) + return -EBUSY; + + dma->format = format->fmt.pix; + dma->fmtinfo = info; + + return 0; +} + +static const struct v4l2_ioctl_ops xvip_dma_ioctl_ops = { + .vidioc_querycap = xvip_dma_querycap, + .vidioc_enum_fmt_vid_cap = xvip_dma_enum_format, + .vidioc_g_fmt_vid_cap = xvip_dma_get_format, + .vidioc_g_fmt_vid_out = xvip_dma_get_format, + .vidioc_s_fmt_vid_cap = xvip_dma_set_format, + .vidioc_s_fmt_vid_out = xvip_dma_set_format, + .vidioc_try_fmt_vid_cap = xvip_dma_try_format, + .vidioc_try_fmt_vid_out = xvip_dma_try_format, + .vidioc_reqbufs = vb2_ioctl_reqbufs, + .vidioc_querybuf = vb2_ioctl_querybuf, + .vidioc_qbuf = vb2_ioctl_qbuf, + .vidioc_dqbuf = vb2_ioctl_dqbuf, + .vidioc_create_bufs = vb2_ioctl_create_bufs, + .vidioc_expbuf = vb2_ioctl_expbuf, + .vidioc_streamon = vb2_ioctl_streamon, + .vidioc_streamoff = vb2_ioctl_streamoff, +}; + +/* ----------------------------------------------------------------------------- + * V4L2 file operations + */ + +static const struct v4l2_file_operations xvip_dma_fops = { + .owner = THIS_MODULE, + .unlocked_ioctl = video_ioctl2, + .open = v4l2_fh_open, + .release = vb2_fop_release, + .poll = vb2_fop_poll, + .mmap = vb2_fop_mmap, +}; + +/* ----------------------------------------------------------------------------- + * Xilinx Video DMA Core + */ + +int xvip_dma_init(struct xvip_composite_device *xdev, struct xvip_dma *dma, + enum v4l2_buf_type type, unsigned int port) +{ + char name[14]; + int ret; + + dma->xdev = xdev; + dma->port = port; + mutex_init(&dma->lock); + mutex_init(&dma->pipe.lock); + INIT_LIST_HEAD(&dma->queued_bufs); + spin_lock_init(&dma->queued_lock); + + dma->fmtinfo = xvip_get_format_by_fourcc(XVIP_DMA_DEF_FORMAT); + dma->format.pixelformat = dma->fmtinfo->fourcc; + dma->format.colorspace = V4L2_COLORSPACE_SRGB; + dma->format.field = V4L2_FIELD_NONE; + dma->format.width = XVIP_DMA_DEF_WIDTH; + dma->format.height = XVIP_DMA_DEF_HEIGHT; + dma->format.bytesperline = dma->format.width * dma->fmtinfo->bpp; + dma->format.sizeimage = dma->format.bytesperline * dma->format.height; + + /* Initialize the media entity... */ + dma->pad.flags = type == V4L2_BUF_TYPE_VIDEO_CAPTURE + ? MEDIA_PAD_FL_SINK : MEDIA_PAD_FL_SOURCE; + + ret = media_entity_init(&dma->video.entity, 1, &dma->pad, 0); + if (ret < 0) + goto error; + + /* ... and the video node... */ + dma->video.fops = &xvip_dma_fops; + dma->video.v4l2_dev = &xdev->v4l2_dev; + dma->video.queue = &dma->queue; + snprintf(dma->video.name, sizeof(dma->video.name), "%s %s %u", + xdev->dev->of_node->name, + type == V4L2_BUF_TYPE_VIDEO_CAPTURE ? "output" : "input", + port); + dma->video.vfl_type = VFL_TYPE_GRABBER; + dma->video.vfl_dir = type == V4L2_BUF_TYPE_VIDEO_CAPTURE + ? VFL_DIR_RX : VFL_DIR_TX; + dma->video.release = video_device_release_empty; + dma->video.ioctl_ops = &xvip_dma_ioctl_ops; + dma->video.lock = &dma->lock; + + video_set_drvdata(&dma->video, dma); + + /* ... and the buffers queue... */ + dma->alloc_ctx = vb2_dma_contig_init_ctx(dma->xdev->dev); + if (IS_ERR(dma->alloc_ctx)) + goto error; + + /* Don't enable VB2_READ and VB2_WRITE, as using the read() and write() + * V4L2 APIs would be inefficient. Testing on the command line with a + * 'cat /dev/video?' thus won't be possible, but given that the driver + * anyway requires a test tool to setup the pipeline before any video + * stream can be started, requiring a specific V4L2 test tool as well + * instead of 'cat' isn't really a drawback. + */ + dma->queue.type = type; + dma->queue.io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF; + dma->queue.lock = &dma->lock; + dma->queue.drv_priv = dma; + dma->queue.buf_struct_size = sizeof(struct xvip_dma_buffer); + dma->queue.ops = &xvip_dma_queue_qops; + dma->queue.mem_ops = &vb2_dma_contig_memops; + dma->queue.timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC + | V4L2_BUF_FLAG_TSTAMP_SRC_EOF; + ret = vb2_queue_init(&dma->queue); + if (ret < 0) { + dev_err(dma->xdev->dev, "failed to initialize VB2 queue\n"); + goto error; + } + + /* ... and the DMA channel. */ + sprintf(name, "port%u", port); + dma->dma = dma_request_slave_channel(dma->xdev->dev, name); + if (dma->dma == NULL) { + dev_err(dma->xdev->dev, "no VDMA channel found\n"); + ret = -ENODEV; + goto error; + } + + dma->align = 1 << dma->dma->device->copy_align; + + ret = video_register_device(&dma->video, VFL_TYPE_GRABBER, -1); + if (ret < 0) { + dev_err(dma->xdev->dev, "failed to register video device\n"); + goto error; + } + + return 0; + +error: + xvip_dma_cleanup(dma); + return ret; +} + +void xvip_dma_cleanup(struct xvip_dma *dma) +{ + if (video_is_registered(&dma->video)) + video_unregister_device(&dma->video); + + if (dma->dma) + dma_release_channel(dma->dma); + + if (!IS_ERR_OR_NULL(dma->alloc_ctx)) + vb2_dma_contig_cleanup_ctx(dma->alloc_ctx); + + media_entity_cleanup(&dma->video.entity); + + mutex_destroy(&dma->lock); + mutex_destroy(&dma->pipe.lock); +} diff --git a/drivers/media/platform/xilinx/xilinx-dma.h b/drivers/media/platform/xilinx/xilinx-dma.h new file mode 100644 index 000000000000..a540111f8d3d --- /dev/null +++ b/drivers/media/platform/xilinx/xilinx-dma.h @@ -0,0 +1,109 @@ +/* + * Xilinx Video DMA + * + * Copyright (C) 2013-2015 Ideas on Board + * Copyright (C) 2013-2015 Xilinx, Inc. + * + * Contacts: Hyun Kwon + * Laurent Pinchart + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef __XILINX_VIP_DMA_H__ +#define __XILINX_VIP_DMA_H__ + +#include +#include +#include +#include + +#include +#include +#include + +struct dma_chan; +struct xvip_composite_device; +struct xvip_video_format; + +/** + * struct xvip_pipeline - Xilinx Video IP pipeline structure + * @pipe: media pipeline + * @lock: protects the pipeline @stream_count + * @use_count: number of DMA engines using the pipeline + * @stream_count: number of DMA engines currently streaming + * @num_dmas: number of DMA engines in the pipeline + * @output: DMA engine at the output of the pipeline + */ +struct xvip_pipeline { + struct media_pipeline pipe; + + struct mutex lock; + unsigned int use_count; + unsigned int stream_count; + + unsigned int num_dmas; + struct xvip_dma *output; +}; + +static inline struct xvip_pipeline *to_xvip_pipeline(struct media_entity *e) +{ + return container_of(e->pipe, struct xvip_pipeline, pipe); +} + +/** + * struct xvip_dma - Video DMA channel + * @list: list entry in a composite device dmas list + * @video: V4L2 video device associated with the DMA channel + * @pad: media pad for the video device entity + * @xdev: composite device the DMA channel belongs to + * @pipe: pipeline belonging to the DMA channel + * @port: composite device DT node port number for the DMA channel + * @lock: protects the @format, @fmtinfo and @queue fields + * @format: active V4L2 pixel format + * @fmtinfo: format information corresponding to the active @format + * @queue: vb2 buffers queue + * @alloc_ctx: allocation context for the vb2 @queue + * @sequence: V4L2 buffers sequence number + * @queued_bufs: list of queued buffers + * @queued_lock: protects the buf_queued list + * @dma: DMA engine channel + * @align: transfer alignment required by the DMA channel (in bytes) + * @xt: dma interleaved template for dma configuration + * @sgl: data chunk structure for dma_interleaved_template + */ +struct xvip_dma { + struct list_head list; + struct video_device video; + struct media_pad pad; + + struct xvip_composite_device *xdev; + struct xvip_pipeline pipe; + unsigned int port; + + struct mutex lock; + struct v4l2_pix_format format; + const struct xvip_video_format *fmtinfo; + + struct vb2_queue queue; + void *alloc_ctx; + unsigned int sequence; + + struct list_head queued_bufs; + spinlock_t queued_lock; + + struct dma_chan *dma; + unsigned int align; + struct dma_interleaved_template xt; + struct data_chunk sgl[1]; +}; + +#define to_xvip_dma(vdev) container_of(vdev, struct xvip_dma, video) + +int xvip_dma_init(struct xvip_composite_device *xdev, struct xvip_dma *dma, + enum v4l2_buf_type type, unsigned int port); +void xvip_dma_cleanup(struct xvip_dma *dma); + +#endif /* __XILINX_VIP_DMA_H__ */ diff --git a/drivers/media/platform/xilinx/xilinx-vip.c b/drivers/media/platform/xilinx/xilinx-vip.c new file mode 100644 index 000000000000..311259129504 --- /dev/null +++ b/drivers/media/platform/xilinx/xilinx-vip.c @@ -0,0 +1,323 @@ +/* + * Xilinx Video IP Core + * + * Copyright (C) 2013-2015 Ideas on Board + * Copyright (C) 2013-2015 Xilinx, Inc. + * + * Contacts: Hyun Kwon + * Laurent Pinchart + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include + +#include + +#include "xilinx-vip.h" + +/* ----------------------------------------------------------------------------- + * Helper functions + */ + +static const struct xvip_video_format xvip_video_formats[] = { + { XVIP_VF_YUV_422, 8, NULL, MEDIA_BUS_FMT_UYVY8_1X16, + 2, V4L2_PIX_FMT_YUYV, "4:2:2, packed, YUYV" }, + { XVIP_VF_YUV_444, 8, NULL, MEDIA_BUS_FMT_VUY8_1X24, + 3, V4L2_PIX_FMT_YUV444, "4:4:4, packed, YUYV" }, + { XVIP_VF_RBG, 8, NULL, MEDIA_BUS_FMT_RBG888_1X24, + 3, 0, NULL }, + { XVIP_VF_MONO_SENSOR, 8, "mono", MEDIA_BUS_FMT_Y8_1X8, + 1, V4L2_PIX_FMT_GREY, "Greyscale 8-bit" }, + { XVIP_VF_MONO_SENSOR, 8, "rggb", MEDIA_BUS_FMT_SRGGB8_1X8, + 1, V4L2_PIX_FMT_SGRBG8, "Bayer 8-bit RGGB" }, + { XVIP_VF_MONO_SENSOR, 8, "grbg", MEDIA_BUS_FMT_SGRBG8_1X8, + 1, V4L2_PIX_FMT_SGRBG8, "Bayer 8-bit GRBG" }, + { XVIP_VF_MONO_SENSOR, 8, "gbrg", MEDIA_BUS_FMT_SGBRG8_1X8, + 1, V4L2_PIX_FMT_SGBRG8, "Bayer 8-bit GBRG" }, + { XVIP_VF_MONO_SENSOR, 8, "bggr", MEDIA_BUS_FMT_SBGGR8_1X8, + 1, V4L2_PIX_FMT_SBGGR8, "Bayer 8-bit BGGR" }, +}; + +/** + * xvip_get_format_by_code - Retrieve format information for a media bus code + * @code: the format media bus code + * + * Return: a pointer to the format information structure corresponding to the + * given V4L2 media bus format @code, or ERR_PTR if no corresponding format can + * be found. + */ +const struct xvip_video_format *xvip_get_format_by_code(unsigned int code) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(xvip_video_formats); ++i) { + const struct xvip_video_format *format = &xvip_video_formats[i]; + + if (format->code == code) + return format; + } + + return ERR_PTR(-EINVAL); +} +EXPORT_SYMBOL_GPL(xvip_get_format_by_code); + +/** + * xvip_get_format_by_fourcc - Retrieve format information for a 4CC + * @fourcc: the format 4CC + * + * Return: a pointer to the format information structure corresponding to the + * given V4L2 format @fourcc, or ERR_PTR if no corresponding format can be + * found. + */ +const struct xvip_video_format *xvip_get_format_by_fourcc(u32 fourcc) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(xvip_video_formats); ++i) { + const struct xvip_video_format *format = &xvip_video_formats[i]; + + if (format->fourcc == fourcc) + return format; + } + + return ERR_PTR(-EINVAL); +} +EXPORT_SYMBOL_GPL(xvip_get_format_by_fourcc); + +/** + * xvip_of_get_format - Parse a device tree node and return format information + * @node: the device tree node + * + * Read the xlnx,video-format, xlnx,video-width and xlnx,cfa-pattern properties + * from the device tree @node passed as an argument and return the corresponding + * format information. + * + * Return: a pointer to the format information structure corresponding to the + * format name and width, or ERR_PTR if no corresponding format can be found. + */ +const struct xvip_video_format *xvip_of_get_format(struct device_node *node) +{ + const char *pattern = "mono"; + unsigned int vf_code; + unsigned int i; + u32 width; + int ret; + + ret = of_property_read_u32(node, "xlnx,video-format", &vf_code); + if (ret < 0) + return ERR_PTR(ret); + + ret = of_property_read_u32(node, "xlnx,video-width", &width); + if (ret < 0) + return ERR_PTR(ret); + + if (vf_code == XVIP_VF_MONO_SENSOR) + of_property_read_string(node, "xlnx,cfa-pattern", &pattern); + + for (i = 0; i < ARRAY_SIZE(xvip_video_formats); ++i) { + const struct xvip_video_format *format = &xvip_video_formats[i]; + + if (format->vf_code != vf_code || format->width != width) + continue; + + if (vf_code == XVIP_VF_MONO_SENSOR && + strcmp(pattern, format->pattern)) + continue; + + return format; + } + + return ERR_PTR(-EINVAL); +} +EXPORT_SYMBOL_GPL(xvip_of_get_format); + +/** + * xvip_set_format_size - Set the media bus frame format size + * @format: V4L2 frame format on media bus + * @fmt: media bus format + * + * Set the media bus frame format size. The width / height from the subdevice + * format are set to the given media bus format. The new format size is stored + * in @format. The width and height are clamped using default min / max values. + */ +void xvip_set_format_size(struct v4l2_mbus_framefmt *format, + const struct v4l2_subdev_format *fmt) +{ + format->width = clamp_t(unsigned int, fmt->format.width, + XVIP_MIN_WIDTH, XVIP_MAX_WIDTH); + format->height = clamp_t(unsigned int, fmt->format.height, + XVIP_MIN_HEIGHT, XVIP_MAX_HEIGHT); +} +EXPORT_SYMBOL_GPL(xvip_set_format_size); + +/** + * xvip_clr_or_set - Clear or set the register with a bitmask + * @xvip: Xilinx Video IP device + * @addr: address of register + * @mask: bitmask to be set or cleared + * @set: boolean flag indicating whether to set or clear + * + * Clear or set the register at address @addr with a bitmask @mask depending on + * the boolean flag @set. When the flag @set is true, the bitmask is set in + * the register, otherwise the bitmask is cleared from the register + * when the flag @set is false. + * + * Fox eample, this function can be used to set a control with a boolean value + * requested by users. If the caller knows whether to set or clear in the first + * place, the caller should call xvip_clr() or xvip_set() directly instead of + * using this function. + */ +void xvip_clr_or_set(struct xvip_device *xvip, u32 addr, u32 mask, bool set) +{ + u32 reg; + + reg = xvip_read(xvip, addr); + reg = set ? reg | mask : reg & ~mask; + xvip_write(xvip, addr, reg); +} +EXPORT_SYMBOL_GPL(xvip_clr_or_set); + +/** + * xvip_clr_and_set - Clear and set the register with a bitmask + * @xvip: Xilinx Video IP device + * @addr: address of register + * @clr: bitmask to be cleared + * @set: bitmask to be set + * + * Clear a bit(s) of mask @clr in the register at address @addr, then set + * a bit(s) of mask @set in the register after. + */ +void xvip_clr_and_set(struct xvip_device *xvip, u32 addr, u32 clr, u32 set) +{ + u32 reg; + + reg = xvip_read(xvip, addr); + reg &= ~clr; + reg |= set; + xvip_write(xvip, addr, reg); +} +EXPORT_SYMBOL_GPL(xvip_clr_and_set); + +int xvip_init_resources(struct xvip_device *xvip) +{ + struct platform_device *pdev = to_platform_device(xvip->dev); + struct resource *res; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + xvip->iomem = devm_ioremap_resource(xvip->dev, res); + if (IS_ERR(xvip->iomem)) + return PTR_ERR(xvip->iomem); + + xvip->clk = devm_clk_get(xvip->dev, NULL); + if (IS_ERR(xvip->clk)) + return PTR_ERR(xvip->clk); + + clk_prepare_enable(xvip->clk); + return 0; +} +EXPORT_SYMBOL_GPL(xvip_init_resources); + +void xvip_cleanup_resources(struct xvip_device *xvip) +{ + clk_disable_unprepare(xvip->clk); +} +EXPORT_SYMBOL_GPL(xvip_cleanup_resources); + +/* ----------------------------------------------------------------------------- + * Subdev operations handlers + */ + +/** + * xvip_enum_mbus_code - Enumerate the media format code + * @subdev: V4L2 subdevice + * @cfg: V4L2 subdev pad configuration + * @code: returning media bus code + * + * Enumerate the media bus code of the subdevice. Return the corresponding + * pad format code. This function only works for subdevices with fixed format + * on all pads. Subdevices with multiple format should have their own + * function to enumerate mbus codes. + * + * Return: 0 if the media bus code is found, or -EINVAL if the format index + * is not valid. + */ +int xvip_enum_mbus_code(struct v4l2_subdev *subdev, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_mbus_code_enum *code) +{ + struct v4l2_mbus_framefmt *format; + + /* Enumerating frame sizes based on the active configuration isn't + * supported yet. + */ + if (code->which == V4L2_SUBDEV_FORMAT_ACTIVE) + return -EINVAL; + + if (code->index) + return -EINVAL; + + format = v4l2_subdev_get_try_format(subdev, cfg, code->pad); + + code->code = format->code; + + return 0; +} +EXPORT_SYMBOL_GPL(xvip_enum_mbus_code); + +/** + * xvip_enum_frame_size - Enumerate the media bus frame size + * @subdev: V4L2 subdevice + * @cfg: V4L2 subdev pad configuration + * @fse: returning media bus frame size + * + * This function is a drop-in implementation of the subdev enum_frame_size pad + * operation. It assumes that the subdevice has one sink pad and one source + * pad, and that the format on the source pad is always identical to the + * format on the sink pad. Entities with different requirements need to + * implement their own enum_frame_size handlers. + * + * Return: 0 if the media bus frame size is found, or -EINVAL + * if the index or the code is not valid. + */ +int xvip_enum_frame_size(struct v4l2_subdev *subdev, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_frame_size_enum *fse) +{ + struct v4l2_mbus_framefmt *format; + + /* Enumerating frame sizes based on the active configuration isn't + * supported yet. + */ + if (fse->which == V4L2_SUBDEV_FORMAT_ACTIVE) + return -EINVAL; + + format = v4l2_subdev_get_try_format(subdev, cfg, fse->pad); + + if (fse->index || fse->code != format->code) + return -EINVAL; + + if (fse->pad == XVIP_PAD_SINK) { + fse->min_width = XVIP_MIN_WIDTH; + fse->max_width = XVIP_MAX_WIDTH; + fse->min_height = XVIP_MIN_HEIGHT; + fse->max_height = XVIP_MAX_HEIGHT; + } else { + /* The size on the source pad is fixed and always identical to + * the size on the sink pad. + */ + fse->min_width = format->width; + fse->max_width = format->width; + fse->min_height = format->height; + fse->max_height = format->height; + } + + return 0; +} +EXPORT_SYMBOL_GPL(xvip_enum_frame_size); diff --git a/drivers/media/platform/xilinx/xilinx-vip.h b/drivers/media/platform/xilinx/xilinx-vip.h new file mode 100644 index 000000000000..42fee2026815 --- /dev/null +++ b/drivers/media/platform/xilinx/xilinx-vip.h @@ -0,0 +1,238 @@ +/* + * Xilinx Video IP Core + * + * Copyright (C) 2013-2015 Ideas on Board + * Copyright (C) 2013-2015 Xilinx, Inc. + * + * Contacts: Hyun Kwon + * Laurent Pinchart + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef __XILINX_VIP_H__ +#define __XILINX_VIP_H__ + +#include +#include + +struct clk; + +/* + * Minimum and maximum width and height common to most video IP cores. IP + * cores with different requirements must define their own values. + */ +#define XVIP_MIN_WIDTH 32 +#define XVIP_MAX_WIDTH 7680 +#define XVIP_MIN_HEIGHT 32 +#define XVIP_MAX_HEIGHT 7680 + +/* + * Pad IDs. IP cores with with multiple inputs or outputs should define + * their own values. + */ +#define XVIP_PAD_SINK 0 +#define XVIP_PAD_SOURCE 1 + +/* Xilinx Video IP Control Registers */ +#define XVIP_CTRL_CONTROL 0x0000 +#define XVIP_CTRL_CONTROL_SW_ENABLE (1 << 0) +#define XVIP_CTRL_CONTROL_REG_UPDATE (1 << 1) +#define XVIP_CTRL_CONTROL_BYPASS (1 << 4) +#define XVIP_CTRL_CONTROL_TEST_PATTERN (1 << 5) +#define XVIP_CTRL_CONTROL_FRAME_SYNC_RESET (1 << 30) +#define XVIP_CTRL_CONTROL_SW_RESET (1 << 31) +#define XVIP_CTRL_STATUS 0x0004 +#define XVIP_CTRL_STATUS_PROC_STARTED (1 << 0) +#define XVIP_CTRL_STATUS_EOF (1 << 1) +#define XVIP_CTRL_ERROR 0x0008 +#define XVIP_CTRL_ERROR_SLAVE_EOL_EARLY (1 << 0) +#define XVIP_CTRL_ERROR_SLAVE_EOL_LATE (1 << 1) +#define XVIP_CTRL_ERROR_SLAVE_SOF_EARLY (1 << 2) +#define XVIP_CTRL_ERROR_SLAVE_SOF_LATE (1 << 3) +#define XVIP_CTRL_IRQ_ENABLE 0x000c +#define XVIP_CTRL_IRQ_ENABLE_PROC_STARTED (1 << 0) +#define XVIP_CTRL_IRQ_EOF (1 << 1) +#define XVIP_CTRL_VERSION 0x0010 +#define XVIP_CTRL_VERSION_MAJOR_MASK (0xff << 24) +#define XVIP_CTRL_VERSION_MAJOR_SHIFT 24 +#define XVIP_CTRL_VERSION_MINOR_MASK (0xff << 16) +#define XVIP_CTRL_VERSION_MINOR_SHIFT 16 +#define XVIP_CTRL_VERSION_REVISION_MASK (0xf << 12) +#define XVIP_CTRL_VERSION_REVISION_SHIFT 12 +#define XVIP_CTRL_VERSION_PATCH_MASK (0xf << 8) +#define XVIP_CTRL_VERSION_PATCH_SHIFT 8 +#define XVIP_CTRL_VERSION_INTERNAL_MASK (0xff << 0) +#define XVIP_CTRL_VERSION_INTERNAL_SHIFT 0 + +/* Xilinx Video IP Timing Registers */ +#define XVIP_ACTIVE_SIZE 0x0020 +#define XVIP_ACTIVE_VSIZE_MASK (0x7ff << 16) +#define XVIP_ACTIVE_VSIZE_SHIFT 16 +#define XVIP_ACTIVE_HSIZE_MASK (0x7ff << 0) +#define XVIP_ACTIVE_HSIZE_SHIFT 0 +#define XVIP_ENCODING 0x0028 +#define XVIP_ENCODING_NBITS_8 (0 << 4) +#define XVIP_ENCODING_NBITS_10 (1 << 4) +#define XVIP_ENCODING_NBITS_12 (2 << 4) +#define XVIP_ENCODING_NBITS_16 (3 << 4) +#define XVIP_ENCODING_NBITS_MASK (3 << 4) +#define XVIP_ENCODING_NBITS_SHIFT 4 +#define XVIP_ENCODING_VIDEO_FORMAT_YUV422 (0 << 0) +#define XVIP_ENCODING_VIDEO_FORMAT_YUV444 (1 << 0) +#define XVIP_ENCODING_VIDEO_FORMAT_RGB (2 << 0) +#define XVIP_ENCODING_VIDEO_FORMAT_YUV420 (3 << 0) +#define XVIP_ENCODING_VIDEO_FORMAT_MASK (3 << 0) +#define XVIP_ENCODING_VIDEO_FORMAT_SHIFT 0 + +/** + * struct xvip_device - Xilinx Video IP device structure + * @subdev: V4L2 subdevice + * @dev: (OF) device + * @iomem: device I/O register space remapped to kernel virtual memory + * @clk: video core clock + * @saved_ctrl: saved control register for resume / suspend + */ +struct xvip_device { + struct v4l2_subdev subdev; + struct device *dev; + void __iomem *iomem; + struct clk *clk; + u32 saved_ctrl; +}; + +/** + * struct xvip_video_format - Xilinx Video IP video format description + * @vf_code: AXI4 video format code + * @width: AXI4 format width in bits per component + * @pattern: CFA pattern for Mono/Sensor formats + * @code: media bus format code + * @bpp: bytes per pixel (when stored in memory) + * @fourcc: V4L2 pixel format FCC identifier + * @description: format description, suitable for userspace + */ +struct xvip_video_format { + unsigned int vf_code; + unsigned int width; + const char *pattern; + unsigned int code; + unsigned int bpp; + u32 fourcc; + const char *description; +}; + +const struct xvip_video_format *xvip_get_format_by_code(unsigned int code); +const struct xvip_video_format *xvip_get_format_by_fourcc(u32 fourcc); +const struct xvip_video_format *xvip_of_get_format(struct device_node *node); +void xvip_set_format_size(struct v4l2_mbus_framefmt *format, + const struct v4l2_subdev_format *fmt); +int xvip_enum_mbus_code(struct v4l2_subdev *subdev, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_mbus_code_enum *code); +int xvip_enum_frame_size(struct v4l2_subdev *subdev, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_frame_size_enum *fse); + +static inline u32 xvip_read(struct xvip_device *xvip, u32 addr) +{ + return ioread32(xvip->iomem + addr); +} + +static inline void xvip_write(struct xvip_device *xvip, u32 addr, u32 value) +{ + iowrite32(value, xvip->iomem + addr); +} + +static inline void xvip_clr(struct xvip_device *xvip, u32 addr, u32 clr) +{ + xvip_write(xvip, addr, xvip_read(xvip, addr) & ~clr); +} + +static inline void xvip_set(struct xvip_device *xvip, u32 addr, u32 set) +{ + xvip_write(xvip, addr, xvip_read(xvip, addr) | set); +} + +void xvip_clr_or_set(struct xvip_device *xvip, u32 addr, u32 mask, bool set); +void xvip_clr_and_set(struct xvip_device *xvip, u32 addr, u32 clr, u32 set); + +int xvip_init_resources(struct xvip_device *xvip); +void xvip_cleanup_resources(struct xvip_device *xvip); + +static inline void xvip_reset(struct xvip_device *xvip) +{ + xvip_write(xvip, XVIP_CTRL_CONTROL, XVIP_CTRL_CONTROL_SW_RESET); +} + +static inline void xvip_start(struct xvip_device *xvip) +{ + xvip_set(xvip, XVIP_CTRL_CONTROL, + XVIP_CTRL_CONTROL_SW_ENABLE | XVIP_CTRL_CONTROL_REG_UPDATE); +} + +static inline void xvip_stop(struct xvip_device *xvip) +{ + xvip_clr(xvip, XVIP_CTRL_CONTROL, XVIP_CTRL_CONTROL_SW_ENABLE); +} + +static inline void xvip_resume(struct xvip_device *xvip) +{ + xvip_write(xvip, XVIP_CTRL_CONTROL, + xvip->saved_ctrl | XVIP_CTRL_CONTROL_SW_ENABLE); +} + +static inline void xvip_suspend(struct xvip_device *xvip) +{ + xvip->saved_ctrl = xvip_read(xvip, XVIP_CTRL_CONTROL); + xvip_write(xvip, XVIP_CTRL_CONTROL, + xvip->saved_ctrl & ~XVIP_CTRL_CONTROL_SW_ENABLE); +} + +static inline void xvip_set_frame_size(struct xvip_device *xvip, + const struct v4l2_mbus_framefmt *format) +{ + xvip_write(xvip, XVIP_ACTIVE_SIZE, + (format->height << XVIP_ACTIVE_VSIZE_SHIFT) | + (format->width << XVIP_ACTIVE_HSIZE_SHIFT)); +} + +static inline void xvip_get_frame_size(struct xvip_device *xvip, + struct v4l2_mbus_framefmt *format) +{ + u32 reg; + + reg = xvip_read(xvip, XVIP_ACTIVE_SIZE); + format->width = (reg & XVIP_ACTIVE_HSIZE_MASK) >> + XVIP_ACTIVE_HSIZE_SHIFT; + format->height = (reg & XVIP_ACTIVE_VSIZE_MASK) >> + XVIP_ACTIVE_VSIZE_SHIFT; +} + +static inline void xvip_enable_reg_update(struct xvip_device *xvip) +{ + xvip_set(xvip, XVIP_CTRL_CONTROL, XVIP_CTRL_CONTROL_REG_UPDATE); +} + +static inline void xvip_disable_reg_update(struct xvip_device *xvip) +{ + xvip_clr(xvip, XVIP_CTRL_CONTROL, XVIP_CTRL_CONTROL_REG_UPDATE); +} + +static inline void xvip_print_version(struct xvip_device *xvip) +{ + u32 version; + + version = xvip_read(xvip, XVIP_CTRL_VERSION); + + dev_info(xvip->dev, "device found, version %u.%02x%x\n", + ((version & XVIP_CTRL_VERSION_MAJOR_MASK) >> + XVIP_CTRL_VERSION_MAJOR_SHIFT), + ((version & XVIP_CTRL_VERSION_MINOR_MASK) >> + XVIP_CTRL_VERSION_MINOR_SHIFT), + ((version & XVIP_CTRL_VERSION_REVISION_MASK) >> + XVIP_CTRL_VERSION_REVISION_SHIFT)); +} + +#endif /* __XILINX_VIP_H__ */ diff --git a/drivers/media/platform/xilinx/xilinx-vipp.c b/drivers/media/platform/xilinx/xilinx-vipp.c new file mode 100644 index 000000000000..7b7cb9c28d2c --- /dev/null +++ b/drivers/media/platform/xilinx/xilinx-vipp.c @@ -0,0 +1,669 @@ +/* + * Xilinx Video IP Composite Device + * + * Copyright (C) 2013-2015 Ideas on Board + * Copyright (C) 2013-2015 Xilinx, Inc. + * + * Contacts: Hyun Kwon + * Laurent Pinchart + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "xilinx-dma.h" +#include "xilinx-vipp.h" + +#define XVIPP_DMA_S2MM 0 +#define XVIPP_DMA_MM2S 1 + +/** + * struct xvip_graph_entity - Entity in the video graph + * @list: list entry in a graph entities list + * @node: the entity's DT node + * @entity: media entity, from the corresponding V4L2 subdev + * @asd: subdev asynchronous registration information + * @subdev: V4L2 subdev + */ +struct xvip_graph_entity { + struct list_head list; + struct device_node *node; + struct media_entity *entity; + + struct v4l2_async_subdev asd; + struct v4l2_subdev *subdev; +}; + +/* ----------------------------------------------------------------------------- + * Graph Management + */ + +static struct xvip_graph_entity * +xvip_graph_find_entity(struct xvip_composite_device *xdev, + const struct device_node *node) +{ + struct xvip_graph_entity *entity; + + list_for_each_entry(entity, &xdev->entities, list) { + if (entity->node == node) + return entity; + } + + return NULL; +} + +static int xvip_graph_build_one(struct xvip_composite_device *xdev, + struct xvip_graph_entity *entity) +{ + u32 link_flags = MEDIA_LNK_FL_ENABLED; + struct media_entity *local = entity->entity; + struct media_entity *remote; + struct media_pad *local_pad; + struct media_pad *remote_pad; + struct xvip_graph_entity *ent; + struct v4l2_of_link link; + struct device_node *ep = NULL; + struct device_node *next; + int ret = 0; + + dev_dbg(xdev->dev, "creating links for entity %s\n", local->name); + + while (1) { + /* Get the next endpoint and parse its link. */ + next = of_graph_get_next_endpoint(entity->node, ep); + if (next == NULL) + break; + + of_node_put(ep); + ep = next; + + dev_dbg(xdev->dev, "processing endpoint %s\n", ep->full_name); + + ret = v4l2_of_parse_link(ep, &link); + if (ret < 0) { + dev_err(xdev->dev, "failed to parse link for %s\n", + ep->full_name); + continue; + } + + /* Skip sink ports, they will be processed from the other end of + * the link. + */ + if (link.local_port >= local->num_pads) { + dev_err(xdev->dev, "invalid port number %u on %s\n", + link.local_port, link.local_node->full_name); + v4l2_of_put_link(&link); + ret = -EINVAL; + break; + } + + local_pad = &local->pads[link.local_port]; + + if (local_pad->flags & MEDIA_PAD_FL_SINK) { + dev_dbg(xdev->dev, "skipping sink port %s:%u\n", + link.local_node->full_name, link.local_port); + v4l2_of_put_link(&link); + continue; + } + + /* Skip DMA engines, they will be processed separately. */ + if (link.remote_node == xdev->dev->of_node) { + dev_dbg(xdev->dev, "skipping DMA port %s:%u\n", + link.local_node->full_name, link.local_port); + v4l2_of_put_link(&link); + continue; + } + + /* Find the remote entity. */ + ent = xvip_graph_find_entity(xdev, link.remote_node); + if (ent == NULL) { + dev_err(xdev->dev, "no entity found for %s\n", + link.remote_node->full_name); + v4l2_of_put_link(&link); + ret = -ENODEV; + break; + } + + remote = ent->entity; + + if (link.remote_port >= remote->num_pads) { + dev_err(xdev->dev, "invalid port number %u on %s\n", + link.remote_port, link.remote_node->full_name); + v4l2_of_put_link(&link); + ret = -EINVAL; + break; + } + + remote_pad = &remote->pads[link.remote_port]; + + v4l2_of_put_link(&link); + + /* Create the media link. */ + dev_dbg(xdev->dev, "creating %s:%u -> %s:%u link\n", + local->name, local_pad->index, + remote->name, remote_pad->index); + + ret = media_entity_create_link(local, local_pad->index, + remote, remote_pad->index, + link_flags); + if (ret < 0) { + dev_err(xdev->dev, + "failed to create %s:%u -> %s:%u link\n", + local->name, local_pad->index, + remote->name, remote_pad->index); + break; + } + } + + of_node_put(ep); + return ret; +} + +static struct xvip_dma * +xvip_graph_find_dma(struct xvip_composite_device *xdev, unsigned int port) +{ + struct xvip_dma *dma; + + list_for_each_entry(dma, &xdev->dmas, list) { + if (dma->port == port) + return dma; + } + + return NULL; +} + +static int xvip_graph_build_dma(struct xvip_composite_device *xdev) +{ + u32 link_flags = MEDIA_LNK_FL_ENABLED; + struct device_node *node = xdev->dev->of_node; + struct media_entity *source; + struct media_entity *sink; + struct media_pad *source_pad; + struct media_pad *sink_pad; + struct xvip_graph_entity *ent; + struct v4l2_of_link link; + struct device_node *ep = NULL; + struct device_node *next; + struct xvip_dma *dma; + int ret = 0; + + dev_dbg(xdev->dev, "creating links for DMA engines\n"); + + while (1) { + /* Get the next endpoint and parse its link. */ + next = of_graph_get_next_endpoint(node, ep); + if (next == NULL) + break; + + of_node_put(ep); + ep = next; + + dev_dbg(xdev->dev, "processing endpoint %s\n", ep->full_name); + + ret = v4l2_of_parse_link(ep, &link); + if (ret < 0) { + dev_err(xdev->dev, "failed to parse link for %s\n", + ep->full_name); + continue; + } + + /* Find the DMA engine. */ + dma = xvip_graph_find_dma(xdev, link.local_port); + if (dma == NULL) { + dev_err(xdev->dev, "no DMA engine found for port %u\n", + link.local_port); + v4l2_of_put_link(&link); + ret = -EINVAL; + break; + } + + dev_dbg(xdev->dev, "creating link for DMA engine %s\n", + dma->video.name); + + /* Find the remote entity. */ + ent = xvip_graph_find_entity(xdev, link.remote_node); + if (ent == NULL) { + dev_err(xdev->dev, "no entity found for %s\n", + link.remote_node->full_name); + v4l2_of_put_link(&link); + ret = -ENODEV; + break; + } + + if (link.remote_port >= ent->entity->num_pads) { + dev_err(xdev->dev, "invalid port number %u on %s\n", + link.remote_port, link.remote_node->full_name); + v4l2_of_put_link(&link); + ret = -EINVAL; + break; + } + + if (dma->pad.flags & MEDIA_PAD_FL_SOURCE) { + source = &dma->video.entity; + source_pad = &dma->pad; + sink = ent->entity; + sink_pad = &sink->pads[link.remote_port]; + } else { + source = ent->entity; + source_pad = &source->pads[link.remote_port]; + sink = &dma->video.entity; + sink_pad = &dma->pad; + } + + v4l2_of_put_link(&link); + + /* Create the media link. */ + dev_dbg(xdev->dev, "creating %s:%u -> %s:%u link\n", + source->name, source_pad->index, + sink->name, sink_pad->index); + + ret = media_entity_create_link(source, source_pad->index, + sink, sink_pad->index, + link_flags); + if (ret < 0) { + dev_err(xdev->dev, + "failed to create %s:%u -> %s:%u link\n", + source->name, source_pad->index, + sink->name, sink_pad->index); + break; + } + } + + of_node_put(ep); + return ret; +} + +static int xvip_graph_notify_complete(struct v4l2_async_notifier *notifier) +{ + struct xvip_composite_device *xdev = + container_of(notifier, struct xvip_composite_device, notifier); + struct xvip_graph_entity *entity; + int ret; + + dev_dbg(xdev->dev, "notify complete, all subdevs registered\n"); + + /* Create links for every entity. */ + list_for_each_entry(entity, &xdev->entities, list) { + ret = xvip_graph_build_one(xdev, entity); + if (ret < 0) + return ret; + } + + /* Create links for DMA channels. */ + ret = xvip_graph_build_dma(xdev); + if (ret < 0) + return ret; + + ret = v4l2_device_register_subdev_nodes(&xdev->v4l2_dev); + if (ret < 0) + dev_err(xdev->dev, "failed to register subdev nodes\n"); + + return ret; +} + +static int xvip_graph_notify_bound(struct v4l2_async_notifier *notifier, + struct v4l2_subdev *subdev, + struct v4l2_async_subdev *asd) +{ + struct xvip_composite_device *xdev = + container_of(notifier, struct xvip_composite_device, notifier); + struct xvip_graph_entity *entity; + + /* Locate the entity corresponding to the bound subdev and store the + * subdev pointer. + */ + list_for_each_entry(entity, &xdev->entities, list) { + if (entity->node != subdev->dev->of_node) + continue; + + if (entity->subdev) { + dev_err(xdev->dev, "duplicate subdev for node %s\n", + entity->node->full_name); + return -EINVAL; + } + + dev_dbg(xdev->dev, "subdev %s bound\n", subdev->name); + entity->entity = &subdev->entity; + entity->subdev = subdev; + return 0; + } + + dev_err(xdev->dev, "no entity for subdev %s\n", subdev->name); + return -EINVAL; +} + +static int xvip_graph_parse_one(struct xvip_composite_device *xdev, + struct device_node *node) +{ + struct xvip_graph_entity *entity; + struct device_node *remote; + struct device_node *ep = NULL; + struct device_node *next; + int ret = 0; + + dev_dbg(xdev->dev, "parsing node %s\n", node->full_name); + + while (1) { + next = of_graph_get_next_endpoint(node, ep); + if (next == NULL) + break; + + of_node_put(ep); + ep = next; + + dev_dbg(xdev->dev, "handling endpoint %s\n", ep->full_name); + + remote = of_graph_get_remote_port_parent(ep); + if (remote == NULL) { + ret = -EINVAL; + break; + } + + /* Skip entities that we have already processed. */ + if (remote == xdev->dev->of_node || + xvip_graph_find_entity(xdev, remote)) { + of_node_put(remote); + continue; + } + + entity = devm_kzalloc(xdev->dev, sizeof(*entity), GFP_KERNEL); + if (entity == NULL) { + of_node_put(remote); + ret = -ENOMEM; + break; + } + + entity->node = remote; + entity->asd.match_type = V4L2_ASYNC_MATCH_OF; + entity->asd.match.of.node = remote; + list_add_tail(&entity->list, &xdev->entities); + xdev->num_subdevs++; + } + + of_node_put(ep); + return ret; +} + +static int xvip_graph_parse(struct xvip_composite_device *xdev) +{ + struct xvip_graph_entity *entity; + int ret; + + /* + * Walk the links to parse the full graph. Start by parsing the + * composite node and then parse entities in turn. The list_for_each + * loop will handle entities added at the end of the list while walking + * the links. + */ + ret = xvip_graph_parse_one(xdev, xdev->dev->of_node); + if (ret < 0) + return 0; + + list_for_each_entry(entity, &xdev->entities, list) { + ret = xvip_graph_parse_one(xdev, entity->node); + if (ret < 0) + break; + } + + return ret; +} + +static int xvip_graph_dma_init_one(struct xvip_composite_device *xdev, + struct device_node *node) +{ + struct xvip_dma *dma; + enum v4l2_buf_type type; + const char *direction; + unsigned int index; + int ret; + + ret = of_property_read_string(node, "direction", &direction); + if (ret < 0) + return ret; + + if (strcmp(direction, "input") == 0) + type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + else if (strcmp(direction, "output") == 0) + type = V4L2_BUF_TYPE_VIDEO_OUTPUT; + else + return -EINVAL; + + of_property_read_u32(node, "reg", &index); + + dma = devm_kzalloc(xdev->dev, sizeof(*dma), GFP_KERNEL); + if (dma == NULL) + return -ENOMEM; + + ret = xvip_dma_init(xdev, dma, type, index); + if (ret < 0) { + dev_err(xdev->dev, "%s initialization failed\n", + node->full_name); + return ret; + } + + list_add_tail(&dma->list, &xdev->dmas); + + xdev->v4l2_caps |= type == V4L2_BUF_TYPE_VIDEO_CAPTURE + ? V4L2_CAP_VIDEO_CAPTURE : V4L2_CAP_VIDEO_OUTPUT; + + return 0; +} + +static int xvip_graph_dma_init(struct xvip_composite_device *xdev) +{ + struct device_node *ports; + struct device_node *port; + int ret; + + ports = of_get_child_by_name(xdev->dev->of_node, "ports"); + if (ports == NULL) { + dev_err(xdev->dev, "ports node not present\n"); + return -EINVAL; + } + + for_each_child_of_node(ports, port) { + ret = xvip_graph_dma_init_one(xdev, port); + if (ret < 0) + return ret; + } + + return 0; +} + +static void xvip_graph_cleanup(struct xvip_composite_device *xdev) +{ + struct xvip_graph_entity *entityp; + struct xvip_graph_entity *entity; + struct xvip_dma *dmap; + struct xvip_dma *dma; + + v4l2_async_notifier_unregister(&xdev->notifier); + + list_for_each_entry_safe(entity, entityp, &xdev->entities, list) { + of_node_put(entity->node); + list_del(&entity->list); + } + + list_for_each_entry_safe(dma, dmap, &xdev->dmas, list) { + xvip_dma_cleanup(dma); + list_del(&dma->list); + } +} + +static int xvip_graph_init(struct xvip_composite_device *xdev) +{ + struct xvip_graph_entity *entity; + struct v4l2_async_subdev **subdevs = NULL; + unsigned int num_subdevs; + unsigned int i; + int ret; + + /* Init the DMA channels. */ + ret = xvip_graph_dma_init(xdev); + if (ret < 0) { + dev_err(xdev->dev, "DMA initialization failed\n"); + goto done; + } + + /* Parse the graph to extract a list of subdevice DT nodes. */ + ret = xvip_graph_parse(xdev); + if (ret < 0) { + dev_err(xdev->dev, "graph parsing failed\n"); + goto done; + } + + if (!xdev->num_subdevs) { + dev_err(xdev->dev, "no subdev found in graph\n"); + goto done; + } + + /* Register the subdevices notifier. */ + num_subdevs = xdev->num_subdevs; + subdevs = devm_kzalloc(xdev->dev, sizeof(*subdevs) * num_subdevs, + GFP_KERNEL); + if (subdevs == NULL) { + ret = -ENOMEM; + goto done; + } + + i = 0; + list_for_each_entry(entity, &xdev->entities, list) + subdevs[i++] = &entity->asd; + + xdev->notifier.subdevs = subdevs; + xdev->notifier.num_subdevs = num_subdevs; + xdev->notifier.bound = xvip_graph_notify_bound; + xdev->notifier.complete = xvip_graph_notify_complete; + + ret = v4l2_async_notifier_register(&xdev->v4l2_dev, &xdev->notifier); + if (ret < 0) { + dev_err(xdev->dev, "notifier registration failed\n"); + goto done; + } + + ret = 0; + +done: + if (ret < 0) + xvip_graph_cleanup(xdev); + + return ret; +} + +/* ----------------------------------------------------------------------------- + * Media Controller and V4L2 + */ + +static void xvip_composite_v4l2_cleanup(struct xvip_composite_device *xdev) +{ + v4l2_device_unregister(&xdev->v4l2_dev); + media_device_unregister(&xdev->media_dev); +} + +static int xvip_composite_v4l2_init(struct xvip_composite_device *xdev) +{ + int ret; + + xdev->media_dev.dev = xdev->dev; + strlcpy(xdev->media_dev.model, "Xilinx Video Composite Device", + sizeof(xdev->media_dev.model)); + xdev->media_dev.hw_revision = 0; + + ret = media_device_register(&xdev->media_dev); + if (ret < 0) { + dev_err(xdev->dev, "media device registration failed (%d)\n", + ret); + return ret; + } + + xdev->v4l2_dev.mdev = &xdev->media_dev; + ret = v4l2_device_register(xdev->dev, &xdev->v4l2_dev); + if (ret < 0) { + dev_err(xdev->dev, "V4L2 device registration failed (%d)\n", + ret); + media_device_unregister(&xdev->media_dev); + return ret; + } + + return 0; +} + +/* ----------------------------------------------------------------------------- + * Platform Device Driver + */ + +static int xvip_composite_probe(struct platform_device *pdev) +{ + struct xvip_composite_device *xdev; + int ret; + + xdev = devm_kzalloc(&pdev->dev, sizeof(*xdev), GFP_KERNEL); + if (!xdev) + return -ENOMEM; + + xdev->dev = &pdev->dev; + INIT_LIST_HEAD(&xdev->entities); + INIT_LIST_HEAD(&xdev->dmas); + + ret = xvip_composite_v4l2_init(xdev); + if (ret < 0) + return ret; + + ret = xvip_graph_init(xdev); + if (ret < 0) + goto error; + + platform_set_drvdata(pdev, xdev); + + dev_info(xdev->dev, "device registered\n"); + + return 0; + +error: + xvip_composite_v4l2_cleanup(xdev); + return ret; +} + +static int xvip_composite_remove(struct platform_device *pdev) +{ + struct xvip_composite_device *xdev = platform_get_drvdata(pdev); + + xvip_graph_cleanup(xdev); + xvip_composite_v4l2_cleanup(xdev); + + return 0; +} + +static const struct of_device_id xvip_composite_of_id_table[] = { + { .compatible = "xlnx,video" }, + { } +}; +MODULE_DEVICE_TABLE(of, xvip_composite_of_id_table); + +static struct platform_driver xvip_composite_driver = { + .driver = { + .name = "xilinx-video", + .of_match_table = xvip_composite_of_id_table, + }, + .probe = xvip_composite_probe, + .remove = xvip_composite_remove, +}; + +module_platform_driver(xvip_composite_driver); + +MODULE_AUTHOR("Laurent Pinchart "); +MODULE_DESCRIPTION("Xilinx Video IP Composite Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/platform/xilinx/xilinx-vipp.h b/drivers/media/platform/xilinx/xilinx-vipp.h new file mode 100644 index 000000000000..faf6b6e80b3b --- /dev/null +++ b/drivers/media/platform/xilinx/xilinx-vipp.h @@ -0,0 +1,49 @@ +/* + * Xilinx Video IP Composite Device + * + * Copyright (C) 2013-2015 Ideas on Board + * Copyright (C) 2013-2015 Xilinx, Inc. + * + * Contacts: Hyun Kwon + * Laurent Pinchart + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef __XILINX_VIPP_H__ +#define __XILINX_VIPP_H__ + +#include +#include +#include +#include +#include +#include + +/** + * struct xvip_composite_device - Xilinx Video IP device structure + * @v4l2_dev: V4L2 device + * @media_dev: media device + * @dev: (OF) device + * @notifier: V4L2 asynchronous subdevs notifier + * @entities: entities in the graph as a list of xvip_graph_entity + * @num_subdevs: number of subdevs in the pipeline + * @dmas: list of DMA channels at the pipeline output and input + * @v4l2_caps: V4L2 capabilities of the whole device (see VIDIOC_QUERYCAP) + */ +struct xvip_composite_device { + struct v4l2_device v4l2_dev; + struct media_device media_dev; + struct device *dev; + + struct v4l2_async_notifier notifier; + struct list_head entities; + unsigned int num_subdevs; + + struct list_head dmas; + u32 v4l2_caps; +}; + +#endif /* __XILINX_VIPP_H__ */ diff --git a/include/dt-bindings/media/xilinx-vip.h b/include/dt-bindings/media/xilinx-vip.h new file mode 100644 index 000000000000..6298fec00685 --- /dev/null +++ b/include/dt-bindings/media/xilinx-vip.h @@ -0,0 +1,39 @@ +/* + * Xilinx Video IP Core + * + * Copyright (C) 2013-2015 Ideas on Board + * Copyright (C) 2013-2015 Xilinx, Inc. + * + * Contacts: Hyun Kwon + * Laurent Pinchart + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef __DT_BINDINGS_MEDIA_XILINX_VIP_H__ +#define __DT_BINDINGS_MEDIA_XILINX_VIP_H__ + +/* + * Video format codes as defined in "AXI4-Stream Video IP and System Design + * Guide". + */ +#define XVIP_VF_YUV_422 0 +#define XVIP_VF_YUV_444 1 +#define XVIP_VF_RBG 2 +#define XVIP_VF_YUV_420 3 +#define XVIP_VF_YUVA_422 4 +#define XVIP_VF_YUVA_444 5 +#define XVIP_VF_RGBA 6 +#define XVIP_VF_YUVA_420 7 +#define XVIP_VF_YUVD_422 8 +#define XVIP_VF_YUVD_444 9 +#define XVIP_VF_RGBD 10 +#define XVIP_VF_YUVD_420 11 +#define XVIP_VF_MONO_SENSOR 12 +#define XVIP_VF_CUSTOM2 13 +#define XVIP_VF_CUSTOM3 14 +#define XVIP_VF_CUSTOM4 15 + +#endif /* __DT_BINDINGS_MEDIA_XILINX_VIP_H__ */ -- cgit v1.2.3 From a5562f65b1371a0988b707c10c44fcc2bba56990 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 15 May 2013 11:36:56 -0300 Subject: [media] v4l: xilinx: Add Test Pattern Generator driver The TPG generates multiple static or dynamic test patterns. The driver currently hardcodes the pattern to the moving box pattern. Signed-off-by: Christian Kohn Signed-off-by: Hyun Kwon Signed-off-by: Laurent Pinchart Signed-off-by: Michal Simek Acked-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- .../bindings/media/xilinx/xlnx,v-tpg.txt | 71 ++ MAINTAINERS | 1 + drivers/media/platform/xilinx/Kconfig | 7 + drivers/media/platform/xilinx/Makefile | 1 + drivers/media/platform/xilinx/xilinx-tpg.c | 931 +++++++++++++++++++++ include/uapi/linux/Kbuild | 1 + include/uapi/linux/xilinx-v4l2-controls.h | 73 ++ 7 files changed, 1085 insertions(+) create mode 100644 Documentation/devicetree/bindings/media/xilinx/xlnx,v-tpg.txt create mode 100644 drivers/media/platform/xilinx/xilinx-tpg.c create mode 100644 include/uapi/linux/xilinx-v4l2-controls.h (limited to 'include') diff --git a/Documentation/devicetree/bindings/media/xilinx/xlnx,v-tpg.txt b/Documentation/devicetree/bindings/media/xilinx/xlnx,v-tpg.txt new file mode 100644 index 000000000000..9dd86b3db937 --- /dev/null +++ b/Documentation/devicetree/bindings/media/xilinx/xlnx,v-tpg.txt @@ -0,0 +1,71 @@ +Xilinx Video Test Pattern Generator (TPG) +----------------------------------------- + +Required properties: + +- compatible: Must contain at least one of + + "xlnx,v-tpg-5.0" (TPG version 5.0) + "xlnx,v-tpg-6.0" (TPG version 6.0) + + TPG versions backward-compatible with previous versions should list all + compatible versions in the newer to older order. + +- reg: Physical base address and length of the registers set for the device. + +- clocks: Reference to the video core clock. + +- xlnx,video-format, xlnx,video-width: Video format and width, as defined in + video.txt. + +- port: Video port, using the DT bindings defined in ../video-interfaces.txt. + The TPG has a single output port numbered 0. + +Optional properties: + +- xlnx,vtc: A phandle referencing the Video Timing Controller that generates + video timings for the TPG test patterns. + +- timing-gpios: Specifier for a GPIO that controls the timing mux at the TPG + input. The GPIO active level corresponds to the selection of VTC-generated + video timings. + +The xlnx,vtc and timing-gpios properties are mandatory when the TPG is +synthesized with two ports and forbidden when synthesized with one port. + +Example: + + tpg_0: tpg@40050000 { + compatible = "xlnx,v-tpg-6.0", "xlnx,v-tpg-5.0"; + reg = <0x40050000 0x10000>; + clocks = <&clkc 15>; + + xlnx,vtc = <&vtc_3>; + timing-gpios = <&ps7_gpio_0 55 GPIO_ACTIVE_LOW>; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + + xlnx,video-format = ; + xlnx,video-width = <8>; + + tpg_in: endpoint { + remote-endpoint = <&adv7611_out>; + }; + }; + port@1 { + reg = <1>; + + xlnx,video-format = ; + xlnx,video-width = <8>; + + tpg1_out: endpoint { + remote-endpoint = <&switch_in0>; + }; + }: + }; + }; diff --git a/MAINTAINERS b/MAINTAINERS index ca3b2638fcc6..30e7e38ccad0 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -10826,6 +10826,7 @@ T: git git://linuxtv.org/media_tree.git S: Supported F: Documentation/devicetree/bindings/media/xilinx/ F: drivers/media/platform/xilinx/ +F: include/uapi/linux/xilinx-v4l2-controls.h XILLYBUS DRIVER M: Eli Billauer diff --git a/drivers/media/platform/xilinx/Kconfig b/drivers/media/platform/xilinx/Kconfig index 19db82343bb8..d7324c726fc2 100644 --- a/drivers/media/platform/xilinx/Kconfig +++ b/drivers/media/platform/xilinx/Kconfig @@ -7,6 +7,13 @@ config VIDEO_XILINX if VIDEO_XILINX +config VIDEO_XILINX_TPG + tristate "Xilinx Video Test Pattern Generator" + depends on VIDEO_XILINX + select VIDEO_XILINX_VTC + ---help--- + Driver for the Xilinx Video Test Pattern Generator + config VIDEO_XILINX_VTC tristate "Xilinx Video Timing Controller" depends on VIDEO_XILINX diff --git a/drivers/media/platform/xilinx/Makefile b/drivers/media/platform/xilinx/Makefile index 6611e3228e3c..e8a0f2a9f733 100644 --- a/drivers/media/platform/xilinx/Makefile +++ b/drivers/media/platform/xilinx/Makefile @@ -1,4 +1,5 @@ xilinx-video-objs += xilinx-dma.o xilinx-vip.o xilinx-vipp.o obj-$(CONFIG_VIDEO_XILINX) += xilinx-video.o +obj-$(CONFIG_VIDEO_XILINX_TPG) += xilinx-tpg.o obj-$(CONFIG_VIDEO_XILINX_VTC) += xilinx-vtc.o diff --git a/drivers/media/platform/xilinx/xilinx-tpg.c b/drivers/media/platform/xilinx/xilinx-tpg.c new file mode 100644 index 000000000000..b5f7d5ecb7f6 --- /dev/null +++ b/drivers/media/platform/xilinx/xilinx-tpg.c @@ -0,0 +1,931 @@ +/* + * Xilinx Test Pattern Generator + * + * Copyright (C) 2013-2015 Ideas on Board + * Copyright (C) 2013-2015 Xilinx, Inc. + * + * Contacts: Hyun Kwon + * Laurent Pinchart + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "xilinx-vip.h" +#include "xilinx-vtc.h" + +#define XTPG_CTRL_STATUS_SLAVE_ERROR (1 << 16) +#define XTPG_CTRL_IRQ_SLAVE_ERROR (1 << 16) + +#define XTPG_PATTERN_CONTROL 0x0100 +#define XTPG_PATTERN_MASK (0xf << 0) +#define XTPG_PATTERN_CONTROL_CROSS_HAIRS (1 << 4) +#define XTPG_PATTERN_CONTROL_MOVING_BOX (1 << 5) +#define XTPG_PATTERN_CONTROL_COLOR_MASK_SHIFT 6 +#define XTPG_PATTERN_CONTROL_COLOR_MASK_MASK (0xf << 6) +#define XTPG_PATTERN_CONTROL_STUCK_PIXEL (1 << 9) +#define XTPG_PATTERN_CONTROL_NOISE (1 << 10) +#define XTPG_PATTERN_CONTROL_MOTION (1 << 12) +#define XTPG_MOTION_SPEED 0x0104 +#define XTPG_CROSS_HAIRS 0x0108 +#define XTPG_CROSS_HAIRS_ROW_SHIFT 0 +#define XTPG_CROSS_HAIRS_ROW_MASK (0xfff << 0) +#define XTPG_CROSS_HAIRS_COLUMN_SHIFT 16 +#define XTPG_CROSS_HAIRS_COLUMN_MASK (0xfff << 16) +#define XTPG_ZPLATE_HOR_CONTROL 0x010c +#define XTPG_ZPLATE_VER_CONTROL 0x0110 +#define XTPG_ZPLATE_START_SHIFT 0 +#define XTPG_ZPLATE_START_MASK (0xffff << 0) +#define XTPG_ZPLATE_SPEED_SHIFT 16 +#define XTPG_ZPLATE_SPEED_MASK (0xffff << 16) +#define XTPG_BOX_SIZE 0x0114 +#define XTPG_BOX_COLOR 0x0118 +#define XTPG_STUCK_PIXEL_THRESH 0x011c +#define XTPG_NOISE_GAIN 0x0120 +#define XTPG_BAYER_PHASE 0x0124 +#define XTPG_BAYER_PHASE_RGGB 0 +#define XTPG_BAYER_PHASE_GRBG 1 +#define XTPG_BAYER_PHASE_GBRG 2 +#define XTPG_BAYER_PHASE_BGGR 3 +#define XTPG_BAYER_PHASE_OFF 4 + +/* + * The minimum blanking value is one clock cycle for the front porch, one clock + * cycle for the sync pulse and one clock cycle for the back porch. + */ +#define XTPG_MIN_HBLANK 3 +#define XTPG_MAX_HBLANK (XVTC_MAX_HSIZE - XVIP_MIN_WIDTH) +#define XTPG_MIN_VBLANK 3 +#define XTPG_MAX_VBLANK (XVTC_MAX_VSIZE - XVIP_MIN_HEIGHT) + +/** + * struct xtpg_device - Xilinx Test Pattern Generator device structure + * @xvip: Xilinx Video IP device + * @pads: media pads + * @npads: number of pads (1 or 2) + * @has_input: whether an input is connected to the sink pad + * @formats: active V4L2 media bus format for each pad + * @default_format: default V4L2 media bus format + * @vip_format: format information corresponding to the active format + * @bayer: boolean flag if TPG is set to any bayer format + * @ctrl_handler: control handler + * @hblank: horizontal blanking control + * @vblank: vertical blanking control + * @pattern: test pattern control + * @streaming: is the video stream active + * @vtc: video timing controller + * @vtmux_gpio: video timing mux GPIO + */ +struct xtpg_device { + struct xvip_device xvip; + + struct media_pad pads[2]; + unsigned int npads; + bool has_input; + + struct v4l2_mbus_framefmt formats[2]; + struct v4l2_mbus_framefmt default_format; + const struct xvip_video_format *vip_format; + bool bayer; + + struct v4l2_ctrl_handler ctrl_handler; + struct v4l2_ctrl *hblank; + struct v4l2_ctrl *vblank; + struct v4l2_ctrl *pattern; + bool streaming; + + struct xvtc_device *vtc; + struct gpio_desc *vtmux_gpio; +}; + +static inline struct xtpg_device *to_tpg(struct v4l2_subdev *subdev) +{ + return container_of(subdev, struct xtpg_device, xvip.subdev); +} + +static u32 xtpg_get_bayer_phase(unsigned int code) +{ + switch (code) { + case MEDIA_BUS_FMT_SRGGB8_1X8: + return XTPG_BAYER_PHASE_RGGB; + case MEDIA_BUS_FMT_SGRBG8_1X8: + return XTPG_BAYER_PHASE_GRBG; + case MEDIA_BUS_FMT_SGBRG8_1X8: + return XTPG_BAYER_PHASE_GBRG; + case MEDIA_BUS_FMT_SBGGR8_1X8: + return XTPG_BAYER_PHASE_BGGR; + default: + return XTPG_BAYER_PHASE_OFF; + } +} + +static void __xtpg_update_pattern_control(struct xtpg_device *xtpg, + bool passthrough, bool pattern) +{ + u32 pattern_mask = (1 << (xtpg->pattern->maximum + 1)) - 1; + + /* + * If the TPG has no sink pad or no input connected to its sink pad + * passthrough mode can't be enabled. + */ + if (xtpg->npads == 1 || !xtpg->has_input) + passthrough = false; + + /* If passthrough mode is allowed unmask bit 0. */ + if (passthrough) + pattern_mask &= ~1; + + /* If test pattern mode is allowed unmask all other bits. */ + if (pattern) + pattern_mask &= 1; + + __v4l2_ctrl_modify_range(xtpg->pattern, 0, xtpg->pattern->maximum, + pattern_mask, pattern ? 9 : 0); +} + +static void xtpg_update_pattern_control(struct xtpg_device *xtpg, + bool passthrough, bool pattern) +{ + mutex_lock(xtpg->ctrl_handler.lock); + __xtpg_update_pattern_control(xtpg, passthrough, pattern); + mutex_unlock(xtpg->ctrl_handler.lock); +} + +/* ----------------------------------------------------------------------------- + * V4L2 Subdevice Video Operations + */ + +static int xtpg_s_stream(struct v4l2_subdev *subdev, int enable) +{ + struct xtpg_device *xtpg = to_tpg(subdev); + unsigned int width = xtpg->formats[0].width; + unsigned int height = xtpg->formats[0].height; + bool passthrough; + u32 bayer_phase; + + if (!enable) { + xvip_stop(&xtpg->xvip); + if (xtpg->vtc) + xvtc_generator_stop(xtpg->vtc); + + xtpg_update_pattern_control(xtpg, true, true); + xtpg->streaming = false; + return 0; + } + + xvip_set_frame_size(&xtpg->xvip, &xtpg->formats[0]); + + if (xtpg->vtc) { + struct xvtc_config config = { + .hblank_start = width, + .hsync_start = width + 1, + .vblank_start = height, + .vsync_start = height + 1, + }; + unsigned int htotal; + unsigned int vtotal; + + htotal = min_t(unsigned int, XVTC_MAX_HSIZE, + v4l2_ctrl_g_ctrl(xtpg->hblank) + width); + vtotal = min_t(unsigned int, XVTC_MAX_VSIZE, + v4l2_ctrl_g_ctrl(xtpg->vblank) + height); + + config.hsync_end = htotal - 1; + config.hsize = htotal; + config.vsync_end = vtotal - 1; + config.vsize = vtotal; + + xvtc_generator_start(xtpg->vtc, &config); + } + + /* + * Configure the bayer phase and video timing mux based on the + * operation mode (passthrough or test pattern generation). The test + * pattern can be modified by the control set handler, we thus need to + * take the control lock here to avoid races. + */ + mutex_lock(xtpg->ctrl_handler.lock); + + xvip_clr_and_set(&xtpg->xvip, XTPG_PATTERN_CONTROL, + XTPG_PATTERN_MASK, xtpg->pattern->cur.val); + + /* + * Switching between passthrough and test pattern generation modes isn't + * allowed during streaming, update the control range accordingly. + */ + passthrough = xtpg->pattern->cur.val == 0; + __xtpg_update_pattern_control(xtpg, passthrough, !passthrough); + + xtpg->streaming = true; + + mutex_unlock(xtpg->ctrl_handler.lock); + + /* + * For TPG v5.0, the bayer phase needs to be off for the pass through + * mode, otherwise the external input would be subsampled. + */ + bayer_phase = passthrough ? XTPG_BAYER_PHASE_OFF + : xtpg_get_bayer_phase(xtpg->formats[0].code); + xvip_write(&xtpg->xvip, XTPG_BAYER_PHASE, bayer_phase); + + if (xtpg->vtmux_gpio) + gpiod_set_value_cansleep(xtpg->vtmux_gpio, !passthrough); + + xvip_start(&xtpg->xvip); + + return 0; +} + +/* ----------------------------------------------------------------------------- + * V4L2 Subdevice Pad Operations + */ + +static struct v4l2_mbus_framefmt * +__xtpg_get_pad_format(struct xtpg_device *xtpg, + struct v4l2_subdev_pad_config *cfg, + unsigned int pad, u32 which) +{ + switch (which) { + case V4L2_SUBDEV_FORMAT_TRY: + return v4l2_subdev_get_try_format(&xtpg->xvip.subdev, cfg, pad); + case V4L2_SUBDEV_FORMAT_ACTIVE: + return &xtpg->formats[pad]; + default: + return NULL; + } +} + +static int xtpg_get_format(struct v4l2_subdev *subdev, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *fmt) +{ + struct xtpg_device *xtpg = to_tpg(subdev); + + fmt->format = *__xtpg_get_pad_format(xtpg, cfg, fmt->pad, fmt->which); + + return 0; +} + +static int xtpg_set_format(struct v4l2_subdev *subdev, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *fmt) +{ + struct xtpg_device *xtpg = to_tpg(subdev); + struct v4l2_mbus_framefmt *__format; + u32 bayer_phase; + + __format = __xtpg_get_pad_format(xtpg, cfg, fmt->pad, fmt->which); + + /* In two pads mode the source pad format is always identical to the + * sink pad format. + */ + if (xtpg->npads == 2 && fmt->pad == 1) { + fmt->format = *__format; + return 0; + } + + /* Bayer phase is configurable at runtime */ + if (xtpg->bayer) { + bayer_phase = xtpg_get_bayer_phase(fmt->format.code); + if (bayer_phase != XTPG_BAYER_PHASE_OFF) + __format->code = fmt->format.code; + } + + xvip_set_format_size(__format, fmt); + + fmt->format = *__format; + + /* Propagate the format to the source pad. */ + if (xtpg->npads == 2) { + __format = __xtpg_get_pad_format(xtpg, cfg, 1, fmt->which); + *__format = fmt->format; + } + + return 0; +} + +/* ----------------------------------------------------------------------------- + * V4L2 Subdevice Operations + */ + +static int xtpg_enum_frame_size(struct v4l2_subdev *subdev, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_frame_size_enum *fse) +{ + struct v4l2_mbus_framefmt *format; + + format = v4l2_subdev_get_try_format(subdev, cfg, fse->pad); + + if (fse->index || fse->code != format->code) + return -EINVAL; + + /* Min / max values for pad 0 is always fixed in both one and two pads + * modes. In two pads mode, the source pad(= 1) size is identical to + * the sink pad size */ + if (fse->pad == 0) { + fse->min_width = XVIP_MIN_WIDTH; + fse->max_width = XVIP_MAX_WIDTH; + fse->min_height = XVIP_MIN_HEIGHT; + fse->max_height = XVIP_MAX_HEIGHT; + } else { + fse->min_width = format->width; + fse->max_width = format->width; + fse->min_height = format->height; + fse->max_height = format->height; + } + + return 0; +} + +static int xtpg_open(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh) +{ + struct xtpg_device *xtpg = to_tpg(subdev); + struct v4l2_mbus_framefmt *format; + + format = v4l2_subdev_get_try_format(subdev, fh->pad, 0); + *format = xtpg->default_format; + + if (xtpg->npads == 2) { + format = v4l2_subdev_get_try_format(subdev, fh->pad, 1); + *format = xtpg->default_format; + } + + return 0; +} + +static int xtpg_close(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh) +{ + return 0; +} + +static int xtpg_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct xtpg_device *xtpg = container_of(ctrl->handler, + struct xtpg_device, + ctrl_handler); + switch (ctrl->id) { + case V4L2_CID_TEST_PATTERN: + xvip_clr_and_set(&xtpg->xvip, XTPG_PATTERN_CONTROL, + XTPG_PATTERN_MASK, ctrl->val); + return 0; + case V4L2_CID_XILINX_TPG_CROSS_HAIRS: + xvip_clr_or_set(&xtpg->xvip, XTPG_PATTERN_CONTROL, + XTPG_PATTERN_CONTROL_CROSS_HAIRS, ctrl->val); + return 0; + case V4L2_CID_XILINX_TPG_MOVING_BOX: + xvip_clr_or_set(&xtpg->xvip, XTPG_PATTERN_CONTROL, + XTPG_PATTERN_CONTROL_MOVING_BOX, ctrl->val); + return 0; + case V4L2_CID_XILINX_TPG_COLOR_MASK: + xvip_clr_and_set(&xtpg->xvip, XTPG_PATTERN_CONTROL, + XTPG_PATTERN_CONTROL_COLOR_MASK_MASK, + ctrl->val << + XTPG_PATTERN_CONTROL_COLOR_MASK_SHIFT); + return 0; + case V4L2_CID_XILINX_TPG_STUCK_PIXEL: + xvip_clr_or_set(&xtpg->xvip, XTPG_PATTERN_CONTROL, + XTPG_PATTERN_CONTROL_STUCK_PIXEL, ctrl->val); + return 0; + case V4L2_CID_XILINX_TPG_NOISE: + xvip_clr_or_set(&xtpg->xvip, XTPG_PATTERN_CONTROL, + XTPG_PATTERN_CONTROL_NOISE, ctrl->val); + return 0; + case V4L2_CID_XILINX_TPG_MOTION: + xvip_clr_or_set(&xtpg->xvip, XTPG_PATTERN_CONTROL, + XTPG_PATTERN_CONTROL_MOTION, ctrl->val); + return 0; + case V4L2_CID_XILINX_TPG_MOTION_SPEED: + xvip_write(&xtpg->xvip, XTPG_MOTION_SPEED, ctrl->val); + return 0; + case V4L2_CID_XILINX_TPG_CROSS_HAIR_ROW: + xvip_clr_and_set(&xtpg->xvip, XTPG_CROSS_HAIRS, + XTPG_CROSS_HAIRS_ROW_MASK, + ctrl->val << XTPG_CROSS_HAIRS_ROW_SHIFT); + return 0; + case V4L2_CID_XILINX_TPG_CROSS_HAIR_COLUMN: + xvip_clr_and_set(&xtpg->xvip, XTPG_CROSS_HAIRS, + XTPG_CROSS_HAIRS_COLUMN_MASK, + ctrl->val << XTPG_CROSS_HAIRS_COLUMN_SHIFT); + return 0; + case V4L2_CID_XILINX_TPG_ZPLATE_HOR_START: + xvip_clr_and_set(&xtpg->xvip, XTPG_ZPLATE_HOR_CONTROL, + XTPG_ZPLATE_START_MASK, + ctrl->val << XTPG_ZPLATE_START_SHIFT); + return 0; + case V4L2_CID_XILINX_TPG_ZPLATE_HOR_SPEED: + xvip_clr_and_set(&xtpg->xvip, XTPG_ZPLATE_HOR_CONTROL, + XTPG_ZPLATE_SPEED_MASK, + ctrl->val << XTPG_ZPLATE_SPEED_SHIFT); + return 0; + case V4L2_CID_XILINX_TPG_ZPLATE_VER_START: + xvip_clr_and_set(&xtpg->xvip, XTPG_ZPLATE_VER_CONTROL, + XTPG_ZPLATE_START_MASK, + ctrl->val << XTPG_ZPLATE_START_SHIFT); + return 0; + case V4L2_CID_XILINX_TPG_ZPLATE_VER_SPEED: + xvip_clr_and_set(&xtpg->xvip, XTPG_ZPLATE_VER_CONTROL, + XTPG_ZPLATE_SPEED_MASK, + ctrl->val << XTPG_ZPLATE_SPEED_SHIFT); + return 0; + case V4L2_CID_XILINX_TPG_BOX_SIZE: + xvip_write(&xtpg->xvip, XTPG_BOX_SIZE, ctrl->val); + return 0; + case V4L2_CID_XILINX_TPG_BOX_COLOR: + xvip_write(&xtpg->xvip, XTPG_BOX_COLOR, ctrl->val); + return 0; + case V4L2_CID_XILINX_TPG_STUCK_PIXEL_THRESH: + xvip_write(&xtpg->xvip, XTPG_STUCK_PIXEL_THRESH, ctrl->val); + return 0; + case V4L2_CID_XILINX_TPG_NOISE_GAIN: + xvip_write(&xtpg->xvip, XTPG_NOISE_GAIN, ctrl->val); + return 0; + } + + return 0; +} + +static const struct v4l2_ctrl_ops xtpg_ctrl_ops = { + .s_ctrl = xtpg_s_ctrl, +}; + +static struct v4l2_subdev_core_ops xtpg_core_ops = { +}; + +static struct v4l2_subdev_video_ops xtpg_video_ops = { + .s_stream = xtpg_s_stream, +}; + +static struct v4l2_subdev_pad_ops xtpg_pad_ops = { + .enum_mbus_code = xvip_enum_mbus_code, + .enum_frame_size = xtpg_enum_frame_size, + .get_fmt = xtpg_get_format, + .set_fmt = xtpg_set_format, +}; + +static struct v4l2_subdev_ops xtpg_ops = { + .core = &xtpg_core_ops, + .video = &xtpg_video_ops, + .pad = &xtpg_pad_ops, +}; + +static const struct v4l2_subdev_internal_ops xtpg_internal_ops = { + .open = xtpg_open, + .close = xtpg_close, +}; + +/* + * Control Config + */ + +static const char *const xtpg_pattern_strings[] = { + "Passthrough", + "Horizontal Ramp", + "Vertical Ramp", + "Temporal Ramp", + "Solid Red", + "Solid Green", + "Solid Blue", + "Solid Black", + "Solid White", + "Color Bars", + "Zone Plate", + "Tartan Color Bars", + "Cross Hatch", + "None", + "Vertical/Horizontal Ramps", + "Black/White Checker Board", +}; + +static struct v4l2_ctrl_config xtpg_ctrls[] = { + { + .ops = &xtpg_ctrl_ops, + .id = V4L2_CID_XILINX_TPG_CROSS_HAIRS, + .name = "Test Pattern: Cross Hairs", + .type = V4L2_CTRL_TYPE_BOOLEAN, + .min = false, + .max = true, + .step = 1, + .def = 0, + }, { + .ops = &xtpg_ctrl_ops, + .id = V4L2_CID_XILINX_TPG_MOVING_BOX, + .name = "Test Pattern: Moving Box", + .type = V4L2_CTRL_TYPE_BOOLEAN, + .min = false, + .max = true, + .step = 1, + .def = 0, + }, { + .ops = &xtpg_ctrl_ops, + .id = V4L2_CID_XILINX_TPG_COLOR_MASK, + .name = "Test Pattern: Color Mask", + .type = V4L2_CTRL_TYPE_BITMASK, + .min = 0, + .max = 0xf, + .def = 0, + }, { + .ops = &xtpg_ctrl_ops, + .id = V4L2_CID_XILINX_TPG_STUCK_PIXEL, + .name = "Test Pattern: Stuck Pixel", + .type = V4L2_CTRL_TYPE_BOOLEAN, + .min = false, + .max = true, + .step = 1, + .def = 0, + }, { + .ops = &xtpg_ctrl_ops, + .id = V4L2_CID_XILINX_TPG_NOISE, + .name = "Test Pattern: Noise", + .type = V4L2_CTRL_TYPE_BOOLEAN, + .min = false, + .max = true, + .step = 1, + .def = 0, + }, { + .ops = &xtpg_ctrl_ops, + .id = V4L2_CID_XILINX_TPG_MOTION, + .name = "Test Pattern: Motion", + .type = V4L2_CTRL_TYPE_BOOLEAN, + .min = false, + .max = true, + .step = 1, + .def = 0, + }, { + .ops = &xtpg_ctrl_ops, + .id = V4L2_CID_XILINX_TPG_MOTION_SPEED, + .name = "Test Pattern: Motion Speed", + .type = V4L2_CTRL_TYPE_INTEGER, + .min = 0, + .max = (1 << 8) - 1, + .step = 1, + .def = 4, + .flags = V4L2_CTRL_FLAG_SLIDER, + }, { + .ops = &xtpg_ctrl_ops, + .id = V4L2_CID_XILINX_TPG_CROSS_HAIR_ROW, + .name = "Test Pattern: Cross Hairs Row", + .type = V4L2_CTRL_TYPE_INTEGER, + .min = 0, + .max = (1 << 12) - 1, + .step = 1, + .def = 0x64, + .flags = V4L2_CTRL_FLAG_SLIDER, + }, { + .ops = &xtpg_ctrl_ops, + .id = V4L2_CID_XILINX_TPG_CROSS_HAIR_COLUMN, + .name = "Test Pattern: Cross Hairs Column", + .type = V4L2_CTRL_TYPE_INTEGER, + .min = 0, + .max = (1 << 12) - 1, + .step = 1, + .def = 0x64, + .flags = V4L2_CTRL_FLAG_SLIDER, + }, { + .ops = &xtpg_ctrl_ops, + .id = V4L2_CID_XILINX_TPG_ZPLATE_HOR_START, + .name = "Test Pattern: Zplate Horizontal Start Pos", + .type = V4L2_CTRL_TYPE_INTEGER, + .min = 0, + .max = (1 << 16) - 1, + .step = 1, + .def = 0x1e, + .flags = V4L2_CTRL_FLAG_SLIDER, + }, { + .ops = &xtpg_ctrl_ops, + .id = V4L2_CID_XILINX_TPG_ZPLATE_HOR_SPEED, + .name = "Test Pattern: Zplate Horizontal Speed", + .type = V4L2_CTRL_TYPE_INTEGER, + .min = 0, + .max = (1 << 16) - 1, + .step = 1, + .def = 0, + .flags = V4L2_CTRL_FLAG_SLIDER, + }, { + .ops = &xtpg_ctrl_ops, + .id = V4L2_CID_XILINX_TPG_ZPLATE_VER_START, + .name = "Test Pattern: Zplate Vertical Start Pos", + .type = V4L2_CTRL_TYPE_INTEGER, + .min = 0, + .max = (1 << 16) - 1, + .step = 1, + .def = 1, + .flags = V4L2_CTRL_FLAG_SLIDER, + }, { + .ops = &xtpg_ctrl_ops, + .id = V4L2_CID_XILINX_TPG_ZPLATE_VER_SPEED, + .name = "Test Pattern: Zplate Vertical Speed", + .type = V4L2_CTRL_TYPE_INTEGER, + .min = 0, + .max = (1 << 16) - 1, + .step = 1, + .def = 0, + .flags = V4L2_CTRL_FLAG_SLIDER, + }, { + .ops = &xtpg_ctrl_ops, + .id = V4L2_CID_XILINX_TPG_BOX_SIZE, + .name = "Test Pattern: Box Size", + .type = V4L2_CTRL_TYPE_INTEGER, + .min = 0, + .max = (1 << 12) - 1, + .step = 1, + .def = 0x32, + .flags = V4L2_CTRL_FLAG_SLIDER, + }, { + .ops = &xtpg_ctrl_ops, + .id = V4L2_CID_XILINX_TPG_BOX_COLOR, + .name = "Test Pattern: Box Color(RGB)", + .type = V4L2_CTRL_TYPE_INTEGER, + .min = 0, + .max = (1 << 24) - 1, + .step = 1, + .def = 0, + }, { + .ops = &xtpg_ctrl_ops, + .id = V4L2_CID_XILINX_TPG_STUCK_PIXEL_THRESH, + .name = "Test Pattern: Stuck Pixel threshold", + .type = V4L2_CTRL_TYPE_INTEGER, + .min = 0, + .max = (1 << 16) - 1, + .step = 1, + .def = 0, + .flags = V4L2_CTRL_FLAG_SLIDER, + }, { + .ops = &xtpg_ctrl_ops, + .id = V4L2_CID_XILINX_TPG_NOISE_GAIN, + .name = "Test Pattern: Noise Gain", + .type = V4L2_CTRL_TYPE_INTEGER, + .min = 0, + .max = (1 << 8) - 1, + .step = 1, + .def = 0, + .flags = V4L2_CTRL_FLAG_SLIDER, + }, +}; + +/* ----------------------------------------------------------------------------- + * Media Operations + */ + +static const struct media_entity_operations xtpg_media_ops = { + .link_validate = v4l2_subdev_link_validate, +}; + +/* ----------------------------------------------------------------------------- + * Power Management + */ + +static int __maybe_unused xtpg_pm_suspend(struct device *dev) +{ + struct xtpg_device *xtpg = dev_get_drvdata(dev); + + xvip_suspend(&xtpg->xvip); + + return 0; +} + +static int __maybe_unused xtpg_pm_resume(struct device *dev) +{ + struct xtpg_device *xtpg = dev_get_drvdata(dev); + + xvip_resume(&xtpg->xvip); + + return 0; +} + +/* ----------------------------------------------------------------------------- + * Platform Device Driver + */ + +static int xtpg_parse_of(struct xtpg_device *xtpg) +{ + struct device *dev = xtpg->xvip.dev; + struct device_node *node = xtpg->xvip.dev->of_node; + struct device_node *ports; + struct device_node *port; + unsigned int nports = 0; + bool has_endpoint = false; + + ports = of_get_child_by_name(node, "ports"); + if (ports == NULL) + ports = node; + + for_each_child_of_node(ports, port) { + const struct xvip_video_format *format; + struct device_node *endpoint; + + if (!port->name || of_node_cmp(port->name, "port")) + continue; + + format = xvip_of_get_format(port); + if (IS_ERR(format)) { + dev_err(dev, "invalid format in DT"); + return PTR_ERR(format); + } + + /* Get and check the format description */ + if (!xtpg->vip_format) { + xtpg->vip_format = format; + } else if (xtpg->vip_format != format) { + dev_err(dev, "in/out format mismatch in DT"); + return -EINVAL; + } + + if (nports == 0) { + endpoint = of_get_next_child(port, NULL); + if (endpoint) + has_endpoint = true; + of_node_put(endpoint); + } + + /* Count the number of ports. */ + nports++; + } + + if (nports != 1 && nports != 2) { + dev_err(dev, "invalid number of ports %u\n", nports); + return -EINVAL; + } + + xtpg->npads = nports; + if (nports == 2 && has_endpoint) + xtpg->has_input = true; + + return 0; +} + +static int xtpg_probe(struct platform_device *pdev) +{ + struct v4l2_subdev *subdev; + struct xtpg_device *xtpg; + u32 i, bayer_phase; + int ret; + + xtpg = devm_kzalloc(&pdev->dev, sizeof(*xtpg), GFP_KERNEL); + if (!xtpg) + return -ENOMEM; + + xtpg->xvip.dev = &pdev->dev; + + ret = xtpg_parse_of(xtpg); + if (ret < 0) + return ret; + + ret = xvip_init_resources(&xtpg->xvip); + if (ret < 0) + return ret; + + xtpg->vtmux_gpio = devm_gpiod_get_optional(&pdev->dev, "timing", + GPIOD_OUT_HIGH); + if (IS_ERR(xtpg->vtmux_gpio)) { + ret = PTR_ERR(xtpg->vtmux_gpio); + goto error_resource; + } + + xtpg->vtc = xvtc_of_get(pdev->dev.of_node); + if (IS_ERR(xtpg->vtc)) { + ret = PTR_ERR(xtpg->vtc); + goto error_resource; + } + + /* Reset and initialize the core */ + xvip_reset(&xtpg->xvip); + + /* Initialize V4L2 subdevice and media entity. Pad numbers depend on the + * number of pads. + */ + if (xtpg->npads == 2) { + xtpg->pads[0].flags = MEDIA_PAD_FL_SINK; + xtpg->pads[1].flags = MEDIA_PAD_FL_SOURCE; + } else { + xtpg->pads[0].flags = MEDIA_PAD_FL_SOURCE; + } + + /* Initialize the default format */ + xtpg->default_format.code = xtpg->vip_format->code; + xtpg->default_format.field = V4L2_FIELD_NONE; + xtpg->default_format.colorspace = V4L2_COLORSPACE_SRGB; + xvip_get_frame_size(&xtpg->xvip, &xtpg->default_format); + + bayer_phase = xtpg_get_bayer_phase(xtpg->vip_format->code); + if (bayer_phase != XTPG_BAYER_PHASE_OFF) + xtpg->bayer = true; + + xtpg->formats[0] = xtpg->default_format; + if (xtpg->npads == 2) + xtpg->formats[1] = xtpg->default_format; + + /* Initialize V4L2 subdevice and media entity */ + subdev = &xtpg->xvip.subdev; + v4l2_subdev_init(subdev, &xtpg_ops); + subdev->dev = &pdev->dev; + subdev->internal_ops = &xtpg_internal_ops; + strlcpy(subdev->name, dev_name(&pdev->dev), sizeof(subdev->name)); + v4l2_set_subdevdata(subdev, xtpg); + subdev->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + subdev->entity.ops = &xtpg_media_ops; + + ret = media_entity_init(&subdev->entity, xtpg->npads, xtpg->pads, 0); + if (ret < 0) + goto error; + + v4l2_ctrl_handler_init(&xtpg->ctrl_handler, 3 + ARRAY_SIZE(xtpg_ctrls)); + + xtpg->vblank = v4l2_ctrl_new_std(&xtpg->ctrl_handler, &xtpg_ctrl_ops, + V4L2_CID_VBLANK, XTPG_MIN_VBLANK, + XTPG_MAX_VBLANK, 1, 100); + xtpg->hblank = v4l2_ctrl_new_std(&xtpg->ctrl_handler, &xtpg_ctrl_ops, + V4L2_CID_HBLANK, XTPG_MIN_HBLANK, + XTPG_MAX_HBLANK, 1, 100); + xtpg->pattern = v4l2_ctrl_new_std_menu_items(&xtpg->ctrl_handler, + &xtpg_ctrl_ops, V4L2_CID_TEST_PATTERN, + ARRAY_SIZE(xtpg_pattern_strings) - 1, + 1, 9, xtpg_pattern_strings); + + for (i = 0; i < ARRAY_SIZE(xtpg_ctrls); i++) + v4l2_ctrl_new_custom(&xtpg->ctrl_handler, &xtpg_ctrls[i], NULL); + + if (xtpg->ctrl_handler.error) { + dev_err(&pdev->dev, "failed to add controls\n"); + ret = xtpg->ctrl_handler.error; + goto error; + } + subdev->ctrl_handler = &xtpg->ctrl_handler; + + xtpg_update_pattern_control(xtpg, true, true); + + ret = v4l2_ctrl_handler_setup(&xtpg->ctrl_handler); + if (ret < 0) { + dev_err(&pdev->dev, "failed to set controls\n"); + goto error; + } + + platform_set_drvdata(pdev, xtpg); + + xvip_print_version(&xtpg->xvip); + + ret = v4l2_async_register_subdev(subdev); + if (ret < 0) { + dev_err(&pdev->dev, "failed to register subdev\n"); + goto error; + } + + return 0; + +error: + v4l2_ctrl_handler_free(&xtpg->ctrl_handler); + media_entity_cleanup(&subdev->entity); + xvtc_put(xtpg->vtc); +error_resource: + xvip_cleanup_resources(&xtpg->xvip); + return ret; +} + +static int xtpg_remove(struct platform_device *pdev) +{ + struct xtpg_device *xtpg = platform_get_drvdata(pdev); + struct v4l2_subdev *subdev = &xtpg->xvip.subdev; + + v4l2_async_unregister_subdev(subdev); + v4l2_ctrl_handler_free(&xtpg->ctrl_handler); + media_entity_cleanup(&subdev->entity); + + xvip_cleanup_resources(&xtpg->xvip); + + return 0; +} + +static SIMPLE_DEV_PM_OPS(xtpg_pm_ops, xtpg_pm_suspend, xtpg_pm_resume); + +static const struct of_device_id xtpg_of_id_table[] = { + { .compatible = "xlnx,v-tpg-5.0" }, + { } +}; +MODULE_DEVICE_TABLE(of, xtpg_of_id_table); + +static struct platform_driver xtpg_driver = { + .driver = { + .name = "xilinx-tpg", + .pm = &xtpg_pm_ops, + .of_match_table = xtpg_of_id_table, + }, + .probe = xtpg_probe, + .remove = xtpg_remove, +}; + +module_platform_driver(xtpg_driver); + +MODULE_AUTHOR("Laurent Pinchart "); +MODULE_DESCRIPTION("Xilinx Test Pattern Generator Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/include/uapi/linux/Kbuild b/include/uapi/linux/Kbuild index 68ceb97c458c..d4cfc17cc414 100644 --- a/include/uapi/linux/Kbuild +++ b/include/uapi/linux/Kbuild @@ -446,5 +446,6 @@ header-y += wireless.h header-y += x25.h header-y += xattr.h header-y += xfrm.h +header-y += xilinx-v4l2-controls.h header-y += zorro.h header-y += zorro_ids.h diff --git a/include/uapi/linux/xilinx-v4l2-controls.h b/include/uapi/linux/xilinx-v4l2-controls.h new file mode 100644 index 000000000000..fb495b91e800 --- /dev/null +++ b/include/uapi/linux/xilinx-v4l2-controls.h @@ -0,0 +1,73 @@ +/* + * Xilinx Controls Header + * + * Copyright (C) 2013-2015 Ideas on Board + * Copyright (C) 2013-2015 Xilinx, Inc. + * + * Contacts: Hyun Kwon + * Laurent Pinchart + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef __UAPI_XILINX_V4L2_CONTROLS_H__ +#define __UAPI_XILINX_V4L2_CONTROLS_H__ + +#include + +#define V4L2_CID_XILINX_OFFSET 0xc000 +#define V4L2_CID_XILINX_BASE (V4L2_CID_USER_BASE + V4L2_CID_XILINX_OFFSET) + +/* + * Private Controls for Xilinx Video IPs + */ + +/* + * Xilinx TPG Video IP + */ + +#define V4L2_CID_XILINX_TPG (V4L2_CID_USER_BASE + 0xc000) + +/* Draw cross hairs */ +#define V4L2_CID_XILINX_TPG_CROSS_HAIRS (V4L2_CID_XILINX_TPG + 1) +/* Enable a moving box */ +#define V4L2_CID_XILINX_TPG_MOVING_BOX (V4L2_CID_XILINX_TPG + 2) +/* Mask out a color component */ +#define V4L2_CID_XILINX_TPG_COLOR_MASK (V4L2_CID_XILINX_TPG + 3) +/* Enable a stuck pixel feature */ +#define V4L2_CID_XILINX_TPG_STUCK_PIXEL (V4L2_CID_XILINX_TPG + 4) +/* Enable a noisy output */ +#define V4L2_CID_XILINX_TPG_NOISE (V4L2_CID_XILINX_TPG + 5) +/* Enable the motion feature */ +#define V4L2_CID_XILINX_TPG_MOTION (V4L2_CID_XILINX_TPG + 6) +/* Configure the motion speed of moving patterns */ +#define V4L2_CID_XILINX_TPG_MOTION_SPEED (V4L2_CID_XILINX_TPG + 7) +/* The row of horizontal cross hair location */ +#define V4L2_CID_XILINX_TPG_CROSS_HAIR_ROW (V4L2_CID_XILINX_TPG + 8) +/* The colum of vertical cross hair location */ +#define V4L2_CID_XILINX_TPG_CROSS_HAIR_COLUMN (V4L2_CID_XILINX_TPG + 9) +/* Set starting point of sine wave for horizontal component */ +#define V4L2_CID_XILINX_TPG_ZPLATE_HOR_START (V4L2_CID_XILINX_TPG + 10) +/* Set speed of the horizontal component */ +#define V4L2_CID_XILINX_TPG_ZPLATE_HOR_SPEED (V4L2_CID_XILINX_TPG + 11) +/* Set starting point of sine wave for vertical component */ +#define V4L2_CID_XILINX_TPG_ZPLATE_VER_START (V4L2_CID_XILINX_TPG + 12) +/* Set speed of the vertical component */ +#define V4L2_CID_XILINX_TPG_ZPLATE_VER_SPEED (V4L2_CID_XILINX_TPG + 13) +/* Moving box size */ +#define V4L2_CID_XILINX_TPG_BOX_SIZE (V4L2_CID_XILINX_TPG + 14) +/* Moving box color */ +#define V4L2_CID_XILINX_TPG_BOX_COLOR (V4L2_CID_XILINX_TPG + 15) +/* Upper limit count of generated stuck pixels */ +#define V4L2_CID_XILINX_TPG_STUCK_PIXEL_THRESH (V4L2_CID_XILINX_TPG + 16) +/* Noise level */ +#define V4L2_CID_XILINX_TPG_NOISE_GAIN (V4L2_CID_XILINX_TPG + 17) + +#endif /* __UAPI_XILINX_V4L2_CONTROLS_H__ */ -- cgit v1.2.3 From 87b1675634e1e9f1570445c55604204771ac9078 Mon Sep 17 00:00:00 2001 From: Herbert Xu Date: Thu, 2 Apr 2015 22:39:40 +0800 Subject: crypto: api - Change crypto_unregister_instance argument type This patch makes crypto_unregister_instance take a crypto_instance instead of a crypto_alg. This allows us to remove a duplicate CRYPTO_ALG_INSTANCE check in crypto_unregister_instance. Signed-off-by: Herbert Xu --- crypto/algapi.c | 8 ++------ crypto/crypto_user.c | 2 +- include/crypto/algapi.h | 2 +- 3 files changed, 4 insertions(+), 8 deletions(-) (limited to 'include') diff --git a/crypto/algapi.c b/crypto/algapi.c index 0f1976eceb27..f1d0307b1d08 100644 --- a/crypto/algapi.c +++ b/crypto/algapi.c @@ -551,17 +551,13 @@ err: } EXPORT_SYMBOL_GPL(crypto_register_instance); -int crypto_unregister_instance(struct crypto_alg *alg) +int crypto_unregister_instance(struct crypto_instance *inst) { - struct crypto_instance *inst = (void *)alg; LIST_HEAD(list); - if (!(alg->cra_flags & CRYPTO_ALG_INSTANCE)) - return -EINVAL; - down_write(&crypto_alg_sem); - crypto_remove_spawns(alg, &list, NULL); + crypto_remove_spawns(&inst->alg, &list, NULL); crypto_remove_instance(inst, &list); up_write(&crypto_alg_sem); diff --git a/crypto/crypto_user.c b/crypto/crypto_user.c index c5148a35ae0a..eab249723830 100644 --- a/crypto/crypto_user.c +++ b/crypto/crypto_user.c @@ -316,7 +316,7 @@ static int crypto_del_alg(struct sk_buff *skb, struct nlmsghdr *nlh, if (atomic_read(&alg->cra_refcnt) != 1) return -EBUSY; - return crypto_unregister_instance(alg); + return crypto_unregister_instance((struct crypto_instance *)alg); } static struct crypto_alg *crypto_user_skcipher_alg(const char *name, u32 type, diff --git a/include/crypto/algapi.h b/include/crypto/algapi.h index 623a59c1ff5a..0ecb7688af71 100644 --- a/include/crypto/algapi.h +++ b/include/crypto/algapi.h @@ -137,7 +137,7 @@ struct crypto_template *crypto_lookup_template(const char *name); int crypto_register_instance(struct crypto_template *tmpl, struct crypto_instance *inst); -int crypto_unregister_instance(struct crypto_alg *alg); +int crypto_unregister_instance(struct crypto_instance *inst); int crypto_init_spawn(struct crypto_spawn *spawn, struct crypto_alg *alg, struct crypto_instance *inst, u32 mask); -- cgit v1.2.3 From cae80b305e1c3944746dd93e33e9b2ccd5a490c1 Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Fri, 3 Apr 2015 09:04:04 -0400 Subject: locks: change lm_get_owner and lm_put_owner prototypes The current prototypes for these operations are somewhat awkward as they deal with fl_owners but take struct file_lock arguments. In the future, we'll want to be able to take references without necessarily dealing with a struct file_lock. Change them to take fl_owner_t arguments instead and have the callers deal with assigning the values to the file_lock structs. Signed-off-by: Jeff Layton --- fs/locks.c | 8 +++++--- fs/nfsd/nfs4state.c | 18 ++++++++++-------- include/linux/fs.h | 4 ++-- 3 files changed, 17 insertions(+), 13 deletions(-) (limited to 'include') diff --git a/fs/locks.c b/fs/locks.c index 54a79883a7f9..3ebaafb4c587 100644 --- a/fs/locks.c +++ b/fs/locks.c @@ -276,8 +276,10 @@ void locks_release_private(struct file_lock *fl) } if (fl->fl_lmops) { - if (fl->fl_lmops->lm_put_owner) - fl->fl_lmops->lm_put_owner(fl); + if (fl->fl_lmops->lm_put_owner) { + fl->fl_lmops->lm_put_owner(fl->fl_owner); + fl->fl_owner = NULL; + } fl->fl_lmops = NULL; } } @@ -333,7 +335,7 @@ void locks_copy_conflock(struct file_lock *new, struct file_lock *fl) if (fl->fl_lmops) { if (fl->fl_lmops->lm_get_owner) - fl->fl_lmops->lm_get_owner(new, fl); + fl->fl_lmops->lm_get_owner(fl->fl_owner); } } EXPORT_SYMBOL(locks_copy_conflock); diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index 8ba1d888f1e6..326a545ea7b2 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -4932,20 +4932,22 @@ nfs4_transform_lock_offset(struct file_lock *lock) lock->fl_end = OFFSET_MAX; } -static void nfsd4_fl_get_owner(struct file_lock *dst, struct file_lock *src) +static fl_owner_t +nfsd4_fl_get_owner(fl_owner_t owner) { - struct nfs4_lockowner *lo = (struct nfs4_lockowner *)src->fl_owner; - dst->fl_owner = (fl_owner_t)lockowner(nfs4_get_stateowner(&lo->lo_owner)); + struct nfs4_lockowner *lo = (struct nfs4_lockowner *)owner; + + nfs4_get_stateowner(&lo->lo_owner); + return owner; } -static void nfsd4_fl_put_owner(struct file_lock *fl) +static void +nfsd4_fl_put_owner(fl_owner_t owner) { - struct nfs4_lockowner *lo = (struct nfs4_lockowner *)fl->fl_owner; + struct nfs4_lockowner *lo = (struct nfs4_lockowner *)owner; - if (lo) { + if (lo) nfs4_put_stateowner(&lo->lo_owner); - fl->fl_owner = NULL; - } } static const struct lock_manager_operations nfsd_posix_mng_ops = { diff --git a/include/linux/fs.h b/include/linux/fs.h index f4131e8ead74..e4111a29697e 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -893,8 +893,8 @@ struct file_lock_operations { struct lock_manager_operations { int (*lm_compare_owner)(struct file_lock *, struct file_lock *); unsigned long (*lm_owner_key)(struct file_lock *); - void (*lm_get_owner)(struct file_lock *, struct file_lock *); - void (*lm_put_owner)(struct file_lock *); + fl_owner_t (*lm_get_owner)(fl_owner_t); + void (*lm_put_owner)(fl_owner_t); void (*lm_notify)(struct file_lock *); /* unblock callback */ int (*lm_grant)(struct file_lock *, int); bool (*lm_break)(struct file_lock *); -- cgit v1.2.3 From 9b3075c59f858d64478f46a15daa6ecda3cf2318 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Wed, 1 Apr 2015 01:37:05 -0700 Subject: nfsd: add NFSEXP_PNFS to the exflags array Signed-off-by: J. Bruce Fields --- fs/nfsd/export.c | 1 + include/uapi/linux/nfsd/export.h | 3 +++ 2 files changed, 4 insertions(+) (limited to 'include') diff --git a/fs/nfsd/export.c b/fs/nfsd/export.c index 2911f519fe64..900c3ae94adc 100644 --- a/fs/nfsd/export.c +++ b/fs/nfsd/export.c @@ -1158,6 +1158,7 @@ static struct flags { { NFSEXP_NOSUBTREECHECK, {"no_subtree_check", ""}}, { NFSEXP_NOAUTHNLM, {"insecure_locks", ""}}, { NFSEXP_V4ROOT, {"v4root", ""}}, + { NFSEXP_PNFS, {"pnfs", ""}}, { 0, {"", ""}} }; diff --git a/include/uapi/linux/nfsd/export.h b/include/uapi/linux/nfsd/export.h index d3bd6ffec041..0df7bd5d2fb1 100644 --- a/include/uapi/linux/nfsd/export.h +++ b/include/uapi/linux/nfsd/export.h @@ -21,6 +21,9 @@ /* * Export flags. + * + * Please update the expflags[] array in fs/nfsd/export.c when adding + * a new flag. */ #define NFSEXP_READONLY 0x0001 #define NFSEXP_INSECURE_PORT 0x0002 -- cgit v1.2.3 From aadc3780f31865edc84c587ab718a33a8eeeb09d Mon Sep 17 00:00:00 2001 From: Dexuan Cui Date: Fri, 27 Mar 2015 09:10:10 -0700 Subject: hv: remove the per-channel workqueue It's not necessary any longer, since we can safely run the blocking message handlers in vmbus_connection.work_queue now. Signed-off-by: Dexuan Cui Cc: K. Y. Srinivasan Signed-off-by: K. Y. Srinivasan Signed-off-by: Greg Kroah-Hartman --- drivers/hv/channel_mgmt.c | 30 +----------------------------- include/linux/hyperv.h | 3 --- 2 files changed, 1 insertion(+), 32 deletions(-) (limited to 'include') diff --git a/drivers/hv/channel_mgmt.c b/drivers/hv/channel_mgmt.c index d69864d4a4d7..0eeb1b3bc048 100644 --- a/drivers/hv/channel_mgmt.c +++ b/drivers/hv/channel_mgmt.c @@ -147,43 +147,15 @@ static struct vmbus_channel *alloc_channel(void) INIT_LIST_HEAD(&channel->sc_list); INIT_LIST_HEAD(&channel->percpu_list); - channel->controlwq = alloc_workqueue("hv_vmbus_ctl/%d", WQ_MEM_RECLAIM, - 1, channel->id); - if (!channel->controlwq) { - kfree(channel); - return NULL; - } - return channel; } -/* - * release_hannel - Release the vmbus channel object itself - */ -static void release_channel(struct work_struct *work) -{ - struct vmbus_channel *channel = container_of(work, - struct vmbus_channel, - work); - - destroy_workqueue(channel->controlwq); - - kfree(channel); -} - /* * free_channel - Release the resources used by the vmbus channel object */ static void free_channel(struct vmbus_channel *channel) { - - /* - * We have to release the channel's workqueue/thread in the vmbus's - * workqueue/thread context - * ie we can't destroy ourselves. - */ - INIT_WORK(&channel->work, release_channel); - queue_work(vmbus_connection.work_queue, &channel->work); + kfree(channel); } static void percpu_channel_enq(void *arg) diff --git a/include/linux/hyperv.h b/include/linux/hyperv.h index 80e444bfc9dc..902c37aef67e 100644 --- a/include/linux/hyperv.h +++ b/include/linux/hyperv.h @@ -653,8 +653,6 @@ struct vmbus_channel { struct hv_device *device_obj; - struct work_struct work; - enum vmbus_channel_state state; struct vmbus_channel_offer_channel offermsg; @@ -675,7 +673,6 @@ struct vmbus_channel { struct hv_ring_buffer_info outbound; /* send to parent */ struct hv_ring_buffer_info inbound; /* receive from parent */ spinlock_t inbound_lock; - struct workqueue_struct *controlwq; struct vmbus_close_msg close_msg; -- cgit v1.2.3 From e79d8429aac95a5cbe4c235795c7cd554c91f924 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Fri, 3 Apr 2015 22:17:17 +1030 Subject: netdevice: document NETDEV_TX_BUSY deprecation. This paraphrases DaveM (and steals some of his words) explaining why a device shouldn't return NETDEV_TX_BUSY, even though it looks so inviting to driver authors. See http://www.spinics.net/lists/netdev/msg322350.html Inspired-by: David Miller Signed-off-by: Rusty Russell Signed-off-by: David S. Miller --- include/linux/netdevice.h | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 846a1f5bc9db..a710d22b174f 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -795,7 +795,10 @@ typedef u16 (*select_queue_fallback_t)(struct net_device *dev, * netdev_tx_t (*ndo_start_xmit)(struct sk_buff *skb, * struct net_device *dev); * Called when a packet needs to be transmitted. - * Must return NETDEV_TX_OK , NETDEV_TX_BUSY. + * Returns NETDEV_TX_OK. Can return NETDEV_TX_BUSY, but you should stop + * the queue before that can happen; it's for obsolete devices and weird + * corner cases, but the stack really does a non-trivial amount + * of useless work if you return NETDEV_TX_BUSY. * (can also return NETDEV_TX_LOCKED iff NETIF_F_LLTX) * Required can not be NULL. * -- cgit v1.2.3 From 2e7056c433216f406b90a003aa0ba42e19d3bdcf Mon Sep 17 00:00:00 2001 From: Alexander Duyck Date: Tue, 31 Mar 2015 14:19:10 -0700 Subject: jhash: Update jhash_[321]words functions to use correct initval Looking over the implementation for jhash2 and comparing it to jhash_3words I realized that the two hashes were in fact very different. Doing a bit of digging led me to "The new jhash implementation" in which lookup2 was supposed to have been replaced with lookup3. In reviewing the patch I noticed that jhash2 had originally initialized a and b to JHASH_GOLDENRATIO and c to initval, but after the patch a, b, and c were initialized to initval + (length << 2) + JHASH_INITVAL. However the changes in jhash_3words simply replaced the initialization of a and b with JHASH_INITVAL. This change corrects what I believe was an oversight so that a, b, and c in jhash_3words all have the same value added consisting of initval + (length << 2) + JHASH_INITVAL so that jhash2 and jhash_3words will now produce the same hash result given the same inputs. Fixes: 60d509c823cca ("The new jhash implementation") Signed-off-by: Alexander Duyck Signed-off-by: David S. Miller --- include/linux/jhash.h | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/include/linux/jhash.h b/include/linux/jhash.h index 47cb09edec1a..348c6f47e4cc 100644 --- a/include/linux/jhash.h +++ b/include/linux/jhash.h @@ -145,11 +145,11 @@ static inline u32 jhash2(const u32 *k, u32 length, u32 initval) } -/* jhash_3words - hash exactly 3, 2 or 1 word(s) */ -static inline u32 jhash_3words(u32 a, u32 b, u32 c, u32 initval) +/* __jhash_nwords - hash exactly 3, 2 or 1 word(s) */ +static inline u32 __jhash_nwords(u32 a, u32 b, u32 c, u32 initval) { - a += JHASH_INITVAL; - b += JHASH_INITVAL; + a += initval; + b += initval; c += initval; __jhash_final(a, b, c); @@ -157,14 +157,19 @@ static inline u32 jhash_3words(u32 a, u32 b, u32 c, u32 initval) return c; } +static inline u32 jhash_3words(u32 a, u32 b, u32 c, u32 initval) +{ + return __jhash_nwords(a, b, c, initval + JHASH_INITVAL + (3 << 2)); +} + static inline u32 jhash_2words(u32 a, u32 b, u32 initval) { - return jhash_3words(a, b, 0, initval); + return __jhash_nwords(a, b, 0, initval + JHASH_INITVAL + (2 << 2)); } static inline u32 jhash_1word(u32 a, u32 initval) { - return jhash_3words(a, 0, 0, initval); + return __jhash_nwords(a, 0, 0, initval + JHASH_INITVAL + (1 << 2)); } #endif /* _LINUX_JHASH_H */ -- cgit v1.2.3 From bcad57182425426dd4aa14deb27f97acb329f3cd Mon Sep 17 00:00:00 2001 From: Daniel Borkmann Date: Fri, 3 Apr 2015 20:52:24 +0200 Subject: ebpf: add skb->priority to offset map for usage in {cls, act}_bpf This adds the ability to read out the skb->priority from an eBPF program, so that it can be taken into account from a tc filter or action for the use-case where the priority is not being used to directly override the filter classification in a qdisc, but to tag traffic otherwise for the classifier; the priority can be assigned from various places incl. user space, in future we may also mangle it from an eBPF program. Signed-off-by: Daniel Borkmann Cc: Alexei Starovoitov Signed-off-by: David S. Miller --- include/uapi/linux/bpf.h | 1 + net/core/filter.c | 7 +++++++ 2 files changed, 8 insertions(+) (limited to 'include') diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h index 74aab6e0d964..0db8580f3cca 100644 --- a/include/uapi/linux/bpf.h +++ b/include/uapi/linux/bpf.h @@ -184,6 +184,7 @@ struct __sk_buff { __u32 vlan_present; __u32 vlan_tci; __u32 vlan_proto; + __u32 priority; }; #endif /* _UAPI__LINUX_BPF_H__ */ diff --git a/net/core/filter.c b/net/core/filter.c index 444a07e4f68d..955a7d77decd 100644 --- a/net/core/filter.c +++ b/net/core/filter.c @@ -1304,6 +1304,13 @@ static u32 sk_filter_convert_ctx_access(int dst_reg, int src_reg, int ctx_off, offsetof(struct sk_buff, vlan_proto)); break; + case offsetof(struct __sk_buff, priority): + BUILD_BUG_ON(FIELD_SIZEOF(struct sk_buff, priority) != 4); + + *insn++ = BPF_LDX_MEM(BPF_W, dst_reg, src_reg, + offsetof(struct sk_buff, priority)); + break; + case offsetof(struct __sk_buff, mark): return convert_skb_access(SKF_AD_MARK, dst_reg, src_reg, insn); -- cgit v1.2.3 From a3bebdce4135a44d09e96ba66c40797c8f9fa902 Mon Sep 17 00:00:00 2001 From: Stas Sergeev Date: Wed, 1 Apr 2015 20:30:31 +0300 Subject: add fixed_phy_update_state() - update state of fixed_phy Currently fixed_phy uses a callback to periodically poll the link state. This patch adds the fixed_phy_update_state() API. It solves the following problems: - On link state interrupt, MAC driver can't update status. Instead it needs to provide the callback to periodically query the HW about the link state. It is more efficient to update status after interrupt. - The callback needs to be unregistered before phy_disconnect(), or otherwise it will be called with net_dev==NULL. phy_disconnect() does not have enough info to unregister the callback automatically. - The callback needs to be registered before of_phy_connect() to avoid running with outdated state, but of_phy_connect() returns the phy_device pointer, which is needed to register the callback. Registering it before of_phy_connect() will therefore require a hack to get the pointer earlier. Overall, this addition makes the subsequent patch that implements SGMII link status for mvneta, much cleaner. CC: Florian Fainelli CC: netdev@vger.kernel.org CC: linux-kernel@vger.kernel.org Signed-off-by: Stas Sergeev Signed-off-by: David S. Miller --- drivers/net/phy/fixed_phy.c | 29 +++++++++++++++++++++++++++++ include/linux/phy_fixed.h | 9 +++++++++ 2 files changed, 38 insertions(+) (limited to 'include') diff --git a/drivers/net/phy/fixed_phy.c b/drivers/net/phy/fixed_phy.c index a08a3c78ba97..1960b46add65 100644 --- a/drivers/net/phy/fixed_phy.c +++ b/drivers/net/phy/fixed_phy.c @@ -183,6 +183,35 @@ int fixed_phy_set_link_update(struct phy_device *phydev, } EXPORT_SYMBOL_GPL(fixed_phy_set_link_update); +int fixed_phy_update_state(struct phy_device *phydev, + const struct fixed_phy_status *status, + const struct fixed_phy_status *changed) +{ + struct fixed_mdio_bus *fmb = &platform_fmb; + struct fixed_phy *fp; + + if (!phydev || !phydev->bus) + return -EINVAL; + + list_for_each_entry(fp, &fmb->phys, node) { + if (fp->addr == phydev->addr) { +#define _UPD(x) if (changed->x) \ + fp->status.x = status->x + _UPD(link); + _UPD(speed); + _UPD(duplex); + _UPD(pause); + _UPD(asym_pause); +#undef _UPD + fixed_phy_update_regs(fp); + return 0; + } + } + + return -ENOENT; +} +EXPORT_SYMBOL(fixed_phy_update_state); + int fixed_phy_add(unsigned int irq, int phy_addr, struct fixed_phy_status *status) { diff --git a/include/linux/phy_fixed.h b/include/linux/phy_fixed.h index 7e75bfe37cc7..fe5732d53eda 100644 --- a/include/linux/phy_fixed.h +++ b/include/linux/phy_fixed.h @@ -21,6 +21,9 @@ extern void fixed_phy_del(int phy_addr); extern int fixed_phy_set_link_update(struct phy_device *phydev, int (*link_update)(struct net_device *, struct fixed_phy_status *)); +extern int fixed_phy_update_state(struct phy_device *phydev, + const struct fixed_phy_status *status, + const struct fixed_phy_status *changed); #else static inline int fixed_phy_add(unsigned int irq, int phy_id, struct fixed_phy_status *status) @@ -43,6 +46,12 @@ static inline int fixed_phy_set_link_update(struct phy_device *phydev, { return -ENODEV; } +static inline int fixed_phy_update_state(struct phy_device *phydev, + const struct fixed_phy_status *status, + const struct fixed_phy_status *changed) +{ + return -ENODEV; +} #endif /* CONFIG_FIXED_PHY */ #endif /* __PHY_FIXED_H */ -- cgit v1.2.3 From 83bd147952ef5c8a99df27b8597ee21f2424348d Mon Sep 17 00:00:00 2001 From: Georgi Djakov Date: Wed, 18 Mar 2015 16:08:21 +0200 Subject: dt-bindings: Add #defines for MSM8916 clocks and resets Add clocks/resets defines for the global clock controller found on Qualcomm MSM8916 SoCs. Signed-off-by: Georgi Djakov Signed-off-by: Kumar Gala Signed-off-by: Olof Johansson --- .../devicetree/bindings/clock/qcom,gcc.txt | 1 + include/dt-bindings/clock/qcom,gcc-msm8916.h | 156 +++++++++++++++++++++ include/dt-bindings/reset/qcom,gcc-msm8916.h | 108 ++++++++++++++ 3 files changed, 265 insertions(+) create mode 100644 include/dt-bindings/clock/qcom,gcc-msm8916.h create mode 100644 include/dt-bindings/reset/qcom,gcc-msm8916.h (limited to 'include') diff --git a/Documentation/devicetree/bindings/clock/qcom,gcc.txt b/Documentation/devicetree/bindings/clock/qcom,gcc.txt index aba3d254e037..54c23f34f194 100644 --- a/Documentation/devicetree/bindings/clock/qcom,gcc.txt +++ b/Documentation/devicetree/bindings/clock/qcom,gcc.txt @@ -8,6 +8,7 @@ Required properties : "qcom,gcc-apq8084" "qcom,gcc-ipq8064" "qcom,gcc-msm8660" + "qcom,gcc-msm8916" "qcom,gcc-msm8960" "qcom,gcc-msm8974" "qcom,gcc-msm8974pro" diff --git a/include/dt-bindings/clock/qcom,gcc-msm8916.h b/include/dt-bindings/clock/qcom,gcc-msm8916.h new file mode 100644 index 000000000000..e430f644dd6c --- /dev/null +++ b/include/dt-bindings/clock/qcom,gcc-msm8916.h @@ -0,0 +1,156 @@ +/* + * Copyright 2015 Linaro Limited + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef _DT_BINDINGS_CLK_MSM_GCC_8916_H +#define _DT_BINDINGS_CLK_MSM_GCC_8916_H + +#define GPLL0 0 +#define GPLL0_VOTE 1 +#define BIMC_PLL 2 +#define BIMC_PLL_VOTE 3 +#define GPLL1 4 +#define GPLL1_VOTE 5 +#define GPLL2 6 +#define GPLL2_VOTE 7 +#define PCNOC_BFDCD_CLK_SRC 8 +#define SYSTEM_NOC_BFDCD_CLK_SRC 9 +#define CAMSS_AHB_CLK_SRC 10 +#define APSS_AHB_CLK_SRC 11 +#define CSI0_CLK_SRC 12 +#define CSI1_CLK_SRC 13 +#define GFX3D_CLK_SRC 14 +#define VFE0_CLK_SRC 15 +#define BLSP1_QUP1_I2C_APPS_CLK_SRC 16 +#define BLSP1_QUP1_SPI_APPS_CLK_SRC 17 +#define BLSP1_QUP2_I2C_APPS_CLK_SRC 18 +#define BLSP1_QUP2_SPI_APPS_CLK_SRC 19 +#define BLSP1_QUP3_I2C_APPS_CLK_SRC 20 +#define BLSP1_QUP3_SPI_APPS_CLK_SRC 21 +#define BLSP1_QUP4_I2C_APPS_CLK_SRC 22 +#define BLSP1_QUP4_SPI_APPS_CLK_SRC 23 +#define BLSP1_QUP5_I2C_APPS_CLK_SRC 24 +#define BLSP1_QUP5_SPI_APPS_CLK_SRC 25 +#define BLSP1_QUP6_I2C_APPS_CLK_SRC 26 +#define BLSP1_QUP6_SPI_APPS_CLK_SRC 27 +#define BLSP1_UART1_APPS_CLK_SRC 28 +#define BLSP1_UART2_APPS_CLK_SRC 29 +#define CCI_CLK_SRC 30 +#define CAMSS_GP0_CLK_SRC 31 +#define CAMSS_GP1_CLK_SRC 32 +#define JPEG0_CLK_SRC 33 +#define MCLK0_CLK_SRC 34 +#define MCLK1_CLK_SRC 35 +#define CSI0PHYTIMER_CLK_SRC 36 +#define CSI1PHYTIMER_CLK_SRC 37 +#define CPP_CLK_SRC 38 +#define CRYPTO_CLK_SRC 39 +#define GP1_CLK_SRC 40 +#define GP2_CLK_SRC 41 +#define GP3_CLK_SRC 42 +#define BYTE0_CLK_SRC 43 +#define ESC0_CLK_SRC 44 +#define MDP_CLK_SRC 45 +#define PCLK0_CLK_SRC 46 +#define VSYNC_CLK_SRC 47 +#define PDM2_CLK_SRC 48 +#define SDCC1_APPS_CLK_SRC 49 +#define SDCC2_APPS_CLK_SRC 50 +#define APSS_TCU_CLK_SRC 51 +#define USB_HS_SYSTEM_CLK_SRC 52 +#define VCODEC0_CLK_SRC 53 +#define GCC_BLSP1_AHB_CLK 54 +#define GCC_BLSP1_SLEEP_CLK 55 +#define GCC_BLSP1_QUP1_I2C_APPS_CLK 56 +#define GCC_BLSP1_QUP1_SPI_APPS_CLK 57 +#define GCC_BLSP1_QUP2_I2C_APPS_CLK 58 +#define GCC_BLSP1_QUP2_SPI_APPS_CLK 59 +#define GCC_BLSP1_QUP3_I2C_APPS_CLK 60 +#define GCC_BLSP1_QUP3_SPI_APPS_CLK 61 +#define GCC_BLSP1_QUP4_I2C_APPS_CLK 62 +#define GCC_BLSP1_QUP4_SPI_APPS_CLK 63 +#define GCC_BLSP1_QUP5_I2C_APPS_CLK 64 +#define GCC_BLSP1_QUP5_SPI_APPS_CLK 65 +#define GCC_BLSP1_QUP6_I2C_APPS_CLK 66 +#define GCC_BLSP1_QUP6_SPI_APPS_CLK 67 +#define GCC_BLSP1_UART1_APPS_CLK 68 +#define GCC_BLSP1_UART2_APPS_CLK 69 +#define GCC_BOOT_ROM_AHB_CLK 70 +#define GCC_CAMSS_CCI_AHB_CLK 71 +#define GCC_CAMSS_CCI_CLK 72 +#define GCC_CAMSS_CSI0_AHB_CLK 73 +#define GCC_CAMSS_CSI0_CLK 74 +#define GCC_CAMSS_CSI0PHY_CLK 75 +#define GCC_CAMSS_CSI0PIX_CLK 76 +#define GCC_CAMSS_CSI0RDI_CLK 77 +#define GCC_CAMSS_CSI1_AHB_CLK 78 +#define GCC_CAMSS_CSI1_CLK 79 +#define GCC_CAMSS_CSI1PHY_CLK 80 +#define GCC_CAMSS_CSI1PIX_CLK 81 +#define GCC_CAMSS_CSI1RDI_CLK 82 +#define GCC_CAMSS_CSI_VFE0_CLK 83 +#define GCC_CAMSS_GP0_CLK 84 +#define GCC_CAMSS_GP1_CLK 85 +#define GCC_CAMSS_ISPIF_AHB_CLK 86 +#define GCC_CAMSS_JPEG0_CLK 87 +#define GCC_CAMSS_JPEG_AHB_CLK 88 +#define GCC_CAMSS_JPEG_AXI_CLK 89 +#define GCC_CAMSS_MCLK0_CLK 90 +#define GCC_CAMSS_MCLK1_CLK 91 +#define GCC_CAMSS_MICRO_AHB_CLK 92 +#define GCC_CAMSS_CSI0PHYTIMER_CLK 93 +#define GCC_CAMSS_CSI1PHYTIMER_CLK 94 +#define GCC_CAMSS_AHB_CLK 95 +#define GCC_CAMSS_TOP_AHB_CLK 96 +#define GCC_CAMSS_CPP_AHB_CLK 97 +#define GCC_CAMSS_CPP_CLK 98 +#define GCC_CAMSS_VFE0_CLK 99 +#define GCC_CAMSS_VFE_AHB_CLK 100 +#define GCC_CAMSS_VFE_AXI_CLK 101 +#define GCC_CRYPTO_AHB_CLK 102 +#define GCC_CRYPTO_AXI_CLK 103 +#define GCC_CRYPTO_CLK 104 +#define GCC_OXILI_GMEM_CLK 105 +#define GCC_GP1_CLK 106 +#define GCC_GP2_CLK 107 +#define GCC_GP3_CLK 108 +#define GCC_MDSS_AHB_CLK 109 +#define GCC_MDSS_AXI_CLK 110 +#define GCC_MDSS_BYTE0_CLK 111 +#define GCC_MDSS_ESC0_CLK 112 +#define GCC_MDSS_MDP_CLK 113 +#define GCC_MDSS_PCLK0_CLK 114 +#define GCC_MDSS_VSYNC_CLK 115 +#define GCC_MSS_CFG_AHB_CLK 116 +#define GCC_OXILI_AHB_CLK 117 +#define GCC_OXILI_GFX3D_CLK 118 +#define GCC_PDM2_CLK 119 +#define GCC_PDM_AHB_CLK 120 +#define GCC_PRNG_AHB_CLK 121 +#define GCC_SDCC1_AHB_CLK 122 +#define GCC_SDCC1_APPS_CLK 123 +#define GCC_SDCC2_AHB_CLK 124 +#define GCC_SDCC2_APPS_CLK 125 +#define GCC_GTCU_AHB_CLK 126 +#define GCC_JPEG_TBU_CLK 127 +#define GCC_MDP_TBU_CLK 128 +#define GCC_SMMU_CFG_CLK 129 +#define GCC_VENUS_TBU_CLK 130 +#define GCC_VFE_TBU_CLK 131 +#define GCC_USB2A_PHY_SLEEP_CLK 132 +#define GCC_USB_HS_AHB_CLK 133 +#define GCC_USB_HS_SYSTEM_CLK 134 +#define GCC_VENUS0_AHB_CLK 135 +#define GCC_VENUS0_AXI_CLK 136 +#define GCC_VENUS0_VCODEC0_CLK 137 + +#endif diff --git a/include/dt-bindings/reset/qcom,gcc-msm8916.h b/include/dt-bindings/reset/qcom,gcc-msm8916.h new file mode 100644 index 000000000000..3d90410f09c7 --- /dev/null +++ b/include/dt-bindings/reset/qcom,gcc-msm8916.h @@ -0,0 +1,108 @@ +/* + * Copyright 2015 Linaro Limited + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef _DT_BINDINGS_RESET_MSM_GCC_8916_H +#define _DT_BINDINGS_RESET_MSM_GCC_8916_H + +#define GCC_BLSP1_BCR 0 +#define GCC_BLSP1_QUP1_BCR 1 +#define GCC_BLSP1_UART1_BCR 2 +#define GCC_BLSP1_QUP2_BCR 3 +#define GCC_BLSP1_UART2_BCR 4 +#define GCC_BLSP1_QUP3_BCR 5 +#define GCC_BLSP1_QUP4_BCR 6 +#define GCC_BLSP1_QUP5_BCR 7 +#define GCC_BLSP1_QUP6_BCR 8 +#define GCC_IMEM_BCR 9 +#define GCC_SMMU_BCR 10 +#define GCC_APSS_TCU_BCR 11 +#define GCC_SMMU_XPU_BCR 12 +#define GCC_PCNOC_TBU_BCR 13 +#define GCC_PRNG_BCR 14 +#define GCC_BOOT_ROM_BCR 15 +#define GCC_CRYPTO_BCR 16 +#define GCC_SEC_CTRL_BCR 17 +#define GCC_AUDIO_CORE_BCR 18 +#define GCC_ULT_AUDIO_BCR 19 +#define GCC_DEHR_BCR 20 +#define GCC_SYSTEM_NOC_BCR 21 +#define GCC_PCNOC_BCR 22 +#define GCC_TCSR_BCR 23 +#define GCC_QDSS_BCR 24 +#define GCC_DCD_BCR 25 +#define GCC_MSG_RAM_BCR 26 +#define GCC_MPM_BCR 27 +#define GCC_SPMI_BCR 28 +#define GCC_SPDM_BCR 29 +#define GCC_MM_SPDM_BCR 30 +#define GCC_BIMC_BCR 31 +#define GCC_RBCPR_BCR 32 +#define GCC_TLMM_BCR 33 +#define GCC_USB_HS_BCR 34 +#define GCC_USB2A_PHY_BCR 35 +#define GCC_SDCC1_BCR 36 +#define GCC_SDCC2_BCR 37 +#define GCC_PDM_BCR 38 +#define GCC_SNOC_BUS_TIMEOUT0_BCR 39 +#define GCC_PCNOC_BUS_TIMEOUT0_BCR 40 +#define GCC_PCNOC_BUS_TIMEOUT1_BCR 41 +#define GCC_PCNOC_BUS_TIMEOUT2_BCR 42 +#define GCC_PCNOC_BUS_TIMEOUT3_BCR 43 +#define GCC_PCNOC_BUS_TIMEOUT4_BCR 44 +#define GCC_PCNOC_BUS_TIMEOUT5_BCR 45 +#define GCC_PCNOC_BUS_TIMEOUT6_BCR 46 +#define GCC_PCNOC_BUS_TIMEOUT7_BCR 47 +#define GCC_PCNOC_BUS_TIMEOUT8_BCR 48 +#define GCC_PCNOC_BUS_TIMEOUT9_BCR 49 +#define GCC_MMSS_BCR 50 +#define GCC_VENUS0_BCR 51 +#define GCC_MDSS_BCR 52 +#define GCC_CAMSS_PHY0_BCR 53 +#define GCC_CAMSS_CSI0_BCR 54 +#define GCC_CAMSS_CSI0PHY_BCR 55 +#define GCC_CAMSS_CSI0RDI_BCR 56 +#define GCC_CAMSS_CSI0PIX_BCR 57 +#define GCC_CAMSS_PHY1_BCR 58 +#define GCC_CAMSS_CSI1_BCR 59 +#define GCC_CAMSS_CSI1PHY_BCR 60 +#define GCC_CAMSS_CSI1RDI_BCR 61 +#define GCC_CAMSS_CSI1PIX_BCR 62 +#define GCC_CAMSS_ISPIF_BCR 63 +#define GCC_CAMSS_CCI_BCR 64 +#define GCC_CAMSS_MCLK0_BCR 65 +#define GCC_CAMSS_MCLK1_BCR 66 +#define GCC_CAMSS_GP0_BCR 67 +#define GCC_CAMSS_GP1_BCR 68 +#define GCC_CAMSS_TOP_BCR 69 +#define GCC_CAMSS_MICRO_BCR 70 +#define GCC_CAMSS_JPEG_BCR 71 +#define GCC_CAMSS_VFE_BCR 72 +#define GCC_CAMSS_CSI_VFE0_BCR 73 +#define GCC_OXILI_BCR 74 +#define GCC_GMEM_BCR 75 +#define GCC_CAMSS_AHB_BCR 76 +#define GCC_MDP_TBU_BCR 77 +#define GCC_GFX_TBU_BCR 78 +#define GCC_GFX_TCU_BCR 79 +#define GCC_MSS_TBU_AXI_BCR 80 +#define GCC_MSS_TBU_GSS_AXI_BCR 81 +#define GCC_MSS_TBU_Q6_AXI_BCR 82 +#define GCC_GTCU_AHB_BCR 83 +#define GCC_SMMU_CFG_BCR 84 +#define GCC_VFE_TBU_BCR 85 +#define GCC_VENUS_TBU_BCR 86 +#define GCC_JPEG_TBU_BCR 87 +#define GCC_PRONTO_TBU_BCR 88 +#define GCC_SMMU_CATS_BCR 89 + +#endif -- cgit v1.2.3 From 97badf873ab60e841243b66133ff9eff2a46ef29 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Fri, 3 Apr 2015 23:23:37 +0200 Subject: device property: Make it possible to use secondary firmware nodes Add a secondary pointer to struct fwnode_handle so as to make it possible for a device to have two firmware nodes associated with it at the same time, for example, an ACPI node and a node with a set of properties provided by platform initialization code. In the future that will allow device property lookup to fall back from the primary firmware node to the secondary one if the given property is not present there to make it easier to provide defaults for device properties used by device drivers. Introduce two helper routines, set_primary_fwnode() and set_secondary_fwnode() allowing callers to add a primary/secondary firmware node to the given device in such a way that (1) If there's only one firmware node for that device, it will be pointed to by the device's firmware node pointer. (2) If both the primary and secondary firmware nodes are present, the primary one will be pointed to by the device's firmware node pointer, while the secondary one will be pointed to by the primary node's secondary pointer. (3) If one of these nodes is removed (by calling one of the new nelpers with NULL as the second argument), the other one will be preserved. Make ACPI use set_primary_fwnode() for attaching its firmware nodes to devices. Signed-off-by: Rafael J. Wysocki Tested-by: Heikki Krogerus Acked-by: Greg Kroah-Hartman --- drivers/base/core.c | 51 ++++++++++++++++++++++++++++++++++++++++++++++++++ include/linux/acpi.h | 4 ++-- include/linux/device.h | 3 +++ include/linux/fwnode.h | 1 + 4 files changed, 57 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/drivers/base/core.c b/drivers/base/core.c index 07304a3b9ee2..c7e2a9a70865 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -12,6 +12,7 @@ #include #include +#include #include #include #include @@ -2133,3 +2134,53 @@ define_dev_printk_level(dev_notice, KERN_NOTICE); define_dev_printk_level(_dev_info, KERN_INFO); #endif + +static inline bool fwnode_is_primary(struct fwnode_handle *fwnode) +{ + return fwnode && !IS_ERR(fwnode->secondary); +} + +/** + * set_primary_fwnode - Change the primary firmware node of a given device. + * @dev: Device to handle. + * @fwnode: New primary firmware node of the device. + * + * Set the device's firmware node pointer to @fwnode, but if a secondary + * firmware node of the device is present, preserve it. + */ +void set_primary_fwnode(struct device *dev, struct fwnode_handle *fwnode) +{ + if (fwnode) { + struct fwnode_handle *fn = dev->fwnode; + + if (fwnode_is_primary(fn)) + fn = fn->secondary; + + fwnode->secondary = fn; + dev->fwnode = fwnode; + } else { + dev->fwnode = fwnode_is_primary(dev->fwnode) ? + dev->fwnode->secondary : NULL; + } +} +EXPORT_SYMBOL_GPL(set_primary_fwnode); + +/** + * set_secondary_fwnode - Change the secondary firmware node of a given device. + * @dev: Device to handle. + * @fwnode: New secondary firmware node of the device. + * + * If a primary firmware node of the device is present, set its secondary + * pointer to @fwnode. Otherwise, set the device's firmware node pointer to + * @fwnode. + */ +void set_secondary_fwnode(struct device *dev, struct fwnode_handle *fwnode) +{ + if (fwnode) + fwnode->secondary = ERR_PTR(-ENODEV); + + if (fwnode_is_primary(dev->fwnode)) + dev->fwnode->secondary = fwnode; + else + dev->fwnode = fwnode; +} diff --git a/include/linux/acpi.h b/include/linux/acpi.h index ec488d03b518..dd12127f171c 100644 --- a/include/linux/acpi.h +++ b/include/linux/acpi.h @@ -54,8 +54,8 @@ static inline acpi_handle acpi_device_handle(struct acpi_device *adev) } #define ACPI_COMPANION(dev) acpi_node((dev)->fwnode) -#define ACPI_COMPANION_SET(dev, adev) (dev)->fwnode = (adev) ? \ - acpi_fwnode_handle(adev) : NULL +#define ACPI_COMPANION_SET(dev, adev) set_primary_fwnode(dev, (adev) ? \ + acpi_fwnode_handle(adev) : NULL) #define ACPI_HANDLE(dev) acpi_device_handle(ACPI_COMPANION(dev)) static inline bool has_acpi_companion(struct device *dev) diff --git a/include/linux/device.h b/include/linux/device.h index badef20b876a..324d02add7b4 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -940,6 +940,9 @@ extern void unlock_device_hotplug(void); extern int lock_device_hotplug_sysfs(void); extern int device_offline(struct device *dev); extern int device_online(struct device *dev); +extern void set_primary_fwnode(struct device *dev, struct fwnode_handle *fwnode); +extern void set_secondary_fwnode(struct device *dev, struct fwnode_handle *fwnode); + /* * Root device objects for grouping under /sys/devices */ diff --git a/include/linux/fwnode.h b/include/linux/fwnode.h index 17bb5f039509..fc30b84b532a 100644 --- a/include/linux/fwnode.h +++ b/include/linux/fwnode.h @@ -20,6 +20,7 @@ enum fwnode_type { struct fwnode_handle { enum fwnode_type type; + struct fwnode_handle *secondary; }; #endif -- cgit v1.2.3 From 16ba08d5c9ec44f89ec03c67ecf7a9c5e2d204fd Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Fri, 3 Apr 2015 16:05:11 +0200 Subject: device property: Introduce firmware node type for platform data Introduce data structures and code allowing "built-in" properties to be associated with devices in such a way that they will be used by the device_property_* API if no proper firmware node (neither DT nor ACPI) is present for the given device. Each property is to be represented by a property_entry structure. An array of property_entry structures (terminated with a null entry) can be pointed to by the properties field of struct property_set that can be added as a firmware node to a struct device using device_add_property_set(). That will cause the device_property_* API to use that property_set as the source of properties if the given device does not have a DT node or an ACPI companion device object associated with it. Signed-off-by: Rafael J. Wysocki Tested-by: Heikki Krogerus Acked-by: Greg Kroah-Hartman --- drivers/base/property.c | 98 +++++++++++++++++++++++++++++++++++++++++++++--- include/linux/fwnode.h | 1 + include/linux/property.h | 33 ++++++++++++++++ 3 files changed, 127 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/drivers/base/property.c b/drivers/base/property.c index 62787bc89a1d..6a3f7d8af341 100644 --- a/drivers/base/property.c +++ b/drivers/base/property.c @@ -10,10 +10,96 @@ * published by the Free Software Foundation. */ -#include -#include #include +#include +#include #include +#include + +/** + * device_add_property_set - Add a collection of properties to a device object. + * @dev: Device to add properties to. + * @pset: Collection of properties to add. + * + * Associate a collection of device properties represented by @pset with @dev + * as its secondary firmware node. + */ +void device_add_property_set(struct device *dev, struct property_set *pset) +{ + if (pset) + pset->fwnode.type = FWNODE_PDATA; + + set_secondary_fwnode(dev, &pset->fwnode); +} +EXPORT_SYMBOL_GPL(device_add_property_set); + +static inline bool is_pset(struct fwnode_handle *fwnode) +{ + return fwnode && fwnode->type == FWNODE_PDATA; +} + +static inline struct property_set *to_pset(struct fwnode_handle *fwnode) +{ + return is_pset(fwnode) ? + container_of(fwnode, struct property_set, fwnode) : NULL; +} + +static struct property_entry *pset_prop_get(struct property_set *pset, + const char *name) +{ + struct property_entry *prop; + + if (!pset || !pset->properties) + return NULL; + + for (prop = pset->properties; prop->name; prop++) + if (!strcmp(name, prop->name)) + return prop; + + return NULL; +} + +static int pset_prop_read_array(struct property_set *pset, const char *name, + enum dev_prop_type type, void *val, size_t nval) +{ + struct property_entry *prop; + unsigned int item_size; + + prop = pset_prop_get(pset, name); + if (!prop) + return -ENODATA; + + if (prop->type != type) + return -EPROTO; + + if (!val) + return prop->nval; + + if (prop->nval < nval) + return -EOVERFLOW; + + switch (type) { + case DEV_PROP_U8: + item_size = sizeof(u8); + break; + case DEV_PROP_U16: + item_size = sizeof(u16); + break; + case DEV_PROP_U32: + item_size = sizeof(u32); + break; + case DEV_PROP_U64: + item_size = sizeof(u64); + break; + case DEV_PROP_STRING: + item_size = sizeof(const char *); + break; + default: + return -EINVAL; + } + memcpy(val, prop->value.raw_data, nval * item_size); + return 0; +} static inline struct fwnode_handle *dev_fwnode(struct device *dev) { @@ -46,7 +132,7 @@ bool fwnode_property_present(struct fwnode_handle *fwnode, const char *propname) else if (is_acpi_node(fwnode)) return !acpi_dev_prop_get(acpi_node(fwnode), propname, NULL); - return false; + return !!pset_prop_get(to_pset(fwnode), propname); } EXPORT_SYMBOL_GPL(fwnode_property_present); @@ -205,7 +291,8 @@ EXPORT_SYMBOL_GPL(device_property_read_string); _ret_ = acpi_dev_prop_read(acpi_node(_fwnode_), _propname_, \ _proptype_, _val_, _nval_); \ else \ - _ret_ = -ENXIO; \ + _ret_ = pset_prop_read_array(to_pset(_fwnode_), _propname_, \ + _proptype_, _val_, _nval_); \ _ret_; \ }) @@ -344,7 +431,8 @@ int fwnode_property_read_string_array(struct fwnode_handle *fwnode, return acpi_dev_prop_read(acpi_node(fwnode), propname, DEV_PROP_STRING, val, nval); - return -ENXIO; + return pset_prop_read_array(to_pset(fwnode), propname, + DEV_PROP_STRING, val, nval); } EXPORT_SYMBOL_GPL(fwnode_property_read_string_array); diff --git a/include/linux/fwnode.h b/include/linux/fwnode.h index fc30b84b532a..0408545bce42 100644 --- a/include/linux/fwnode.h +++ b/include/linux/fwnode.h @@ -16,6 +16,7 @@ enum fwnode_type { FWNODE_INVALID = 0, FWNODE_OF, FWNODE_ACPI, + FWNODE_PDATA, }; struct fwnode_handle { diff --git a/include/linux/property.h b/include/linux/property.h index 31dfd3db35d6..de8bdf417a35 100644 --- a/include/linux/property.h +++ b/include/linux/property.h @@ -131,4 +131,37 @@ static inline int fwnode_property_read_u64(struct fwnode_handle *fwnode, return fwnode_property_read_u64_array(fwnode, propname, val, 1); } +/** + * struct property_entry - "Built-in" device property representation. + * @name: Name of the property. + * @type: Type of the property. + * @nval: Number of items of type @type making up the value. + * @value: Value of the property (an array of @nval items of type @type). + */ +struct property_entry { + const char *name; + enum dev_prop_type type; + size_t nval; + union { + void *raw_data; + u8 *u8_data; + u16 *u16_data; + u32 *u32_data; + u64 *u64_data; + const char **str; + } value; +}; + +/** + * struct property_set - Collection of "built-in" device properties. + * @fwnode: Handle to be pointed to by the fwnode field of struct device. + * @properties: Array of properties terminated with a null entry. + */ +struct property_set { + struct fwnode_handle fwnode; + struct property_entry *properties; +}; + +void device_add_property_set(struct device *dev, struct property_set *pset); + #endif /* _LINUX_PROPERTY_H_ */ -- cgit v1.2.3 From 8b6f53c2f6d3d04c077e74fdb07130242a2d6f58 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Fri, 27 Mar 2015 09:28:16 -0700 Subject: Input: tc3589x - localize platform data This driver can only get its platform data from the device tree, and all platforms using it do that. Localize the platform data for the keypad. A later patch will enforce the device tree / OF dependence. Signed-off-by: Linus Walleij Acked-by: Lee Jones Signed-off-by: Dmitry Torokhov --- drivers/input/keyboard/tc3589x-keypad.c | 33 ++++++++++++++++++++++++++------- include/linux/mfd/tc3589x.h | 23 ----------------------- 2 files changed, 26 insertions(+), 30 deletions(-) (limited to 'include') diff --git a/drivers/input/keyboard/tc3589x-keypad.c b/drivers/input/keyboard/tc3589x-keypad.c index 563932500ff1..ae90df3468e0 100644 --- a/drivers/input/keyboard/tc3589x-keypad.c +++ b/drivers/input/keyboard/tc3589x-keypad.c @@ -69,6 +69,28 @@ #define TC3589x_EVT_INT_CLR 0x2 #define TC3589x_KBD_INT_CLR 0x1 +/** + * struct tc35893_keypad_platform_data - platform specific keypad data + * @keymap_data: matrix scan code table for keycodes + * @krow: mask for available rows, value is 0xFF + * @kcol: mask for available columns, value is 0xFF + * @debounce_period: platform specific debounce time + * @settle_time: platform specific settle down time + * @irqtype: type of interrupt, falling or rising edge + * @enable_wakeup: specifies if keypad event can wake up system from sleep + * @no_autorepeat: flag for auto repetition + */ +struct tc3589x_keypad_platform_data { + const struct matrix_keymap_data *keymap_data; + u8 krow; + u8 kcol; + u8 debounce_period; + u8 settle_time; + unsigned long irqtype; + bool enable_wakeup; + bool no_autorepeat; +}; + /** * struct tc_keypad - data structure used by keypad driver * @tc3589x: pointer to tc35893 @@ -363,13 +385,10 @@ static int tc3589x_keypad_probe(struct platform_device *pdev) const struct tc3589x_keypad_platform_data *plat; int error, irq; - plat = tc3589x->pdata->keypad; - if (!plat) { - plat = tc3589x_keypad_of_probe(&pdev->dev); - if (IS_ERR(plat)) { - dev_err(&pdev->dev, "invalid keypad platform data\n"); - return PTR_ERR(plat); - } + plat = tc3589x_keypad_of_probe(&pdev->dev); + if (IS_ERR(plat)) { + dev_err(&pdev->dev, "invalid keypad platform data\n"); + return PTR_ERR(plat); } irq = platform_get_irq(pdev, 0); diff --git a/include/linux/mfd/tc3589x.h b/include/linux/mfd/tc3589x.h index c203c9c56776..468c31a27fcf 100644 --- a/include/linux/mfd/tc3589x.h +++ b/include/linux/mfd/tc3589x.h @@ -140,36 +140,13 @@ extern int tc3589x_set_bits(struct tc3589x *tc3589x, u8 reg, u8 mask, u8 val); #define TC_KPD_DEBOUNCE_PERIOD 0xA3 #define TC_KPD_SETTLE_TIME 0xA3 -/** - * struct tc35893_platform_data - data structure for platform specific data - * @keymap_data: matrix scan code table for keycodes - * @krow: mask for available rows, value is 0xFF - * @kcol: mask for available columns, value is 0xFF - * @debounce_period: platform specific debounce time - * @settle_time: platform specific settle down time - * @irqtype: type of interrupt, falling or rising edge - * @enable_wakeup: specifies if keypad event can wake up system from sleep - * @no_autorepeat: flag for auto repetition - */ -struct tc3589x_keypad_platform_data { - const struct matrix_keymap_data *keymap_data; - u8 krow; - u8 kcol; - u8 debounce_period; - u8 settle_time; - unsigned long irqtype; - bool enable_wakeup; - bool no_autorepeat; -}; /** * struct tc3589x_platform_data - TC3589x platform data * @block: bitmask of blocks to enable (use TC3589x_BLOCK_*) - * @keypad: keypad-specific platform data */ struct tc3589x_platform_data { unsigned int block; - const struct tc3589x_keypad_platform_data *keypad; }; #endif -- cgit v1.2.3 From d545a57c5f84c01b50187177921e232a450858c7 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Sat, 4 Apr 2015 12:17:28 +0200 Subject: ALSA: hda - Sync node attributes at resume from widget power saving So far we assumed that the node attributes like amp values remain during the power state transition of the node itself. While this is true for IDT/STAC codecs I've tested, but some other codecs don't seem behaving in that way. This patch implements a partial sync mechanism specific to the given widget node. Now we've merged the regmap support, and it can be easily written with regcache_sync_region(). Tested-by: Hui Wang Signed-off-by: Takashi Iwai --- include/sound/hda_regmap.h | 12 ++++++++++++ sound/pci/hda/hda_generic.c | 6 ++---- 2 files changed, 14 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/include/sound/hda_regmap.h b/include/sound/hda_regmap.h index 76648ccfbbf8..53a18b3635e2 100644 --- a/include/sound/hda_regmap.h +++ b/include/sound/hda_regmap.h @@ -202,4 +202,16 @@ snd_hdac_regmap_update_amp_stereo(struct hdac_device *codec, hda_nid_t nid, return snd_hdac_regmap_update_raw(codec, cmd, mask, val); } +/** + * snd_hdac_regmap_sync_node - sync the widget node attributes + * @codec: HD-audio codec + * @nid: NID to sync + */ +static inline void +snd_hdac_regmap_sync_node(struct hdac_device *codec, hda_nid_t nid) +{ + regcache_mark_dirty(codec->regmap); + regcache_sync_region(codec->regmap, nid << 20, ((nid + 1) << 20) - 1); +} + #endif /* __SOUND_HDA_REGMAP_H */ diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index f7ccef5559de..1f2ca7be1468 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -842,10 +842,8 @@ static hda_nid_t path_power_update(struct hda_codec *codec, snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_POWER_STATE, state); changed = nid; - /* here we assume that widget attributes (e.g. amp, - * pinctl connection) don't change with local power - * state change. If not, need to sync the cache. - */ + if (state == AC_PWRST_D0) + snd_hdac_regmap_sync_node(&codec->core, nid); } } return changed; -- cgit v1.2.3 From cfdfab314647b1755afedc33ab66f3f247e161ae Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Fri, 3 Apr 2015 16:23:58 -0400 Subject: netfilter: Create and use nf_hook_state. Instead of passing a large number of arguments down into the nf_hook() entry points, create a structure which carries this state down through the hook processing layers. This makes is so that if we want to change the types or signatures of any of these pieces of state, there are less places that need to be changed. Signed-off-by: David S. Miller --- include/linux/netfilter.h | 28 +++++++++++++++++++++++----- net/netfilter/core.c | 32 +++++++++++++------------------- net/netfilter/nf_internals.h | 11 +++-------- net/netfilter/nf_queue.c | 38 ++++++++++++++++++++------------------ 4 files changed, 59 insertions(+), 50 deletions(-) (limited to 'include') diff --git a/include/linux/netfilter.h b/include/linux/netfilter.h index 2517ece98820..aee7ef1e23ed 100644 --- a/include/linux/netfilter.h +++ b/include/linux/netfilter.h @@ -44,6 +44,16 @@ int netfilter_init(void); struct sk_buff; struct nf_hook_ops; + +struct nf_hook_state { + unsigned int hook; + int thresh; + u_int8_t pf; + struct net_device *in; + struct net_device *out; + int (*okfn)(struct sk_buff *); +}; + typedef unsigned int nf_hookfn(const struct nf_hook_ops *ops, struct sk_buff *skb, const struct net_device *in, @@ -118,9 +128,7 @@ static inline bool nf_hooks_active(u_int8_t pf, unsigned int hook) } #endif -int nf_hook_slow(u_int8_t pf, unsigned int hook, struct sk_buff *skb, - struct net_device *indev, struct net_device *outdev, - int (*okfn)(struct sk_buff *), int thresh); +int nf_hook_slow(struct sk_buff *skb, struct nf_hook_state *state); /** * nf_hook_thresh - call a netfilter hook @@ -135,8 +143,18 @@ static inline int nf_hook_thresh(u_int8_t pf, unsigned int hook, struct net_device *outdev, int (*okfn)(struct sk_buff *), int thresh) { - if (nf_hooks_active(pf, hook)) - return nf_hook_slow(pf, hook, skb, indev, outdev, okfn, thresh); + if (nf_hooks_active(pf, hook)) { + struct nf_hook_state state = { + .hook = hook, + .thresh = thresh, + .pf = pf, + .in = indev, + .out = outdev, + .okfn = okfn + }; + + return nf_hook_slow(skb, &state); + } return 1; } diff --git a/net/netfilter/core.c b/net/netfilter/core.c index fea9ef566427..11d04ebfc5e3 100644 --- a/net/netfilter/core.c +++ b/net/netfilter/core.c @@ -120,12 +120,8 @@ EXPORT_SYMBOL(nf_unregister_hooks); unsigned int nf_iterate(struct list_head *head, struct sk_buff *skb, - unsigned int hook, - const struct net_device *indev, - const struct net_device *outdev, - struct nf_hook_ops **elemp, - int (*okfn)(struct sk_buff *), - int hook_thresh) + struct nf_hook_state *state, + struct nf_hook_ops **elemp) { unsigned int verdict; @@ -134,19 +130,20 @@ unsigned int nf_iterate(struct list_head *head, * function because of risk of continuing from deleted element. */ list_for_each_entry_continue_rcu((*elemp), head, list) { - if (hook_thresh > (*elemp)->priority) + if (state->thresh > (*elemp)->priority) continue; /* Optimization: we don't need to hold module reference here, since function can't sleep. --RR */ repeat: - verdict = (*elemp)->hook(*elemp, skb, indev, outdev, okfn); + verdict = (*elemp)->hook(*elemp, skb, state->in, state->out, + state->okfn); if (verdict != NF_ACCEPT) { #ifdef CONFIG_NETFILTER_DEBUG if (unlikely((verdict & NF_VERDICT_MASK) > NF_MAX_VERDICT)) { NFDEBUG("Evil return from %p(%u).\n", - (*elemp)->hook, hook); + (*elemp)->hook, state->hook); continue; } #endif @@ -161,11 +158,7 @@ repeat: /* Returns 1 if okfn() needs to be executed by the caller, * -EPERM for NF_DROP, 0 otherwise. */ -int nf_hook_slow(u_int8_t pf, unsigned int hook, struct sk_buff *skb, - struct net_device *indev, - struct net_device *outdev, - int (*okfn)(struct sk_buff *), - int hook_thresh) +int nf_hook_slow(struct sk_buff *skb, struct nf_hook_state *state) { struct nf_hook_ops *elem; unsigned int verdict; @@ -174,10 +167,11 @@ int nf_hook_slow(u_int8_t pf, unsigned int hook, struct sk_buff *skb, /* We may already have this, but read-locks nest anyway */ rcu_read_lock(); - elem = list_entry_rcu(&nf_hooks[pf][hook], struct nf_hook_ops, list); + elem = list_entry_rcu(&nf_hooks[state->pf][state->hook], + struct nf_hook_ops, list); next_hook: - verdict = nf_iterate(&nf_hooks[pf][hook], skb, hook, indev, - outdev, &elem, okfn, hook_thresh); + verdict = nf_iterate(&nf_hooks[state->pf][state->hook], skb, state, + &elem); if (verdict == NF_ACCEPT || verdict == NF_STOP) { ret = 1; } else if ((verdict & NF_VERDICT_MASK) == NF_DROP) { @@ -186,8 +180,8 @@ next_hook: if (ret == 0) ret = -EPERM; } else if ((verdict & NF_VERDICT_MASK) == NF_QUEUE) { - int err = nf_queue(skb, elem, pf, hook, indev, outdev, okfn, - verdict >> NF_VERDICT_QBITS); + int err = nf_queue(skb, elem, state, + verdict >> NF_VERDICT_QBITS); if (err < 0) { if (err == -ECANCELED) goto next_hook; diff --git a/net/netfilter/nf_internals.h b/net/netfilter/nf_internals.h index 61a3c927e63c..ea7f36784b3d 100644 --- a/net/netfilter/nf_internals.h +++ b/net/netfilter/nf_internals.h @@ -14,16 +14,11 @@ /* core.c */ unsigned int nf_iterate(struct list_head *head, struct sk_buff *skb, - unsigned int hook, const struct net_device *indev, - const struct net_device *outdev, - struct nf_hook_ops **elemp, - int (*okfn)(struct sk_buff *), int hook_thresh); + struct nf_hook_state *state, struct nf_hook_ops **elemp); /* nf_queue.c */ -int nf_queue(struct sk_buff *skb, struct nf_hook_ops *elem, u_int8_t pf, - unsigned int hook, struct net_device *indev, - struct net_device *outdev, int (*okfn)(struct sk_buff *), - unsigned int queuenum); +int nf_queue(struct sk_buff *skb, struct nf_hook_ops *elem, + struct nf_hook_state *state, unsigned int queuenum); int __init netfilter_queue_init(void); /* nf_log.c */ diff --git a/net/netfilter/nf_queue.c b/net/netfilter/nf_queue.c index 4c8b68e5fa16..6f8e9485cc83 100644 --- a/net/netfilter/nf_queue.c +++ b/net/netfilter/nf_queue.c @@ -100,12 +100,9 @@ EXPORT_SYMBOL_GPL(nf_queue_entry_get_refs); * through nf_reinject(). */ int nf_queue(struct sk_buff *skb, - struct nf_hook_ops *elem, - u_int8_t pf, unsigned int hook, - struct net_device *indev, - struct net_device *outdev, - int (*okfn)(struct sk_buff *), - unsigned int queuenum) + struct nf_hook_ops *elem, + struct nf_hook_state *state, + unsigned int queuenum) { int status = -ENOENT; struct nf_queue_entry *entry = NULL; @@ -121,7 +118,7 @@ int nf_queue(struct sk_buff *skb, goto err_unlock; } - afinfo = nf_get_afinfo(pf); + afinfo = nf_get_afinfo(state->pf); if (!afinfo) goto err_unlock; @@ -134,11 +131,11 @@ int nf_queue(struct sk_buff *skb, *entry = (struct nf_queue_entry) { .skb = skb, .elem = elem, - .pf = pf, - .hook = hook, - .indev = indev, - .outdev = outdev, - .okfn = okfn, + .pf = state->pf, + .hook = state->hook, + .indev = state->in, + .outdev = state->out, + .okfn = state->okfn, .size = sizeof(*entry) + afinfo->route_key_size, }; @@ -171,6 +168,7 @@ void nf_reinject(struct nf_queue_entry *entry, unsigned int verdict) struct sk_buff *skb = entry->skb; struct nf_hook_ops *elem = entry->elem; const struct nf_afinfo *afinfo; + struct nf_hook_state state; int err; rcu_read_lock(); @@ -189,12 +187,17 @@ void nf_reinject(struct nf_queue_entry *entry, unsigned int verdict) verdict = NF_DROP; } + state.hook = entry->hook; + state.thresh = INT_MIN; + state.pf = entry->pf; + state.in = entry->indev; + state.out = entry->outdev; + state.okfn = entry->okfn; + if (verdict == NF_ACCEPT) { next_hook: verdict = nf_iterate(&nf_hooks[entry->pf][entry->hook], - skb, entry->hook, - entry->indev, entry->outdev, &elem, - entry->okfn, INT_MIN); + skb, &state, &elem); } switch (verdict & NF_VERDICT_MASK) { @@ -205,9 +208,8 @@ void nf_reinject(struct nf_queue_entry *entry, unsigned int verdict) local_bh_enable(); break; case NF_QUEUE: - err = nf_queue(skb, elem, entry->pf, entry->hook, - entry->indev, entry->outdev, entry->okfn, - verdict >> NF_VERDICT_QBITS); + err = nf_queue(skb, elem, &state, + verdict >> NF_VERDICT_QBITS); if (err < 0) { if (err == -ECANCELED) goto next_hook; -- cgit v1.2.3 From 1d1de89b9a4746f1dd055a3b8d073dd2f962a3b6 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Fri, 3 Apr 2015 16:31:01 -0400 Subject: netfilter: Use nf_hook_state in nf_queue_entry. That way we don't have to reinstantiate another nf_hook_state on the stack of the nf_reinject() path. Signed-off-by: David S. Miller --- include/net/netfilter/nf_queue.h | 6 +---- net/ipv4/netfilter.c | 4 ++-- net/ipv6/netfilter.c | 4 ++-- net/netfilter/nf_queue.c | 44 ++++++++++++++++-------------------- net/netfilter/nfnetlink_queue_core.c | 30 ++++++++++++------------ 5 files changed, 39 insertions(+), 49 deletions(-) (limited to 'include') diff --git a/include/net/netfilter/nf_queue.h b/include/net/netfilter/nf_queue.h index 84a53d780306..d81d584157e1 100644 --- a/include/net/netfilter/nf_queue.h +++ b/include/net/netfilter/nf_queue.h @@ -12,12 +12,8 @@ struct nf_queue_entry { unsigned int id; struct nf_hook_ops *elem; - u_int8_t pf; + struct nf_hook_state state; u16 size; /* sizeof(entry) + saved route keys */ - unsigned int hook; - struct net_device *indev; - struct net_device *outdev; - int (*okfn)(struct sk_buff *); /* extra space to store route keys */ }; diff --git a/net/ipv4/netfilter.c b/net/ipv4/netfilter.c index 7ebd6e37875c..65de0684e22a 100644 --- a/net/ipv4/netfilter.c +++ b/net/ipv4/netfilter.c @@ -94,7 +94,7 @@ static void nf_ip_saveroute(const struct sk_buff *skb, { struct ip_rt_info *rt_info = nf_queue_entry_reroute(entry); - if (entry->hook == NF_INET_LOCAL_OUT) { + if (entry->state.hook == NF_INET_LOCAL_OUT) { const struct iphdr *iph = ip_hdr(skb); rt_info->tos = iph->tos; @@ -109,7 +109,7 @@ static int nf_ip_reroute(struct sk_buff *skb, { const struct ip_rt_info *rt_info = nf_queue_entry_reroute(entry); - if (entry->hook == NF_INET_LOCAL_OUT) { + if (entry->state.hook == NF_INET_LOCAL_OUT) { const struct iphdr *iph = ip_hdr(skb); if (!(iph->tos == rt_info->tos && diff --git a/net/ipv6/netfilter.c b/net/ipv6/netfilter.c index 398377a9d018..d958718b5031 100644 --- a/net/ipv6/netfilter.c +++ b/net/ipv6/netfilter.c @@ -84,7 +84,7 @@ static void nf_ip6_saveroute(const struct sk_buff *skb, { struct ip6_rt_info *rt_info = nf_queue_entry_reroute(entry); - if (entry->hook == NF_INET_LOCAL_OUT) { + if (entry->state.hook == NF_INET_LOCAL_OUT) { const struct ipv6hdr *iph = ipv6_hdr(skb); rt_info->daddr = iph->daddr; @@ -98,7 +98,7 @@ static int nf_ip6_reroute(struct sk_buff *skb, { struct ip6_rt_info *rt_info = nf_queue_entry_reroute(entry); - if (entry->hook == NF_INET_LOCAL_OUT) { + if (entry->state.hook == NF_INET_LOCAL_OUT) { const struct ipv6hdr *iph = ipv6_hdr(skb); if (!ipv6_addr_equal(&iph->daddr, &rt_info->daddr) || !ipv6_addr_equal(&iph->saddr, &rt_info->saddr) || diff --git a/net/netfilter/nf_queue.c b/net/netfilter/nf_queue.c index 6f8e9485cc83..d3cd37edca18 100644 --- a/net/netfilter/nf_queue.c +++ b/net/netfilter/nf_queue.c @@ -47,11 +47,13 @@ EXPORT_SYMBOL(nf_unregister_queue_handler); void nf_queue_entry_release_refs(struct nf_queue_entry *entry) { + struct nf_hook_state *state = &entry->state; + /* Release those devices we held, or Alexey will kill me. */ - if (entry->indev) - dev_put(entry->indev); - if (entry->outdev) - dev_put(entry->outdev); + if (state->in) + dev_put(state->in); + if (state->out) + dev_put(state->out); #if IS_ENABLED(CONFIG_BRIDGE_NETFILTER) if (entry->skb->nf_bridge) { struct nf_bridge_info *nf_bridge = entry->skb->nf_bridge; @@ -70,13 +72,15 @@ EXPORT_SYMBOL_GPL(nf_queue_entry_release_refs); /* Bump dev refs so they don't vanish while packet is out */ bool nf_queue_entry_get_refs(struct nf_queue_entry *entry) { + struct nf_hook_state *state = &entry->state; + if (!try_module_get(entry->elem->owner)) return false; - if (entry->indev) - dev_hold(entry->indev); - if (entry->outdev) - dev_hold(entry->outdev); + if (state->in) + dev_hold(state->in); + if (state->out) + dev_hold(state->out); #if IS_ENABLED(CONFIG_BRIDGE_NETFILTER) if (entry->skb->nf_bridge) { struct nf_bridge_info *nf_bridge = entry->skb->nf_bridge; @@ -131,11 +135,7 @@ int nf_queue(struct sk_buff *skb, *entry = (struct nf_queue_entry) { .skb = skb, .elem = elem, - .pf = state->pf, - .hook = state->hook, - .indev = state->in, - .outdev = state->out, - .okfn = state->okfn, + .state = *state, .size = sizeof(*entry) + afinfo->route_key_size, }; @@ -168,7 +168,6 @@ void nf_reinject(struct nf_queue_entry *entry, unsigned int verdict) struct sk_buff *skb = entry->skb; struct nf_hook_ops *elem = entry->elem; const struct nf_afinfo *afinfo; - struct nf_hook_state state; int err; rcu_read_lock(); @@ -182,33 +181,28 @@ void nf_reinject(struct nf_queue_entry *entry, unsigned int verdict) } if (verdict == NF_ACCEPT) { - afinfo = nf_get_afinfo(entry->pf); + afinfo = nf_get_afinfo(entry->state.pf); if (!afinfo || afinfo->reroute(skb, entry) < 0) verdict = NF_DROP; } - state.hook = entry->hook; - state.thresh = INT_MIN; - state.pf = entry->pf; - state.in = entry->indev; - state.out = entry->outdev; - state.okfn = entry->okfn; + entry->state.thresh = INT_MIN; if (verdict == NF_ACCEPT) { next_hook: - verdict = nf_iterate(&nf_hooks[entry->pf][entry->hook], - skb, &state, &elem); + verdict = nf_iterate(&nf_hooks[entry->state.pf][entry->state.hook], + skb, &entry->state, &elem); } switch (verdict & NF_VERDICT_MASK) { case NF_ACCEPT: case NF_STOP: local_bh_disable(); - entry->okfn(skb); + entry->state.okfn(skb); local_bh_enable(); break; case NF_QUEUE: - err = nf_queue(skb, elem, &state, + err = nf_queue(skb, elem, &entry->state, verdict >> NF_VERDICT_QBITS); if (err < 0) { if (err == -ECANCELED) diff --git a/net/netfilter/nfnetlink_queue_core.c b/net/netfilter/nfnetlink_queue_core.c index 86ee8b05adae..6e74655a8d4f 100644 --- a/net/netfilter/nfnetlink_queue_core.c +++ b/net/netfilter/nfnetlink_queue_core.c @@ -314,13 +314,13 @@ nfqnl_build_packet_message(struct net *net, struct nfqnl_instance *queue, if (entskb->tstamp.tv64) size += nla_total_size(sizeof(struct nfqnl_msg_packet_timestamp)); - if (entry->hook <= NF_INET_FORWARD || - (entry->hook == NF_INET_POST_ROUTING && entskb->sk == NULL)) + if (entry->state.hook <= NF_INET_FORWARD || + (entry->state.hook == NF_INET_POST_ROUTING && entskb->sk == NULL)) csum_verify = !skb_csum_unnecessary(entskb); else csum_verify = false; - outdev = entry->outdev; + outdev = entry->state.out; switch ((enum nfqnl_config_mode)ACCESS_ONCE(queue->copy_mode)) { case NFQNL_COPY_META: @@ -368,23 +368,23 @@ nfqnl_build_packet_message(struct net *net, struct nfqnl_instance *queue, return NULL; } nfmsg = nlmsg_data(nlh); - nfmsg->nfgen_family = entry->pf; + nfmsg->nfgen_family = entry->state.pf; nfmsg->version = NFNETLINK_V0; nfmsg->res_id = htons(queue->queue_num); nla = __nla_reserve(skb, NFQA_PACKET_HDR, sizeof(*pmsg)); pmsg = nla_data(nla); pmsg->hw_protocol = entskb->protocol; - pmsg->hook = entry->hook; + pmsg->hook = entry->state.hook; *packet_id_ptr = &pmsg->packet_id; - indev = entry->indev; + indev = entry->state.in; if (indev) { #if !IS_ENABLED(CONFIG_BRIDGE_NETFILTER) if (nla_put_be32(skb, NFQA_IFINDEX_INDEV, htonl(indev->ifindex))) goto nla_put_failure; #else - if (entry->pf == PF_BRIDGE) { + if (entry->state.pf == PF_BRIDGE) { /* Case 1: indev is physical input device, we need to * look for bridge group (when called from * netfilter_bridge) */ @@ -414,7 +414,7 @@ nfqnl_build_packet_message(struct net *net, struct nfqnl_instance *queue, if (nla_put_be32(skb, NFQA_IFINDEX_OUTDEV, htonl(outdev->ifindex))) goto nla_put_failure; #else - if (entry->pf == PF_BRIDGE) { + if (entry->state.pf == PF_BRIDGE) { /* Case 1: outdev is physical output device, we need to * look for bridge group (when called from * netfilter_bridge) */ @@ -633,8 +633,8 @@ nfqnl_enqueue_packet(struct nf_queue_entry *entry, unsigned int queuenum) struct nfqnl_instance *queue; struct sk_buff *skb, *segs; int err = -ENOBUFS; - struct net *net = dev_net(entry->indev ? - entry->indev : entry->outdev); + struct net *net = dev_net(entry->state.in ? + entry->state.in : entry->state.out); struct nfnl_queue_net *q = nfnl_queue_pernet(net); /* rcu_read_lock()ed by nf_hook_slow() */ @@ -647,7 +647,7 @@ nfqnl_enqueue_packet(struct nf_queue_entry *entry, unsigned int queuenum) skb = entry->skb; - switch (entry->pf) { + switch (entry->state.pf) { case NFPROTO_IPV4: skb->protocol = htons(ETH_P_IP); break; @@ -757,11 +757,11 @@ nfqnl_set_mode(struct nfqnl_instance *queue, static int dev_cmp(struct nf_queue_entry *entry, unsigned long ifindex) { - if (entry->indev) - if (entry->indev->ifindex == ifindex) + if (entry->state.in) + if (entry->state.in->ifindex == ifindex) return 1; - if (entry->outdev) - if (entry->outdev->ifindex == ifindex) + if (entry->state.out) + if (entry->state.out->ifindex == ifindex) return 1; #if IS_ENABLED(CONFIG_BRIDGE_NETFILTER) if (entry->skb->nf_bridge) { -- cgit v1.2.3 From 238e54c9cb9385a1ba99e92801f3615a2fb398b6 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Fri, 3 Apr 2015 20:32:56 -0400 Subject: netfilter: Make nf_hookfn use nf_hook_state. Pass the nf_hook_state all the way down into the hook functions themselves. Signed-off-by: David S. Miller --- include/linux/netfilter.h | 4 +-- net/bridge/br_netfilter.c | 46 +++++++++----------------- net/bridge/netfilter/ebtable_filter.c | 14 ++++---- net/bridge/netfilter/ebtable_nat.c | 14 ++++---- net/bridge/netfilter/nf_tables_bridge.c | 10 +++--- net/decnet/netfilter/dn_rtmsg.c | 4 +-- net/ipv4/netfilter/arptable_filter.c | 7 ++-- net/ipv4/netfilter/ipt_CLUSTERIP.c | 10 +++--- net/ipv4/netfilter/ipt_SYNPROXY.c | 6 ++-- net/ipv4/netfilter/iptable_filter.c | 7 ++-- net/ipv4/netfilter/iptable_mangle.c | 14 ++++---- net/ipv4/netfilter/iptable_nat.c | 28 +++++++--------- net/ipv4/netfilter/iptable_raw.c | 8 ++--- net/ipv4/netfilter/iptable_security.c | 8 ++--- net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c | 20 ++++------- net/ipv4/netfilter/nf_defrag_ipv4.c | 4 +-- net/ipv4/netfilter/nf_tables_arp.c | 6 ++-- net/ipv4/netfilter/nf_tables_ipv4.c | 12 +++---- net/ipv4/netfilter/nft_chain_nat_ipv4.c | 25 +++++--------- net/ipv4/netfilter/nft_chain_route_ipv4.c | 6 ++-- net/ipv6/netfilter/ip6t_SYNPROXY.c | 6 ++-- net/ipv6/netfilter/ip6table_filter.c | 7 ++-- net/ipv6/netfilter/ip6table_mangle.c | 13 ++++---- net/ipv6/netfilter/ip6table_nat.c | 28 +++++++--------- net/ipv6/netfilter/ip6table_raw.c | 7 ++-- net/ipv6/netfilter/ip6table_security.c | 8 ++--- net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c | 20 ++++------- net/ipv6/netfilter/nf_defrag_ipv6_hooks.c | 8 ++--- net/ipv6/netfilter/nf_tables_ipv6.c | 12 +++---- net/ipv6/netfilter/nft_chain_nat_ipv6.c | 24 +++++--------- net/ipv6/netfilter/nft_chain_route_ipv6.c | 6 ++-- net/netfilter/core.c | 3 +- net/netfilter/ipvs/ip_vs_core.c | 32 ++++++------------ security/selinux/hooks.c | 28 +++++----------- security/smack/smack_netfilter.c | 8 ++--- 35 files changed, 169 insertions(+), 294 deletions(-) (limited to 'include') diff --git a/include/linux/netfilter.h b/include/linux/netfilter.h index aee7ef1e23ed..c480c43ad8f7 100644 --- a/include/linux/netfilter.h +++ b/include/linux/netfilter.h @@ -56,9 +56,7 @@ struct nf_hook_state { typedef unsigned int nf_hookfn(const struct nf_hook_ops *ops, struct sk_buff *skb, - const struct net_device *in, - const struct net_device *out, - int (*okfn)(struct sk_buff *)); + const struct nf_hook_state *state); struct nf_hook_ops { struct list_head list; diff --git a/net/bridge/br_netfilter.c b/net/bridge/br_netfilter.c index f3884a1b942f..7527e94dd5dc 100644 --- a/net/bridge/br_netfilter.c +++ b/net/bridge/br_netfilter.c @@ -562,9 +562,7 @@ bad: * to ip6tables, which doesn't support NAT, so things are fairly simple. */ static unsigned int br_nf_pre_routing_ipv6(const struct nf_hook_ops *ops, struct sk_buff *skb, - const struct net_device *in, - const struct net_device *out, - int (*okfn)(struct sk_buff *)) + const struct nf_hook_state *state) { const struct ipv6hdr *hdr; u32 pkt_len; @@ -612,9 +610,7 @@ static unsigned int br_nf_pre_routing_ipv6(const struct nf_hook_ops *ops, * address to be able to detect DNAT afterwards. */ static unsigned int br_nf_pre_routing(const struct nf_hook_ops *ops, struct sk_buff *skb, - const struct net_device *in, - const struct net_device *out, - int (*okfn)(struct sk_buff *)) + const struct nf_hook_state *state) { struct net_bridge_port *p; struct net_bridge *br; @@ -623,7 +619,7 @@ static unsigned int br_nf_pre_routing(const struct nf_hook_ops *ops, if (unlikely(!pskb_may_pull(skb, len))) return NF_DROP; - p = br_port_get_rcu(in); + p = br_port_get_rcu(state->in); if (p == NULL) return NF_DROP; br = p->br; @@ -633,7 +629,7 @@ static unsigned int br_nf_pre_routing(const struct nf_hook_ops *ops, return NF_ACCEPT; nf_bridge_pull_encap_header_rcsum(skb); - return br_nf_pre_routing_ipv6(ops, skb, in, out, okfn); + return br_nf_pre_routing_ipv6(ops, skb, state); } if (!brnf_call_iptables && !br->nf_call_iptables) @@ -671,9 +667,7 @@ static unsigned int br_nf_pre_routing(const struct nf_hook_ops *ops, * prevent this from happening. */ static unsigned int br_nf_local_in(const struct nf_hook_ops *ops, struct sk_buff *skb, - const struct net_device *in, - const struct net_device *out, - int (*okfn)(struct sk_buff *)) + const struct nf_hook_state *state) { br_drop_fake_rtable(skb); return NF_ACCEPT; @@ -710,9 +704,7 @@ static int br_nf_forward_finish(struct sk_buff *skb) * bridge ports. */ static unsigned int br_nf_forward_ip(const struct nf_hook_ops *ops, struct sk_buff *skb, - const struct net_device *in, - const struct net_device *out, - int (*okfn)(struct sk_buff *)) + const struct nf_hook_state *state) { struct nf_bridge_info *nf_bridge; struct net_device *parent; @@ -726,7 +718,7 @@ static unsigned int br_nf_forward_ip(const struct nf_hook_ops *ops, if (!nf_bridge_unshare(skb)) return NF_DROP; - parent = bridge_parent(out); + parent = bridge_parent(state->out); if (!parent) return NF_DROP; @@ -754,23 +746,21 @@ static unsigned int br_nf_forward_ip(const struct nf_hook_ops *ops, else skb->protocol = htons(ETH_P_IPV6); - NF_HOOK(pf, NF_INET_FORWARD, skb, brnf_get_logical_dev(skb, in), parent, - br_nf_forward_finish); + NF_HOOK(pf, NF_INET_FORWARD, skb, brnf_get_logical_dev(skb, state->in), + parent, br_nf_forward_finish); return NF_STOLEN; } static unsigned int br_nf_forward_arp(const struct nf_hook_ops *ops, struct sk_buff *skb, - const struct net_device *in, - const struct net_device *out, - int (*okfn)(struct sk_buff *)) + const struct nf_hook_state *state) { struct net_bridge_port *p; struct net_bridge *br; struct net_device **d = (struct net_device **)(skb->cb); - p = br_port_get_rcu(out); + p = br_port_get_rcu(state->out); if (p == NULL) return NF_ACCEPT; br = p->br; @@ -789,9 +779,9 @@ static unsigned int br_nf_forward_arp(const struct nf_hook_ops *ops, nf_bridge_push_encap_header(skb); return NF_ACCEPT; } - *d = (struct net_device *)in; - NF_HOOK(NFPROTO_ARP, NF_ARP_FORWARD, skb, (struct net_device *)in, - (struct net_device *)out, br_nf_forward_finish); + *d = state->in; + NF_HOOK(NFPROTO_ARP, NF_ARP_FORWARD, skb, state->in, + state->out, br_nf_forward_finish); return NF_STOLEN; } @@ -859,9 +849,7 @@ static int br_nf_dev_queue_xmit(struct sk_buff *skb) /* PF_BRIDGE/POST_ROUTING ********************************************/ static unsigned int br_nf_post_routing(const struct nf_hook_ops *ops, struct sk_buff *skb, - const struct net_device *in, - const struct net_device *out, - int (*okfn)(struct sk_buff *)) + const struct nf_hook_state *state) { struct nf_bridge_info *nf_bridge = skb->nf_bridge; struct net_device *realoutdev = bridge_parent(skb->dev); @@ -910,9 +898,7 @@ static unsigned int br_nf_post_routing(const struct nf_hook_ops *ops, * for the second time. */ static unsigned int ip_sabotage_in(const struct nf_hook_ops *ops, struct sk_buff *skb, - const struct net_device *in, - const struct net_device *out, - int (*okfn)(struct sk_buff *)) + const struct nf_hook_state *state) { if (skb->nf_bridge && !(skb->nf_bridge->mask & BRNF_NF_BRIDGE_PREROUTING)) { diff --git a/net/bridge/netfilter/ebtable_filter.c b/net/bridge/netfilter/ebtable_filter.c index ce205aabf9c5..8a3f63b2e807 100644 --- a/net/bridge/netfilter/ebtable_filter.c +++ b/net/bridge/netfilter/ebtable_filter.c @@ -58,20 +58,18 @@ static const struct ebt_table frame_filter = { static unsigned int ebt_in_hook(const struct nf_hook_ops *ops, struct sk_buff *skb, - const struct net_device *in, const struct net_device *out, - int (*okfn)(struct sk_buff *)) + const struct nf_hook_state *state) { - return ebt_do_table(ops->hooknum, skb, in, out, - dev_net(in)->xt.frame_filter); + return ebt_do_table(ops->hooknum, skb, state->in, state->out, + dev_net(state->in)->xt.frame_filter); } static unsigned int ebt_out_hook(const struct nf_hook_ops *ops, struct sk_buff *skb, - const struct net_device *in, const struct net_device *out, - int (*okfn)(struct sk_buff *)) + const struct nf_hook_state *state) { - return ebt_do_table(ops->hooknum, skb, in, out, - dev_net(out)->xt.frame_filter); + return ebt_do_table(ops->hooknum, skb, state->in, state->out, + dev_net(state->out)->xt.frame_filter); } static struct nf_hook_ops ebt_ops_filter[] __read_mostly = { diff --git a/net/bridge/netfilter/ebtable_nat.c b/net/bridge/netfilter/ebtable_nat.c index a0ac2984fb6c..c5ef5b1ab678 100644 --- a/net/bridge/netfilter/ebtable_nat.c +++ b/net/bridge/netfilter/ebtable_nat.c @@ -58,20 +58,18 @@ static struct ebt_table frame_nat = { static unsigned int ebt_nat_in(const struct nf_hook_ops *ops, struct sk_buff *skb, - const struct net_device *in, const struct net_device *out, - int (*okfn)(struct sk_buff *)) + const struct nf_hook_state *state) { - return ebt_do_table(ops->hooknum, skb, in, out, - dev_net(in)->xt.frame_nat); + return ebt_do_table(ops->hooknum, skb, state->in, state->out, + dev_net(state->in)->xt.frame_nat); } static unsigned int ebt_nat_out(const struct nf_hook_ops *ops, struct sk_buff *skb, - const struct net_device *in, const struct net_device *out, - int (*okfn)(struct sk_buff *)) + const struct nf_hook_state *state) { - return ebt_do_table(ops->hooknum, skb, in, out, - dev_net(out)->xt.frame_nat); + return ebt_do_table(ops->hooknum, skb, state->in, state->out, + dev_net(state->out)->xt.frame_nat); } static struct nf_hook_ops ebt_ops_nat[] __read_mostly = { diff --git a/net/bridge/netfilter/nf_tables_bridge.c b/net/bridge/netfilter/nf_tables_bridge.c index 19473a9371b8..2c46a47160a8 100644 --- a/net/bridge/netfilter/nf_tables_bridge.c +++ b/net/bridge/netfilter/nf_tables_bridge.c @@ -93,21 +93,19 @@ static inline void nft_bridge_set_pktinfo_ipv6(struct nft_pktinfo *pkt, static unsigned int nft_do_chain_bridge(const struct nf_hook_ops *ops, struct sk_buff *skb, - const struct net_device *in, - const struct net_device *out, - int (*okfn)(struct sk_buff *)) + const struct nf_hook_state *state) { struct nft_pktinfo pkt; switch (eth_hdr(skb)->h_proto) { case htons(ETH_P_IP): - nft_bridge_set_pktinfo_ipv4(&pkt, ops, skb, in, out); + nft_bridge_set_pktinfo_ipv4(&pkt, ops, skb, state->in, state->out); break; case htons(ETH_P_IPV6): - nft_bridge_set_pktinfo_ipv6(&pkt, ops, skb, in, out); + nft_bridge_set_pktinfo_ipv6(&pkt, ops, skb, state->in, state->out); break; default: - nft_set_pktinfo(&pkt, ops, skb, in, out); + nft_set_pktinfo(&pkt, ops, skb, state->in, state->out); break; } diff --git a/net/decnet/netfilter/dn_rtmsg.c b/net/decnet/netfilter/dn_rtmsg.c index e4d9560a910b..af34fc9bdf69 100644 --- a/net/decnet/netfilter/dn_rtmsg.c +++ b/net/decnet/netfilter/dn_rtmsg.c @@ -89,9 +89,7 @@ static void dnrmg_send_peer(struct sk_buff *skb) static unsigned int dnrmg_hook(const struct nf_hook_ops *ops, struct sk_buff *skb, - const struct net_device *in, - const struct net_device *out, - int (*okfn)(struct sk_buff *)) + const struct nf_hook_state *state) { dnrmg_send_peer(skb); return NF_ACCEPT; diff --git a/net/ipv4/netfilter/arptable_filter.c b/net/ipv4/netfilter/arptable_filter.c index 802ddecb30b8..6a641cb41062 100644 --- a/net/ipv4/netfilter/arptable_filter.c +++ b/net/ipv4/netfilter/arptable_filter.c @@ -28,12 +28,11 @@ static const struct xt_table packet_filter = { /* The work comes in here from netfilter.c */ static unsigned int arptable_filter_hook(const struct nf_hook_ops *ops, struct sk_buff *skb, - const struct net_device *in, const struct net_device *out, - int (*okfn)(struct sk_buff *)) + const struct nf_hook_state *state) { - const struct net *net = dev_net((in != NULL) ? in : out); + const struct net *net = dev_net(state->in ? state->in : state->out); - return arpt_do_table(skb, ops->hooknum, in, out, + return arpt_do_table(skb, ops->hooknum, state->in, state->out, net->ipv4.arptable_filter); } diff --git a/net/ipv4/netfilter/ipt_CLUSTERIP.c b/net/ipv4/netfilter/ipt_CLUSTERIP.c index f75e9df5e017..771ab3d01ad3 100644 --- a/net/ipv4/netfilter/ipt_CLUSTERIP.c +++ b/net/ipv4/netfilter/ipt_CLUSTERIP.c @@ -504,14 +504,12 @@ static void arp_print(struct arp_payload *payload) static unsigned int arp_mangle(const struct nf_hook_ops *ops, struct sk_buff *skb, - const struct net_device *in, - const struct net_device *out, - int (*okfn)(struct sk_buff *)) + const struct nf_hook_state *state) { struct arphdr *arp = arp_hdr(skb); struct arp_payload *payload; struct clusterip_config *c; - struct net *net = dev_net(in ? in : out); + struct net *net = dev_net(state->in ? state->in : state->out); /* we don't care about non-ethernet and non-ipv4 ARP */ if (arp->ar_hrd != htons(ARPHRD_ETHER) || @@ -536,10 +534,10 @@ arp_mangle(const struct nf_hook_ops *ops, * addresses on different interfacs. However, in the CLUSTERIP case * this wouldn't work, since we didn't subscribe the mcast group on * other interfaces */ - if (c->dev != out) { + if (c->dev != state->out) { pr_debug("not mangling arp reply on different " "interface: cip'%s'-skb'%s'\n", - c->dev->name, out->name); + c->dev->name, state->out->name); clusterip_config_put(c); return NF_ACCEPT; } diff --git a/net/ipv4/netfilter/ipt_SYNPROXY.c b/net/ipv4/netfilter/ipt_SYNPROXY.c index a313c3fbeb46..e9e67793055f 100644 --- a/net/ipv4/netfilter/ipt_SYNPROXY.c +++ b/net/ipv4/netfilter/ipt_SYNPROXY.c @@ -300,11 +300,9 @@ synproxy_tg4(struct sk_buff *skb, const struct xt_action_param *par) static unsigned int ipv4_synproxy_hook(const struct nf_hook_ops *ops, struct sk_buff *skb, - const struct net_device *in, - const struct net_device *out, - int (*okfn)(struct sk_buff *)) + const struct nf_hook_state *nhs) { - struct synproxy_net *snet = synproxy_pernet(dev_net(in ? : out)); + struct synproxy_net *snet = synproxy_pernet(dev_net(nhs->in ? : nhs->out)); enum ip_conntrack_info ctinfo; struct nf_conn *ct; struct nf_conn_synproxy *synproxy; diff --git a/net/ipv4/netfilter/iptable_filter.c b/net/ipv4/netfilter/iptable_filter.c index e08a74a243a8..1df0d42bfd39 100644 --- a/net/ipv4/netfilter/iptable_filter.c +++ b/net/ipv4/netfilter/iptable_filter.c @@ -34,8 +34,7 @@ static const struct xt_table packet_filter = { static unsigned int iptable_filter_hook(const struct nf_hook_ops *ops, struct sk_buff *skb, - const struct net_device *in, const struct net_device *out, - int (*okfn)(struct sk_buff *)) + const struct nf_hook_state *state) { const struct net *net; @@ -45,8 +44,8 @@ iptable_filter_hook(const struct nf_hook_ops *ops, struct sk_buff *skb, /* root is playing with raw sockets. */ return NF_ACCEPT; - net = dev_net((in != NULL) ? in : out); - return ipt_do_table(skb, ops->hooknum, in, out, + net = dev_net(state->in ? state->in : state->out); + return ipt_do_table(skb, ops->hooknum, state->in, state->out, net->ipv4.iptable_filter); } diff --git a/net/ipv4/netfilter/iptable_mangle.c b/net/ipv4/netfilter/iptable_mangle.c index 6a5079c34bb3..7a825e740045 100644 --- a/net/ipv4/netfilter/iptable_mangle.c +++ b/net/ipv4/netfilter/iptable_mangle.c @@ -81,18 +81,16 @@ ipt_mangle_out(struct sk_buff *skb, const struct net_device *out) static unsigned int iptable_mangle_hook(const struct nf_hook_ops *ops, struct sk_buff *skb, - const struct net_device *in, - const struct net_device *out, - int (*okfn)(struct sk_buff *)) + const struct nf_hook_state *state) { if (ops->hooknum == NF_INET_LOCAL_OUT) - return ipt_mangle_out(skb, out); + return ipt_mangle_out(skb, state->out); if (ops->hooknum == NF_INET_POST_ROUTING) - return ipt_do_table(skb, ops->hooknum, in, out, - dev_net(out)->ipv4.iptable_mangle); + return ipt_do_table(skb, ops->hooknum, state->in, state->out, + dev_net(state->out)->ipv4.iptable_mangle); /* PREROUTING/INPUT/FORWARD: */ - return ipt_do_table(skb, ops->hooknum, in, out, - dev_net(in)->ipv4.iptable_mangle); + return ipt_do_table(skb, ops->hooknum, state->in, state->out, + dev_net(state->in)->ipv4.iptable_mangle); } static struct nf_hook_ops *mangle_ops __read_mostly; diff --git a/net/ipv4/netfilter/iptable_nat.c b/net/ipv4/netfilter/iptable_nat.c index 6b67d7e9a75d..7a7fea4711e5 100644 --- a/net/ipv4/netfilter/iptable_nat.c +++ b/net/ipv4/netfilter/iptable_nat.c @@ -41,38 +41,34 @@ static unsigned int iptable_nat_do_chain(const struct nf_hook_ops *ops, static unsigned int iptable_nat_ipv4_fn(const struct nf_hook_ops *ops, struct sk_buff *skb, - const struct net_device *in, - const struct net_device *out, - int (*okfn)(struct sk_buff *)) + const struct nf_hook_state *state) { - return nf_nat_ipv4_fn(ops, skb, in, out, iptable_nat_do_chain); + return nf_nat_ipv4_fn(ops, skb, state->in, state->out, + iptable_nat_do_chain); } static unsigned int iptable_nat_ipv4_in(const struct nf_hook_ops *ops, struct sk_buff *skb, - const struct net_device *in, - const struct net_device *out, - int (*okfn)(struct sk_buff *)) + const struct nf_hook_state *state) { - return nf_nat_ipv4_in(ops, skb, in, out, iptable_nat_do_chain); + return nf_nat_ipv4_in(ops, skb, state->in, state->out, + iptable_nat_do_chain); } static unsigned int iptable_nat_ipv4_out(const struct nf_hook_ops *ops, struct sk_buff *skb, - const struct net_device *in, - const struct net_device *out, - int (*okfn)(struct sk_buff *)) + const struct nf_hook_state *state) { - return nf_nat_ipv4_out(ops, skb, in, out, iptable_nat_do_chain); + return nf_nat_ipv4_out(ops, skb, state->in, state->out, + iptable_nat_do_chain); } static unsigned int iptable_nat_ipv4_local_fn(const struct nf_hook_ops *ops, struct sk_buff *skb, - const struct net_device *in, - const struct net_device *out, - int (*okfn)(struct sk_buff *)) + const struct nf_hook_state *state) { - return nf_nat_ipv4_local_fn(ops, skb, in, out, iptable_nat_do_chain); + return nf_nat_ipv4_local_fn(ops, skb, state->in, state->out, + iptable_nat_do_chain); } static struct nf_hook_ops nf_nat_ipv4_ops[] __read_mostly = { diff --git a/net/ipv4/netfilter/iptable_raw.c b/net/ipv4/netfilter/iptable_raw.c index b2f7e8f98316..fac8f607c70b 100644 --- a/net/ipv4/netfilter/iptable_raw.c +++ b/net/ipv4/netfilter/iptable_raw.c @@ -21,8 +21,7 @@ static const struct xt_table packet_raw = { /* The work comes in here from netfilter.c. */ static unsigned int iptable_raw_hook(const struct nf_hook_ops *ops, struct sk_buff *skb, - const struct net_device *in, const struct net_device *out, - int (*okfn)(struct sk_buff *)) + const struct nf_hook_state *state) { const struct net *net; @@ -32,8 +31,9 @@ iptable_raw_hook(const struct nf_hook_ops *ops, struct sk_buff *skb, /* root is playing with raw sockets. */ return NF_ACCEPT; - net = dev_net((in != NULL) ? in : out); - return ipt_do_table(skb, ops->hooknum, in, out, net->ipv4.iptable_raw); + net = dev_net(state->in ? state->in : state->out); + return ipt_do_table(skb, ops->hooknum, state->in, state->out, + net->ipv4.iptable_raw); } static struct nf_hook_ops *rawtable_ops __read_mostly; diff --git a/net/ipv4/netfilter/iptable_security.c b/net/ipv4/netfilter/iptable_security.c index c86647ed2078..d9ad60a57413 100644 --- a/net/ipv4/netfilter/iptable_security.c +++ b/net/ipv4/netfilter/iptable_security.c @@ -38,9 +38,7 @@ static const struct xt_table security_table = { static unsigned int iptable_security_hook(const struct nf_hook_ops *ops, struct sk_buff *skb, - const struct net_device *in, - const struct net_device *out, - int (*okfn)(struct sk_buff *)) + const struct nf_hook_state *state) { const struct net *net; @@ -50,8 +48,8 @@ iptable_security_hook(const struct nf_hook_ops *ops, struct sk_buff *skb, /* Somebody is playing with raw sockets. */ return NF_ACCEPT; - net = dev_net((in != NULL) ? in : out); - return ipt_do_table(skb, ops->hooknum, in, out, + net = dev_net(state->in ? state->in : state->out); + return ipt_do_table(skb, ops->hooknum, state->in, state->out, net->ipv4.iptable_security); } diff --git a/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c b/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c index 8c8d6642cbb0..30ad9554b5e9 100644 --- a/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c +++ b/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c @@ -94,9 +94,7 @@ static int ipv4_get_l4proto(const struct sk_buff *skb, unsigned int nhoff, static unsigned int ipv4_helper(const struct nf_hook_ops *ops, struct sk_buff *skb, - const struct net_device *in, - const struct net_device *out, - int (*okfn)(struct sk_buff *)) + const struct nf_hook_state *state) { struct nf_conn *ct; enum ip_conntrack_info ctinfo; @@ -123,9 +121,7 @@ static unsigned int ipv4_helper(const struct nf_hook_ops *ops, static unsigned int ipv4_confirm(const struct nf_hook_ops *ops, struct sk_buff *skb, - const struct net_device *in, - const struct net_device *out, - int (*okfn)(struct sk_buff *)) + const struct nf_hook_state *state) { struct nf_conn *ct; enum ip_conntrack_info ctinfo; @@ -149,24 +145,20 @@ out: static unsigned int ipv4_conntrack_in(const struct nf_hook_ops *ops, struct sk_buff *skb, - const struct net_device *in, - const struct net_device *out, - int (*okfn)(struct sk_buff *)) + const struct nf_hook_state *state) { - return nf_conntrack_in(dev_net(in), PF_INET, ops->hooknum, skb); + return nf_conntrack_in(dev_net(state->in), PF_INET, ops->hooknum, skb); } static unsigned int ipv4_conntrack_local(const struct nf_hook_ops *ops, struct sk_buff *skb, - const struct net_device *in, - const struct net_device *out, - int (*okfn)(struct sk_buff *)) + const struct nf_hook_state *state) { /* root is playing with raw sockets. */ if (skb->len < sizeof(struct iphdr) || ip_hdrlen(skb) < sizeof(struct iphdr)) return NF_ACCEPT; - return nf_conntrack_in(dev_net(out), PF_INET, ops->hooknum, skb); + return nf_conntrack_in(dev_net(state->out), PF_INET, ops->hooknum, skb); } /* Connection tracking may drop packets, but never alters them, so diff --git a/net/ipv4/netfilter/nf_defrag_ipv4.c b/net/ipv4/netfilter/nf_defrag_ipv4.c index 7e5ca6f2d0cd..c88b7d434718 100644 --- a/net/ipv4/netfilter/nf_defrag_ipv4.c +++ b/net/ipv4/netfilter/nf_defrag_ipv4.c @@ -63,9 +63,7 @@ static enum ip_defrag_users nf_ct_defrag_user(unsigned int hooknum, static unsigned int ipv4_conntrack_defrag(const struct nf_hook_ops *ops, struct sk_buff *skb, - const struct net_device *in, - const struct net_device *out, - int (*okfn)(struct sk_buff *)) + const struct nf_hook_state *state) { struct sock *sk = skb->sk; struct inet_sock *inet = inet_sk(skb->sk); diff --git a/net/ipv4/netfilter/nf_tables_arp.c b/net/ipv4/netfilter/nf_tables_arp.c index 19412a4063fb..fceb50e1e87d 100644 --- a/net/ipv4/netfilter/nf_tables_arp.c +++ b/net/ipv4/netfilter/nf_tables_arp.c @@ -17,13 +17,11 @@ static unsigned int nft_do_chain_arp(const struct nf_hook_ops *ops, struct sk_buff *skb, - const struct net_device *in, - const struct net_device *out, - int (*okfn)(struct sk_buff *)) + const struct nf_hook_state *state) { struct nft_pktinfo pkt; - nft_set_pktinfo(&pkt, ops, skb, in, out); + nft_set_pktinfo(&pkt, ops, skb, state->in, state->out); return nft_do_chain(&pkt, ops); } diff --git a/net/ipv4/netfilter/nf_tables_ipv4.c b/net/ipv4/netfilter/nf_tables_ipv4.c index 6820c8c40842..708e388e3dbe 100644 --- a/net/ipv4/netfilter/nf_tables_ipv4.c +++ b/net/ipv4/netfilter/nf_tables_ipv4.c @@ -20,22 +20,18 @@ static unsigned int nft_do_chain_ipv4(const struct nf_hook_ops *ops, struct sk_buff *skb, - const struct net_device *in, - const struct net_device *out, - int (*okfn)(struct sk_buff *)) + const struct nf_hook_state *state) { struct nft_pktinfo pkt; - nft_set_pktinfo_ipv4(&pkt, ops, skb, in, out); + nft_set_pktinfo_ipv4(&pkt, ops, skb, state->in, state->out); return nft_do_chain(&pkt, ops); } static unsigned int nft_ipv4_output(const struct nf_hook_ops *ops, struct sk_buff *skb, - const struct net_device *in, - const struct net_device *out, - int (*okfn)(struct sk_buff *)) + const struct nf_hook_state *state) { if (unlikely(skb->len < sizeof(struct iphdr) || ip_hdr(skb)->ihl < sizeof(struct iphdr) / 4)) { @@ -45,7 +41,7 @@ static unsigned int nft_ipv4_output(const struct nf_hook_ops *ops, return NF_ACCEPT; } - return nft_do_chain_ipv4(ops, skb, in, out, okfn); + return nft_do_chain_ipv4(ops, skb, state); } struct nft_af_info nft_af_ipv4 __read_mostly = { diff --git a/net/ipv4/netfilter/nft_chain_nat_ipv4.c b/net/ipv4/netfilter/nft_chain_nat_ipv4.c index df547bf50078..d08db6b0fcc3 100644 --- a/net/ipv4/netfilter/nft_chain_nat_ipv4.c +++ b/net/ipv4/netfilter/nft_chain_nat_ipv4.c @@ -41,38 +41,31 @@ static unsigned int nft_nat_do_chain(const struct nf_hook_ops *ops, static unsigned int nft_nat_ipv4_fn(const struct nf_hook_ops *ops, struct sk_buff *skb, - const struct net_device *in, - const struct net_device *out, - int (*okfn)(struct sk_buff *)) + const struct nf_hook_state *state) { - return nf_nat_ipv4_fn(ops, skb, in, out, nft_nat_do_chain); + return nf_nat_ipv4_fn(ops, skb, state->in, state->out, nft_nat_do_chain); } static unsigned int nft_nat_ipv4_in(const struct nf_hook_ops *ops, struct sk_buff *skb, - const struct net_device *in, - const struct net_device *out, - int (*okfn)(struct sk_buff *)) + const struct nf_hook_state *state) { - return nf_nat_ipv4_in(ops, skb, in, out, nft_nat_do_chain); + return nf_nat_ipv4_in(ops, skb, state->in, state->out, nft_nat_do_chain); } static unsigned int nft_nat_ipv4_out(const struct nf_hook_ops *ops, struct sk_buff *skb, - const struct net_device *in, - const struct net_device *out, - int (*okfn)(struct sk_buff *)) + const struct nf_hook_state *state) { - return nf_nat_ipv4_out(ops, skb, in, out, nft_nat_do_chain); + return nf_nat_ipv4_out(ops, skb, state->in, state->out, nft_nat_do_chain); } static unsigned int nft_nat_ipv4_local_fn(const struct nf_hook_ops *ops, struct sk_buff *skb, - const struct net_device *in, - const struct net_device *out, - int (*okfn)(struct sk_buff *)) + const struct nf_hook_state *state) { - return nf_nat_ipv4_local_fn(ops, skb, in, out, nft_nat_do_chain); + return nf_nat_ipv4_local_fn(ops, skb, state->in, state->out, + nft_nat_do_chain); } static const struct nf_chain_type nft_chain_nat_ipv4 = { diff --git a/net/ipv4/netfilter/nft_chain_route_ipv4.c b/net/ipv4/netfilter/nft_chain_route_ipv4.c index 125b66766c0a..073d0776ae7f 100644 --- a/net/ipv4/netfilter/nft_chain_route_ipv4.c +++ b/net/ipv4/netfilter/nft_chain_route_ipv4.c @@ -23,9 +23,7 @@ static unsigned int nf_route_table_hook(const struct nf_hook_ops *ops, struct sk_buff *skb, - const struct net_device *in, - const struct net_device *out, - int (*okfn)(struct sk_buff *)) + const struct nf_hook_state *state) { unsigned int ret; struct nft_pktinfo pkt; @@ -39,7 +37,7 @@ static unsigned int nf_route_table_hook(const struct nf_hook_ops *ops, ip_hdrlen(skb) < sizeof(struct iphdr)) return NF_ACCEPT; - nft_set_pktinfo_ipv4(&pkt, ops, skb, in, out); + nft_set_pktinfo_ipv4(&pkt, ops, skb, state->in, state->out); mark = skb->mark; iph = ip_hdr(skb); diff --git a/net/ipv6/netfilter/ip6t_SYNPROXY.c b/net/ipv6/netfilter/ip6t_SYNPROXY.c index a0d17270117c..6edb7b106de7 100644 --- a/net/ipv6/netfilter/ip6t_SYNPROXY.c +++ b/net/ipv6/netfilter/ip6t_SYNPROXY.c @@ -315,11 +315,9 @@ synproxy_tg6(struct sk_buff *skb, const struct xt_action_param *par) static unsigned int ipv6_synproxy_hook(const struct nf_hook_ops *ops, struct sk_buff *skb, - const struct net_device *in, - const struct net_device *out, - int (*okfn)(struct sk_buff *)) + const struct nf_hook_state *nhs) { - struct synproxy_net *snet = synproxy_pernet(dev_net(in ? : out)); + struct synproxy_net *snet = synproxy_pernet(dev_net(nhs->in ? : nhs->out)); enum ip_conntrack_info ctinfo; struct nf_conn *ct; struct nf_conn_synproxy *synproxy; diff --git a/net/ipv6/netfilter/ip6table_filter.c b/net/ipv6/netfilter/ip6table_filter.c index ca7f6c128086..eb9ef093454f 100644 --- a/net/ipv6/netfilter/ip6table_filter.c +++ b/net/ipv6/netfilter/ip6table_filter.c @@ -33,12 +33,11 @@ static const struct xt_table packet_filter = { /* The work comes in here from netfilter.c. */ static unsigned int ip6table_filter_hook(const struct nf_hook_ops *ops, struct sk_buff *skb, - const struct net_device *in, const struct net_device *out, - int (*okfn)(struct sk_buff *)) + const struct nf_hook_state *state) { - const struct net *net = dev_net((in != NULL) ? in : out); + const struct net *net = dev_net(state->in ? state->in : state->out); - return ip6t_do_table(skb, ops->hooknum, in, out, + return ip6t_do_table(skb, ops->hooknum, state->in, state->out, net->ipv6.ip6table_filter); } diff --git a/net/ipv6/netfilter/ip6table_mangle.c b/net/ipv6/netfilter/ip6table_mangle.c index 307bbb782d14..e713b8d3dbbc 100644 --- a/net/ipv6/netfilter/ip6table_mangle.c +++ b/net/ipv6/netfilter/ip6table_mangle.c @@ -77,17 +77,16 @@ ip6t_mangle_out(struct sk_buff *skb, const struct net_device *out) /* The work comes in here from netfilter.c. */ static unsigned int ip6table_mangle_hook(const struct nf_hook_ops *ops, struct sk_buff *skb, - const struct net_device *in, const struct net_device *out, - int (*okfn)(struct sk_buff *)) + const struct nf_hook_state *state) { if (ops->hooknum == NF_INET_LOCAL_OUT) - return ip6t_mangle_out(skb, out); + return ip6t_mangle_out(skb, state->out); if (ops->hooknum == NF_INET_POST_ROUTING) - return ip6t_do_table(skb, ops->hooknum, in, out, - dev_net(out)->ipv6.ip6table_mangle); + return ip6t_do_table(skb, ops->hooknum, state->in, state->out, + dev_net(state->out)->ipv6.ip6table_mangle); /* INPUT/FORWARD */ - return ip6t_do_table(skb, ops->hooknum, in, out, - dev_net(in)->ipv6.ip6table_mangle); + return ip6t_do_table(skb, ops->hooknum, state->in, state->out, + dev_net(state->in)->ipv6.ip6table_mangle); } static struct nf_hook_ops *mangle_ops __read_mostly; diff --git a/net/ipv6/netfilter/ip6table_nat.c b/net/ipv6/netfilter/ip6table_nat.c index b0634ac996b7..e32b0d0315e6 100644 --- a/net/ipv6/netfilter/ip6table_nat.c +++ b/net/ipv6/netfilter/ip6table_nat.c @@ -43,38 +43,34 @@ static unsigned int ip6table_nat_do_chain(const struct nf_hook_ops *ops, static unsigned int ip6table_nat_fn(const struct nf_hook_ops *ops, struct sk_buff *skb, - const struct net_device *in, - const struct net_device *out, - int (*okfn)(struct sk_buff *)) + const struct nf_hook_state *state) { - return nf_nat_ipv6_fn(ops, skb, in, out, ip6table_nat_do_chain); + return nf_nat_ipv6_fn(ops, skb, state->in, state->out, + ip6table_nat_do_chain); } static unsigned int ip6table_nat_in(const struct nf_hook_ops *ops, struct sk_buff *skb, - const struct net_device *in, - const struct net_device *out, - int (*okfn)(struct sk_buff *)) + const struct nf_hook_state *state) { - return nf_nat_ipv6_in(ops, skb, in, out, ip6table_nat_do_chain); + return nf_nat_ipv6_in(ops, skb, state->in, state->out, + ip6table_nat_do_chain); } static unsigned int ip6table_nat_out(const struct nf_hook_ops *ops, struct sk_buff *skb, - const struct net_device *in, - const struct net_device *out, - int (*okfn)(struct sk_buff *)) + const struct nf_hook_state *state) { - return nf_nat_ipv6_out(ops, skb, in, out, ip6table_nat_do_chain); + return nf_nat_ipv6_out(ops, skb, state->in, state->out, + ip6table_nat_do_chain); } static unsigned int ip6table_nat_local_fn(const struct nf_hook_ops *ops, struct sk_buff *skb, - const struct net_device *in, - const struct net_device *out, - int (*okfn)(struct sk_buff *)) + const struct nf_hook_state *state) { - return nf_nat_ipv6_local_fn(ops, skb, in, out, ip6table_nat_do_chain); + return nf_nat_ipv6_local_fn(ops, skb, state->in, state->out, + ip6table_nat_do_chain); } static struct nf_hook_ops nf_nat_ipv6_ops[] __read_mostly = { diff --git a/net/ipv6/netfilter/ip6table_raw.c b/net/ipv6/netfilter/ip6table_raw.c index 5274740acecc..937908e25862 100644 --- a/net/ipv6/netfilter/ip6table_raw.c +++ b/net/ipv6/netfilter/ip6table_raw.c @@ -20,12 +20,11 @@ static const struct xt_table packet_raw = { /* The work comes in here from netfilter.c. */ static unsigned int ip6table_raw_hook(const struct nf_hook_ops *ops, struct sk_buff *skb, - const struct net_device *in, const struct net_device *out, - int (*okfn)(struct sk_buff *)) + const struct nf_hook_state *state) { - const struct net *net = dev_net((in != NULL) ? in : out); + const struct net *net = dev_net(state->in ? state->in : state->out); - return ip6t_do_table(skb, ops->hooknum, in, out, + return ip6t_do_table(skb, ops->hooknum, state->in, state->out, net->ipv6.ip6table_raw); } diff --git a/net/ipv6/netfilter/ip6table_security.c b/net/ipv6/netfilter/ip6table_security.c index ab3b0219ecfa..f33b41e8e294 100644 --- a/net/ipv6/netfilter/ip6table_security.c +++ b/net/ipv6/netfilter/ip6table_security.c @@ -37,13 +37,11 @@ static const struct xt_table security_table = { static unsigned int ip6table_security_hook(const struct nf_hook_ops *ops, struct sk_buff *skb, - const struct net_device *in, - const struct net_device *out, - int (*okfn)(struct sk_buff *)) + const struct nf_hook_state *state) { - const struct net *net = dev_net((in != NULL) ? in : out); + const struct net *net = dev_net(state->in ? state->in : state->out); - return ip6t_do_table(skb, ops->hooknum, in, out, + return ip6t_do_table(skb, ops->hooknum, state->in, state->out, net->ipv6.ip6table_security); } diff --git a/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c b/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c index fba91c6fc7ca..4ba0c34c627b 100644 --- a/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c +++ b/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c @@ -97,9 +97,7 @@ static int ipv6_get_l4proto(const struct sk_buff *skb, unsigned int nhoff, static unsigned int ipv6_helper(const struct nf_hook_ops *ops, struct sk_buff *skb, - const struct net_device *in, - const struct net_device *out, - int (*okfn)(struct sk_buff *)) + const struct nf_hook_state *state) { struct nf_conn *ct; const struct nf_conn_help *help; @@ -135,9 +133,7 @@ static unsigned int ipv6_helper(const struct nf_hook_ops *ops, static unsigned int ipv6_confirm(const struct nf_hook_ops *ops, struct sk_buff *skb, - const struct net_device *in, - const struct net_device *out, - int (*okfn)(struct sk_buff *)) + const struct nf_hook_state *state) { struct nf_conn *ct; enum ip_conntrack_info ctinfo; @@ -171,25 +167,21 @@ out: static unsigned int ipv6_conntrack_in(const struct nf_hook_ops *ops, struct sk_buff *skb, - const struct net_device *in, - const struct net_device *out, - int (*okfn)(struct sk_buff *)) + const struct nf_hook_state *state) { - return nf_conntrack_in(dev_net(in), PF_INET6, ops->hooknum, skb); + return nf_conntrack_in(dev_net(state->in), PF_INET6, ops->hooknum, skb); } static unsigned int ipv6_conntrack_local(const struct nf_hook_ops *ops, struct sk_buff *skb, - const struct net_device *in, - const struct net_device *out, - int (*okfn)(struct sk_buff *)) + const struct nf_hook_state *state) { /* root is playing with raw sockets. */ if (skb->len < sizeof(struct ipv6hdr)) { net_notice_ratelimited("ipv6_conntrack_local: packet too short\n"); return NF_ACCEPT; } - return nf_conntrack_in(dev_net(out), PF_INET6, ops->hooknum, skb); + return nf_conntrack_in(dev_net(state->out), PF_INET6, ops->hooknum, skb); } static struct nf_hook_ops ipv6_conntrack_ops[] __read_mostly = { diff --git a/net/ipv6/netfilter/nf_defrag_ipv6_hooks.c b/net/ipv6/netfilter/nf_defrag_ipv6_hooks.c index e70382e4dfb5..e2b882056751 100644 --- a/net/ipv6/netfilter/nf_defrag_ipv6_hooks.c +++ b/net/ipv6/netfilter/nf_defrag_ipv6_hooks.c @@ -54,9 +54,7 @@ static enum ip6_defrag_users nf_ct6_defrag_user(unsigned int hooknum, static unsigned int ipv6_defrag(const struct nf_hook_ops *ops, struct sk_buff *skb, - const struct net_device *in, - const struct net_device *out, - int (*okfn)(struct sk_buff *)) + const struct nf_hook_state *state) { struct sk_buff *reasm; @@ -78,8 +76,8 @@ static unsigned int ipv6_defrag(const struct nf_hook_ops *ops, nf_ct_frag6_consume_orig(reasm); NF_HOOK_THRESH(NFPROTO_IPV6, ops->hooknum, reasm, - (struct net_device *) in, (struct net_device *) out, - okfn, NF_IP6_PRI_CONNTRACK_DEFRAG + 1); + state->in, state->out, + state->okfn, NF_IP6_PRI_CONNTRACK_DEFRAG + 1); return NF_STOLEN; } diff --git a/net/ipv6/netfilter/nf_tables_ipv6.c b/net/ipv6/netfilter/nf_tables_ipv6.c index 0d812b31277d..224bc8971a0b 100644 --- a/net/ipv6/netfilter/nf_tables_ipv6.c +++ b/net/ipv6/netfilter/nf_tables_ipv6.c @@ -18,14 +18,12 @@ static unsigned int nft_do_chain_ipv6(const struct nf_hook_ops *ops, struct sk_buff *skb, - const struct net_device *in, - const struct net_device *out, - int (*okfn)(struct sk_buff *)) + const struct nf_hook_state *state) { struct nft_pktinfo pkt; /* malformed packet, drop it */ - if (nft_set_pktinfo_ipv6(&pkt, ops, skb, in, out) < 0) + if (nft_set_pktinfo_ipv6(&pkt, ops, skb, state->in, state->out) < 0) return NF_DROP; return nft_do_chain(&pkt, ops); @@ -33,9 +31,7 @@ static unsigned int nft_do_chain_ipv6(const struct nf_hook_ops *ops, static unsigned int nft_ipv6_output(const struct nf_hook_ops *ops, struct sk_buff *skb, - const struct net_device *in, - const struct net_device *out, - int (*okfn)(struct sk_buff *)) + const struct nf_hook_state *state) { if (unlikely(skb->len < sizeof(struct ipv6hdr))) { if (net_ratelimit()) @@ -44,7 +40,7 @@ static unsigned int nft_ipv6_output(const struct nf_hook_ops *ops, return NF_ACCEPT; } - return nft_do_chain_ipv6(ops, skb, in, out, okfn); + return nft_do_chain_ipv6(ops, skb, state); } struct nft_af_info nft_af_ipv6 __read_mostly = { diff --git a/net/ipv6/netfilter/nft_chain_nat_ipv6.c b/net/ipv6/netfilter/nft_chain_nat_ipv6.c index 1c4b75dd425b..f73f4ae25bc2 100644 --- a/net/ipv6/netfilter/nft_chain_nat_ipv6.c +++ b/net/ipv6/netfilter/nft_chain_nat_ipv6.c @@ -39,38 +39,30 @@ static unsigned int nft_nat_do_chain(const struct nf_hook_ops *ops, static unsigned int nft_nat_ipv6_fn(const struct nf_hook_ops *ops, struct sk_buff *skb, - const struct net_device *in, - const struct net_device *out, - int (*okfn)(struct sk_buff *)) + const struct nf_hook_state *state) { - return nf_nat_ipv6_fn(ops, skb, in, out, nft_nat_do_chain); + return nf_nat_ipv6_fn(ops, skb, state->in, state->out, nft_nat_do_chain); } static unsigned int nft_nat_ipv6_in(const struct nf_hook_ops *ops, struct sk_buff *skb, - const struct net_device *in, - const struct net_device *out, - int (*okfn)(struct sk_buff *)) + const struct nf_hook_state *state) { - return nf_nat_ipv6_in(ops, skb, in, out, nft_nat_do_chain); + return nf_nat_ipv6_in(ops, skb, state->in, state->out, nft_nat_do_chain); } static unsigned int nft_nat_ipv6_out(const struct nf_hook_ops *ops, struct sk_buff *skb, - const struct net_device *in, - const struct net_device *out, - int (*okfn)(struct sk_buff *)) + const struct nf_hook_state *state) { - return nf_nat_ipv6_out(ops, skb, in, out, nft_nat_do_chain); + return nf_nat_ipv6_out(ops, skb, state->in, state->out, nft_nat_do_chain); } static unsigned int nft_nat_ipv6_local_fn(const struct nf_hook_ops *ops, struct sk_buff *skb, - const struct net_device *in, - const struct net_device *out, - int (*okfn)(struct sk_buff *)) + const struct nf_hook_state *state) { - return nf_nat_ipv6_local_fn(ops, skb, in, out, nft_nat_do_chain); + return nf_nat_ipv6_local_fn(ops, skb, state->in, state->out, nft_nat_do_chain); } static const struct nf_chain_type nft_chain_nat_ipv6 = { diff --git a/net/ipv6/netfilter/nft_chain_route_ipv6.c b/net/ipv6/netfilter/nft_chain_route_ipv6.c index 42031299585e..c826c3c854b2 100644 --- a/net/ipv6/netfilter/nft_chain_route_ipv6.c +++ b/net/ipv6/netfilter/nft_chain_route_ipv6.c @@ -24,9 +24,7 @@ static unsigned int nf_route_table_hook(const struct nf_hook_ops *ops, struct sk_buff *skb, - const struct net_device *in, - const struct net_device *out, - int (*okfn)(struct sk_buff *)) + const struct nf_hook_state *state) { unsigned int ret; struct nft_pktinfo pkt; @@ -35,7 +33,7 @@ static unsigned int nf_route_table_hook(const struct nf_hook_ops *ops, u32 mark, flowlabel; /* malformed packet, drop it */ - if (nft_set_pktinfo_ipv6(&pkt, ops, skb, in, out) < 0) + if (nft_set_pktinfo_ipv6(&pkt, ops, skb, state->in, state->out) < 0) return NF_DROP; /* save source/dest address, mark, hoplimit, flowlabel, priority */ diff --git a/net/netfilter/core.c b/net/netfilter/core.c index 11d04ebfc5e3..e6163017c42d 100644 --- a/net/netfilter/core.c +++ b/net/netfilter/core.c @@ -136,8 +136,7 @@ unsigned int nf_iterate(struct list_head *head, /* Optimization: we don't need to hold module reference here, since function can't sleep. --RR */ repeat: - verdict = (*elemp)->hook(*elemp, skb, state->in, state->out, - state->okfn); + verdict = (*elemp)->hook(*elemp, skb, state); if (verdict != NF_ACCEPT) { #ifdef CONFIG_NETFILTER_DEBUG if (unlikely((verdict & NF_VERDICT_MASK) diff --git a/net/netfilter/ipvs/ip_vs_core.c b/net/netfilter/ipvs/ip_vs_core.c index 04dbd9c7213f..5d2b806a862e 100644 --- a/net/netfilter/ipvs/ip_vs_core.c +++ b/net/netfilter/ipvs/ip_vs_core.c @@ -1272,8 +1272,7 @@ ip_vs_out(unsigned int hooknum, struct sk_buff *skb, int af) */ static unsigned int ip_vs_reply4(const struct nf_hook_ops *ops, struct sk_buff *skb, - const struct net_device *in, const struct net_device *out, - int (*okfn)(struct sk_buff *)) + const struct nf_hook_state *state) { return ip_vs_out(ops->hooknum, skb, AF_INET); } @@ -1284,8 +1283,7 @@ ip_vs_reply4(const struct nf_hook_ops *ops, struct sk_buff *skb, */ static unsigned int ip_vs_local_reply4(const struct nf_hook_ops *ops, struct sk_buff *skb, - const struct net_device *in, const struct net_device *out, - int (*okfn)(struct sk_buff *)) + const struct nf_hook_state *state) { return ip_vs_out(ops->hooknum, skb, AF_INET); } @@ -1299,8 +1297,7 @@ ip_vs_local_reply4(const struct nf_hook_ops *ops, struct sk_buff *skb, */ static unsigned int ip_vs_reply6(const struct nf_hook_ops *ops, struct sk_buff *skb, - const struct net_device *in, const struct net_device *out, - int (*okfn)(struct sk_buff *)) + const struct nf_hook_state *state) { return ip_vs_out(ops->hooknum, skb, AF_INET6); } @@ -1311,8 +1308,7 @@ ip_vs_reply6(const struct nf_hook_ops *ops, struct sk_buff *skb, */ static unsigned int ip_vs_local_reply6(const struct nf_hook_ops *ops, struct sk_buff *skb, - const struct net_device *in, const struct net_device *out, - int (*okfn)(struct sk_buff *)) + const struct nf_hook_state *state) { return ip_vs_out(ops->hooknum, skb, AF_INET6); } @@ -1769,9 +1765,7 @@ ip_vs_in(unsigned int hooknum, struct sk_buff *skb, int af) */ static unsigned int ip_vs_remote_request4(const struct nf_hook_ops *ops, struct sk_buff *skb, - const struct net_device *in, - const struct net_device *out, - int (*okfn)(struct sk_buff *)) + const struct nf_hook_state *state) { return ip_vs_in(ops->hooknum, skb, AF_INET); } @@ -1782,8 +1776,7 @@ ip_vs_remote_request4(const struct nf_hook_ops *ops, struct sk_buff *skb, */ static unsigned int ip_vs_local_request4(const struct nf_hook_ops *ops, struct sk_buff *skb, - const struct net_device *in, const struct net_device *out, - int (*okfn)(struct sk_buff *)) + const struct nf_hook_state *state) { return ip_vs_in(ops->hooknum, skb, AF_INET); } @@ -1796,9 +1789,7 @@ ip_vs_local_request4(const struct nf_hook_ops *ops, struct sk_buff *skb, */ static unsigned int ip_vs_remote_request6(const struct nf_hook_ops *ops, struct sk_buff *skb, - const struct net_device *in, - const struct net_device *out, - int (*okfn)(struct sk_buff *)) + const struct nf_hook_state *state) { return ip_vs_in(ops->hooknum, skb, AF_INET6); } @@ -1809,8 +1800,7 @@ ip_vs_remote_request6(const struct nf_hook_ops *ops, struct sk_buff *skb, */ static unsigned int ip_vs_local_request6(const struct nf_hook_ops *ops, struct sk_buff *skb, - const struct net_device *in, const struct net_device *out, - int (*okfn)(struct sk_buff *)) + const struct nf_hook_state *state) { return ip_vs_in(ops->hooknum, skb, AF_INET6); } @@ -1829,8 +1819,7 @@ ip_vs_local_request6(const struct nf_hook_ops *ops, struct sk_buff *skb, */ static unsigned int ip_vs_forward_icmp(const struct nf_hook_ops *ops, struct sk_buff *skb, - const struct net_device *in, const struct net_device *out, - int (*okfn)(struct sk_buff *)) + const struct nf_hook_state *state) { int r; struct net *net; @@ -1851,8 +1840,7 @@ ip_vs_forward_icmp(const struct nf_hook_ops *ops, struct sk_buff *skb, #ifdef CONFIG_IP_VS_IPV6 static unsigned int ip_vs_forward_icmp_v6(const struct nf_hook_ops *ops, struct sk_buff *skb, - const struct net_device *in, const struct net_device *out, - int (*okfn)(struct sk_buff *)) + const struct nf_hook_state *state) { int r; struct net *net; diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index edc66de39f2e..7e392edaab97 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -4852,21 +4852,17 @@ static unsigned int selinux_ip_forward(struct sk_buff *skb, static unsigned int selinux_ipv4_forward(const struct nf_hook_ops *ops, struct sk_buff *skb, - const struct net_device *in, - const struct net_device *out, - int (*okfn)(struct sk_buff *)) + const struct nf_hook_state *state) { - return selinux_ip_forward(skb, in, PF_INET); + return selinux_ip_forward(skb, state->in, PF_INET); } #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) static unsigned int selinux_ipv6_forward(const struct nf_hook_ops *ops, struct sk_buff *skb, - const struct net_device *in, - const struct net_device *out, - int (*okfn)(struct sk_buff *)) + const struct nf_hook_state *state) { - return selinux_ip_forward(skb, in, PF_INET6); + return selinux_ip_forward(skb, state->in, PF_INET6); } #endif /* IPV6 */ @@ -4914,9 +4910,7 @@ static unsigned int selinux_ip_output(struct sk_buff *skb, static unsigned int selinux_ipv4_output(const struct nf_hook_ops *ops, struct sk_buff *skb, - const struct net_device *in, - const struct net_device *out, - int (*okfn)(struct sk_buff *)) + const struct nf_hook_state *state) { return selinux_ip_output(skb, PF_INET); } @@ -5091,21 +5085,17 @@ static unsigned int selinux_ip_postroute(struct sk_buff *skb, static unsigned int selinux_ipv4_postroute(const struct nf_hook_ops *ops, struct sk_buff *skb, - const struct net_device *in, - const struct net_device *out, - int (*okfn)(struct sk_buff *)) + const struct nf_hook_state *state) { - return selinux_ip_postroute(skb, out, PF_INET); + return selinux_ip_postroute(skb, state->out, PF_INET); } #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) static unsigned int selinux_ipv6_postroute(const struct nf_hook_ops *ops, struct sk_buff *skb, - const struct net_device *in, - const struct net_device *out, - int (*okfn)(struct sk_buff *)) + const struct nf_hook_state *state) { - return selinux_ip_postroute(skb, out, PF_INET6); + return selinux_ip_postroute(skb, state->out, PF_INET6); } #endif /* IPV6 */ diff --git a/security/smack/smack_netfilter.c b/security/smack/smack_netfilter.c index c952632afb0d..a455cfc9ec1f 100644 --- a/security/smack/smack_netfilter.c +++ b/security/smack/smack_netfilter.c @@ -23,9 +23,7 @@ static unsigned int smack_ipv6_output(const struct nf_hook_ops *ops, struct sk_buff *skb, - const struct net_device *in, - const struct net_device *out, - int (*okfn)(struct sk_buff *)) + const struct nf_hook_state *state) { struct socket_smack *ssp; struct smack_known *skp; @@ -42,9 +40,7 @@ static unsigned int smack_ipv6_output(const struct nf_hook_ops *ops, static unsigned int smack_ipv4_output(const struct nf_hook_ops *ops, struct sk_buff *skb, - const struct net_device *in, - const struct net_device *out, - int (*okfn)(struct sk_buff *)) + const struct nf_hook_state *state) { struct socket_smack *ssp; struct smack_known *skp; -- cgit v1.2.3 From d7cf4081ed454dba02cb632e492ecf5e29d1ee44 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Fri, 3 Apr 2015 20:51:13 -0400 Subject: netfilter: Pass nf_hook_state through nf_nat_ipv4_{in,out,fn,local_fn}(). Signed-off-by: David S. Miller --- include/net/netfilter/nf_nat_l3proto.h | 24 ++++++++--------------- net/ipv4/netfilter/iptable_nat.c | 18 +++++++---------- net/ipv4/netfilter/nf_nat_l3proto_ipv4.c | 33 +++++++++++++++----------------- net/ipv4/netfilter/nft_chain_nat_ipv4.c | 14 ++++++-------- 4 files changed, 36 insertions(+), 53 deletions(-) (limited to 'include') diff --git a/include/net/netfilter/nf_nat_l3proto.h b/include/net/netfilter/nf_nat_l3proto.h index 340c013795a4..e596f35f14c9 100644 --- a/include/net/netfilter/nf_nat_l3proto.h +++ b/include/net/netfilter/nf_nat_l3proto.h @@ -44,40 +44,32 @@ int nf_nat_icmp_reply_translation(struct sk_buff *skb, struct nf_conn *ct, unsigned int hooknum); unsigned int nf_nat_ipv4_in(const struct nf_hook_ops *ops, struct sk_buff *skb, - const struct net_device *in, - const struct net_device *out, + const struct nf_hook_state *state, unsigned int (*do_chain)(const struct nf_hook_ops *ops, struct sk_buff *skb, - const struct net_device *in, - const struct net_device *out, + const struct nf_hook_state *state, struct nf_conn *ct)); unsigned int nf_nat_ipv4_out(const struct nf_hook_ops *ops, struct sk_buff *skb, - const struct net_device *in, - const struct net_device *out, + const struct nf_hook_state *state, unsigned int (*do_chain)(const struct nf_hook_ops *ops, struct sk_buff *skb, - const struct net_device *in, - const struct net_device *out, + const struct nf_hook_state *state, struct nf_conn *ct)); unsigned int nf_nat_ipv4_local_fn(const struct nf_hook_ops *ops, struct sk_buff *skb, - const struct net_device *in, - const struct net_device *out, + const struct nf_hook_state *state, unsigned int (*do_chain)(const struct nf_hook_ops *ops, struct sk_buff *skb, - const struct net_device *in, - const struct net_device *out, + const struct nf_hook_state *state, struct nf_conn *ct)); unsigned int nf_nat_ipv4_fn(const struct nf_hook_ops *ops, struct sk_buff *skb, - const struct net_device *in, - const struct net_device *out, + const struct nf_hook_state *state, unsigned int (*do_chain)(const struct nf_hook_ops *ops, struct sk_buff *skb, - const struct net_device *in, - const struct net_device *out, + const struct nf_hook_state *state, struct nf_conn *ct)); int nf_nat_icmpv6_reply_translation(struct sk_buff *skb, struct nf_conn *ct, diff --git a/net/ipv4/netfilter/iptable_nat.c b/net/ipv4/netfilter/iptable_nat.c index 7a7fea4711e5..086e2311438e 100644 --- a/net/ipv4/netfilter/iptable_nat.c +++ b/net/ipv4/netfilter/iptable_nat.c @@ -30,45 +30,41 @@ static const struct xt_table nf_nat_ipv4_table = { static unsigned int iptable_nat_do_chain(const struct nf_hook_ops *ops, struct sk_buff *skb, - const struct net_device *in, - const struct net_device *out, + const struct nf_hook_state *state, struct nf_conn *ct) { struct net *net = nf_ct_net(ct); - return ipt_do_table(skb, ops->hooknum, in, out, net->ipv4.nat_table); + return ipt_do_table(skb, ops->hooknum, state->in, state->out, + net->ipv4.nat_table); } static unsigned int iptable_nat_ipv4_fn(const struct nf_hook_ops *ops, struct sk_buff *skb, const struct nf_hook_state *state) { - return nf_nat_ipv4_fn(ops, skb, state->in, state->out, - iptable_nat_do_chain); + return nf_nat_ipv4_fn(ops, skb, state, iptable_nat_do_chain); } static unsigned int iptable_nat_ipv4_in(const struct nf_hook_ops *ops, struct sk_buff *skb, const struct nf_hook_state *state) { - return nf_nat_ipv4_in(ops, skb, state->in, state->out, - iptable_nat_do_chain); + return nf_nat_ipv4_in(ops, skb, state, iptable_nat_do_chain); } static unsigned int iptable_nat_ipv4_out(const struct nf_hook_ops *ops, struct sk_buff *skb, const struct nf_hook_state *state) { - return nf_nat_ipv4_out(ops, skb, state->in, state->out, - iptable_nat_do_chain); + return nf_nat_ipv4_out(ops, skb, state, iptable_nat_do_chain); } static unsigned int iptable_nat_ipv4_local_fn(const struct nf_hook_ops *ops, struct sk_buff *skb, const struct nf_hook_state *state) { - return nf_nat_ipv4_local_fn(ops, skb, state->in, state->out, - iptable_nat_do_chain); + return nf_nat_ipv4_local_fn(ops, skb, state, iptable_nat_do_chain); } static struct nf_hook_ops nf_nat_ipv4_ops[] __read_mostly = { diff --git a/net/ipv4/netfilter/nf_nat_l3proto_ipv4.c b/net/ipv4/netfilter/nf_nat_l3proto_ipv4.c index fc37711e11f3..e59cc05c09e9 100644 --- a/net/ipv4/netfilter/nf_nat_l3proto_ipv4.c +++ b/net/ipv4/netfilter/nf_nat_l3proto_ipv4.c @@ -256,11 +256,10 @@ EXPORT_SYMBOL_GPL(nf_nat_icmp_reply_translation); unsigned int nf_nat_ipv4_fn(const struct nf_hook_ops *ops, struct sk_buff *skb, - const struct net_device *in, const struct net_device *out, + const struct nf_hook_state *state, unsigned int (*do_chain)(const struct nf_hook_ops *ops, struct sk_buff *skb, - const struct net_device *in, - const struct net_device *out, + const struct nf_hook_state *state, struct nf_conn *ct)) { struct nf_conn *ct; @@ -309,7 +308,7 @@ nf_nat_ipv4_fn(const struct nf_hook_ops *ops, struct sk_buff *skb, if (!nf_nat_initialized(ct, maniptype)) { unsigned int ret; - ret = do_chain(ops, skb, in, out, ct); + ret = do_chain(ops, skb, state, ct); if (ret != NF_ACCEPT) return ret; @@ -323,7 +322,8 @@ nf_nat_ipv4_fn(const struct nf_hook_ops *ops, struct sk_buff *skb, pr_debug("Already setup manip %s for ct %p\n", maniptype == NF_NAT_MANIP_SRC ? "SRC" : "DST", ct); - if (nf_nat_oif_changed(ops->hooknum, ctinfo, nat, out)) + if (nf_nat_oif_changed(ops->hooknum, ctinfo, nat, + state->out)) goto oif_changed; } break; @@ -332,7 +332,7 @@ nf_nat_ipv4_fn(const struct nf_hook_ops *ops, struct sk_buff *skb, /* ESTABLISHED */ NF_CT_ASSERT(ctinfo == IP_CT_ESTABLISHED || ctinfo == IP_CT_ESTABLISHED_REPLY); - if (nf_nat_oif_changed(ops->hooknum, ctinfo, nat, out)) + if (nf_nat_oif_changed(ops->hooknum, ctinfo, nat, state->out)) goto oif_changed; } @@ -346,17 +346,16 @@ EXPORT_SYMBOL_GPL(nf_nat_ipv4_fn); unsigned int nf_nat_ipv4_in(const struct nf_hook_ops *ops, struct sk_buff *skb, - const struct net_device *in, const struct net_device *out, + const struct nf_hook_state *state, unsigned int (*do_chain)(const struct nf_hook_ops *ops, struct sk_buff *skb, - const struct net_device *in, - const struct net_device *out, + const struct nf_hook_state *state, struct nf_conn *ct)) { unsigned int ret; __be32 daddr = ip_hdr(skb)->daddr; - ret = nf_nat_ipv4_fn(ops, skb, in, out, do_chain); + ret = nf_nat_ipv4_fn(ops, skb, state, do_chain); if (ret != NF_DROP && ret != NF_STOLEN && daddr != ip_hdr(skb)->daddr) skb_dst_drop(skb); @@ -367,11 +366,10 @@ EXPORT_SYMBOL_GPL(nf_nat_ipv4_in); unsigned int nf_nat_ipv4_out(const struct nf_hook_ops *ops, struct sk_buff *skb, - const struct net_device *in, const struct net_device *out, + const struct nf_hook_state *state, unsigned int (*do_chain)(const struct nf_hook_ops *ops, struct sk_buff *skb, - const struct net_device *in, - const struct net_device *out, + const struct nf_hook_state *state, struct nf_conn *ct)) { #ifdef CONFIG_XFRM @@ -386,7 +384,7 @@ nf_nat_ipv4_out(const struct nf_hook_ops *ops, struct sk_buff *skb, ip_hdrlen(skb) < sizeof(struct iphdr)) return NF_ACCEPT; - ret = nf_nat_ipv4_fn(ops, skb, in, out, do_chain); + ret = nf_nat_ipv4_fn(ops, skb, state, do_chain); #ifdef CONFIG_XFRM if (ret != NF_DROP && ret != NF_STOLEN && !(IPCB(skb)->flags & IPSKB_XFRM_TRANSFORMED) && @@ -410,11 +408,10 @@ EXPORT_SYMBOL_GPL(nf_nat_ipv4_out); unsigned int nf_nat_ipv4_local_fn(const struct nf_hook_ops *ops, struct sk_buff *skb, - const struct net_device *in, const struct net_device *out, + const struct nf_hook_state *state, unsigned int (*do_chain)(const struct nf_hook_ops *ops, struct sk_buff *skb, - const struct net_device *in, - const struct net_device *out, + const struct nf_hook_state *state, struct nf_conn *ct)) { const struct nf_conn *ct; @@ -427,7 +424,7 @@ nf_nat_ipv4_local_fn(const struct nf_hook_ops *ops, struct sk_buff *skb, ip_hdrlen(skb) < sizeof(struct iphdr)) return NF_ACCEPT; - ret = nf_nat_ipv4_fn(ops, skb, in, out, do_chain); + ret = nf_nat_ipv4_fn(ops, skb, state, do_chain); if (ret != NF_DROP && ret != NF_STOLEN && (ct = nf_ct_get(skb, &ctinfo)) != NULL) { enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo); diff --git a/net/ipv4/netfilter/nft_chain_nat_ipv4.c b/net/ipv4/netfilter/nft_chain_nat_ipv4.c index d08db6b0fcc3..f56bbb1ab1a0 100644 --- a/net/ipv4/netfilter/nft_chain_nat_ipv4.c +++ b/net/ipv4/netfilter/nft_chain_nat_ipv4.c @@ -28,13 +28,12 @@ static unsigned int nft_nat_do_chain(const struct nf_hook_ops *ops, struct sk_buff *skb, - const struct net_device *in, - const struct net_device *out, + const struct nf_hook_state *state, struct nf_conn *ct) { struct nft_pktinfo pkt; - nft_set_pktinfo_ipv4(&pkt, ops, skb, in, out); + nft_set_pktinfo_ipv4(&pkt, ops, skb, state->in, state->out); return nft_do_chain(&pkt, ops); } @@ -43,29 +42,28 @@ static unsigned int nft_nat_ipv4_fn(const struct nf_hook_ops *ops, struct sk_buff *skb, const struct nf_hook_state *state) { - return nf_nat_ipv4_fn(ops, skb, state->in, state->out, nft_nat_do_chain); + return nf_nat_ipv4_fn(ops, skb, state, nft_nat_do_chain); } static unsigned int nft_nat_ipv4_in(const struct nf_hook_ops *ops, struct sk_buff *skb, const struct nf_hook_state *state) { - return nf_nat_ipv4_in(ops, skb, state->in, state->out, nft_nat_do_chain); + return nf_nat_ipv4_in(ops, skb, state, nft_nat_do_chain); } static unsigned int nft_nat_ipv4_out(const struct nf_hook_ops *ops, struct sk_buff *skb, const struct nf_hook_state *state) { - return nf_nat_ipv4_out(ops, skb, state->in, state->out, nft_nat_do_chain); + return nf_nat_ipv4_out(ops, skb, state, nft_nat_do_chain); } static unsigned int nft_nat_ipv4_local_fn(const struct nf_hook_ops *ops, struct sk_buff *skb, const struct nf_hook_state *state) { - return nf_nat_ipv4_local_fn(ops, skb, state->in, state->out, - nft_nat_do_chain); + return nf_nat_ipv4_local_fn(ops, skb, state, nft_nat_do_chain); } static const struct nf_chain_type nft_chain_nat_ipv4 = { -- cgit v1.2.3 From 1c491ba2592f621f21a693d43fab06302527fc0f Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Fri, 3 Apr 2015 20:56:08 -0400 Subject: netfilter: Pass nf_hook_state through ipt_do_table(). Signed-off-by: David S. Miller --- include/linux/netfilter_ipv4/ip_tables.h | 3 +-- net/ipv4/netfilter/ip_tables.c | 13 ++++++------- net/ipv4/netfilter/iptable_filter.c | 3 +-- net/ipv4/netfilter/iptable_mangle.c | 11 ++++++----- net/ipv4/netfilter/iptable_nat.c | 3 +-- net/ipv4/netfilter/iptable_raw.c | 3 +-- net/ipv4/netfilter/iptable_security.c | 2 +- 7 files changed, 17 insertions(+), 21 deletions(-) (limited to 'include') diff --git a/include/linux/netfilter_ipv4/ip_tables.h b/include/linux/netfilter_ipv4/ip_tables.h index 901e84db847d..4073510da485 100644 --- a/include/linux/netfilter_ipv4/ip_tables.h +++ b/include/linux/netfilter_ipv4/ip_tables.h @@ -65,8 +65,7 @@ struct ipt_error { extern void *ipt_alloc_initial_table(const struct xt_table *); extern unsigned int ipt_do_table(struct sk_buff *skb, unsigned int hook, - const struct net_device *in, - const struct net_device *out, + const struct nf_hook_state *state, struct xt_table *table); #ifdef CONFIG_COMPAT diff --git a/net/ipv4/netfilter/ip_tables.c b/net/ipv4/netfilter/ip_tables.c index cf5e82f39d3b..c69db7fa25ee 100644 --- a/net/ipv4/netfilter/ip_tables.c +++ b/net/ipv4/netfilter/ip_tables.c @@ -288,8 +288,7 @@ struct ipt_entry *ipt_next_entry(const struct ipt_entry *entry) unsigned int ipt_do_table(struct sk_buff *skb, unsigned int hook, - const struct net_device *in, - const struct net_device *out, + const struct nf_hook_state *state, struct xt_table *table) { static const char nulldevname[IFNAMSIZ] __attribute__((aligned(sizeof(long)))); @@ -306,8 +305,8 @@ ipt_do_table(struct sk_buff *skb, /* Initialization */ ip = ip_hdr(skb); - indev = in ? in->name : nulldevname; - outdev = out ? out->name : nulldevname; + indev = state->in ? state->in->name : nulldevname; + outdev = state->out ? state->out->name : nulldevname; /* We handle fragments by dealing with the first fragment as * if it was a normal packet. All other fragments are treated * normally, except that they will NEVER match rules that ask @@ -317,8 +316,8 @@ ipt_do_table(struct sk_buff *skb, acpar.fragoff = ntohs(ip->frag_off) & IP_OFFSET; acpar.thoff = ip_hdrlen(skb); acpar.hotdrop = false; - acpar.in = in; - acpar.out = out; + acpar.in = state->in; + acpar.out = state->out; acpar.family = NFPROTO_IPV4; acpar.hooknum = hook; @@ -370,7 +369,7 @@ ipt_do_table(struct sk_buff *skb, #if IS_ENABLED(CONFIG_NETFILTER_XT_TARGET_TRACE) /* The packet is traced: log it */ if (unlikely(skb->nf_trace)) - trace_packet(skb, hook, in, out, + trace_packet(skb, hook, state->in, state->out, table->name, private, e); #endif /* Standard target? */ diff --git a/net/ipv4/netfilter/iptable_filter.c b/net/ipv4/netfilter/iptable_filter.c index 1df0d42bfd39..a0f3beca52d2 100644 --- a/net/ipv4/netfilter/iptable_filter.c +++ b/net/ipv4/netfilter/iptable_filter.c @@ -45,8 +45,7 @@ iptable_filter_hook(const struct nf_hook_ops *ops, struct sk_buff *skb, return NF_ACCEPT; net = dev_net(state->in ? state->in : state->out); - return ipt_do_table(skb, ops->hooknum, state->in, state->out, - net->ipv4.iptable_filter); + return ipt_do_table(skb, ops->hooknum, state, net->ipv4.iptable_filter); } static struct nf_hook_ops *filter_ops __read_mostly; diff --git a/net/ipv4/netfilter/iptable_mangle.c b/net/ipv4/netfilter/iptable_mangle.c index 7a825e740045..62cbb8c5f4a8 100644 --- a/net/ipv4/netfilter/iptable_mangle.c +++ b/net/ipv4/netfilter/iptable_mangle.c @@ -37,8 +37,9 @@ static const struct xt_table packet_mangler = { }; static unsigned int -ipt_mangle_out(struct sk_buff *skb, const struct net_device *out) +ipt_mangle_out(struct sk_buff *skb, const struct nf_hook_state *state) { + struct net_device *out = state->out; unsigned int ret; const struct iphdr *iph; u_int8_t tos; @@ -58,7 +59,7 @@ ipt_mangle_out(struct sk_buff *skb, const struct net_device *out) daddr = iph->daddr; tos = iph->tos; - ret = ipt_do_table(skb, NF_INET_LOCAL_OUT, NULL, out, + ret = ipt_do_table(skb, NF_INET_LOCAL_OUT, state, dev_net(out)->ipv4.iptable_mangle); /* Reroute for ANY change. */ if (ret != NF_DROP && ret != NF_STOLEN) { @@ -84,12 +85,12 @@ iptable_mangle_hook(const struct nf_hook_ops *ops, const struct nf_hook_state *state) { if (ops->hooknum == NF_INET_LOCAL_OUT) - return ipt_mangle_out(skb, state->out); + return ipt_mangle_out(skb, state); if (ops->hooknum == NF_INET_POST_ROUTING) - return ipt_do_table(skb, ops->hooknum, state->in, state->out, + return ipt_do_table(skb, ops->hooknum, state, dev_net(state->out)->ipv4.iptable_mangle); /* PREROUTING/INPUT/FORWARD: */ - return ipt_do_table(skb, ops->hooknum, state->in, state->out, + return ipt_do_table(skb, ops->hooknum, state, dev_net(state->in)->ipv4.iptable_mangle); } diff --git a/net/ipv4/netfilter/iptable_nat.c b/net/ipv4/netfilter/iptable_nat.c index 086e2311438e..0d4d9cdf98a4 100644 --- a/net/ipv4/netfilter/iptable_nat.c +++ b/net/ipv4/netfilter/iptable_nat.c @@ -35,8 +35,7 @@ static unsigned int iptable_nat_do_chain(const struct nf_hook_ops *ops, { struct net *net = nf_ct_net(ct); - return ipt_do_table(skb, ops->hooknum, state->in, state->out, - net->ipv4.nat_table); + return ipt_do_table(skb, ops->hooknum, state, net->ipv4.nat_table); } static unsigned int iptable_nat_ipv4_fn(const struct nf_hook_ops *ops, diff --git a/net/ipv4/netfilter/iptable_raw.c b/net/ipv4/netfilter/iptable_raw.c index fac8f607c70b..0356e6da4bb7 100644 --- a/net/ipv4/netfilter/iptable_raw.c +++ b/net/ipv4/netfilter/iptable_raw.c @@ -32,8 +32,7 @@ iptable_raw_hook(const struct nf_hook_ops *ops, struct sk_buff *skb, return NF_ACCEPT; net = dev_net(state->in ? state->in : state->out); - return ipt_do_table(skb, ops->hooknum, state->in, state->out, - net->ipv4.iptable_raw); + return ipt_do_table(skb, ops->hooknum, state, net->ipv4.iptable_raw); } static struct nf_hook_ops *rawtable_ops __read_mostly; diff --git a/net/ipv4/netfilter/iptable_security.c b/net/ipv4/netfilter/iptable_security.c index d9ad60a57413..4bce3980ccd9 100644 --- a/net/ipv4/netfilter/iptable_security.c +++ b/net/ipv4/netfilter/iptable_security.c @@ -49,7 +49,7 @@ iptable_security_hook(const struct nf_hook_ops *ops, struct sk_buff *skb, return NF_ACCEPT; net = dev_net(state->in ? state->in : state->out); - return ipt_do_table(skb, ops->hooknum, state->in, state->out, + return ipt_do_table(skb, ops->hooknum, state, net->ipv4.iptable_security); } -- cgit v1.2.3 From 8fe22382d1e73dc0ded8098ccf761c986149f72b Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Fri, 3 Apr 2015 21:05:07 -0400 Subject: netfilter: Pass nf_hook_state through nf_nat_ipv6_{in,out,fn,local_fn}(). Signed-off-by: David S. Miller --- include/net/netfilter/nf_nat_l3proto.h | 24 ++++++++---------------- net/ipv6/netfilter/ip6table_nat.c | 18 +++++++----------- net/ipv6/netfilter/nf_nat_l3proto_ipv6.c | 32 ++++++++++++++------------------ net/ipv6/netfilter/nft_chain_nat_ipv6.c | 13 ++++++------- 4 files changed, 35 insertions(+), 52 deletions(-) (limited to 'include') diff --git a/include/net/netfilter/nf_nat_l3proto.h b/include/net/netfilter/nf_nat_l3proto.h index e596f35f14c9..a3127325f624 100644 --- a/include/net/netfilter/nf_nat_l3proto.h +++ b/include/net/netfilter/nf_nat_l3proto.h @@ -77,40 +77,32 @@ int nf_nat_icmpv6_reply_translation(struct sk_buff *skb, struct nf_conn *ct, unsigned int hooknum, unsigned int hdrlen); unsigned int nf_nat_ipv6_in(const struct nf_hook_ops *ops, struct sk_buff *skb, - const struct net_device *in, - const struct net_device *out, + const struct nf_hook_state *state, unsigned int (*do_chain)(const struct nf_hook_ops *ops, struct sk_buff *skb, - const struct net_device *in, - const struct net_device *out, + const struct nf_hook_state *state, struct nf_conn *ct)); unsigned int nf_nat_ipv6_out(const struct nf_hook_ops *ops, struct sk_buff *skb, - const struct net_device *in, - const struct net_device *out, + const struct nf_hook_state *state, unsigned int (*do_chain)(const struct nf_hook_ops *ops, struct sk_buff *skb, - const struct net_device *in, - const struct net_device *out, + const struct nf_hook_state *state, struct nf_conn *ct)); unsigned int nf_nat_ipv6_local_fn(const struct nf_hook_ops *ops, struct sk_buff *skb, - const struct net_device *in, - const struct net_device *out, + const struct nf_hook_state *state, unsigned int (*do_chain)(const struct nf_hook_ops *ops, struct sk_buff *skb, - const struct net_device *in, - const struct net_device *out, + const struct nf_hook_state *state, struct nf_conn *ct)); unsigned int nf_nat_ipv6_fn(const struct nf_hook_ops *ops, struct sk_buff *skb, - const struct net_device *in, - const struct net_device *out, + const struct nf_hook_state *state, unsigned int (*do_chain)(const struct nf_hook_ops *ops, struct sk_buff *skb, - const struct net_device *in, - const struct net_device *out, + const struct nf_hook_state *state, struct nf_conn *ct)); #endif /* _NF_NAT_L3PROTO_H */ diff --git a/net/ipv6/netfilter/ip6table_nat.c b/net/ipv6/netfilter/ip6table_nat.c index e32b0d0315e6..d78f69c7abce 100644 --- a/net/ipv6/netfilter/ip6table_nat.c +++ b/net/ipv6/netfilter/ip6table_nat.c @@ -32,45 +32,41 @@ static const struct xt_table nf_nat_ipv6_table = { static unsigned int ip6table_nat_do_chain(const struct nf_hook_ops *ops, struct sk_buff *skb, - const struct net_device *in, - const struct net_device *out, + const struct nf_hook_state *state, struct nf_conn *ct) { struct net *net = nf_ct_net(ct); - return ip6t_do_table(skb, ops->hooknum, in, out, net->ipv6.ip6table_nat); + return ip6t_do_table(skb, ops->hooknum, state->in, state->out, + net->ipv6.ip6table_nat); } static unsigned int ip6table_nat_fn(const struct nf_hook_ops *ops, struct sk_buff *skb, const struct nf_hook_state *state) { - return nf_nat_ipv6_fn(ops, skb, state->in, state->out, - ip6table_nat_do_chain); + return nf_nat_ipv6_fn(ops, skb, state, ip6table_nat_do_chain); } static unsigned int ip6table_nat_in(const struct nf_hook_ops *ops, struct sk_buff *skb, const struct nf_hook_state *state) { - return nf_nat_ipv6_in(ops, skb, state->in, state->out, - ip6table_nat_do_chain); + return nf_nat_ipv6_in(ops, skb, state, ip6table_nat_do_chain); } static unsigned int ip6table_nat_out(const struct nf_hook_ops *ops, struct sk_buff *skb, const struct nf_hook_state *state) { - return nf_nat_ipv6_out(ops, skb, state->in, state->out, - ip6table_nat_do_chain); + return nf_nat_ipv6_out(ops, skb, state, ip6table_nat_do_chain); } static unsigned int ip6table_nat_local_fn(const struct nf_hook_ops *ops, struct sk_buff *skb, const struct nf_hook_state *state) { - return nf_nat_ipv6_local_fn(ops, skb, state->in, state->out, - ip6table_nat_do_chain); + return nf_nat_ipv6_local_fn(ops, skb, state, ip6table_nat_do_chain); } static struct nf_hook_ops nf_nat_ipv6_ops[] __read_mostly = { diff --git a/net/ipv6/netfilter/nf_nat_l3proto_ipv6.c b/net/ipv6/netfilter/nf_nat_l3proto_ipv6.c index c5812e1c1ffb..e76900e0aa92 100644 --- a/net/ipv6/netfilter/nf_nat_l3proto_ipv6.c +++ b/net/ipv6/netfilter/nf_nat_l3proto_ipv6.c @@ -263,11 +263,10 @@ EXPORT_SYMBOL_GPL(nf_nat_icmpv6_reply_translation); unsigned int nf_nat_ipv6_fn(const struct nf_hook_ops *ops, struct sk_buff *skb, - const struct net_device *in, const struct net_device *out, + const struct nf_hook_state *state, unsigned int (*do_chain)(const struct nf_hook_ops *ops, struct sk_buff *skb, - const struct net_device *in, - const struct net_device *out, + const struct nf_hook_state *state, struct nf_conn *ct)) { struct nf_conn *ct; @@ -318,7 +317,7 @@ nf_nat_ipv6_fn(const struct nf_hook_ops *ops, struct sk_buff *skb, if (!nf_nat_initialized(ct, maniptype)) { unsigned int ret; - ret = do_chain(ops, skb, in, out, ct); + ret = do_chain(ops, skb, state, ct); if (ret != NF_ACCEPT) return ret; @@ -332,7 +331,7 @@ nf_nat_ipv6_fn(const struct nf_hook_ops *ops, struct sk_buff *skb, pr_debug("Already setup manip %s for ct %p\n", maniptype == NF_NAT_MANIP_SRC ? "SRC" : "DST", ct); - if (nf_nat_oif_changed(ops->hooknum, ctinfo, nat, out)) + if (nf_nat_oif_changed(ops->hooknum, ctinfo, nat, state->out)) goto oif_changed; } break; @@ -341,7 +340,7 @@ nf_nat_ipv6_fn(const struct nf_hook_ops *ops, struct sk_buff *skb, /* ESTABLISHED */ NF_CT_ASSERT(ctinfo == IP_CT_ESTABLISHED || ctinfo == IP_CT_ESTABLISHED_REPLY); - if (nf_nat_oif_changed(ops->hooknum, ctinfo, nat, out)) + if (nf_nat_oif_changed(ops->hooknum, ctinfo, nat, state->out)) goto oif_changed; } @@ -355,17 +354,16 @@ EXPORT_SYMBOL_GPL(nf_nat_ipv6_fn); unsigned int nf_nat_ipv6_in(const struct nf_hook_ops *ops, struct sk_buff *skb, - const struct net_device *in, const struct net_device *out, + const struct nf_hook_state *state, unsigned int (*do_chain)(const struct nf_hook_ops *ops, struct sk_buff *skb, - const struct net_device *in, - const struct net_device *out, + const struct nf_hook_state *state, struct nf_conn *ct)) { unsigned int ret; struct in6_addr daddr = ipv6_hdr(skb)->daddr; - ret = nf_nat_ipv6_fn(ops, skb, in, out, do_chain); + ret = nf_nat_ipv6_fn(ops, skb, state, do_chain); if (ret != NF_DROP && ret != NF_STOLEN && ipv6_addr_cmp(&daddr, &ipv6_hdr(skb)->daddr)) skb_dst_drop(skb); @@ -376,11 +374,10 @@ EXPORT_SYMBOL_GPL(nf_nat_ipv6_in); unsigned int nf_nat_ipv6_out(const struct nf_hook_ops *ops, struct sk_buff *skb, - const struct net_device *in, const struct net_device *out, + const struct nf_hook_state *state, unsigned int (*do_chain)(const struct nf_hook_ops *ops, struct sk_buff *skb, - const struct net_device *in, - const struct net_device *out, + const struct nf_hook_state *state, struct nf_conn *ct)) { #ifdef CONFIG_XFRM @@ -394,7 +391,7 @@ nf_nat_ipv6_out(const struct nf_hook_ops *ops, struct sk_buff *skb, if (skb->len < sizeof(struct ipv6hdr)) return NF_ACCEPT; - ret = nf_nat_ipv6_fn(ops, skb, in, out, do_chain); + ret = nf_nat_ipv6_fn(ops, skb, state, do_chain); #ifdef CONFIG_XFRM if (ret != NF_DROP && ret != NF_STOLEN && !(IP6CB(skb)->flags & IP6SKB_XFRM_TRANSFORMED) && @@ -418,11 +415,10 @@ EXPORT_SYMBOL_GPL(nf_nat_ipv6_out); unsigned int nf_nat_ipv6_local_fn(const struct nf_hook_ops *ops, struct sk_buff *skb, - const struct net_device *in, const struct net_device *out, + const struct nf_hook_state *state, unsigned int (*do_chain)(const struct nf_hook_ops *ops, struct sk_buff *skb, - const struct net_device *in, - const struct net_device *out, + const struct nf_hook_state *state, struct nf_conn *ct)) { const struct nf_conn *ct; @@ -434,7 +430,7 @@ nf_nat_ipv6_local_fn(const struct nf_hook_ops *ops, struct sk_buff *skb, if (skb->len < sizeof(struct ipv6hdr)) return NF_ACCEPT; - ret = nf_nat_ipv6_fn(ops, skb, in, out, do_chain); + ret = nf_nat_ipv6_fn(ops, skb, state, do_chain); if (ret != NF_DROP && ret != NF_STOLEN && (ct = nf_ct_get(skb, &ctinfo)) != NULL) { enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo); diff --git a/net/ipv6/netfilter/nft_chain_nat_ipv6.c b/net/ipv6/netfilter/nft_chain_nat_ipv6.c index f73f4ae25bc2..11c6f75fcc4d 100644 --- a/net/ipv6/netfilter/nft_chain_nat_ipv6.c +++ b/net/ipv6/netfilter/nft_chain_nat_ipv6.c @@ -26,13 +26,12 @@ static unsigned int nft_nat_do_chain(const struct nf_hook_ops *ops, struct sk_buff *skb, - const struct net_device *in, - const struct net_device *out, + const struct nf_hook_state *state, struct nf_conn *ct) { struct nft_pktinfo pkt; - nft_set_pktinfo_ipv6(&pkt, ops, skb, in, out); + nft_set_pktinfo_ipv6(&pkt, ops, skb, state->in, state->out); return nft_do_chain(&pkt, ops); } @@ -41,28 +40,28 @@ static unsigned int nft_nat_ipv6_fn(const struct nf_hook_ops *ops, struct sk_buff *skb, const struct nf_hook_state *state) { - return nf_nat_ipv6_fn(ops, skb, state->in, state->out, nft_nat_do_chain); + return nf_nat_ipv6_fn(ops, skb, state, nft_nat_do_chain); } static unsigned int nft_nat_ipv6_in(const struct nf_hook_ops *ops, struct sk_buff *skb, const struct nf_hook_state *state) { - return nf_nat_ipv6_in(ops, skb, state->in, state->out, nft_nat_do_chain); + return nf_nat_ipv6_in(ops, skb, state, nft_nat_do_chain); } static unsigned int nft_nat_ipv6_out(const struct nf_hook_ops *ops, struct sk_buff *skb, const struct nf_hook_state *state) { - return nf_nat_ipv6_out(ops, skb, state->in, state->out, nft_nat_do_chain); + return nf_nat_ipv6_out(ops, skb, state, nft_nat_do_chain); } static unsigned int nft_nat_ipv6_local_fn(const struct nf_hook_ops *ops, struct sk_buff *skb, const struct nf_hook_state *state) { - return nf_nat_ipv6_local_fn(ops, skb, state->in, state->out, nft_nat_do_chain); + return nf_nat_ipv6_local_fn(ops, skb, state, nft_nat_do_chain); } static const struct nf_chain_type nft_chain_nat_ipv6 = { -- cgit v1.2.3 From 8f8a37152df49d541c43f010543f2b0176fcfb8e Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Fri, 3 Apr 2015 21:09:51 -0400 Subject: netfilter: Pass nf_hook_state through ip6t_do_table(). Signed-off-by: David S. Miller --- include/linux/netfilter_ipv6/ip6_tables.h | 3 +-- net/ipv6/netfilter/ip6_tables.c | 13 ++++++------- net/ipv6/netfilter/ip6table_filter.c | 3 +-- net/ipv6/netfilter/ip6table_mangle.c | 12 ++++++------ net/ipv6/netfilter/ip6table_nat.c | 3 +-- net/ipv6/netfilter/ip6table_raw.c | 3 +-- net/ipv6/netfilter/ip6table_security.c | 2 +- 7 files changed, 17 insertions(+), 22 deletions(-) (limited to 'include') diff --git a/include/linux/netfilter_ipv6/ip6_tables.h b/include/linux/netfilter_ipv6/ip6_tables.h index 610208b18c05..b40d2b635778 100644 --- a/include/linux/netfilter_ipv6/ip6_tables.h +++ b/include/linux/netfilter_ipv6/ip6_tables.h @@ -31,8 +31,7 @@ extern struct xt_table *ip6t_register_table(struct net *net, extern void ip6t_unregister_table(struct net *net, struct xt_table *table); extern unsigned int ip6t_do_table(struct sk_buff *skb, unsigned int hook, - const struct net_device *in, - const struct net_device *out, + const struct nf_hook_state *state, struct xt_table *table); /* Check for an extension */ diff --git a/net/ipv6/netfilter/ip6_tables.c b/net/ipv6/netfilter/ip6_tables.c index 83f59dc3cccc..1a732a1d3c8e 100644 --- a/net/ipv6/netfilter/ip6_tables.c +++ b/net/ipv6/netfilter/ip6_tables.c @@ -317,8 +317,7 @@ ip6t_next_entry(const struct ip6t_entry *entry) unsigned int ip6t_do_table(struct sk_buff *skb, unsigned int hook, - const struct net_device *in, - const struct net_device *out, + const struct nf_hook_state *state, struct xt_table *table) { static const char nulldevname[IFNAMSIZ] __attribute__((aligned(sizeof(long)))); @@ -333,8 +332,8 @@ ip6t_do_table(struct sk_buff *skb, unsigned int addend; /* Initialization */ - indev = in ? in->name : nulldevname; - outdev = out ? out->name : nulldevname; + indev = state->in ? state->in->name : nulldevname; + outdev = state->out ? state->out->name : nulldevname; /* We handle fragments by dealing with the first fragment as * if it was a normal packet. All other fragments are treated * normally, except that they will NEVER match rules that ask @@ -342,8 +341,8 @@ ip6t_do_table(struct sk_buff *skb, * rule is also a fragment-specific rule, non-fragments won't * match it. */ acpar.hotdrop = false; - acpar.in = in; - acpar.out = out; + acpar.in = state->in; + acpar.out = state->out; acpar.family = NFPROTO_IPV6; acpar.hooknum = hook; @@ -393,7 +392,7 @@ ip6t_do_table(struct sk_buff *skb, #if IS_ENABLED(CONFIG_NETFILTER_XT_TARGET_TRACE) /* The packet is traced: log it */ if (unlikely(skb->nf_trace)) - trace_packet(skb, hook, in, out, + trace_packet(skb, hook, state->in, state->out, table->name, private, e); #endif /* Standard target? */ diff --git a/net/ipv6/netfilter/ip6table_filter.c b/net/ipv6/netfilter/ip6table_filter.c index eb9ef093454f..5c33d8abc077 100644 --- a/net/ipv6/netfilter/ip6table_filter.c +++ b/net/ipv6/netfilter/ip6table_filter.c @@ -37,8 +37,7 @@ ip6table_filter_hook(const struct nf_hook_ops *ops, struct sk_buff *skb, { const struct net *net = dev_net(state->in ? state->in : state->out); - return ip6t_do_table(skb, ops->hooknum, state->in, state->out, - net->ipv6.ip6table_filter); + return ip6t_do_table(skb, ops->hooknum, state, net->ipv6.ip6table_filter); } static struct nf_hook_ops *filter_ops __read_mostly; diff --git a/net/ipv6/netfilter/ip6table_mangle.c b/net/ipv6/netfilter/ip6table_mangle.c index e713b8d3dbbc..b551f5b79fe2 100644 --- a/net/ipv6/netfilter/ip6table_mangle.c +++ b/net/ipv6/netfilter/ip6table_mangle.c @@ -32,7 +32,7 @@ static const struct xt_table packet_mangler = { }; static unsigned int -ip6t_mangle_out(struct sk_buff *skb, const struct net_device *out) +ip6t_mangle_out(struct sk_buff *skb, const struct nf_hook_state *state) { unsigned int ret; struct in6_addr saddr, daddr; @@ -57,8 +57,8 @@ ip6t_mangle_out(struct sk_buff *skb, const struct net_device *out) /* flowlabel and prio (includes version, which shouldn't change either */ flowlabel = *((u_int32_t *)ipv6_hdr(skb)); - ret = ip6t_do_table(skb, NF_INET_LOCAL_OUT, NULL, out, - dev_net(out)->ipv6.ip6table_mangle); + ret = ip6t_do_table(skb, NF_INET_LOCAL_OUT, state, + dev_net(state->out)->ipv6.ip6table_mangle); if (ret != NF_DROP && ret != NF_STOLEN && (!ipv6_addr_equal(&ipv6_hdr(skb)->saddr, &saddr) || @@ -80,12 +80,12 @@ ip6table_mangle_hook(const struct nf_hook_ops *ops, struct sk_buff *skb, const struct nf_hook_state *state) { if (ops->hooknum == NF_INET_LOCAL_OUT) - return ip6t_mangle_out(skb, state->out); + return ip6t_mangle_out(skb, state); if (ops->hooknum == NF_INET_POST_ROUTING) - return ip6t_do_table(skb, ops->hooknum, state->in, state->out, + return ip6t_do_table(skb, ops->hooknum, state, dev_net(state->out)->ipv6.ip6table_mangle); /* INPUT/FORWARD */ - return ip6t_do_table(skb, ops->hooknum, state->in, state->out, + return ip6t_do_table(skb, ops->hooknum, state, dev_net(state->in)->ipv6.ip6table_mangle); } diff --git a/net/ipv6/netfilter/ip6table_nat.c b/net/ipv6/netfilter/ip6table_nat.c index d78f69c7abce..c3a7f7af0ed4 100644 --- a/net/ipv6/netfilter/ip6table_nat.c +++ b/net/ipv6/netfilter/ip6table_nat.c @@ -37,8 +37,7 @@ static unsigned int ip6table_nat_do_chain(const struct nf_hook_ops *ops, { struct net *net = nf_ct_net(ct); - return ip6t_do_table(skb, ops->hooknum, state->in, state->out, - net->ipv6.ip6table_nat); + return ip6t_do_table(skb, ops->hooknum, state, net->ipv6.ip6table_nat); } static unsigned int ip6table_nat_fn(const struct nf_hook_ops *ops, diff --git a/net/ipv6/netfilter/ip6table_raw.c b/net/ipv6/netfilter/ip6table_raw.c index 937908e25862..0b33caad2b69 100644 --- a/net/ipv6/netfilter/ip6table_raw.c +++ b/net/ipv6/netfilter/ip6table_raw.c @@ -24,8 +24,7 @@ ip6table_raw_hook(const struct nf_hook_ops *ops, struct sk_buff *skb, { const struct net *net = dev_net(state->in ? state->in : state->out); - return ip6t_do_table(skb, ops->hooknum, state->in, state->out, - net->ipv6.ip6table_raw); + return ip6t_do_table(skb, ops->hooknum, state, net->ipv6.ip6table_raw); } static struct nf_hook_ops *rawtable_ops __read_mostly; diff --git a/net/ipv6/netfilter/ip6table_security.c b/net/ipv6/netfilter/ip6table_security.c index f33b41e8e294..fcef83c25f7b 100644 --- a/net/ipv6/netfilter/ip6table_security.c +++ b/net/ipv6/netfilter/ip6table_security.c @@ -41,7 +41,7 @@ ip6table_security_hook(const struct nf_hook_ops *ops, struct sk_buff *skb, { const struct net *net = dev_net(state->in ? state->in : state->out); - return ip6t_do_table(skb, ops->hooknum, state->in, state->out, + return ip6t_do_table(skb, ops->hooknum, state, net->ipv6.ip6table_security); } -- cgit v1.2.3 From 073bfd56860446a2cb349bcf282fc17a36ca386c Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Fri, 3 Apr 2015 21:16:25 -0400 Subject: netfilter: Pass nf_hook_state through nft_set_pktinfo*(). Signed-off-by: David S. Miller --- include/net/netfilter/nf_tables.h | 7 +++---- include/net/netfilter/nf_tables_ipv4.h | 5 ++--- include/net/netfilter/nf_tables_ipv6.h | 5 ++--- net/bridge/netfilter/nf_tables_bridge.c | 24 +++++++++++------------- net/ipv4/netfilter/nf_tables_arp.c | 2 +- net/ipv4/netfilter/nf_tables_ipv4.c | 2 +- net/ipv4/netfilter/nft_chain_nat_ipv4.c | 2 +- net/ipv4/netfilter/nft_chain_route_ipv4.c | 2 +- net/ipv6/netfilter/nf_tables_ipv6.c | 2 +- net/ipv6/netfilter/nft_chain_nat_ipv6.c | 2 +- net/ipv6/netfilter/nft_chain_route_ipv6.c | 2 +- 11 files changed, 25 insertions(+), 30 deletions(-) (limited to 'include') diff --git a/include/net/netfilter/nf_tables.h b/include/net/netfilter/nf_tables.h index b8cd60dcb4e1..804981980393 100644 --- a/include/net/netfilter/nf_tables.h +++ b/include/net/netfilter/nf_tables.h @@ -26,12 +26,11 @@ struct nft_pktinfo { static inline void nft_set_pktinfo(struct nft_pktinfo *pkt, const struct nf_hook_ops *ops, struct sk_buff *skb, - const struct net_device *in, - const struct net_device *out) + const struct nf_hook_state *state) { pkt->skb = skb; - pkt->in = pkt->xt.in = in; - pkt->out = pkt->xt.out = out; + pkt->in = pkt->xt.in = state->in; + pkt->out = pkt->xt.out = state->out; pkt->ops = ops; pkt->xt.hooknum = ops->hooknum; pkt->xt.family = ops->pf; diff --git a/include/net/netfilter/nf_tables_ipv4.h b/include/net/netfilter/nf_tables_ipv4.h index cba143fbd2e4..2df7f96902ee 100644 --- a/include/net/netfilter/nf_tables_ipv4.h +++ b/include/net/netfilter/nf_tables_ipv4.h @@ -8,12 +8,11 @@ static inline void nft_set_pktinfo_ipv4(struct nft_pktinfo *pkt, const struct nf_hook_ops *ops, struct sk_buff *skb, - const struct net_device *in, - const struct net_device *out) + const struct nf_hook_state *state) { struct iphdr *ip; - nft_set_pktinfo(pkt, ops, skb, in, out); + nft_set_pktinfo(pkt, ops, skb, state); ip = ip_hdr(pkt->skb); pkt->tprot = ip->protocol; diff --git a/include/net/netfilter/nf_tables_ipv6.h b/include/net/netfilter/nf_tables_ipv6.h index 74d976137658..97db2e3a5e65 100644 --- a/include/net/netfilter/nf_tables_ipv6.h +++ b/include/net/netfilter/nf_tables_ipv6.h @@ -8,13 +8,12 @@ static inline int nft_set_pktinfo_ipv6(struct nft_pktinfo *pkt, const struct nf_hook_ops *ops, struct sk_buff *skb, - const struct net_device *in, - const struct net_device *out) + const struct nf_hook_state *state) { int protohdr, thoff = 0; unsigned short frag_off; - nft_set_pktinfo(pkt, ops, skb, in, out); + nft_set_pktinfo(pkt, ops, skb, state); protohdr = ipv6_find_hdr(pkt->skb, &thoff, -1, &frag_off, NULL); /* If malformed, drop it */ diff --git a/net/bridge/netfilter/nf_tables_bridge.c b/net/bridge/netfilter/nf_tables_bridge.c index 2c46a47160a8..a343e62442b1 100644 --- a/net/bridge/netfilter/nf_tables_bridge.c +++ b/net/bridge/netfilter/nf_tables_bridge.c @@ -67,27 +67,25 @@ EXPORT_SYMBOL_GPL(nft_bridge_ip6hdr_validate); static inline void nft_bridge_set_pktinfo_ipv4(struct nft_pktinfo *pkt, const struct nf_hook_ops *ops, struct sk_buff *skb, - const struct net_device *in, - const struct net_device *out) + const struct nf_hook_state *state) { if (nft_bridge_iphdr_validate(skb)) - nft_set_pktinfo_ipv4(pkt, ops, skb, in, out); + nft_set_pktinfo_ipv4(pkt, ops, skb, state); else - nft_set_pktinfo(pkt, ops, skb, in, out); + nft_set_pktinfo(pkt, ops, skb, state); } static inline void nft_bridge_set_pktinfo_ipv6(struct nft_pktinfo *pkt, - const struct nf_hook_ops *ops, - struct sk_buff *skb, - const struct net_device *in, - const struct net_device *out) + const struct nf_hook_ops *ops, + struct sk_buff *skb, + const struct nf_hook_state *state) { #if IS_ENABLED(CONFIG_IPV6) if (nft_bridge_ip6hdr_validate(skb) && - nft_set_pktinfo_ipv6(pkt, ops, skb, in, out) == 0) + nft_set_pktinfo_ipv6(pkt, ops, skb, state) == 0) return; #endif - nft_set_pktinfo(pkt, ops, skb, in, out); + nft_set_pktinfo(pkt, ops, skb, state); } static unsigned int @@ -99,13 +97,13 @@ nft_do_chain_bridge(const struct nf_hook_ops *ops, switch (eth_hdr(skb)->h_proto) { case htons(ETH_P_IP): - nft_bridge_set_pktinfo_ipv4(&pkt, ops, skb, state->in, state->out); + nft_bridge_set_pktinfo_ipv4(&pkt, ops, skb, state); break; case htons(ETH_P_IPV6): - nft_bridge_set_pktinfo_ipv6(&pkt, ops, skb, state->in, state->out); + nft_bridge_set_pktinfo_ipv6(&pkt, ops, skb, state); break; default: - nft_set_pktinfo(&pkt, ops, skb, state->in, state->out); + nft_set_pktinfo(&pkt, ops, skb, state); break; } diff --git a/net/ipv4/netfilter/nf_tables_arp.c b/net/ipv4/netfilter/nf_tables_arp.c index fceb50e1e87d..8412268bbad1 100644 --- a/net/ipv4/netfilter/nf_tables_arp.c +++ b/net/ipv4/netfilter/nf_tables_arp.c @@ -21,7 +21,7 @@ nft_do_chain_arp(const struct nf_hook_ops *ops, { struct nft_pktinfo pkt; - nft_set_pktinfo(&pkt, ops, skb, state->in, state->out); + nft_set_pktinfo(&pkt, ops, skb, state); return nft_do_chain(&pkt, ops); } diff --git a/net/ipv4/netfilter/nf_tables_ipv4.c b/net/ipv4/netfilter/nf_tables_ipv4.c index 708e388e3dbe..aa180d3a69a5 100644 --- a/net/ipv4/netfilter/nf_tables_ipv4.c +++ b/net/ipv4/netfilter/nf_tables_ipv4.c @@ -24,7 +24,7 @@ static unsigned int nft_do_chain_ipv4(const struct nf_hook_ops *ops, { struct nft_pktinfo pkt; - nft_set_pktinfo_ipv4(&pkt, ops, skb, state->in, state->out); + nft_set_pktinfo_ipv4(&pkt, ops, skb, state); return nft_do_chain(&pkt, ops); } diff --git a/net/ipv4/netfilter/nft_chain_nat_ipv4.c b/net/ipv4/netfilter/nft_chain_nat_ipv4.c index f56bbb1ab1a0..bf5c30ae14e4 100644 --- a/net/ipv4/netfilter/nft_chain_nat_ipv4.c +++ b/net/ipv4/netfilter/nft_chain_nat_ipv4.c @@ -33,7 +33,7 @@ static unsigned int nft_nat_do_chain(const struct nf_hook_ops *ops, { struct nft_pktinfo pkt; - nft_set_pktinfo_ipv4(&pkt, ops, skb, state->in, state->out); + nft_set_pktinfo_ipv4(&pkt, ops, skb, state); return nft_do_chain(&pkt, ops); } diff --git a/net/ipv4/netfilter/nft_chain_route_ipv4.c b/net/ipv4/netfilter/nft_chain_route_ipv4.c index 073d0776ae7f..e335b0afdaf3 100644 --- a/net/ipv4/netfilter/nft_chain_route_ipv4.c +++ b/net/ipv4/netfilter/nft_chain_route_ipv4.c @@ -37,7 +37,7 @@ static unsigned int nf_route_table_hook(const struct nf_hook_ops *ops, ip_hdrlen(skb) < sizeof(struct iphdr)) return NF_ACCEPT; - nft_set_pktinfo_ipv4(&pkt, ops, skb, state->in, state->out); + nft_set_pktinfo_ipv4(&pkt, ops, skb, state); mark = skb->mark; iph = ip_hdr(skb); diff --git a/net/ipv6/netfilter/nf_tables_ipv6.c b/net/ipv6/netfilter/nf_tables_ipv6.c index 224bc8971a0b..c8148ba76d1a 100644 --- a/net/ipv6/netfilter/nf_tables_ipv6.c +++ b/net/ipv6/netfilter/nf_tables_ipv6.c @@ -23,7 +23,7 @@ static unsigned int nft_do_chain_ipv6(const struct nf_hook_ops *ops, struct nft_pktinfo pkt; /* malformed packet, drop it */ - if (nft_set_pktinfo_ipv6(&pkt, ops, skb, state->in, state->out) < 0) + if (nft_set_pktinfo_ipv6(&pkt, ops, skb, state) < 0) return NF_DROP; return nft_do_chain(&pkt, ops); diff --git a/net/ipv6/netfilter/nft_chain_nat_ipv6.c b/net/ipv6/netfilter/nft_chain_nat_ipv6.c index 11c6f75fcc4d..951bb458b7bd 100644 --- a/net/ipv6/netfilter/nft_chain_nat_ipv6.c +++ b/net/ipv6/netfilter/nft_chain_nat_ipv6.c @@ -31,7 +31,7 @@ static unsigned int nft_nat_do_chain(const struct nf_hook_ops *ops, { struct nft_pktinfo pkt; - nft_set_pktinfo_ipv6(&pkt, ops, skb, state->in, state->out); + nft_set_pktinfo_ipv6(&pkt, ops, skb, state); return nft_do_chain(&pkt, ops); } diff --git a/net/ipv6/netfilter/nft_chain_route_ipv6.c b/net/ipv6/netfilter/nft_chain_route_ipv6.c index c826c3c854b2..0dafdaac5e17 100644 --- a/net/ipv6/netfilter/nft_chain_route_ipv6.c +++ b/net/ipv6/netfilter/nft_chain_route_ipv6.c @@ -33,7 +33,7 @@ static unsigned int nf_route_table_hook(const struct nf_hook_ops *ops, u32 mark, flowlabel; /* malformed packet, drop it */ - if (nft_set_pktinfo_ipv6(&pkt, ops, skb, state->in, state->out) < 0) + if (nft_set_pktinfo_ipv6(&pkt, ops, skb, state) < 0) return NF_DROP; /* save source/dest address, mark, hoplimit, flowlabel, priority */ -- cgit v1.2.3 From b85c3dc9bd5347ad9540ec8d103b7c049c48b7cd Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Fri, 3 Apr 2015 21:18:46 -0400 Subject: netfilter: Pass nf_hook_state through arpt_do_table(). Signed-off-by: David S. Miller --- include/linux/netfilter_arp/arp_tables.h | 3 +-- net/ipv4/netfilter/arp_tables.c | 11 +++++------ net/ipv4/netfilter/arptable_filter.c | 2 +- 3 files changed, 7 insertions(+), 9 deletions(-) (limited to 'include') diff --git a/include/linux/netfilter_arp/arp_tables.h b/include/linux/netfilter_arp/arp_tables.h index cfb7191e6efa..c22a7fb8d0df 100644 --- a/include/linux/netfilter_arp/arp_tables.h +++ b/include/linux/netfilter_arp/arp_tables.h @@ -54,8 +54,7 @@ extern struct xt_table *arpt_register_table(struct net *net, extern void arpt_unregister_table(struct xt_table *table); extern unsigned int arpt_do_table(struct sk_buff *skb, unsigned int hook, - const struct net_device *in, - const struct net_device *out, + const struct nf_hook_state *state, struct xt_table *table); #ifdef CONFIG_COMPAT diff --git a/net/ipv4/netfilter/arp_tables.c b/net/ipv4/netfilter/arp_tables.c index f95b6f93814b..13bfe84bf3ca 100644 --- a/net/ipv4/netfilter/arp_tables.c +++ b/net/ipv4/netfilter/arp_tables.c @@ -248,8 +248,7 @@ struct arpt_entry *arpt_next_entry(const struct arpt_entry *entry) unsigned int arpt_do_table(struct sk_buff *skb, unsigned int hook, - const struct net_device *in, - const struct net_device *out, + const struct nf_hook_state *state, struct xt_table *table) { static const char nulldevname[IFNAMSIZ] __attribute__((aligned(sizeof(long)))); @@ -265,8 +264,8 @@ unsigned int arpt_do_table(struct sk_buff *skb, if (!pskb_may_pull(skb, arp_hdr_len(skb->dev))) return NF_DROP; - indev = in ? in->name : nulldevname; - outdev = out ? out->name : nulldevname; + indev = state->in ? state->in->name : nulldevname; + outdev = state->out ? state->out->name : nulldevname; local_bh_disable(); addend = xt_write_recseq_begin(); @@ -281,8 +280,8 @@ unsigned int arpt_do_table(struct sk_buff *skb, e = get_entry(table_base, private->hook_entry[hook]); back = get_entry(table_base, private->underflow[hook]); - acpar.in = in; - acpar.out = out; + acpar.in = state->in; + acpar.out = state->out; acpar.hooknum = hook; acpar.family = NFPROTO_ARP; acpar.hotdrop = false; diff --git a/net/ipv4/netfilter/arptable_filter.c b/net/ipv4/netfilter/arptable_filter.c index 6a641cb41062..93876d03120c 100644 --- a/net/ipv4/netfilter/arptable_filter.c +++ b/net/ipv4/netfilter/arptable_filter.c @@ -32,7 +32,7 @@ arptable_filter_hook(const struct nf_hook_ops *ops, struct sk_buff *skb, { const struct net *net = dev_net(state->in ? state->in : state->out); - return arpt_do_table(skb, ops->hooknum, state->in, state->out, + return arpt_do_table(skb, ops->hooknum, state, net->ipv4.arptable_filter); } -- cgit v1.2.3 From eab0fbfa41040f4f76b173cad17c0c8ed40cba33 Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Sat, 4 Apr 2015 13:38:25 -0700 Subject: ALSA: Use const struct ac97_quirk Use const to reduce data by ~3Kb. Signed-off-by: Joe Perches Signed-off-by: Takashi Iwai --- include/sound/ac97_codec.h | 4 +++- sound/pci/ac97/ac97_codec.c | 3 ++- sound/pci/ad1889.c | 2 +- sound/pci/atiixp.c | 2 +- sound/pci/cs5535audio/cs5535audio.c | 2 +- sound/pci/intel8x0.c | 2 +- sound/pci/via82xx.c | 2 +- 7 files changed, 10 insertions(+), 7 deletions(-) (limited to 'include') diff --git a/include/sound/ac97_codec.h b/include/sound/ac97_codec.h index d315a08d6c6d..0e9d75b49bed 100644 --- a/include/sound/ac97_codec.h +++ b/include/sound/ac97_codec.h @@ -608,7 +608,9 @@ struct ac97_quirk { int type; /* quirk type above */ }; -int snd_ac97_tune_hardware(struct snd_ac97 *ac97, struct ac97_quirk *quirk, const char *override); +int snd_ac97_tune_hardware(struct snd_ac97 *ac97, + const struct ac97_quirk *quirk, + const char *override); int snd_ac97_set_rate(struct snd_ac97 *ac97, int reg, unsigned int rate); /* diff --git a/sound/pci/ac97/ac97_codec.c b/sound/pci/ac97/ac97_codec.c index 5bca1a33fed6..82259ca61e64 100644 --- a/sound/pci/ac97/ac97_codec.c +++ b/sound/pci/ac97/ac97_codec.c @@ -2902,7 +2902,8 @@ static int apply_quirk_str(struct snd_ac97 *ac97, const char *typestr) * Return: Zero if successful, or a negative error code on failure. */ -int snd_ac97_tune_hardware(struct snd_ac97 *ac97, struct ac97_quirk *quirk, const char *override) +int snd_ac97_tune_hardware(struct snd_ac97 *ac97, + const struct ac97_quirk *quirk, const char *override) { int result; diff --git a/sound/pci/ad1889.c b/sound/pci/ad1889.c index 850a8c984c25..66ddd981d1d5 100644 --- a/sound/pci/ad1889.c +++ b/sound/pci/ad1889.c @@ -747,7 +747,7 @@ snd_ad1889_proc_init(struct snd_ad1889 *chip) snd_info_set_text_ops(entry, chip, snd_ad1889_proc_read); } -static struct ac97_quirk ac97_quirks[] = { +static const struct ac97_quirk ac97_quirks[] = { { .subvendor = 0x11d4, /* AD */ .subdevice = 0x1889, /* AD1889 */ diff --git a/sound/pci/atiixp.c b/sound/pci/atiixp.c index d5f15c9bbeda..42a20c806b39 100644 --- a/sound/pci/atiixp.c +++ b/sound/pci/atiixp.c @@ -1390,7 +1390,7 @@ static irqreturn_t snd_atiixp_interrupt(int irq, void *dev_id) * ac97 mixer section */ -static struct ac97_quirk ac97_quirks[] = { +static const struct ac97_quirk ac97_quirks[] = { { .subvendor = 0x103c, .subdevice = 0x006b, diff --git a/sound/pci/cs5535audio/cs5535audio.c b/sound/pci/cs5535audio/cs5535audio.c index 802c33f1cc59..963b912550d4 100644 --- a/sound/pci/cs5535audio/cs5535audio.c +++ b/sound/pci/cs5535audio/cs5535audio.c @@ -43,7 +43,7 @@ static char *ac97_quirk; module_param(ac97_quirk, charp, 0444); MODULE_PARM_DESC(ac97_quirk, "AC'97 board specific workarounds."); -static struct ac97_quirk ac97_quirks[] = { +static const struct ac97_quirk ac97_quirks[] = { #if 0 /* Not yet confirmed if all 5536 boards are HP only */ { .subvendor = PCI_VENDOR_ID_AMD, diff --git a/sound/pci/intel8x0.c b/sound/pci/intel8x0.c index 2c5484eeb963..749069aa6997 100644 --- a/sound/pci/intel8x0.c +++ b/sound/pci/intel8x0.c @@ -1795,7 +1795,7 @@ static struct ac97_pcm ac97_pcm_defs[] = { }, }; -static struct ac97_quirk ac97_quirks[] = { +static const struct ac97_quirk ac97_quirks[] = { { .subvendor = 0x0e11, .subdevice = 0x000e, diff --git a/sound/pci/via82xx.c b/sound/pci/via82xx.c index 8622283e89f3..3dd038bdb204 100644 --- a/sound/pci/via82xx.c +++ b/sound/pci/via82xx.c @@ -1812,7 +1812,7 @@ static void snd_via82xx_mixer_free_ac97(struct snd_ac97 *ac97) chip->ac97 = NULL; } -static struct ac97_quirk ac97_quirks[] = { +static const struct ac97_quirk ac97_quirks[] = { { .subvendor = 0x1106, .subdevice = 0x4161, -- cgit v1.2.3 From 0b040964a01ccfd880207ddc498e55e6c990a5d5 Mon Sep 17 00:00:00 2001 From: Christophe Ricard Date: Tue, 31 Mar 2015 08:02:18 +0200 Subject: nfc: hci: Add comment to explain NFC_HCI_MAX_PIPES According to specification etsi 102 622 chapter 4.4 pipes identifier is 7 bits long giving a 127 possible pipes value. Reported-by: Dan Carpenter Signed-off-by: Christophe Ricard Signed-off-by: Samuel Ortiz --- include/net/nfc/hci.h | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'include') diff --git a/include/net/nfc/hci.h b/include/net/nfc/hci.h index ab672b537dd4..020a814bc8ed 100644 --- a/include/net/nfc/hci.h +++ b/include/net/nfc/hci.h @@ -83,6 +83,10 @@ struct nfc_hci_pipe { }; #define NFC_HCI_MAX_CUSTOM_GATES 50 +/* + * According to specification 102 622 chapter 4.4 Pipes, + * the pipe identifier is 7 bits long. + */ #define NFC_HCI_MAX_PIPES 127 struct nfc_hci_init_data { u8 gate_count; -- cgit v1.2.3 From 0fc4a1291aeef338593e44e5396559df27f0bfdf Mon Sep 17 00:00:00 2001 From: Christophe Ricard Date: Tue, 31 Mar 2015 08:02:19 +0200 Subject: nfc: Reduce nfc_evt_transaction params length to 0 According to etsi 102 622 chapter 11.2.2.4 EVT_TRANSACTION, the nfc_evt_transaction parameters can be 0 up to 255 byte long. Signed-off-by: Christophe Ricard Signed-off-by: Samuel Ortiz --- include/net/nfc/nfc.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/net/nfc/nfc.h b/include/net/nfc/nfc.h index 73190e65d5c1..7ac029c07546 100644 --- a/include/net/nfc/nfc.h +++ b/include/net/nfc/nfc.h @@ -157,7 +157,7 @@ struct nfc_evt_transaction { u32 aid_len; u8 aid[NFC_MAX_AID_LENGTH]; u8 params_len; - u8 params[NFC_MAX_PARAMS_LENGTH]; + u8 params[0]; } __packed; struct nfc_genl_data { -- cgit v1.2.3 From 1f74f323e2c30b7fd53f6ad16c1990f2c80bbfc0 Mon Sep 17 00:00:00 2001 From: Christophe Ricard Date: Tue, 31 Mar 2015 08:02:21 +0200 Subject: nfc: nci: Add comment to explain NCI_HCI_MAX_PIPES According to specification etsi 102 622 chapter 4.4 pipes identifier is 7 bits long giving a 127 possible pipes value. Reported-by: Dan Carpenter Signed-off-by: Christophe Ricard Signed-off-by: Samuel Ortiz --- include/net/nfc/nci_core.h | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'include') diff --git a/include/net/nfc/nci_core.h b/include/net/nfc/nci_core.h index 34a6e09b3f7f..d4dcc7199fd7 100644 --- a/include/net/nfc/nci_core.h +++ b/include/net/nfc/nci_core.h @@ -138,6 +138,10 @@ struct nci_conn_info { #define NCI_HCI_INVALID_HOST 0x80 #define NCI_HCI_MAX_CUSTOM_GATES 50 +/* + * According to specification 102 622 chapter 4.4 Pipes, + * the pipe identifier is 7 bits long. + */ #define NCI_HCI_MAX_PIPES 127 struct nci_hci_gate { -- cgit v1.2.3 From 91bc4822c3d61b9bb7ef66d3b77948a4f9177954 Mon Sep 17 00:00:00 2001 From: Alexei Starovoitov Date: Wed, 1 Apr 2015 17:12:13 -0700 Subject: tc: bpf: add checksum helpers Commit 608cd71a9c7c ("tc: bpf: generalize pedit action") has added the possibility to mangle packet data to BPF programs in the tc pipeline. This patch adds two helpers bpf_l3_csum_replace() and bpf_l4_csum_replace() for fixing up the protocol checksums after the packet mangling. It also adds 'flags' argument to bpf_skb_store_bytes() helper to avoid unnecessary checksum recomputations when BPF programs adjusting l3/l4 checksums and documents all three helpers in uapi header. Moreover, a sample program is added to show how BPF programs can make use of the mangle and csum helpers. Signed-off-by: Alexei Starovoitov Acked-by: Daniel Borkmann Signed-off-by: David S. Miller --- include/uapi/linux/bpf.h | 38 +++++++++++++++- net/core/filter.c | 108 ++++++++++++++++++++++++++++++++++++++++++++-- samples/bpf/Makefile | 1 + samples/bpf/bpf_helpers.h | 7 +++ samples/bpf/tcbpf1_kern.c | 71 ++++++++++++++++++++++++++++++ 5 files changed, 220 insertions(+), 5 deletions(-) create mode 100644 samples/bpf/tcbpf1_kern.c (limited to 'include') diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h index 0db8580f3cca..23df3e7f8e7d 100644 --- a/include/uapi/linux/bpf.h +++ b/include/uapi/linux/bpf.h @@ -168,7 +168,43 @@ enum bpf_func_id { BPF_FUNC_map_delete_elem, /* int map_delete_elem(&map, &key) */ BPF_FUNC_get_prandom_u32, /* u32 prandom_u32(void) */ BPF_FUNC_get_smp_processor_id, /* u32 raw_smp_processor_id(void) */ - BPF_FUNC_skb_store_bytes, /* int skb_store_bytes(skb, offset, from, len) */ + + /** + * skb_store_bytes(skb, offset, from, len, flags) - store bytes into packet + * @skb: pointer to skb + * @offset: offset within packet from skb->data + * @from: pointer where to copy bytes from + * @len: number of bytes to store into packet + * @flags: bit 0 - if true, recompute skb->csum + * other bits - reserved + * Return: 0 on success + */ + BPF_FUNC_skb_store_bytes, + + /** + * l3_csum_replace(skb, offset, from, to, flags) - recompute IP checksum + * @skb: pointer to skb + * @offset: offset within packet where IP checksum is located + * @from: old value of header field + * @to: new value of header field + * @flags: bits 0-3 - size of header field + * other bits - reserved + * Return: 0 on success + */ + BPF_FUNC_l3_csum_replace, + + /** + * l4_csum_replace(skb, offset, from, to, flags) - recompute TCP/UDP checksum + * @skb: pointer to skb + * @offset: offset within packet where TCP/UDP checksum is located + * @from: old value of header field + * @to: new value of header field + * @flags: bits 0-3 - size of header field + * bit 4 - is pseudo header + * other bits - reserved + * Return: 0 on success + */ + BPF_FUNC_l4_csum_replace, __BPF_FUNC_MAX_ID, }; diff --git a/net/core/filter.c b/net/core/filter.c index 955a7d77decd..b669e75d2b36 100644 --- a/net/core/filter.c +++ b/net/core/filter.c @@ -1175,7 +1175,9 @@ int sk_attach_bpf(u32 ufd, struct sock *sk) return 0; } -static u64 bpf_skb_store_bytes(u64 r1, u64 r2, u64 r3, u64 r4, u64 r5) +#define BPF_RECOMPUTE_CSUM(flags) ((flags) & 1) + +static u64 bpf_skb_store_bytes(u64 r1, u64 r2, u64 r3, u64 r4, u64 flags) { struct sk_buff *skb = (struct sk_buff *) (long) r1; unsigned int offset = (unsigned int) r2; @@ -1192,7 +1194,7 @@ static u64 bpf_skb_store_bytes(u64 r1, u64 r2, u64 r3, u64 r4, u64 r5) * * so check for invalid 'offset' and too large 'len' */ - if (offset > 0xffff || len > sizeof(buf)) + if (unlikely(offset > 0xffff || len > sizeof(buf))) return -EFAULT; if (skb_cloned(skb) && !skb_clone_writable(skb, offset + len)) @@ -1202,7 +1204,8 @@ static u64 bpf_skb_store_bytes(u64 r1, u64 r2, u64 r3, u64 r4, u64 r5) if (unlikely(!ptr)) return -EFAULT; - skb_postpull_rcsum(skb, ptr, len); + if (BPF_RECOMPUTE_CSUM(flags)) + skb_postpull_rcsum(skb, ptr, len); memcpy(ptr, from, len); @@ -1210,7 +1213,7 @@ static u64 bpf_skb_store_bytes(u64 r1, u64 r2, u64 r3, u64 r4, u64 r5) /* skb_store_bits cannot return -EFAULT here */ skb_store_bits(skb, offset, ptr, len); - if (skb->ip_summed == CHECKSUM_COMPLETE) + if (BPF_RECOMPUTE_CSUM(flags) && skb->ip_summed == CHECKSUM_COMPLETE) skb->csum = csum_add(skb->csum, csum_partial(ptr, len, 0)); return 0; } @@ -1223,6 +1226,99 @@ const struct bpf_func_proto bpf_skb_store_bytes_proto = { .arg2_type = ARG_ANYTHING, .arg3_type = ARG_PTR_TO_STACK, .arg4_type = ARG_CONST_STACK_SIZE, + .arg5_type = ARG_ANYTHING, +}; + +#define BPF_HEADER_FIELD_SIZE(flags) ((flags) & 0x0f) +#define BPF_IS_PSEUDO_HEADER(flags) ((flags) & 0x10) + +static u64 bpf_l3_csum_replace(u64 r1, u64 offset, u64 from, u64 to, u64 flags) +{ + struct sk_buff *skb = (struct sk_buff *) (long) r1; + __sum16 sum, *ptr; + + if (unlikely(offset > 0xffff)) + return -EFAULT; + + if (skb_cloned(skb) && !skb_clone_writable(skb, offset + sizeof(sum))) + return -EFAULT; + + ptr = skb_header_pointer(skb, offset, sizeof(sum), &sum); + if (unlikely(!ptr)) + return -EFAULT; + + switch (BPF_HEADER_FIELD_SIZE(flags)) { + case 2: + csum_replace2(ptr, from, to); + break; + case 4: + csum_replace4(ptr, from, to); + break; + default: + return -EINVAL; + } + + if (ptr == &sum) + /* skb_store_bits guaranteed to not return -EFAULT here */ + skb_store_bits(skb, offset, ptr, sizeof(sum)); + + return 0; +} + +const struct bpf_func_proto bpf_l3_csum_replace_proto = { + .func = bpf_l3_csum_replace, + .gpl_only = false, + .ret_type = RET_INTEGER, + .arg1_type = ARG_PTR_TO_CTX, + .arg2_type = ARG_ANYTHING, + .arg3_type = ARG_ANYTHING, + .arg4_type = ARG_ANYTHING, + .arg5_type = ARG_ANYTHING, +}; + +static u64 bpf_l4_csum_replace(u64 r1, u64 offset, u64 from, u64 to, u64 flags) +{ + struct sk_buff *skb = (struct sk_buff *) (long) r1; + u32 is_pseudo = BPF_IS_PSEUDO_HEADER(flags); + __sum16 sum, *ptr; + + if (unlikely(offset > 0xffff)) + return -EFAULT; + + if (skb_cloned(skb) && !skb_clone_writable(skb, offset + sizeof(sum))) + return -EFAULT; + + ptr = skb_header_pointer(skb, offset, sizeof(sum), &sum); + if (unlikely(!ptr)) + return -EFAULT; + + switch (BPF_HEADER_FIELD_SIZE(flags)) { + case 2: + inet_proto_csum_replace2(ptr, skb, from, to, is_pseudo); + break; + case 4: + inet_proto_csum_replace4(ptr, skb, from, to, is_pseudo); + break; + default: + return -EINVAL; + } + + if (ptr == &sum) + /* skb_store_bits guaranteed to not return -EFAULT here */ + skb_store_bits(skb, offset, ptr, sizeof(sum)); + + return 0; +} + +const struct bpf_func_proto bpf_l4_csum_replace_proto = { + .func = bpf_l4_csum_replace, + .gpl_only = false, + .ret_type = RET_INTEGER, + .arg1_type = ARG_PTR_TO_CTX, + .arg2_type = ARG_ANYTHING, + .arg3_type = ARG_ANYTHING, + .arg4_type = ARG_ANYTHING, + .arg5_type = ARG_ANYTHING, }; static const struct bpf_func_proto * @@ -1250,6 +1346,10 @@ tc_cls_act_func_proto(enum bpf_func_id func_id) switch (func_id) { case BPF_FUNC_skb_store_bytes: return &bpf_skb_store_bytes_proto; + case BPF_FUNC_l3_csum_replace: + return &bpf_l3_csum_replace_proto; + case BPF_FUNC_l4_csum_replace: + return &bpf_l4_csum_replace_proto; default: return sk_filter_func_proto(func_id); } diff --git a/samples/bpf/Makefile b/samples/bpf/Makefile index b5b3600dcdf5..d24f51bca465 100644 --- a/samples/bpf/Makefile +++ b/samples/bpf/Makefile @@ -17,6 +17,7 @@ sockex2-objs := bpf_load.o libbpf.o sockex2_user.o always := $(hostprogs-y) always += sockex1_kern.o always += sockex2_kern.o +always += tcbpf1_kern.o HOSTCFLAGS += -I$(objtree)/usr/include diff --git a/samples/bpf/bpf_helpers.h b/samples/bpf/bpf_helpers.h index ca0333146006..72540ec1f003 100644 --- a/samples/bpf/bpf_helpers.h +++ b/samples/bpf/bpf_helpers.h @@ -37,4 +37,11 @@ struct bpf_map_def { unsigned int max_entries; }; +static int (*bpf_skb_store_bytes)(void *ctx, int off, void *from, int len, int flags) = + (void *) BPF_FUNC_skb_store_bytes; +static int (*bpf_l3_csum_replace)(void *ctx, int off, int from, int to, int flags) = + (void *) BPF_FUNC_l3_csum_replace; +static int (*bpf_l4_csum_replace)(void *ctx, int off, int from, int to, int flags) = + (void *) BPF_FUNC_l4_csum_replace; + #endif diff --git a/samples/bpf/tcbpf1_kern.c b/samples/bpf/tcbpf1_kern.c new file mode 100644 index 000000000000..7cf3f42a6e39 --- /dev/null +++ b/samples/bpf/tcbpf1_kern.c @@ -0,0 +1,71 @@ +#include +#include +#include +#include +#include +#include +#include "bpf_helpers.h" + +/* compiler workaround */ +#define _htonl __builtin_bswap32 + +static inline void set_dst_mac(struct __sk_buff *skb, char *mac) +{ + bpf_skb_store_bytes(skb, 0, mac, ETH_ALEN, 1); +} + +/* use 1 below for ingress qdisc and 0 for egress */ +#if 0 +#undef ETH_HLEN +#define ETH_HLEN 0 +#endif + +#define IP_CSUM_OFF (ETH_HLEN + offsetof(struct iphdr, check)) +#define TOS_OFF (ETH_HLEN + offsetof(struct iphdr, tos)) + +static inline void set_ip_tos(struct __sk_buff *skb, __u8 new_tos) +{ + __u8 old_tos = load_byte(skb, TOS_OFF); + + bpf_l3_csum_replace(skb, IP_CSUM_OFF, htons(old_tos), htons(new_tos), 2); + bpf_skb_store_bytes(skb, TOS_OFF, &new_tos, sizeof(new_tos), 0); +} + +#define TCP_CSUM_OFF (ETH_HLEN + sizeof(struct iphdr) + offsetof(struct tcphdr, check)) +#define IP_SRC_OFF (ETH_HLEN + offsetof(struct iphdr, saddr)) + +#define IS_PSEUDO 0x10 + +static inline void set_tcp_ip_src(struct __sk_buff *skb, __u32 new_ip) +{ + __u32 old_ip = _htonl(load_word(skb, IP_SRC_OFF)); + + bpf_l4_csum_replace(skb, TCP_CSUM_OFF, old_ip, new_ip, IS_PSEUDO | sizeof(new_ip)); + bpf_l3_csum_replace(skb, IP_CSUM_OFF, old_ip, new_ip, sizeof(new_ip)); + bpf_skb_store_bytes(skb, IP_SRC_OFF, &new_ip, sizeof(new_ip), 0); +} + +#define TCP_DPORT_OFF (ETH_HLEN + sizeof(struct iphdr) + offsetof(struct tcphdr, dest)) +static inline void set_tcp_dest_port(struct __sk_buff *skb, __u16 new_port) +{ + __u16 old_port = htons(load_half(skb, TCP_DPORT_OFF)); + + bpf_l4_csum_replace(skb, TCP_CSUM_OFF, old_port, new_port, sizeof(new_port)); + bpf_skb_store_bytes(skb, TCP_DPORT_OFF, &new_port, sizeof(new_port), 0); +} + +SEC("classifier") +int bpf_prog1(struct __sk_buff *skb) +{ + __u8 proto = load_byte(skb, ETH_HLEN + offsetof(struct iphdr, protocol)); + long *value; + + if (proto == IPPROTO_TCP) { + set_ip_tos(skb, 8); + set_tcp_ip_src(skb, 0xA010101); + set_tcp_dest_port(skb, 5001); + } + + return 0; +} +char _license[] SEC("license") = "GPL"; -- cgit v1.2.3 From 4c385b25fab119144bffb255ad77712fe586ac10 Mon Sep 17 00:00:00 2001 From: Archit Taneja Date: Thu, 2 Apr 2015 11:20:41 +0530 Subject: clk: qcom: Add EBI2 clocks for IPQ806x The NAND controller within EBI2 requires EBI2_CLK and EBI2_ALWAYS_ON_CLK clocks. Create structs for these clocks so that they can be used by the NAND controller driver. Add an entry for EBI2_AON_CLK in the gcc-ipq806x DT binding document. Signed-off-by: Archit Taneja Signed-off-by: Stephen Boyd --- drivers/clk/qcom/gcc-ipq806x.c | 32 ++++++++++++++++++++++++++++ include/dt-bindings/clock/qcom,gcc-ipq806x.h | 1 + 2 files changed, 33 insertions(+) (limited to 'include') diff --git a/drivers/clk/qcom/gcc-ipq806x.c b/drivers/clk/qcom/gcc-ipq806x.c index ee73cc7f6e55..a50936a17376 100644 --- a/drivers/clk/qcom/gcc-ipq806x.c +++ b/drivers/clk/qcom/gcc-ipq806x.c @@ -2172,6 +2172,36 @@ static struct clk_branch usb_fs1_h_clk = { }, }; +static struct clk_branch ebi2_clk = { + .hwcg_reg = 0x3b00, + .hwcg_bit = 6, + .halt_reg = 0x2fcc, + .halt_bit = 1, + .clkr = { + .enable_reg = 0x3b00, + .enable_mask = BIT(4), + .hw.init = &(struct clk_init_data){ + .name = "ebi2_clk", + .ops = &clk_branch_ops, + .flags = CLK_IS_ROOT, + }, + }, +}; + +static struct clk_branch ebi2_aon_clk = { + .halt_reg = 0x2fcc, + .halt_bit = 0, + .clkr = { + .enable_reg = 0x3b00, + .enable_mask = BIT(8), + .hw.init = &(struct clk_init_data){ + .name = "ebi2_always_on_clk", + .ops = &clk_branch_ops, + .flags = CLK_IS_ROOT, + }, + }, +}; + static struct clk_regmap *gcc_ipq806x_clks[] = { [PLL0] = &pll0.clkr, [PLL0_VOTE] = &pll0_vote, @@ -2275,6 +2305,8 @@ static struct clk_regmap *gcc_ipq806x_clks[] = { [USB_FS1_XCVR_SRC] = &usb_fs1_xcvr_clk_src.clkr, [USB_FS1_XCVR_CLK] = &usb_fs1_xcvr_clk.clkr, [USB_FS1_SYSTEM_CLK] = &usb_fs1_sys_clk.clkr, + [EBI2_CLK] = &ebi2_clk.clkr, + [EBI2_AON_CLK] = &ebi2_aon_clk.clkr, }; static const struct qcom_reset_map gcc_ipq806x_resets[] = { diff --git a/include/dt-bindings/clock/qcom,gcc-ipq806x.h b/include/dt-bindings/clock/qcom,gcc-ipq806x.h index 04fb29ae30e6..ebd63fd05649 100644 --- a/include/dt-bindings/clock/qcom,gcc-ipq806x.h +++ b/include/dt-bindings/clock/qcom,gcc-ipq806x.h @@ -288,5 +288,6 @@ #define UBI32_CORE2_CLK_SRC 278 #define UBI32_CORE1_CLK 279 #define UBI32_CORE2_CLK 280 +#define EBI2_AON_CLK 281 #endif -- cgit v1.2.3 From 0fefcb6876d0d650ac2255c57051b31c48c1b165 Mon Sep 17 00:00:00 2001 From: Carlo Caione Date: Sun, 29 Mar 2015 12:56:42 +0200 Subject: pinctrl: Add support for Meson8b This patch adds support for the AmLogic Meson8b SoC. Signed-off-by: Carlo Caione Acked-by: Beniamino Galvani Signed-off-by: Linus Walleij --- drivers/pinctrl/meson/Makefile | 2 +- drivers/pinctrl/meson/pinctrl-meson.c | 4 + drivers/pinctrl/meson/pinctrl-meson.h | 1 + drivers/pinctrl/meson/pinctrl-meson8b.c | 899 ++++++++++++++++++++++++++++++++ include/dt-bindings/gpio/meson8b-gpio.h | 32 ++ 5 files changed, 937 insertions(+), 1 deletion(-) create mode 100644 drivers/pinctrl/meson/pinctrl-meson8b.c create mode 100644 include/dt-bindings/gpio/meson8b-gpio.h (limited to 'include') diff --git a/drivers/pinctrl/meson/Makefile b/drivers/pinctrl/meson/Makefile index eafc216067a4..c751d22fdf29 100644 --- a/drivers/pinctrl/meson/Makefile +++ b/drivers/pinctrl/meson/Makefile @@ -1,2 +1,2 @@ -obj-y += pinctrl-meson8.o +obj-y += pinctrl-meson8.o pinctrl-meson8b.o obj-y += pinctrl-meson.o diff --git a/drivers/pinctrl/meson/pinctrl-meson.c b/drivers/pinctrl/meson/pinctrl-meson.c index e0195c1081c4..edcd140e0899 100644 --- a/drivers/pinctrl/meson/pinctrl-meson.c +++ b/drivers/pinctrl/meson/pinctrl-meson.c @@ -545,6 +545,10 @@ static const struct of_device_id meson_pinctrl_dt_match[] = { .compatible = "amlogic,meson8-pinctrl", .data = &meson8_pinctrl_data, }, + { + .compatible = "amlogic,meson8b-pinctrl", + .data = &meson8b_pinctrl_data, + }, { }, }; MODULE_DEVICE_TABLE(of, meson_pinctrl_dt_match); diff --git a/drivers/pinctrl/meson/pinctrl-meson.h b/drivers/pinctrl/meson/pinctrl-meson.h index bc48c781ca51..0fe7d53849ce 100644 --- a/drivers/pinctrl/meson/pinctrl-meson.h +++ b/drivers/pinctrl/meson/pinctrl-meson.h @@ -209,3 +209,4 @@ struct meson_pinctrl { #define MESON_PIN(x, b) PINCTRL_PIN(PIN(x, b), #x) extern struct meson_pinctrl_data meson8_pinctrl_data; +extern struct meson_pinctrl_data meson8b_pinctrl_data; diff --git a/drivers/pinctrl/meson/pinctrl-meson8b.c b/drivers/pinctrl/meson/pinctrl-meson8b.c new file mode 100644 index 000000000000..2f7ea6229880 --- /dev/null +++ b/drivers/pinctrl/meson/pinctrl-meson8b.c @@ -0,0 +1,899 @@ +/* + * Pin controller and GPIO driver for Amlogic Meson8b. + * + * Copyright (C) 2015 Endless Mobile, Inc. + * Author: Carlo Caione + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include "pinctrl-meson.h" + +#define AO_OFF 130 + +static const struct pinctrl_pin_desc meson8b_pins[] = { + MESON_PIN(GPIOX_0, 0), + MESON_PIN(GPIOX_1, 0), + MESON_PIN(GPIOX_2, 0), + MESON_PIN(GPIOX_3, 0), + MESON_PIN(GPIOX_4, 0), + MESON_PIN(GPIOX_5, 0), + MESON_PIN(GPIOX_6, 0), + MESON_PIN(GPIOX_7, 0), + MESON_PIN(GPIOX_8, 0), + MESON_PIN(GPIOX_9, 0), + MESON_PIN(GPIOX_10, 0), + MESON_PIN(GPIOX_11, 0), + MESON_PIN(GPIOX_16, 0), + MESON_PIN(GPIOX_17, 0), + MESON_PIN(GPIOX_18, 0), + MESON_PIN(GPIOX_19, 0), + MESON_PIN(GPIOX_20, 0), + MESON_PIN(GPIOX_21, 0), + + MESON_PIN(GPIOY_0, 0), + MESON_PIN(GPIOY_1, 0), + MESON_PIN(GPIOY_3, 0), + MESON_PIN(GPIOY_6, 0), + MESON_PIN(GPIOY_7, 0), + MESON_PIN(GPIOY_8, 0), + MESON_PIN(GPIOY_9, 0), + MESON_PIN(GPIOY_10, 0), + MESON_PIN(GPIOY_11, 0), + MESON_PIN(GPIOY_12, 0), + MESON_PIN(GPIOY_13, 0), + MESON_PIN(GPIOY_14, 0), + + MESON_PIN(GPIODV_9, 0), + MESON_PIN(GPIODV_24, 0), + MESON_PIN(GPIODV_25, 0), + MESON_PIN(GPIODV_26, 0), + MESON_PIN(GPIODV_27, 0), + MESON_PIN(GPIODV_28, 0), + MESON_PIN(GPIODV_29, 0), + + MESON_PIN(GPIOH_0, 0), + MESON_PIN(GPIOH_1, 0), + MESON_PIN(GPIOH_2, 0), + MESON_PIN(GPIOH_3, 0), + MESON_PIN(GPIOH_4, 0), + MESON_PIN(GPIOH_5, 0), + MESON_PIN(GPIOH_6, 0), + MESON_PIN(GPIOH_7, 0), + MESON_PIN(GPIOH_8, 0), + MESON_PIN(GPIOH_9, 0), + + MESON_PIN(CARD_0, 0), + MESON_PIN(CARD_1, 0), + MESON_PIN(CARD_2, 0), + MESON_PIN(CARD_3, 0), + MESON_PIN(CARD_4, 0), + MESON_PIN(CARD_5, 0), + MESON_PIN(CARD_6, 0), + + MESON_PIN(BOOT_0, 0), + MESON_PIN(BOOT_1, 0), + MESON_PIN(BOOT_2, 0), + MESON_PIN(BOOT_3, 0), + MESON_PIN(BOOT_4, 0), + MESON_PIN(BOOT_5, 0), + MESON_PIN(BOOT_6, 0), + MESON_PIN(BOOT_7, 0), + MESON_PIN(BOOT_8, 0), + MESON_PIN(BOOT_9, 0), + MESON_PIN(BOOT_10, 0), + MESON_PIN(BOOT_11, 0), + MESON_PIN(BOOT_12, 0), + MESON_PIN(BOOT_13, 0), + MESON_PIN(BOOT_14, 0), + MESON_PIN(BOOT_15, 0), + MESON_PIN(BOOT_16, 0), + MESON_PIN(BOOT_17, 0), + MESON_PIN(BOOT_18, 0), + + MESON_PIN(DIF_0_P, 0), + MESON_PIN(DIF_0_N, 0), + MESON_PIN(DIF_1_P, 0), + MESON_PIN(DIF_1_N, 0), + MESON_PIN(DIF_2_P, 0), + MESON_PIN(DIF_2_N, 0), + MESON_PIN(DIF_3_P, 0), + MESON_PIN(DIF_3_N, 0), + MESON_PIN(DIF_4_P, 0), + MESON_PIN(DIF_4_N, 0), + + MESON_PIN(GPIOAO_0, AO_OFF), + MESON_PIN(GPIOAO_1, AO_OFF), + MESON_PIN(GPIOAO_2, AO_OFF), + MESON_PIN(GPIOAO_3, AO_OFF), + MESON_PIN(GPIOAO_4, AO_OFF), + MESON_PIN(GPIOAO_5, AO_OFF), + MESON_PIN(GPIOAO_6, AO_OFF), + MESON_PIN(GPIOAO_7, AO_OFF), + MESON_PIN(GPIOAO_8, AO_OFF), + MESON_PIN(GPIOAO_9, AO_OFF), + MESON_PIN(GPIOAO_10, AO_OFF), + MESON_PIN(GPIOAO_11, AO_OFF), + MESON_PIN(GPIOAO_12, AO_OFF), + MESON_PIN(GPIOAO_13, AO_OFF), + MESON_PIN(GPIO_BSD_EN, AO_OFF), + MESON_PIN(GPIO_TEST_N, AO_OFF), +}; + +/* bank X */ +static const unsigned int sd_d0_a_pins[] = { PIN(GPIOX_0, 0) }; +static const unsigned int sd_d1_a_pins[] = { PIN(GPIOX_1, 0) }; +static const unsigned int sd_d2_a_pins[] = { PIN(GPIOX_2, 0) }; +static const unsigned int sd_d3_a_pins[] = { PIN(GPIOX_3, 0) }; +static const unsigned int sdxc_d0_0_a_pins[] = { PIN(GPIOX_4, 0) }; +static const unsigned int sdxc_d47_a_pins[] = { PIN(GPIOX_4, 0), PIN(GPIOX_5, 0), + PIN(GPIOX_6, 0), PIN(GPIOX_7, 0) }; +static const unsigned int sdxc_d13_0_a_pins[] = { PIN(GPIOX_5, 0), PIN(GPIOX_6, 0), + PIN(GPIOX_7, 0) }; +static const unsigned int sd_clk_a_pins[] = { PIN(GPIOX_8, 0) }; +static const unsigned int sd_cmd_a_pins[] = { PIN(GPIOX_9, 0) }; +static const unsigned int xtal_32k_out_pins[] = { PIN(GPIOX_10, 0) }; +static const unsigned int xtal_24m_out_pins[] = { PIN(GPIOX_11, 0) }; +static const unsigned int uart_tx_b0_pins[] = { PIN(GPIOX_16, 0) }; +static const unsigned int uart_rx_b0_pins[] = { PIN(GPIOX_17, 0) }; +static const unsigned int uart_cts_b0_pins[] = { PIN(GPIOX_18, 0) }; +static const unsigned int uart_rts_b0_pins[] = { PIN(GPIOX_19, 0) }; + +static const unsigned int sdxc_d0_1_a_pins[] = { PIN(GPIOX_0, 0) }; +static const unsigned int sdxc_d13_1_a_pins[] = { PIN(GPIOX_1, 0), PIN(GPIOX_2, 0), + PIN(GPIOX_3, 0) }; +static const unsigned int pcm_out_a_pins[] = { PIN(GPIOX_4, 0) }; +static const unsigned int pcm_in_a_pins[] = { PIN(GPIOX_5, 0) }; +static const unsigned int pcm_fs_a_pins[] = { PIN(GPIOX_6, 0) }; +static const unsigned int pcm_clk_a_pins[] = { PIN(GPIOX_7, 0) }; +static const unsigned int sdxc_clk_a_pins[] = { PIN(GPIOX_8, 0) }; +static const unsigned int sdxc_cmd_a_pins[] = { PIN(GPIOX_9, 0) }; +static const unsigned int pwm_vs_0_pins[] = { PIN(GPIOX_10, 0) }; +static const unsigned int pwm_e_pins[] = { PIN(GPIOX_10, 0) }; +static const unsigned int pwm_vs_1_pins[] = { PIN(GPIOX_11, 0) }; + +static const unsigned int uart_tx_a_pins[] = { PIN(GPIOX_4, 0) }; +static const unsigned int uart_rx_a_pins[] = { PIN(GPIOX_5, 0) }; +static const unsigned int uart_cts_a_pins[] = { PIN(GPIOX_6, 0) }; +static const unsigned int uart_rts_a_pins[] = { PIN(GPIOX_7, 0) }; +static const unsigned int uart_tx_b1_pins[] = { PIN(GPIOX_8, 0) }; +static const unsigned int uart_rx_b1_pins[] = { PIN(GPIOX_9, 0) }; +static const unsigned int uart_cts_b1_pins[] = { PIN(GPIOX_10, 0) }; +static const unsigned int uart_rts_b1_pins[] = { PIN(GPIOX_20, 0) }; + +static const unsigned int iso7816_0_clk_pins[] = { PIN(GPIOX_6, 0) }; +static const unsigned int iso7816_0_data_pins[] = { PIN(GPIOX_7, 0) }; +static const unsigned int spi_sclk_0_pins[] = { PIN(GPIOX_8, 0) }; +static const unsigned int spi_miso_0_pins[] = { PIN(GPIOX_9, 0) }; +static const unsigned int spi_mosi_0_pins[] = { PIN(GPIOX_10, 0) }; +static const unsigned int iso7816_det_pins[] = { PIN(GPIOX_16, 0) }; +static const unsigned int iso7816_reset_pins[] = { PIN(GPIOX_17, 0) }; +static const unsigned int iso7816_1_clk_pins[] = { PIN(GPIOX_18, 0) }; +static const unsigned int iso7816_1_data_pins[] = { PIN(GPIOX_19, 0) }; +static const unsigned int spi_ss0_0_pins[] = { PIN(GPIOX_20, 0) }; + +static const unsigned int tsin_clk_b_pins[] = { PIN(GPIOX_8, 0) }; +static const unsigned int tsin_sop_b_pins[] = { PIN(GPIOX_9, 0) }; +static const unsigned int tsin_d0_b_pins[] = { PIN(GPIOX_10, 0) }; +static const unsigned int pwm_b_pins[] = { PIN(GPIOX_11, 0) }; +static const unsigned int i2c_sda_d0_pins[] = { PIN(GPIOX_16, 0) }; +static const unsigned int i2c_sck_d0_pins[] = { PIN(GPIOX_17, 0) }; +static const unsigned int tsin_d_valid_b_pins[] = { PIN(GPIOX_20, 0) }; + +/* bank Y */ +static const unsigned int tsin_d_valid_a_pins[] = { PIN(GPIOY_0, 0) }; +static const unsigned int tsin_sop_a_pins[] = { PIN(GPIOY_1, 0) }; +static const unsigned int tsin_d17_a_pins[] = { PIN(GPIOY_6, 0), PIN(GPIOY_7, 0), + PIN(GPIOY_10, 0), PIN(GPIOY_11, 0), + PIN(GPIOY_12, 0), PIN(GPIOY_13, 0), + PIN(GPIOY_14, 0) }; +static const unsigned int tsin_clk_a_pins[] = { PIN(GPIOY_8, 0) }; +static const unsigned int tsin_d0_a_pins[] = { PIN(GPIOY_9, 0) }; + +static const unsigned int spdif_out_0_pins[] = { PIN(GPIOY_3, 0) }; + +static const unsigned int xtal_24m_pins[] = { PIN(GPIOY_3, 0) }; +static const unsigned int iso7816_2_clk_pins[] = { PIN(GPIOY_13, 0) }; +static const unsigned int iso7816_2_data_pins[] = { PIN(GPIOY_14, 0) }; + +/* bank DV */ +static const unsigned int pwm_d_pins[] = { PIN(GPIODV_28, 0) }; +static const unsigned int pwm_c0_pins[] = { PIN(GPIODV_29, 0) }; + +static const unsigned int pwm_vs_2_pins[] = { PIN(GPIODV_9, 0) }; +static const unsigned int pwm_vs_3_pins[] = { PIN(GPIODV_28, 0) }; +static const unsigned int pwm_vs_4_pins[] = { PIN(GPIODV_29, 0) }; + +static const unsigned int xtal24_out_pins[] = { PIN(GPIODV_29, 0) }; + +static const unsigned int uart_tx_c_pins[] = { PIN(GPIODV_24, 0) }; +static const unsigned int uart_rx_c_pins[] = { PIN(GPIODV_25, 0) }; +static const unsigned int uart_cts_c_pins[] = { PIN(GPIODV_26, 0) }; +static const unsigned int uart_rts_c_pins[] = { PIN(GPIODV_27, 0) }; + +static const unsigned int pwm_c1_pins[] = { PIN(GPIODV_9, 0) }; + +static const unsigned int i2c_sda_a_pins[] = { PIN(GPIODV_24, 0) }; +static const unsigned int i2c_sck_a_pins[] = { PIN(GPIODV_25, 0) }; +static const unsigned int i2c_sda_b0_pins[] = { PIN(GPIODV_26, 0) }; +static const unsigned int i2c_sck_b0_pins[] = { PIN(GPIODV_27, 0) }; +static const unsigned int i2c_sda_c0_pins[] = { PIN(GPIODV_28, 0) }; +static const unsigned int i2c_sck_c0_pins[] = { PIN(GPIODV_29, 0) }; + +/* bank H */ +static const unsigned int hdmi_hpd_pins[] = { PIN(GPIOH_0, 0) }; +static const unsigned int hdmi_sda_pins[] = { PIN(GPIOH_1, 0) }; +static const unsigned int hdmi_scl_pins[] = { PIN(GPIOH_2, 0) }; +static const unsigned int hdmi_cec_0_pins[] = { PIN(GPIOH_3, 0) }; +static const unsigned int eth_txd1_0_pins[] = { PIN(GPIOH_5, 0) }; +static const unsigned int eth_txd0_0_pins[] = { PIN(GPIOH_6, 0) }; +static const unsigned int clk_24m_out_pins[] = { PIN(GPIOH_9, 0) }; + +static const unsigned int spi_ss1_pins[] = { PIN(GPIOH_0, 0) }; +static const unsigned int spi_ss2_pins[] = { PIN(GPIOH_1, 0) }; +static const unsigned int spi_ss0_1_pins[] = { PIN(GPIOH_3, 0) }; +static const unsigned int spi_miso_1_pins[] = { PIN(GPIOH_4, 0) }; +static const unsigned int spi_mosi_1_pins[] = { PIN(GPIOH_5, 0) }; +static const unsigned int spi_sclk_1_pins[] = { PIN(GPIOH_6, 0) }; + +static const unsigned int eth_txd3_pins[] = { PIN(GPIOH_7, 0) }; +static const unsigned int eth_txd2_pins[] = { PIN(GPIOH_8, 0) }; +static const unsigned int eth_tx_clk_pins[] = { PIN(GPIOH_9, 0) }; + +static const unsigned int i2c_sda_b1_pins[] = { PIN(GPIOH_3, 0) }; +static const unsigned int i2c_sck_b1_pins[] = { PIN(GPIOH_4, 0) }; +static const unsigned int i2c_sda_c1_pins[] = { PIN(GPIOH_5, 0) }; +static const unsigned int i2c_sck_c1_pins[] = { PIN(GPIOH_6, 0) }; +static const unsigned int i2c_sda_d1_pins[] = { PIN(GPIOH_7, 0) }; +static const unsigned int i2c_sck_d1_pins[] = { PIN(GPIOH_8, 0) }; + +/* bank BOOT */ +static const unsigned int nand_io_pins[] = { PIN(BOOT_0, 0), PIN(BOOT_1, 0), + PIN(BOOT_2, 0), PIN(BOOT_3, 0), + PIN(BOOT_4, 0), PIN(BOOT_5, 0), + PIN(BOOT_6, 0), PIN(BOOT_7, 0) }; +static const unsigned int nand_io_ce0_pins[] = { PIN(BOOT_8, 0) }; +static const unsigned int nand_io_ce1_pins[] = { PIN(BOOT_9, 0) }; +static const unsigned int nand_io_rb0_pins[] = { PIN(BOOT_10, 0) }; +static const unsigned int nand_ale_pins[] = { PIN(BOOT_11, 0) }; +static const unsigned int nand_cle_pins[] = { PIN(BOOT_12, 0) }; +static const unsigned int nand_wen_clk_pins[] = { PIN(BOOT_13, 0) }; +static const unsigned int nand_ren_clk_pins[] = { PIN(BOOT_14, 0) }; +static const unsigned int nand_dqs_0_pins[] = { PIN(BOOT_15, 0) }; +static const unsigned int nand_dqs_1_pins[] = { PIN(BOOT_18, 0) }; + +static const unsigned int sdxc_d0_c_pins[] = { PIN(BOOT_0, 0)}; +static const unsigned int sdxc_d13_c_pins[] = { PIN(BOOT_1, 0), PIN(BOOT_2, 0), + PIN(BOOT_3, 0) }; +static const unsigned int sdxc_d47_c_pins[] = { PIN(BOOT_4, 0), PIN(BOOT_5, 0), + PIN(BOOT_6, 0), PIN(BOOT_7, 0) }; +static const unsigned int sdxc_clk_c_pins[] = { PIN(BOOT_8, 0) }; +static const unsigned int sdxc_cmd_c_pins[] = { PIN(BOOT_10, 0) }; +static const unsigned int nor_d_pins[] = { PIN(BOOT_11, 0) }; +static const unsigned int nor_q_pins[] = { PIN(BOOT_12, 0) }; +static const unsigned int nor_c_pins[] = { PIN(BOOT_13, 0) }; +static const unsigned int nor_cs_pins[] = { PIN(BOOT_18, 0) }; + +static const unsigned int sd_d0_c_pins[] = { PIN(BOOT_0, 0) }; +static const unsigned int sd_d1_c_pins[] = { PIN(BOOT_1, 0) }; +static const unsigned int sd_d2_c_pins[] = { PIN(BOOT_2, 0) }; +static const unsigned int sd_d3_c_pins[] = { PIN(BOOT_3, 0) }; +static const unsigned int sd_cmd_c_pins[] = { PIN(BOOT_8, 0) }; +static const unsigned int sd_clk_c_pins[] = { PIN(BOOT_10, 0) }; + +/* bank CARD */ +static const unsigned int sd_d1_b_pins[] = { PIN(CARD_0, 0) }; +static const unsigned int sd_d0_b_pins[] = { PIN(CARD_1, 0) }; +static const unsigned int sd_clk_b_pins[] = { PIN(CARD_2, 0) }; +static const unsigned int sd_cmd_b_pins[] = { PIN(CARD_3, 0) }; +static const unsigned int sd_d3_b_pins[] = { PIN(CARD_4, 0) }; +static const unsigned int sd_d2_b_pins[] = { PIN(CARD_5, 0) }; + +static const unsigned int sdxc_d13_b_pins[] = { PIN(CARD_0, 0), PIN(CARD_4, 0), + PIN(CARD_5, 0) }; +static const unsigned int sdxc_d0_b_pins[] = { PIN(CARD_1, 0) }; +static const unsigned int sdxc_clk_b_pins[] = { PIN(CARD_2, 0) }; +static const unsigned int sdxc_cmd_b_pins[] = { PIN(CARD_3, 0) }; + +/* bank AO */ +static const unsigned int uart_tx_ao_a_pins[] = { PIN(GPIOAO_0, AO_OFF) }; +static const unsigned int uart_rx_ao_a_pins[] = { PIN(GPIOAO_1, AO_OFF) }; +static const unsigned int uart_cts_ao_a_pins[] = { PIN(GPIOAO_2, AO_OFF) }; +static const unsigned int uart_rts_ao_a_pins[] = { PIN(GPIOAO_3, AO_OFF) }; +static const unsigned int i2c_mst_sck_ao_pins[] = { PIN(GPIOAO_4, AO_OFF) }; +static const unsigned int i2c_mst_sda_ao_pins[] = { PIN(GPIOAO_5, AO_OFF) }; +static const unsigned int clk_32k_in_out_pins[] = { PIN(GPIOAO_6, AO_OFF) }; +static const unsigned int remote_input_pins[] = { PIN(GPIOAO_7, AO_OFF) }; +static const unsigned int hdmi_cec_1_pins[] = { PIN(GPIOAO_12, AO_OFF) }; +static const unsigned int ir_blaster_pins[] = { PIN(GPIOAO_13, AO_OFF) }; + +static const unsigned int pwm_c2_pins[] = { PIN(GPIOAO_3, AO_OFF) }; +static const unsigned int i2c_sck_ao_pins[] = { PIN(GPIOAO_4, AO_OFF) }; +static const unsigned int i2c_sda_ao_pins[] = { PIN(GPIOAO_5, AO_OFF) }; +static const unsigned int ir_remote_out_pins[] = { PIN(GPIOAO_7, AO_OFF) }; +static const unsigned int i2s_am_clk_out_pins[] = { PIN(GPIOAO_8, AO_OFF) }; +static const unsigned int i2s_ao_clk_out_pins[] = { PIN(GPIOAO_9, AO_OFF) }; +static const unsigned int i2s_lr_clk_out_pins[] = { PIN(GPIOAO_10, AO_OFF) }; +static const unsigned int i2s_out_01_pins[] = { PIN(GPIOAO_11, AO_OFF) }; + +static const unsigned int uart_tx_ao_b0_pins[] = { PIN(GPIOAO_0, AO_OFF) }; +static const unsigned int uart_rx_ao_b0_pins[] = { PIN(GPIOAO_1, AO_OFF) }; +static const unsigned int uart_cts_ao_b_pins[] = { PIN(GPIOAO_2, AO_OFF) }; +static const unsigned int uart_rts_ao_b_pins[] = { PIN(GPIOAO_3, AO_OFF) }; +static const unsigned int uart_tx_ao_b1_pins[] = { PIN(GPIOAO_4, AO_OFF) }; +static const unsigned int uart_rx_ao_b1_pins[] = { PIN(GPIOAO_5, AO_OFF) }; +static const unsigned int spdif_out_1_pins[] = { PIN(GPIOAO_6, AO_OFF) }; + +static const unsigned int i2s_in_ch01_pins[] = { PIN(GPIOAO_6, AO_OFF) }; +static const unsigned int i2s_ao_clk_in_pins[] = { PIN(GPIOAO_9, AO_OFF) }; +static const unsigned int i2s_lr_clk_in_pins[] = { PIN(GPIOAO_10, AO_OFF) }; + +/* bank DIF */ +static const unsigned int eth_rxd1_pins[] = { PIN(DIF_0_P, 0) }; +static const unsigned int eth_rxd0_pins[] = { PIN(DIF_0_N, 0) }; +static const unsigned int eth_rx_dv_pins[] = { PIN(DIF_1_P, 0) }; +static const unsigned int eth_rx_clk_pins[] = { PIN(DIF_1_N, 0) }; +static const unsigned int eth_txd0_1_pins[] = { PIN(DIF_2_P, 0) }; +static const unsigned int eth_txd1_1_pins[] = { PIN(DIF_2_N, 0) }; +static const unsigned int eth_tx_en_pins[] = { PIN(DIF_3_P, 0) }; +static const unsigned int eth_ref_clk_pins[] = { PIN(DIF_3_N, 0) }; +static const unsigned int eth_mdc_pins[] = { PIN(DIF_4_P, 0) }; +static const unsigned int eth_mdio_en_pins[] = { PIN(DIF_4_N, 0) }; + +static struct meson_pmx_group meson8b_groups[] = { + GPIO_GROUP(GPIOX_0, 0), + GPIO_GROUP(GPIOX_1, 0), + GPIO_GROUP(GPIOX_2, 0), + GPIO_GROUP(GPIOX_3, 0), + GPIO_GROUP(GPIOX_4, 0), + GPIO_GROUP(GPIOX_5, 0), + GPIO_GROUP(GPIOX_6, 0), + GPIO_GROUP(GPIOX_7, 0), + GPIO_GROUP(GPIOX_8, 0), + GPIO_GROUP(GPIOX_9, 0), + GPIO_GROUP(GPIOX_10, 0), + GPIO_GROUP(GPIOX_11, 0), + GPIO_GROUP(GPIOX_16, 0), + GPIO_GROUP(GPIOX_17, 0), + GPIO_GROUP(GPIOX_18, 0), + GPIO_GROUP(GPIOX_19, 0), + GPIO_GROUP(GPIOX_20, 0), + GPIO_GROUP(GPIOX_21, 0), + + GPIO_GROUP(GPIOY_0, 0), + GPIO_GROUP(GPIOY_1, 0), + GPIO_GROUP(GPIOY_3, 0), + GPIO_GROUP(GPIOY_6, 0), + GPIO_GROUP(GPIOY_7, 0), + GPIO_GROUP(GPIOY_8, 0), + GPIO_GROUP(GPIOY_9, 0), + GPIO_GROUP(GPIOY_10, 0), + GPIO_GROUP(GPIOY_11, 0), + GPIO_GROUP(GPIOY_12, 0), + GPIO_GROUP(GPIOY_13, 0), + GPIO_GROUP(GPIOY_14, 0), + + GPIO_GROUP(GPIODV_9, 0), + GPIO_GROUP(GPIODV_24, 0), + GPIO_GROUP(GPIODV_25, 0), + GPIO_GROUP(GPIODV_26, 0), + GPIO_GROUP(GPIODV_27, 0), + GPIO_GROUP(GPIODV_28, 0), + GPIO_GROUP(GPIODV_29, 0), + + GPIO_GROUP(GPIOH_0, 0), + GPIO_GROUP(GPIOH_1, 0), + GPIO_GROUP(GPIOH_2, 0), + GPIO_GROUP(GPIOH_3, 0), + GPIO_GROUP(GPIOH_4, 0), + GPIO_GROUP(GPIOH_5, 0), + GPIO_GROUP(GPIOH_6, 0), + GPIO_GROUP(GPIOH_7, 0), + GPIO_GROUP(GPIOH_8, 0), + GPIO_GROUP(GPIOH_9, 0), + + GPIO_GROUP(DIF_0_P, 0), + GPIO_GROUP(DIF_0_N, 0), + GPIO_GROUP(DIF_1_P, 0), + GPIO_GROUP(DIF_1_N, 0), + GPIO_GROUP(DIF_2_P, 0), + GPIO_GROUP(DIF_2_N, 0), + GPIO_GROUP(DIF_3_P, 0), + GPIO_GROUP(DIF_3_N, 0), + GPIO_GROUP(DIF_4_P, 0), + GPIO_GROUP(DIF_4_N, 0), + + GPIO_GROUP(GPIOAO_0, AO_OFF), + GPIO_GROUP(GPIOAO_1, AO_OFF), + GPIO_GROUP(GPIOAO_2, AO_OFF), + GPIO_GROUP(GPIOAO_3, AO_OFF), + GPIO_GROUP(GPIOAO_4, AO_OFF), + GPIO_GROUP(GPIOAO_5, AO_OFF), + GPIO_GROUP(GPIOAO_6, AO_OFF), + GPIO_GROUP(GPIOAO_7, AO_OFF), + GPIO_GROUP(GPIOAO_8, AO_OFF), + GPIO_GROUP(GPIOAO_9, AO_OFF), + GPIO_GROUP(GPIOAO_10, AO_OFF), + GPIO_GROUP(GPIOAO_11, AO_OFF), + GPIO_GROUP(GPIOAO_12, AO_OFF), + GPIO_GROUP(GPIOAO_13, AO_OFF), + GPIO_GROUP(GPIO_BSD_EN, AO_OFF), + GPIO_GROUP(GPIO_TEST_N, AO_OFF), + + /* bank X */ + GROUP(sd_d0_a, 8, 5), + GROUP(sd_d1_a, 8, 4), + GROUP(sd_d2_a, 8, 3), + GROUP(sd_d3_a, 8, 2), + GROUP(sdxc_d0_0_a, 5, 29), + GROUP(sdxc_d47_a, 5, 12), + GROUP(sdxc_d13_0_a, 5, 28), + GROUP(sd_clk_a, 8, 1), + GROUP(sd_cmd_a, 8, 0), + GROUP(xtal_32k_out, 3, 22), + GROUP(xtal_24m_out, 3, 20), + GROUP(uart_tx_b0, 4, 9), + GROUP(uart_rx_b0, 4, 8), + GROUP(uart_cts_b0, 4, 7), + GROUP(uart_rts_b0, 4, 6), + GROUP(sdxc_d0_1_a, 5, 14), + GROUP(sdxc_d13_1_a, 5, 13), + GROUP(pcm_out_a, 3, 30), + GROUP(pcm_in_a, 3, 29), + GROUP(pcm_fs_a, 3, 28), + GROUP(pcm_clk_a, 3, 27), + GROUP(sdxc_clk_a, 5, 11), + GROUP(sdxc_cmd_a, 5, 10), + GROUP(pwm_vs_0, 7, 31), + GROUP(pwm_e, 9, 19), + GROUP(pwm_vs_1, 7, 30), + GROUP(uart_tx_a, 4, 17), + GROUP(uart_rx_a, 4, 16), + GROUP(uart_cts_a, 4, 15), + GROUP(uart_rts_a, 4, 14), + GROUP(uart_tx_b1, 6, 19), + GROUP(uart_rx_b1, 6, 18), + GROUP(uart_cts_b1, 6, 17), + GROUP(uart_rts_b1, 6, 16), + GROUP(iso7816_0_clk, 5, 9), + GROUP(iso7816_0_data, 5, 8), + GROUP(spi_sclk_0, 4, 22), + GROUP(spi_miso_0, 4, 24), + GROUP(spi_mosi_0, 4, 23), + GROUP(iso7816_det, 4, 21), + GROUP(iso7816_reset, 4, 20), + GROUP(iso7816_1_clk, 4, 19), + GROUP(iso7816_1_data, 4, 18), + GROUP(spi_ss0_0, 4, 25), + GROUP(tsin_clk_b, 3, 6), + GROUP(tsin_sop_b, 3, 7), + GROUP(tsin_d0_b, 3, 8), + GROUP(pwm_b, 2, 3), + GROUP(i2c_sda_d0, 4, 5), + GROUP(i2c_sck_d0, 4, 4), + GROUP(tsin_d_valid_b, 3, 9), + + /* bank Y */ + GROUP(tsin_d_valid_a, 3, 2), + GROUP(tsin_sop_a, 3, 1), + GROUP(tsin_d17_a, 3, 5), + GROUP(tsin_clk_a, 3, 0), + GROUP(tsin_d0_a, 3, 4), + GROUP(spdif_out_0, 1, 7), + GROUP(xtal_24m, 3, 18), + GROUP(iso7816_2_clk, 5, 7), + GROUP(iso7816_2_data, 5, 6), + + /* bank DV */ + GROUP(pwm_d, 3, 26), + GROUP(pwm_c0, 3, 25), + GROUP(pwm_vs_2, 7, 28), + GROUP(pwm_vs_3, 7, 27), + GROUP(pwm_vs_4, 7, 26), + GROUP(xtal24_out, 7, 25), + GROUP(uart_tx_c, 6, 23), + GROUP(uart_rx_c, 6, 22), + GROUP(uart_cts_c, 6, 21), + GROUP(uart_rts_c, 6, 20), + GROUP(pwm_c1, 3, 24), + GROUP(i2c_sda_a, 9, 31), + GROUP(i2c_sck_a, 9, 30), + GROUP(i2c_sda_b0, 9, 29), + GROUP(i2c_sck_b0, 9, 28), + GROUP(i2c_sda_c0, 9, 27), + GROUP(i2c_sck_c0, 9, 26), + + /* bank H */ + GROUP(hdmi_hpd, 1, 26), + GROUP(hdmi_sda, 1, 25), + GROUP(hdmi_scl, 1, 24), + GROUP(hdmi_cec_0, 1, 23), + GROUP(eth_txd1_0, 7, 21), + GROUP(eth_txd0_0, 7, 20), + GROUP(clk_24m_out, 4, 1), + GROUP(spi_ss1, 8, 11), + GROUP(spi_ss2, 8, 12), + GROUP(spi_ss0_1, 9, 13), + GROUP(spi_miso_1, 9, 12), + GROUP(spi_mosi_1, 9, 11), + GROUP(spi_sclk_1, 9, 10), + GROUP(eth_txd3, 6, 13), + GROUP(eth_txd2, 6, 12), + GROUP(eth_tx_clk, 6, 11), + GROUP(i2c_sda_b1, 5, 27), + GROUP(i2c_sck_b1, 5, 26), + GROUP(i2c_sda_c1, 5, 25), + GROUP(i2c_sck_c1, 5, 24), + GROUP(i2c_sda_d1, 4, 3), + GROUP(i2c_sck_d1, 4, 2), + + /* bank BOOT */ + GROUP(nand_io, 2, 26), + GROUP(nand_io_ce0, 2, 25), + GROUP(nand_io_ce1, 2, 24), + GROUP(nand_io_rb0, 2, 17), + GROUP(nand_ale, 2, 21), + GROUP(nand_cle, 2, 20), + GROUP(nand_wen_clk, 2, 19), + GROUP(nand_ren_clk, 2, 18), + GROUP(nand_dqs_0, 2, 27), + GROUP(nand_dqs_1, 2, 28), + GROUP(sdxc_d0_c, 4, 30), + GROUP(sdxc_d13_c, 4, 29), + GROUP(sdxc_d47_c, 4, 28), + GROUP(sdxc_clk_c, 7, 19), + GROUP(sdxc_cmd_c, 7, 18), + GROUP(nor_d, 5, 1), + GROUP(nor_q, 5, 3), + GROUP(nor_c, 5, 2), + GROUP(nor_cs, 5, 0), + GROUP(sd_d0_c, 6, 29), + GROUP(sd_d1_c, 6, 28), + GROUP(sd_d2_c, 6, 27), + GROUP(sd_d3_c, 6, 26), + GROUP(sd_cmd_c, 6, 30), + GROUP(sd_clk_c, 6, 31), + + /* bank CARD */ + GROUP(sd_d1_b, 2, 14), + GROUP(sd_d0_b, 2, 15), + GROUP(sd_clk_b, 2, 11), + GROUP(sd_cmd_b, 2, 10), + GROUP(sd_d3_b, 2, 12), + GROUP(sd_d2_b, 2, 13), + GROUP(sdxc_d13_b, 2, 6), + GROUP(sdxc_d0_b, 2, 7), + GROUP(sdxc_clk_b, 2, 5), + GROUP(sdxc_cmd_b, 2, 4), + + /* bank AO */ + GROUP(uart_tx_ao_a, 0, 12), + GROUP(uart_rx_ao_a, 0, 11), + GROUP(uart_cts_ao_a, 0, 10), + GROUP(uart_rts_ao_a, 0, 9), + GROUP(i2c_mst_sck_ao, 0, 6), + GROUP(i2c_mst_sda_ao, 0, 5), + GROUP(clk_32k_in_out, 0, 18), + GROUP(remote_input, 0, 0), + GROUP(hdmi_cec_1, 0, 17), + GROUP(ir_blaster, 0, 31), + GROUP(pwm_c2, 0, 22), + GROUP(i2c_sck_ao, 0, 2), + GROUP(i2c_sda_ao, 0, 1), + GROUP(ir_remote_out, 0, 21), + GROUP(i2s_am_clk_out, 0, 30), + GROUP(i2s_ao_clk_out, 0, 29), + GROUP(i2s_lr_clk_out, 0, 28), + GROUP(i2s_out_01, 0, 27), + GROUP(uart_tx_ao_b0, 0, 26), + GROUP(uart_rx_ao_b0, 0, 25), + GROUP(uart_cts_ao_b, 0, 8), + GROUP(uart_rts_ao_b, 0, 7), + GROUP(uart_tx_ao_b1, 0, 24), + GROUP(uart_rx_ao_b1, 0, 23), + GROUP(spdif_out_1, 0, 16), + GROUP(i2s_in_ch01, 0, 13), + GROUP(i2s_ao_clk_in, 0, 15), + GROUP(i2s_lr_clk_in, 0, 14), + + /* bank DIF */ + GROUP(eth_rxd1, 6, 0), + GROUP(eth_rxd0, 6, 1), + GROUP(eth_rx_dv, 6, 2), + GROUP(eth_rx_clk, 6, 3), + GROUP(eth_txd0_1, 6, 4), + GROUP(eth_txd1_1, 6, 5), + GROUP(eth_tx_en, 6, 0), + GROUP(eth_ref_clk, 6, 8), + GROUP(eth_mdc, 6, 9), + GROUP(eth_mdio_en, 6, 10), +}; + +static const char * const gpio_groups[] = { + "GPIOX_0", "GPIOX_1", "GPIOX_2", "GPIOX_3", "GPIOX_4", + "GPIOX_5", "GPIOX_6", "GPIOX_7", "GPIOX_8", "GPIOX_9", + "GPIOX_10", "GPIOX_11", "GPIOX_16", "GPIOX_17", "GPIOX_18", + "GPIOX_19", "GPIOX_20", "GPIOX_21", + + "GPIOY_0", "GPIOY_1", "GPIOY_3", "GPIOY_6", "GPIOY_7", + "GPIOY_8", "GPIOY_9", "GPIOY_10", "GPIOY_11", "GPIOY_12", + "GPIOY_13", "GPIOY_14", + + "GPIODV_9", "GPIODV_24", "GPIODV_25", "GPIODV_26", + "GPIODV_27", "GPIODV_28", "GPIODV_29", + + "GPIOH_0", "GPIOH_1", "GPIOH_2", "GPIOH_3", "GPIOH_4", + "GPIOH_5", "GPIOH_6", "GPIOH_7", "GPIOH_8", "GPIOH_9", + + "CARD_0", "CARD_1", "CARD_2", "CARD_3", "CARD_4", + "CARD_5", "CARD_6", + + "BOOT_0", "BOOT_1", "BOOT_2", "BOOT_3", "BOOT_4", + "BOOT_5", "BOOT_6", "BOOT_7", "BOOT_8", "BOOT_9", + "BOOT_10", "BOOT_11", "BOOT_12", "BOOT_13", "BOOT_14", + "BOOT_15", "BOOT_16", "BOOT_17", "BOOT_18", + + "GPIOAO_0", "GPIOAO_1", "GPIOAO_2", "GPIOAO_3", + "GPIOAO_4", "GPIOAO_5", "GPIOAO_6", "GPIOAO_7", + "GPIOAO_8", "GPIOAO_9", "GPIOAO_10", "GPIOAO_11", + "GPIOAO_12", "GPIOAO_13", "GPIO_BSD_EN", "GPIO_TEST_N", + + "DIF_0_P", "DIF_0_N", "DIF_1_P", "DIF_1_N", + "DIF_2_P", "DIF_2_N", "DIF_3_P", "DIF_3_N", + "DIF_4_P", "DIF_4_N" +}; + +static const char * const sd_a_groups[] = { + "sd_d0_a", "sd_d1_a", "sd_d2_a", "sd_d3_a", "sd_clk_a", + "sd_cmd_a" +}; + +static const char * const sdxc_a_groups[] = { + "sdxc_d0_0_a", "sdxc_d13_0_a", "sdxc_d47_a", "sdxc_clk_a", + "sdxc_cmd_a", "sdxc_d0_1_a", "sdxc_d0_13_1_a" +}; + +static const char * const pcm_a_groups[] = { + "pcm_out_a", "pcm_in_a", "pcm_fs_a", "pcm_clk_a" +}; + +static const char * const uart_a_groups[] = { + "uart_tx_a", "uart_rx_a", "uart_cts_a", "uart_rts_a" +}; + +static const char * const uart_b_groups[] = { + "uart_tx_b0", "uart_rx_b0", "uart_cts_b0", "uart_rts_b0", + "uart_tx_b1", "uart_rx_b1", "uart_cts_b1", "uart_rts_b1" +}; + +static const char * const iso7816_groups[] = { + "iso7816_det", "iso7816_reset", "iso7816_0_clk", "iso7816_0_data", + "iso7816_1_clk", "iso7816_1_data", "iso7816_2_clk", "iso7816_2_data" +}; + +static const char * const i2c_d_groups[] = { + "i2c_sda_d0", "i2c_sck_d0", "i2c_sda_d1", "i2c_sck_d1" +}; + +static const char * const xtal_groups[] = { + "xtal_32k_out", "xtal_24m_out", "xtal_24m", "xtal24_out" +}; + +static const char * const uart_c_groups[] = { + "uart_tx_c", "uart_rx_c", "uart_cts_c", "uart_rts_c" +}; + +static const char * const i2c_c_groups[] = { + "i2c_sda_c0", "i2c_sck_c0", "i2c_sda_c1", "i2c_sck_c1" +}; + +static const char * const hdmi_groups[] = { + "hdmi_hpd", "hdmi_sda", "hdmi_scl", "hdmi_cec_0", + "hdmi_cec_1" +}; + +static const char * const spi_groups[] = { + "spi_ss0_0", "spi_miso_0", "spi_mosi_0", "spi_sclk_0", + "spi_ss0_1", "spi_ss1", "spi_sclk_1", "spi_mosi_1", + "spi_miso_1", "spi_ss2" +}; + +static const char * const ethernet_groups[] = { + "eth_tx_clk", "eth_tx_en", "eth_txd1_0", "eth_txd1_1", + "eth_txd0_0", "eth_txd0_1", "eth_rx_clk", "eth_rx_dv", + "eth_rxd1", "eth_rxd0", "eth_mdio_en", "eth_mdc", "eth_ref_clk", + "eth_txd2", "eth_txd3" +}; + +static const char * const i2c_a_groups[] = { + "i2c_sda_a", "i2c_sck_a", +}; + +static const char * const i2c_b_groups[] = { + "i2c_sda_b0", "i2c_sck_b0", "i2c_sda_b1", "i2c_sck_b1" +}; + +static const char * const sd_c_groups[] = { + "sd_d0_c", "sd_d1_c", "sd_d2_c", "sd_d3_c", + "sd_cmd_c", "sd_clk_c" +}; + +static const char * const sdxc_c_groups[] = { + "sdxc_d0_c", "sdxc_d13_c", "sdxc_d47_c", "sdxc_cmd_c", + "sdxc_clk_c" +}; + +static const char * const nand_groups[] = { + "nand_io", "nand_io_ce0", "nand_io_ce1", + "nand_io_rb0", "nand_ale", "nand_cle", + "nand_wen_clk", "nand_ren_clk", "nand_dqs0", + "nand_dqs1" +}; + +static const char * const nor_groups[] = { + "nor_d", "nor_q", "nor_c", "nor_cs" +}; + +static const char * const sd_b_groups[] = { + "sd_d1_b", "sd_d0_b", "sd_clk_b", "sd_cmd_b", + "sd_d3_b", "sd_d2_b" +}; + +static const char * const sdxc_b_groups[] = { + "sdxc_d13_b", "sdxc_d0_b", "sdxc_clk_b", "sdxc_cmd_b" +}; + +static const char * const uart_ao_groups[] = { + "uart_tx_ao_a", "uart_rx_ao_a", "uart_cts_ao_a", "uart_rts_ao_a" +}; + +static const char * const remote_groups[] = { + "remote_input", "ir_blaster", "ir_remote_out" +}; + +static const char * const i2c_slave_ao_groups[] = { + "i2c_sck_ao", "i2c_sda_ao" +}; + +static const char * const uart_ao_b_groups[] = { + "uart_tx_ao_b0", "uart_rx_ao_b0", "uart_tx_ao_b1", "uart_rx_ao_b1", + "uart_cts_ao_b", "uart_rts_ao_b" +}; + +static const char * const i2c_mst_ao_groups[] = { + "i2c_mst_sck_ao", "i2c_mst_sda_ao" +}; + +static const char * const clk_groups[] = { + "clk_24m_out", "clk_32k_in_out" +}; + +static const char * const spdif_groups[] = { + "spdif_out_1", "spdif_out_0" +}; + +static const char * const i2s_groups[] = { + "i2s_am_clk_out", "i2s_ao_clk_out", "i2s_lr_clk_out", + "i2s_out_01", "i2s_in_ch01", "i2s_ao_clk_in", + "i2s_lr_clk_in" +}; + +static const char * const pwm_b_groups[] = { + "pwm_b" +}; + +static const char * const pwm_c_groups[] = { + "pwm_c0", "pwm_c1", "pwm_c2" +}; + +static const char * const pwm_d_groups[] = { + "pwm_d" +}; + +static const char * const pwm_e_groups[] = { + "pwm_e" +}; + +static const char * const pwm_vs_groups[] = { + "pwm_vs_0", "pwm_vs_1", "pwm_vs_2", + "pwm_vs_3", "pwm_vs_4" +}; + +static const char * const tsin_a_groups[] = { + "tsin_d0_a", "tsin_d17_a", "tsin_clk_a", "tsin_sop_a", + "tsin_d_valid_a" +}; + +static const char * const tsin_b_groups[] = { + "tsin_d0_b", "tsin_clk_b", "tsin_sop_b", "tsin_d_valid_b" +}; + +static struct meson_pmx_func meson8b_functions[] = { + FUNCTION(gpio), + FUNCTION(sd_a), + FUNCTION(sdxc_a), + FUNCTION(pcm_a), + FUNCTION(uart_a), + FUNCTION(uart_b), + FUNCTION(iso7816), + FUNCTION(i2c_d), + FUNCTION(xtal), + FUNCTION(uart_c), + FUNCTION(i2c_c), + FUNCTION(hdmi), + FUNCTION(spi), + FUNCTION(ethernet), + FUNCTION(i2c_a), + FUNCTION(i2c_b), + FUNCTION(sd_c), + FUNCTION(sdxc_c), + FUNCTION(nand), + FUNCTION(nor), + FUNCTION(sd_b), + FUNCTION(sdxc_b), + FUNCTION(uart_ao), + FUNCTION(remote), + FUNCTION(i2c_slave_ao), + FUNCTION(uart_ao_b), + FUNCTION(i2c_mst_ao), + FUNCTION(clk), + FUNCTION(spdif), + FUNCTION(i2s), + FUNCTION(pwm_b), + FUNCTION(pwm_c), + FUNCTION(pwm_d), + FUNCTION(pwm_e), + FUNCTION(pwm_vs), + FUNCTION(tsin_a), + FUNCTION(tsin_b), +}; + +static struct meson_bank meson8b_banks[] = { + /* name first last pullen pull dir out in */ + BANK("X", PIN(GPIOX_0, 0), PIN(GPIOX_21, 0), 4, 0, 4, 0, 0, 0, 1, 0, 2, 0), + BANK("Y", PIN(GPIOY_0, 0), PIN(GPIOY_14, 0), 3, 0, 3, 0, 3, 0, 4, 0, 5, 0), + BANK("DV", PIN(GPIODV_9, 0), PIN(GPIODV_29, 0), 0, 0, 0, 0, 7, 0, 8, 0, 9, 0), + BANK("H", PIN(GPIOH_0, 0), PIN(GPIOH_9, 0), 1, 16, 1, 16, 9, 19, 10, 19, 11, 19), + BANK("CARD", PIN(CARD_0, 0), PIN(CARD_6, 0), 2, 20, 2, 20, 0, 22, 1, 22, 2, 22), + BANK("BOOT", PIN(BOOT_0, 0), PIN(BOOT_18, 0), 2, 0, 2, 0, 9, 0, 10, 0, 11, 0), + BANK("DIF", PIN(DIF_0_P, 0), PIN(DIF_4_N, 0), 5, 8, 5, 8, 12, 12, 13, 12, 14, 12), +}; + +static struct meson_bank meson8b_ao_banks[] = { + /* name first last pullen pull dir out in */ + BANK("AO", PIN(GPIOAO_0, AO_OFF), PIN(GPIO_TEST_N, AO_OFF), 0, 0, 0, 16, 0, 0, 0, 16, 1, 0), +}; + +static struct meson_domain_data meson8b_domain_data[] = { + { + .name = "banks", + .banks = meson8b_banks, + .num_banks = ARRAY_SIZE(meson8b_banks), + .pin_base = 0, + .num_pins = 83, + }, + { + .name = "ao-bank", + .banks = meson8b_ao_banks, + .num_banks = ARRAY_SIZE(meson8b_ao_banks), + .pin_base = 83, + .num_pins = 16, + }, +}; + +struct meson_pinctrl_data meson8b_pinctrl_data = { + .pins = meson8b_pins, + .groups = meson8b_groups, + .funcs = meson8b_functions, + .domain_data = meson8b_domain_data, + .num_pins = ARRAY_SIZE(meson8b_pins), + .num_groups = ARRAY_SIZE(meson8b_groups), + .num_funcs = ARRAY_SIZE(meson8b_functions), + .num_domains = ARRAY_SIZE(meson8b_domain_data), +}; diff --git a/include/dt-bindings/gpio/meson8b-gpio.h b/include/dt-bindings/gpio/meson8b-gpio.h new file mode 100644 index 000000000000..c38cb20d7182 --- /dev/null +++ b/include/dt-bindings/gpio/meson8b-gpio.h @@ -0,0 +1,32 @@ +/* + * GPIO definitions for Amlogic Meson8b SoCs + * + * Copyright (C) 2015 Endless Mobile, Inc. + * Author: Carlo Caione + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef _DT_BINDINGS_MESON8B_GPIO_H +#define _DT_BINDINGS_MESON8B_GPIO_H + +#include + +/* GPIO Bank DIF */ +#define DIF_0_P 120 +#define DIF_0_N 121 +#define DIF_1_P 122 +#define DIF_1_N 123 +#define DIF_2_P 124 +#define DIF_2_N 125 +#define DIF_3_P 126 +#define DIF_3_N 127 +#define DIF_4_P 128 +#define DIF_4_N 129 + +#endif /* _DT_BINDINGS_MESON8B_GPIO_H */ -- cgit v1.2.3 From 29464ccc78f1e8f77b8c37ddbe95af46d13c2ec2 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 31 Mar 2015 15:36:22 +0200 Subject: cfg80211: move IE split utilities here from mac80211 As the next patch will require the IE splitting utility functions in cfg80211, move them there from mac80211. Signed-off-by: Johannes Berg --- include/net/cfg80211.h | 58 ++++++++++++++++++++++++++++++++++++++++++++++ include/net/mac80211.h | 28 ---------------------- net/mac80211/ieee80211_i.h | 4 ---- net/mac80211/util.c | 40 -------------------------------- net/wireless/util.c | 41 ++++++++++++++++++++++++++++++++ 5 files changed, 99 insertions(+), 72 deletions(-) (limited to 'include') diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 441306436569..f8d6813cd5b2 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -5000,6 +5000,64 @@ int cfg80211_get_p2p_attr(const u8 *ies, unsigned int len, enum ieee80211_p2p_attr_id attr, u8 *buf, unsigned int bufsize); +/** + * ieee80211_ie_split_ric - split an IE buffer according to ordering (with RIC) + * @ies: the IE buffer + * @ielen: the length of the IE buffer + * @ids: an array with element IDs that are allowed before + * the split + * @n_ids: the size of the element ID array + * @after_ric: array IE types that come after the RIC element + * @n_after_ric: size of the @after_ric array + * @offset: offset where to start splitting in the buffer + * + * This function splits an IE buffer by updating the @offset + * variable to point to the location where the buffer should be + * split. + * + * It assumes that the given IE buffer is well-formed, this + * has to be guaranteed by the caller! + * + * It also assumes that the IEs in the buffer are ordered + * correctly, if not the result of using this function will not + * be ordered correctly either, i.e. it does no reordering. + * + * The function returns the offset where the next part of the + * buffer starts, which may be @ielen if the entire (remainder) + * of the buffer should be used. + */ +size_t ieee80211_ie_split_ric(const u8 *ies, size_t ielen, + const u8 *ids, int n_ids, + const u8 *after_ric, int n_after_ric, + size_t offset); + +/** + * ieee80211_ie_split - split an IE buffer according to ordering + * @ies: the IE buffer + * @ielen: the length of the IE buffer + * @ids: an array with element IDs that are allowed before + * the split + * @n_ids: the size of the element ID array + * @offset: offset where to start splitting in the buffer + * + * This function splits an IE buffer by updating the @offset + * variable to point to the location where the buffer should be + * split. + * + * It assumes that the given IE buffer is well-formed, this + * has to be guaranteed by the caller! + * + * It also assumes that the IEs in the buffer are ordered + * correctly, if not the result of using this function will not + * be ordered correctly either, i.e. it does no reordering. + * + * The function returns the offset where the next part of the + * buffer starts, which may be @ielen if the entire (remainder) + * of the buffer should be used. + */ +size_t ieee80211_ie_split(const u8 *ies, size_t ielen, + const u8 *ids, int n_ids, size_t offset); + /** * cfg80211_report_wowlan_wakeup - report wakeup from WoWLAN * @wdev: the wireless device reporting the wakeup diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 3578da96b41a..b4bef1152c05 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -5379,34 +5379,6 @@ int ieee80211_reserve_tid(struct ieee80211_sta *sta, u8 tid); */ void ieee80211_unreserve_tid(struct ieee80211_sta *sta, u8 tid); -/** - * ieee80211_ie_split - split an IE buffer according to ordering - * - * @ies: the IE buffer - * @ielen: the length of the IE buffer - * @ids: an array with element IDs that are allowed before - * the split - * @n_ids: the size of the element ID array - * @offset: offset where to start splitting in the buffer - * - * This function splits an IE buffer by updating the @offset - * variable to point to the location where the buffer should be - * split. - * - * It assumes that the given IE buffer is well-formed, this - * has to be guaranteed by the caller! - * - * It also assumes that the IEs in the buffer are ordered - * correctly, if not the result of using this function will not - * be ordered correctly either, i.e. it does no reordering. - * - * The function returns the offset where the next part of the - * buffer starts, which may be @ielen if the entire (remainder) - * of the buffer should be used. - */ -size_t ieee80211_ie_split(const u8 *ies, size_t ielen, - const u8 *ids, int n_ids, size_t offset); - /** * ieee80211_tx_dequeue - dequeue a packet from a software tx queue * diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 04b32f3e0395..ab46ab4a7249 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -1965,10 +1965,6 @@ int __ieee80211_request_smps_ap(struct ieee80211_sub_if_data *sdata, void ieee80211_recalc_smps(struct ieee80211_sub_if_data *sdata); void ieee80211_recalc_min_chandef(struct ieee80211_sub_if_data *sdata); -size_t ieee80211_ie_split_ric(const u8 *ies, size_t ielen, - const u8 *ids, int n_ids, - const u8 *after_ric, int n_after_ric, - size_t offset); size_t ieee80211_ie_split_vendor(const u8 *ies, size_t ielen, size_t offset); u8 *ieee80211_ie_build_ht_cap(u8 *pos, struct ieee80211_sta_ht_cap *ht_cap, u16 cap); diff --git a/net/mac80211/util.c b/net/mac80211/util.c index 482b85c19a36..79412f16b61d 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -2194,46 +2194,6 @@ void ieee80211_recalc_min_chandef(struct ieee80211_sub_if_data *sdata) mutex_unlock(&local->chanctx_mtx); } -static bool ieee80211_id_in_list(const u8 *ids, int n_ids, u8 id) -{ - int i; - - for (i = 0; i < n_ids; i++) - if (ids[i] == id) - return true; - return false; -} - -size_t ieee80211_ie_split_ric(const u8 *ies, size_t ielen, - const u8 *ids, int n_ids, - const u8 *after_ric, int n_after_ric, - size_t offset) -{ - size_t pos = offset; - - while (pos < ielen && ieee80211_id_in_list(ids, n_ids, ies[pos])) { - if (ies[pos] == WLAN_EID_RIC_DATA && n_after_ric) { - pos += 2 + ies[pos + 1]; - - while (pos < ielen && - !ieee80211_id_in_list(after_ric, n_after_ric, - ies[pos])) - pos += 2 + ies[pos + 1]; - } else { - pos += 2 + ies[pos + 1]; - } - } - - return pos; -} - -size_t ieee80211_ie_split(const u8 *ies, size_t ielen, - const u8 *ids, int n_ids, size_t offset) -{ - return ieee80211_ie_split_ric(ies, ielen, ids, n_ids, NULL, 0, offset); -} -EXPORT_SYMBOL(ieee80211_ie_split); - size_t ieee80211_ie_split_vendor(const u8 *ies, size_t ielen, size_t offset) { size_t pos = offset; diff --git a/net/wireless/util.c b/net/wireless/util.c index f218b151530a..70051ab52f4f 100644 --- a/net/wireless/util.c +++ b/net/wireless/util.c @@ -1290,6 +1290,47 @@ int cfg80211_get_p2p_attr(const u8 *ies, unsigned int len, } EXPORT_SYMBOL(cfg80211_get_p2p_attr); +static bool ieee80211_id_in_list(const u8 *ids, int n_ids, u8 id) +{ + int i; + + for (i = 0; i < n_ids; i++) + if (ids[i] == id) + return true; + return false; +} + +size_t ieee80211_ie_split_ric(const u8 *ies, size_t ielen, + const u8 *ids, int n_ids, + const u8 *after_ric, int n_after_ric, + size_t offset) +{ + size_t pos = offset; + + while (pos < ielen && ieee80211_id_in_list(ids, n_ids, ies[pos])) { + if (ies[pos] == WLAN_EID_RIC_DATA && n_after_ric) { + pos += 2 + ies[pos + 1]; + + while (pos < ielen && + !ieee80211_id_in_list(after_ric, n_after_ric, + ies[pos])) + pos += 2 + ies[pos + 1]; + } else { + pos += 2 + ies[pos + 1]; + } + } + + return pos; +} +EXPORT_SYMBOL(ieee80211_ie_split_ric); + +size_t ieee80211_ie_split(const u8 *ies, size_t ielen, + const u8 *ids, int n_ids, size_t offset) +{ + return ieee80211_ie_split_ric(ies, ielen, ids, n_ids, NULL, 0, offset); +} +EXPORT_SYMBOL(ieee80211_ie_split); + bool ieee80211_operating_class_to_band(u8 operating_class, enum ieee80211_band *band) { -- cgit v1.2.3 From 0578be680c6623ad6515b60a0b41ec70ebc1f204 Mon Sep 17 00:00:00 2001 From: Maarten Lankhorst Date: Tue, 7 Apr 2015 15:56:07 +0200 Subject: drm: Use kref_put_mutex in drm_gem_object_unreference_unlocked If kref_put_mutex returns true then the caller or the put function is responsible for unlocking the mutex. The usual pattern assumes that the free callback unlocks the mutex, but since that is shared with the locked variant we need to explicitly unlock here. Signed-off-by: Maarten Lankhorst Signed-off-by: Daniel Vetter --- include/drm/drm_gem.h | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/include/drm/drm_gem.h b/include/drm/drm_gem.h index 1e6ae1458f7a..7a592d7e398b 100644 --- a/include/drm/drm_gem.h +++ b/include/drm/drm_gem.h @@ -149,14 +149,16 @@ drm_gem_object_unreference(struct drm_gem_object *obj) static inline void drm_gem_object_unreference_unlocked(struct drm_gem_object *obj) { - if (obj && !atomic_add_unless(&obj->refcount.refcount, -1, 1)) { - struct drm_device *dev = obj->dev; + struct drm_device *dev; + + if (!obj) + return; - mutex_lock(&dev->struct_mutex); - if (likely(atomic_dec_and_test(&obj->refcount.refcount))) - drm_gem_object_free(&obj->refcount); + dev = obj->dev; + if (kref_put_mutex(&obj->refcount, drm_gem_object_free, &dev->struct_mutex)) mutex_unlock(&dev->struct_mutex); - } + else + might_lock(&dev->struct_mutex); } int drm_gem_handle_create(struct drm_file *file_priv, -- cgit v1.2.3 From 882156e0407f06ca3d4c6500a7d347c3a2ed88b4 Mon Sep 17 00:00:00 2001 From: "Steven Rostedt (Red Hat)" Date: Wed, 1 Apr 2015 14:14:53 -0400 Subject: tracing: Add TRACE_SYSTEM_VAR to intel-sst New code will require TRACE_SYSTEM to be a valid C variable name, but some tracepoints have TRACE_SYSTEM with '-' and not '_', so it can not be used. Instead, add a TRACE_SYSTEM_VAR that can give the tracing infrastructure a unique name for the trace system. Link: http://lkml.kernel.org/r/20150402142831.GT6023@sirena.org.uk Acked-by: Mark Brown Reviewed-by: Masami Hiramatsu Tested-by: Masami Hiramatsu Signed-off-by: Steven Rostedt --- include/trace/events/intel-sst.h | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'include') diff --git a/include/trace/events/intel-sst.h b/include/trace/events/intel-sst.h index 76c72d3f1902..edc24e6dea1b 100644 --- a/include/trace/events/intel-sst.h +++ b/include/trace/events/intel-sst.h @@ -1,6 +1,13 @@ #undef TRACE_SYSTEM #define TRACE_SYSTEM intel-sst +/* + * The TRACE_SYSTEM_VAR defaults to TRACE_SYSTEM, but must be a + * legitimate C variable. It is not exported to user space. + */ +#undef TRACE_SYSTEM_VAR +#define TRACE_SYSTEM_VAR intel_sst + #if !defined(_TRACE_INTEL_SST_H) || defined(TRACE_HEADER_MULTI_READ) #define _TRACE_INTEL_SST_H -- cgit v1.2.3 From 5c7d2dd2852c7cd8f07cf91291b351c01b781044 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Sat, 4 Apr 2015 20:59:40 -0700 Subject: Bluetooth: Make data pointer of hci_recv_stream_fragment const The data pointer provided to hci_recv_stream_fragment function should have been marked const. The function has no business in modifying the original data. So fix this now. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- include/net/bluetooth/hci_core.h | 2 +- net/bluetooth/hci_core.c | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 93fd3e756b8a..ad957f336ead 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -1012,7 +1012,7 @@ int hci_remove_remote_oob_data(struct hci_dev *hdev, bdaddr_t *bdaddr, void hci_event_packet(struct hci_dev *hdev, struct sk_buff *skb); int hci_recv_frame(struct hci_dev *hdev, struct sk_buff *skb); -int hci_recv_stream_fragment(struct hci_dev *hdev, void *data, int count); +int hci_recv_stream_fragment(struct hci_dev *hdev, const void *data, int count); void hci_init_sysfs(struct hci_dev *hdev); void hci_conn_init_sysfs(struct hci_conn *conn); diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 46b114c0140b..c9e7cafb245a 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -3320,7 +3320,7 @@ int hci_recv_frame(struct hci_dev *hdev, struct sk_buff *skb) } EXPORT_SYMBOL(hci_recv_frame); -static int hci_reassembly(struct hci_dev *hdev, int type, void *data, +static int hci_reassembly(struct hci_dev *hdev, int type, const void *data, int count, __u8 index) { int len = 0; @@ -3430,7 +3430,7 @@ static int hci_reassembly(struct hci_dev *hdev, int type, void *data, #define STREAM_REASSEMBLY 0 -int hci_recv_stream_fragment(struct hci_dev *hdev, void *data, int count) +int hci_recv_stream_fragment(struct hci_dev *hdev, const void *data, int count) { int type; int rem = 0; @@ -3439,7 +3439,7 @@ int hci_recv_stream_fragment(struct hci_dev *hdev, void *data, int count) struct sk_buff *skb = hdev->reassembly[STREAM_REASSEMBLY]; if (!skb) { - struct { char type; } *pkt; + const struct { char type; } *pkt; /* Start of the frame */ pkt = data; -- cgit v1.2.3 From 2d7cc19eebc182dbdda228aa26eb5bfff97ac072 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Sat, 4 Apr 2015 21:59:27 -0700 Subject: Bluetooth: Remove hci_recv_stream_fragment function The hci_recv_stream_fragment function should have never been introduced in the first place. The Bluetooth core does not need to know anything about the HCI transport protocol. With all transport protocol specific detailed moved back into the drivers where they belong (mainly generic USB and UART drivers), this function can now be removed. This reduces the size of hci_dev structure and also removes an exported symbol from the Bluetooth core module. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- include/net/bluetooth/hci_core.h | 3 - net/bluetooth/hci_core.c | 148 +-------------------------------------- 2 files changed, 1 insertion(+), 150 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index ad957f336ead..a056c2bfeb81 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -185,7 +185,6 @@ struct amp_assoc { #define HCI_MAX_PAGES 3 -#define NUM_REASSEMBLY 4 struct hci_dev { struct list_head list; struct mutex lock; @@ -327,7 +326,6 @@ struct hci_dev { struct sk_buff_head cmd_q; struct sk_buff *sent_cmd; - struct sk_buff *reassembly[NUM_REASSEMBLY]; struct mutex req_lock; wait_queue_head_t req_wait_q; @@ -1012,7 +1010,6 @@ int hci_remove_remote_oob_data(struct hci_dev *hdev, bdaddr_t *bdaddr, void hci_event_packet(struct hci_dev *hdev, struct sk_buff *skb); int hci_recv_frame(struct hci_dev *hdev, struct sk_buff *skb); -int hci_recv_stream_fragment(struct hci_dev *hdev, const void *data, int count); void hci_init_sysfs(struct hci_dev *hdev); void hci_conn_init_sysfs(struct hci_conn *conn); diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index c9e7cafb245a..476709bd068a 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -3200,7 +3200,7 @@ EXPORT_SYMBOL(hci_register_dev); /* Unregister HCI device */ void hci_unregister_dev(struct hci_dev *hdev) { - int i, id; + int id; BT_DBG("%p name %s bus %d", hdev, hdev->name, hdev->bus); @@ -3214,9 +3214,6 @@ void hci_unregister_dev(struct hci_dev *hdev) hci_dev_do_close(hdev); - for (i = 0; i < NUM_REASSEMBLY; i++) - kfree_skb(hdev->reassembly[i]); - cancel_work_sync(&hdev->power_on); if (!test_bit(HCI_INIT, &hdev->flags) && @@ -3320,149 +3317,6 @@ int hci_recv_frame(struct hci_dev *hdev, struct sk_buff *skb) } EXPORT_SYMBOL(hci_recv_frame); -static int hci_reassembly(struct hci_dev *hdev, int type, const void *data, - int count, __u8 index) -{ - int len = 0; - int hlen = 0; - int remain = count; - struct sk_buff *skb; - struct bt_skb_cb *scb; - - if ((type < HCI_ACLDATA_PKT || type > HCI_EVENT_PKT) || - index >= NUM_REASSEMBLY) - return -EILSEQ; - - skb = hdev->reassembly[index]; - - if (!skb) { - switch (type) { - case HCI_ACLDATA_PKT: - len = HCI_MAX_FRAME_SIZE; - hlen = HCI_ACL_HDR_SIZE; - break; - case HCI_EVENT_PKT: - len = HCI_MAX_EVENT_SIZE; - hlen = HCI_EVENT_HDR_SIZE; - break; - case HCI_SCODATA_PKT: - len = HCI_MAX_SCO_SIZE; - hlen = HCI_SCO_HDR_SIZE; - break; - } - - skb = bt_skb_alloc(len, GFP_ATOMIC); - if (!skb) - return -ENOMEM; - - scb = (void *) skb->cb; - scb->expect = hlen; - scb->pkt_type = type; - - hdev->reassembly[index] = skb; - } - - while (count) { - scb = (void *) skb->cb; - len = min_t(uint, scb->expect, count); - - memcpy(skb_put(skb, len), data, len); - - count -= len; - data += len; - scb->expect -= len; - remain = count; - - switch (type) { - case HCI_EVENT_PKT: - if (skb->len == HCI_EVENT_HDR_SIZE) { - struct hci_event_hdr *h = hci_event_hdr(skb); - scb->expect = h->plen; - - if (skb_tailroom(skb) < scb->expect) { - kfree_skb(skb); - hdev->reassembly[index] = NULL; - return -ENOMEM; - } - } - break; - - case HCI_ACLDATA_PKT: - if (skb->len == HCI_ACL_HDR_SIZE) { - struct hci_acl_hdr *h = hci_acl_hdr(skb); - scb->expect = __le16_to_cpu(h->dlen); - - if (skb_tailroom(skb) < scb->expect) { - kfree_skb(skb); - hdev->reassembly[index] = NULL; - return -ENOMEM; - } - } - break; - - case HCI_SCODATA_PKT: - if (skb->len == HCI_SCO_HDR_SIZE) { - struct hci_sco_hdr *h = hci_sco_hdr(skb); - scb->expect = h->dlen; - - if (skb_tailroom(skb) < scb->expect) { - kfree_skb(skb); - hdev->reassembly[index] = NULL; - return -ENOMEM; - } - } - break; - } - - if (scb->expect == 0) { - /* Complete frame */ - - bt_cb(skb)->pkt_type = type; - hci_recv_frame(hdev, skb); - - hdev->reassembly[index] = NULL; - return remain; - } - } - - return remain; -} - -#define STREAM_REASSEMBLY 0 - -int hci_recv_stream_fragment(struct hci_dev *hdev, const void *data, int count) -{ - int type; - int rem = 0; - - while (count) { - struct sk_buff *skb = hdev->reassembly[STREAM_REASSEMBLY]; - - if (!skb) { - const struct { char type; } *pkt; - - /* Start of the frame */ - pkt = data; - type = pkt->type; - - data++; - count--; - } else - type = bt_cb(skb)->pkt_type; - - rem = hci_reassembly(hdev, type, data, count, - STREAM_REASSEMBLY); - if (rem < 0) - return rem; - - data += (count - rem); - count = rem; - } - - return rem; -} -EXPORT_SYMBOL(hci_recv_stream_fragment); - /* ---- Interface to upper protocols ---- */ int hci_register_cb(struct hci_cb *cb) -- cgit v1.2.3 From 0ea18b40cdd382a8a338d02e06c4646c35bd753b Mon Sep 17 00:00:00 2001 From: Alex Williamson Date: Tue, 7 Apr 2015 11:14:38 -0600 Subject: vgaarb: Stub vga_set_legacy_decoding() vga_set_legacy_decoding() is defined in drivers/gpu/vga/vgaarb.c, which is only compiled with CONFIG_VGA_ARB. A caller would therefore get an undefined symbol if the VGA arbiter is not enabled. Signed-off-by: Alex Williamson Acked-by: Dave Airlie --- include/linux/vgaarb.h | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'include') diff --git a/include/linux/vgaarb.h b/include/linux/vgaarb.h index c37bd4d06739..8c3b412d84df 100644 --- a/include/linux/vgaarb.h +++ b/include/linux/vgaarb.h @@ -65,8 +65,13 @@ struct pci_dev; * out of the arbitration process (and can be safe to take * interrupts at any time. */ +#if defined(CONFIG_VGA_ARB) extern void vga_set_legacy_decoding(struct pci_dev *pdev, unsigned int decodes); +#else +static inline void vga_set_legacy_decoding(struct pci_dev *pdev, + unsigned int decodes) { }; +#endif /** * vga_get - acquire & locks VGA resources -- cgit v1.2.3 From 107a9f4dc9211c1f91703d1739d7fd22ac58b332 Mon Sep 17 00:00:00 2001 From: David Miller Date: Sun, 5 Apr 2015 22:18:54 -0400 Subject: netfilter: Add nf_hook_state initializer function. This way we can consolidate where we setup new nf_hook_state objects, to make sure the entire thing is initialized. The only other place an nf_hook_object is instantiated is nf_queue, wherein a structure copy is used. Signed-off-by: David S. Miller --- include/linux/netfilter.h | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) (limited to 'include') diff --git a/include/linux/netfilter.h b/include/linux/netfilter.h index c480c43ad8f7..b8c88f3c85ff 100644 --- a/include/linux/netfilter.h +++ b/include/linux/netfilter.h @@ -54,6 +54,21 @@ struct nf_hook_state { int (*okfn)(struct sk_buff *); }; +static inline void nf_hook_state_init(struct nf_hook_state *p, + unsigned int hook, + int thresh, u_int8_t pf, + struct net_device *indev, + struct net_device *outdev, + int (*okfn)(struct sk_buff *)) +{ + p->hook = hook; + p->thresh = thresh; + p->pf = pf; + p->in = indev; + p->out = outdev; + p->okfn = okfn; +} + typedef unsigned int nf_hookfn(const struct nf_hook_ops *ops, struct sk_buff *skb, const struct nf_hook_state *state); @@ -142,15 +157,10 @@ static inline int nf_hook_thresh(u_int8_t pf, unsigned int hook, int (*okfn)(struct sk_buff *), int thresh) { if (nf_hooks_active(pf, hook)) { - struct nf_hook_state state = { - .hook = hook, - .thresh = thresh, - .pf = pf, - .in = indev, - .out = outdev, - .okfn = okfn - }; + struct nf_hook_state state; + nf_hook_state_init(&state, hook, thresh, pf, + indev, outdev, okfn); return nf_hook_slow(skb, &state); } return 1; -- cgit v1.2.3 From 1c984f8a5df085bcf35364a8a870bd4db4da4ed3 Mon Sep 17 00:00:00 2001 From: David Miller Date: Sun, 5 Apr 2015 22:19:00 -0400 Subject: netfilter: Add socket pointer to nf_hook_state. It is currently always set to NULL, but nf_queue is adjusted to be prepared for it being set to a real socket by taking and releasing a reference to that socket when necessary. Signed-off-by: David S. Miller --- include/linux/netfilter.h | 7 ++++++- net/netfilter/nf_queue.c | 4 ++++ 2 files changed, 10 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/netfilter.h b/include/linux/netfilter.h index b8c88f3c85ff..f8f58fab2402 100644 --- a/include/linux/netfilter.h +++ b/include/linux/netfilter.h @@ -45,12 +45,15 @@ struct sk_buff; struct nf_hook_ops; +struct sock; + struct nf_hook_state { unsigned int hook; int thresh; u_int8_t pf; struct net_device *in; struct net_device *out; + struct sock *sk; int (*okfn)(struct sk_buff *); }; @@ -59,6 +62,7 @@ static inline void nf_hook_state_init(struct nf_hook_state *p, int thresh, u_int8_t pf, struct net_device *indev, struct net_device *outdev, + struct sock *sk, int (*okfn)(struct sk_buff *)) { p->hook = hook; @@ -66,6 +70,7 @@ static inline void nf_hook_state_init(struct nf_hook_state *p, p->pf = pf; p->in = indev; p->out = outdev; + p->sk = sk; p->okfn = okfn; } @@ -160,7 +165,7 @@ static inline int nf_hook_thresh(u_int8_t pf, unsigned int hook, struct nf_hook_state state; nf_hook_state_init(&state, hook, thresh, pf, - indev, outdev, okfn); + indev, outdev, NULL, okfn); return nf_hook_slow(skb, &state); } return 1; diff --git a/net/netfilter/nf_queue.c b/net/netfilter/nf_queue.c index d3cd37edca18..c4a706678f88 100644 --- a/net/netfilter/nf_queue.c +++ b/net/netfilter/nf_queue.c @@ -54,6 +54,8 @@ void nf_queue_entry_release_refs(struct nf_queue_entry *entry) dev_put(state->in); if (state->out) dev_put(state->out); + if (state->sk) + sock_put(state->sk); #if IS_ENABLED(CONFIG_BRIDGE_NETFILTER) if (entry->skb->nf_bridge) { struct nf_bridge_info *nf_bridge = entry->skb->nf_bridge; @@ -81,6 +83,8 @@ bool nf_queue_entry_get_refs(struct nf_queue_entry *entry) dev_hold(state->in); if (state->out) dev_hold(state->out); + if (state->sk) + sock_hold(state->sk); #if IS_ENABLED(CONFIG_BRIDGE_NETFILTER) if (entry->skb->nf_bridge) { struct nf_bridge_info *nf_bridge = entry->skb->nf_bridge; -- cgit v1.2.3 From 7026b1ddb6b8d4e6ee33dc2bd06c0ca8746fa7ab Mon Sep 17 00:00:00 2001 From: David Miller Date: Sun, 5 Apr 2015 22:19:04 -0400 Subject: netfilter: Pass socket pointer down through okfn(). On the output paths in particular, we have to sometimes deal with two socket contexts. First, and usually skb->sk, is the local socket that generated the frame. And second, is potentially the socket used to control a tunneling socket, such as one the encapsulates using UDP. We do not want to disassociate skb->sk when encapsulating in order to fix this, because that would break socket memory accounting. The most extreme case where this can cause huge problems is an AF_PACKET socket transmitting over a vxlan device. We hit code paths doing checks that assume they are dealing with an ipv4 socket, but are actually operating upon the AF_PACKET one. Signed-off-by: David S. Miller --- include/linux/netdevice.h | 14 +++++-- include/linux/netfilter.h | 62 +++++++++++++++++-------------- include/linux/netfilter_bridge.h | 2 +- include/net/dn_neigh.h | 6 +-- include/net/ip.h | 3 +- include/net/ip6_route.h | 3 +- include/net/ipv6.h | 2 +- include/net/xfrm.h | 8 ++-- net/bridge/br_forward.c | 13 ++++--- net/bridge/br_input.c | 16 ++++---- net/bridge/br_multicast.c | 3 +- net/bridge/br_netfilter.c | 56 +++++++++++++++------------- net/bridge/br_private.h | 6 +-- net/bridge/br_stp_bpdu.c | 5 ++- net/core/dev.c | 10 ++--- net/decnet/dn_neigh.c | 35 +++++++++-------- net/decnet/dn_nsp_in.c | 5 ++- net/decnet/dn_route.c | 26 +++++++------ net/ipv4/arp.c | 10 +++-- net/ipv4/ip_forward.c | 8 ++-- net/ipv4/ip_input.c | 10 +++-- net/ipv4/ip_output.c | 45 ++++++++++++---------- net/ipv4/ipmr.c | 7 ++-- net/ipv4/raw.c | 4 +- net/ipv4/xfrm4_input.c | 5 ++- net/ipv4/xfrm4_output.c | 12 +++--- net/ipv6/ip6_input.c | 11 +++--- net/ipv6/ip6_output.c | 33 ++++++++-------- net/ipv6/ip6mr.c | 7 ++-- net/ipv6/mcast.c | 9 +++-- net/ipv6/ndisc.c | 5 ++- net/ipv6/netfilter/nf_defrag_ipv6_hooks.c | 2 +- net/ipv6/output_core.c | 4 +- net/ipv6/raw.c | 4 +- net/ipv6/xfrm6_input.c | 3 +- net/ipv6/xfrm6_output.c | 15 ++++---- net/netfilter/ipvs/ip_vs_xmit.c | 8 ++-- net/netfilter/nf_queue.c | 2 +- net/xfrm/xfrm_output.c | 16 ++++---- 39 files changed, 277 insertions(+), 218 deletions(-) (limited to 'include') diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 41bf58a2b936..45823db2efb0 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -2165,8 +2165,12 @@ int dev_open(struct net_device *dev); int dev_close(struct net_device *dev); int dev_close_many(struct list_head *head, bool unlink); void dev_disable_lro(struct net_device *dev); -int dev_loopback_xmit(struct sk_buff *newskb); -int dev_queue_xmit(struct sk_buff *skb); +int dev_loopback_xmit(struct sock *sk, struct sk_buff *newskb); +int dev_queue_xmit_sk(struct sock *sk, struct sk_buff *skb); +static inline int dev_queue_xmit(struct sk_buff *skb) +{ + return dev_queue_xmit_sk(skb->sk, skb); +} int dev_queue_xmit_accel(struct sk_buff *skb, void *accel_priv); int register_netdevice(struct net_device *dev); void unregister_netdevice_queue(struct net_device *dev, struct list_head *head); @@ -2927,7 +2931,11 @@ static inline void dev_consume_skb_any(struct sk_buff *skb) int netif_rx(struct sk_buff *skb); int netif_rx_ni(struct sk_buff *skb); -int netif_receive_skb(struct sk_buff *skb); +int netif_receive_skb_sk(struct sock *sk, struct sk_buff *skb); +static inline int netif_receive_skb(struct sk_buff *skb) +{ + return netif_receive_skb_sk(skb->sk, skb); +} gro_result_t napi_gro_receive(struct napi_struct *napi, struct sk_buff *skb); void napi_gro_flush(struct napi_struct *napi, bool flush_old); struct sk_buff *napi_get_frags(struct napi_struct *napi); diff --git a/include/linux/netfilter.h b/include/linux/netfilter.h index f8f58fab2402..63560d0a8dfe 100644 --- a/include/linux/netfilter.h +++ b/include/linux/netfilter.h @@ -54,7 +54,7 @@ struct nf_hook_state { struct net_device *in; struct net_device *out; struct sock *sk; - int (*okfn)(struct sk_buff *); + int (*okfn)(struct sock *, struct sk_buff *); }; static inline void nf_hook_state_init(struct nf_hook_state *p, @@ -63,7 +63,7 @@ static inline void nf_hook_state_init(struct nf_hook_state *p, struct net_device *indev, struct net_device *outdev, struct sock *sk, - int (*okfn)(struct sk_buff *)) + int (*okfn)(struct sock *, struct sk_buff *)) { p->hook = hook; p->thresh = thresh; @@ -156,26 +156,29 @@ int nf_hook_slow(struct sk_buff *skb, struct nf_hook_state *state); * value indicates the packet has been consumed by the hook. */ static inline int nf_hook_thresh(u_int8_t pf, unsigned int hook, + struct sock *sk, struct sk_buff *skb, struct net_device *indev, struct net_device *outdev, - int (*okfn)(struct sk_buff *), int thresh) + int (*okfn)(struct sock *, struct sk_buff *), + int thresh) { if (nf_hooks_active(pf, hook)) { struct nf_hook_state state; nf_hook_state_init(&state, hook, thresh, pf, - indev, outdev, NULL, okfn); + indev, outdev, sk, okfn); return nf_hook_slow(skb, &state); } return 1; } -static inline int nf_hook(u_int8_t pf, unsigned int hook, struct sk_buff *skb, - struct net_device *indev, struct net_device *outdev, - int (*okfn)(struct sk_buff *)) +static inline int nf_hook(u_int8_t pf, unsigned int hook, struct sock *sk, + struct sk_buff *skb, struct net_device *indev, + struct net_device *outdev, + int (*okfn)(struct sock *, struct sk_buff *)) { - return nf_hook_thresh(pf, hook, skb, indev, outdev, okfn, INT_MIN); + return nf_hook_thresh(pf, hook, sk, skb, indev, outdev, okfn, INT_MIN); } /* Activate hook; either okfn or kfree_skb called, unless a hook @@ -196,35 +199,36 @@ static inline int nf_hook(u_int8_t pf, unsigned int hook, struct sk_buff *skb, */ static inline int -NF_HOOK_THRESH(uint8_t pf, unsigned int hook, struct sk_buff *skb, - struct net_device *in, struct net_device *out, - int (*okfn)(struct sk_buff *), int thresh) +NF_HOOK_THRESH(uint8_t pf, unsigned int hook, struct sock *sk, + struct sk_buff *skb, struct net_device *in, + struct net_device *out, + int (*okfn)(struct sock *, struct sk_buff *), int thresh) { - int ret = nf_hook_thresh(pf, hook, skb, in, out, okfn, thresh); + int ret = nf_hook_thresh(pf, hook, sk, skb, in, out, okfn, thresh); if (ret == 1) - ret = okfn(skb); + ret = okfn(sk, skb); return ret; } static inline int -NF_HOOK_COND(uint8_t pf, unsigned int hook, struct sk_buff *skb, - struct net_device *in, struct net_device *out, - int (*okfn)(struct sk_buff *), bool cond) +NF_HOOK_COND(uint8_t pf, unsigned int hook, struct sock *sk, + struct sk_buff *skb, struct net_device *in, struct net_device *out, + int (*okfn)(struct sock *, struct sk_buff *), bool cond) { int ret; if (!cond || - ((ret = nf_hook_thresh(pf, hook, skb, in, out, okfn, INT_MIN)) == 1)) - ret = okfn(skb); + ((ret = nf_hook_thresh(pf, hook, sk, skb, in, out, okfn, INT_MIN)) == 1)) + ret = okfn(sk, skb); return ret; } static inline int -NF_HOOK(uint8_t pf, unsigned int hook, struct sk_buff *skb, +NF_HOOK(uint8_t pf, unsigned int hook, struct sock *sk, struct sk_buff *skb, struct net_device *in, struct net_device *out, - int (*okfn)(struct sk_buff *)) + int (*okfn)(struct sock *, struct sk_buff *)) { - return NF_HOOK_THRESH(pf, hook, skb, in, out, okfn, INT_MIN); + return NF_HOOK_THRESH(pf, hook, sk, skb, in, out, okfn, INT_MIN); } /* Call setsockopt() */ @@ -324,19 +328,21 @@ nf_nat_decode_session(struct sk_buff *skb, struct flowi *fl, u_int8_t family) } #else /* !CONFIG_NETFILTER */ -#define NF_HOOK(pf, hook, skb, indev, outdev, okfn) (okfn)(skb) -#define NF_HOOK_COND(pf, hook, skb, indev, outdev, okfn, cond) (okfn)(skb) +#define NF_HOOK(pf, hook, sk, skb, indev, outdev, okfn) (okfn)(sk, skb) +#define NF_HOOK_COND(pf, hook, sk, skb, indev, outdev, okfn, cond) (okfn)(sk, skb) static inline int nf_hook_thresh(u_int8_t pf, unsigned int hook, + struct sock *sk, struct sk_buff *skb, struct net_device *indev, struct net_device *outdev, - int (*okfn)(struct sk_buff *), int thresh) + int (*okfn)(struct sock *sk, struct sk_buff *), int thresh) { - return okfn(skb); + return okfn(sk, skb); } -static inline int nf_hook(u_int8_t pf, unsigned int hook, struct sk_buff *skb, - struct net_device *indev, struct net_device *outdev, - int (*okfn)(struct sk_buff *)) +static inline int nf_hook(u_int8_t pf, unsigned int hook, struct sock *sk, + struct sk_buff *skb, struct net_device *indev, + struct net_device *outdev, + int (*okfn)(struct sock *, struct sk_buff *)) { return 1; } diff --git a/include/linux/netfilter_bridge.h b/include/linux/netfilter_bridge.h index 2734977199ca..5fc0a0fe244b 100644 --- a/include/linux/netfilter_bridge.h +++ b/include/linux/netfilter_bridge.h @@ -30,7 +30,7 @@ static inline unsigned int nf_bridge_mtu_reduction(const struct sk_buff *skb) return 0; } -int br_handle_frame_finish(struct sk_buff *skb); +int br_handle_frame_finish(struct sock *sk, struct sk_buff *skb); static inline void br_drop_fake_rtable(struct sk_buff *skb) { diff --git a/include/net/dn_neigh.h b/include/net/dn_neigh.h index 0f26aa707e62..d0424269313f 100644 --- a/include/net/dn_neigh.h +++ b/include/net/dn_neigh.h @@ -18,11 +18,11 @@ struct dn_neigh { void dn_neigh_init(void); void dn_neigh_cleanup(void); -int dn_neigh_router_hello(struct sk_buff *skb); -int dn_neigh_endnode_hello(struct sk_buff *skb); +int dn_neigh_router_hello(struct sock *sk, struct sk_buff *skb); +int dn_neigh_endnode_hello(struct sock *sk, struct sk_buff *skb); void dn_neigh_pointopoint_hello(struct sk_buff *skb); int dn_neigh_elist(struct net_device *dev, unsigned char *ptr, int n); -int dn_to_neigh_output(struct sk_buff *skb); +int dn_to_neigh_output(struct sock *sk, struct sk_buff *skb); extern struct neigh_table dn_neigh_table; diff --git a/include/net/ip.h b/include/net/ip.h index 69cd9cb8400c..d14af7edd197 100644 --- a/include/net/ip.h +++ b/include/net/ip.h @@ -108,7 +108,8 @@ int ip_local_deliver(struct sk_buff *skb); int ip_mr_input(struct sk_buff *skb); int ip_output(struct sock *sk, struct sk_buff *skb); int ip_mc_output(struct sock *sk, struct sk_buff *skb); -int ip_fragment(struct sk_buff *skb, int (*output)(struct sk_buff *)); +int ip_fragment(struct sock *sk, struct sk_buff *skb, + int (*output)(struct sock *, struct sk_buff *)); int ip_do_nat(struct sk_buff *skb); void ip_send_check(struct iphdr *ip); int __ip_local_out(struct sk_buff *skb); diff --git a/include/net/ip6_route.h b/include/net/ip6_route.h index eda131d179d9..5e192068e6cb 100644 --- a/include/net/ip6_route.h +++ b/include/net/ip6_route.h @@ -170,7 +170,8 @@ static inline bool ipv6_anycast_destination(const struct sk_buff *skb) return rt->rt6i_flags & RTF_ANYCAST; } -int ip6_fragment(struct sk_buff *skb, int (*output)(struct sk_buff *)); +int ip6_fragment(struct sock *sk, struct sk_buff *skb, + int (*output)(struct sock *, struct sk_buff *)); static inline int ip6_skb_dst_mtu(struct sk_buff *skb) { diff --git a/include/net/ipv6.h b/include/net/ipv6.h index 65142e6af440..b6ae959824ff 100644 --- a/include/net/ipv6.h +++ b/include/net/ipv6.h @@ -769,7 +769,7 @@ static inline u8 ip6_tclass(__be32 flowinfo) int ipv6_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt, struct net_device *orig_dev); -int ip6_rcv_finish(struct sk_buff *skb); +int ip6_rcv_finish(struct sock *sk, struct sk_buff *skb); /* * upper-layer output functions diff --git a/include/net/xfrm.h b/include/net/xfrm.h index 461f83539493..36ac102c97c7 100644 --- a/include/net/xfrm.h +++ b/include/net/xfrm.h @@ -332,7 +332,7 @@ struct xfrm_state_afinfo { int (*tmpl_sort)(struct xfrm_tmpl **dst, struct xfrm_tmpl **src, int n); int (*state_sort)(struct xfrm_state **dst, struct xfrm_state **src, int n); int (*output)(struct sock *sk, struct sk_buff *skb); - int (*output_finish)(struct sk_buff *skb); + int (*output_finish)(struct sock *sk, struct sk_buff *skb); int (*extract_input)(struct xfrm_state *x, struct sk_buff *skb); int (*extract_output)(struct xfrm_state *x, @@ -1503,7 +1503,7 @@ int xfrm_prepare_input(struct xfrm_state *x, struct sk_buff *skb); int xfrm_input(struct sk_buff *skb, int nexthdr, __be32 spi, int encap_type); int xfrm_input_resume(struct sk_buff *skb, int nexthdr); int xfrm_output_resume(struct sk_buff *skb, int err); -int xfrm_output(struct sk_buff *skb); +int xfrm_output(struct sock *sk, struct sk_buff *skb); int xfrm_inner_extract_output(struct xfrm_state *x, struct sk_buff *skb); void xfrm_local_error(struct sk_buff *skb, int mtu); int xfrm4_extract_header(struct sk_buff *skb); @@ -1524,7 +1524,7 @@ static inline int xfrm4_rcv_spi(struct sk_buff *skb, int nexthdr, __be32 spi) int xfrm4_extract_output(struct xfrm_state *x, struct sk_buff *skb); int xfrm4_prepare_output(struct xfrm_state *x, struct sk_buff *skb); int xfrm4_output(struct sock *sk, struct sk_buff *skb); -int xfrm4_output_finish(struct sk_buff *skb); +int xfrm4_output_finish(struct sock *sk, struct sk_buff *skb); int xfrm4_rcv_cb(struct sk_buff *skb, u8 protocol, int err); int xfrm4_protocol_register(struct xfrm4_protocol *handler, unsigned char protocol); int xfrm4_protocol_deregister(struct xfrm4_protocol *handler, unsigned char protocol); @@ -1549,7 +1549,7 @@ __be32 xfrm6_tunnel_spi_lookup(struct net *net, const xfrm_address_t *saddr); int xfrm6_extract_output(struct xfrm_state *x, struct sk_buff *skb); int xfrm6_prepare_output(struct xfrm_state *x, struct sk_buff *skb); int xfrm6_output(struct sock *sk, struct sk_buff *skb); -int xfrm6_output_finish(struct sk_buff *skb); +int xfrm6_output_finish(struct sock *sk, struct sk_buff *skb); int xfrm6_find_1stfragopt(struct xfrm_state *x, struct sk_buff *skb, u8 **prevhdr); diff --git a/net/bridge/br_forward.c b/net/bridge/br_forward.c index 3304a5442331..e97572b5d2cc 100644 --- a/net/bridge/br_forward.c +++ b/net/bridge/br_forward.c @@ -35,7 +35,7 @@ static inline int should_deliver(const struct net_bridge_port *p, p->state == BR_STATE_FORWARDING; } -int br_dev_queue_push_xmit(struct sk_buff *skb) +int br_dev_queue_push_xmit(struct sock *sk, struct sk_buff *skb) { if (!is_skb_forwardable(skb->dev, skb)) { kfree_skb(skb); @@ -49,9 +49,10 @@ int br_dev_queue_push_xmit(struct sk_buff *skb) } EXPORT_SYMBOL_GPL(br_dev_queue_push_xmit); -int br_forward_finish(struct sk_buff *skb) +int br_forward_finish(struct sock *sk, struct sk_buff *skb) { - return NF_HOOK(NFPROTO_BRIDGE, NF_BR_POST_ROUTING, skb, NULL, skb->dev, + return NF_HOOK(NFPROTO_BRIDGE, NF_BR_POST_ROUTING, sk, skb, + NULL, skb->dev, br_dev_queue_push_xmit); } @@ -75,7 +76,8 @@ static void __br_deliver(const struct net_bridge_port *to, struct sk_buff *skb) return; } - NF_HOOK(NFPROTO_BRIDGE, NF_BR_LOCAL_OUT, skb, NULL, skb->dev, + NF_HOOK(NFPROTO_BRIDGE, NF_BR_LOCAL_OUT, NULL, skb, + NULL, skb->dev, br_forward_finish); } @@ -96,7 +98,8 @@ static void __br_forward(const struct net_bridge_port *to, struct sk_buff *skb) skb->dev = to->dev; skb_forward_csum(skb); - NF_HOOK(NFPROTO_BRIDGE, NF_BR_FORWARD, skb, indev, skb->dev, + NF_HOOK(NFPROTO_BRIDGE, NF_BR_FORWARD, NULL, skb, + indev, skb->dev, br_forward_finish); } diff --git a/net/bridge/br_input.c b/net/bridge/br_input.c index 052c5ebbc947..f921a5dce22d 100644 --- a/net/bridge/br_input.c +++ b/net/bridge/br_input.c @@ -55,8 +55,9 @@ static int br_pass_frame_up(struct sk_buff *skb) if (!skb) return NET_RX_DROP; - return NF_HOOK(NFPROTO_BRIDGE, NF_BR_LOCAL_IN, skb, indev, NULL, - netif_receive_skb); + return NF_HOOK(NFPROTO_BRIDGE, NF_BR_LOCAL_IN, NULL, skb, + indev, NULL, + netif_receive_skb_sk); } static void br_do_proxy_arp(struct sk_buff *skb, struct net_bridge *br, @@ -119,7 +120,7 @@ static void br_do_proxy_arp(struct sk_buff *skb, struct net_bridge *br, } /* note: already called with rcu_read_lock */ -int br_handle_frame_finish(struct sk_buff *skb) +int br_handle_frame_finish(struct sock *sk, struct sk_buff *skb) { const unsigned char *dest = eth_hdr(skb)->h_dest; struct net_bridge_port *p = br_port_get_rcu(skb->dev); @@ -207,7 +208,7 @@ drop: EXPORT_SYMBOL_GPL(br_handle_frame_finish); /* note: already called with rcu_read_lock */ -static int br_handle_local_finish(struct sk_buff *skb) +static int br_handle_local_finish(struct sock *sk, struct sk_buff *skb) { struct net_bridge_port *p = br_port_get_rcu(skb->dev); u16 vid = 0; @@ -277,8 +278,8 @@ rx_handler_result_t br_handle_frame(struct sk_buff **pskb) } /* Deliver packet to local host only */ - if (NF_HOOK(NFPROTO_BRIDGE, NF_BR_LOCAL_IN, skb, skb->dev, - NULL, br_handle_local_finish)) { + if (NF_HOOK(NFPROTO_BRIDGE, NF_BR_LOCAL_IN, NULL, skb, + skb->dev, NULL, br_handle_local_finish)) { return RX_HANDLER_CONSUMED; /* consumed by filter */ } else { *pskb = skb; @@ -302,7 +303,8 @@ forward: if (ether_addr_equal(p->br->dev->dev_addr, dest)) skb->pkt_type = PACKET_HOST; - NF_HOOK(NFPROTO_BRIDGE, NF_BR_PRE_ROUTING, skb, skb->dev, NULL, + NF_HOOK(NFPROTO_BRIDGE, NF_BR_PRE_ROUTING, NULL, skb, + skb->dev, NULL, br_handle_frame_finish); break; default: diff --git a/net/bridge/br_multicast.c b/net/bridge/br_multicast.c index c465876c7861..4b6722f8f179 100644 --- a/net/bridge/br_multicast.c +++ b/net/bridge/br_multicast.c @@ -814,7 +814,8 @@ static void __br_multicast_send_query(struct net_bridge *br, if (port) { skb->dev = port->dev; - NF_HOOK(NFPROTO_BRIDGE, NF_BR_LOCAL_OUT, skb, NULL, skb->dev, + NF_HOOK(NFPROTO_BRIDGE, NF_BR_LOCAL_OUT, NULL, skb, + NULL, skb->dev, br_dev_queue_push_xmit); } else { br_multicast_select_own_querier(br, ip, skb); diff --git a/net/bridge/br_netfilter.c b/net/bridge/br_netfilter.c index 7527e94dd5dc..acd31c9f2116 100644 --- a/net/bridge/br_netfilter.c +++ b/net/bridge/br_netfilter.c @@ -261,7 +261,7 @@ static void nf_bridge_update_protocol(struct sk_buff *skb) /* PF_BRIDGE/PRE_ROUTING *********************************************/ /* Undo the changes made for ip6tables PREROUTING and continue the * bridge PRE_ROUTING hook. */ -static int br_nf_pre_routing_finish_ipv6(struct sk_buff *skb) +static int br_nf_pre_routing_finish_ipv6(struct sock *sk, struct sk_buff *skb) { struct nf_bridge_info *nf_bridge = skb->nf_bridge; struct rtable *rt; @@ -282,7 +282,8 @@ static int br_nf_pre_routing_finish_ipv6(struct sk_buff *skb) skb->dev = nf_bridge->physindev; nf_bridge_update_protocol(skb); nf_bridge_push_encap_header(skb); - NF_HOOK_THRESH(NFPROTO_BRIDGE, NF_BR_PRE_ROUTING, skb, skb->dev, NULL, + NF_HOOK_THRESH(NFPROTO_BRIDGE, NF_BR_PRE_ROUTING, sk, skb, + skb->dev, NULL, br_handle_frame_finish, 1); return 0; @@ -293,7 +294,7 @@ static int br_nf_pre_routing_finish_ipv6(struct sk_buff *skb) * don't, we use the neighbour framework to find out. In both cases, we make * sure that br_handle_frame_finish() is called afterwards. */ -static int br_nf_pre_routing_finish_bridge(struct sk_buff *skb) +static int br_nf_pre_routing_finish_bridge(struct sock *sk, struct sk_buff *skb) { struct nf_bridge_info *nf_bridge = skb->nf_bridge; struct neighbour *neigh; @@ -310,7 +311,7 @@ static int br_nf_pre_routing_finish_bridge(struct sk_buff *skb) if (neigh->hh.hh_len) { neigh_hh_bridge(&neigh->hh, skb); skb->dev = nf_bridge->physindev; - ret = br_handle_frame_finish(skb); + ret = br_handle_frame_finish(sk, skb); } else { /* the neighbour function below overwrites the complete * MAC header, so we save the Ethernet source address and @@ -387,7 +388,7 @@ static bool dnat_took_place(const struct sk_buff *skb) * device, we proceed as if ip_route_input() succeeded. If it differs from the * logical bridge port or if ip_route_output_key() fails we drop the packet. */ -static int br_nf_pre_routing_finish(struct sk_buff *skb) +static int br_nf_pre_routing_finish(struct sock *sk, struct sk_buff *skb) { struct net_device *dev = skb->dev; struct iphdr *iph = ip_hdr(skb); @@ -440,7 +441,7 @@ bridged_dnat: nf_bridge_push_encap_header(skb); NF_HOOK_THRESH(NFPROTO_BRIDGE, NF_BR_PRE_ROUTING, - skb, skb->dev, NULL, + sk, skb, skb->dev, NULL, br_nf_pre_routing_finish_bridge, 1); return 0; @@ -460,7 +461,8 @@ bridged_dnat: skb->dev = nf_bridge->physindev; nf_bridge_update_protocol(skb); nf_bridge_push_encap_header(skb); - NF_HOOK_THRESH(NFPROTO_BRIDGE, NF_BR_PRE_ROUTING, skb, skb->dev, NULL, + NF_HOOK_THRESH(NFPROTO_BRIDGE, NF_BR_PRE_ROUTING, sk, skb, + skb->dev, NULL, br_handle_frame_finish, 1); return 0; @@ -596,7 +598,8 @@ static unsigned int br_nf_pre_routing_ipv6(const struct nf_hook_ops *ops, return NF_DROP; skb->protocol = htons(ETH_P_IPV6); - NF_HOOK(NFPROTO_IPV6, NF_INET_PRE_ROUTING, skb, skb->dev, NULL, + NF_HOOK(NFPROTO_IPV6, NF_INET_PRE_ROUTING, state->sk, skb, + skb->dev, NULL, br_nf_pre_routing_finish_ipv6); return NF_STOLEN; @@ -651,7 +654,8 @@ static unsigned int br_nf_pre_routing(const struct nf_hook_ops *ops, skb->protocol = htons(ETH_P_IP); - NF_HOOK(NFPROTO_IPV4, NF_INET_PRE_ROUTING, skb, skb->dev, NULL, + NF_HOOK(NFPROTO_IPV4, NF_INET_PRE_ROUTING, state->sk, skb, + skb->dev, NULL, br_nf_pre_routing_finish); return NF_STOLEN; @@ -674,7 +678,7 @@ static unsigned int br_nf_local_in(const struct nf_hook_ops *ops, } /* PF_BRIDGE/FORWARD *************************************************/ -static int br_nf_forward_finish(struct sk_buff *skb) +static int br_nf_forward_finish(struct sock *sk, struct sk_buff *skb) { struct nf_bridge_info *nf_bridge = skb->nf_bridge; struct net_device *in; @@ -691,8 +695,8 @@ static int br_nf_forward_finish(struct sk_buff *skb) } nf_bridge_push_encap_header(skb); - NF_HOOK_THRESH(NFPROTO_BRIDGE, NF_BR_FORWARD, skb, in, - skb->dev, br_forward_finish, 1); + NF_HOOK_THRESH(NFPROTO_BRIDGE, NF_BR_FORWARD, sk, skb, + in, skb->dev, br_forward_finish, 1); return 0; } @@ -746,7 +750,8 @@ static unsigned int br_nf_forward_ip(const struct nf_hook_ops *ops, else skb->protocol = htons(ETH_P_IPV6); - NF_HOOK(pf, NF_INET_FORWARD, skb, brnf_get_logical_dev(skb, state->in), + NF_HOOK(pf, NF_INET_FORWARD, NULL, skb, + brnf_get_logical_dev(skb, state->in), parent, br_nf_forward_finish); return NF_STOLEN; @@ -780,8 +785,8 @@ static unsigned int br_nf_forward_arp(const struct nf_hook_ops *ops, return NF_ACCEPT; } *d = state->in; - NF_HOOK(NFPROTO_ARP, NF_ARP_FORWARD, skb, state->in, - state->out, br_nf_forward_finish); + NF_HOOK(NFPROTO_ARP, NF_ARP_FORWARD, state->sk, skb, + state->in, state->out, br_nf_forward_finish); return NF_STOLEN; } @@ -804,24 +809,24 @@ static bool nf_bridge_copy_header(struct sk_buff *skb) return true; } -static int br_nf_push_frag_xmit(struct sk_buff *skb) +static int br_nf_push_frag_xmit(struct sock *sk, struct sk_buff *skb) { if (!nf_bridge_copy_header(skb)) { kfree_skb(skb); return 0; } - return br_dev_queue_push_xmit(skb); + return br_dev_queue_push_xmit(sk, skb); } -static int br_nf_dev_queue_xmit(struct sk_buff *skb) +static int br_nf_dev_queue_xmit(struct sock *sk, struct sk_buff *skb) { int ret; int frag_max_size; unsigned int mtu_reserved; if (skb_is_gso(skb) || skb->protocol != htons(ETH_P_IP)) - return br_dev_queue_push_xmit(skb); + return br_dev_queue_push_xmit(sk, skb); mtu_reserved = nf_bridge_mtu_reduction(skb); /* This is wrong! We should preserve the original fragment @@ -833,16 +838,16 @@ static int br_nf_dev_queue_xmit(struct sk_buff *skb) /* Drop invalid packet */ return NF_DROP; IPCB(skb)->frag_max_size = frag_max_size; - ret = ip_fragment(skb, br_nf_push_frag_xmit); + ret = ip_fragment(sk, skb, br_nf_push_frag_xmit); } else - ret = br_dev_queue_push_xmit(skb); + ret = br_dev_queue_push_xmit(sk, skb); return ret; } #else -static int br_nf_dev_queue_xmit(struct sk_buff *skb) +static int br_nf_dev_queue_xmit(struct sock *sk, struct sk_buff *skb) { - return br_dev_queue_push_xmit(skb); + return br_dev_queue_push_xmit(sk, skb); } #endif @@ -887,7 +892,8 @@ static unsigned int br_nf_post_routing(const struct nf_hook_ops *ops, else skb->protocol = htons(ETH_P_IPV6); - NF_HOOK(pf, NF_INET_POST_ROUTING, skb, NULL, realoutdev, + NF_HOOK(pf, NF_INET_POST_ROUTING, state->sk, skb, + NULL, realoutdev, br_nf_dev_queue_xmit); return NF_STOLEN; @@ -927,7 +933,7 @@ static void br_nf_pre_routing_finish_bridge_slow(struct sk_buff *skb) skb_copy_to_linear_data_offset(skb, -(ETH_HLEN-ETH_ALEN), skb->nf_bridge->data, ETH_HLEN-ETH_ALEN); skb->dev = nf_bridge->physindev; - br_handle_frame_finish(skb); + br_handle_frame_finish(NULL, skb); } static int br_nf_dev_xmit(struct sk_buff *skb) diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h index b46fa0c5b8ec..6ca0251cb478 100644 --- a/net/bridge/br_private.h +++ b/net/bridge/br_private.h @@ -410,10 +410,10 @@ int br_fdb_external_learn_del(struct net_bridge *br, struct net_bridge_port *p, /* br_forward.c */ void br_deliver(const struct net_bridge_port *to, struct sk_buff *skb); -int br_dev_queue_push_xmit(struct sk_buff *skb); +int br_dev_queue_push_xmit(struct sock *sk, struct sk_buff *skb); void br_forward(const struct net_bridge_port *to, struct sk_buff *skb, struct sk_buff *skb0); -int br_forward_finish(struct sk_buff *skb); +int br_forward_finish(struct sock *sk, struct sk_buff *skb); void br_flood_deliver(struct net_bridge *br, struct sk_buff *skb, bool unicast); void br_flood_forward(struct net_bridge *br, struct sk_buff *skb, struct sk_buff *skb2, bool unicast); @@ -431,7 +431,7 @@ void br_port_flags_change(struct net_bridge_port *port, unsigned long mask); void br_manage_promisc(struct net_bridge *br); /* br_input.c */ -int br_handle_frame_finish(struct sk_buff *skb); +int br_handle_frame_finish(struct sock *sk, struct sk_buff *skb); rx_handler_result_t br_handle_frame(struct sk_buff **pskb); static inline bool br_rx_handler_check_rcu(const struct net_device *dev) diff --git a/net/bridge/br_stp_bpdu.c b/net/bridge/br_stp_bpdu.c index bdb459d21ad8..534fc4cd263e 100644 --- a/net/bridge/br_stp_bpdu.c +++ b/net/bridge/br_stp_bpdu.c @@ -54,8 +54,9 @@ static void br_send_bpdu(struct net_bridge_port *p, skb_reset_mac_header(skb); - NF_HOOK(NFPROTO_BRIDGE, NF_BR_LOCAL_OUT, skb, NULL, skb->dev, - dev_queue_xmit); + NF_HOOK(NFPROTO_BRIDGE, NF_BR_LOCAL_OUT, NULL, skb, + NULL, skb->dev, + dev_queue_xmit_sk); } static inline void br_set_ticks(unsigned char *dest, int j) diff --git a/net/core/dev.c b/net/core/dev.c index 3b3965288f52..b2775f06c710 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -2879,7 +2879,7 @@ EXPORT_SYMBOL(xmit_recursion); * dev_loopback_xmit - loop back @skb * @skb: buffer to transmit */ -int dev_loopback_xmit(struct sk_buff *skb) +int dev_loopback_xmit(struct sock *sk, struct sk_buff *skb) { skb_reset_mac_header(skb); __skb_pull(skb, skb_network_offset(skb)); @@ -3017,11 +3017,11 @@ out: return rc; } -int dev_queue_xmit(struct sk_buff *skb) +int dev_queue_xmit_sk(struct sock *sk, struct sk_buff *skb) { return __dev_queue_xmit(skb, NULL); } -EXPORT_SYMBOL(dev_queue_xmit); +EXPORT_SYMBOL(dev_queue_xmit_sk); int dev_queue_xmit_accel(struct sk_buff *skb, void *accel_priv) { @@ -3853,13 +3853,13 @@ static int netif_receive_skb_internal(struct sk_buff *skb) * NET_RX_SUCCESS: no congestion * NET_RX_DROP: packet was dropped */ -int netif_receive_skb(struct sk_buff *skb) +int netif_receive_skb_sk(struct sock *sk, struct sk_buff *skb) { trace_netif_receive_skb_entry(skb); return netif_receive_skb_internal(skb); } -EXPORT_SYMBOL(netif_receive_skb); +EXPORT_SYMBOL(netif_receive_skb_sk); /* Network device is going away, flush any packets still pending * Called with irqs disabled. diff --git a/net/decnet/dn_neigh.c b/net/decnet/dn_neigh.c index be1f08cdad29..4507b188fc51 100644 --- a/net/decnet/dn_neigh.c +++ b/net/decnet/dn_neigh.c @@ -194,7 +194,7 @@ static int dn_neigh_output(struct neighbour *neigh, struct sk_buff *skb) return err; } -static int dn_neigh_output_packet(struct sk_buff *skb) +static int dn_neigh_output_packet(struct sock *sk, struct sk_buff *skb) { struct dst_entry *dst = skb_dst(skb); struct dn_route *rt = (struct dn_route *)dst; @@ -206,7 +206,8 @@ static int dn_neigh_output_packet(struct sk_buff *skb) /* * For talking to broadcast devices: Ethernet & PPP */ -static int dn_long_output(struct neighbour *neigh, struct sk_buff *skb) +static int dn_long_output(struct neighbour *neigh, struct sock *sk, + struct sk_buff *skb) { struct net_device *dev = neigh->dev; int headroom = dev->hard_header_len + sizeof(struct dn_long_packet) + 3; @@ -245,14 +246,15 @@ static int dn_long_output(struct neighbour *neigh, struct sk_buff *skb) skb_reset_network_header(skb); - return NF_HOOK(NFPROTO_DECNET, NF_DN_POST_ROUTING, skb, NULL, - neigh->dev, dn_neigh_output_packet); + return NF_HOOK(NFPROTO_DECNET, NF_DN_POST_ROUTING, sk, skb, + NULL, neigh->dev, dn_neigh_output_packet); } /* * For talking to pointopoint and multidrop devices: DDCMP and X.25 */ -static int dn_short_output(struct neighbour *neigh, struct sk_buff *skb) +static int dn_short_output(struct neighbour *neigh, struct sock *sk, + struct sk_buff *skb) { struct net_device *dev = neigh->dev; int headroom = dev->hard_header_len + sizeof(struct dn_short_packet) + 2; @@ -284,8 +286,8 @@ static int dn_short_output(struct neighbour *neigh, struct sk_buff *skb) skb_reset_network_header(skb); - return NF_HOOK(NFPROTO_DECNET, NF_DN_POST_ROUTING, skb, NULL, - neigh->dev, dn_neigh_output_packet); + return NF_HOOK(NFPROTO_DECNET, NF_DN_POST_ROUTING, sk, skb, + NULL, neigh->dev, dn_neigh_output_packet); } /* @@ -293,7 +295,8 @@ static int dn_short_output(struct neighbour *neigh, struct sk_buff *skb) * Phase 3 output is the same as short output, execpt that * it clears the area bits before transmission. */ -static int dn_phase3_output(struct neighbour *neigh, struct sk_buff *skb) +static int dn_phase3_output(struct neighbour *neigh, struct sock *sk, + struct sk_buff *skb) { struct net_device *dev = neigh->dev; int headroom = dev->hard_header_len + sizeof(struct dn_short_packet) + 2; @@ -324,11 +327,11 @@ static int dn_phase3_output(struct neighbour *neigh, struct sk_buff *skb) skb_reset_network_header(skb); - return NF_HOOK(NFPROTO_DECNET, NF_DN_POST_ROUTING, skb, NULL, - neigh->dev, dn_neigh_output_packet); + return NF_HOOK(NFPROTO_DECNET, NF_DN_POST_ROUTING, sk, skb, + NULL, neigh->dev, dn_neigh_output_packet); } -int dn_to_neigh_output(struct sk_buff *skb) +int dn_to_neigh_output(struct sock *sk, struct sk_buff *skb) { struct dst_entry *dst = skb_dst(skb); struct dn_route *rt = (struct dn_route *) dst; @@ -347,11 +350,11 @@ int dn_to_neigh_output(struct sk_buff *skb) rcu_read_unlock(); if (dn->flags & DN_NDFLAG_P3) - return dn_phase3_output(neigh, skb); + return dn_phase3_output(neigh, sk, skb); if (use_long) - return dn_long_output(neigh, skb); + return dn_long_output(neigh, sk, skb); else - return dn_short_output(neigh, skb); + return dn_short_output(neigh, sk, skb); } /* @@ -372,7 +375,7 @@ void dn_neigh_pointopoint_hello(struct sk_buff *skb) /* * Ethernet router hello message received */ -int dn_neigh_router_hello(struct sk_buff *skb) +int dn_neigh_router_hello(struct sock *sk, struct sk_buff *skb) { struct rtnode_hello_message *msg = (struct rtnode_hello_message *)skb->data; @@ -434,7 +437,7 @@ int dn_neigh_router_hello(struct sk_buff *skb) /* * Endnode hello message received */ -int dn_neigh_endnode_hello(struct sk_buff *skb) +int dn_neigh_endnode_hello(struct sock *sk, struct sk_buff *skb) { struct endnode_hello_message *msg = (struct endnode_hello_message *)skb->data; struct neighbour *neigh; diff --git a/net/decnet/dn_nsp_in.c b/net/decnet/dn_nsp_in.c index fe5f01485d33..a321eac9fd0c 100644 --- a/net/decnet/dn_nsp_in.c +++ b/net/decnet/dn_nsp_in.c @@ -714,7 +714,7 @@ out: return ret; } -static int dn_nsp_rx_packet(struct sk_buff *skb) +static int dn_nsp_rx_packet(struct sock *sk2, struct sk_buff *skb) { struct dn_skb_cb *cb = DN_SKB_CB(skb); struct sock *sk = NULL; @@ -814,7 +814,8 @@ free_out: int dn_nsp_rx(struct sk_buff *skb) { - return NF_HOOK(NFPROTO_DECNET, NF_DN_LOCAL_IN, skb, skb->dev, NULL, + return NF_HOOK(NFPROTO_DECNET, NF_DN_LOCAL_IN, NULL, skb, + skb->dev, NULL, dn_nsp_rx_packet); } diff --git a/net/decnet/dn_route.c b/net/decnet/dn_route.c index 9ab0c4ba297f..03227ffd19ce 100644 --- a/net/decnet/dn_route.c +++ b/net/decnet/dn_route.c @@ -512,7 +512,7 @@ static int dn_return_long(struct sk_buff *skb) * * Returns: result of input function if route is found, error code otherwise */ -static int dn_route_rx_packet(struct sk_buff *skb) +static int dn_route_rx_packet(struct sock *sk, struct sk_buff *skb) { struct dn_skb_cb *cb; int err; @@ -573,7 +573,8 @@ static int dn_route_rx_long(struct sk_buff *skb) ptr++; cb->hops = *ptr++; /* Visit Count */ - return NF_HOOK(NFPROTO_DECNET, NF_DN_PRE_ROUTING, skb, skb->dev, NULL, + return NF_HOOK(NFPROTO_DECNET, NF_DN_PRE_ROUTING, NULL, skb, + skb->dev, NULL, dn_route_rx_packet); drop_it: @@ -600,7 +601,8 @@ static int dn_route_rx_short(struct sk_buff *skb) ptr += 2; cb->hops = *ptr & 0x3f; - return NF_HOOK(NFPROTO_DECNET, NF_DN_PRE_ROUTING, skb, skb->dev, NULL, + return NF_HOOK(NFPROTO_DECNET, NF_DN_PRE_ROUTING, NULL, skb, + skb->dev, NULL, dn_route_rx_packet); drop_it: @@ -608,7 +610,7 @@ drop_it: return NET_RX_DROP; } -static int dn_route_discard(struct sk_buff *skb) +static int dn_route_discard(struct sock *sk, struct sk_buff *skb) { /* * I know we drop the packet here, but thats considered success in @@ -618,7 +620,7 @@ static int dn_route_discard(struct sk_buff *skb) return NET_RX_SUCCESS; } -static int dn_route_ptp_hello(struct sk_buff *skb) +static int dn_route_ptp_hello(struct sock *sk, struct sk_buff *skb) { dn_dev_hello(skb); dn_neigh_pointopoint_hello(skb); @@ -704,22 +706,22 @@ int dn_route_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type switch (flags & DN_RT_CNTL_MSK) { case DN_RT_PKT_HELO: return NF_HOOK(NFPROTO_DECNET, NF_DN_HELLO, - skb, skb->dev, NULL, + NULL, skb, skb->dev, NULL, dn_route_ptp_hello); case DN_RT_PKT_L1RT: case DN_RT_PKT_L2RT: return NF_HOOK(NFPROTO_DECNET, NF_DN_ROUTE, - skb, skb->dev, NULL, + NULL, skb, skb->dev, NULL, dn_route_discard); case DN_RT_PKT_ERTH: return NF_HOOK(NFPROTO_DECNET, NF_DN_HELLO, - skb, skb->dev, NULL, + NULL, skb, skb->dev, NULL, dn_neigh_router_hello); case DN_RT_PKT_EEDH: return NF_HOOK(NFPROTO_DECNET, NF_DN_HELLO, - skb, skb->dev, NULL, + NULL, skb, skb->dev, NULL, dn_neigh_endnode_hello); } } else { @@ -768,7 +770,8 @@ static int dn_output(struct sock *sk, struct sk_buff *skb) cb->rt_flags |= DN_RT_F_IE; cb->hops = 0; - return NF_HOOK(NFPROTO_DECNET, NF_DN_LOCAL_OUT, skb, NULL, dev, + return NF_HOOK(NFPROTO_DECNET, NF_DN_LOCAL_OUT, sk, skb, + NULL, dev, dn_to_neigh_output); error: @@ -816,7 +819,8 @@ static int dn_forward(struct sk_buff *skb) if (rt->rt_flags & RTCF_DOREDIRECT) cb->rt_flags |= DN_RT_F_IE; - return NF_HOOK(NFPROTO_DECNET, NF_DN_FORWARD, skb, dev, skb->dev, + return NF_HOOK(NFPROTO_DECNET, NF_DN_FORWARD, NULL, skb, + dev, skb->dev, dn_to_neigh_output); drop: diff --git a/net/ipv4/arp.c b/net/ipv4/arp.c index c6e67aa46c32..933a92820d26 100644 --- a/net/ipv4/arp.c +++ b/net/ipv4/arp.c @@ -591,7 +591,8 @@ EXPORT_SYMBOL(arp_create); void arp_xmit(struct sk_buff *skb) { /* Send it off, maybe filter it using firewalling first. */ - NF_HOOK(NFPROTO_ARP, NF_ARP_OUT, skb, NULL, skb->dev, dev_queue_xmit); + NF_HOOK(NFPROTO_ARP, NF_ARP_OUT, NULL, skb, + NULL, skb->dev, dev_queue_xmit_sk); } EXPORT_SYMBOL(arp_xmit); @@ -625,7 +626,7 @@ EXPORT_SYMBOL(arp_send); * Process an arp request. */ -static int arp_process(struct sk_buff *skb) +static int arp_process(struct sock *sk, struct sk_buff *skb) { struct net_device *dev = skb->dev; struct in_device *in_dev = __in_dev_get_rcu(dev); @@ -846,7 +847,7 @@ out: static void parp_redo(struct sk_buff *skb) { - arp_process(skb); + arp_process(NULL, skb); } @@ -879,7 +880,8 @@ static int arp_rcv(struct sk_buff *skb, struct net_device *dev, memset(NEIGH_CB(skb), 0, sizeof(struct neighbour_cb)); - return NF_HOOK(NFPROTO_ARP, NF_ARP_IN, skb, dev, NULL, arp_process); + return NF_HOOK(NFPROTO_ARP, NF_ARP_IN, NULL, skb, + dev, NULL, arp_process); consumeskb: consume_skb(skb); diff --git a/net/ipv4/ip_forward.c b/net/ipv4/ip_forward.c index d9bc28ac5d1b..939992c456f3 100644 --- a/net/ipv4/ip_forward.c +++ b/net/ipv4/ip_forward.c @@ -57,7 +57,7 @@ static bool ip_exceeds_mtu(const struct sk_buff *skb, unsigned int mtu) } -static int ip_forward_finish(struct sk_buff *skb) +static int ip_forward_finish(struct sock *sk, struct sk_buff *skb) { struct ip_options *opt = &(IPCB(skb)->opt); @@ -68,7 +68,7 @@ static int ip_forward_finish(struct sk_buff *skb) ip_forward_options(skb); skb_sender_cpu_clear(skb); - return dst_output(skb); + return dst_output_sk(sk, skb); } int ip_forward(struct sk_buff *skb) @@ -136,8 +136,8 @@ int ip_forward(struct sk_buff *skb) skb->priority = rt_tos2priority(iph->tos); - return NF_HOOK(NFPROTO_IPV4, NF_INET_FORWARD, skb, skb->dev, - rt->dst.dev, ip_forward_finish); + return NF_HOOK(NFPROTO_IPV4, NF_INET_FORWARD, NULL, skb, + skb->dev, rt->dst.dev, ip_forward_finish); sr_failed: /* diff --git a/net/ipv4/ip_input.c b/net/ipv4/ip_input.c index 2e0410ed8f16..2db4c8773c1b 100644 --- a/net/ipv4/ip_input.c +++ b/net/ipv4/ip_input.c @@ -187,7 +187,7 @@ bool ip_call_ra_chain(struct sk_buff *skb) return false; } -static int ip_local_deliver_finish(struct sk_buff *skb) +static int ip_local_deliver_finish(struct sock *sk, struct sk_buff *skb) { struct net *net = dev_net(skb->dev); @@ -253,7 +253,8 @@ int ip_local_deliver(struct sk_buff *skb) return 0; } - return NF_HOOK(NFPROTO_IPV4, NF_INET_LOCAL_IN, skb, skb->dev, NULL, + return NF_HOOK(NFPROTO_IPV4, NF_INET_LOCAL_IN, NULL, skb, + skb->dev, NULL, ip_local_deliver_finish); } @@ -309,7 +310,7 @@ drop: int sysctl_ip_early_demux __read_mostly = 1; EXPORT_SYMBOL(sysctl_ip_early_demux); -static int ip_rcv_finish(struct sk_buff *skb) +static int ip_rcv_finish(struct sock *sk, struct sk_buff *skb) { const struct iphdr *iph = ip_hdr(skb); struct rtable *rt; @@ -451,7 +452,8 @@ int ip_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt, /* Must drop socket now because of tproxy. */ skb_orphan(skb); - return NF_HOOK(NFPROTO_IPV4, NF_INET_PRE_ROUTING, skb, dev, NULL, + return NF_HOOK(NFPROTO_IPV4, NF_INET_PRE_ROUTING, NULL, skb, + dev, NULL, ip_rcv_finish); csum_error: diff --git a/net/ipv4/ip_output.c b/net/ipv4/ip_output.c index 26f6f7956168..5da4d15262fd 100644 --- a/net/ipv4/ip_output.c +++ b/net/ipv4/ip_output.c @@ -91,14 +91,19 @@ void ip_send_check(struct iphdr *iph) } EXPORT_SYMBOL(ip_send_check); -int __ip_local_out(struct sk_buff *skb) +int __ip_local_out_sk(struct sock *sk, struct sk_buff *skb) { struct iphdr *iph = ip_hdr(skb); iph->tot_len = htons(skb->len); ip_send_check(iph); - return nf_hook(NFPROTO_IPV4, NF_INET_LOCAL_OUT, skb, NULL, - skb_dst(skb)->dev, dst_output); + return nf_hook(NFPROTO_IPV4, NF_INET_LOCAL_OUT, sk, skb, NULL, + skb_dst(skb)->dev, dst_output_sk); +} + +int __ip_local_out(struct sk_buff *skb) +{ + return __ip_local_out_sk(skb->sk, skb); } int ip_local_out_sk(struct sock *sk, struct sk_buff *skb) @@ -163,7 +168,7 @@ int ip_build_and_send_pkt(struct sk_buff *skb, struct sock *sk, } EXPORT_SYMBOL_GPL(ip_build_and_send_pkt); -static inline int ip_finish_output2(struct sk_buff *skb) +static inline int ip_finish_output2(struct sock *sk, struct sk_buff *skb) { struct dst_entry *dst = skb_dst(skb); struct rtable *rt = (struct rtable *)dst; @@ -211,7 +216,7 @@ static inline int ip_finish_output2(struct sk_buff *skb) return -EINVAL; } -static int ip_finish_output_gso(struct sk_buff *skb) +static int ip_finish_output_gso(struct sock *sk, struct sk_buff *skb) { netdev_features_t features; struct sk_buff *segs; @@ -220,7 +225,7 @@ static int ip_finish_output_gso(struct sk_buff *skb) /* common case: locally created skb or seglen is <= mtu */ if (((IPCB(skb)->flags & IPSKB_FORWARDED) == 0) || skb_gso_network_seglen(skb) <= ip_skb_dst_mtu(skb)) - return ip_finish_output2(skb); + return ip_finish_output2(sk, skb); /* Slowpath - GSO segment length is exceeding the dst MTU. * @@ -243,7 +248,7 @@ static int ip_finish_output_gso(struct sk_buff *skb) int err; segs->next = NULL; - err = ip_fragment(segs, ip_finish_output2); + err = ip_fragment(sk, segs, ip_finish_output2); if (err && ret == 0) ret = err; @@ -253,22 +258,22 @@ static int ip_finish_output_gso(struct sk_buff *skb) return ret; } -static int ip_finish_output(struct sk_buff *skb) +static int ip_finish_output(struct sock *sk, struct sk_buff *skb) { #if defined(CONFIG_NETFILTER) && defined(CONFIG_XFRM) /* Policy lookup after SNAT yielded a new policy */ if (skb_dst(skb)->xfrm) { IPCB(skb)->flags |= IPSKB_REROUTED; - return dst_output(skb); + return dst_output_sk(sk, skb); } #endif if (skb_is_gso(skb)) - return ip_finish_output_gso(skb); + return ip_finish_output_gso(sk, skb); if (skb->len > ip_skb_dst_mtu(skb)) - return ip_fragment(skb, ip_finish_output2); + return ip_fragment(sk, skb, ip_finish_output2); - return ip_finish_output2(skb); + return ip_finish_output2(sk, skb); } int ip_mc_output(struct sock *sk, struct sk_buff *skb) @@ -307,7 +312,7 @@ int ip_mc_output(struct sock *sk, struct sk_buff *skb) struct sk_buff *newskb = skb_clone(skb, GFP_ATOMIC); if (newskb) NF_HOOK(NFPROTO_IPV4, NF_INET_POST_ROUTING, - newskb, NULL, newskb->dev, + sk, newskb, NULL, newskb->dev, dev_loopback_xmit); } @@ -322,11 +327,11 @@ int ip_mc_output(struct sock *sk, struct sk_buff *skb) if (rt->rt_flags&RTCF_BROADCAST) { struct sk_buff *newskb = skb_clone(skb, GFP_ATOMIC); if (newskb) - NF_HOOK(NFPROTO_IPV4, NF_INET_POST_ROUTING, newskb, + NF_HOOK(NFPROTO_IPV4, NF_INET_POST_ROUTING, sk, newskb, NULL, newskb->dev, dev_loopback_xmit); } - return NF_HOOK_COND(NFPROTO_IPV4, NF_INET_POST_ROUTING, skb, NULL, + return NF_HOOK_COND(NFPROTO_IPV4, NF_INET_POST_ROUTING, sk, skb, NULL, skb->dev, ip_finish_output, !(IPCB(skb)->flags & IPSKB_REROUTED)); } @@ -340,7 +345,8 @@ int ip_output(struct sock *sk, struct sk_buff *skb) skb->dev = dev; skb->protocol = htons(ETH_P_IP); - return NF_HOOK_COND(NFPROTO_IPV4, NF_INET_POST_ROUTING, skb, NULL, dev, + return NF_HOOK_COND(NFPROTO_IPV4, NF_INET_POST_ROUTING, sk, skb, + NULL, dev, ip_finish_output, !(IPCB(skb)->flags & IPSKB_REROUTED)); } @@ -480,7 +486,8 @@ static void ip_copy_metadata(struct sk_buff *to, struct sk_buff *from) * single device frame, and queue such a frame for sending. */ -int ip_fragment(struct sk_buff *skb, int (*output)(struct sk_buff *)) +int ip_fragment(struct sock *sk, struct sk_buff *skb, + int (*output)(struct sock *, struct sk_buff *)) { struct iphdr *iph; int ptr; @@ -593,7 +600,7 @@ int ip_fragment(struct sk_buff *skb, int (*output)(struct sk_buff *)) ip_send_check(iph); } - err = output(skb); + err = output(sk, skb); if (!err) IP_INC_STATS(dev_net(dev), IPSTATS_MIB_FRAGCREATES); @@ -730,7 +737,7 @@ slow_path: ip_send_check(iph); - err = output(skb2); + err = output(sk, skb2); if (err) goto fail; diff --git a/net/ipv4/ipmr.c b/net/ipv4/ipmr.c index 5f17d0e78071..3a2c0162c3ba 100644 --- a/net/ipv4/ipmr.c +++ b/net/ipv4/ipmr.c @@ -1679,7 +1679,7 @@ static void ip_encap(struct net *net, struct sk_buff *skb, nf_reset(skb); } -static inline int ipmr_forward_finish(struct sk_buff *skb) +static inline int ipmr_forward_finish(struct sock *sk, struct sk_buff *skb) { struct ip_options *opt = &(IPCB(skb)->opt); @@ -1689,7 +1689,7 @@ static inline int ipmr_forward_finish(struct sk_buff *skb) if (unlikely(opt->optlen)) ip_forward_options(skb); - return dst_output(skb); + return dst_output_sk(sk, skb); } /* @@ -1788,7 +1788,8 @@ static void ipmr_queue_xmit(struct net *net, struct mr_table *mrt, * not mrouter) cannot join to more than one interface - it will * result in receiving multiple packets. */ - NF_HOOK(NFPROTO_IPV4, NF_INET_FORWARD, skb, skb->dev, dev, + NF_HOOK(NFPROTO_IPV4, NF_INET_FORWARD, NULL, skb, + skb->dev, dev, ipmr_forward_finish); return; diff --git a/net/ipv4/raw.c b/net/ipv4/raw.c index 6d0fa8fb8af0..c0bb648fb2f9 100644 --- a/net/ipv4/raw.c +++ b/net/ipv4/raw.c @@ -412,8 +412,8 @@ static int raw_send_hdrinc(struct sock *sk, struct flowi4 *fl4, icmp_out_count(net, ((struct icmphdr *) skb_transport_header(skb))->type); - err = NF_HOOK(NFPROTO_IPV4, NF_INET_LOCAL_OUT, skb, NULL, - rt->dst.dev, dst_output); + err = NF_HOOK(NFPROTO_IPV4, NF_INET_LOCAL_OUT, sk, skb, + NULL, rt->dst.dev, dst_output_sk); if (err > 0) err = net_xmit_errno(err); if (err) diff --git a/net/ipv4/xfrm4_input.c b/net/ipv4/xfrm4_input.c index cac7468db0a1..60b032f58ccc 100644 --- a/net/ipv4/xfrm4_input.c +++ b/net/ipv4/xfrm4_input.c @@ -22,7 +22,7 @@ int xfrm4_extract_input(struct xfrm_state *x, struct sk_buff *skb) return xfrm4_extract_header(skb); } -static inline int xfrm4_rcv_encap_finish(struct sk_buff *skb) +static inline int xfrm4_rcv_encap_finish(struct sock *sk, struct sk_buff *skb) { if (!skb_dst(skb)) { const struct iphdr *iph = ip_hdr(skb); @@ -52,7 +52,8 @@ int xfrm4_transport_finish(struct sk_buff *skb, int async) iph->tot_len = htons(skb->len); ip_send_check(iph); - NF_HOOK(NFPROTO_IPV4, NF_INET_PRE_ROUTING, skb, skb->dev, NULL, + NF_HOOK(NFPROTO_IPV4, NF_INET_PRE_ROUTING, NULL, skb, + skb->dev, NULL, xfrm4_rcv_encap_finish); return 0; } diff --git a/net/ipv4/xfrm4_output.c b/net/ipv4/xfrm4_output.c index dab73813cb92..2878dbfffeb7 100644 --- a/net/ipv4/xfrm4_output.c +++ b/net/ipv4/xfrm4_output.c @@ -69,7 +69,7 @@ int xfrm4_prepare_output(struct xfrm_state *x, struct sk_buff *skb) } EXPORT_SYMBOL(xfrm4_prepare_output); -int xfrm4_output_finish(struct sk_buff *skb) +int xfrm4_output_finish(struct sock *sk, struct sk_buff *skb) { memset(IPCB(skb), 0, sizeof(*IPCB(skb))); @@ -77,26 +77,26 @@ int xfrm4_output_finish(struct sk_buff *skb) IPCB(skb)->flags |= IPSKB_XFRM_TRANSFORMED; #endif - return xfrm_output(skb); + return xfrm_output(sk, skb); } -static int __xfrm4_output(struct sk_buff *skb) +static int __xfrm4_output(struct sock *sk, struct sk_buff *skb) { struct xfrm_state *x = skb_dst(skb)->xfrm; #ifdef CONFIG_NETFILTER if (!x) { IPCB(skb)->flags |= IPSKB_REROUTED; - return dst_output(skb); + return dst_output_sk(sk, skb); } #endif - return x->outer_mode->afinfo->output_finish(skb); + return x->outer_mode->afinfo->output_finish(sk, skb); } int xfrm4_output(struct sock *sk, struct sk_buff *skb) { - return NF_HOOK_COND(NFPROTO_IPV4, NF_INET_POST_ROUTING, skb, + return NF_HOOK_COND(NFPROTO_IPV4, NF_INET_POST_ROUTING, sk, skb, NULL, skb_dst(skb)->dev, __xfrm4_output, !(IPCB(skb)->flags & IPSKB_REROUTED)); } diff --git a/net/ipv6/ip6_input.c b/net/ipv6/ip6_input.c index fb97f7f8d4ed..f2e464eba5ef 100644 --- a/net/ipv6/ip6_input.c +++ b/net/ipv6/ip6_input.c @@ -46,8 +46,7 @@ #include #include - -int ip6_rcv_finish(struct sk_buff *skb) +int ip6_rcv_finish(struct sock *sk, struct sk_buff *skb) { if (sysctl_ip_early_demux && !skb_dst(skb) && skb->sk == NULL) { const struct inet6_protocol *ipprot; @@ -183,7 +182,8 @@ int ipv6_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt /* Must drop socket now because of tproxy. */ skb_orphan(skb); - return NF_HOOK(NFPROTO_IPV6, NF_INET_PRE_ROUTING, skb, dev, NULL, + return NF_HOOK(NFPROTO_IPV6, NF_INET_PRE_ROUTING, NULL, skb, + dev, NULL, ip6_rcv_finish); err: IP6_INC_STATS_BH(net, idev, IPSTATS_MIB_INHDRERRORS); @@ -198,7 +198,7 @@ drop: */ -static int ip6_input_finish(struct sk_buff *skb) +static int ip6_input_finish(struct sock *sk, struct sk_buff *skb) { struct net *net = dev_net(skb_dst(skb)->dev); const struct inet6_protocol *ipprot; @@ -277,7 +277,8 @@ discard: int ip6_input(struct sk_buff *skb) { - return NF_HOOK(NFPROTO_IPV6, NF_INET_LOCAL_IN, skb, skb->dev, NULL, + return NF_HOOK(NFPROTO_IPV6, NF_INET_LOCAL_IN, NULL, skb, + skb->dev, NULL, ip6_input_finish); } diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c index 654f245aa930..7fde1f265c90 100644 --- a/net/ipv6/ip6_output.c +++ b/net/ipv6/ip6_output.c @@ -56,7 +56,7 @@ #include #include -static int ip6_finish_output2(struct sk_buff *skb) +static int ip6_finish_output2(struct sock *sk, struct sk_buff *skb) { struct dst_entry *dst = skb_dst(skb); struct net_device *dev = dst->dev; @@ -70,7 +70,7 @@ static int ip6_finish_output2(struct sk_buff *skb) if (ipv6_addr_is_multicast(&ipv6_hdr(skb)->daddr)) { struct inet6_dev *idev = ip6_dst_idev(skb_dst(skb)); - if (!(dev->flags & IFF_LOOPBACK) && sk_mc_loop(skb->sk) && + if (!(dev->flags & IFF_LOOPBACK) && sk_mc_loop(sk) && ((mroute6_socket(dev_net(dev), skb) && !(IP6CB(skb)->flags & IP6SKB_FORWARDED)) || ipv6_chk_mcast_addr(dev, &ipv6_hdr(skb)->daddr, @@ -82,7 +82,7 @@ static int ip6_finish_output2(struct sk_buff *skb) */ if (newskb) NF_HOOK(NFPROTO_IPV6, NF_INET_POST_ROUTING, - newskb, NULL, newskb->dev, + sk, newskb, NULL, newskb->dev, dev_loopback_xmit); if (ipv6_hdr(skb)->hop_limit == 0) { @@ -122,14 +122,14 @@ static int ip6_finish_output2(struct sk_buff *skb) return -EINVAL; } -static int ip6_finish_output(struct sk_buff *skb) +static int ip6_finish_output(struct sock *sk, struct sk_buff *skb) { if ((skb->len > ip6_skb_dst_mtu(skb) && !skb_is_gso(skb)) || dst_allfrag(skb_dst(skb)) || (IP6CB(skb)->frag_max_size && skb->len > IP6CB(skb)->frag_max_size)) - return ip6_fragment(skb, ip6_finish_output2); + return ip6_fragment(sk, skb, ip6_finish_output2); else - return ip6_finish_output2(skb); + return ip6_finish_output2(sk, skb); } int ip6_output(struct sock *sk, struct sk_buff *skb) @@ -143,7 +143,8 @@ int ip6_output(struct sock *sk, struct sk_buff *skb) return 0; } - return NF_HOOK_COND(NFPROTO_IPV6, NF_INET_POST_ROUTING, skb, NULL, dev, + return NF_HOOK_COND(NFPROTO_IPV6, NF_INET_POST_ROUTING, sk, skb, + NULL, dev, ip6_finish_output, !(IP6CB(skb)->flags & IP6SKB_REROUTED)); } @@ -223,8 +224,8 @@ int ip6_xmit(struct sock *sk, struct sk_buff *skb, struct flowi6 *fl6, if ((skb->len <= mtu) || skb->ignore_df || skb_is_gso(skb)) { IP6_UPD_PO_STATS(net, ip6_dst_idev(skb_dst(skb)), IPSTATS_MIB_OUT, skb->len); - return NF_HOOK(NFPROTO_IPV6, NF_INET_LOCAL_OUT, skb, NULL, - dst->dev, dst_output); + return NF_HOOK(NFPROTO_IPV6, NF_INET_LOCAL_OUT, sk, skb, + NULL, dst->dev, dst_output_sk); } skb->dev = dst->dev; @@ -316,10 +317,10 @@ static int ip6_forward_proxy_check(struct sk_buff *skb) return 0; } -static inline int ip6_forward_finish(struct sk_buff *skb) +static inline int ip6_forward_finish(struct sock *sk, struct sk_buff *skb) { skb_sender_cpu_clear(skb); - return dst_output(skb); + return dst_output_sk(sk, skb); } static unsigned int ip6_dst_mtu_forward(const struct dst_entry *dst) @@ -511,7 +512,8 @@ int ip6_forward(struct sk_buff *skb) IP6_INC_STATS_BH(net, ip6_dst_idev(dst), IPSTATS_MIB_OUTFORWDATAGRAMS); IP6_ADD_STATS_BH(net, ip6_dst_idev(dst), IPSTATS_MIB_OUTOCTETS, skb->len); - return NF_HOOK(NFPROTO_IPV6, NF_INET_FORWARD, skb, skb->dev, dst->dev, + return NF_HOOK(NFPROTO_IPV6, NF_INET_FORWARD, NULL, skb, + skb->dev, dst->dev, ip6_forward_finish); error: @@ -538,7 +540,8 @@ static void ip6_copy_metadata(struct sk_buff *to, struct sk_buff *from) skb_copy_secmark(to, from); } -int ip6_fragment(struct sk_buff *skb, int (*output)(struct sk_buff *)) +int ip6_fragment(struct sock *sk, struct sk_buff *skb, + int (*output)(struct sock *, struct sk_buff *)) { struct sk_buff *frag; struct rt6_info *rt = (struct rt6_info *)skb_dst(skb); @@ -667,7 +670,7 @@ int ip6_fragment(struct sk_buff *skb, int (*output)(struct sk_buff *)) ip6_copy_metadata(frag, skb); } - err = output(skb); + err = output(sk, skb); if (!err) IP6_INC_STATS(net, ip6_dst_idev(&rt->dst), IPSTATS_MIB_FRAGCREATES); @@ -800,7 +803,7 @@ slow_path: /* * Put this fragment into the sending queue. */ - err = output(frag); + err = output(sk, frag); if (err) goto fail; diff --git a/net/ipv6/ip6mr.c b/net/ipv6/ip6mr.c index 8493a22e74eb..74ceb73c1c9a 100644 --- a/net/ipv6/ip6mr.c +++ b/net/ipv6/ip6mr.c @@ -1986,13 +1986,13 @@ int ip6mr_compat_ioctl(struct sock *sk, unsigned int cmd, void __user *arg) } #endif -static inline int ip6mr_forward2_finish(struct sk_buff *skb) +static inline int ip6mr_forward2_finish(struct sock *sk, struct sk_buff *skb) { IP6_INC_STATS_BH(dev_net(skb_dst(skb)->dev), ip6_dst_idev(skb_dst(skb)), IPSTATS_MIB_OUTFORWDATAGRAMS); IP6_ADD_STATS_BH(dev_net(skb_dst(skb)->dev), ip6_dst_idev(skb_dst(skb)), IPSTATS_MIB_OUTOCTETS, skb->len); - return dst_output(skb); + return dst_output_sk(sk, skb); } /* @@ -2064,7 +2064,8 @@ static int ip6mr_forward2(struct net *net, struct mr6_table *mrt, IP6CB(skb)->flags |= IP6SKB_FORWARDED; - return NF_HOOK(NFPROTO_IPV6, NF_INET_FORWARD, skb, skb->dev, dev, + return NF_HOOK(NFPROTO_IPV6, NF_INET_FORWARD, NULL, skb, + skb->dev, dev, ip6mr_forward2_finish); out_free: diff --git a/net/ipv6/mcast.c b/net/ipv6/mcast.c index fac1f27e428e..083b2927fc67 100644 --- a/net/ipv6/mcast.c +++ b/net/ipv6/mcast.c @@ -1644,8 +1644,9 @@ static void mld_sendpack(struct sk_buff *skb) payload_len = skb->len; - err = NF_HOOK(NFPROTO_IPV6, NF_INET_LOCAL_OUT, skb, NULL, skb->dev, - dst_output); + err = NF_HOOK(NFPROTO_IPV6, NF_INET_LOCAL_OUT, + net->ipv6.igmp_sk, skb, NULL, skb->dev, + dst_output_sk); out: if (!err) { ICMP6MSGOUT_INC_STATS(net, idev, ICMPV6_MLD2_REPORT); @@ -2007,8 +2008,8 @@ static void igmp6_send(struct in6_addr *addr, struct net_device *dev, int type) } skb_dst_set(skb, dst); - err = NF_HOOK(NFPROTO_IPV6, NF_INET_LOCAL_OUT, skb, NULL, skb->dev, - dst_output); + err = NF_HOOK(NFPROTO_IPV6, NF_INET_LOCAL_OUT, sk, skb, + NULL, skb->dev, dst_output_sk); out: if (!err) { ICMP6MSGOUT_INC_STATS(net, idev, type); diff --git a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c index 71fde6cafb35..96f153c0846b 100644 --- a/net/ipv6/ndisc.c +++ b/net/ipv6/ndisc.c @@ -463,8 +463,9 @@ static void ndisc_send_skb(struct sk_buff *skb, idev = __in6_dev_get(dst->dev); IP6_UPD_PO_STATS(net, idev, IPSTATS_MIB_OUT, skb->len); - err = NF_HOOK(NFPROTO_IPV6, NF_INET_LOCAL_OUT, skb, NULL, dst->dev, - dst_output); + err = NF_HOOK(NFPROTO_IPV6, NF_INET_LOCAL_OUT, sk, skb, + NULL, dst->dev, + dst_output_sk); if (!err) { ICMP6MSGOUT_INC_STATS(net, idev, type); ICMP6_INC_STATS(net, idev, ICMP6_MIB_OUTMSGS); diff --git a/net/ipv6/netfilter/nf_defrag_ipv6_hooks.c b/net/ipv6/netfilter/nf_defrag_ipv6_hooks.c index e2b882056751..a45db0b4785c 100644 --- a/net/ipv6/netfilter/nf_defrag_ipv6_hooks.c +++ b/net/ipv6/netfilter/nf_defrag_ipv6_hooks.c @@ -75,7 +75,7 @@ static unsigned int ipv6_defrag(const struct nf_hook_ops *ops, nf_ct_frag6_consume_orig(reasm); - NF_HOOK_THRESH(NFPROTO_IPV6, ops->hooknum, reasm, + NF_HOOK_THRESH(NFPROTO_IPV6, ops->hooknum, state->sk, reasm, state->in, state->out, state->okfn, NF_IP6_PRI_CONNTRACK_DEFRAG + 1); diff --git a/net/ipv6/output_core.c b/net/ipv6/output_core.c index 4016a6ef9d61..7d1131dc29fe 100644 --- a/net/ipv6/output_core.c +++ b/net/ipv6/output_core.c @@ -146,8 +146,8 @@ int __ip6_local_out(struct sk_buff *skb) ipv6_hdr(skb)->payload_len = htons(len); IP6CB(skb)->nhoff = offsetof(struct ipv6hdr, nexthdr); - return nf_hook(NFPROTO_IPV6, NF_INET_LOCAL_OUT, skb, NULL, - skb_dst(skb)->dev, dst_output); + return nf_hook(NFPROTO_IPV6, NF_INET_LOCAL_OUT, skb->sk, skb, + NULL, skb_dst(skb)->dev, dst_output_sk); } EXPORT_SYMBOL_GPL(__ip6_local_out); diff --git a/net/ipv6/raw.c b/net/ipv6/raw.c index 79ccdb4c1b33..8072bd4139b7 100644 --- a/net/ipv6/raw.c +++ b/net/ipv6/raw.c @@ -652,8 +652,8 @@ static int rawv6_send_hdrinc(struct sock *sk, struct msghdr *msg, int length, goto error_fault; IP6_UPD_PO_STATS(sock_net(sk), rt->rt6i_idev, IPSTATS_MIB_OUT, skb->len); - err = NF_HOOK(NFPROTO_IPV6, NF_INET_LOCAL_OUT, skb, NULL, - rt->dst.dev, dst_output); + err = NF_HOOK(NFPROTO_IPV6, NF_INET_LOCAL_OUT, sk, skb, + NULL, rt->dst.dev, dst_output_sk); if (err > 0) err = net_xmit_errno(err); if (err) diff --git a/net/ipv6/xfrm6_input.c b/net/ipv6/xfrm6_input.c index f48fbe4d16f5..74bd17882a2f 100644 --- a/net/ipv6/xfrm6_input.c +++ b/net/ipv6/xfrm6_input.c @@ -42,7 +42,8 @@ int xfrm6_transport_finish(struct sk_buff *skb, int async) ipv6_hdr(skb)->payload_len = htons(skb->len); __skb_push(skb, skb->data - skb_network_header(skb)); - NF_HOOK(NFPROTO_IPV6, NF_INET_PRE_ROUTING, skb, skb->dev, NULL, + NF_HOOK(NFPROTO_IPV6, NF_INET_PRE_ROUTING, NULL, skb, + skb->dev, NULL, ip6_rcv_finish); return -1; } diff --git a/net/ipv6/xfrm6_output.c b/net/ipv6/xfrm6_output.c index 010f8bd2d577..09c76a7b474d 100644 --- a/net/ipv6/xfrm6_output.c +++ b/net/ipv6/xfrm6_output.c @@ -120,7 +120,7 @@ int xfrm6_prepare_output(struct xfrm_state *x, struct sk_buff *skb) } EXPORT_SYMBOL(xfrm6_prepare_output); -int xfrm6_output_finish(struct sk_buff *skb) +int xfrm6_output_finish(struct sock *sk, struct sk_buff *skb) { memset(IP6CB(skb), 0, sizeof(*IP6CB(skb))); @@ -128,10 +128,10 @@ int xfrm6_output_finish(struct sk_buff *skb) IP6CB(skb)->flags |= IP6SKB_XFRM_TRANSFORMED; #endif - return xfrm_output(skb); + return xfrm_output(sk, skb); } -static int __xfrm6_output(struct sk_buff *skb) +static int __xfrm6_output(struct sock *sk, struct sk_buff *skb) { struct dst_entry *dst = skb_dst(skb); struct xfrm_state *x = dst->xfrm; @@ -140,7 +140,7 @@ static int __xfrm6_output(struct sk_buff *skb) #ifdef CONFIG_NETFILTER if (!x) { IP6CB(skb)->flags |= IP6SKB_REROUTED; - return dst_output(skb); + return dst_output_sk(sk, skb); } #endif @@ -160,14 +160,15 @@ static int __xfrm6_output(struct sk_buff *skb) if (x->props.mode == XFRM_MODE_TUNNEL && ((skb->len > mtu && !skb_is_gso(skb)) || dst_allfrag(skb_dst(skb)))) { - return ip6_fragment(skb, x->outer_mode->afinfo->output_finish); + return ip6_fragment(sk, skb, + x->outer_mode->afinfo->output_finish); } - return x->outer_mode->afinfo->output_finish(skb); + return x->outer_mode->afinfo->output_finish(sk, skb); } int xfrm6_output(struct sock *sk, struct sk_buff *skb) { - return NF_HOOK_COND(NFPROTO_IPV6, NF_INET_POST_ROUTING, skb, + return NF_HOOK_COND(NFPROTO_IPV6, NF_INET_POST_ROUTING, sk, skb, NULL, skb_dst(skb)->dev, __xfrm6_output, !(IP6CB(skb)->flags & IP6SKB_REROUTED)); } diff --git a/net/netfilter/ipvs/ip_vs_xmit.c b/net/netfilter/ipvs/ip_vs_xmit.c index bf02932b7188..19986ec5f21a 100644 --- a/net/netfilter/ipvs/ip_vs_xmit.c +++ b/net/netfilter/ipvs/ip_vs_xmit.c @@ -536,8 +536,8 @@ static inline int ip_vs_nat_send_or_cont(int pf, struct sk_buff *skb, ip_vs_update_conntrack(skb, cp, 1); if (!local) { skb_forward_csum(skb); - NF_HOOK(pf, NF_INET_LOCAL_OUT, skb, NULL, skb_dst(skb)->dev, - dst_output); + NF_HOOK(pf, NF_INET_LOCAL_OUT, NULL, skb, + NULL, skb_dst(skb)->dev, dst_output_sk); } else ret = NF_ACCEPT; return ret; @@ -554,8 +554,8 @@ static inline int ip_vs_send_or_cont(int pf, struct sk_buff *skb, ip_vs_notrack(skb); if (!local) { skb_forward_csum(skb); - NF_HOOK(pf, NF_INET_LOCAL_OUT, skb, NULL, skb_dst(skb)->dev, - dst_output); + NF_HOOK(pf, NF_INET_LOCAL_OUT, NULL, skb, + NULL, skb_dst(skb)->dev, dst_output_sk); } else ret = NF_ACCEPT; return ret; diff --git a/net/netfilter/nf_queue.c b/net/netfilter/nf_queue.c index c4a706678f88..3f3ac57b2998 100644 --- a/net/netfilter/nf_queue.c +++ b/net/netfilter/nf_queue.c @@ -202,7 +202,7 @@ void nf_reinject(struct nf_queue_entry *entry, unsigned int verdict) case NF_ACCEPT: case NF_STOP: local_bh_disable(); - entry->state.okfn(skb); + entry->state.okfn(entry->state.sk, skb); local_bh_enable(); break; case NF_QUEUE: diff --git a/net/xfrm/xfrm_output.c b/net/xfrm/xfrm_output.c index 7c532856b398..fbcedbe33190 100644 --- a/net/xfrm/xfrm_output.c +++ b/net/xfrm/xfrm_output.c @@ -19,7 +19,7 @@ #include #include -static int xfrm_output2(struct sk_buff *skb); +static int xfrm_output2(struct sock *sk, struct sk_buff *skb); static int xfrm_skb_check_space(struct sk_buff *skb) { @@ -130,7 +130,7 @@ int xfrm_output_resume(struct sk_buff *skb, int err) return dst_output(skb); err = nf_hook(skb_dst(skb)->ops->family, - NF_INET_POST_ROUTING, skb, + NF_INET_POST_ROUTING, skb->sk, skb, NULL, skb_dst(skb)->dev, xfrm_output2); if (unlikely(err != 1)) goto out; @@ -144,12 +144,12 @@ out: } EXPORT_SYMBOL_GPL(xfrm_output_resume); -static int xfrm_output2(struct sk_buff *skb) +static int xfrm_output2(struct sock *sk, struct sk_buff *skb) { return xfrm_output_resume(skb, 1); } -static int xfrm_output_gso(struct sk_buff *skb) +static int xfrm_output_gso(struct sock *sk, struct sk_buff *skb) { struct sk_buff *segs; @@ -165,7 +165,7 @@ static int xfrm_output_gso(struct sk_buff *skb) int err; segs->next = NULL; - err = xfrm_output2(segs); + err = xfrm_output2(sk, segs); if (unlikely(err)) { kfree_skb_list(nskb); @@ -178,13 +178,13 @@ static int xfrm_output_gso(struct sk_buff *skb) return 0; } -int xfrm_output(struct sk_buff *skb) +int xfrm_output(struct sock *sk, struct sk_buff *skb) { struct net *net = dev_net(skb_dst(skb)->dev); int err; if (skb_is_gso(skb)) - return xfrm_output_gso(skb); + return xfrm_output_gso(sk, skb); if (skb->ip_summed == CHECKSUM_PARTIAL) { err = skb_checksum_help(skb); @@ -195,7 +195,7 @@ int xfrm_output(struct sk_buff *skb) } } - return xfrm_output2(skb); + return xfrm_output2(sk, skb); } EXPORT_SYMBOL_GPL(xfrm_output); -- cgit v1.2.3 From 79b16aadea32cce077acbe9e229fcb58a7801687 Mon Sep 17 00:00:00 2001 From: David Miller Date: Sun, 5 Apr 2015 22:19:09 -0400 Subject: udp_tunnel: Pass UDP socket down through udp_tunnel{, 6}_xmit_skb(). That was we can make sure the output path of ipv4/ipv6 operate on the UDP socket rather than whatever random thing happens to be in skb->sk. Based upon a patch by Jiri Pirko. Signed-off-by: David S. Miller Acked-by: Hannes Frederic Sowa --- drivers/net/vxlan.c | 14 ++++++++------ include/net/ip6_tunnel.h | 5 +++-- include/net/ipv6.h | 1 + include/net/udp_tunnel.h | 5 +++-- include/net/vxlan.h | 2 +- net/ipv4/geneve.c | 2 +- net/ipv4/ip_tunnel.c | 2 +- net/ipv4/udp_tunnel.c | 4 ++-- net/ipv6/ip6_gre.c | 2 +- net/ipv6/ip6_tunnel.c | 2 +- net/ipv6/ip6_udp_tunnel.c | 5 +++-- net/ipv6/output_core.c | 21 ++++++++++++++++----- net/openvswitch/vport-vxlan.c | 5 +++-- net/tipc/udp_media.c | 6 ++++-- 14 files changed, 48 insertions(+), 28 deletions(-) (limited to 'include') diff --git a/drivers/net/vxlan.c b/drivers/net/vxlan.c index b5fecb49a0c6..51baac725a48 100644 --- a/drivers/net/vxlan.c +++ b/drivers/net/vxlan.c @@ -1672,7 +1672,8 @@ static void vxlan_build_gbp_hdr(struct vxlanhdr *vxh, u32 vxflags, } #if IS_ENABLED(CONFIG_IPV6) -static int vxlan6_xmit_skb(struct dst_entry *dst, struct sk_buff *skb, +static int vxlan6_xmit_skb(struct dst_entry *dst, struct sock *sk, + struct sk_buff *skb, struct net_device *dev, struct in6_addr *saddr, struct in6_addr *daddr, __u8 prio, __u8 ttl, __be16 src_port, __be16 dst_port, @@ -1748,7 +1749,7 @@ static int vxlan6_xmit_skb(struct dst_entry *dst, struct sk_buff *skb, skb_set_inner_protocol(skb, htons(ETH_P_TEB)); - udp_tunnel6_xmit_skb(dst, skb, dev, saddr, daddr, prio, + udp_tunnel6_xmit_skb(dst, sk, skb, dev, saddr, daddr, prio, ttl, src_port, dst_port, !!(vxflags & VXLAN_F_UDP_ZERO_CSUM6_TX)); return 0; @@ -1758,7 +1759,7 @@ err: } #endif -int vxlan_xmit_skb(struct rtable *rt, struct sk_buff *skb, +int vxlan_xmit_skb(struct rtable *rt, struct sock *sk, struct sk_buff *skb, __be32 src, __be32 dst, __u8 tos, __u8 ttl, __be16 df, __be16 src_port, __be16 dst_port, struct vxlan_metadata *md, bool xnet, u32 vxflags) @@ -1827,7 +1828,7 @@ int vxlan_xmit_skb(struct rtable *rt, struct sk_buff *skb, skb_set_inner_protocol(skb, htons(ETH_P_TEB)); - return udp_tunnel_xmit_skb(rt, skb, src, dst, tos, + return udp_tunnel_xmit_skb(rt, sk, skb, src, dst, tos, ttl, df, src_port, dst_port, xnet, !(vxflags & VXLAN_F_UDP_CSUM)); } @@ -1882,6 +1883,7 @@ static void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev, struct vxlan_rdst *rdst, bool did_rsc) { struct vxlan_dev *vxlan = netdev_priv(dev); + struct sock *sk = vxlan->vn_sock->sock->sk; struct rtable *rt = NULL; const struct iphdr *old_iph; struct flowi4 fl4; @@ -1961,7 +1963,7 @@ static void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev, md.vni = htonl(vni << 8); md.gbp = skb->mark; - err = vxlan_xmit_skb(rt, skb, fl4.saddr, + err = vxlan_xmit_skb(rt, sk, skb, fl4.saddr, dst->sin.sin_addr.s_addr, tos, ttl, df, src_port, dst_port, &md, !net_eq(vxlan->net, dev_net(vxlan->dev)), @@ -2021,7 +2023,7 @@ static void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev, md.vni = htonl(vni << 8); md.gbp = skb->mark; - err = vxlan6_xmit_skb(ndst, skb, dev, &fl6.saddr, &fl6.daddr, + err = vxlan6_xmit_skb(ndst, sk, skb, dev, &fl6.saddr, &fl6.daddr, 0, ttl, src_port, dst_port, &md, !net_eq(vxlan->net, dev_net(vxlan->dev)), vxlan->flags); diff --git a/include/net/ip6_tunnel.h b/include/net/ip6_tunnel.h index 1668be5937e6..b8529aa1dae7 100644 --- a/include/net/ip6_tunnel.h +++ b/include/net/ip6_tunnel.h @@ -73,13 +73,14 @@ __u32 ip6_tnl_get_cap(struct ip6_tnl *t, const struct in6_addr *laddr, struct net *ip6_tnl_get_link_net(const struct net_device *dev); int ip6_tnl_get_iflink(const struct net_device *dev); -static inline void ip6tunnel_xmit(struct sk_buff *skb, struct net_device *dev) +static inline void ip6tunnel_xmit(struct sock *sk, struct sk_buff *skb, + struct net_device *dev) { struct net_device_stats *stats = &dev->stats; int pkt_len, err; pkt_len = skb->len; - err = ip6_local_out(skb); + err = ip6_local_out_sk(sk, skb); if (net_xmit_eval(err) == 0) { struct pcpu_sw_netstats *tstats = this_cpu_ptr(dev->tstats); diff --git a/include/net/ipv6.h b/include/net/ipv6.h index b6ae959824ff..27470cd1d5f8 100644 --- a/include/net/ipv6.h +++ b/include/net/ipv6.h @@ -827,6 +827,7 @@ int ip6_input(struct sk_buff *skb); int ip6_mc_input(struct sk_buff *skb); int __ip6_local_out(struct sk_buff *skb); +int ip6_local_out_sk(struct sock *sk, struct sk_buff *skb); int ip6_local_out(struct sk_buff *skb); /* diff --git a/include/net/udp_tunnel.h b/include/net/udp_tunnel.h index 1a20d33d56bc..c491c1221606 100644 --- a/include/net/udp_tunnel.h +++ b/include/net/udp_tunnel.h @@ -77,13 +77,14 @@ void setup_udp_tunnel_sock(struct net *net, struct socket *sock, struct udp_tunnel_sock_cfg *sock_cfg); /* Transmit the skb using UDP encapsulation. */ -int udp_tunnel_xmit_skb(struct rtable *rt, struct sk_buff *skb, +int udp_tunnel_xmit_skb(struct rtable *rt, struct sock *sk, struct sk_buff *skb, __be32 src, __be32 dst, __u8 tos, __u8 ttl, __be16 df, __be16 src_port, __be16 dst_port, bool xnet, bool nocheck); #if IS_ENABLED(CONFIG_IPV6) -int udp_tunnel6_xmit_skb(struct dst_entry *dst, struct sk_buff *skb, +int udp_tunnel6_xmit_skb(struct dst_entry *dst, struct sock *sk, + struct sk_buff *skb, struct net_device *dev, struct in6_addr *saddr, struct in6_addr *daddr, __u8 prio, __u8 ttl, __be16 src_port, diff --git a/include/net/vxlan.h b/include/net/vxlan.h index 756e4636bad8..0082b5d33d7d 100644 --- a/include/net/vxlan.h +++ b/include/net/vxlan.h @@ -145,7 +145,7 @@ struct vxlan_sock *vxlan_sock_add(struct net *net, __be16 port, void vxlan_sock_release(struct vxlan_sock *vs); -int vxlan_xmit_skb(struct rtable *rt, struct sk_buff *skb, +int vxlan_xmit_skb(struct rtable *rt, struct sock *sk, struct sk_buff *skb, __be32 src, __be32 dst, __u8 tos, __u8 ttl, __be16 df, __be16 src_port, __be16 dst_port, struct vxlan_metadata *md, bool xnet, u32 vxflags); diff --git a/net/ipv4/geneve.c b/net/ipv4/geneve.c index e64f8e9785d1..b77f5e84c623 100644 --- a/net/ipv4/geneve.c +++ b/net/ipv4/geneve.c @@ -136,7 +136,7 @@ int geneve_xmit_skb(struct geneve_sock *gs, struct rtable *rt, skb_set_inner_protocol(skb, htons(ETH_P_TEB)); - return udp_tunnel_xmit_skb(rt, skb, src, dst, + return udp_tunnel_xmit_skb(rt, gs->sock->sk, skb, src, dst, tos, ttl, df, src_port, dst_port, xnet, !csum); } diff --git a/net/ipv4/ip_tunnel.c b/net/ipv4/ip_tunnel.c index 6d364ab8e14e..4c2c3ba4ba65 100644 --- a/net/ipv4/ip_tunnel.c +++ b/net/ipv4/ip_tunnel.c @@ -782,7 +782,7 @@ void ip_tunnel_xmit(struct sk_buff *skb, struct net_device *dev, return; } - err = iptunnel_xmit(skb->sk, rt, skb, fl4.saddr, fl4.daddr, protocol, + err = iptunnel_xmit(NULL, rt, skb, fl4.saddr, fl4.daddr, protocol, tos, ttl, df, !net_eq(tunnel->net, dev_net(dev))); iptunnel_xmit_stats(err, &dev->stats, dev->tstats); diff --git a/net/ipv4/udp_tunnel.c b/net/ipv4/udp_tunnel.c index c83b35485056..6bb98cc193c9 100644 --- a/net/ipv4/udp_tunnel.c +++ b/net/ipv4/udp_tunnel.c @@ -75,7 +75,7 @@ void setup_udp_tunnel_sock(struct net *net, struct socket *sock, } EXPORT_SYMBOL_GPL(setup_udp_tunnel_sock); -int udp_tunnel_xmit_skb(struct rtable *rt, struct sk_buff *skb, +int udp_tunnel_xmit_skb(struct rtable *rt, struct sock *sk, struct sk_buff *skb, __be32 src, __be32 dst, __u8 tos, __u8 ttl, __be16 df, __be16 src_port, __be16 dst_port, bool xnet, bool nocheck) @@ -92,7 +92,7 @@ int udp_tunnel_xmit_skb(struct rtable *rt, struct sk_buff *skb, udp_set_csum(nocheck, skb, src, dst, skb->len); - return iptunnel_xmit(skb->sk, rt, skb, src, dst, IPPROTO_UDP, + return iptunnel_xmit(sk, rt, skb, src, dst, IPPROTO_UDP, tos, ttl, df, xnet); } EXPORT_SYMBOL_GPL(udp_tunnel_xmit_skb); diff --git a/net/ipv6/ip6_gre.c b/net/ipv6/ip6_gre.c index f724329d7436..b5e6cc1d4a73 100644 --- a/net/ipv6/ip6_gre.c +++ b/net/ipv6/ip6_gre.c @@ -760,7 +760,7 @@ static netdev_tx_t ip6gre_xmit2(struct sk_buff *skb, skb_set_inner_protocol(skb, protocol); - ip6tunnel_xmit(skb, dev); + ip6tunnel_xmit(NULL, skb, dev); if (ndst) ip6_tnl_dst_store(tunnel, ndst); return 0; diff --git a/net/ipv6/ip6_tunnel.c b/net/ipv6/ip6_tunnel.c index b6a211a150b2..5cafd92c2312 100644 --- a/net/ipv6/ip6_tunnel.c +++ b/net/ipv6/ip6_tunnel.c @@ -1100,7 +1100,7 @@ static int ip6_tnl_xmit2(struct sk_buff *skb, ipv6h->nexthdr = proto; ipv6h->saddr = fl6->saddr; ipv6h->daddr = fl6->daddr; - ip6tunnel_xmit(skb, dev); + ip6tunnel_xmit(NULL, skb, dev); if (ndst) ip6_tnl_dst_store(t, ndst); return 0; diff --git a/net/ipv6/ip6_udp_tunnel.c b/net/ipv6/ip6_udp_tunnel.c index 32d9b268e7d8..bba8903e871f 100644 --- a/net/ipv6/ip6_udp_tunnel.c +++ b/net/ipv6/ip6_udp_tunnel.c @@ -62,7 +62,8 @@ error: } EXPORT_SYMBOL_GPL(udp_sock_create6); -int udp_tunnel6_xmit_skb(struct dst_entry *dst, struct sk_buff *skb, +int udp_tunnel6_xmit_skb(struct dst_entry *dst, struct sock *sk, + struct sk_buff *skb, struct net_device *dev, struct in6_addr *saddr, struct in6_addr *daddr, __u8 prio, __u8 ttl, __be16 src_port, @@ -97,7 +98,7 @@ int udp_tunnel6_xmit_skb(struct dst_entry *dst, struct sk_buff *skb, ip6h->daddr = *daddr; ip6h->saddr = *saddr; - ip6tunnel_xmit(skb, dev); + ip6tunnel_xmit(sk, skb, dev); return 0; } EXPORT_SYMBOL_GPL(udp_tunnel6_xmit_skb); diff --git a/net/ipv6/output_core.c b/net/ipv6/output_core.c index 7d1131dc29fe..85892af57364 100644 --- a/net/ipv6/output_core.c +++ b/net/ipv6/output_core.c @@ -136,7 +136,7 @@ int ip6_dst_hoplimit(struct dst_entry *dst) EXPORT_SYMBOL(ip6_dst_hoplimit); #endif -int __ip6_local_out(struct sk_buff *skb) +static int __ip6_local_out_sk(struct sock *sk, struct sk_buff *skb) { int len; @@ -146,19 +146,30 @@ int __ip6_local_out(struct sk_buff *skb) ipv6_hdr(skb)->payload_len = htons(len); IP6CB(skb)->nhoff = offsetof(struct ipv6hdr, nexthdr); - return nf_hook(NFPROTO_IPV6, NF_INET_LOCAL_OUT, skb->sk, skb, + return nf_hook(NFPROTO_IPV6, NF_INET_LOCAL_OUT, sk, skb, NULL, skb_dst(skb)->dev, dst_output_sk); } + +int __ip6_local_out(struct sk_buff *skb) +{ + return __ip6_local_out_sk(skb->sk, skb); +} EXPORT_SYMBOL_GPL(__ip6_local_out); -int ip6_local_out(struct sk_buff *skb) +int ip6_local_out_sk(struct sock *sk, struct sk_buff *skb) { int err; - err = __ip6_local_out(skb); + err = __ip6_local_out_sk(sk, skb); if (likely(err == 1)) - err = dst_output(skb); + err = dst_output_sk(sk, skb); return err; } +EXPORT_SYMBOL_GPL(ip6_local_out_sk); + +int ip6_local_out(struct sk_buff *skb) +{ + return ip6_local_out_sk(skb->sk, skb); +} EXPORT_SYMBOL_GPL(ip6_local_out); diff --git a/net/openvswitch/vport-vxlan.c b/net/openvswitch/vport-vxlan.c index 3277a7520e31..6d39766e7828 100644 --- a/net/openvswitch/vport-vxlan.c +++ b/net/openvswitch/vport-vxlan.c @@ -222,7 +222,8 @@ static int vxlan_tnl_send(struct vport *vport, struct sk_buff *skb) { struct net *net = ovs_dp_get_net(vport->dp); struct vxlan_port *vxlan_port = vxlan_vport(vport); - __be16 dst_port = inet_sk(vxlan_port->vs->sock->sk)->inet_sport; + struct sock *sk = vxlan_port->vs->sock->sk; + __be16 dst_port = inet_sk(sk)->inet_sport; const struct ovs_key_ipv4_tunnel *tun_key; struct vxlan_metadata md = {0}; struct rtable *rt; @@ -255,7 +256,7 @@ static int vxlan_tnl_send(struct vport *vport, struct sk_buff *skb) vxflags = vxlan_port->exts | (tun_key->tun_flags & TUNNEL_CSUM ? VXLAN_F_UDP_CSUM : 0); - err = vxlan_xmit_skb(rt, skb, fl.saddr, tun_key->ipv4_dst, + err = vxlan_xmit_skb(rt, sk, skb, fl.saddr, tun_key->ipv4_dst, tun_key->ipv4_tos, tun_key->ipv4_ttl, df, src_port, dst_port, &md, false, vxflags); diff --git a/net/tipc/udp_media.c b/net/tipc/udp_media.c index ef3d7aa2854a..66deebc66aa1 100644 --- a/net/tipc/udp_media.c +++ b/net/tipc/udp_media.c @@ -176,7 +176,8 @@ static int tipc_udp_send_msg(struct net *net, struct sk_buff *skb, goto tx_error; } ttl = ip4_dst_hoplimit(&rt->dst); - err = udp_tunnel_xmit_skb(rt, clone, src->ipv4.s_addr, + err = udp_tunnel_xmit_skb(rt, ub->ubsock->sk, clone, + src->ipv4.s_addr, dst->ipv4.s_addr, 0, ttl, 0, src->udp_port, dst->udp_port, false, true); @@ -197,7 +198,8 @@ static int tipc_udp_send_msg(struct net *net, struct sk_buff *skb, if (err) goto tx_error; ttl = ip6_dst_hoplimit(ndst); - err = udp_tunnel6_xmit_skb(ndst, clone, ndst->dev, &src->ipv6, + err = udp_tunnel6_xmit_skb(ndst, ub->ubsock->sk, clone, + ndst->dev, &src->ipv6, &dst->ipv6, 0, ttl, src->udp_port, dst->udp_port, false); #endif -- cgit v1.2.3 From d619ffcfbd07983d34dc87d474af6cb5c21adf27 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Tue, 7 Apr 2015 21:52:21 +0300 Subject: Bluetooth: Update SSP OOB data EIR definitions Since Bluetooth 4.1 there are two additional values for SSP OOB data, namely C-256 and R-256. This patch updates the EIR definitions to take into account both the 192 and 256 bit variants of C and R. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- include/net/bluetooth/hci.h | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index 3acecf35420b..2f8c830e600c 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -463,12 +463,14 @@ enum { #define EIR_NAME_COMPLETE 0x09 /* complete local name */ #define EIR_TX_POWER 0x0A /* transmit power level */ #define EIR_CLASS_OF_DEV 0x0D /* Class of Device */ -#define EIR_SSP_HASH_C 0x0E /* Simple Pairing Hash C */ -#define EIR_SSP_RAND_R 0x0F /* Simple Pairing Randomizer R */ +#define EIR_SSP_HASH_C192 0x0E /* Simple Pairing Hash C-192 */ +#define EIR_SSP_RAND_R192 0x0F /* Simple Pairing Randomizer R-192 */ #define EIR_DEVICE_ID 0x10 /* device ID */ #define EIR_APPEARANCE 0x19 /* Device appearance */ #define EIR_LE_BDADDR 0x1B /* LE Bluetooth device address */ #define EIR_LE_ROLE 0x1C /* LE role */ +#define EIR_SSP_HASH_C256 0x1D /* Simple Pairing Hash C-256 */ +#define EIR_SSP_RAND_R256 0x1E /* Simple Pairing Rand R-256 */ #define EIR_LE_SC_CONFIRM 0x22 /* LE SC Confirmation Value */ #define EIR_LE_SC_RANDOM 0x23 /* LE SC Random Value */ -- cgit v1.2.3 From 9a9634545c7051f567096117d417e9c3be24706d Mon Sep 17 00:00:00 2001 From: Nicolas Dichtel Date: Tue, 7 Apr 2015 11:51:53 +0200 Subject: netns: notify netns id events With this patch, netns ids that are created and deleted are advertised into the group RTNLGRP_NSID. Because callers of rtnl_net_notifyid() already know the id of the peer, there is no need to call __peernet2id() in rtnl_net_fill(). Signed-off-by: Nicolas Dichtel Signed-off-by: David S. Miller --- include/uapi/linux/rtnetlink.h | 4 ++++ net/core/net_namespace.c | 52 +++++++++++++++++++++++++++++++++++------- 2 files changed, 48 insertions(+), 8 deletions(-) (limited to 'include') diff --git a/include/uapi/linux/rtnetlink.h b/include/uapi/linux/rtnetlink.h index bea910f924dd..974db03f7b1a 100644 --- a/include/uapi/linux/rtnetlink.h +++ b/include/uapi/linux/rtnetlink.h @@ -134,6 +134,8 @@ enum { RTM_NEWNSID = 88, #define RTM_NEWNSID RTM_NEWNSID + RTM_DELNSID = 89, +#define RTM_DELNSID RTM_DELNSID RTM_GETNSID = 90, #define RTM_GETNSID RTM_GETNSID @@ -635,6 +637,8 @@ enum rtnetlink_groups { #define RTNLGRP_MDB RTNLGRP_MDB RTNLGRP_MPLS_ROUTE, #define RTNLGRP_MPLS_ROUTE RTNLGRP_MPLS_ROUTE + RTNLGRP_NSID, +#define RTNLGRP_NSID RTNLGRP_NSID __RTNLGRP_MAX }; #define RTNLGRP_MAX (__RTNLGRP_MAX - 1) diff --git a/net/core/net_namespace.c b/net/core/net_namespace.c index be28afccfbbb..b3b5f22f0e90 100644 --- a/net/core/net_namespace.c +++ b/net/core/net_namespace.c @@ -148,9 +148,11 @@ static void ops_free_list(const struct pernet_operations *ops, } } +static void rtnl_net_notifyid(struct net *net, struct net *peer, int cmd, + int id); static int alloc_netid(struct net *net, struct net *peer, int reqid) { - int min = 0, max = 0; + int min = 0, max = 0, id; ASSERT_RTNL(); @@ -159,7 +161,11 @@ static int alloc_netid(struct net *net, struct net *peer, int reqid) max = reqid + 1; } - return idr_alloc(&net->netns_ids, peer, min, max, GFP_KERNEL); + id = idr_alloc(&net->netns_ids, peer, min, max, GFP_KERNEL); + if (id >= 0) + rtnl_net_notifyid(net, peer, RTM_NEWNSID, id); + + return id; } /* This function is used by idr_for_each(). If net is equal to peer, the @@ -359,8 +365,10 @@ static void cleanup_net(struct work_struct *work) for_each_net(tmp) { int id = __peernet2id(tmp, net, false); - if (id >= 0) + if (id >= 0) { + rtnl_net_notifyid(tmp, net, RTM_DELNSID, id); idr_remove(&tmp->netns_ids, id); + } } idr_destroy(&net->netns_ids); @@ -531,7 +539,8 @@ static int rtnl_net_get_size(void) } static int rtnl_net_fill(struct sk_buff *skb, u32 portid, u32 seq, int flags, - int cmd, struct net *net, struct net *peer) + int cmd, struct net *net, struct net *peer, + int nsid) { struct nlmsghdr *nlh; struct rtgenmsg *rth; @@ -546,9 +555,13 @@ static int rtnl_net_fill(struct sk_buff *skb, u32 portid, u32 seq, int flags, rth = nlmsg_data(nlh); rth->rtgen_family = AF_UNSPEC; - id = __peernet2id(net, peer, false); - if (id < 0) - id = NETNSA_NSID_NOT_ASSIGNED; + if (nsid >= 0) { + id = nsid; + } else { + id = __peernet2id(net, peer, false); + if (id < 0) + id = NETNSA_NSID_NOT_ASSIGNED; + } if (nla_put_s32(skb, NETNSA_NSID, id)) goto nla_put_failure; @@ -589,7 +602,7 @@ static int rtnl_net_getid(struct sk_buff *skb, struct nlmsghdr *nlh) } err = rtnl_net_fill(msg, NETLINK_CB(skb).portid, nlh->nlmsg_seq, 0, - RTM_GETNSID, net, peer); + RTM_GETNSID, net, peer, -1); if (err < 0) goto err_out; @@ -603,6 +616,29 @@ out: return err; } +static void rtnl_net_notifyid(struct net *net, struct net *peer, int cmd, + int id) +{ + struct sk_buff *msg; + int err = -ENOMEM; + + msg = nlmsg_new(rtnl_net_get_size(), GFP_KERNEL); + if (!msg) + goto out; + + err = rtnl_net_fill(msg, 0, 0, 0, cmd, net, peer, id); + if (err < 0) + goto err_out; + + rtnl_notify(msg, net, 0, RTNLGRP_NSID, NULL, 0); + return; + +err_out: + nlmsg_free(msg); +out: + rtnl_set_sk_err(net, RTNLGRP_NSID, err); +} + static int __init net_ns_init(void) { struct net_generic *ng; -- cgit v1.2.3 From 388069d30240e9524ad69ce6cc692fff2a5a7829 Mon Sep 17 00:00:00 2001 From: Nicolas Dichtel Date: Tue, 7 Apr 2015 12:10:16 +0200 Subject: netdevice.h: remove iflink description Also move 'group' description to match the order of the net_device structure. Fixes: 7a66bbc96ce9 ("net: remove iflink field from struct net_device") Reported-by: Fengguang Wu Signed-off-by: Nicolas Dichtel Signed-off-by: David S. Miller --- include/linux/netdevice.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'include') diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 45823db2efb0..bf6d9df34d7b 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -1328,7 +1328,7 @@ enum netdev_priv_flags { * @mpls_features: Mask of features inheritable by MPLS * * @ifindex: interface index - * @iflink: unique device identifier + * @group: The group, that the device belongs to * * @stats: Statistics struct, which was left as a legacy, use * rtnl_link_stats64 instead @@ -1488,7 +1488,6 @@ enum netdev_priv_flags { * * @qdisc_tx_busylock: XXX: need comments on this one * - * @group: The group, that the device belongs to * @pm_qos_req: Power Management QoS object * * FIXME: cleanup struct net_device such that network protocol info -- cgit v1.2.3 From 7f9b838b71eb78a27de27a12ca5de8542fac3115 Mon Sep 17 00:00:00 2001 From: Daniel Lee Date: Mon, 6 Apr 2015 14:37:26 -0700 Subject: tcp: RFC7413 option support for Fast Open server Fast Open has been using the experimental option with a magic number (RFC6994) to request and grant Fast Open cookies. This patch enables the server to support the official IANA option 34 in RFC7413 in addition. The change has passed all existing Fast Open tests with both old and new options at Google. Signed-off-by: Daniel Lee Signed-off-by: Yuchung Cheng Signed-off-by: Neal Cardwell Signed-off-by: David S. Miller --- include/linux/tcp.h | 1 + include/net/tcp.h | 2 ++ net/ipv4/tcp_fastopen.c | 1 + net/ipv4/tcp_input.c | 42 ++++++++++++++++++++++++++++++------------ net/ipv4/tcp_output.c | 34 +++++++++++++++++++++++----------- 5 files changed, 57 insertions(+), 23 deletions(-) (limited to 'include') diff --git a/include/linux/tcp.h b/include/linux/tcp.h index f869ae8afbaf..a48d00318683 100644 --- a/include/linux/tcp.h +++ b/include/linux/tcp.h @@ -58,6 +58,7 @@ static inline unsigned int tcp_optlen(const struct sk_buff *skb) struct tcp_fastopen_cookie { s8 len; u8 val[TCP_FASTOPEN_COOKIE_MAX]; + bool exp; /* In RFC6994 experimental option format */ }; /* This defines a selective acknowledgement block. */ diff --git a/include/net/tcp.h b/include/net/tcp.h index 963303fb96ae..7292c3c575bc 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -179,6 +179,7 @@ void tcp_time_wait(struct sock *sk, int state, int timeo); #define TCPOPT_SACK 5 /* SACK Block */ #define TCPOPT_TIMESTAMP 8 /* Better RTT estimations/PAWS */ #define TCPOPT_MD5SIG 19 /* MD5 Signature (RFC2385) */ +#define TCPOPT_FASTOPEN 34 /* Fast open (RFC7413) */ #define TCPOPT_EXP 254 /* Experimental */ /* Magic number to be after the option value for sharing TCP * experimental options. See draft-ietf-tcpm-experimental-options-00.txt @@ -194,6 +195,7 @@ void tcp_time_wait(struct sock *sk, int state, int timeo); #define TCPOLEN_SACK_PERM 2 #define TCPOLEN_TIMESTAMP 10 #define TCPOLEN_MD5SIG 18 +#define TCPOLEN_FASTOPEN_BASE 2 #define TCPOLEN_EXP_FASTOPEN_BASE 4 /* But this is what stacks really send out. */ diff --git a/net/ipv4/tcp_fastopen.c b/net/ipv4/tcp_fastopen.c index 5da55e2b5cd2..e3d87aca6be8 100644 --- a/net/ipv4/tcp_fastopen.c +++ b/net/ipv4/tcp_fastopen.c @@ -303,6 +303,7 @@ fastopen: } else if (foc->len > 0) /* Client presents an invalid cookie */ NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_TCPFASTOPENPASSIVEFAIL); + valid_foc.exp = foc->exp; *foc = valid_foc; return false; } diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index c1ce304ba8d2..24f1630b2afb 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -3603,6 +3603,23 @@ old_ack: return 0; } +static void tcp_parse_fastopen_option(int len, const unsigned char *cookie, + bool syn, struct tcp_fastopen_cookie *foc, + bool exp_opt) +{ + /* Valid only in SYN or SYN-ACK with an even length. */ + if (!foc || !syn || len < 0 || (len & 1)) + return; + + if (len >= TCP_FASTOPEN_COOKIE_MIN && + len <= TCP_FASTOPEN_COOKIE_MAX) + memcpy(foc->val, cookie, len); + else if (len != 0) + len = -1; + foc->len = len; + foc->exp = exp_opt; +} + /* Look for tcp options. Normally only called on SYN and SYNACK packets. * But, this can also be called on packets in the established flow when * the fast version below fails. @@ -3692,21 +3709,22 @@ void tcp_parse_options(const struct sk_buff *skb, */ break; #endif + case TCPOPT_FASTOPEN: + tcp_parse_fastopen_option( + opsize - TCPOLEN_FASTOPEN_BASE, + ptr, th->syn, foc, false); + break; + case TCPOPT_EXP: /* Fast Open option shares code 254 using a - * 16 bits magic number. It's valid only in - * SYN or SYN-ACK with an even size. + * 16 bits magic number. */ - if (opsize < TCPOLEN_EXP_FASTOPEN_BASE || - get_unaligned_be16(ptr) != TCPOPT_FASTOPEN_MAGIC || - !foc || !th->syn || (opsize & 1)) - break; - foc->len = opsize - TCPOLEN_EXP_FASTOPEN_BASE; - if (foc->len >= TCP_FASTOPEN_COOKIE_MIN && - foc->len <= TCP_FASTOPEN_COOKIE_MAX) - memcpy(foc->val, ptr + 2, foc->len); - else if (foc->len != 0) - foc->len = -1; + if (opsize >= TCPOLEN_EXP_FASTOPEN_BASE && + get_unaligned_be16(ptr) == + TCPOPT_FASTOPEN_MAGIC) + tcp_parse_fastopen_option(opsize - + TCPOLEN_EXP_FASTOPEN_BASE, + ptr + 2, th->syn, foc, true); break; } diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c index 7404e5238e00..464bd8c5de69 100644 --- a/net/ipv4/tcp_output.c +++ b/net/ipv4/tcp_output.c @@ -518,17 +518,26 @@ static void tcp_options_write(__be32 *ptr, struct tcp_sock *tp, if (unlikely(OPTION_FAST_OPEN_COOKIE & options)) { struct tcp_fastopen_cookie *foc = opts->fastopen_cookie; + u8 *p = (u8 *)ptr; + u32 len; /* Fast Open option length */ + + if (foc->exp) { + len = TCPOLEN_EXP_FASTOPEN_BASE + foc->len; + *ptr = htonl((TCPOPT_EXP << 24) | (len << 16) | + TCPOPT_FASTOPEN_MAGIC); + p += TCPOLEN_EXP_FASTOPEN_BASE; + } else { + len = TCPOLEN_FASTOPEN_BASE + foc->len; + *p++ = TCPOPT_FASTOPEN; + *p++ = len; + } - *ptr++ = htonl((TCPOPT_EXP << 24) | - ((TCPOLEN_EXP_FASTOPEN_BASE + foc->len) << 16) | - TCPOPT_FASTOPEN_MAGIC); - - memcpy(ptr, foc->val, foc->len); - if ((foc->len & 3) == 2) { - u8 *align = ((u8 *)ptr) + foc->len; - align[0] = align[1] = TCPOPT_NOP; + memcpy(p, foc->val, foc->len); + if ((len & 3) == 2) { + p[foc->len] = TCPOPT_NOP; + p[foc->len + 1] = TCPOPT_NOP; } - ptr += (foc->len + 3) >> 2; + ptr += (len + 3) >> 2; } } @@ -641,8 +650,11 @@ static unsigned int tcp_synack_options(struct sock *sk, if (unlikely(!ireq->tstamp_ok)) remaining -= TCPOLEN_SACKPERM_ALIGNED; } - if (foc && foc->len >= 0) { - u32 need = TCPOLEN_EXP_FASTOPEN_BASE + foc->len; + if (foc != NULL && foc->len >= 0) { + u32 need = foc->len; + + need += foc->exp ? TCPOLEN_EXP_FASTOPEN_BASE : + TCPOLEN_FASTOPEN_BASE; need = (need + 3) & ~3U; /* Align to 32 bits */ if (remaining >= need) { opts->options |= OPTION_FAST_OPEN_COOKIE; -- cgit v1.2.3 From 2646c831c00c5d22aa72b79d24069c1b412cda7c Mon Sep 17 00:00:00 2001 From: Daniel Lee Date: Mon, 6 Apr 2015 14:37:27 -0700 Subject: tcp: RFC7413 option support for Fast Open client Fast Open has been using an experimental option with a magic number (RFC6994). This patch makes the client by default use the RFC7413 option (34) to get and send Fast Open cookies. This patch makes the client solicit cookies from a given server first with the RFC7413 option. If that fails to elicit a cookie, then it tries the RFC6994 experimental option. If that also fails, it uses the RFC7413 option on all subsequent connect attempts. If the server returns a Fast Open cookie then the client caches the form of the option that successfully elicited a cookie, and uses that form on later connects when it presents that cookie. The idea is to gradually obsolete the use of experimental options as the servers and clients upgrade, while keeping the interoperability meanwhile. Signed-off-by: Daniel Lee Signed-off-by: Yuchung Cheng Signed-off-by: Neal Cardwell Signed-off-by: David S. Miller --- include/linux/tcp.h | 1 + include/net/tcp.h | 3 ++- net/ipv4/tcp_input.c | 29 +++++++++++++++++++---------- net/ipv4/tcp_metrics.c | 13 +++++++++++-- net/ipv4/tcp_output.c | 6 +++++- net/ipv4/tcp_timer.c | 2 +- 6 files changed, 39 insertions(+), 15 deletions(-) (limited to 'include') diff --git a/include/linux/tcp.h b/include/linux/tcp.h index a48d00318683..0caa3a2d4106 100644 --- a/include/linux/tcp.h +++ b/include/linux/tcp.h @@ -189,6 +189,7 @@ struct tcp_sock { u8 do_early_retrans:1,/* Enable RFC5827 early-retransmit */ syn_data:1, /* SYN includes data */ syn_fastopen:1, /* SYN includes Fast Open option */ + syn_fastopen_exp:1,/* SYN includes Fast Open exp. option */ syn_data_acked:1,/* data in SYN is acked by SYN-ACK */ is_cwnd_limited:1;/* forward progress limited by snd_cwnd? */ u32 tlp_high_seq; /* snd_nxt at the time of TLP retransmit. */ diff --git a/include/net/tcp.h b/include/net/tcp.h index 7292c3c575bc..9598871485ce 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -1339,7 +1339,8 @@ void tcp_fastopen_cache_get(struct sock *sk, u16 *mss, struct tcp_fastopen_cookie *cookie, int *syn_loss, unsigned long *last_syn_loss); void tcp_fastopen_cache_set(struct sock *sk, u16 mss, - struct tcp_fastopen_cookie *cookie, bool syn_lost); + struct tcp_fastopen_cookie *cookie, bool syn_lost, + u16 try_exp); struct tcp_fastopen_request { /* Fast Open cookie. Size 0 means a cookie request */ struct tcp_fastopen_cookie cookie; diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index 24f1630b2afb..031cf72cd05c 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -5378,8 +5378,8 @@ static bool tcp_rcv_fastopen_synack(struct sock *sk, struct sk_buff *synack, { struct tcp_sock *tp = tcp_sk(sk); struct sk_buff *data = tp->syn_data ? tcp_write_queue_head(sk) : NULL; - u16 mss = tp->rx_opt.mss_clamp; - bool syn_drop; + u16 mss = tp->rx_opt.mss_clamp, try_exp = 0; + bool syn_drop = false; if (mss == tp->rx_opt.user_mss) { struct tcp_options_received opt; @@ -5391,16 +5391,25 @@ static bool tcp_rcv_fastopen_synack(struct sock *sk, struct sk_buff *synack, mss = opt.mss_clamp; } - if (!tp->syn_fastopen) /* Ignore an unsolicited cookie */ + if (!tp->syn_fastopen) { + /* Ignore an unsolicited cookie */ cookie->len = -1; + } else if (tp->total_retrans) { + /* SYN timed out and the SYN-ACK neither has a cookie nor + * acknowledges data. Presumably the remote received only + * the retransmitted (regular) SYNs: either the original + * SYN-data or the corresponding SYN-ACK was dropped. + */ + syn_drop = (cookie->len < 0 && data); + } else if (cookie->len < 0 && !tp->syn_data) { + /* We requested a cookie but didn't get it. If we did not use + * the (old) exp opt format then try so next time (try_exp=1). + * Otherwise we go back to use the RFC7413 opt (try_exp=2). + */ + try_exp = tp->syn_fastopen_exp ? 2 : 1; + } - /* The SYN-ACK neither has cookie nor acknowledges the data. Presumably - * the remote receives only the retransmitted (regular) SYNs: either - * the original SYN-data or the corresponding SYN-ACK is lost. - */ - syn_drop = (cookie->len <= 0 && data && tp->total_retrans); - - tcp_fastopen_cache_set(sk, mss, cookie, syn_drop); + tcp_fastopen_cache_set(sk, mss, cookie, syn_drop, try_exp); if (data) { /* Retransmit unacked data in SYN */ tcp_for_write_queue_from(data, sk) { diff --git a/net/ipv4/tcp_metrics.c b/net/ipv4/tcp_metrics.c index 78ecc4a01712..a51d63a43e33 100644 --- a/net/ipv4/tcp_metrics.c +++ b/net/ipv4/tcp_metrics.c @@ -28,7 +28,8 @@ static struct tcp_metrics_block *__tcp_get_metrics(const struct inetpeer_addr *s struct tcp_fastopen_metrics { u16 mss; - u16 syn_loss:10; /* Recurring Fast Open SYN losses */ + u16 syn_loss:10, /* Recurring Fast Open SYN losses */ + try_exp:2; /* Request w/ exp. option (once) */ unsigned long last_syn_loss; /* Last Fast Open SYN loss */ struct tcp_fastopen_cookie cookie; }; @@ -131,6 +132,8 @@ static void tcpm_suck_dst(struct tcp_metrics_block *tm, if (fastopen_clear) { tm->tcpm_fastopen.mss = 0; tm->tcpm_fastopen.syn_loss = 0; + tm->tcpm_fastopen.try_exp = 0; + tm->tcpm_fastopen.cookie.exp = false; tm->tcpm_fastopen.cookie.len = 0; } } @@ -713,6 +716,8 @@ void tcp_fastopen_cache_get(struct sock *sk, u16 *mss, if (tfom->mss) *mss = tfom->mss; *cookie = tfom->cookie; + if (cookie->len <= 0 && tfom->try_exp == 1) + cookie->exp = true; *syn_loss = tfom->syn_loss; *last_syn_loss = *syn_loss ? tfom->last_syn_loss : 0; } while (read_seqretry(&fastopen_seqlock, seq)); @@ -721,7 +726,8 @@ void tcp_fastopen_cache_get(struct sock *sk, u16 *mss, } void tcp_fastopen_cache_set(struct sock *sk, u16 mss, - struct tcp_fastopen_cookie *cookie, bool syn_lost) + struct tcp_fastopen_cookie *cookie, bool syn_lost, + u16 try_exp) { struct dst_entry *dst = __sk_dst_get(sk); struct tcp_metrics_block *tm; @@ -738,6 +744,9 @@ void tcp_fastopen_cache_set(struct sock *sk, u16 mss, tfom->mss = mss; if (cookie && cookie->len > 0) tfom->cookie = *cookie; + else if (try_exp > tfom->try_exp && + tfom->cookie.len <= 0 && !tfom->cookie.exp) + tfom->try_exp = try_exp; if (syn_lost) { ++tfom->syn_loss; tfom->last_syn_loss = jiffies; diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c index 464bd8c5de69..e662d85d1635 100644 --- a/net/ipv4/tcp_output.c +++ b/net/ipv4/tcp_output.c @@ -592,13 +592,17 @@ static unsigned int tcp_syn_options(struct sock *sk, struct sk_buff *skb, } if (fastopen && fastopen->cookie.len >= 0) { - u32 need = TCPOLEN_EXP_FASTOPEN_BASE + fastopen->cookie.len; + u32 need = fastopen->cookie.len; + + need += fastopen->cookie.exp ? TCPOLEN_EXP_FASTOPEN_BASE : + TCPOLEN_FASTOPEN_BASE; need = (need + 3) & ~3U; /* Align to 32 bits */ if (remaining >= need) { opts->options |= OPTION_FAST_OPEN_COOKIE; opts->fastopen_cookie = &fastopen->cookie; remaining -= need; tp->syn_fastopen = 1; + tp->syn_fastopen_exp = fastopen->cookie.exp ? 1 : 0; } } diff --git a/net/ipv4/tcp_timer.c b/net/ipv4/tcp_timer.c index 2568fd282873..8c65dc147d8b 100644 --- a/net/ipv4/tcp_timer.c +++ b/net/ipv4/tcp_timer.c @@ -167,7 +167,7 @@ static int tcp_write_timeout(struct sock *sk) if (icsk->icsk_retransmits) { dst_negative_advice(sk); if (tp->syn_fastopen || tp->syn_data) - tcp_fastopen_cache_set(sk, 0, NULL, true); + tcp_fastopen_cache_set(sk, 0, NULL, true, 0); if (tp->syn_data) NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_TCPFASTOPENACTIVEFAIL); -- cgit v1.2.3 From a67a95134ffddd0ca4527c77e86375c3deb2938f Mon Sep 17 00:00:00 2001 From: Keith Busch Date: Tue, 7 Apr 2015 16:57:19 -0600 Subject: NVMe: Meta data handling through submit io ioctl This adds support for the extended metadata formats through the submit IO ioctl, and simplifies the rest when using a separate metadata format. Signed-off-by: Keith Busch Signed-off-by: Jens Axboe --- drivers/block/nvme-core.c | 123 ++++++++++++++++++---------------------------- include/linux/nvme.h | 5 +- 2 files changed, 52 insertions(+), 76 deletions(-) (limited to 'include') diff --git a/drivers/block/nvme-core.c b/drivers/block/nvme-core.c index 973c895bd7af..e919de48ff25 100644 --- a/drivers/block/nvme-core.c +++ b/drivers/block/nvme-core.c @@ -1745,25 +1745,31 @@ static int nvme_submit_io(struct nvme_ns *ns, struct nvme_user_io __user *uio) struct nvme_dev *dev = ns->dev; struct nvme_user_io io; struct nvme_command c; - unsigned length, meta_len; - int status, i; - struct nvme_iod *iod, *meta_iod = NULL; - dma_addr_t meta_dma_addr; - void *meta, *uninitialized_var(meta_mem); + unsigned length, meta_len, prp_len; + int status, write; + struct nvme_iod *iod; + dma_addr_t meta_dma = 0; + void *meta = NULL; if (copy_from_user(&io, uio, sizeof(io))) return -EFAULT; length = (io.nblocks + 1) << ns->lba_shift; meta_len = (io.nblocks + 1) * ns->ms; - if (meta_len && ((io.metadata & 3) || !io.metadata)) + if (meta_len && ((io.metadata & 3) || !io.metadata) && !ns->ext) return -EINVAL; + else if (meta_len && ns->ext) { + length += meta_len; + meta_len = 0; + } + + write = io.opcode & 1; switch (io.opcode) { case nvme_cmd_write: case nvme_cmd_read: case nvme_cmd_compare: - iod = nvme_map_user_pages(dev, io.opcode & 1, io.addr, length); + iod = nvme_map_user_pages(dev, write, io.addr, length); break; default: return -EINVAL; @@ -1772,6 +1778,27 @@ static int nvme_submit_io(struct nvme_ns *ns, struct nvme_user_io __user *uio) if (IS_ERR(iod)) return PTR_ERR(iod); + prp_len = nvme_setup_prps(dev, iod, length, GFP_KERNEL); + if (length != prp_len) { + status = -ENOMEM; + goto unmap; + } + if (meta_len) { + meta = dma_alloc_coherent(&dev->pci_dev->dev, meta_len, + &meta_dma, GFP_KERNEL); + if (!meta) { + status = -ENOMEM; + goto unmap; + } + if (write) { + if (copy_from_user(meta, (void __user *)io.metadata, + meta_len)) { + status = -EFAULT; + goto unmap; + } + } + } + memset(&c, 0, sizeof(c)); c.rw.opcode = io.opcode; c.rw.flags = io.flags; @@ -1783,75 +1810,21 @@ static int nvme_submit_io(struct nvme_ns *ns, struct nvme_user_io __user *uio) c.rw.reftag = cpu_to_le32(io.reftag); c.rw.apptag = cpu_to_le16(io.apptag); c.rw.appmask = cpu_to_le16(io.appmask); - - if (meta_len) { - meta_iod = nvme_map_user_pages(dev, io.opcode & 1, io.metadata, - meta_len); - if (IS_ERR(meta_iod)) { - status = PTR_ERR(meta_iod); - meta_iod = NULL; - goto unmap; - } - - meta_mem = dma_alloc_coherent(&dev->pci_dev->dev, meta_len, - &meta_dma_addr, GFP_KERNEL); - if (!meta_mem) { - status = -ENOMEM; - goto unmap; - } - - if (io.opcode & 1) { - int meta_offset = 0; - - for (i = 0; i < meta_iod->nents; i++) { - meta = kmap_atomic(sg_page(&meta_iod->sg[i])) + - meta_iod->sg[i].offset; - memcpy(meta_mem + meta_offset, meta, - meta_iod->sg[i].length); - kunmap_atomic(meta); - meta_offset += meta_iod->sg[i].length; - } - } - - c.rw.metadata = cpu_to_le64(meta_dma_addr); - } - - length = nvme_setup_prps(dev, iod, length, GFP_KERNEL); c.rw.prp1 = cpu_to_le64(sg_dma_address(iod->sg)); c.rw.prp2 = cpu_to_le64(iod->first_dma); - - if (length != (io.nblocks + 1) << ns->lba_shift) - status = -ENOMEM; - else - status = nvme_submit_io_cmd(dev, ns, &c, NULL); - - if (meta_len) { - if (status == NVME_SC_SUCCESS && !(io.opcode & 1)) { - int meta_offset = 0; - - for (i = 0; i < meta_iod->nents; i++) { - meta = kmap_atomic(sg_page(&meta_iod->sg[i])) + - meta_iod->sg[i].offset; - memcpy(meta, meta_mem + meta_offset, - meta_iod->sg[i].length); - kunmap_atomic(meta); - meta_offset += meta_iod->sg[i].length; - } - } - - dma_free_coherent(&dev->pci_dev->dev, meta_len, meta_mem, - meta_dma_addr); - } - + c.rw.metadata = cpu_to_le64(meta_dma); + status = nvme_submit_io_cmd(dev, ns, &c, NULL); unmap: - nvme_unmap_user_pages(dev, io.opcode & 1, iod); + nvme_unmap_user_pages(dev, write, iod); nvme_free_iod(dev, iod); - - if (meta_iod) { - nvme_unmap_user_pages(dev, io.opcode & 1, meta_iod); - nvme_free_iod(dev, meta_iod); + if (meta) { + if (status == NVME_SC_SUCCESS && !write) { + if (copy_to_user((void __user *)io.metadata, meta, + meta_len)) + status = -EFAULT; + } + dma_free_coherent(&dev->pci_dev->dev, meta_len, meta, meta_dma); } - return status; } @@ -2014,7 +1987,8 @@ static int nvme_revalidate_disk(struct gendisk *disk) struct nvme_dev *dev = ns->dev; struct nvme_id_ns *id; dma_addr_t dma_addr; - int lbaf, pi_type, old_ms; + u8 lbaf, pi_type; + u16 old_ms; unsigned short bs; id = dma_alloc_coherent(&dev->pci_dev->dev, 4096, &dma_addr, @@ -2035,6 +2009,7 @@ static int nvme_revalidate_disk(struct gendisk *disk) lbaf = id->flbas & NVME_NS_FLBAS_LBA_MASK; ns->lba_shift = id->lbaf[lbaf].ds; ns->ms = le16_to_cpu(id->lbaf[lbaf].ms); + ns->ext = ns->ms && (id->flbas & NVME_NS_FLBAS_META_EXT); /* * If identify namespace failed, use default 512 byte block size so @@ -2051,14 +2026,14 @@ static int nvme_revalidate_disk(struct gendisk *disk) if (blk_get_integrity(disk) && (ns->pi_type != pi_type || ns->ms != old_ms || bs != queue_logical_block_size(disk->queue) || - (ns->ms && id->flbas & NVME_NS_FLBAS_META_EXT))) + (ns->ms && ns->ext))) blk_integrity_unregister(disk); ns->pi_type = pi_type; blk_queue_logical_block_size(ns->queue, bs); if (ns->ms && !blk_get_integrity(disk) && (disk->flags & GENHD_FL_UP) && - !(id->flbas & NVME_NS_FLBAS_META_EXT)) + !ns->ext) nvme_init_integrity(ns); if (id->ncap == 0 || (ns->ms && !blk_get_integrity(disk))) diff --git a/include/linux/nvme.h b/include/linux/nvme.h index 0adad4a5419b..8dbd05e70f09 100644 --- a/include/linux/nvme.h +++ b/include/linux/nvme.h @@ -117,8 +117,9 @@ struct nvme_ns { unsigned ns_id; int lba_shift; - int ms; - int pi_type; + u16 ms; + bool ext; + u8 pi_type; u64 mode_select_num_blocks; u32 mode_select_block_len; }; -- cgit v1.2.3 From 8bc0034cf6951a107e0c75c2d10b17b57d681229 Mon Sep 17 00:00:00 2001 From: Sheng Yong Date: Wed, 8 Apr 2015 01:26:18 +0000 Subject: net: remove extra newlines Signed-off-by: Sheng Yong Signed-off-by: David S. Miller --- include/net/ipv6.h | 2 -- net/core/skbuff.c | 1 - net/ipv4/ip_output.c | 3 --- net/ipv4/udp.c | 5 ----- net/ipv6/udp.c | 3 --- 5 files changed, 14 deletions(-) (limited to 'include') diff --git a/include/net/ipv6.h b/include/net/ipv6.h index 27470cd1d5f8..eec8ad3c9843 100644 --- a/include/net/ipv6.h +++ b/include/net/ipv6.h @@ -47,8 +47,6 @@ #define NEXTHDR_MAX 255 - - #define IPV6_DEFAULT_HOPLIMIT 64 #define IPV6_DEFAULT_MCASTHOPS 1 diff --git a/net/core/skbuff.c b/net/core/skbuff.c index cdb939b731aa..3b6e5830256e 100644 --- a/net/core/skbuff.c +++ b/net/core/skbuff.c @@ -3752,7 +3752,6 @@ void skb_complete_wifi_ack(struct sk_buff *skb, bool acked) } EXPORT_SYMBOL_GPL(skb_complete_wifi_ack); - /** * skb_partial_csum_set - set up and verify partial csum values for packet * @skb: the skb to set diff --git a/net/ipv4/ip_output.c b/net/ipv4/ip_output.c index 5da4d15262fd..c65b93a7b711 100644 --- a/net/ipv4/ip_output.c +++ b/net/ipv4/ip_output.c @@ -455,7 +455,6 @@ no_route: } EXPORT_SYMBOL(ip_queue_xmit); - static void ip_copy_metadata(struct sk_buff *to, struct sk_buff *from) { to->pkt_type = from->pkt_type; @@ -820,7 +819,6 @@ static inline int ip_ufo_append_data(struct sock *sk, skb->csum = 0; - __skb_queue_tail(queue, skb); } else if (skb_is_gso(skb)) { goto append; @@ -1218,7 +1216,6 @@ ssize_t ip_append_page(struct sock *sk, struct flowi4 *fl4, struct page *page, skb_shinfo(skb)->gso_type = SKB_GSO_UDP; } - while (size > 0) { int i; diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c index 2162fc6ce1c1..d10b7e0112eb 100644 --- a/net/ipv4/udp.c +++ b/net/ipv4/udp.c @@ -433,7 +433,6 @@ static u32 udp_ehashfn(const struct net *net, const __be32 laddr, udp_ehash_secret + net_hash_mix(net)); } - /* called with read_rcu_lock() */ static struct sock *udp4_lib_lookup2(struct net *net, __be32 saddr, __be16 sport, @@ -1171,7 +1170,6 @@ out: return ret; } - /** * first_packet_length - return length of first packet in receive queue * @sk: socket @@ -1355,7 +1353,6 @@ csum_copy_err: goto try_again; } - int udp_disconnect(struct sock *sk, int flags) { struct inet_sock *inet = inet_sk(sk); @@ -1579,7 +1576,6 @@ int udp_queue_rcv_skb(struct sock *sk, struct sk_buff *skb) udp_lib_checksum_complete(skb)) goto csum_error; - if (sk_rcvqueues_full(sk, sk->sk_rcvbuf)) { UDP_INC_STATS_BH(sock_net(sk), UDP_MIB_RCVBUFERRORS, is_udplite); @@ -1609,7 +1605,6 @@ drop: return -1; } - static void flush_stack(struct sock **stack, unsigned int count, struct sk_buff *skb, unsigned int final) { diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c index 120aff9aa010..3477c919fcc8 100644 --- a/net/ipv6/udp.c +++ b/net/ipv6/udp.c @@ -120,7 +120,6 @@ static u32 udp6_portaddr_hash(const struct net *net, return hash ^ port; } - int udp_v6_get_port(struct sock *sk, unsigned short snum) { unsigned int hash2_nulladdr = @@ -385,7 +384,6 @@ struct sock *udp6_lib_lookup(struct net *net, const struct in6_addr *saddr, __be } EXPORT_SYMBOL_GPL(udp6_lib_lookup); - /* * This should be easy, if there is something there we * return it, otherwise we block. @@ -1555,7 +1553,6 @@ static struct inet_protosw udpv6_protosw = { .flags = INET_PROTOSW_PERMANENT, }; - int __init udpv6_init(void) { int ret; -- cgit v1.2.3 From 38b57f82f66dfb21ebe321d71c84c0e3469980c4 Mon Sep 17 00:00:00 2001 From: Nicholas Bellinger Date: Fri, 27 Feb 2015 22:05:21 -0800 Subject: target: Add protected fabric + unprotected device support This patch adds a new target_core_fabric_ops callback for allowing fabric drivers to expose a TPG attribute for signaling when a T10-PI protected fabric wants to function with an un-protected device without T10-PI. This specifically is to allow LIO to perform WRITE_STRIP + READ_INSERT operations when functioning with non T10-PI enabled devices, seperate from any available hw offloads the fabric supports. This is done using a new se_sess->sess_prot_type that is set at fabric session creation time based upon the TPG attribute. It currently cannot be changed for individual sessions after initial creation. Also, update existing target_core_sbc.c code to honor sess_prot_type when setting up cmd->prot_op + cmd->prot_type assignments. (Add unlikely and !! boolean conversion in sbc_check_prot - Sagi) Cc: Martin Petersen Cc: Sagi Grimberg Cc: Christoph Hellwig Cc: Doug Gilbert Signed-off-by: Nicholas Bellinger --- drivers/target/target_core_sbc.c | 44 +++++++++++++++++++++++++--------- drivers/target/target_core_transport.c | 8 +++++++ include/target/target_core_base.h | 1 + include/target/target_core_fabric.h | 8 +++++++ 4 files changed, 50 insertions(+), 11 deletions(-) (limited to 'include') diff --git a/drivers/target/target_core_sbc.c b/drivers/target/target_core_sbc.c index 95a7a7444965..9efd1fd985ee 100644 --- a/drivers/target/target_core_sbc.c +++ b/drivers/target/target_core_sbc.c @@ -581,12 +581,13 @@ sbc_compare_and_write(struct se_cmd *cmd) } static int -sbc_set_prot_op_checks(u8 protect, enum target_prot_type prot_type, +sbc_set_prot_op_checks(u8 protect, bool fabric_prot, enum target_prot_type prot_type, bool is_write, struct se_cmd *cmd) { if (is_write) { - cmd->prot_op = protect ? TARGET_PROT_DOUT_PASS : - TARGET_PROT_DOUT_INSERT; + cmd->prot_op = fabric_prot ? TARGET_PROT_DOUT_STRIP : + protect ? TARGET_PROT_DOUT_PASS : + TARGET_PROT_DOUT_INSERT; switch (protect) { case 0x0: case 0x3: @@ -610,8 +611,9 @@ sbc_set_prot_op_checks(u8 protect, enum target_prot_type prot_type, return -EINVAL; } } else { - cmd->prot_op = protect ? TARGET_PROT_DIN_PASS : - TARGET_PROT_DIN_STRIP; + cmd->prot_op = fabric_prot ? TARGET_PROT_DIN_INSERT : + protect ? TARGET_PROT_DIN_PASS : + TARGET_PROT_DIN_STRIP; switch (protect) { case 0x0: case 0x1: @@ -644,11 +646,15 @@ sbc_check_prot(struct se_device *dev, struct se_cmd *cmd, unsigned char *cdb, u32 sectors, bool is_write) { u8 protect = cdb[1] >> 5; + int sp_ops = cmd->se_sess->sup_prot_ops; + int pi_prot_type = dev->dev_attrib.pi_prot_type; + bool fabric_prot = false; if (!cmd->t_prot_sg || !cmd->t_prot_nents) { - if (protect && !dev->dev_attrib.pi_prot_type) { - pr_err("CDB contains protect bit, but device does not" - " advertise PROTECT=1 feature bit\n"); + if (unlikely(protect && + !dev->dev_attrib.pi_prot_type && !cmd->se_sess->sess_prot_type)) { + pr_err("CDB contains protect bit, but device + fabric does" + " not advertise PROTECT=1 feature bit\n"); return TCM_INVALID_CDB_FIELD; } if (cmd->prot_pto) @@ -669,15 +675,28 @@ sbc_check_prot(struct se_device *dev, struct se_cmd *cmd, unsigned char *cdb, cmd->reftag_seed = cmd->t_task_lba; break; case TARGET_DIF_TYPE0_PROT: + /* + * See if the fabric supports T10-PI, and the session has been + * configured to allow export PROTECT=1 feature bit with backend + * devices that don't support T10-PI. + */ + fabric_prot = is_write ? + !!(sp_ops & (TARGET_PROT_DOUT_PASS | TARGET_PROT_DOUT_STRIP)) : + !!(sp_ops & (TARGET_PROT_DIN_PASS | TARGET_PROT_DIN_INSERT)); + + if (fabric_prot && cmd->se_sess->sess_prot_type) { + pi_prot_type = cmd->se_sess->sess_prot_type; + break; + } + /* Fallthrough */ default: return TCM_NO_SENSE; } - if (sbc_set_prot_op_checks(protect, dev->dev_attrib.pi_prot_type, - is_write, cmd)) + if (sbc_set_prot_op_checks(protect, fabric_prot, pi_prot_type, is_write, cmd)) return TCM_INVALID_CDB_FIELD; - cmd->prot_type = dev->dev_attrib.pi_prot_type; + cmd->prot_type = pi_prot_type; cmd->prot_length = dev->prot_length * sectors; /** @@ -1231,6 +1250,9 @@ sbc_dif_copy_prot(struct se_cmd *cmd, unsigned int sectors, bool read, unsigned int i, len, left; unsigned int offset = sg_off; + if (!sg) + return; + left = sectors * dev->prot_length; for_each_sg(cmd->t_prot_sg, psg, cmd->t_prot_nents, i) { diff --git a/drivers/target/target_core_transport.c b/drivers/target/target_core_transport.c index 4a00ed5c1880..aef989e165ed 100644 --- a/drivers/target/target_core_transport.c +++ b/drivers/target/target_core_transport.c @@ -322,10 +322,18 @@ void __transport_register_session( struct se_session *se_sess, void *fabric_sess_ptr) { + struct target_core_fabric_ops *tfo = se_tpg->se_tpg_tfo; unsigned char buf[PR_REG_ISID_LEN]; se_sess->se_tpg = se_tpg; se_sess->fabric_sess_ptr = fabric_sess_ptr; + /* + * Determine if fabric allows for T10-PI feature bits to be exposed + * to initiators for device backends with !dev->dev_attrib.pi_prot_type + */ + if (tfo->tpg_check_prot_fabric_only) + se_sess->sess_prot_type = tfo->tpg_check_prot_fabric_only(se_tpg); + /* * Used by struct se_node_acl's under ConfigFS to locate active se_session-t * diff --git a/include/target/target_core_base.h b/include/target/target_core_base.h index 672150b6aaf5..fe25a78fff46 100644 --- a/include/target/target_core_base.h +++ b/include/target/target_core_base.h @@ -616,6 +616,7 @@ struct se_session { unsigned sess_tearing_down:1; u64 sess_bin_isid; enum target_prot_op sup_prot_ops; + enum target_prot_type sess_prot_type; struct se_node_acl *se_node_acl; struct se_portal_group *se_tpg; void *fabric_sess_ptr; diff --git a/include/target/target_core_fabric.h b/include/target/target_core_fabric.h index 2f4a2505db4c..c93cfdf0d8e5 100644 --- a/include/target/target_core_fabric.h +++ b/include/target/target_core_fabric.h @@ -27,6 +27,14 @@ struct target_core_fabric_ops { * inquiry response */ int (*tpg_check_demo_mode_login_only)(struct se_portal_group *); + /* + * Optionally used as a configfs tunable to determine when + * target-core should signal the PROTECT=1 feature bit for + * backends that don't support T10-PI, so that either fabric + * HW offload or target-core emulation performs the associated + * WRITE_STRIP and READ_INSERT operations. + */ + int (*tpg_check_prot_fabric_only)(struct se_portal_group *); struct se_node_acl *(*tpg_alloc_fabric_acl)( struct se_portal_group *); void (*tpg_release_fabric_acl)(struct se_portal_group *, -- cgit v1.2.3 From 901c04a33f138e5fb935a1621375a1a0997fe7b1 Mon Sep 17 00:00:00 2001 From: Nicholas Bellinger Date: Sun, 29 Mar 2015 19:36:16 -0700 Subject: iscsi/iser-target: Add fabric_prot_type attribute support This patch updates iscsi/iser-target to add a new fabric_prot_type TPG attribute for iser-target, used for controlling LLD level protection into LIO when the backend device does not support T10-PI. This is required for ib_isert to enable WRITE_STRIP + READ_INSERT hardware offloads. It's disabled by default and controls which se_sesion->sess_prot_type are set at iscsi_target_locate_portal() session registration time. Cc: Sagi Grimberg Cc: Martin Petersen Signed-off-by: Nicholas Bellinger --- drivers/target/iscsi/iscsi_target_configfs.c | 22 ++++++++++++++++++++++ drivers/target/iscsi/iscsi_target_tpg.c | 19 +++++++++++++++++++ drivers/target/iscsi/iscsi_target_tpg.h | 1 + include/target/iscsi/iscsi_target_core.h | 2 ++ 4 files changed, 44 insertions(+) (limited to 'include') diff --git a/drivers/target/iscsi/iscsi_target_configfs.c b/drivers/target/iscsi/iscsi_target_configfs.c index 95a67f604073..9cb5ab472a52 100644 --- a/drivers/target/iscsi/iscsi_target_configfs.c +++ b/drivers/target/iscsi/iscsi_target_configfs.c @@ -1052,6 +1052,11 @@ TPG_ATTR(default_erl, S_IRUGO | S_IWUSR); */ DEF_TPG_ATTRIB(t10_pi); TPG_ATTR(t10_pi, S_IRUGO | S_IWUSR); +/* + * Define iscsi_tpg_attrib_s_fabric_prot_type + */ +DEF_TPG_ATTRIB(fabric_prot_type); +TPG_ATTR(fabric_prot_type, S_IRUGO | S_IWUSR); static struct configfs_attribute *lio_target_tpg_attrib_attrs[] = { &iscsi_tpg_attrib_authentication.attr, @@ -1065,6 +1070,7 @@ static struct configfs_attribute *lio_target_tpg_attrib_attrs[] = { &iscsi_tpg_attrib_demo_mode_discovery.attr, &iscsi_tpg_attrib_default_erl.attr, &iscsi_tpg_attrib_t10_pi.attr, + &iscsi_tpg_attrib_fabric_prot_type.attr, NULL, }; @@ -1882,6 +1888,20 @@ static int lio_tpg_check_prod_mode_write_protect( return tpg->tpg_attrib.prod_mode_write_protect; } +static int lio_tpg_check_prot_fabric_only( + struct se_portal_group *se_tpg) +{ + struct iscsi_portal_group *tpg = se_tpg->se_tpg_fabric_ptr; + /* + * Only report fabric_prot_type if t10_pi has also been enabled + * for incoming ib_isert sessions. + */ + if (!tpg->tpg_attrib.t10_pi) + return 0; + + return tpg->tpg_attrib.fabric_prot_type; +} + static void lio_tpg_release_fabric_acl( struct se_portal_group *se_tpg, struct se_node_acl *se_acl) @@ -1997,6 +2017,8 @@ int iscsi_target_register_configfs(void) &lio_tpg_check_demo_mode_write_protect; fabric->tf_ops.tpg_check_prod_mode_write_protect = &lio_tpg_check_prod_mode_write_protect; + fabric->tf_ops.tpg_check_prot_fabric_only = + &lio_tpg_check_prot_fabric_only; fabric->tf_ops.tpg_alloc_fabric_acl = &lio_tpg_alloc_fabric_acl; fabric->tf_ops.tpg_release_fabric_acl = &lio_tpg_release_fabric_acl; fabric->tf_ops.tpg_get_inst_index = &lio_tpg_get_inst_index; diff --git a/drivers/target/iscsi/iscsi_target_tpg.c b/drivers/target/iscsi/iscsi_target_tpg.c index bdd127c0e3ae..3076e6f3a831 100644 --- a/drivers/target/iscsi/iscsi_target_tpg.c +++ b/drivers/target/iscsi/iscsi_target_tpg.c @@ -228,6 +228,7 @@ static void iscsit_set_default_tpg_attribs(struct iscsi_portal_group *tpg) a->demo_mode_discovery = TA_DEMO_MODE_DISCOVERY; a->default_erl = TA_DEFAULT_ERL; a->t10_pi = TA_DEFAULT_T10_PI; + a->fabric_prot_type = TA_DEFAULT_FABRIC_PROT_TYPE; } int iscsit_tpg_add_portal_group(struct iscsi_tiqn *tiqn, struct iscsi_portal_group *tpg) @@ -878,3 +879,21 @@ int iscsit_ta_t10_pi( return 0; } + +int iscsit_ta_fabric_prot_type( + struct iscsi_portal_group *tpg, + u32 prot_type) +{ + struct iscsi_tpg_attrib *a = &tpg->tpg_attrib; + + if ((prot_type != 0) && (prot_type != 1) && (prot_type != 3)) { + pr_err("Illegal value for fabric_prot_type: %u\n", prot_type); + return -EINVAL; + } + + a->fabric_prot_type = prot_type; + pr_debug("iSCSI_TPG[%hu] - T10 Fabric Protection Type: %u\n", + tpg->tpgt, prot_type); + + return 0; +} diff --git a/drivers/target/iscsi/iscsi_target_tpg.h b/drivers/target/iscsi/iscsi_target_tpg.h index e7265337bc43..95ff5bdecd71 100644 --- a/drivers/target/iscsi/iscsi_target_tpg.h +++ b/drivers/target/iscsi/iscsi_target_tpg.h @@ -39,5 +39,6 @@ extern int iscsit_ta_prod_mode_write_protect(struct iscsi_portal_group *, u32); extern int iscsit_ta_demo_mode_discovery(struct iscsi_portal_group *, u32); extern int iscsit_ta_default_erl(struct iscsi_portal_group *, u32); extern int iscsit_ta_t10_pi(struct iscsi_portal_group *, u32); +extern int iscsit_ta_fabric_prot_type(struct iscsi_portal_group *, u32); #endif /* ISCSI_TARGET_TPG_H */ diff --git a/include/target/iscsi/iscsi_target_core.h b/include/target/iscsi/iscsi_target_core.h index 0e394a031c28..54e7af301888 100644 --- a/include/target/iscsi/iscsi_target_core.h +++ b/include/target/iscsi/iscsi_target_core.h @@ -62,6 +62,7 @@ #define TA_CACHE_CORE_NPS 0 /* T10 protection information disabled by default */ #define TA_DEFAULT_T10_PI 0 +#define TA_DEFAULT_FABRIC_PROT_TYPE 0 #define ISCSI_IOV_DATA_BUFFER 5 @@ -772,6 +773,7 @@ struct iscsi_tpg_attrib { u32 demo_mode_discovery; u32 default_erl; u8 t10_pi; + u32 fabric_prot_type; struct iscsi_portal_group *tpg; }; -- cgit v1.2.3 From c3d0a7c21db219ef87679c2a667aba9f138524db Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Tue, 7 Apr 2015 19:11:16 +0200 Subject: target: remove the unused SCF_CMD_XCOPY_PASSTHROUGH flag Signed-off-by: Christoph Hellwig Signed-off-by: Nicholas Bellinger --- drivers/target/target_core_xcopy.c | 4 ++-- include/target/target_core_base.h | 1 - 2 files changed, 2 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/drivers/target/target_core_xcopy.c b/drivers/target/target_core_xcopy.c index 04cad3b36297..b09b40e058a0 100644 --- a/drivers/target/target_core_xcopy.c +++ b/drivers/target/target_core_xcopy.c @@ -553,7 +553,7 @@ static int target_xcopy_init_pt_lun( * target_xcopy_setup_pt_port() */ if (!remote_port) { - pt_cmd->se_cmd_flags |= SCF_SE_LUN_CMD | SCF_CMD_XCOPY_PASSTHROUGH; + pt_cmd->se_cmd_flags |= SCF_SE_LUN_CMD; return 0; } @@ -561,7 +561,7 @@ static int target_xcopy_init_pt_lun( pt_cmd->se_dev = se_dev; pr_debug("Setup emulated se_dev: %p from se_dev\n", pt_cmd->se_dev); - pt_cmd->se_cmd_flags |= SCF_SE_LUN_CMD | SCF_CMD_XCOPY_PASSTHROUGH; + pt_cmd->se_cmd_flags |= SCF_SE_LUN_CMD; pr_debug("Setup emulated se_dev: %p to pt_cmd->se_lun->lun_se_dev\n", pt_cmd->se_lun->lun_se_dev); diff --git a/include/target/target_core_base.h b/include/target/target_core_base.h index fe25a78fff46..71699cf3cc71 100644 --- a/include/target/target_core_base.h +++ b/include/target/target_core_base.h @@ -168,7 +168,6 @@ enum se_cmd_flags_table { SCF_ACK_KREF = 0x00040000, SCF_COMPARE_AND_WRITE = 0x00080000, SCF_COMPARE_AND_WRITE_POST = 0x00100000, - SCF_CMD_XCOPY_PASSTHROUGH = 0x00200000, }; /* struct se_dev_entry->lun_flags and struct se_lun->lun_access */ -- cgit v1.2.3 From b6e5b8f1a90230f922de8af59cdaf1ad83e1ac1e Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Date: Fri, 20 Mar 2015 10:45:43 -0300 Subject: [media] media: New flag V4L2_CTRL_FLAG_EXECUTE_ON_WRITE Create a new flag that represent controls which its value needs to be passed to the driver even if it has not changed. They typically represent actions, like triggering a flash or clearing an error flag. So writing to such a control means some action is executed. Signed-off-by: Ricardo Ribalda Delgado Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- include/uapi/linux/videodev2.h | 1 + 1 file changed, 1 insertion(+) (limited to 'include') diff --git a/include/uapi/linux/videodev2.h b/include/uapi/linux/videodev2.h index 47df18f36c4b..810bade873f4 100644 --- a/include/uapi/linux/videodev2.h +++ b/include/uapi/linux/videodev2.h @@ -1457,6 +1457,7 @@ struct v4l2_querymenu { #define V4L2_CTRL_FLAG_WRITE_ONLY 0x0040 #define V4L2_CTRL_FLAG_VOLATILE 0x0080 #define V4L2_CTRL_FLAG_HAS_PAYLOAD 0x0100 +#define V4L2_CTRL_FLAG_EXECUTE_ON_WRITE 0x0200 /* Query flags, to be ORed with the control ID */ #define V4L2_CTRL_FLAG_NEXT_CTRL 0x80000000 -- cgit v1.2.3 From 5ce65d1f874ddc109780fc781bb3b099bff82001 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 20 Mar 2015 14:05:03 -0300 Subject: [media] videodev2.h/v4l2-dv-timings.h: add V4L2_DV_FL_IS_CE_VIDEO flag In the past the V4L2_DV_BT_STD_CEA861 standard bit was used to determine whether the format is a CE (Consumer Electronics) format or not. However, the 640x480p59.94 format is part of the CEA-861 standard, but it is *not* a CE video format. Add a new flag to make this explicit. This information is needed in order to determine the default R'G'B' encoding for the format: for CE video this is limited range (16-235) instead of full range (0-255). The header with all the timings has been updated with this new flag. Signed-off-by: Hans Verkuil Cc: Martin Bugge Cc: Mats Randgaard Signed-off-by: Mauro Carvalho Chehab --- include/uapi/linux/v4l2-dv-timings.h | 64 ++++++++++++++++++++++-------------- include/uapi/linux/videodev2.h | 6 ++++ 2 files changed, 45 insertions(+), 25 deletions(-) (limited to 'include') diff --git a/include/uapi/linux/v4l2-dv-timings.h b/include/uapi/linux/v4l2-dv-timings.h index 6c8f159e416e..c039f1d68a09 100644 --- a/include/uapi/linux/v4l2-dv-timings.h +++ b/include/uapi/linux/v4l2-dv-timings.h @@ -48,14 +48,15 @@ .type = V4L2_DV_BT_656_1120, \ V4L2_INIT_BT_TIMINGS(720, 480, 1, 0, \ 13500000, 19, 62, 57, 4, 3, 15, 4, 3, 16, \ - V4L2_DV_BT_STD_CEA861, V4L2_DV_FL_HALF_LINE) \ + V4L2_DV_BT_STD_CEA861, \ + V4L2_DV_FL_HALF_LINE | V4L2_DV_FL_IS_CE_VIDEO) \ } #define V4L2_DV_BT_CEA_720X480P59_94 { \ .type = V4L2_DV_BT_656_1120, \ V4L2_INIT_BT_TIMINGS(720, 480, 0, 0, \ 27000000, 16, 62, 60, 9, 6, 30, 0, 0, 0, \ - V4L2_DV_BT_STD_CEA861, 0) \ + V4L2_DV_BT_STD_CEA861, V4L2_DV_FL_IS_CE_VIDEO) \ } /* Note: these are the nominal timings, for HDMI links this format is typically @@ -64,14 +65,15 @@ .type = V4L2_DV_BT_656_1120, \ V4L2_INIT_BT_TIMINGS(720, 576, 1, 0, \ 13500000, 12, 63, 69, 2, 3, 19, 2, 3, 20, \ - V4L2_DV_BT_STD_CEA861, V4L2_DV_FL_HALF_LINE) \ + V4L2_DV_BT_STD_CEA861, \ + V4L2_DV_FL_HALF_LINE | V4L2_DV_FL_IS_CE_VIDEO) \ } #define V4L2_DV_BT_CEA_720X576P50 { \ .type = V4L2_DV_BT_656_1120, \ V4L2_INIT_BT_TIMINGS(720, 576, 0, 0, \ 27000000, 12, 64, 68, 5, 5, 39, 0, 0, 0, \ - V4L2_DV_BT_STD_CEA861, 0) \ + V4L2_DV_BT_STD_CEA861, V4L2_DV_FL_IS_CE_VIDEO) \ } #define V4L2_DV_BT_CEA_1280X720P24 { \ @@ -88,7 +90,7 @@ V4L2_INIT_BT_TIMINGS(1280, 720, 0, \ V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \ 74250000, 2420, 40, 220, 5, 5, 20, 0, 0, 0, \ - V4L2_DV_BT_STD_CEA861, 0) \ + V4L2_DV_BT_STD_CEA861, V4L2_DV_FL_IS_CE_VIDEO) \ } #define V4L2_DV_BT_CEA_1280X720P30 { \ @@ -96,7 +98,8 @@ V4L2_INIT_BT_TIMINGS(1280, 720, 0, \ V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \ 74250000, 1760, 40, 220, 5, 5, 20, 0, 0, 0, \ - V4L2_DV_BT_STD_CEA861, V4L2_DV_FL_CAN_REDUCE_FPS) \ + V4L2_DV_BT_STD_CEA861, \ + V4L2_DV_FL_CAN_REDUCE_FPS | V4L2_DV_FL_IS_CE_VIDEO) \ } #define V4L2_DV_BT_CEA_1280X720P50 { \ @@ -104,7 +107,7 @@ V4L2_INIT_BT_TIMINGS(1280, 720, 0, \ V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \ 74250000, 440, 40, 220, 5, 5, 20, 0, 0, 0, \ - V4L2_DV_BT_STD_CEA861, 0) \ + V4L2_DV_BT_STD_CEA861, V4L2_DV_FL_IS_CE_VIDEO) \ } #define V4L2_DV_BT_CEA_1280X720P60 { \ @@ -112,7 +115,8 @@ V4L2_INIT_BT_TIMINGS(1280, 720, 0, \ V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \ 74250000, 110, 40, 220, 5, 5, 20, 0, 0, 0, \ - V4L2_DV_BT_STD_CEA861, V4L2_DV_FL_CAN_REDUCE_FPS) \ + V4L2_DV_BT_STD_CEA861, \ + V4L2_DV_FL_CAN_REDUCE_FPS | V4L2_DV_FL_IS_CE_VIDEO) \ } #define V4L2_DV_BT_CEA_1920X1080P24 { \ @@ -120,7 +124,8 @@ V4L2_INIT_BT_TIMINGS(1920, 1080, 0, \ V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \ 74250000, 638, 44, 148, 4, 5, 36, 0, 0, 0, \ - V4L2_DV_BT_STD_CEA861, V4L2_DV_FL_CAN_REDUCE_FPS) \ + V4L2_DV_BT_STD_CEA861, \ + V4L2_DV_FL_CAN_REDUCE_FPS | V4L2_DV_FL_IS_CE_VIDEO) \ } #define V4L2_DV_BT_CEA_1920X1080P25 { \ @@ -128,7 +133,7 @@ V4L2_INIT_BT_TIMINGS(1920, 1080, 0, \ V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \ 74250000, 528, 44, 148, 4, 5, 36, 0, 0, 0, \ - V4L2_DV_BT_STD_CEA861, 0) \ + V4L2_DV_BT_STD_CEA861, V4L2_DV_FL_IS_CE_VIDEO) \ } #define V4L2_DV_BT_CEA_1920X1080P30 { \ @@ -136,7 +141,8 @@ V4L2_INIT_BT_TIMINGS(1920, 1080, 0, \ V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \ 74250000, 88, 44, 148, 4, 5, 36, 0, 0, 0, \ - V4L2_DV_BT_STD_CEA861, V4L2_DV_FL_CAN_REDUCE_FPS) \ + V4L2_DV_BT_STD_CEA861, \ + V4L2_DV_FL_CAN_REDUCE_FPS | V4L2_DV_FL_IS_CE_VIDEO) \ } #define V4L2_DV_BT_CEA_1920X1080I50 { \ @@ -144,7 +150,8 @@ V4L2_INIT_BT_TIMINGS(1920, 1080, 1, \ V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \ 74250000, 528, 44, 148, 2, 5, 15, 2, 5, 16, \ - V4L2_DV_BT_STD_CEA861, V4L2_DV_FL_HALF_LINE) \ + V4L2_DV_BT_STD_CEA861, \ + V4L2_DV_FL_HALF_LINE | V4L2_DV_FL_IS_CE_VIDEO) \ } #define V4L2_DV_BT_CEA_1920X1080P50 { \ @@ -152,7 +159,7 @@ V4L2_INIT_BT_TIMINGS(1920, 1080, 0, \ V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \ 148500000, 528, 44, 148, 4, 5, 36, 0, 0, 0, \ - V4L2_DV_BT_STD_CEA861, 0) \ + V4L2_DV_BT_STD_CEA861, V4L2_DV_FL_IS_CE_VIDEO) \ } #define V4L2_DV_BT_CEA_1920X1080I60 { \ @@ -161,7 +168,8 @@ V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \ 74250000, 88, 44, 148, 2, 5, 15, 2, 5, 16, \ V4L2_DV_BT_STD_CEA861, \ - V4L2_DV_FL_CAN_REDUCE_FPS | V4L2_DV_FL_HALF_LINE) \ + V4L2_DV_FL_CAN_REDUCE_FPS | \ + V4L2_DV_FL_HALF_LINE | V4L2_DV_FL_IS_CE_VIDEO) \ } #define V4L2_DV_BT_CEA_1920X1080P60 { \ @@ -170,77 +178,83 @@ V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \ 148500000, 88, 44, 148, 4, 5, 36, 0, 0, 0, \ V4L2_DV_BT_STD_DMT | V4L2_DV_BT_STD_CEA861, \ - V4L2_DV_FL_CAN_REDUCE_FPS) \ + V4L2_DV_FL_CAN_REDUCE_FPS | V4L2_DV_FL_IS_CE_VIDEO) \ } #define V4L2_DV_BT_CEA_3840X2160P24 { \ .type = V4L2_DV_BT_656_1120, \ V4L2_INIT_BT_TIMINGS(3840, 2160, 0, V4L2_DV_HSYNC_POS_POL, \ 297000000, 1276, 88, 296, 8, 10, 72, 0, 0, 0, \ - V4L2_DV_BT_STD_CEA861, V4L2_DV_FL_CAN_REDUCE_FPS) \ + V4L2_DV_BT_STD_CEA861, \ + V4L2_DV_FL_CAN_REDUCE_FPS | V4L2_DV_FL_IS_CE_VIDEO) \ } #define V4L2_DV_BT_CEA_3840X2160P25 { \ .type = V4L2_DV_BT_656_1120, \ V4L2_INIT_BT_TIMINGS(3840, 2160, 0, V4L2_DV_HSYNC_POS_POL, \ 297000000, 1056, 88, 296, 8, 10, 72, 0, 0, 0, \ - V4L2_DV_BT_STD_CEA861, 0) \ + V4L2_DV_BT_STD_CEA861, V4L2_DV_FL_IS_CE_VIDEO) \ } #define V4L2_DV_BT_CEA_3840X2160P30 { \ .type = V4L2_DV_BT_656_1120, \ V4L2_INIT_BT_TIMINGS(3840, 2160, 0, V4L2_DV_HSYNC_POS_POL, \ 297000000, 176, 88, 296, 8, 10, 72, 0, 0, 0, \ - V4L2_DV_BT_STD_CEA861, V4L2_DV_FL_CAN_REDUCE_FPS) \ + V4L2_DV_BT_STD_CEA861, \ + V4L2_DV_FL_CAN_REDUCE_FPS | V4L2_DV_FL_IS_CE_VIDEO) \ } #define V4L2_DV_BT_CEA_3840X2160P50 { \ .type = V4L2_DV_BT_656_1120, \ V4L2_INIT_BT_TIMINGS(3840, 2160, 0, V4L2_DV_HSYNC_POS_POL, \ 594000000, 1056, 88, 296, 8, 10, 72, 0, 0, 0, \ - V4L2_DV_BT_STD_CEA861, 0) \ + V4L2_DV_BT_STD_CEA861, V4L2_DV_FL_IS_CE_VIDEO) \ } #define V4L2_DV_BT_CEA_3840X2160P60 { \ .type = V4L2_DV_BT_656_1120, \ V4L2_INIT_BT_TIMINGS(3840, 2160, 0, V4L2_DV_HSYNC_POS_POL, \ 594000000, 176, 88, 296, 8, 10, 72, 0, 0, 0, \ - V4L2_DV_BT_STD_CEA861, V4L2_DV_FL_CAN_REDUCE_FPS) \ + V4L2_DV_BT_STD_CEA861, \ + V4L2_DV_FL_CAN_REDUCE_FPS | V4L2_DV_FL_IS_CE_VIDEO) \ } #define V4L2_DV_BT_CEA_4096X2160P24 { \ .type = V4L2_DV_BT_656_1120, \ V4L2_INIT_BT_TIMINGS(4096, 2160, 0, V4L2_DV_HSYNC_POS_POL, \ 297000000, 1020, 88, 296, 8, 10, 72, 0, 0, 0, \ - V4L2_DV_BT_STD_CEA861, V4L2_DV_FL_CAN_REDUCE_FPS) \ + V4L2_DV_BT_STD_CEA861, \ + V4L2_DV_FL_CAN_REDUCE_FPS | V4L2_DV_FL_IS_CE_VIDEO) \ } #define V4L2_DV_BT_CEA_4096X2160P25 { \ .type = V4L2_DV_BT_656_1120, \ V4L2_INIT_BT_TIMINGS(4096, 2160, 0, V4L2_DV_HSYNC_POS_POL, \ 297000000, 968, 88, 128, 8, 10, 72, 0, 0, 0, \ - V4L2_DV_BT_STD_CEA861, 0) \ + V4L2_DV_BT_STD_CEA861, V4L2_DV_FL_IS_CE_VIDEO) \ } #define V4L2_DV_BT_CEA_4096X2160P30 { \ .type = V4L2_DV_BT_656_1120, \ V4L2_INIT_BT_TIMINGS(4096, 2160, 0, V4L2_DV_HSYNC_POS_POL, \ 297000000, 88, 88, 128, 8, 10, 72, 0, 0, 0, \ - V4L2_DV_BT_STD_CEA861, V4L2_DV_FL_CAN_REDUCE_FPS) \ + V4L2_DV_BT_STD_CEA861, \ + V4L2_DV_FL_CAN_REDUCE_FPS | V4L2_DV_FL_IS_CE_VIDEO) \ } #define V4L2_DV_BT_CEA_4096X2160P50 { \ .type = V4L2_DV_BT_656_1120, \ V4L2_INIT_BT_TIMINGS(4096, 2160, 0, V4L2_DV_HSYNC_POS_POL, \ 594000000, 968, 88, 128, 8, 10, 72, 0, 0, 0, \ - V4L2_DV_BT_STD_CEA861, 0) \ + V4L2_DV_BT_STD_CEA861, V4L2_DV_FL_IS_CE_VIDEO) \ } #define V4L2_DV_BT_CEA_4096X2160P60 { \ .type = V4L2_DV_BT_656_1120, \ V4L2_INIT_BT_TIMINGS(4096, 2160, 0, V4L2_DV_HSYNC_POS_POL, \ 594000000, 88, 88, 128, 8, 10, 72, 0, 0, 0, \ - V4L2_DV_BT_STD_CEA861, V4L2_DV_FL_CAN_REDUCE_FPS) \ + V4L2_DV_BT_STD_CEA861, \ + V4L2_DV_FL_CAN_REDUCE_FPS | V4L2_DV_FL_IS_CE_VIDEO) \ } diff --git a/include/uapi/linux/videodev2.h b/include/uapi/linux/videodev2.h index 810bade873f4..fa376f7666ba 100644 --- a/include/uapi/linux/videodev2.h +++ b/include/uapi/linux/videodev2.h @@ -1188,6 +1188,12 @@ struct v4l2_bt_timings { exactly the same number of half-lines. Whether half-lines can be detected or used depends on the hardware. */ #define V4L2_DV_FL_HALF_LINE (1 << 3) +/* If set, then this is a Consumer Electronics (CE) video format. Such formats + * differ from other formats (commonly called IT formats) in that if RGB + * encoding is used then by default the RGB values use limited range (i.e. + * use the range 16-235) as opposed to 0-255. All formats defined in CEA-861 + * except for the 640x480 format are CE formats. */ +#define V4L2_DV_FL_IS_CE_VIDEO (1 << 4) /* A few useful defines to calculate the total blanking and frame sizes */ #define V4L2_DV_BT_BLANKING_WIDTH(bt) \ -- cgit v1.2.3 From 664c715573c2c116c2d8f5de7d59ce85a98a1751 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 8 Apr 2015 11:43:14 +0200 Subject: ALSA: hda - Work around races of power up/down with runtime PM Currently, snd_hdac_power_up()/down() helpers checks whether the codec is being in pm (suspend/resume), and skips the call of runtime get/put during it. This is needed as there are lots of power up/down sequences called in the paths that are also used in the PM itself. An example is found in hda_codec.c::codec_exec_verb(), where this can power up the codec while it may be called again in its power up sequence, too. The above works in most cases, but sometimes we really want to wait for the real power up. For example, the control element get/put may want explicit power up so that the value change is assured to reach to the hardware. Using the current snd_hdac_power_up(), however, results in a race, e.g. when it's called during the runtime suspend is being performed. In the worst case, as found in patch_ca0132.c, it can even lead to the deadlock because the code assumes the power up while it was skipped due to the check above. For dealing with such cases, this patch makes snd_hdac_power_up() and _down() to two variants: with and without in_pm flag check. The version with pm flag check is named as snd_hdac_power_up_pm() while the version without pm flag check is still kept as snd_hdac_power_up(). (Just because the usage of the former is fewer.) Then finally, the patch replaces each call potentially done in PM with the new _pm() variant. In theory, we can implement a unified version -- if we can distinguish the current context whether it's in the pm path. But such an implementation is cumbersome, so leave the code like this a bit messy way for now... Bugzilla: https://bugzilla.kernel.org/show_bug.cgi?id=96271 Signed-off-by: Takashi Iwai --- include/sound/hdaudio.h | 28 ++++++++++++++++++++++++++++ sound/hda/hdac_device.c | 16 +++++++--------- sound/hda/hdac_regmap.c | 8 ++++---- sound/pci/hda/hda_codec.c | 8 ++++---- sound/pci/hda/hda_codec.h | 2 ++ sound/pci/hda/patch_ca0132.c | 12 ++++++------ sound/pci/hda/patch_hdmi.c | 4 ++-- 7 files changed, 53 insertions(+), 25 deletions(-) (limited to 'include') diff --git a/include/sound/hdaudio.h b/include/sound/hdaudio.h index 95acc337aea5..30446f17c6a6 100644 --- a/include/sound/hdaudio.h +++ b/include/sound/hdaudio.h @@ -144,6 +144,34 @@ static inline void snd_hdac_power_up(struct hdac_device *codec) {} static inline void snd_hdac_power_down(struct hdac_device *codec) {} #endif +/** + * snd_hdac_power_up_pm - power up the codec + * @codec: the codec object + * + * This function can be called in a recursive code path like init code + * which may be called by PM suspend/resume again. OTOH, if a power-up + * call must wake up the sleeper (e.g. in a kctl callback), use + * snd_hdac_power_up() instead. + */ +static inline void snd_hdac_power_up_pm(struct hdac_device *codec) +{ + if (!atomic_read(&codec->in_pm)) + snd_hdac_power_up(codec); +} + +/** + * snd_hdac_power_down_pm - power down the codec + * @codec: the codec object + * + * Like snd_hdac_power_up_pm(), this function is used in a recursive + * code path like init code which may be called by PM suspend/resume again. + */ +static inline void snd_hdac_power_down_pm(struct hdac_device *codec) +{ + if (!atomic_read(&codec->in_pm)) + snd_hdac_power_down(codec); +} + /* * HD-audio codec base driver */ diff --git a/sound/hda/hdac_device.c b/sound/hda/hdac_device.c index d4a0e723af2c..92604bbcee5f 100644 --- a/sound/hda/hdac_device.c +++ b/sound/hda/hdac_device.c @@ -494,29 +494,27 @@ EXPORT_SYMBOL_GPL(snd_hdac_get_connections); #ifdef CONFIG_PM /** - * snd_hdac_power_up - increment the runtime pm counter + * snd_hdac_power_up - power up the codec * @codec: the codec object + * + * This function calls the runtime PM helper to power up the given codec. + * Unlike snd_hdac_power_up_pm(), you should call this only for the code + * path that isn't included in PM path. Otherwise it gets stuck. */ void snd_hdac_power_up(struct hdac_device *codec) { - struct device *dev = &codec->dev; - - if (atomic_read(&codec->in_pm)) - return; - pm_runtime_get_sync(dev); + pm_runtime_get_sync(&codec->dev); } EXPORT_SYMBOL_GPL(snd_hdac_power_up); /** - * snd_hdac_power_up - decrement the runtime pm counter + * snd_hdac_power_down - power down the codec * @codec: the codec object */ void snd_hdac_power_down(struct hdac_device *codec) { struct device *dev = &codec->dev; - if (atomic_read(&codec->in_pm)) - return; pm_runtime_mark_last_busy(dev); pm_runtime_put_autosuspend(dev); } diff --git a/sound/hda/hdac_regmap.c b/sound/hda/hdac_regmap.c index 1eb43209fe2c..64876fa357c9 100644 --- a/sound/hda/hdac_regmap.c +++ b/sound/hda/hdac_regmap.c @@ -402,9 +402,9 @@ int snd_hdac_regmap_write_raw(struct hdac_device *codec, unsigned int reg, err = reg_raw_write(codec, reg, val); if (err == -EAGAIN) { - snd_hdac_power_up(codec); + snd_hdac_power_up_pm(codec); err = reg_raw_write(codec, reg, val); - snd_hdac_power_down(codec); + snd_hdac_power_down_pm(codec); } return err; } @@ -434,9 +434,9 @@ int snd_hdac_regmap_read_raw(struct hdac_device *codec, unsigned int reg, err = reg_raw_read(codec, reg, val); if (err == -EAGAIN) { - snd_hdac_power_up(codec); + snd_hdac_power_up_pm(codec); err = reg_raw_read(codec, reg, val); - snd_hdac_power_down(codec); + snd_hdac_power_down_pm(codec); } return err; } diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index 16dfa1ed10dd..e70a7fb393dd 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -137,7 +137,7 @@ static int codec_exec_verb(struct hdac_device *dev, unsigned int cmd, return -1; again: - snd_hda_power_up(codec); + snd_hda_power_up_pm(codec); mutex_lock(&bus->core.cmd_mutex); if (flags & HDA_RW_NO_RESPONSE_FALLBACK) bus->no_response_fallback = 1; @@ -145,7 +145,7 @@ static int codec_exec_verb(struct hdac_device *dev, unsigned int cmd, cmd, res); bus->no_response_fallback = 0; mutex_unlock(&bus->core.cmd_mutex); - snd_hda_power_down(codec); + snd_hda_power_down_pm(codec); if (!codec_in_pm(codec) && res && err < 0 && bus->rirb_error) { if (bus->response_reset) { codec_dbg(codec, @@ -3951,7 +3951,7 @@ int snd_hda_check_amp_list_power(struct hda_codec *codec, if (!(v & HDA_AMP_MUTE) && v > 0) { if (!check->power_on) { check->power_on = 1; - snd_hda_power_up(codec); + snd_hda_power_up_pm(codec); } return 1; } @@ -3959,7 +3959,7 @@ int snd_hda_check_amp_list_power(struct hda_codec *codec, } if (check->power_on) { check->power_on = 0; - snd_hda_power_down(codec); + snd_hda_power_down_pm(codec); } return 0; } diff --git a/sound/pci/hda/hda_codec.h b/sound/pci/hda/hda_codec.h index acf868c6a785..9075ac28dc4b 100644 --- a/sound/pci/hda/hda_codec.h +++ b/sound/pci/hda/hda_codec.h @@ -508,7 +508,9 @@ const char *snd_hda_get_jack_location(u32 cfg); * power saving */ #define snd_hda_power_up(codec) snd_hdac_power_up(&(codec)->core) +#define snd_hda_power_up_pm(codec) snd_hdac_power_up_pm(&(codec)->core) #define snd_hda_power_down(codec) snd_hdac_power_down(&(codec)->core) +#define snd_hda_power_down_pm(codec) snd_hdac_power_down_pm(&(codec)->core) #ifdef CONFIG_PM void snd_hda_set_power_save(struct hda_bus *bus, int delay); void snd_hda_update_power_acct(struct hda_codec *codec); diff --git a/sound/pci/hda/patch_ca0132.c b/sound/pci/hda/patch_ca0132.c index 5aff35a09fd4..4a4e7b282e4f 100644 --- a/sound/pci/hda/patch_ca0132.c +++ b/sound/pci/hda/patch_ca0132.c @@ -3131,7 +3131,7 @@ static int ca0132_select_out(struct hda_codec *codec) codec_dbg(codec, "ca0132_select_out\n"); - snd_hda_power_up(codec); + snd_hda_power_up_pm(codec); auto_jack = spec->vnode_lswitch[VNID_HP_ASEL - VNODE_START_NID]; @@ -3215,7 +3215,7 @@ static int ca0132_select_out(struct hda_codec *codec) } exit: - snd_hda_power_down(codec); + snd_hda_power_down_pm(codec); return err < 0 ? err : 0; } @@ -3293,7 +3293,7 @@ static int ca0132_select_mic(struct hda_codec *codec) codec_dbg(codec, "ca0132_select_mic\n"); - snd_hda_power_up(codec); + snd_hda_power_up_pm(codec); auto_jack = spec->vnode_lswitch[VNID_AMIC1_ASEL - VNODE_START_NID]; @@ -3326,7 +3326,7 @@ static int ca0132_select_mic(struct hda_codec *codec) ca0132_effects_set(codec, VOICE_FOCUS, 0); } - snd_hda_power_down(codec); + snd_hda_power_down_pm(codec); return 0; } @@ -4546,7 +4546,7 @@ static int ca0132_init(struct hda_codec *codec) spec->dsp_state = DSP_DOWNLOAD_INIT; spec->curr_chip_addx = INVALID_CHIP_ADDRESS; - snd_hda_power_up(codec); + snd_hda_power_up_pm(codec); ca0132_init_unsol(codec); @@ -4577,7 +4577,7 @@ static int ca0132_init(struct hda_codec *codec) snd_hda_jack_report_sync(codec); - snd_hda_power_down(codec); + snd_hda_power_down_pm(codec); return 0; } diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c index ca0c05e1c42e..5f44f60a6389 100644 --- a/sound/pci/hda/patch_hdmi.c +++ b/sound/pci/hda/patch_hdmi.c @@ -1545,7 +1545,7 @@ static bool hdmi_present_sense(struct hdmi_spec_per_pin *per_pin, int repoll) bool eld_changed = false; bool ret; - snd_hda_power_up(codec); + snd_hda_power_up_pm(codec); present = snd_hda_pin_sense(codec, pin_nid); mutex_lock(&per_pin->lock); @@ -1631,7 +1631,7 @@ static bool hdmi_present_sense(struct hdmi_spec_per_pin *per_pin, int repoll) jack->block_report = !ret; mutex_unlock(&per_pin->lock); - snd_hda_power_down(codec); + snd_hda_power_down_pm(codec); return ret; } -- cgit v1.2.3 From acd388fd3af350ab24c6ab6f19b83fc4a4f3aa60 Mon Sep 17 00:00:00 2001 From: "Steven Rostedt (Red Hat)" Date: Tue, 31 Mar 2015 14:37:12 -0400 Subject: tracing: Give system name a pointer Normally the compiler will use the same pointer for a string throughout the file. But there's no guarantee of that happening. Later changes will require that all events have the same pointer to the system string. Name the system string and have all events point to it. Testing this, it did not increases the size of the text, except for the notes section, which should not harm the real size any. Link: http://lkml.kernel.org/r/20150403013802.220157513@goodmis.org Reviewed-by: Masami Hiramatsu Tested-by: Masami Hiramatsu Signed-off-by: Steven Rostedt --- include/linux/ftrace_event.h | 2 +- include/trace/ftrace.h | 19 +++++++++++++++++-- 2 files changed, 18 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/include/linux/ftrace_event.h b/include/linux/ftrace_event.h index c674ee8f7fca..62b8fac7ded5 100644 --- a/include/linux/ftrace_event.h +++ b/include/linux/ftrace_event.h @@ -202,7 +202,7 @@ enum trace_reg { struct ftrace_event_call; struct ftrace_event_class { - char *system; + const char *system; void *probe; #ifdef CONFIG_PERF_EVENTS void *perf_probe; diff --git a/include/trace/ftrace.h b/include/trace/ftrace.h index 41bf65f04dd9..2f9b95b6d3fb 100644 --- a/include/trace/ftrace.h +++ b/include/trace/ftrace.h @@ -18,6 +18,21 @@ #include +#ifndef TRACE_SYSTEM_VAR +#define TRACE_SYSTEM_VAR TRACE_SYSTEM +#endif + +#define __app__(x, y) str__##x##y +#define __app(x, y) __app__(x, y) + +#define TRACE_SYSTEM_STRING __app(TRACE_SYSTEM_VAR,__trace_system_name) + +#define TRACE_MAKE_SYSTEM_STR() \ + static const char TRACE_SYSTEM_STRING[] = \ + __stringify(TRACE_SYSTEM) + +TRACE_MAKE_SYSTEM_STR(); + /* * DECLARE_EVENT_CLASS can be used to add a generic function * handlers for events. That is, if all events have the same @@ -105,7 +120,6 @@ #include TRACE_INCLUDE(TRACE_INCLUDE_FILE) - /* * Stage 2 of the trace events. * @@ -692,7 +706,7 @@ static inline void ftrace_test_probe_##call(void) \ _TRACE_PERF_PROTO(call, PARAMS(proto)); \ static const char print_fmt_##call[] = print; \ static struct ftrace_event_class __used __refdata event_class_##call = { \ - .system = __stringify(TRACE_SYSTEM), \ + .system = TRACE_SYSTEM_STRING, \ .define_fields = ftrace_define_fields_##call, \ .fields = LIST_HEAD_INIT(event_class_##call.fields),\ .raw_init = trace_event_raw_init, \ @@ -735,6 +749,7 @@ __attribute__((section("_ftrace_events"))) *__event_##call = &event_##call #include TRACE_INCLUDE(TRACE_INCLUDE_FILE) +#undef TRACE_SYSTEM_VAR #ifdef CONFIG_PERF_EVENTS -- cgit v1.2.3 From 0c564a538aa934ad15b2145aaf8b64f3feb0be63 Mon Sep 17 00:00:00 2001 From: "Steven Rostedt (Red Hat)" Date: Tue, 24 Mar 2015 17:58:09 -0400 Subject: tracing: Add TRACE_DEFINE_ENUM() macro to map enums to their values Several tracepoints use the helper functions __print_symbolic() or __print_flags() and pass in enums that do the mapping between the binary data stored and the value to print. This works well for reading the ASCII trace files, but when the data is read via userspace tools such as perf and trace-cmd, the conversion of the binary value to a human string format is lost if an enum is used, as userspace does not have access to what the ENUM is. For example, the tracepoint trace_tlb_flush() has: __print_symbolic(REC->reason, { TLB_FLUSH_ON_TASK_SWITCH, "flush on task switch" }, { TLB_REMOTE_SHOOTDOWN, "remote shootdown" }, { TLB_LOCAL_SHOOTDOWN, "local shootdown" }, { TLB_LOCAL_MM_SHOOTDOWN, "local mm shootdown" }) Which maps the enum values to the strings they represent. But perf and trace-cmd do no know what value TLB_LOCAL_MM_SHOOTDOWN is, and would not be able to map it. With TRACE_DEFINE_ENUM(), developers can place these in the event header files and ftrace will convert the enums to their values: By adding: TRACE_DEFINE_ENUM(TLB_FLUSH_ON_TASK_SWITCH); TRACE_DEFINE_ENUM(TLB_REMOTE_SHOOTDOWN); TRACE_DEFINE_ENUM(TLB_LOCAL_SHOOTDOWN); TRACE_DEFINE_ENUM(TLB_LOCAL_MM_SHOOTDOWN); $ cat /sys/kernel/debug/tracing/events/tlb/tlb_flush/format [...] __print_symbolic(REC->reason, { 0, "flush on task switch" }, { 1, "remote shootdown" }, { 2, "local shootdown" }, { 3, "local mm shootdown" }) The above is what userspace expects to see, and tools do not need to be modified to parse them. Link: http://lkml.kernel.org/r/20150403013802.220157513@goodmis.org Cc: Guilherme Cox Cc: Tony Luck Cc: Xie XiuQi Acked-by: Namhyung Kim Reviewed-by: Masami Hiramatsu Tested-by: Masami Hiramatsu Signed-off-by: Steven Rostedt --- include/asm-generic/vmlinux.lds.h | 5 +- include/linux/ftrace_event.h | 2 +- include/linux/tracepoint.h | 8 +++ include/trace/ftrace.h | 22 ++++++- kernel/trace/trace.c | 26 ++++++++- kernel/trace/trace.h | 2 + kernel/trace/trace_events.c | 119 ++++++++++++++++++++++++++++++++++++++ 7 files changed, 178 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/include/asm-generic/vmlinux.lds.h b/include/asm-generic/vmlinux.lds.h index ac78910d7416..f8e8b34dc427 100644 --- a/include/asm-generic/vmlinux.lds.h +++ b/include/asm-generic/vmlinux.lds.h @@ -124,7 +124,10 @@ #define FTRACE_EVENTS() . = ALIGN(8); \ VMLINUX_SYMBOL(__start_ftrace_events) = .; \ *(_ftrace_events) \ - VMLINUX_SYMBOL(__stop_ftrace_events) = .; + VMLINUX_SYMBOL(__stop_ftrace_events) = .; \ + VMLINUX_SYMBOL(__start_ftrace_enum_maps) = .; \ + *(_ftrace_enum_map) \ + VMLINUX_SYMBOL(__stop_ftrace_enum_maps) = .; #else #define FTRACE_EVENTS() #endif diff --git a/include/linux/ftrace_event.h b/include/linux/ftrace_event.h index 62b8fac7ded5..112cf49d9576 100644 --- a/include/linux/ftrace_event.h +++ b/include/linux/ftrace_event.h @@ -285,7 +285,7 @@ struct ftrace_event_call { struct tracepoint *tp; }; struct trace_event event; - const char *print_fmt; + char *print_fmt; struct event_filter *filter; void *mod; void *data; diff --git a/include/linux/tracepoint.h b/include/linux/tracepoint.h index c72851328ca9..a5f7f3ecafa3 100644 --- a/include/linux/tracepoint.h +++ b/include/linux/tracepoint.h @@ -36,6 +36,12 @@ struct tracepoint { struct tracepoint_func __rcu *funcs; }; +struct trace_enum_map { + const char *system; + const char *enum_string; + unsigned long enum_value; +}; + extern int tracepoint_probe_register(struct tracepoint *tp, void *probe, void *data); extern int @@ -87,6 +93,8 @@ extern void syscall_unregfunc(void); #define PARAMS(args...) args +#define TRACE_DEFINE_ENUM(x) + #endif /* _LINUX_TRACEPOINT_H */ /* diff --git a/include/trace/ftrace.h b/include/trace/ftrace.h index 2f9b95b6d3fb..37d4b10b111d 100644 --- a/include/trace/ftrace.h +++ b/include/trace/ftrace.h @@ -33,6 +33,19 @@ TRACE_MAKE_SYSTEM_STR(); +#undef TRACE_DEFINE_ENUM +#define TRACE_DEFINE_ENUM(a) \ + static struct trace_enum_map __used __initdata \ + __##TRACE_SYSTEM##_##a = \ + { \ + .system = TRACE_SYSTEM_STRING, \ + .enum_string = #a, \ + .enum_value = a \ + }; \ + static struct trace_enum_map __used \ + __attribute__((section("_ftrace_enum_map"))) \ + *TRACE_SYSTEM##_##a = &__##TRACE_SYSTEM##_##a + /* * DECLARE_EVENT_CLASS can be used to add a generic function * handlers for events. That is, if all events have the same @@ -136,6 +149,9 @@ TRACE_MAKE_SYSTEM_STR(); * The size of an array is also encoded, in the higher 16 bits of . */ +#undef TRACE_DEFINE_ENUM +#define TRACE_DEFINE_ENUM(a) + #undef __field #define __field(type, item) @@ -553,7 +569,7 @@ static inline notrace int ftrace_get_offsets_##call( \ * .trace = ftrace_raw_output_, <-- stage 2 * }; * - * static const char print_fmt_[] = ; + * static char print_fmt_[] = ; * * static struct ftrace_event_class __used event_class_