From d4f65b5d2497b2fd9c45f06b71deb4ab084a5b66 Mon Sep 17 00:00:00 2001 From: David Howells Date: Thu, 13 Sep 2012 13:06:29 +0100 Subject: KEYS: Add payload preparsing opportunity prior to key instantiate or update Give the key type the opportunity to preparse the payload prior to the instantiation and update routines being called. This is done with the provision of two new key type operations: int (*preparse)(struct key_preparsed_payload *prep); void (*free_preparse)(struct key_preparsed_payload *prep); If the first operation is present, then it is called before key creation (in the add/update case) or before the key semaphore is taken (in the update and instantiate cases). The second operation is called to clean up if the first was called. preparse() is given the opportunity to fill in the following structure: struct key_preparsed_payload { char *description; void *type_data[2]; void *payload; const void *data; size_t datalen; size_t quotalen; }; Before the preparser is called, the first three fields will have been cleared, the payload pointer and size will be stored in data and datalen and the default quota size from the key_type struct will be stored into quotalen. The preparser may parse the payload in any way it likes and may store data in the type_data[] and payload fields for use by the instantiate() and update() ops. The preparser may also propose a description for the key by attaching it as a string to the description field. This can be used by passing a NULL or "" description to the add_key() system call or the key_create_or_update() function. This cannot work with request_key() as that required the description to tell the upcall about the key to be created. This, for example permits keys that store PGP public keys to generate their own name from the user ID and public key fingerprint in the key. The instantiate() and update() operations are then modified to look like this: int (*instantiate)(struct key *key, struct key_preparsed_payload *prep); int (*update)(struct key *key, struct key_preparsed_payload *prep); and the new payload data is passed in *prep, whether or not it was preparsed. Signed-off-by: David Howells --- Documentation/security/keys.txt | 50 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 49 insertions(+), 1 deletion(-) (limited to 'Documentation') diff --git a/Documentation/security/keys.txt b/Documentation/security/keys.txt index aa0dbd74b71b..7d9ca92022d8 100644 --- a/Documentation/security/keys.txt +++ b/Documentation/security/keys.txt @@ -412,6 +412,10 @@ The main syscalls are: to the keyring. In this case, an error will be generated if the process does not have permission to write to the keyring. + If the key type supports it, if the description is NULL or an empty + string, the key type will try and generate a description from the content + of the payload. + The payload is optional, and the pointer can be NULL if not required by the type. The payload is plen in size, and plen can be zero for an empty payload. @@ -1114,12 +1118,53 @@ The structure has a number of fields, some of which are mandatory: it should return 0. - (*) int (*instantiate)(struct key *key, const void *data, size_t datalen); + (*) int (*preparse)(struct key_preparsed_payload *prep); + + This optional method permits the key type to attempt to parse payload + before a key is created (add key) or the key semaphore is taken (update or + instantiate key). The structure pointed to by prep looks like: + + struct key_preparsed_payload { + char *description; + void *type_data[2]; + void *payload; + const void *data; + size_t datalen; + size_t quotalen; + }; + + Before calling the method, the caller will fill in data and datalen with + the payload blob parameters; quotalen will be filled in with the default + quota size from the key type and the rest will be cleared. + + If a description can be proposed from the payload contents, that should be + attached as a string to the description field. This will be used for the + key description if the caller of add_key() passes NULL or "". + + The method can attach anything it likes to type_data[] and payload. These + are merely passed along to the instantiate() or update() operations. + + The method should return 0 if success ful or a negative error code + otherwise. + + + (*) void (*free_preparse)(struct key_preparsed_payload *prep); + + This method is only required if the preparse() method is provided, + otherwise it is unused. It cleans up anything attached to the + description, type_data and payload fields of the key_preparsed_payload + struct as filled in by the preparse() method. + + + (*) int (*instantiate)(struct key *key, struct key_preparsed_payload *prep); This method is called to attach a payload to a key during construction. The payload attached need not bear any relation to the data passed to this function. + The prep->data and prep->datalen fields will define the original payload + blob. If preparse() was supplied then other fields may be filled in also. + If the amount of data attached to the key differs from the size in keytype->def_datalen, then key_payload_reserve() should be called. @@ -1135,6 +1180,9 @@ The structure has a number of fields, some of which are mandatory: If this type of key can be updated, then this method should be provided. It is called to update a key's payload from the blob of data provided. + The prep->data and prep->datalen fields will define the original payload + blob. If preparse() was supplied then other fields may be filled in also. + key_payload_reserve() should be called if the data length might change before any changes are actually made. Note that if this succeeds, the type is committed to changing the key because it's already been altered, so all -- cgit v1.2.3-59-g8ed1b From 8347bbecf3518aa1518f6157e661812a35775130 Mon Sep 17 00:00:00 2001 From: Thomas Renninger Date: Mon, 1 Oct 2012 00:23:57 +0200 Subject: ACPI: Document ACPI table overriding via initrd Signed-off-by: Thomas Renninger Link: http://lkml.kernel.org/r/1349043837-22659-7-git-send-email-trenn@suse.de Cc: Len Brown Cc: Robert Moore Cc: Yinghai Lu Cc: Eric Piel Signed-off-by: H. Peter Anvin --- Documentation/acpi/initrd_table_override.txt | 94 ++++++++++++++++++++++++++++ 1 file changed, 94 insertions(+) create mode 100644 Documentation/acpi/initrd_table_override.txt (limited to 'Documentation') diff --git a/Documentation/acpi/initrd_table_override.txt b/Documentation/acpi/initrd_table_override.txt new file mode 100644 index 000000000000..35c3f5415476 --- /dev/null +++ b/Documentation/acpi/initrd_table_override.txt @@ -0,0 +1,94 @@ +Overriding ACPI tables via initrd +================================= + +1) Introduction (What is this about) +2) What is this for +3) How does it work +4) References (Where to retrieve userspace tools) + +1) What is this about +--------------------- + +If the ACPI_INITRD_TABLE_OVERRIDE compile option is true, it is possible to +override nearly any ACPI table provided by the BIOS with an instrumented, +modified one. + +For a full list of ACPI tables that can be overridden, take a look at +the char *table_sigs[MAX_ACPI_SIGNATURE]; definition in drivers/acpi/osl.c +All ACPI tables iasl (Intel's ACPI compiler and disassembler) knows should +be overridable, except: + - ACPI_SIG_RSDP (has a signature of 6 bytes) + - ACPI_SIG_FACS (does not have an ordinary ACPI table header) +Both could get implemented as well. + + +2) What is this for +------------------- + +Please keep in mind that this is a debug option. +ACPI tables should not get overridden for productive use. +If BIOS ACPI tables are overridden the kernel will get tainted with the +TAINT_OVERRIDDEN_ACPI_TABLE flag. +Complain to your platform/BIOS vendor if you find a bug which is so sever +that a workaround is not accepted in the Linux kernel. + +Still, it can and should be enabled in any kernel, because: + - There is no functional change with not instrumented initrds + - It provides a powerful feature to easily debug and test ACPI BIOS table + compatibility with the Linux kernel. + + +3) How does it work +------------------- + +# Extract the machine's ACPI tables: +cd /tmp +acpidump >acpidump +acpixtract -a acpidump +# Disassemble, modify and recompile them: +iasl -d *.dat +# For example add this statement into a _PRT (PCI Routing Table) function +# of the DSDT: +Store("HELLO WORLD", debug) +iasl -sa dsdt.dsl +# Add the raw ACPI tables to an uncompressed cpio archive. +# They must be put into a /kernel/firmware/acpi directory inside the +# cpio archive. +# The uncompressed cpio archive must be the first. +# Other, typically compressed cpio archives, must be +# concatenated on top of the uncompressed one. +mkdir -p kernel/firmware/acpi +cp dsdt.aml kernel/firmware/acpi +# A maximum of: #define ACPI_OVERRIDE_TABLES 10 +# tables are currently allowed (see osl.c): +iasl -sa facp.dsl +iasl -sa ssdt1.dsl +cp facp.aml kernel/firmware/acpi +cp ssdt1.aml kernel/firmware/acpi +# Create the uncompressed cpio archive and concatenate the original initrd +# on top: +find kernel | cpio -H newc --create > /boot/instrumented_initrd +cat /boot/initrd >>/boot/instrumented_initrd +# reboot with increased acpi debug level, e.g. boot params: +acpi.debug_level=0x2 acpi.debug_layer=0xFFFFFFFF +# and check your syslog: +[ 1.268089] ACPI: PCI Interrupt Routing Table [\_SB_.PCI0._PRT] +[ 1.272091] [ACPI Debug] String [0x0B] "HELLO WORLD" + +iasl is able to disassemble and recompile quite a lot different, +also static ACPI tables. + + +4) Where to retrieve userspace tools +------------------------------------ + +iasl and acpixtract are part of Intel's ACPICA project: +http://acpica.org/ +and should be packaged by distributions (for example in the acpica package +on SUSE). + +acpidump can be found in Len Browns pmtools: +ftp://kernel.org/pub/linux/kernel/people/lenb/acpi/utils/pmtools/acpidump +This tool is also part of the acpica package on SUSE. +Alternatively, used ACPI tables can be retrieved via sysfs in latest kernels: +/sys/firmware/acpi/tables -- cgit v1.2.3-59-g8ed1b From 87b526d349b04c31d7b3a40b434eb3f825d22305 Mon Sep 17 00:00:00 2001 From: Andy Lutomirski Date: Mon, 1 Oct 2012 11:40:45 -0700 Subject: seccomp: Make syscall skipping and nr changes more consistent This fixes two issues that could cause incompatibility between kernel versions: - If a tracer uses SECCOMP_RET_TRACE to select a syscall number higher than the largest known syscall, emulate the unknown vsyscall by returning -ENOSYS. (This is unlikely to make a noticeable difference on x86-64 due to the way the system call entry works.) - On x86-64 with vsyscall=emulate, skipped vsyscalls were buggy. This updates the documentation accordingly. Signed-off-by: Andy Lutomirski Acked-by: Will Drewry Signed-off-by: James Morris --- Documentation/prctl/seccomp_filter.txt | 74 ++++++++++++++++++++-- arch/x86/kernel/vsyscall_64.c | 110 ++++++++++++++++++--------------- kernel/seccomp.c | 13 +++- 3 files changed, 137 insertions(+), 60 deletions(-) (limited to 'Documentation') diff --git a/Documentation/prctl/seccomp_filter.txt b/Documentation/prctl/seccomp_filter.txt index 597c3c581375..1e469ef75778 100644 --- a/Documentation/prctl/seccomp_filter.txt +++ b/Documentation/prctl/seccomp_filter.txt @@ -95,12 +95,15 @@ SECCOMP_RET_KILL: SECCOMP_RET_TRAP: Results in the kernel sending a SIGSYS signal to the triggering - task without executing the system call. The kernel will - rollback the register state to just before the system call - entry such that a signal handler in the task will be able to - inspect the ucontext_t->uc_mcontext registers and emulate - system call success or failure upon return from the signal - handler. + task without executing the system call. siginfo->si_call_addr + will show the address of the system call instruction, and + siginfo->si_syscall and siginfo->si_arch will indicate which + syscall was attempted. The program counter will be as though + the syscall happened (i.e. it will not point to the syscall + instruction). The return value register will contain an arch- + dependent value -- if resuming execution, set it to something + sensible. (The architecture dependency is because replacing + it with -ENOSYS could overwrite some useful information.) The SECCOMP_RET_DATA portion of the return value will be passed as si_errno. @@ -123,6 +126,18 @@ SECCOMP_RET_TRACE: the BPF program return value will be available to the tracer via PTRACE_GETEVENTMSG. + The tracer can skip the system call by changing the syscall number + to -1. Alternatively, the tracer can change the system call + requested by changing the system call to a valid syscall number. If + the tracer asks to skip the system call, then the system call will + appear to return the value that the tracer puts in the return value + register. + + The seccomp check will not be run again after the tracer is + notified. (This means that seccomp-based sandboxes MUST NOT + allow use of ptrace, even of other sandboxed processes, without + extreme care; ptracers can use this mechanism to escape.) + SECCOMP_RET_ALLOW: Results in the system call being executed. @@ -161,3 +176,50 @@ architecture supports both ptrace_event and seccomp, it will be able to support seccomp filter with minor fixup: SIGSYS support and seccomp return value checking. Then it must just add CONFIG_HAVE_ARCH_SECCOMP_FILTER to its arch-specific Kconfig. + + + +Caveats +------- + +The vDSO can cause some system calls to run entirely in userspace, +leading to surprises when you run programs on different machines that +fall back to real syscalls. To minimize these surprises on x86, make +sure you test with +/sys/devices/system/clocksource/clocksource0/current_clocksource set to +something like acpi_pm. + +On x86-64, vsyscall emulation is enabled by default. (vsyscalls are +legacy variants on vDSO calls.) Currently, emulated vsyscalls will honor seccomp, with a few oddities: + +- A return value of SECCOMP_RET_TRAP will set a si_call_addr pointing to + the vsyscall entry for the given call and not the address after the + 'syscall' instruction. Any code which wants to restart the call + should be aware that (a) a ret instruction has been emulated and (b) + trying to resume the syscall will again trigger the standard vsyscall + emulation security checks, making resuming the syscall mostly + pointless. + +- A return value of SECCOMP_RET_TRACE will signal the tracer as usual, + but the syscall may not be changed to another system call using the + orig_rax register. It may only be changed to -1 order to skip the + currently emulated call. Any other change MAY terminate the process. + The rip value seen by the tracer will be the syscall entry address; + this is different from normal behavior. The tracer MUST NOT modify + rip or rsp. (Do not rely on other changes terminating the process. + They might work. For example, on some kernels, choosing a syscall + that only exists in future kernels will be correctly emulated (by + returning -ENOSYS). + +To detect this quirky behavior, check for addr & ~0x0C00 == +0xFFFFFFFFFF600000. (For SECCOMP_RET_TRACE, use rip. For +SECCOMP_RET_TRAP, use siginfo->si_call_addr.) Do not check any other +condition: future kernels may improve vsyscall emulation and current +kernels in vsyscall=native mode will behave differently, but the +instructions at 0xF...F600{0,4,8,C}00 will not be system calls in these +cases. + +Note that modern systems are unlikely to use vsyscalls at all -- they +are a legacy feature and they are considerably slower than standard +syscalls. New code will use the vDSO, and vDSO-issued system calls +are indistinguishable from normal system calls. diff --git a/arch/x86/kernel/vsyscall_64.c b/arch/x86/kernel/vsyscall_64.c index 8d141b309046..b2e58a248b3b 100644 --- a/arch/x86/kernel/vsyscall_64.c +++ b/arch/x86/kernel/vsyscall_64.c @@ -136,19 +136,6 @@ static int addr_to_vsyscall_nr(unsigned long addr) return nr; } -#ifdef CONFIG_SECCOMP -static int vsyscall_seccomp(struct task_struct *tsk, int syscall_nr) -{ - if (!seccomp_mode(&tsk->seccomp)) - return 0; - task_pt_regs(tsk)->orig_ax = syscall_nr; - task_pt_regs(tsk)->ax = syscall_nr; - return __secure_computing(syscall_nr); -} -#else -#define vsyscall_seccomp(_tsk, _nr) 0 -#endif - static bool write_ok_or_segv(unsigned long ptr, size_t size) { /* @@ -181,10 +168,9 @@ bool emulate_vsyscall(struct pt_regs *regs, unsigned long address) { struct task_struct *tsk; unsigned long caller; - int vsyscall_nr; + int vsyscall_nr, syscall_nr, tmp; int prev_sig_on_uaccess_error; long ret; - int skip; /* * No point in checking CS -- the only way to get here is a user mode @@ -216,56 +202,84 @@ bool emulate_vsyscall(struct pt_regs *regs, unsigned long address) } tsk = current; - /* - * With a real vsyscall, page faults cause SIGSEGV. We want to - * preserve that behavior to make writing exploits harder. - */ - prev_sig_on_uaccess_error = current_thread_info()->sig_on_uaccess_error; - current_thread_info()->sig_on_uaccess_error = 1; /* + * Check for access_ok violations and find the syscall nr. + * * NULL is a valid user pointer (in the access_ok sense) on 32-bit and * 64-bit, so we don't need to special-case it here. For all the * vsyscalls, NULL means "don't write anything" not "write it at * address 0". */ - ret = -EFAULT; - skip = 0; switch (vsyscall_nr) { case 0: - skip = vsyscall_seccomp(tsk, __NR_gettimeofday); - if (skip) - break; - if (!write_ok_or_segv(regs->di, sizeof(struct timeval)) || - !write_ok_or_segv(regs->si, sizeof(struct timezone))) - break; + !write_ok_or_segv(regs->si, sizeof(struct timezone))) { + ret = -EFAULT; + goto check_fault; + } + + syscall_nr = __NR_gettimeofday; + break; + + case 1: + if (!write_ok_or_segv(regs->di, sizeof(time_t))) { + ret = -EFAULT; + goto check_fault; + } + + syscall_nr = __NR_time; + break; + + case 2: + if (!write_ok_or_segv(regs->di, sizeof(unsigned)) || + !write_ok_or_segv(regs->si, sizeof(unsigned))) { + ret = -EFAULT; + goto check_fault; + } + + syscall_nr = __NR_getcpu; + break; + } + + /* + * Handle seccomp. regs->ip must be the original value. + * See seccomp_send_sigsys and Documentation/prctl/seccomp_filter.txt. + * + * We could optimize the seccomp disabled case, but performance + * here doesn't matter. + */ + regs->orig_ax = syscall_nr; + regs->ax = -ENOSYS; + tmp = secure_computing(syscall_nr); + if ((!tmp && regs->orig_ax != syscall_nr) || regs->ip != address) { + warn_bad_vsyscall(KERN_DEBUG, regs, + "seccomp tried to change syscall nr or ip"); + do_exit(SIGSYS); + } + if (tmp) + goto do_ret; /* skip requested */ + /* + * With a real vsyscall, page faults cause SIGSEGV. We want to + * preserve that behavior to make writing exploits harder. + */ + prev_sig_on_uaccess_error = current_thread_info()->sig_on_uaccess_error; + current_thread_info()->sig_on_uaccess_error = 1; + + ret = -EFAULT; + switch (vsyscall_nr) { + case 0: ret = sys_gettimeofday( (struct timeval __user *)regs->di, (struct timezone __user *)regs->si); break; case 1: - skip = vsyscall_seccomp(tsk, __NR_time); - if (skip) - break; - - if (!write_ok_or_segv(regs->di, sizeof(time_t))) - break; - ret = sys_time((time_t __user *)regs->di); break; case 2: - skip = vsyscall_seccomp(tsk, __NR_getcpu); - if (skip) - break; - - if (!write_ok_or_segv(regs->di, sizeof(unsigned)) || - !write_ok_or_segv(regs->si, sizeof(unsigned))) - break; - ret = sys_getcpu((unsigned __user *)regs->di, (unsigned __user *)regs->si, NULL); @@ -274,12 +288,7 @@ bool emulate_vsyscall(struct pt_regs *regs, unsigned long address) current_thread_info()->sig_on_uaccess_error = prev_sig_on_uaccess_error; - if (skip) { - if ((long)regs->ax <= 0L) /* seccomp errno emulation */ - goto do_ret; - goto done; /* seccomp trace/trap */ - } - +check_fault: if (ret == -EFAULT) { /* Bad news -- userspace fed a bad pointer to a vsyscall. */ warn_bad_vsyscall(KERN_INFO, regs, @@ -302,7 +311,6 @@ do_ret: /* Emulate a ret instruction. */ regs->ip = caller; regs->sp += 8; -done: return true; sigsegv: diff --git a/kernel/seccomp.c b/kernel/seccomp.c index ee376beedaf9..5af44b593770 100644 --- a/kernel/seccomp.c +++ b/kernel/seccomp.c @@ -396,25 +396,29 @@ int __secure_computing(int this_syscall) #ifdef CONFIG_SECCOMP_FILTER case SECCOMP_MODE_FILTER: { int data; + struct pt_regs *regs = task_pt_regs(current); ret = seccomp_run_filters(this_syscall); data = ret & SECCOMP_RET_DATA; ret &= SECCOMP_RET_ACTION; switch (ret) { case SECCOMP_RET_ERRNO: /* Set the low-order 16-bits as a errno. */ - syscall_set_return_value(current, task_pt_regs(current), + syscall_set_return_value(current, regs, -data, 0); goto skip; case SECCOMP_RET_TRAP: /* Show the handler the original registers. */ - syscall_rollback(current, task_pt_regs(current)); + syscall_rollback(current, regs); /* Let the filter pass back 16 bits of data. */ seccomp_send_sigsys(this_syscall, data); goto skip; case SECCOMP_RET_TRACE: /* Skip these calls if there is no tracer. */ - if (!ptrace_event_enabled(current, PTRACE_EVENT_SECCOMP)) + if (!ptrace_event_enabled(current, PTRACE_EVENT_SECCOMP)) { + syscall_set_return_value(current, regs, + -ENOSYS, 0); goto skip; + } /* Allow the BPF to provide the event message */ ptrace_event(PTRACE_EVENT_SECCOMP, data); /* @@ -425,6 +429,9 @@ int __secure_computing(int this_syscall) */ if (fatal_signal_pending(current)) break; + if (syscall_get_nr(current, regs) < 0) + goto skip; /* Explicit request to skip. */ + return 0; case SECCOMP_RET_ALLOW: return 0; -- cgit v1.2.3-59-g8ed1b From f8aa23a55f813c9bddec2a6176e0e67274e6e7c1 Mon Sep 17 00:00:00 2001 From: David Howells Date: Tue, 2 Oct 2012 19:24:56 +0100 Subject: KEYS: Use keyring_alloc() to create special keyrings Use keyring_alloc() to create special keyrings now that it has a permissions parameter rather than using key_alloc() + key_instantiate_and_link(). Also document and export keyring_alloc() so that modules can use it too. Signed-off-by: David Howells --- Documentation/security/keys.txt | 17 +++++++++++++++++ fs/cifs/cifsacl.c | 12 ++++-------- fs/nfs/idmap.c | 12 ++++-------- net/dns_resolver/dns_key.c | 13 +++++-------- security/keys/keyring.c | 1 + 5 files changed, 31 insertions(+), 24 deletions(-) (limited to 'Documentation') diff --git a/Documentation/security/keys.txt b/Documentation/security/keys.txt index aa0dbd74b71b..a4f9125c0333 100644 --- a/Documentation/security/keys.txt +++ b/Documentation/security/keys.txt @@ -990,6 +990,23 @@ payload contents" for more information. reference pointer if successful. +(*) A keyring can be created by: + + struct key *keyring_alloc(const char *description, uid_t uid, gid_t gid, + const struct cred *cred, + key_perm_t perm, + unsigned long flags, + struct key *dest); + + This creates a keyring with the given attributes and returns it. If dest + is not NULL, the new keyring will be linked into the keyring to which it + points. No permission checks are made upon the destination keyring. + + Error EDQUOT can be returned if the keyring would overload the quota (pass + KEY_ALLOC_NOT_IN_QUOTA in flags if the keyring shouldn't be accounted + towards the user's quota). Error ENOMEM can also be returned. + + (*) To check the validity of a key, this function can be called: int validate_key(struct key *key); diff --git a/fs/cifs/cifsacl.c b/fs/cifs/cifsacl.c index 05f4dc263a23..a8a753c8fcd5 100644 --- a/fs/cifs/cifsacl.c +++ b/fs/cifs/cifsacl.c @@ -537,19 +537,15 @@ init_cifs_idmap(void) if (!cred) return -ENOMEM; - keyring = key_alloc(&key_type_keyring, ".cifs_idmap", 0, 0, cred, - (KEY_POS_ALL & ~KEY_POS_SETATTR) | - KEY_USR_VIEW | KEY_USR_READ, - KEY_ALLOC_NOT_IN_QUOTA); + keyring = keyring_alloc(".cifs_idmap", 0, 0, cred, + (KEY_POS_ALL & ~KEY_POS_SETATTR) | + KEY_USR_VIEW | KEY_USR_READ, + KEY_ALLOC_NOT_IN_QUOTA, NULL); if (IS_ERR(keyring)) { ret = PTR_ERR(keyring); goto failed_put_cred; } - ret = key_instantiate_and_link(keyring, NULL, 0, NULL, NULL); - if (ret < 0) - goto failed_put_key; - ret = register_key_type(&cifs_idmap_key_type); if (ret < 0) goto failed_put_key; diff --git a/fs/nfs/idmap.c b/fs/nfs/idmap.c index a850079467d8..957134b4c0fd 100644 --- a/fs/nfs/idmap.c +++ b/fs/nfs/idmap.c @@ -192,19 +192,15 @@ static int nfs_idmap_init_keyring(void) if (!cred) return -ENOMEM; - keyring = key_alloc(&key_type_keyring, ".id_resolver", 0, 0, cred, - (KEY_POS_ALL & ~KEY_POS_SETATTR) | - KEY_USR_VIEW | KEY_USR_READ, - KEY_ALLOC_NOT_IN_QUOTA); + keyring = keyring_alloc(".id_resolver", 0, 0, cred, + (KEY_POS_ALL & ~KEY_POS_SETATTR) | + KEY_USR_VIEW | KEY_USR_READ, + KEY_ALLOC_NOT_IN_QUOTA, NULL); if (IS_ERR(keyring)) { ret = PTR_ERR(keyring); goto failed_put_cred; } - ret = key_instantiate_and_link(keyring, NULL, 0, NULL, NULL); - if (ret < 0) - goto failed_put_key; - ret = register_key_type(&key_type_id_resolver); if (ret < 0) goto failed_put_key; diff --git a/net/dns_resolver/dns_key.c b/net/dns_resolver/dns_key.c index d9507dd05818..f2c379d835e3 100644 --- a/net/dns_resolver/dns_key.c +++ b/net/dns_resolver/dns_key.c @@ -259,19 +259,15 @@ static int __init init_dns_resolver(void) if (!cred) return -ENOMEM; - keyring = key_alloc(&key_type_keyring, ".dns_resolver", 0, 0, cred, - (KEY_POS_ALL & ~KEY_POS_SETATTR) | - KEY_USR_VIEW | KEY_USR_READ, - KEY_ALLOC_NOT_IN_QUOTA); + keyring = keyring_alloc(".dns_resolver", 0, 0, cred, + (KEY_POS_ALL & ~KEY_POS_SETATTR) | + KEY_USR_VIEW | KEY_USR_READ, + KEY_ALLOC_NOT_IN_QUOTA, NULL); if (IS_ERR(keyring)) { ret = PTR_ERR(keyring); goto failed_put_cred; } - ret = key_instantiate_and_link(keyring, NULL, 0, NULL, NULL); - if (ret < 0) - goto failed_put_key; - ret = register_key_type(&key_type_dns_resolver); if (ret < 0) goto failed_put_key; @@ -303,3 +299,4 @@ static void __exit exit_dns_resolver(void) module_init(init_dns_resolver) module_exit(exit_dns_resolver) MODULE_LICENSE("GPL"); + diff --git a/security/keys/keyring.c b/security/keys/keyring.c index cf704a92083f..8c25558da14e 100644 --- a/security/keys/keyring.c +++ b/security/keys/keyring.c @@ -275,6 +275,7 @@ struct key *keyring_alloc(const char *description, uid_t uid, gid_t gid, return keyring; } +EXPORT_SYMBOL(keyring_alloc); /** * keyring_search_aux - Search a keyring tree for a key matching some criteria -- cgit v1.2.3-59-g8ed1b From 9202e07636f0c4858ba6c30773a3f160b2b5659a Mon Sep 17 00:00:00 2001 From: Liu Yu-B13201 Date: Tue, 3 Jul 2012 05:48:52 +0000 Subject: KVM: PPC: Add support for ePAPR idle hcall in host kernel And add a new flag definition in kvm_ppc_pvinfo to indicate whether the host supports the EV_IDLE hcall. Signed-off-by: Liu Yu [stuart.yoder@freescale.com: cleanup,fixes for conditions allowing idle] Signed-off-by: Stuart Yoder [agraf: fix typo] Signed-off-by: Alexander Graf --- Documentation/virtual/kvm/api.txt | 7 +++++-- arch/powerpc/include/asm/Kbuild | 1 + arch/powerpc/kvm/powerpc.c | 10 ++++++++-- include/linux/kvm.h | 2 ++ 4 files changed, 16 insertions(+), 4 deletions(-) (limited to 'Documentation') diff --git a/Documentation/virtual/kvm/api.txt b/Documentation/virtual/kvm/api.txt index f6ec3a92e621..170afb1fbc2e 100644 --- a/Documentation/virtual/kvm/api.txt +++ b/Documentation/virtual/kvm/api.txt @@ -1194,12 +1194,15 @@ struct kvm_ppc_pvinfo { This ioctl fetches PV specific information that need to be passed to the guest using the device tree or other means from vm context. -For now the only implemented piece of information distributed here is an array -of 4 instructions that make up a hypercall. +The hcall array defines 4 instructions that make up a hypercall. If any additional field gets added to this structure later on, a bit for that additional piece of information will be set in the flags bitmap. +The flags bitmap is defined as: + + /* the host supports the ePAPR idle hcall + #define KVM_PPC_PVINFO_FLAGS_EV_IDLE (1<<0) 4.48 KVM_ASSIGN_PCI_DEVICE diff --git a/arch/powerpc/include/asm/Kbuild b/arch/powerpc/include/asm/Kbuild index 7e313f1ed183..13d6b7bf3b69 100644 --- a/arch/powerpc/include/asm/Kbuild +++ b/arch/powerpc/include/asm/Kbuild @@ -34,5 +34,6 @@ header-y += termios.h header-y += types.h header-y += ucontext.h header-y += unistd.h +header-y += epapr_hcalls.h generic-y += rwsem.h diff --git a/arch/powerpc/kvm/powerpc.c b/arch/powerpc/kvm/powerpc.c index a478e662b2bc..dbf56e173c25 100644 --- a/arch/powerpc/kvm/powerpc.c +++ b/arch/powerpc/kvm/powerpc.c @@ -38,8 +38,7 @@ int kvm_arch_vcpu_runnable(struct kvm_vcpu *v) { - return !(v->arch.shared->msr & MSR_WE) || - !!(v->arch.pending_exceptions) || + return !!(v->arch.pending_exceptions) || v->requests; } @@ -86,6 +85,11 @@ int kvmppc_kvm_pv(struct kvm_vcpu *vcpu) /* Second return value is in r4 */ break; + case EV_HCALL_TOKEN(EV_IDLE): + r = EV_SUCCESS; + kvm_vcpu_block(vcpu); + clear_bit(KVM_REQ_UNHALT, &vcpu->requests); + break; default: r = EV_UNIMPLEMENTED; break; @@ -779,6 +783,8 @@ static int kvm_vm_ioctl_get_pvinfo(struct kvm_ppc_pvinfo *pvinfo) pvinfo->hcall[3] = inst_nop; #endif + pvinfo->flags = KVM_PPC_PVINFO_FLAGS_EV_IDLE; + return 0; } diff --git a/include/linux/kvm.h b/include/linux/kvm.h index 0a6d6ba44c85..4cb3761bebae 100644 --- a/include/linux/kvm.h +++ b/include/linux/kvm.h @@ -477,6 +477,8 @@ struct kvm_ppc_smmu_info { struct kvm_ppc_one_seg_page_size sps[KVM_PPC_PAGE_SIZES_MAX_SZ]; }; +#define KVM_PPC_PVINFO_FLAGS_EV_IDLE (1<<0) + #define KVMIO 0xAE /* machine type bits, to be used as argument to KVM_CREATE_VM */ -- cgit v1.2.3-59-g8ed1b From 2e2327023fe090d68aacad395178298645eaffea Mon Sep 17 00:00:00 2001 From: Bharat Bhushan Date: Wed, 15 Aug 2012 17:37:13 +0000 Subject: Document IACx/DACx registers access using ONE_REG API Patch to access the debug registers (IACx/DACx) using ONE_REG api was sent earlier. But that missed the respective documentation. Also corrected the index number referencing in section 4.69 Signed-off-by: Bharat Bhushan Signed-off-by: Alexander Graf --- Documentation/virtual/kvm/api.txt | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) (limited to 'Documentation') diff --git a/Documentation/virtual/kvm/api.txt b/Documentation/virtual/kvm/api.txt index 170afb1fbc2e..c84739e2c0f3 100644 --- a/Documentation/virtual/kvm/api.txt +++ b/Documentation/virtual/kvm/api.txt @@ -1734,7 +1734,12 @@ registers, find a list below: Arch | Register | Width (bits) | | PPC | KVM_REG_PPC_HIOR | 64 - + PPC | KVM_REG_PPC_IAC1 | 64 + PPC | KVM_REG_PPC_IAC2 | 64 + PPC | KVM_REG_PPC_IAC3 | 64 + PPC | KVM_REG_PPC_IAC4 | 64 + PPC | KVM_REG_PPC_DAC1 | 64 + PPC | KVM_REG_PPC_DAC2 | 64 4.69 KVM_GET_ONE_REG @@ -1750,7 +1755,7 @@ kvm_one_reg struct passed in. On success, the register value can be found at the memory location pointed to by "addr". The list of registers accessible using this interface is identical to the -list in 4.64. +list in 4.68. 4.70 KVM_KVMCLOCK_CTRL -- cgit v1.2.3-59-g8ed1b From a136a8bdc02fc14625ac45ee846cc646fc46597e Mon Sep 17 00:00:00 2001 From: Paul Mackerras Date: Tue, 25 Sep 2012 20:31:56 +0000 Subject: KVM: PPC: Book3S: Get/set guest SPRs using the GET/SET_ONE_REG interface This enables userspace to get and set various SPRs (special-purpose registers) using the KVM_[GS]ET_ONE_REG ioctls. With this, userspace can get and set all the SPRs that are part of the guest state, either through the KVM_[GS]ET_REGS ioctls, the KVM_[GS]ET_SREGS ioctls, or the KVM_[GS]ET_ONE_REG ioctls. The SPRs that are added here are: - DABR: Data address breakpoint register - DSCR: Data stream control register - PURR: Processor utilization of resources register - SPURR: Scaled PURR - DAR: Data address register - DSISR: Data storage interrupt status register - AMR: Authority mask register - UAMOR: User authority mask override register - MMCR0, MMCR1, MMCRA: Performance monitor unit control registers - PMC1..PMC8: Performance monitor unit counter registers In order to reduce code duplication between PR and HV KVM code, this moves the kvm_vcpu_ioctl_[gs]et_one_reg functions into book3s.c and centralizes the copying between user and kernel space there. The registers that are handled differently between PR and HV, and those that exist only in one flavor, are handled in kvmppc_[gs]et_one_reg() functions that are specific to each flavor. Signed-off-by: Paul Mackerras [agraf: minimal style fixes] Signed-off-by: Alexander Graf --- Documentation/virtual/kvm/api.txt | 19 ++++++++++ arch/powerpc/include/asm/kvm.h | 21 +++++++++++ arch/powerpc/include/asm/kvm_ppc.h | 32 ++++++++++++++++ arch/powerpc/kvm/book3s.c | 68 ++++++++++++++++++++++++++++++++++ arch/powerpc/kvm/book3s_hv.c | 76 ++++++++++++++++++++++++++++++++------ arch/powerpc/kvm/book3s_pr.c | 23 ++++++------ 6 files changed, 215 insertions(+), 24 deletions(-) (limited to 'Documentation') diff --git a/Documentation/virtual/kvm/api.txt b/Documentation/virtual/kvm/api.txt index c84739e2c0f3..af9b7a06bcae 100644 --- a/Documentation/virtual/kvm/api.txt +++ b/Documentation/virtual/kvm/api.txt @@ -1740,6 +1740,25 @@ registers, find a list below: PPC | KVM_REG_PPC_IAC4 | 64 PPC | KVM_REG_PPC_DAC1 | 64 PPC | KVM_REG_PPC_DAC2 | 64 + PPC | KVM_REG_PPC_DABR | 64 + PPC | KVM_REG_PPC_DSCR | 64 + PPC | KVM_REG_PPC_PURR | 64 + PPC | KVM_REG_PPC_SPURR | 64 + PPC | KVM_REG_PPC_DAR | 64 + PPC | KVM_REG_PPC_DSISR | 32 + PPC | KVM_REG_PPC_AMR | 64 + PPC | KVM_REG_PPC_UAMOR | 64 + PPC | KVM_REG_PPC_MMCR0 | 64 + PPC | KVM_REG_PPC_MMCR1 | 64 + PPC | KVM_REG_PPC_MMCRA | 64 + PPC | KVM_REG_PPC_PMC1 | 32 + PPC | KVM_REG_PPC_PMC2 | 32 + PPC | KVM_REG_PPC_PMC3 | 32 + PPC | KVM_REG_PPC_PMC4 | 32 + PPC | KVM_REG_PPC_PMC5 | 32 + PPC | KVM_REG_PPC_PMC6 | 32 + PPC | KVM_REG_PPC_PMC7 | 32 + PPC | KVM_REG_PPC_PMC8 | 32 4.69 KVM_GET_ONE_REG diff --git a/arch/powerpc/include/asm/kvm.h b/arch/powerpc/include/asm/kvm.h index 3c14202a3c84..9557576a5325 100644 --- a/arch/powerpc/include/asm/kvm.h +++ b/arch/powerpc/include/asm/kvm.h @@ -338,5 +338,26 @@ struct kvm_book3e_206_tlb_params { #define KVM_REG_PPC_IAC4 (KVM_REG_PPC | KVM_REG_SIZE_U64 | 0x5) #define KVM_REG_PPC_DAC1 (KVM_REG_PPC | KVM_REG_SIZE_U64 | 0x6) #define KVM_REG_PPC_DAC2 (KVM_REG_PPC | KVM_REG_SIZE_U64 | 0x7) +#define KVM_REG_PPC_DABR (KVM_REG_PPC | KVM_REG_SIZE_U64 | 0x8) +#define KVM_REG_PPC_DSCR (KVM_REG_PPC | KVM_REG_SIZE_U64 | 0x9) +#define KVM_REG_PPC_PURR (KVM_REG_PPC | KVM_REG_SIZE_U64 | 0xa) +#define KVM_REG_PPC_SPURR (KVM_REG_PPC | KVM_REG_SIZE_U64 | 0xb) +#define KVM_REG_PPC_DAR (KVM_REG_PPC | KVM_REG_SIZE_U64 | 0xc) +#define KVM_REG_PPC_DSISR (KVM_REG_PPC | KVM_REG_SIZE_U32 | 0xd) +#define KVM_REG_PPC_AMR (KVM_REG_PPC | KVM_REG_SIZE_U64 | 0xe) +#define KVM_REG_PPC_UAMOR (KVM_REG_PPC | KVM_REG_SIZE_U64 | 0xf) + +#define KVM_REG_PPC_MMCR0 (KVM_REG_PPC | KVM_REG_SIZE_U64 | 0x10) +#define KVM_REG_PPC_MMCR1 (KVM_REG_PPC | KVM_REG_SIZE_U64 | 0x11) +#define KVM_REG_PPC_MMCRA (KVM_REG_PPC | KVM_REG_SIZE_U64 | 0x12) + +#define KVM_REG_PPC_PMC1 (KVM_REG_PPC | KVM_REG_SIZE_U32 | 0x18) +#define KVM_REG_PPC_PMC2 (KVM_REG_PPC | KVM_REG_SIZE_U32 | 0x19) +#define KVM_REG_PPC_PMC3 (KVM_REG_PPC | KVM_REG_SIZE_U32 | 0x1a) +#define KVM_REG_PPC_PMC4 (KVM_REG_PPC | KVM_REG_SIZE_U32 | 0x1b) +#define KVM_REG_PPC_PMC5 (KVM_REG_PPC | KVM_REG_SIZE_U32 | 0x1c) +#define KVM_REG_PPC_PMC6 (KVM_REG_PPC | KVM_REG_SIZE_U32 | 0x1d) +#define KVM_REG_PPC_PMC7 (KVM_REG_PPC | KVM_REG_SIZE_U32 | 0x1e) +#define KVM_REG_PPC_PMC8 (KVM_REG_PPC | KVM_REG_SIZE_U32 | 0x1f) #endif /* __LINUX_KVM_POWERPC_H */ diff --git a/arch/powerpc/include/asm/kvm_ppc.h b/arch/powerpc/include/asm/kvm_ppc.h index 3fb980d293e5..709f0ddae1f1 100644 --- a/arch/powerpc/include/asm/kvm_ppc.h +++ b/arch/powerpc/include/asm/kvm_ppc.h @@ -28,6 +28,7 @@ #include #include #include +#include #ifdef CONFIG_PPC_BOOK3S #include #else @@ -196,6 +197,35 @@ static inline u32 kvmppc_set_field(u64 inst, int msb, int lsb, int value) return r; } +union kvmppc_one_reg { + u32 wval; + u64 dval; +}; + +#define one_reg_size(id) \ + (1ul << (((id) & KVM_REG_SIZE_MASK) >> KVM_REG_SIZE_SHIFT)) + +#define get_reg_val(id, reg) ({ \ + union kvmppc_one_reg __u; \ + switch (one_reg_size(id)) { \ + case 4: __u.wval = (reg); break; \ + case 8: __u.dval = (reg); break; \ + default: BUG(); \ + } \ + __u; \ +}) + + +#define set_reg_val(id, val) ({ \ + u64 __v; \ + switch (one_reg_size(id)) { \ + case 4: __v = (val).wval; break; \ + case 8: __v = (val).dval; break; \ + default: BUG(); \ + } \ + __v; \ +}) + void kvmppc_core_get_sregs(struct kvm_vcpu *vcpu, struct kvm_sregs *sregs); int kvmppc_core_set_sregs(struct kvm_vcpu *vcpu, struct kvm_sregs *sregs); @@ -204,6 +234,8 @@ int kvmppc_set_sregs_ivor(struct kvm_vcpu *vcpu, struct kvm_sregs *sregs); int kvm_vcpu_ioctl_get_one_reg(struct kvm_vcpu *vcpu, struct kvm_one_reg *reg); int kvm_vcpu_ioctl_set_one_reg(struct kvm_vcpu *vcpu, struct kvm_one_reg *reg); +int kvmppc_get_one_reg(struct kvm_vcpu *vcpu, u64 id, union kvmppc_one_reg *); +int kvmppc_set_one_reg(struct kvm_vcpu *vcpu, u64 id, union kvmppc_one_reg *); void kvmppc_set_pid(struct kvm_vcpu *vcpu, u32 pid); diff --git a/arch/powerpc/kvm/book3s.c b/arch/powerpc/kvm/book3s.c index e94666566fa9..a5af28fc3a8f 100644 --- a/arch/powerpc/kvm/book3s.c +++ b/arch/powerpc/kvm/book3s.c @@ -485,6 +485,74 @@ int kvm_arch_vcpu_ioctl_set_fpu(struct kvm_vcpu *vcpu, struct kvm_fpu *fpu) return -ENOTSUPP; } +int kvm_vcpu_ioctl_get_one_reg(struct kvm_vcpu *vcpu, struct kvm_one_reg *reg) +{ + int r; + union kvmppc_one_reg val; + int size; + + size = one_reg_size(reg->id); + if (size > sizeof(val)) + return -EINVAL; + + r = kvmppc_get_one_reg(vcpu, reg->id, &val); + + if (r == -EINVAL) { + r = 0; + switch (reg->id) { + case KVM_REG_PPC_DAR: + val = get_reg_val(reg->id, vcpu->arch.shared->dar); + break; + case KVM_REG_PPC_DSISR: + val = get_reg_val(reg->id, vcpu->arch.shared->dsisr); + break; + default: + r = -EINVAL; + break; + } + } + if (r) + return r; + + if (copy_to_user((char __user *)(unsigned long)reg->addr, &val, size)) + r = -EFAULT; + + return r; +} + +int kvm_vcpu_ioctl_set_one_reg(struct kvm_vcpu *vcpu, struct kvm_one_reg *reg) +{ + int r; + union kvmppc_one_reg val; + int size; + + size = one_reg_size(reg->id); + if (size > sizeof(val)) + return -EINVAL; + + if (copy_from_user(&val, (char __user *)(unsigned long)reg->addr, size)) + return -EFAULT; + + r = kvmppc_set_one_reg(vcpu, reg->id, &val); + + if (r == -EINVAL) { + r = 0; + switch (reg->id) { + case KVM_REG_PPC_DAR: + vcpu->arch.shared->dar = set_reg_val(reg->id, val); + break; + case KVM_REG_PPC_DSISR: + vcpu->arch.shared->dsisr = set_reg_val(reg->id, val); + break; + default: + r = -EINVAL; + break; + } + } + + return r; +} + int kvm_arch_vcpu_ioctl_translate(struct kvm_vcpu *vcpu, struct kvm_translation *tr) { diff --git a/arch/powerpc/kvm/book3s_hv.c b/arch/powerpc/kvm/book3s_hv.c index c9ae3148c981..1cc6b77fa63d 100644 --- a/arch/powerpc/kvm/book3s_hv.c +++ b/arch/powerpc/kvm/book3s_hv.c @@ -544,36 +544,88 @@ int kvm_arch_vcpu_ioctl_set_sregs(struct kvm_vcpu *vcpu, return 0; } -int kvm_vcpu_ioctl_get_one_reg(struct kvm_vcpu *vcpu, struct kvm_one_reg *reg) +int kvmppc_get_one_reg(struct kvm_vcpu *vcpu, u64 id, union kvmppc_one_reg *val) { - int r = -EINVAL; + int r = 0; + long int i; - switch (reg->id) { + switch (id) { case KVM_REG_PPC_HIOR: - r = put_user(0, (u64 __user *)reg->addr); + *val = get_reg_val(id, 0); + break; + case KVM_REG_PPC_DABR: + *val = get_reg_val(id, vcpu->arch.dabr); + break; + case KVM_REG_PPC_DSCR: + *val = get_reg_val(id, vcpu->arch.dscr); + break; + case KVM_REG_PPC_PURR: + *val = get_reg_val(id, vcpu->arch.purr); + break; + case KVM_REG_PPC_SPURR: + *val = get_reg_val(id, vcpu->arch.spurr); + break; + case KVM_REG_PPC_AMR: + *val = get_reg_val(id, vcpu->arch.amr); + break; + case KVM_REG_PPC_UAMOR: + *val = get_reg_val(id, vcpu->arch.uamor); + break; + case KVM_REG_PPC_MMCR0 ... KVM_REG_PPC_MMCRA: + i = id - KVM_REG_PPC_MMCR0; + *val = get_reg_val(id, vcpu->arch.mmcr[i]); + break; + case KVM_REG_PPC_PMC1 ... KVM_REG_PPC_PMC8: + i = id - KVM_REG_PPC_PMC1; + *val = get_reg_val(id, vcpu->arch.pmc[i]); break; default: + r = -EINVAL; break; } return r; } -int kvm_vcpu_ioctl_set_one_reg(struct kvm_vcpu *vcpu, struct kvm_one_reg *reg) +int kvmppc_set_one_reg(struct kvm_vcpu *vcpu, u64 id, union kvmppc_one_reg *val) { - int r = -EINVAL; + int r = 0; + long int i; - switch (reg->id) { + switch (id) { case KVM_REG_PPC_HIOR: - { - u64 hior; /* Only allow this to be set to zero */ - r = get_user(hior, (u64 __user *)reg->addr); - if (!r && (hior != 0)) + if (set_reg_val(id, *val)) r = -EINVAL; break; - } + case KVM_REG_PPC_DABR: + vcpu->arch.dabr = set_reg_val(id, *val); + break; + case KVM_REG_PPC_DSCR: + vcpu->arch.dscr = set_reg_val(id, *val); + break; + case KVM_REG_PPC_PURR: + vcpu->arch.purr = set_reg_val(id, *val); + break; + case KVM_REG_PPC_SPURR: + vcpu->arch.spurr = set_reg_val(id, *val); + break; + case KVM_REG_PPC_AMR: + vcpu->arch.amr = set_reg_val(id, *val); + break; + case KVM_REG_PPC_UAMOR: + vcpu->arch.uamor = set_reg_val(id, *val); + break; + case KVM_REG_PPC_MMCR0 ... KVM_REG_PPC_MMCRA: + i = id - KVM_REG_PPC_MMCR0; + vcpu->arch.mmcr[i] = set_reg_val(id, *val); + break; + case KVM_REG_PPC_PMC1 ... KVM_REG_PPC_PMC8: + i = id - KVM_REG_PPC_PMC1; + vcpu->arch.pmc[i] = set_reg_val(id, *val); + break; default: + r = -EINVAL; break; } diff --git a/arch/powerpc/kvm/book3s_pr.c b/arch/powerpc/kvm/book3s_pr.c index bf3ec5d66d8c..c81109f3a376 100644 --- a/arch/powerpc/kvm/book3s_pr.c +++ b/arch/powerpc/kvm/book3s_pr.c @@ -945,34 +945,33 @@ int kvm_arch_vcpu_ioctl_set_sregs(struct kvm_vcpu *vcpu, return 0; } -int kvm_vcpu_ioctl_get_one_reg(struct kvm_vcpu *vcpu, struct kvm_one_reg *reg) +int kvmppc_get_one_reg(struct kvm_vcpu *vcpu, u64 id, union kvmppc_one_reg *val) { - int r = -EINVAL; + int r = 0; - switch (reg->id) { + switch (id) { case KVM_REG_PPC_HIOR: - r = copy_to_user((u64 __user *)(long)reg->addr, - &to_book3s(vcpu)->hior, sizeof(u64)); + *val = get_reg_val(id, to_book3s(vcpu)->hior); break; default: + r = -EINVAL; break; } return r; } -int kvm_vcpu_ioctl_set_one_reg(struct kvm_vcpu *vcpu, struct kvm_one_reg *reg) +int kvmppc_set_one_reg(struct kvm_vcpu *vcpu, u64 id, union kvmppc_one_reg *val) { - int r = -EINVAL; + int r = 0; - switch (reg->id) { + switch (id) { case KVM_REG_PPC_HIOR: - r = copy_from_user(&to_book3s(vcpu)->hior, - (u64 __user *)(long)reg->addr, sizeof(u64)); - if (!r) - to_book3s(vcpu)->hior_explicit = true; + to_book3s(vcpu)->hior = set_reg_val(id, *val); + to_book3s(vcpu)->hior_explicit = true; break; default: + r = -EINVAL; break; } -- cgit v1.2.3-59-g8ed1b From a8bd19ef4dd49f0eef86a4a8eb43d60f967236b8 Mon Sep 17 00:00:00 2001 From: Paul Mackerras Date: Tue, 25 Sep 2012 20:32:30 +0000 Subject: KVM: PPC: Book3S: Get/set guest FP regs using the GET/SET_ONE_REG interface This enables userspace to get and set all the guest floating-point state using the KVM_[GS]ET_ONE_REG ioctls. The floating-point state includes all of the traditional floating-point registers and the FPSCR (floating point status/control register), all the VMX/Altivec vector registers and the VSCR (vector status/control register), and on POWER7, the vector-scalar registers (note that each FP register is the high-order half of the corresponding VSR). Most of these are implemented in common Book 3S code, except for VSX on POWER7. Because HV and PR differ in how they store the FP and VSX registers on POWER7, the code for these cases is not common. On POWER7, the FP registers are the upper halves of the VSX registers vsr0 - vsr31. PR KVM stores vsr0 - vsr31 in two halves, with the upper halves in the arch.fpr[] array and the lower halves in the arch.vsr[] array, whereas HV KVM on POWER7 stores the whole VSX register in arch.vsr[]. Signed-off-by: Paul Mackerras [agraf: fix whitespace, vsx compilation] Signed-off-by: Alexander Graf --- Documentation/virtual/kvm/api.txt | 11 +++++++++ arch/powerpc/include/asm/kvm.h | 20 ++++++++++++++++ arch/powerpc/include/asm/kvm_ppc.h | 2 ++ arch/powerpc/kvm/book3s.c | 48 ++++++++++++++++++++++++++++++++++++++ arch/powerpc/kvm/book3s_hv.c | 42 +++++++++++++++++++++++++++++++++ arch/powerpc/kvm/book3s_pr.c | 26 +++++++++++++++++++++ 6 files changed, 149 insertions(+) (limited to 'Documentation') diff --git a/Documentation/virtual/kvm/api.txt b/Documentation/virtual/kvm/api.txt index af9b7a06bcae..71dc7e2ec8eb 100644 --- a/Documentation/virtual/kvm/api.txt +++ b/Documentation/virtual/kvm/api.txt @@ -1759,6 +1759,17 @@ registers, find a list below: PPC | KVM_REG_PPC_PMC6 | 32 PPC | KVM_REG_PPC_PMC7 | 32 PPC | KVM_REG_PPC_PMC8 | 32 + PPC | KVM_REG_PPC_FPR0 | 64 + ... + PPC | KVM_REG_PPC_FPR31 | 64 + PPC | KVM_REG_PPC_VR0 | 128 + ... + PPC | KVM_REG_PPC_VR31 | 128 + PPC | KVM_REG_PPC_VSR0 | 128 + ... + PPC | KVM_REG_PPC_VSR31 | 128 + PPC | KVM_REG_PPC_FPSCR | 64 + PPC | KVM_REG_PPC_VSCR | 32 4.69 KVM_GET_ONE_REG diff --git a/arch/powerpc/include/asm/kvm.h b/arch/powerpc/include/asm/kvm.h index 9557576a5325..1466975129c7 100644 --- a/arch/powerpc/include/asm/kvm.h +++ b/arch/powerpc/include/asm/kvm.h @@ -360,4 +360,24 @@ struct kvm_book3e_206_tlb_params { #define KVM_REG_PPC_PMC7 (KVM_REG_PPC | KVM_REG_SIZE_U32 | 0x1e) #define KVM_REG_PPC_PMC8 (KVM_REG_PPC | KVM_REG_SIZE_U32 | 0x1f) +/* 32 floating-point registers */ +#define KVM_REG_PPC_FPR0 (KVM_REG_PPC | KVM_REG_SIZE_U64 | 0x20) +#define KVM_REG_PPC_FPR(n) (KVM_REG_PPC_FPR0 + (n)) +#define KVM_REG_PPC_FPR31 (KVM_REG_PPC | KVM_REG_SIZE_U64 | 0x3f) + +/* 32 VMX/Altivec vector registers */ +#define KVM_REG_PPC_VR0 (KVM_REG_PPC | KVM_REG_SIZE_U128 | 0x40) +#define KVM_REG_PPC_VR(n) (KVM_REG_PPC_VR0 + (n)) +#define KVM_REG_PPC_VR31 (KVM_REG_PPC | KVM_REG_SIZE_U128 | 0x5f) + +/* 32 double-width FP registers for VSX */ +/* High-order halves overlap with FP regs */ +#define KVM_REG_PPC_VSR0 (KVM_REG_PPC | KVM_REG_SIZE_U128 | 0x60) +#define KVM_REG_PPC_VSR(n) (KVM_REG_PPC_VSR0 + (n)) +#define KVM_REG_PPC_VSR31 (KVM_REG_PPC | KVM_REG_SIZE_U128 | 0x7f) + +/* FP and vector status/control registers */ +#define KVM_REG_PPC_FPSCR (KVM_REG_PPC | KVM_REG_SIZE_U64 | 0x80) +#define KVM_REG_PPC_VSCR (KVM_REG_PPC | KVM_REG_SIZE_U32 | 0x81) + #endif /* __LINUX_KVM_POWERPC_H */ diff --git a/arch/powerpc/include/asm/kvm_ppc.h b/arch/powerpc/include/asm/kvm_ppc.h index 709f0ddae1f1..51604a16c8a5 100644 --- a/arch/powerpc/include/asm/kvm_ppc.h +++ b/arch/powerpc/include/asm/kvm_ppc.h @@ -200,6 +200,8 @@ static inline u32 kvmppc_set_field(u64 inst, int msb, int lsb, int value) union kvmppc_one_reg { u32 wval; u64 dval; + vector128 vval; + u64 vsxval[2]; }; #define one_reg_size(id) \ diff --git a/arch/powerpc/kvm/book3s.c b/arch/powerpc/kvm/book3s.c index a5af28fc3a8f..a4b645285240 100644 --- a/arch/powerpc/kvm/book3s.c +++ b/arch/powerpc/kvm/book3s.c @@ -490,6 +490,7 @@ int kvm_vcpu_ioctl_get_one_reg(struct kvm_vcpu *vcpu, struct kvm_one_reg *reg) int r; union kvmppc_one_reg val; int size; + long int i; size = one_reg_size(reg->id); if (size > sizeof(val)) @@ -506,6 +507,29 @@ int kvm_vcpu_ioctl_get_one_reg(struct kvm_vcpu *vcpu, struct kvm_one_reg *reg) case KVM_REG_PPC_DSISR: val = get_reg_val(reg->id, vcpu->arch.shared->dsisr); break; + case KVM_REG_PPC_FPR0 ... KVM_REG_PPC_FPR31: + i = reg->id - KVM_REG_PPC_FPR0; + val = get_reg_val(reg->id, vcpu->arch.fpr[i]); + break; + case KVM_REG_PPC_FPSCR: + val = get_reg_val(reg->id, vcpu->arch.fpscr); + break; +#ifdef CONFIG_ALTIVEC + case KVM_REG_PPC_VR0 ... KVM_REG_PPC_VR31: + if (!cpu_has_feature(CPU_FTR_ALTIVEC)) { + r = -ENXIO; + break; + } + val.vval = vcpu->arch.vr[reg->id - KVM_REG_PPC_VR0]; + break; + case KVM_REG_PPC_VSCR: + if (!cpu_has_feature(CPU_FTR_ALTIVEC)) { + r = -ENXIO; + break; + } + val = get_reg_val(reg->id, vcpu->arch.vscr.u[3]); + break; +#endif /* CONFIG_ALTIVEC */ default: r = -EINVAL; break; @@ -525,6 +549,7 @@ int kvm_vcpu_ioctl_set_one_reg(struct kvm_vcpu *vcpu, struct kvm_one_reg *reg) int r; union kvmppc_one_reg val; int size; + long int i; size = one_reg_size(reg->id); if (size > sizeof(val)) @@ -544,6 +569,29 @@ int kvm_vcpu_ioctl_set_one_reg(struct kvm_vcpu *vcpu, struct kvm_one_reg *reg) case KVM_REG_PPC_DSISR: vcpu->arch.shared->dsisr = set_reg_val(reg->id, val); break; + case KVM_REG_PPC_FPR0 ... KVM_REG_PPC_FPR31: + i = reg->id - KVM_REG_PPC_FPR0; + vcpu->arch.fpr[i] = set_reg_val(reg->id, val); + break; + case KVM_REG_PPC_FPSCR: + vcpu->arch.fpscr = set_reg_val(reg->id, val); + break; +#ifdef CONFIG_ALTIVEC + case KVM_REG_PPC_VR0 ... KVM_REG_PPC_VR31: + if (!cpu_has_feature(CPU_FTR_ALTIVEC)) { + r = -ENXIO; + break; + } + vcpu->arch.vr[reg->id - KVM_REG_PPC_VR0] = val.vval; + break; + case KVM_REG_PPC_VSCR: + if (!cpu_has_feature(CPU_FTR_ALTIVEC)) { + r = -ENXIO; + break; + } + vcpu->arch.vscr.u[3] = set_reg_val(reg->id, val); + break; +#endif /* CONFIG_ALTIVEC */ default: r = -EINVAL; break; diff --git a/arch/powerpc/kvm/book3s_hv.c b/arch/powerpc/kvm/book3s_hv.c index 1cc6b77fa63d..94ec0e30969d 100644 --- a/arch/powerpc/kvm/book3s_hv.c +++ b/arch/powerpc/kvm/book3s_hv.c @@ -579,6 +579,27 @@ int kvmppc_get_one_reg(struct kvm_vcpu *vcpu, u64 id, union kvmppc_one_reg *val) i = id - KVM_REG_PPC_PMC1; *val = get_reg_val(id, vcpu->arch.pmc[i]); break; +#ifdef CONFIG_VSX + case KVM_REG_PPC_FPR0 ... KVM_REG_PPC_FPR31: + if (cpu_has_feature(CPU_FTR_VSX)) { + /* VSX => FP reg i is stored in arch.vsr[2*i] */ + long int i = id - KVM_REG_PPC_FPR0; + *val = get_reg_val(id, vcpu->arch.vsr[2 * i]); + } else { + /* let generic code handle it */ + r = -EINVAL; + } + break; + case KVM_REG_PPC_VSR0 ... KVM_REG_PPC_VSR31: + if (cpu_has_feature(CPU_FTR_VSX)) { + long int i = id - KVM_REG_PPC_VSR0; + val->vsxval[0] = vcpu->arch.vsr[2 * i]; + val->vsxval[1] = vcpu->arch.vsr[2 * i + 1]; + } else { + r = -ENXIO; + } + break; +#endif /* CONFIG_VSX */ default: r = -EINVAL; break; @@ -624,6 +645,27 @@ int kvmppc_set_one_reg(struct kvm_vcpu *vcpu, u64 id, union kvmppc_one_reg *val) i = id - KVM_REG_PPC_PMC1; vcpu->arch.pmc[i] = set_reg_val(id, *val); break; +#ifdef CONFIG_VSX + case KVM_REG_PPC_FPR0 ... KVM_REG_PPC_FPR31: + if (cpu_has_feature(CPU_FTR_VSX)) { + /* VSX => FP reg i is stored in arch.vsr[2*i] */ + long int i = id - KVM_REG_PPC_FPR0; + vcpu->arch.vsr[2 * i] = set_reg_val(id, *val); + } else { + /* let generic code handle it */ + r = -EINVAL; + } + break; + case KVM_REG_PPC_VSR0 ... KVM_REG_PPC_VSR31: + if (cpu_has_feature(CPU_FTR_VSX)) { + long int i = id - KVM_REG_PPC_VSR0; + vcpu->arch.vsr[2 * i] = val->vsxval[0]; + vcpu->arch.vsr[2 * i + 1] = val->vsxval[1]; + } else { + r = -ENXIO; + } + break; +#endif /* CONFIG_VSX */ default: r = -EINVAL; break; diff --git a/arch/powerpc/kvm/book3s_pr.c b/arch/powerpc/kvm/book3s_pr.c index c81109f3a376..b853696b6d8e 100644 --- a/arch/powerpc/kvm/book3s_pr.c +++ b/arch/powerpc/kvm/book3s_pr.c @@ -953,6 +953,19 @@ int kvmppc_get_one_reg(struct kvm_vcpu *vcpu, u64 id, union kvmppc_one_reg *val) case KVM_REG_PPC_HIOR: *val = get_reg_val(id, to_book3s(vcpu)->hior); break; +#ifdef CONFIG_VSX + case KVM_REG_PPC_VSR0 ... KVM_REG_PPC_VSR31: { + long int i = id - KVM_REG_PPC_VSR0; + + if (!cpu_has_feature(CPU_FTR_VSX)) { + r = -ENXIO; + break; + } + val->vsxval[0] = vcpu->arch.fpr[i]; + val->vsxval[1] = vcpu->arch.vsr[i]; + break; + } +#endif /* CONFIG_VSX */ default: r = -EINVAL; break; @@ -970,6 +983,19 @@ int kvmppc_set_one_reg(struct kvm_vcpu *vcpu, u64 id, union kvmppc_one_reg *val) to_book3s(vcpu)->hior = set_reg_val(id, *val); to_book3s(vcpu)->hior_explicit = true; break; +#ifdef CONFIG_VSX + case KVM_REG_PPC_VSR0 ... KVM_REG_PPC_VSR31: { + long int i = id - KVM_REG_PPC_VSR0; + + if (!cpu_has_feature(CPU_FTR_VSX)) { + r = -ENXIO; + break; + } + vcpu->arch.fpr[i] = val->vsxval[0]; + vcpu->arch.vsr[i] = val->vsxval[1]; + break; + } +#endif /* CONFIG_VSX */ default: r = -EINVAL; break; -- cgit v1.2.3-59-g8ed1b From 55b665b0263ae88a776071306ef1eee4b769016b Mon Sep 17 00:00:00 2001 From: Paul Mackerras Date: Tue, 25 Sep 2012 20:33:06 +0000 Subject: KVM: PPC: Book3S HV: Provide a way for userspace to get/set per-vCPU areas The PAPR paravirtualization interface lets guests register three different types of per-vCPU buffer areas in its memory for communication with the hypervisor. These are called virtual processor areas (VPAs). Currently the hypercalls to register and unregister VPAs are handled by KVM in the kernel, and userspace has no way to know about or save and restore these registrations across a migration. This adds "register" codes for these three areas that userspace can use with the KVM_GET/SET_ONE_REG ioctls to see what addresses have been registered, and to register or unregister them. This will be needed for guest hibernation and migration, and is also needed so that userspace can unregister them on reset (otherwise we corrupt guest memory after reboot by writing to the VPAs registered by the previous kernel). The "register" for the VPA is a 64-bit value containing the address, since the length of the VPA is fixed. The "registers" for the SLB shadow buffer and dispatch trace log (DTL) are 128 bits long, consisting of the guest physical address in the high (first) 64 bits and the length in the low 64 bits. This also fixes a bug where we were calling init_vpa unconditionally, leading to an oops when unregistering the VPA. Signed-off-by: Paul Mackerras Signed-off-by: Alexander Graf --- Documentation/virtual/kvm/api.txt | 3 ++ arch/powerpc/include/asm/kvm.h | 6 ++++ arch/powerpc/include/asm/kvm_ppc.h | 4 +++ arch/powerpc/kvm/book3s_hv.c | 64 +++++++++++++++++++++++++++++++++++++- 4 files changed, 76 insertions(+), 1 deletion(-) (limited to 'Documentation') diff --git a/Documentation/virtual/kvm/api.txt b/Documentation/virtual/kvm/api.txt index 71dc7e2ec8eb..e726d76eeebb 100644 --- a/Documentation/virtual/kvm/api.txt +++ b/Documentation/virtual/kvm/api.txt @@ -1770,6 +1770,9 @@ registers, find a list below: PPC | KVM_REG_PPC_VSR31 | 128 PPC | KVM_REG_PPC_FPSCR | 64 PPC | KVM_REG_PPC_VSCR | 32 + PPC | KVM_REG_PPC_VPA_ADDR | 64 + PPC | KVM_REG_PPC_VPA_SLB | 128 + PPC | KVM_REG_PPC_VPA_DTL | 128 4.69 KVM_GET_ONE_REG diff --git a/arch/powerpc/include/asm/kvm.h b/arch/powerpc/include/asm/kvm.h index 1466975129c7..b89ae4db45ce 100644 --- a/arch/powerpc/include/asm/kvm.h +++ b/arch/powerpc/include/asm/kvm.h @@ -380,4 +380,10 @@ struct kvm_book3e_206_tlb_params { #define KVM_REG_PPC_FPSCR (KVM_REG_PPC | KVM_REG_SIZE_U64 | 0x80) #define KVM_REG_PPC_VSCR (KVM_REG_PPC | KVM_REG_SIZE_U32 | 0x81) +/* Virtual processor areas */ +/* For SLB & DTL, address in high (first) half, length in low half */ +#define KVM_REG_PPC_VPA_ADDR (KVM_REG_PPC | KVM_REG_SIZE_U64 | 0x82) +#define KVM_REG_PPC_VPA_SLB (KVM_REG_PPC | KVM_REG_SIZE_U128 | 0x83) +#define KVM_REG_PPC_VPA_DTL (KVM_REG_PPC | KVM_REG_SIZE_U128 | 0x84) + #endif /* __LINUX_KVM_POWERPC_H */ diff --git a/arch/powerpc/include/asm/kvm_ppc.h b/arch/powerpc/include/asm/kvm_ppc.h index 51604a16c8a5..609cca3e9426 100644 --- a/arch/powerpc/include/asm/kvm_ppc.h +++ b/arch/powerpc/include/asm/kvm_ppc.h @@ -202,6 +202,10 @@ union kvmppc_one_reg { u64 dval; vector128 vval; u64 vsxval[2]; + struct { + u64 addr; + u64 length; + } vpaval; }; #define one_reg_size(id) \ diff --git a/arch/powerpc/kvm/book3s_hv.c b/arch/powerpc/kvm/book3s_hv.c index 94ec0e30969d..9a15da76e56b 100644 --- a/arch/powerpc/kvm/book3s_hv.c +++ b/arch/powerpc/kvm/book3s_hv.c @@ -143,6 +143,22 @@ static void init_vpa(struct kvm_vcpu *vcpu, struct lppaca *vpa) vpa->yield_count = 1; } +static int set_vpa(struct kvm_vcpu *vcpu, struct kvmppc_vpa *v, + unsigned long addr, unsigned long len) +{ + /* check address is cacheline aligned */ + if (addr & (L1_CACHE_BYTES - 1)) + return -EINVAL; + spin_lock(&vcpu->arch.vpa_update_lock); + if (v->next_gpa != addr || v->len != len) { + v->next_gpa = addr; + v->len = addr ? len : 0; + v->update_pending = 1; + } + spin_unlock(&vcpu->arch.vpa_update_lock); + return 0; +} + /* Length for a per-processor buffer is passed in at offset 4 in the buffer */ struct reg_vpa { u32 dummy; @@ -321,7 +337,8 @@ static void kvmppc_update_vpas(struct kvm_vcpu *vcpu) spin_lock(&vcpu->arch.vpa_update_lock); if (vcpu->arch.vpa.update_pending) { kvmppc_update_vpa(vcpu, &vcpu->arch.vpa); - init_vpa(vcpu, vcpu->arch.vpa.pinned_addr); + if (vcpu->arch.vpa.pinned_addr) + init_vpa(vcpu, vcpu->arch.vpa.pinned_addr); } if (vcpu->arch.dtl.update_pending) { kvmppc_update_vpa(vcpu, &vcpu->arch.dtl); @@ -600,6 +617,23 @@ int kvmppc_get_one_reg(struct kvm_vcpu *vcpu, u64 id, union kvmppc_one_reg *val) } break; #endif /* CONFIG_VSX */ + case KVM_REG_PPC_VPA_ADDR: + spin_lock(&vcpu->arch.vpa_update_lock); + *val = get_reg_val(id, vcpu->arch.vpa.next_gpa); + spin_unlock(&vcpu->arch.vpa_update_lock); + break; + case KVM_REG_PPC_VPA_SLB: + spin_lock(&vcpu->arch.vpa_update_lock); + val->vpaval.addr = vcpu->arch.slb_shadow.next_gpa; + val->vpaval.length = vcpu->arch.slb_shadow.len; + spin_unlock(&vcpu->arch.vpa_update_lock); + break; + case KVM_REG_PPC_VPA_DTL: + spin_lock(&vcpu->arch.vpa_update_lock); + val->vpaval.addr = vcpu->arch.dtl.next_gpa; + val->vpaval.length = vcpu->arch.dtl.len; + spin_unlock(&vcpu->arch.vpa_update_lock); + break; default: r = -EINVAL; break; @@ -612,6 +646,7 @@ int kvmppc_set_one_reg(struct kvm_vcpu *vcpu, u64 id, union kvmppc_one_reg *val) { int r = 0; long int i; + unsigned long addr, len; switch (id) { case KVM_REG_PPC_HIOR: @@ -666,6 +701,33 @@ int kvmppc_set_one_reg(struct kvm_vcpu *vcpu, u64 id, union kvmppc_one_reg *val) } break; #endif /* CONFIG_VSX */ + case KVM_REG_PPC_VPA_ADDR: + addr = set_reg_val(id, *val); + r = -EINVAL; + if (!addr && (vcpu->arch.slb_shadow.next_gpa || + vcpu->arch.dtl.next_gpa)) + break; + r = set_vpa(vcpu, &vcpu->arch.vpa, addr, sizeof(struct lppaca)); + break; + case KVM_REG_PPC_VPA_SLB: + addr = val->vpaval.addr; + len = val->vpaval.length; + r = -EINVAL; + if (addr && !vcpu->arch.vpa.next_gpa) + break; + r = set_vpa(vcpu, &vcpu->arch.slb_shadow, addr, len); + break; + case KVM_REG_PPC_VPA_DTL: + addr = val->vpaval.addr; + len = val->vpaval.length; + r = -EINVAL; + if (len < sizeof(struct dtl_entry)) + break; + if (addr && !vcpu->arch.vpa.next_gpa) + break; + len -= len % sizeof(struct dtl_entry); + r = set_vpa(vcpu, &vcpu->arch.dtl, addr, len); + break; default: r = -EINVAL; break; -- cgit v1.2.3-59-g8ed1b From 416ad65f73f8a68783c98dd734f570e4829d996b Mon Sep 17 00:00:00 2001 From: Cornelia Huck Date: Tue, 2 Oct 2012 16:25:37 +0200 Subject: s390/kvm: Add documentation for KVM_S390_INTERRUPT Signed-off-by: Cornelia Huck Signed-off-by: Martin Schwidefsky Signed-off-by: Christian Borntraeger Signed-off-by: Marcelo Tosatti --- Documentation/virtual/kvm/api.txt | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) (limited to 'Documentation') diff --git a/Documentation/virtual/kvm/api.txt b/Documentation/virtual/kvm/api.txt index f6ec3a92e621..a46a416810fb 100644 --- a/Documentation/virtual/kvm/api.txt +++ b/Documentation/virtual/kvm/api.txt @@ -1997,6 +1997,39 @@ return the hash table order in the parameter. (If the guest is using the virtualized real-mode area (VRMA) facility, the kernel will re-create the VMRA HPTEs on the next KVM_RUN of any vcpu.) +4.77 KVM_S390_INTERRUPT + +Capability: basic +Architectures: s390 +Type: vm ioctl, vcpu ioctl +Parameters: struct kvm_s390_interrupt (in) +Returns: 0 on success, -1 on error + +Allows to inject an interrupt to the guest. Interrupts can be floating +(vm ioctl) or per cpu (vcpu ioctl), depending on the interrupt type. + +Interrupt parameters are passed via kvm_s390_interrupt: + +struct kvm_s390_interrupt { + __u32 type; + __u32 parm; + __u64 parm64; +}; + +type can be one of the following: + +KVM_S390_SIGP_STOP (vcpu) - sigp restart +KVM_S390_PROGRAM_INT (vcpu) - program check; code in parm +KVM_S390_SIGP_SET_PREFIX (vcpu) - sigp set prefix; prefix address in parm +KVM_S390_RESTART (vcpu) - restart +KVM_S390_INT_VIRTIO (vm) - virtio external interrupt; external interrupt + parameters in parm and parm64 +KVM_S390_INT_SERVICE (vm) - sclp external interrupt; sclp parameter in parm +KVM_S390_INT_EMERGENCY (vcpu) - sigp emergency; source cpu in parm +KVM_S390_INT_EXTERNAL_CALL (vcpu) - sigp external call; source cpu in parm + +Note that the vcpu ioctl is asynchronous to vcpu execution. + 5. The kvm_run structure ------------------------ -- cgit v1.2.3-59-g8ed1b From ad5396ee32afbdabb6188ffba67778080ea795b8 Mon Sep 17 00:00:00 2001 From: Tomasz Figa Date: Thu, 11 Oct 2012 01:03:50 -0700 Subject: Input: mms114 - add device tree bindings Add device tree bindings for mms114 touchscreen. [Dmitry Torokhov: added #ifdef CONFIG_OF guards] Signed-off-by: Tomasz Figa Signed-off-by: Dmitry Torokhov --- .../bindings/input/touchscreen/mms114.txt | 34 ++++++++++++ drivers/input/touchscreen/mms114.c | 62 +++++++++++++++++++++- 2 files changed, 94 insertions(+), 2 deletions(-) create mode 100644 Documentation/devicetree/bindings/input/touchscreen/mms114.txt (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/input/touchscreen/mms114.txt b/Documentation/devicetree/bindings/input/touchscreen/mms114.txt new file mode 100644 index 000000000000..89d4c56c5671 --- /dev/null +++ b/Documentation/devicetree/bindings/input/touchscreen/mms114.txt @@ -0,0 +1,34 @@ +* MELFAS MMS114 touchscreen controller + +Required properties: +- compatible: must be "melfas,mms114" +- reg: I2C address of the chip +- interrupts: interrupt to which the chip is connected +- x-size: horizontal resolution of touchscreen +- y-size: vertical resolution of touchscreen + +Optional properties: +- contact-threshold: +- moving-threshold: +- x-invert: invert X axis +- y-invert: invert Y axis + +Example: + + i2c@00000000 { + /* ... */ + + touchscreen@48 { + compatible = "melfas,mms114"; + reg = <0x48>; + interrupts = <39 0>; + x-size = <720>; + y-size = <1280>; + contact-threshold = <10>; + moving-threshold = <10>; + x-invert; + y-invert; + }; + + /* ... */ + }; diff --git a/drivers/input/touchscreen/mms114.c b/drivers/input/touchscreen/mms114.c index 560cf09d1c5a..3426d2e11a36 100644 --- a/drivers/input/touchscreen/mms114.c +++ b/drivers/input/touchscreen/mms114.c @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -360,14 +361,63 @@ static void mms114_input_close(struct input_dev *dev) mms114_stop(data); } +#ifdef CONFIG_OF +static struct mms114_platform_data * __devinit mms114_parse_dt(struct device *dev) +{ + struct mms114_platform_data *pdata; + struct device_node *np = dev->of_node; + + if (!np) + return NULL; + + pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) { + dev_err(dev, "failed to allocate platform data\n"); + return NULL; + } + + if (of_property_read_u32(np, "x-size", &pdata->x_size)) { + dev_err(dev, "failed to get x-size property\n"); + return NULL; + }; + + if (of_property_read_u32(np, "y-size", &pdata->y_size)) { + dev_err(dev, "failed to get y-size property\n"); + return NULL; + }; + + of_property_read_u32(np, "contact-threshold", + &pdata->contact_threshold); + of_property_read_u32(np, "moving-threshold", + &pdata->moving_threshold); + + if (of_find_property(np, "x-invert", NULL)) + pdata->x_invert = true; + if (of_find_property(np, "y-invert", NULL)) + pdata->y_invert = true; + + return pdata; +} +#else +static inline struct mms114_platform_data *mms114_parse_dt(struct device *dev) +{ + return NULL; +} +#endif + static int __devinit mms114_probe(struct i2c_client *client, const struct i2c_device_id *id) { + const struct mms114_platform_data *pdata; struct mms114_data *data; struct input_dev *input_dev; int error; - if (!client->dev.platform_data) { + pdata = dev_get_platdata(&client->dev); + if (!pdata) + pdata = mms114_parse_dt(&client->dev); + + if (!pdata) { dev_err(&client->dev, "Need platform data\n"); return -EINVAL; } @@ -389,7 +439,7 @@ static int __devinit mms114_probe(struct i2c_client *client, data->client = client; data->input_dev = input_dev; - data->pdata = client->dev.platform_data; + data->pdata = pdata; input_dev->name = "MELPAS MMS114 Touchscreen"; input_dev->id.bustype = BUS_I2C; @@ -525,11 +575,19 @@ static const struct i2c_device_id mms114_id[] = { }; MODULE_DEVICE_TABLE(i2c, mms114_id); +#ifdef CONFIG_OF +static struct of_device_id __devinitdata mms114_dt_match[] = { + { .compatible = "melfas,mms114" }, + { } +}; +#endif + static struct i2c_driver mms114_driver = { .driver = { .name = "mms114", .owner = THIS_MODULE, .pm = &mms114_pm_ops, + .of_match_table = of_match_ptr(mms114_dt_match), }, .probe = mms114_probe, .remove = __devexit_p(mms114_remove), -- cgit v1.2.3-59-g8ed1b From fc33ff43134790ef3cb997ed90048a50b4d1b15e Mon Sep 17 00:00:00 2001 From: Jean-Christophe PLAGNIOL-VILLARD Date: Sat, 14 Jul 2012 15:26:08 +0800 Subject: arm: at91: at91sam9x5: fix gpio number per bank On the at91sam9x5 SoC series, GPIO banks B and D only have 19 and 22 pins. So add a property to set this parameter. Acked-by: Nicolas Ferre Acked-by: Linus Walleij Signed-off-by: Jean-Christophe PLAGNIOL-VILLARD --- .../devicetree/bindings/gpio/gpio_atmel.txt | 5 ++++ arch/arm/boot/dts/at91sam9x5.dtsi | 2 ++ arch/arm/mach-at91/gpio.c | 33 ++++++++++++++-------- 3 files changed, 29 insertions(+), 11 deletions(-) (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/gpio/gpio_atmel.txt b/Documentation/devicetree/bindings/gpio/gpio_atmel.txt index 66efc804806a..85f8c0d084fa 100644 --- a/Documentation/devicetree/bindings/gpio/gpio_atmel.txt +++ b/Documentation/devicetree/bindings/gpio/gpio_atmel.txt @@ -9,6 +9,10 @@ Required properties: unused). - gpio-controller: Marks the device node as a GPIO controller. +optional properties: +- #gpio-lines: Number of gpio if absent 32. + + Example: pioA: gpio@fffff200 { compatible = "atmel,at91rm9200-gpio"; @@ -16,5 +20,6 @@ Example: interrupts = <2 4>; #gpio-cells = <2>; gpio-controller; + #gpio-lines = <19>; }; diff --git a/arch/arm/boot/dts/at91sam9x5.dtsi b/arch/arm/boot/dts/at91sam9x5.dtsi index 507a84d2a5d7..065698ac4deb 100644 --- a/arch/arm/boot/dts/at91sam9x5.dtsi +++ b/arch/arm/boot/dts/at91sam9x5.dtsi @@ -133,6 +133,7 @@ interrupts = <2 4 1>; #gpio-cells = <2>; gpio-controller; + #gpio-lines = <19>; interrupt-controller; #interrupt-cells = <2>; }; @@ -153,6 +154,7 @@ interrupts = <3 4 1>; #gpio-cells = <2>; gpio-controller; + #gpio-lines = <22>; interrupt-controller; #interrupt-cells = <2>; }; diff --git a/arch/arm/mach-at91/gpio.c b/arch/arm/mach-at91/gpio.c index 3b8f463bfa1d..a34f0ed291c0 100644 --- a/arch/arm/mach-at91/gpio.c +++ b/arch/arm/mach-at91/gpio.c @@ -33,6 +33,8 @@ #include "generic.h" +#define MAX_NB_GPIO_PER_BANK 32 + struct at91_gpio_chip { struct gpio_chip chip; struct at91_gpio_chip *next; /* Bank sharing same clock */ @@ -56,7 +58,7 @@ static int at91_gpiolib_direction_input(struct gpio_chip *chip, unsigned offset); static int at91_gpiolib_to_irq(struct gpio_chip *chip, unsigned offset); -#define AT91_GPIO_CHIP(name, nr_gpio) \ +#define AT91_GPIO_CHIP(name) \ { \ .chip = { \ .label = name, \ @@ -67,16 +69,16 @@ static int at91_gpiolib_to_irq(struct gpio_chip *chip, unsigned offset); .set = at91_gpiolib_set, \ .dbg_show = at91_gpiolib_dbg_show, \ .to_irq = at91_gpiolib_to_irq, \ - .ngpio = nr_gpio, \ + .ngpio = MAX_NB_GPIO_PER_BANK, \ }, \ } static struct at91_gpio_chip gpio_chip[] = { - AT91_GPIO_CHIP("pioA", 32), - AT91_GPIO_CHIP("pioB", 32), - AT91_GPIO_CHIP("pioC", 32), - AT91_GPIO_CHIP("pioD", 32), - AT91_GPIO_CHIP("pioE", 32), + AT91_GPIO_CHIP("pioA"), + AT91_GPIO_CHIP("pioB"), + AT91_GPIO_CHIP("pioC"), + AT91_GPIO_CHIP("pioD"), + AT91_GPIO_CHIP("pioE"), }; static int gpio_banks; @@ -91,7 +93,7 @@ static unsigned long at91_gpio_caps; static inline void __iomem *pin_to_controller(unsigned pin) { - pin /= 32; + pin /= MAX_NB_GPIO_PER_BANK; if (likely(pin < gpio_banks)) return gpio_chip[pin].regbase; @@ -100,7 +102,7 @@ static inline void __iomem *pin_to_controller(unsigned pin) static inline unsigned pin_to_mask(unsigned pin) { - return 1 << (pin % 32); + return 1 << (pin % MAX_NB_GPIO_PER_BANK); } @@ -992,6 +994,7 @@ static void __init of_at91_gpio_init_one(struct device_node *np) { int alias_idx; struct at91_gpio_chip *at91_gpio; + uint32_t ngpio; if (!np) return; @@ -1004,7 +1007,7 @@ static void __init of_at91_gpio_init_one(struct device_node *np) } at91_gpio = &gpio_chip[alias_idx]; - at91_gpio->chip.base = alias_idx * at91_gpio->chip.ngpio; + at91_gpio->chip.base = alias_idx * MAX_NB_GPIO_PER_BANK; at91_gpio->regbase = of_iomap(np, 0); if (!at91_gpio->regbase) { @@ -1024,6 +1027,14 @@ static void __init of_at91_gpio_init_one(struct device_node *np) if (of_device_is_compatible(np, "atmel,at91sam9x5-gpio")) at91_gpio_caps |= AT91_GPIO_CAP_PIO3; + if (!of_property_read_u32(np, "#gpio-lines", &ngpio)) { + if (ngpio >= MAX_NB_GPIO_PER_BANK) + pr_err("at91_gpio.%d, gpio-nb >= %d failback to %d\n", + alias_idx, MAX_NB_GPIO_PER_BANK, MAX_NB_GPIO_PER_BANK); + else + at91_gpio->chip.ngpio = ngpio; + } + /* Setup clock */ if (at91_gpio_setup_clk(alias_idx)) goto ioremap_err; @@ -1061,7 +1072,7 @@ static void __init at91_gpio_init_one(int idx, u32 regbase, int pioc_hwirq) { struct at91_gpio_chip *at91_gpio = &gpio_chip[idx]; - at91_gpio->chip.base = idx * at91_gpio->chip.ngpio; + at91_gpio->chip.base = idx * MAX_NB_GPIO_PER_BANK; at91_gpio->pioc_hwirq = pioc_hwirq; at91_gpio->pioc_idx = idx; -- cgit v1.2.3-59-g8ed1b From 6732ae5cb47c4f9a72727585956f2a5e069d1637 Mon Sep 17 00:00:00 2001 From: Jean-Christophe PLAGNIOL-VILLARD Date: Thu, 12 Jul 2012 23:35:02 +0800 Subject: ARM: at91: add pinctrl support This is also include the gpio controller as the IP share both. Each soc will have to describe the SoC limitation and pin configuration via DT. This will allow to do not need to touch the C code when adding new SoC if the IP version is supported. Signed-off-by: Jean-Christophe PLAGNIOL-VILLARD --- .../bindings/pinctrl/atmel,at91-pinctrl.txt | 136 ++ arch/arm/Kconfig | 2 + arch/arm/mach-at91/board-dt.c | 2 - arch/arm/mach-at91/gpio.c | 165 +-- drivers/pinctrl/Kconfig | 9 + drivers/pinctrl/Makefile | 1 + drivers/pinctrl/pinctrl-at91.c | 1490 ++++++++++++++++++++ 7 files changed, 1643 insertions(+), 162 deletions(-) create mode 100644 Documentation/devicetree/bindings/pinctrl/atmel,at91-pinctrl.txt create mode 100644 drivers/pinctrl/pinctrl-at91.c (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/pinctrl/atmel,at91-pinctrl.txt b/Documentation/devicetree/bindings/pinctrl/atmel,at91-pinctrl.txt new file mode 100644 index 000000000000..20a987e55a28 --- /dev/null +++ b/Documentation/devicetree/bindings/pinctrl/atmel,at91-pinctrl.txt @@ -0,0 +1,136 @@ +* Atmel AT91 Pinmux Controller + +The AT91 Pinmux Controler, enables the IC +to share one PAD to several functional blocks. The sharing is done by +multiplexing the PAD input/output signals. For each PAD there are up to +8 muxing options (called periph modes). Since different modules require +different PAD settings (like pull up, keeper, etc) the contoller controls +also the PAD settings parameters. + +Please refer to pinctrl-bindings.txt in this directory for details of the +common pinctrl bindings used by client devices, including the meaning of the +phrase "pin configuration node". + +Atmel AT91 pin configuration node is a node of a group of pins which can be +used for a specific device or function. This node represents both mux and config +of the pins in that group. The 'pins' selects the function mode(also named pin +mode) this pin can work on and the 'config' configures various pad settings +such as pull-up, multi drive, etc. + +Required properties for iomux controller: +- compatible: "atmel,at91rm9200-pinctrl" +- atmel,mux-mask: array of mask (periph per bank) to describe if a pin can be + configured in this periph mode. All the periph and bank need to be describe. + +How to create such array: + +Each column will represent the possible peripheral of the pinctrl +Each line will represent a pio bank + +Take an example on the 9260 +Peripheral: 2 ( A and B) +Bank: 3 (A, B and C) +=> + + /* A B */ + 0xffffffff 0xffc00c3b /* pioA */ + 0xffffffff 0x7fff3ccf /* pioB */ + 0xffffffff 0x007fffff /* pioC */ + +For each peripheral/bank we will descibe in a u32 if a pin can can be +configured in it by putting 1 to the pin bit (1 << pin) + +Let's take the pioA on peripheral B +From the datasheet Table 10-2. +Peripheral B +PA0 MCDB0 +PA1 MCCDB +PA2 +PA3 MCDB3 +PA4 MCDB2 +PA5 MCDB1 +PA6 +PA7 +PA8 +PA9 +PA10 ETX2 +PA11 ETX3 +PA12 +PA13 +PA14 +PA15 +PA16 +PA17 +PA18 +PA19 +PA20 +PA21 +PA22 ETXER +PA23 ETX2 +PA24 ETX3 +PA25 ERX2 +PA26 ERX3 +PA27 ERXCK +PA28 ECRS +PA29 ECOL +PA30 RXD4 +PA31 TXD4 + +=> 0xffc00c3b + +Required properties for pin configuration node: +- atmel,pins: 4 integers array, represents a group of pins mux and config + setting. The format is atmel,pins = . + The PERIPH 0 means gpio. + +Bits used for CONFIG: +PULL_UP(1 << 0): indicate this pin need a pull up. +MULTIDRIVE(1 << 1): indicate this pin need to be configured as multidrive. + +NOTE: +Some requirements for using atmel,at91rm9200-pinctrl binding: +1. We have pin function node defined under at91 controller node to represent + what pinmux functions this SoC supports. +2. The driver can use the function node's name and pin configuration node's + name describe the pin function and group hierarchy. + For example, Linux at91 pinctrl driver takes the function node's name + as the function name and pin configuration node's name as group name to + create the map table. +3. Each pin configuration node should have a phandle, devices can set pins + configurations by referring to the phandle of that pin configuration node. +4. The gpio controller must be describe in the pinctrl simple-bus. + +Examples: + +pinctrl@fffff400 { + #address-cells = <1>; + #size-cells = <1>; + ranges; + compatible = "atmel,at91rm9200-pinctrl", "simple-bus"; + reg = <0xfffff400 0x600>; + + atmel,mux-mask = < + /* A B */ + 0xffffffff 0xffc00c3b /* pioA */ + 0xffffffff 0x7fff3ccf /* pioB */ + 0xffffffff 0x007fffff /* pioC */ + >; + + /* shared pinctrl settings */ + dbgu { + pinctrl_dbgu: dbgu-0 { + atmel,pins = + <1 14 0x1 0x0 /* PB14 periph A */ + 1 15 0x1 0x1>; /* PB15 periph with pullup */ + }; + }; +}; + +dbgu: serial@fffff200 { + compatible = "atmel,at91sam9260-usart"; + reg = <0xfffff200 0x200>; + interrupts = <1 4 7>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_dbgu>; + status = "disabled"; +}; diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index 431c3753145a..3d7f11fe610d 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -329,6 +329,8 @@ config ARCH_AT91 select IRQ_DOMAIN select NEED_MACH_GPIO_H select NEED_MACH_IO_H if PCCARD + select PINCTRL + select PINCTRL_AT91 if USE_OF help This enables support for systems based on Atmel AT91RM9200 and AT91SAM9* processors. diff --git a/arch/arm/mach-at91/board-dt.c b/arch/arm/mach-at91/board-dt.c index e8f45c4e0ea8..3b6a94820fa0 100644 --- a/arch/arm/mach-at91/board-dt.c +++ b/arch/arm/mach-at91/board-dt.c @@ -30,8 +30,6 @@ static const struct of_device_id irq_of_match[] __initconst = { { .compatible = "atmel,at91rm9200-aic", .data = at91_aic_of_init }, - { .compatible = "atmel,at91rm9200-gpio", .data = at91_gpio_of_irq_setup }, - { .compatible = "atmel,at91sam9x5-gpio", .data = at91_gpio_of_irq_setup }, { /*sentinel*/ } }; diff --git a/arch/arm/mach-at91/gpio.c b/arch/arm/mach-at91/gpio.c index a34f0ed291c0..c5d7e1e9d757 100644 --- a/arch/arm/mach-at91/gpio.c +++ b/arch/arm/mach-at91/gpio.c @@ -23,8 +23,6 @@ #include #include #include -#include -#include #include @@ -717,80 +715,6 @@ postcore_initcall(at91_gpio_debugfs_init); */ static struct lock_class_key gpio_lock_class; -#if defined(CONFIG_OF) -static int at91_gpio_irq_map(struct irq_domain *h, unsigned int virq, - irq_hw_number_t hw) -{ - struct at91_gpio_chip *at91_gpio = h->host_data; - - irq_set_lockdep_class(virq, &gpio_lock_class); - - /* - * Can use the "simple" and not "edge" handler since it's - * shorter, and the AIC handles interrupts sanely. - */ - irq_set_chip_and_handler(virq, &gpio_irqchip, - handle_simple_irq); - set_irq_flags(virq, IRQF_VALID); - irq_set_chip_data(virq, at91_gpio); - - return 0; -} - -static struct irq_domain_ops at91_gpio_ops = { - .map = at91_gpio_irq_map, - .xlate = irq_domain_xlate_twocell, -}; - -int __init at91_gpio_of_irq_setup(struct device_node *node, - struct device_node *parent) -{ - struct at91_gpio_chip *prev = NULL; - int alias_idx = of_alias_get_id(node, "gpio"); - struct at91_gpio_chip *at91_gpio = &gpio_chip[alias_idx]; - - /* Setup proper .irq_set_type function */ - if (has_pio3()) - gpio_irqchip.irq_set_type = alt_gpio_irq_type; - else - gpio_irqchip.irq_set_type = gpio_irq_type; - - /* Disable irqs of this PIO controller */ - __raw_writel(~0, at91_gpio->regbase + PIO_IDR); - - /* Setup irq domain */ - at91_gpio->domain = irq_domain_add_linear(node, at91_gpio->chip.ngpio, - &at91_gpio_ops, at91_gpio); - if (!at91_gpio->domain) - panic("at91_gpio.%d: couldn't allocate irq domain (DT).\n", - at91_gpio->pioc_idx); - - /* Setup chained handler */ - if (at91_gpio->pioc_idx) - prev = &gpio_chip[at91_gpio->pioc_idx - 1]; - - /* The toplevel handler handles one bank of GPIOs, except - * on some SoC it can handles up to three... - * We only set up the handler for the first of the list. - */ - if (prev && prev->next == at91_gpio) - return 0; - - at91_gpio->pioc_virq = irq_create_mapping(irq_find_host(parent), - at91_gpio->pioc_hwirq); - irq_set_chip_data(at91_gpio->pioc_virq, at91_gpio); - irq_set_chained_handler(at91_gpio->pioc_virq, gpio_irq_handler); - - return 0; -} -#else -int __init at91_gpio_of_irq_setup(struct device_node *node, - struct device_node *parent) -{ - return -EINVAL; -} -#endif - /* * irqdomain initialization: pile up irqdomains on top of AIC range */ @@ -989,85 +913,6 @@ err: return -EINVAL; } -#ifdef CONFIG_OF_GPIO -static void __init of_at91_gpio_init_one(struct device_node *np) -{ - int alias_idx; - struct at91_gpio_chip *at91_gpio; - uint32_t ngpio; - - if (!np) - return; - - alias_idx = of_alias_get_id(np, "gpio"); - if (alias_idx >= MAX_GPIO_BANKS) { - pr_err("at91_gpio, failed alias idx(%d) > MAX_GPIO_BANKS(%d), ignoring.\n", - alias_idx, MAX_GPIO_BANKS); - return; - } - - at91_gpio = &gpio_chip[alias_idx]; - at91_gpio->chip.base = alias_idx * MAX_NB_GPIO_PER_BANK; - - at91_gpio->regbase = of_iomap(np, 0); - if (!at91_gpio->regbase) { - pr_err("at91_gpio.%d, failed to map registers, ignoring.\n", - alias_idx); - return; - } - - /* Get the interrupts property */ - if (of_property_read_u32(np, "interrupts", &at91_gpio->pioc_hwirq)) { - pr_err("at91_gpio.%d, failed to get interrupts property, ignoring.\n", - alias_idx); - goto ioremap_err; - } - - /* Get capabilities from compatibility property */ - if (of_device_is_compatible(np, "atmel,at91sam9x5-gpio")) - at91_gpio_caps |= AT91_GPIO_CAP_PIO3; - - if (!of_property_read_u32(np, "#gpio-lines", &ngpio)) { - if (ngpio >= MAX_NB_GPIO_PER_BANK) - pr_err("at91_gpio.%d, gpio-nb >= %d failback to %d\n", - alias_idx, MAX_NB_GPIO_PER_BANK, MAX_NB_GPIO_PER_BANK); - else - at91_gpio->chip.ngpio = ngpio; - } - - /* Setup clock */ - if (at91_gpio_setup_clk(alias_idx)) - goto ioremap_err; - - at91_gpio->chip.of_node = np; - gpio_banks = max(gpio_banks, alias_idx + 1); - at91_gpio->pioc_idx = alias_idx; - return; - -ioremap_err: - iounmap(at91_gpio->regbase); -} - -static int __init of_at91_gpio_init(void) -{ - struct device_node *np = NULL; - - /* - * This isn't ideal, but it gets things hooked up until this - * driver is converted into a platform_device - */ - for_each_compatible_node(np, NULL, "atmel,at91rm9200-gpio") - of_at91_gpio_init_one(np); - - return gpio_banks > 0 ? 0 : -EINVAL; -} -#else -static int __init of_at91_gpio_init(void) -{ - return -EINVAL; -} -#endif - static void __init at91_gpio_init_one(int idx, u32 regbase, int pioc_hwirq) { struct at91_gpio_chip *at91_gpio = &gpio_chip[idx]; @@ -1102,11 +947,11 @@ void __init at91_gpio_init(struct at91_gpio_bank *data, int nr_banks) BUG_ON(nr_banks > MAX_GPIO_BANKS); - if (of_at91_gpio_init() < 0) { - /* No GPIO controller found in device tree */ - for (i = 0; i < nr_banks; i++) - at91_gpio_init_one(i, data[i].regbase, data[i].id); - } + if (of_have_populated_dt()) + return; + + for (i = 0; i < nr_banks; i++) + at91_gpio_init_one(i, data[i].regbase, data[i].id); for (i = 0; i < gpio_banks; i++) { at91_gpio = &gpio_chip[i]; diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig index 7bf914df6e91..4787f0e4597d 100644 --- a/drivers/pinctrl/Kconfig +++ b/drivers/pinctrl/Kconfig @@ -26,6 +26,15 @@ config DEBUG_PINCTRL help Say Y here to add some extra checks and diagnostics to PINCTRL calls. +config PINCTRL_AT91 + bool "AT91 pinctrl driver" + depends on OF + depends on ARCH_AT91 + select PINMUX + select PINCONF + help + Say Y here to enable the at91 pinctrl driver + config PINCTRL_BCM2835 bool select PINMUX diff --git a/drivers/pinctrl/Makefile b/drivers/pinctrl/Makefile index f395ba5cec25..78a191c85adc 100644 --- a/drivers/pinctrl/Makefile +++ b/drivers/pinctrl/Makefile @@ -9,6 +9,7 @@ ifeq ($(CONFIG_OF),y) obj-$(CONFIG_PINCTRL) += devicetree.o endif obj-$(CONFIG_GENERIC_PINCONF) += pinconf-generic.o +obj-$(CONFIG_PINCTRL_AT91) += pinctrl-at91.o obj-$(CONFIG_PINCTRL_BCM2835) += pinctrl-bcm2835.o obj-$(CONFIG_PINCTRL_IMX) += pinctrl-imx.o obj-$(CONFIG_PINCTRL_IMX35) += pinctrl-imx35.o diff --git a/drivers/pinctrl/pinctrl-at91.c b/drivers/pinctrl/pinctrl-at91.c new file mode 100644 index 000000000000..01bf92459fdc --- /dev/null +++ b/drivers/pinctrl/pinctrl-at91.c @@ -0,0 +1,1490 @@ +/* + * at91 pinctrl driver based on at91 pinmux core + * + * Copyright (C) 2011-2012 Jean-Christophe PLAGNIOL-VILLARD + * + * Under GPLv2 only + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +/* Since we request GPIOs from ourself */ +#include + +#include + +#include +#include + +#include "core.h" + +#define MAX_NB_GPIO_PER_BANK 32 + +struct at91_pinctrl_mux_ops; + +struct at91_gpio_chip { + struct gpio_chip chip; + struct pinctrl_gpio_range range; + struct at91_gpio_chip *next; /* Bank sharing same clock */ + int pioc_hwirq; /* PIO bank interrupt identifier on AIC */ + int pioc_virq; /* PIO bank Linux virtual interrupt */ + int pioc_idx; /* PIO bank index */ + void __iomem *regbase; /* PIO bank virtual address */ + struct clk *clock; /* associated clock */ + struct irq_domain *domain; /* associated irq domain */ + struct at91_pinctrl_mux_ops *ops; /* ops */ +}; + +#define to_at91_gpio_chip(c) container_of(c, struct at91_gpio_chip, chip) + +static struct at91_gpio_chip *gpio_chips[MAX_GPIO_BANKS]; + +static int gpio_banks; + +#define PULL_UP (0 << 1) +#define MULTI_DRIVE (1 << 1) + +/** + * struct at91_pmx_func - describes AT91 pinmux functions + * @name: the name of this specific function + * @groups: corresponding pin groups + * @ngroups: the number of groups + */ +struct at91_pmx_func { + const char *name; + const char **groups; + unsigned ngroups; +}; + +enum at91_mux { + AT91_MUX_GPIO = 0, + AT91_MUX_PERIPH_A = 1, + AT91_MUX_PERIPH_B = 2, + AT91_MUX_PERIPH_C = 3, + AT91_MUX_PERIPH_D = 4, +}; + +/** + * struct at91_pmx_pin - describes an At91 pin mux + * @bank: the bank of the pin + * @pin: the pin number in the @bank + * @mux: the mux mode : gpio or periph_x of the pin i.e. alternate function. + * @conf: the configuration of the pin: PULL_UP, MULTIDRIVE etc... + */ +struct at91_pmx_pin { + uint32_t bank; + uint32_t pin; + enum at91_mux mux; + unsigned long conf; +}; + +/** + * struct at91_pin_group - describes an At91 pin group + * @name: the name of this specific pin group + * @pins_conf: the mux mode for each pin in this group. The size of this + * array is the same as pins. + * @pins: an array of discrete physical pins used in this group, taken + * from the driver-local pin enumeration space + * @npins: the number of pins in this group array, i.e. the number of + * elements in .pins so we can iterate over that array + */ +struct at91_pin_group { + const char *name; + struct at91_pmx_pin *pins_conf; + unsigned int *pins; + unsigned npins; +}; + +/** + * struct at91_pinctrl_mux_ops - describes an At91 mux ops group + * on new IP with support for periph C and D the way to mux in + * periph A and B has changed + * So provide the right call back + * if not present means the IP does not support it + * @get_periph: return the periph mode configured + * @mux_A_periph: mux as periph A + * @mux_B_periph: mux as periph B + * @mux_C_periph: mux as periph C + * @mux_D_periph: mux as periph D + * @irq_type: return irq type + */ +struct at91_pinctrl_mux_ops { + enum at91_mux (*get_periph)(void __iomem *pio, unsigned mask); + void (*mux_A_periph)(void __iomem *pio, unsigned mask); + void (*mux_B_periph)(void __iomem *pio, unsigned mask); + void (*mux_C_periph)(void __iomem *pio, unsigned mask); + void (*mux_D_periph)(void __iomem *pio, unsigned mask); + /* irq */ + int (*irq_type)(struct irq_data *d, unsigned type); +}; + +static int gpio_irq_type(struct irq_data *d, unsigned type); +static int alt_gpio_irq_type(struct irq_data *d, unsigned type); + +struct at91_pinctrl { + struct device *dev; + struct pinctrl_dev *pctl; + + int nbanks; + + uint32_t *mux_mask; + int nmux; + + struct at91_pmx_func *functions; + int nfunctions; + + struct at91_pin_group *groups; + int ngroups; + + struct at91_pinctrl_mux_ops *ops; +}; + +static const inline struct at91_pin_group *at91_pinctrl_find_group_by_name( + const struct at91_pinctrl *info, + const char *name) +{ + const struct at91_pin_group *grp = NULL; + int i; + + for (i = 0; i < info->ngroups; i++) { + if (strcmp(info->groups[i].name, name)) + continue; + + grp = &info->groups[i]; + dev_dbg(info->dev, "%s: %d 0:%d\n", name, grp->npins, grp->pins[0]); + break; + } + + return grp; +} + +static int at91_get_groups_count(struct pinctrl_dev *pctldev) +{ + struct at91_pinctrl *info = pinctrl_dev_get_drvdata(pctldev); + + return info->ngroups; +} + +static const char *at91_get_group_name(struct pinctrl_dev *pctldev, + unsigned selector) +{ + struct at91_pinctrl *info = pinctrl_dev_get_drvdata(pctldev); + + return info->groups[selector].name; +} + +static int at91_get_group_pins(struct pinctrl_dev *pctldev, unsigned selector, + const unsigned **pins, + unsigned *npins) +{ + struct at91_pinctrl *info = pinctrl_dev_get_drvdata(pctldev); + + if (selector >= info->ngroups) + return -EINVAL; + + *pins = info->groups[selector].pins; + *npins = info->groups[selector].npins; + + return 0; +} + +static void at91_pin_dbg_show(struct pinctrl_dev *pctldev, struct seq_file *s, + unsigned offset) +{ + seq_printf(s, "%s", dev_name(pctldev->dev)); +} + +static int at91_dt_node_to_map(struct pinctrl_dev *pctldev, + struct device_node *np, + struct pinctrl_map **map, unsigned *num_maps) +{ + struct at91_pinctrl *info = pinctrl_dev_get_drvdata(pctldev); + const struct at91_pin_group *grp; + struct pinctrl_map *new_map; + struct device_node *parent; + int map_num = 1; + int i; + struct at91_pmx_pin *pin; + + /* + * first find the group of this node and check if we need create + * config maps for pins + */ + grp = at91_pinctrl_find_group_by_name(info, np->name); + if (!grp) { + dev_err(info->dev, "unable to find group for node %s\n", + np->name); + return -EINVAL; + } + + map_num += grp->npins; + new_map = devm_kzalloc(pctldev->dev, sizeof(*new_map) * map_num, GFP_KERNEL); + if (!new_map) + return -ENOMEM; + + *map = new_map; + *num_maps = map_num; + + /* create mux map */ + parent = of_get_parent(np); + if (!parent) { + kfree(new_map); + return -EINVAL; + } + new_map[0].type = PIN_MAP_TYPE_MUX_GROUP; + new_map[0].data.mux.function = parent->name; + new_map[0].data.mux.group = np->name; + of_node_put(parent); + + /* create config map */ + new_map++; + for (i = 0; i < grp->npins; i++) { + pin = &grp->pins_conf[i]; + + new_map[i].type = PIN_MAP_TYPE_CONFIGS_PIN; + new_map[i].data.configs.group_or_pin = + pin_get_name(pctldev, grp->pins[i]); + new_map[i].data.configs.configs = &grp->pins_conf[i].conf; + new_map[i].data.configs.num_configs = 1; + } + + dev_dbg(pctldev->dev, "maps: function %s group %s num %d\n", + (*map)->data.mux.function, (*map)->data.mux.group, map_num); + + return 0; +} + +static void at91_dt_free_map(struct pinctrl_dev *pctldev, + struct pinctrl_map *map, unsigned num_maps) +{ +} + +static struct pinctrl_ops at91_pctrl_ops = { + .get_groups_count = at91_get_groups_count, + .get_group_name = at91_get_group_name, + .get_group_pins = at91_get_group_pins, + .pin_dbg_show = at91_pin_dbg_show, + .dt_node_to_map = at91_dt_node_to_map, + .dt_free_map = at91_dt_free_map, +}; + +static void __iomem * pin_to_controller(struct at91_pinctrl *info, + unsigned int bank) +{ + return gpio_chips[bank]->regbase; +} + +static inline int pin_to_bank(unsigned pin) +{ + return pin /= MAX_NB_GPIO_PER_BANK; +} + +static unsigned pin_to_mask(unsigned int pin) +{ + return 1 << pin; +} + +static void at91_mux_disable_interrupt(void __iomem *pio, unsigned mask) +{ + writel_relaxed(mask, pio + PIO_IDR); +} + +static unsigned at91_mux_get_pullup(void __iomem *pio, unsigned pin) +{ + return (readl_relaxed(pio + PIO_PUSR) >> pin) & 0x1; +} + +static void at91_mux_set_pullup(void __iomem *pio, unsigned mask, bool on) +{ + writel_relaxed(mask, pio + (on ? PIO_PUER : PIO_PUDR)); +} + +static unsigned at91_mux_get_multidrive(void __iomem *pio, unsigned pin) +{ + return (readl_relaxed(pio + PIO_MDSR) >> pin) & 0x1; +} + +static void at91_mux_set_multidrive(void __iomem *pio, unsigned mask, bool on) +{ + writel_relaxed(mask, pio + (on ? PIO_MDER : PIO_MDDR)); +} + +static void at91_mux_set_A_periph(void __iomem *pio, unsigned mask) +{ + writel_relaxed(mask, pio + PIO_ASR); +} + +static void at91_mux_set_B_periph(void __iomem *pio, unsigned mask) +{ + writel_relaxed(mask, pio + PIO_BSR); +} + +static void at91_mux_pio3_set_A_periph(void __iomem *pio, unsigned mask) +{ + + writel_relaxed(readl_relaxed(pio + PIO_ABCDSR1) & ~mask, + pio + PIO_ABCDSR1); + writel_relaxed(readl_relaxed(pio + PIO_ABCDSR2) & ~mask, + pio + PIO_ABCDSR2); +} + +static void at91_mux_pio3_set_B_periph(void __iomem *pio, unsigned mask) +{ + writel_relaxed(readl_relaxed(pio + PIO_ABCDSR1) | mask, + pio + PIO_ABCDSR1); + writel_relaxed(readl_relaxed(pio + PIO_ABCDSR2) & ~mask, + pio + PIO_ABCDSR2); +} + +static void at91_mux_pio3_set_C_periph(void __iomem *pio, unsigned mask) +{ + writel_relaxed(readl_relaxed(pio + PIO_ABCDSR1) & ~mask, pio + PIO_ABCDSR1); + writel_relaxed(readl_relaxed(pio + PIO_ABCDSR2) | mask, pio + PIO_ABCDSR2); +} + +static void at91_mux_pio3_set_D_periph(void __iomem *pio, unsigned mask) +{ + writel_relaxed(readl_relaxed(pio + PIO_ABCDSR1) | mask, pio + PIO_ABCDSR1); + writel_relaxed(readl_relaxed(pio + PIO_ABCDSR2) | mask, pio + PIO_ABCDSR2); +} + +static enum at91_mux at91_mux_pio3_get_periph(void __iomem *pio, unsigned mask) +{ + unsigned select; + + if (readl_relaxed(pio + PIO_PSR) & mask) + return AT91_MUX_GPIO; + + select = !!(readl_relaxed(pio + PIO_ABCDSR1) & mask); + select |= (!!(readl_relaxed(pio + PIO_ABCDSR2) & mask) << 1); + + return select + 1; +} + +static enum at91_mux at91_mux_get_periph(void __iomem *pio, unsigned mask) +{ + unsigned select; + + if (readl_relaxed(pio + PIO_PSR) & mask) + return AT91_MUX_GPIO; + + select = readl_relaxed(pio + PIO_ABSR) & mask; + + return select + 1; +} + +static struct at91_pinctrl_mux_ops at91rm9200_ops = { + .get_periph = at91_mux_get_periph, + .mux_A_periph = at91_mux_set_A_periph, + .mux_B_periph = at91_mux_set_B_periph, + .irq_type = gpio_irq_type, +}; + +static struct at91_pinctrl_mux_ops at91sam9x5_ops = { + .get_periph = at91_mux_pio3_get_periph, + .mux_A_periph = at91_mux_pio3_set_A_periph, + .mux_B_periph = at91_mux_pio3_set_B_periph, + .mux_C_periph = at91_mux_pio3_set_C_periph, + .mux_D_periph = at91_mux_pio3_set_D_periph, + .irq_type = alt_gpio_irq_type, +}; + +static void at91_pin_dbg(const struct device *dev, const struct at91_pmx_pin *pin) +{ + if (pin->mux) { + dev_dbg(dev, "pio%c%d configured as periph%c with conf = 0x%lu\n", + pin->bank + 'A', pin->pin, pin->mux - 1 + 'A', pin->conf); + } else { + dev_dbg(dev, "pio%c%d configured as gpio with conf = 0x%lu\n", + pin->bank + 'A', pin->pin, pin->conf); + } +} + +static int pin_check_config(struct at91_pinctrl *info, const char* name, + int index, const struct at91_pmx_pin *pin) +{ + int mux; + + /* check if it's a valid config */ + if (pin->bank >= info->nbanks) { + dev_err(info->dev, "%s: pin conf %d bank_id %d >= nbanks %d\n", + name, index, pin->bank, info->nbanks); + return -EINVAL; + } + + if (pin->pin >= MAX_NB_GPIO_PER_BANK) { + dev_err(info->dev, "%s: pin conf %d pin_bank_id %d >= %d\n", + name, index, pin->pin, MAX_NB_GPIO_PER_BANK); + return -EINVAL; + } + + if (!pin->mux) + return 0; + + mux = pin->mux - 1; + + if (mux >= info->nmux) { + dev_err(info->dev, "%s: pin conf %d mux_id %d >= nmux %d\n", + name, index, mux, info->nmux); + return -EINVAL; + } + + if (!(info->mux_mask[pin->bank * info->nmux + mux] & 1 << pin->pin)) { + dev_err(info->dev, "%s: pin conf %d mux_id %d not supported for pio%c%d\n", + name, index, mux, pin->bank + 'A', pin->pin); + return -EINVAL; + } + + return 0; +} + +static void at91_mux_gpio_disable(void __iomem *pio, unsigned mask) +{ + writel_relaxed(mask, pio + PIO_PDR); +} + +static void at91_mux_gpio_enable(void __iomem *pio, unsigned mask, bool input) +{ + writel_relaxed(mask, pio + PIO_PER); + writel_relaxed(mask, pio + (input ? PIO_ODR : PIO_OER)); +} + +static int at91_pmx_enable(struct pinctrl_dev *pctldev, unsigned selector, + unsigned group) +{ + struct at91_pinctrl *info = pinctrl_dev_get_drvdata(pctldev); + const struct at91_pmx_pin *pins_conf = info->groups[group].pins_conf; + const struct at91_pmx_pin *pin; + uint32_t npins = info->groups[group].npins; + int i, ret; + unsigned mask; + void __iomem *pio; + + dev_dbg(info->dev, "enable function %s group %s\n", + info->functions[selector].name, info->groups[group].name); + + /* first check that all the pins of the group are valid with a valid + * paramter */ + for (i = 0; i < npins; i++) { + pin = &pins_conf[i]; + ret = pin_check_config(info, info->groups[group].name, i, pin); + if (ret) + return ret; + } + + for (i = 0; i < npins; i++) { + pin = &pins_conf[i]; + at91_pin_dbg(info->dev, pin); + pio = pin_to_controller(info, pin->bank); + mask = pin_to_mask(pin->pin); + at91_mux_disable_interrupt(pio, mask); + switch(pin->mux) { + case AT91_MUX_GPIO: + at91_mux_gpio_enable(pio, mask, 1); + break; + case AT91_MUX_PERIPH_A: + info->ops->mux_A_periph(pio, mask); + break; + case AT91_MUX_PERIPH_B: + info->ops->mux_B_periph(pio, mask); + break; + case AT91_MUX_PERIPH_C: + if (!info->ops->mux_C_periph) + return -EINVAL; + info->ops->mux_C_periph(pio, mask); + break; + case AT91_MUX_PERIPH_D: + if (!info->ops->mux_D_periph) + return -EINVAL; + info->ops->mux_D_periph(pio, mask); + break; + } + if (pin->mux) + at91_mux_gpio_disable(pio, mask); + } + + return 0; +} + +static void at91_pmx_disable(struct pinctrl_dev *pctldev, unsigned selector, + unsigned group) +{ + struct at91_pinctrl *info = pinctrl_dev_get_drvdata(pctldev); + const struct at91_pmx_pin *pins_conf = info->groups[group].pins_conf; + const struct at91_pmx_pin *pin; + uint32_t npins = info->groups[group].npins; + int i; + unsigned mask; + void __iomem *pio; + + for (i = 0; i < npins; i++) { + pin = &pins_conf[i]; + at91_pin_dbg(info->dev, pin); + pio = pin_to_controller(info, pin->bank); + mask = pin_to_mask(pin->pin); + at91_mux_gpio_enable(pio, mask, 1); + } +} + +static int at91_pmx_get_funcs_count(struct pinctrl_dev *pctldev) +{ + struct at91_pinctrl *info = pinctrl_dev_get_drvdata(pctldev); + + return info->nfunctions; +} + +static const char *at91_pmx_get_func_name(struct pinctrl_dev *pctldev, + unsigned selector) +{ + struct at91_pinctrl *info = pinctrl_dev_get_drvdata(pctldev); + + return info->functions[selector].name; +} + +static int at91_pmx_get_groups(struct pinctrl_dev *pctldev, unsigned selector, + const char * const **groups, + unsigned * const num_groups) +{ + struct at91_pinctrl *info = pinctrl_dev_get_drvdata(pctldev); + + *groups = info->functions[selector].groups; + *num_groups = info->functions[selector].ngroups; + + return 0; +} + +int at91_gpio_request_enable(struct pinctrl_dev *pctldev, + struct pinctrl_gpio_range *range, + unsigned offset) +{ + struct at91_pinctrl *npct = pinctrl_dev_get_drvdata(pctldev); + struct at91_gpio_chip *at91_chip; + struct gpio_chip *chip; + unsigned mask; + + if (!range) { + dev_err(npct->dev, "invalid range\n"); + return -EINVAL; + } + if (!range->gc) { + dev_err(npct->dev, "missing GPIO chip in range\n"); + return -EINVAL; + } + chip = range->gc; + at91_chip = container_of(chip, struct at91_gpio_chip, chip); + + dev_dbg(npct->dev, "enable pin %u as GPIO\n", offset); + + mask = 1 << (offset - chip->base); + + dev_dbg(npct->dev, "enable pin %u as PIO%c%d 0x%x\n", + offset, 'A' + range->id, offset - chip->base, mask); + + writel_relaxed(mask, at91_chip->regbase + PIO_PER); + + return 0; +} + +void at91_gpio_disable_free(struct pinctrl_dev *pctldev, + struct pinctrl_gpio_range *range, + unsigned offset) +{ + struct at91_pinctrl *npct = pinctrl_dev_get_drvdata(pctldev); + + dev_dbg(npct->dev, "disable pin %u as GPIO\n", offset); + /* Set the pin to some default state, GPIO is usually default */ +} + +static struct pinmux_ops at91_pmx_ops = { + .get_functions_count = at91_pmx_get_funcs_count, + .get_function_name = at91_pmx_get_func_name, + .get_function_groups = at91_pmx_get_groups, + .enable = at91_pmx_enable, + .disable = at91_pmx_disable, + .gpio_request_enable = at91_gpio_request_enable, + .gpio_disable_free = at91_gpio_disable_free, +}; + +static int at91_pinconf_get(struct pinctrl_dev *pctldev, + unsigned pin_id, unsigned long *config) +{ + struct at91_pinctrl *info = pinctrl_dev_get_drvdata(pctldev); + void __iomem *pio; + unsigned pin; + + dev_dbg(info->dev, "%s:%d, pin_id=%d, config=0x%lx", __func__, __LINE__, pin_id, *config); + pio = pin_to_controller(info, pin_to_bank(pin_id)); + pin = pin_id % MAX_NB_GPIO_PER_BANK; + + if (at91_mux_get_multidrive(pio, pin)) + *config |= MULTI_DRIVE; + + if (at91_mux_get_pullup(pio, pin)) + *config |= PULL_UP; + + return 0; +} + +static int at91_pinconf_set(struct pinctrl_dev *pctldev, + unsigned pin_id, unsigned long config) +{ + struct at91_pinctrl *info = pinctrl_dev_get_drvdata(pctldev); + unsigned mask; + void __iomem *pio; + + dev_dbg(info->dev, "%s:%d, pin_id=%d, config=0x%lx", __func__, __LINE__, pin_id, config); + pio = pin_to_controller(info, pin_to_bank(pin_id)); + mask = pin_to_mask(pin_id % MAX_NB_GPIO_PER_BANK); + + at91_mux_set_pullup(pio, mask, config & PULL_UP); + at91_mux_set_multidrive(pio, mask, config & MULTI_DRIVE); + return 0; +} + +static void at91_pinconf_dbg_show(struct pinctrl_dev *pctldev, + struct seq_file *s, unsigned pin_id) +{ + +} + +static void at91_pinconf_group_dbg_show(struct pinctrl_dev *pctldev, + struct seq_file *s, unsigned group) +{ +} + +struct pinconf_ops at91_pinconf_ops = { + .pin_config_get = at91_pinconf_get, + .pin_config_set = at91_pinconf_set, + .pin_config_dbg_show = at91_pinconf_dbg_show, + .pin_config_group_dbg_show = at91_pinconf_group_dbg_show, +}; + +static struct pinctrl_desc at91_pinctrl_desc = { + .pctlops = &at91_pctrl_ops, + .pmxops = &at91_pmx_ops, + .confops = &at91_pinconf_ops, + .owner = THIS_MODULE, +}; + +static const char *gpio_compat = "atmel,at91rm9200-gpio"; + +static void __devinit at91_pinctrl_child_count(struct at91_pinctrl *info, + struct device_node *np) +{ + struct device_node *child; + + for_each_child_of_node(np, child) { + if (of_device_is_compatible(child, gpio_compat)) { + info->nbanks++; + } else { + info->nfunctions++; + info->ngroups += of_get_child_count(child); + } + } +} + +static int __devinit at91_pinctrl_mux_mask(struct at91_pinctrl *info, + struct device_node *np) +{ + int ret = 0; + int size; + const const __be32 *list; + + list = of_get_property(np, "atmel,mux-mask", &size); + if (!list) { + dev_err(info->dev, "can not read the mux-mask of %d\n", size); + return -EINVAL; + } + + size /= sizeof(*list); + if (!size || size % info->nbanks) { + dev_err(info->dev, "wrong mux mask array should be by %d\n", info->nbanks); + return -EINVAL; + } + info->nmux = size / info->nbanks; + + info->mux_mask = devm_kzalloc(info->dev, sizeof(u32) * size, GFP_KERNEL); + if (!info->mux_mask) { + dev_err(info->dev, "could not alloc mux_mask\n"); + return -ENOMEM; + } + + ret = of_property_read_u32_array(np, "atmel,mux-mask", + info->mux_mask, size); + if (ret) + dev_err(info->dev, "can not read the mux-mask of %d\n", size); + return ret; +} + +static int __devinit at91_pinctrl_parse_groups(struct device_node *np, + struct at91_pin_group *grp, + struct at91_pinctrl *info, + u32 index) +{ + struct at91_pmx_pin *pin; + int size; + const const __be32 *list; + int i, j; + + dev_dbg(info->dev, "group(%d): %s\n", index, np->name); + + /* Initialise group */ + grp->name = np->name; + + /* + * the binding format is atmel,pins = , + * do sanity check and calculate pins number + */ + list = of_get_property(np, "atmel,pins", &size); + /* we do not check return since it's safe node passed down */ + size /= sizeof(*list); + if (!size || size % 4) { + dev_err(info->dev, "wrong pins number or pins and configs should be by 4\n"); + return -EINVAL; + } + + grp->npins = size / 4; + pin = grp->pins_conf = devm_kzalloc(info->dev, grp->npins * sizeof(struct at91_pmx_pin), + GFP_KERNEL); + grp->pins = devm_kzalloc(info->dev, grp->npins * sizeof(unsigned int), + GFP_KERNEL); + if (!grp->pins_conf || !grp->pins) + return -ENOMEM; + + for (i = 0, j = 0; i < size; i += 4, j++) { + pin->bank = be32_to_cpu(*list++); + pin->pin = be32_to_cpu(*list++); + grp->pins[j] = pin->bank * MAX_NB_GPIO_PER_BANK + pin->pin; + pin->mux = be32_to_cpu(*list++); + pin->conf = be32_to_cpu(*list++); + + at91_pin_dbg(info->dev, pin); + pin++; + } + + return 0; +} + +static int __devinit at91_pinctrl_parse_functions(struct device_node *np, + struct at91_pinctrl *info, u32 index) +{ + struct device_node *child; + struct at91_pmx_func *func; + struct at91_pin_group *grp; + int ret; + static u32 grp_index; + u32 i = 0; + + dev_dbg(info->dev, "parse function(%d): %s\n", index, np->name); + + func = &info->functions[index]; + + /* Initialise function */ + func->name = np->name; + func->ngroups = of_get_child_count(np); + if (func->ngroups <= 0) { + dev_err(info->dev, "no groups defined\n"); + return -EINVAL; + } + func->groups = devm_kzalloc(info->dev, + func->ngroups * sizeof(char *), GFP_KERNEL); + if (!func->groups) + return -ENOMEM; + + for_each_child_of_node(np, child) { + func->groups[i] = child->name; + grp = &info->groups[grp_index++]; + ret = at91_pinctrl_parse_groups(child, grp, info, i++); + if (ret) + return ret; + } + + return 0; +} + +static struct of_device_id at91_pinctrl_of_match[] __devinitdata = { + { .compatible = "atmel,at91sam9x5-pinctrl", .data = &at91sam9x5_ops }, + { .compatible = "atmel,at91rm9200-pinctrl", .data = &at91rm9200_ops }, + { /* sentinel */ } +}; + +static int __devinit at91_pinctrl_probe_dt(struct platform_device *pdev, + struct at91_pinctrl *info) +{ + int ret = 0; + int i, j; + uint32_t *tmp; + struct device_node *np = pdev->dev.of_node; + struct device_node *child; + + if (!np) + return -ENODEV; + + info->dev = &pdev->dev; + info->ops = + of_match_device(at91_pinctrl_of_match, &pdev->dev)->data; + at91_pinctrl_child_count(info, np); + + if (info->nbanks < 1) { + dev_err(&pdev->dev, "you need to specify atleast one gpio-controller\n"); + return -EINVAL; + } + + ret = at91_pinctrl_mux_mask(info, np); + if (ret) + return ret; + + dev_dbg(&pdev->dev, "nmux = %d\n", info->nmux); + + dev_dbg(&pdev->dev, "mux-mask\n"); + tmp = info->mux_mask; + for (i = 0; i < info->nbanks; i++) { + for (j = 0; j < info->nmux; j++, tmp++) { + dev_dbg(&pdev->dev, "%d:%d\t0x%x\n", i, j, tmp[0]); + } + } + + dev_dbg(&pdev->dev, "nfunctions = %d\n", info->nfunctions); + dev_dbg(&pdev->dev, "ngroups = %d\n", info->ngroups); + info->functions = devm_kzalloc(&pdev->dev, info->nfunctions * sizeof(struct at91_pmx_func), + GFP_KERNEL); + if (!info->functions) + return -ENOMEM; + + info->groups = devm_kzalloc(&pdev->dev, info->ngroups * sizeof(struct at91_pin_group), + GFP_KERNEL); + if (!info->groups) + return -ENOMEM; + + dev_dbg(&pdev->dev, "nbanks = %d\n", info->nbanks); + dev_dbg(&pdev->dev, "nfunctions = %d\n", info->nfunctions); + dev_dbg(&pdev->dev, "ngroups = %d\n", info->ngroups); + + i = 0; + + for_each_child_of_node(np, child) { + if (of_device_is_compatible(child, gpio_compat)) + continue; + ret = at91_pinctrl_parse_functions(child, info, i++); + if (ret) { + dev_err(&pdev->dev, "failed to parse function\n"); + return ret; + } + } + + return 0; +} + +static int __devinit at91_pinctrl_probe(struct platform_device *pdev) +{ + struct at91_pinctrl *info; + struct pinctrl_pin_desc *pdesc; + int ret, i, j ,k; + + info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL); + if (!info) + return -ENOMEM; + + ret = at91_pinctrl_probe_dt(pdev, info); + if (ret) + return ret; + + /* + * We need all the GPIO drivers to probe FIRST, or we will not be able + * to obtain references to the struct gpio_chip * for them, and we + * need this to proceed. + */ + for (i = 0; i < info->nbanks; i++) { + if (!gpio_chips[i]) { + dev_warn(&pdev->dev, "GPIO chip %d not registered yet\n", i); + devm_kfree(&pdev->dev, info); + return -EPROBE_DEFER; + } + } + + at91_pinctrl_desc.name = dev_name(&pdev->dev); + at91_pinctrl_desc.npins = info->nbanks * MAX_NB_GPIO_PER_BANK; + at91_pinctrl_desc.pins = pdesc = + devm_kzalloc(&pdev->dev, sizeof(*pdesc) * at91_pinctrl_desc.npins, GFP_KERNEL); + + if (!at91_pinctrl_desc.pins) + return -ENOMEM; + + for (i = 0 , k = 0; i < info->nbanks; i++) { + for (j = 0; j < MAX_NB_GPIO_PER_BANK; j++, k++) { + pdesc->number = k; + pdesc->name = kasprintf(GFP_KERNEL, "pio%c%d", i + 'A', j); + pdesc++; + } + } + + platform_set_drvdata(pdev, info); + info->pctl = pinctrl_register(&at91_pinctrl_desc, &pdev->dev, info); + + if (!info->pctl) { + dev_err(&pdev->dev, "could not register AT91 pinctrl driver\n"); + ret = -EINVAL; + goto err; + } + + /* We will handle a range of GPIO pins */ + for (i = 0; i < info->nbanks; i++) + pinctrl_add_gpio_range(info->pctl, &gpio_chips[i]->range); + + dev_info(&pdev->dev, "initialized AT91 pinctrl driver\n"); + + return 0; + +err: + return ret; +} + +int __devexit at91_pinctrl_remove(struct platform_device *pdev) +{ + struct at91_pinctrl *info = platform_get_drvdata(pdev); + + pinctrl_unregister(info->pctl); + + return 0; +} + +static int at91_gpio_request(struct gpio_chip *chip, unsigned offset) +{ + /* + * Map back to global GPIO space and request muxing, the direction + * parameter does not matter for this controller. + */ + int gpio = chip->base + offset; + int bank = chip->base / chip->ngpio; + + dev_dbg(chip->dev, "%s:%d pio%c%d(%d)\n", __func__, __LINE__, + 'A' + bank, offset, gpio); + + return pinctrl_request_gpio(gpio); +} + +static void at91_gpio_free(struct gpio_chip *chip, unsigned offset) +{ + int gpio = chip->base + offset; + + pinctrl_free_gpio(gpio); +} + +static int at91_gpio_direction_input(struct gpio_chip *chip, unsigned offset) +{ + struct at91_gpio_chip *at91_gpio = to_at91_gpio_chip(chip); + void __iomem *pio = at91_gpio->regbase; + unsigned mask = 1 << offset; + + writel_relaxed(mask, pio + PIO_ODR); + return 0; +} + +static int at91_gpio_get(struct gpio_chip *chip, unsigned offset) +{ + struct at91_gpio_chip *at91_gpio = to_at91_gpio_chip(chip); + void __iomem *pio = at91_gpio->regbase; + unsigned mask = 1 << offset; + u32 pdsr; + + pdsr = readl_relaxed(pio + PIO_PDSR); + return (pdsr & mask) != 0; +} + +static void at91_gpio_set(struct gpio_chip *chip, unsigned offset, + int val) +{ + struct at91_gpio_chip *at91_gpio = to_at91_gpio_chip(chip); + void __iomem *pio = at91_gpio->regbase; + unsigned mask = 1 << offset; + + writel_relaxed(mask, pio + (val ? PIO_SODR : PIO_CODR)); +} + +static int at91_gpio_direction_output(struct gpio_chip *chip, unsigned offset, + int val) +{ + struct at91_gpio_chip *at91_gpio = to_at91_gpio_chip(chip); + void __iomem *pio = at91_gpio->regbase; + unsigned mask = 1 << offset; + + writel_relaxed(mask, pio + (val ? PIO_SODR : PIO_CODR)); + writel_relaxed(mask, pio + PIO_OER); + + return 0; +} + +static int at91_gpio_to_irq(struct gpio_chip *chip, unsigned offset) +{ + struct at91_gpio_chip *at91_gpio = to_at91_gpio_chip(chip); + int virq; + + if (offset < chip->ngpio) + virq = irq_create_mapping(at91_gpio->domain, offset); + else + virq = -ENXIO; + + dev_dbg(chip->dev, "%s: request IRQ for GPIO %d, return %d\n", + chip->label, offset + chip->base, virq); + return virq; +} + +#ifdef CONFIG_DEBUG_FS +static void at91_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip) +{ + enum at91_mux mode; + int i; + struct at91_gpio_chip *at91_gpio = to_at91_gpio_chip(chip); + void __iomem *pio = at91_gpio->regbase; + + for (i = 0; i < chip->ngpio; i++) { + unsigned pin = chip->base + i; + unsigned mask = pin_to_mask(pin); + const char *gpio_label; + u32 pdsr; + + gpio_label = gpiochip_is_requested(chip, i); + if (!gpio_label) + continue; + mode = at91_gpio->ops->get_periph(pio, mask); + seq_printf(s, "[%s] GPIO%s%d: ", + gpio_label, chip->label, i); + if (mode == AT91_MUX_GPIO) { + pdsr = readl_relaxed(pio + PIO_PDSR); + + seq_printf(s, "[gpio] %s\n", + pdsr & mask ? + "set" : "clear"); + } else { + seq_printf(s, "[periph %c]\n", + mode + 'A' - 1); + } + } +} +#else +#define at91_gpio_dbg_show NULL +#endif + +/* Several AIC controller irqs are dispatched through this GPIO handler. + * To use any AT91_PIN_* as an externally triggered IRQ, first call + * at91_set_gpio_input() then maybe enable its glitch filter. + * Then just request_irq() with the pin ID; it works like any ARM IRQ + * handler. + * First implementation always triggers on rising and falling edges + * whereas the newer PIO3 can be additionally configured to trigger on + * level, edge with any polarity. + * + * Alternatively, certain pins may be used directly as IRQ0..IRQ6 after + * configuring them with at91_set_a_periph() or at91_set_b_periph(). + * IRQ0..IRQ6 should be configurable, e.g. level vs edge triggering. + */ + +static void gpio_irq_mask(struct irq_data *d) +{ + struct at91_gpio_chip *at91_gpio = irq_data_get_irq_chip_data(d); + void __iomem *pio = at91_gpio->regbase; + unsigned mask = 1 << d->hwirq; + + if (pio) + writel_relaxed(mask, pio + PIO_IDR); +} + +static void gpio_irq_unmask(struct irq_data *d) +{ + struct at91_gpio_chip *at91_gpio = irq_data_get_irq_chip_data(d); + void __iomem *pio = at91_gpio->regbase; + unsigned mask = 1 << d->hwirq; + + if (pio) + writel_relaxed(mask, pio + PIO_IER); +} + +static int gpio_irq_type(struct irq_data *d, unsigned type) +{ + switch (type) { + case IRQ_TYPE_NONE: + case IRQ_TYPE_EDGE_BOTH: + return 0; + default: + return -EINVAL; + } +} + +/* Alternate irq type for PIO3 support */ +static int alt_gpio_irq_type(struct irq_data *d, unsigned type) +{ + struct at91_gpio_chip *at91_gpio = irq_data_get_irq_chip_data(d); + void __iomem *pio = at91_gpio->regbase; + unsigned mask = 1 << d->hwirq; + + switch (type) { + case IRQ_TYPE_EDGE_RISING: + writel_relaxed(mask, pio + PIO_ESR); + writel_relaxed(mask, pio + PIO_REHLSR); + break; + case IRQ_TYPE_EDGE_FALLING: + writel_relaxed(mask, pio + PIO_ESR); + writel_relaxed(mask, pio + PIO_FELLSR); + break; + case IRQ_TYPE_LEVEL_LOW: + writel_relaxed(mask, pio + PIO_LSR); + writel_relaxed(mask, pio + PIO_FELLSR); + break; + case IRQ_TYPE_LEVEL_HIGH: + writel_relaxed(mask, pio + PIO_LSR); + writel_relaxed(mask, pio + PIO_REHLSR); + break; + case IRQ_TYPE_EDGE_BOTH: + /* + * disable additional interrupt modes: + * fall back to default behavior + */ + writel_relaxed(mask, pio + PIO_AIMDR); + return 0; + case IRQ_TYPE_NONE: + default: + pr_warn("AT91: No type for irq %d\n", gpio_to_irq(d->irq)); + return -EINVAL; + } + + /* enable additional interrupt modes */ + writel_relaxed(mask, pio + PIO_AIMER); + + return 0; +} + +#ifdef CONFIG_PM +static int gpio_irq_set_wake(struct irq_data *d, unsigned state) +{ + struct at91_gpio_chip *at91_gpio = irq_data_get_irq_chip_data(d); + unsigned bank = at91_gpio->pioc_idx; + + if (unlikely(bank >= MAX_GPIO_BANKS)) + return -EINVAL; + + irq_set_irq_wake(at91_gpio->pioc_virq, state); + + return 0; +} +#else +#define gpio_irq_set_wake NULL +#endif + +static struct irq_chip gpio_irqchip = { + .name = "GPIO", + .irq_disable = gpio_irq_mask, + .irq_mask = gpio_irq_mask, + .irq_unmask = gpio_irq_unmask, + /* .irq_set_type is set dynamically */ + .irq_set_wake = gpio_irq_set_wake, +}; + +static void gpio_irq_handler(unsigned irq, struct irq_desc *desc) +{ + struct irq_chip *chip = irq_desc_get_chip(desc); + struct irq_data *idata = irq_desc_get_irq_data(desc); + struct at91_gpio_chip *at91_gpio = irq_data_get_irq_chip_data(idata); + void __iomem *pio = at91_gpio->regbase; + unsigned long isr; + int n; + + chained_irq_enter(chip, desc); + for (;;) { + /* Reading ISR acks pending (edge triggered) GPIO interrupts. + * When there none are pending, we're finished unless we need + * to process multiple banks (like ID_PIOCDE on sam9263). + */ + isr = readl_relaxed(pio + PIO_ISR) & readl_relaxed(pio + PIO_IMR); + if (!isr) { + if (!at91_gpio->next) + break; + at91_gpio = at91_gpio->next; + pio = at91_gpio->regbase; + continue; + } + + n = find_first_bit(&isr, BITS_PER_LONG); + while (n < BITS_PER_LONG) { + generic_handle_irq(irq_find_mapping(at91_gpio->domain, n)); + n = find_next_bit(&isr, BITS_PER_LONG, n + 1); + } + } + chained_irq_exit(chip, desc); + /* now it may re-trigger */ +} + +/* + * This lock class tells lockdep that GPIO irqs are in a different + * category than their parents, so it won't report false recursion. + */ +static struct lock_class_key gpio_lock_class; + +static int at91_gpio_irq_map(struct irq_domain *h, unsigned int virq, + irq_hw_number_t hw) +{ + struct at91_gpio_chip *at91_gpio = h->host_data; + + irq_set_lockdep_class(virq, &gpio_lock_class); + + /* + * Can use the "simple" and not "edge" handler since it's + * shorter, and the AIC handles interrupts sanely. + */ + irq_set_chip_and_handler(virq, &gpio_irqchip, + handle_simple_irq); + set_irq_flags(virq, IRQF_VALID); + irq_set_chip_data(virq, at91_gpio); + + return 0; +} + +static struct irq_domain_ops at91_gpio_ops = { + .map = at91_gpio_irq_map, + .xlate = irq_domain_xlate_twocell, +}; + +static int at91_gpio_of_irq_setup(struct device_node *node, + struct at91_gpio_chip *at91_gpio) +{ + struct at91_gpio_chip *prev = NULL; + struct irq_data *d = irq_get_irq_data(at91_gpio->pioc_virq); + + at91_gpio->pioc_hwirq = irqd_to_hwirq(d); + + /* Setup proper .irq_set_type function */ + gpio_irqchip.irq_set_type = at91_gpio->ops->irq_type; + + /* Disable irqs of this PIO controller */ + writel_relaxed(~0, at91_gpio->regbase + PIO_IDR); + + /* Setup irq domain */ + at91_gpio->domain = irq_domain_add_linear(node, at91_gpio->chip.ngpio, + &at91_gpio_ops, at91_gpio); + if (!at91_gpio->domain) + panic("at91_gpio.%d: couldn't allocate irq domain (DT).\n", + at91_gpio->pioc_idx); + + /* Setup chained handler */ + if (at91_gpio->pioc_idx) + prev = gpio_chips[at91_gpio->pioc_idx - 1]; + + /* The toplevel handler handles one bank of GPIOs, except + * on some SoC it can handles up to three... + * We only set up the handler for the first of the list. + */ + if (prev && prev->next == at91_gpio) + return 0; + + irq_set_chip_data(at91_gpio->pioc_virq, at91_gpio); + irq_set_chained_handler(at91_gpio->pioc_virq, gpio_irq_handler); + + return 0; +} + +/* This structure is replicated for each GPIO block allocated at probe time */ +static struct gpio_chip at91_gpio_template = { + .request = at91_gpio_request, + .free = at91_gpio_free, + .direction_input = at91_gpio_direction_input, + .get = at91_gpio_get, + .direction_output = at91_gpio_direction_output, + .set = at91_gpio_set, + .to_irq = at91_gpio_to_irq, + .dbg_show = at91_gpio_dbg_show, + .can_sleep = 0, + .ngpio = MAX_NB_GPIO_PER_BANK, +}; + +static void __devinit at91_gpio_probe_fixup(void) +{ + unsigned i; + struct at91_gpio_chip *at91_gpio, *last = NULL; + + for (i = 0; i < gpio_banks; i++) { + at91_gpio = gpio_chips[i]; + + /* + * GPIO controller are grouped on some SoC: + * PIOC, PIOD and PIOE can share the same IRQ line + */ + if (last && last->pioc_virq == at91_gpio->pioc_virq) + last->next = at91_gpio; + last = at91_gpio; + } +} + +static struct of_device_id at91_gpio_of_match[] __devinitdata = { + { .compatible = "atmel,at91sam9x5-gpio", .data = &at91sam9x5_ops, }, + { .compatible = "atmel,at91rm9200-gpio", .data = &at91rm9200_ops }, + { /* sentinel */ } +}; + +static int __devinit at91_gpio_probe(struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + struct resource *res; + struct at91_gpio_chip *at91_chip = NULL; + struct gpio_chip *chip; + struct pinctrl_gpio_range *range; + int ret = 0; + int irq; + int alias_idx = of_alias_get_id(np, "gpio"); + uint32_t ngpio; + + BUG_ON(alias_idx >= ARRAY_SIZE(gpio_chips)); + if (gpio_chips[alias_idx]) { + ret = -EBUSY; + goto err; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + ret = -ENOENT; + goto err; + } + + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + ret = irq; + goto err; + } + + at91_chip = devm_kzalloc(&pdev->dev, sizeof(*at91_chip), GFP_KERNEL); + if (!at91_chip) { + ret = -ENOMEM; + goto err; + } + + at91_chip->regbase = devm_request_and_ioremap(&pdev->dev, res); + if (!at91_chip->regbase) { + dev_err(&pdev->dev, "failed to map registers, ignoring.\n"); + ret = -EBUSY; + goto err; + } + + at91_chip->ops = + of_match_device(at91_gpio_of_match, &pdev->dev)->data; + at91_chip->pioc_virq = irq; + at91_chip->pioc_idx = alias_idx; + + at91_chip->clock = clk_get(&pdev->dev, NULL); + if (IS_ERR(at91_chip->clock)) { + dev_err(&pdev->dev, "failed to get clock, ignoring.\n"); + goto err; + } + + if (clk_prepare(at91_chip->clock)) + goto clk_prep_err; + + /* enable PIO controller's clock */ + if (clk_enable(at91_chip->clock)) { + dev_err(&pdev->dev, "failed to enable clock, ignoring.\n"); + goto clk_err; + } + + at91_chip->chip = at91_gpio_template; + + chip = &at91_chip->chip; + chip->of_node = np; + chip->label = dev_name(&pdev->dev); + chip->dev = &pdev->dev; + chip->owner = THIS_MODULE; + chip->base = alias_idx * MAX_NB_GPIO_PER_BANK; + + if (!of_property_read_u32(np, "#gpio-lines", &ngpio)) { + if (ngpio >= MAX_NB_GPIO_PER_BANK) + pr_err("at91_gpio.%d, gpio-nb >= %d failback to %d\n", + alias_idx, MAX_NB_GPIO_PER_BANK, MAX_NB_GPIO_PER_BANK); + else + chip->ngpio = ngpio; + } + + range = &at91_chip->range; + range->name = chip->label; + range->id = alias_idx; + range->pin_base = range->base = range->id * MAX_NB_GPIO_PER_BANK; + + range->npins = chip->ngpio; + range->gc = chip; + + ret = gpiochip_add(chip); + if (ret) + goto clk_err; + + gpio_chips[alias_idx] = at91_chip; + gpio_banks = max(gpio_banks, alias_idx + 1); + + at91_gpio_probe_fixup(); + + at91_gpio_of_irq_setup(np, at91_chip); + + dev_info(&pdev->dev, "at address %p\n", at91_chip->regbase); + + return 0; + +clk_err: + clk_unprepare(at91_chip->clock); +clk_prep_err: + clk_put(at91_chip->clock); +err: + dev_err(&pdev->dev, "Failure %i for GPIO %i\n", ret, alias_idx); + + return ret; +} + +static struct platform_driver at91_gpio_driver = { + .driver = { + .name = "gpio-at91", + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(at91_gpio_of_match), + }, + .probe = at91_gpio_probe, +}; + +static struct platform_driver at91_pinctrl_driver = { + .driver = { + .name = "pinctrl-at91", + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(at91_pinctrl_of_match), + }, + .probe = at91_pinctrl_probe, + .remove = __devexit_p(at91_pinctrl_remove), +}; + +static int __init at91_pinctrl_init(void) +{ + int ret; + + ret = platform_driver_register(&at91_gpio_driver); + if (ret) + return ret; + return platform_driver_register(&at91_pinctrl_driver); +} +arch_initcall(at91_pinctrl_init); + +static void __exit at91_pinctrl_exit(void) +{ + platform_driver_unregister(&at91_pinctrl_driver); +} + +module_exit(at91_pinctrl_exit); +MODULE_AUTHOR("Jean-Christophe PLAGNIOL-VILLARD "); +MODULE_DESCRIPTION("Atmel AT91 pinctrl driver"); +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3-59-g8ed1b From 07d336006346f7f2e93e690de7c53a7e605f87f9 Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Tue, 2 Oct 2012 11:16:18 -0700 Subject: Documentation/hwmon: remove CONFIG_EXPERIMENTAL This config item has not carried much meaning for a while now and is almost always enabled by default. As agreed during the Linux kernel summit, remove it. CC: Jean Delvare CC: Guenter Roeck CC: Rob Landley Signed-off-by: Kees Cook Signed-off-by: Guenter Roeck --- Documentation/hwmon/submitting-patches | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'Documentation') diff --git a/Documentation/hwmon/submitting-patches b/Documentation/hwmon/submitting-patches index 790f774a3032..843751c41fea 100644 --- a/Documentation/hwmon/submitting-patches +++ b/Documentation/hwmon/submitting-patches @@ -60,8 +60,7 @@ increase the chances of your change being accepted. * Add the driver to Kconfig and Makefile in alphabetical order. -* Make sure that all dependencies are listed in Kconfig. For new drivers, it - is most likely prudent to add a dependency on EXPERIMENTAL. +* Make sure that all dependencies are listed in Kconfig. * Avoid forward declarations if you can. Rearrange the code if necessary. -- cgit v1.2.3-59-g8ed1b From 1102dcab849313bd5a340b299b5cf61b518fbc0f Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Tue, 9 Oct 2012 13:23:57 -0700 Subject: hwmon: (coretemp) Add support for Atom CE4110/4150/4170 TjMax for the CE4100 series of Atom CPUs was previously reported to be 110 degrees C. cpuinfo logs on the web show existing CPU types CE4110, CE4150, and CE4170, reported as "model name : Intel(R) Atom(TM) CPU CE41{1|5|7}0 @ 1.{2|6}0GHz" with model 28 (0x1c) and stepping 10 (0x0a). Add the three known variants to the tjmax table. Signed-off-by: Guenter Roeck cc: stable@vger.kernel.org Acked-by: Jean Delvare --- Documentation/hwmon/coretemp | 1 + drivers/hwmon/coretemp.c | 7 +++++-- 2 files changed, 6 insertions(+), 2 deletions(-) (limited to 'Documentation') diff --git a/Documentation/hwmon/coretemp b/Documentation/hwmon/coretemp index c86b50c03ea8..f17256f069ba 100644 --- a/Documentation/hwmon/coretemp +++ b/Documentation/hwmon/coretemp @@ -105,6 +105,7 @@ Process Processor TjMax(C) 330/230 125 E680/660/640/620 90 E680T/660T/640T/620T 110 + CE4170/4150/4110 110 45nm Core2 Processors Solo ULV SU3500/3300 100 diff --git a/drivers/hwmon/coretemp.c b/drivers/hwmon/coretemp.c index 984a3f13923b..47b8d84b489d 100644 --- a/drivers/hwmon/coretemp.c +++ b/drivers/hwmon/coretemp.c @@ -205,8 +205,11 @@ static const struct tjmax __cpuinitconst tjmax_table[] = { { "CPU N455", 100000 }, { "CPU N470", 100000 }, { "CPU N475", 100000 }, - { "CPU 230", 100000 }, - { "CPU 330", 125000 }, + { "CPU 230", 100000 }, /* Model 0x1c, stepping 2 */ + { "CPU 330", 125000 }, /* Model 0x1c, stepping 2 */ + { "CPU CE4110", 110000 }, /* Model 0x1c, stepping 10 */ + { "CPU CE4150", 110000 }, /* Model 0x1c, stepping 10 */ + { "CPU CE4170", 110000 }, /* Model 0x1c, stepping 10 */ }; static int __cpuinit adjust_tjmax(struct cpuinfo_x86 *c, u32 id, -- cgit v1.2.3-59-g8ed1b From ac5dbea0d483bc0f6281f55261fab1dee7e6ac96 Mon Sep 17 00:00:00 2001 From: Daniel Mack Date: Sun, 7 Oct 2012 17:51:24 +0200 Subject: ASoC: ak4104: add DT bindings Signed-off-by: Daniel Mack Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/sound/ak4104.txt | 17 +++++++++++++++++ sound/soc/codecs/ak4104.c | 7 +++++++ 2 files changed, 24 insertions(+) create mode 100644 Documentation/devicetree/bindings/sound/ak4104.txt (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/sound/ak4104.txt b/Documentation/devicetree/bindings/sound/ak4104.txt new file mode 100644 index 000000000000..1f8e5066d443 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/ak4104.txt @@ -0,0 +1,17 @@ +AK4104 S/PDIF transmitter + +This device supports SPI mode only. + +Required properties: + + - compatible : "asahi-kasei,ak4104" + + - reg : The chip select number on the SPI bus + +Example: + +spdif: ak4104@0 { + compatible = "asahi-kasei,ak4104"; + reg = <0>; + spi-max-frequency = <5000000>; +}; diff --git a/sound/soc/codecs/ak4104.c b/sound/soc/codecs/ak4104.c index d14163f57224..d4d485808894 100644 --- a/sound/soc/codecs/ak4104.c +++ b/sound/soc/codecs/ak4104.c @@ -258,10 +258,17 @@ static int __devexit ak4104_spi_remove(struct spi_device *spi) return 0; } +static const struct of_device_id ak4104_of_match[] = { + { .compatible = "asahi-kasei,ak4104", }, + { } +}; +MODULE_DEVICE_TABLE(of, ak4104_of_match); + static struct spi_driver ak4104_spi_driver = { .driver = { .name = DRV_NAME, .owner = THIS_MODULE, + .of_match_table = ak4104_of_match, }, .probe = ak4104_spi_probe, .remove = __devexit_p(ak4104_spi_remove), -- cgit v1.2.3-59-g8ed1b From 293750f9bcb3368d5298ed1a091b5402de359d3b Mon Sep 17 00:00:00 2001 From: Daniel Mack Date: Thu, 4 Oct 2012 14:03:23 +0200 Subject: ASoC: cs4271: add support for AMUTEB=BMUTEC feature The CS4271 has a feature to sync its analog mute flags, so one mute circuitry can be used for both channels. Give users access to this feature with a new DT property and a flag in the platform data. Signed-off-by: Daniel Mack Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/sound/cs4271.txt | 2 ++ include/sound/cs4271.h | 1 + sound/soc/codecs/cs4271.c | 21 ++++++++++++++++++--- 3 files changed, 21 insertions(+), 3 deletions(-) (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/sound/cs4271.txt b/Documentation/devicetree/bindings/sound/cs4271.txt index c81b5fd5a5bc..a850fb9c88ea 100644 --- a/Documentation/devicetree/bindings/sound/cs4271.txt +++ b/Documentation/devicetree/bindings/sound/cs4271.txt @@ -18,6 +18,8 @@ Optional properties: - reset-gpio: a GPIO spec to define which pin is connected to the chip's !RESET pin + - cirrus,amuteb-eq-bmutec: When given, the Codec's AMUTEB=BMUTEC flag + is enabled. Examples: diff --git a/include/sound/cs4271.h b/include/sound/cs4271.h index 50a059e7d116..6d9e15ed1dcf 100644 --- a/include/sound/cs4271.h +++ b/include/sound/cs4271.h @@ -19,6 +19,7 @@ struct cs4271_platform_data { int gpio_nreset; /* GPIO driving Reset pin, if any */ + int amutec_eq_bmutec:1; /* flag to enable AMUTEC=BMUTEC */ }; #endif /* __CS4271_H */ diff --git a/sound/soc/codecs/cs4271.c b/sound/soc/codecs/cs4271.c index f994af34f552..2ac5fe61a96c 100644 --- a/sound/soc/codecs/cs4271.c +++ b/sound/soc/codecs/cs4271.c @@ -474,15 +474,25 @@ static int cs4271_probe(struct snd_soc_codec *codec) struct cs4271_platform_data *cs4271plat = codec->dev->platform_data; int ret; int gpio_nreset = -EINVAL; + int amutec_eq_bmutec = 0; #ifdef CONFIG_OF - if (of_match_device(cs4271_dt_ids, codec->dev)) + if (of_match_device(cs4271_dt_ids, codec->dev)) { gpio_nreset = of_get_named_gpio(codec->dev->of_node, "reset-gpio", 0); + + if (!of_get_property(codec->dev->of_node, + "cirrus,amutec-eq-bmutec", NULL)) + amutec_eq_bmutec = 1; + } #endif - if (cs4271plat && gpio_is_valid(cs4271plat->gpio_nreset)) - gpio_nreset = cs4271plat->gpio_nreset; + if (cs4271plat) { + if (gpio_is_valid(cs4271plat->gpio_nreset)) + gpio_nreset = cs4271plat->gpio_nreset; + + amutec_eq_bmutec = cs4271plat->amutec_eq_bmutec; + } if (gpio_nreset >= 0) if (gpio_request(gpio_nreset, "CS4271 Reset")) @@ -528,6 +538,11 @@ static int cs4271_probe(struct snd_soc_codec *codec) /* Power-up sequence requires 85 uS */ udelay(85); + if (amutec_eq_bmutec) + snd_soc_update_bits(codec, CS4271_MODE2, + CS4271_MODE2_MUTECAEQUB, + CS4271_MODE2_MUTECAEQUB); + return snd_soc_add_codec_controls(codec, cs4271_snd_controls, ARRAY_SIZE(cs4271_snd_controls)); } -- cgit v1.2.3-59-g8ed1b From 5bdfba29f18f0a36df8e28328315213ea47eb529 Mon Sep 17 00:00:00 2001 From: Shawn Guo Date: Fri, 14 Sep 2012 15:19:00 +0800 Subject: i2c: imx: remove cpu_is_xxx by using platform_device_id This is some amount of work left/forgot from device tree conversion. Instead of checking cpu_is_xxx to determine the controller type, the driver should use platform_device_id, which should match the device tree compatible string. The patch changes the driver to use platform_device_id rather than cpu_is_xxx to determine the controller type/version. It also updates the platform code and device tree source accordingly. As the result, mach/hardware.h inclusion gets removed from the driver. Signed-off-by: Shawn Guo Acked-by: Sascha Hauer Acked-by: Arnd Bergmann Cc: Wolfram Sang Cc: linux-i2c@vger.kernel.org --- .../devicetree/bindings/i2c/fsl-imx-i2c.txt | 4 +-- arch/arm/boot/dts/imx27.dtsi | 4 +-- arch/arm/boot/dts/imx51.dtsi | 4 +-- arch/arm/boot/dts/imx53.dtsi | 6 ++-- arch/arm/boot/dts/imx6q.dtsi | 6 ++-- arch/arm/mach-imx/clk-imx1.c | 2 +- arch/arm/mach-imx/clk-imx21.c | 2 +- arch/arm/mach-imx/clk-imx25.c | 6 ++-- arch/arm/mach-imx/clk-imx27.c | 4 +-- arch/arm/mach-imx/clk-imx31.c | 6 ++-- arch/arm/mach-imx/clk-imx35.c | 6 ++-- arch/arm/mach-imx/clk-imx51-imx53.c | 8 ++--- arch/arm/mach-imx/devices/devices-common.h | 1 + arch/arm/mach-imx/devices/platform-imx-i2c.c | 28 ++++++++------- arch/arm/mach-imx/imx27-dt.c | 4 +-- arch/arm/mach-imx/imx51-dt.c | 4 +-- arch/arm/mach-imx/mach-imx53.c | 6 ++-- drivers/i2c/busses/i2c-imx.c | 40 +++++++++++++++++++--- 18 files changed, 87 insertions(+), 54 deletions(-) (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/i2c/fsl-imx-i2c.txt b/Documentation/devicetree/bindings/i2c/fsl-imx-i2c.txt index f3cf43b66f7e..3614242e7732 100644 --- a/Documentation/devicetree/bindings/i2c/fsl-imx-i2c.txt +++ b/Documentation/devicetree/bindings/i2c/fsl-imx-i2c.txt @@ -12,13 +12,13 @@ Optional properties: Examples: i2c@83fc4000 { /* I2C2 on i.MX51 */ - compatible = "fsl,imx51-i2c", "fsl,imx1-i2c"; + compatible = "fsl,imx51-i2c", "fsl,imx21-i2c"; reg = <0x83fc4000 0x4000>; interrupts = <63>; }; i2c@70038000 { /* HS-I2C on i.MX51 */ - compatible = "fsl,imx51-i2c", "fsl,imx1-i2c"; + compatible = "fsl,imx51-i2c", "fsl,imx21-i2c"; reg = <0x70038000 0x4000>; interrupts = <64>; clock-frequency = <400000>; diff --git a/arch/arm/boot/dts/imx27.dtsi b/arch/arm/boot/dts/imx27.dtsi index 3e54f1498841..67d672792b0d 100644 --- a/arch/arm/boot/dts/imx27.dtsi +++ b/arch/arm/boot/dts/imx27.dtsi @@ -113,7 +113,7 @@ i2c1: i2c@10012000 { #address-cells = <1>; #size-cells = <0>; - compatible = "fsl,imx27-i2c", "fsl,imx1-i2c"; + compatible = "fsl,imx27-i2c", "fsl,imx21-i2c"; reg = <0x10012000 0x1000>; interrupts = <12>; status = "disabled"; @@ -205,7 +205,7 @@ i2c2: i2c@1001d000 { #address-cells = <1>; #size-cells = <0>; - compatible = "fsl,imx27-i2c", "fsl,imx1-i2c"; + compatible = "fsl,imx27-i2c", "fsl,imx21-i2c"; reg = <0x1001d000 0x1000>; interrupts = <1>; status = "disabled"; diff --git a/arch/arm/boot/dts/imx51.dtsi b/arch/arm/boot/dts/imx51.dtsi index 75d069fcf897..54aea74769a1 100644 --- a/arch/arm/boot/dts/imx51.dtsi +++ b/arch/arm/boot/dts/imx51.dtsi @@ -377,7 +377,7 @@ i2c@83fc4000 { /* I2C2 */ #address-cells = <1>; #size-cells = <0>; - compatible = "fsl,imx51-i2c", "fsl,imx1-i2c"; + compatible = "fsl,imx51-i2c", "fsl,imx21-i2c"; reg = <0x83fc4000 0x4000>; interrupts = <63>; status = "disabled"; @@ -386,7 +386,7 @@ i2c@83fc8000 { /* I2C1 */ #address-cells = <1>; #size-cells = <0>; - compatible = "fsl,imx51-i2c", "fsl,imx1-i2c"; + compatible = "fsl,imx51-i2c", "fsl,imx21-i2c"; reg = <0x83fc8000 0x4000>; interrupts = <62>; status = "disabled"; diff --git a/arch/arm/boot/dts/imx53.dtsi b/arch/arm/boot/dts/imx53.dtsi index 76ebb1ad2675..caf09ff73f10 100644 --- a/arch/arm/boot/dts/imx53.dtsi +++ b/arch/arm/boot/dts/imx53.dtsi @@ -432,7 +432,7 @@ i2c@53fec000 { /* I2C3 */ #address-cells = <1>; #size-cells = <0>; - compatible = "fsl,imx53-i2c", "fsl,imx1-i2c"; + compatible = "fsl,imx53-i2c", "fsl,imx21-i2c"; reg = <0x53fec000 0x4000>; interrupts = <64>; status = "disabled"; @@ -488,7 +488,7 @@ i2c@63fc4000 { /* I2C2 */ #address-cells = <1>; #size-cells = <0>; - compatible = "fsl,imx53-i2c", "fsl,imx1-i2c"; + compatible = "fsl,imx53-i2c", "fsl,imx21-i2c"; reg = <0x63fc4000 0x4000>; interrupts = <63>; status = "disabled"; @@ -497,7 +497,7 @@ i2c@63fc8000 { /* I2C1 */ #address-cells = <1>; #size-cells = <0>; - compatible = "fsl,imx53-i2c", "fsl,imx1-i2c"; + compatible = "fsl,imx53-i2c", "fsl,imx21-i2c"; reg = <0x63fc8000 0x4000>; interrupts = <62>; status = "disabled"; diff --git a/arch/arm/boot/dts/imx6q.dtsi b/arch/arm/boot/dts/imx6q.dtsi index f3990b04fecf..f604a44a5c66 100644 --- a/arch/arm/boot/dts/imx6q.dtsi +++ b/arch/arm/boot/dts/imx6q.dtsi @@ -882,7 +882,7 @@ i2c@021a0000 { /* I2C1 */ #address-cells = <1>; #size-cells = <0>; - compatible = "fsl,imx6q-i2c", "fsl,imx1-i2c"; + compatible = "fsl,imx6q-i2c", "fsl,imx21-i2c"; reg = <0x021a0000 0x4000>; interrupts = <0 36 0x04>; clocks = <&clks 125>; @@ -892,7 +892,7 @@ i2c@021a4000 { /* I2C2 */ #address-cells = <1>; #size-cells = <0>; - compatible = "fsl,imx6q-i2c", "fsl,imx1-i2c"; + compatible = "fsl,imx6q-i2c", "fsl,imx21-i2c"; reg = <0x021a4000 0x4000>; interrupts = <0 37 0x04>; clocks = <&clks 126>; @@ -902,7 +902,7 @@ i2c@021a8000 { /* I2C3 */ #address-cells = <1>; #size-cells = <0>; - compatible = "fsl,imx6q-i2c", "fsl,imx1-i2c"; + compatible = "fsl,imx6q-i2c", "fsl,imx21-i2c"; reg = <0x021a8000 0x4000>; interrupts = <0 38 0x04>; clocks = <&clks 127>; diff --git a/arch/arm/mach-imx/clk-imx1.c b/arch/arm/mach-imx/clk-imx1.c index b5f90cc9e371..198238859446 100644 --- a/arch/arm/mach-imx/clk-imx1.c +++ b/arch/arm/mach-imx/clk-imx1.c @@ -95,7 +95,7 @@ int __init mx1_clocks_init(unsigned long fref) clk_register_clkdev(clk[hclk], "ipg", "imx1-uart.1"); clk_register_clkdev(clk[per1], "per", "imx1-uart.2"); clk_register_clkdev(clk[hclk], "ipg", "imx1-uart.2"); - clk_register_clkdev(clk[hclk], NULL, "imx-i2c.0"); + clk_register_clkdev(clk[hclk], NULL, "imx1-i2c.0"); clk_register_clkdev(clk[per2], "per", "imx1-cspi.0"); clk_register_clkdev(clk[dummy], "ipg", "imx1-cspi.0"); clk_register_clkdev(clk[per2], "per", "imx1-cspi.1"); diff --git a/arch/arm/mach-imx/clk-imx21.c b/arch/arm/mach-imx/clk-imx21.c index fbee6a4b1de1..09fc31c58474 100644 --- a/arch/arm/mach-imx/clk-imx21.c +++ b/arch/arm/mach-imx/clk-imx21.c @@ -166,7 +166,7 @@ int __init mx21_clocks_init(unsigned long lref, unsigned long href) clk_register_clkdev(clk[dma_hclk_gate], "ahb", "imx-dma"); clk_register_clkdev(clk[dma_gate], "ipg", "imx-dma"); clk_register_clkdev(clk[wdog_gate], NULL, "imx2-wdt.0"); - clk_register_clkdev(clk[i2c_gate], NULL, "imx-i2c.0"); + clk_register_clkdev(clk[i2c_gate], NULL, "imx21-i2c.0"); clk_register_clkdev(clk[kpp_gate], NULL, "mxc-keypad"); clk_register_clkdev(clk[owire_gate], NULL, "mxc_w1.0"); clk_register_clkdev(clk[brom_gate], "brom", NULL); diff --git a/arch/arm/mach-imx/clk-imx25.c b/arch/arm/mach-imx/clk-imx25.c index 08a143a19e63..f1d75e72e002 100644 --- a/arch/arm/mach-imx/clk-imx25.c +++ b/arch/arm/mach-imx/clk-imx25.c @@ -213,9 +213,9 @@ int __init mx25_clocks_init(void) clk_register_clkdev(clk[per10], "per", "mxc_pwm.3"); clk_register_clkdev(clk[kpp_ipg], NULL, "imx-keypad"); clk_register_clkdev(clk[tsc_ipg], NULL, "mx25-adc"); - clk_register_clkdev(clk[i2c_ipg_per], NULL, "imx-i2c.0"); - clk_register_clkdev(clk[i2c_ipg_per], NULL, "imx-i2c.1"); - clk_register_clkdev(clk[i2c_ipg_per], NULL, "imx-i2c.2"); + clk_register_clkdev(clk[i2c_ipg_per], NULL, "imx21-i2c.0"); + clk_register_clkdev(clk[i2c_ipg_per], NULL, "imx21-i2c.1"); + clk_register_clkdev(clk[i2c_ipg_per], NULL, "imx21-i2c.2"); clk_register_clkdev(clk[fec_ipg], "ipg", "imx25-fec.0"); clk_register_clkdev(clk[fec_ahb], "ahb", "imx25-fec.0"); clk_register_clkdev(clk[dryice_ipg], NULL, "imxdi_rtc.0"); diff --git a/arch/arm/mach-imx/clk-imx27.c b/arch/arm/mach-imx/clk-imx27.c index 31010cb93c6d..c2f111f42ddb 100644 --- a/arch/arm/mach-imx/clk-imx27.c +++ b/arch/arm/mach-imx/clk-imx27.c @@ -247,8 +247,8 @@ int __init mx27_clocks_init(unsigned long fref) clk_register_clkdev(clk[fec_ipg_gate], "ipg", "imx27-fec.0"); clk_register_clkdev(clk[fec_ahb_gate], "ahb", "imx27-fec.0"); clk_register_clkdev(clk[wdog_ipg_gate], NULL, "imx2-wdt.0"); - clk_register_clkdev(clk[i2c1_ipg_gate], NULL, "imx-i2c.0"); - clk_register_clkdev(clk[i2c2_ipg_gate], NULL, "imx-i2c.1"); + clk_register_clkdev(clk[i2c1_ipg_gate], NULL, "imx21-i2c.0"); + clk_register_clkdev(clk[i2c2_ipg_gate], NULL, "imx21-i2c.1"); clk_register_clkdev(clk[owire_ipg_gate], NULL, "mxc_w1.0"); clk_register_clkdev(clk[kpp_ipg_gate], NULL, "imx-keypad"); clk_register_clkdev(clk[emma_ahb_gate], "emma-ahb", "mx2-camera.0"); diff --git a/arch/arm/mach-imx/clk-imx31.c b/arch/arm/mach-imx/clk-imx31.c index a4c298acd04e..76e6462cf9ae 100644 --- a/arch/arm/mach-imx/clk-imx31.c +++ b/arch/arm/mach-imx/clk-imx31.c @@ -155,9 +155,9 @@ int __init mx31_clocks_init(unsigned long fref) clk_register_clkdev(clk[ipg], "ipg", "imx21-uart.3"); clk_register_clkdev(clk[uart5_gate], "per", "imx21-uart.4"); clk_register_clkdev(clk[ipg], "ipg", "imx21-uart.4"); - clk_register_clkdev(clk[i2c1_gate], NULL, "imx-i2c.0"); - clk_register_clkdev(clk[i2c2_gate], NULL, "imx-i2c.1"); - clk_register_clkdev(clk[i2c3_gate], NULL, "imx-i2c.2"); + clk_register_clkdev(clk[i2c1_gate], NULL, "imx21-i2c.0"); + clk_register_clkdev(clk[i2c2_gate], NULL, "imx21-i2c.1"); + clk_register_clkdev(clk[i2c3_gate], NULL, "imx21-i2c.2"); clk_register_clkdev(clk[owire_gate], NULL, "mxc_w1.0"); clk_register_clkdev(clk[sdhc1_gate], NULL, "mxc-mmc.0"); clk_register_clkdev(clk[sdhc2_gate], NULL, "mxc-mmc.1"); diff --git a/arch/arm/mach-imx/clk-imx35.c b/arch/arm/mach-imx/clk-imx35.c index ba26bf3ee933..ee2581254fbc 100644 --- a/arch/arm/mach-imx/clk-imx35.c +++ b/arch/arm/mach-imx/clk-imx35.c @@ -226,9 +226,9 @@ int __init mx35_clocks_init() clk_register_clkdev(clk[fec_gate], NULL, "imx27-fec.0"); clk_register_clkdev(clk[gpt_gate], "per", "imx-gpt.0"); clk_register_clkdev(clk[ipg], "ipg", "imx-gpt.0"); - clk_register_clkdev(clk[i2c1_gate], NULL, "imx-i2c.0"); - clk_register_clkdev(clk[i2c2_gate], NULL, "imx-i2c.1"); - clk_register_clkdev(clk[i2c3_gate], NULL, "imx-i2c.2"); + clk_register_clkdev(clk[i2c1_gate], NULL, "imx21-i2c.0"); + clk_register_clkdev(clk[i2c2_gate], NULL, "imx21-i2c.1"); + clk_register_clkdev(clk[i2c3_gate], NULL, "imx21-i2c.2"); clk_register_clkdev(clk[ipu_gate], NULL, "ipu-core"); clk_register_clkdev(clk[ipu_gate], NULL, "mx3_sdc_fb"); clk_register_clkdev(clk[kpp_gate], NULL, "imx-keypad"); diff --git a/arch/arm/mach-imx/clk-imx51-imx53.c b/arch/arm/mach-imx/clk-imx51-imx53.c index 024587cee620..2449d8ff4a18 100644 --- a/arch/arm/mach-imx/clk-imx51-imx53.c +++ b/arch/arm/mach-imx/clk-imx51-imx53.c @@ -258,8 +258,8 @@ static void __init mx5_clocks_common_init(unsigned long rate_ckil, clk_register_clkdev(clk[cspi_ipg_gate], NULL, "imx35-cspi.2"); clk_register_clkdev(clk[pwm1_ipg_gate], "pwm", "mxc_pwm.0"); clk_register_clkdev(clk[pwm2_ipg_gate], "pwm", "mxc_pwm.1"); - clk_register_clkdev(clk[i2c1_gate], NULL, "imx-i2c.0"); - clk_register_clkdev(clk[i2c2_gate], NULL, "imx-i2c.1"); + clk_register_clkdev(clk[i2c1_gate], NULL, "imx21-i2c.0"); + clk_register_clkdev(clk[i2c2_gate], NULL, "imx21-i2c.1"); clk_register_clkdev(clk[usboh3_per_gate], "per", "mxc-ehci.0"); clk_register_clkdev(clk[usboh3_gate], "ipg", "mxc-ehci.0"); clk_register_clkdev(clk[usboh3_gate], "ahb", "mxc-ehci.0"); @@ -345,7 +345,7 @@ int __init mx51_clocks_init(unsigned long rate_ckil, unsigned long rate_osc, mx5_clocks_common_init(rate_ckil, rate_osc, rate_ckih1, rate_ckih2); - clk_register_clkdev(clk[hsi2c_gate], NULL, "imx-i2c.2"); + clk_register_clkdev(clk[hsi2c_gate], NULL, "imx21-i2c.2"); clk_register_clkdev(clk[mx51_mipi], "mipi_hsp", NULL); clk_register_clkdev(clk[vpu_gate], NULL, "imx51-vpu.0"); clk_register_clkdev(clk[fec_gate], NULL, "imx27-fec.0"); @@ -440,7 +440,7 @@ int __init mx53_clocks_init(unsigned long rate_ckil, unsigned long rate_osc, mx5_clocks_common_init(rate_ckil, rate_osc, rate_ckih1, rate_ckih2); clk_register_clkdev(clk[vpu_gate], NULL, "imx53-vpu.0"); - clk_register_clkdev(clk[i2c3_gate], NULL, "imx-i2c.2"); + clk_register_clkdev(clk[i2c3_gate], NULL, "imx21-i2c.2"); clk_register_clkdev(clk[fec_gate], NULL, "imx25-fec.0"); clk_register_clkdev(clk[ipu_gate], "bus", "imx53-ipu"); clk_register_clkdev(clk[ipu_di0_gate], "di0", "imx53-ipu"); diff --git a/arch/arm/mach-imx/devices/devices-common.h b/arch/arm/mach-imx/devices/devices-common.h index eaf79d220c9a..86bf34cd4865 100644 --- a/arch/arm/mach-imx/devices/devices-common.h +++ b/arch/arm/mach-imx/devices/devices-common.h @@ -118,6 +118,7 @@ struct platform_device *__init imx_add_imx_fb( #include struct imx_imx_i2c_data { + const char *devid; int id; resource_size_t iobase; resource_size_t iosize; diff --git a/arch/arm/mach-imx/devices/platform-imx-i2c.c b/arch/arm/mach-imx/devices/platform-imx-i2c.c index e0c7d6291bed..424ce246fe43 100644 --- a/arch/arm/mach-imx/devices/platform-imx-i2c.c +++ b/arch/arm/mach-imx/devices/platform-imx-i2c.c @@ -9,31 +9,32 @@ #include #include "devices-common.h" -#define imx_imx_i2c_data_entry_single(soc, _id, _hwid, _size) \ +#define imx_imx_i2c_data_entry_single(soc, _devid, _id, _hwid, _size) \ { \ + .devid = _devid, \ .id = _id, \ .iobase = soc ## _I2C ## _hwid ## _BASE_ADDR, \ .iosize = _size, \ .irq = soc ## _INT_I2C ## _hwid, \ } -#define imx_imx_i2c_data_entry(soc, _id, _hwid, _size) \ - [_id] = imx_imx_i2c_data_entry_single(soc, _id, _hwid, _size) +#define imx_imx_i2c_data_entry(soc, _devid, _id, _hwid, _size) \ + [_id] = imx_imx_i2c_data_entry_single(soc, _devid, _id, _hwid, _size) #ifdef CONFIG_SOC_IMX1 const struct imx_imx_i2c_data imx1_imx_i2c_data __initconst = - imx_imx_i2c_data_entry_single(MX1, 0, , SZ_4K); + imx_imx_i2c_data_entry_single(MX1, "imx1-i2c", 0, , SZ_4K); #endif /* ifdef CONFIG_SOC_IMX1 */ #ifdef CONFIG_SOC_IMX21 const struct imx_imx_i2c_data imx21_imx_i2c_data __initconst = - imx_imx_i2c_data_entry_single(MX21, 0, , SZ_4K); + imx_imx_i2c_data_entry_single(MX21, "imx21-i2c", 0, , SZ_4K); #endif /* ifdef CONFIG_SOC_IMX21 */ #ifdef CONFIG_SOC_IMX25 const struct imx_imx_i2c_data imx25_imx_i2c_data[] __initconst = { #define imx25_imx_i2c_data_entry(_id, _hwid) \ - imx_imx_i2c_data_entry(MX25, _id, _hwid, SZ_16K) + imx_imx_i2c_data_entry(MX25, "imx21-i2c", _id, _hwid, SZ_16K) imx25_imx_i2c_data_entry(0, 1), imx25_imx_i2c_data_entry(1, 2), imx25_imx_i2c_data_entry(2, 3), @@ -43,7 +44,7 @@ const struct imx_imx_i2c_data imx25_imx_i2c_data[] __initconst = { #ifdef CONFIG_SOC_IMX27 const struct imx_imx_i2c_data imx27_imx_i2c_data[] __initconst = { #define imx27_imx_i2c_data_entry(_id, _hwid) \ - imx_imx_i2c_data_entry(MX27, _id, _hwid, SZ_4K) + imx_imx_i2c_data_entry(MX27, "imx21-i2c", _id, _hwid, SZ_4K) imx27_imx_i2c_data_entry(0, 1), imx27_imx_i2c_data_entry(1, 2), }; @@ -52,7 +53,7 @@ const struct imx_imx_i2c_data imx27_imx_i2c_data[] __initconst = { #ifdef CONFIG_SOC_IMX31 const struct imx_imx_i2c_data imx31_imx_i2c_data[] __initconst = { #define imx31_imx_i2c_data_entry(_id, _hwid) \ - imx_imx_i2c_data_entry(MX31, _id, _hwid, SZ_4K) + imx_imx_i2c_data_entry(MX31, "imx21-i2c", _id, _hwid, SZ_4K) imx31_imx_i2c_data_entry(0, 1), imx31_imx_i2c_data_entry(1, 2), imx31_imx_i2c_data_entry(2, 3), @@ -62,7 +63,7 @@ const struct imx_imx_i2c_data imx31_imx_i2c_data[] __initconst = { #ifdef CONFIG_SOC_IMX35 const struct imx_imx_i2c_data imx35_imx_i2c_data[] __initconst = { #define imx35_imx_i2c_data_entry(_id, _hwid) \ - imx_imx_i2c_data_entry(MX35, _id, _hwid, SZ_4K) + imx_imx_i2c_data_entry(MX35, "imx21-i2c", _id, _hwid, SZ_4K) imx35_imx_i2c_data_entry(0, 1), imx35_imx_i2c_data_entry(1, 2), imx35_imx_i2c_data_entry(2, 3), @@ -72,7 +73,7 @@ const struct imx_imx_i2c_data imx35_imx_i2c_data[] __initconst = { #ifdef CONFIG_SOC_IMX50 const struct imx_imx_i2c_data imx50_imx_i2c_data[] __initconst = { #define imx50_imx_i2c_data_entry(_id, _hwid) \ - imx_imx_i2c_data_entry(MX50, _id, _hwid, SZ_4K) + imx_imx_i2c_data_entry(MX50, "imx21-i2c", _id, _hwid, SZ_4K) imx50_imx_i2c_data_entry(0, 1), imx50_imx_i2c_data_entry(1, 2), imx50_imx_i2c_data_entry(2, 3), @@ -82,10 +83,11 @@ const struct imx_imx_i2c_data imx50_imx_i2c_data[] __initconst = { #ifdef CONFIG_SOC_IMX51 const struct imx_imx_i2c_data imx51_imx_i2c_data[] __initconst = { #define imx51_imx_i2c_data_entry(_id, _hwid) \ - imx_imx_i2c_data_entry(MX51, _id, _hwid, SZ_4K) + imx_imx_i2c_data_entry(MX51, "imx21-i2c", _id, _hwid, SZ_4K) imx51_imx_i2c_data_entry(0, 1), imx51_imx_i2c_data_entry(1, 2), { + .devid = "imx21-i2c", .id = 2, .iobase = MX51_HSI2C_DMA_BASE_ADDR, .iosize = SZ_16K, @@ -97,7 +99,7 @@ const struct imx_imx_i2c_data imx51_imx_i2c_data[] __initconst = { #ifdef CONFIG_SOC_IMX53 const struct imx_imx_i2c_data imx53_imx_i2c_data[] __initconst = { #define imx53_imx_i2c_data_entry(_id, _hwid) \ - imx_imx_i2c_data_entry(MX53, _id, _hwid, SZ_4K) + imx_imx_i2c_data_entry(MX53, "imx21-i2c", _id, _hwid, SZ_4K) imx53_imx_i2c_data_entry(0, 1), imx53_imx_i2c_data_entry(1, 2), imx53_imx_i2c_data_entry(2, 3), @@ -120,7 +122,7 @@ struct platform_device *__init imx_add_imx_i2c( }, }; - return imx_add_platform_device("imx-i2c", data->id, + return imx_add_platform_device(data->devid, data->id, res, ARRAY_SIZE(res), pdata, sizeof(*pdata)); } diff --git a/arch/arm/mach-imx/imx27-dt.c b/arch/arm/mach-imx/imx27-dt.c index 83c56fb46aa2..43fbf8640109 100644 --- a/arch/arm/mach-imx/imx27-dt.c +++ b/arch/arm/mach-imx/imx27-dt.c @@ -23,8 +23,8 @@ static const struct of_dev_auxdata imx27_auxdata_lookup[] __initconst = { OF_DEV_AUXDATA("fsl,imx27-uart", MX27_UART2_BASE_ADDR, "imx21-uart.1", NULL), OF_DEV_AUXDATA("fsl,imx27-uart", MX27_UART3_BASE_ADDR, "imx21-uart.2", NULL), OF_DEV_AUXDATA("fsl,imx27-fec", MX27_FEC_BASE_ADDR, "imx27-fec.0", NULL), - OF_DEV_AUXDATA("fsl,imx27-i2c", MX27_I2C1_BASE_ADDR, "imx-i2c.0", NULL), - OF_DEV_AUXDATA("fsl,imx27-i2c", MX27_I2C2_BASE_ADDR, "imx-i2c.1", NULL), + OF_DEV_AUXDATA("fsl,imx27-i2c", MX27_I2C1_BASE_ADDR, "imx21-i2c.0", NULL), + OF_DEV_AUXDATA("fsl,imx27-i2c", MX27_I2C2_BASE_ADDR, "imx21-i2c.1", NULL), OF_DEV_AUXDATA("fsl,imx27-cspi", MX27_CSPI1_BASE_ADDR, "imx27-cspi.0", NULL), OF_DEV_AUXDATA("fsl,imx27-cspi", MX27_CSPI2_BASE_ADDR, "imx27-cspi.1", NULL), OF_DEV_AUXDATA("fsl,imx27-cspi", MX27_CSPI3_BASE_ADDR, "imx27-cspi.2", NULL), diff --git a/arch/arm/mach-imx/imx51-dt.c b/arch/arm/mach-imx/imx51-dt.c index e105f12f1e6f..a0391a14ad22 100644 --- a/arch/arm/mach-imx/imx51-dt.c +++ b/arch/arm/mach-imx/imx51-dt.c @@ -37,8 +37,8 @@ static const struct of_dev_auxdata imx51_auxdata_lookup[] __initconst = { OF_DEV_AUXDATA("fsl,imx51-ecspi", MX51_ECSPI1_BASE_ADDR, "imx51-ecspi.0", NULL), OF_DEV_AUXDATA("fsl,imx51-ecspi", MX51_ECSPI2_BASE_ADDR, "imx51-ecspi.1", NULL), OF_DEV_AUXDATA("fsl,imx51-cspi", MX51_CSPI_BASE_ADDR, "imx35-cspi.0", NULL), - OF_DEV_AUXDATA("fsl,imx51-i2c", MX51_I2C1_BASE_ADDR, "imx-i2c.0", NULL), - OF_DEV_AUXDATA("fsl,imx51-i2c", MX51_I2C2_BASE_ADDR, "imx-i2c.1", NULL), + OF_DEV_AUXDATA("fsl,imx51-i2c", MX51_I2C1_BASE_ADDR, "imx21-i2c.0", NULL), + OF_DEV_AUXDATA("fsl,imx51-i2c", MX51_I2C2_BASE_ADDR, "imx21-i2c.1", NULL), OF_DEV_AUXDATA("fsl,imx51-sdma", MX51_SDMA_BASE_ADDR, "imx35-sdma", NULL), OF_DEV_AUXDATA("fsl,imx51-wdt", MX51_WDOG1_BASE_ADDR, "imx2-wdt.0", NULL), { /* sentinel */ } diff --git a/arch/arm/mach-imx/mach-imx53.c b/arch/arm/mach-imx/mach-imx53.c index aaa90a781b35..0e0aadfb625e 100644 --- a/arch/arm/mach-imx/mach-imx53.c +++ b/arch/arm/mach-imx/mach-imx53.c @@ -43,9 +43,9 @@ static const struct of_dev_auxdata imx53_auxdata_lookup[] __initconst = { OF_DEV_AUXDATA("fsl,imx53-ecspi", MX53_ECSPI1_BASE_ADDR, "imx51-ecspi.0", NULL), OF_DEV_AUXDATA("fsl,imx53-ecspi", MX53_ECSPI2_BASE_ADDR, "imx51-ecspi.1", NULL), OF_DEV_AUXDATA("fsl,imx53-cspi", MX53_CSPI_BASE_ADDR, "imx35-cspi.0", NULL), - OF_DEV_AUXDATA("fsl,imx53-i2c", MX53_I2C1_BASE_ADDR, "imx-i2c.0", NULL), - OF_DEV_AUXDATA("fsl,imx53-i2c", MX53_I2C2_BASE_ADDR, "imx-i2c.1", NULL), - OF_DEV_AUXDATA("fsl,imx53-i2c", MX53_I2C3_BASE_ADDR, "imx-i2c.2", NULL), + OF_DEV_AUXDATA("fsl,imx53-i2c", MX53_I2C1_BASE_ADDR, "imx21-i2c.0", NULL), + OF_DEV_AUXDATA("fsl,imx53-i2c", MX53_I2C2_BASE_ADDR, "imx21-i2c.1", NULL), + OF_DEV_AUXDATA("fsl,imx53-i2c", MX53_I2C3_BASE_ADDR, "imx21-i2c.2", NULL), OF_DEV_AUXDATA("fsl,imx53-sdma", MX53_SDMA_BASE_ADDR, "imx35-sdma", NULL), OF_DEV_AUXDATA("fsl,imx53-wdt", MX53_WDOG1_BASE_ADDR, "imx2-wdt.0", NULL), { /* sentinel */ } diff --git a/drivers/i2c/busses/i2c-imx.c b/drivers/i2c/busses/i2c-imx.c index 2ef162d148cb..b9734747d610 100644 --- a/drivers/i2c/busses/i2c-imx.c +++ b/drivers/i2c/busses/i2c-imx.c @@ -52,8 +52,6 @@ #include #include #include - -#include #include /** Defines ******************************************************************** @@ -115,6 +113,11 @@ static u16 __initdata i2c_clk_div[50][2] = { { 3072, 0x1E }, { 3840, 0x1F } }; +enum imx_i2c_type { + IMX1_I2C, + IMX21_I2C, +}; + struct imx_i2c_struct { struct i2c_adapter adapter; struct clk *clk; @@ -124,13 +127,33 @@ struct imx_i2c_struct { unsigned int disable_delay; int stopped; unsigned int ifdr; /* IMX_I2C_IFDR */ + enum imx_i2c_type devtype; +}; + +static struct platform_device_id imx_i2c_devtype[] = { + { + .name = "imx1-i2c", + .driver_data = IMX1_I2C, + }, { + .name = "imx21-i2c", + .driver_data = IMX21_I2C, + }, { + /* sentinel */ + } }; +MODULE_DEVICE_TABLE(platform, imx_i2c_devtype); static const struct of_device_id i2c_imx_dt_ids[] = { - { .compatible = "fsl,imx1-i2c", }, + { .compatible = "fsl,imx1-i2c", .data = &imx_i2c_devtype[IMX1_I2C], }, + { .compatible = "fsl,imx21-i2c", .data = &imx_i2c_devtype[IMX21_I2C], }, { /* sentinel */ } }; +static inline int is_imx1_i2c(struct imx_i2c_struct *i2c_imx) +{ + return i2c_imx->devtype == IMX1_I2C; +} + /** Functions for IMX I2C adapter driver *************************************** *******************************************************************************/ @@ -223,7 +246,7 @@ static void i2c_imx_stop(struct imx_i2c_struct *i2c_imx) temp &= ~(I2CR_MSTA | I2CR_MTX); writeb(temp, i2c_imx->base + IMX_I2C_I2CR); } - if (cpu_is_mx1()) { + if (is_imx1_i2c(i2c_imx)) { /* * This delay caused by an i.MXL hardware bug. * If no (or too short) delay, no "STOP" bit will be generated. @@ -465,6 +488,8 @@ static struct i2c_algorithm i2c_imx_algo = { static int __init i2c_imx_probe(struct platform_device *pdev) { + const struct of_device_id *of_id = of_match_device(i2c_imx_dt_ids, + &pdev->dev); struct imx_i2c_struct *i2c_imx; struct resource *res; struct imxi2c_platform_data *pdata = pdev->dev.platform_data; @@ -497,6 +522,10 @@ static int __init i2c_imx_probe(struct platform_device *pdev) return -ENOMEM; } + if (of_id) + pdev->id_entry = of_id->data; + i2c_imx->devtype = pdev->id_entry->driver_data; + /* Setup i2c_imx driver structure */ strlcpy(i2c_imx->adapter.name, pdev->name, sizeof(i2c_imx->adapter.name)); i2c_imx->adapter.owner = THIS_MODULE; @@ -593,7 +622,8 @@ static struct platform_driver i2c_imx_driver = { .name = DRIVER_NAME, .owner = THIS_MODULE, .of_match_table = i2c_imx_dt_ids, - } + }, + .id_table = imx_i2c_devtype, }; static int __init i2c_adap_imx_init(void) -- cgit v1.2.3-59-g8ed1b From 31e54086dd7bb86ad40f1d76a9063f2a95866b87 Mon Sep 17 00:00:00 2001 From: Pawel Moll Date: Mon, 24 Sep 2012 18:56:54 +0100 Subject: regulator: Versatile Express regulator driver Implementation of the regulator framework driver for the Versatile Express voltage control. Devices without voltage constraints (ie. "regulator-[min|max]-microvolt" properties in the DT node) are treated as fixed (or rather read-only) regulators. Signed-off-by: Pawel Moll Signed-off-by: Mark Brown --- .../devicetree/bindings/regulator/vexpress.txt | 32 +++++ drivers/regulator/Kconfig | 7 + drivers/regulator/Makefile | 1 + drivers/regulator/vexpress.c | 146 +++++++++++++++++++++ 4 files changed, 186 insertions(+) create mode 100644 Documentation/devicetree/bindings/regulator/vexpress.txt create mode 100644 drivers/regulator/vexpress.c (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/regulator/vexpress.txt b/Documentation/devicetree/bindings/regulator/vexpress.txt new file mode 100644 index 000000000000..d775f72487aa --- /dev/null +++ b/Documentation/devicetree/bindings/regulator/vexpress.txt @@ -0,0 +1,32 @@ +Versatile Express voltage regulators +------------------------------------ + +Requires node properties: +- "compatible" value: "arm,vexpress-volt" +- "arm,vexpress-sysreg,func" when controlled via vexpress-sysreg + (see Documentation/devicetree/bindings/arm/vexpress-sysreg.txt + for more details) + +Required regulator properties: +- "regulator-name" +- "regulator-always-on" + +Optional regulator properties: +- "regulator-min-microvolt" +- "regulator-max-microvolt" + +See Documentation/devicetree/bindings/regulator/regulator.txt +for more details about the regulator properties. + +When no "regulator-[min|max]-microvolt" properties are defined, +the device is treated as fixed (or rather "read-only") regulator. + +Example: + volt@0 { + compatible = "arm,vexpress-volt"; + arm,vexpress-sysreg,func = <2 0>; + regulator-name = "Cores"; + regulator-min-microvolt = <800000>; + regulator-max-microvolt = <1050000>; + regulator-always-on; + }; diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig index 67d47b59a66d..b44b019b9433 100644 --- a/drivers/regulator/Kconfig +++ b/drivers/regulator/Kconfig @@ -422,6 +422,13 @@ config REGULATOR_TWL4030 This driver supports the voltage regulators provided by this family of companion chips. +config REGULATOR_VEXPRESS + tristate "Versatile Express regulators" + depends on VEXPRESS_CONFIG + help + This driver provides support for voltage regulators available + on the ARM Ltd's Versatile Express platform. + config REGULATOR_WM831X tristate "Wolfson Microelectronics WM831x PMIC regulators" depends on MFD_WM831X diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile index e431eed8a878..9fa7a7bc42d8 100644 --- a/drivers/regulator/Makefile +++ b/drivers/regulator/Makefile @@ -57,6 +57,7 @@ obj-$(CONFIG_REGULATOR_TPS6586X) += tps6586x-regulator.o obj-$(CONFIG_REGULATOR_TPS65910) += tps65910-regulator.o obj-$(CONFIG_REGULATOR_TPS65912) += tps65912-regulator.o obj-$(CONFIG_REGULATOR_TWL4030) += twl-regulator.o +obj-$(CONFIG_REGULATOR_VEXPRESS) += vexpress.o obj-$(CONFIG_REGULATOR_WM831X) += wm831x-dcdc.o obj-$(CONFIG_REGULATOR_WM831X) += wm831x-isink.o obj-$(CONFIG_REGULATOR_WM831X) += wm831x-ldo.o diff --git a/drivers/regulator/vexpress.c b/drivers/regulator/vexpress.c new file mode 100644 index 000000000000..1702945a93a9 --- /dev/null +++ b/drivers/regulator/vexpress.c @@ -0,0 +1,146 @@ +/* + * 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. + * + * Copyright (C) 2012 ARM Limited + */ + +#define DRVNAME "vexpress-regulator" +#define pr_fmt(fmt) DRVNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include + +struct vexpress_regulator { + struct regulator_desc desc; + struct regulator_dev *regdev; + struct vexpress_config_func *func; +}; + +static int vexpress_regulator_get_voltage(struct regulator_dev *regdev) +{ + struct vexpress_regulator *reg = rdev_get_drvdata(regdev); + u32 uV; + int err = vexpress_config_read(reg->func, 0, &uV); + + return err ? err : uV; +} + +static int vexpress_regulator_set_voltage(struct regulator_dev *regdev, + int min_uV, int max_uV, unsigned *selector) +{ + struct vexpress_regulator *reg = rdev_get_drvdata(regdev); + + return vexpress_config_write(reg->func, 0, min_uV); +} + +static struct regulator_ops vexpress_regulator_ops_ro = { + .get_voltage = vexpress_regulator_get_voltage, +}; + +static struct regulator_ops vexpress_regulator_ops = { + .get_voltage = vexpress_regulator_get_voltage, + .set_voltage = vexpress_regulator_set_voltage, +}; + +static int vexpress_regulator_probe(struct platform_device *pdev) +{ + int err; + struct vexpress_regulator *reg; + struct regulator_init_data *init_data; + struct regulator_config config = { }; + + reg = devm_kzalloc(&pdev->dev, sizeof(*reg), GFP_KERNEL); + if (!reg) { + err = -ENOMEM; + goto error_kzalloc; + } + + reg->func = vexpress_config_func_get_by_dev(&pdev->dev); + if (!reg->func) { + err = -ENXIO; + goto error_get_func; + } + + reg->desc.name = dev_name(&pdev->dev); + reg->desc.type = REGULATOR_VOLTAGE; + reg->desc.owner = THIS_MODULE; + reg->desc.continuous_voltage_range = true; + + init_data = of_get_regulator_init_data(&pdev->dev, pdev->dev.of_node); + if (!init_data) { + err = -EINVAL; + goto error_get_regulator_init_data; + } + + init_data->constraints.apply_uV = 0; + if (init_data->constraints.min_uV && init_data->constraints.max_uV) + reg->desc.ops = &vexpress_regulator_ops; + else + reg->desc.ops = &vexpress_regulator_ops_ro; + + config.dev = &pdev->dev; + config.init_data = init_data; + config.driver_data = reg; + config.of_node = pdev->dev.of_node; + + reg->regdev = regulator_register(®->desc, &config); + if (IS_ERR(reg->regdev)) { + err = PTR_ERR(reg->regdev); + goto error_regulator_register; + } + + platform_set_drvdata(pdev, reg); + + return 0; + +error_regulator_register: +error_get_regulator_init_data: + vexpress_config_func_put(reg->func); +error_get_func: +error_kzalloc: + return err; +} + +static int __devexit vexpress_regulator_remove(struct platform_device *pdev) +{ + struct vexpress_regulator *reg = platform_get_drvdata(pdev); + + vexpress_config_func_put(reg->func); + regulator_unregister(reg->regdev); + + return 0; +} + +static struct of_device_id vexpress_regulator_of_match[] = { + { .compatible = "arm,vexpress-volt", }, +}; + +static struct platform_driver vexpress_regulator_driver = { + .probe = vexpress_regulator_probe, + .remove = __devexit_p(vexpress_regulator_remove), + .driver = { + .name = DRVNAME, + .owner = THIS_MODULE, + .of_match_table = vexpress_regulator_of_match, + }, +}; + +module_platform_driver(vexpress_regulator_driver); + +MODULE_AUTHOR("Pawel Moll "); +MODULE_DESCRIPTION("Versatile Express regulator"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:vexpress-regulator"); -- cgit v1.2.3-59-g8ed1b From b33ef91f4b799a5de5904c4b361df97a0c3ec7dd Mon Sep 17 00:00:00 2001 From: Tomasz Figa Date: Thu, 11 Oct 2012 10:11:21 +0200 Subject: Documentation: Update samsung-pinctrl device tree bindings documentation Signed-off-by: Tomasz Figa Reviewed-by: Kyungmin Park Acked-by: Thomas Abraham Signed-off-by: Linus Walleij --- .../bindings/pinctrl/samsung-pinctrl.txt | 118 ++++++++++++++++----- 1 file changed, 93 insertions(+), 25 deletions(-) (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/pinctrl/samsung-pinctrl.txt b/Documentation/devicetree/bindings/pinctrl/samsung-pinctrl.txt index 03dee50532f5..63806e2d49c2 100644 --- a/Documentation/devicetree/bindings/pinctrl/samsung-pinctrl.txt +++ b/Documentation/devicetree/bindings/pinctrl/samsung-pinctrl.txt @@ -13,8 +13,14 @@ Required Properties: - reg: Base address of the pin controller hardware module and length of the address space it occupies. -- interrupts: interrupt specifier for the controller. The format and value of - the interrupt specifier depends on the interrupt parent for the controller. +- Pin banks as child nodes: Pin banks of the controller are represented by child + nodes of the controller node. Bank name is taken from name of the node. Each + bank node must contain following properties: + + - gpio-controller: identifies the node as a gpio controller and pin bank. + - #gpio-cells: number of cells in GPIO specifier. Since the generic GPIO + binding is used, the amount of cells must be specified as 2. See generic + GPIO binding documentation for description of particular cells. - Pin mux/config groups as child nodes: The pin mux (selecting pin function mode) and pin config (pull up/down, driver strength) settings are represented @@ -72,16 +78,24 @@ used as system wakeup events. A. External GPIO Interrupts: For supporting external gpio interrupts, the following properties should be specified in the pin-controller device node. -- interrupt-controller: identifies the controller node as interrupt-parent. -- #interrupt-cells: the value of this property should be 2. - - First Cell: represents the external gpio interrupt number local to the - external gpio interrupt space of the controller. - - Second Cell: flags to identify the type of the interrupt - - 1 = rising edge triggered - - 2 = falling edge triggered - - 3 = rising and falling edge triggered - - 4 = high level triggered - - 8 = low level triggered + - interrupt-parent: phandle of the interrupt parent to which the external + GPIO interrupts are forwarded to. + - interrupts: interrupt specifier for the controller. The format and value of + the interrupt specifier depends on the interrupt parent for the controller. + + In addition, following properties must be present in node of every bank + of pins supporting GPIO interrupts: + + - interrupt-controller: identifies the controller node as interrupt-parent. + - #interrupt-cells: the value of this property should be 2. + - First Cell: represents the external gpio interrupt number local to the + external gpio interrupt space of the controller. + - Second Cell: flags to identify the type of the interrupt + - 1 = rising edge triggered + - 2 = falling edge triggered + - 3 = rising and falling edge triggered + - 4 = high level triggered + - 8 = low level triggered B. External Wakeup Interrupts: For supporting external wakeup interrupts, a child node representing the external wakeup interrupt controller should be @@ -94,6 +108,11 @@ B. External Wakeup Interrupts: For supporting external wakeup interrupts, a found on Samsung Exynos4210 SoC. - interrupt-parent: phandle of the interrupt parent to which the external wakeup interrupts are forwarded to. + - interrupts: interrupt used by multiplexed wakeup interrupts. + + In addition, following properties must be present in node of every bank + of pins supporting wake-up interrupts: + - interrupt-controller: identifies the node as interrupt-parent. - #interrupt-cells: the value of this property should be 2 - First Cell: represents the external wakeup interrupt number local to @@ -105,11 +124,63 @@ B. External Wakeup Interrupts: For supporting external wakeup interrupts, a - 4 = high level triggered - 8 = low level triggered + Node of every bank of pins supporting direct wake-up interrupts (without + multiplexing) must contain following properties: + + - interrupt-parent: phandle of the interrupt parent to which the external + wakeup interrupts are forwarded to. + - interrupts: interrupts of the interrupt parent which are used for external + wakeup interrupts from pins of the bank, must contain interrupts for all + pins of the bank. + Aliases: All the pin controller nodes should be represented in the aliases node using the following format 'pinctrl{n}' where n is a unique number for the alias. +Example: A pin-controller node with pin banks: + + pinctrl_0: pinctrl@11400000 { + compatible = "samsung,pinctrl-exynos4210"; + reg = <0x11400000 0x1000>; + interrupts = <0 47 0>; + + /* ... */ + + /* Pin bank without external interrupts */ + gpy0: gpy0 { + gpio-controller; + #gpio-cells = <2>; + }; + + /* ... */ + + /* Pin bank with external GPIO or muxed wake-up interrupts */ + gpj0: gpj0 { + gpio-controller; + #gpio-cells = <2>; + + interrupt-controller; + #interrupt-cells = <2>; + }; + + /* ... */ + + /* Pin bank with external direct wake-up interrupts */ + gpx0: gpx0 { + gpio-controller; + #gpio-cells = <2>; + + interrupt-controller; + interrupt-parent = <&gic>; + interrupts = <0 16 0>, <0 17 0>, <0 18 0>, <0 19 0>, + <0 20 0>, <0 21 0>, <0 22 0>, <0 23 0>; + #interrupt-cells = <2>; + }; + + /* ... */ + }; + Example 1: A pin-controller node with pin groups. pinctrl_0: pinctrl@11400000 { @@ -117,6 +188,8 @@ Example 1: A pin-controller node with pin groups. reg = <0x11400000 0x1000>; interrupts = <0 47 0>; + /* ... */ + uart0_data: uart0-data { samsung,pins = "gpa0-0", "gpa0-1"; samsung,pin-function = <2>; @@ -158,20 +231,14 @@ Example 2: A pin-controller node with external wakeup interrupt controller node. pinctrl_1: pinctrl@11000000 { compatible = "samsung,pinctrl-exynos4210"; reg = <0x11000000 0x1000>; - interrupts = <0 46 0>; - interrupt-controller; - #interrupt-cells = <2>; + interrupts = <0 46 0> - wakup_eint: wakeup-interrupt-controller { + /* ... */ + + wakeup-interrupt-controller { compatible = "samsung,exynos4210-wakeup-eint"; interrupt-parent = <&gic>; - interrupt-controller; - #interrupt-cells = <2>; - interrupts = <0 16 0>, <0 17 0>, <0 18 0>, <0 19 0>, - <0 20 0>, <0 21 0>, <0 22 0>, <0 23 0>, - <0 24 0>, <0 25 0>, <0 26 0>, <0 27 0>, - <0 28 0>, <0 29 0>, <0 30 0>, <0 31 0>, - <0 32 0>; + interrupts = <0 32 0>; }; }; @@ -190,7 +257,8 @@ Example 4: Set up the default pin state for uart controller. static int s3c24xx_serial_probe(struct platform_device *pdev) { struct pinctrl *pinctrl; - ... - ... + + /* ... */ + pinctrl = devm_pinctrl_get_select_default(&pdev->dev); } -- cgit v1.2.3-59-g8ed1b From 29bb4cc5e0547a7589556f8629e39016c5d203c0 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Sun, 14 Oct 2012 18:50:05 -0700 Subject: docbook: networking: fix file paths for uapi headers Update file paths in Documentation/DocBook/networking.tmpl for uapi headers. Signed-off-by: Randy Dunlap Signed-off-by: Linus Torvalds --- Documentation/DocBook/networking.tmpl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'Documentation') diff --git a/Documentation/DocBook/networking.tmpl b/Documentation/DocBook/networking.tmpl index 59ad69a9d777..29df25016c7c 100644 --- a/Documentation/DocBook/networking.tmpl +++ b/Documentation/DocBook/networking.tmpl @@ -56,7 +56,7 @@ !Enet/core/filter.c Generic Network Statistics -!Iinclude/linux/gen_stats.h +!Iinclude/uapi/linux/gen_stats.h !Enet/core/gen_stats.c !Enet/core/gen_estimator.c @@ -80,7 +80,7 @@ !Enet/wimax/op-rfkill.c !Enet/wimax/stack.c !Iinclude/net/wimax.h -!Iinclude/linux/wimax.h +!Iinclude/uapi/linux/wimax.h -- cgit v1.2.3-59-g8ed1b From 48ed8877244637b52aec0a114cdccd8ec26e66b1 Mon Sep 17 00:00:00 2001 From: Pawel Moll Date: Mon, 17 Sep 2012 18:40:09 +0100 Subject: hwmon: Versatile Express hwmon driver hwmon framework driver for Versatile Express sensors, providing information about board level voltage (only when regulator driver is not configured), currents, temperature and power/energy usage. Labels for the values can be defined as DT properties. Signed-off-by: Pawel Moll Acked-by: Guenter Roeck --- .../devicetree/bindings/hwmon/vexpress.txt | 23 +++ Documentation/hwmon/vexpress | 34 +++ drivers/hwmon/Kconfig | 8 + drivers/hwmon/Makefile | 1 + drivers/hwmon/vexpress.c | 229 +++++++++++++++++++++ 5 files changed, 295 insertions(+) create mode 100644 Documentation/devicetree/bindings/hwmon/vexpress.txt create mode 100644 Documentation/hwmon/vexpress create mode 100644 drivers/hwmon/vexpress.c (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/hwmon/vexpress.txt b/Documentation/devicetree/bindings/hwmon/vexpress.txt new file mode 100644 index 000000000000..9c27ed694bbb --- /dev/null +++ b/Documentation/devicetree/bindings/hwmon/vexpress.txt @@ -0,0 +1,23 @@ +Versatile Express hwmon sensors +------------------------------- + +Requires node properties: +- "compatible" value : one of + "arm,vexpress-volt" + "arm,vexpress-amp" + "arm,vexpress-temp" + "arm,vexpress-power" + "arm,vexpress-energy" +- "arm,vexpress-sysreg,func" when controlled via vexpress-sysreg + (see Documentation/devicetree/bindings/arm/vexpress-sysreg.txt + for more details) + +Optional node properties: +- label : string describing the monitored value + +Example: + energy@0 { + compatible = "arm,vexpress-energy"; + arm,vexpress-sysreg,func = <13 0>; + label = "A15 Jcore"; + }; diff --git a/Documentation/hwmon/vexpress b/Documentation/hwmon/vexpress new file mode 100644 index 000000000000..557d6d5ad90d --- /dev/null +++ b/Documentation/hwmon/vexpress @@ -0,0 +1,34 @@ +Kernel driver vexpress +====================== + +Supported systems: + * ARM Ltd. Versatile Express platform + Prefix: 'vexpress' + Datasheets: + * "Hardware Description" sections of the Technical Reference Manuals + for the Versatile Express boards: + http://infocenter.arm.com/help/topic/com.arm.doc.subset.boards.express/index.html + * Section "4.4.14. System Configuration registers" of the V2M-P1 TRM: + http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0447-/index.html + +Author: Pawel Moll + +Description +----------- + +Versatile Express platform (http://www.arm.com/versatileexpress/) is a +reference & prototyping system for ARM Ltd. processors. It can be set up +from a wide range of boards, each of them containing (apart of the main +chip/FPGA) a number of microcontrollers responsible for platform +configuration and control. Theses microcontrollers can also monitor the +board and its environment by a number of internal and external sensors, +providing information about power lines voltages and currents, board +temperature and power usage. Some of them also calculate consumed energy +and provide a cumulative use counter. + +The configuration devices are _not_ memory mapped and must be accessed +via a custom interface, abstracted by the "vexpress_config" API. + +As these devices are non-discoverable, they must be described in a Device +Tree passed to the kernel. Details of the DT binding for them can be found +in Documentation/devicetree/bindings/hwmon/vexpress.txt. diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index c4633de64465..db213fe958a5 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -1197,6 +1197,14 @@ config SENSORS_TWL4030_MADC This driver can also be built as a module. If so it will be called twl4030-madc-hwmon. +config SENSORS_VEXPRESS + tristate "Versatile Express" + depends on VEXPRESS_CONFIG + help + This driver provides support for hardware sensors available on + the ARM Ltd's Versatile Express platform. It can provide wide + range of information like temperature, power, energy. + config SENSORS_VIA_CPUTEMP tristate "VIA CPU temperature sensor" depends on X86 diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile index 8d5fcb5e8e9f..aac8b7c619d6 100644 --- a/drivers/hwmon/Makefile +++ b/drivers/hwmon/Makefile @@ -121,6 +121,7 @@ obj-$(CONFIG_SENSORS_TMP102) += tmp102.o obj-$(CONFIG_SENSORS_TMP401) += tmp401.o obj-$(CONFIG_SENSORS_TMP421) += tmp421.o obj-$(CONFIG_SENSORS_TWL4030_MADC)+= twl4030-madc-hwmon.o +obj-$(CONFIG_SENSORS_VEXPRESS) += vexpress.o obj-$(CONFIG_SENSORS_VIA_CPUTEMP)+= via-cputemp.o obj-$(CONFIG_SENSORS_VIA686A) += via686a.o obj-$(CONFIG_SENSORS_VT1211) += vt1211.o diff --git a/drivers/hwmon/vexpress.c b/drivers/hwmon/vexpress.c new file mode 100644 index 000000000000..59fd1268e58a --- /dev/null +++ b/drivers/hwmon/vexpress.c @@ -0,0 +1,229 @@ +/* + * 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. + * + * Copyright (C) 2012 ARM Limited + */ + +#define DRVNAME "vexpress-hwmon" +#define pr_fmt(fmt) DRVNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include + +struct vexpress_hwmon_data { + struct device *hwmon_dev; + struct vexpress_config_func *func; +}; + +static ssize_t vexpress_hwmon_name_show(struct device *dev, + struct device_attribute *dev_attr, char *buffer) +{ + const char *compatible = of_get_property(dev->of_node, "compatible", + NULL); + + return sprintf(buffer, "%s\n", compatible); +} + +static ssize_t vexpress_hwmon_label_show(struct device *dev, + struct device_attribute *dev_attr, char *buffer) +{ + const char *label = of_get_property(dev->of_node, "label", NULL); + + if (!label) + return -ENOENT; + + return snprintf(buffer, PAGE_SIZE, "%s\n", label); +} + +static ssize_t vexpress_hwmon_u32_show(struct device *dev, + struct device_attribute *dev_attr, char *buffer) +{ + struct vexpress_hwmon_data *data = dev_get_drvdata(dev); + int err; + u32 value; + + err = vexpress_config_read(data->func, 0, &value); + if (err) + return err; + + return snprintf(buffer, PAGE_SIZE, "%u\n", value / + to_sensor_dev_attr(dev_attr)->index); +} + +static ssize_t vexpress_hwmon_u64_show(struct device *dev, + struct device_attribute *dev_attr, char *buffer) +{ + struct vexpress_hwmon_data *data = dev_get_drvdata(dev); + int err; + u32 value_hi, value_lo; + + err = vexpress_config_read(data->func, 0, &value_lo); + if (err) + return err; + + err = vexpress_config_read(data->func, 1, &value_hi); + if (err) + return err; + + return snprintf(buffer, PAGE_SIZE, "%llu\n", + div_u64(((u64)value_hi << 32) | value_lo, + to_sensor_dev_attr(dev_attr)->index)); +} + +static DEVICE_ATTR(name, S_IRUGO, vexpress_hwmon_name_show, NULL); + +#define VEXPRESS_HWMON_ATTRS(_name, _label_attr, _input_attr) \ +struct attribute *vexpress_hwmon_attrs_##_name[] = { \ + &dev_attr_name.attr, \ + &dev_attr_##_label_attr.attr, \ + &sensor_dev_attr_##_input_attr.dev_attr.attr, \ + NULL \ +} + +#if !defined(CONFIG_REGULATOR_VEXPRESS) +static DEVICE_ATTR(in1_label, S_IRUGO, vexpress_hwmon_label_show, NULL); +static SENSOR_DEVICE_ATTR(in1_input, S_IRUGO, vexpress_hwmon_u32_show, + NULL, 1000); +static VEXPRESS_HWMON_ATTRS(volt, in1_label, in1_input); +static struct attribute_group vexpress_hwmon_group_volt = { + .attrs = vexpress_hwmon_attrs_volt, +}; +#endif + +static DEVICE_ATTR(curr1_label, S_IRUGO, vexpress_hwmon_label_show, NULL); +static SENSOR_DEVICE_ATTR(curr1_input, S_IRUGO, vexpress_hwmon_u32_show, + NULL, 1000); +static VEXPRESS_HWMON_ATTRS(amp, curr1_label, curr1_input); +static struct attribute_group vexpress_hwmon_group_amp = { + .attrs = vexpress_hwmon_attrs_amp, +}; + +static DEVICE_ATTR(temp1_label, S_IRUGO, vexpress_hwmon_label_show, NULL); +static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, vexpress_hwmon_u32_show, + NULL, 1000); +static VEXPRESS_HWMON_ATTRS(temp, temp1_label, temp1_input); +static struct attribute_group vexpress_hwmon_group_temp = { + .attrs = vexpress_hwmon_attrs_temp, +}; + +static DEVICE_ATTR(power1_label, S_IRUGO, vexpress_hwmon_label_show, NULL); +static SENSOR_DEVICE_ATTR(power1_input, S_IRUGO, vexpress_hwmon_u32_show, + NULL, 1); +static VEXPRESS_HWMON_ATTRS(power, power1_label, power1_input); +static struct attribute_group vexpress_hwmon_group_power = { + .attrs = vexpress_hwmon_attrs_power, +}; + +static DEVICE_ATTR(energy1_label, S_IRUGO, vexpress_hwmon_label_show, NULL); +static SENSOR_DEVICE_ATTR(energy1_input, S_IRUGO, vexpress_hwmon_u64_show, + NULL, 1); +static VEXPRESS_HWMON_ATTRS(energy, energy1_label, energy1_input); +static struct attribute_group vexpress_hwmon_group_energy = { + .attrs = vexpress_hwmon_attrs_energy, +}; + +static struct of_device_id vexpress_hwmon_of_match[] = { +#if !defined(CONFIG_REGULATOR_VEXPRESS) + { + .compatible = "arm,vexpress-volt", + .data = &vexpress_hwmon_group_volt, + }, +#endif + { + .compatible = "arm,vexpress-amp", + .data = &vexpress_hwmon_group_amp, + }, { + .compatible = "arm,vexpress-temp", + .data = &vexpress_hwmon_group_temp, + }, { + .compatible = "arm,vexpress-power", + .data = &vexpress_hwmon_group_power, + }, { + .compatible = "arm,vexpress-energy", + .data = &vexpress_hwmon_group_energy, + }, + {} +}; +MODULE_DEVICE_TABLE(of, vexpress_hwmon_of_match); + +static int vexpress_hwmon_probe(struct platform_device *pdev) +{ + int err; + const struct of_device_id *match; + struct vexpress_hwmon_data *data; + + data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + platform_set_drvdata(pdev, data); + + match = of_match_device(vexpress_hwmon_of_match, &pdev->dev); + if (!match) + return -ENODEV; + + data->func = vexpress_config_func_get_by_dev(&pdev->dev); + if (!data->func) + return -ENODEV; + + err = sysfs_create_group(&pdev->dev.kobj, match->data); + if (err) + goto error; + + data->hwmon_dev = hwmon_device_register(&pdev->dev); + if (IS_ERR(data->hwmon_dev)) { + err = PTR_ERR(data->hwmon_dev); + goto error; + } + + return 0; + +error: + sysfs_remove_group(&pdev->dev.kobj, match->data); + vexpress_config_func_put(data->func); + return err; +} + +static int __devexit vexpress_hwmon_remove(struct platform_device *pdev) +{ + struct vexpress_hwmon_data *data = platform_get_drvdata(pdev); + const struct of_device_id *match; + + hwmon_device_unregister(data->hwmon_dev); + + match = of_match_device(vexpress_hwmon_of_match, &pdev->dev); + sysfs_remove_group(&pdev->dev.kobj, match->data); + + vexpress_config_func_put(data->func); + + return 0; +} + +static struct platform_driver vexpress_hwmon_driver = { + .probe = vexpress_hwmon_probe, + .remove = __devexit_p(vexpress_hwmon_remove), + .driver = { + .name = DRVNAME, + .owner = THIS_MODULE, + .of_match_table = vexpress_hwmon_of_match, + }, +}; + +module_platform_driver(vexpress_hwmon_driver); + +MODULE_AUTHOR("Pawel Moll "); +MODULE_DESCRIPTION("Versatile Express hwmon sensors driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:vexpress-hwmon"); -- cgit v1.2.3-59-g8ed1b From 916ca14aaf12a7191118adb51bb95e3c7866380d Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Tue, 16 Oct 2012 09:34:01 -0700 Subject: sparc64: Add global PMU register dumping via sysrq. Signed-off-by: David S. Miller --- Documentation/sysrq.txt | 1 + arch/sparc/include/asm/ptrace.h | 13 ++++- arch/sparc/include/asm/smp_64.h | 2 + arch/sparc/kernel/process_64.c | 120 +++++++++++++++++++++++++++++++++------- arch/sparc/kernel/smp_64.c | 11 ++++ arch/sparc/mm/ultra.S | 64 ++++++++++++++++++++- drivers/tty/sysrq.c | 1 + 7 files changed, 189 insertions(+), 23 deletions(-) (limited to 'Documentation') diff --git a/Documentation/sysrq.txt b/Documentation/sysrq.txt index 642f84495b29..2a4cdda4828e 100644 --- a/Documentation/sysrq.txt +++ b/Documentation/sysrq.txt @@ -116,6 +116,7 @@ On all - write a character to /proc/sysrq-trigger. e.g.: 'w' - Dumps tasks that are in uninterruptable (blocked) state. 'x' - Used by xmon interface on ppc/powerpc platforms. + Show global PMU Registers on sparc64. 'y' - Show global CPU Registers [SPARC-64 specific] diff --git a/arch/sparc/include/asm/ptrace.h b/arch/sparc/include/asm/ptrace.h index 0c6f6b068289..da43bdc62294 100644 --- a/arch/sparc/include/asm/ptrace.h +++ b/arch/sparc/include/asm/ptrace.h @@ -42,7 +42,18 @@ struct global_reg_snapshot { struct thread_info *thread; unsigned long pad1; }; -extern struct global_reg_snapshot global_reg_snapshot[NR_CPUS]; + +struct global_pmu_snapshot { + unsigned long pcr[4]; + unsigned long pic[4]; +}; + +union global_cpu_snapshot { + struct global_reg_snapshot reg; + struct global_pmu_snapshot pmu; +}; + +extern union global_cpu_snapshot global_cpu_snapshot[NR_CPUS]; #define force_successful_syscall_return() \ do { current_thread_info()->syscall_noerror = 1; \ diff --git a/arch/sparc/include/asm/smp_64.h b/arch/sparc/include/asm/smp_64.h index 29862a9e9065..dd3bef4b9896 100644 --- a/arch/sparc/include/asm/smp_64.h +++ b/arch/sparc/include/asm/smp_64.h @@ -48,6 +48,7 @@ extern void smp_fill_in_sib_core_maps(void); extern void cpu_play_dead(void); extern void smp_fetch_global_regs(void); +extern void smp_fetch_global_pmu(void); struct seq_file; void smp_bogo(struct seq_file *); @@ -65,6 +66,7 @@ extern void __cpu_die(unsigned int cpu); #define hard_smp_processor_id() 0 #define smp_fill_in_sib_core_maps() do { } while (0) #define smp_fetch_global_regs() do { } while (0) +#define smp_fetch_global_pmu() do { } while (0) #endif /* !(CONFIG_SMP) */ diff --git a/arch/sparc/kernel/process_64.c b/arch/sparc/kernel/process_64.c index fcaa59421126..d778248ef3f8 100644 --- a/arch/sparc/kernel/process_64.c +++ b/arch/sparc/kernel/process_64.c @@ -27,6 +27,7 @@ #include #include #include +#include #include #include #include @@ -47,6 +48,7 @@ #include #include #include +#include #include "kstack.h" @@ -204,18 +206,22 @@ void show_regs(struct pt_regs *regs) show_stack(current, (unsigned long *) regs->u_regs[UREG_FP]); } -struct global_reg_snapshot global_reg_snapshot[NR_CPUS]; -static DEFINE_SPINLOCK(global_reg_snapshot_lock); +union global_cpu_snapshot global_cpu_snapshot[NR_CPUS]; +static DEFINE_SPINLOCK(global_cpu_snapshot_lock); static void __global_reg_self(struct thread_info *tp, struct pt_regs *regs, int this_cpu) { + struct global_reg_snapshot *rp; + flushw_all(); - global_reg_snapshot[this_cpu].tstate = regs->tstate; - global_reg_snapshot[this_cpu].tpc = regs->tpc; - global_reg_snapshot[this_cpu].tnpc = regs->tnpc; - global_reg_snapshot[this_cpu].o7 = regs->u_regs[UREG_I7]; + rp = &global_cpu_snapshot[this_cpu].reg; + + rp->tstate = regs->tstate; + rp->tpc = regs->tpc; + rp->tnpc = regs->tnpc; + rp->o7 = regs->u_regs[UREG_I7]; if (regs->tstate & TSTATE_PRIV) { struct reg_window *rw; @@ -223,17 +229,17 @@ static void __global_reg_self(struct thread_info *tp, struct pt_regs *regs, rw = (struct reg_window *) (regs->u_regs[UREG_FP] + STACK_BIAS); if (kstack_valid(tp, (unsigned long) rw)) { - global_reg_snapshot[this_cpu].i7 = rw->ins[7]; + rp->i7 = rw->ins[7]; rw = (struct reg_window *) (rw->ins[6] + STACK_BIAS); if (kstack_valid(tp, (unsigned long) rw)) - global_reg_snapshot[this_cpu].rpc = rw->ins[7]; + rp->rpc = rw->ins[7]; } } else { - global_reg_snapshot[this_cpu].i7 = 0; - global_reg_snapshot[this_cpu].rpc = 0; + rp->i7 = 0; + rp->rpc = 0; } - global_reg_snapshot[this_cpu].thread = tp; + rp->thread = tp; } /* In order to avoid hangs we do not try to synchronize with the @@ -261,9 +267,9 @@ void arch_trigger_all_cpu_backtrace(void) if (!regs) regs = tp->kregs; - spin_lock_irqsave(&global_reg_snapshot_lock, flags); + spin_lock_irqsave(&global_cpu_snapshot_lock, flags); - memset(global_reg_snapshot, 0, sizeof(global_reg_snapshot)); + memset(global_cpu_snapshot, 0, sizeof(global_cpu_snapshot)); this_cpu = raw_smp_processor_id(); @@ -272,7 +278,7 @@ void arch_trigger_all_cpu_backtrace(void) smp_fetch_global_regs(); for_each_online_cpu(cpu) { - struct global_reg_snapshot *gp = &global_reg_snapshot[cpu]; + struct global_reg_snapshot *gp = &global_cpu_snapshot[cpu].reg; __global_reg_poll(gp); @@ -295,9 +301,9 @@ void arch_trigger_all_cpu_backtrace(void) } } - memset(global_reg_snapshot, 0, sizeof(global_reg_snapshot)); + memset(global_cpu_snapshot, 0, sizeof(global_cpu_snapshot)); - spin_unlock_irqrestore(&global_reg_snapshot_lock, flags); + spin_unlock_irqrestore(&global_cpu_snapshot_lock, flags); } #ifdef CONFIG_MAGIC_SYSRQ @@ -309,16 +315,90 @@ static void sysrq_handle_globreg(int key) static struct sysrq_key_op sparc_globalreg_op = { .handler = sysrq_handle_globreg, - .help_msg = "Globalregs", + .help_msg = "global-regs(Y)", .action_msg = "Show Global CPU Regs", }; -static int __init sparc_globreg_init(void) +static void __global_pmu_self(int this_cpu) +{ + struct global_pmu_snapshot *pp; + int i, num; + + pp = &global_cpu_snapshot[this_cpu].pmu; + + num = 1; + if (tlb_type == hypervisor && + sun4v_chip_type >= SUN4V_CHIP_NIAGARA4) + num = 4; + + for (i = 0; i < num; i++) { + pp->pcr[i] = pcr_ops->read_pcr(i); + pp->pic[i] = pcr_ops->read_pic(i); + } +} + +static void __global_pmu_poll(struct global_pmu_snapshot *pp) +{ + int limit = 0; + + while (!pp->pcr[0] && ++limit < 100) { + barrier(); + udelay(1); + } +} + +static void pmu_snapshot_all_cpus(void) { - return register_sysrq_key('y', &sparc_globalreg_op); + unsigned long flags; + int this_cpu, cpu; + + spin_lock_irqsave(&global_cpu_snapshot_lock, flags); + + memset(global_cpu_snapshot, 0, sizeof(global_cpu_snapshot)); + + this_cpu = raw_smp_processor_id(); + + __global_pmu_self(this_cpu); + + smp_fetch_global_pmu(); + + for_each_online_cpu(cpu) { + struct global_pmu_snapshot *pp = &global_cpu_snapshot[cpu].pmu; + + __global_pmu_poll(pp); + + printk("%c CPU[%3d]: PCR[%08lx:%08lx:%08lx:%08lx] PIC[%08lx:%08lx:%08lx:%08lx]\n", + (cpu == this_cpu ? '*' : ' '), cpu, + pp->pcr[0], pp->pcr[1], pp->pcr[2], pp->pcr[3], + pp->pic[0], pp->pic[1], pp->pic[2], pp->pic[3]); + } + + memset(global_cpu_snapshot, 0, sizeof(global_cpu_snapshot)); + + spin_unlock_irqrestore(&global_cpu_snapshot_lock, flags); +} + +static void sysrq_handle_globpmu(int key) +{ + pmu_snapshot_all_cpus(); +} + +static struct sysrq_key_op sparc_globalpmu_op = { + .handler = sysrq_handle_globpmu, + .help_msg = "global-pmu(X)", + .action_msg = "Show Global PMU Regs", +}; + +static int __init sparc_sysrq_init(void) +{ + int ret = register_sysrq_key('y', &sparc_globalreg_op); + + if (!ret) + ret = register_sysrq_key('x', &sparc_globalpmu_op); + return ret; } -core_initcall(sparc_globreg_init); +core_initcall(sparc_sysrq_init); #endif diff --git a/arch/sparc/kernel/smp_64.c b/arch/sparc/kernel/smp_64.c index 781bcb10b8bd..d94b878577b7 100644 --- a/arch/sparc/kernel/smp_64.c +++ b/arch/sparc/kernel/smp_64.c @@ -852,6 +852,8 @@ extern unsigned long xcall_flush_tlb_mm; extern unsigned long xcall_flush_tlb_pending; extern unsigned long xcall_flush_tlb_kernel_range; extern unsigned long xcall_fetch_glob_regs; +extern unsigned long xcall_fetch_glob_pmu; +extern unsigned long xcall_fetch_glob_pmu_n4; extern unsigned long xcall_receive_signal; extern unsigned long xcall_new_mmu_context_version; #ifdef CONFIG_KGDB @@ -1000,6 +1002,15 @@ void smp_fetch_global_regs(void) smp_cross_call(&xcall_fetch_glob_regs, 0, 0, 0); } +void smp_fetch_global_pmu(void) +{ + if (tlb_type == hypervisor && + sun4v_chip_type >= SUN4V_CHIP_NIAGARA4) + smp_cross_call(&xcall_fetch_glob_pmu_n4, 0, 0, 0); + else + smp_cross_call(&xcall_fetch_glob_pmu, 0, 0, 0); +} + /* We know that the window frames of the user have been flushed * to the stack before we get here because all callers of us * are flush_tlb_*() routines, and these run after flush_cache_*() diff --git a/arch/sparc/mm/ultra.S b/arch/sparc/mm/ultra.S index 874162a11ceb..f8e13d421fcb 100644 --- a/arch/sparc/mm/ultra.S +++ b/arch/sparc/mm/ultra.S @@ -481,8 +481,8 @@ xcall_sync_tick: .globl xcall_fetch_glob_regs xcall_fetch_glob_regs: - sethi %hi(global_reg_snapshot), %g1 - or %g1, %lo(global_reg_snapshot), %g1 + sethi %hi(global_cpu_snapshot), %g1 + or %g1, %lo(global_cpu_snapshot), %g1 __GET_CPUID(%g2) sllx %g2, 6, %g3 add %g1, %g3, %g1 @@ -509,6 +509,66 @@ xcall_fetch_glob_regs: stx %g3, [%g1 + GR_SNAP_THREAD] retry + .globl xcall_fetch_glob_pmu +xcall_fetch_glob_pmu: + sethi %hi(global_cpu_snapshot), %g1 + or %g1, %lo(global_cpu_snapshot), %g1 + __GET_CPUID(%g2) + sllx %g2, 6, %g3 + add %g1, %g3, %g1 + rd %pic, %g7 + stx %g7, [%g1 + (4 * 8)] + rd %pcr, %g7 + stx %g7, [%g1 + (0 * 8)] + retry + + .globl xcall_fetch_glob_pmu_n4 +xcall_fetch_glob_pmu_n4: + sethi %hi(global_cpu_snapshot), %g1 + or %g1, %lo(global_cpu_snapshot), %g1 + __GET_CPUID(%g2) + sllx %g2, 6, %g3 + add %g1, %g3, %g1 + + ldxa [%g0] ASI_PIC, %g7 + stx %g7, [%g1 + (4 * 8)] + mov 0x08, %g3 + ldxa [%g3] ASI_PIC, %g7 + stx %g7, [%g1 + (5 * 8)] + mov 0x10, %g3 + ldxa [%g3] ASI_PIC, %g7 + stx %g7, [%g1 + (6 * 8)] + mov 0x18, %g3 + ldxa [%g3] ASI_PIC, %g7 + stx %g7, [%g1 + (7 * 8)] + + mov %o0, %g2 + mov %o1, %g3 + mov %o5, %g7 + + mov HV_FAST_VT_GET_PERFREG, %o5 + mov 3, %o0 + ta HV_FAST_TRAP + stx %o1, [%g1 + (3 * 8)] + mov HV_FAST_VT_GET_PERFREG, %o5 + mov 2, %o0 + ta HV_FAST_TRAP + stx %o1, [%g1 + (2 * 8)] + mov HV_FAST_VT_GET_PERFREG, %o5 + mov 1, %o0 + ta HV_FAST_TRAP + stx %o1, [%g1 + (1 * 8)] + mov HV_FAST_VT_GET_PERFREG, %o5 + mov 0, %o0 + ta HV_FAST_TRAP + stx %o1, [%g1 + (0 * 8)] + + mov %g2, %o0 + mov %g3, %o1 + mov %g7, %o5 + + retry + #ifdef DCACHE_ALIASING_POSSIBLE .align 32 .globl xcall_flush_dcache_page_cheetah diff --git a/drivers/tty/sysrq.c b/drivers/tty/sysrq.c index 05728894a88c..16ee6cee07da 100644 --- a/drivers/tty/sysrq.c +++ b/drivers/tty/sysrq.c @@ -452,6 +452,7 @@ static struct sysrq_key_op *sysrq_key_table[36] = { NULL, /* v */ &sysrq_showstate_blocked_op, /* w */ /* x: May be registered on ppc/powerpc for xmon */ + /* x: May be registered on sparc64 for global PMU dump */ NULL, /* x */ /* y: May be registered on sparc64 for global register dump */ NULL, /* y */ -- cgit v1.2.3-59-g8ed1b From d1bdc40b3389b1165e705c7794441417ed2a43bd Mon Sep 17 00:00:00 2001 From: Corey Minyard Date: Tue, 16 Oct 2012 15:53:36 -0500 Subject: IPMI: Remove SMBus driver info from the docs Some documentation for the SMBus driver is in the IPMI docs, but that code is not in the kernel tree at this point. So remove the docs to avoid confusion. Signed-off-by: Corey Minyard Signed-off-by: Linus Torvalds --- Documentation/IPMI.txt | 65 +++----------------------------------------------- 1 file changed, 3 insertions(+), 62 deletions(-) (limited to 'Documentation') diff --git a/Documentation/IPMI.txt b/Documentation/IPMI.txt index b2bea15137d2..16eb4c9e9233 100644 --- a/Documentation/IPMI.txt +++ b/Documentation/IPMI.txt @@ -42,13 +42,7 @@ The driver interface depends on your hardware. If your system properly provides the SMBIOS info for IPMI, the driver will detect it and just work. If you have a board with a standard interface (These will generally be either "KCS", "SMIC", or "BT", consult your hardware -manual), choose the 'IPMI SI handler' option. A driver also exists -for direct I2C access to the IPMI management controller. Some boards -support this, but it is unknown if it will work on every board. For -this, choose 'IPMI SMBus handler', but be ready to try to do some -figuring to see if it will work on your system if the SMBIOS/APCI -information is wrong or not present. It is fairly safe to have both -these enabled and let the drivers auto-detect what is present. +manual), choose the 'IPMI SI handler' option. You should generally enable ACPI on your system, as systems with IPMI can have ACPI tables describing them. @@ -58,8 +52,7 @@ their job correctly, the IPMI controller should be automatically detected (via ACPI or SMBIOS tables) and should just work. Sadly, many boards do not have this information. The driver attempts standard defaults, but they may not work. If you fall into this -situation, you need to read the section below named 'The SI Driver' or -"The SMBus Driver" on how to hand-configure your system. +situation, you need to read the section below named 'The SI Driver'. IPMI defines a standard watchdog timer. You can enable this with the 'IPMI Watchdog Timer' config option. If you compile the driver into @@ -104,12 +97,7 @@ driver, each open file for this device ties in to the message handler as an IPMI user. ipmi_si - A driver for various system interfaces. This supports KCS, -SMIC, and BT interfaces. Unless you have an SMBus interface or your -own custom interface, you probably need to use this. - -ipmi_smb - A driver for accessing BMCs on the SMBus. It uses the -I2C kernel driver's SMBus interfaces to send and receive IPMI messages -over the SMBus. +SMIC, and BT interfaces. ipmi_watchdog - IPMI requires systems to have a very capable watchdog timer. This driver implements the standard Linux watchdog timer @@ -482,53 +470,6 @@ for specifying an interface. Note that when removing an interface, only the first three parameters (si type, address type, and address) are used for the comparison. Any options are ignored for removing. -The SMBus Driver ----------------- - -The SMBus driver allows up to 4 SMBus devices to be configured in the -system. By default, the driver will register any SMBus interfaces it finds -in the I2C address range of 0x20 to 0x4f on any adapter. You can change this -at module load time (for a module) with: - - modprobe ipmi_smb.o - addr=,[,,[,...]] - dbg=,... - [defaultprobe=1] [dbg_probe=1] - -The addresses are specified in pairs, the first is the adapter ID and the -second is the I2C address on that adapter. - -The debug flags are bit flags for each BMC found, they are: -IPMI messages: 1, driver state: 2, timing: 4, I2C probe: 8 - -Setting smb_defaultprobe to zero disabled the default probing of SMBus -interfaces at address range 0x20 to 0x4f. This means that only the -BMCs specified on the smb_addr line will be detected. - -Setting smb_dbg_probe to 1 will enable debugging of the probing and -detection process for BMCs on the SMBusses. - -Discovering the IPMI compliant BMC on the SMBus can cause devices -on the I2C bus to fail. The SMBus driver writes a "Get Device ID" IPMI -message as a block write to the I2C bus and waits for a response. -This action can be detrimental to some I2C devices. It is highly recommended -that the known I2c address be given to the SMBus driver in the smb_addr -parameter. The default address range will not be used when a smb_addr -parameter is provided. - -When compiled into the kernel, the addresses can be specified on the -kernel command line as: - - ipmb_smb.addr=,[,,[,...]] - ipmi_smb.dbg=,... - ipmi_smb.defaultprobe=0 ipmi_smb.dbg_probe=1 - -These are the same options as on the module command line. - -Note that you might need some I2C changes if CONFIG_IPMI_PANIC_EVENT -is enabled along with this, so the I2C driver knows to run to -completion during sending a panic event. - Other Pieces ------------ -- cgit v1.2.3-59-g8ed1b From 0384e90b853357d24935c65ba0e1bdd27faa6e58 Mon Sep 17 00:00:00 2001 From: Daniel Mack Date: Sun, 7 Oct 2012 18:19:44 +0200 Subject: spi/mcspi: allow configuration of pin directions Allow D0 to be an input and D1 to be an output, configurable via platform data and a new DT property. Based on a patch from Matus Ujhelyi Signed-off-by: Daniel Mack Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/spi/omap-spi.txt | 4 +++- drivers/spi/spi-omap2-mcspi.c | 25 ++++++++++++++++------ include/linux/platform_data/spi-omap2-mcspi.h | 4 ++++ 3 files changed, 25 insertions(+), 8 deletions(-) (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/spi/omap-spi.txt b/Documentation/devicetree/bindings/spi/omap-spi.txt index 81df374adbb9..2ef0a6b85653 100644 --- a/Documentation/devicetree/bindings/spi/omap-spi.txt +++ b/Documentation/devicetree/bindings/spi/omap-spi.txt @@ -6,7 +6,9 @@ Required properties: - "ti,omap4-spi" for OMAP4+. - ti,spi-num-cs : Number of chipselect supported by the instance. - ti,hwmods: Name of the hwmod associated to the McSPI - +- ti,pindir-d0-in-d1-out: Select the D0 pin as input and D1 as + output. The default is D0 as output and + D1 as input. Example: diff --git a/drivers/spi/spi-omap2-mcspi.c b/drivers/spi/spi-omap2-mcspi.c index 3542fdc664b1..51046332677c 100644 --- a/drivers/spi/spi-omap2-mcspi.c +++ b/drivers/spi/spi-omap2-mcspi.c @@ -130,6 +130,7 @@ struct omap2_mcspi { struct omap2_mcspi_dma *dma_channels; struct device *dev; struct omap2_mcspi_regs ctx; + unsigned int pin_dir:1; }; struct omap2_mcspi_cs { @@ -765,8 +766,15 @@ static int omap2_mcspi_setup_transfer(struct spi_device *spi, /* standard 4-wire master mode: SCK, MOSI/out, MISO/in, nCS * REVISIT: this controller could support SPI_3WIRE mode. */ - l &= ~(OMAP2_MCSPI_CHCONF_IS|OMAP2_MCSPI_CHCONF_DPE1); - l |= OMAP2_MCSPI_CHCONF_DPE0; + if (mcspi->pin_dir == MCSPI_PINDIR_D0_OUT_D1_IN) { + l &= ~OMAP2_MCSPI_CHCONF_IS; + l &= ~OMAP2_MCSPI_CHCONF_DPE1; + l |= OMAP2_MCSPI_CHCONF_DPE0; + } else { + l |= OMAP2_MCSPI_CHCONF_IS; + l |= OMAP2_MCSPI_CHCONF_DPE1; + l &= ~OMAP2_MCSPI_CHCONF_DPE0; + } /* wordlength */ l &= ~OMAP2_MCSPI_CHCONF_WL_MASK; @@ -1167,6 +1175,11 @@ static int __devinit omap2_mcspi_probe(struct platform_device *pdev) master->cleanup = omap2_mcspi_cleanup; master->dev.of_node = node; + dev_set_drvdata(&pdev->dev, master); + + mcspi = spi_master_get_devdata(master); + mcspi->master = master; + match = of_match_device(omap_mcspi_of_match, &pdev->dev); if (match) { u32 num_cs = 1; /* default number of chipselect */ @@ -1175,19 +1188,17 @@ static int __devinit omap2_mcspi_probe(struct platform_device *pdev) of_property_read_u32(node, "ti,spi-num-cs", &num_cs); master->num_chipselect = num_cs; master->bus_num = bus_num++; + if (of_get_property(node, "ti,pindir-d0-in-d1-out", NULL)) + mcspi->pin_dir = MCSPI_PINDIR_D0_IN_D1_OUT; } else { pdata = pdev->dev.platform_data; master->num_chipselect = pdata->num_cs; if (pdev->id != -1) master->bus_num = pdev->id; + mcspi->pin_dir = pdata->pin_dir; } regs_offset = pdata->regs_offset; - dev_set_drvdata(&pdev->dev, master); - - mcspi = spi_master_get_devdata(master); - mcspi->master = master; - r = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (r == NULL) { status = -ENODEV; diff --git a/include/linux/platform_data/spi-omap2-mcspi.h b/include/linux/platform_data/spi-omap2-mcspi.h index a357eb26bd25..ce70f7b5a8e1 100644 --- a/include/linux/platform_data/spi-omap2-mcspi.h +++ b/include/linux/platform_data/spi-omap2-mcspi.h @@ -7,9 +7,13 @@ #define OMAP4_MCSPI_REG_OFFSET 0x100 +#define MCSPI_PINDIR_D0_OUT_D1_IN 0 +#define MCSPI_PINDIR_D0_IN_D1_OUT 1 + struct omap2_mcspi_platform_config { unsigned short num_cs; unsigned int regs_offset; + unsigned int pin_dir:1; }; struct omap2_mcspi_dev_attr { -- cgit v1.2.3-59-g8ed1b From 4424f616e48e0a48ac31de8d223ba1bdb46a84f1 Mon Sep 17 00:00:00 2001 From: Stefan Achatz Date: Tue, 16 Oct 2012 17:40:09 +0200 Subject: HID: roccat: add support for Roccat Lua This patch adds support for Roccat Lua gaming mouse. Userland tools can soon be found at http://sourceforge.net/projects/roccat Signed-off-by: Stefan Achatz Signed-off-by: Jiri Kosina --- .../ABI/testing/sysfs-driver-hid-roccat-lua | 7 + drivers/hid/Makefile | 4 +- drivers/hid/hid-core.c | 1 + drivers/hid/hid-ids.h | 1 + drivers/hid/hid-roccat-lua.c | 227 +++++++++++++++++++++ drivers/hid/hid-roccat-lua.h | 29 +++ 6 files changed, 267 insertions(+), 2 deletions(-) create mode 100644 Documentation/ABI/testing/sysfs-driver-hid-roccat-lua create mode 100644 drivers/hid/hid-roccat-lua.c create mode 100644 drivers/hid/hid-roccat-lua.h (limited to 'Documentation') diff --git a/Documentation/ABI/testing/sysfs-driver-hid-roccat-lua b/Documentation/ABI/testing/sysfs-driver-hid-roccat-lua new file mode 100644 index 000000000000..31c6c4c8ba2b --- /dev/null +++ b/Documentation/ABI/testing/sysfs-driver-hid-roccat-lua @@ -0,0 +1,7 @@ +What: /sys/bus/usb/devices/-:./control +Date: October 2012 +Contact: Stefan Achatz +Description: When written, cpi, button and light settings can be configured. + When read, actual cpi setting and sensor data are returned. + The data has to be 8 bytes long. +Users: http://roccat.sourceforge.net diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile index cef68ca859d3..ffe848fd83a4 100644 --- a/drivers/hid/Makefile +++ b/drivers/hid/Makefile @@ -93,8 +93,8 @@ obj-$(CONFIG_HID_PRIMAX) += hid-primax.o obj-$(CONFIG_HID_PS3REMOTE) += hid-ps3remote.o obj-$(CONFIG_HID_ROCCAT) += hid-roccat.o hid-roccat-common.o \ hid-roccat-arvo.o hid-roccat-isku.o hid-roccat-kone.o \ - hid-roccat-koneplus.o hid-roccat-kovaplus.o hid-roccat-pyra.o \ - hid-roccat-savu.o + hid-roccat-koneplus.o hid-roccat-kovaplus.o hid-roccat-lua.o \ + hid-roccat-pyra.o hid-roccat-savu.o obj-$(CONFIG_HID_SAITEK) += hid-saitek.o obj-$(CONFIG_HID_SAMSUNG) += hid-samsung.o obj-$(CONFIG_HID_SMARTJOYPLUS) += hid-sjoy.o diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index bd3971bf31bf..9904776a7065 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -1655,6 +1655,7 @@ static const struct hid_device_id hid_have_special_driver[] = { { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_ISKU) }, { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_KONEPLUS) }, { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_KOVAPLUS) }, + { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_LUA) }, { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_PYRA_WIRED) }, { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_PYRA_WIRELESS) }, { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_SAVU) }, diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index 269b50912a4a..52b27b12a4a7 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -675,6 +675,7 @@ #define USB_DEVICE_ID_ROCCAT_KONE 0x2ced #define USB_DEVICE_ID_ROCCAT_KONEPLUS 0x2d51 #define USB_DEVICE_ID_ROCCAT_KOVAPLUS 0x2d50 +#define USB_DEVICE_ID_ROCCAT_LUA 0x2c2e #define USB_DEVICE_ID_ROCCAT_PYRA_WIRED 0x2c24 #define USB_DEVICE_ID_ROCCAT_PYRA_WIRELESS 0x2cf6 #define USB_DEVICE_ID_ROCCAT_SAVU 0x2d5a diff --git a/drivers/hid/hid-roccat-lua.c b/drivers/hid/hid-roccat-lua.c new file mode 100644 index 000000000000..5084fb4b7e91 --- /dev/null +++ b/drivers/hid/hid-roccat-lua.c @@ -0,0 +1,227 @@ +/* + * Roccat Lua driver for Linux + * + * Copyright (c) 2012 Stefan Achatz + */ + +/* + * 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. + */ + +/* + * Roccat Lua is a gamer mouse which cpi, button and light settings can be + * configured. + */ + +#include +#include +#include +#include +#include +#include +#include "hid-ids.h" +#include "hid-roccat-common.h" +#include "hid-roccat-lua.h" + +static ssize_t lua_sysfs_read(struct file *fp, struct kobject *kobj, + char *buf, loff_t off, size_t count, + size_t real_size, uint command) +{ + struct device *dev = container_of(kobj, struct device, kobj); + struct lua_device *lua = hid_get_drvdata(dev_get_drvdata(dev)); + struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev)); + int retval; + + if (off >= real_size) + return 0; + + if (off != 0 || count != real_size) + return -EINVAL; + + mutex_lock(&lua->lua_lock); + retval = roccat_common2_receive(usb_dev, command, buf, real_size); + mutex_unlock(&lua->lua_lock); + + return retval ? retval : real_size; +} + +static ssize_t lua_sysfs_write(struct file *fp, struct kobject *kobj, + void const *buf, loff_t off, size_t count, + size_t real_size, uint command) +{ + struct device *dev = container_of(kobj, struct device, kobj); + struct lua_device *lua = hid_get_drvdata(dev_get_drvdata(dev)); + struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev)); + int retval; + + if (off != 0 || count != real_size) + return -EINVAL; + + mutex_lock(&lua->lua_lock); + retval = roccat_common2_send(usb_dev, command, (void *)buf, real_size); + mutex_unlock(&lua->lua_lock); + + return retval ? retval : real_size; +} + +#define LUA_SYSFS_W(thingy, THINGY) \ +static ssize_t lua_sysfs_write_ ## thingy(struct file *fp, \ + struct kobject *kobj, struct bin_attribute *attr, \ + char *buf, loff_t off, size_t count) \ +{ \ + return lua_sysfs_write(fp, kobj, buf, off, count, \ + LUA_SIZE_ ## THINGY, LUA_COMMAND_ ## THINGY); \ +} + +#define LUA_SYSFS_R(thingy, THINGY) \ +static ssize_t lua_sysfs_read_ ## thingy(struct file *fp, \ + struct kobject *kobj, struct bin_attribute *attr, \ + char *buf, loff_t off, size_t count) \ +{ \ + return lua_sysfs_read(fp, kobj, buf, off, count, \ + LUA_SIZE_ ## THINGY, LUA_COMMAND_ ## THINGY); \ +} + +#define LUA_BIN_ATTRIBUTE_RW(thingy, THINGY) \ +LUA_SYSFS_W(thingy, THINGY) \ +LUA_SYSFS_R(thingy, THINGY) \ +static struct bin_attribute lua_ ## thingy ## _attr = { \ + .attr = { .name = #thingy, .mode = 0660 }, \ + .size = LUA_SIZE_ ## THINGY, \ + .read = lua_sysfs_read_ ## thingy, \ + .write = lua_sysfs_write_ ## thingy \ +}; + +LUA_BIN_ATTRIBUTE_RW(control, CONTROL) + +static int lua_create_sysfs_attributes(struct usb_interface *intf) +{ + return sysfs_create_bin_file(&intf->dev.kobj, &lua_control_attr); +} + +static void lua_remove_sysfs_attributes(struct usb_interface *intf) +{ + sysfs_remove_bin_file(&intf->dev.kobj, &lua_control_attr); +} + +static int lua_init_lua_device_struct(struct usb_device *usb_dev, + struct lua_device *lua) +{ + mutex_init(&lua->lua_lock); + + return 0; +} + +static int lua_init_specials(struct hid_device *hdev) +{ + struct usb_interface *intf = to_usb_interface(hdev->dev.parent); + struct usb_device *usb_dev = interface_to_usbdev(intf); + struct lua_device *lua; + int retval; + + lua = kzalloc(sizeof(*lua), GFP_KERNEL); + if (!lua) { + hid_err(hdev, "can't alloc device descriptor\n"); + return -ENOMEM; + } + hid_set_drvdata(hdev, lua); + + retval = lua_init_lua_device_struct(usb_dev, lua); + if (retval) { + hid_err(hdev, "couldn't init struct lua_device\n"); + goto exit; + } + + retval = lua_create_sysfs_attributes(intf); + if (retval) { + hid_err(hdev, "cannot create sysfs files\n"); + goto exit; + } + + return 0; +exit: + kfree(lua); + return retval; +} + +static void lua_remove_specials(struct hid_device *hdev) +{ + struct usb_interface *intf = to_usb_interface(hdev->dev.parent); + struct lua_device *lua; + + lua_remove_sysfs_attributes(intf); + + lua = hid_get_drvdata(hdev); + kfree(lua); +} + +static int lua_probe(struct hid_device *hdev, + const struct hid_device_id *id) +{ + int retval; + + retval = hid_parse(hdev); + if (retval) { + hid_err(hdev, "parse failed\n"); + goto exit; + } + + retval = hid_hw_start(hdev, HID_CONNECT_DEFAULT); + if (retval) { + hid_err(hdev, "hw start failed\n"); + goto exit; + } + + retval = lua_init_specials(hdev); + if (retval) { + hid_err(hdev, "couldn't install mouse\n"); + goto exit_stop; + } + + return 0; + +exit_stop: + hid_hw_stop(hdev); +exit: + return retval; +} + +static void lua_remove(struct hid_device *hdev) +{ + lua_remove_specials(hdev); + hid_hw_stop(hdev); +} + +static const struct hid_device_id lua_devices[] = { + { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_LUA) }, + { } +}; + +MODULE_DEVICE_TABLE(hid, lua_devices); + +static struct hid_driver lua_driver = { + .name = "lua", + .id_table = lua_devices, + .probe = lua_probe, + .remove = lua_remove +}; + +static int __init lua_init(void) +{ + return hid_register_driver(&lua_driver); +} + +static void __exit lua_exit(void) +{ + hid_unregister_driver(&lua_driver); +} + +module_init(lua_init); +module_exit(lua_exit); + +MODULE_AUTHOR("Stefan Achatz"); +MODULE_DESCRIPTION("USB Roccat Lua driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/hid/hid-roccat-lua.h b/drivers/hid/hid-roccat-lua.h new file mode 100644 index 000000000000..547d77a375d1 --- /dev/null +++ b/drivers/hid/hid-roccat-lua.h @@ -0,0 +1,29 @@ +#ifndef __HID_ROCCAT_LUA_H +#define __HID_ROCCAT_LUA_H + +/* + * Copyright (c) 2012 Stefan Achatz + */ + +/* + * 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 + +enum { + LUA_SIZE_CONTROL = 8, +}; + +enum lua_commands { + LUA_COMMAND_CONTROL = 3, +}; + +struct lua_device { + struct mutex lua_lock; +}; + +#endif -- cgit v1.2.3-59-g8ed1b From ce3b5952bac17835363543ddf97e89d98b1d771f Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Wed, 17 Oct 2012 10:05:09 -0300 Subject: DocBook/media/Makefile: Fix build due to uapi breakage The uapi changeset forgot to fix the header locations, needed for the DocBook specs. Fix it. Cc: David Howells Signed-off-by: Mauro Carvalho Chehab --- Documentation/DocBook/media/Makefile | 76 ++++++++++++++++++------------------ 1 file changed, 38 insertions(+), 38 deletions(-) (limited to 'Documentation') diff --git a/Documentation/DocBook/media/Makefile b/Documentation/DocBook/media/Makefile index 9b7e4c557928..f9fd615427fb 100644 --- a/Documentation/DocBook/media/Makefile +++ b/Documentation/DocBook/media/Makefile @@ -56,15 +56,15 @@ FUNCS = \ write \ IOCTLS = \ - $(shell perl -ne 'print "$$1 " if /\#define\s+([^\s]+)\s+_IO/' $(srctree)/include/linux/videodev2.h) \ - $(shell perl -ne 'print "$$1 " if /\#define\s+([^\s]+)\s+_IO/' $(srctree)/include/linux/dvb/audio.h) \ - $(shell perl -ne 'print "$$1 " if /\#define\s+([^\s]+)\s+_IO/' $(srctree)/include/linux/dvb/ca.h) \ - $(shell perl -ne 'print "$$1 " if /\#define\s+([^\s]+)\s+_IO/' $(srctree)/include/linux/dvb/dmx.h) \ - $(shell perl -ne 'print "$$1 " if /\#define\s+([^\s]+)\s+_IO/' $(srctree)/include/linux/dvb/frontend.h) \ - $(shell perl -ne 'print "$$1 " if /\#define\s+([A-Z][^\s]+)\s+_IO/' $(srctree)/include/linux/dvb/net.h) \ - $(shell perl -ne 'print "$$1 " if /\#define\s+([^\s]+)\s+_IO/' $(srctree)/include/linux/dvb/video.h) \ - $(shell perl -ne 'print "$$1 " if /\#define\s+([^\s]+)\s+_IO/' $(srctree)/include/linux/media.h) \ - $(shell perl -ne 'print "$$1 " if /\#define\s+([^\s]+)\s+_IO/' $(srctree)/include/linux/v4l2-subdev.h) \ + $(shell perl -ne 'print "$$1 " if /\#define\s+([^\s]+)\s+_IO/' $(srctree)/include/uapi/linux/videodev2.h) \ + $(shell perl -ne 'print "$$1 " if /\#define\s+([^\s]+)\s+_IO/' $(srctree)/include/uapi/linux/dvb/audio.h) \ + $(shell perl -ne 'print "$$1 " if /\#define\s+([^\s]+)\s+_IO/' $(srctree)/include/uapi/linux/dvb/ca.h) \ + $(shell perl -ne 'print "$$1 " if /\#define\s+([^\s]+)\s+_IO/' $(srctree)/include/uapi/linux/dvb/dmx.h) \ + $(shell perl -ne 'print "$$1 " if /\#define\s+([^\s]+)\s+_IO/' $(srctree)/include/uapi/linux/dvb/frontend.h) \ + $(shell perl -ne 'print "$$1 " if /\#define\s+([A-Z][^\s]+)\s+_IO/' $(srctree)/include/uapi/linux/dvb/net.h) \ + $(shell perl -ne 'print "$$1 " if /\#define\s+([^\s]+)\s+_IO/' $(srctree)/include/uapi/linux/dvb/video.h) \ + $(shell perl -ne 'print "$$1 " if /\#define\s+([^\s]+)\s+_IO/' $(srctree)/include/uapi/linux/media.h) \ + $(shell perl -ne 'print "$$1 " if /\#define\s+([^\s]+)\s+_IO/' $(srctree)/include/uapi/linux/v4l2-subdev.h) \ VIDIOC_SUBDEV_G_FRAME_INTERVAL \ VIDIOC_SUBDEV_S_FRAME_INTERVAL \ VIDIOC_SUBDEV_ENUM_MBUS_CODE \ @@ -74,32 +74,32 @@ IOCTLS = \ VIDIOC_SUBDEV_S_SELECTION \ TYPES = \ - $(shell perl -ne 'print "$$1 " if /^typedef\s+[^\s]+\s+([^\s]+)\;/' $(srctree)/include/linux/videodev2.h) \ - $(shell perl -ne 'print "$$1 " if /^}\s+([a-z0-9_]+_t)/' $(srctree)/include/linux/dvb/frontend.h) + $(shell perl -ne 'print "$$1 " if /^typedef\s+[^\s]+\s+([^\s]+)\;/' $(srctree)/include/uapi/linux/videodev2.h) \ + $(shell perl -ne 'print "$$1 " if /^}\s+([a-z0-9_]+_t)/' $(srctree)/include/uapi/linux/dvb/frontend.h) ENUMS = \ - $(shell perl -ne 'print "$$1 " if /^enum\s+([^\s]+)\s+/' $(srctree)/include/linux/videodev2.h) \ - $(shell perl -ne 'print "$$1 " if /^enum\s+([^\s]+)\s+/' $(srctree)/include/linux/dvb/audio.h) \ - $(shell perl -ne 'print "$$1 " if /^enum\s+([^\s]+)\s+/' $(srctree)/include/linux/dvb/ca.h) \ - $(shell perl -ne 'print "$$1 " if /^enum\s+([^\s]+)\s+/' $(srctree)/include/linux/dvb/dmx.h) \ - $(shell perl -ne 'print "$$1 " if /^enum\s+([^\s]+)\s+/' $(srctree)/include/linux/dvb/frontend.h) \ - $(shell perl -ne 'print "$$1 " if /^enum\s+([^\s]+)\s+/' $(srctree)/include/linux/dvb/net.h) \ - $(shell perl -ne 'print "$$1 " if /^enum\s+([^\s]+)\s+/' $(srctree)/include/linux/dvb/video.h) \ - $(shell perl -ne 'print "$$1 " if /^enum\s+([^\s]+)\s+/' $(srctree)/include/linux/media.h) \ - $(shell perl -ne 'print "$$1 " if /^enum\s+([^\s]+)\s+/' $(srctree)/include/linux/v4l2-mediabus.h) \ - $(shell perl -ne 'print "$$1 " if /^enum\s+([^\s]+)\s+/' $(srctree)/include/linux/v4l2-subdev.h) + $(shell perl -ne 'print "$$1 " if /^enum\s+([^\s]+)\s+/' $(srctree)/include/uapi/linux/videodev2.h) \ + $(shell perl -ne 'print "$$1 " if /^enum\s+([^\s]+)\s+/' $(srctree)/include/uapi/linux/dvb/audio.h) \ + $(shell perl -ne 'print "$$1 " if /^enum\s+([^\s]+)\s+/' $(srctree)/include/uapi/linux/dvb/ca.h) \ + $(shell perl -ne 'print "$$1 " if /^enum\s+([^\s]+)\s+/' $(srctree)/include/uapi/linux/dvb/dmx.h) \ + $(shell perl -ne 'print "$$1 " if /^enum\s+([^\s]+)\s+/' $(srctree)/include/uapi/linux/dvb/frontend.h) \ + $(shell perl -ne 'print "$$1 " if /^enum\s+([^\s]+)\s+/' $(srctree)/include/uapi/linux/dvb/net.h) \ + $(shell perl -ne 'print "$$1 " if /^enum\s+([^\s]+)\s+/' $(srctree)/include/uapi/linux/dvb/video.h) \ + $(shell perl -ne 'print "$$1 " if /^enum\s+([^\s]+)\s+/' $(srctree)/include/uapi/linux/media.h) \ + $(shell perl -ne 'print "$$1 " if /^enum\s+([^\s]+)\s+/' $(srctree)/include/uapi/linux/v4l2-mediabus.h) \ + $(shell perl -ne 'print "$$1 " if /^enum\s+([^\s]+)\s+/' $(srctree)/include/uapi/linux/v4l2-subdev.h) STRUCTS = \ - $(shell perl -ne 'print "$$1 " if /^struct\s+([^\s]+)\s+/' $(srctree)/include/linux/videodev2.h) \ - $(shell perl -ne 'print "$$1 " if (/^struct\s+([^\s\{]+)\s*/)' $(srctree)/include/linux/dvb/audio.h) \ - $(shell perl -ne 'print "$$1 " if (/^struct\s+([^\s]+)\s+/)' $(srctree)/include/linux/dvb/ca.h) \ - $(shell perl -ne 'print "$$1 " if (/^struct\s+([^\s]+)\s+/)' $(srctree)/include/linux/dvb/dmx.h) \ - $(shell perl -ne 'print "$$1 " if (!/dtv\_cmds\_h/ && /^struct\s+([^\s]+)\s+/)' $(srctree)/include/linux/dvb/frontend.h) \ - $(shell perl -ne 'print "$$1 " if (/^struct\s+([A-Z][^\s]+)\s+/)' $(srctree)/include/linux/dvb/net.h) \ - $(shell perl -ne 'print "$$1 " if (/^struct\s+([^\s]+)\s+/)' $(srctree)/include/linux/dvb/video.h) \ - $(shell perl -ne 'print "$$1 " if /^struct\s+([^\s]+)\s+/' $(srctree)/include/linux/media.h) \ - $(shell perl -ne 'print "$$1 " if /^struct\s+([^\s]+)\s+/' $(srctree)/include/linux/v4l2-subdev.h) \ - $(shell perl -ne 'print "$$1 " if /^struct\s+([^\s]+)\s+/' $(srctree)/include/linux/v4l2-mediabus.h) + $(shell perl -ne 'print "$$1 " if /^struct\s+([^\s]+)\s+/' $(srctree)/include/uapi/linux/videodev2.h) \ + $(shell perl -ne 'print "$$1 " if (/^struct\s+([^\s\{]+)\s*/)' $(srctree)/include/uapi/linux/dvb/audio.h) \ + $(shell perl -ne 'print "$$1 " if (/^struct\s+([^\s]+)\s+/)' $(srctree)/include/uapi/linux/dvb/ca.h) \ + $(shell perl -ne 'print "$$1 " if (/^struct\s+([^\s]+)\s+/)' $(srctree)/include/uapi/linux/dvb/dmx.h) \ + $(shell perl -ne 'print "$$1 " if (!/dtv\_cmds\_h/ && /^struct\s+([^\s]+)\s+/)' $(srctree)/include/uapi/linux/dvb/frontend.h) \ + $(shell perl -ne 'print "$$1 " if (/^struct\s+([A-Z][^\s]+)\s+/)' $(srctree)/include/uapi/linux/dvb/net.h) \ + $(shell perl -ne 'print "$$1 " if (/^struct\s+([^\s]+)\s+/)' $(srctree)/include/uapi/linux/dvb/video.h) \ + $(shell perl -ne 'print "$$1 " if /^struct\s+([^\s]+)\s+/' $(srctree)/include/uapi/linux/media.h) \ + $(shell perl -ne 'print "$$1 " if /^struct\s+([^\s]+)\s+/' $(srctree)/include/uapi/linux/v4l2-subdev.h) \ + $(shell perl -ne 'print "$$1 " if /^struct\s+([^\s]+)\s+/' $(srctree)/include/uapi/linux/v4l2-mediabus.h) ERRORS = \ E2BIG \ @@ -205,7 +205,7 @@ $(MEDIA_OBJ_DIR)/v4l2.xml: $(OBJIMGFILES) @(ln -sf $(MEDIA_SRC_DIR)/v4l/*xml $(MEDIA_OBJ_DIR)/) @(ln -sf $(MEDIA_SRC_DIR)/dvb/*xml $(MEDIA_OBJ_DIR)/) -$(MEDIA_OBJ_DIR)/videodev2.h.xml: $(srctree)/include/linux/videodev2.h $(MEDIA_OBJ_DIR)/v4l2.xml +$(MEDIA_OBJ_DIR)/videodev2.h.xml: $(srctree)/include/uapi/linux/videodev2.h $(MEDIA_OBJ_DIR)/v4l2.xml @$($(quiet)gen_xml) @( \ echo "") > $@ @@ -216,7 +216,7 @@ $(MEDIA_OBJ_DIR)/videodev2.h.xml: $(srctree)/include/linux/videodev2.h $(MEDIA_O @( \ echo "") >> $@ -$(MEDIA_OBJ_DIR)/audio.h.xml: $(srctree)/include/linux/dvb/audio.h $(MEDIA_OBJ_DIR)/v4l2.xml +$(MEDIA_OBJ_DIR)/audio.h.xml: $(srctree)/include/uapi/linux/dvb/audio.h $(MEDIA_OBJ_DIR)/v4l2.xml @$($(quiet)gen_xml) @( \ echo "") > $@ @@ -227,7 +227,7 @@ $(MEDIA_OBJ_DIR)/audio.h.xml: $(srctree)/include/linux/dvb/audio.h $(MEDIA_OBJ_D @( \ echo "") >> $@ -$(MEDIA_OBJ_DIR)/ca.h.xml: $(srctree)/include/linux/dvb/ca.h $(MEDIA_OBJ_DIR)/v4l2.xml +$(MEDIA_OBJ_DIR)/ca.h.xml: $(srctree)/include/uapi/linux/dvb/ca.h $(MEDIA_OBJ_DIR)/v4l2.xml @$($(quiet)gen_xml) @( \ echo "") > $@ @@ -238,7 +238,7 @@ $(MEDIA_OBJ_DIR)/ca.h.xml: $(srctree)/include/linux/dvb/ca.h $(MEDIA_OBJ_DIR)/v4 @( \ echo "") >> $@ -$(MEDIA_OBJ_DIR)/dmx.h.xml: $(srctree)/include/linux/dvb/dmx.h $(MEDIA_OBJ_DIR)/v4l2.xml +$(MEDIA_OBJ_DIR)/dmx.h.xml: $(srctree)/include/uapi/linux/dvb/dmx.h $(MEDIA_OBJ_DIR)/v4l2.xml @$($(quiet)gen_xml) @( \ echo "") > $@ @@ -249,7 +249,7 @@ $(MEDIA_OBJ_DIR)/dmx.h.xml: $(srctree)/include/linux/dvb/dmx.h $(MEDIA_OBJ_DIR)/ @( \ echo "") >> $@ -$(MEDIA_OBJ_DIR)/frontend.h.xml: $(srctree)/include/linux/dvb/frontend.h $(MEDIA_OBJ_DIR)/v4l2.xml +$(MEDIA_OBJ_DIR)/frontend.h.xml: $(srctree)/include/uapi/linux/dvb/frontend.h $(MEDIA_OBJ_DIR)/v4l2.xml @$($(quiet)gen_xml) @( \ echo "") > $@ @@ -260,7 +260,7 @@ $(MEDIA_OBJ_DIR)/frontend.h.xml: $(srctree)/include/linux/dvb/frontend.h $(MEDIA @( \ echo "") >> $@ -$(MEDIA_OBJ_DIR)/net.h.xml: $(srctree)/include/linux/dvb/net.h $(MEDIA_OBJ_DIR)/v4l2.xml +$(MEDIA_OBJ_DIR)/net.h.xml: $(srctree)/include/uapi/linux/dvb/net.h $(MEDIA_OBJ_DIR)/v4l2.xml @$($(quiet)gen_xml) @( \ echo "") > $@ @@ -271,7 +271,7 @@ $(MEDIA_OBJ_DIR)/net.h.xml: $(srctree)/include/linux/dvb/net.h $(MEDIA_OBJ_DIR)/ @( \ echo "") >> $@ -$(MEDIA_OBJ_DIR)/video.h.xml: $(srctree)/include/linux/dvb/video.h $(MEDIA_OBJ_DIR)/v4l2.xml +$(MEDIA_OBJ_DIR)/video.h.xml: $(srctree)/include/uapi/linux/dvb/video.h $(MEDIA_OBJ_DIR)/v4l2.xml @$($(quiet)gen_xml) @( \ echo "") > $@ -- cgit v1.2.3-59-g8ed1b From 006694d099e86e5a928fb39bdea280ab42c9d59c Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Mon, 15 Oct 2012 14:16:59 +0100 Subject: regulator: gpio-regulator: Allow use of GPIO controlled regulators though DT Here we provide the GPIO Regulator driver with Device Tree capability, so that when a platform is booting with DT instead of platform data we can still make full use of it. Signed-off-by: Lee Jones Reviewed-by: Stephen Warren Signed-off-by: Mark Brown --- .../bindings/regulator/gpio-regulator.txt | 36 +++++++++ drivers/regulator/gpio-regulator.c | 94 ++++++++++++++++++++++ 2 files changed, 130 insertions(+) create mode 100644 Documentation/devicetree/bindings/regulator/gpio-regulator.txt (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/regulator/gpio-regulator.txt b/Documentation/devicetree/bindings/regulator/gpio-regulator.txt new file mode 100644 index 000000000000..3703be2ab821 --- /dev/null +++ b/Documentation/devicetree/bindings/regulator/gpio-regulator.txt @@ -0,0 +1,36 @@ +GPIO controlled regulators + +Required properties: +- compatible : Must be "regulator-gpio". + +Optional properties: +- gpio-enable : GPIO to use to enable/disable the regulator. +- gpios : GPIO group used to control voltage. +- states : Selection of available voltages and GPIO configs. +- startup-delay-us : Startup time in microseconds. +- enable-active-high : Polarity of GPIO is active high (default is low). + +Any property defined as part of the core regulator binding defined in +regulator.txt can also be used. + +Example: + + mmciv: gpio-regulator { + compatible = "regulator-gpio"; + + regulator-name = "mmci-gpio-supply"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <2600000>; + regulator-boot-on; + + gpio-enable = <&gpio0 23 0x4>; + gpios = <&gpio0 24 0x4 + &gpio0 25 0x4>; + states = <1800000 0x3 + 2200000 0x2 + 2600000 0x1 + 2900000 0x0>; + + startup-delay-us = <100000>; + enable-active-high; + }; diff --git a/drivers/regulator/gpio-regulator.c b/drivers/regulator/gpio-regulator.c index 8b5944f2d7d1..e467d0ac8705 100644 --- a/drivers/regulator/gpio-regulator.c +++ b/drivers/regulator/gpio-regulator.c @@ -28,9 +28,12 @@ #include #include #include +#include #include #include #include +#include +#include struct gpio_regulator_data { struct regulator_desc desc; @@ -129,6 +132,84 @@ static struct regulator_ops gpio_regulator_voltage_ops = { .list_voltage = gpio_regulator_list_voltage, }; +struct gpio_regulator_config * +of_get_gpio_regulator_config(struct device *dev, struct device_node *np) +{ + struct gpio_regulator_config *config; + struct property *prop; + const char *regtype; + int proplen, gpio, i; + + config = devm_kzalloc(dev, + sizeof(struct gpio_regulator_config), + GFP_KERNEL); + if (!config) + return ERR_PTR(-ENOMEM); + + config->init_data = of_get_regulator_init_data(dev, np); + if (!config->init_data) + return ERR_PTR(-EINVAL); + + config->supply_name = config->init_data->constraints.name; + + if (of_property_read_bool(np, "enable-active-high")) + config->enable_high = true; + + if (of_property_read_bool(np, "enable-at-boot")) + config->enabled_at_boot = true; + + of_property_read_u32(np, "startup-delay-us", &config->startup_delay); + + config->enable_gpio = of_get_named_gpio(np, "enable-gpio", 0); + + /* Fetch GPIOs. */ + for (i = 0; ; i++) + if (of_get_named_gpio(np, "gpios", i) < 0) + break; + config->nr_gpios = i; + + config->gpios = devm_kzalloc(dev, + sizeof(struct gpio) * config->nr_gpios, + GFP_KERNEL); + if (!config->gpios) + return ERR_PTR(-ENOMEM); + + for (i = 0; config->nr_gpios; i++) { + gpio = of_get_named_gpio(np, "gpios", i); + if (gpio < 0) + break; + config->gpios[i].gpio = gpio; + } + + /* Fetch states. */ + prop = of_find_property(np, "states", NULL); + proplen = prop->length / sizeof(int); + + config->states = devm_kzalloc(dev, + sizeof(struct gpio_regulator_state) + * (proplen / 2), + GFP_KERNEL); + if (!config->states) + return ERR_PTR(-ENOMEM); + + for (i = 0; i < proplen / 2; i++) { + config->states[i].value = + be32_to_cpup((int *)prop->value + (i * 2)); + config->states[i].gpios = + be32_to_cpup((int *)prop->value + (i * 2 + 1)); + } + config->nr_states = i; + + of_property_read_string(np, "regulator-type", ®type); + + if (!strncmp("voltage", regtype, 7)) + config->type = REGULATOR_VOLTAGE; + else if (!strncmp("current", regtype, 7)) + config->type = REGULATOR_CURRENT; + + return config; +} + static struct regulator_ops gpio_regulator_current_ops = { .get_current_limit = gpio_regulator_get_value, .set_current_limit = gpio_regulator_set_current_limit, @@ -137,10 +218,17 @@ static struct regulator_ops gpio_regulator_current_ops = { static int __devinit gpio_regulator_probe(struct platform_device *pdev) { struct gpio_regulator_config *config = pdev->dev.platform_data; + struct device_node *np = pdev->dev.of_node; struct gpio_regulator_data *drvdata; struct regulator_config cfg = { }; int ptr, ret, state; + if (np) { + config = of_get_gpio_regulator_config(&pdev->dev, np); + if (IS_ERR(config)) + return PTR_ERR(config); + } + drvdata = devm_kzalloc(&pdev->dev, sizeof(struct gpio_regulator_data), GFP_KERNEL); if (drvdata == NULL) { @@ -270,12 +358,18 @@ static int __devexit gpio_regulator_remove(struct platform_device *pdev) return 0; } +static const struct of_device_id regulator_gpio_of_match[] __devinitconst = { + { .compatible = "regulator-gpio", }, + {}, +}; + static struct platform_driver gpio_regulator_driver = { .probe = gpio_regulator_probe, .remove = __devexit_p(gpio_regulator_remove), .driver = { .name = "gpio-regulator", .owner = THIS_MODULE, + .of_match_table = regulator_gpio_of_match, }, }; -- cgit v1.2.3-59-g8ed1b From 5277e97c2474a85eb0325ac6fff71910a4e6c134 Mon Sep 17 00:00:00 2001 From: Stefan Achatz Date: Wed, 17 Oct 2012 16:35:42 +0200 Subject: HID: roccat: allow readout of koneplus sensor register data tcu sysfs attr was used to only control calibration process so far. Direct sensor register access possibility has been revealed. Allowing readout of tcu permits usage of this feature. Signed-off-by: Stefan Achatz Signed-off-by: Jiri Kosina --- Documentation/ABI/testing/sysfs-driver-hid-roccat-koneplus | 6 +++--- drivers/hid/hid-roccat-koneplus.c | 11 ++++++++++- 2 files changed, 13 insertions(+), 4 deletions(-) (limited to 'Documentation') diff --git a/Documentation/ABI/testing/sysfs-driver-hid-roccat-koneplus b/Documentation/ABI/testing/sysfs-driver-hid-roccat-koneplus index 65e6e5dd67e8..f9e2a61900e5 100644 --- a/Documentation/ABI/testing/sysfs-driver-hid-roccat-koneplus +++ b/Documentation/ABI/testing/sysfs-driver-hid-roccat-koneplus @@ -104,9 +104,9 @@ What: /sys/bus/usb/devices/-:./ Description: When written a calibration process for the tracking control unit - can be initiated/cancelled. - The data has to be 3 bytes long. - This file is writeonly. + can be initiated/cancelled. Also lets one read/write sensor + registers. + The data has to be 4 bytes long. Users: http://roccat.sourceforge.net What: /sys/bus/usb/devices/-:./::./koneplus/roccatkoneplus/tcu_image diff --git a/drivers/hid/hid-roccat-koneplus.c b/drivers/hid/hid-roccat-koneplus.c index f5602fec4865..c47540a5d4f3 100644 --- a/drivers/hid/hid-roccat-koneplus.c +++ b/drivers/hid/hid-roccat-koneplus.c @@ -222,6 +222,14 @@ static ssize_t koneplus_sysfs_write_tcu(struct file *fp, sizeof(struct koneplus_tcu), KONEPLUS_COMMAND_TCU); } +static ssize_t koneplus_sysfs_read_tcu(struct file *fp, + struct kobject *kobj, struct bin_attribute *attr, char *buf, + loff_t off, size_t count) +{ + return koneplus_sysfs_read(fp, kobj, buf, off, count, + sizeof(struct koneplus_tcu), KONEPLUS_COMMAND_TCU); +} + static ssize_t koneplus_sysfs_read_tcu_image(struct file *fp, struct kobject *kobj, struct bin_attribute *attr, char *buf, loff_t off, size_t count) @@ -426,8 +434,9 @@ static struct bin_attribute koneplus_bin_attributes[] = { .write = koneplus_sysfs_write_sensor }, { - .attr = { .name = "tcu", .mode = 0220 }, + .attr = { .name = "tcu", .mode = 0660 }, .size = sizeof(struct koneplus_tcu), + .read = koneplus_sysfs_read_tcu, .write = koneplus_sysfs_write_tcu }, { -- cgit v1.2.3-59-g8ed1b From d6ec6b60a56a1e7d99da1fc69c031fa5ab54ba94 Mon Sep 17 00:00:00 2001 From: Pritesh Raithatha Date: Wed, 17 Oct 2012 17:07:00 +0530 Subject: dt: Document: correct tegra20/30 pinctrl slew-rate name change nvidia,slew_rate* to nvidia,slew-rate* Cc: stable@kernel.org Signed-off-by: Pritesh Raithatha Acked-by: Stephen Warren Tested-by: Stephen Warren Signed-off-by: Linus Walleij --- Documentation/devicetree/bindings/pinctrl/nvidia,tegra20-pinmux.txt | 2 +- Documentation/devicetree/bindings/pinctrl/nvidia,tegra30-pinmux.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/pinctrl/nvidia,tegra20-pinmux.txt b/Documentation/devicetree/bindings/pinctrl/nvidia,tegra20-pinmux.txt index c8e578263ce2..683fde93c4fb 100644 --- a/Documentation/devicetree/bindings/pinctrl/nvidia,tegra20-pinmux.txt +++ b/Documentation/devicetree/bindings/pinctrl/nvidia,tegra20-pinmux.txt @@ -93,7 +93,7 @@ Valid values for pin and group names are: With some exceptions, these support nvidia,high-speed-mode, nvidia,schmitt, nvidia,low-power-mode, nvidia,pull-down-strength, - nvidia,pull-up-strength, nvidia,slew_rate-rising, nvidia,slew_rate-falling. + nvidia,pull-up-strength, nvidia,slew-rate-rising, nvidia,slew-rate-falling. drive_ao1, drive_ao2, drive_at1, drive_at2, drive_cdev1, drive_cdev2, drive_csus, drive_dap1, drive_dap2, drive_dap3, drive_dap4, drive_dbg, diff --git a/Documentation/devicetree/bindings/pinctrl/nvidia,tegra30-pinmux.txt b/Documentation/devicetree/bindings/pinctrl/nvidia,tegra30-pinmux.txt index c275b70349c1..6f426ed7009e 100644 --- a/Documentation/devicetree/bindings/pinctrl/nvidia,tegra30-pinmux.txt +++ b/Documentation/devicetree/bindings/pinctrl/nvidia,tegra30-pinmux.txt @@ -83,7 +83,7 @@ Valid values for pin and group names are: drive groups: These all support nvidia,pull-down-strength, nvidia,pull-up-strength, - nvidia,slew_rate-rising, nvidia,slew_rate-falling. Most but not all + nvidia,slew-rate-rising, nvidia,slew-rate-falling. Most but not all support nvidia,high-speed-mode, nvidia,schmitt, nvidia,low-power-mode. ao1, ao2, at1, at2, at3, at4, at5, cdev1, cdev2, cec, crt, csus, dap1, -- cgit v1.2.3-59-g8ed1b From 2eae613b95a786714bd1b5825ea3abc78d229d3f Mon Sep 17 00:00:00 2001 From: Arun Kumar K Date: Tue, 23 Oct 2012 22:51:33 +0900 Subject: ARM: EXYNOS: Add MFC device tree support This patch adds device tree entry for MFC v6 in the Exynos5 SoC. Makes the required changes in the clock files and adds MFC to the DT device list. Signed-off-by: Naveen Krishna Chatradhi Signed-off-by: Arun Kumar K [kgene.kim@samsung.com: fixed section mismatches Seung-Woo Kim reported] Signed-off-by: Kukjin Kim --- .../devicetree/bindings/media/s5p-mfc.txt | 23 +++++++++++++++ arch/arm/boot/dts/exynos5250-smdk5250.dts | 5 ++++ arch/arm/boot/dts/exynos5250.dtsi | 6 ++++ arch/arm/mach-exynos/Kconfig | 1 + arch/arm/mach-exynos/clock-exynos5.c | 2 +- arch/arm/mach-exynos/mach-exynos5-dt.c | 16 ++++++++++ arch/arm/plat-samsung/devs.c | 1 + arch/arm/plat-samsung/include/plat/mfc.h | 11 +++++++ arch/arm/plat-samsung/s5p-dev-mfc.c | 34 ++++++++++++++++++++++ 9 files changed, 98 insertions(+), 1 deletion(-) create mode 100644 Documentation/devicetree/bindings/media/s5p-mfc.txt (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/media/s5p-mfc.txt b/Documentation/devicetree/bindings/media/s5p-mfc.txt new file mode 100644 index 000000000000..67ec3d4ccc7f --- /dev/null +++ b/Documentation/devicetree/bindings/media/s5p-mfc.txt @@ -0,0 +1,23 @@ +* Samsung Multi Format Codec (MFC) + +Multi Format Codec (MFC) is the IP present in Samsung SoCs which +supports high resolution decoding and encoding functionalities. +The MFC device driver is a v4l2 driver which can encode/decode +video raw/elementary streams and has support for all popular +video codecs. + +Required properties: + - compatible : value should be either one among the following + (a) "samsung,mfc-v5" for MFC v5 present in Exynos4 SoCs + (b) "samsung,mfc-v6" for MFC v6 present in Exynos5 SoCs + + - reg : Physical base address of the IP registers and length of memory + mapped region. + + - interrupts : MFC interrupt number to the CPU. + + - samsung,mfc-r : Base address of the first memory bank used by MFC + for DMA contiguous memory allocation and its size. + + - samsung,mfc-l : Base address of the second memory bank used by MFC + for DMA contiguous memory allocation and its size. diff --git a/arch/arm/boot/dts/exynos5250-smdk5250.dts b/arch/arm/boot/dts/exynos5250-smdk5250.dts index a352df403b7a..21d4ccdb0a5a 100644 --- a/arch/arm/boot/dts/exynos5250-smdk5250.dts +++ b/arch/arm/boot/dts/exynos5250-smdk5250.dts @@ -166,4 +166,9 @@ spi_2: spi@12d40000 { status = "disabled"; }; + + codec@11000000 { + samsung,mfc-r = <0x43000000 0x800000>; + samsung,mfc-l = <0x51000000 0x800000>; + }; }; diff --git a/arch/arm/boot/dts/exynos5250.dtsi b/arch/arm/boot/dts/exynos5250.dtsi index dddfd6e444dc..49546bc43ec0 100644 --- a/arch/arm/boot/dts/exynos5250.dtsi +++ b/arch/arm/boot/dts/exynos5250.dtsi @@ -62,6 +62,12 @@ interrupts = <0 42 0>; }; + codec@11000000 { + compatible = "samsung,mfc-v6"; + reg = <0x11000000 0x10000>; + interrupts = <0 96 0>; + }; + rtc { compatible = "samsung,s3c6410-rtc"; reg = <0x101E0000 0x100>; diff --git a/arch/arm/mach-exynos/Kconfig b/arch/arm/mach-exynos/Kconfig index da55107033dd..bb3b09aa9183 100644 --- a/arch/arm/mach-exynos/Kconfig +++ b/arch/arm/mach-exynos/Kconfig @@ -63,6 +63,7 @@ config SOC_EXYNOS5250 depends on ARCH_EXYNOS5 select S5P_PM if PM select S5P_SLEEP if PM + select S5P_DEV_MFC select SAMSUNG_DMADEV help Enable EXYNOS5250 SoC support diff --git a/arch/arm/mach-exynos/clock-exynos5.c b/arch/arm/mach-exynos/clock-exynos5.c index c44ca1ee1b8d..8c4e3253cdb0 100644 --- a/arch/arm/mach-exynos/clock-exynos5.c +++ b/arch/arm/mach-exynos/clock-exynos5.c @@ -664,7 +664,7 @@ static struct clk exynos5_init_clocks_off[] = { .ctrlbit = (1 << 25), }, { .name = "mfc", - .devname = "s5p-mfc", + .devname = "s5p-mfc-v6", .enable = exynos5_clk_ip_mfc_ctrl, .ctrlbit = (1 << 0), }, { diff --git a/arch/arm/mach-exynos/mach-exynos5-dt.c b/arch/arm/mach-exynos/mach-exynos5-dt.c index db1cd8eacf28..b7f1154088bd 100644 --- a/arch/arm/mach-exynos/mach-exynos5-dt.c +++ b/arch/arm/mach-exynos/mach-exynos5-dt.c @@ -11,6 +11,8 @@ #include #include +#include +#include #include #include @@ -18,6 +20,7 @@ #include #include +#include #include "common.h" @@ -72,6 +75,7 @@ static const struct of_dev_auxdata exynos5250_auxdata_lookup[] __initconst = { "exynos-gsc.2", NULL), OF_DEV_AUXDATA("samsung,exynos5-gsc", EXYNOS5_PA_GSC3, "exynos-gsc.3", NULL), + OF_DEV_AUXDATA("samsung,mfc-v6", 0x11000000, "s5p-mfc-v6", NULL), {}, }; @@ -92,6 +96,17 @@ static char const *exynos5250_dt_compat[] __initdata = { NULL }; +static void __init exynos5_reserve(void) +{ + struct s5p_mfc_dt_meminfo mfc_mem; + + /* Reserve memory for MFC only if it's available */ + mfc_mem.compatible = "samsung,mfc-v6"; + if (of_scan_flat_dt(s5p_fdt_find_mfc_mem, &mfc_mem)) + s5p_mfc_reserve_mem(mfc_mem.roff, mfc_mem.rsize, mfc_mem.loff, + mfc_mem.lsize); +} + DT_MACHINE_START(EXYNOS5_DT, "SAMSUNG EXYNOS5 (Flattened Device Tree)") /* Maintainer: Kukjin Kim */ .init_irq = exynos5_init_irq, @@ -103,4 +118,5 @@ DT_MACHINE_START(EXYNOS5_DT, "SAMSUNG EXYNOS5 (Flattened Device Tree)") .timer = &exynos4_timer, .dt_compat = exynos5250_dt_compat, .restart = exynos5_restart, + .reserve = exynos5_reserve, MACHINE_END diff --git a/arch/arm/plat-samsung/devs.c b/arch/arm/plat-samsung/devs.c index 03f654d55eff..52dfa8f914c7 100644 --- a/arch/arm/plat-samsung/devs.c +++ b/arch/arm/plat-samsung/devs.c @@ -933,6 +933,7 @@ struct platform_device s5p_device_mfc_r = { .coherent_dma_mask = DMA_BIT_MASK(32), }, }; + #endif /* CONFIG_S5P_DEV_MFC */ /* MIPI CSIS */ diff --git a/arch/arm/plat-samsung/include/plat/mfc.h b/arch/arm/plat-samsung/include/plat/mfc.h index ac13227272f0..e6d7c42d68b6 100644 --- a/arch/arm/plat-samsung/include/plat/mfc.h +++ b/arch/arm/plat-samsung/include/plat/mfc.h @@ -10,6 +10,14 @@ #ifndef __PLAT_SAMSUNG_MFC_H #define __PLAT_SAMSUNG_MFC_H __FILE__ +struct s5p_mfc_dt_meminfo { + unsigned long loff; + unsigned long lsize; + unsigned long roff; + unsigned long rsize; + char *compatible; +}; + /** * s5p_mfc_reserve_mem - function to early reserve memory for MFC driver * @rbase: base address for MFC 'right' memory interface @@ -24,4 +32,7 @@ void __init s5p_mfc_reserve_mem(phys_addr_t rbase, unsigned int rsize, phys_addr_t lbase, unsigned int lsize); +int __init s5p_fdt_find_mfc_mem(unsigned long node, const char *uname, + int depth, void *data); + #endif /* __PLAT_SAMSUNG_MFC_H */ diff --git a/arch/arm/plat-samsung/s5p-dev-mfc.c b/arch/arm/plat-samsung/s5p-dev-mfc.c index ad6089465e2a..5ec104b5408b 100644 --- a/arch/arm/plat-samsung/s5p-dev-mfc.c +++ b/arch/arm/plat-samsung/s5p-dev-mfc.c @@ -14,6 +14,8 @@ #include #include #include +#include +#include #include #include @@ -69,3 +71,35 @@ static int __init s5p_mfc_memory_init(void) return 0; } device_initcall(s5p_mfc_memory_init); + +#ifdef CONFIG_OF +int __init s5p_fdt_find_mfc_mem(unsigned long node, const char *uname, + int depth, void *data) +{ + __be32 *prop; + unsigned long len; + struct s5p_mfc_dt_meminfo *mfc_mem = data; + + if (!data) + return 0; + + if (!of_flat_dt_is_compatible(node, mfc_mem->compatible)) + return 0; + + prop = of_get_flat_dt_prop(node, "samsung,mfc-l", &len); + if (!prop || (len != 2 * sizeof(unsigned long))) + return 0; + + mfc_mem->loff = be32_to_cpu(prop[0]); + mfc_mem->lsize = be32_to_cpu(prop[1]); + + prop = of_get_flat_dt_prop(node, "samsung,mfc-r", &len); + if (!prop || (len != 2 * sizeof(unsigned long))) + return 0; + + mfc_mem->roff = be32_to_cpu(prop[0]); + mfc_mem->rsize = be32_to_cpu(prop[1]); + + return 1; +} +#endif -- cgit v1.2.3-59-g8ed1b From fd0587339d80dd2fea5ead7f734676c9c618eace Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Sun, 21 Oct 2012 12:05:51 +0200 Subject: Documentation: Reflect the new location of the NMI watchdog info Commit 9919cba7 ("watchdog: Update documentation") moved the NMI watchdog documentation from nmi_watchdog.txt to lockup-watchdogs.txt. Update the index file accordingly. Signed-off-by: Jean Delvare Cc: Fernando Luis Vazquez Cao Cc: Randy Dunlap Cc: Don Zickus Link: http://lkml.kernel.org/r/20121021120551.4656d99b@endymion.delvare Signed-off-by: Ingo Molnar --- Documentation/00-INDEX | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'Documentation') diff --git a/Documentation/00-INDEX b/Documentation/00-INDEX index 49c051380daf..fec55dc5ed2f 100644 --- a/Documentation/00-INDEX +++ b/Documentation/00-INDEX @@ -210,6 +210,8 @@ local_ops.txt - semantics and behavior of local atomic operations. lockdep-design.txt - documentation on the runtime locking correctness validator. +lockup-watchdogs.txt + - info on soft and hard lockup detectors (aka nmi_watchdog). logo.gif - full colour GIF image of Linux logo (penguin - Tux). logo.txt @@ -240,8 +242,6 @@ netlabel/ - directory with information on the NetLabel subsystem. networking/ - directory with info on various aspects of networking with Linux. -nmi_watchdog.txt - - info on NMI watchdog for SMP systems. nommu-mmap.txt - documentation about no-mmu memory mapping support. numastat.txt -- cgit v1.2.3-59-g8ed1b From a03bede5c73a6876fa891cfe82a65460dc9f4698 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Mon, 1 Oct 2012 10:31:53 -0400 Subject: USB: update documentation for URB_ISO_ASAP This patch (as1611) updates the USB documentation and kerneldoc to give a more precise meaning for the URB_ISO_ASAP flag and to explain more of the details of scheduling for isochronous URBs. Signed-off-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- Documentation/usb/error-codes.txt | 5 ++--- drivers/usb/core/urb.c | 22 +++++++++++++++++++--- include/linux/usb.h | 27 ++++++++++++++++----------- 3 files changed, 37 insertions(+), 17 deletions(-) (limited to 'Documentation') diff --git a/Documentation/usb/error-codes.txt b/Documentation/usb/error-codes.txt index b3f606b81a03..8d1e2a9ebbba 100644 --- a/Documentation/usb/error-codes.txt +++ b/Documentation/usb/error-codes.txt @@ -35,9 +35,8 @@ USB-specific: d) ISO: number_of_packets is < 0 e) various other cases --EAGAIN a) specified ISO start frame too early - b) (using ISO-ASAP) too much scheduled for the future - wait some time and try again. +-EXDEV ISO: URB_ISO_ASAP wasn't specified and all the frames + the URB would be scheduled in have already expired. -EFBIG Host controller driver can't schedule that many ISO frames. diff --git a/drivers/usb/core/urb.c b/drivers/usb/core/urb.c index 9d912bfdcffe..3662287e2f4f 100644 --- a/drivers/usb/core/urb.c +++ b/drivers/usb/core/urb.c @@ -214,9 +214,25 @@ EXPORT_SYMBOL_GPL(usb_unanchor_urb); * urb->interval is modified to reflect the actual transfer period used * (normally some power of two units). And for isochronous urbs, * urb->start_frame is modified to reflect when the URB's transfers were - * scheduled to start. Not all isochronous transfer scheduling policies - * will work, but most host controller drivers should easily handle ISO - * queues going from now until 10-200 msec into the future. + * scheduled to start. + * + * Not all isochronous transfer scheduling policies will work, but most + * host controller drivers should easily handle ISO queues going from now + * until 10-200 msec into the future. Drivers should try to keep at + * least one or two msec of data in the queue; many controllers require + * that new transfers start at least 1 msec in the future when they are + * added. If the driver is unable to keep up and the queue empties out, + * the behavior for new submissions is governed by the URB_ISO_ASAP flag. + * If the flag is set, or if the queue is idle, then the URB is always + * assigned to the first available (and not yet expired) slot in the + * endpoint's schedule. If the flag is not set and the queue is active + * then the URB is always assigned to the next slot in the schedule + * following the end of the endpoint's previous URB, even if that slot is + * in the past. When a packet is assigned in this way to a slot that has + * already expired, the packet is not transmitted and the corresponding + * usb_iso_packet_descriptor's status field will return -EXDEV. If this + * would happen to all the packets in the URB, submission fails with a + * -EXDEV error code. * * For control endpoints, the synchronous usb_control_msg() call is * often used (in non-interrupt context) instead of this call. diff --git a/include/linux/usb.h b/include/linux/usb.h index 10278d18709c..f92cdf0c1457 100644 --- a/include/linux/usb.h +++ b/include/linux/usb.h @@ -1129,8 +1129,8 @@ extern int usb_disabled(void); * Note: URB_DIR_IN/OUT is automatically set in usb_submit_urb(). */ #define URB_SHORT_NOT_OK 0x0001 /* report short reads as errors */ -#define URB_ISO_ASAP 0x0002 /* iso-only, urb->start_frame - * ignored */ +#define URB_ISO_ASAP 0x0002 /* iso-only; use the first unexpired + * slot in the schedule */ #define URB_NO_TRANSFER_DMA_MAP 0x0004 /* urb->transfer_dma valid on submit */ #define URB_NO_FSBR 0x0020 /* UHCI-specific */ #define URB_ZERO_PACKET 0x0040 /* Finish bulk OUT with short packet */ @@ -1309,15 +1309,20 @@ typedef void (*usb_complete_t)(struct urb *); * the transfer interval in the endpoint descriptor is logarithmic. * Device drivers must convert that value to linear units themselves.) * - * Isochronous URBs normally use the URB_ISO_ASAP transfer flag, telling - * the host controller to schedule the transfer as soon as bandwidth - * utilization allows, and then set start_frame to reflect the actual frame - * selected during submission. Otherwise drivers must specify the start_frame - * and handle the case where the transfer can't begin then. However, drivers - * won't know how bandwidth is currently allocated, and while they can - * find the current frame using usb_get_current_frame_number () they can't - * know the range for that frame number. (Ranges for frame counter values - * are HC-specific, and can go from 256 to 65536 frames from "now".) + * If an isochronous endpoint queue isn't already running, the host + * controller will schedule a new URB to start as soon as bandwidth + * utilization allows. If the queue is running then a new URB will be + * scheduled to start in the first transfer slot following the end of the + * preceding URB, if that slot has not already expired. If the slot has + * expired (which can happen when IRQ delivery is delayed for a long time), + * the scheduling behavior depends on the URB_ISO_ASAP flag. If the flag + * is clear then the URB will be scheduled to start in the expired slot, + * implying that some of its packets will not be transferred; if the flag + * is set then the URB will be scheduled in the first unexpired slot, + * breaking the queue's synchronization. Upon URB completion, the + * start_frame field will be set to the (micro)frame number in which the + * transfer was scheduled. Ranges for frame counter values are HC-specific + * and can go from as low as 256 to as high as 65536 frames. * * Isochronous URBs have a different data transfer model, in part because * the quality of service is only "best effort". Callers provide specially -- cgit v1.2.3-59-g8ed1b From ae0fb4b72c8db7e6c4ef32bc58a43a759ad414b9 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Tue, 23 Oct 2012 01:09:12 +0200 Subject: PM / QoS: Introduce PM QoS device flags support Modify the device PM QoS core code to support PM QoS flags requests. First, add a new field of type struct pm_qos_flags called "flags" to struct dev_pm_qos for representing the list of PM QoS flags requests for the given device. Accordingly, add a new "type" field to struct dev_pm_qos_request (along with an enum for representing request types) and a new member called "flr" to its data union for representig flags requests. Second, modify dev_pm_qos_add_request(), dev_pm_qos_update_request(), the internal routine apply_constraint() used by them and their existing callers to cover flags requests as well as latency requests. In particular, dev_pm_qos_add_request() gets a new argument called "type" for specifying the type of a request to be added. Finally, introduce two routines, __dev_pm_qos_flags() and dev_pm_qos_flags(), allowing their callers to check which PM QoS flags have been requested for the given device (the caller is supposed to pass the mask of flags to check as the routine's second argument and examine its return value for the result). Signed-off-by: Rafael J. Wysocki Reviewed-by: Jean Pihet Reviewed-by: mark gross --- Documentation/power/pm_qos_interface.txt | 2 +- drivers/base/power/qos.c | 124 ++++++++++++++++++++++++------- drivers/mtd/nand/sh_flctl.c | 4 +- include/linux/pm_qos.h | 26 ++++++- 4 files changed, 127 insertions(+), 29 deletions(-) (limited to 'Documentation') diff --git a/Documentation/power/pm_qos_interface.txt b/Documentation/power/pm_qos_interface.txt index 17e130a80347..79a2a58425ee 100644 --- a/Documentation/power/pm_qos_interface.txt +++ b/Documentation/power/pm_qos_interface.txt @@ -99,7 +99,7 @@ reading the aggregated value does not require any locking mechanism. From kernel mode the use of this interface is the following: -int dev_pm_qos_add_request(device, handle, value): +int dev_pm_qos_add_request(device, handle, type, value): Will insert an element into the list for that identified device with the target value. Upon change to this list the new target is recomputed and any registered notifiers are called only if the target value is now different. diff --git a/drivers/base/power/qos.c b/drivers/base/power/qos.c index 96d27b821bb8..3c66f75d14b0 100644 --- a/drivers/base/power/qos.c +++ b/drivers/base/power/qos.c @@ -47,6 +47,50 @@ static DEFINE_MUTEX(dev_pm_qos_mtx); static BLOCKING_NOTIFIER_HEAD(dev_pm_notifiers); +/** + * __dev_pm_qos_flags - Check PM QoS flags for a given device. + * @dev: Device to check the PM QoS flags for. + * @mask: Flags to check against. + * + * This routine must be called with dev->power.lock held. + */ +enum pm_qos_flags_status __dev_pm_qos_flags(struct device *dev, s32 mask) +{ + struct dev_pm_qos *qos = dev->power.qos; + struct pm_qos_flags *pqf; + s32 val; + + if (!qos) + return PM_QOS_FLAGS_UNDEFINED; + + pqf = &qos->flags; + if (list_empty(&pqf->list)) + return PM_QOS_FLAGS_UNDEFINED; + + val = pqf->effective_flags & mask; + if (val) + return (val == mask) ? PM_QOS_FLAGS_ALL : PM_QOS_FLAGS_SOME; + + return PM_QOS_FLAGS_NONE; +} + +/** + * dev_pm_qos_flags - Check PM QoS flags for a given device (locked). + * @dev: Device to check the PM QoS flags for. + * @mask: Flags to check against. + */ +enum pm_qos_flags_status dev_pm_qos_flags(struct device *dev, s32 mask) +{ + unsigned long irqflags; + enum pm_qos_flags_status ret; + + spin_lock_irqsave(&dev->power.lock, irqflags); + ret = __dev_pm_qos_flags(dev, mask); + spin_unlock_irqrestore(&dev->power.lock, irqflags); + + return ret; +} + /** * __dev_pm_qos_read_value - Get PM QoS constraint for a given device. * @dev: Device to get the PM QoS constraint value for. @@ -74,30 +118,39 @@ s32 dev_pm_qos_read_value(struct device *dev) return ret; } -/* - * apply_constraint - * @req: constraint request to apply - * @action: action to perform add/update/remove, of type enum pm_qos_req_action - * @value: defines the qos request +/** + * apply_constraint - Add/modify/remove device PM QoS request. + * @req: Constraint request to apply + * @action: Action to perform (add/update/remove). + * @value: Value to assign to the QoS request. * * Internal function to update the constraints list using the PM QoS core * code and if needed call the per-device and the global notification * callbacks */ static int apply_constraint(struct dev_pm_qos_request *req, - enum pm_qos_req_action action, int value) + enum pm_qos_req_action action, s32 value) { - int ret, curr_value; - - ret = pm_qos_update_target(&req->dev->power.qos->latency, - &req->data.pnode, action, value); + struct dev_pm_qos *qos = req->dev->power.qos; + int ret; - if (ret) { - /* Call the global callbacks if needed */ - curr_value = pm_qos_read_value(&req->dev->power.qos->latency); - blocking_notifier_call_chain(&dev_pm_notifiers, - (unsigned long)curr_value, - req); + switch(req->type) { + case DEV_PM_QOS_LATENCY: + ret = pm_qos_update_target(&qos->latency, &req->data.pnode, + action, value); + if (ret) { + value = pm_qos_read_value(&qos->latency); + blocking_notifier_call_chain(&dev_pm_notifiers, + (unsigned long)value, + req); + } + break; + case DEV_PM_QOS_FLAGS: + ret = pm_qos_update_flags(&qos->flags, &req->data.flr, + action, value); + break; + default: + ret = -EINVAL; } return ret; @@ -134,6 +187,8 @@ static int dev_pm_qos_constraints_allocate(struct device *dev) c->type = PM_QOS_MIN; c->notifiers = n; + INIT_LIST_HEAD(&qos->flags.list); + spin_lock_irq(&dev->power.lock); dev->power.qos = qos; spin_unlock_irq(&dev->power.lock); @@ -207,6 +262,7 @@ void dev_pm_qos_constraints_destroy(struct device *dev) * dev_pm_qos_add_request - inserts new qos request into the list * @dev: target device for the constraint * @req: pointer to a preallocated handle + * @type: type of the request * @value: defines the qos request * * This function inserts a new entry in the device constraints list of @@ -222,7 +278,7 @@ void dev_pm_qos_constraints_destroy(struct device *dev) * from the system. */ int dev_pm_qos_add_request(struct device *dev, struct dev_pm_qos_request *req, - s32 value) + enum dev_pm_qos_req_type type, s32 value) { int ret = 0; @@ -253,8 +309,10 @@ int dev_pm_qos_add_request(struct device *dev, struct dev_pm_qos_request *req, } } - if (!ret) + if (!ret) { + req->type = type; ret = apply_constraint(req, PM_QOS_ADD_REQ, value); + } out: mutex_unlock(&dev_pm_qos_mtx); @@ -281,6 +339,7 @@ EXPORT_SYMBOL_GPL(dev_pm_qos_add_request); int dev_pm_qos_update_request(struct dev_pm_qos_request *req, s32 new_value) { + s32 curr_value; int ret = 0; if (!req) /*guard against callers passing in null */ @@ -292,15 +351,27 @@ int dev_pm_qos_update_request(struct dev_pm_qos_request *req, mutex_lock(&dev_pm_qos_mtx); - if (req->dev->power.qos) { - if (new_value != req->data.pnode.prio) - ret = apply_constraint(req, PM_QOS_UPDATE_REQ, - new_value); - } else { - /* Return if the device has been removed */ + if (!req->dev->power.qos) { ret = -ENODEV; + goto out; } + switch(req->type) { + case DEV_PM_QOS_LATENCY: + curr_value = req->data.pnode.prio; + break; + case DEV_PM_QOS_FLAGS: + curr_value = req->data.flr.flags; + break; + default: + ret = -EINVAL; + goto out; + } + + if (curr_value != new_value) + ret = apply_constraint(req, PM_QOS_UPDATE_REQ, new_value); + + out: mutex_unlock(&dev_pm_qos_mtx); return ret; } @@ -451,7 +522,8 @@ int dev_pm_qos_add_ancestor_request(struct device *dev, ancestor = ancestor->parent; if (ancestor) - error = dev_pm_qos_add_request(ancestor, req, value); + error = dev_pm_qos_add_request(ancestor, req, + DEV_PM_QOS_LATENCY, value); if (error) req->dev = NULL; @@ -487,7 +559,7 @@ int dev_pm_qos_expose_latency_limit(struct device *dev, s32 value) if (!req) return -ENOMEM; - ret = dev_pm_qos_add_request(dev, req, value); + ret = dev_pm_qos_add_request(dev, req, DEV_PM_QOS_LATENCY, value); if (ret < 0) return ret; diff --git a/drivers/mtd/nand/sh_flctl.c b/drivers/mtd/nand/sh_flctl.c index 4fbfe96e37a1..f48ac5d80bbf 100644 --- a/drivers/mtd/nand/sh_flctl.c +++ b/drivers/mtd/nand/sh_flctl.c @@ -727,7 +727,9 @@ static void flctl_select_chip(struct mtd_info *mtd, int chipnr) if (!flctl->qos_request) { ret = dev_pm_qos_add_request(&flctl->pdev->dev, - &flctl->pm_qos, 100); + &flctl->pm_qos, + DEV_PM_QOS_LATENCY, + 100); if (ret < 0) dev_err(&flctl->pdev->dev, "PM QoS request failed: %d\n", ret); diff --git a/include/linux/pm_qos.h b/include/linux/pm_qos.h index 3b9d14964d2b..3af7d8573c23 100644 --- a/include/linux/pm_qos.h +++ b/include/linux/pm_qos.h @@ -20,6 +20,13 @@ enum { PM_QOS_NUM_CLASSES, }; +enum pm_qos_flags_status { + PM_QOS_FLAGS_UNDEFINED = -1, + PM_QOS_FLAGS_NONE, + PM_QOS_FLAGS_SOME, + PM_QOS_FLAGS_ALL, +}; + #define PM_QOS_DEFAULT_VALUE -1 #define PM_QOS_CPU_DMA_LAT_DEFAULT_VALUE (2000 * USEC_PER_SEC) @@ -38,9 +45,16 @@ struct pm_qos_flags_request { s32 flags; /* Do not change to 64 bit */ }; +enum dev_pm_qos_req_type { + DEV_PM_QOS_LATENCY = 1, + DEV_PM_QOS_FLAGS, +}; + struct dev_pm_qos_request { + enum dev_pm_qos_req_type type; union { struct plist_node pnode; + struct pm_qos_flags_request flr; } data; struct device *dev; }; @@ -71,6 +85,7 @@ struct pm_qos_flags { struct dev_pm_qos { struct pm_qos_constraints latency; + struct pm_qos_flags flags; }; /* Action requested to pm_qos_update_target */ @@ -105,10 +120,12 @@ int pm_qos_request_active(struct pm_qos_request *req); s32 pm_qos_read_value(struct pm_qos_constraints *c); #ifdef CONFIG_PM +enum pm_qos_flags_status __dev_pm_qos_flags(struct device *dev, s32 mask); +enum pm_qos_flags_status dev_pm_qos_flags(struct device *dev, s32 mask); s32 __dev_pm_qos_read_value(struct device *dev); s32 dev_pm_qos_read_value(struct device *dev); int dev_pm_qos_add_request(struct device *dev, struct dev_pm_qos_request *req, - s32 value); + enum dev_pm_qos_req_type type, s32 value); int dev_pm_qos_update_request(struct dev_pm_qos_request *req, s32 new_value); int dev_pm_qos_remove_request(struct dev_pm_qos_request *req); int dev_pm_qos_add_notifier(struct device *dev, @@ -122,12 +139,19 @@ void dev_pm_qos_constraints_destroy(struct device *dev); int dev_pm_qos_add_ancestor_request(struct device *dev, struct dev_pm_qos_request *req, s32 value); #else +static inline enum pm_qos_flags_status __dev_pm_qos_flags(struct device *dev, + s32 mask) + { return PM_QOS_FLAGS_UNDEFINED; } +static inline enum pm_qos_flags_status dev_pm_qos_flags(struct device *dev, + s32 mask) + { return PM_QOS_FLAGS_UNDEFINED; } static inline s32 __dev_pm_qos_read_value(struct device *dev) { return 0; } static inline s32 dev_pm_qos_read_value(struct device *dev) { return 0; } static inline int dev_pm_qos_add_request(struct device *dev, struct dev_pm_qos_request *req, + enum dev_pm_qos_req_type type, s32 value) { return 0; } static inline int dev_pm_qos_update_request(struct dev_pm_qos_request *req, -- cgit v1.2.3-59-g8ed1b From e3978cded41dc7b364e74037f56d6bc558c11fd7 Mon Sep 17 00:00:00 2001 From: Catalin Marinas Date: Tue, 23 Oct 2012 14:51:16 +0100 Subject: arm64: Move PCI_IOBASE closer to MODULES_VADDR This is to reuse the same pmd table that is sparsely populated with the modules space. Signed-off-by: Catalin Marinas --- Documentation/arm64/memory.txt | 12 ++++++------ arch/arm64/include/asm/io.h | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) (limited to 'Documentation') diff --git a/Documentation/arm64/memory.txt b/Documentation/arm64/memory.txt index dbbdcbba75a3..4110cca96bd6 100644 --- a/Documentation/arm64/memory.txt +++ b/Documentation/arm64/memory.txt @@ -27,17 +27,17 @@ Start End Size Use ----------------------------------------------------------------------- 0000000000000000 0000007fffffffff 512GB user -ffffff8000000000 ffffffbbfffcffff ~240GB vmalloc +ffffff8000000000 ffffffbbfffeffff ~240GB vmalloc -ffffffbbfffd0000 ffffffbcfffdffff 64KB [guard page] +ffffffbbffff0000 ffffffbbffffffff 64KB [guard page] -ffffffbbfffe0000 ffffffbcfffeffff 64KB PCI I/O space +ffffffbc00000000 ffffffbdffffffff 8GB vmemmap -ffffffbbffff0000 ffffffbcffffffff 64KB [guard page] +ffffffbe00000000 ffffffbffbbfffff ~8GB [guard, future vmmemap] -ffffffbc00000000 ffffffbdffffffff 8GB vmemmap +ffffffbffbe00000 ffffffbffbe0ffff 64KB PCI I/O space -ffffffbe00000000 ffffffbffbffffff ~8GB [guard, future vmmemap] +ffffffbbffff0000 ffffffbcffffffff ~2MB [guard] ffffffbffc000000 ffffffbfffffffff 64MB modules diff --git a/arch/arm64/include/asm/io.h b/arch/arm64/include/asm/io.h index d10778fe91e5..54f6116697f7 100644 --- a/arch/arm64/include/asm/io.h +++ b/arch/arm64/include/asm/io.h @@ -114,7 +114,7 @@ static inline u64 __raw_readq(const volatile void __iomem *addr) * I/O port access primitives. */ #define IO_SPACE_LIMIT 0xffff -#define PCI_IOBASE ((void __iomem *)0xffffffbbfffe0000UL) +#define PCI_IOBASE ((void __iomem *)(MODULES_VADDR - SZ_2M)) static inline u8 inb(unsigned long addr) { -- cgit v1.2.3-59-g8ed1b From f191eec58803e1e16c3421638cdcc9195c425851 Mon Sep 17 00:00:00 2001 From: "Paul E. McKenney" Date: Wed, 3 Oct 2012 10:28:30 -0700 Subject: Documentation: Fix memory-barriers.txt example This commit fixes a broken example of overlapping stores in the Documentation/memory-barriers.txt file. Reported-by: Nikunj A Dadhania Signed-off-by: Paul E. McKenney --- Documentation/memory-barriers.txt | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'Documentation') diff --git a/Documentation/memory-barriers.txt b/Documentation/memory-barriers.txt index 2759f7c188f0..3c4e1b3b80a1 100644 --- a/Documentation/memory-barriers.txt +++ b/Documentation/memory-barriers.txt @@ -251,12 +251,13 @@ And there are a number of things that _must_ or _must_not_ be assumed: And for: - *A = X; Y = *A; + *A = X; *(A + 4) = Y; - we may get either of: + we may get any of: - STORE *A = X; Y = LOAD *A; - STORE *A = Y = X; + STORE *A = X; STORE *(A + 4) = Y; + STORE *(A + 4) = Y; STORE *A = X; + STORE {*A, *(A + 4) } = {X, Y}; ========================= -- cgit v1.2.3-59-g8ed1b From 0f9574d83234274c4d8f8f742b45022e5718b16e Mon Sep 17 00:00:00 2001 From: Dhaval Giani Date: Wed, 17 Oct 2012 14:46:13 -0700 Subject: rcu: Correct the name of a reference in list of RCU papers Trying to go through the history of RCU (not for the weak minded) led me to search for a non-existent paper. Correct it to the actual reference Signed-off-by: Dhaval Giani Cc: Peter Zijlstra Signed-off-by: Paul E. McKenney --- Documentation/RCU/RTFP.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Documentation') diff --git a/Documentation/RCU/RTFP.txt b/Documentation/RCU/RTFP.txt index 7c1dfb19fc40..7f40c72a9c51 100644 --- a/Documentation/RCU/RTFP.txt +++ b/Documentation/RCU/RTFP.txt @@ -186,7 +186,7 @@ Bibtex Entries @article{Kung80 ,author="H. T. Kung and Q. Lehman" -,title="Concurrent Maintenance of Binary Search Trees" +,title="Concurrent Manipulation of Binary Search Trees" ,Year="1980" ,Month="September" ,journal="ACM Transactions on Database Systems" -- cgit v1.2.3-59-g8ed1b From e39473d0b9448e770f49b0b15e514be884264438 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 24 Oct 2012 02:08:18 +0200 Subject: PM / QoS: Make it possible to expose PM QoS device flags to user space Define two device PM QoS flags, PM_QOS_FLAG_NO_POWER_OFF and PM_QOS_FLAG_REMOTE_WAKEUP, and introduce routines dev_pm_qos_expose_flags() and dev_pm_qos_hide_flags() allowing the caller to expose those two flags to user space or to hide them from it, respectively. After the flags have been exposed, user space will see two additional sysfs attributes, pm_qos_no_power_off and pm_qos_remote_wakeup, under the device's /sys/devices/.../power/ directory. Then, writing 1 to one of them will update the PM QoS flags request owned by user space so that the corresponding flag is requested to be set. In turn, writing 0 to one of them will cause the corresponding flag in the user space's request to be cleared (however, the owners of the other PM QoS flags requests for the same device may still request the flag to be set and it may be effectively set even if user space doesn't request that). Signed-off-by: Rafael J. Wysocki Reviewed-by: Jean Pihet Acked-by: mark gross --- Documentation/ABI/testing/sysfs-devices-power | 31 +++++ drivers/base/power/power.h | 6 +- drivers/base/power/qos.c | 168 ++++++++++++++++++++------ drivers/base/power/sysfs.c | 94 ++++++++++++-- include/linux/pm.h | 1 - include/linux/pm_qos.h | 26 ++++ 6 files changed, 278 insertions(+), 48 deletions(-) (limited to 'Documentation') diff --git a/Documentation/ABI/testing/sysfs-devices-power b/Documentation/ABI/testing/sysfs-devices-power index 45000f0db4d4..7fc2997b23a6 100644 --- a/Documentation/ABI/testing/sysfs-devices-power +++ b/Documentation/ABI/testing/sysfs-devices-power @@ -204,3 +204,34 @@ Description: This attribute has no effect on system-wide suspend/resume and hibernation. + +What: /sys/devices/.../power/pm_qos_no_power_off +Date: September 2012 +Contact: Rafael J. Wysocki +Description: + The /sys/devices/.../power/pm_qos_no_power_off attribute + is used for manipulating the PM QoS "no power off" flag. If + set, this flag indicates to the kernel that power should not + be removed entirely from the device. + + Not all drivers support this attribute. If it isn't supported, + it is not present. + + This attribute has no effect on system-wide suspend/resume and + hibernation. + +What: /sys/devices/.../power/pm_qos_remote_wakeup +Date: September 2012 +Contact: Rafael J. Wysocki +Description: + The /sys/devices/.../power/pm_qos_remote_wakeup attribute + is used for manipulating the PM QoS "remote wakeup required" + flag. If set, this flag indicates to the kernel that the + device is a source of user events that have to be signaled from + its low-power states. + + Not all drivers support this attribute. If it isn't supported, + it is not present. + + This attribute has no effect on system-wide suspend/resume and + hibernation. diff --git a/drivers/base/power/power.h b/drivers/base/power/power.h index 0dbfdf4419af..b16686a0a5a2 100644 --- a/drivers/base/power/power.h +++ b/drivers/base/power/power.h @@ -93,8 +93,10 @@ extern void dpm_sysfs_remove(struct device *dev); extern void rpm_sysfs_remove(struct device *dev); extern int wakeup_sysfs_add(struct device *dev); extern void wakeup_sysfs_remove(struct device *dev); -extern int pm_qos_sysfs_add(struct device *dev); -extern void pm_qos_sysfs_remove(struct device *dev); +extern int pm_qos_sysfs_add_latency(struct device *dev); +extern void pm_qos_sysfs_remove_latency(struct device *dev); +extern int pm_qos_sysfs_add_flags(struct device *dev); +extern void pm_qos_sysfs_remove_flags(struct device *dev); #else /* CONFIG_PM */ diff --git a/drivers/base/power/qos.c b/drivers/base/power/qos.c index 3c66f75d14b0..167834dcc82a 100644 --- a/drivers/base/power/qos.c +++ b/drivers/base/power/qos.c @@ -40,6 +40,7 @@ #include #include #include +#include #include "power.h" @@ -321,6 +322,37 @@ int dev_pm_qos_add_request(struct device *dev, struct dev_pm_qos_request *req, } EXPORT_SYMBOL_GPL(dev_pm_qos_add_request); +/** + * __dev_pm_qos_update_request - Modify an existing device PM QoS request. + * @req : PM QoS request to modify. + * @new_value: New value to request. + */ +static int __dev_pm_qos_update_request(struct dev_pm_qos_request *req, + s32 new_value) +{ + s32 curr_value; + int ret = 0; + + if (!req->dev->power.qos) + return -ENODEV; + + switch(req->type) { + case DEV_PM_QOS_LATENCY: + curr_value = req->data.pnode.prio; + break; + case DEV_PM_QOS_FLAGS: + curr_value = req->data.flr.flags; + break; + default: + return -EINVAL; + } + + if (curr_value != new_value) + ret = apply_constraint(req, PM_QOS_UPDATE_REQ, new_value); + + return ret; +} + /** * dev_pm_qos_update_request - modifies an existing qos request * @req : handle to list element holding a dev_pm_qos request to use @@ -336,11 +368,9 @@ EXPORT_SYMBOL_GPL(dev_pm_qos_add_request); * -EINVAL in case of wrong parameters, -ENODEV if the device has been * removed from the system */ -int dev_pm_qos_update_request(struct dev_pm_qos_request *req, - s32 new_value) +int dev_pm_qos_update_request(struct dev_pm_qos_request *req, s32 new_value) { - s32 curr_value; - int ret = 0; + int ret; if (!req) /*guard against callers passing in null */ return -EINVAL; @@ -350,29 +380,9 @@ int dev_pm_qos_update_request(struct dev_pm_qos_request *req, return -EINVAL; mutex_lock(&dev_pm_qos_mtx); - - if (!req->dev->power.qos) { - ret = -ENODEV; - goto out; - } - - switch(req->type) { - case DEV_PM_QOS_LATENCY: - curr_value = req->data.pnode.prio; - break; - case DEV_PM_QOS_FLAGS: - curr_value = req->data.flr.flags; - break; - default: - ret = -EINVAL; - goto out; - } - - if (curr_value != new_value) - ret = apply_constraint(req, PM_QOS_UPDATE_REQ, new_value); - - out: + __dev_pm_qos_update_request(req, new_value); mutex_unlock(&dev_pm_qos_mtx); + return ret; } EXPORT_SYMBOL_GPL(dev_pm_qos_update_request); @@ -533,10 +543,19 @@ int dev_pm_qos_add_ancestor_request(struct device *dev, EXPORT_SYMBOL_GPL(dev_pm_qos_add_ancestor_request); #ifdef CONFIG_PM_RUNTIME -static void __dev_pm_qos_drop_user_request(struct device *dev) +static void __dev_pm_qos_drop_user_request(struct device *dev, + enum dev_pm_qos_req_type type) { - dev_pm_qos_remove_request(dev->power.pq_req); - dev->power.pq_req = NULL; + switch(type) { + case DEV_PM_QOS_LATENCY: + dev_pm_qos_remove_request(dev->power.qos->latency_req); + dev->power.qos->latency_req = NULL; + break; + case DEV_PM_QOS_FLAGS: + dev_pm_qos_remove_request(dev->power.qos->flags_req); + dev->power.qos->flags_req = NULL; + break; + } } /** @@ -552,7 +571,7 @@ int dev_pm_qos_expose_latency_limit(struct device *dev, s32 value) if (!device_is_registered(dev) || value < 0) return -EINVAL; - if (dev->power.pq_req) + if (dev->power.qos && dev->power.qos->latency_req) return -EEXIST; req = kzalloc(sizeof(*req), GFP_KERNEL); @@ -563,10 +582,10 @@ int dev_pm_qos_expose_latency_limit(struct device *dev, s32 value) if (ret < 0) return ret; - dev->power.pq_req = req; - ret = pm_qos_sysfs_add(dev); + dev->power.qos->latency_req = req; + ret = pm_qos_sysfs_add_latency(dev); if (ret) - __dev_pm_qos_drop_user_request(dev); + __dev_pm_qos_drop_user_request(dev, DEV_PM_QOS_LATENCY); return ret; } @@ -578,10 +597,87 @@ EXPORT_SYMBOL_GPL(dev_pm_qos_expose_latency_limit); */ void dev_pm_qos_hide_latency_limit(struct device *dev) { - if (dev->power.pq_req) { - pm_qos_sysfs_remove(dev); - __dev_pm_qos_drop_user_request(dev); + if (dev->power.qos && dev->power.qos->latency_req) { + pm_qos_sysfs_remove_latency(dev); + __dev_pm_qos_drop_user_request(dev, DEV_PM_QOS_LATENCY); } } EXPORT_SYMBOL_GPL(dev_pm_qos_hide_latency_limit); + +/** + * dev_pm_qos_expose_flags - Expose PM QoS flags of a device to user space. + * @dev: Device whose PM QoS flags are to be exposed to user space. + * @val: Initial values of the flags. + */ +int dev_pm_qos_expose_flags(struct device *dev, s32 val) +{ + struct dev_pm_qos_request *req; + int ret; + + if (!device_is_registered(dev)) + return -EINVAL; + + if (dev->power.qos && dev->power.qos->flags_req) + return -EEXIST; + + req = kzalloc(sizeof(*req), GFP_KERNEL); + if (!req) + return -ENOMEM; + + ret = dev_pm_qos_add_request(dev, req, DEV_PM_QOS_FLAGS, val); + if (ret < 0) + return ret; + + dev->power.qos->flags_req = req; + ret = pm_qos_sysfs_add_flags(dev); + if (ret) + __dev_pm_qos_drop_user_request(dev, DEV_PM_QOS_FLAGS); + + return ret; +} +EXPORT_SYMBOL_GPL(dev_pm_qos_expose_flags); + +/** + * dev_pm_qos_hide_flags - Hide PM QoS flags of a device from user space. + * @dev: Device whose PM QoS flags are to be hidden from user space. + */ +void dev_pm_qos_hide_flags(struct device *dev) +{ + if (dev->power.qos && dev->power.qos->flags_req) { + pm_qos_sysfs_remove_flags(dev); + __dev_pm_qos_drop_user_request(dev, DEV_PM_QOS_FLAGS); + } +} +EXPORT_SYMBOL_GPL(dev_pm_qos_hide_flags); + +/** + * dev_pm_qos_update_flags - Update PM QoS flags request owned by user space. + * @dev: Device to update the PM QoS flags request for. + * @mask: Flags to set/clear. + * @set: Whether to set or clear the flags (true means set). + */ +int dev_pm_qos_update_flags(struct device *dev, s32 mask, bool set) +{ + s32 value; + int ret; + + if (!dev->power.qos || !dev->power.qos->flags_req) + return -EINVAL; + + pm_runtime_get_sync(dev); + mutex_lock(&dev_pm_qos_mtx); + + value = dev_pm_qos_requested_flags(dev); + if (set) + value |= mask; + else + value &= ~mask; + + ret = __dev_pm_qos_update_request(dev->power.qos->flags_req, value); + + mutex_unlock(&dev_pm_qos_mtx); + pm_runtime_put(dev); + + return ret; +} #endif /* CONFIG_PM_RUNTIME */ diff --git a/drivers/base/power/sysfs.c b/drivers/base/power/sysfs.c index 54c61ffa2044..50d16e3cb0a9 100644 --- a/drivers/base/power/sysfs.c +++ b/drivers/base/power/sysfs.c @@ -221,7 +221,7 @@ static DEVICE_ATTR(autosuspend_delay_ms, 0644, autosuspend_delay_ms_show, static ssize_t pm_qos_latency_show(struct device *dev, struct device_attribute *attr, char *buf) { - return sprintf(buf, "%d\n", dev->power.pq_req->data.pnode.prio); + return sprintf(buf, "%d\n", dev_pm_qos_requested_latency(dev)); } static ssize_t pm_qos_latency_store(struct device *dev, @@ -237,12 +237,66 @@ static ssize_t pm_qos_latency_store(struct device *dev, if (value < 0) return -EINVAL; - ret = dev_pm_qos_update_request(dev->power.pq_req, value); + ret = dev_pm_qos_update_request(dev->power.qos->latency_req, value); return ret < 0 ? ret : n; } static DEVICE_ATTR(pm_qos_resume_latency_us, 0644, pm_qos_latency_show, pm_qos_latency_store); + +static ssize_t pm_qos_no_power_off_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + return sprintf(buf, "%d\n", !!(dev_pm_qos_requested_flags(dev) + & PM_QOS_FLAG_NO_POWER_OFF)); +} + +static ssize_t pm_qos_no_power_off_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t n) +{ + int ret; + + if (kstrtoint(buf, 0, &ret)) + return -EINVAL; + + if (ret != 0 && ret != 1) + return -EINVAL; + + ret = dev_pm_qos_update_flags(dev, PM_QOS_FLAG_NO_POWER_OFF, ret); + return ret < 0 ? ret : n; +} + +static DEVICE_ATTR(pm_qos_no_power_off, 0644, + pm_qos_no_power_off_show, pm_qos_no_power_off_store); + +static ssize_t pm_qos_remote_wakeup_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + return sprintf(buf, "%d\n", !!(dev_pm_qos_requested_flags(dev) + & PM_QOS_FLAG_REMOTE_WAKEUP)); +} + +static ssize_t pm_qos_remote_wakeup_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t n) +{ + int ret; + + if (kstrtoint(buf, 0, &ret)) + return -EINVAL; + + if (ret != 0 && ret != 1) + return -EINVAL; + + ret = dev_pm_qos_update_flags(dev, PM_QOS_FLAG_REMOTE_WAKEUP, ret); + return ret < 0 ? ret : n; +} + +static DEVICE_ATTR(pm_qos_remote_wakeup, 0644, + pm_qos_remote_wakeup_show, pm_qos_remote_wakeup_store); #endif /* CONFIG_PM_RUNTIME */ #ifdef CONFIG_PM_SLEEP @@ -564,15 +618,27 @@ static struct attribute_group pm_runtime_attr_group = { .attrs = runtime_attrs, }; -static struct attribute *pm_qos_attrs[] = { +static struct attribute *pm_qos_latency_attrs[] = { #ifdef CONFIG_PM_RUNTIME &dev_attr_pm_qos_resume_latency_us.attr, #endif /* CONFIG_PM_RUNTIME */ NULL, }; -static struct attribute_group pm_qos_attr_group = { +static struct attribute_group pm_qos_latency_attr_group = { .name = power_group_name, - .attrs = pm_qos_attrs, + .attrs = pm_qos_latency_attrs, +}; + +static struct attribute *pm_qos_flags_attrs[] = { +#ifdef CONFIG_PM_RUNTIME + &dev_attr_pm_qos_no_power_off.attr, + &dev_attr_pm_qos_remote_wakeup.attr, +#endif /* CONFIG_PM_RUNTIME */ + NULL, +}; +static struct attribute_group pm_qos_flags_attr_group = { + .name = power_group_name, + .attrs = pm_qos_flags_attrs, }; int dpm_sysfs_add(struct device *dev) @@ -615,14 +681,24 @@ void wakeup_sysfs_remove(struct device *dev) sysfs_unmerge_group(&dev->kobj, &pm_wakeup_attr_group); } -int pm_qos_sysfs_add(struct device *dev) +int pm_qos_sysfs_add_latency(struct device *dev) +{ + return sysfs_merge_group(&dev->kobj, &pm_qos_latency_attr_group); +} + +void pm_qos_sysfs_remove_latency(struct device *dev) +{ + sysfs_unmerge_group(&dev->kobj, &pm_qos_latency_attr_group); +} + +int pm_qos_sysfs_add_flags(struct device *dev) { - return sysfs_merge_group(&dev->kobj, &pm_qos_attr_group); + return sysfs_merge_group(&dev->kobj, &pm_qos_flags_attr_group); } -void pm_qos_sysfs_remove(struct device *dev) +void pm_qos_sysfs_remove_flags(struct device *dev) { - sysfs_unmerge_group(&dev->kobj, &pm_qos_attr_group); + sysfs_unmerge_group(&dev->kobj, &pm_qos_flags_attr_group); } void rpm_sysfs_remove(struct device *dev) diff --git a/include/linux/pm.h b/include/linux/pm.h index 0ce6df94221a..03d7bb145311 100644 --- a/include/linux/pm.h +++ b/include/linux/pm.h @@ -546,7 +546,6 @@ struct dev_pm_info { unsigned long active_jiffies; unsigned long suspended_jiffies; unsigned long accounting_timestamp; - struct dev_pm_qos_request *pq_req; #endif struct pm_subsys_data *subsys_data; /* Owned by the subsystem. */ struct dev_pm_qos *qos; diff --git a/include/linux/pm_qos.h b/include/linux/pm_qos.h index 3af7d8573c23..5a95013905c8 100644 --- a/include/linux/pm_qos.h +++ b/include/linux/pm_qos.h @@ -34,6 +34,9 @@ enum pm_qos_flags_status { #define PM_QOS_NETWORK_THROUGHPUT_DEFAULT_VALUE 0 #define PM_QOS_DEV_LAT_DEFAULT_VALUE 0 +#define PM_QOS_FLAG_NO_POWER_OFF (1 << 0) +#define PM_QOS_FLAG_REMOTE_WAKEUP (1 << 1) + struct pm_qos_request { struct plist_node node; int pm_qos_class; @@ -86,6 +89,8 @@ struct pm_qos_flags { struct dev_pm_qos { struct pm_qos_constraints latency; struct pm_qos_flags flags; + struct dev_pm_qos_request *latency_req; + struct dev_pm_qos_request *flags_req; }; /* Action requested to pm_qos_update_target */ @@ -187,10 +192,31 @@ static inline int dev_pm_qos_add_ancestor_request(struct device *dev, #ifdef CONFIG_PM_RUNTIME int dev_pm_qos_expose_latency_limit(struct device *dev, s32 value); void dev_pm_qos_hide_latency_limit(struct device *dev); +int dev_pm_qos_expose_flags(struct device *dev, s32 value); +void dev_pm_qos_hide_flags(struct device *dev); +int dev_pm_qos_update_flags(struct device *dev, s32 mask, bool set); + +static inline s32 dev_pm_qos_requested_latency(struct device *dev) +{ + return dev->power.qos->latency_req->data.pnode.prio; +} + +static inline s32 dev_pm_qos_requested_flags(struct device *dev) +{ + return dev->power.qos->flags_req->data.flr.flags; +} #else static inline int dev_pm_qos_expose_latency_limit(struct device *dev, s32 value) { return 0; } static inline void dev_pm_qos_hide_latency_limit(struct device *dev) {} +static inline int dev_pm_qos_expose_flags(struct device *dev, s32 value) + { return 0; } +static inline void dev_pm_qos_hide_flags(struct device *dev) {} +static inline int dev_pm_qos_update_flags(struct device *dev, s32 m, bool set) + { return 0; } + +static inline s32 dev_pm_qos_requested_latency(struct device *dev) { return 0; } +static inline s32 dev_pm_qos_requested_flags(struct device *dev) { return 0; } #endif #endif -- cgit v1.2.3-59-g8ed1b From 6c9c6d6301287e369a754d628230fa6e50cdb74b Mon Sep 17 00:00:00 2001 From: Shuah Khan Date: Mon, 8 Oct 2012 11:08:06 -0600 Subject: dma-debug: New interfaces to debug dma mapping errors Add dma-debug interface debug_dma_mapping_error() to debug drivers that fail to check dma mapping errors on addresses returned by dma_map_single() and dma_map_page() interfaces. This interface clears a flag set by debug_dma_map_page() to indicate that dma_mapping_error() has been called by the driver. When driver does unmap, debug_dma_unmap() checks the flag and if this flag is still set, prints warning message that includes call trace that leads up to the unmap. This interface can be called from dma_mapping_error() routines to enable dma mapping error check debugging. Tested: Intel iommu and swiotlb (iommu=soft) on x86-64 with CONFIG_DMA_API_DEBUG enabled and disabled. Signed-off-by: Shuah Khan Reviewed-by: Konrad Rzeszutek Wilk Signed-off-by: Joerg Roedel --- Documentation/DMA-API.txt | 12 +++++++ arch/x86/include/asm/dma-mapping.h | 1 + include/linux/dma-debug.h | 7 ++++ lib/dma-debug.c | 71 +++++++++++++++++++++++++++++++++++--- 4 files changed, 87 insertions(+), 4 deletions(-) (limited to 'Documentation') diff --git a/Documentation/DMA-API.txt b/Documentation/DMA-API.txt index 66bd97a95f10..78a6c569d204 100644 --- a/Documentation/DMA-API.txt +++ b/Documentation/DMA-API.txt @@ -678,3 +678,15 @@ out of dma_debug_entries. These entries are preallocated at boot. The number of preallocated entries is defined per architecture. If it is too low for you boot with 'dma_debug_entries=' to overwrite the architectural default. + +void debug_dmap_mapping_error(struct device *dev, dma_addr_t dma_addr); + +dma-debug interface debug_dma_mapping_error() to debug drivers that fail +to check dma mapping errors on addresses returned by dma_map_single() and +dma_map_page() interfaces. This interface clears a flag set by +debug_dma_map_page() to indicate that dma_mapping_error() has been called by +the driver. When driver does unmap, debug_dma_unmap() checks the flag and if +this flag is still set, prints warning message that includes call trace that +leads up to the unmap. This interface can be called from dma_mapping_error() +routines to enable dma mapping error check debugging. + diff --git a/arch/x86/include/asm/dma-mapping.h b/arch/x86/include/asm/dma-mapping.h index f7b4c7903e7e..808dae63eeea 100644 --- a/arch/x86/include/asm/dma-mapping.h +++ b/arch/x86/include/asm/dma-mapping.h @@ -47,6 +47,7 @@ static inline struct dma_map_ops *get_dma_ops(struct device *dev) static inline int dma_mapping_error(struct device *dev, dma_addr_t dma_addr) { struct dma_map_ops *ops = get_dma_ops(dev); + debug_dma_mapping_error(dev, dma_addr); if (ops->mapping_error) return ops->mapping_error(dev, dma_addr); diff --git a/include/linux/dma-debug.h b/include/linux/dma-debug.h index 171ad8aedc83..fc0e34ce038f 100644 --- a/include/linux/dma-debug.h +++ b/include/linux/dma-debug.h @@ -39,6 +39,8 @@ extern void debug_dma_map_page(struct device *dev, struct page *page, int direction, dma_addr_t dma_addr, bool map_single); +extern void debug_dma_mapping_error(struct device *dev, dma_addr_t dma_addr); + extern void debug_dma_unmap_page(struct device *dev, dma_addr_t addr, size_t size, int direction, bool map_single); @@ -105,6 +107,11 @@ static inline void debug_dma_map_page(struct device *dev, struct page *page, { } +static inline void debug_dma_mapping_error(struct device *dev, + dma_addr_t dma_addr) +{ +} + static inline void debug_dma_unmap_page(struct device *dev, dma_addr_t addr, size_t size, int direction, bool map_single) diff --git a/lib/dma-debug.c b/lib/dma-debug.c index d84beb994f36..59f4a1a8187d 100644 --- a/lib/dma-debug.c +++ b/lib/dma-debug.c @@ -45,6 +45,12 @@ enum { dma_debug_coherent, }; +enum map_err_types { + MAP_ERR_CHECK_NOT_APPLICABLE, + MAP_ERR_NOT_CHECKED, + MAP_ERR_CHECKED, +}; + #define DMA_DEBUG_STACKTRACE_ENTRIES 5 struct dma_debug_entry { @@ -57,6 +63,7 @@ struct dma_debug_entry { int direction; int sg_call_ents; int sg_mapped_ents; + enum map_err_types map_err_type; #ifdef CONFIG_STACKTRACE struct stack_trace stacktrace; unsigned long st_entries[DMA_DEBUG_STACKTRACE_ENTRIES]; @@ -114,6 +121,12 @@ static struct device_driver *current_driver __read_mostly; static DEFINE_RWLOCK(driver_name_lock); +static const char *const maperr2str[] = { + [MAP_ERR_CHECK_NOT_APPLICABLE] = "dma map error check not applicable", + [MAP_ERR_NOT_CHECKED] = "dma map error not checked", + [MAP_ERR_CHECKED] = "dma map error checked", +}; + static const char *type2name[4] = { "single", "page", "scather-gather", "coherent" }; @@ -376,11 +389,12 @@ void debug_dma_dump_mappings(struct device *dev) list_for_each_entry(entry, &bucket->list, list) { if (!dev || dev == entry->dev) { dev_info(entry->dev, - "%s idx %d P=%Lx D=%Lx L=%Lx %s\n", + "%s idx %d P=%Lx D=%Lx L=%Lx %s %s\n", type2name[entry->type], idx, (unsigned long long)entry->paddr, entry->dev_addr, entry->size, - dir2name[entry->direction]); + dir2name[entry->direction], + maperr2str[entry->map_err_type]); } } @@ -838,13 +852,28 @@ static __init int dma_debug_entries_cmdline(char *str) __setup("dma_debug=", dma_debug_cmdline); __setup("dma_debug_entries=", dma_debug_entries_cmdline); +/* Calling dma_mapping_error() from dma-debug api will result in calling + debug_dma_mapping_error() - need internal mapping error routine to + avoid debug checks */ +#ifndef DMA_ERROR_CODE +#define DMA_ERROR_CODE 0 +#endif +static inline int has_mapping_error(struct device *dev, dma_addr_t dma_addr) +{ + const struct dma_map_ops *ops = get_dma_ops(dev); + if (ops->mapping_error) + return ops->mapping_error(dev, dma_addr); + + return (dma_addr == DMA_ERROR_CODE); +} + static void check_unmap(struct dma_debug_entry *ref) { struct dma_debug_entry *entry; struct hash_bucket *bucket; unsigned long flags; - if (dma_mapping_error(ref->dev, ref->dev_addr)) { + if (unlikely(has_mapping_error(ref->dev, ref->dev_addr))) { err_printk(ref->dev, NULL, "DMA-API: device driver tries " "to free an invalid DMA memory address\n"); return; @@ -910,6 +939,15 @@ static void check_unmap(struct dma_debug_entry *ref) dir2name[ref->direction]); } + if (entry->map_err_type == MAP_ERR_NOT_CHECKED) { + err_printk(ref->dev, entry, + "DMA-API: device driver failed to check map error" + "[device address=0x%016llx] [size=%llu bytes] " + "[mapped as %s]", + ref->dev_addr, ref->size, + type2name[entry->type]); + } + hash_bucket_del(entry); dma_entry_free(entry); @@ -1017,7 +1055,7 @@ void debug_dma_map_page(struct device *dev, struct page *page, size_t offset, if (unlikely(global_disable)) return; - if (unlikely(dma_mapping_error(dev, dma_addr))) + if (unlikely(has_mapping_error(dev, dma_addr))) return; entry = dma_entry_alloc(); @@ -1030,6 +1068,7 @@ void debug_dma_map_page(struct device *dev, struct page *page, size_t offset, entry->dev_addr = dma_addr; entry->size = size; entry->direction = direction; + entry->map_err_type = MAP_ERR_NOT_CHECKED; if (map_single) entry->type = dma_debug_single; @@ -1045,6 +1084,30 @@ void debug_dma_map_page(struct device *dev, struct page *page, size_t offset, } EXPORT_SYMBOL(debug_dma_map_page); +void debug_dma_mapping_error(struct device *dev, dma_addr_t dma_addr) +{ + struct dma_debug_entry ref; + struct dma_debug_entry *entry; + struct hash_bucket *bucket; + unsigned long flags; + + if (unlikely(global_disable)) + return; + + ref.dev = dev; + ref.dev_addr = dma_addr; + bucket = get_hash_bucket(&ref, &flags); + entry = bucket_find_exact(bucket, &ref); + + if (!entry) + goto out; + + entry->map_err_type = MAP_ERR_CHECKED; +out: + put_hash_bucket(bucket, &flags); +} +EXPORT_SYMBOL(debug_dma_mapping_error); + void debug_dma_unmap_page(struct device *dev, dma_addr_t addr, size_t size, int direction, bool map_single) { -- cgit v1.2.3-59-g8ed1b From 8d7f62e6a7aa0bfd7163d3c4a5fb59280f6a6eb5 Mon Sep 17 00:00:00 2001 From: Shuah Khan Date: Thu, 18 Oct 2012 14:00:58 -0600 Subject: Documentation DMA-API-HOWTO.txt Add dma mapping error check usage examples Enhance the document to discuss the importance of dma mapping error checks after dma_map_single() and dma_map_page() calls. Also added usage examples that include unmap examples in error paths when dma mapping error is returned. Includes correct and incorrect usages to high light some common mistakes in error paths especially when dma mapping fails when more than one dma mapping call is made. Signed-off-by: Shuah Khan Reviewed-by: Konrad Rzeszutek Wilk Signed-off-by: Joerg Roedel --- Documentation/DMA-API-HOWTO.txt | 126 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 126 insertions(+) (limited to 'Documentation') diff --git a/Documentation/DMA-API-HOWTO.txt b/Documentation/DMA-API-HOWTO.txt index a0b6250add79..4a4fb295ceef 100644 --- a/Documentation/DMA-API-HOWTO.txt +++ b/Documentation/DMA-API-HOWTO.txt @@ -468,11 +468,46 @@ To map a single region, you do: size_t size = buffer->len; dma_handle = dma_map_single(dev, addr, size, direction); + if (dma_mapping_error(dma_handle)) { + /* + * reduce current DMA mapping usage, + * delay and try again later or + * reset driver. + */ + goto map_error_handling; + } and to unmap it: dma_unmap_single(dev, dma_handle, size, direction); +You should call dma_mapping_error() as dma_map_single() could fail and return +error. Not all dma implementations support dma_mapping_error() interface. +However, it is a good practice to call dma_mapping_error() interface, which +will invoke the generic mapping error check interface. Doing so will ensure +that the mapping code will work correctly on all dma implementations without +any dependency on the specifics of the underlying implementation. Using the +returned address without checking for errors could result in failures ranging +from panics to silent data corruption. Couple of example of incorrect ways to +check for errors that make assumptions about the underlying dma implementation +are as follows and these are applicable to dma_map_page() as well. + +Incorrect example 1: + dma_addr_t dma_handle; + + dma_handle = dma_map_single(dev, addr, size, direction); + if ((dma_handle & 0xffff != 0) || (dma_handle >= 0x1000000)) { + goto map_error; + } + +Incorrect example 2: + dma_addr_t dma_handle; + + dma_handle = dma_map_single(dev, addr, size, direction); + if (dma_handle == DMA_ERROR_CODE) { + goto map_error; + } + You should call dma_unmap_single when the DMA activity is finished, e.g. from the interrupt which told you that the DMA transfer is done. @@ -489,6 +524,14 @@ Specifically: size_t size = buffer->len; dma_handle = dma_map_page(dev, page, offset, size, direction); + if (dma_mapping_error(dma_handle)) { + /* + * reduce current DMA mapping usage, + * delay and try again later or + * reset driver. + */ + goto map_error_handling; + } ... @@ -496,6 +539,12 @@ Specifically: Here, "offset" means byte offset within the given page. +You should call dma_mapping_error() as dma_map_page() could fail and return +error as outlined under the dma_map_single() discussion. + +You should call dma_unmap_page when the DMA activity is finished, e.g. +from the interrupt which told you that the DMA transfer is done. + With scatterlists, you map a region gathered from several regions by: int i, count = dma_map_sg(dev, sglist, nents, direction); @@ -578,6 +627,14 @@ to use the dma_sync_*() interfaces. dma_addr_t mapping; mapping = dma_map_single(cp->dev, buffer, len, DMA_FROM_DEVICE); + if (dma_mapping_error(dma_handle)) { + /* + * reduce current DMA mapping usage, + * delay and try again later or + * reset driver. + */ + goto map_error_handling; + } cp->rx_buf = buffer; cp->rx_len = len; @@ -658,6 +715,75 @@ failure can be determined by: * delay and try again later or * reset driver. */ + goto map_error_handling; + } + +- unmap pages that are already mapped, when mapping error occurs in the middle + of a multiple page mapping attempt. These example are applicable to + dma_map_page() as well. + +Example 1: + dma_addr_t dma_handle1; + dma_addr_t dma_handle2; + + dma_handle1 = dma_map_single(dev, addr, size, direction); + if (dma_mapping_error(dev, dma_handle1)) { + /* + * reduce current DMA mapping usage, + * delay and try again later or + * reset driver. + */ + goto map_error_handling1; + } + dma_handle2 = dma_map_single(dev, addr, size, direction); + if (dma_mapping_error(dev, dma_handle2)) { + /* + * reduce current DMA mapping usage, + * delay and try again later or + * reset driver. + */ + goto map_error_handling2; + } + + ... + + map_error_handling2: + dma_unmap_single(dma_handle1); + map_error_handling1: + +Example 2: (if buffers are allocated a loop, unmap all mapped buffers when + mapping error is detected in the middle) + + dma_addr_t dma_addr; + dma_addr_t array[DMA_BUFFERS]; + int save_index = 0; + + for (i = 0; i < DMA_BUFFERS; i++) { + + ... + + dma_addr = dma_map_single(dev, addr, size, direction); + if (dma_mapping_error(dev, dma_addr)) { + /* + * reduce current DMA mapping usage, + * delay and try again later or + * reset driver. + */ + goto map_error_handling; + } + array[i].dma_addr = dma_addr; + save_index++; + } + + ... + + map_error_handling: + + for (i = 0; i < save_index; i++) { + + ... + + dma_unmap_single(array[i].dma_addr); } Networking drivers must call dev_kfree_skb to free the socket buffer -- cgit v1.2.3-59-g8ed1b From 11930c530f3edf81160e4962e363d579f5cdce7e Mon Sep 17 00:00:00 2001 From: Josh Wu Date: Fri, 14 Sep 2012 17:01:29 +0800 Subject: ARM: at91/tc: fix typo in the DT document Signed-off-by: Josh Wu Signed-off-by: Nicolas Ferre Acked-by: Jean-Christophe PLAGNIOL-VILLARD Cc: stable [v3.4] --- Documentation/devicetree/bindings/arm/atmel-at91.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/arm/atmel-at91.txt b/Documentation/devicetree/bindings/arm/atmel-at91.txt index ecc81e368715..d187e9f7cf1c 100644 --- a/Documentation/devicetree/bindings/arm/atmel-at91.txt +++ b/Documentation/devicetree/bindings/arm/atmel-at91.txt @@ -8,7 +8,7 @@ PIT Timer required properties: shared across all System Controller members. TC/TCLIB Timer required properties: -- compatible: Should be "atmel,-pit". +- compatible: Should be "atmel,-tcb". can be "at91rm9200" or "at91sam9x5" - reg: Should contain registers location and length - interrupts: Should contain all interrupts for the TC block -- cgit v1.2.3-59-g8ed1b From b533a83008c3fb4983c1213276790cacd39b518f Mon Sep 17 00:00:00 2001 From: Damian Hobson-Garcia Date: Tue, 25 Sep 2012 15:09:12 +0900 Subject: Add uio_dmem_genirq description to UIO documentation Signed-off-by: Damian Hobson-Garcia Signed-off-by: "Hans J. Koch" Signed-off-by: Greg Kroah-Hartman --- Documentation/DocBook/uio-howto.tmpl | 56 ++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) (limited to 'Documentation') diff --git a/Documentation/DocBook/uio-howto.tmpl b/Documentation/DocBook/uio-howto.tmpl index ac3d0018140c..fdbf86fcfccd 100644 --- a/Documentation/DocBook/uio-howto.tmpl +++ b/Documentation/DocBook/uio-howto.tmpl @@ -719,6 +719,62 @@ framework to set up sysfs files for this region. Simply leave it alone. + +Using uio_dmem_genirq for platform devices + + In addition to statically allocated memory ranges, they may also be + a desire to use dynamically allocated regions in a user space driver. + In particular, being able to access memory made available through the + dma-mapping API, may be particularly useful. The + uio_dmem_genirq driver provides a way to accomplish + this. + + + This driver is used in a similar manner to the + "uio_pdrv_genirq" driver with respect to interrupt + configuration and handling. + + + Set the .name element of + struct platform_device to + "uio_dmem_genirq" to use this driver. + + + When using this driver, fill in the .platform_data + element of struct platform_device, which is of type + struct uio_dmem_genirq_pdata and which contains the + following elements: + + + struct uio_info uioinfo: The same + structure used as the uio_pdrv_genirq platform + data + unsigned int *dynamic_region_sizes: + Pointer to list of sizes of dynamic memory regions to be mapped into + user space. + + unsigned int num_dynamic_regions: + Number of elements in dynamic_region_sizes array. + + + + The dynamic regions defined in the platform data will be appended to + the mem[] array after the platform device + resources, which implies that the total number of static and dynamic + memory regions cannot exceed MAX_UIO_MAPS. + + + The dynamic memory regions will be allocated when the UIO device file, + /dev/uioX is opened. + Simiar to static memory resources, the memory region information for + dynamic regions is then visible via sysfs at + /sys/class/uio/uioX/maps/mapY/*. + The dynmaic memory regions will be freed when the UIO device file is + closed. When no processes are holding the device file open, the address + returned to userspace is DMA_ERROR_CODE. + + + -- cgit v1.2.3-59-g8ed1b From ae495e844a77344fdaedbb2ad97d925d096e9f0d Mon Sep 17 00:00:00 2001 From: Hui Wang Date: Thu, 25 Oct 2012 00:38:01 -0700 Subject: Input: egalax_ts - get gpio from devicetree The irq_to_gpio() is old, most platforms use GENERIC_GPIO framework and don't support this API anymore. The i.MX6q sabrelite platform equips an egalax touchscreen controller, and this platform already transfered to GENERIC_GPIO framework, to support this driver, we use a more generic way to get gpio. Add a return value checking for waking up the controller in the probe function, this guarantee only a workable device can pass init. [dmitry.torokhov@gmail.com: Make driver depend on CONFIG_OF as it is now required.] Acked-by Zhang Jiejing Reviewed-by: Shawn Guo Signed-off-by: Hui Wang Signed-off-by: Dmitry Torokhov --- .../bindings/input/touchscreen/egalax-ts.txt | 19 ++++++++++++++++++ drivers/input/touchscreen/Kconfig | 2 +- drivers/input/touchscreen/egalax_ts.c | 23 ++++++++++++++++++++-- 3 files changed, 41 insertions(+), 3 deletions(-) create mode 100644 Documentation/devicetree/bindings/input/touchscreen/egalax-ts.txt (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/input/touchscreen/egalax-ts.txt b/Documentation/devicetree/bindings/input/touchscreen/egalax-ts.txt new file mode 100644 index 000000000000..df70318a617f --- /dev/null +++ b/Documentation/devicetree/bindings/input/touchscreen/egalax-ts.txt @@ -0,0 +1,19 @@ +* EETI eGalax Multiple Touch Controller + +Required properties: +- compatible: must be "eeti,egalax_ts" +- reg: i2c slave address +- interrupt-parent: the phandle for the interrupt controller +- interrupts: touch controller interrupt +- wakeup-gpios: the gpio pin to be used for waking up the controller + as well as uased as irq pin + +Example: + + egalax_ts@04 { + compatible = "eeti,egalax_ts"; + reg = <0x04>; + interrupt-parent = <&gpio1>; + interrupts = <9 2>; + wakeup-gpios = <&gpio1 9 0>; + }; diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index 1ba232cbc09d..f7668b24c378 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -239,7 +239,7 @@ config TOUCHSCREEN_EETI config TOUCHSCREEN_EGALAX tristate "EETI eGalax multi-touch panel support" - depends on I2C + depends on I2C && OF help Say Y here to enable support for I2C connected EETI eGalax multi-touch panels. diff --git a/drivers/input/touchscreen/egalax_ts.c b/drivers/input/touchscreen/egalax_ts.c index c1e3460f1195..13fa62fdfb0b 100644 --- a/drivers/input/touchscreen/egalax_ts.c +++ b/drivers/input/touchscreen/egalax_ts.c @@ -28,6 +28,7 @@ #include #include #include +#include /* * Mouse Mode: some panel may configure the controller to mouse mode, @@ -122,9 +123,17 @@ static irqreturn_t egalax_ts_interrupt(int irq, void *dev_id) /* wake up controller by an falling edge of interrupt gpio. */ static int egalax_wake_up_device(struct i2c_client *client) { - int gpio = irq_to_gpio(client->irq); + struct device_node *np = client->dev.of_node; + int gpio; int ret; + if (!np) + return -ENODEV; + + gpio = of_get_named_gpio(np, "wakeup-gpios", 0); + if (!gpio_is_valid(gpio)) + return -ENODEV; + ret = gpio_request(gpio, "egalax_irq"); if (ret < 0) { dev_err(&client->dev, @@ -181,7 +190,11 @@ static int __devinit egalax_ts_probe(struct i2c_client *client, ts->input_dev = input_dev; /* controller may be in sleep, wake it up. */ - egalax_wake_up_device(client); + error = egalax_wake_up_device(client); + if (error) { + dev_err(&client->dev, "Failed to wake up the controller\n"); + goto err_free_dev; + } ret = egalax_firmware_version(client); if (ret < 0) { @@ -274,11 +287,17 @@ static int egalax_ts_resume(struct device *dev) static SIMPLE_DEV_PM_OPS(egalax_ts_pm_ops, egalax_ts_suspend, egalax_ts_resume); +static struct of_device_id egalax_ts_dt_ids[] = { + { .compatible = "eeti,egalax_ts" }, + { /* sentinel */ } +}; + static struct i2c_driver egalax_ts_driver = { .driver = { .name = "egalax_ts", .owner = THIS_MODULE, .pm = &egalax_ts_pm_ops, + .of_match_table = of_match_ptr(egalax_ts_dt_ids), }, .id_table = egalax_ts_id, .probe = egalax_ts_probe, -- cgit v1.2.3-59-g8ed1b From ab72fa5523866cb93681abb9f9a401d43a93b7be Mon Sep 17 00:00:00 2001 From: Murali Karicheri Date: Mon, 22 Oct 2012 11:58:02 -0400 Subject: Documentation: of-serial.txt - update for clocks phandle for clk Signed-off-by: Murali Karicheri Signed-off-by: Greg Kroah-Hartman --- Documentation/devicetree/bindings/tty/serial/of-serial.txt | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/tty/serial/of-serial.txt b/Documentation/devicetree/bindings/tty/serial/of-serial.txt index ba385f2e0ddc..1e1145ca4f3c 100644 --- a/Documentation/devicetree/bindings/tty/serial/of-serial.txt +++ b/Documentation/devicetree/bindings/tty/serial/of-serial.txt @@ -14,7 +14,10 @@ Required properties: - "serial" if the port type is unknown. - reg : offset and length of the register set for the device. - interrupts : should contain uart interrupt. -- clock-frequency : the input clock frequency for the UART. +- clock-frequency : the input clock frequency for the UART + or + clocks phandle to refer to the clk used as per Documentation/devicetree + /bindings/clock/clock-bindings.txt Optional properties: - current-speed : the current active speed of the UART. -- cgit v1.2.3-59-g8ed1b From 10bd4c7572b6c6eb97f1c15dedd2d36c63907f91 Mon Sep 17 00:00:00 2001 From: Ming Lei Date: Wed, 24 Oct 2012 10:49:33 +0800 Subject: firmware loader: document kernel direct loading This patch adds description on recently introduced direct firmware loading by Linus. Cc: Linus Torvalds Signed-off-by: Ming Lei Signed-off-by: Greg Kroah-Hartman --- Documentation/firmware_class/README | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) (limited to 'Documentation') diff --git a/Documentation/firmware_class/README b/Documentation/firmware_class/README index 7eceaff63f5f..815b711bcd85 100644 --- a/Documentation/firmware_class/README +++ b/Documentation/firmware_class/README @@ -18,32 +18,40 @@ High level behavior (mixed): ============================ - kernel(driver): calls request_firmware(&fw_entry, $FIRMWARE, device) - - userspace: + 1), kernel(driver): + - calls request_firmware(&fw_entry, $FIRMWARE, device) + - kernel searchs the fimware image with name $FIRMWARE directly + in the below search path of root filesystem: + "/lib/firmware/updates/" UTS_RELEASE, + "/lib/firmware/updates", + "/lib/firmware/" UTS_RELEASE, + "/lib/firmware" + - If found, goto 7), else goto 2) + + 2), userspace: - /sys/class/firmware/xxx/{loading,data} appear. - hotplug gets called with a firmware identifier in $FIRMWARE and the usual hotplug environment. - hotplug: echo 1 > /sys/class/firmware/xxx/loading - kernel: Discard any previous partial load. + 3), kernel: Discard any previous partial load. - userspace: + 4), userspace: - hotplug: cat appropriate_firmware_image > \ /sys/class/firmware/xxx/data - kernel: grows a buffer in PAGE_SIZE increments to hold the image as it + 5), kernel: grows a buffer in PAGE_SIZE increments to hold the image as it comes in. - userspace: + 6), userspace: - hotplug: echo 0 > /sys/class/firmware/xxx/loading - kernel: request_firmware() returns and the driver has the firmware + 7), kernel: request_firmware() returns and the driver has the firmware image in fw_entry->{data,size}. If something went wrong request_firmware() returns non-zero and fw_entry is set to NULL. - kernel(driver): Driver code calls release_firmware(fw_entry) releasing + 8), kernel(driver): Driver code calls release_firmware(fw_entry) releasing the firmware image and any related resource. High level behavior (driver code): -- cgit v1.2.3-59-g8ed1b From b5416d32a6f1a4cd4e399654b4bac7f558ab237e Mon Sep 17 00:00:00 2001 From: Tekkaman Ninja Date: Thu, 27 Sep 2012 23:36:33 +0800 Subject: Documentation:Chinese translation of Documentation/IRQ.txt This is a Chinese translated version of Documentation/IRQ.txt Signed-off-by: Fu Wei Acked-by: Harry Wei Signed-off-by: Greg Kroah-Hartman --- Documentation/zh_CN/IRQ.txt | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 Documentation/zh_CN/IRQ.txt (limited to 'Documentation') diff --git a/Documentation/zh_CN/IRQ.txt b/Documentation/zh_CN/IRQ.txt new file mode 100644 index 000000000000..956026d5cf82 --- /dev/null +++ b/Documentation/zh_CN/IRQ.txt @@ -0,0 +1,39 @@ +Chinese translated version of Documentation/IRQ.txt + +If you have any comment or update to the content, please contact the +original document maintainer directly. However, if you have a problem +communicating in English you can also ask the Chinese maintainer for +help. Contact the Chinese maintainer if this translation is outdated +or if there is a problem with the translation. + +Maintainer: Eric W. Biederman +Chinese maintainer: Fu Wei +--------------------------------------------------------------------- +Documentation/IRQ.txt 的中文翻译 + +如果想评论或更新本文的内容,请直接联系原文档的维护者。如果你使用英文 +交流有困难的话,也可以向中文版维护者求助。如果本翻译更新不及时或者翻 +译存在问题,请联系中文版维护者。 +英文版维护者: Eric W. Biederman +中文版维护者: 傅炜 Fu Wei +中文版翻译者: 傅炜 Fu Wei +中文版校译者: 傅炜 Fu Wei + + +以下为正文 +--------------------------------------------------------------------- +何为 IRQ? + +一个 IRQ 是来自某个设备的一个中断请求。目前,它们可以来自一个硬件引脚, +或来自一个数据包。多个设备可能连接到同个硬件引脚,从而共享一个 IRQ。 + +一个 IRQ 编号是用于告知硬件中断源的内核标识。通常情况下,这是一个 +全局 irq_desc 数组的索引,但是除了在 linux/interrupt.h 中的实现, +具体的细节是体系结构特定的。 + +一个 IRQ 编号是设备上某个可能的中断源的枚举。通常情况下,枚举的编号是 +该引脚在系统内中断控制器的所有输入引脚中的编号。对于 ISA 总线中的情况, +枚举的是在两个 i8259 中断控制器中 16 个输入引脚。 + +架构可以对 IRQ 编号指定额外的含义,在硬件涉及任何手工配置的情况下, +是被提倡的。ISA 的 IRQ 是一个分配这类额外含义的典型例子。 -- cgit v1.2.3-59-g8ed1b From 4bdac7b668d29dcaa53b72921b7f6a51d0f80b9b Mon Sep 17 00:00:00 2001 From: Tekkaman Ninja Date: Wed, 24 Oct 2012 23:56:47 +0800 Subject: Documentation:Chinese translation of Documentation/arm64/booting.txt This is a Chinese translated version of Documentation/arm64/booting.txt Signed-off-by: Fu Wei Acked-by: Catalin Marinas Signed-off-by: Greg Kroah-Hartman --- Documentation/zh_CN/arm64/booting.txt | 156 ++++++++++++++++++++++++++++++++++ 1 file changed, 156 insertions(+) create mode 100644 Documentation/zh_CN/arm64/booting.txt (limited to 'Documentation') diff --git a/Documentation/zh_CN/arm64/booting.txt b/Documentation/zh_CN/arm64/booting.txt new file mode 100644 index 000000000000..28fa325b7461 --- /dev/null +++ b/Documentation/zh_CN/arm64/booting.txt @@ -0,0 +1,156 @@ +Chinese translated version of Documentation/arm64/booting.txt + +If you have any comment or update to the content, please contact the +original document maintainer directly. However, if you have a problem +communicating in English you can also ask the Chinese maintainer for +help. Contact the Chinese maintainer if this translation is outdated +or if there is a problem with the translation. + +Maintainer: Will Deacon +Chinese maintainer: Fu Wei +--------------------------------------------------------------------- +Documentation/arm64/booting.txt 的中文翻译 + +如果想评论或更新本文的内容,请直接联系原文档的维护者。如果你使用英文 +交流有困难的话,也可以向中文版维护者求助。如果本翻译更新不及时或者翻 +译存在问题,请联系中文版维护者。 + +英文版维护者: Will Deacon +中文版维护者: 傅炜 Fu Wei +中文版翻译者: 傅炜 Fu Wei +中文版校译者: 傅炜 Fu Wei + +以下为正文 +--------------------------------------------------------------------- + 启动 AArch64 Linux + ================== + +作者: Will Deacon +日期: 2012 年 09 月 07 日 + +本文档基于 Russell King 的 ARM 启动文档,且适用于所有公开发布的 +AArch64 Linux 内核代码。 + +AArch64 异常模型由多个异常级别(EL0 - EL3)组成,对于 EL0 和 EL1 +异常级有对应的安全和非安全模式。EL2 是系统管理级,且仅存在于 +非安全模式下。EL3 是最高特权级,且仅存在于安全模式下。 + +基于本文档的目的,我们将简单地使用‘引导装载程序’(‘boot loader’) +这个术语来定义在将控制权交给 Linux 内核前 CPU 上执行的所有软件。 +这可能包含安全监控和系统管理代码,或者它可能只是一些用于准备最小启动 +环境的指令。 + +基本上,引导装载程序(至少)应实现以下操作: + +1、设置和初始化 RAM +2、设置设备树数据 +3、解压内核映像 +4、调用内核映像 + + +1、设置和初始化 RAM +----------------- + +必要性: 强制 + +引导装载程序应该找到并初始化系统中所有内核用于保持系统变量数据的 RAM。 +这个操作的执行是设备依赖的。(它可能使用内部算法来自动定位和计算所有 +RAM,或可能使用对这个设备已知的 RAM 信息,还可能使用任何引导装载程序 +设计者想到的匹配方法。) + + +2、设置设备树数据 +--------------- + +必要性: 强制 + +设备树数据块(dtb)大小必须不大于 2 MB,且位于从内核映像起始算起第一个 +512MB 内的 2MB 边界上。这使得内核可以通过初始页表中的单个节描述符来 +映射此数据块。 + + +3、解压内核映像 +------------- + +必要性: 可选 + +AArch64 内核当前没有提供自解压代码,因此如果使用了压缩内核映像文件 +(比如 Image.gz),则需要通过引导装载程序(使用 gzip 等)来进行解压。 +若引导装载程序没有实现这个需求,就要使用非压缩内核映像文件。 + + +4、调用内核映像 +------------- + +必要性: 强制 + +已解压的内核映像包含一个 32 字节的头,内容如下: + + u32 magic = 0x14000008; /* 跳转到 stext, 小端 */ + u32 res0 = 0; /* 保留 */ + u64 text_offset; /* 映像装载偏移 */ + u64 res1 = 0; /* 保留 */ + u64 res2 = 0; /* 保留 */ + +映像必须位于系统 RAM 起始处的特定偏移(当前是 0x80000)。系统 RAM +的起始地址必须是以 2MB 对齐的。 + +在跳转入内核前,必须符合以下状态: + +- 停止所有 DMA 设备,这样内存数据就不会因为虚假网络包或磁盘数据而 + 被破坏。这可能可以节省你许多的调试时间。 + +- 主 CPU 通用寄存器设置 + x0 = 系统 RAM 中设备树数据块(dtb)的物理地址。 + x1 = 0 (保留,将来可能使用) + x2 = 0 (保留,将来可能使用) + x3 = 0 (保留,将来可能使用) + +- CPU 模式 + 所有形式的中断必须在 PSTATE.DAIF 中被屏蔽(Debug、SError、IRQ + 和 FIQ)。 + CPU 必须处于 EL2(推荐,可访问虚拟化扩展)或非安全 EL1 模式下。 + +- 高速缓存、MMU + MMU 必须关闭。 + 指令缓存开启或关闭都可以。 + 数据缓存必须关闭且无效。 + 外部高速缓存(如果存在)必须配置并禁用。 + +- 架构计时器 + CNTFRQ 必须设定为计时器的频率。 + 如果在 EL1 模式下进入内核,则 CNTHCTL_EL2 中的 EL1PCTEN (bit 0) + 必须置位。 + +- 一致性 + 通过内核启动的所有 CPU 在内核入口地址上必须处于相同的一致性域中。 + 这可能要根据具体实现来定义初始化过程,以使能每个CPU上对维护操作的 + 接收。 + +- 系统寄存器 + 在进入内核映像的异常级中,所有构架中可写的系统寄存器必须通过软件 + 在一个更高的异常级别下初始化,以防止在 未知 状态下运行。 + +引导装载程序必须在每个 CPU 处于以下状态时跳入内核入口: + +- 主 CPU 必须直接跳入内核映像的第一条指令。通过此 CPU 传递的设备树 + 数据块必须在每个 CPU 节点中包含以下内容: + + 1、‘enable-method’属性。目前,此字段支持的值仅为字符串“spin-table”。 + + 2、‘cpu-release-addr’标识一个 64-bit、初始化为零的内存位置。 + + 引导装载程序必须生成这些设备树属性,并在跳入内核入口之前将其插入 + 数据块。 + +- 任何辅助 CPU 必须在内存保留区(通过设备树中的 /memreserve/ 域传递 + 给内核)中自旋于内核之外,轮询它们的 cpu-release-addr 位置(必须 + 包含在保留区中)。可通过插入 wfe 指令来降低忙循环开销,而主 CPU 将 + 发出 sev 指令。当对 cpu-release-addr 所指位置的读取操作返回非零值 + 时,CPU 必须直接跳入此值所指向的地址。 + +- 辅助 CPU 通用寄存器设置 + x0 = 0 (保留,将来可能使用) + x1 = 0 (保留,将来可能使用) + x2 = 0 (保留,将来可能使用) + x3 = 0 (保留,将来可能使用) -- cgit v1.2.3-59-g8ed1b From bf34be058158fd30622601346819d098dc5d623f Mon Sep 17 00:00:00 2001 From: Tekkaman Ninja Date: Thu, 25 Oct 2012 00:02:53 +0800 Subject: Documentation:Chinese translation of Documentation/arm64/memory.txt This is a Chinese translated version of Documentation/arm64/memory.txt Signed-off-by: Fu Wei Acked-by: Catalin Marinas Signed-off-by: Greg Kroah-Hartman --- Documentation/zh_CN/arm64/memory.txt | 93 ++++++++++++++++++++++++++++++++++++ 1 file changed, 93 insertions(+) create mode 100644 Documentation/zh_CN/arm64/memory.txt (limited to 'Documentation') diff --git a/Documentation/zh_CN/arm64/memory.txt b/Documentation/zh_CN/arm64/memory.txt new file mode 100644 index 000000000000..83b519314706 --- /dev/null +++ b/Documentation/zh_CN/arm64/memory.txt @@ -0,0 +1,93 @@ +Chinese translated version of Documentation/arm64/memory.txt + +If you have any comment or update to the content, please contact the +original document maintainer directly. However, if you have a problem +communicating in English you can also ask the Chinese maintainer for +help. Contact the Chinese maintainer if this translation is outdated +or if there is a problem with the translation. + +Maintainer: Catalin Marinas +Chinese maintainer: Fu Wei +--------------------------------------------------------------------- +Documentation/arm64/memory.txt 的中文翻译 + +如果想评论或更新本文的内容,请直接联系原文档的维护者。如果你使用英文 +交流有困难的话,也可以向中文版维护者求助。如果本翻译更新不及时或者翻 +译存在问题,请联系中文版维护者。 + +英文版维护者: Catalin Marinas +中文版维护者: 傅炜 Fu Wei +中文版翻译者: 傅炜 Fu Wei +中文版校译者: 傅炜 Fu Wei + +以下为正文 +--------------------------------------------------------------------- + Linux 在 AArch64 中的内存布局 + =========================== + +作者: Catalin Marinas +日期: 2012 年 02 月 20 日 + +本文档描述 AArch64 Linux 内核所使用的虚拟内存布局。此构架可以实现 +页大小为 4KB 的 4 级转换表和页大小为 64KB 的 3 级转换表。 + +AArch64 Linux 使用页大小为 4KB 的 3 级转换表配置,对于用户和内核 +都有 39-bit (512GB) 的虚拟地址空间。对于页大小为 64KB的配置,仅 +使用 2 级转换表,但内存布局相同。 + +用户地址空间的 63:39 位为 0,而内核地址空间的相应位为 1。TTBRx 的 +选择由虚拟地址的 63 位给出。swapper_pg_dir 仅包含内核(全局)映射, +而用户 pgd 仅包含用户(非全局)映射。swapper_pgd_dir 地址被写入 +TTBR1 中,且从不写入 TTBR0。 + + +AArch64 Linux 内存布局: + +起始地址 结束地址 大小 用途 +----------------------------------------------------------------------- +0000000000000000 0000007fffffffff 512GB 用户空间 + +ffffff8000000000 ffffffbbfffcffff ~240GB vmalloc + +ffffffbbfffd0000 ffffffbcfffdffff 64KB [防护页] + +ffffffbbfffe0000 ffffffbcfffeffff 64KB PCI I/O 空间 + +ffffffbbffff0000 ffffffbcffffffff 64KB [防护页] + +ffffffbc00000000 ffffffbdffffffff 8GB vmemmap + +ffffffbe00000000 ffffffbffbffffff ~8GB [防护页,未来用于 vmmemap] + +ffffffbffc000000 ffffffbfffffffff 64MB 模块 + +ffffffc000000000 ffffffffffffffff 256GB 内存空间 + + +4KB 页大小的转换表查找: + ++--------+--------+--------+--------+--------+--------+--------+--------+ +|63 56|55 48|47 40|39 32|31 24|23 16|15 8|7 0| ++--------+--------+--------+--------+--------+--------+--------+--------+ + | | | | | | + | | | | | v + | | | | | [11:0] 页内偏移 + | | | | +-> [20:12] L3 索引 + | | | +-----------> [29:21] L2 索引 + | | +---------------------> [38:30] L1 索引 + | +-------------------------------> [47:39] L0 索引 (未使用) + +-------------------------------------------------> [63] TTBR0/1 + + +64KB 页大小的转换表查找: + ++--------+--------+--------+--------+--------+--------+--------+--------+ +|63 56|55 48|47 40|39 32|31 24|23 16|15 8|7 0| ++--------+--------+--------+--------+--------+--------+--------+--------+ + | | | | | + | | | | v + | | | | [15:0] 页内偏移 + | | | +----------> [28:16] L3 索引 + | | +--------------------------> [41:29] L2 索引 (仅使用 38:29 ) + | +-------------------------------> [47:42] L1 索引 (未使用) + +-------------------------------------------------> [63] TTBR0/1 -- cgit v1.2.3-59-g8ed1b From d0f1c7ffaa32bdda2d413d2db41c51bbdd105834 Mon Sep 17 00:00:00 2001 From: Stephen Warren Date: Sat, 15 Sep 2012 22:18:10 -0600 Subject: ARM: bcm2835: implement machine restart hook Implement the machine restart hook using the SoC's watchdog timer module. To support this, define a DT binding for the watchdog module, and add it to the device tree. The downstream rpi-split branch contains a full watchdog timer driver implementation, which also implements the restart hook. However, the restart function is largely separate from the watchdog driver, so for simplicity, the restart hook is implemented here directly in the main machine source file. Overall structure (separate setup/restart) functions derived from the picoxcell ARM support. Watchdog register IO sequence taken from code by Simon Arlott. Note that the watchdog module is not documented in BCM2835-ARM-Peripherals.pdf. Signed-off-by: Stephen Warren --- .../bindings/watchdog/brcm,bcm2835-pm-wdog.txt | 13 ++++++ arch/arm/boot/dts/bcm2835.dtsi | 5 +++ arch/arm/mach-bcm2835/bcm2835.c | 46 ++++++++++++++++++++++ 3 files changed, 64 insertions(+) create mode 100644 Documentation/devicetree/bindings/watchdog/brcm,bcm2835-pm-wdog.txt (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/watchdog/brcm,bcm2835-pm-wdog.txt b/Documentation/devicetree/bindings/watchdog/brcm,bcm2835-pm-wdog.txt new file mode 100644 index 000000000000..d209366b4a69 --- /dev/null +++ b/Documentation/devicetree/bindings/watchdog/brcm,bcm2835-pm-wdog.txt @@ -0,0 +1,13 @@ +BCM2835 Watchdog timer + +Required properties: + +- compatible : should be "brcm,bcm2835-pm-wdt" +- reg : Specifies base physical address and size of the registers. + +Example: + +watchdog { + compatible = "brcm,bcm2835-pm-wdt"; + reg = <0x7e100000 0x28>; +}; diff --git a/arch/arm/boot/dts/bcm2835.dtsi b/arch/arm/boot/dts/bcm2835.dtsi index 0b619398532c..5c5cbafed191 100644 --- a/arch/arm/boot/dts/bcm2835.dtsi +++ b/arch/arm/boot/dts/bcm2835.dtsi @@ -29,6 +29,11 @@ #interrupt-cells = <2>; }; + watchdog { + compatible = "brcm,bcm2835-pm-wdt"; + reg = <0x7e100000 0x28>; + }; + uart@20201000 { compatible = "brcm,bcm2835-pl011", "arm,pl011", "arm,primecell"; reg = <0x7e201000 0x1000>; diff --git a/arch/arm/mach-bcm2835/bcm2835.c b/arch/arm/mach-bcm2835/bcm2835.c index f6fea4933571..c4dd210f4db1 100644 --- a/arch/arm/mach-bcm2835/bcm2835.c +++ b/arch/arm/mach-bcm2835/bcm2835.c @@ -12,8 +12,10 @@ * GNU General Public License for more details. */ +#include #include #include +#include #include #include #include @@ -23,6 +25,48 @@ #include +#define PM_RSTC 0x1c +#define PM_WDOG 0x24 + +#define PM_PASSWORD 0x5a000000 +#define PM_RSTC_WRCFG_MASK 0x00000030 +#define PM_RSTC_WRCFG_FULL_RESET 0x00000020 + +static void __iomem *wdt_regs; + +/* + * The machine restart method can be called from an atomic context so we won't + * be able to ioremap the regs then. + */ +static void bcm2835_setup_restart(void) +{ + struct device_node *np = of_find_compatible_node(NULL, NULL, + "brcm,bcm2835-pm-wdt"); + if (WARN(!np, "unable to setup watchdog restart")) + return; + + wdt_regs = of_iomap(np, 0); + WARN(!wdt_regs, "failed to remap watchdog regs"); +} + +static void bcm2835_restart(char mode, const char *cmd) +{ + u32 val; + + if (!wdt_regs) + return; + + /* use a timeout of 10 ticks (~150us) */ + writel_relaxed(10 | PM_PASSWORD, wdt_regs + PM_WDOG); + val = readl_relaxed(wdt_regs + PM_RSTC); + val &= ~PM_RSTC_WRCFG_MASK; + val |= PM_PASSWORD | PM_RSTC_WRCFG_FULL_RESET; + writel_relaxed(val, wdt_regs + PM_RSTC); + + /* No sleeping, possibly atomic. */ + mdelay(1); +} + static struct map_desc io_map __initdata = { .virtual = BCM2835_PERIPH_VIRT, .pfn = __phys_to_pfn(BCM2835_PERIPH_PHYS), @@ -39,6 +83,7 @@ void __init bcm2835_init(void) { int ret; + bcm2835_setup_restart(); bcm2835_init_clocks(); ret = of_platform_populate(NULL, of_default_bus_match_table, NULL, @@ -60,5 +105,6 @@ DT_MACHINE_START(BCM2835, "BCM2835") .handle_irq = bcm2835_handle_irq, .init_machine = bcm2835_init, .timer = &bcm2835_timer, + .restart = bcm2835_restart, .dt_compat = bcm2835_compat MACHINE_END -- cgit v1.2.3-59-g8ed1b From 2066306099d35430d01ed1b3d81c0536d77bf390 Mon Sep 17 00:00:00 2001 From: Josh Cartwright Date: Tue, 23 Oct 2012 19:53:06 -0500 Subject: arm: l2cc: doc: fix device tree example typo The list of attributes above details the use of the 'filter-ranges' property, but the example improperly used 'filter-latency'. Make these consistent by fixing up the example. Signed-off-by: Josh Cartwright Signed-off-by: Rob Herring --- Documentation/devicetree/bindings/arm/l2cc.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/arm/l2cc.txt b/Documentation/devicetree/bindings/arm/l2cc.txt index 7ca52161e7ab..7c3ee3aeb7b7 100644 --- a/Documentation/devicetree/bindings/arm/l2cc.txt +++ b/Documentation/devicetree/bindings/arm/l2cc.txt @@ -37,7 +37,7 @@ L2: cache-controller { reg = <0xfff12000 0x1000>; arm,data-latency = <1 1 1>; arm,tag-latency = <2 2 2>; - arm,filter-latency = <0x80000000 0x8000000>; + arm,filter-ranges = <0x80000000 0x8000000>; cache-unified; cache-level = <2>; interrupts = <45>; -- cgit v1.2.3-59-g8ed1b From 3c68198e75111a905ac2412be12bf7b29099729b Mon Sep 17 00:00:00 2001 From: Neil Horman Date: Wed, 24 Oct 2012 09:20:03 +0000 Subject: sctp: Make hmac algorithm selection for cookie generation dynamic Currently sctp allows for the optional use of md5 of sha1 hmac algorithms to generate cookie values when establishing new connections via two build time config options. Theres no real reason to make this a static selection. We can add a sysctl that allows for the dynamic selection of these algorithms at run time, with the default value determined by the corresponding crypto library availability. This comes in handy when, for example running a system in FIPS mode, where use of md5 is disallowed, but SHA1 is permitted. Note: This new sysctl has no corresponding socket option to select the cookie hmac algorithm. I chose not to implement that intentionally, as RFC 6458 contains no option for this value, and I opted not to pollute the socket option namespace. Change notes: v2) * Updated subject to have the proper sctp prefix as per Dave M. * Replaced deafult selection options with new options that allow developers to explicitly select available hmac algs at build time as per suggestion by Vlad Y. Signed-off-by: Neil Horman CC: Vlad Yasevich CC: "David S. Miller" CC: netdev@vger.kernel.org Acked-by: Vlad Yasevich Signed-off-by: David S. Miller --- Documentation/networking/ip-sysctl.txt | 14 ++++++++ include/net/netns/sctp.h | 3 ++ include/net/sctp/constants.h | 8 ----- include/net/sctp/structs.h | 1 + net/sctp/Kconfig | 39 ++++++++-------------- net/sctp/protocol.c | 9 ++++++ net/sctp/socket.c | 11 ++++--- net/sctp/sysctl.c | 59 ++++++++++++++++++++++++++++++++++ 8 files changed, 106 insertions(+), 38 deletions(-) (limited to 'Documentation') diff --git a/Documentation/networking/ip-sysctl.txt b/Documentation/networking/ip-sysctl.txt index c7fc10724948..98ac0d7552a1 100644 --- a/Documentation/networking/ip-sysctl.txt +++ b/Documentation/networking/ip-sysctl.txt @@ -1514,6 +1514,20 @@ cookie_preserve_enable - BOOLEAN Default: 1 +cookie_hmac_alg - STRING + Select the hmac algorithm used when generating the cookie value sent by + a listening sctp socket to a connecting client in the INIT-ACK chunk. + Valid values are: + * md5 + * sha1 + * none + Ability to assign md5 or sha1 as the selected alg is predicated on the + configuarion of those algorithms at build time (CONFIG_CRYPTO_MD5 and + CONFIG_CRYPTO_SHA1). + + Default: Dependent on configuration. MD5 if available, else SHA1 if + available, else none. + rcvbuf_policy - INTEGER Determines if the receive buffer is attributed to the socket or to association. SCTP supports the capability to create multiple diff --git a/include/net/netns/sctp.h b/include/net/netns/sctp.h index 5e5eb1f9f14b..3573a81815ad 100644 --- a/include/net/netns/sctp.h +++ b/include/net/netns/sctp.h @@ -62,6 +62,9 @@ struct netns_sctp { /* Whether Cookie Preservative is enabled(1) or not(0) */ int cookie_preserve_enable; + /* The namespace default hmac alg */ + char *sctp_hmac_alg; + /* Valid.Cookie.Life - 60 seconds */ unsigned int valid_cookie_life; diff --git a/include/net/sctp/constants.h b/include/net/sctp/constants.h index d053d2e99876..c29707d654c0 100644 --- a/include/net/sctp/constants.h +++ b/include/net/sctp/constants.h @@ -312,14 +312,6 @@ enum { SCTP_MAX_GABS = 16 }; * functions simpler to write. */ -#if defined (CONFIG_SCTP_HMAC_MD5) -#define SCTP_COOKIE_HMAC_ALG "hmac(md5)" -#elif defined (CONFIG_SCTP_HMAC_SHA1) -#define SCTP_COOKIE_HMAC_ALG "hmac(sha1)" -#else -#define SCTP_COOKIE_HMAC_ALG NULL -#endif - /* These return values describe the success or failure of a number of * routines which form the lower interface to SCTP_outqueue. */ diff --git a/include/net/sctp/structs.h b/include/net/sctp/structs.h index 64158aa1bb5f..2b2f61dd4036 100644 --- a/include/net/sctp/structs.h +++ b/include/net/sctp/structs.h @@ -177,6 +177,7 @@ struct sctp_sock { /* Access to HMAC transform. */ struct crypto_hash *hmac; + char *sctp_hmac_alg; /* What is our base endpointer? */ struct sctp_endpoint *ep; diff --git a/net/sctp/Kconfig b/net/sctp/Kconfig index 126b014eb79b..a9edd2e205f4 100644 --- a/net/sctp/Kconfig +++ b/net/sctp/Kconfig @@ -9,7 +9,6 @@ menuconfig IP_SCTP select CRYPTO select CRYPTO_HMAC select CRYPTO_SHA1 - select CRYPTO_MD5 if SCTP_HMAC_MD5 select LIBCRC32C ---help--- Stream Control Transmission Protocol @@ -68,33 +67,21 @@ config SCTP_DBG_OBJCNT If unsure, say N -choice - prompt "SCTP: Cookie HMAC Algorithm" - default SCTP_HMAC_MD5 +config SCTP_COOKIE_HMAC_MD5 + bool "Enable optional MD5 hmac cookie generation" help - HMAC algorithm to be used during association initialization. It - is strongly recommended to use HMAC-SHA1 or HMAC-MD5. See - configuration for Cryptographic API and enable those algorithms - to make usable by SCTP. - -config SCTP_HMAC_NONE - bool "None" - help - Choosing this disables the use of an HMAC during association - establishment. It is advised to use either HMAC-MD5 or HMAC-SHA1. - -config SCTP_HMAC_SHA1 - bool "HMAC-SHA1" - help - Enable the use of HMAC-SHA1 during association establishment. It - is advised to use either HMAC-MD5 or HMAC-SHA1. - -config SCTP_HMAC_MD5 - bool "HMAC-MD5" + Enable optional MD5 hmac based SCTP cookie generation + default y + select CRYPTO_HMAC if SCTP_COOKIE_HMAC_MD5 + select CRYPTO_MD5 if SCTP_COOKIE_HMAC_MD5 + +config SCTP_COOKIE_HMAC_SHA1 + bool "Enable optional SHA1 hmac cookie generation" help - Enable the use of HMAC-MD5 during association establishment. It is - advised to use either HMAC-MD5 or HMAC-SHA1. + Enable optional SHA1 hmac based SCTP cookie generation + default y + select CRYPTO_HMAC if SCTP_COOKIE_HMAC_SHA1 + select CRYPTO_SHA1 if SCTP_COOKIE_HMAC_SHA1 -endchoice endif # IP_SCTP diff --git a/net/sctp/protocol.c b/net/sctp/protocol.c index 2d518425d598..456bc3dbdd51 100644 --- a/net/sctp/protocol.c +++ b/net/sctp/protocol.c @@ -1190,6 +1190,15 @@ static int sctp_net_init(struct net *net) /* Whether Cookie Preservative is enabled(1) or not(0) */ net->sctp.cookie_preserve_enable = 1; + /* Default sctp sockets to use md5 as their hmac alg */ +#if defined (CONFIG_CRYPTO_MD5) + net->sctp.sctp_hmac_alg = "md5"; +#elif defined (CONFIG_CRYPTO_SHA1) + net->sctp.sctp_hmac_alg = "sha1"; +#else + net->sctp.sctp_hmac_alg = NULL; +#endif + /* Max.Burst - 4 */ net->sctp.max_burst = SCTP_DEFAULT_MAX_BURST; diff --git a/net/sctp/socket.c b/net/sctp/socket.c index 59d16ea927f0..fa81bdee00a5 100644 --- a/net/sctp/socket.c +++ b/net/sctp/socket.c @@ -110,7 +110,6 @@ static int sctp_do_bind(struct sock *, union sctp_addr *, int); static int sctp_autobind(struct sock *sk); static void sctp_sock_migrate(struct sock *, struct sock *, struct sctp_association *, sctp_socket_type_t); -static char *sctp_hmac_alg = SCTP_COOKIE_HMAC_ALG; extern struct kmem_cache *sctp_bucket_cachep; extern long sysctl_sctp_mem[3]; @@ -3890,6 +3889,8 @@ SCTP_STATIC int sctp_init_sock(struct sock *sk) sp->default_rcv_context = 0; sp->max_burst = net->sctp.max_burst; + sp->sctp_hmac_alg = net->sctp.sctp_hmac_alg; + /* Initialize default setup parameters. These parameters * can be modified with the SCTP_INITMSG socket option or * overridden by the SCTP_INIT CMSG. @@ -5981,13 +5982,15 @@ SCTP_STATIC int sctp_listen_start(struct sock *sk, int backlog) struct sctp_sock *sp = sctp_sk(sk); struct sctp_endpoint *ep = sp->ep; struct crypto_hash *tfm = NULL; + char alg[32]; /* Allocate HMAC for generating cookie. */ - if (!sctp_sk(sk)->hmac && sctp_hmac_alg) { - tfm = crypto_alloc_hash(sctp_hmac_alg, 0, CRYPTO_ALG_ASYNC); + if (!sp->hmac && sp->sctp_hmac_alg) { + sprintf(alg, "hmac(%s)", sp->sctp_hmac_alg); + tfm = crypto_alloc_hash(alg, 0, CRYPTO_ALG_ASYNC); if (IS_ERR(tfm)) { net_info_ratelimited("failed to load transform for %s: %ld\n", - sctp_hmac_alg, PTR_ERR(tfm)); + sp->sctp_hmac_alg, PTR_ERR(tfm)); return -ENOSYS; } sctp_sk(sk)->hmac = tfm; diff --git a/net/sctp/sysctl.c b/net/sctp/sysctl.c index 70e3ba5cb50b..043889ac86c0 100644 --- a/net/sctp/sysctl.c +++ b/net/sctp/sysctl.c @@ -62,6 +62,11 @@ 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(ctl_table *ctl, + int write, + void __user *buffer, size_t *lenp, + + loff_t *ppos); static ctl_table sctp_table[] = { { .procname = "sctp_mem", @@ -146,6 +151,12 @@ static ctl_table sctp_net_table[] = { .mode = 0644, .proc_handler = proc_dointvec, }, + { + .procname = "cookie_hmac_alg", + .maxlen = 8, + .mode = 0644, + .proc_handler = proc_sctp_do_hmac_alg, + }, { .procname = "valid_cookie_life", .data = &init_net.sctp.valid_cookie_life, @@ -289,6 +300,54 @@ static ctl_table sctp_net_table[] = { { /* sentinel */ } }; +static int proc_sctp_do_hmac_alg(ctl_table *ctl, + int write, + void __user *buffer, size_t *lenp, + loff_t *ppos) +{ + struct net *net = current->nsproxy->net_ns; + char tmp[8]; + ctl_table tbl; + int ret; + int changed = 0; + char *none = "none"; + + memset(&tbl, 0, sizeof(struct ctl_table)); + + if (write) { + tbl.data = tmp; + tbl.maxlen = 8; + } else { + tbl.data = net->sctp.sctp_hmac_alg ? : none; + tbl.maxlen = strlen(tbl.data); + } + ret = proc_dostring(&tbl, write, buffer, lenp, ppos); + + if (write) { +#ifdef CONFIG_CRYPTO_MD5 + if (!strncmp(tmp, "md5", 3)) { + net->sctp.sctp_hmac_alg = "md5"; + changed = 1; + } +#endif +#ifdef CONFIG_CRYPTO_SHA1 + if (!strncmp(tmp, "sha1", 4)) { + net->sctp.sctp_hmac_alg = "sha1"; + changed = 1; + } +#endif + if (!strncmp(tmp, "none", 4)) { + net->sctp.sctp_hmac_alg = NULL; + changed = 1; + } + + if (!changed) + ret = -EINVAL; + } + + return ret; +} + int sctp_sysctl_net_register(struct net *net) { struct ctl_table *table; -- cgit v1.2.3-59-g8ed1b From c6039f4a00901259517b7c027007697105209968 Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Thu, 25 Oct 2012 20:17:02 -0700 Subject: x86/boot/doc: Fix grammar and typo in boot.txt Fixes some minor issues in the x86 boot documentation. Signed-off-by: Kees Cook Cc: Rob Landley Link: http://lkml.kernel.org/r/20121026031702.GA23828@www.outflux.net Signed-off-by: Ingo Molnar --- Documentation/x86/boot.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'Documentation') diff --git a/Documentation/x86/boot.txt b/Documentation/x86/boot.txt index 9efceff51bfb..f15cb74c4f78 100644 --- a/Documentation/x86/boot.txt +++ b/Documentation/x86/boot.txt @@ -1013,7 +1013,7 @@ boot_params as that of 16-bit boot protocol, the boot loader should also fill the additional fields of the struct boot_params as that described in zero-page.txt. -After setupping the struct boot_params, the boot loader can load the +After setting up the struct boot_params, the boot loader can load the 32/64-bit kernel in the same way as that of 16-bit boot protocol. In 32-bit boot protocol, the kernel is started by jumping to the @@ -1023,7 +1023,7 @@ In 32-bit boot protocol, the kernel is started by jumping to the At entry, the CPU must be in 32-bit protected mode with paging disabled; a GDT must be loaded with the descriptors for selectors __BOOT_CS(0x10) and __BOOT_DS(0x18); both descriptors must be 4G flat -segment; __BOOS_CS must have execute/read permission, and __BOOT_DS +segment; __BOOT_CS must have execute/read permission, and __BOOT_DS must have read/write permission; CS must be __BOOT_CS and DS, ES, SS must be __BOOT_DS; interrupt must be disabled; %esi must hold the base address of the struct boot_params; %ebp, %edi and %ebx must be zero. -- cgit v1.2.3-59-g8ed1b From 9c4566a117a6fe404a0e49b27ac71b631945a70f Mon Sep 17 00:00:00 2001 From: Dinh Nguyen Date: Thu, 25 Oct 2012 10:41:39 -0600 Subject: ARM: socfpga: Enable SMP for socfpga Enable SMP for the SOCFPGA platform. Signed-off-by: Pavel Machek Signed-off-by: Dinh Nguyen Reviewed-by: Rob Herring Signed-off-by: Arnd Bergmann --- .../bindings/arm/altera/socfpga-reset.txt | 11 ++ .../bindings/arm/altera/socfpga-system.txt | 11 ++ arch/arm/boot/dts/socfpga.dtsi | 10 ++ arch/arm/configs/socfpga_defconfig | 3 +- arch/arm/mach-socfpga/Kconfig | 1 + arch/arm/mach-socfpga/Makefile | 1 + arch/arm/mach-socfpga/core.h | 34 ++++++ arch/arm/mach-socfpga/headsmp.S | 24 +++++ arch/arm/mach-socfpga/platsmp.c | 116 +++++++++++++++++++++ arch/arm/mach-socfpga/socfpga.c | 45 +++++++- 10 files changed, 254 insertions(+), 2 deletions(-) create mode 100644 Documentation/devicetree/bindings/arm/altera/socfpga-reset.txt create mode 100644 Documentation/devicetree/bindings/arm/altera/socfpga-system.txt create mode 100644 arch/arm/mach-socfpga/core.h create mode 100644 arch/arm/mach-socfpga/headsmp.S create mode 100644 arch/arm/mach-socfpga/platsmp.c (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/arm/altera/socfpga-reset.txt b/Documentation/devicetree/bindings/arm/altera/socfpga-reset.txt new file mode 100644 index 000000000000..ecdb57d69dbf --- /dev/null +++ b/Documentation/devicetree/bindings/arm/altera/socfpga-reset.txt @@ -0,0 +1,11 @@ +Altera SOCFPGA Reset Manager + +Required properties: +- compatible : "altr,rst-mgr" +- reg : Should contain 1 register ranges(address and length) + +Example: + rstmgr@ffd05000 { + compatible = "altr,rst-mgr"; + reg = <0xffd05000 0x1000>; + }; diff --git a/Documentation/devicetree/bindings/arm/altera/socfpga-system.txt b/Documentation/devicetree/bindings/arm/altera/socfpga-system.txt new file mode 100644 index 000000000000..07c65e3cdcbe --- /dev/null +++ b/Documentation/devicetree/bindings/arm/altera/socfpga-system.txt @@ -0,0 +1,11 @@ +Altera SOCFPGA System Manager + +Required properties: +- compatible : "altr,sys-mgr" +- reg : Should contain 1 register ranges(address and length) + +Example: + sysmgr@ffd08000 { + compatible = "altr,sys-mgr"; + reg = <0xffd08000 0x1000>; + }; diff --git a/arch/arm/boot/dts/socfpga.dtsi b/arch/arm/boot/dts/socfpga.dtsi index 0772f5739f59..19aec421bb26 100644 --- a/arch/arm/boot/dts/socfpga.dtsi +++ b/arch/arm/boot/dts/socfpga.dtsi @@ -143,5 +143,15 @@ reg-shift = <2>; reg-io-width = <4>; }; + + rstmgr@ffd05000 { + compatible = "altr,rst-mgr"; + reg = <0xffd05000 0x1000>; + }; + + sysmgr@ffd08000 { + compatible = "altr,sys-mgr"; + reg = <0xffd08000 0x4000>; + }; }; }; diff --git a/arch/arm/configs/socfpga_defconfig b/arch/arm/configs/socfpga_defconfig index 0ac1293dba10..4e1ce211d43f 100644 --- a/arch/arm/configs/socfpga_defconfig +++ b/arch/arm/configs/socfpga_defconfig @@ -18,9 +18,10 @@ CONFIG_MODULE_UNLOAD=y CONFIG_ARCH_SOCFPGA=y CONFIG_MACH_SOCFPGA_CYCLONE5=y CONFIG_ARM_THUMBEE=y +# CONFIG_ARCH_VEXPRESS_CORTEX_A5_A9_ERRATA is not set # CONFIG_CACHE_L2X0 is not set CONFIG_HIGH_RES_TIMERS=y -CONFIG_VMSPLIT_2G=y +CONFIG_SMP=y CONFIG_NR_CPUS=2 CONFIG_AEABI=y CONFIG_ZBOOT_ROM_TEXT=0x0 diff --git a/arch/arm/mach-socfpga/Kconfig b/arch/arm/mach-socfpga/Kconfig index 803a3281feb5..566e804d4036 100644 --- a/arch/arm/mach-socfpga/Kconfig +++ b/arch/arm/mach-socfpga/Kconfig @@ -12,5 +12,6 @@ config ARCH_SOCFPGA select GENERIC_CLOCKEVENTS select GPIO_PL061 if GPIOLIB select HAVE_ARM_SCU + select HAVE_SMP select SPARSE_IRQ select USE_OF diff --git a/arch/arm/mach-socfpga/Makefile b/arch/arm/mach-socfpga/Makefile index 4fb93240971d..6dd7a93a90fe 100644 --- a/arch/arm/mach-socfpga/Makefile +++ b/arch/arm/mach-socfpga/Makefile @@ -3,3 +3,4 @@ # obj-y := socfpga.o +obj-$(CONFIG_SMP) += headsmp.o platsmp.o diff --git a/arch/arm/mach-socfpga/core.h b/arch/arm/mach-socfpga/core.h new file mode 100644 index 000000000000..9941caa94931 --- /dev/null +++ b/arch/arm/mach-socfpga/core.h @@ -0,0 +1,34 @@ +/* + * Copyright 2012 Pavel Machek + * Copyright (C) 2012 Altera 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; 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __MACH_CORE_H +#define __MACH_CORE_H + +extern void secondary_startup(void); +extern void __iomem *socfpga_scu_base_addr; + +extern void socfpga_init_clocks(void); +extern void socfpga_sysmgr_init(void); + +extern struct smp_operations socfpga_smp_ops; +extern char secondary_trampoline, secondary_trampoline_end; + +#define SOCFPGA_SCU_VIRT_BASE 0xfffec000 + +#endif diff --git a/arch/arm/mach-socfpga/headsmp.S b/arch/arm/mach-socfpga/headsmp.S new file mode 100644 index 000000000000..17d6eaf9afff --- /dev/null +++ b/arch/arm/mach-socfpga/headsmp.S @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2003 ARM Limited + * Copyright (c) u-boot contributors + * Copyright (c) 2012 Pavel Machek + * + * 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 + + __INIT + +#define CPU1_START_ADDR 0xffd08010 + +ENTRY(secondary_trampoline) + movw r0, #:lower16:CPU1_START_ADDR + movt r0, #:upper16:CPU1_START_ADDR + + ldr r1, [r0] + bx r1 + +ENTRY(secondary_trampoline_end) diff --git a/arch/arm/mach-socfpga/platsmp.c b/arch/arm/mach-socfpga/platsmp.c new file mode 100644 index 000000000000..68dd1b69512a --- /dev/null +++ b/arch/arm/mach-socfpga/platsmp.c @@ -0,0 +1,116 @@ +/* + * Copyright 2010-2011 Calxeda, Inc. + * Copyright 2012 Pavel Machek + * Based on platsmp.c, Copyright (C) 2002 ARM Ltd. + * Copyright (C) 2012 Altera Corporation + * + * 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 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 "core.h" + +extern void __iomem *sys_manager_base_addr; +extern void __iomem *rst_manager_base_addr; + +static void __cpuinit socfpga_secondary_init(unsigned int cpu) +{ + /* + * if any interrupts are already enabled for the primary + * core (e.g. timer irq), then they will not have been enabled + * for us: do so + */ + gic_secondary_init(0); +} + +static int __cpuinit socfpga_boot_secondary(unsigned int cpu, struct task_struct *idle) +{ + int trampoline_size = &secondary_trampoline_end - &secondary_trampoline; + + memcpy(phys_to_virt(0), &secondary_trampoline, trampoline_size); + + __raw_writel(virt_to_phys(secondary_startup), (sys_manager_base_addr+0x10)); + + flush_cache_all(); + smp_wmb(); + outer_clean_range(0, trampoline_size); + + /* This will release CPU #1 out of reset.*/ + __raw_writel(0, rst_manager_base_addr + 0x10); + + return 0; +} + +/* + * Initialise the CPU possible map early - this describes the CPUs + * which may be present or become present in the system. + */ +static void __init socfpga_smp_init_cpus(void) +{ + unsigned int i, ncores; + + ncores = scu_get_core_count(socfpga_scu_base_addr); + + for (i = 0; i < ncores; i++) + set_cpu_possible(i, true); + + /* sanity check */ + if (ncores > num_possible_cpus()) { + pr_warn("socfpga: no. of cores (%d) greater than configured" + "maximum of %d - clipping\n", ncores, num_possible_cpus()); + ncores = num_possible_cpus(); + } + + for (i = 0; i < ncores; i++) + set_cpu_possible(i, true); + + set_smp_cross_call(gic_raise_softirq); +} + +static void __init socfpga_smp_prepare_cpus(unsigned int max_cpus) +{ + scu_enable(socfpga_scu_base_addr); +} + +/* + * platform-specific code to shutdown a CPU + * + * Called with IRQs disabled + */ +static void socfpga_cpu_die(unsigned int cpu) +{ + cpu_do_idle(); + + /* We should have never returned from idle */ + panic("cpu %d unexpectedly exit from shutdown\n", cpu); +} + +struct smp_operations socfpga_smp_ops __initdata = { + .smp_init_cpus = socfpga_smp_init_cpus, + .smp_prepare_cpus = socfpga_smp_prepare_cpus, + .smp_secondary_init = socfpga_secondary_init, + .smp_boot_secondary = socfpga_boot_secondary, +#ifdef CONFIG_HOTPLUG_CPU + .cpu_die = socfpga_cpu_die, +#endif +}; diff --git a/arch/arm/mach-socfpga/socfpga.c b/arch/arm/mach-socfpga/socfpga.c index f01e1ebf5396..ab81ea91a7c4 100644 --- a/arch/arm/mach-socfpga/socfpga.c +++ b/arch/arm/mach-socfpga/socfpga.c @@ -15,23 +15,64 @@ * along with this program. If not, see . */ #include +#include #include #include #include #include #include +#include -extern void socfpga_init_clocks(void); +#include "core.h" + +void __iomem *socfpga_scu_base_addr = ((void __iomem *)(SOCFPGA_SCU_VIRT_BASE)); +void __iomem *sys_manager_base_addr; +void __iomem *rst_manager_base_addr; + +static struct map_desc scu_io_desc __initdata = { + .virtual = SOCFPGA_SCU_VIRT_BASE, + .pfn = 0, /* run-time */ + .length = SZ_8K, + .type = MT_DEVICE, +}; + +static void __init socfpga_scu_map_io(void) +{ + unsigned long base; + + /* Get SCU base */ + asm("mrc p15, 4, %0, c15, c0, 0" : "=r" (base)); + + scu_io_desc.pfn = __phys_to_pfn(base); + iotable_init(&scu_io_desc, 1); +} + +static void __init socfpga_map_io(void) +{ + socfpga_scu_map_io(); +} const static struct of_device_id irq_match[] = { { .compatible = "arm,cortex-a9-gic", .data = gic_of_init, }, {} }; +void __init socfpga_sysmgr_init(void) +{ + struct device_node *np; + + np = of_find_compatible_node(NULL, NULL, "altr,sys-mgr"); + sys_manager_base_addr = of_iomap(np, 0); + + np = of_find_compatible_node(NULL, NULL, "altr,rst-mgr"); + rst_manager_base_addr = of_iomap(np, 0); +} + static void __init gic_init_irq(void) { of_irq_init(irq_match); + socfpga_sysmgr_init(); } static void socfpga_cyclone5_restart(char mode, const char *cmd) @@ -53,6 +94,8 @@ static const char *altera_dt_match[] = { }; DT_MACHINE_START(SOCFPGA, "Altera SOCFPGA") + .smp = smp_ops(socfpga_smp_ops), + .map_io = socfpga_map_io, .init_irq = gic_init_irq, .handle_irq = gic_handle_irq, .timer = &dw_apb_timer, -- cgit v1.2.3-59-g8ed1b From eaadf9a76e80b00dfac31ee7ee0a5ca8fb284baf Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Fri, 26 Oct 2012 16:51:54 -0700 Subject: Staging: telephony: remove unneeded Documentation Joe pointed out that I forgot to remove the documentation, so do that here. Cc: Joe Perches Signed-off-by: Greg Kroah-Hartman --- Documentation/telephony/00-INDEX | 4 - Documentation/telephony/ixj.txt | 394 --------------------------------------- 2 files changed, 398 deletions(-) delete mode 100644 Documentation/telephony/00-INDEX delete mode 100644 Documentation/telephony/ixj.txt (limited to 'Documentation') diff --git a/Documentation/telephony/00-INDEX b/Documentation/telephony/00-INDEX deleted file mode 100644 index 4ffe0ed5b6fb..000000000000 --- a/Documentation/telephony/00-INDEX +++ /dev/null @@ -1,4 +0,0 @@ -00-INDEX - - this file. -ixj.txt - - document describing the Quicknet drivers. diff --git a/Documentation/telephony/ixj.txt b/Documentation/telephony/ixj.txt deleted file mode 100644 index db94fb6c5678..000000000000 --- a/Documentation/telephony/ixj.txt +++ /dev/null @@ -1,394 +0,0 @@ -Linux Quicknet-Drivers-Howto -Quicknet Technologies, Inc. (www.quicknet.net) -Version 0.3.4 December 18, 1999 - -1.0 Introduction - -This document describes the first GPL release version of the Linux -driver for the Quicknet Internet PhoneJACK and Internet LineJACK -cards. More information about these cards is available at -www.quicknet.net. The driver version discussed in this document is -0.3.4. - -These cards offer nice telco style interfaces to use your standard -telephone/key system/PBX as the user interface for VoIP applications. -The Internet LineJACK also offers PSTN connectivity for a single line -Internet to PSTN gateway. Of course, you can add more than one card -to a system to obtain multi-line functionality. At this time, the -driver supports the POTS port on both the Internet PhoneJACK and the -Internet LineJACK, but the PSTN port on the latter card is not yet -supported. - -This document, and the drivers for the cards, are intended for a -limited audience that includes technically capable programmers who -would like to experiment with Quicknet cards. The drivers are -considered in ALPHA status and are not yet considered stable enough -for general, widespread use in an unlimited audience. - -That's worth saying again: - -THE LINUX DRIVERS FOR QUICKNET CARDS ARE PRESENTLY IN A ALPHA STATE -AND SHOULD NOT BE CONSIDERED AS READY FOR NORMAL WIDESPREAD USE. - -They are released early in the spirit of Internet development and to -make this technology available to innovators who would benefit from -early exposure. - -When we promote the device driver to "beta" level it will be -considered ready for non-programmer, non-technical users. Until then, -please be aware that these drivers may not be stable and may affect -the performance of your system. - - -1.1 Latest Additions/Improvements - -The 0.3.4 version of the driver is the first GPL release. Several -features had to be removed from the prior binary only module, mostly -for reasons of Intellectual Property rights. We can't release -information that is not ours - so certain aspects of the driver had to -be removed to protect the rights of others. - -Specifically, very old Internet PhoneJACK cards have non-standard -G.723.1 codecs (due to the early nature of the DSPs in those days). -The auto-conversion code to bring those cards into compliance with -today's standards is available as a binary only module to those people -needing it. If you bought your card after 1997 or so, you are OK - -it's only the very old cards that are affected. - -Also, the code to download G.728/G.729/G.729a codecs to the DSP is -available as a binary only module as well. This IP is not ours to -release. - -Hooks are built into the GPL driver to allow it to work with other -companion modules that are completely separate from this module. - -1.2 Copyright, Trademarks, Disclaimer, & Credits - -Copyright - -Copyright (c) 1999 Quicknet Technologies, Inc. Permission is granted -to freely copy and distribute this document provided you preserve it -in its original form. For corrections and minor changes contact the -maintainer at linux@quicknet.net. - -Trademarks - -Internet PhoneJACK and Internet LineJACK are registered trademarks of -Quicknet Technologies, Inc. - -Disclaimer - -Much of the info in this HOWTO is early information released by -Quicknet Technologies, Inc. for the express purpose of allowing early -testing and use of the Linux drivers developed for their products. -While every attempt has been made to be thorough, complete and -accurate, the information contained here may be unreliable and there -are likely a number of errors in this document. Please let the -maintainer know about them. Since this is free documentation, it -should be obvious that neither I nor previous authors can be held -legally responsible for any errors. - -Credits - -This HOWTO was written by: - - Greg Herlein - Ed Okerson - -1.3 Future Plans: You Can Help - -Please let the maintainer know of any errors in facts, opinions, -logic, spelling, grammar, clarity, links, etc. But first, if the date -is over a month old, check to see that you have the latest -version. Please send any info that you think belongs in this document. - -You can also contribute code and/or bug-fixes for the sample -applications. - - -1.4 Where to get things - -Info on latest versions of the driver are here: - -http://web.archive.org/web/*/http://www.quicknet.net/develop.htm - -1.5 Mailing List - -Quicknet operates a mailing list to provide a public forum on using -these drivers. - -To subscribe to the linux-sdk mailing list, send an email to: - - majordomo@linux.quicknet.net - -In the body of the email, type: - - subscribe linux-sdk - -Please delete any signature block that you would normally add to the -bottom of your email - it tends to confuse majordomo. - -To send mail to the list, address your mail to - - linux-sdk@linux.quicknet.net - -Your message will go out to everyone on the list. - -To unsubscribe to the linux-sdk mailing list, send an email to: - - majordomo@linux.quicknet.net - -In the body of the email, type: - - unsubscribe linux-sdk - - - -2.0 Requirements - -2.1 Quicknet Card(s) - -You will need at least one Internet PhoneJACK or Internet LineJACK -cards. These are ISA or PCI bus devices that use Plug-n-Play for -configuration, and use no IRQs. The driver will support up to 16 -cards in any one system, of any mix between the two types. - -Note that you will need two cards to do any useful testing alone, since -you will need a card on both ends of the connection. Of course, if -you are doing collaborative work, perhaps your friends or coworkers -have cards too. If not, we'll gladly sell them some! - - -2.2 ISAPNP - -Since the Quicknet cards are Plug-n-Play devices, you will need the -isapnp tools package to configure the cards, or you can use the isapnp -module to autoconfigure them. The former package probably came with -your Linux distribution. Documentation on this package is available -online at: - -http://mailer.wiwi.uni-marburg.de/linux/LDP/HOWTO/Plug-and-Play-HOWTO.html - -The isapnp autoconfiguration is available on the Quicknet website at: - - http://www.quicknet.net/develop.htm - -though it may be in the kernel by the time you read this. - - -3.0 Card Configuration - -If you did not get your drivers as part of the linux kernel, do the -following to install them: - - a. untar the distribution file. We use the following command: - tar -xvzf ixj-0.x.x.tgz - -This creates a subdirectory holding all the necessary files. Go to that -subdirectory. - - b. run the "ixj_dev_create" script to remove any stray device -files left in the /dev directory, and to create the new officially -designated device files. Note that the old devices were called -/dev/ixj, and the new method uses /dev/phone. - - c. type "make;make install" - this will compile and install the -module. - - d. type "depmod -av" to rebuild all your kernel version dependencies. - - e. if you are using the isapnp module to configure the cards - automatically, then skip to step f. Otherwise, ensure that you - have run the isapnp configuration utility to properly configure - the cards. - - e1. The Internet PhoneJACK has one configuration register that - requires 16 IO ports. The Internet LineJACK card has two - configuration registers and isapnp reports that IO 0 - requires 16 IO ports and IO 1 requires 8. The Quicknet - driver assumes that these registers are configured to be - contiguous, i.e. if IO 0 is set to 0x340 then IO 1 should - be set to 0x350. - - Make sure that none of the cards overlap if you have - multiple cards in the system. - - If you are new to the isapnp tools, you can jumpstart - yourself by doing the following: - - e2. go to the /etc directory and run pnpdump to get a blank - isapnp.conf file. - - pnpdump > /etc/isapnp.conf - - e3. edit the /etc/isapnp.conf file to set the IO warnings and - the register IO addresses. The IO warnings means that you - should find the line in the file that looks like this: - - (CONFLICT (IO FATAL)(IRQ FATAL)(DMA FATAL)(MEM FATAL)) # or WARNING - - and you should edit the line to look like this: - - (CONFLICT (IO WARNING)(IRQ FATAL)(DMA FATAL)(MEM FATAL)) # - or WARNING - - The next step is to set the IO port addresses. The issue - here is that isapnp does not identify all of the ports out - there. Specifically any device that does not have a driver - or module loaded by Linux will not be registered. This - includes older sound cards and network cards. We have - found that the IO port 0x300 is often used even though - isapnp claims that no-one is using those ports. We - recommend that for a single card installation that port - 0x340 (and 0x350) be used. The IO port line should change - from this: - - (IO 0 (SIZE 16) (BASE 0x0300) (CHECK)) - - to this: - - (IO 0 (SIZE 16) (BASE 0x0340) ) - - e4. if you have multiple Quicknet cards, make sure that you do - not have any overlaps. Be especially careful if you are - mixing Internet PhoneJACK and Internet LineJACK cards in - the same system. In these cases we recommend moving the - IO port addresses to the 0x400 block. Please note that on - a few machines the 0x400 series are used. Feel free to - experiment with other addresses. Our cards have been - proven to work using IO addresses of up to 0xFF0. - - e5. the last step is to uncomment the activation line so the - drivers will be associated with the port. This means the - line (immediately below) the IO line should go from this: - - # (ACT Y) - - to this: - - (ACT Y) - - Once you have finished editing the isapnp.conf file you - must submit it into the pnp driverconfigure the cards. - This is done using the following command: - - isapnp isapnp.conf - - If this works you should see a line that identifies the - Quicknet device, the IO port(s) chosen, and a message - "Enabled OK". - - f. if you are loading the module by hand, use insmod. An example -of this would look like this: - - insmod phonedev - insmod ixj dspio=0x320,0x310 xio=0,0x330 - -Then verify the module loaded by running lsmod. If you are not using a -module that matches your kernel version, you may need to "force" the -load using the -f option in the insmod command. - - insmod phonedev - insmod -f ixj dspio=0x320,0x310 xio=0,0x330 - - -If you are using isapnp to autoconfigure your card, then you do NOT -need any of the above, though you need to use depmod to load the -driver, like this: - - depmod ixj - -which will result in the needed drivers getting loaded automatically. - - g. if you are planning on having the kernel automatically request -the module for you, then you need to edit /etc/conf.modules and add the -following lines: - - options ixj dspio=0x340 xio=0x330 ixjdebug=0 - -If you do this, then when you execute an application that uses the -module the kernel will request that it is loaded. - - h. if you want non-root users to be able to read and write to the -ixj devices (this is a good idea!) you should do the following: - - - decide upon a group name to use and create that group if - needed. Add the user names to that group that you wish to - have access to the device. For example, we typically will - create a group named "ixj" in /etc/group and add all users - to that group that we want to run software that can use the - ixjX devices. - - - change the permissions on the device files, like this: - - chgrp ixj /dev/ixj* - chmod 660 /dev/ixj* - -Once this is done, then non-root users should be able to use the -devices. If you have enabled autoloading of modules, then the user -should be able to open the device and have the module loaded -automatically for them. - - -4.0 Driver Installation problems. - -We have tested these drivers on the 2.2.9, 2.2.10, 2.2.12, and 2.2.13 kernels -and in all cases have eventually been able to get the drivers to load and -run. We have found four types of problems that prevent this from happening. -The problems and solutions are: - - a. A step was missed in the installation. Go back and use section 3 -as a checklist. Many people miss running the ixj_dev_create script and thus -never load the device names into the filesystem. - - b. The kernel is inconsistently linked. We have found this problem in -the Out Of the Box installation of several distributions. The symptoms -are that neither driver will load, and that the unknown symbols include "jiffy" -and "kmalloc". The solution is to recompile both the kernel and the -modules. The command string for the final compile looks like this: - - In the kernel directory: - 1. cp .config /tmp - 2. make mrproper - 3. cp /tmp/.config . - 4. make clean;make bzImage;make modules;make modules_install - -This rebuilds both the kernel and all the modules and makes sure they all -have the same linkages. This generally solves the problem once the new -kernel is installed and the system rebooted. - - c. The kernel has been patched, then unpatched. This happens when -someone decides to use an earlier kernel after they load a later kernel. -The symptoms are proceeding through all three above steps and still not -being able to load the driver. What has happened is that the generated -header files are out of sync with the kernel itself. The solution is -to recompile (again) using "make mrproper". This will remove and then -regenerate all the necessary header files. Once this is done, then you -need to install and reboot the kernel. We have not seen any problem -loading one of our drivers after this treatment. - -5.0 Known Limitations - -We cannot currently play "dial-tone" and listen for DTMF digits at the -same time using the ISA PhoneJACK. This is a bug in the 8020 DSP chip -used on that product. All other Quicknet products function normally -in this regard. We have a work-around, but it's not done yet. Until -then, if you want dial-tone, you can always play a recorded dial-tone -sound into the audio until you have gathered the DTMF digits. - - - - - - - - - - - - - - - - - -- cgit v1.2.3-59-g8ed1b From e7d993550167ef810b6658fc7b0a73517c2ed957 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Sun, 21 Oct 2012 15:30:02 -0300 Subject: [media] v4l: Correct definition of v4l2_buffer.flags related to cache management V4L2_BUF_FLAG_NO_CACHE_INVALIDATE and V4L2_BUF_FLAG_NO_CACHE_CLEAN were define incorrectly in the documentation. Fix this by changing the documentation to match reality. Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- Documentation/DocBook/media/v4l/io.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'Documentation') diff --git a/Documentation/DocBook/media/v4l/io.xml b/Documentation/DocBook/media/v4l/io.xml index b5d1cbdc558b..7e2f3d7d78ad 100644 --- a/Documentation/DocBook/media/v4l/io.xml +++ b/Documentation/DocBook/media/v4l/io.xml @@ -923,7 +923,7 @@ application. Drivers set or clear this flag when the V4L2_BUF_FLAG_NO_CACHE_INVALIDATE - 0x0400 + 0x0800 Caches do not have to be invalidated for this buffer. Typically applications shall use this flag if the data captured in the buffer is not going to be touched by the CPU, instead the buffer will, probably, be @@ -932,7 +932,7 @@ passed on to a DMA-capable hardware unit for further processing or output. V4L2_BUF_FLAG_NO_CACHE_CLEAN - 0x0800 + 0x1000 Caches do not have to be cleaned for this buffer. Typically applications shall use this flag for output buffers if the data in this buffer has not been created by the CPU but by some DMA-capable unit, -- cgit v1.2.3-59-g8ed1b From 689e331f3fb6e0a44155eef35fe19caf3285c6ce Mon Sep 17 00:00:00 2001 From: Sekhar Nori Date: Tue, 28 Aug 2012 15:27:52 +0530 Subject: ARM: davinci: da850: add DT boot support Add support for booting DA850 using flattened device tree to describe the hardware. At this time only the very basic bootup using a serial console is supported. Signed-off-by: Sekhar Nori --- Documentation/devicetree/bindings/arm/davinci.txt | 9 ++++ arch/arm/Kconfig | 1 + arch/arm/mach-davinci/Kconfig | 8 +++ arch/arm/mach-davinci/Makefile | 1 + arch/arm/mach-davinci/da8xx-dt.c | 64 +++++++++++++++++++++++ 5 files changed, 83 insertions(+) create mode 100644 Documentation/devicetree/bindings/arm/davinci.txt create mode 100644 arch/arm/mach-davinci/da8xx-dt.c (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/arm/davinci.txt b/Documentation/devicetree/bindings/arm/davinci.txt new file mode 100644 index 000000000000..aa4e7a156dd2 --- /dev/null +++ b/Documentation/devicetree/bindings/arm/davinci.txt @@ -0,0 +1,9 @@ +Texas Instruments DaVinci Platforms Device Tree Bindings +-------------------------------------------------------- + +Generic DaVinci Boards +---------------------- + +DA850/OMAP-L138/AM18x generic board +Required root node properties: + - compatible = "ti,da850"; diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index 73067efd4845..06e0adf2368e 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -924,6 +924,7 @@ config ARCH_DAVINCI select GENERIC_IRQ_CHIP select HAVE_IDE select NEED_MACH_GPIO_H + select USE_OF select ZONE_DMA help Support for TI's DaVinci platform. diff --git a/arch/arm/mach-davinci/Kconfig b/arch/arm/mach-davinci/Kconfig index f8eecb959413..0153950f6068 100644 --- a/arch/arm/mach-davinci/Kconfig +++ b/arch/arm/mach-davinci/Kconfig @@ -58,6 +58,14 @@ config ARCH_DAVINCI_TNETV107X comment "DaVinci Board Type" +config MACH_DA8XX_DT + bool "Support DA8XX platforms using device tree" + default y + depends on ARCH_DAVINCI_DA8XX + help + Say y here to include support for TI DaVinci DA850 based using + Flattened Device Tree. More information at Documentation/devicetree + config MACH_DAVINCI_EVM bool "TI DM644x EVM" default ARCH_DAVINCI_DM644x diff --git a/arch/arm/mach-davinci/Makefile b/arch/arm/mach-davinci/Makefile index 2227effcb0e9..fb5c1aa98a63 100644 --- a/arch/arm/mach-davinci/Makefile +++ b/arch/arm/mach-davinci/Makefile @@ -22,6 +22,7 @@ obj-$(CONFIG_AINTC) += irq.o obj-$(CONFIG_CP_INTC) += cp_intc.o # Board specific +obj-$(CONFIG_MACH_DA8XX_DT) += da8xx-dt.o obj-$(CONFIG_MACH_DAVINCI_EVM) += board-dm644x-evm.o obj-$(CONFIG_MACH_SFFSDR) += board-sffsdr.o obj-$(CONFIG_MACH_NEUROS_OSD2) += board-neuros-osd2.o diff --git a/arch/arm/mach-davinci/da8xx-dt.c b/arch/arm/mach-davinci/da8xx-dt.c new file mode 100644 index 000000000000..df393d9b1517 --- /dev/null +++ b/arch/arm/mach-davinci/da8xx-dt.c @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/ + * + * Modified from mach-omap/omap2/board-generic.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 + +#include +#include +#include + +#define DA8XX_NUM_UARTS 3 + +void __init da8xx_uart_clk_enable(void) +{ + int i; + for (i = 0; i < DA8XX_NUM_UARTS; i++) + davinci_serial_setup_clk(i, NULL); +} + +static struct of_device_id da8xx_irq_match[] __initdata = { + { .compatible = "ti,cp-intc", .data = cp_intc_of_init, }, + { } +}; + +static void __init da8xx_init_irq(void) +{ + of_irq_init(da8xx_irq_match); +} + +#ifdef CONFIG_ARCH_DAVINCI_DA850 + +static void __init da850_init_machine(void) +{ + of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL); + + da8xx_uart_clk_enable(); +} + +static const char *da850_boards_compat[] __initdata = { + "ti,da850", + NULL, +}; + +DT_MACHINE_START(DA850_DT, "Generic DA850/OMAP-L138/AM18x") + .map_io = da850_init, + .init_irq = da8xx_init_irq, + .timer = &davinci_timer, + .init_machine = da850_init_machine, + .dt_compat = da850_boards_compat, + .init_late = davinci_init_late, + .restart = da8xx_restart, +MACHINE_END + +#endif -- cgit v1.2.3-59-g8ed1b From 27e6e0d3dafa5b487dc28b219753443db300f289 Mon Sep 17 00:00:00 2001 From: Sekhar Nori Date: Wed, 29 Aug 2012 23:25:27 +0530 Subject: ARM: davinci: da850 evm: add DT data Add device tree data for DA850 EVM. At this time, only information on serial ports is added. Signed-off-by: Sekhar Nori --- Documentation/devicetree/bindings/arm/davinci.txt | 4 ++++ arch/arm/boot/dts/da850-evm.dts | 28 +++++++++++++++++++++++ arch/arm/mach-davinci/da8xx-dt.c | 1 + 3 files changed, 33 insertions(+) create mode 100644 arch/arm/boot/dts/da850-evm.dts (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/arm/davinci.txt b/Documentation/devicetree/bindings/arm/davinci.txt index aa4e7a156dd2..2dda756414cc 100644 --- a/Documentation/devicetree/bindings/arm/davinci.txt +++ b/Documentation/devicetree/bindings/arm/davinci.txt @@ -1,6 +1,10 @@ Texas Instruments DaVinci Platforms Device Tree Bindings -------------------------------------------------------- +DA850/OMAP-L138/AM18x Evaluation Module (EVM) board +Required root node properties: + - compatible = "ti,da850-evm", "ti,da850"; + Generic DaVinci Boards ---------------------- diff --git a/arch/arm/boot/dts/da850-evm.dts b/arch/arm/boot/dts/da850-evm.dts new file mode 100644 index 000000000000..37dc5a3243b8 --- /dev/null +++ b/arch/arm/boot/dts/da850-evm.dts @@ -0,0 +1,28 @@ +/* + * Device Tree for DA850 EVM board + * + * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/ + * + * 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. + */ +/dts-v1/; +/include/ "da850.dtsi" + +/ { + compatible = "ti,da850-evm", "ti,da850"; + model = "DA850/AM1808/OMAP-L138 EVM"; + + soc { + serial0: serial@1c42000 { + status = "okay"; + }; + serial1: serial@1d0c000 { + status = "okay"; + }; + serial2: serial@1d0d000 { + status = "okay"; + }; + }; +}; diff --git a/arch/arm/mach-davinci/da8xx-dt.c b/arch/arm/mach-davinci/da8xx-dt.c index df393d9b1517..94b09fd4cca4 100644 --- a/arch/arm/mach-davinci/da8xx-dt.c +++ b/arch/arm/mach-davinci/da8xx-dt.c @@ -47,6 +47,7 @@ static void __init da850_init_machine(void) } static const char *da850_boards_compat[] __initdata = { + "ti,da850-evm", "ti,da850", NULL, }; -- cgit v1.2.3-59-g8ed1b From 3015fb33483b80f64f82092f8acc89ce9c3f07e3 Mon Sep 17 00:00:00 2001 From: Heiko Schocher Date: Wed, 30 May 2012 12:19:03 +0200 Subject: ARM: davinci: add support for am1808 based EnBW CMC board This board supports: - AM1808 based board - 64 MiB DDR ram - 2 MiB Nor flash - 128 MiB NAND flash - use internal RTC - I2C support - hwmon lm75 support - UBI/UBIFS support - MMC support - USB OTG support This patch adds the basic DTS file enabling the serial ports. Other features will be added in due course of time. Signed-off-by: Heiko Schocher Cc: linux-arm-kernel@lists.infradead.org Cc: devicetree-discuss@lists.ozlabs.org Cc: davinci-linux-open-source@linux.davincidsp.com Cc: Sekhar Nori Cc: Kevin Hilman Cc: Wolfgang Denk [nsekhar@ti.com: modified after dtsi refactoring and added binding documentation] Signed-off-by: Sekhar Nori --- Documentation/devicetree/bindings/arm/davinci.txt | 4 +++ arch/arm/boot/dts/da850-enbw-cmc.dts | 30 +++++++++++++++++++++++ arch/arm/mach-davinci/da8xx-dt.c | 1 + 3 files changed, 35 insertions(+) create mode 100644 arch/arm/boot/dts/da850-enbw-cmc.dts (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/arm/davinci.txt b/Documentation/devicetree/bindings/arm/davinci.txt index 2dda756414cc..cfaeda4274e6 100644 --- a/Documentation/devicetree/bindings/arm/davinci.txt +++ b/Documentation/devicetree/bindings/arm/davinci.txt @@ -5,6 +5,10 @@ DA850/OMAP-L138/AM18x Evaluation Module (EVM) board Required root node properties: - compatible = "ti,da850-evm", "ti,da850"; +EnBW AM1808 based CMC board +Required root node properties: + - compatible = "enbw,cmc", "ti,da850; + Generic DaVinci Boards ---------------------- diff --git a/arch/arm/boot/dts/da850-enbw-cmc.dts b/arch/arm/boot/dts/da850-enbw-cmc.dts new file mode 100644 index 000000000000..422fdb3fcfc1 --- /dev/null +++ b/arch/arm/boot/dts/da850-enbw-cmc.dts @@ -0,0 +1,30 @@ +/* + * Device Tree for AM1808 EnBW CMC board + * + * Copyright 2012 DENX Software Engineering GmbH + * Heiko Schocher + * + * 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. + */ +/dts-v1/; +/include/ "da850.dtsi" + +/ { + compatible = "enbw,cmc", "ti,da850"; + model = "EnBW CMC"; + + soc { + serial0: serial@1c42000 { + status = "okay"; + }; + serial1: serial@1d0c000 { + status = "okay"; + }; + serial2: serial@1d0d000 { + status = "okay"; + }; + }; +}; diff --git a/arch/arm/mach-davinci/da8xx-dt.c b/arch/arm/mach-davinci/da8xx-dt.c index 94b09fd4cca4..37c27af18fa0 100644 --- a/arch/arm/mach-davinci/da8xx-dt.c +++ b/arch/arm/mach-davinci/da8xx-dt.c @@ -47,6 +47,7 @@ static void __init da850_init_machine(void) } static const char *da850_boards_compat[] __initdata = { + "enbw,cmc", "ti,da850-evm", "ti,da850", NULL, -- cgit v1.2.3-59-g8ed1b From 566cf8eef26d99415aa8a542bd6bba6a719d2031 Mon Sep 17 00:00:00 2001 From: Rahul Sharma Date: Mon, 29 Oct 2012 21:48:43 +0900 Subject: ARM: dts: add device tree support for exynos5 hdmi This patch adds support for device tree based discovery for exynos5 hdmi. Hdmi node is also renamed with "exynos5-hdmi". Signed-off-by: Rahul Sharma Signed-off-by: Kukjin Kim --- .../devicetree/bindings/drm/exynos/hdmi.txt | 22 ++++++++++++++++++++++ arch/arm/boot/dts/exynos5250-smdk5250.dts | 4 ++++ arch/arm/boot/dts/exynos5250.dtsi | 6 ++++++ arch/arm/mach-exynos/mach-exynos5-dt.c | 2 ++ 4 files changed, 34 insertions(+) create mode 100644 Documentation/devicetree/bindings/drm/exynos/hdmi.txt (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/drm/exynos/hdmi.txt b/Documentation/devicetree/bindings/drm/exynos/hdmi.txt new file mode 100644 index 000000000000..589edee37394 --- /dev/null +++ b/Documentation/devicetree/bindings/drm/exynos/hdmi.txt @@ -0,0 +1,22 @@ +Device-Tree bindings for drm hdmi driver + +Required properties: +- compatible: value should be "samsung,exynos5-hdmi". +- reg: physical base address of the hdmi and length of memory mapped + region. +- interrupts: interrupt number to the cpu. +- hpd-gpio: following information about the hotplug gpio pin. + a) phandle of the gpio controller node. + b) pin number within the gpio controller. + c) pin function mode. + d) optional flags and pull up/down. + e) drive strength. + +Example: + + hdmi { + compatible = "samsung,exynos5-hdmi"; + reg = <0x14530000 0x100000>; + interrupts = <0 95 0>; + hpd-gpio = <&gpx3 7 0xf 1 3>; + }; diff --git a/arch/arm/boot/dts/exynos5250-smdk5250.dts b/arch/arm/boot/dts/exynos5250-smdk5250.dts index a352df403b7a..2a478abbd98a 100644 --- a/arch/arm/boot/dts/exynos5250-smdk5250.dts +++ b/arch/arm/boot/dts/exynos5250-smdk5250.dts @@ -166,4 +166,8 @@ spi_2: spi@12d40000 { status = "disabled"; }; + + hdmi { + hpd-gpio = <&gpx3 7 0xf 1 3>; + }; }; diff --git a/arch/arm/boot/dts/exynos5250.dtsi b/arch/arm/boot/dts/exynos5250.dtsi index dddfd6e444dc..b68be1cab2e0 100644 --- a/arch/arm/boot/dts/exynos5250.dtsi +++ b/arch/arm/boot/dts/exynos5250.dtsi @@ -520,4 +520,10 @@ reg = <0x13e30000 0x1000>; interrupts = <0 88 0>; }; + + hdmi { + compatible = "samsung,exynos5-hdmi"; + reg = <0x14530000 0x100000>; + interrupts = <0 95 0>; + }; }; diff --git a/arch/arm/mach-exynos/mach-exynos5-dt.c b/arch/arm/mach-exynos/mach-exynos5-dt.c index db1cd8eacf28..5e38208e0d13 100644 --- a/arch/arm/mach-exynos/mach-exynos5-dt.c +++ b/arch/arm/mach-exynos/mach-exynos5-dt.c @@ -72,6 +72,8 @@ static const struct of_dev_auxdata exynos5250_auxdata_lookup[] __initconst = { "exynos-gsc.2", NULL), OF_DEV_AUXDATA("samsung,exynos5-gsc", EXYNOS5_PA_GSC3, "exynos-gsc.3", NULL), + OF_DEV_AUXDATA("samsung,exynos5-hdmi", 0x14530000, + "exynos5-hdmi", NULL), {}, }; -- cgit v1.2.3-59-g8ed1b From 5af0d8a372811c353ab7de5d161463f58aa2173a Mon Sep 17 00:00:00 2001 From: Rahul Sharma Date: Mon, 29 Oct 2012 21:51:36 +0900 Subject: ARM: dts: add device tree support for exynos5 mixer This patch adds support for device tree based discovery for exynos5 mixer. Mixer node is also renamed with "exynos5-mixer". Signed-off-by: Rahul Sharma Signed-off-by: Kukjin Kim --- Documentation/devicetree/bindings/drm/exynos/mixer.txt | 15 +++++++++++++++ arch/arm/boot/dts/exynos5250.dtsi | 6 ++++++ arch/arm/mach-exynos/mach-exynos5-dt.c | 2 ++ 3 files changed, 23 insertions(+) create mode 100644 Documentation/devicetree/bindings/drm/exynos/mixer.txt (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/drm/exynos/mixer.txt b/Documentation/devicetree/bindings/drm/exynos/mixer.txt new file mode 100644 index 000000000000..9b2ea0343566 --- /dev/null +++ b/Documentation/devicetree/bindings/drm/exynos/mixer.txt @@ -0,0 +1,15 @@ +Device-Tree bindings for mixer driver + +Required properties: +- compatible: value should be "samsung,exynos5-mixer". +- reg: physical base address of the mixer and length of memory mapped + region. +- interrupts: interrupt number to the cpu. + +Example: + + mixer { + compatible = "samsung,exynos5-mixer"; + reg = <0x14450000 0x10000>; + interrupts = <0 94 0>; + }; diff --git a/arch/arm/boot/dts/exynos5250.dtsi b/arch/arm/boot/dts/exynos5250.dtsi index b68be1cab2e0..2a203e62f745 100644 --- a/arch/arm/boot/dts/exynos5250.dtsi +++ b/arch/arm/boot/dts/exynos5250.dtsi @@ -526,4 +526,10 @@ reg = <0x14530000 0x100000>; interrupts = <0 95 0>; }; + + mixer { + compatible = "samsung,exynos5-mixer"; + reg = <0x14450000 0x10000>; + interrupts = <0 94 0>; + }; }; diff --git a/arch/arm/mach-exynos/mach-exynos5-dt.c b/arch/arm/mach-exynos/mach-exynos5-dt.c index 5e38208e0d13..4fc15daf7c17 100644 --- a/arch/arm/mach-exynos/mach-exynos5-dt.c +++ b/arch/arm/mach-exynos/mach-exynos5-dt.c @@ -74,6 +74,8 @@ static const struct of_dev_auxdata exynos5250_auxdata_lookup[] __initconst = { "exynos-gsc.3", NULL), OF_DEV_AUXDATA("samsung,exynos5-hdmi", 0x14530000, "exynos5-hdmi", NULL), + OF_DEV_AUXDATA("samsung,exynos5-mixer", 0x14450000, + "exynos5-mixer", NULL), {}, }; -- cgit v1.2.3-59-g8ed1b From 3e3e9ce4474c3c1ec6e3e05268705ed086825159 Mon Sep 17 00:00:00 2001 From: Rahul Sharma Date: Mon, 29 Oct 2012 21:51:42 +0900 Subject: ARM: dts: add device tree support for exynos5 hdmiphy This patch adds support for device tree based discovery for exynos5 hdmiphy. Signed-off-by: Rahul Sharma Signed-off-by: Kukjin Kim --- Documentation/devicetree/bindings/drm/exynos/hdmiphy.txt | 12 ++++++++++++ arch/arm/boot/dts/exynos5250-smdk5250.dts | 10 ++++++++++ arch/arm/boot/dts/exynos5250.dtsi | 8 ++++++++ arch/arm/mach-exynos/mach-exynos5-dt.c | 2 ++ 4 files changed, 32 insertions(+) create mode 100644 Documentation/devicetree/bindings/drm/exynos/hdmiphy.txt (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/drm/exynos/hdmiphy.txt b/Documentation/devicetree/bindings/drm/exynos/hdmiphy.txt new file mode 100644 index 000000000000..858f4f9b902f --- /dev/null +++ b/Documentation/devicetree/bindings/drm/exynos/hdmiphy.txt @@ -0,0 +1,12 @@ +Device-Tree bindings for hdmiphy driver + +Required properties: +- compatible: value should be "samsung,exynos5-hdmiphy". +- reg: I2C address of the hdmiphy device. + +Example: + + hdmiphy { + compatible = "samsung,exynos5-hdmiphy"; + reg = <0x38>; + }; diff --git a/arch/arm/boot/dts/exynos5250-smdk5250.dts b/arch/arm/boot/dts/exynos5250-smdk5250.dts index 2a478abbd98a..7dc196f6b0a1 100644 --- a/arch/arm/boot/dts/exynos5250-smdk5250.dts +++ b/arch/arm/boot/dts/exynos5250-smdk5250.dts @@ -79,6 +79,16 @@ status = "disabled"; }; + i2c@12CE0000 { + samsung,i2c-sda-delay = <100>; + samsung,i2c-max-bus-freq = <66000>; + + hdmiphy@38 { + compatible = "samsung,exynos5-hdmiphy"; + reg = <0x38>; + }; + }; + dwmmc_0: dwmmc0@12200000 { num-slots = <1>; supports-highspeed; diff --git a/arch/arm/boot/dts/exynos5250.dtsi b/arch/arm/boot/dts/exynos5250.dtsi index 2a203e62f745..190068f66499 100644 --- a/arch/arm/boot/dts/exynos5250.dtsi +++ b/arch/arm/boot/dts/exynos5250.dtsi @@ -156,6 +156,14 @@ #size-cells = <0>; }; + i2c@12CE0000 { + compatible = "samsung,s3c2440-hdmiphy-i2c"; + reg = <0x12CE0000 0x1000>; + interrupts = <0 64 0>; + #address-cells = <1>; + #size-cells = <0>; + }; + spi_0: spi@12d20000 { compatible = "samsung,exynos4210-spi"; reg = <0x12d20000 0x100>; diff --git a/arch/arm/mach-exynos/mach-exynos5-dt.c b/arch/arm/mach-exynos/mach-exynos5-dt.c index 4fc15daf7c17..0f20ba8fcf1b 100644 --- a/arch/arm/mach-exynos/mach-exynos5-dt.c +++ b/arch/arm/mach-exynos/mach-exynos5-dt.c @@ -47,6 +47,8 @@ static const struct of_dev_auxdata exynos5250_auxdata_lookup[] __initconst = { "s3c2440-i2c.0", NULL), OF_DEV_AUXDATA("samsung,s3c2440-i2c", EXYNOS5_PA_IIC(1), "s3c2440-i2c.1", NULL), + OF_DEV_AUXDATA("samsung,s3c2440-hdmiphy-i2c", EXYNOS5_PA_IIC(8), + "s3c2440-hdmiphy-i2c", NULL), OF_DEV_AUXDATA("samsung,exynos5250-dw-mshc", EXYNOS5_PA_DWMCI0, "dw_mmc.0", NULL), OF_DEV_AUXDATA("samsung,exynos5250-dw-mshc", EXYNOS5_PA_DWMCI1, -- cgit v1.2.3-59-g8ed1b From 62a1f83f0cbbc9c2e2776b4ad91ccb2f500b6a0b Mon Sep 17 00:00:00 2001 From: Rahul Sharma Date: Mon, 29 Oct 2012 21:51:47 +0900 Subject: ARM: dts: add device tree support for exynos5 hdmiddc This patch adds support for device tree based discovery for exynos5 hdmi ddc. Signed-off-by: Rahul Sharma Signed-off-by: Kukjin Kim --- Documentation/devicetree/bindings/drm/exynos/hdmiddc.txt | 12 ++++++++++++ arch/arm/boot/dts/exynos5250-smdk5250.dts | 10 +++++++++- arch/arm/mach-exynos/mach-exynos5-dt.c | 2 ++ 3 files changed, 23 insertions(+), 1 deletion(-) create mode 100644 Documentation/devicetree/bindings/drm/exynos/hdmiddc.txt (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/drm/exynos/hdmiddc.txt b/Documentation/devicetree/bindings/drm/exynos/hdmiddc.txt new file mode 100644 index 000000000000..fa166d945809 --- /dev/null +++ b/Documentation/devicetree/bindings/drm/exynos/hdmiddc.txt @@ -0,0 +1,12 @@ +Device-Tree bindings for hdmiddc driver + +Required properties: +- compatible: value should be "samsung,exynos5-hdmiddc". +- reg: I2C address of the hdmiddc device. + +Example: + + hdmiddc { + compatible = "samsung,exynos5-hdmiddc"; + reg = <0x50>; + }; diff --git a/arch/arm/boot/dts/exynos5250-smdk5250.dts b/arch/arm/boot/dts/exynos5250-smdk5250.dts index 7dc196f6b0a1..7212d538086d 100644 --- a/arch/arm/boot/dts/exynos5250-smdk5250.dts +++ b/arch/arm/boot/dts/exynos5250-smdk5250.dts @@ -56,7 +56,15 @@ }; i2c@12C80000 { - status = "disabled"; + samsung,i2c-sda-delay = <100>; + samsung,i2c-max-bus-freq = <66000>; + gpios = <&gpa0 6 3 3 0>, + <&gpa0 7 3 3 0>; + + hdmiddc@50 { + compatible = "samsung,exynos5-hdmiddc"; + reg = <0x50>; + }; }; i2c@12C90000 { diff --git a/arch/arm/mach-exynos/mach-exynos5-dt.c b/arch/arm/mach-exynos/mach-exynos5-dt.c index 0f20ba8fcf1b..db035374087e 100644 --- a/arch/arm/mach-exynos/mach-exynos5-dt.c +++ b/arch/arm/mach-exynos/mach-exynos5-dt.c @@ -47,6 +47,8 @@ static const struct of_dev_auxdata exynos5250_auxdata_lookup[] __initconst = { "s3c2440-i2c.0", NULL), OF_DEV_AUXDATA("samsung,s3c2440-i2c", EXYNOS5_PA_IIC(1), "s3c2440-i2c.1", NULL), + OF_DEV_AUXDATA("samsung,s3c2440-i2c", EXYNOS5_PA_IIC(2), + "s3c2440-i2c.2", NULL), OF_DEV_AUXDATA("samsung,s3c2440-hdmiphy-i2c", EXYNOS5_PA_IIC(8), "s3c2440-hdmiphy-i2c", NULL), OF_DEV_AUXDATA("samsung,exynos5250-dw-mshc", EXYNOS5_PA_DWMCI0, -- cgit v1.2.3-59-g8ed1b From 1c48ddbaa61aa017fefacac1bce233227933d18e Mon Sep 17 00:00:00 2001 From: AnilKumar Ch Date: Fri, 19 Oct 2012 12:28:56 +0530 Subject: Documentation: dt: i2c: Update trivial-devices list Update i2c trivial-devices list by adding the description for ti,tmp275 temperature sensor and taos,tsl2550 ambient light sensor. Signed-off-by: AnilKumar Ch Signed-off-by: Benoit Cousson --- Documentation/devicetree/bindings/i2c/trivial-devices.txt | 2 ++ 1 file changed, 2 insertions(+) (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/i2c/trivial-devices.txt b/Documentation/devicetree/bindings/i2c/trivial-devices.txt index 2f5322b119eb..446859fcdca4 100644 --- a/Documentation/devicetree/bindings/i2c/trivial-devices.txt +++ b/Documentation/devicetree/bindings/i2c/trivial-devices.txt @@ -55,5 +55,7 @@ st-micro,24c256 i2c serial eeprom (24cxx) stm,m41t00 Serial Access TIMEKEEPER stm,m41t62 Serial real-time clock (RTC) with alarm stm,m41t80 M41T80 - SERIAL ACCESS RTC WITH ALARMS +taos,tsl2550 Ambient Light Sensor with SMBUS/Two Wire Serial Interface ti,tsc2003 I2C Touch-Screen Controller ti,tmp102 Low Power Digital Temperature Sensor with SMBUS/Two Wire Serial Interface +ti,tmp275 Digital Temperature Sensor -- cgit v1.2.3-59-g8ed1b From 3ce0a99cd4c23762441f0efb18c0e0ef1e9d8bfc Mon Sep 17 00:00:00 2001 From: Kishon Vijay Abraham I Date: Wed, 19 Sep 2012 16:02:51 +0530 Subject: ARM: dts: OMAP4: add *reg* property for ocp2scp *reg* property for ocp2scp was previously obtained from ti,hwmods property. But that is now explicitly added to the dt node. Also updated the documentation with *reg* info. Signed-off-by: Kishon Vijay Abraham I Signed-off-by: Benoit Cousson --- Documentation/devicetree/bindings/bus/omap-ocp2scp.txt | 18 ++++++++++++++++++ arch/arm/boot/dts/omap4.dtsi | 3 ++- 2 files changed, 20 insertions(+), 1 deletion(-) (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/bus/omap-ocp2scp.txt b/Documentation/devicetree/bindings/bus/omap-ocp2scp.txt index d2fe064a828b..63dd8051521c 100644 --- a/Documentation/devicetree/bindings/bus/omap-ocp2scp.txt +++ b/Documentation/devicetree/bindings/bus/omap-ocp2scp.txt @@ -2,9 +2,27 @@ properties: - compatible : Should be "ti,omap-ocp2scp" +- reg : Address and length of the register set for the device - #address-cells, #size-cells : Must be present if the device has sub-nodes - ranges : the child address space are mapped 1:1 onto the parent address space - ti,hwmods : must be "ocp2scp_usb_phy" Sub-nodes: All the devices connected to ocp2scp are described using sub-node to ocp2scp + +ocp2scp@4a0ad000 { + compatible = "ti,omap-ocp2scp"; + reg = <0x4a0ad000 0x1f>; + #address-cells = <1>; + #size-cells = <1>; + ranges; + ti,hwmods = "ocp2scp_usb_phy"; + + subnode1 { + ... + }; + + subnode2 { + ... + }; +}; diff --git a/arch/arm/boot/dts/omap4.dtsi b/arch/arm/boot/dts/omap4.dtsi index 3883f94fdbd0..812461efdea6 100644 --- a/arch/arm/boot/dts/omap4.dtsi +++ b/arch/arm/boot/dts/omap4.dtsi @@ -431,8 +431,9 @@ hw-caps-temp-alert; }; - ocp2scp { + ocp2scp@4a0ad000 { compatible = "ti,omap-ocp2scp"; + reg = <0x4a0ad000 0x1f>; #address-cells = <1>; #size-cells = <1>; ranges; -- cgit v1.2.3-59-g8ed1b From fab8ad0b2b5f2b6d25c6020a61bf3339e53fec61 Mon Sep 17 00:00:00 2001 From: Jon Hunter Date: Fri, 19 Oct 2012 09:59:00 -0500 Subject: ARM: dts: OMAP: Add timer nodes Add the 12 GP timers nodes present in OMAP2. Add the 12 GP timers nodes present in OMAP3. Add the 11 GP timers nodes present in OMAP4. Add the 7 GP timers nodes present in AM33xx. Add documentation for timer properties specific to OMAP. Thanks to Vaibhav Hiremath for creating the AM33xx timer nodes. I have modified Vaibhav's original nodes adding information on which timers support a PWM output. V5 changes: - Updated timer register sizes for OMAP2/3/4. - Modified AM335x timer register size to be 1KB instead of 4KB to align with HWMOD. Signed-off-by: Jon Hunter Acked-Reviewed-&-Tested-By: Vaibhav Hiremath Signed-off-by: Benoit Cousson --- .../devicetree/bindings/arm/omap/timer.txt | 31 +++++++ arch/arm/boot/dts/am33xx.dtsi | 54 ++++++++++++ arch/arm/boot/dts/omap2.dtsi | 85 +++++++++++++++++++ arch/arm/boot/dts/omap2420.dtsi | 8 ++ arch/arm/boot/dts/omap2430.dtsi | 8 ++ arch/arm/boot/dts/omap3.dtsi | 95 ++++++++++++++++++++++ arch/arm/boot/dts/omap4.dtsi | 86 ++++++++++++++++++++ 7 files changed, 367 insertions(+) create mode 100644 Documentation/devicetree/bindings/arm/omap/timer.txt (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/arm/omap/timer.txt b/Documentation/devicetree/bindings/arm/omap/timer.txt new file mode 100644 index 000000000000..8732d4d41f8b --- /dev/null +++ b/Documentation/devicetree/bindings/arm/omap/timer.txt @@ -0,0 +1,31 @@ +OMAP Timer bindings + +Required properties: +- compatible: Must be "ti,omap2-timer" for OMAP2+ controllers. +- reg: Contains timer register address range (base address and + length). +- interrupts: Contains the interrupt information for the timer. The + format is being dependent on which interrupt controller + the OMAP device uses. +- ti,hwmods: Name of the hwmod associated to the timer, "timer", + where is the instance number of the timer from the + HW spec. + +Optional properties: +- ti,timer-alwon: Indicates the timer is in an alway-on power domain. +- ti,timer-dsp: Indicates the timer can interrupt the on-chip DSP in + addition to the ARM CPU. +- ti,timer-pwm: Indicates the timer can generate a PWM output. +- ti,timer-secure: Indicates the timer is reserved on a secure OMAP device + and therefore cannot be used by the kernel. + +Example: + +timer12: timer@48304000 { + compatible = "ti,omap2-timer"; + reg = <0x48304000 0x400>; + interrupts = <95>; + ti,hwmods = "timer12" + ti,timer-alwon; + ti,timer-secure; +}; diff --git a/arch/arm/boot/dts/am33xx.dtsi b/arch/arm/boot/dts/am33xx.dtsi index 470926946ed6..70d24b848c11 100644 --- a/arch/arm/boot/dts/am33xx.dtsi +++ b/arch/arm/boot/dts/am33xx.dtsi @@ -237,5 +237,59 @@ interrupts = <55>; status = "disabled"; }; + + timer1: timer@44e31000 { + compatible = "ti,omap2-timer"; + reg = <0x44e31000 0x400>; + interrupts = <67>; + ti,hwmods = "timer1"; + ti,timer-alwon; + }; + + timer2: timer@48040000 { + compatible = "ti,omap2-timer"; + reg = <0x48040000 0x400>; + interrupts = <68>; + ti,hwmods = "timer2"; + }; + + timer3: timer@48042000 { + compatible = "ti,omap2-timer"; + reg = <0x48042000 0x400>; + interrupts = <69>; + ti,hwmods = "timer3"; + }; + + timer4: timer@48044000 { + compatible = "ti,omap2-timer"; + reg = <0x48044000 0x400>; + interrupts = <92>; + ti,hwmods = "timer4"; + ti,timer-pwm; + }; + + timer5: timer@48046000 { + compatible = "ti,omap2-timer"; + reg = <0x48046000 0x400>; + interrupts = <93>; + ti,hwmods = "timer5"; + ti,timer-pwm; + }; + + timer6: timer@48048000 { + compatible = "ti,omap2-timer"; + reg = <0x48048000 0x400>; + interrupts = <94>; + ti,hwmods = "timer6"; + ti,timer-pwm; + }; + + timer7: timer@4804a000 { + compatible = "ti,omap2-timer"; + reg = <0x4804a000 0x400>; + interrupts = <95>; + ti,hwmods = "timer7"; + ti,timer-pwm; + }; }; }; diff --git a/arch/arm/boot/dts/omap2.dtsi b/arch/arm/boot/dts/omap2.dtsi index f366482216c0..761c4b69b25b 100644 --- a/arch/arm/boot/dts/omap2.dtsi +++ b/arch/arm/boot/dts/omap2.dtsi @@ -66,5 +66,90 @@ ti,hwmods = "uart3"; clock-frequency = <48000000>; }; + + timer2: timer@4802a000 { + compatible = "ti,omap2-timer"; + reg = <0x4802a000 0x400>; + interrupts = <38>; + ti,hwmods = "timer2"; + }; + + timer3: timer@48078000 { + compatible = "ti,omap2-timer"; + reg = <0x48078000 0x400>; + interrupts = <39>; + ti,hwmods = "timer3"; + }; + + timer4: timer@4807a000 { + compatible = "ti,omap2-timer"; + reg = <0x4807a000 0x400>; + interrupts = <40>; + ti,hwmods = "timer4"; + }; + + timer5: timer@4807c000 { + compatible = "ti,omap2-timer"; + reg = <0x4807c000 0x400>; + interrupts = <41>; + ti,hwmods = "timer5"; + ti,timer-dsp; + }; + + timer6: timer@4807e000 { + compatible = "ti,omap2-timer"; + reg = <0x4807e000 0x400>; + interrupts = <42>; + ti,hwmods = "timer6"; + ti,timer-dsp; + }; + + timer7: timer@48080000 { + compatible = "ti,omap2-timer"; + reg = <0x48080000 0x400>; + interrupts = <43>; + ti,hwmods = "timer7"; + ti,timer-dsp; + }; + + timer8: timer@48082000 { + compatible = "ti,omap2-timer"; + reg = <0x48082000 0x400>; + interrupts = <44>; + ti,hwmods = "timer8"; + ti,timer-dsp; + }; + + timer9: timer@48084000 { + compatible = "ti,omap2-timer"; + reg = <0x48084000 0x400>; + interrupts = <45>; + ti,hwmods = "timer9"; + ti,timer-pwm; + }; + + timer10: timer@48086000 { + compatible = "ti,omap2-timer"; + reg = <0x48086000 0x400>; + interrupts = <46>; + ti,hwmods = "timer10"; + ti,timer-pwm; + }; + + timer11: timer@48088000 { + compatible = "ti,omap2-timer"; + reg = <0x48088000 0x400>; + interrupts = <47>; + ti,hwmods = "timer11"; + ti,timer-pwm; + }; + + timer12: timer@4808a000 { + compatible = "ti,omap2-timer"; + reg = <0x4808a000 0x400>; + interrupts = <48>; + ti,hwmods = "timer12"; + ti,timer-pwm; + }; }; }; diff --git a/arch/arm/boot/dts/omap2420.dtsi b/arch/arm/boot/dts/omap2420.dtsi index 4d5ce91416c3..af5ee262d0cf 100644 --- a/arch/arm/boot/dts/omap2420.dtsi +++ b/arch/arm/boot/dts/omap2420.dtsi @@ -42,5 +42,13 @@ interrupt-names = "tx", "rx"; ti,hwmods = "mcbsp2"; }; + + timer1: timer@48028000 { + compatible = "ti,omap2-timer"; + reg = <0x48028000 0x400>; + interrupts = <37>; + ti,hwmods = "timer1"; + ti,timer-alwon; + }; }; }; diff --git a/arch/arm/boot/dts/omap2430.dtsi b/arch/arm/boot/dts/omap2430.dtsi index fa845324b642..688729840f14 100644 --- a/arch/arm/boot/dts/omap2430.dtsi +++ b/arch/arm/boot/dts/omap2430.dtsi @@ -83,5 +83,13 @@ ti,buffer-size = <128>; ti,hwmods = "mcbsp5"; }; + + timer1: timer@49018000 { + compatible = "ti,omap2-timer"; + reg = <0x49018000 0x400>; + interrupts = <37>; + ti,hwmods = "timer1"; + ti,timer-alwon; + }; }; }; diff --git a/arch/arm/boot/dts/omap3.dtsi b/arch/arm/boot/dts/omap3.dtsi index fa155418eaad..af9b1822ff84 100644 --- a/arch/arm/boot/dts/omap3.dtsi +++ b/arch/arm/boot/dts/omap3.dtsi @@ -296,5 +296,100 @@ ti,buffer-size = <128>; ti,hwmods = "mcbsp5"; }; + + timer1: timer@48318000 { + compatible = "ti,omap2-timer"; + reg = <0x48318000 0x400>; + interrupts = <37>; + ti,hwmods = "timer1"; + ti,timer-alwon; + }; + + timer2: timer@49032000 { + compatible = "ti,omap2-timer"; + reg = <0x49032000 0x400>; + interrupts = <38>; + ti,hwmods = "timer2"; + }; + + timer3: timer@49034000 { + compatible = "ti,omap2-timer"; + reg = <0x49034000 0x400>; + interrupts = <39>; + ti,hwmods = "timer3"; + }; + + timer4: timer@49036000 { + compatible = "ti,omap2-timer"; + reg = <0x49036000 0x400>; + interrupts = <40>; + ti,hwmods = "timer4"; + }; + + timer5: timer@49038000 { + compatible = "ti,omap2-timer"; + reg = <0x49038000 0x400>; + interrupts = <41>; + ti,hwmods = "timer5"; + ti,timer-dsp; + }; + + timer6: timer@4903a000 { + compatible = "ti,omap2-timer"; + reg = <0x4903a000 0x400>; + interrupts = <42>; + ti,hwmods = "timer6"; + ti,timer-dsp; + }; + + timer7: timer@4903c000 { + compatible = "ti,omap2-timer"; + reg = <0x4903c000 0x400>; + interrupts = <43>; + ti,hwmods = "timer7"; + ti,timer-dsp; + }; + + timer8: timer@4903e000 { + compatible = "ti,omap2-timer"; + reg = <0x4903e000 0x400>; + interrupts = <44>; + ti,hwmods = "timer8"; + ti,timer-pwm; + ti,timer-dsp; + }; + + timer9: timer@49040000 { + compatible = "ti,omap2-timer"; + reg = <0x49040000 0x400>; + interrupts = <45>; + ti,hwmods = "timer9"; + ti,timer-pwm; + }; + + timer10: timer@48086000 { + compatible = "ti,omap2-timer"; + reg = <0x48086000 0x400>; + interrupts = <46>; + ti,hwmods = "timer10"; + ti,timer-pwm; + }; + + timer11: timer@48088000 { + compatible = "ti,omap2-timer"; + reg = <0x48088000 0x400>; + interrupts = <47>; + ti,hwmods = "timer11"; + ti,timer-pwm; + }; + + timer12: timer@48304000 { + compatible = "ti,omap2-timer"; + reg = <0x48304000 0x400>; + interrupts = <95>; + ti,hwmods = "timer12"; + ti,timer-alwon; + ti,timer-secure; + }; }; }; diff --git a/arch/arm/boot/dts/omap4.dtsi b/arch/arm/boot/dts/omap4.dtsi index 2ab6e68ccbf7..d3a82e0c3804 100644 --- a/arch/arm/boot/dts/omap4.dtsi +++ b/arch/arm/boot/dts/omap4.dtsi @@ -433,5 +433,91 @@ ranges; ti,hwmods = "ocp2scp_usb_phy"; }; + + timer1: timer@4a318000 { + compatible = "ti,omap2-timer"; + reg = <0x4a318000 0x80>; + interrupts = <0 37 0x4>; + ti,hwmods = "timer1"; + ti,timer-alwon; + }; + + timer2: timer@48032000 { + compatible = "ti,omap2-timer"; + reg = <0x48032000 0x80>; + interrupts = <0 38 0x4>; + ti,hwmods = "timer2"; + }; + + timer3: timer@48034000 { + compatible = "ti,omap2-timer"; + reg = <0x48034000 0x80>; + interrupts = <0 39 0x4>; + ti,hwmods = "timer3"; + }; + + timer4: timer@48036000 { + compatible = "ti,omap2-timer"; + reg = <0x48036000 0x80>; + interrupts = <0 40 0x4>; + ti,hwmods = "timer4"; + }; + + timer5: timer@49038000 { + compatible = "ti,omap2-timer"; + reg = <0x49038000 0x80>; + interrupts = <0 41 0x4>; + ti,hwmods = "timer5"; + ti,timer-dsp; + }; + + timer6: timer@4903a000 { + compatible = "ti,omap2-timer"; + reg = <0x4903a000 0x80>; + interrupts = <0 42 0x4>; + ti,hwmods = "timer6"; + ti,timer-dsp; + }; + + timer7: timer@4903c000 { + compatible = "ti,omap2-timer"; + reg = <0x4903c000 0x80>; + interrupts = <0 43 0x4>; + ti,hwmods = "timer7"; + ti,timer-dsp; + }; + + timer8: timer@4903e000 { + compatible = "ti,omap2-timer"; + reg = <0x4903e000 0x80>; + interrupts = <0 44 0x4>; + ti,hwmods = "timer8"; + ti,timer-pwm; + ti,timer-dsp; + }; + + timer9: timer@4803e000 { + compatible = "ti,omap2-timer"; + reg = <0x4803e000 0x80>; + interrupts = <0 45 0x4>; + ti,hwmods = "timer9"; + ti,timer-pwm; + }; + + timer10: timer@48086000 { + compatible = "ti,omap2-timer"; + reg = <0x48086000 0x80>; + interrupts = <0 46 0x4>; + ti,hwmods = "timer10"; + ti,timer-pwm; + }; + + timer11: timer@48088000 { + compatible = "ti,omap2-timer"; + reg = <0x48088000 0x80>; + interrupts = <0 47 0x4>; + ti,hwmods = "timer11"; + ti,timer-pwm; + }; }; }; -- cgit v1.2.3-59-g8ed1b From 510c0ffdd408ced2654f073d0397f0fec410a235 Mon Sep 17 00:00:00 2001 From: Jon Hunter Date: Thu, 25 Oct 2012 14:24:14 -0500 Subject: ARM: dts: OMAP: Add counter-32k nodes Adds the counter-32k timers nodes present in OMAP2/3/4 devices and device-tree binding documentation for OMAP counter-32k. Signed-off-by: Jon Hunter Signed-off-by: Benoit Cousson --- Documentation/devicetree/bindings/arm/omap/counter.txt | 15 +++++++++++++++ arch/arm/boot/dts/omap2420.dtsi | 6 ++++++ arch/arm/boot/dts/omap2430.dtsi | 6 ++++++ arch/arm/boot/dts/omap3.dtsi | 6 ++++++ arch/arm/boot/dts/omap4.dtsi | 6 ++++++ 5 files changed, 39 insertions(+) create mode 100644 Documentation/devicetree/bindings/arm/omap/counter.txt (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/arm/omap/counter.txt b/Documentation/devicetree/bindings/arm/omap/counter.txt new file mode 100644 index 000000000000..5bd8aa091315 --- /dev/null +++ b/Documentation/devicetree/bindings/arm/omap/counter.txt @@ -0,0 +1,15 @@ +OMAP Counter-32K bindings + +Required properties: +- compatible: Must be "ti,omap-counter32k" for OMAP controllers +- reg: Contains timer register address range (base address and length) +- ti,hwmods: Name of the hwmod associated to the counter, which is typically + "counter_32k" + +Example: + +counter32k: counter@4a304000 { + compatible = "ti,omap-counter32k"; + reg = <0x4a304000 0x20>; + ti,hwmods = "counter_32k"; +}; diff --git a/arch/arm/boot/dts/omap2420.dtsi b/arch/arm/boot/dts/omap2420.dtsi index af5ee262d0cf..af6560908905 100644 --- a/arch/arm/boot/dts/omap2420.dtsi +++ b/arch/arm/boot/dts/omap2420.dtsi @@ -14,6 +14,12 @@ compatible = "ti,omap2420", "ti,omap2"; ocp { + counter32k: counter@48004000 { + compatible = "ti,omap-counter32k"; + reg = <0x48004000 0x20>; + ti,hwmods = "counter_32k"; + }; + omap2420_pmx: pinmux@48000030 { compatible = "ti,omap2420-padconf", "pinctrl-single"; reg = <0x48000030 0x0113>; diff --git a/arch/arm/boot/dts/omap2430.dtsi b/arch/arm/boot/dts/omap2430.dtsi index 688729840f14..c3924457c9b6 100644 --- a/arch/arm/boot/dts/omap2430.dtsi +++ b/arch/arm/boot/dts/omap2430.dtsi @@ -14,6 +14,12 @@ compatible = "ti,omap2430", "ti,omap2"; ocp { + counter32k: counter@49020000 { + compatible = "ti,omap-counter32k"; + reg = <0x49020000 0x20>; + ti,hwmods = "counter_32k"; + }; + omap2430_pmx: pinmux@49002030 { compatible = "ti,omap2430-padconf", "pinctrl-single"; reg = <0x49002030 0x0154>; diff --git a/arch/arm/boot/dts/omap3.dtsi b/arch/arm/boot/dts/omap3.dtsi index af9b1822ff84..1acc26148ffc 100644 --- a/arch/arm/boot/dts/omap3.dtsi +++ b/arch/arm/boot/dts/omap3.dtsi @@ -61,6 +61,12 @@ ranges; ti,hwmods = "l3_main"; + counter32k: counter@48320000 { + compatible = "ti,omap-counter32k"; + reg = <0x48320000 0x20>; + ti,hwmods = "counter_32k"; + }; + intc: interrupt-controller@48200000 { compatible = "ti,omap2-intc"; interrupt-controller; diff --git a/arch/arm/boot/dts/omap4.dtsi b/arch/arm/boot/dts/omap4.dtsi index d3a82e0c3804..23ee1498c98c 100644 --- a/arch/arm/boot/dts/omap4.dtsi +++ b/arch/arm/boot/dts/omap4.dtsi @@ -95,6 +95,12 @@ ranges; ti,hwmods = "l3_main_1", "l3_main_2", "l3_main_3"; + counter32k: counter@4a304000 { + compatible = "ti,omap-counter32k"; + reg = <0x4a304000 0x20>; + ti,hwmods = "counter_32k"; + }; + omap4_pmx_core: pinmux@4a100040 { compatible = "ti,omap4-padconf", "pinctrl-single"; reg = <0x4a100040 0x0196>; -- cgit v1.2.3-59-g8ed1b From d034fbf08b6fe86271d0d0bd332edabeb5749fd6 Mon Sep 17 00:00:00 2001 From: Andreas Herrmann Date: Mon, 29 Oct 2012 18:50:47 +0100 Subject: hwmon, fam15h_power: Change email address, MAINTAINERS entry Signed-off-by: Andreas Herrmann Signed-off-by: Guenter Roeck --- Documentation/hwmon/fam15h_power | 2 +- MAINTAINERS | 2 +- drivers/hwmon/fam15h_power.c | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) (limited to 'Documentation') diff --git a/Documentation/hwmon/fam15h_power b/Documentation/hwmon/fam15h_power index a92918e0bd69..80654813d04a 100644 --- a/Documentation/hwmon/fam15h_power +++ b/Documentation/hwmon/fam15h_power @@ -10,7 +10,7 @@ Supported chips: BIOS and Kernel Developer's Guide (BKDG) For AMD Family 15h Processors (not yet published) -Author: Andreas Herrmann +Author: Andreas Herrmann Description ----------- diff --git a/MAINTAINERS b/MAINTAINERS index 1fa907441f8f..a4e81367a3a1 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -503,7 +503,7 @@ F: include/linux/altera_uart.h F: include/linux/altera_jtaguart.h AMD FAM15H PROCESSOR POWER MONITORING DRIVER -M: Andreas Herrmann +M: Andreas Herrmann L: lm-sensors@lm-sensors.org S: Maintained F: Documentation/hwmon/fam15h_power diff --git a/drivers/hwmon/fam15h_power.c b/drivers/hwmon/fam15h_power.c index 68ad7d255512..4f4110407387 100644 --- a/drivers/hwmon/fam15h_power.c +++ b/drivers/hwmon/fam15h_power.c @@ -2,7 +2,7 @@ * fam15h_power.c - AMD Family 15h processor power monitoring * * Copyright (c) 2011 Advanced Micro Devices, Inc. - * Author: Andreas Herrmann + * Author: Andreas Herrmann * * * This driver is free software; you can redistribute it and/or @@ -28,7 +28,7 @@ #include MODULE_DESCRIPTION("AMD Family 15h CPU processor power monitor"); -MODULE_AUTHOR("Andreas Herrmann "); +MODULE_AUTHOR("Andreas Herrmann "); MODULE_LICENSE("GPL"); /* D18F3 */ -- cgit v1.2.3-59-g8ed1b From 686de182a217d315e50fef74ba5e7bcd7ea6c246 Mon Sep 17 00:00:00 2001 From: Alexander Graf Date: Sun, 7 Oct 2012 15:22:59 +0200 Subject: KVM: Documentation: Fix reentry-to-be-consistent paragraph All user space offloaded instruction emulation needs to reenter kvm to produce consistent state again. Fix the section in the documentation to mention all of them. Signed-off-by: Alexander Graf --- Documentation/virtual/kvm/api.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'Documentation') diff --git a/Documentation/virtual/kvm/api.txt b/Documentation/virtual/kvm/api.txt index 4258180b1ecd..6671fdc0afb1 100644 --- a/Documentation/virtual/kvm/api.txt +++ b/Documentation/virtual/kvm/api.txt @@ -2183,7 +2183,8 @@ executed a memory-mapped I/O instruction which could not be satisfied by kvm. The 'data' member contains the written data if 'is_write' is true, and should be filled by application code otherwise. -NOTE: For KVM_EXIT_IO, KVM_EXIT_MMIO and KVM_EXIT_OSI, the corresponding +NOTE: For KVM_EXIT_IO, KVM_EXIT_MMIO, KVM_EXIT_OSI, KVM_EXIT_DCR + and KVM_EXIT_PAPR the corresponding operations are complete (and guest state is consistent) only after userspace has re-entered the kernel with KVM_RUN. The kernel side will first finish incomplete operations and then check for pending signals. Userspace -- cgit v1.2.3-59-g8ed1b From e913ca7d16d70b75367ff56a3b201980501d542c Mon Sep 17 00:00:00 2001 From: Matt Fleming Date: Thu, 4 Oct 2012 09:57:31 +0100 Subject: efivarfs: Add documentation for the EFI variable filesystem Signed-off-by: Matt Fleming --- Documentation/filesystems/00-INDEX | 2 ++ Documentation/filesystems/efivarfs.txt | 16 ++++++++++++++++ 2 files changed, 18 insertions(+) create mode 100644 Documentation/filesystems/efivarfs.txt (limited to 'Documentation') diff --git a/Documentation/filesystems/00-INDEX b/Documentation/filesystems/00-INDEX index 8c624a18f67d..7b52ba7bf32a 100644 --- a/Documentation/filesystems/00-INDEX +++ b/Documentation/filesystems/00-INDEX @@ -38,6 +38,8 @@ dnotify_test.c - example program for dnotify ecryptfs.txt - docs on eCryptfs: stacked cryptographic filesystem for Linux. +efivarfs.txt + - info for the efivarfs filesystem. exofs.txt - info, usage, mount options, design about EXOFS. ext2.txt diff --git a/Documentation/filesystems/efivarfs.txt b/Documentation/filesystems/efivarfs.txt new file mode 100644 index 000000000000..c477af086e65 --- /dev/null +++ b/Documentation/filesystems/efivarfs.txt @@ -0,0 +1,16 @@ + +efivarfs - a (U)EFI variable filesystem + +The efivarfs filesystem was created to address the shortcomings of +using entries in sysfs to maintain EFI variables. The old sysfs EFI +variables code only supported variables of up to 1024 bytes. This +limitation existed in version 0.99 of the EFI specification, but was +removed before any full releases. Since variables can now be larger +than a single page, sysfs isn't the best interface for this. + +Variables can be created, deleted and modified with the efivarfs +filesystem. + +efivarfs is typically mounted like this, + + mount -t efivarfs none /sys/firmware/efi/efivars -- cgit v1.2.3-59-g8ed1b From dc4dc36056392c0b0b1ca9e81bebff964b9297e0 Mon Sep 17 00:00:00 2001 From: Laxman Dewangan Date: Tue, 30 Oct 2012 12:34:05 +0530 Subject: spi: tegra: add spi driver for SLINK controller Tegra20/Tegra30 supports the spi interface through its SLINK controller. Add spi driver for SLINK controller. Signed-off-by: Laxman Dewangan Reviewed-by: Stephen Warren Signed-off-by: Mark Brown --- .../bindings/spi/nvidia,tegra20-slink.txt | 26 + drivers/spi/Kconfig | 6 + drivers/spi/Makefile | 2 +- drivers/spi/spi-tegra20-slink.c | 1359 ++++++++++++++++++++ include/linux/spi/spi-tegra.h | 40 + 5 files changed, 1432 insertions(+), 1 deletion(-) create mode 100644 Documentation/devicetree/bindings/spi/nvidia,tegra20-slink.txt create mode 100644 drivers/spi/spi-tegra20-slink.c create mode 100644 include/linux/spi/spi-tegra.h (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/spi/nvidia,tegra20-slink.txt b/Documentation/devicetree/bindings/spi/nvidia,tegra20-slink.txt new file mode 100644 index 000000000000..f5b1ad1a1ec3 --- /dev/null +++ b/Documentation/devicetree/bindings/spi/nvidia,tegra20-slink.txt @@ -0,0 +1,26 @@ +NVIDIA Tegra20/Tegra30 SLINK controller. + +Required properties: +- compatible : should be "nvidia,tegra20-slink", "nvidia,tegra30-slink". +- reg: Should contain SLINK registers location and length. +- interrupts: Should contain SLINK interrupts. +- nvidia,dma-request-selector : The Tegra DMA controller's phandle and + request selector for this SLINK controller. + +Recommended properties: +- spi-max-frequency: Definition as per + Documentation/devicetree/bindings/spi/spi-bus.txt + +Example: + +slink@7000d600 { + compatible = "nvidia,tegra20-slink"; + reg = <0x7000d600 0x200>; + interrupts = <0 82 0x04>; + nvidia,dma-request-selector = <&apbdma 16>; + spi-max-frequency = <25000000>; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; +}; + diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index 1acae359cabe..25290d9780b2 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -385,6 +385,12 @@ config SPI_MXS help SPI driver for Freescale MXS devices. +config SPI_TEGRA20_SLINK + tristate "Nvidia Tegra20/Tegra30 SLINK Controller" + depends on ARCH_TEGRA && TEGRA20_APB_DMA + help + SPI driver for Nvidia Tegra20/Tegra30 SLINK Controller interface. + config SPI_TI_SSP tristate "TI Sequencer Serial Port - SPI Support" depends on MFD_TI_SSP diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index c48df47e4b0f..f87c0f142e5a 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -60,10 +60,10 @@ obj-$(CONFIG_SPI_SH_MSIOF) += spi-sh-msiof.o obj-$(CONFIG_SPI_SH_SCI) += spi-sh-sci.o obj-$(CONFIG_SPI_SIRF) += spi-sirf.o obj-$(CONFIG_SPI_STMP3XXX) += spi-stmp.o +obj-$(CONFIG_SPI_TEGRA20_SLINK) += spi-tegra20-slink.o obj-$(CONFIG_SPI_TI_SSP) += spi-ti-ssp.o obj-$(CONFIG_SPI_TLE62X0) += spi-tle62x0.o obj-$(CONFIG_SPI_TOPCLIFF_PCH) += spi-topcliff-pch.o obj-$(CONFIG_SPI_TXX9) += spi-txx9.o obj-$(CONFIG_SPI_XCOMM) += spi-xcomm.o obj-$(CONFIG_SPI_XILINX) += spi-xilinx.o - diff --git a/drivers/spi/spi-tegra20-slink.c b/drivers/spi/spi-tegra20-slink.c new file mode 100644 index 000000000000..b8985be81d96 --- /dev/null +++ b/drivers/spi/spi-tegra20-slink.c @@ -0,0 +1,1359 @@ +/* + * SPI driver for Nvidia's Tegra20/Tegra30 SLINK Controller. + * + * Copyright (c) 2012, NVIDIA 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 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 +#include +#include +#include +#include +#include + +#define SLINK_COMMAND 0x000 +#define SLINK_BIT_LENGTH(x) (((x) & 0x1f) << 0) +#define SLINK_WORD_SIZE(x) (((x) & 0x1f) << 5) +#define SLINK_BOTH_EN (1 << 10) +#define SLINK_CS_SW (1 << 11) +#define SLINK_CS_VALUE (1 << 12) +#define SLINK_CS_POLARITY (1 << 13) +#define SLINK_IDLE_SDA_DRIVE_LOW (0 << 16) +#define SLINK_IDLE_SDA_DRIVE_HIGH (1 << 16) +#define SLINK_IDLE_SDA_PULL_LOW (2 << 16) +#define SLINK_IDLE_SDA_PULL_HIGH (3 << 16) +#define SLINK_IDLE_SDA_MASK (3 << 16) +#define SLINK_CS_POLARITY1 (1 << 20) +#define SLINK_CK_SDA (1 << 21) +#define SLINK_CS_POLARITY2 (1 << 22) +#define SLINK_CS_POLARITY3 (1 << 23) +#define SLINK_IDLE_SCLK_DRIVE_LOW (0 << 24) +#define SLINK_IDLE_SCLK_DRIVE_HIGH (1 << 24) +#define SLINK_IDLE_SCLK_PULL_LOW (2 << 24) +#define SLINK_IDLE_SCLK_PULL_HIGH (3 << 24) +#define SLINK_IDLE_SCLK_MASK (3 << 24) +#define SLINK_M_S (1 << 28) +#define SLINK_WAIT (1 << 29) +#define SLINK_GO (1 << 30) +#define SLINK_ENB (1 << 31) + +#define SLINK_MODES (SLINK_IDLE_SCLK_MASK | SLINK_CK_SDA) + +#define SLINK_COMMAND2 0x004 +#define SLINK_LSBFE (1 << 0) +#define SLINK_SSOE (1 << 1) +#define SLINK_SPIE (1 << 4) +#define SLINK_BIDIROE (1 << 6) +#define SLINK_MODFEN (1 << 7) +#define SLINK_INT_SIZE(x) (((x) & 0x1f) << 8) +#define SLINK_CS_ACTIVE_BETWEEN (1 << 17) +#define SLINK_SS_EN_CS(x) (((x) & 0x3) << 18) +#define SLINK_SS_SETUP(x) (((x) & 0x3) << 20) +#define SLINK_FIFO_REFILLS_0 (0 << 22) +#define SLINK_FIFO_REFILLS_1 (1 << 22) +#define SLINK_FIFO_REFILLS_2 (2 << 22) +#define SLINK_FIFO_REFILLS_3 (3 << 22) +#define SLINK_FIFO_REFILLS_MASK (3 << 22) +#define SLINK_WAIT_PACK_INT(x) (((x) & 0x7) << 26) +#define SLINK_SPC0 (1 << 29) +#define SLINK_TXEN (1 << 30) +#define SLINK_RXEN (1 << 31) + +#define SLINK_STATUS 0x008 +#define SLINK_COUNT(val) (((val) >> 0) & 0x1f) +#define SLINK_WORD(val) (((val) >> 5) & 0x1f) +#define SLINK_BLK_CNT(val) (((val) >> 0) & 0xffff) +#define SLINK_MODF (1 << 16) +#define SLINK_RX_UNF (1 << 18) +#define SLINK_TX_OVF (1 << 19) +#define SLINK_TX_FULL (1 << 20) +#define SLINK_TX_EMPTY (1 << 21) +#define SLINK_RX_FULL (1 << 22) +#define SLINK_RX_EMPTY (1 << 23) +#define SLINK_TX_UNF (1 << 24) +#define SLINK_RX_OVF (1 << 25) +#define SLINK_TX_FLUSH (1 << 26) +#define SLINK_RX_FLUSH (1 << 27) +#define SLINK_SCLK (1 << 28) +#define SLINK_ERR (1 << 29) +#define SLINK_RDY (1 << 30) +#define SLINK_BSY (1 << 31) +#define SLINK_FIFO_ERROR (SLINK_TX_OVF | SLINK_RX_UNF | \ + SLINK_TX_UNF | SLINK_RX_OVF) + +#define SLINK_FIFO_EMPTY (SLINK_TX_EMPTY | SLINK_RX_EMPTY) + +#define SLINK_MAS_DATA 0x010 +#define SLINK_SLAVE_DATA 0x014 + +#define SLINK_DMA_CTL 0x018 +#define SLINK_DMA_BLOCK_SIZE(x) (((x) & 0xffff) << 0) +#define SLINK_TX_TRIG_1 (0 << 16) +#define SLINK_TX_TRIG_4 (1 << 16) +#define SLINK_TX_TRIG_8 (2 << 16) +#define SLINK_TX_TRIG_16 (3 << 16) +#define SLINK_TX_TRIG_MASK (3 << 16) +#define SLINK_RX_TRIG_1 (0 << 18) +#define SLINK_RX_TRIG_4 (1 << 18) +#define SLINK_RX_TRIG_8 (2 << 18) +#define SLINK_RX_TRIG_16 (3 << 18) +#define SLINK_RX_TRIG_MASK (3 << 18) +#define SLINK_PACKED (1 << 20) +#define SLINK_PACK_SIZE_4 (0 << 21) +#define SLINK_PACK_SIZE_8 (1 << 21) +#define SLINK_PACK_SIZE_16 (2 << 21) +#define SLINK_PACK_SIZE_32 (3 << 21) +#define SLINK_PACK_SIZE_MASK (3 << 21) +#define SLINK_IE_TXC (1 << 26) +#define SLINK_IE_RXC (1 << 27) +#define SLINK_DMA_EN (1 << 31) + +#define SLINK_STATUS2 0x01c +#define SLINK_TX_FIFO_EMPTY_COUNT(val) (((val) & 0x3f) >> 0) +#define SLINK_RX_FIFO_FULL_COUNT(val) (((val) & 0x3f0000) >> 16) +#define SLINK_SS_HOLD_TIME(val) (((val) & 0xF) << 6) + +#define SLINK_TX_FIFO 0x100 +#define SLINK_RX_FIFO 0x180 + +#define DATA_DIR_TX (1 << 0) +#define DATA_DIR_RX (1 << 1) + +#define SLINK_DMA_TIMEOUT (msecs_to_jiffies(1000)) + +#define DEFAULT_SPI_DMA_BUF_LEN (16*1024) +#define TX_FIFO_EMPTY_COUNT_MAX SLINK_TX_FIFO_EMPTY_COUNT(0x20) +#define RX_FIFO_FULL_COUNT_ZERO SLINK_RX_FIFO_FULL_COUNT(0) + +#define SLINK_STATUS2_RESET \ + (TX_FIFO_EMPTY_COUNT_MAX | RX_FIFO_FULL_COUNT_ZERO << 16) + +#define MAX_CHIP_SELECT 4 +#define SLINK_FIFO_DEPTH 32 + +struct tegra_slink_chip_data { + bool cs_hold_time; +}; + +struct tegra_slink_data { + struct device *dev; + struct spi_master *master; + const struct tegra_slink_chip_data *chip_data; + spinlock_t lock; + + struct clk *clk; + void __iomem *base; + phys_addr_t phys; + unsigned irq; + int dma_req_sel; + u32 spi_max_frequency; + u32 cur_speed; + + struct spi_device *cur_spi; + unsigned cur_pos; + unsigned cur_len; + unsigned words_per_32bit; + unsigned bytes_per_word; + unsigned curr_dma_words; + unsigned cur_direction; + + unsigned cur_rx_pos; + unsigned cur_tx_pos; + + unsigned dma_buf_size; + unsigned max_buf_size; + bool is_curr_dma_xfer; + bool is_hw_based_cs; + + struct completion rx_dma_complete; + struct completion tx_dma_complete; + + u32 tx_status; + u32 rx_status; + u32 status_reg; + bool is_packed; + unsigned long packed_size; + + u32 command_reg; + u32 command2_reg; + u32 dma_control_reg; + u32 def_command_reg; + u32 def_command2_reg; + + struct completion xfer_completion; + struct spi_transfer *curr_xfer; + struct dma_chan *rx_dma_chan; + u32 *rx_dma_buf; + dma_addr_t rx_dma_phys; + struct dma_async_tx_descriptor *rx_dma_desc; + + struct dma_chan *tx_dma_chan; + u32 *tx_dma_buf; + dma_addr_t tx_dma_phys; + struct dma_async_tx_descriptor *tx_dma_desc; +}; + +static int tegra_slink_runtime_suspend(struct device *dev); +static int tegra_slink_runtime_resume(struct device *dev); + +static inline unsigned long tegra_slink_readl(struct tegra_slink_data *tspi, + unsigned long reg) +{ + return readl(tspi->base + reg); +} + +static inline void tegra_slink_writel(struct tegra_slink_data *tspi, + unsigned long val, unsigned long reg) +{ + writel(val, tspi->base + reg); + + /* Read back register to make sure that register writes completed */ + if (reg != SLINK_TX_FIFO) + readl(tspi->base + SLINK_MAS_DATA); +} + +static void tegra_slink_clear_status(struct tegra_slink_data *tspi) +{ + unsigned long val; + unsigned long val_write = 0; + + val = tegra_slink_readl(tspi, SLINK_STATUS); + + /* Write 1 to clear status register */ + val_write = SLINK_RDY | SLINK_FIFO_ERROR; + tegra_slink_writel(tspi, val_write, SLINK_STATUS); +} + +static unsigned long tegra_slink_get_packed_size(struct tegra_slink_data *tspi, + struct spi_transfer *t) +{ + unsigned long val; + + switch (tspi->bytes_per_word) { + case 0: + val = SLINK_PACK_SIZE_4; + break; + case 1: + val = SLINK_PACK_SIZE_8; + break; + case 2: + val = SLINK_PACK_SIZE_16; + break; + case 4: + val = SLINK_PACK_SIZE_32; + break; + default: + val = 0; + } + return val; +} + +static unsigned tegra_slink_calculate_curr_xfer_param( + struct spi_device *spi, struct tegra_slink_data *tspi, + struct spi_transfer *t) +{ + unsigned remain_len = t->len - tspi->cur_pos; + unsigned max_word; + unsigned bits_per_word ; + unsigned max_len; + unsigned total_fifo_words; + + bits_per_word = t->bits_per_word ? t->bits_per_word : + spi->bits_per_word; + tspi->bytes_per_word = (bits_per_word - 1) / 8 + 1; + + if (bits_per_word == 8 || bits_per_word == 16) { + tspi->is_packed = 1; + tspi->words_per_32bit = 32/bits_per_word; + } else { + tspi->is_packed = 0; + tspi->words_per_32bit = 1; + } + tspi->packed_size = tegra_slink_get_packed_size(tspi, t); + + if (tspi->is_packed) { + max_len = min(remain_len, tspi->max_buf_size); + tspi->curr_dma_words = max_len/tspi->bytes_per_word; + total_fifo_words = max_len/4; + } else { + max_word = (remain_len - 1) / tspi->bytes_per_word + 1; + max_word = min(max_word, tspi->max_buf_size/4); + tspi->curr_dma_words = max_word; + total_fifo_words = max_word; + } + return total_fifo_words; +} + +static unsigned tegra_slink_fill_tx_fifo_from_client_txbuf( + struct tegra_slink_data *tspi, struct spi_transfer *t) +{ + unsigned nbytes; + unsigned tx_empty_count; + unsigned long fifo_status; + unsigned max_n_32bit; + unsigned i, count; + unsigned long x; + unsigned int written_words; + unsigned fifo_words_left; + u8 *tx_buf = (u8 *)t->tx_buf + tspi->cur_tx_pos; + + fifo_status = tegra_slink_readl(tspi, SLINK_STATUS2); + tx_empty_count = SLINK_TX_FIFO_EMPTY_COUNT(fifo_status); + + if (tspi->is_packed) { + fifo_words_left = tx_empty_count * tspi->words_per_32bit; + written_words = min(fifo_words_left, tspi->curr_dma_words); + nbytes = written_words * tspi->bytes_per_word; + max_n_32bit = DIV_ROUND_UP(nbytes, 4); + for (count = 0; count < max_n_32bit; count++) { + x = 0; + for (i = 0; (i < 4) && nbytes; i++, nbytes--) + x |= (*tx_buf++) << (i*8); + tegra_slink_writel(tspi, x, SLINK_TX_FIFO); + } + } else { + max_n_32bit = min(tspi->curr_dma_words, tx_empty_count); + written_words = max_n_32bit; + nbytes = written_words * tspi->bytes_per_word; + for (count = 0; count < max_n_32bit; count++) { + x = 0; + for (i = 0; nbytes && (i < tspi->bytes_per_word); + i++, nbytes--) + x |= ((*tx_buf++) << i*8); + tegra_slink_writel(tspi, x, SLINK_TX_FIFO); + } + } + tspi->cur_tx_pos += written_words * tspi->bytes_per_word; + return written_words; +} + +static unsigned int tegra_slink_read_rx_fifo_to_client_rxbuf( + struct tegra_slink_data *tspi, struct spi_transfer *t) +{ + unsigned rx_full_count; + unsigned long fifo_status; + unsigned i, count; + unsigned long x; + unsigned int read_words = 0; + unsigned len; + u8 *rx_buf = (u8 *)t->rx_buf + tspi->cur_rx_pos; + + fifo_status = tegra_slink_readl(tspi, SLINK_STATUS2); + rx_full_count = SLINK_RX_FIFO_FULL_COUNT(fifo_status); + if (tspi->is_packed) { + len = tspi->curr_dma_words * tspi->bytes_per_word; + for (count = 0; count < rx_full_count; count++) { + x = tegra_slink_readl(tspi, SLINK_RX_FIFO); + for (i = 0; len && (i < 4); i++, len--) + *rx_buf++ = (x >> i*8) & 0xFF; + } + tspi->cur_rx_pos += tspi->curr_dma_words * tspi->bytes_per_word; + read_words += tspi->curr_dma_words; + } else { + unsigned int bits_per_word; + + bits_per_word = t->bits_per_word ? t->bits_per_word : + tspi->cur_spi->bits_per_word; + for (count = 0; count < rx_full_count; count++) { + x = tegra_slink_readl(tspi, SLINK_RX_FIFO); + for (i = 0; (i < tspi->bytes_per_word); i++) + *rx_buf++ = (x >> (i*8)) & 0xFF; + } + tspi->cur_rx_pos += rx_full_count * tspi->bytes_per_word; + read_words += rx_full_count; + } + return read_words; +} + +static void tegra_slink_copy_client_txbuf_to_spi_txbuf( + struct tegra_slink_data *tspi, struct spi_transfer *t) +{ + unsigned len; + + /* Make the dma buffer to read by cpu */ + dma_sync_single_for_cpu(tspi->dev, tspi->tx_dma_phys, + tspi->dma_buf_size, DMA_TO_DEVICE); + + if (tspi->is_packed) { + len = tspi->curr_dma_words * tspi->bytes_per_word; + memcpy(tspi->tx_dma_buf, t->tx_buf + tspi->cur_pos, len); + } else { + unsigned int i; + unsigned int count; + u8 *tx_buf = (u8 *)t->tx_buf + tspi->cur_tx_pos; + unsigned consume = tspi->curr_dma_words * tspi->bytes_per_word; + unsigned int x; + + for (count = 0; count < tspi->curr_dma_words; count++) { + x = 0; + for (i = 0; consume && (i < tspi->bytes_per_word); + i++, consume--) + x |= ((*tx_buf++) << i * 8); + tspi->tx_dma_buf[count] = x; + } + } + tspi->cur_tx_pos += tspi->curr_dma_words * tspi->bytes_per_word; + + /* Make the dma buffer to read by dma */ + dma_sync_single_for_device(tspi->dev, tspi->tx_dma_phys, + tspi->dma_buf_size, DMA_TO_DEVICE); +} + +static void tegra_slink_copy_spi_rxbuf_to_client_rxbuf( + struct tegra_slink_data *tspi, struct spi_transfer *t) +{ + unsigned len; + + /* Make the dma buffer to read by cpu */ + dma_sync_single_for_cpu(tspi->dev, tspi->rx_dma_phys, + tspi->dma_buf_size, DMA_FROM_DEVICE); + + if (tspi->is_packed) { + len = tspi->curr_dma_words * tspi->bytes_per_word; + memcpy(t->rx_buf + tspi->cur_rx_pos, tspi->rx_dma_buf, len); + } else { + unsigned int i; + unsigned int count; + unsigned char *rx_buf = t->rx_buf + tspi->cur_rx_pos; + unsigned int x; + unsigned int rx_mask, bits_per_word; + + bits_per_word = t->bits_per_word ? t->bits_per_word : + tspi->cur_spi->bits_per_word; + rx_mask = (1 << bits_per_word) - 1; + for (count = 0; count < tspi->curr_dma_words; count++) { + x = tspi->rx_dma_buf[count]; + x &= rx_mask; + for (i = 0; (i < tspi->bytes_per_word); i++) + *rx_buf++ = (x >> (i*8)) & 0xFF; + } + } + tspi->cur_rx_pos += tspi->curr_dma_words * tspi->bytes_per_word; + + /* Make the dma buffer to read by dma */ + dma_sync_single_for_device(tspi->dev, tspi->rx_dma_phys, + tspi->dma_buf_size, DMA_FROM_DEVICE); +} + +static void tegra_slink_dma_complete(void *args) +{ + struct completion *dma_complete = args; + + complete(dma_complete); +} + +static int tegra_slink_start_tx_dma(struct tegra_slink_data *tspi, int len) +{ + INIT_COMPLETION(tspi->tx_dma_complete); + tspi->tx_dma_desc = dmaengine_prep_slave_single(tspi->tx_dma_chan, + tspi->tx_dma_phys, len, DMA_MEM_TO_DEV, + DMA_PREP_INTERRUPT | DMA_CTRL_ACK); + if (!tspi->tx_dma_desc) { + dev_err(tspi->dev, "Not able to get desc for Tx\n"); + return -EIO; + } + + tspi->tx_dma_desc->callback = tegra_slink_dma_complete; + tspi->tx_dma_desc->callback_param = &tspi->tx_dma_complete; + + dmaengine_submit(tspi->tx_dma_desc); + dma_async_issue_pending(tspi->tx_dma_chan); + return 0; +} + +static int tegra_slink_start_rx_dma(struct tegra_slink_data *tspi, int len) +{ + INIT_COMPLETION(tspi->rx_dma_complete); + tspi->rx_dma_desc = dmaengine_prep_slave_single(tspi->rx_dma_chan, + tspi->rx_dma_phys, len, DMA_DEV_TO_MEM, + DMA_PREP_INTERRUPT | DMA_CTRL_ACK); + if (!tspi->rx_dma_desc) { + dev_err(tspi->dev, "Not able to get desc for Rx\n"); + return -EIO; + } + + tspi->rx_dma_desc->callback = tegra_slink_dma_complete; + tspi->rx_dma_desc->callback_param = &tspi->rx_dma_complete; + + dmaengine_submit(tspi->rx_dma_desc); + dma_async_issue_pending(tspi->rx_dma_chan); + return 0; +} + +static int tegra_slink_start_dma_based_transfer( + struct tegra_slink_data *tspi, struct spi_transfer *t) +{ + unsigned long val; + unsigned long test_val; + unsigned int len; + int ret = 0; + unsigned long status; + + /* Make sure that Rx and Tx fifo are empty */ + status = tegra_slink_readl(tspi, SLINK_STATUS); + if ((status & SLINK_FIFO_EMPTY) != SLINK_FIFO_EMPTY) { + dev_err(tspi->dev, + "Rx/Tx fifo are not empty status 0x%08lx\n", status); + return -EIO; + } + + val = SLINK_DMA_BLOCK_SIZE(tspi->curr_dma_words - 1); + val |= tspi->packed_size; + if (tspi->is_packed) + len = DIV_ROUND_UP(tspi->curr_dma_words * tspi->bytes_per_word, + 4) * 4; + else + len = tspi->curr_dma_words * 4; + + /* Set attention level based on length of transfer */ + if (len & 0xF) + val |= SLINK_TX_TRIG_1 | SLINK_RX_TRIG_1; + else if (((len) >> 4) & 0x1) + val |= SLINK_TX_TRIG_4 | SLINK_RX_TRIG_4; + else + val |= SLINK_TX_TRIG_8 | SLINK_RX_TRIG_8; + + if (tspi->cur_direction & DATA_DIR_TX) + val |= SLINK_IE_TXC; + + if (tspi->cur_direction & DATA_DIR_RX) + val |= SLINK_IE_RXC; + + tegra_slink_writel(tspi, val, SLINK_DMA_CTL); + tspi->dma_control_reg = val; + + if (tspi->cur_direction & DATA_DIR_TX) { + tegra_slink_copy_client_txbuf_to_spi_txbuf(tspi, t); + wmb(); + ret = tegra_slink_start_tx_dma(tspi, len); + if (ret < 0) { + dev_err(tspi->dev, + "Starting tx dma failed, err %d\n", ret); + return ret; + } + + /* Wait for tx fifo to be fill before starting slink */ + test_val = tegra_slink_readl(tspi, SLINK_STATUS); + while (!(test_val & SLINK_TX_FULL)) + test_val = tegra_slink_readl(tspi, SLINK_STATUS); + } + + if (tspi->cur_direction & DATA_DIR_RX) { + /* Make the dma buffer to read by dma */ + dma_sync_single_for_device(tspi->dev, tspi->rx_dma_phys, + tspi->dma_buf_size, DMA_FROM_DEVICE); + + ret = tegra_slink_start_rx_dma(tspi, len); + if (ret < 0) { + dev_err(tspi->dev, + "Starting rx dma failed, err %d\n", ret); + if (tspi->cur_direction & DATA_DIR_TX) + dmaengine_terminate_all(tspi->tx_dma_chan); + return ret; + } + } + tspi->is_curr_dma_xfer = true; + if (tspi->is_packed) { + val |= SLINK_PACKED; + tegra_slink_writel(tspi, val, SLINK_DMA_CTL); + /* HW need small delay after settign Packed mode */ + udelay(1); + } + tspi->dma_control_reg = val; + + val |= SLINK_DMA_EN; + tegra_slink_writel(tspi, val, SLINK_DMA_CTL); + return ret; +} + +static int tegra_slink_start_cpu_based_transfer( + struct tegra_slink_data *tspi, struct spi_transfer *t) +{ + unsigned long val; + unsigned cur_words; + + val = tspi->packed_size; + if (tspi->cur_direction & DATA_DIR_TX) + val |= SLINK_IE_TXC; + + if (tspi->cur_direction & DATA_DIR_RX) + val |= SLINK_IE_RXC; + + tegra_slink_writel(tspi, val, SLINK_DMA_CTL); + tspi->dma_control_reg = val; + + if (tspi->cur_direction & DATA_DIR_TX) + cur_words = tegra_slink_fill_tx_fifo_from_client_txbuf(tspi, t); + else + cur_words = tspi->curr_dma_words; + val |= SLINK_DMA_BLOCK_SIZE(cur_words - 1); + tegra_slink_writel(tspi, val, SLINK_DMA_CTL); + tspi->dma_control_reg = val; + + tspi->is_curr_dma_xfer = false; + if (tspi->is_packed) { + val |= SLINK_PACKED; + tegra_slink_writel(tspi, val, SLINK_DMA_CTL); + udelay(1); + wmb(); + } + tspi->dma_control_reg = val; + val |= SLINK_DMA_EN; + tegra_slink_writel(tspi, val, SLINK_DMA_CTL); + return 0; +} + +static int tegra_slink_init_dma_param(struct tegra_slink_data *tspi, + bool dma_to_memory) +{ + struct dma_chan *dma_chan; + u32 *dma_buf; + dma_addr_t dma_phys; + int ret; + struct dma_slave_config dma_sconfig; + dma_cap_mask_t mask; + + dma_cap_zero(mask); + dma_cap_set(DMA_SLAVE, mask); + dma_chan = dma_request_channel(mask, NULL, NULL); + if (!dma_chan) { + dev_err(tspi->dev, + "Dma channel is not available, will try later\n"); + return -EPROBE_DEFER; + } + + dma_buf = dma_alloc_coherent(tspi->dev, tspi->dma_buf_size, + &dma_phys, GFP_KERNEL); + if (!dma_buf) { + dev_err(tspi->dev, " Not able to allocate the dma buffer\n"); + dma_release_channel(dma_chan); + return -ENOMEM; + } + + dma_sconfig.slave_id = tspi->dma_req_sel; + if (dma_to_memory) { + dma_sconfig.src_addr = tspi->phys + SLINK_RX_FIFO; + dma_sconfig.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; + dma_sconfig.src_maxburst = 0; + } else { + dma_sconfig.dst_addr = tspi->phys + SLINK_TX_FIFO; + dma_sconfig.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; + dma_sconfig.dst_maxburst = 0; + } + + ret = dmaengine_slave_config(dma_chan, &dma_sconfig); + if (ret) + goto scrub; + if (dma_to_memory) { + tspi->rx_dma_chan = dma_chan; + tspi->rx_dma_buf = dma_buf; + tspi->rx_dma_phys = dma_phys; + } else { + tspi->tx_dma_chan = dma_chan; + tspi->tx_dma_buf = dma_buf; + tspi->tx_dma_phys = dma_phys; + } + return 0; + +scrub: + dma_free_coherent(tspi->dev, tspi->dma_buf_size, dma_buf, dma_phys); + dma_release_channel(dma_chan); + return ret; +} + +static void tegra_slink_deinit_dma_param(struct tegra_slink_data *tspi, + bool dma_to_memory) +{ + u32 *dma_buf; + dma_addr_t dma_phys; + struct dma_chan *dma_chan; + + if (dma_to_memory) { + dma_buf = tspi->rx_dma_buf; + dma_chan = tspi->rx_dma_chan; + dma_phys = tspi->rx_dma_phys; + tspi->rx_dma_chan = NULL; + tspi->rx_dma_buf = NULL; + } else { + dma_buf = tspi->tx_dma_buf; + dma_chan = tspi->tx_dma_chan; + dma_phys = tspi->tx_dma_phys; + tspi->tx_dma_buf = NULL; + tspi->tx_dma_chan = NULL; + } + if (!dma_chan) + return; + + dma_free_coherent(tspi->dev, tspi->dma_buf_size, dma_buf, dma_phys); + dma_release_channel(dma_chan); +} + +static int tegra_slink_start_transfer_one(struct spi_device *spi, + struct spi_transfer *t, bool is_first_of_msg, + bool is_single_xfer) +{ + struct tegra_slink_data *tspi = spi_master_get_devdata(spi->master); + u32 speed; + u8 bits_per_word; + unsigned total_fifo_words; + int ret; + struct tegra_spi_device_controller_data *cdata = spi->controller_data; + unsigned long command; + unsigned long command2; + + bits_per_word = t->bits_per_word ? t->bits_per_word : + spi->bits_per_word; + speed = t->speed_hz ? t->speed_hz : spi->max_speed_hz; + if (!speed) + speed = tspi->spi_max_frequency; + if (speed != tspi->cur_speed) { + clk_set_rate(tspi->clk, speed * 4); + tspi->cur_speed = speed; + } + + tspi->cur_spi = spi; + tspi->cur_pos = 0; + tspi->cur_rx_pos = 0; + tspi->cur_tx_pos = 0; + tspi->curr_xfer = t; + total_fifo_words = tegra_slink_calculate_curr_xfer_param(spi, tspi, t); + + if (is_first_of_msg) { + tegra_slink_clear_status(tspi); + + command = tspi->def_command_reg; + command |= SLINK_BIT_LENGTH(bits_per_word - 1); + + command2 = tspi->def_command2_reg; + command2 |= SLINK_SS_EN_CS(spi->chip_select); + + /* possibly use the hw based chip select */ + tspi->is_hw_based_cs = false; + if (cdata && cdata->is_hw_based_cs && is_single_xfer && + ((tspi->curr_dma_words * tspi->bytes_per_word) == + (t->len - tspi->cur_pos))) { + int setup_count; + int sts2; + + setup_count = cdata->cs_setup_clk_count >> 1; + setup_count = max(setup_count, 3); + command2 |= SLINK_SS_SETUP(setup_count); + if (tspi->chip_data->cs_hold_time) { + int hold_count; + + hold_count = cdata->cs_hold_clk_count; + hold_count = max(hold_count, 0xF); + sts2 = tegra_slink_readl(tspi, SLINK_STATUS2); + sts2 &= ~SLINK_SS_HOLD_TIME(0xF); + sts2 |= SLINK_SS_HOLD_TIME(hold_count); + tegra_slink_writel(tspi, sts2, SLINK_STATUS2); + } + tspi->is_hw_based_cs = true; + } + + if (tspi->is_hw_based_cs) + command &= ~SLINK_CS_SW; + else + command |= SLINK_CS_SW | SLINK_CS_VALUE; + + command &= ~SLINK_MODES; + if (spi->mode & SPI_CPHA) + command |= SLINK_CK_SDA; + + if (spi->mode & SPI_CPOL) + command |= SLINK_IDLE_SCLK_DRIVE_HIGH; + else + command |= SLINK_IDLE_SCLK_DRIVE_LOW; + } else { + command = tspi->command_reg; + command &= ~SLINK_BIT_LENGTH(~0); + command |= SLINK_BIT_LENGTH(bits_per_word - 1); + + command2 = tspi->command2_reg; + command2 &= ~(SLINK_RXEN | SLINK_TXEN); + } + + tegra_slink_writel(tspi, command, SLINK_COMMAND); + tspi->command_reg = command; + + tspi->cur_direction = 0; + if (t->rx_buf) { + command2 |= SLINK_RXEN; + tspi->cur_direction |= DATA_DIR_RX; + } + if (t->tx_buf) { + command2 |= SLINK_TXEN; + tspi->cur_direction |= DATA_DIR_TX; + } + tegra_slink_writel(tspi, command2, SLINK_COMMAND2); + tspi->command2_reg = command2; + + if (total_fifo_words > SLINK_FIFO_DEPTH) + ret = tegra_slink_start_dma_based_transfer(tspi, t); + else + ret = tegra_slink_start_cpu_based_transfer(tspi, t); + return ret; +} + +static int tegra_slink_setup(struct spi_device *spi) +{ + struct tegra_slink_data *tspi = spi_master_get_devdata(spi->master); + unsigned long val; + unsigned long flags; + int ret; + unsigned int cs_pol_bit[MAX_CHIP_SELECT] = { + SLINK_CS_POLARITY, + SLINK_CS_POLARITY1, + SLINK_CS_POLARITY2, + SLINK_CS_POLARITY3, + }; + + dev_dbg(&spi->dev, "setup %d bpw, %scpol, %scpha, %dHz\n", + spi->bits_per_word, + spi->mode & SPI_CPOL ? "" : "~", + spi->mode & SPI_CPHA ? "" : "~", + spi->max_speed_hz); + + BUG_ON(spi->chip_select >= MAX_CHIP_SELECT); + + ret = pm_runtime_get_sync(tspi->dev); + if (ret < 0) { + dev_err(tspi->dev, "pm runtime failed, e = %d\n", ret); + return ret; + } + + spin_lock_irqsave(&tspi->lock, flags); + val = tspi->def_command_reg; + if (spi->mode & SPI_CS_HIGH) + val |= cs_pol_bit[spi->chip_select]; + else + val &= ~cs_pol_bit[spi->chip_select]; + tspi->def_command_reg = val; + tegra_slink_writel(tspi, tspi->def_command_reg, SLINK_COMMAND); + spin_unlock_irqrestore(&tspi->lock, flags); + + pm_runtime_put(tspi->dev); + return 0; +} + +static int tegra_slink_prepare_transfer(struct spi_master *master) +{ + struct tegra_slink_data *tspi = spi_master_get_devdata(master); + + return pm_runtime_get_sync(tspi->dev); +} + +static int tegra_slink_unprepare_transfer(struct spi_master *master) +{ + struct tegra_slink_data *tspi = spi_master_get_devdata(master); + + pm_runtime_put(tspi->dev); + return 0; +} + +static int tegra_slink_transfer_one_message(struct spi_master *master, + struct spi_message *msg) +{ + bool is_first_msg = true; + int single_xfer; + struct tegra_slink_data *tspi = spi_master_get_devdata(master); + struct spi_transfer *xfer; + struct spi_device *spi = msg->spi; + int ret; + + msg->status = 0; + msg->actual_length = 0; + single_xfer = list_is_singular(&msg->transfers); + list_for_each_entry(xfer, &msg->transfers, transfer_list) { + INIT_COMPLETION(tspi->xfer_completion); + ret = tegra_slink_start_transfer_one(spi, xfer, + is_first_msg, single_xfer); + if (ret < 0) { + dev_err(tspi->dev, + "spi can not start transfer, err %d\n", ret); + goto exit; + } + is_first_msg = false; + ret = wait_for_completion_timeout(&tspi->xfer_completion, + SLINK_DMA_TIMEOUT); + if (WARN_ON(ret == 0)) { + dev_err(tspi->dev, + "spi trasfer timeout, err %d\n", ret); + ret = -EIO; + goto exit; + } + + if (tspi->tx_status || tspi->rx_status) { + dev_err(tspi->dev, "Error in Transfer\n"); + ret = -EIO; + goto exit; + } + msg->actual_length += xfer->len; + if (xfer->cs_change && xfer->delay_usecs) { + tegra_slink_writel(tspi, tspi->def_command_reg, + SLINK_COMMAND); + udelay(xfer->delay_usecs); + } + } + ret = 0; +exit: + tegra_slink_writel(tspi, tspi->def_command_reg, SLINK_COMMAND); + tegra_slink_writel(tspi, tspi->def_command2_reg, SLINK_COMMAND2); + msg->status = ret; + spi_finalize_current_message(master); + return ret; +} + +static irqreturn_t handle_cpu_based_xfer(struct tegra_slink_data *tspi) +{ + struct spi_transfer *t = tspi->curr_xfer; + unsigned long flags; + + spin_lock_irqsave(&tspi->lock, flags); + if (tspi->tx_status || tspi->rx_status || + (tspi->status_reg & SLINK_BSY)) { + dev_err(tspi->dev, + "CpuXfer ERROR bit set 0x%x\n", tspi->status_reg); + dev_err(tspi->dev, + "CpuXfer 0x%08x:0x%08x:0x%08x\n", tspi->command_reg, + tspi->command2_reg, tspi->dma_control_reg); + tegra_periph_reset_assert(tspi->clk); + udelay(2); + tegra_periph_reset_deassert(tspi->clk); + complete(&tspi->xfer_completion); + goto exit; + } + + if (tspi->cur_direction & DATA_DIR_RX) + tegra_slink_read_rx_fifo_to_client_rxbuf(tspi, t); + + if (tspi->cur_direction & DATA_DIR_TX) + tspi->cur_pos = tspi->cur_tx_pos; + else + tspi->cur_pos = tspi->cur_rx_pos; + + if (tspi->cur_pos == t->len) { + complete(&tspi->xfer_completion); + goto exit; + } + + tegra_slink_calculate_curr_xfer_param(tspi->cur_spi, tspi, t); + tegra_slink_start_cpu_based_transfer(tspi, t); +exit: + spin_unlock_irqrestore(&tspi->lock, flags); + return IRQ_HANDLED; +} + +static irqreturn_t handle_dma_based_xfer(struct tegra_slink_data *tspi) +{ + struct spi_transfer *t = tspi->curr_xfer; + long wait_status; + int err = 0; + unsigned total_fifo_words; + unsigned long flags; + + /* Abort dmas if any error */ + if (tspi->cur_direction & DATA_DIR_TX) { + if (tspi->tx_status) { + dmaengine_terminate_all(tspi->tx_dma_chan); + err += 1; + } else { + wait_status = wait_for_completion_interruptible_timeout( + &tspi->tx_dma_complete, SLINK_DMA_TIMEOUT); + if (wait_status <= 0) { + dmaengine_terminate_all(tspi->tx_dma_chan); + dev_err(tspi->dev, "TxDma Xfer failed\n"); + err += 1; + } + } + } + + if (tspi->cur_direction & DATA_DIR_RX) { + if (tspi->rx_status) { + dmaengine_terminate_all(tspi->rx_dma_chan); + err += 2; + } else { + wait_status = wait_for_completion_interruptible_timeout( + &tspi->rx_dma_complete, SLINK_DMA_TIMEOUT); + if (wait_status <= 0) { + dmaengine_terminate_all(tspi->rx_dma_chan); + dev_err(tspi->dev, "RxDma Xfer failed\n"); + err += 2; + } + } + } + + spin_lock_irqsave(&tspi->lock, flags); + if (err) { + dev_err(tspi->dev, + "DmaXfer: ERROR bit set 0x%x\n", tspi->status_reg); + dev_err(tspi->dev, + "DmaXfer 0x%08x:0x%08x:0x%08x\n", tspi->command_reg, + tspi->command2_reg, tspi->dma_control_reg); + tegra_periph_reset_assert(tspi->clk); + udelay(2); + tegra_periph_reset_deassert(tspi->clk); + complete(&tspi->xfer_completion); + spin_unlock_irqrestore(&tspi->lock, flags); + return IRQ_HANDLED; + } + + if (tspi->cur_direction & DATA_DIR_RX) + tegra_slink_copy_spi_rxbuf_to_client_rxbuf(tspi, t); + + if (tspi->cur_direction & DATA_DIR_TX) + tspi->cur_pos = tspi->cur_tx_pos; + else + tspi->cur_pos = tspi->cur_rx_pos; + + if (tspi->cur_pos == t->len) { + complete(&tspi->xfer_completion); + goto exit; + } + + /* Continue transfer in current message */ + total_fifo_words = tegra_slink_calculate_curr_xfer_param(tspi->cur_spi, + tspi, t); + if (total_fifo_words > SLINK_FIFO_DEPTH) + err = tegra_slink_start_dma_based_transfer(tspi, t); + else + err = tegra_slink_start_cpu_based_transfer(tspi, t); + +exit: + spin_unlock_irqrestore(&tspi->lock, flags); + return IRQ_HANDLED; +} + +static irqreturn_t tegra_slink_isr_thread(int irq, void *context_data) +{ + struct tegra_slink_data *tspi = context_data; + + if (!tspi->is_curr_dma_xfer) + return handle_cpu_based_xfer(tspi); + return handle_dma_based_xfer(tspi); +} + +static irqreturn_t tegra_slink_isr(int irq, void *context_data) +{ + struct tegra_slink_data *tspi = context_data; + + tspi->status_reg = tegra_slink_readl(tspi, SLINK_STATUS); + if (tspi->cur_direction & DATA_DIR_TX) + tspi->tx_status = tspi->status_reg & + (SLINK_TX_OVF | SLINK_TX_UNF); + + if (tspi->cur_direction & DATA_DIR_RX) + tspi->rx_status = tspi->status_reg & + (SLINK_RX_OVF | SLINK_RX_UNF); + tegra_slink_clear_status(tspi); + + return IRQ_WAKE_THREAD; +} + +static struct tegra_spi_platform_data *tegra_slink_parse_dt( + struct platform_device *pdev) +{ + struct tegra_spi_platform_data *pdata; + const unsigned int *prop; + struct device_node *np = pdev->dev.of_node; + u32 of_dma[2]; + + pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) { + dev_err(&pdev->dev, "Memory alloc for pdata failed\n"); + return NULL; + } + + if (of_property_read_u32_array(np, "nvidia,dma-request-selector", + of_dma, 2) >= 0) + pdata->dma_req_sel = of_dma[1]; + + prop = of_get_property(np, "spi-max-frequency", NULL); + if (prop) + pdata->spi_max_frequency = be32_to_cpup(prop); + + return pdata; +} + +const struct tegra_slink_chip_data tegra30_spi_cdata = { + .cs_hold_time = true, +}; + +const struct tegra_slink_chip_data tegra20_spi_cdata = { + .cs_hold_time = false, +}; + +static struct of_device_id tegra_slink_of_match[] __devinitconst = { + { .compatible = "nvidia,tegra20-slink", .data = &tegra20_spi_cdata, }, + { .compatible = "nvidia,tegra30-slink", .data = &tegra30_spi_cdata, }, + {} +}; +MODULE_DEVICE_TABLE(of, tegra_slink_of_match); + +static int __devinit tegra_slink_probe(struct platform_device *pdev) +{ + struct spi_master *master; + struct tegra_slink_data *tspi; + struct resource *r; + struct tegra_spi_platform_data *pdata = pdev->dev.platform_data; + int ret, spi_irq; + const struct tegra_slink_chip_data *cdata = NULL; + const struct of_device_id *match; + + match = of_match_device(of_match_ptr(tegra_slink_of_match), &pdev->dev); + if (!match) { + dev_err(&pdev->dev, "Error: No device match found\n"); + return -ENODEV; + } + cdata = match->data; + if (!pdata && pdev->dev.of_node) + pdata = tegra_slink_parse_dt(pdev); + + if (!pdata) { + dev_err(&pdev->dev, "No platform data, exiting\n"); + return -ENODEV; + } + + if (!pdata->spi_max_frequency) + pdata->spi_max_frequency = 25000000; /* 25MHz */ + + master = spi_alloc_master(&pdev->dev, sizeof(*tspi)); + if (!master) { + dev_err(&pdev->dev, "master allocation failed\n"); + return -ENOMEM; + } + + /* the spi->mode bits understood by this driver: */ + master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH; + master->setup = tegra_slink_setup; + master->prepare_transfer_hardware = tegra_slink_prepare_transfer; + master->transfer_one_message = tegra_slink_transfer_one_message; + master->unprepare_transfer_hardware = tegra_slink_unprepare_transfer; + master->num_chipselect = MAX_CHIP_SELECT; + master->bus_num = -1; + + dev_set_drvdata(&pdev->dev, master); + tspi = spi_master_get_devdata(master); + tspi->master = master; + tspi->dma_req_sel = pdata->dma_req_sel; + tspi->dev = &pdev->dev; + tspi->chip_data = cdata; + spin_lock_init(&tspi->lock); + + r = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!r) { + dev_err(&pdev->dev, "No IO memory resource\n"); + ret = -ENODEV; + goto exit_free_master; + } + tspi->phys = r->start; + tspi->base = devm_request_and_ioremap(&pdev->dev, r); + if (!tspi->base) { + dev_err(&pdev->dev, + "Cannot request memregion/iomap dma address\n"); + ret = -EADDRNOTAVAIL; + goto exit_free_master; + } + + spi_irq = platform_get_irq(pdev, 0); + tspi->irq = spi_irq; + ret = request_threaded_irq(tspi->irq, tegra_slink_isr, + tegra_slink_isr_thread, IRQF_ONESHOT, + dev_name(&pdev->dev), tspi); + if (ret < 0) { + dev_err(&pdev->dev, "Failed to register ISR for IRQ %d\n", + tspi->irq); + goto exit_free_master; + } + + tspi->clk = devm_clk_get(&pdev->dev, "slink"); + if (IS_ERR(tspi->clk)) { + dev_err(&pdev->dev, "can not get clock\n"); + ret = PTR_ERR(tspi->clk); + goto exit_free_irq; + } + + tspi->max_buf_size = SLINK_FIFO_DEPTH << 2; + tspi->dma_buf_size = DEFAULT_SPI_DMA_BUF_LEN; + tspi->spi_max_frequency = pdata->spi_max_frequency; + + if (pdata->dma_req_sel) { + ret = tegra_slink_init_dma_param(tspi, true); + if (ret < 0) { + dev_err(&pdev->dev, "RxDma Init failed, err %d\n", ret); + goto exit_free_irq; + } + + ret = tegra_slink_init_dma_param(tspi, false); + if (ret < 0) { + dev_err(&pdev->dev, "TxDma Init failed, err %d\n", ret); + goto exit_rx_dma_free; + } + tspi->max_buf_size = tspi->dma_buf_size; + init_completion(&tspi->tx_dma_complete); + init_completion(&tspi->rx_dma_complete); + } + + init_completion(&tspi->xfer_completion); + + pm_runtime_enable(&pdev->dev); + if (!pm_runtime_enabled(&pdev->dev)) { + ret = tegra_slink_runtime_resume(&pdev->dev); + if (ret) + goto exit_pm_disable; + } + + ret = pm_runtime_get_sync(&pdev->dev); + if (ret < 0) { + dev_err(&pdev->dev, "pm runtime get failed, e = %d\n", ret); + goto exit_pm_disable; + } + tspi->def_command_reg = SLINK_M_S; + tspi->def_command2_reg = SLINK_CS_ACTIVE_BETWEEN; + tegra_slink_writel(tspi, tspi->def_command_reg, SLINK_COMMAND); + tegra_slink_writel(tspi, tspi->def_command2_reg, SLINK_COMMAND2); + pm_runtime_put(&pdev->dev); + + master->dev.of_node = pdev->dev.of_node; + ret = spi_register_master(master); + if (ret < 0) { + dev_err(&pdev->dev, "can not register to master err %d\n", ret); + goto exit_pm_disable; + } + return ret; + +exit_pm_disable: + pm_runtime_disable(&pdev->dev); + if (!pm_runtime_status_suspended(&pdev->dev)) + tegra_slink_runtime_suspend(&pdev->dev); + tegra_slink_deinit_dma_param(tspi, false); +exit_rx_dma_free: + tegra_slink_deinit_dma_param(tspi, true); +exit_free_irq: + free_irq(spi_irq, tspi); +exit_free_master: + spi_master_put(master); + return ret; +} + +static int __devexit tegra_slink_remove(struct platform_device *pdev) +{ + struct spi_master *master = dev_get_drvdata(&pdev->dev); + struct tegra_slink_data *tspi = spi_master_get_devdata(master); + + free_irq(tspi->irq, tspi); + spi_unregister_master(master); + + if (tspi->tx_dma_chan) + tegra_slink_deinit_dma_param(tspi, false); + + if (tspi->rx_dma_chan) + tegra_slink_deinit_dma_param(tspi, true); + + pm_runtime_disable(&pdev->dev); + if (!pm_runtime_status_suspended(&pdev->dev)) + tegra_slink_runtime_suspend(&pdev->dev); + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int tegra_slink_suspend(struct device *dev) +{ + struct spi_master *master = dev_get_drvdata(dev); + + return spi_master_suspend(master); +} + +static int tegra_slink_resume(struct device *dev) +{ + struct spi_master *master = dev_get_drvdata(dev); + struct tegra_slink_data *tspi = spi_master_get_devdata(master); + int ret; + + ret = pm_runtime_get_sync(dev); + if (ret < 0) { + dev_err(dev, "pm runtime failed, e = %d\n", ret); + return ret; + } + tegra_slink_writel(tspi, tspi->command_reg, SLINK_COMMAND); + tegra_slink_writel(tspi, tspi->command2_reg, SLINK_COMMAND2); + pm_runtime_put(dev); + + return spi_master_resume(master); +} +#endif + +static int tegra_slink_runtime_suspend(struct device *dev) +{ + struct spi_master *master = dev_get_drvdata(dev); + struct tegra_slink_data *tspi = spi_master_get_devdata(master); + + /* Flush all write which are in PPSB queue by reading back */ + tegra_slink_readl(tspi, SLINK_MAS_DATA); + + clk_disable_unprepare(tspi->clk); + return 0; +} + +static int tegra_slink_runtime_resume(struct device *dev) +{ + struct spi_master *master = dev_get_drvdata(dev); + struct tegra_slink_data *tspi = spi_master_get_devdata(master); + int ret; + + ret = clk_prepare_enable(tspi->clk); + if (ret < 0) { + dev_err(tspi->dev, "clk_prepare failed: %d\n", ret); + return ret; + } + return 0; +} + +static const struct dev_pm_ops slink_pm_ops = { + SET_RUNTIME_PM_OPS(tegra_slink_runtime_suspend, + tegra_slink_runtime_resume, NULL) + SET_SYSTEM_SLEEP_PM_OPS(tegra_slink_suspend, tegra_slink_resume) +}; +static struct platform_driver tegra_slink_driver = { + .driver = { + .name = "spi-tegra-slink", + .owner = THIS_MODULE, + .pm = &slink_pm_ops, + .of_match_table = of_match_ptr(tegra_slink_of_match), + }, + .probe = tegra_slink_probe, + .remove = __devexit_p(tegra_slink_remove), +}; +module_platform_driver(tegra_slink_driver); + +MODULE_ALIAS("platform:spi-tegra-slink"); +MODULE_DESCRIPTION("NVIDIA Tegra20/Tegra30 SLINK Controller Driver"); +MODULE_AUTHOR("Laxman Dewangan "); +MODULE_LICENSE("GPL v2"); diff --git a/include/linux/spi/spi-tegra.h b/include/linux/spi/spi-tegra.h new file mode 100644 index 000000000000..786932c62edb --- /dev/null +++ b/include/linux/spi/spi-tegra.h @@ -0,0 +1,40 @@ +/* + * spi-tegra.h: SPI interface for Nvidia Tegra20 SLINK controller. + * + * Copyright (C) 2011 NVIDIA 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; 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, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef _LINUX_SPI_TEGRA_H +#define _LINUX_SPI_TEGRA_H + +struct tegra_spi_platform_data { + int dma_req_sel; + unsigned int spi_max_frequency; +}; + +/* + * Controller data from device to pass some info like + * hw based chip select can be used or not and if yes + * then CS hold and setup time. + */ +struct tegra_spi_device_controller_data { + bool is_hw_based_cs; + int cs_setup_clk_count; + int cs_hold_clk_count; +}; + +#endif /* _LINUX_SPI_TEGRA_H */ -- cgit v1.2.3-59-g8ed1b From 373bac4cf4c3198cc6d6b9aec7c5d576a06f1f1c Mon Sep 17 00:00:00 2001 From: Alan Cox Date: Mon, 29 Oct 2012 15:20:40 +0000 Subject: uart: add other serial core layer get attributes Signed-off-by: Alan Cox Signed-off-by: Greg Kroah-Hartman --- Documentation/ABI/testing/sysfs-tty | 112 ++++++++++++++++++++++++++++ drivers/tty/serial/serial_core.c | 145 ++++++++++++++++++++++++++++++++++++ 2 files changed, 257 insertions(+) (limited to 'Documentation') diff --git a/Documentation/ABI/testing/sysfs-tty b/Documentation/ABI/testing/sysfs-tty index 0c430150d929..ad22fb0ee765 100644 --- a/Documentation/ABI/testing/sysfs-tty +++ b/Documentation/ABI/testing/sysfs-tty @@ -26,3 +26,115 @@ Description: UART port in serial_core, that is bound to TTY like ttyS0. uartclk = 16 * baud_base + These sysfs values expose the TIOCGSERIAL interface via + sysfs rather than via ioctls. + +What: /sys/class/tty/ttyS0/type +Date: October 2012 +Contact: Alan Cox +Description: + Shows the current tty type for this port. + + These sysfs values expose the TIOCGSERIAL interface via + sysfs rather than via ioctls. + +What: /sys/class/tty/ttyS0/line +Date: October 2012 +Contact: Alan Cox +Description: + Shows the current tty line number for this port. + + These sysfs values expose the TIOCGSERIAL interface via + sysfs rather than via ioctls. + +What: /sys/class/tty/ttyS0/port +Date: October 2012 +Contact: Alan Cox +Description: + Shows the current tty port I/O address for this port. + + These sysfs values expose the TIOCGSERIAL interface via + sysfs rather than via ioctls. + +What: /sys/class/tty/ttyS0/irq +Date: October 2012 +Contact: Alan Cox +Description: + Shows the current primary interrupt for this port. + + These sysfs values expose the TIOCGSERIAL interface via + sysfs rather than via ioctls. + +What: /sys/class/tty/ttyS0/flags +Date: October 2012 +Contact: Alan Cox +Description: + Show the tty port status flags for this port. + + These sysfs values expose the TIOCGSERIAL interface via + sysfs rather than via ioctls. + +What: /sys/class/tty/ttyS0/xmit_fifo_size +Date: October 2012 +Contact: Alan Cox +Description: + Show the transmit FIFO size for this port. + + These sysfs values expose the TIOCGSERIAL interface via + sysfs rather than via ioctls. + +What: /sys/class/tty/ttyS0/close_delay +Date: October 2012 +Contact: Alan Cox +Description: + Show the closing delay time for this port in ms. + + These sysfs values expose the TIOCGSERIAL interface via + sysfs rather than via ioctls. + +What: /sys/class/tty/ttyS0/closing_wait +Date: October 2012 +Contact: Alan Cox +Description: + Show the close wait time for this port in ms. + + These sysfs values expose the TIOCGSERIAL interface via + sysfs rather than via ioctls. + +What: /sys/class/tty/ttyS0/custom_divisor +Date: October 2012 +Contact: Alan Cox +Description: + Show the custom divisor if any that is set on this port. + + These sysfs values expose the TIOCGSERIAL interface via + sysfs rather than via ioctls. + +What: /sys/class/tty/ttyS0/io_type +Date: October 2012 +Contact: Alan Cox +Description: + Show the I/O type that is to be used with the iomem base + address. + + These sysfs values expose the TIOCGSERIAL interface via + sysfs rather than via ioctls. + +What: /sys/class/tty/ttyS0/iomem_base +Date: October 2012 +Contact: Alan Cox +Description: + The I/O memory base for this port. + + These sysfs values expose the TIOCGSERIAL interface via + sysfs rather than via ioctls. + +What: /sys/class/tty/ttyS0/iomem_reg_shift +Date: October 2012 +Contact: Alan Cox +Description: + Show the register shift indicating the spacing to be used + for accesses on this iomem address. + + These sysfs values expose the TIOCGSERIAL interface via + sysfs rather than via ioctls. diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c index 0c4304ef66d9..d3dd4ad984f6 100644 --- a/drivers/tty/serial/serial_core.c +++ b/drivers/tty/serial/serial_core.c @@ -2340,10 +2340,155 @@ static ssize_t uart_get_attr_uartclk(struct device *dev, return snprintf(buf, PAGE_SIZE, "%d\n", tmp.baud_base * 16); } +static ssize_t uart_get_attr_type(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct serial_struct tmp; + struct tty_port *port = dev_get_drvdata(dev); + + uart_get_info(port, &tmp); + return snprintf(buf, PAGE_SIZE, "%d\n", tmp.type); +} +static ssize_t uart_get_attr_line(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct serial_struct tmp; + struct tty_port *port = dev_get_drvdata(dev); + + uart_get_info(port, &tmp); + return snprintf(buf, PAGE_SIZE, "%d\n", tmp.line); +} + +static ssize_t uart_get_attr_port(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct serial_struct tmp; + struct tty_port *port = dev_get_drvdata(dev); + + uart_get_info(port, &tmp); + return snprintf(buf, PAGE_SIZE, "0x%lX\n", (unsigned long)(tmp.port | (tmp.port_high << HIGH_BITS_OFFSET))); +} + +static ssize_t uart_get_attr_irq(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct serial_struct tmp; + struct tty_port *port = dev_get_drvdata(dev); + + uart_get_info(port, &tmp); + return snprintf(buf, PAGE_SIZE, "%d\n", tmp.irq); +} + +static ssize_t uart_get_attr_flags(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct serial_struct tmp; + struct tty_port *port = dev_get_drvdata(dev); + + uart_get_info(port, &tmp); + return snprintf(buf, PAGE_SIZE, "0x%X\n", tmp.flags); +} + +static ssize_t uart_get_attr_xmit_fifo_size(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct serial_struct tmp; + struct tty_port *port = dev_get_drvdata(dev); + + uart_get_info(port, &tmp); + return snprintf(buf, PAGE_SIZE, "%d\n", tmp.xmit_fifo_size); +} + + +static ssize_t uart_get_attr_close_delay(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct serial_struct tmp; + struct tty_port *port = dev_get_drvdata(dev); + + uart_get_info(port, &tmp); + return snprintf(buf, PAGE_SIZE, "%d\n", tmp.close_delay); +} + + +static ssize_t uart_get_attr_closing_wait(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct serial_struct tmp; + struct tty_port *port = dev_get_drvdata(dev); + + uart_get_info(port, &tmp); + return snprintf(buf, PAGE_SIZE, "%d\n", tmp.closing_wait); +} + +static ssize_t uart_get_attr_custom_divisor(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct serial_struct tmp; + struct tty_port *port = dev_get_drvdata(dev); + + uart_get_info(port, &tmp); + return snprintf(buf, PAGE_SIZE, "%d\n", tmp.custom_divisor); +} + +static ssize_t uart_get_attr_io_type(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct serial_struct tmp; + struct tty_port *port = dev_get_drvdata(dev); + + uart_get_info(port, &tmp); + return snprintf(buf, PAGE_SIZE, "%d\n", tmp.io_type); +} + +static ssize_t uart_get_attr_iomem_base(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct serial_struct tmp; + struct tty_port *port = dev_get_drvdata(dev); + + uart_get_info(port, &tmp); + return snprintf(buf, PAGE_SIZE, "0x%lX\n", (unsigned long)tmp.iomem_base); +} + +static ssize_t uart_get_attr_iomem_reg_shift(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct serial_struct tmp; + struct tty_port *port = dev_get_drvdata(dev); + + uart_get_info(port, &tmp); + return snprintf(buf, PAGE_SIZE, "%d\n", tmp.iomem_reg_shift); +} + +static DEVICE_ATTR(type, S_IRUSR | S_IRGRP, uart_get_attr_type, NULL); +static DEVICE_ATTR(line, S_IRUSR | S_IRGRP, uart_get_attr_line, NULL); +static DEVICE_ATTR(port, S_IRUSR | S_IRGRP, uart_get_attr_port, NULL); +static DEVICE_ATTR(irq, S_IRUSR | S_IRGRP, uart_get_attr_irq, NULL); +static DEVICE_ATTR(flags, S_IRUSR | S_IRGRP, uart_get_attr_flags, NULL); +static DEVICE_ATTR(xmit_fifo_size, S_IRUSR | S_IRGRP, uart_get_attr_xmit_fifo_size, NULL); static DEVICE_ATTR(uartclk, S_IRUSR | S_IRGRP, uart_get_attr_uartclk, NULL); +static DEVICE_ATTR(close_delay, S_IRUSR | S_IRGRP, uart_get_attr_close_delay, NULL); +static DEVICE_ATTR(closing_wait, S_IRUSR | S_IRGRP, uart_get_attr_closing_wait, NULL); +static DEVICE_ATTR(custom_divisor, S_IRUSR | S_IRGRP, uart_get_attr_custom_divisor, NULL); +static DEVICE_ATTR(io_type, S_IRUSR | S_IRGRP, uart_get_attr_io_type, NULL); +static DEVICE_ATTR(iomem_base, S_IRUSR | S_IRGRP, uart_get_attr_iomem_base, NULL); +static DEVICE_ATTR(iomem_reg_shift, S_IRUSR | S_IRGRP, uart_get_attr_iomem_reg_shift, NULL); static struct attribute *tty_dev_attrs[] = { + &dev_attr_type.attr, + &dev_attr_line.attr, + &dev_attr_port.attr, + &dev_attr_irq.attr, + &dev_attr_flags.attr, + &dev_attr_xmit_fifo_size.attr, &dev_attr_uartclk.attr, + &dev_attr_close_delay.attr, + &dev_attr_closing_wait.attr, + &dev_attr_custom_divisor.attr, + &dev_attr_io_type.attr, + &dev_attr_iomem_base.attr, + &dev_attr_iomem_reg_shift.attr, NULL, }; -- cgit v1.2.3-59-g8ed1b From 7d6ab9b8620bbca6718b36242113f4f069840641 Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Thu, 25 Oct 2012 11:59:09 -0500 Subject: ARM: dts: Add Calxeda ECX-2000 support Separate out common dts pieces from highbank dts and add support for Calxeda ECX-2000 (Midway) SOC. Signed-off-by: Rob Herring Acked-by: Arnd Bergmann --- Documentation/devicetree/bindings/arm/calxeda.txt | 13 +- arch/arm/boot/dts/Makefile | 3 +- arch/arm/boot/dts/ecx-2000.dts | 104 ++++++++++ arch/arm/boot/dts/ecx-common.dtsi | 237 ++++++++++++++++++++++ arch/arm/boot/dts/highbank.dts | 219 +------------------- 5 files changed, 356 insertions(+), 220 deletions(-) create mode 100644 arch/arm/boot/dts/ecx-2000.dts create mode 100644 arch/arm/boot/dts/ecx-common.dtsi (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/arm/calxeda.txt b/Documentation/devicetree/bindings/arm/calxeda.txt index 4755caaccba6..25fcf96795ca 100644 --- a/Documentation/devicetree/bindings/arm/calxeda.txt +++ b/Documentation/devicetree/bindings/arm/calxeda.txt @@ -1,8 +1,15 @@ -Calxeda Highbank Platforms Device Tree Bindings +Calxeda Platforms Device Tree Bindings ----------------------------------------------- -Boards with Calxeda Cortex-A9 based Highbank SOC shall have the following -properties. +Boards with Calxeda Cortex-A9 based ECX-1000 (Highbank) SOC shall have the +following properties. Required root node properties: - compatible = "calxeda,highbank"; + + +Boards with Calxeda Cortex-A15 based ECX-2000 SOC shall have the following +properties. + +Required root node properties: + - compatible = "calxeda,ecx-2000"; diff --git a/arch/arm/boot/dts/Makefile b/arch/arm/boot/dts/Makefile index f37cf9fa5fa0..5cc95667fa3b 100644 --- a/arch/arm/boot/dts/Makefile +++ b/arch/arm/boot/dts/Makefile @@ -24,7 +24,8 @@ dtb-$(CONFIG_ARCH_EXYNOS) += exynos4210-origen.dtb \ exynos4210-smdkv310.dtb \ exynos4210-trats.dtb \ exynos5250-smdk5250.dtb -dtb-$(CONFIG_ARCH_HIGHBANK) += highbank.dtb +dtb-$(CONFIG_ARCH_HIGHBANK) += highbank.dtb \ + ecx-2000.dtb dtb-$(CONFIG_ARCH_INTEGRATOR) += integratorap.dtb \ integratorcp.dtb dtb-$(CONFIG_ARCH_LPC32XX) += ea3250.dtb phy3250.dtb diff --git a/arch/arm/boot/dts/ecx-2000.dts b/arch/arm/boot/dts/ecx-2000.dts new file mode 100644 index 000000000000..46477ac1de99 --- /dev/null +++ b/arch/arm/boot/dts/ecx-2000.dts @@ -0,0 +1,104 @@ +/* + * Copyright 2011-2012 Calxeda, 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. + * + * This program is distributed in the hope 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 . + */ + +/dts-v1/; + +/* First 4KB has pen for secondary cores. */ +/memreserve/ 0x00000000 0x0001000; + +/ { + model = "Calxeda ECX-2000"; + compatible = "calxeda,ecx-2000"; + #address-cells = <2>; + #size-cells = <2>; + clock-ranges; + + cpus { + #address-cells = <1>; + #size-cells = <0>; + + cpu@0 { + compatible = "arm,cortex-a15"; + reg = <0>; + clocks = <&a9pll>; + clock-names = "cpu"; + }; + + cpu@1 { + compatible = "arm,cortex-a15"; + reg = <1>; + clocks = <&a9pll>; + clock-names = "cpu"; + }; + + cpu@2 { + compatible = "arm,cortex-a15"; + reg = <2>; + clocks = <&a9pll>; + clock-names = "cpu"; + }; + + cpu@3 { + compatible = "arm,cortex-a15"; + reg = <3>; + clocks = <&a9pll>; + clock-names = "cpu"; + }; + }; + + memory@0 { + name = "memory"; + device_type = "memory"; + reg = <0x00000000 0x00000000 0x00000000 0xff800000>; + }; + + memory@200000000 { + name = "memory"; + device_type = "memory"; + reg = <0x00000002 0x00000000 0x00000003 0x00000000>; + }; + + soc { + ranges = <0x00000000 0x00000000 0x00000000 0xffffffff>; + + timer { + compatible = "arm,cortex-a15-timer", "arm,armv7-timer"; interrupts = <1 13 0xf08>, + <1 14 0xf08>, + <1 11 0xf08>, + <1 10 0xf08>; + }; + + intc: interrupt-controller@fff11000 { + compatible = "arm,cortex-a15-gic"; + #interrupt-cells = <3>; + #size-cells = <0>; + #address-cells = <1>; + interrupt-controller; + interrupts = <1 9 0xf04>; + reg = <0xfff11000 0x1000>, + <0xfff12000 0x1000>, + <0xfff14000 0x2000>, + <0xfff16000 0x2000>; + }; + + pmu { + compatible = "arm,cortex-a9-pmu"; + interrupts = <0 76 4 0 75 4 0 74 4 0 73 4>; + }; + }; +}; + +/include/ "ecx-common.dtsi" diff --git a/arch/arm/boot/dts/ecx-common.dtsi b/arch/arm/boot/dts/ecx-common.dtsi new file mode 100644 index 000000000000..d61b535f682a --- /dev/null +++ b/arch/arm/boot/dts/ecx-common.dtsi @@ -0,0 +1,237 @@ +/* + * Copyright 2011-2012 Calxeda, 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. + * + * This program is distributed in the hope 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 . + */ + +/ { + chosen { + bootargs = "console=ttyAMA0"; + }; + + soc { + #address-cells = <1>; + #size-cells = <1>; + compatible = "simple-bus"; + interrupt-parent = <&intc>; + + sata@ffe08000 { + compatible = "calxeda,hb-ahci"; + reg = <0xffe08000 0x10000>; + interrupts = <0 83 4>; + dma-coherent; + calxeda,port-phys = <&combophy5 0 &combophy0 0 + &combophy0 1 &combophy0 2 + &combophy0 3>; + }; + + sdhci@ffe0e000 { + compatible = "calxeda,hb-sdhci"; + reg = <0xffe0e000 0x1000>; + interrupts = <0 90 4>; + clocks = <&eclk>; + status = "disabled"; + }; + + memory-controller@fff00000 { + compatible = "calxeda,hb-ddr-ctrl"; + reg = <0xfff00000 0x1000>; + interrupts = <0 91 4>; + }; + + ipc@fff20000 { + compatible = "arm,pl320", "arm,primecell"; + reg = <0xfff20000 0x1000>; + interrupts = <0 7 4>; + clocks = <&pclk>; + clock-names = "apb_pclk"; + }; + + gpioe: gpio@fff30000 { + #gpio-cells = <2>; + compatible = "arm,pl061", "arm,primecell"; + gpio-controller; + reg = <0xfff30000 0x1000>; + interrupts = <0 14 4>; + clocks = <&pclk>; + clock-names = "apb_pclk"; + status = "disabled"; + }; + + gpiof: gpio@fff31000 { + #gpio-cells = <2>; + compatible = "arm,pl061", "arm,primecell"; + gpio-controller; + reg = <0xfff31000 0x1000>; + interrupts = <0 15 4>; + clocks = <&pclk>; + clock-names = "apb_pclk"; + status = "disabled"; + }; + + gpiog: gpio@fff32000 { + #gpio-cells = <2>; + compatible = "arm,pl061", "arm,primecell"; + gpio-controller; + reg = <0xfff32000 0x1000>; + interrupts = <0 16 4>; + clocks = <&pclk>; + clock-names = "apb_pclk"; + status = "disabled"; + }; + + gpioh: gpio@fff33000 { + #gpio-cells = <2>; + compatible = "arm,pl061", "arm,primecell"; + gpio-controller; + reg = <0xfff33000 0x1000>; + interrupts = <0 17 4>; + clocks = <&pclk>; + clock-names = "apb_pclk"; + status = "disabled"; + }; + + timer@fff34000 { + compatible = "arm,sp804", "arm,primecell"; + reg = <0xfff34000 0x1000>; + interrupts = <0 18 4>; + clocks = <&pclk>; + clock-names = "apb_pclk"; + }; + + rtc@fff35000 { + compatible = "arm,pl031", "arm,primecell"; + reg = <0xfff35000 0x1000>; + interrupts = <0 19 4>; + clocks = <&pclk>; + clock-names = "apb_pclk"; + }; + + serial@fff36000 { + compatible = "arm,pl011", "arm,primecell"; + reg = <0xfff36000 0x1000>; + interrupts = <0 20 4>; + clocks = <&pclk>; + clock-names = "apb_pclk"; + }; + + smic@fff3a000 { + compatible = "ipmi-smic"; + device_type = "ipmi"; + reg = <0xfff3a000 0x1000>; + interrupts = <0 24 4>; + reg-size = <4>; + reg-spacing = <4>; + }; + + sregs@fff3c000 { + compatible = "calxeda,hb-sregs"; + reg = <0xfff3c000 0x1000>; + + clocks { + #address-cells = <1>; + #size-cells = <0>; + + osc: oscillator { + #clock-cells = <0>; + compatible = "fixed-clock"; + clock-frequency = <33333000>; + }; + + ddrpll: ddrpll { + #clock-cells = <0>; + compatible = "calxeda,hb-pll-clock"; + clocks = <&osc>; + reg = <0x108>; + }; + + a9pll: a9pll { + #clock-cells = <0>; + compatible = "calxeda,hb-pll-clock"; + clocks = <&osc>; + reg = <0x100>; + }; + + a9periphclk: a9periphclk { + #clock-cells = <0>; + compatible = "calxeda,hb-a9periph-clock"; + clocks = <&a9pll>; + reg = <0x104>; + }; + + a9bclk: a9bclk { + #clock-cells = <0>; + compatible = "calxeda,hb-a9bus-clock"; + clocks = <&a9pll>; + reg = <0x104>; + }; + + emmcpll: emmcpll { + #clock-cells = <0>; + compatible = "calxeda,hb-pll-clock"; + clocks = <&osc>; + reg = <0x10C>; + }; + + eclk: eclk { + #clock-cells = <0>; + compatible = "calxeda,hb-emmc-clock"; + clocks = <&emmcpll>; + reg = <0x114>; + }; + + pclk: pclk { + #clock-cells = <0>; + compatible = "fixed-clock"; + clock-frequency = <150000000>; + }; + }; + }; + + dma@fff3d000 { + compatible = "arm,pl330", "arm,primecell"; + reg = <0xfff3d000 0x1000>; + interrupts = <0 92 4>; + clocks = <&pclk>; + clock-names = "apb_pclk"; + }; + + ethernet@fff50000 { + compatible = "calxeda,hb-xgmac"; + reg = <0xfff50000 0x1000>; + interrupts = <0 77 4 0 78 4 0 79 4>; + dma-coherent; + }; + + ethernet@fff51000 { + compatible = "calxeda,hb-xgmac"; + reg = <0xfff51000 0x1000>; + interrupts = <0 80 4 0 81 4 0 82 4>; + dma-coherent; + }; + + combophy0: combo-phy@fff58000 { + compatible = "calxeda,hb-combophy"; + #phy-cells = <1>; + reg = <0xfff58000 0x1000>; + phydev = <5>; + }; + + combophy5: combo-phy@fff5d000 { + compatible = "calxeda,hb-combophy"; + #phy-cells = <1>; + reg = <0xfff5d000 0x1000>; + phydev = <31>; + }; + }; +}; diff --git a/arch/arm/boot/dts/highbank.dts b/arch/arm/boot/dts/highbank.dts index e39a79a61e0c..a9ae5d32e80d 100644 --- a/arch/arm/boot/dts/highbank.dts +++ b/arch/arm/boot/dts/highbank.dts @@ -69,16 +69,8 @@ reg = <0x00000000 0xff900000>; }; - chosen { - bootargs = "console=ttyAMA0"; - }; - soc { - #address-cells = <1>; - #size-cells = <1>; - compatible = "simple-bus"; - interrupt-parent = <&intc>; - ranges; + ranges = <0x00000000 0x00000000 0xffffffff>; timer@fff10600 { compatible = "arm,cortex-a9-twd-timer"; @@ -117,178 +109,6 @@ interrupts = <0 76 4 0 75 4 0 74 4 0 73 4>; }; - sata@ffe08000 { - compatible = "calxeda,hb-ahci"; - reg = <0xffe08000 0x10000>; - interrupts = <0 83 4>; - calxeda,port-phys = <&combophy5 0 &combophy0 0 - &combophy0 1 &combophy0 2 - &combophy0 3>; - dma-coherent; - }; - - sdhci@ffe0e000 { - compatible = "calxeda,hb-sdhci"; - reg = <0xffe0e000 0x1000>; - interrupts = <0 90 4>; - clocks = <&eclk>; - status = "disabled"; - }; - - memory-controller@fff00000 { - compatible = "calxeda,hb-ddr-ctrl"; - reg = <0xfff00000 0x1000>; - interrupts = <0 91 4>; - }; - - ipc@fff20000 { - compatible = "arm,pl320", "arm,primecell"; - reg = <0xfff20000 0x1000>; - interrupts = <0 7 4>; - clocks = <&pclk>; - clock-names = "apb_pclk"; - }; - - gpioe: gpio@fff30000 { - #gpio-cells = <2>; - compatible = "arm,pl061", "arm,primecell"; - gpio-controller; - reg = <0xfff30000 0x1000>; - interrupts = <0 14 4>; - clocks = <&pclk>; - clock-names = "apb_pclk"; - status = "disabled"; - }; - - gpiof: gpio@fff31000 { - #gpio-cells = <2>; - compatible = "arm,pl061", "arm,primecell"; - gpio-controller; - reg = <0xfff31000 0x1000>; - interrupts = <0 15 4>; - clocks = <&pclk>; - clock-names = "apb_pclk"; - status = "disabled"; - }; - - gpiog: gpio@fff32000 { - #gpio-cells = <2>; - compatible = "arm,pl061", "arm,primecell"; - gpio-controller; - reg = <0xfff32000 0x1000>; - interrupts = <0 16 4>; - clocks = <&pclk>; - clock-names = "apb_pclk"; - status = "disabled"; - }; - - gpioh: gpio@fff33000 { - #gpio-cells = <2>; - compatible = "arm,pl061", "arm,primecell"; - gpio-controller; - reg = <0xfff33000 0x1000>; - interrupts = <0 17 4>; - clocks = <&pclk>; - clock-names = "apb_pclk"; - status = "disabled"; - }; - - timer { - compatible = "arm,sp804", "arm,primecell"; - reg = <0xfff34000 0x1000>; - interrupts = <0 18 4>; - clocks = <&pclk>; - clock-names = "apb_pclk"; - }; - - rtc@fff35000 { - compatible = "arm,pl031", "arm,primecell"; - reg = <0xfff35000 0x1000>; - interrupts = <0 19 4>; - clocks = <&pclk>; - clock-names = "apb_pclk"; - }; - - serial@fff36000 { - compatible = "arm,pl011", "arm,primecell"; - reg = <0xfff36000 0x1000>; - interrupts = <0 20 4>; - clocks = <&pclk>; - clock-names = "apb_pclk"; - }; - - smic@fff3a000 { - compatible = "ipmi-smic"; - device_type = "ipmi"; - reg = <0xfff3a000 0x1000>; - interrupts = <0 24 4>; - reg-size = <4>; - reg-spacing = <4>; - }; - - sregs@fff3c000 { - compatible = "calxeda,hb-sregs"; - reg = <0xfff3c000 0x1000>; - - clocks { - #address-cells = <1>; - #size-cells = <0>; - - osc: oscillator { - #clock-cells = <0>; - compatible = "fixed-clock"; - clock-frequency = <33333000>; - }; - - ddrpll: ddrpll { - #clock-cells = <0>; - compatible = "calxeda,hb-pll-clock"; - clocks = <&osc>; - reg = <0x108>; - }; - - a9pll: a9pll { - #clock-cells = <0>; - compatible = "calxeda,hb-pll-clock"; - clocks = <&osc>; - reg = <0x100>; - }; - - a9periphclk: a9periphclk { - #clock-cells = <0>; - compatible = "calxeda,hb-a9periph-clock"; - clocks = <&a9pll>; - reg = <0x104>; - }; - - a9bclk: a9bclk { - #clock-cells = <0>; - compatible = "calxeda,hb-a9bus-clock"; - clocks = <&a9pll>; - reg = <0x104>; - }; - - emmcpll: emmcpll { - #clock-cells = <0>; - compatible = "calxeda,hb-pll-clock"; - clocks = <&osc>; - reg = <0x10C>; - }; - - eclk: eclk { - #clock-cells = <0>; - compatible = "calxeda,hb-emmc-clock"; - clocks = <&emmcpll>; - reg = <0x114>; - }; - - pclk: pclk { - #clock-cells = <0>; - compatible = "fixed-clock"; - clock-frequency = <150000000>; - }; - }; - }; sregs@fff3c200 { compatible = "calxeda,hb-sregs-l2-ecc"; @@ -296,40 +116,7 @@ interrupts = <0 71 4 0 72 4>; }; - dma@fff3d000 { - compatible = "arm,pl330", "arm,primecell"; - reg = <0xfff3d000 0x1000>; - interrupts = <0 92 4>; - clocks = <&pclk>; - clock-names = "apb_pclk"; - }; - - ethernet@fff50000 { - compatible = "calxeda,hb-xgmac"; - reg = <0xfff50000 0x1000>; - interrupts = <0 77 4 0 78 4 0 79 4>; - dma-coherent; - }; - - ethernet@fff51000 { - compatible = "calxeda,hb-xgmac"; - reg = <0xfff51000 0x1000>; - interrupts = <0 80 4 0 81 4 0 82 4>; - dma-coherent; - }; - - combophy0: combo-phy@fff58000 { - compatible = "calxeda,hb-combophy"; - #phy-cells = <1>; - reg = <0xfff58000 0x1000>; - phydev = <5>; - }; - - combophy5: combo-phy@fff5d000 { - compatible = "calxeda,hb-combophy"; - #phy-cells = <1>; - reg = <0xfff5d000 0x1000>; - phydev = <31>; - }; }; }; + +/include/ "ecx-common.dtsi" -- cgit v1.2.3-59-g8ed1b From fb647002e4b6393a1f552b2fe48ba88c50d1d6e4 Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Fri, 28 Sep 2012 15:24:40 +0100 Subject: Documentation: Detail permitted DT properties for the BU21013 Touch Screen Here we apply required documentation for the Rohm BU21013 Touch Screen driver which describe available properties and how to use them. Cc: Dmitry Torokhov Cc: linux-input@vger.kernel.org Cc: devicetree-discuss@lists.ozlabs.org Acked-by: Arnd Bergmann Acked-by: Linus Walleij Signed-off-by: Lee Jones --- .../bindings/input/touchscreen/bu21013.txt | 28 ++++++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 Documentation/devicetree/bindings/input/touchscreen/bu21013.txt (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/input/touchscreen/bu21013.txt b/Documentation/devicetree/bindings/input/touchscreen/bu21013.txt new file mode 100644 index 000000000000..ca5a2c86480c --- /dev/null +++ b/Documentation/devicetree/bindings/input/touchscreen/bu21013.txt @@ -0,0 +1,28 @@ +* Rohm BU21013 Touch Screen + +Required properties: + - compatible : "rohm,bu21013_tp" + - reg : I2C device address + +Optional properties: + - touch-gpio : GPIO pin registering a touch event + - -supply : Phandle to a regulator supply + - rohm,touch-max-x : Maximum outward permitted limit in the X axis + - rohm,touch-max-y : Maximum outward permitted limit in the Y axis + - rohm,flip-x : Flip touch coordinates on the X axis + - rohm,flip-y : Flip touch coordinates on the Y axis + +Example: + + i2c@80110000 { + bu21013_tp@0x5c { + compatible = "rohm,bu21013_tp"; + reg = <0x5c>; + touch-gpio = <&gpio2 20 0x4>; + avdd-supply = <&ab8500_ldo_aux1_reg>; + + rohm,touch-max-x = <384>; + rohm,touch-max-y = <704>; + rohm,flip-y; + }; + }; -- cgit v1.2.3-59-g8ed1b From 86b00e0da6be7bbc16412f126c5b548ac5d91d50 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Thu, 25 Oct 2012 23:34:42 -0500 Subject: rbd: get parent spec for version 2 images Add support for getting the the information identifying the parent image for rbd images that have them. The child image holds a reference to its parent image specification structure. Create a new entry "parent" in /sys/bus/rbd/image/N/ to report the identifying information for the parent image, if any. Signed-off-by: Alex Elder Reviewed-by: Josh Durgin --- Documentation/ABI/testing/sysfs-bus-rbd | 4 + drivers/block/rbd.c | 131 ++++++++++++++++++++++++++++++++ include/linux/ceph/rados.h | 2 + 3 files changed, 137 insertions(+) (limited to 'Documentation') diff --git a/Documentation/ABI/testing/sysfs-bus-rbd b/Documentation/ABI/testing/sysfs-bus-rbd index 1cf2adf46b11..cd9213ccf3dc 100644 --- a/Documentation/ABI/testing/sysfs-bus-rbd +++ b/Documentation/ABI/testing/sysfs-bus-rbd @@ -70,6 +70,10 @@ snap_* A directory per each snapshot +parent + + Information identifying the pool, image, and snapshot id for + the parent image in a layered rbd image (format 2 only). Entries under /sys/bus/rbd/devices//snap_ ------------------------------------------------------------- diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c index 28052ff679ca..bce1fcfb5185 100644 --- a/drivers/block/rbd.c +++ b/drivers/block/rbd.c @@ -217,6 +217,9 @@ struct rbd_device { struct ceph_osd_event *watch_event; struct ceph_osd_request *watch_request; + struct rbd_spec *parent_spec; + u64 parent_overlap; + /* protects updating the header */ struct rw_semaphore header_rwsem; @@ -2009,6 +2012,49 @@ static ssize_t rbd_snap_show(struct device *dev, return sprintf(buf, "%s\n", rbd_dev->spec->snap_name); } +/* + * For an rbd v2 image, shows the pool id, image id, and snapshot id + * for the parent image. If there is no parent, simply shows + * "(no parent image)". + */ +static ssize_t rbd_parent_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct rbd_device *rbd_dev = dev_to_rbd_dev(dev); + struct rbd_spec *spec = rbd_dev->parent_spec; + int count; + char *bufp = buf; + + if (!spec) + return sprintf(buf, "(no parent image)\n"); + + count = sprintf(bufp, "pool_id %llu\npool_name %s\n", + (unsigned long long) spec->pool_id, spec->pool_name); + if (count < 0) + return count; + bufp += count; + + count = sprintf(bufp, "image_id %s\nimage_name %s\n", spec->image_id, + spec->image_name ? spec->image_name : "(unknown)"); + if (count < 0) + return count; + bufp += count; + + count = sprintf(bufp, "snap_id %llu\nsnap_name %s\n", + (unsigned long long) spec->snap_id, spec->snap_name); + if (count < 0) + return count; + bufp += count; + + count = sprintf(bufp, "overlap %llu\n", rbd_dev->parent_overlap); + if (count < 0) + return count; + bufp += count; + + return (ssize_t) (bufp - buf); +} + static ssize_t rbd_image_refresh(struct device *dev, struct device_attribute *attr, const char *buf, @@ -2032,6 +2078,7 @@ static DEVICE_ATTR(name, S_IRUGO, rbd_name_show, NULL); static DEVICE_ATTR(image_id, S_IRUGO, rbd_image_id_show, NULL); static DEVICE_ATTR(refresh, S_IWUSR, NULL, rbd_image_refresh); static DEVICE_ATTR(current_snap, S_IRUGO, rbd_snap_show, NULL); +static DEVICE_ATTR(parent, S_IRUGO, rbd_parent_show, NULL); static struct attribute *rbd_attrs[] = { &dev_attr_size.attr, @@ -2043,6 +2090,7 @@ static struct attribute *rbd_attrs[] = { &dev_attr_name.attr, &dev_attr_image_id.attr, &dev_attr_current_snap.attr, + &dev_attr_parent.attr, &dev_attr_refresh.attr, NULL }; @@ -2192,6 +2240,7 @@ struct rbd_device *rbd_dev_create(struct rbd_client *rbdc, static void rbd_dev_destroy(struct rbd_device *rbd_dev) { + rbd_spec_put(rbd_dev->parent_spec); kfree(rbd_dev->header_name); rbd_put_client(rbd_dev->rbd_client); rbd_spec_put(rbd_dev->spec); @@ -2400,6 +2449,71 @@ static int rbd_dev_v2_features(struct rbd_device *rbd_dev) &rbd_dev->header.features); } +static int rbd_dev_v2_parent_info(struct rbd_device *rbd_dev) +{ + struct rbd_spec *parent_spec; + size_t size; + void *reply_buf = NULL; + __le64 snapid; + void *p; + void *end; + char *image_id; + u64 overlap; + size_t len = 0; + int ret; + + parent_spec = rbd_spec_alloc(); + if (!parent_spec) + return -ENOMEM; + + size = sizeof (__le64) + /* pool_id */ + sizeof (__le32) + RBD_IMAGE_ID_LEN_MAX + /* image_id */ + sizeof (__le64) + /* snap_id */ + sizeof (__le64); /* overlap */ + reply_buf = kmalloc(size, GFP_KERNEL); + if (!reply_buf) { + ret = -ENOMEM; + goto out_err; + } + + snapid = cpu_to_le64(CEPH_NOSNAP); + ret = rbd_req_sync_exec(rbd_dev, rbd_dev->header_name, + "rbd", "get_parent", + (char *) &snapid, sizeof (snapid), + (char *) reply_buf, size, + CEPH_OSD_FLAG_READ, NULL); + dout("%s: rbd_req_sync_exec returned %d\n", __func__, ret); + if (ret < 0) + goto out_err; + + ret = -ERANGE; + p = reply_buf; + end = (char *) reply_buf + size; + ceph_decode_64_safe(&p, end, parent_spec->pool_id, out_err); + if (parent_spec->pool_id == CEPH_NOPOOL) + goto out; /* No parent? No problem. */ + + image_id = ceph_extract_encoded_string(&p, end, &len, GFP_KERNEL); + if (IS_ERR(image_id)) { + ret = PTR_ERR(image_id); + goto out_err; + } + parent_spec->image_id = image_id; + ceph_decode_64_safe(&p, end, parent_spec->snap_id, out_err); + ceph_decode_64_safe(&p, end, overlap, out_err); + + rbd_dev->parent_overlap = overlap; + rbd_dev->parent_spec = parent_spec; + parent_spec = NULL; /* rbd_dev now owns this */ +out: + ret = 0; +out_err: + kfree(reply_buf); + rbd_spec_put(parent_spec); + + return ret; +} + static int rbd_dev_v2_snap_context(struct rbd_device *rbd_dev, u64 *ver) { size_t size; @@ -3154,6 +3268,12 @@ static int rbd_dev_v1_probe(struct rbd_device *rbd_dev) ret = rbd_read_header(rbd_dev, &rbd_dev->header); if (ret < 0) goto out_err; + + /* Version 1 images have no parent (no layering) */ + + rbd_dev->parent_spec = NULL; + rbd_dev->parent_overlap = 0; + rbd_dev->image_format = 1; dout("discovered version 1 image, header name is %s\n", @@ -3205,6 +3325,14 @@ static int rbd_dev_v2_probe(struct rbd_device *rbd_dev) if (ret < 0) goto out_err; + /* If the image supports layering, get the parent info */ + + if (rbd_dev->header.features & RBD_FEATURE_LAYERING) { + ret = rbd_dev_v2_parent_info(rbd_dev); + if (ret < 0) + goto out_err; + } + /* crypto and compression type aren't (yet) supported for v2 images */ rbd_dev->header.crypt_type = 0; @@ -3224,6 +3352,9 @@ static int rbd_dev_v2_probe(struct rbd_device *rbd_dev) return 0; out_err: + rbd_dev->parent_overlap = 0; + rbd_spec_put(rbd_dev->parent_spec); + rbd_dev->parent_spec = NULL; kfree(rbd_dev->header_name); rbd_dev->header_name = NULL; kfree(rbd_dev->header.object_prefix); diff --git a/include/linux/ceph/rados.h b/include/linux/ceph/rados.h index 0a99099801a4..15077db662ed 100644 --- a/include/linux/ceph/rados.h +++ b/include/linux/ceph/rados.h @@ -87,6 +87,8 @@ struct ceph_pg { * * lpgp_num -- as above. */ +#define CEPH_NOPOOL ((__u64) (-1)) /* pool id not defined */ + #define CEPH_PG_TYPE_REP 1 #define CEPH_PG_TYPE_RAID4 2 #define CEPH_PG_POOL_VERSION 2 -- cgit v1.2.3-59-g8ed1b From 63e1ed2364050073770c085021377d7764969b85 Mon Sep 17 00:00:00 2001 From: Tony Prisk Date: Sat, 27 Oct 2012 14:49:57 +1300 Subject: pwm: vt8500: Update vt8500 PWM driver support This patch updates pwm-vt8500.c to support devicetree probing and make use of the common clock subsystem. A binding document describing the PWM controller found on arch-vt8500 is also included. Signed-off-by: Tony Prisk Acked-by: Arnd Bergmann Signed-off-by: Thierry Reding --- .../devicetree/bindings/pwm/vt8500-pwm.txt | 17 +++++ drivers/pwm/pwm-vt8500.c | 86 ++++++++++++++++------ 2 files changed, 80 insertions(+), 23 deletions(-) create mode 100644 Documentation/devicetree/bindings/pwm/vt8500-pwm.txt (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/pwm/vt8500-pwm.txt b/Documentation/devicetree/bindings/pwm/vt8500-pwm.txt new file mode 100644 index 000000000000..bcc63678a9a5 --- /dev/null +++ b/Documentation/devicetree/bindings/pwm/vt8500-pwm.txt @@ -0,0 +1,17 @@ +VIA/Wondermedia VT8500/WM8xxx series SoC PWM controller + +Required properties: +- compatible: should be "via,vt8500-pwm" +- reg: physical base address and length of the controller's registers +- #pwm-cells: should be 2. The first cell specifies the per-chip index + of the PWM to use and the second cell is the period in nanoseconds. +- clocks: phandle to the PWM source clock + +Example: + +pwm1: pwm@d8220000 { + #pwm-cells = <2>; + compatible = "via,vt8500-pwm"; + reg = <0xd8220000 0x1000>; + clocks = <&clkpwm>; +}; diff --git a/drivers/pwm/pwm-vt8500.c b/drivers/pwm/pwm-vt8500.c index ad14389b7144..970b0c6e268f 100644 --- a/drivers/pwm/pwm-vt8500.c +++ b/drivers/pwm/pwm-vt8500.c @@ -1,7 +1,8 @@ /* * drivers/pwm/pwm-vt8500.c * - * Copyright (C) 2010 Alexey Charkov + * Copyright (C) 2012 Tony Prisk + * Copyright (C) 2010 Alexey Charkov * * This software is licensed under the terms of the GNU General Public * License version 2, as published by the Free Software Foundation, and @@ -21,14 +22,24 @@ #include #include #include +#include #include -#define VT8500_NR_PWMS 4 +#include +#include +#include + +/* + * SoC architecture allocates register space for 4 PWMs but only + * 2 are currently implemented. + */ +#define VT8500_NR_PWMS 2 struct vt8500_chip { struct pwm_chip chip; void __iomem *base; + struct clk *clk; }; #define to_vt8500_chip(chip) container_of(chip, struct vt8500_chip, chip) @@ -52,7 +63,7 @@ static int vt8500_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, unsigned long long c; unsigned long period_cycles, prescale, pv, dc; - c = 25000000/2; /* wild guess --- need to implement clocks */ + c = clk_get_rate(vt8500->clk); c = c * period_ns; do_div(c, 1000000000); period_cycles = c; @@ -85,8 +96,15 @@ static int vt8500_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, static int vt8500_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) { + int err; struct vt8500_chip *vt8500 = to_vt8500_chip(chip); + err = clk_enable(vt8500->clk); + if (err < 0) + dev_err(chip->dev, "failed to enable clock\n"); + return err; + }; + pwm_busy_wait(vt8500->base + 0x40 + pwm->hwpwm, (1 << 0)); writel(5, vt8500->base + (pwm->hwpwm << 4)); return 0; @@ -98,6 +116,8 @@ static void vt8500_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm) pwm_busy_wait(vt8500->base + 0x40 + pwm->hwpwm, (1 << 0)); writel(0, vt8500->base + (pwm->hwpwm << 4)); + + clk_disable(vt8500->clk); } static struct pwm_ops vt8500_pwm_ops = { @@ -107,12 +127,24 @@ static struct pwm_ops vt8500_pwm_ops = { .owner = THIS_MODULE, }; -static int __devinit pwm_probe(struct platform_device *pdev) +static const struct of_device_id vt8500_pwm_dt_ids[] = { + { .compatible = "via,vt8500-pwm", }, + { /* Sentinel */ } +}; +MODULE_DEVICE_TABLE(of, vt8500_pwm_dt_ids); + +static int vt8500_pwm_probe(struct platform_device *pdev) { struct vt8500_chip *chip; struct resource *r; + struct device_node *np = pdev->dev.of_node; int ret; + if (!np) { + dev_err(&pdev->dev, "invalid devicetree node\n"); + return -EINVAL; + } + chip = devm_kzalloc(&pdev->dev, sizeof(*chip), GFP_KERNEL); if (chip == NULL) { dev_err(&pdev->dev, "failed to allocate memory\n"); @@ -124,6 +156,12 @@ static int __devinit pwm_probe(struct platform_device *pdev) chip->chip.base = -1; chip->chip.npwm = VT8500_NR_PWMS; + chip->clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(chip->clk)) { + dev_err(&pdev->dev, "clock source not specified\n"); + return PTR_ERR(chip->clk); + } + r = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (r == NULL) { dev_err(&pdev->dev, "no memory resource defined\n"); @@ -131,18 +169,26 @@ static int __devinit pwm_probe(struct platform_device *pdev) } chip->base = devm_request_and_ioremap(&pdev->dev, r); - if (chip->base == NULL) + if (!chip->base) return -EADDRNOTAVAIL; + ret = clk_prepare(chip->clk); + if (ret < 0) { + dev_err(&pdev->dev, "failed to prepare clock\n"); + return ret; + } + ret = pwmchip_add(&chip->chip); - if (ret < 0) + if (ret < 0) { + dev_err(&pdev->dev, "failed to add PWM chip\n"); return ret; + } platform_set_drvdata(pdev, chip); return ret; } -static int __devexit pwm_remove(struct platform_device *pdev) +static int vt8500_pwm_remove(struct platform_device *pdev) { struct vt8500_chip *chip; @@ -150,28 +196,22 @@ static int __devexit pwm_remove(struct platform_device *pdev) if (chip == NULL) return -ENODEV; + clk_unprepare(chip->clk); + return pwmchip_remove(&chip->chip); } -static struct platform_driver pwm_driver = { +static struct platform_driver vt8500_pwm_driver = { + .probe = vt8500_pwm_probe, + .remove = vt8500_pwm_remove, .driver = { .name = "vt8500-pwm", .owner = THIS_MODULE, + .of_match_table = vt8500_pwm_dt_ids, }, - .probe = pwm_probe, - .remove = __devexit_p(pwm_remove), }; +module_platform_driver(vt8500_pwm_driver); -static int __init pwm_init(void) -{ - return platform_driver_register(&pwm_driver); -} -arch_initcall(pwm_init); - -static void __exit pwm_exit(void) -{ - platform_driver_unregister(&pwm_driver); -} -module_exit(pwm_exit); - -MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("VT8500 PWM Driver"); +MODULE_AUTHOR("Tony Prisk "); +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3-59-g8ed1b From bb3215e97bb1ab917ed858322a9399fb32e69e49 Mon Sep 17 00:00:00 2001 From: Tekkaman Ninja Date: Sun, 28 Oct 2012 11:17:59 +0800 Subject: Documentation: Fixes a term's translation in Documentation/zh_CN/arm64/memory.txt MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes a term's translation in Documentation/zh_CN/arm64/memory.txt The modification is based on Catalin Marinas 's suggestion: "memory" in "ffffffc000000000 ffffffffffffffff 256GB memory" should be treated as "kernel logical memory map", so the translation should be "内核逻辑内存映射". Signed-off-by: Fu Wei Signed-off-by: Greg Kroah-Hartman --- Documentation/zh_CN/arm64/memory.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Documentation') diff --git a/Documentation/zh_CN/arm64/memory.txt b/Documentation/zh_CN/arm64/memory.txt index 83b519314706..2a744997d30e 100644 --- a/Documentation/zh_CN/arm64/memory.txt +++ b/Documentation/zh_CN/arm64/memory.txt @@ -61,7 +61,7 @@ ffffffbe00000000 ffffffbffbffffff ~8GB [防护页,未来用于 vmmemap] ffffffbffc000000 ffffffbfffffffff 64MB 模块 -ffffffc000000000 ffffffffffffffff 256GB 内存空间 +ffffffc000000000 ffffffffffffffff 256GB 内核逻辑内存映射 4KB 页大小的转换表查找: -- cgit v1.2.3-59-g8ed1b From 9750a3ade7b635a18f04371b4fddad0de0b4e6d8 Mon Sep 17 00:00:00 2001 From: Richard Cochran Date: Mon, 29 Oct 2012 08:45:15 +0000 Subject: cpsw: support both silicon versions This patch fixes the cpsw driver to operate correctly with both the dm814x and the am335x versions of the switch hardware. Signed-off-by: Richard Cochran Signed-off-by: David S. Miller --- Documentation/devicetree/bindings/net/cpsw.txt | 8 +- drivers/net/ethernet/ti/cpsw.c | 106 +++++++++++++++++++++---- 2 files changed, 94 insertions(+), 20 deletions(-) (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/net/cpsw.txt b/Documentation/devicetree/bindings/net/cpsw.txt index dcaabe9fe869..3af47b78caea 100644 --- a/Documentation/devicetree/bindings/net/cpsw.txt +++ b/Documentation/devicetree/bindings/net/cpsw.txt @@ -59,14 +59,14 @@ Examples: mac_control = <0x20>; slaves = <2>; cpsw_emac0: slave@0 { - slave_reg_ofs = <0x208>; + slave_reg_ofs = <0x200>; sliver_reg_ofs = <0xd80>; phy_id = "davinci_mdio.16:00"; /* Filled in by U-Boot */ mac-address = [ 00 00 00 00 00 00 ]; }; cpsw_emac1: slave@1 { - slave_reg_ofs = <0x308>; + slave_reg_ofs = <0x300>; sliver_reg_ofs = <0xdc0>; phy_id = "davinci_mdio.16:01"; /* Filled in by U-Boot */ @@ -93,14 +93,14 @@ Examples: mac_control = <0x20>; slaves = <2>; cpsw_emac0: slave@0 { - slave_reg_ofs = <0x208>; + slave_reg_ofs = <0x200>; sliver_reg_ofs = <0xd80>; phy_id = "davinci_mdio.16:00"; /* Filled in by U-Boot */ mac-address = [ 00 00 00 00 00 00 ]; }; cpsw_emac1: slave@1 { - slave_reg_ofs = <0x308>; + slave_reg_ofs = <0x300>; sliver_reg_ofs = <0xdc0>; phy_id = "davinci_mdio.16:01"; /* Filled in by U-Boot */ diff --git a/drivers/net/ethernet/ti/cpsw.c b/drivers/net/ethernet/ti/cpsw.c index 5c427cf1ef87..588f5c340490 100644 --- a/drivers/net/ethernet/ti/cpsw.c +++ b/drivers/net/ethernet/ti/cpsw.c @@ -160,18 +160,74 @@ struct cpsw_ss_regs { u32 dlr_ltype; }; -struct cpsw_slave_regs { - u32 max_blks; - u32 blk_cnt; - u32 flow_thresh; - u32 port_vlan; - u32 tx_pri_map; - u32 ts_ctl; - u32 ts_seq_ltype; - u32 ts_vlan; - u32 sa_lo; - u32 sa_hi; -}; +/* CPSW_PORT_V1 */ +#define CPSW1_MAX_BLKS 0x00 /* Maximum FIFO Blocks */ +#define CPSW1_BLK_CNT 0x04 /* FIFO Block Usage Count (Read Only) */ +#define CPSW1_TX_IN_CTL 0x08 /* Transmit FIFO Control */ +#define CPSW1_PORT_VLAN 0x0c /* VLAN Register */ +#define CPSW1_TX_PRI_MAP 0x10 /* Tx Header Priority to Switch Pri Mapping */ +#define CPSW1_TS_CTL 0x14 /* Time Sync Control */ +#define CPSW1_TS_SEQ_LTYPE 0x18 /* Time Sync Sequence ID Offset and Msg Type */ +#define CPSW1_TS_VLAN 0x1c /* Time Sync VLAN1 and VLAN2 */ + +/* CPSW_PORT_V2 */ +#define CPSW2_CONTROL 0x00 /* Control Register */ +#define CPSW2_MAX_BLKS 0x08 /* Maximum FIFO Blocks */ +#define CPSW2_BLK_CNT 0x0c /* FIFO Block Usage Count (Read Only) */ +#define CPSW2_TX_IN_CTL 0x10 /* Transmit FIFO Control */ +#define CPSW2_PORT_VLAN 0x14 /* VLAN Register */ +#define CPSW2_TX_PRI_MAP 0x18 /* Tx Header Priority to Switch Pri Mapping */ +#define CPSW2_TS_SEQ_MTYPE 0x1c /* Time Sync Sequence ID Offset and Msg Type */ + +/* CPSW_PORT_V1 and V2 */ +#define SA_LO 0x20 /* CPGMAC_SL Source Address Low */ +#define SA_HI 0x24 /* CPGMAC_SL Source Address High */ +#define SEND_PERCENT 0x28 /* Transmit Queue Send Percentages */ + +/* CPSW_PORT_V2 only */ +#define RX_DSCP_PRI_MAP0 0x30 /* Rx DSCP Priority to Rx Packet Mapping */ +#define RX_DSCP_PRI_MAP1 0x34 /* Rx DSCP Priority to Rx Packet Mapping */ +#define RX_DSCP_PRI_MAP2 0x38 /* Rx DSCP Priority to Rx Packet Mapping */ +#define RX_DSCP_PRI_MAP3 0x3c /* Rx DSCP Priority to Rx Packet Mapping */ +#define RX_DSCP_PRI_MAP4 0x40 /* Rx DSCP Priority to Rx Packet Mapping */ +#define RX_DSCP_PRI_MAP5 0x44 /* Rx DSCP Priority to Rx Packet Mapping */ +#define RX_DSCP_PRI_MAP6 0x48 /* Rx DSCP Priority to Rx Packet Mapping */ +#define RX_DSCP_PRI_MAP7 0x4c /* Rx DSCP Priority to Rx Packet Mapping */ + +/* Bit definitions for the CPSW2_CONTROL register */ +#define PASS_PRI_TAGGED (1<<24) /* Pass Priority Tagged */ +#define VLAN_LTYPE2_EN (1<<21) /* VLAN LTYPE 2 enable */ +#define VLAN_LTYPE1_EN (1<<20) /* VLAN LTYPE 1 enable */ +#define DSCP_PRI_EN (1<<16) /* DSCP Priority Enable */ +#define TS_320 (1<<14) /* Time Sync Dest Port 320 enable */ +#define TS_319 (1<<13) /* Time Sync Dest Port 319 enable */ +#define TS_132 (1<<12) /* Time Sync Dest IP Addr 132 enable */ +#define TS_131 (1<<11) /* Time Sync Dest IP Addr 131 enable */ +#define TS_130 (1<<10) /* Time Sync Dest IP Addr 130 enable */ +#define TS_129 (1<<9) /* Time Sync Dest IP Addr 129 enable */ +#define TS_BIT8 (1<<8) /* ts_ttl_nonzero? */ +#define TS_ANNEX_D_EN (1<<4) /* Time Sync Annex D enable */ +#define TS_LTYPE2_EN (1<<3) /* Time Sync LTYPE 2 enable */ +#define TS_LTYPE1_EN (1<<2) /* Time Sync LTYPE 1 enable */ +#define TS_TX_EN (1<<1) /* Time Sync Transmit Enable */ +#define TS_RX_EN (1<<0) /* Time Sync Receive Enable */ + +#define CTRL_TS_BITS \ + (TS_320 | TS_319 | TS_132 | TS_131 | TS_130 | TS_129 | TS_BIT8 | \ + TS_ANNEX_D_EN | TS_LTYPE1_EN) + +#define CTRL_ALL_TS_MASK (CTRL_TS_BITS | TS_TX_EN | TS_RX_EN) +#define CTRL_TX_TS_BITS (CTRL_TS_BITS | TS_TX_EN) +#define CTRL_RX_TS_BITS (CTRL_TS_BITS | TS_RX_EN) + +/* Bit definitions for the CPSW2_TS_SEQ_MTYPE register */ +#define TS_SEQ_ID_OFFSET_SHIFT (16) /* Time Sync Sequence ID Offset */ +#define TS_SEQ_ID_OFFSET_MASK (0x3f) +#define TS_MSG_TYPE_EN_SHIFT (0) /* Time Sync Message Type Enable */ +#define TS_MSG_TYPE_EN_MASK (0xffff) + +/* The PTP event messages - Sync, Delay_Req, Pdelay_Req, and Pdelay_Resp. */ +#define EVENT_MSG_BITS ((1<<0) | (1<<1) | (1<<2) | (1<<3)) struct cpsw_host_regs { u32 max_blks; @@ -197,7 +253,7 @@ struct cpsw_sliver_regs { }; struct cpsw_slave { - struct cpsw_slave_regs __iomem *regs; + void __iomem *regs; struct cpsw_sliver_regs __iomem *sliver; int slave_num; u32 mac_control; @@ -205,6 +261,16 @@ struct cpsw_slave { struct phy_device *phy; }; +static inline u32 slave_read(struct cpsw_slave *slave, u32 offset) +{ + return __raw_readl(slave->regs + offset); +} + +static inline void slave_write(struct cpsw_slave *slave, u32 val, u32 offset) +{ + __raw_writel(val, slave->regs + offset); +} + struct cpsw_priv { spinlock_t lock; struct platform_device *pdev; @@ -396,8 +462,8 @@ static inline void soft_reset(const char *module, void __iomem *reg) static void cpsw_set_slave_mac(struct cpsw_slave *slave, struct cpsw_priv *priv) { - __raw_writel(mac_hi(priv->mac_addr), &slave->regs->sa_hi); - __raw_writel(mac_lo(priv->mac_addr), &slave->regs->sa_lo); + slave_write(slave, mac_hi(priv->mac_addr), SA_HI); + slave_write(slave, mac_lo(priv->mac_addr), SA_LO); } static void _cpsw_adjust_link(struct cpsw_slave *slave, @@ -483,7 +549,15 @@ static void cpsw_slave_open(struct cpsw_slave *slave, struct cpsw_priv *priv) /* setup priority mapping */ __raw_writel(RX_PRIORITY_MAPPING, &slave->sliver->rx_pri_map); - __raw_writel(TX_PRIORITY_MAPPING, &slave->regs->tx_pri_map); + + switch (priv->version) { + case CPSW_VERSION_1: + slave_write(slave, TX_PRIORITY_MAPPING, CPSW1_TX_PRI_MAP); + break; + case CPSW_VERSION_2: + slave_write(slave, TX_PRIORITY_MAPPING, CPSW2_TX_PRI_MAP); + break; + } /* setup max packet size, and mac address */ __raw_writel(priv->rx_packet_max, &slave->sliver->rx_maxlen); -- cgit v1.2.3-59-g8ed1b From 6b60393e08f9263c7b129d54eeb261e8f970175c Mon Sep 17 00:00:00 2001 From: Richard Cochran Date: Mon, 29 Oct 2012 08:45:17 +0000 Subject: cpsw: add a DT field for the cpts offset Signed-off-by: Richard Cochran Signed-off-by: David S. Miller --- Documentation/devicetree/bindings/net/cpsw.txt | 3 +++ drivers/net/ethernet/ti/cpsw.c | 7 +++++++ include/linux/platform_data/cpsw.h | 1 + 3 files changed, 11 insertions(+) (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/net/cpsw.txt b/Documentation/devicetree/bindings/net/cpsw.txt index 3af47b78caea..dba014fc5e7b 100644 --- a/Documentation/devicetree/bindings/net/cpsw.txt +++ b/Documentation/devicetree/bindings/net/cpsw.txt @@ -16,6 +16,7 @@ Required properties: - ale_entries : Specifies No of entries ALE can hold - host_port_reg_ofs : Specifies host port register offset - hw_stats_reg_ofs : Specifies hardware statistics register offset +- cpts_reg_ofs : Specifies the offset of the CPTS registers - bd_ram_ofs : Specifies internal desciptor RAM offset - bd_ram_size : Specifies internal descriptor RAM size - rx_descs : Specifies number of Rx descriptors @@ -52,6 +53,7 @@ Examples: ale_entries = <1024>; host_port_reg_ofs = <0x108>; hw_stats_reg_ofs = <0x900>; + cpts_reg_ofs = <0xc00>; bd_ram_ofs = <0x2000>; bd_ram_size = <0x2000>; no_bd_ram = <0>; @@ -86,6 +88,7 @@ Examples: ale_entries = <1024>; host_port_reg_ofs = <0x108>; hw_stats_reg_ofs = <0x900>; + cpts_reg_ofs = <0xc00>; bd_ram_ofs = <0x2000>; bd_ram_size = <0x2000>; no_bd_ram = <0>; diff --git a/drivers/net/ethernet/ti/cpsw.c b/drivers/net/ethernet/ti/cpsw.c index 588f5c340490..f1af5e08cabb 100644 --- a/drivers/net/ethernet/ti/cpsw.c +++ b/drivers/net/ethernet/ti/cpsw.c @@ -912,6 +912,13 @@ static int cpsw_probe_dt(struct cpsw_platform_data *data, } data->hw_stats_reg_ofs = prop; + if (of_property_read_u32(node, "cpts_reg_ofs", &prop)) { + pr_err("Missing cpts_reg_ofs property in the DT.\n"); + ret = -EINVAL; + goto error_ret; + } + data->cpts_reg_ofs = prop; + if (of_property_read_u32(node, "bd_ram_ofs", &prop)) { pr_err("Missing bd_ram_ofs property in the DT.\n"); ret = -EINVAL; diff --git a/include/linux/platform_data/cpsw.h b/include/linux/platform_data/cpsw.h index c4e23d029498..a052b1dca957 100644 --- a/include/linux/platform_data/cpsw.h +++ b/include/linux/platform_data/cpsw.h @@ -41,6 +41,7 @@ struct cpsw_platform_data { u32 host_port_num; /* The port number for the host port */ u32 hw_stats_reg_ofs; /* cpsw hardware statistics counters */ + u32 cpts_reg_ofs; /* cpts registers */ u32 bd_ram_ofs; /* embedded buffer descriptor RAM offset*/ u32 bd_ram_size; /*buffer descriptor ram size */ -- cgit v1.2.3-59-g8ed1b From 78ca0b287314ad6b7b06161b3ff9b13e8a8bcce0 Mon Sep 17 00:00:00 2001 From: Richard Cochran Date: Mon, 29 Oct 2012 08:45:18 +0000 Subject: cpsw: add a DT field for the active time stamping port Because time stamping on both external ports of the switch simultaneously is positively useless from the application's point of view, this patch provides a DT configuration method to choose the active port. Signed-off-by: Richard Cochran Signed-off-by: David S. Miller --- Documentation/devicetree/bindings/net/cpsw.txt | 3 +++ drivers/net/ethernet/ti/cpsw.c | 7 +++++++ include/linux/platform_data/cpsw.h | 1 + 3 files changed, 11 insertions(+) (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/net/cpsw.txt b/Documentation/devicetree/bindings/net/cpsw.txt index dba014fc5e7b..9f61f2b51681 100644 --- a/Documentation/devicetree/bindings/net/cpsw.txt +++ b/Documentation/devicetree/bindings/net/cpsw.txt @@ -23,6 +23,7 @@ Required properties: - mac_control : Specifies Default MAC control register content for the specific platform - slaves : Specifies number for slaves +- cpts_active_slave : Specifies the slave to use for time stamping - slave_reg_ofs : Specifies slave register offset - sliver_reg_ofs : Specifies slave sliver register offset - phy_id : Specifies slave phy id @@ -60,6 +61,7 @@ Examples: rx_descs = <64>; mac_control = <0x20>; slaves = <2>; + cpts_active_slave = <0>; cpsw_emac0: slave@0 { slave_reg_ofs = <0x200>; sliver_reg_ofs = <0xd80>; @@ -95,6 +97,7 @@ Examples: rx_descs = <64>; mac_control = <0x20>; slaves = <2>; + cpts_active_slave = <0>; cpsw_emac0: slave@0 { slave_reg_ofs = <0x200>; sliver_reg_ofs = <0xd80>; diff --git a/drivers/net/ethernet/ti/cpsw.c b/drivers/net/ethernet/ti/cpsw.c index f1af5e08cabb..f16579123c1b 100644 --- a/drivers/net/ethernet/ti/cpsw.c +++ b/drivers/net/ethernet/ti/cpsw.c @@ -847,6 +847,13 @@ static int cpsw_probe_dt(struct cpsw_platform_data *data, } data->slaves = prop; + if (of_property_read_u32(node, "cpts_active_slave", &prop)) { + pr_err("Missing cpts_active_slave property in the DT.\n"); + ret = -EINVAL; + goto error_ret; + } + data->cpts_active_slave = prop; + data->slave_data = kzalloc(sizeof(struct cpsw_slave_data) * data->slaves, GFP_KERNEL); if (!data->slave_data) { diff --git a/include/linux/platform_data/cpsw.h b/include/linux/platform_data/cpsw.h index a052b1dca957..15a077eb0689 100644 --- a/include/linux/platform_data/cpsw.h +++ b/include/linux/platform_data/cpsw.h @@ -33,6 +33,7 @@ struct cpsw_platform_data { u32 slaves; /* number of slave cpgmac ports */ struct cpsw_slave_data *slave_data; + u32 cpts_active_slave; /* time stamping slave */ u32 ale_reg_ofs; /* address lookup engine reg offset */ u32 ale_entries; /* ale table size */ -- cgit v1.2.3-59-g8ed1b From 00ab94eeaf6c1ad38ad7368c5148fed31403c8a2 Mon Sep 17 00:00:00 2001 From: Richard Cochran Date: Mon, 29 Oct 2012 08:45:19 +0000 Subject: cpts: specify the input clock frequency via DT This patch adds a way to configure the CPTS input clock scaling factors via the device tree. Signed-off-by: Richard Cochran Signed-off-by: David S. Miller --- Documentation/devicetree/bindings/net/cpsw.txt | 6 ++++++ drivers/net/ethernet/ti/cpsw.c | 14 ++++++++++++++ include/linux/platform_data/cpsw.h | 2 ++ 3 files changed, 22 insertions(+) (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/net/cpsw.txt b/Documentation/devicetree/bindings/net/cpsw.txt index 9f61f2b51681..221460714c56 100644 --- a/Documentation/devicetree/bindings/net/cpsw.txt +++ b/Documentation/devicetree/bindings/net/cpsw.txt @@ -24,6 +24,8 @@ Required properties: for the specific platform - slaves : Specifies number for slaves - cpts_active_slave : Specifies the slave to use for time stamping +- cpts_clock_mult : Numerator to convert input clock ticks into nanoseconds +- cpts_clock_shift : Denominator to convert input clock ticks into nanoseconds - slave_reg_ofs : Specifies slave register offset - sliver_reg_ofs : Specifies slave sliver register offset - phy_id : Specifies slave phy id @@ -62,6 +64,8 @@ Examples: mac_control = <0x20>; slaves = <2>; cpts_active_slave = <0>; + cpts_clock_mult = <0x80000000>; + cpts_clock_shift = <29>; cpsw_emac0: slave@0 { slave_reg_ofs = <0x200>; sliver_reg_ofs = <0xd80>; @@ -98,6 +102,8 @@ Examples: mac_control = <0x20>; slaves = <2>; cpts_active_slave = <0>; + cpts_clock_mult = <0x80000000>; + cpts_clock_shift = <29>; cpsw_emac0: slave@0 { slave_reg_ofs = <0x200>; sliver_reg_ofs = <0xd80>; diff --git a/drivers/net/ethernet/ti/cpsw.c b/drivers/net/ethernet/ti/cpsw.c index f16579123c1b..c04627cd60dd 100644 --- a/drivers/net/ethernet/ti/cpsw.c +++ b/drivers/net/ethernet/ti/cpsw.c @@ -854,6 +854,20 @@ static int cpsw_probe_dt(struct cpsw_platform_data *data, } data->cpts_active_slave = prop; + if (of_property_read_u32(node, "cpts_clock_mult", &prop)) { + pr_err("Missing cpts_clock_mult property in the DT.\n"); + ret = -EINVAL; + goto error_ret; + } + data->cpts_clock_mult = prop; + + if (of_property_read_u32(node, "cpts_clock_shift", &prop)) { + pr_err("Missing cpts_clock_shift property in the DT.\n"); + ret = -EINVAL; + goto error_ret; + } + data->cpts_clock_shift = prop; + data->slave_data = kzalloc(sizeof(struct cpsw_slave_data) * data->slaves, GFP_KERNEL); if (!data->slave_data) { diff --git a/include/linux/platform_data/cpsw.h b/include/linux/platform_data/cpsw.h index 15a077eb0689..b5c16c3df458 100644 --- a/include/linux/platform_data/cpsw.h +++ b/include/linux/platform_data/cpsw.h @@ -34,6 +34,8 @@ struct cpsw_platform_data { u32 slaves; /* number of slave cpgmac ports */ struct cpsw_slave_data *slave_data; u32 cpts_active_slave; /* time stamping slave */ + u32 cpts_clock_mult; /* convert input clock ticks to nanoseconds */ + u32 cpts_clock_shift; /* convert input clock ticks to nanoseconds */ u32 ale_reg_ofs; /* address lookup engine reg offset */ u32 ale_entries; /* ale table size */ -- cgit v1.2.3-59-g8ed1b From 279f1461432ccdec0b98c0bcbe0a8e2c0f6fdda5 Mon Sep 17 00:00:00 2001 From: Suresh Siddha Date: Mon, 22 Oct 2012 14:37:58 -0700 Subject: x86: apic: Use tsc deadline for oneshot when available If the TSC deadline mode is supported, LAPIC timer one-shot mode can be implemented using IA32_TSC_DEADLINE MSR. An interrupt will be generated when the TSC value equals or exceeds the value in the IA32_TSC_DEADLINE MSR. This enables us to skip the APIC calibration during boot. Also, in xapic mode, this enables us to skip the uncached apic access to re-arm the APIC timer. As this timer ticks at the high frequency TSC rate, we use the TSC_DIVISOR (32) to work with the 32-bit restrictions in the clockevent API's to avoid 64-bit divides etc (frequency is u32 and "unsigned long" in the set_next_event(), max_delta limits the next event to 32-bit for 32-bit kernel). Signed-off-by: Suresh Siddha Cc: venki@google.com Cc: len.brown@intel.com Link: http://lkml.kernel.org/r/1350941878.6017.31.camel@sbsiddha-desk.sc.intel.com Signed-off-by: Thomas Gleixner --- Documentation/kernel-parameters.txt | 4 ++ arch/x86/include/asm/msr-index.h | 2 + arch/x86/kernel/apic/apic.c | 73 +++++++++++++++++++++++++++---------- 3 files changed, 59 insertions(+), 20 deletions(-) (limited to 'Documentation') diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt index 9776f068306b..4aa9ca0de63c 100644 --- a/Documentation/kernel-parameters.txt +++ b/Documentation/kernel-parameters.txt @@ -1304,6 +1304,10 @@ bytes respectively. Such letter suffixes can also be entirely omitted. lapic [X86-32,APIC] Enable the local APIC even if BIOS disabled it. + lapic= [x86,APIC] "notscdeadline" Do not use TSC deadline + value for LAPIC timer one-shot implementation. Default + back to the programmable timer unit in the LAPIC. + lapic_timer_c2_ok [X86,APIC] trust the local apic timer in C2 power state. diff --git a/arch/x86/include/asm/msr-index.h b/arch/x86/include/asm/msr-index.h index 7f0edceb7563..e400cdb2dd65 100644 --- a/arch/x86/include/asm/msr-index.h +++ b/arch/x86/include/asm/msr-index.h @@ -337,6 +337,8 @@ #define MSR_IA32_MISC_ENABLE_TURBO_DISABLE (1ULL << 38) #define MSR_IA32_MISC_ENABLE_IP_PREF_DISABLE (1ULL << 39) +#define MSR_IA32_TSC_DEADLINE 0x000006E0 + /* P4/Xeon+ specific */ #define MSR_IA32_MCG_EAX 0x00000180 #define MSR_IA32_MCG_EBX 0x00000181 diff --git a/arch/x86/kernel/apic/apic.c b/arch/x86/kernel/apic/apic.c index b17416e72fbd..b994cc84aa7e 100644 --- a/arch/x86/kernel/apic/apic.c +++ b/arch/x86/kernel/apic/apic.c @@ -90,21 +90,6 @@ EXPORT_EARLY_PER_CPU_SYMBOL(x86_bios_cpu_apicid); */ DEFINE_EARLY_PER_CPU_READ_MOSTLY(int, x86_cpu_to_logical_apicid, BAD_APICID); -/* - * Knob to control our willingness to enable the local APIC. - * - * +1=force-enable - */ -static int force_enable_local_apic __initdata; -/* - * APIC command line parameters - */ -static int __init parse_lapic(char *arg) -{ - force_enable_local_apic = 1; - return 0; -} -early_param("lapic", parse_lapic); /* Local APIC was disabled by the BIOS and enabled by the kernel */ static int enabled_via_apicbase; @@ -133,6 +118,25 @@ static inline void imcr_apic_to_pic(void) } #endif +/* + * Knob to control our willingness to enable the local APIC. + * + * +1=force-enable + */ +static int force_enable_local_apic __initdata; +/* + * APIC command line parameters + */ +static int __init parse_lapic(char *arg) +{ + if (config_enabled(CONFIG_X86_32) && !arg) + force_enable_local_apic = 1; + else if (!strncmp(arg, "notscdeadline", 13)) + setup_clear_cpu_cap(X86_FEATURE_TSC_DEADLINE_TIMER); + return 0; +} +early_param("lapic", parse_lapic); + #ifdef CONFIG_X86_64 static int apic_calibrate_pmtmr __initdata; static __init int setup_apicpmtimer(char *s) @@ -315,6 +319,7 @@ int lapic_get_maxlvt(void) /* Clock divisor */ #define APIC_DIVISOR 16 +#define TSC_DIVISOR 32 /* * This function sets up the local APIC timer, with a timeout of @@ -333,6 +338,9 @@ static void __setup_APIC_LVTT(unsigned int clocks, int oneshot, int irqen) lvtt_value = LOCAL_TIMER_VECTOR; if (!oneshot) lvtt_value |= APIC_LVT_TIMER_PERIODIC; + else if (boot_cpu_has(X86_FEATURE_TSC_DEADLINE_TIMER)) + lvtt_value |= APIC_LVT_TIMER_TSCDEADLINE; + if (!lapic_is_integrated()) lvtt_value |= SET_APIC_TIMER_BASE(APIC_TIMER_BASE_DIV); @@ -341,6 +349,11 @@ static void __setup_APIC_LVTT(unsigned int clocks, int oneshot, int irqen) apic_write(APIC_LVTT, lvtt_value); + if (lvtt_value & APIC_LVT_TIMER_TSCDEADLINE) { + printk_once(KERN_DEBUG "TSC deadline timer enabled\n"); + return; + } + /* * Divide PICLK by 16 */ @@ -453,6 +466,16 @@ static int lapic_next_event(unsigned long delta, return 0; } +static int lapic_next_deadline(unsigned long delta, + struct clock_event_device *evt) +{ + u64 tsc; + + rdtscll(tsc); + wrmsrl(MSR_IA32_TSC_DEADLINE, tsc + (((u64) delta) * TSC_DIVISOR)); + return 0; +} + /* * Setup the lapic timer in periodic or oneshot mode */ @@ -533,7 +556,15 @@ static void __cpuinit setup_APIC_timer(void) memcpy(levt, &lapic_clockevent, sizeof(*levt)); levt->cpumask = cpumask_of(smp_processor_id()); - clockevents_register_device(levt); + if (this_cpu_has(X86_FEATURE_TSC_DEADLINE_TIMER)) { + levt->features &= ~(CLOCK_EVT_FEAT_PERIODIC | + CLOCK_EVT_FEAT_DUMMY); + levt->set_next_event = lapic_next_deadline; + clockevents_config_and_register(levt, + (tsc_khz / TSC_DIVISOR) * 1000, + 0xF, ~0UL); + } else + clockevents_register_device(levt); } /* @@ -661,7 +692,9 @@ static int __init calibrate_APIC_clock(void) * in the clockevent structure and return. */ - if (lapic_timer_frequency) { + if (boot_cpu_has(X86_FEATURE_TSC_DEADLINE_TIMER)) { + return 0; + } else if (lapic_timer_frequency) { apic_printk(APIC_VERBOSE, "lapic timer already calibrated %d\n", lapic_timer_frequency); lapic_clockevent.mult = div_sc(lapic_timer_frequency/APIC_DIVISOR, @@ -674,6 +707,9 @@ static int __init calibrate_APIC_clock(void) return 0; } + apic_printk(APIC_VERBOSE, "Using local APIC timer interrupts.\n" + "calibrating APIC timer ...\n"); + local_irq_disable(); /* Replace the global interrupt handler */ @@ -811,9 +847,6 @@ void __init setup_boot_APIC_clock(void) return; } - apic_printk(APIC_VERBOSE, "Using local APIC timer interrupts.\n" - "calibrating APIC timer ...\n"); - if (calibrate_APIC_clock()) { /* No broadcast on UP ! */ if (num_possible_cpus() > 1) -- cgit v1.2.3-59-g8ed1b From 7bcfaf54f591a0775254c4ea679faf615152ee3a Mon Sep 17 00:00:00 2001 From: Steven Rostedt Date: Thu, 1 Nov 2012 22:56:07 -0400 Subject: tracing: Add trace_options kernel command line parameter Add trace_options to the kernel command line parameter to be able to set options at early boot. For example, to enable stack dumps of events, add the following: trace_options=stacktrace This along with the trace_event option, you can get not only traces of the events but also the stack dumps with them. Requested-by: Frederic Weisbecker Cc: Peter Zijlstra Cc: Thomas Gleixner Signed-off-by: Steven Rostedt --- Documentation/kernel-parameters.txt | 16 +++++++++++ kernel/trace/trace.c | 54 ++++++++++++++++++++++++++----------- 2 files changed, 55 insertions(+), 15 deletions(-) (limited to 'Documentation') diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt index 9776f068306b..2b48c52464a1 100644 --- a/Documentation/kernel-parameters.txt +++ b/Documentation/kernel-parameters.txt @@ -2859,6 +2859,22 @@ bytes respectively. Such letter suffixes can also be entirely omitted. to facilitate early boot debugging. See also Documentation/trace/events.txt + trace_options=[option-list] + [FTRACE] Enable or disable tracer options at boot. + The option-list is a comma delimited list of options + that can be enabled or disabled just as if you were + to echo the option name into + + /sys/kernel/debug/tracing/trace_options + + For example, to enable stacktrace option (to dump the + stack trace of each event), add to the command line: + + trace_options=stacktrace + + See also Documentation/trace/ftrace.txt "trace options" + section. + transparent_hugepage= [KNL] Format: [always|madvise|never] diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c index 37d1c703e3ec..c1434b5ce4d1 100644 --- a/kernel/trace/trace.c +++ b/kernel/trace/trace.c @@ -155,6 +155,18 @@ static int __init set_ftrace_dump_on_oops(char *str) } __setup("ftrace_dump_on_oops", set_ftrace_dump_on_oops); + +static char trace_boot_options_buf[MAX_TRACER_SIZE] __initdata; +static char *trace_boot_options __initdata; + +static int __init set_trace_boot_options(char *str) +{ + strncpy(trace_boot_options_buf, str, MAX_TRACER_SIZE); + trace_boot_options = trace_boot_options_buf; + return 0; +} +__setup("trace_options=", set_trace_boot_options); + unsigned long long ns2usecs(cycle_t nsec) { nsec += 500; @@ -2838,24 +2850,14 @@ static void set_tracer_flags(unsigned int mask, int enabled) trace_printk_start_stop_comm(enabled); } -static ssize_t -tracing_trace_options_write(struct file *filp, const char __user *ubuf, - size_t cnt, loff_t *ppos) +static int trace_set_options(char *option) { - char buf[64]; char *cmp; int neg = 0; - int ret; + int ret = 0; int i; - if (cnt >= sizeof(buf)) - return -EINVAL; - - if (copy_from_user(&buf, ubuf, cnt)) - return -EFAULT; - - buf[cnt] = 0; - cmp = strstrip(buf); + cmp = strstrip(option); if (strncmp(cmp, "no", 2) == 0) { neg = 1; @@ -2874,10 +2876,25 @@ tracing_trace_options_write(struct file *filp, const char __user *ubuf, mutex_lock(&trace_types_lock); ret = set_tracer_option(current_trace, cmp, neg); mutex_unlock(&trace_types_lock); - if (ret) - return ret; } + return ret; +} + +static ssize_t +tracing_trace_options_write(struct file *filp, const char __user *ubuf, + size_t cnt, loff_t *ppos) +{ + char buf[64]; + + if (cnt >= sizeof(buf)) + return -EINVAL; + + if (copy_from_user(&buf, ubuf, cnt)) + return -EFAULT; + + trace_set_options(buf); + *ppos += cnt; return cnt; @@ -5133,6 +5150,13 @@ __init static int tracer_alloc_buffers(void) register_die_notifier(&trace_die_notifier); + while (trace_boot_options) { + char *option; + + option = strsep(&trace_boot_options, ","); + trace_set_options(option); + } + return 0; out_free_cpumask: -- cgit v1.2.3-59-g8ed1b From c99abb8f560798625323c7b21d5636259017825d Mon Sep 17 00:00:00 2001 From: Carlos Maiolino Date: Thu, 18 Oct 2012 12:28:58 -0300 Subject: xfs: Update mount options documentation Once inode64 is the default allocation mode now, kernel documentation should be updated to match this behaviour. Signed-off-by: Carlos Maiolino Reviewed-by: Mark Tinguely Signed-off-by: Ben Myers --- Documentation/filesystems/xfs.txt | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) (limited to 'Documentation') diff --git a/Documentation/filesystems/xfs.txt b/Documentation/filesystems/xfs.txt index 3fc0c31a6f5d..17187750270a 100644 --- a/Documentation/filesystems/xfs.txt +++ b/Documentation/filesystems/xfs.txt @@ -72,8 +72,15 @@ When mounting an XFS filesystem, the following options are accepted. Indicates that XFS is allowed to create inodes at any location in the filesystem, including those which will result in inode numbers occupying more than 32 bits of significance. This is - provided for backwards compatibility, but causes problems for - backup applications that cannot handle large inode numbers. + the default allocation option. Applications which do not handle + inode numbers bigger than 32 bits, should use inode32 option. + + inode32 + Indicates that XFS is limited to create inodes at locations which + will not result in inode numbers with more than 32 bits of + significance. This is provided for backwards compatibility, since + 64 bits inode numbers might cause problems for some applications + that cannot handle large inode numbers. largeio/nolargeio If "nolargeio" is specified, the optimal I/O reported in -- cgit v1.2.3-59-g8ed1b From d56a289be2ce01d1aa426a6cf45dede14a8db41e Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Sat, 3 Nov 2012 12:05:52 -0700 Subject: Input: pwm-beeper - add devicetree probing support A very simple binding, the only property is the phandle to the PWM. Signed-off-by: Sascha Hauer Reviewed-by: Thierry Reding Signed-off-by: Dmitry Torokhov --- Documentation/devicetree/bindings/input/pwm-beeper.txt | 7 +++++++ drivers/input/misc/Kconfig | 2 +- drivers/input/misc/pwm-beeper.c | 14 +++++++++++++- 3 files changed, 21 insertions(+), 2 deletions(-) create mode 100644 Documentation/devicetree/bindings/input/pwm-beeper.txt (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/input/pwm-beeper.txt b/Documentation/devicetree/bindings/input/pwm-beeper.txt new file mode 100644 index 000000000000..be332ae4f2d6 --- /dev/null +++ b/Documentation/devicetree/bindings/input/pwm-beeper.txt @@ -0,0 +1,7 @@ +* PWM beeper device tree bindings + +Registers a PWM device as beeper. + +Required properties: +- compatible: should be "pwm-beeper" +- pwms: phandle to the physical PWM device diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig index c8353570344c..0f959d744e74 100644 --- a/drivers/input/misc/Kconfig +++ b/drivers/input/misc/Kconfig @@ -441,7 +441,7 @@ config INPUT_PCF8574 config INPUT_PWM_BEEPER tristate "PWM beeper support" - depends on HAVE_PWM + depends on HAVE_PWM || PWM help Say Y here to get support for PWM based beeper devices. diff --git a/drivers/input/misc/pwm-beeper.c b/drivers/input/misc/pwm-beeper.c index fc84c8a51147..502544c7e0a0 100644 --- a/drivers/input/misc/pwm-beeper.c +++ b/drivers/input/misc/pwm-beeper.c @@ -75,7 +75,11 @@ static int __devinit pwm_beeper_probe(struct platform_device *pdev) if (!beeper) return -ENOMEM; - beeper->pwm = pwm_request(pwm_id, "pwm beeper"); + beeper->pwm = pwm_get(&pdev->dev, NULL); + if (IS_ERR(beeper->pwm)) { + dev_dbg(&pdev->dev, "unable to request PWM, trying legacy API\n"); + beeper->pwm = pwm_request(pwm_id, "pwm beeper"); + } if (IS_ERR(beeper->pwm)) { error = PTR_ERR(beeper->pwm); @@ -171,6 +175,13 @@ static SIMPLE_DEV_PM_OPS(pwm_beeper_pm_ops, #define PWM_BEEPER_PM_OPS NULL #endif +#ifdef CONFIG_OF +static const struct of_device_id pwm_beeper_match[] = { + { .compatible = "pwm-beeper", }, + { }, +}; +#endif + static struct platform_driver pwm_beeper_driver = { .probe = pwm_beeper_probe, .remove = __devexit_p(pwm_beeper_remove), @@ -178,6 +189,7 @@ static struct platform_driver pwm_beeper_driver = { .name = "pwm-beeper", .owner = THIS_MODULE, .pm = PWM_BEEPER_PM_OPS, + .of_match_table = of_match_ptr(pwm_beeper_match), }, }; module_platform_driver(pwm_beeper_driver); -- cgit v1.2.3-59-g8ed1b From 6ea083b18d39b35730e59587128cfd6f68bd30bc Mon Sep 17 00:00:00 2001 From: Durgadoss R Date: Tue, 18 Sep 2012 11:05:06 +0530 Subject: Thermal: Add documentation for platform layer data This patch adds documentation for thermal_bind_params and thermal_zone_params structures. Also, adds details on EXPORT_SYMBOL APIs. Signed-off-by: Durgadoss R Signed-off-by: Zhang Rui --- Documentation/thermal/sysfs-api.txt | 64 +++++++++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) (limited to 'Documentation') diff --git a/Documentation/thermal/sysfs-api.txt b/Documentation/thermal/sysfs-api.txt index ca1a1a34970e..88c02334e356 100644 --- a/Documentation/thermal/sysfs-api.txt +++ b/Documentation/thermal/sysfs-api.txt @@ -112,6 +112,29 @@ temperature) and throttle appropriate devices. trip: indicates which trip point the cooling devices is associated with in this thermal zone. +1.4 Thermal Zone Parameters +1.4.1 struct thermal_bind_params + This structure defines the following parameters that are used to bind + a zone with a cooling device for a particular trip point. + .cdev: The cooling device pointer + .weight: The 'influence' of a particular cooling device on this zone. + This is on a percentage scale. The sum of all these weights + (for a particular zone) cannot exceed 100. + .trip_mask:This is a bit mask that gives the binding relation between + this thermal zone and cdev, for a particular trip point. + If nth bit is set, then the cdev and thermal zone are bound + for trip point n. + .match: This call back returns success(0) if the 'tz and cdev' need to + be bound, as per platform data. +1.4.2 struct thermal_zone_params + This structure defines the platform level parameters for a thermal zone. + This data, for each thermal zone should come from the platform layer. + This is an optional feature where some platforms can choose not to + provide this data. + .governor_name: Name of the thermal governor used for this zone + .num_tbps: Number of thermal_bind_params entries for this zone + .tbp: thermal_bind_params entries + 2. sysfs attributes structure RO read only value @@ -126,6 +149,7 @@ Thermal zone device sys I/F, created once it's registered: |---type: Type of the thermal zone |---temp: Current temperature |---mode: Working mode of the thermal zone + |---policy: Thermal governor used for this zone |---trip_point_[0-*]_temp: Trip point temperature |---trip_point_[0-*]_type: Trip point type |---trip_point_[0-*]_hyst: Hysteresis value for this trip point @@ -187,6 +211,10 @@ mode charge of the thermal management. RW, Optional +policy + One of the various thermal governors used for a particular zone. + RW, Required + trip_point_[0-*]_temp The temperature above which trip point will be fired. Unit: millidegree Celsius @@ -264,6 +292,7 @@ method, the sys I/F structure will be built like this: |---type: acpitz |---temp: 37000 |---mode: enabled + |---policy: step_wise |---trip_point_0_temp: 100000 |---trip_point_0_type: critical |---trip_point_1_temp: 80000 @@ -305,3 +334,38 @@ to a thermal_zone_device when it registers itself with the framework. The event will be one of:{THERMAL_AUX0, THERMAL_AUX1, THERMAL_CRITICAL, THERMAL_DEV_FAULT}. Notification can be sent when the current temperature crosses any of the configured thresholds. + +5. Export Symbol APIs: + +5.1: get_tz_trend: +This function returns the trend of a thermal zone, i.e the rate of change +of temperature of the thermal zone. Ideally, the thermal sensor drivers +are supposed to implement the callback. If they don't, the thermal +framework calculated the trend by comparing the previous and the current +temperature values. + +5.2:get_thermal_instance: +This function returns the thermal_instance corresponding to a given +{thermal_zone, cooling_device, trip_point} combination. Returns NULL +if such an instance does not exist. + +5.3:notify_thermal_framework: +This function handles the trip events from sensor drivers. It starts +throttling the cooling devices according to the policy configured. +For CRITICAL and HOT trip points, this notifies the respective drivers, +and does actual throttling for other trip points i.e ACTIVE and PASSIVE. +The throttling policy is based on the configured platform data; if no +platform data is provided, this uses the step_wise throttling policy. + +5.4:thermal_cdev_update: +This function serves as an arbitrator to set the state of a cooling +device. It sets the cooling device to the deepest cooling state if +possible. + +5.5:thermal_register_governor: +This function lets the various thermal governors to register themselves +with the Thermal framework. At run time, depending on a zone's platform +data, a particular governor is used for throttling. + +5.6:thermal_unregister_governor: +This function unregisters a governor from the thermal framework. -- cgit v1.2.3-59-g8ed1b From 9e78eb8fbba5b00678d3d20e8e435cd0500716f3 Mon Sep 17 00:00:00 2001 From: Stefan Achatz Date: Sun, 4 Nov 2012 09:38:58 +0100 Subject: HID: roccat: enable Savu device reset Device can be reset to factory state by sending a command via info sysfs attr. Changed from ro to rw to enable this feature. Signed-off-by: Stefan Achatz Signed-off-by: Jiri Kosina --- Documentation/ABI/testing/sysfs-driver-hid-roccat-savu | 3 +-- drivers/hid/hid-roccat-savu.c | 4 ++-- 2 files changed, 3 insertions(+), 4 deletions(-) (limited to 'Documentation') diff --git a/Documentation/ABI/testing/sysfs-driver-hid-roccat-savu b/Documentation/ABI/testing/sysfs-driver-hid-roccat-savu index b42922cf6b1f..f1e02a98bd9d 100644 --- a/Documentation/ABI/testing/sysfs-driver-hid-roccat-savu +++ b/Documentation/ABI/testing/sysfs-driver-hid-roccat-savu @@ -40,8 +40,8 @@ What: /sys/bus/usb/devices/-:./ Description: When read, this file returns general data like firmware version. + When written, the device can be reset. The data is 8 bytes long. - This file is readonly. Users: http://roccat.sourceforge.net What: /sys/bus/usb/devices/-:./::./savu/roccatsavu/macro @@ -74,4 +74,3 @@ Description: The mouse has a Avago ADNS-3090 sensor. This file allows reading and writing of the mouse sensors registers. The data has to be 4 bytes long. Users: http://roccat.sourceforge.net - diff --git a/drivers/hid/hid-roccat-savu.c b/drivers/hid/hid-roccat-savu.c index 014afba407e0..31747a29c093 100644 --- a/drivers/hid/hid-roccat-savu.c +++ b/drivers/hid/hid-roccat-savu.c @@ -120,7 +120,7 @@ SAVU_SYSFS_RW(profile, PROFILE) SAVU_SYSFS_RW(general, GENERAL) SAVU_SYSFS_RW(buttons, BUTTONS) SAVU_SYSFS_RW(macro, MACRO) -SAVU_SYSFS_R(info, INFO) +SAVU_SYSFS_RW(info, INFO) SAVU_SYSFS_RW(sensor, SENSOR) static struct bin_attribute savu_bin_attributes[] = { @@ -129,7 +129,7 @@ static struct bin_attribute savu_bin_attributes[] = { SAVU_BIN_ATTRIBUTE_RW(general, GENERAL), SAVU_BIN_ATTRIBUTE_RW(buttons, BUTTONS), SAVU_BIN_ATTRIBUTE_RW(macro, MACRO), - SAVU_BIN_ATTRIBUTE_R(info, INFO), + SAVU_BIN_ATTRIBUTE_RW(info, INFO), SAVU_BIN_ATTRIBUTE_RW(sensor, SENSOR), __ATTR_NULL }; -- cgit v1.2.3-59-g8ed1b From fabe51eb68d450d25b18882988b29626f51592fd Mon Sep 17 00:00:00 2001 From: Stefan Achatz Date: Sun, 4 Nov 2012 09:39:04 +0100 Subject: HID: roccat: enable Koneplus device reset Device can be reset to factory state by sending a command via info sysfs attr. Added binary attribute info for this purpose, which obsoletes firmware_version attribute. Signed-off-by: Stefan Achatz Signed-off-by: Jiri Kosina --- .../ABI/obsolete/sysfs-driver-hid-roccat-koneplus | 11 +++++++++++ .../ABI/testing/sysfs-driver-hid-roccat-koneplus | 13 +++++-------- drivers/hid/hid-roccat-koneplus.c | 22 ++++++++++++++++++++++ 3 files changed, 38 insertions(+), 8 deletions(-) (limited to 'Documentation') diff --git a/Documentation/ABI/obsolete/sysfs-driver-hid-roccat-koneplus b/Documentation/ABI/obsolete/sysfs-driver-hid-roccat-koneplus index c2a270b45b03..22568b45973e 100644 --- a/Documentation/ABI/obsolete/sysfs-driver-hid-roccat-koneplus +++ b/Documentation/ABI/obsolete/sysfs-driver-hid-roccat-koneplus @@ -8,3 +8,14 @@ Description: The integer value of this attribute ranges from 0-4. When written, this file sets the number of the startup profile and the mouse activates this profile immediately. Please use actual_profile, it does the same thing. + +What: /sys/bus/usb/devices/-:./::./koneplus/roccatkoneplus/firmware_version +Date: October 2010 +Contact: Stefan Achatz +Description: When read, this file returns the raw integer version number of the + firmware reported by the mouse. Using the integer value eases + further usage in other programs. To receive the real version + number the decimal point has to be shifted 2 positions to the + left. E.g. a returned value of 121 means 1.21 + This file is readonly. + Please read binary attribute info which contains firmware version. diff --git a/Documentation/ABI/testing/sysfs-driver-hid-roccat-koneplus b/Documentation/ABI/testing/sysfs-driver-hid-roccat-koneplus index f9e2a61900e5..ed1213defb81 100644 --- a/Documentation/ABI/testing/sysfs-driver-hid-roccat-koneplus +++ b/Documentation/ABI/testing/sysfs-driver-hid-roccat-koneplus @@ -9,15 +9,12 @@ Description: The integer value of this attribute ranges from 0-4. and the mouse activates this profile immediately. Users: http://roccat.sourceforge.net -What: /sys/bus/usb/devices/-:./::./koneplus/roccatkoneplus/firmware_version -Date: October 2010 +What: /sys/bus/usb/devices/-:./::./koneplus/roccatkoneplus/info +Date: November 2012 Contact: Stefan Achatz -Description: When read, this file returns the raw integer version number of the - firmware reported by the mouse. Using the integer value eases - further usage in other programs. To receive the real version - number the decimal point has to be shifted 2 positions to the - left. E.g. a returned value of 121 means 1.21 - This file is readonly. +Description: When read, this file returns general data like firmware version. + When written, the device can be reset. + The data is 8 bytes long. Users: http://roccat.sourceforge.net What: /sys/bus/usb/devices/-:./::./koneplus/roccatkoneplus/macro diff --git a/drivers/hid/hid-roccat-koneplus.c b/drivers/hid/hid-roccat-koneplus.c index 0df408362ef1..69592f427579 100644 --- a/drivers/hid/hid-roccat-koneplus.c +++ b/drivers/hid/hid-roccat-koneplus.c @@ -183,6 +183,22 @@ static ssize_t koneplus_sysfs_write(struct file *fp, struct kobject *kobj, return real_size; } +static ssize_t koneplus_sysfs_read_info(struct file *fp, + struct kobject *kobj, struct bin_attribute *attr, char *buf, + loff_t off, size_t count) +{ + return koneplus_sysfs_read(fp, kobj, buf, off, count, + sizeof(struct koneplus_info), KONEPLUS_COMMAND_INFO); +} + +static ssize_t koneplus_sysfs_write_info(struct file *fp, + struct kobject *kobj, struct bin_attribute *attr, char *buf, + loff_t off, size_t count) +{ + return koneplus_sysfs_write(fp, kobj, buf, off, count, + sizeof(struct koneplus_info), KONEPLUS_COMMAND_INFO); +} + static ssize_t koneplus_sysfs_write_talk(struct file *fp, struct kobject *kobj, struct bin_attribute *attr, char *buf, loff_t off, size_t count) @@ -428,6 +444,12 @@ static struct device_attribute koneplus_attributes[] = { }; static struct bin_attribute koneplus_bin_attributes[] = { + { + .attr = { .name = "info", .mode = 0660 }, + .size = sizeof(struct koneplus_info), + .read = koneplus_sysfs_read_info, + .write = koneplus_sysfs_write_info + }, { .attr = { .name = "sensor", .mode = 0660 }, .size = sizeof(struct koneplus_sensor), -- cgit v1.2.3-59-g8ed1b From 3ecbf05be159a95e1d23ba9b3b21c5bc2941ba6b Mon Sep 17 00:00:00 2001 From: Pawel Moll Date: Mon, 24 Sep 2012 14:55:40 +0100 Subject: mfd: Versatile Express config infrastructure Versatile Express platform has an elaborated configuration system, consisting of microcontrollers residing on the mother- and daughterboards known as Motherboard/Daughterboard Configuration Controller (MCC and DCC). The controllers are responsible for the platform initialization (reset generation, flash programming, FPGA bitfiles loading etc.) but also control clock generators, voltage regulators, gather environmental data like temperature, power consumption etc. Even the video output switch (FPGA) is controlled that way. Those devices are _not_ visible in the main address space and the usual communication channel uses some kind of a bridge in the peripheral block sending commands (requests) to the controllers and receiving responses. It can take up to 500 microseconds for a transaction to be completed, therefore it is important to provide a non-blocking interface to it. This patch adds an abstraction of this infrastructure. Bridge drivers can register themselves with the framework. Then, a driver of a device can request an abstract "function" - the request will be redirected to a bridge referred by thedd "arm,vexpress,config-bridge" property of the device tree node. Signed-off-by: Pawel Moll --- Documentation/devicetree/bindings/arm/vexpress.txt | 68 ++++- drivers/mfd/Kconfig | 6 + drivers/mfd/Makefile | 1 + drivers/mfd/vexpress-config.c | 277 +++++++++++++++++++++ include/linux/vexpress.h | 85 +++++++ 5 files changed, 436 insertions(+), 1 deletion(-) create mode 100644 drivers/mfd/vexpress-config.c create mode 100644 include/linux/vexpress.h (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/arm/vexpress.txt b/Documentation/devicetree/bindings/arm/vexpress.txt index ec8b50cbb2e8..5d9996b9eabf 100644 --- a/Documentation/devicetree/bindings/arm/vexpress.txt +++ b/Documentation/devicetree/bindings/arm/vexpress.txt @@ -11,6 +11,10 @@ the motherboard file using a /include/ directive. As the motherboard can be initialized in one of two different configurations ("memory maps"), care must be taken to include the correct one. + +Root node +--------- + Required properties in the root node: - compatible value: compatible = "arm,vexpress,", "arm,vexpress"; @@ -45,6 +49,10 @@ Optional properties in the root node: - Coretile Express A9x4 (V2P-CA9) HBI-0225: arm,hbi = <0x225>; + +CPU nodes +--------- + Top-level standard "cpus" node is required. It must contain a node with device_type = "cpu" property for every available core, eg.: @@ -59,6 +67,52 @@ with device_type = "cpu" property for every available core, eg.: }; }; + +Configuration infrastructure +---------------------------- + +The platform has an elaborated configuration system, consisting of +microcontrollers residing on the mother- and daughterboards known +as Motherboard/Daughterboard Configuration Controller (MCC and DCC). +The controllers are responsible for the platform initialization +(reset generation, flash programming, FPGA bitfiles loading etc.) +but also control clock generators, voltage regulators, gather +environmental data like temperature, power consumption etc. Even +the video output switch (FPGA) is controlled that way. + +Nodes describing devices controlled by this infrastructure should +point at the bridge device node: +- bridge phandle: + arm,vexpress,config-bridge = ; +This property can be also defined in a parent node (eg. for a DCC) +and is effective for all children. + + +Platform topology +----------------- + +As Versatile Express can be configured in number of physically +different setups, the device tree should describe platform topology. +Root node and main motherboard node must define the following +property, describing physical location of the children nodes: +- site number: + arm,vexpress,site = ; + where 0 means motherboard, 1 or 2 are daugtherboard sites, + 0xf means "master" site (site containing main CPU tile) +- when daughterboards are stacked on one site, their position + in the stack be be described with: + arm,vexpress,position = ; +- when describing tiles consisting more than one DCC, its number + can be described with: + arm,vexpress,dcc = ; + +Any of the numbers above defaults to zero if not defined in +the node or any of its parent. + + +Motherboard +----------- + The motherboard description file provides a single "motherboard" node using 2 address cells corresponding to the Static Memory Bus used between the motherboard and the tile. The first cell defines the Chip @@ -96,13 +150,16 @@ The tile description must define "ranges", "interrupt-map-mask" and "interrupt-map" properties to translate the motherboard's address and interrupt space into one used by the tile's processor. -Abbreviated example: + +Example of a VE tile description (simplified) +--------------------------------------------- /dts-v1/; / { model = "V2P-CA5s"; arm,hbi = <0x225>; + arm,vexpress,site = <0xf>; compatible = "arm,vexpress-v2p-ca5s", "arm,vexpress"; interrupt-parent = <&gic>; #address-cells = <1>; @@ -134,6 +191,15 @@ Abbreviated example: <0x2c000100 0x100>; }; + dcc { + compatible = "simple-bus"; + arm,vexpress,config-bridge = <&v2m_sysreg>; + + osc@0 { + compatible = "arm,vexpress-osc"; + }; + }; + motherboard { /* CS0 is visible at 0x08000000 */ ranges = <0 0 0x08000000 0x04000000>; diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index acab3ef8a310..637bcdf8ce77 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -1070,3 +1070,9 @@ config MCP_UCB1200_TS depends on MCP_UCB1200 && INPUT endmenu + +config VEXPRESS_CONFIG + bool + help + Platform configuration infrastructure for the ARM Ltd. + Versatile Express. diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index d8ccb630ddb0..e807164f68da 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -138,3 +138,4 @@ obj-$(CONFIG_MFD_RC5T583) += rc5t583.o rc5t583-irq.o obj-$(CONFIG_MFD_SEC_CORE) += sec-core.o sec-irq.o obj-$(CONFIG_MFD_SYSCON) += syscon.o obj-$(CONFIG_MFD_LM3533) += lm3533-core.o lm3533-ctrlbank.o +obj-$(CONFIG_VEXPRESS_CONFIG) += vexpress-config.o diff --git a/drivers/mfd/vexpress-config.c b/drivers/mfd/vexpress-config.c new file mode 100644 index 000000000000..fae15d880758 --- /dev/null +++ b/drivers/mfd/vexpress-config.c @@ -0,0 +1,277 @@ +/* + * 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. + * + * Copyright (C) 2012 ARM Limited + */ + +#define pr_fmt(fmt) "vexpress-config: " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#define VEXPRESS_CONFIG_MAX_BRIDGES 2 + +struct vexpress_config_bridge { + struct device_node *node; + struct vexpress_config_bridge_info *info; + struct list_head transactions; + spinlock_t transactions_lock; +} vexpress_config_bridges[VEXPRESS_CONFIG_MAX_BRIDGES]; + +static DECLARE_BITMAP(vexpress_config_bridges_map, + ARRAY_SIZE(vexpress_config_bridges)); +static DEFINE_MUTEX(vexpress_config_bridges_mutex); + +struct vexpress_config_bridge *vexpress_config_bridge_register( + struct device_node *node, + struct vexpress_config_bridge_info *info) +{ + struct vexpress_config_bridge *bridge; + int i; + + pr_debug("Registering bridge '%s'\n", info->name); + + mutex_lock(&vexpress_config_bridges_mutex); + i = find_first_zero_bit(vexpress_config_bridges_map, + ARRAY_SIZE(vexpress_config_bridges)); + if (i >= ARRAY_SIZE(vexpress_config_bridges)) { + pr_err("Can't register more bridges!\n"); + mutex_unlock(&vexpress_config_bridges_mutex); + return NULL; + } + __set_bit(i, vexpress_config_bridges_map); + bridge = &vexpress_config_bridges[i]; + + bridge->node = node; + bridge->info = info; + INIT_LIST_HEAD(&bridge->transactions); + spin_lock_init(&bridge->transactions_lock); + + mutex_unlock(&vexpress_config_bridges_mutex); + + return bridge; +} + +void vexpress_config_bridge_unregister(struct vexpress_config_bridge *bridge) +{ + struct vexpress_config_bridge __bridge = *bridge; + int i; + + mutex_lock(&vexpress_config_bridges_mutex); + for (i = 0; i < ARRAY_SIZE(vexpress_config_bridges); i++) + if (&vexpress_config_bridges[i] == bridge) + __clear_bit(i, vexpress_config_bridges_map); + mutex_unlock(&vexpress_config_bridges_mutex); + + WARN_ON(!list_empty(&__bridge.transactions)); + while (!list_empty(&__bridge.transactions)) + cpu_relax(); +} + + +struct vexpress_config_func { + struct vexpress_config_bridge *bridge; + void *func; +}; + +struct vexpress_config_func *__vexpress_config_func_get(struct device *dev, + struct device_node *node) +{ + struct device_node *bridge_node; + struct vexpress_config_func *func; + int i; + + if (WARN_ON(dev && node && dev->of_node != node)) + return NULL; + if (dev && !node) + node = dev->of_node; + + func = kzalloc(sizeof(*func), GFP_KERNEL); + if (!func) + return NULL; + + bridge_node = of_node_get(node); + while (bridge_node) { + const __be32 *prop = of_get_property(bridge_node, + "arm,vexpress,config-bridge", NULL); + + if (prop) { + bridge_node = of_find_node_by_phandle( + be32_to_cpup(prop)); + break; + } + + bridge_node = of_get_next_parent(bridge_node); + } + + mutex_lock(&vexpress_config_bridges_mutex); + for (i = 0; i < ARRAY_SIZE(vexpress_config_bridges); i++) { + struct vexpress_config_bridge *bridge = + &vexpress_config_bridges[i]; + + if (test_bit(i, vexpress_config_bridges_map) && + bridge->node == bridge_node) { + func->bridge = bridge; + func->func = bridge->info->func_get(dev, node); + break; + } + } + mutex_unlock(&vexpress_config_bridges_mutex); + + if (!func->func) { + of_node_put(node); + kfree(func); + return NULL; + } + + return func; +} + +void vexpress_config_func_put(struct vexpress_config_func *func) +{ + func->bridge->info->func_put(func->func); + of_node_put(func->bridge->node); + kfree(func); +} + + +struct vexpress_config_trans { + struct vexpress_config_func *func; + int offset; + bool write; + u32 *data; + int status; + struct completion completion; + struct list_head list; +}; + +static void vexpress_config_dump_trans(const char *what, + struct vexpress_config_trans *trans) +{ + pr_debug("%s %s trans %p func 0x%p offset %d data 0x%x status %d\n", + what, trans->write ? "write" : "read", trans, + trans->func->func, trans->offset, + trans->data ? *trans->data : 0, trans->status); +} + +static int vexpress_config_schedule(struct vexpress_config_trans *trans) +{ + int status; + struct vexpress_config_bridge *bridge = trans->func->bridge; + unsigned long flags; + + init_completion(&trans->completion); + trans->status = -EFAULT; + + spin_lock_irqsave(&bridge->transactions_lock, flags); + + vexpress_config_dump_trans("Executing", trans); + + if (list_empty(&bridge->transactions)) + status = bridge->info->func_exec(trans->func->func, + trans->offset, trans->write, trans->data); + else + status = VEXPRESS_CONFIG_STATUS_WAIT; + + switch (status) { + case VEXPRESS_CONFIG_STATUS_DONE: + vexpress_config_dump_trans("Finished", trans); + trans->status = status; + break; + case VEXPRESS_CONFIG_STATUS_WAIT: + list_add_tail(&trans->list, &bridge->transactions); + break; + } + + spin_unlock_irqrestore(&bridge->transactions_lock, flags); + + return status; +} + +void vexpress_config_complete(struct vexpress_config_bridge *bridge, + int status) +{ + struct vexpress_config_trans *trans; + unsigned long flags; + + spin_lock_irqsave(&bridge->transactions_lock, flags); + + trans = list_first_entry(&bridge->transactions, + struct vexpress_config_trans, list); + vexpress_config_dump_trans("Completed", trans); + + trans->status = status; + list_del(&trans->list); + + if (!list_empty(&bridge->transactions)) { + vexpress_config_dump_trans("Pending", trans); + + bridge->info->func_exec(trans->func->func, trans->offset, + trans->write, trans->data); + } + spin_unlock_irqrestore(&bridge->transactions_lock, flags); + + complete(&trans->completion); +} + +int vexpress_config_wait(struct vexpress_config_trans *trans) +{ + wait_for_completion(&trans->completion); + + return trans->status; +} + + +int vexpress_config_read(struct vexpress_config_func *func, int offset, + u32 *data) +{ + struct vexpress_config_trans trans = { + .func = func, + .offset = offset, + .write = false, + .data = data, + .status = 0, + }; + int status = vexpress_config_schedule(&trans); + + if (status == VEXPRESS_CONFIG_STATUS_WAIT) + status = vexpress_config_wait(&trans); + + return status; +} +EXPORT_SYMBOL(vexpress_config_read); + +int vexpress_config_write(struct vexpress_config_func *func, int offset, + u32 data) +{ + struct vexpress_config_trans trans = { + .func = func, + .offset = offset, + .write = true, + .data = &data, + .status = 0, + }; + int status = vexpress_config_schedule(&trans); + + if (status == VEXPRESS_CONFIG_STATUS_WAIT) + status = vexpress_config_wait(&trans); + + return status; +} +EXPORT_SYMBOL(vexpress_config_write); diff --git a/include/linux/vexpress.h b/include/linux/vexpress.h new file mode 100644 index 000000000000..c2d877a7b691 --- /dev/null +++ b/include/linux/vexpress.h @@ -0,0 +1,85 @@ +/* + * 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. + * + * Copyright (C) 2012 ARM Limited + */ + +#ifndef _LINUX_VEXPRESS_H +#define _LINUX_VEXPRESS_H + +#include + +#define VEXPRESS_SITE_MB 0 +#define VEXPRESS_SITE_DB1 1 +#define VEXPRESS_SITE_DB2 2 +#define VEXPRESS_SITE_MASTER 0xf + +#define VEXPRESS_CONFIG_STATUS_DONE 0 +#define VEXPRESS_CONFIG_STATUS_WAIT 1 + +/* Config bridge API */ + +/** + * struct vexpress_config_bridge_info - description of the platform + * configuration infrastructure bridge. + * + * @name: Bridge name + * + * @func_get: Obtains pointer to a configuration function for a given + * device or a Device Tree node, to be used with @func_put + * and @func_exec. The node pointer should take precedence + * over device pointer when both are passed. + * + * @func_put: Tells the bridge that the function will not be used any + * more, so all allocated resources can be released. + * + * @func_exec: Executes a configuration function read or write operation. + * The offset selects a 32 bit word of the value accessed. + * Must return VEXPRESS_CONFIG_STATUS_DONE when operation + * is finished immediately, VEXPRESS_CONFIG_STATUS_WAIT when + * will be completed in some time or negative value in case + * of error. + */ +struct vexpress_config_bridge_info { + const char *name; + void *(*func_get)(struct device *dev, struct device_node *node); + void (*func_put)(void *func); + int (*func_exec)(void *func, int offset, bool write, u32 *data); +}; + +struct vexpress_config_bridge; + +struct vexpress_config_bridge *vexpress_config_bridge_register( + struct device_node *node, + struct vexpress_config_bridge_info *info); +void vexpress_config_bridge_unregister(struct vexpress_config_bridge *bridge); + +void vexpress_config_complete(struct vexpress_config_bridge *bridge, + int status); + +/* Config function API */ + +struct vexpress_config_func; + +struct vexpress_config_func *__vexpress_config_func_get(struct device *dev, + struct device_node *node); +#define vexpress_config_func_get_by_dev(dev) \ + __vexpress_config_func_get(dev, NULL) +#define vexpress_config_func_get_by_node(node) \ + __vexpress_config_func_get(NULL, node) +void vexpress_config_func_put(struct vexpress_config_func *func); + +/* Both may sleep! */ +int vexpress_config_read(struct vexpress_config_func *func, int offset, + u32 *data); +int vexpress_config_write(struct vexpress_config_func *func, int offset, + u32 data); + +#endif -- cgit v1.2.3-59-g8ed1b From 88e0abcd7a8171ca7af3402373e7bd81fe9b6754 Mon Sep 17 00:00:00 2001 From: Pawel Moll Date: Tue, 18 Sep 2012 12:24:57 +0100 Subject: mfd: Versatile Express system registers driver This is a platform driver for Versatile Express' "system register" block. It's a random collection of registers providing the following functionality: - low level platform functions like board ID access; in order to use those, the driver must be initialized early, either statically or based on the DT - config bus bridge via "system control" interface; as the response from the controller does not generate interrupt (yet), the status register is periodically polled using a timer - pseudo GPIO lines providing MMC card status and Flash WP# signal control - LED interface for a set of 8 LEDs on the motherboard, with "heartbeat", "mmc0" and "cpu0" to "cpu5" as default triggers Signed-off-by: Pawel Moll --- .../devicetree/bindings/arm/vexpress-sysreg.txt | 50 ++ drivers/mfd/Makefile | 2 +- drivers/mfd/vexpress-sysreg.c | 552 +++++++++++++++++++++ include/linux/vexpress.h | 25 + 4 files changed, 628 insertions(+), 1 deletion(-) create mode 100644 Documentation/devicetree/bindings/arm/vexpress-sysreg.txt create mode 100644 drivers/mfd/vexpress-sysreg.c (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/arm/vexpress-sysreg.txt b/Documentation/devicetree/bindings/arm/vexpress-sysreg.txt new file mode 100644 index 000000000000..9cf3f25544c7 --- /dev/null +++ b/Documentation/devicetree/bindings/arm/vexpress-sysreg.txt @@ -0,0 +1,50 @@ +ARM Versatile Express system registers +-------------------------------------- + +This is a system control registers block, providing multiple low level +platform functions like board detection and identification, software +interrupt generation, MMC and NOR Flash control etc. + +Required node properties: +- compatible value : = "arm,vexpress,sysreg"; +- reg : physical base address and the size of the registers window +- gpio-controller : specifies that the node is a GPIO controller +- #gpio-cells : size of the GPIO specifier, should be 2: + - first cell is the pseudo-GPIO line number: + 0 - MMC CARDIN + 1 - MMC WPROT + 2 - NOR FLASH WPn + - second cell can take standard GPIO flags (currently ignored). + +Example: + v2m_sysreg: sysreg@10000000 { + compatible = "arm,vexpress-sysreg"; + reg = <0x10000000 0x1000>; + gpio-controller; + #gpio-cells = <2>; + }; + +This block also can also act a bridge to the platform's configuration +bus via "system control" interface, addressing devices with site number, +position in the board stack, config controller, function and device +numbers - see motherboard's TRM for more details. + +The node describing a config device must refer to the sysreg node via +"arm,vexpress,config-bridge" phandle (can be also defined in the node's +parent) and relies on the board topology properties - see main vexpress +node documentation for more details. It must must also define the +following property: +- arm,vexpress-sysreg,func : must contain two cells: + - first cell defines function number (eg. 1 for clock generator, + 2 for voltage regulators etc.) + - device number (eg. osc 0, osc 1 etc.) + +Example: + mcc { + arm,vexpress,config-bridge = <&v2m_sysreg>; + + osc@0 { + compatible = "arm,vexpress-osc"; + arm,vexpress-sysreg,func = <1 0>; + }; + }; diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index e807164f68da..296817c6c06f 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -138,4 +138,4 @@ obj-$(CONFIG_MFD_RC5T583) += rc5t583.o rc5t583-irq.o obj-$(CONFIG_MFD_SEC_CORE) += sec-core.o sec-irq.o obj-$(CONFIG_MFD_SYSCON) += syscon.o obj-$(CONFIG_MFD_LM3533) += lm3533-core.o lm3533-ctrlbank.o -obj-$(CONFIG_VEXPRESS_CONFIG) += vexpress-config.o +obj-$(CONFIG_VEXPRESS_CONFIG) += vexpress-config.o vexpress-sysreg.o diff --git a/drivers/mfd/vexpress-sysreg.c b/drivers/mfd/vexpress-sysreg.c new file mode 100644 index 000000000000..059d6b17b14a --- /dev/null +++ b/drivers/mfd/vexpress-sysreg.c @@ -0,0 +1,552 @@ +/* + * 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. + * + * Copyright (C) 2012 ARM Limited + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define SYS_ID 0x000 +#define SYS_SW 0x004 +#define SYS_LED 0x008 +#define SYS_100HZ 0x024 +#define SYS_FLAGS 0x030 +#define SYS_FLAGSSET 0x030 +#define SYS_FLAGSCLR 0x034 +#define SYS_NVFLAGS 0x038 +#define SYS_NVFLAGSSET 0x038 +#define SYS_NVFLAGSCLR 0x03c +#define SYS_MCI 0x048 +#define SYS_FLASH 0x04c +#define SYS_CFGSW 0x058 +#define SYS_24MHZ 0x05c +#define SYS_MISC 0x060 +#define SYS_DMA 0x064 +#define SYS_PROCID0 0x084 +#define SYS_PROCID1 0x088 +#define SYS_CFGDATA 0x0a0 +#define SYS_CFGCTRL 0x0a4 +#define SYS_CFGSTAT 0x0a8 + +#define SYS_HBI_MASK 0xfff +#define SYS_ID_HBI_SHIFT 16 +#define SYS_PROCIDx_HBI_SHIFT 0 + +#define SYS_MCI_CARDIN (1 << 0) +#define SYS_MCI_WPROT (1 << 1) + +#define SYS_FLASH_WPn (1 << 0) + +#define SYS_MISC_MASTERSITE (1 << 14) + +#define SYS_CFGCTRL_START (1 << 31) +#define SYS_CFGCTRL_WRITE (1 << 30) +#define SYS_CFGCTRL_DCC(n) (((n) & 0xf) << 26) +#define SYS_CFGCTRL_FUNC(n) (((n) & 0x3f) << 20) +#define SYS_CFGCTRL_SITE(n) (((n) & 0x3) << 16) +#define SYS_CFGCTRL_POSITION(n) (((n) & 0xf) << 12) +#define SYS_CFGCTRL_DEVICE(n) (((n) & 0xfff) << 0) + +#define SYS_CFGSTAT_ERR (1 << 1) +#define SYS_CFGSTAT_COMPLETE (1 << 0) + + +static void __iomem *vexpress_sysreg_base; +static struct device *vexpress_sysreg_dev; +static int vexpress_master_site; + + +void vexpress_flags_set(u32 data) +{ + writel(~0, vexpress_sysreg_base + SYS_FLAGSCLR); + writel(data, vexpress_sysreg_base + SYS_FLAGSSET); +} + +u32 vexpress_get_procid(int site) +{ + if (site == VEXPRESS_SITE_MASTER) + site = vexpress_master_site; + + return readl(vexpress_sysreg_base + (site == VEXPRESS_SITE_DB1 ? + SYS_PROCID0 : SYS_PROCID1)); +} + +u32 vexpress_get_hbi(int site) +{ + u32 id; + + switch (site) { + case VEXPRESS_SITE_MB: + id = readl(vexpress_sysreg_base + SYS_ID); + return (id >> SYS_ID_HBI_SHIFT) & SYS_HBI_MASK; + case VEXPRESS_SITE_MASTER: + case VEXPRESS_SITE_DB1: + case VEXPRESS_SITE_DB2: + id = vexpress_get_procid(site); + return (id >> SYS_PROCIDx_HBI_SHIFT) & SYS_HBI_MASK; + } + + return ~0; +} + +void __iomem *vexpress_get_24mhz_clock_base(void) +{ + return vexpress_sysreg_base + SYS_24MHZ; +} + + +static void vexpress_sysreg_find_prop(struct device_node *node, + const char *name, u32 *val) +{ + of_node_get(node); + while (node) { + if (of_property_read_u32(node, name, val) == 0) { + of_node_put(node); + return; + } + node = of_get_next_parent(node); + } +} + +unsigned __vexpress_get_site(struct device *dev, struct device_node *node) +{ + u32 site = 0; + + WARN_ON(dev && node && dev->of_node != node); + if (dev && !node) + node = dev->of_node; + + if (node) { + vexpress_sysreg_find_prop(node, "arm,vexpress,site", &site); + } else if (dev && dev->bus == &platform_bus_type) { + struct platform_device *pdev = to_platform_device(dev); + + if (pdev->num_resources == 1 && + pdev->resource[0].flags == IORESOURCE_BUS) + site = pdev->resource[0].start; + } else if (dev && strncmp(dev_name(dev), "ct:", 3) == 0) { + site = VEXPRESS_SITE_MASTER; + } + + if (site == VEXPRESS_SITE_MASTER) + site = vexpress_master_site; + + return site; +} + + +struct vexpress_sysreg_config_func { + u32 template; + u32 device; +}; + +static struct vexpress_config_bridge *vexpress_sysreg_config_bridge; +static struct timer_list vexpress_sysreg_config_timer; +static u32 *vexpress_sysreg_config_data; +static int vexpress_sysreg_config_tries; + +static void *vexpress_sysreg_config_func_get(struct device *dev, + struct device_node *node) +{ + struct vexpress_sysreg_config_func *config_func; + u32 site; + u32 position = 0; + u32 dcc = 0; + u32 func_device[2]; + int err = -EFAULT; + + if (node) { + of_node_get(node); + vexpress_sysreg_find_prop(node, "arm,vexpress,site", &site); + vexpress_sysreg_find_prop(node, "arm,vexpress,position", + &position); + vexpress_sysreg_find_prop(node, "arm,vexpress,dcc", &dcc); + err = of_property_read_u32_array(node, + "arm,vexpress-sysreg,func", func_device, + ARRAY_SIZE(func_device)); + of_node_put(node); + } else if (dev && dev->bus == &platform_bus_type) { + struct platform_device *pdev = to_platform_device(dev); + + if (pdev->num_resources == 1 && + pdev->resource[0].flags == IORESOURCE_BUS) { + site = pdev->resource[0].start; + func_device[0] = pdev->resource[0].end; + func_device[1] = pdev->id; + err = 0; + } + } + if (err) + return NULL; + + config_func = kzalloc(sizeof(*config_func), GFP_KERNEL); + if (!config_func) + return NULL; + + config_func->template = SYS_CFGCTRL_DCC(dcc); + config_func->template |= SYS_CFGCTRL_FUNC(func_device[0]); + config_func->template |= SYS_CFGCTRL_SITE(site == VEXPRESS_SITE_MASTER ? + vexpress_master_site : site); + config_func->template |= SYS_CFGCTRL_POSITION(position); + config_func->device |= func_device[1]; + + dev_dbg(vexpress_sysreg_dev, "func 0x%p = 0x%x, %d\n", config_func, + config_func->template, config_func->device); + + return config_func; +} + +static void vexpress_sysreg_config_func_put(void *func) +{ + kfree(func); +} + +static int vexpress_sysreg_config_func_exec(void *func, int offset, + bool write, u32 *data) +{ + int status; + struct vexpress_sysreg_config_func *config_func = func; + u32 command; + + if (WARN_ON(!vexpress_sysreg_base)) + return -ENOENT; + + command = readl(vexpress_sysreg_base + SYS_CFGCTRL); + if (WARN_ON(command & SYS_CFGCTRL_START)) + return -EBUSY; + + command = SYS_CFGCTRL_START; + command |= write ? SYS_CFGCTRL_WRITE : 0; + command |= config_func->template; + command |= SYS_CFGCTRL_DEVICE(config_func->device + offset); + + /* Use a canary for reads */ + if (!write) + *data = 0xdeadbeef; + + dev_dbg(vexpress_sysreg_dev, "command %x, data %x\n", + command, *data); + writel(*data, vexpress_sysreg_base + SYS_CFGDATA); + writel(0, vexpress_sysreg_base + SYS_CFGSTAT); + writel(command, vexpress_sysreg_base + SYS_CFGCTRL); + mb(); + + if (vexpress_sysreg_dev) { + /* Schedule completion check */ + if (!write) + vexpress_sysreg_config_data = data; + vexpress_sysreg_config_tries = 100; + mod_timer(&vexpress_sysreg_config_timer, + jiffies + usecs_to_jiffies(100)); + status = VEXPRESS_CONFIG_STATUS_WAIT; + } else { + /* Early execution, no timer available, have to spin */ + u32 cfgstat; + + do { + cpu_relax(); + cfgstat = readl(vexpress_sysreg_base + SYS_CFGSTAT); + } while (!cfgstat); + + if (!write && (cfgstat & SYS_CFGSTAT_COMPLETE)) + *data = readl(vexpress_sysreg_base + SYS_CFGDATA); + status = VEXPRESS_CONFIG_STATUS_DONE; + + if (cfgstat & SYS_CFGSTAT_ERR) + status = -EINVAL; + } + + return status; +} + +struct vexpress_config_bridge_info vexpress_sysreg_config_bridge_info = { + .name = "vexpress-sysreg", + .func_get = vexpress_sysreg_config_func_get, + .func_put = vexpress_sysreg_config_func_put, + .func_exec = vexpress_sysreg_config_func_exec, +}; + +static void vexpress_sysreg_config_complete(unsigned long data) +{ + int status = VEXPRESS_CONFIG_STATUS_DONE; + u32 cfgstat = readl(vexpress_sysreg_base + SYS_CFGSTAT); + + if (cfgstat & SYS_CFGSTAT_ERR) + status = -EINVAL; + if (!vexpress_sysreg_config_tries--) + status = -ETIMEDOUT; + + if (status < 0) { + dev_err(vexpress_sysreg_dev, "error %d\n", status); + } else if (!(cfgstat & SYS_CFGSTAT_COMPLETE)) { + mod_timer(&vexpress_sysreg_config_timer, + jiffies + usecs_to_jiffies(50)); + return; + } + + if (vexpress_sysreg_config_data) { + *vexpress_sysreg_config_data = readl(vexpress_sysreg_base + + SYS_CFGDATA); + dev_dbg(vexpress_sysreg_dev, "read data %x\n", + *vexpress_sysreg_config_data); + vexpress_sysreg_config_data = NULL; + } + + vexpress_config_complete(vexpress_sysreg_config_bridge, status); +} + + +void __init vexpress_sysreg_early_init(void __iomem *base) +{ + struct device_node *node = of_find_compatible_node(NULL, NULL, + "arm,vexpress-sysreg"); + + if (node) + base = of_iomap(node, 0); + + if (WARN_ON(!base)) + return; + + vexpress_sysreg_base = base; + + if (readl(vexpress_sysreg_base + SYS_MISC) & SYS_MISC_MASTERSITE) + vexpress_master_site = VEXPRESS_SITE_DB2; + else + vexpress_master_site = VEXPRESS_SITE_DB1; + + vexpress_sysreg_config_bridge = vexpress_config_bridge_register( + node, &vexpress_sysreg_config_bridge_info); + WARN_ON(!vexpress_sysreg_config_bridge); +} + +void __init vexpress_sysreg_of_early_init(void) +{ + vexpress_sysreg_early_init(NULL); +} + + +static struct vexpress_sysreg_gpio { + unsigned long reg; + u32 value; +} vexpress_sysreg_gpios[] = { + [VEXPRESS_GPIO_MMC_CARDIN] = { + .reg = SYS_MCI, + .value = SYS_MCI_CARDIN, + }, + [VEXPRESS_GPIO_MMC_WPROT] = { + .reg = SYS_MCI, + .value = SYS_MCI_WPROT, + }, + [VEXPRESS_GPIO_FLASH_WPn] = { + .reg = SYS_FLASH, + .value = SYS_FLASH_WPn, + }, +}; + +static int vexpress_sysreg_gpio_direction_input(struct gpio_chip *chip, + unsigned offset) +{ + return 0; +} + +static int vexpress_sysreg_gpio_direction_output(struct gpio_chip *chip, + unsigned offset, int value) +{ + return 0; +} + +static int vexpress_sysreg_gpio_get(struct gpio_chip *chip, + unsigned offset) +{ + struct vexpress_sysreg_gpio *gpio = &vexpress_sysreg_gpios[offset]; + u32 reg_value = readl(vexpress_sysreg_base + gpio->reg); + + return !!(reg_value & gpio->value); +} + +static void vexpress_sysreg_gpio_set(struct gpio_chip *chip, + unsigned offset, int value) +{ + struct vexpress_sysreg_gpio *gpio = &vexpress_sysreg_gpios[offset]; + u32 reg_value = readl(vexpress_sysreg_base + gpio->reg); + + if (value) + reg_value |= gpio->value; + else + reg_value &= ~gpio->value; + + writel(reg_value, vexpress_sysreg_base + gpio->reg); +} + +static struct gpio_chip vexpress_sysreg_gpio_chip = { + .label = "vexpress-sysreg", + .direction_input = vexpress_sysreg_gpio_direction_input, + .direction_output = vexpress_sysreg_gpio_direction_output, + .get = vexpress_sysreg_gpio_get, + .set = vexpress_sysreg_gpio_set, + .ngpio = ARRAY_SIZE(vexpress_sysreg_gpios), + .base = 0, +}; + + +static ssize_t vexpress_sysreg_sys_id_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "0x%08x\n", readl(vexpress_sysreg_base + SYS_ID)); +} + +DEVICE_ATTR(sys_id, S_IRUGO, vexpress_sysreg_sys_id_show, NULL); + +static int __devinit vexpress_sysreg_probe(struct platform_device *pdev) +{ + int err; + struct resource *res = platform_get_resource(pdev, + IORESOURCE_MEM, 0); + + if (!devm_request_mem_region(&pdev->dev, res->start, + resource_size(res), pdev->name)) { + dev_err(&pdev->dev, "Failed to request memory region!\n"); + return -EBUSY; + } + + if (!vexpress_sysreg_base) + vexpress_sysreg_base = devm_ioremap(&pdev->dev, res->start, + resource_size(res)); + + if (!vexpress_sysreg_base) { + dev_err(&pdev->dev, "Failed to obtain base address!\n"); + return -EFAULT; + } + + setup_timer(&vexpress_sysreg_config_timer, + vexpress_sysreg_config_complete, 0); + + vexpress_sysreg_gpio_chip.dev = &pdev->dev; + err = gpiochip_add(&vexpress_sysreg_gpio_chip); + if (err) { + vexpress_config_bridge_unregister( + vexpress_sysreg_config_bridge); + dev_err(&pdev->dev, "Failed to register GPIO chip! (%d)\n", + err); + return err; + } + + vexpress_sysreg_dev = &pdev->dev; + + device_create_file(vexpress_sysreg_dev, &dev_attr_sys_id); + + return 0; +} + +static const struct of_device_id vexpress_sysreg_match[] = { + { .compatible = "arm,vexpress-sysreg", }, + {}, +}; + +static struct platform_driver vexpress_sysreg_driver = { + .driver = { + .name = "vexpress-sysreg", + .of_match_table = vexpress_sysreg_match, + }, + .probe = vexpress_sysreg_probe, +}; + +static int __init vexpress_sysreg_init(void) +{ + return platform_driver_register(&vexpress_sysreg_driver); +} +core_initcall(vexpress_sysreg_init); + + +#if defined(CONFIG_LEDS_CLASS) + +struct vexpress_sysreg_led { + u32 mask; + struct led_classdev cdev; +} vexpress_sysreg_leds[] = { + { .mask = 1 << 0, .cdev.name = "v2m:green:user1", + .cdev.default_trigger = "heartbeat", }, + { .mask = 1 << 1, .cdev.name = "v2m:green:user2", + .cdev.default_trigger = "mmc0", }, + { .mask = 1 << 2, .cdev.name = "v2m:green:user3", + .cdev.default_trigger = "cpu0", }, + { .mask = 1 << 3, .cdev.name = "v2m:green:user4", + .cdev.default_trigger = "cpu1", }, + { .mask = 1 << 4, .cdev.name = "v2m:green:user5", + .cdev.default_trigger = "cpu2", }, + { .mask = 1 << 5, .cdev.name = "v2m:green:user6", + .cdev.default_trigger = "cpu3", }, + { .mask = 1 << 6, .cdev.name = "v2m:green:user7", + .cdev.default_trigger = "cpu4", }, + { .mask = 1 << 7, .cdev.name = "v2m:green:user8", + .cdev.default_trigger = "cpu5", }, +}; + +static DEFINE_SPINLOCK(vexpress_sysreg_leds_lock); + +static void vexpress_sysreg_led_brightness_set(struct led_classdev *cdev, + enum led_brightness brightness) +{ + struct vexpress_sysreg_led *led = container_of(cdev, + struct vexpress_sysreg_led, cdev); + unsigned long flags; + u32 val; + + spin_lock_irqsave(&vexpress_sysreg_leds_lock, flags); + + val = readl(vexpress_sysreg_base + SYS_LED); + if (brightness == LED_OFF) + val &= ~led->mask; + else + val |= led->mask; + writel(val, vexpress_sysreg_base + SYS_LED); + + spin_unlock_irqrestore(&vexpress_sysreg_leds_lock, flags); +} + +static int __init vexpress_sysreg_init_leds(void) +{ + struct vexpress_sysreg_led *led; + int i; + + /* Clear all user LEDs */ + writel(0, vexpress_sysreg_base + SYS_LED); + + for (i = 0, led = vexpress_sysreg_leds; + i < ARRAY_SIZE(vexpress_sysreg_leds); i++, led++) { + int err; + + led->cdev.brightness_set = vexpress_sysreg_led_brightness_set; + err = led_classdev_register(vexpress_sysreg_dev, &led->cdev); + if (err) { + dev_err(vexpress_sysreg_dev, + "Failed to register LED %d! (%d)\n", + i, err); + while (led--, i--) + led_classdev_unregister(&led->cdev); + return err; + } + } + + return 0; +} +device_initcall(vexpress_sysreg_init_leds); + +#endif diff --git a/include/linux/vexpress.h b/include/linux/vexpress.h index c2d877a7b691..09c81d7a22de 100644 --- a/include/linux/vexpress.h +++ b/include/linux/vexpress.h @@ -24,6 +24,17 @@ #define VEXPRESS_CONFIG_STATUS_DONE 0 #define VEXPRESS_CONFIG_STATUS_WAIT 1 +#define VEXPRESS_GPIO_MMC_CARDIN 0 +#define VEXPRESS_GPIO_MMC_WPROT 1 +#define VEXPRESS_GPIO_FLASH_WPn 2 + +#define VEXPRESS_RES_FUNC(_site, _func) \ +{ \ + .start = (_site), \ + .end = (_func), \ + .flags = IORESOURCE_BUS, \ +} + /* Config bridge API */ /** @@ -82,4 +93,18 @@ int vexpress_config_read(struct vexpress_config_func *func, int offset, int vexpress_config_write(struct vexpress_config_func *func, int offset, u32 data); +/* Platform control */ + +u32 vexpress_get_procid(int site); +u32 vexpress_get_hbi(int site); +void *vexpress_get_24mhz_clock_base(void); +void vexpress_flags_set(u32 data); + +#define vexpress_get_site_by_node(node) __vexpress_get_site(NULL, node) +#define vexpress_get_site_by_dev(dev) __vexpress_get_site(dev, NULL) +unsigned __vexpress_get_site(struct device *dev, struct device_node *node); + +void vexpress_sysreg_early_init(void __iomem *base); +void vexpress_sysreg_of_early_init(void); + #endif -- cgit v1.2.3-59-g8ed1b From 433683a66401adb0150792e725cc4f631c94de46 Mon Sep 17 00:00:00 2001 From: Pawel Moll Date: Tue, 16 Oct 2012 15:27:12 +0100 Subject: ARM: vexpress: Remove motherboard dependencies in the DTS files The way the VE motherboard Device Trees were constructed enforced naming and structure of daughterboard files. This patch makes it possible to simply include the motherboard description anywhere in the main Device Tree and retires the "arm,v2m-timer" alias - any of the motherboard SP804 timers will be used instead. Signed-off-by: Pawel Moll --- Documentation/devicetree/bindings/arm/vexpress.txt | 32 +++++++++++++++------- arch/arm/boot/dts/vexpress-v2m-rs1.dtsi | 11 +++----- arch/arm/boot/dts/vexpress-v2m.dtsi | 11 +++----- arch/arm/boot/dts/vexpress-v2p-ca15-tc1.dts | 11 ++++++-- arch/arm/boot/dts/vexpress-v2p-ca15_a7.dts | 11 ++++++-- arch/arm/boot/dts/vexpress-v2p-ca5s.dts | 11 ++++++-- arch/arm/boot/dts/vexpress-v2p-ca9.dts | 11 ++++++-- arch/arm/mach-vexpress/v2m.c | 19 +++++++------ 8 files changed, 73 insertions(+), 44 deletions(-) (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/arm/vexpress.txt b/Documentation/devicetree/bindings/arm/vexpress.txt index 5d9996b9eabf..ae49161e478a 100644 --- a/Documentation/devicetree/bindings/arm/vexpress.txt +++ b/Documentation/devicetree/bindings/arm/vexpress.txt @@ -141,14 +141,19 @@ can be used to obtain required phandle in the tile's "aliases" node: - SP804 timers: v2m_timer01 and v2m_timer23 -Current Linux implementation requires a "arm,v2m_timer" alias -pointing at one of the motherboard's SP804 timers, if it is to be -used as the system timer. This alias should be defined in the -motherboard files. - -The tile description must define "ranges", "interrupt-map-mask" and -"interrupt-map" properties to translate the motherboard's address -and interrupt space into one used by the tile's processor. +The tile description should define a "smb" node, describing the +Static Memory Bus between the tile and motherboard. It must define +the following properties: +- "simple-bus" compatible value (to ensure creation of the children) + compatible = "simple-bus"; +- mapping of the SMB CS/offset addresses into main address space: + #address-cells = <2>; + #size-cells = <1>; + ranges = <...>; +- interrupts mapping: + #interrupt-cells = <1>; + interrupt-map-mask = <0 0 63>; + interrupt-map = <...>; Example of a VE tile description (simplified) @@ -200,13 +205,20 @@ Example of a VE tile description (simplified) }; }; - motherboard { + smb { + compatible = "simple-bus"; + + #address-cells = <2>; + #size-cells = <1>; /* CS0 is visible at 0x08000000 */ ranges = <0 0 0x08000000 0x04000000>; + + #interrupt-cells = <1>; interrupt-map-mask = <0 0 63>; /* Active high IRQ 0 is connected to GIC's SPI0 */ interrupt-map = <0 0 0 &gic 0 0 4>; + + /include/ "vexpress-v2m-rs1.dtsi" }; }; -/include/ "vexpress-v2m-rs1.dtsi" diff --git a/arch/arm/boot/dts/vexpress-v2m-rs1.dtsi b/arch/arm/boot/dts/vexpress-v2m-rs1.dtsi index 9a7b6922f83b..ac870fb3fa0d 100644 --- a/arch/arm/boot/dts/vexpress-v2m-rs1.dtsi +++ b/arch/arm/boot/dts/vexpress-v2m-rs1.dtsi @@ -17,18 +17,16 @@ * CHANGES TO vexpress-v2m.dtsi! */ -/ { - aliases { - arm,v2m_timer = &v2m_timer01; - }; - motherboard { - compatible = "simple-bus"; + model = "V2M-P1"; + arm,hbi = <0x190>; arm,vexpress,site = <0>; arm,v2m-memory-map = "rs1"; + compatible = "arm,vexpress,v2m-p1", "simple-bus"; #address-cells = <2>; /* SMB chipselect number and offset */ #size-cells = <1>; #interrupt-cells = <1>; + ranges; flash@0,00000000 { compatible = "arm,vexpress-flash", "cfi-flash"; @@ -340,4 +338,3 @@ }; }; }; -}; diff --git a/arch/arm/boot/dts/vexpress-v2m.dtsi b/arch/arm/boot/dts/vexpress-v2m.dtsi index 4d321a832dbd..f1420368355b 100644 --- a/arch/arm/boot/dts/vexpress-v2m.dtsi +++ b/arch/arm/boot/dts/vexpress-v2m.dtsi @@ -17,17 +17,15 @@ * CHANGES TO vexpress-v2m-rs1.dtsi! */ -/ { - aliases { - arm,v2m_timer = &v2m_timer01; - }; - motherboard { - compatible = "simple-bus"; + model = "V2M-P1"; + arm,hbi = <0x190>; arm,vexpress,site = <0>; + compatible = "arm,vexpress,v2m-p1", "simple-bus"; #address-cells = <2>; /* SMB chipselect number and offset */ #size-cells = <1>; #interrupt-cells = <1>; + ranges; flash@0,00000000 { compatible = "arm,vexpress-flash", "cfi-flash"; @@ -339,4 +337,3 @@ }; }; }; -}; diff --git a/arch/arm/boot/dts/vexpress-v2p-ca15-tc1.dts b/arch/arm/boot/dts/vexpress-v2p-ca15-tc1.dts index 4bbed10de45b..a3d37ec2655d 100644 --- a/arch/arm/boot/dts/vexpress-v2p-ca15-tc1.dts +++ b/arch/arm/boot/dts/vexpress-v2p-ca15-tc1.dts @@ -220,7 +220,11 @@ }; }; - motherboard { + smb { + compatible = "simple-bus"; + + #address-cells = <2>; + #size-cells = <1>; ranges = <0 0 0 0x08000000 0x04000000>, <1 0 0 0x14000000 0x04000000>, <2 0 0 0x18000000 0x04000000>, @@ -228,6 +232,7 @@ <4 0 0 0x0c000000 0x04000000>, <5 0 0 0x10000000 0x04000000>; + #interrupt-cells = <1>; interrupt-map-mask = <0 0 63>; interrupt-map = <0 0 0 &gic 0 0 4>, <0 0 1 &gic 0 1 4>, @@ -272,7 +277,7 @@ <0 0 40 &gic 0 40 4>, <0 0 41 &gic 0 41 4>, <0 0 42 &gic 0 42 4>; + + /include/ "vexpress-v2m-rs1.dtsi" }; }; - -/include/ "vexpress-v2m-rs1.dtsi" diff --git a/arch/arm/boot/dts/vexpress-v2p-ca15_a7.dts b/arch/arm/boot/dts/vexpress-v2p-ca15_a7.dts index 3f4e1d00f4be..1fc405a9ecfb 100644 --- a/arch/arm/boot/dts/vexpress-v2p-ca15_a7.dts +++ b/arch/arm/boot/dts/vexpress-v2p-ca15_a7.dts @@ -305,7 +305,11 @@ }; }; - motherboard { + smb { + compatible = "simple-bus"; + + #address-cells = <2>; + #size-cells = <1>; ranges = <0 0 0 0x08000000 0x04000000>, <1 0 0 0x14000000 0x04000000>, <2 0 0 0x18000000 0x04000000>, @@ -313,6 +317,7 @@ <4 0 0 0x0c000000 0x04000000>, <5 0 0 0x10000000 0x04000000>; + #interrupt-cells = <1>; interrupt-map-mask = <0 0 63>; interrupt-map = <0 0 0 &gic 0 0 4>, <0 0 1 &gic 0 1 4>, @@ -357,7 +362,7 @@ <0 0 40 &gic 0 40 4>, <0 0 41 &gic 0 41 4>, <0 0 42 &gic 0 42 4>; + + /include/ "vexpress-v2m-rs1.dtsi" }; }; - -/include/ "vexpress-v2m-rs1.dtsi" diff --git a/arch/arm/boot/dts/vexpress-v2p-ca5s.dts b/arch/arm/boot/dts/vexpress-v2p-ca5s.dts index f3c1f2a44072..6328cbc71d30 100644 --- a/arch/arm/boot/dts/vexpress-v2p-ca5s.dts +++ b/arch/arm/boot/dts/vexpress-v2p-ca5s.dts @@ -182,7 +182,11 @@ }; }; - motherboard { + smb { + compatible = "simple-bus"; + + #address-cells = <2>; + #size-cells = <1>; ranges = <0 0 0x08000000 0x04000000>, <1 0 0x14000000 0x04000000>, <2 0 0x18000000 0x04000000>, @@ -190,6 +194,7 @@ <4 0 0x0c000000 0x04000000>, <5 0 0x10000000 0x04000000>; + #interrupt-cells = <1>; interrupt-map-mask = <0 0 63>; interrupt-map = <0 0 0 &gic 0 0 4>, <0 0 1 &gic 0 1 4>, @@ -234,7 +239,7 @@ <0 0 40 &gic 0 40 4>, <0 0 41 &gic 0 41 4>, <0 0 42 &gic 0 42 4>; + + /include/ "vexpress-v2m-rs1.dtsi" }; }; - -/include/ "vexpress-v2m-rs1.dtsi" diff --git a/arch/arm/boot/dts/vexpress-v2p-ca9.dts b/arch/arm/boot/dts/vexpress-v2p-ca9.dts index 005259db541d..1420bb14d95c 100644 --- a/arch/arm/boot/dts/vexpress-v2p-ca9.dts +++ b/arch/arm/boot/dts/vexpress-v2p-ca9.dts @@ -265,13 +265,18 @@ }; }; - motherboard { + smb { + compatible = "simple-bus"; + + #address-cells = <2>; + #size-cells = <1>; ranges = <0 0 0x40000000 0x04000000>, <1 0 0x44000000 0x04000000>, <2 0 0x48000000 0x04000000>, <3 0 0x4c000000 0x04000000>, <7 0 0x10000000 0x00020000>; + #interrupt-cells = <1>; interrupt-map-mask = <0 0 63>; interrupt-map = <0 0 0 &gic 0 0 4>, <0 0 1 &gic 0 1 4>, @@ -316,7 +321,7 @@ <0 0 40 &gic 0 40 4>, <0 0 41 &gic 0 41 4>, <0 0 42 &gic 0 42 4>; + + /include/ "vexpress-v2m.dtsi" }; }; - -/include/ "vexpress-v2m.dtsi" diff --git a/arch/arm/mach-vexpress/v2m.c b/arch/arm/mach-vexpress/v2m.c index 99d4172816be..4e168e81fb42 100644 --- a/arch/arm/mach-vexpress/v2m.c +++ b/arch/arm/mach-vexpress/v2m.c @@ -446,17 +446,20 @@ static void __init v2m_dt_init_irq(void) static void __init v2m_dt_timer_init(void) { - struct device_node *node; - const char *path; - int err; + struct device_node *node = NULL; vexpress_clk_of_init(); - err = of_property_read_string(of_aliases, "arm,v2m_timer", &path); - if (WARN_ON(err)) - return; - node = of_find_node_by_path(path); - v2m_sp804_init(of_iomap(node, 0), irq_of_parse_and_map(node, 0)); + do { + node = of_find_compatible_node(node, NULL, "arm,sp804"); + } while (node && vexpress_get_site_by_node(node) != VEXPRESS_SITE_MB); + if (node) { + pr_info("Using SP804 '%s' as a clock & events source\n", + node->full_name); + v2m_sp804_init(of_iomap(node, 0), + irq_of_parse_and_map(node, 0)); + } + if (arch_timer_of_register() != 0) twd_local_timer_of_register(); -- cgit v1.2.3-59-g8ed1b From 96f6360c5a0c20fd3e006c3030aac0d1addbe3d9 Mon Sep 17 00:00:00 2001 From: Jean-Christophe PLAGNIOL-VILLARD Date: Sun, 4 Nov 2012 21:34:51 +0000 Subject: net: at91_ether: add dt support Signed-off-by: Jean-Christophe PLAGNIOL-VILLARD Tested-by: Joachim Eastwood Cc: Nicolas Ferre Cc: netdev@vger.kernel.org Signed-off-by: David S. Miller --- .../devicetree/bindings/net/cdns-emac.txt | 23 +++++++ drivers/net/ethernet/cadence/at91_ether.c | 73 +++++++++++++++++++--- 2 files changed, 89 insertions(+), 7 deletions(-) create mode 100644 Documentation/devicetree/bindings/net/cdns-emac.txt (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/net/cdns-emac.txt b/Documentation/devicetree/bindings/net/cdns-emac.txt new file mode 100644 index 000000000000..09055c2495f0 --- /dev/null +++ b/Documentation/devicetree/bindings/net/cdns-emac.txt @@ -0,0 +1,23 @@ +* Cadence EMAC Ethernet controller + +Required properties: +- compatible: Should be "cdns,[-]{emac}" + Use "cdns,at91rm9200-emac" Atmel at91rm9200 SoC. + or the generic form: "cdns,emac". +- reg: Address and length of the register set for the device +- interrupts: Should contain macb interrupt +- phy-mode: String, operation mode of the PHY interface. + Supported values are: "mii", "rmii". + +Optional properties: +- local-mac-address: 6 bytes, mac address + +Examples: + + macb0: ethernet@fffc4000 { + compatible = "cdns,at91rm9200-emac"; + reg = <0xfffc4000 0x4000>; + interrupts = <21>; + phy-mode = "rmii"; + local-mac-address = [3a 0e 03 04 05 06]; + }; diff --git a/drivers/net/ethernet/cadence/at91_ether.c b/drivers/net/ethernet/cadence/at91_ether.c index 0d6392d24ff7..039e7ef6c50e 100644 --- a/drivers/net/ethernet/cadence/at91_ether.c +++ b/drivers/net/ethernet/cadence/at91_ether.c @@ -31,6 +31,9 @@ #include #include #include +#include +#include +#include #include "macb.h" @@ -443,6 +446,50 @@ static const struct net_device_ops at91ether_netdev_ops = { #endif }; +#if defined(CONFIG_OF) +static const struct of_device_id at91ether_dt_ids[] = { + { .compatible = "cdns,at91rm9200-emac" }, + { .compatible = "cdns,emac" }, + { /* sentinel */ } +}; + +MODULE_DEVICE_TABLE(of, at91ether_dt_ids); + +static int at91ether_get_phy_mode_dt(struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + + if (np) + return of_get_phy_mode(np); + + return -ENODEV; +} + +static int at91ether_get_hwaddr_dt(struct macb *bp) +{ + struct device_node *np = bp->pdev->dev.of_node; + + if (np) { + const char *mac = of_get_mac_address(np); + if (mac) { + memcpy(bp->dev->dev_addr, mac, ETH_ALEN); + return 0; + } + } + + return -ENODEV; +} +#else +static int at91ether_get_phy_mode_dt(struct platform_device *pdev) +{ + return -ENODEV; +} +static int at91ether_get_hwaddr_dt(struct macb *bp) +{ + return -ENODEV; +} +#endif + /* * Detect MAC & PHY and perform ethernet interface initialization */ @@ -466,7 +513,8 @@ static int __init at91ether_probe(struct platform_device *pdev) lp = netdev_priv(dev); lp->pdev = pdev; lp->dev = dev; - lp->board_data = *board_data; + if (board_data) + lp->board_data = *board_data; spin_lock_init(&lp->lock); dev->base_addr = regs->start; /* physical base address */ @@ -496,18 +544,28 @@ static int __init at91ether_probe(struct platform_device *pdev) platform_set_drvdata(pdev, dev); SET_NETDEV_DEV(dev, &pdev->dev); - get_mac_address(dev); /* Get ethernet address and store it in dev->dev_addr */ + res = at91ether_get_hwaddr_dt(lp); + if (res < 0) + get_mac_address(dev); /* Get ethernet address and store it in dev->dev_addr */ + update_mac_address(dev); /* Program ethernet address into MAC */ + res = at91ether_get_phy_mode_dt(pdev); + if (res < 0) { + if (board_data && board_data->is_rmii) + lp->phy_interface = PHY_INTERFACE_MODE_RMII; + else + lp->phy_interface = PHY_INTERFACE_MODE_MII; + } else { + lp->phy_interface = res; + } + macb_writel(lp, NCR, 0); - if (board_data->is_rmii) { + if (lp->phy_interface == PHY_INTERFACE_MODE_RMII) macb_writel(lp, NCFGR, MACB_BF(CLK, MACB_CLK_DIV32) | MACB_BIT(BIG) | MACB_BIT(RM9200_RMII)); - lp->phy_interface = PHY_INTERFACE_MODE_RMII; - } else { + else macb_writel(lp, NCFGR, MACB_BF(CLK, MACB_CLK_DIV32) | MACB_BIT(BIG)); - lp->phy_interface = PHY_INTERFACE_MODE_MII; - } /* Register the network interface */ res = register_netdev(dev); @@ -602,6 +660,7 @@ static struct platform_driver at91ether_driver = { .driver = { .name = DRV_NAME, .owner = THIS_MODULE, + .of_match_table = of_match_ptr(at91ether_dt_ids), }, }; -- cgit v1.2.3-59-g8ed1b From 099343c64e1615ae83b0994197027be363ca4899 Mon Sep 17 00:00:00 2001 From: Bo Shen Date: Wed, 7 Nov 2012 11:41:41 +0800 Subject: ARM: at91: atmel-ssc: add device tree support Add atmel-ssc for device tree support Match "atmel,at91rm9200-ssc" for using pdc for data transfer Match "atmel,at91sam9g45-ssc" for using dma for data transfer Signed-off-by: Bo Shen Acked-by: Nicolas Ferre Signed-off-by: Mark Brown --- .../devicetree/bindings/misc/atmel-ssc.txt | 15 +++++++ arch/arm/boot/dts/at91sam9260.dtsi | 8 ++++ arch/arm/boot/dts/at91sam9263.dtsi | 16 +++++++ arch/arm/boot/dts/at91sam9g45.dtsi | 16 +++++++ arch/arm/boot/dts/at91sam9x5.dtsi | 8 ++++ arch/arm/mach-at91/at91rm9200.c | 3 ++ arch/arm/mach-at91/at91sam9260.c | 1 + arch/arm/mach-at91/at91sam9261.c | 3 ++ arch/arm/mach-at91/at91sam9263.c | 2 + arch/arm/mach-at91/at91sam9g45.c | 2 + arch/arm/mach-at91/at91sam9rl.c | 2 + arch/arm/mach-at91/at91sam9x5.c | 1 + drivers/misc/atmel-ssc.c | 49 ++++++++++++++++++++-- 13 files changed, 123 insertions(+), 3 deletions(-) create mode 100644 Documentation/devicetree/bindings/misc/atmel-ssc.txt (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/misc/atmel-ssc.txt b/Documentation/devicetree/bindings/misc/atmel-ssc.txt new file mode 100644 index 000000000000..38e51ad2e07e --- /dev/null +++ b/Documentation/devicetree/bindings/misc/atmel-ssc.txt @@ -0,0 +1,15 @@ +* Atmel SSC driver. + +Required properties: +- compatible: "atmel,at91rm9200-ssc" or "atmel,at91sam9g45-ssc" + - atmel,at91rm9200-ssc: support pdc transfer + - atmel,at91sam9g45-ssc: support dma transfer +- reg: Should contain SSC registers location and length +- interrupts: Should contain SSC interrupt + +Example: +ssc0: ssc@fffbc000 { + compatible = "atmel,at91rm9200-ssc"; + reg = <0xfffbc000 0x4000>; + interrupts = <14 4 5>; +}; diff --git a/arch/arm/boot/dts/at91sam9260.dtsi b/arch/arm/boot/dts/at91sam9260.dtsi index d410581a5a85..aaa42d8d4f88 100644 --- a/arch/arm/boot/dts/at91sam9260.dtsi +++ b/arch/arm/boot/dts/at91sam9260.dtsi @@ -29,6 +29,7 @@ tcb0 = &tcb0; tcb1 = &tcb1; i2c0 = &i2c0; + ssc0 = &ssc0; }; cpus { cpu@0 { @@ -212,6 +213,13 @@ status = "disabled"; }; + ssc0: ssc@fffbc000 { + compatible = "atmel,at91rm9200-ssc"; + reg = <0xfffbc000 0x4000>; + interrupts = <14 4 5>; + status = "disable"; + }; + adc0: adc@fffe0000 { compatible = "atmel,at91sam9260-adc"; reg = <0xfffe0000 0x100>; diff --git a/arch/arm/boot/dts/at91sam9263.dtsi b/arch/arm/boot/dts/at91sam9263.dtsi index 3e6e5c1abbf3..3b721ee59b10 100644 --- a/arch/arm/boot/dts/at91sam9263.dtsi +++ b/arch/arm/boot/dts/at91sam9263.dtsi @@ -25,6 +25,8 @@ gpio4 = &pioE; tcb0 = &tcb0; i2c0 = &i2c0; + ssc0 = &ssc0; + ssc1 = &ssc1; }; cpus { cpu@0 { @@ -173,6 +175,20 @@ status = "disabled"; }; + ssc0: ssc@fff98000 { + compatible = "atmel,at91rm9200-ssc"; + reg = <0xfff98000 0x4000>; + interrupts = <16 4 5>; + status = "disable"; + }; + + ssc1: ssc@fff9c000 { + compatible = "atmel,at91rm9200-ssc"; + reg = <0xfff9c000 0x4000>; + interrupts = <17 4 5>; + status = "disable"; + }; + macb0: ethernet@fffbc000 { compatible = "cdns,at32ap7000-macb", "cdns,macb"; reg = <0xfffbc000 0x100>; diff --git a/arch/arm/boot/dts/at91sam9g45.dtsi b/arch/arm/boot/dts/at91sam9g45.dtsi index 3add030d61f8..acfa207162ff 100644 --- a/arch/arm/boot/dts/at91sam9g45.dtsi +++ b/arch/arm/boot/dts/at91sam9g45.dtsi @@ -31,6 +31,8 @@ tcb1 = &tcb1; i2c0 = &i2c0; i2c1 = &i2c1; + ssc0 = &ssc0; + ssc1 = &ssc1; }; cpus { cpu@0 { @@ -226,6 +228,20 @@ status = "disabled"; }; + ssc0: ssc@fff9c000 { + compatible = "atmel,at91sam9g45-ssc"; + reg = <0xfff9c000 0x4000>; + interrupts = <16 4 5>; + status = "disable"; + }; + + ssc1: ssc@fffa0000 { + compatible = "atmel,at91sam9g45-ssc"; + reg = <0xfffa0000 0x4000>; + interrupts = <17 4 5>; + status = "disable"; + }; + adc0: adc@fffb0000 { compatible = "atmel,at91sam9260-adc"; reg = <0xfffb0000 0x100>; diff --git a/arch/arm/boot/dts/at91sam9x5.dtsi b/arch/arm/boot/dts/at91sam9x5.dtsi index 03fc136421c5..69667d0ac347 100644 --- a/arch/arm/boot/dts/at91sam9x5.dtsi +++ b/arch/arm/boot/dts/at91sam9x5.dtsi @@ -30,6 +30,7 @@ i2c0 = &i2c0; i2c1 = &i2c1; i2c2 = &i2c2; + ssc0 = &ssc0; }; cpus { cpu@0 { @@ -87,6 +88,13 @@ interrupts = <1 4 7>; }; + ssc0: ssc@f0010000 { + compatible = "atmel,at91sam9g45-ssc"; + reg = <0xf0010000 0x4000>; + interrupts = <28 4 5>; + status = "disable"; + }; + tcb0: timer@f8008000 { compatible = "atmel,at91sam9x5-tcb"; reg = <0xf8008000 0x100>; diff --git a/arch/arm/mach-at91/at91rm9200.c b/arch/arm/mach-at91/at91rm9200.c index 400d1a34683c..af47c75db513 100644 --- a/arch/arm/mach-at91/at91rm9200.c +++ b/arch/arm/mach-at91/at91rm9200.c @@ -187,6 +187,9 @@ static struct clk_lookup periph_clocks_lookups[] = { CLKDEV_CON_DEV_ID("pclk", "at91rm9200_ssc.0", &ssc0_clk), CLKDEV_CON_DEV_ID("pclk", "at91rm9200_ssc.1", &ssc1_clk), CLKDEV_CON_DEV_ID("pclk", "at91rm9200_ssc.2", &ssc2_clk), + CLKDEV_CON_DEV_ID("pclk", "fffd0000.ssc", &ssc0_clk), + CLKDEV_CON_DEV_ID("pclk", "fffd4000.ssc", &ssc1_clk), + CLKDEV_CON_DEV_ID("pclk", "fffd8000.ssc", &ssc2_clk), CLKDEV_CON_DEV_ID(NULL, "i2c-at91rm9200.0", &twi_clk), /* fake hclk clock */ CLKDEV_CON_DEV_ID("hclk", "at91_ohci", &ohci_clk), diff --git a/arch/arm/mach-at91/at91sam9260.c b/arch/arm/mach-at91/at91sam9260.c index d14ab6aea250..a41eb3d23f68 100644 --- a/arch/arm/mach-at91/at91sam9260.c +++ b/arch/arm/mach-at91/at91sam9260.c @@ -211,6 +211,7 @@ static struct clk_lookup periph_clocks_lookups[] = { CLKDEV_CON_DEV_ID("t1_clk", "atmel_tcb.1", &tc4_clk), CLKDEV_CON_DEV_ID("t2_clk", "atmel_tcb.1", &tc5_clk), CLKDEV_CON_DEV_ID("pclk", "at91rm9200_ssc.0", &ssc_clk), + CLKDEV_CON_DEV_ID("pclk", "fffbc000.ssc", &ssc_clk), CLKDEV_CON_DEV_ID(NULL, "i2c-at91sam9260.0", &twi_clk), CLKDEV_CON_DEV_ID(NULL, "i2c-at91sam9g20.0", &twi_clk), /* more usart lookup table for DT entries */ diff --git a/arch/arm/mach-at91/at91sam9261.c b/arch/arm/mach-at91/at91sam9261.c index c7b605796dd4..7fcbe0583342 100644 --- a/arch/arm/mach-at91/at91sam9261.c +++ b/arch/arm/mach-at91/at91sam9261.c @@ -177,6 +177,9 @@ static struct clk_lookup periph_clocks_lookups[] = { CLKDEV_CON_DEV_ID("pclk", "at91rm9200_ssc.0", &ssc0_clk), CLKDEV_CON_DEV_ID("pclk", "at91rm9200_ssc.1", &ssc1_clk), CLKDEV_CON_DEV_ID("pclk", "at91rm9200_ssc.2", &ssc2_clk), + CLKDEV_CON_DEV_ID("pclk", "fffbc000.ssc", &ssc0_clk), + CLKDEV_CON_DEV_ID("pclk", "fffc0000.ssc", &ssc1_clk), + CLKDEV_CON_DEV_ID("pclk", "fffc4000.ssc", &ssc2_clk), CLKDEV_CON_DEV_ID("hclk", "at91_ohci", &hck0), CLKDEV_CON_DEV_ID(NULL, "i2c-at91sam9261.0", &twi_clk), CLKDEV_CON_DEV_ID(NULL, "i2c-at91sam9g10.0", &twi_clk), diff --git a/arch/arm/mach-at91/at91sam9263.c b/arch/arm/mach-at91/at91sam9263.c index b87be18f08ae..c0f4c8c1f4ed 100644 --- a/arch/arm/mach-at91/at91sam9263.c +++ b/arch/arm/mach-at91/at91sam9263.c @@ -188,6 +188,8 @@ static struct clk_lookup periph_clocks_lookups[] = { CLKDEV_CON_ID("hclk", &macb_clk), CLKDEV_CON_DEV_ID("pclk", "at91rm9200_ssc.0", &ssc0_clk), CLKDEV_CON_DEV_ID("pclk", "at91rm9200_ssc.1", &ssc1_clk), + CLKDEV_CON_DEV_ID("pclk", "fff98000.ssc", &ssc0_clk), + CLKDEV_CON_DEV_ID("pclk", "fff9c000.ssc", &ssc1_clk), CLKDEV_CON_DEV_ID("mci_clk", "atmel_mci.0", &mmc0_clk), CLKDEV_CON_DEV_ID("mci_clk", "atmel_mci.1", &mmc1_clk), CLKDEV_CON_DEV_ID("spi_clk", "atmel_spi.0", &spi0_clk), diff --git a/arch/arm/mach-at91/at91sam9g45.c b/arch/arm/mach-at91/at91sam9g45.c index f4f96a61e4b8..a4282d3742bf 100644 --- a/arch/arm/mach-at91/at91sam9g45.c +++ b/arch/arm/mach-at91/at91sam9g45.c @@ -241,6 +241,8 @@ static struct clk_lookup periph_clocks_lookups[] = { CLKDEV_CON_DEV_ID(NULL, "i2c-at91sam9g10.1", &twi1_clk), CLKDEV_CON_DEV_ID("pclk", "at91sam9g45_ssc.0", &ssc0_clk), CLKDEV_CON_DEV_ID("pclk", "at91sam9g45_ssc.1", &ssc1_clk), + CLKDEV_CON_DEV_ID("pclk", "fff9c000.ssc", &ssc0_clk), + CLKDEV_CON_DEV_ID("pclk", "fffa0000.ssc", &ssc1_clk), CLKDEV_CON_DEV_ID(NULL, "atmel-trng", &trng_clk), CLKDEV_CON_DEV_ID(NULL, "atmel_sha", &aestdessha_clk), CLKDEV_CON_DEV_ID(NULL, "atmel_tdes", &aestdessha_clk), diff --git a/arch/arm/mach-at91/at91sam9rl.c b/arch/arm/mach-at91/at91sam9rl.c index 4110b5480d49..b683fdc699f1 100644 --- a/arch/arm/mach-at91/at91sam9rl.c +++ b/arch/arm/mach-at91/at91sam9rl.c @@ -186,6 +186,8 @@ static struct clk_lookup periph_clocks_lookups[] = { CLKDEV_CON_DEV_ID("t2_clk", "atmel_tcb.0", &tc2_clk), CLKDEV_CON_DEV_ID("pclk", "at91rm9200_ssc.0", &ssc0_clk), CLKDEV_CON_DEV_ID("pclk", "at91rm9200_ssc.1", &ssc1_clk), + CLKDEV_CON_DEV_ID("pclk", "fffc0000.ssc", &ssc0_clk), + CLKDEV_CON_DEV_ID("pclk", "fffc4000.ssc", &ssc1_clk), CLKDEV_CON_DEV_ID(NULL, "i2c-at91sam9g20.0", &twi0_clk), CLKDEV_CON_DEV_ID(NULL, "i2c-at91sam9g20.1", &twi1_clk), CLKDEV_CON_ID("pioA", &pioA_clk), diff --git a/arch/arm/mach-at91/at91sam9x5.c b/arch/arm/mach-at91/at91sam9x5.c index e5035380dcbc..18fbbb27f97f 100644 --- a/arch/arm/mach-at91/at91sam9x5.c +++ b/arch/arm/mach-at91/at91sam9x5.c @@ -231,6 +231,7 @@ static struct clk_lookup periph_clocks_lookups[] = { CLKDEV_CON_DEV_ID("t0_clk", "f800c000.timer", &tcb0_clk), CLKDEV_CON_DEV_ID("dma_clk", "ffffec00.dma-controller", &dma0_clk), CLKDEV_CON_DEV_ID("dma_clk", "ffffee00.dma-controller", &dma1_clk), + CLKDEV_CON_DEV_ID("pclk", "f0010000.ssc", &ssc_clk), CLKDEV_CON_DEV_ID(NULL, "f8010000.i2c", &twi0_clk), CLKDEV_CON_DEV_ID(NULL, "f8014000.i2c", &twi1_clk), CLKDEV_CON_DEV_ID(NULL, "f8018000.i2c", &twi2_clk), diff --git a/drivers/misc/atmel-ssc.c b/drivers/misc/atmel-ssc.c index f40abd8a6695..a769719e36bf 100644 --- a/drivers/misc/atmel-ssc.c +++ b/drivers/misc/atmel-ssc.c @@ -18,6 +18,8 @@ #include #include +#include + /* Serialize access to ssc_list and user count */ static DEFINE_SPINLOCK(user_lock); static LIST_HEAD(ssc_list); @@ -29,7 +31,13 @@ struct ssc_device *ssc_request(unsigned int ssc_num) spin_lock(&user_lock); list_for_each_entry(ssc, &ssc_list, list) { - if (ssc->pdev->id == ssc_num) { + if (ssc->pdev->dev.of_node) { + if (of_alias_get_id(ssc->pdev->dev.of_node, "ssc") + == ssc_num) { + ssc_valid = 1; + break; + } + } else if (ssc->pdev->id == ssc_num) { ssc_valid = 1; break; } @@ -88,10 +96,41 @@ static const struct platform_device_id atmel_ssc_devtypes[] = { } }; +#ifdef CONFIG_OF +static const struct of_device_id atmel_ssc_dt_ids[] = { + { + .compatible = "atmel,at91rm9200-ssc", + .data = &at91rm9200_config, + }, { + .compatible = "atmel,at91sam9g45-ssc", + .data = &at91sam9g45_config, + }, { + /* sentinel */ + } +}; +MODULE_DEVICE_TABLE(of, atmel_ssc_dt_ids); +#endif + +static inline const struct atmel_ssc_platform_data * __init + atmel_ssc_get_driver_data(struct platform_device *pdev) +{ + if (pdev->dev.of_node) { + const struct of_device_id *match; + match = of_match_node(atmel_ssc_dt_ids, pdev->dev.of_node); + if (match == NULL) + return NULL; + return match->data; + } + + return (struct atmel_ssc_platform_data *) + platform_get_device_id(pdev)->driver_data; +} + static int ssc_probe(struct platform_device *pdev) { struct resource *regs; struct ssc_device *ssc; + const struct atmel_ssc_platform_data *plat_dat; ssc = devm_kzalloc(&pdev->dev, sizeof(struct ssc_device), GFP_KERNEL); if (!ssc) { @@ -100,8 +139,11 @@ static int ssc_probe(struct platform_device *pdev) } ssc->pdev = pdev; - ssc->pdata = (struct atmel_ssc_platform_data *) - platform_get_device_id(pdev)->driver_data; + + plat_dat = atmel_ssc_get_driver_data(pdev); + if (!plat_dat) + return -ENODEV; + ssc->pdata = (struct atmel_ssc_platform_data *)plat_dat; regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!regs) { @@ -160,6 +202,7 @@ static struct platform_driver ssc_driver = { .driver = { .name = "ssc", .owner = THIS_MODULE, + .of_match_table = of_match_ptr(atmel_ssc_dt_ids), }, .id_table = atmel_ssc_devtypes, .probe = ssc_probe, -- cgit v1.2.3-59-g8ed1b From 5a2bc4c6607c709aa69f508502e33aebbf32f1f5 Mon Sep 17 00:00:00 2001 From: Afzal Mohammed Date: Tue, 6 Nov 2012 19:26:06 +0530 Subject: usb: musb: dsps: dt binding - add resources, example OMAP2+ family of devices are now obtaining resources via DT, earlier it was obtained from hwmod. Update binding document accrodingly, while at it add example. Signed-off-by: Afzal Mohammed Reviewed-by: Felipe Balbi Signed-off-by: Benoit Cousson --- .../devicetree/bindings/usb/am33xx-usb.txt | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/usb/am33xx-usb.txt b/Documentation/devicetree/bindings/usb/am33xx-usb.txt index ca8fa56e9f03..707c1a2dae06 100644 --- a/Documentation/devicetree/bindings/usb/am33xx-usb.txt +++ b/Documentation/devicetree/bindings/usb/am33xx-usb.txt @@ -1,5 +1,7 @@ AM33XX MUSB GLUE - compatible : Should be "ti,musb-am33xx" + - reg : offset and length of register sets, first usbss, then for musb instances + - interrupts : usbss, musb instance interrupts in order - ti,hwmods : must be "usb_otg_hs" - multipoint : Should be "1" indicating the musb controller supports multipoint. This is a MUSB configuration-specific setting. @@ -12,3 +14,22 @@ AM33XX MUSB GLUE represents PERIPHERAL. - power : Should be "250". This signifies the controller can supply upto 500mA when operating in host mode. + +Example: + +usb@47400000 { + compatible = "ti,musb-am33xx"; + reg = <0x47400000 0x1000 /* usbss */ + 0x47401000 0x800 /* musb instance 0 */ + 0x47401800 0x800>; /* musb instance 1 */ + interrupts = <17 /* usbss */ + 18 /* musb instance 0 */ + 19>; /* musb instance 1 */ + multipoint = <1>; + num-eps = <16>; + ram-bits = <12>; + port0-mode = <3>; + port1-mode = <3>; + power = <250>; + ti,hwmods = "usb_otg_hs"; +}; -- cgit v1.2.3-59-g8ed1b From 45b6f8e8fc014fe404d155a657a04b25b861001d Mon Sep 17 00:00:00 2001 From: Qing Xu Date: Wed, 7 Nov 2012 18:09:53 +0800 Subject: regulator: max8925: support dt for regulator Signed-off-by: Qing Xu Signed-off-by: Mark Brown --- .../bindings/regulator/max8925-regulator.txt | 29 +++++++++ drivers/regulator/max8925-regulator.c | 72 +++++++++++++++++++++- 2 files changed, 98 insertions(+), 3 deletions(-) create mode 100644 Documentation/devicetree/bindings/regulator/max8925-regulator.txt (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/regulator/max8925-regulator.txt b/Documentation/devicetree/bindings/regulator/max8925-regulator.txt new file mode 100644 index 000000000000..073b41d90f2c --- /dev/null +++ b/Documentation/devicetree/bindings/regulator/max8925-regulator.txt @@ -0,0 +1,29 @@ +Max8925 Voltage regulators + +max8925 regulator device register is still handled by mfd_add_devices, not by +of_xxx, so, it is not necessary to add compatible name. Also, those reg +offset and id info is stored in mfd_cell(see max8925-core.c), as a result +there is not private properties in dts. + +node's name should match with the definition in max8925_regulator_matches +(see max8925-regulator.c) + + +Optional properties: +- Any optional property defined in bindings/regulator/regulator.txt + + +Example: + + + regulators { + SDV1 { + regulator-min-microvolt = <637500>; + regulator-max-microvolt = <1425000>; + regulator-boot-on; + regulator-always-on; + }; + + ... + ... + } diff --git a/drivers/regulator/max8925-regulator.c b/drivers/regulator/max8925-regulator.c index 9bb0be37495f..2b549791c975 100644 --- a/drivers/regulator/max8925-regulator.c +++ b/drivers/regulator/max8925-regulator.c @@ -17,6 +17,8 @@ #include #include #include +#include +#include #define SD1_DVM_VMIN 850000 #define SD1_DVM_VMAX 1000000 @@ -187,6 +189,34 @@ static struct regulator_ops max8925_regulator_ldo_ops = { .enable_reg = MAX8925_LDOCTL##_id, \ } +#ifdef CONFIG_OF +static struct of_regulator_match max8925_regulator_matches[] = { + { .name = "SDV1",}, + { .name = "SDV2",}, + { .name = "SDV3",}, + { .name = "LDO1",}, + { .name = "LDO2",}, + { .name = "LDO3",}, + { .name = "LDO4",}, + { .name = "LDO5",}, + { .name = "LDO6",}, + { .name = "LDO7",}, + { .name = "LDO8",}, + { .name = "LDO9",}, + { .name = "LDO10",}, + { .name = "LDO11",}, + { .name = "LDO12",}, + { .name = "LDO13",}, + { .name = "LDO14",}, + { .name = "LDO15",}, + { .name = "LDO16",}, + { .name = "LDO17",}, + { .name = "LDO18",}, + { .name = "LDO19",}, + { .name = "LDO20",}, +}; +#endif + static struct max8925_regulator_info max8925_regulator_info[] = { MAX8925_SDV(1, 637.5, 1425, 12.5), MAX8925_SDV(2, 650, 2225, 25), @@ -214,6 +244,36 @@ static struct max8925_regulator_info max8925_regulator_info[] = { MAX8925_LDO(20, 750, 3900, 50), }; +#ifdef CONFIG_OF +static int max8925_regulator_dt_init(struct platform_device *pdev, + struct max8925_regulator_info *info, + struct regulator_config *config, + int ridx) +{ + struct device_node *nproot, *np; + int rcount; + nproot = pdev->dev.parent->of_node; + if (!nproot) + return -ENODEV; + np = of_find_node_by_name(nproot, "regulators"); + if (!np) { + dev_err(&pdev->dev, "failed to find regulators node\n"); + return -ENODEV; + } + + rcount = of_regulator_match(&pdev->dev, np, + &max8925_regulator_matches[ridx], 1); + if (rcount < 0) + return -ENODEV; + config->init_data = max8925_regulator_matches[ridx].init_data; + config->of_node = max8925_regulator_matches[ridx].of_node; + + return 0; +} +#else +#define max8925_regulator_dt_init(w, x, y, z) (-1) +#endif + static int __devinit max8925_regulator_probe(struct platform_device *pdev) { struct max8925_chip *chip = dev_get_drvdata(pdev->dev.parent); @@ -222,7 +282,7 @@ static int __devinit max8925_regulator_probe(struct platform_device *pdev) struct max8925_regulator_info *ri; struct resource *res; struct regulator_dev *rdev; - int i; + int i, regulator_idx; res = platform_get_resource(pdev, IORESOURCE_REG, 0); if (!res) { @@ -231,9 +291,12 @@ static int __devinit max8925_regulator_probe(struct platform_device *pdev) } for (i = 0; i < ARRAY_SIZE(max8925_regulator_info); i++) { ri = &max8925_regulator_info[i]; - if (ri->vol_reg == res->start) + if (ri->vol_reg == res->start) { + regulator_idx = i; break; + } } + if (i == ARRAY_SIZE(max8925_regulator_info)) { dev_err(&pdev->dev, "Failed to find regulator %llu\n", (unsigned long long)res->start); @@ -243,9 +306,12 @@ static int __devinit max8925_regulator_probe(struct platform_device *pdev) ri->chip = chip; config.dev = &pdev->dev; - config.init_data = pdata; config.driver_data = ri; + if (max8925_regulator_dt_init(pdev, ri, &config, regulator_idx)) + if (pdata) + config.init_data = pdata; + rdev = regulator_register(&ri->desc, &config); if (IS_ERR(rdev)) { dev_err(&pdev->dev, "failed to register regulator %s\n", -- cgit v1.2.3-59-g8ed1b From 0e861a3c4ffef56822e1d51c355e5020deaeaf5a Mon Sep 17 00:00:00 2001 From: Antonio Quartulli Date: Mon, 1 Oct 2012 09:57:36 +0200 Subject: batman-adv: Distributed ARP Table - add a new debug log level A new log level has been added to concentrate messages regarding DAT: ARP snooping, requests, response and DHT related messages. The new log level is named BATADV_DBG_DAT Signed-off-by: Antonio Quartulli --- Documentation/networking/batman-adv.txt | 3 ++- net/batman-adv/main.h | 4 +++- 2 files changed, 5 insertions(+), 2 deletions(-) (limited to 'Documentation') diff --git a/Documentation/networking/batman-adv.txt b/Documentation/networking/batman-adv.txt index a173d2a879f5..c1d82047a4b1 100644 --- a/Documentation/networking/batman-adv.txt +++ b/Documentation/networking/batman-adv.txt @@ -203,7 +203,8 @@ abled during run time. Following log_levels are defined: 2 - Enable messages related to route added / changed / deleted 4 - Enable messages related to translation table operations 8 - Enable messages related to bridge loop avoidance -15 - enable all messages +16 - Enable messaged related to DAT, ARP snooping and parsing +31 - Enable all messages The debug output can be changed at runtime using the file /sys/class/net/bat0/mesh/log_level. e.g. diff --git a/net/batman-adv/main.h b/net/batman-adv/main.h index 897ba6a8f8f2..c08724a165b9 100644 --- a/net/batman-adv/main.h +++ b/net/batman-adv/main.h @@ -171,6 +171,7 @@ int batadv_algo_seq_print_text(struct seq_file *seq, void *offset); * @BATADV_DBG_ROUTES: route added / changed / deleted * @BATADV_DBG_TT: translation table messages * @BATADV_DBG_BLA: bridge loop avoidance messages + * @BATADV_DBG_DAT: ARP snooping and DAT related messages * @BATADV_DBG_ALL: the union of all the above log levels */ enum batadv_dbg_level { @@ -178,7 +179,8 @@ enum batadv_dbg_level { BATADV_DBG_ROUTES = BIT(1), BATADV_DBG_TT = BIT(2), BATADV_DBG_BLA = BIT(3), - BATADV_DBG_ALL = 15, + BATADV_DBG_DAT = BIT(4), + BATADV_DBG_ALL = 31, }; #ifdef CONFIG_BATMAN_ADV_DEBUG -- cgit v1.2.3-59-g8ed1b From 5920cd3a41f1aefc30e9ce86384fc2fe9f5fe0c0 Mon Sep 17 00:00:00 2001 From: Paul Chavent Date: Tue, 6 Nov 2012 23:10:47 +0000 Subject: packet: tx_ring: allow the user to choose tx data offset The tx data offset of packet mmap tx ring used to be : (TPACKET2_HDRLEN - sizeof(struct sockaddr_ll)) The problem is that, with SOCK_RAW socket, the payload (14 bytes after the beginning of the user data) is misaligned. This patch allows to let the user gives an offset for it's tx data if he desires. Set sock option PACKET_TX_HAS_OFF to 1, then specify in each frame of your tx ring tp_net for SOCK_DGRAM, or tp_mac for SOCK_RAW. Signed-off-by: Paul Chavent Signed-off-by: David S. Miller --- Documentation/networking/packet_mmap.txt | 13 +++++++++ include/uapi/linux/if_packet.h | 1 + net/packet/af_packet.c | 46 +++++++++++++++++++++++++++++++- net/packet/internal.h | 1 + 4 files changed, 60 insertions(+), 1 deletion(-) (limited to 'Documentation') diff --git a/Documentation/networking/packet_mmap.txt b/Documentation/networking/packet_mmap.txt index 1c08a4b0981f..7cd879eba5dc 100644 --- a/Documentation/networking/packet_mmap.txt +++ b/Documentation/networking/packet_mmap.txt @@ -163,6 +163,19 @@ As capture, each frame contains two parts: A complete tutorial is available at: http://wiki.gnu-log.net/ +By default, the user should put data at : + frame base + TPACKET_HDRLEN - sizeof(struct sockaddr_ll) + +So, whatever you choose for the socket mode (SOCK_DGRAM or SOCK_RAW), +the beginning of the user data will be at : + frame base + TPACKET_ALIGN(sizeof(struct tpacket_hdr)) + +If you wish to put user data at a custom offset from the beginning of +the frame (for payload alignment with SOCK_RAW mode for instance) you +can set tp_net (with SOCK_DGRAM) or tp_mac (with SOCK_RAW). In order +to make this work it must be enabled previously with setsockopt() +and the PACKET_TX_HAS_OFF option. + -------------------------------------------------------------------------------- + PACKET_MMAP settings -------------------------------------------------------------------------------- diff --git a/include/uapi/linux/if_packet.h b/include/uapi/linux/if_packet.h index f3799295d231..f9a60375f0d0 100644 --- a/include/uapi/linux/if_packet.h +++ b/include/uapi/linux/if_packet.h @@ -50,6 +50,7 @@ struct sockaddr_ll { #define PACKET_TX_TIMESTAMP 16 #define PACKET_TIMESTAMP 17 #define PACKET_FANOUT 18 +#define PACKET_TX_HAS_OFF 19 #define PACKET_FANOUT_HASH 0 #define PACKET_FANOUT_LB 1 diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c index 9034f52659b5..f262dbfc7f06 100644 --- a/net/packet/af_packet.c +++ b/net/packet/af_packet.c @@ -1881,7 +1881,35 @@ static int tpacket_fill_skb(struct packet_sock *po, struct sk_buff *skb, skb_reserve(skb, hlen); skb_reset_network_header(skb); - data = ph.raw + po->tp_hdrlen - sizeof(struct sockaddr_ll); + if (po->tp_tx_has_off) { + int off_min, off_max, off; + off_min = po->tp_hdrlen - sizeof(struct sockaddr_ll); + off_max = po->tx_ring.frame_size - tp_len; + if (sock->type == SOCK_DGRAM) { + switch (po->tp_version) { + case TPACKET_V2: + off = ph.h2->tp_net; + break; + default: + off = ph.h1->tp_net; + break; + } + } else { + switch (po->tp_version) { + case TPACKET_V2: + off = ph.h2->tp_mac; + break; + default: + off = ph.h1->tp_mac; + break; + } + } + if (unlikely((off < off_min) || (off_max < off))) + return -EINVAL; + data = ph.raw + off; + } else { + data = ph.raw + po->tp_hdrlen - sizeof(struct sockaddr_ll); + } to_write = tp_len; if (sock->type == SOCK_DGRAM) { @@ -3109,6 +3137,19 @@ packet_setsockopt(struct socket *sock, int level, int optname, char __user *optv return fanout_add(sk, val & 0xffff, val >> 16); } + case PACKET_TX_HAS_OFF: + { + unsigned int val; + + if (optlen != sizeof(val)) + return -EINVAL; + if (po->rx_ring.pg_vec || po->tx_ring.pg_vec) + return -EBUSY; + if (copy_from_user(&val, optval, sizeof(val))) + return -EFAULT; + po->tp_tx_has_off = !!val; + return 0; + } default: return -ENOPROTOOPT; } @@ -3200,6 +3241,9 @@ static int packet_getsockopt(struct socket *sock, int level, int optname, ((u32)po->fanout->type << 16)) : 0); break; + case PACKET_TX_HAS_OFF: + val = po->tp_tx_has_off; + break; default: return -ENOPROTOOPT; } diff --git a/net/packet/internal.h b/net/packet/internal.h index 44945f6b7252..e84cab8cb7a9 100644 --- a/net/packet/internal.h +++ b/net/packet/internal.h @@ -109,6 +109,7 @@ struct packet_sock { unsigned int tp_hdrlen; unsigned int tp_reserve; unsigned int tp_loss:1; + unsigned int tp_tx_has_off:1; unsigned int tp_tstamp; struct packet_type prot_hook ____cacheline_aligned_in_smp; }; -- cgit v1.2.3-59-g8ed1b From cb73a9f4649bf63c0397e565a15abf8a91ecf56f Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Thu, 1 Nov 2012 18:09:48 -0400 Subject: nfsd4: implement backchannel_ctl operation This operation is mandatory for servers to implement. Signed-off-by: J. Bruce Fields --- Documentation/filesystems/nfs/nfs41-server.txt | 7 +------ fs/nfsd/nfs4proc.c | 6 ++++++ fs/nfsd/nfs4state.c | 14 ++++++++++++++ fs/nfsd/nfs4xdr.c | 13 ++++++++++++- fs/nfsd/state.h | 5 +++++ fs/nfsd/xdr4.h | 2 ++ 6 files changed, 40 insertions(+), 7 deletions(-) (limited to 'Documentation') diff --git a/Documentation/filesystems/nfs/nfs41-server.txt b/Documentation/filesystems/nfs/nfs41-server.txt index 092fad92a3f0..f5ddff2da302 100644 --- a/Documentation/filesystems/nfs/nfs41-server.txt +++ b/Documentation/filesystems/nfs/nfs41-server.txt @@ -39,11 +39,6 @@ interoperability problems with future clients. Known issues: from a linux client are possible, but we aren't really conformant with the spec (for example, we don't use kerberos on the backchannel correctly). - - Incomplete backchannel support: incomplete backchannel gss - support and no support for BACKCHANNEL_CTL mean that - callbacks (hence delegations and layouts) may not be - available and clients confused by the incomplete - implementation may fail. - We do not support SSV, which provides security for shared client-server state (thus preventing unauthorized tampering with locks and opens, for example). It is mandatory for @@ -89,7 +84,7 @@ Operations | | MNI | or OPT) | | +----------------------+------------+--------------+----------------+ | ACCESS | REQ | | Section 18.1 | -NS | BACKCHANNEL_CTL | REQ | | Section 18.33 | +I | BACKCHANNEL_CTL | REQ | | Section 18.33 | I | BIND_CONN_TO_SESSION | REQ | | Section 18.34 | | CLOSE | REQ | | Section 18.2 | | COMMIT | REQ | | Section 18.3 | diff --git a/fs/nfsd/nfs4proc.c b/fs/nfsd/nfs4proc.c index 6c9a4b291dba..f955176f1b6f 100644 --- a/fs/nfsd/nfs4proc.c +++ b/fs/nfsd/nfs4proc.c @@ -1666,6 +1666,12 @@ static struct nfsd4_operation nfsd4_ops[] = { .op_name = "OP_EXCHANGE_ID", .op_rsize_bop = (nfsd4op_rsize)nfsd4_exchange_id_rsize, }, + [OP_BACKCHANNEL_CTL] = { + .op_func = (nfsd4op_func)nfsd4_backchannel_ctl, + .op_flags = ALLOWED_WITHOUT_FH | OP_MODIFIES_SOMETHING, + .op_name = "OP_BACKCHANNEL_CTL", + .op_rsize_bop = (nfsd4op_rsize)nfsd4_only_status_rsize, + }, [OP_BIND_CONN_TO_SESSION] = { .op_func = (nfsd4op_func)nfsd4_bind_conn_to_session, .op_flags = ALLOWED_WITHOUT_FH | ALLOWED_AS_FIRST_OP diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index dbbbd2fe5236..4023e77687ee 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -1865,6 +1865,20 @@ static __be32 nfsd4_map_bcts_dir(u32 *dir) return nfserr_inval; } +__be32 nfsd4_backchannel_ctl(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, struct nfsd4_backchannel_ctl *bc) +{ + struct nfsd4_session *session = cstate->session; + + spin_lock(&client_lock); + session->se_cb_prog = bc->bc_cb_program; + session->se_cb_sec = bc->bc_cb_sec; + spin_unlock(&client_lock); + + nfsd4_probe_callback(session->se_client); + + return nfs_ok; +} + __be32 nfsd4_bind_conn_to_session(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, struct nfsd4_bind_conn_to_session *bcts) diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c index 511f980b605c..d7e7c110246e 100644 --- a/fs/nfsd/nfs4xdr.c +++ b/fs/nfsd/nfs4xdr.c @@ -483,6 +483,17 @@ static __be32 nfsd4_decode_cb_sec(struct nfsd4_compoundargs *argp, struct nfsd4_ DECODE_TAIL; } +static __be32 nfsd4_decode_backchannel_ctl(struct nfsd4_compoundargs *argp, struct nfsd4_backchannel_ctl *bc) +{ + DECODE_HEAD; + + READ_BUF(4); + READ32(bc->bc_cb_program); + nfsd4_decode_cb_sec(argp, &bc->bc_cb_sec); + + DECODE_TAIL; +} + static __be32 nfsd4_decode_bind_conn_to_session(struct nfsd4_compoundargs *argp, struct nfsd4_bind_conn_to_session *bcts) { DECODE_HEAD; @@ -1536,7 +1547,7 @@ static nfsd4_dec nfsd41_dec_ops[] = { [OP_RELEASE_LOCKOWNER] = (nfsd4_dec)nfsd4_decode_notsupp, /* new operations for NFSv4.1 */ - [OP_BACKCHANNEL_CTL] = (nfsd4_dec)nfsd4_decode_notsupp, + [OP_BACKCHANNEL_CTL] = (nfsd4_dec)nfsd4_decode_backchannel_ctl, [OP_BIND_CONN_TO_SESSION]= (nfsd4_dec)nfsd4_decode_bind_conn_to_session, [OP_EXCHANGE_ID] = (nfsd4_dec)nfsd4_decode_exchange_id, [OP_CREATE_SESSION] = (nfsd4_dec)nfsd4_decode_create_session, diff --git a/fs/nfsd/state.h b/fs/nfsd/state.h index bff856c34a32..758bc9c2646b 100644 --- a/fs/nfsd/state.h +++ b/fs/nfsd/state.h @@ -166,6 +166,11 @@ struct nfsd4_create_session { struct nfsd4_cb_sec cb_sec; }; +struct nfsd4_backchannel_ctl { + u32 bc_cb_program; + struct nfsd4_cb_sec bc_cb_sec; +}; + struct nfsd4_bind_conn_to_session { struct nfs4_sessionid sessionid; u32 dir; diff --git a/fs/nfsd/xdr4.h b/fs/nfsd/xdr4.h index acd127d4ee82..71c5c47f2750 100644 --- a/fs/nfsd/xdr4.h +++ b/fs/nfsd/xdr4.h @@ -462,6 +462,7 @@ struct nfsd4_op { /* NFSv4.1 */ struct nfsd4_exchange_id exchange_id; + struct nfsd4_backchannel_ctl backchannel_ctl; struct nfsd4_bind_conn_to_session bind_conn_to_session; struct nfsd4_create_session create_session; struct nfsd4_destroy_session destroy_session; @@ -566,6 +567,7 @@ extern __be32 nfsd4_replay_cache_entry(struct nfsd4_compoundres *resp, struct nfsd4_sequence *seq); extern __be32 nfsd4_exchange_id(struct svc_rqst *rqstp, struct nfsd4_compound_state *, struct nfsd4_exchange_id *); +extern __be32 nfsd4_backchannel_ctl(struct svc_rqst *, struct nfsd4_compound_state *, struct nfsd4_backchannel_ctl *); extern __be32 nfsd4_bind_conn_to_session(struct svc_rqst *, struct nfsd4_compound_state *, struct nfsd4_bind_conn_to_session *); extern __be32 nfsd4_create_session(struct svc_rqst *, struct nfsd4_compound_state *, -- cgit v1.2.3-59-g8ed1b From fa06920a3ece1ed43333992d35c0044e7a6c048a Mon Sep 17 00:00:00 2001 From: Michal Nazarewicz Date: Tue, 6 Nov 2012 22:52:36 +0100 Subject: usb: gadget: Remove File-backed Storage Gadget (g_file_storage). The File-backed Storage Gadget (g_file_storage) gadget has been replaced with Mass Storage Gadget (g_mass_storage) which uses the composite framework. This commit removes g_file_storage (and most references to it). Signed-off-by: Michal Nazarewicz Acked-by: Alan Stern Signed-off-by: Felipe Balbi --- Documentation/DocBook/gadget.tmpl | 2 +- Documentation/usb/mass-storage.txt | 15 +- drivers/usb/gadget/Kconfig | 29 +- drivers/usb/gadget/Makefile | 2 - drivers/usb/gadget/file_storage.c | 3656 ------------------------------------ drivers/usb/gadget/net2280.c | 2 +- drivers/usb/gadget/pxa27x_udc.h | 2 +- 7 files changed, 12 insertions(+), 3696 deletions(-) delete mode 100644 drivers/usb/gadget/file_storage.c (limited to 'Documentation') diff --git a/Documentation/DocBook/gadget.tmpl b/Documentation/DocBook/gadget.tmpl index 6ef2f0073e5a..4017f147ba2f 100644 --- a/Documentation/DocBook/gadget.tmpl +++ b/Documentation/DocBook/gadget.tmpl @@ -671,7 +671,7 @@ than a kernel driver. There's a USB Mass Storage class driver, which provides a different solution for interoperability with systems such as MS-Windows and MacOS. -That File-backed Storage driver uses a +That Mass Storage driver uses a file or block device as backing store for a drive, like the loop driver. The USB host uses the BBB, CB, or CBI versions of the mass diff --git a/Documentation/usb/mass-storage.txt b/Documentation/usb/mass-storage.txt index e9b9334627bf..59063ad7a60d 100644 --- a/Documentation/usb/mass-storage.txt +++ b/Documentation/usb/mass-storage.txt @@ -20,9 +20,9 @@ This document describes how to use the gadget from user space, its relation to mass storage function (or MSF) and different gadgets - using it, and how it differs from File Storage Gadget (or FSG). It - will talk only briefly about how to use MSF within composite - gadgets. + using it, and how it differs from File Storage Gadget (or FSG) + (which is no longer included in Linux). It will talk only briefly + about how to use MSF within composite gadgets. * Module parameters @@ -198,16 +198,15 @@ The Mass Storage Function and thus the Mass Storage Gadget has been based on the File Storage Gadget. The difference between the two is that MSG is a composite gadget (ie. uses the composite framework) - while file storage gadget is a traditional gadget. From userspace + while file storage gadget was a traditional gadget. From userspace point of view this distinction does not really matter, but from kernel hacker's point of view, this means that (i) MSG does not duplicate code needed for handling basic USB protocol commands and (ii) MSF can be used in any other composite gadget. - Because of that, File Storage Gadget has been deprecated and - scheduled to be removed in Linux 3.8. All users need to transition - to the Mass Storage Gadget by that time. The two gadgets behave - mostly the same from the outside except: + Because of that, File Storage Gadget has been removed in Linux 3.8. + All users need to transition to the Mass Storage Gadget. The two + gadgets behave mostly the same from the outside except: 1. In FSG the “removable” and “cdrom” module parameters set the flag for all logical units whereas in MSG they accept a list of y/n diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig index dfb51a45496c..63b5869e0940 100644 --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig @@ -721,31 +721,6 @@ config USB_FUNCTIONFS_GENERIC Include a configuration with the Function Filesystem alone with no Ethernet interface. -config USB_FILE_STORAGE - tristate "File-backed Storage Gadget (DEPRECATED)" - depends on BLOCK - help - The File-backed Storage Gadget acts as a USB Mass Storage - disk drive. As its storage repository it can use a regular - file or a block device (in much the same way as the "loop" - device driver), specified as a module parameter. - - Say "y" to link the driver statically, or "m" to build a - dynamically linked module called "g_file_storage". - - NOTE: This driver is deprecated. Its replacement is the - Mass Storage Gadget. - -config USB_FILE_STORAGE_TEST - bool "File-backed Storage Gadget testing version" - depends on USB_FILE_STORAGE - default n - help - Say "y" to generate the larger testing version of the - File-backed Storage Gadget, useful for probing the - behavior of USB Mass Storage hosts. Not needed for - normal operation. - config USB_MASS_STORAGE tristate "Mass Storage Gadget" depends on BLOCK @@ -756,8 +731,8 @@ config USB_MASS_STORAGE device (in much the same way as the "loop" device driver), specified as a module parameter or sysfs option. - This driver is an updated replacement for the deprecated - File-backed Storage Gadget (g_file_storage). + This driver is a replacement for now removed File-backed + Storage Gadget (g_file_storage). Say "y" to link the driver statically, or "m" to build a dynamically linked module called "g_mass_storage". diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile index 307be5fa824c..8b4acfd92aa3 100644 --- a/drivers/usb/gadget/Makefile +++ b/drivers/usb/gadget/Makefile @@ -44,7 +44,6 @@ g_ether-y := ether.o g_serial-y := serial.o g_midi-y := gmidi.o gadgetfs-y := inode.o -g_file_storage-y := file_storage.o g_mass_storage-y := mass_storage.o g_printer-y := printer.o g_cdc-y := cdc2.o @@ -62,7 +61,6 @@ obj-$(CONFIG_USB_AUDIO) += g_audio.o obj-$(CONFIG_USB_ETH) += g_ether.o obj-$(CONFIG_USB_GADGETFS) += gadgetfs.o obj-$(CONFIG_USB_FUNCTIONFS) += g_ffs.o -obj-$(CONFIG_USB_FILE_STORAGE) += g_file_storage.o obj-$(CONFIG_USB_MASS_STORAGE) += g_mass_storage.o obj-$(CONFIG_USB_G_SERIAL) += g_serial.o obj-$(CONFIG_USB_G_PRINTER) += g_printer.o diff --git a/drivers/usb/gadget/file_storage.c b/drivers/usb/gadget/file_storage.c deleted file mode 100644 index 3f7d640b6758..000000000000 --- a/drivers/usb/gadget/file_storage.c +++ /dev/null @@ -1,3656 +0,0 @@ -/* - * file_storage.c -- File-backed USB Storage Gadget, for USB development - * - * Copyright (C) 2003-2008 Alan Stern - * 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, - * without modification. - * 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. The names of the above-listed copyright holders may not 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") as published by the Free Software - * Foundation, either version 2 of that License or (at your option) any - * later version. - * - * 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. - */ - - -/* - * The File-backed Storage Gadget acts as a USB Mass Storage device, - * appearing to the host as a disk drive or as a CD-ROM drive. In addition - * to providing an example of a genuinely useful gadget driver for a USB - * device, it also illustrates a technique of double-buffering for increased - * throughput. Last but not least, it gives an easy way to probe the - * behavior of the Mass Storage drivers in a USB host. - * - * Backing storage is provided by a regular file or a block device, specified - * by the "file" module parameter. Access can be limited to read-only by - * setting the optional "ro" module parameter. (For CD-ROM emulation, - * access is always read-only.) The gadget will indicate that it has - * removable media if the optional "removable" module parameter is set. - * - * The gadget supports the Control-Bulk (CB), Control-Bulk-Interrupt (CBI), - * and Bulk-Only (also known as Bulk-Bulk-Bulk or BBB) transports, selected - * by the optional "transport" module parameter. It also supports the - * following protocols: RBC (0x01), ATAPI or SFF-8020i (0x02), QIC-157 (0c03), - * UFI (0x04), SFF-8070i (0x05), and transparent SCSI (0x06), selected by - * the optional "protocol" module parameter. In addition, the default - * Vendor ID, Product ID, release number and serial number can be overridden. - * - * There is support for multiple logical units (LUNs), each of which has - * its own backing file. The number of LUNs can be set using the optional - * "luns" module parameter (anywhere from 1 to 8), and the corresponding - * files are specified using comma-separated lists for "file" and "ro". - * The default number of LUNs is taken from the number of "file" elements; - * it is 1 if "file" is not given. If "removable" is not set then a backing - * file must be specified for each LUN. If it is set, then an unspecified - * or empty backing filename means the LUN's medium is not loaded. Ideally - * each LUN would be settable independently as a disk drive or a CD-ROM - * drive, but currently all LUNs have to be the same type. The CD-ROM - * emulation includes a single data track and no audio tracks; hence there - * need be only one backing file per LUN. - * - * Requirements are modest; only a bulk-in and a bulk-out endpoint are - * needed (an interrupt-out endpoint is also needed for CBI). The memory - * requirement amounts to two 16K buffers, size configurable by a parameter. - * Support is included for both full-speed and high-speed operation. - * - * Note that the driver is slightly non-portable in that it assumes a - * single memory/DMA buffer will be useable for bulk-in, bulk-out, and - * interrupt-in endpoints. With most device controllers this isn't an - * issue, but there may be some with hardware restrictions that prevent - * a buffer from being used by more than one endpoint. - * - * Module options: - * - * file=filename[,filename...] - * Required if "removable" is not set, names of - * the files or block devices used for - * backing storage - * serial=HHHH... Required serial number (string of hex chars) - * ro=b[,b...] Default false, booleans for read-only access - * removable Default false, boolean for removable media - * luns=N Default N = number of filenames, number of - * LUNs to support - * nofua=b[,b...] Default false, booleans for ignore FUA flag - * in SCSI WRITE(10,12) commands - * stall Default determined according to the type of - * USB device controller (usually true), - * boolean to permit the driver to halt - * bulk endpoints - * cdrom Default false, boolean for whether to emulate - * a CD-ROM drive - * transport=XXX Default BBB, transport name (CB, CBI, or BBB) - * protocol=YYY Default SCSI, protocol name (RBC, 8020 or - * ATAPI, QIC, UFI, 8070, or SCSI; - * also 1 - 6) - * vendor=0xVVVV Default 0x0525 (NetChip), USB Vendor ID - * product=0xPPPP Default 0xa4a5 (FSG), USB Product ID - * release=0xRRRR Override the USB release number (bcdDevice) - * buflen=N Default N=16384, buffer size used (will be - * rounded down to a multiple of - * PAGE_CACHE_SIZE) - * - * If CONFIG_USB_FILE_STORAGE_TEST is not set, only the "file", "serial", "ro", - * "removable", "luns", "nofua", "stall", and "cdrom" options are available; - * default values are used for everything else. - * - * The pathnames of the backing files and the ro settings are available in - * the attribute files "file", "nofua", and "ro" in the lun subdirectory of - * the gadget's sysfs directory. If the "removable" option is set, writing to - * these files will simulate ejecting/loading the medium (writing an empty - * line means eject) and adjusting a write-enable tab. Changes to the ro - * setting are not allowed when the medium is loaded or if CD-ROM emulation - * is being used. - * - * This gadget driver is heavily based on "Gadget Zero" by David Brownell. - * The driver's SCSI command interface was based on the "Information - * technology - Small Computer System Interface - 2" document from - * X3T9.2 Project 375D, Revision 10L, 7-SEP-93, available at - * . The single exception - * is opcode 0x23 (READ FORMAT CAPACITIES), which was based on the - * "Universal Serial Bus Mass Storage Class UFI Command Specification" - * document, Revision 1.0, December 14, 1998, available at - * . - */ - - -/* - * Driver Design - * - * The FSG driver is fairly straightforward. There is a main kernel - * thread that handles most of the work. Interrupt routines field - * callbacks from the controller driver: bulk- and interrupt-request - * completion notifications, endpoint-0 events, and disconnect events. - * Completion events are passed to the main thread by wakeup calls. Many - * ep0 requests are handled at interrupt time, but SetInterface, - * SetConfiguration, and device reset requests are forwarded to the - * thread in the form of "exceptions" using SIGUSR1 signals (since they - * should interrupt any ongoing file I/O operations). - * - * The thread's main routine implements the standard command/data/status - * parts of a SCSI interaction. It and its subroutines are full of tests - * for pending signals/exceptions -- all this polling is necessary since - * the kernel has no setjmp/longjmp equivalents. (Maybe this is an - * indication that the driver really wants to be running in userspace.) - * An important point is that so long as the thread is alive it keeps an - * open reference to the backing file. This will prevent unmounting - * the backing file's underlying filesystem and could cause problems - * during system shutdown, for example. To prevent such problems, the - * thread catches INT, TERM, and KILL signals and converts them into - * an EXIT exception. - * - * In normal operation the main thread is started during the gadget's - * fsg_bind() callback and stopped during fsg_unbind(). But it can also - * exit when it receives a signal, and there's no point leaving the - * gadget running when the thread is dead. So just before the thread - * exits, it deregisters the gadget driver. This makes things a little - * tricky: The driver is deregistered at two places, and the exiting - * thread can indirectly call fsg_unbind() which in turn can tell the - * thread to exit. The first problem is resolved through the use of the - * REGISTERED atomic bitflag; the driver will only be deregistered once. - * The second problem is resolved by having fsg_unbind() check - * fsg->state; it won't try to stop the thread if the state is already - * FSG_STATE_TERMINATED. - * - * To provide maximum throughput, the driver uses a circular pipeline of - * buffer heads (struct fsg_buffhd). In principle the pipeline can be - * arbitrarily long; in practice the benefits don't justify having more - * than 2 stages (i.e., double buffering). But it helps to think of the - * pipeline as being a long one. Each buffer head contains a bulk-in and - * a bulk-out request pointer (since the buffer can be used for both - * output and input -- directions always are given from the host's - * point of view) as well as a pointer to the buffer and various state - * variables. - * - * Use of the pipeline follows a simple protocol. There is a variable - * (fsg->next_buffhd_to_fill) that points to the next buffer head to use. - * At any time that buffer head may still be in use from an earlier - * request, so each buffer head has a state variable indicating whether - * it is EMPTY, FULL, or BUSY. Typical use involves waiting for the - * buffer head to be EMPTY, filling the buffer either by file I/O or by - * USB I/O (during which the buffer head is BUSY), and marking the buffer - * head FULL when the I/O is complete. Then the buffer will be emptied - * (again possibly by USB I/O, during which it is marked BUSY) and - * finally marked EMPTY again (possibly by a completion routine). - * - * A module parameter tells the driver to avoid stalling the bulk - * endpoints wherever the transport specification allows. This is - * necessary for some UDCs like the SuperH, which cannot reliably clear a - * halt on a bulk endpoint. However, under certain circumstances the - * Bulk-only specification requires a stall. In such cases the driver - * will halt the endpoint and set a flag indicating that it should clear - * the halt in software during the next device reset. Hopefully this - * will permit everything to work correctly. Furthermore, although the - * specification allows the bulk-out endpoint to halt when the host sends - * too much data, implementing this would cause an unavoidable race. - * The driver will always use the "no-stall" approach for OUT transfers. - * - * One subtle point concerns sending status-stage responses for ep0 - * requests. Some of these requests, such as device reset, can involve - * interrupting an ongoing file I/O operation, which might take an - * arbitrarily long time. During that delay the host might give up on - * the original ep0 request and issue a new one. When that happens the - * driver should not notify the host about completion of the original - * request, as the host will no longer be waiting for it. So the driver - * assigns to each ep0 request a unique tag, and it keeps track of the - * tag value of the request associated with a long-running exception - * (device-reset, interface-change, or configuration-change). When the - * exception handler is finished, the status-stage response is submitted - * only if the current ep0 request tag is equal to the exception request - * tag. Thus only the most recently received ep0 request will get a - * status-stage response. - * - * Warning: This driver source file is too long. It ought to be split up - * into a header file plus about 3 separate .c files, to handle the details - * of the Gadget, USB Mass Storage, and SCSI protocols. - */ - - -/* #define VERBOSE_DEBUG */ -/* #define DUMP_MSGS */ - - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include "gadget_chips.h" - -#define DRIVER_DESC "File-backed Storage Gadget" -#define DRIVER_NAME "g_file_storage" -#define DRIVER_VERSION "1 September 2010" - -static char fsg_string_manufacturer[64]; -static const char fsg_string_product[] = DRIVER_DESC; -static const char fsg_string_config[] = "Self-powered"; -static const char fsg_string_interface[] = "Mass Storage"; - - -#include "storage_common.c" - - -MODULE_DESCRIPTION(DRIVER_DESC); -MODULE_AUTHOR("Alan Stern"); -MODULE_LICENSE("Dual BSD/GPL"); - -/* - * This driver assumes self-powered hardware and has no way for users to - * trigger remote wakeup. It uses autoconfiguration to select endpoints - * and endpoint addresses. - */ - - -/*-------------------------------------------------------------------------*/ - - -/* Encapsulate the module parameter settings */ - -static struct { - char *file[FSG_MAX_LUNS]; - char *serial; - bool ro[FSG_MAX_LUNS]; - bool nofua[FSG_MAX_LUNS]; - unsigned int num_filenames; - unsigned int num_ros; - unsigned int num_nofuas; - unsigned int nluns; - - bool removable; - bool can_stall; - bool cdrom; - - char *transport_parm; - char *protocol_parm; - unsigned short vendor; - unsigned short product; - unsigned short release; - unsigned int buflen; - - int transport_type; - char *transport_name; - int protocol_type; - char *protocol_name; - -} mod_data = { // Default values - .transport_parm = "BBB", - .protocol_parm = "SCSI", - .removable = 0, - .can_stall = 1, - .cdrom = 0, - .vendor = FSG_VENDOR_ID, - .product = FSG_PRODUCT_ID, - .release = 0xffff, // Use controller chip type - .buflen = 16384, - }; - - -module_param_array_named(file, mod_data.file, charp, &mod_data.num_filenames, - S_IRUGO); -MODULE_PARM_DESC(file, "names of backing files or devices"); - -module_param_named(serial, mod_data.serial, charp, S_IRUGO); -MODULE_PARM_DESC(serial, "USB serial number"); - -module_param_array_named(ro, mod_data.ro, bool, &mod_data.num_ros, S_IRUGO); -MODULE_PARM_DESC(ro, "true to force read-only"); - -module_param_array_named(nofua, mod_data.nofua, bool, &mod_data.num_nofuas, - S_IRUGO); -MODULE_PARM_DESC(nofua, "true to ignore SCSI WRITE(10,12) FUA bit"); - -module_param_named(luns, mod_data.nluns, uint, S_IRUGO); -MODULE_PARM_DESC(luns, "number of LUNs"); - -module_param_named(removable, mod_data.removable, bool, S_IRUGO); -MODULE_PARM_DESC(removable, "true to simulate removable media"); - -module_param_named(stall, mod_data.can_stall, bool, S_IRUGO); -MODULE_PARM_DESC(stall, "false to prevent bulk stalls"); - -module_param_named(cdrom, mod_data.cdrom, bool, S_IRUGO); -MODULE_PARM_DESC(cdrom, "true to emulate cdrom instead of disk"); - -/* In the non-TEST version, only the module parameters listed above - * are available. */ -#ifdef CONFIG_USB_FILE_STORAGE_TEST - -module_param_named(transport, mod_data.transport_parm, charp, S_IRUGO); -MODULE_PARM_DESC(transport, "type of transport (BBB, CBI, or CB)"); - -module_param_named(protocol, mod_data.protocol_parm, charp, S_IRUGO); -MODULE_PARM_DESC(protocol, "type of protocol (RBC, 8020, QIC, UFI, " - "8070, or SCSI)"); - -module_param_named(vendor, mod_data.vendor, ushort, S_IRUGO); -MODULE_PARM_DESC(vendor, "USB Vendor ID"); - -module_param_named(product, mod_data.product, ushort, S_IRUGO); -MODULE_PARM_DESC(product, "USB Product ID"); - -module_param_named(release, mod_data.release, ushort, S_IRUGO); -MODULE_PARM_DESC(release, "USB release number"); - -module_param_named(buflen, mod_data.buflen, uint, S_IRUGO); -MODULE_PARM_DESC(buflen, "I/O buffer size"); - -#endif /* CONFIG_USB_FILE_STORAGE_TEST */ - - -/* - * These definitions will permit the compiler to avoid generating code for - * parts of the driver that aren't used in the non-TEST version. Even gcc - * can recognize when a test of a constant expression yields a dead code - * path. - */ - -#ifdef CONFIG_USB_FILE_STORAGE_TEST - -#define transport_is_bbb() (mod_data.transport_type == USB_PR_BULK) -#define transport_is_cbi() (mod_data.transport_type == USB_PR_CBI) -#define protocol_is_scsi() (mod_data.protocol_type == USB_SC_SCSI) - -#else - -#define transport_is_bbb() 1 -#define transport_is_cbi() 0 -#define protocol_is_scsi() 1 - -#endif /* CONFIG_USB_FILE_STORAGE_TEST */ - - -/*-------------------------------------------------------------------------*/ - - -struct fsg_dev { - /* lock protects: state, all the req_busy's, and cbbuf_cmnd */ - spinlock_t lock; - struct usb_gadget *gadget; - - /* filesem protects: backing files in use */ - struct rw_semaphore filesem; - - /* reference counting: wait until all LUNs are released */ - struct kref ref; - - struct usb_ep *ep0; // Handy copy of gadget->ep0 - struct usb_request *ep0req; // For control responses - unsigned int ep0_req_tag; - const char *ep0req_name; - - struct usb_request *intreq; // For interrupt responses - int intreq_busy; - struct fsg_buffhd *intr_buffhd; - - unsigned int bulk_out_maxpacket; - enum fsg_state state; // For exception handling - unsigned int exception_req_tag; - - u8 config, new_config; - - unsigned int running : 1; - unsigned int bulk_in_enabled : 1; - unsigned int bulk_out_enabled : 1; - unsigned int intr_in_enabled : 1; - unsigned int phase_error : 1; - unsigned int short_packet_received : 1; - unsigned int bad_lun_okay : 1; - - unsigned long atomic_bitflags; -#define REGISTERED 0 -#define IGNORE_BULK_OUT 1 -#define SUSPENDED 2 - - struct usb_ep *bulk_in; - struct usb_ep *bulk_out; - struct usb_ep *intr_in; - - struct fsg_buffhd *next_buffhd_to_fill; - struct fsg_buffhd *next_buffhd_to_drain; - - int thread_wakeup_needed; - struct completion thread_notifier; - struct task_struct *thread_task; - - int cmnd_size; - u8 cmnd[MAX_COMMAND_SIZE]; - enum data_direction data_dir; - u32 data_size; - u32 data_size_from_cmnd; - u32 tag; - unsigned int lun; - u32 residue; - u32 usb_amount_left; - - /* The CB protocol offers no way for a host to know when a command - * has completed. As a result the next command may arrive early, - * and we will still have to handle it. For that reason we need - * a buffer to store new commands when using CB (or CBI, which - * does not oblige a host to wait for command completion either). */ - int cbbuf_cmnd_size; - u8 cbbuf_cmnd[MAX_COMMAND_SIZE]; - - unsigned int nluns; - struct fsg_lun *luns; - struct fsg_lun *curlun; - /* Must be the last entry */ - struct fsg_buffhd buffhds[]; -}; - -typedef void (*fsg_routine_t)(struct fsg_dev *); - -static int exception_in_progress(struct fsg_dev *fsg) -{ - return (fsg->state > FSG_STATE_IDLE); -} - -/* Make bulk-out requests be divisible by the maxpacket size */ -static void set_bulk_out_req_length(struct fsg_dev *fsg, - struct fsg_buffhd *bh, unsigned int length) -{ - unsigned int rem; - - bh->bulk_out_intended_length = length; - rem = length % fsg->bulk_out_maxpacket; - if (rem > 0) - length += fsg->bulk_out_maxpacket - rem; - bh->outreq->length = length; -} - -static struct fsg_dev *the_fsg; -static struct usb_gadget_driver fsg_driver; - - -/*-------------------------------------------------------------------------*/ - -static int fsg_set_halt(struct fsg_dev *fsg, struct usb_ep *ep) -{ - const char *name; - - if (ep == fsg->bulk_in) - name = "bulk-in"; - else if (ep == fsg->bulk_out) - name = "bulk-out"; - else - name = ep->name; - DBG(fsg, "%s set halt\n", name); - return usb_ep_set_halt(ep); -} - - -/*-------------------------------------------------------------------------*/ - -/* - * DESCRIPTORS ... most are static, but strings and (full) configuration - * descriptors are built on demand. Also the (static) config and interface - * descriptors are adjusted during fsg_bind(). - */ - -/* There is only one configuration. */ -#define CONFIG_VALUE 1 - -static struct usb_device_descriptor -device_desc = { - .bLength = sizeof device_desc, - .bDescriptorType = USB_DT_DEVICE, - - .bcdUSB = cpu_to_le16(0x0200), - .bDeviceClass = USB_CLASS_PER_INTERFACE, - - /* The next three values can be overridden by module parameters */ - .idVendor = cpu_to_le16(FSG_VENDOR_ID), - .idProduct = cpu_to_le16(FSG_PRODUCT_ID), - .bcdDevice = cpu_to_le16(0xffff), - - .iManufacturer = FSG_STRING_MANUFACTURER, - .iProduct = FSG_STRING_PRODUCT, - .iSerialNumber = FSG_STRING_SERIAL, - .bNumConfigurations = 1, -}; - -static struct usb_config_descriptor -config_desc = { - .bLength = sizeof config_desc, - .bDescriptorType = USB_DT_CONFIG, - - /* wTotalLength computed by usb_gadget_config_buf() */ - .bNumInterfaces = 1, - .bConfigurationValue = CONFIG_VALUE, - .iConfiguration = FSG_STRING_CONFIG, - .bmAttributes = USB_CONFIG_ATT_ONE | USB_CONFIG_ATT_SELFPOWER, - .bMaxPower = CONFIG_USB_GADGET_VBUS_DRAW / 2, -}; - - -static struct usb_qualifier_descriptor -dev_qualifier = { - .bLength = sizeof dev_qualifier, - .bDescriptorType = USB_DT_DEVICE_QUALIFIER, - - .bcdUSB = cpu_to_le16(0x0200), - .bDeviceClass = USB_CLASS_PER_INTERFACE, - - .bNumConfigurations = 1, -}; - -static int populate_bos(struct fsg_dev *fsg, u8 *buf) -{ - memcpy(buf, &fsg_bos_desc, USB_DT_BOS_SIZE); - buf += USB_DT_BOS_SIZE; - - memcpy(buf, &fsg_ext_cap_desc, USB_DT_USB_EXT_CAP_SIZE); - buf += USB_DT_USB_EXT_CAP_SIZE; - - memcpy(buf, &fsg_ss_cap_desc, USB_DT_USB_SS_CAP_SIZE); - - return USB_DT_BOS_SIZE + USB_DT_USB_SS_CAP_SIZE - + USB_DT_USB_EXT_CAP_SIZE; -} - -/* - * Config descriptors must agree with the code that sets configurations - * and with code managing interfaces and their altsettings. They must - * also handle different speeds and other-speed requests. - */ -static int populate_config_buf(struct usb_gadget *gadget, - u8 *buf, u8 type, unsigned index) -{ - enum usb_device_speed speed = gadget->speed; - int len; - const struct usb_descriptor_header **function; - - if (index > 0) - return -EINVAL; - - if (gadget_is_dualspeed(gadget) && type == USB_DT_OTHER_SPEED_CONFIG) - speed = (USB_SPEED_FULL + USB_SPEED_HIGH) - speed; - function = gadget_is_dualspeed(gadget) && speed == USB_SPEED_HIGH - ? (const struct usb_descriptor_header **)fsg_hs_function - : (const struct usb_descriptor_header **)fsg_fs_function; - - /* for now, don't advertise srp-only devices */ - if (!gadget_is_otg(gadget)) - function++; - - len = usb_gadget_config_buf(&config_desc, buf, EP0_BUFSIZE, function); - ((struct usb_config_descriptor *) buf)->bDescriptorType = type; - return len; -} - - -/*-------------------------------------------------------------------------*/ - -/* These routines may be called in process context or in_irq */ - -/* Caller must hold fsg->lock */ -static void wakeup_thread(struct fsg_dev *fsg) -{ - /* Tell the main thread that something has happened */ - fsg->thread_wakeup_needed = 1; - if (fsg->thread_task) - wake_up_process(fsg->thread_task); -} - - -static void raise_exception(struct fsg_dev *fsg, enum fsg_state new_state) -{ - unsigned long flags; - - /* Do nothing if a higher-priority exception is already in progress. - * If a lower-or-equal priority exception is in progress, preempt it - * and notify the main thread by sending it a signal. */ - spin_lock_irqsave(&fsg->lock, flags); - if (fsg->state <= new_state) { - fsg->exception_req_tag = fsg->ep0_req_tag; - fsg->state = new_state; - if (fsg->thread_task) - send_sig_info(SIGUSR1, SEND_SIG_FORCED, - fsg->thread_task); - } - spin_unlock_irqrestore(&fsg->lock, flags); -} - - -/*-------------------------------------------------------------------------*/ - -/* The disconnect callback and ep0 routines. These always run in_irq, - * except that ep0_queue() is called in the main thread to acknowledge - * completion of various requests: set config, set interface, and - * Bulk-only device reset. */ - -static void fsg_disconnect(struct usb_gadget *gadget) -{ - struct fsg_dev *fsg = get_gadget_data(gadget); - - DBG(fsg, "disconnect or port reset\n"); - raise_exception(fsg, FSG_STATE_DISCONNECT); -} - - -static int ep0_queue(struct fsg_dev *fsg) -{ - int rc; - - rc = usb_ep_queue(fsg->ep0, fsg->ep0req, GFP_ATOMIC); - if (rc != 0 && rc != -ESHUTDOWN) { - - /* We can't do much more than wait for a reset */ - WARNING(fsg, "error in submission: %s --> %d\n", - fsg->ep0->name, rc); - } - return rc; -} - -static void ep0_complete(struct usb_ep *ep, struct usb_request *req) -{ - struct fsg_dev *fsg = ep->driver_data; - - if (req->actual > 0) - dump_msg(fsg, fsg->ep0req_name, req->buf, req->actual); - if (req->status || req->actual != req->length) - DBG(fsg, "%s --> %d, %u/%u\n", __func__, - req->status, req->actual, req->length); - if (req->status == -ECONNRESET) // Request was cancelled - usb_ep_fifo_flush(ep); - - if (req->status == 0 && req->context) - ((fsg_routine_t) (req->context))(fsg); -} - - -/*-------------------------------------------------------------------------*/ - -/* Bulk and interrupt endpoint completion handlers. - * These always run in_irq. */ - -static void bulk_in_complete(struct usb_ep *ep, struct usb_request *req) -{ - struct fsg_dev *fsg = ep->driver_data; - struct fsg_buffhd *bh = req->context; - - if (req->status || req->actual != req->length) - DBG(fsg, "%s --> %d, %u/%u\n", __func__, - req->status, req->actual, req->length); - if (req->status == -ECONNRESET) // Request was cancelled - usb_ep_fifo_flush(ep); - - /* Hold the lock while we update the request and buffer states */ - smp_wmb(); - spin_lock(&fsg->lock); - bh->inreq_busy = 0; - bh->state = BUF_STATE_EMPTY; - wakeup_thread(fsg); - spin_unlock(&fsg->lock); -} - -static void bulk_out_complete(struct usb_ep *ep, struct usb_request *req) -{ - struct fsg_dev *fsg = ep->driver_data; - struct fsg_buffhd *bh = req->context; - - dump_msg(fsg, "bulk-out", req->buf, req->actual); - if (req->status || req->actual != bh->bulk_out_intended_length) - DBG(fsg, "%s --> %d, %u/%u\n", __func__, - req->status, req->actual, - bh->bulk_out_intended_length); - if (req->status == -ECONNRESET) // Request was cancelled - usb_ep_fifo_flush(ep); - - /* Hold the lock while we update the request and buffer states */ - smp_wmb(); - spin_lock(&fsg->lock); - bh->outreq_busy = 0; - bh->state = BUF_STATE_FULL; - wakeup_thread(fsg); - spin_unlock(&fsg->lock); -} - - -#ifdef CONFIG_USB_FILE_STORAGE_TEST -static void intr_in_complete(struct usb_ep *ep, struct usb_request *req) -{ - struct fsg_dev *fsg = ep->driver_data; - struct fsg_buffhd *bh = req->context; - - if (req->status || req->actual != req->length) - DBG(fsg, "%s --> %d, %u/%u\n", __func__, - req->status, req->actual, req->length); - if (req->status == -ECONNRESET) // Request was cancelled - usb_ep_fifo_flush(ep); - - /* Hold the lock while we update the request and buffer states */ - smp_wmb(); - spin_lock(&fsg->lock); - fsg->intreq_busy = 0; - bh->state = BUF_STATE_EMPTY; - wakeup_thread(fsg); - spin_unlock(&fsg->lock); -} - -#else -static void intr_in_complete(struct usb_ep *ep, struct usb_request *req) -{} -#endif /* CONFIG_USB_FILE_STORAGE_TEST */ - - -/*-------------------------------------------------------------------------*/ - -/* Ep0 class-specific handlers. These always run in_irq. */ - -#ifdef CONFIG_USB_FILE_STORAGE_TEST -static void received_cbi_adsc(struct fsg_dev *fsg, struct fsg_buffhd *bh) -{ - struct usb_request *req = fsg->ep0req; - static u8 cbi_reset_cmnd[6] = { - SEND_DIAGNOSTIC, 4, 0xff, 0xff, 0xff, 0xff}; - - /* Error in command transfer? */ - if (req->status || req->length != req->actual || - req->actual < 6 || req->actual > MAX_COMMAND_SIZE) { - - /* Not all controllers allow a protocol stall after - * receiving control-out data, but we'll try anyway. */ - fsg_set_halt(fsg, fsg->ep0); - return; // Wait for reset - } - - /* Is it the special reset command? */ - if (req->actual >= sizeof cbi_reset_cmnd && - memcmp(req->buf, cbi_reset_cmnd, - sizeof cbi_reset_cmnd) == 0) { - - /* Raise an exception to stop the current operation - * and reinitialize our state. */ - DBG(fsg, "cbi reset request\n"); - raise_exception(fsg, FSG_STATE_RESET); - return; - } - - VDBG(fsg, "CB[I] accept device-specific command\n"); - spin_lock(&fsg->lock); - - /* Save the command for later */ - if (fsg->cbbuf_cmnd_size) - WARNING(fsg, "CB[I] overwriting previous command\n"); - fsg->cbbuf_cmnd_size = req->actual; - memcpy(fsg->cbbuf_cmnd, req->buf, fsg->cbbuf_cmnd_size); - - wakeup_thread(fsg); - spin_unlock(&fsg->lock); -} - -#else -static void received_cbi_adsc(struct fsg_dev *fsg, struct fsg_buffhd *bh) -{} -#endif /* CONFIG_USB_FILE_STORAGE_TEST */ - - -static int class_setup_req(struct fsg_dev *fsg, - const struct usb_ctrlrequest *ctrl) -{ - struct usb_request *req = fsg->ep0req; - int value = -EOPNOTSUPP; - u16 w_index = le16_to_cpu(ctrl->wIndex); - u16 w_value = le16_to_cpu(ctrl->wValue); - u16 w_length = le16_to_cpu(ctrl->wLength); - - if (!fsg->config) - return value; - - /* Handle Bulk-only class-specific requests */ - if (transport_is_bbb()) { - switch (ctrl->bRequest) { - - case US_BULK_RESET_REQUEST: - if (ctrl->bRequestType != (USB_DIR_OUT | - USB_TYPE_CLASS | USB_RECIP_INTERFACE)) - break; - if (w_index != 0 || w_value != 0 || w_length != 0) { - value = -EDOM; - break; - } - - /* Raise an exception to stop the current operation - * and reinitialize our state. */ - DBG(fsg, "bulk reset request\n"); - raise_exception(fsg, FSG_STATE_RESET); - value = DELAYED_STATUS; - break; - - case US_BULK_GET_MAX_LUN: - if (ctrl->bRequestType != (USB_DIR_IN | - USB_TYPE_CLASS | USB_RECIP_INTERFACE)) - break; - if (w_index != 0 || w_value != 0 || w_length != 1) { - value = -EDOM; - break; - } - VDBG(fsg, "get max LUN\n"); - *(u8 *) req->buf = fsg->nluns - 1; - value = 1; - break; - } - } - - /* Handle CBI class-specific requests */ - else { - switch (ctrl->bRequest) { - - case USB_CBI_ADSC_REQUEST: - if (ctrl->bRequestType != (USB_DIR_OUT | - USB_TYPE_CLASS | USB_RECIP_INTERFACE)) - break; - if (w_index != 0 || w_value != 0) { - value = -EDOM; - break; - } - if (w_length > MAX_COMMAND_SIZE) { - value = -EOVERFLOW; - break; - } - value = w_length; - fsg->ep0req->context = received_cbi_adsc; - break; - } - } - - if (value == -EOPNOTSUPP) - VDBG(fsg, - "unknown class-specific control req " - "%02x.%02x v%04x i%04x l%u\n", - ctrl->bRequestType, ctrl->bRequest, - le16_to_cpu(ctrl->wValue), w_index, w_length); - return value; -} - - -/*-------------------------------------------------------------------------*/ - -/* Ep0 standard request handlers. These always run in_irq. */ - -static int standard_setup_req(struct fsg_dev *fsg, - const struct usb_ctrlrequest *ctrl) -{ - struct usb_request *req = fsg->ep0req; - int value = -EOPNOTSUPP; - u16 w_index = le16_to_cpu(ctrl->wIndex); - u16 w_value = le16_to_cpu(ctrl->wValue); - - /* Usually this just stores reply data in the pre-allocated ep0 buffer, - * but config change events will also reconfigure hardware. */ - switch (ctrl->bRequest) { - - case USB_REQ_GET_DESCRIPTOR: - if (ctrl->bRequestType != (USB_DIR_IN | USB_TYPE_STANDARD | - USB_RECIP_DEVICE)) - break; - switch (w_value >> 8) { - - case USB_DT_DEVICE: - VDBG(fsg, "get device descriptor\n"); - device_desc.bMaxPacketSize0 = fsg->ep0->maxpacket; - value = sizeof device_desc; - memcpy(req->buf, &device_desc, value); - break; - case USB_DT_DEVICE_QUALIFIER: - VDBG(fsg, "get device qualifier\n"); - if (!gadget_is_dualspeed(fsg->gadget) || - fsg->gadget->speed == USB_SPEED_SUPER) - break; - /* - * Assume ep0 uses the same maxpacket value for both - * speeds - */ - dev_qualifier.bMaxPacketSize0 = fsg->ep0->maxpacket; - value = sizeof dev_qualifier; - memcpy(req->buf, &dev_qualifier, value); - break; - - case USB_DT_OTHER_SPEED_CONFIG: - VDBG(fsg, "get other-speed config descriptor\n"); - if (!gadget_is_dualspeed(fsg->gadget) || - fsg->gadget->speed == USB_SPEED_SUPER) - break; - goto get_config; - case USB_DT_CONFIG: - VDBG(fsg, "get configuration descriptor\n"); -get_config: - value = populate_config_buf(fsg->gadget, - req->buf, - w_value >> 8, - w_value & 0xff); - break; - - case USB_DT_STRING: - VDBG(fsg, "get string descriptor\n"); - - /* wIndex == language code */ - value = usb_gadget_get_string(&fsg_stringtab, - w_value & 0xff, req->buf); - break; - - case USB_DT_BOS: - VDBG(fsg, "get bos descriptor\n"); - - if (gadget_is_superspeed(fsg->gadget)) - value = populate_bos(fsg, req->buf); - break; - } - - break; - - /* One config, two speeds */ - case USB_REQ_SET_CONFIGURATION: - if (ctrl->bRequestType != (USB_DIR_OUT | USB_TYPE_STANDARD | - USB_RECIP_DEVICE)) - break; - VDBG(fsg, "set configuration\n"); - if (w_value == CONFIG_VALUE || w_value == 0) { - fsg->new_config = w_value; - - /* Raise an exception to wipe out previous transaction - * state (queued bufs, etc) and set the new config. */ - raise_exception(fsg, FSG_STATE_CONFIG_CHANGE); - value = DELAYED_STATUS; - } - break; - case USB_REQ_GET_CONFIGURATION: - if (ctrl->bRequestType != (USB_DIR_IN | USB_TYPE_STANDARD | - USB_RECIP_DEVICE)) - break; - VDBG(fsg, "get configuration\n"); - *(u8 *) req->buf = fsg->config; - value = 1; - break; - - case USB_REQ_SET_INTERFACE: - if (ctrl->bRequestType != (USB_DIR_OUT| USB_TYPE_STANDARD | - USB_RECIP_INTERFACE)) - break; - if (fsg->config && w_index == 0) { - - /* Raise an exception to wipe out previous transaction - * state (queued bufs, etc) and install the new - * interface altsetting. */ - raise_exception(fsg, FSG_STATE_INTERFACE_CHANGE); - value = DELAYED_STATUS; - } - break; - case USB_REQ_GET_INTERFACE: - if (ctrl->bRequestType != (USB_DIR_IN | USB_TYPE_STANDARD | - USB_RECIP_INTERFACE)) - break; - if (!fsg->config) - break; - if (w_index != 0) { - value = -EDOM; - break; - } - VDBG(fsg, "get interface\n"); - *(u8 *) req->buf = 0; - value = 1; - break; - - default: - VDBG(fsg, - "unknown control req %02x.%02x v%04x i%04x l%u\n", - ctrl->bRequestType, ctrl->bRequest, - w_value, w_index, le16_to_cpu(ctrl->wLength)); - } - - return value; -} - - -static int fsg_setup(struct usb_gadget *gadget, - const struct usb_ctrlrequest *ctrl) -{ - struct fsg_dev *fsg = get_gadget_data(gadget); - int rc; - int w_length = le16_to_cpu(ctrl->wLength); - - ++fsg->ep0_req_tag; // Record arrival of a new request - fsg->ep0req->context = NULL; - fsg->ep0req->length = 0; - dump_msg(fsg, "ep0-setup", (u8 *) ctrl, sizeof(*ctrl)); - - if ((ctrl->bRequestType & USB_TYPE_MASK) == USB_TYPE_CLASS) - rc = class_setup_req(fsg, ctrl); - else - rc = standard_setup_req(fsg, ctrl); - - /* Respond with data/status or defer until later? */ - if (rc >= 0 && rc != DELAYED_STATUS) { - rc = min(rc, w_length); - fsg->ep0req->length = rc; - fsg->ep0req->zero = rc < w_length; - fsg->ep0req_name = (ctrl->bRequestType & USB_DIR_IN ? - "ep0-in" : "ep0-out"); - rc = ep0_queue(fsg); - } - - /* Device either stalls (rc < 0) or reports success */ - return rc; -} - - -/*-------------------------------------------------------------------------*/ - -/* All the following routines run in process context */ - - -/* Use this for bulk or interrupt transfers, not ep0 */ -static void start_transfer(struct fsg_dev *fsg, struct usb_ep *ep, - struct usb_request *req, int *pbusy, - enum fsg_buffer_state *state) -{ - int rc; - - if (ep == fsg->bulk_in) - dump_msg(fsg, "bulk-in", req->buf, req->length); - else if (ep == fsg->intr_in) - dump_msg(fsg, "intr-in", req->buf, req->length); - - spin_lock_irq(&fsg->lock); - *pbusy = 1; - *state = BUF_STATE_BUSY; - spin_unlock_irq(&fsg->lock); - rc = usb_ep_queue(ep, req, GFP_KERNEL); - if (rc != 0) { - *pbusy = 0; - *state = BUF_STATE_EMPTY; - - /* We can't do much more than wait for a reset */ - - /* Note: currently the net2280 driver fails zero-length - * submissions if DMA is enabled. */ - if (rc != -ESHUTDOWN && !(rc == -EOPNOTSUPP && - req->length == 0)) - WARNING(fsg, "error in submission: %s --> %d\n", - ep->name, rc); - } -} - - -static int sleep_thread(struct fsg_dev *fsg) -{ - int rc = 0; - - /* Wait until a signal arrives or we are woken up */ - for (;;) { - try_to_freeze(); - set_current_state(TASK_INTERRUPTIBLE); - if (signal_pending(current)) { - rc = -EINTR; - break; - } - if (fsg->thread_wakeup_needed) - break; - schedule(); - } - __set_current_state(TASK_RUNNING); - fsg->thread_wakeup_needed = 0; - return rc; -} - - -/*-------------------------------------------------------------------------*/ - -static int do_read(struct fsg_dev *fsg) -{ - struct fsg_lun *curlun = fsg->curlun; - u32 lba; - struct fsg_buffhd *bh; - int rc; - u32 amount_left; - loff_t file_offset, file_offset_tmp; - unsigned int amount; - ssize_t nread; - - /* Get the starting Logical Block Address and check that it's - * not too big */ - if (fsg->cmnd[0] == READ_6) - lba = get_unaligned_be24(&fsg->cmnd[1]); - else { - lba = get_unaligned_be32(&fsg->cmnd[2]); - - /* We allow DPO (Disable Page Out = don't save data in the - * cache) and FUA (Force Unit Access = don't read from the - * cache), but we don't implement them. */ - if ((fsg->cmnd[1] & ~0x18) != 0) { - curlun->sense_data = SS_INVALID_FIELD_IN_CDB; - return -EINVAL; - } - } - if (lba >= curlun->num_sectors) { - curlun->sense_data = SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE; - return -EINVAL; - } - file_offset = ((loff_t) lba) << curlun->blkbits; - - /* Carry out the file reads */ - amount_left = fsg->data_size_from_cmnd; - if (unlikely(amount_left == 0)) - return -EIO; // No default reply - - for (;;) { - - /* Figure out how much we need to read: - * Try to read the remaining amount. - * But don't read more than the buffer size. - * And don't try to read past the end of the file. - */ - amount = min((unsigned int) amount_left, mod_data.buflen); - amount = min((loff_t) amount, - curlun->file_length - file_offset); - - /* Wait for the next buffer to become available */ - bh = fsg->next_buffhd_to_fill; - while (bh->state != BUF_STATE_EMPTY) { - rc = sleep_thread(fsg); - if (rc) - return rc; - } - - /* If we were asked to read past the end of file, - * end with an empty buffer. */ - if (amount == 0) { - curlun->sense_data = - SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE; - curlun->sense_data_info = file_offset >> curlun->blkbits; - curlun->info_valid = 1; - bh->inreq->length = 0; - bh->state = BUF_STATE_FULL; - break; - } - - /* Perform the read */ - file_offset_tmp = file_offset; - nread = vfs_read(curlun->filp, - (char __user *) bh->buf, - amount, &file_offset_tmp); - VLDBG(curlun, "file read %u @ %llu -> %d\n", amount, - (unsigned long long) file_offset, - (int) nread); - if (signal_pending(current)) - return -EINTR; - - if (nread < 0) { - LDBG(curlun, "error in file read: %d\n", - (int) nread); - nread = 0; - } else if (nread < amount) { - LDBG(curlun, "partial file read: %d/%u\n", - (int) nread, amount); - nread = round_down(nread, curlun->blksize); - } - file_offset += nread; - amount_left -= nread; - fsg->residue -= nread; - - /* Except at the end of the transfer, nread will be - * equal to the buffer size, which is divisible by the - * bulk-in maxpacket size. - */ - bh->inreq->length = nread; - bh->state = BUF_STATE_FULL; - - /* If an error occurred, report it and its position */ - if (nread < amount) { - curlun->sense_data = SS_UNRECOVERED_READ_ERROR; - curlun->sense_data_info = file_offset >> curlun->blkbits; - curlun->info_valid = 1; - break; - } - - if (amount_left == 0) - break; // No more left to read - - /* Send this buffer and go read some more */ - bh->inreq->zero = 0; - start_transfer(fsg, fsg->bulk_in, bh->inreq, - &bh->inreq_busy, &bh->state); - fsg->next_buffhd_to_fill = bh->next; - } - - return -EIO; // No default reply -} - - -/*-------------------------------------------------------------------------*/ - -static int do_write(struct fsg_dev *fsg) -{ - struct fsg_lun *curlun = fsg->curlun; - u32 lba; - struct fsg_buffhd *bh; - int get_some_more; - u32 amount_left_to_req, amount_left_to_write; - loff_t usb_offset, file_offset, file_offset_tmp; - unsigned int amount; - ssize_t nwritten; - int rc; - - if (curlun->ro) { - curlun->sense_data = SS_WRITE_PROTECTED; - return -EINVAL; - } - spin_lock(&curlun->filp->f_lock); - curlun->filp->f_flags &= ~O_SYNC; // Default is not to wait - spin_unlock(&curlun->filp->f_lock); - - /* Get the starting Logical Block Address and check that it's - * not too big */ - if (fsg->cmnd[0] == WRITE_6) - lba = get_unaligned_be24(&fsg->cmnd[1]); - else { - lba = get_unaligned_be32(&fsg->cmnd[2]); - - /* We allow DPO (Disable Page Out = don't save data in the - * cache) and FUA (Force Unit Access = write directly to the - * medium). We don't implement DPO; we implement FUA by - * performing synchronous output. */ - if ((fsg->cmnd[1] & ~0x18) != 0) { - curlun->sense_data = SS_INVALID_FIELD_IN_CDB; - return -EINVAL; - } - /* FUA */ - if (!curlun->nofua && (fsg->cmnd[1] & 0x08)) { - spin_lock(&curlun->filp->f_lock); - curlun->filp->f_flags |= O_DSYNC; - spin_unlock(&curlun->filp->f_lock); - } - } - if (lba >= curlun->num_sectors) { - curlun->sense_data = SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE; - return -EINVAL; - } - - /* Carry out the file writes */ - get_some_more = 1; - file_offset = usb_offset = ((loff_t) lba) << curlun->blkbits; - amount_left_to_req = amount_left_to_write = fsg->data_size_from_cmnd; - - while (amount_left_to_write > 0) { - - /* Queue a request for more data from the host */ - bh = fsg->next_buffhd_to_fill; - if (bh->state == BUF_STATE_EMPTY && get_some_more) { - - /* Figure out how much we want to get: - * Try to get the remaining amount, - * but not more than the buffer size. - */ - amount = min(amount_left_to_req, mod_data.buflen); - - /* Beyond the end of the backing file? */ - if (usb_offset >= curlun->file_length) { - get_some_more = 0; - curlun->sense_data = - SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE; - curlun->sense_data_info = usb_offset >> curlun->blkbits; - curlun->info_valid = 1; - continue; - } - - /* Get the next buffer */ - usb_offset += amount; - fsg->usb_amount_left -= amount; - amount_left_to_req -= amount; - if (amount_left_to_req == 0) - get_some_more = 0; - - /* Except at the end of the transfer, amount will be - * equal to the buffer size, which is divisible by - * the bulk-out maxpacket size. - */ - set_bulk_out_req_length(fsg, bh, amount); - start_transfer(fsg, fsg->bulk_out, bh->outreq, - &bh->outreq_busy, &bh->state); - fsg->next_buffhd_to_fill = bh->next; - continue; - } - - /* Write the received data to the backing file */ - bh = fsg->next_buffhd_to_drain; - if (bh->state == BUF_STATE_EMPTY && !get_some_more) - break; // We stopped early - if (bh->state == BUF_STATE_FULL) { - smp_rmb(); - fsg->next_buffhd_to_drain = bh->next; - bh->state = BUF_STATE_EMPTY; - - /* Did something go wrong with the transfer? */ - if (bh->outreq->status != 0) { - curlun->sense_data = SS_COMMUNICATION_FAILURE; - curlun->sense_data_info = file_offset >> curlun->blkbits; - curlun->info_valid = 1; - break; - } - - amount = bh->outreq->actual; - if (curlun->file_length - file_offset < amount) { - LERROR(curlun, - "write %u @ %llu beyond end %llu\n", - amount, (unsigned long long) file_offset, - (unsigned long long) curlun->file_length); - amount = curlun->file_length - file_offset; - } - - /* Don't accept excess data. The spec doesn't say - * what to do in this case. We'll ignore the error. - */ - amount = min(amount, bh->bulk_out_intended_length); - - /* Don't write a partial block */ - amount = round_down(amount, curlun->blksize); - if (amount == 0) - goto empty_write; - - /* Perform the write */ - file_offset_tmp = file_offset; - nwritten = vfs_write(curlun->filp, - (char __user *) bh->buf, - amount, &file_offset_tmp); - VLDBG(curlun, "file write %u @ %llu -> %d\n", amount, - (unsigned long long) file_offset, - (int) nwritten); - if (signal_pending(current)) - return -EINTR; // Interrupted! - - if (nwritten < 0) { - LDBG(curlun, "error in file write: %d\n", - (int) nwritten); - nwritten = 0; - } else if (nwritten < amount) { - LDBG(curlun, "partial file write: %d/%u\n", - (int) nwritten, amount); - nwritten = round_down(nwritten, curlun->blksize); - } - file_offset += nwritten; - amount_left_to_write -= nwritten; - fsg->residue -= nwritten; - - /* If an error occurred, report it and its position */ - if (nwritten < amount) { - curlun->sense_data = SS_WRITE_ERROR; - curlun->sense_data_info = file_offset >> curlun->blkbits; - curlun->info_valid = 1; - break; - } - - empty_write: - /* Did the host decide to stop early? */ - if (bh->outreq->actual < bh->bulk_out_intended_length) { - fsg->short_packet_received = 1; - break; - } - continue; - } - - /* Wait for something to happen */ - rc = sleep_thread(fsg); - if (rc) - return rc; - } - - return -EIO; // No default reply -} - - -/*-------------------------------------------------------------------------*/ - -static int do_synchronize_cache(struct fsg_dev *fsg) -{ - struct fsg_lun *curlun = fsg->curlun; - int rc; - - /* We ignore the requested LBA and write out all file's - * dirty data buffers. */ - rc = fsg_lun_fsync_sub(curlun); - if (rc) - curlun->sense_data = SS_WRITE_ERROR; - return 0; -} - - -/*-------------------------------------------------------------------------*/ - -static void invalidate_sub(struct fsg_lun *curlun) -{ - struct file *filp = curlun->filp; - struct inode *inode = filp->f_path.dentry->d_inode; - unsigned long rc; - - rc = invalidate_mapping_pages(inode->i_mapping, 0, -1); - VLDBG(curlun, "invalidate_mapping_pages -> %ld\n", rc); -} - -static int do_verify(struct fsg_dev *fsg) -{ - struct fsg_lun *curlun = fsg->curlun; - u32 lba; - u32 verification_length; - struct fsg_buffhd *bh = fsg->next_buffhd_to_fill; - loff_t file_offset, file_offset_tmp; - u32 amount_left; - unsigned int amount; - ssize_t nread; - - /* Get the starting Logical Block Address and check that it's - * not too big */ - lba = get_unaligned_be32(&fsg->cmnd[2]); - if (lba >= curlun->num_sectors) { - curlun->sense_data = SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE; - return -EINVAL; - } - - /* We allow DPO (Disable Page Out = don't save data in the - * cache) but we don't implement it. */ - if ((fsg->cmnd[1] & ~0x10) != 0) { - curlun->sense_data = SS_INVALID_FIELD_IN_CDB; - return -EINVAL; - } - - verification_length = get_unaligned_be16(&fsg->cmnd[7]); - if (unlikely(verification_length == 0)) - return -EIO; // No default reply - - /* Prepare to carry out the file verify */ - amount_left = verification_length << curlun->blkbits; - file_offset = ((loff_t) lba) << curlun->blkbits; - - /* Write out all the dirty buffers before invalidating them */ - fsg_lun_fsync_sub(curlun); - if (signal_pending(current)) - return -EINTR; - - invalidate_sub(curlun); - if (signal_pending(current)) - return -EINTR; - - /* Just try to read the requested blocks */ - while (amount_left > 0) { - - /* Figure out how much we need to read: - * Try to read the remaining amount, but not more than - * the buffer size. - * And don't try to read past the end of the file. - */ - amount = min((unsigned int) amount_left, mod_data.buflen); - amount = min((loff_t) amount, - curlun->file_length - file_offset); - if (amount == 0) { - curlun->sense_data = - SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE; - curlun->sense_data_info = file_offset >> curlun->blkbits; - curlun->info_valid = 1; - break; - } - - /* Perform the read */ - file_offset_tmp = file_offset; - nread = vfs_read(curlun->filp, - (char __user *) bh->buf, - amount, &file_offset_tmp); - VLDBG(curlun, "file read %u @ %llu -> %d\n", amount, - (unsigned long long) file_offset, - (int) nread); - if (signal_pending(current)) - return -EINTR; - - if (nread < 0) { - LDBG(curlun, "error in file verify: %d\n", - (int) nread); - nread = 0; - } else if (nread < amount) { - LDBG(curlun, "partial file verify: %d/%u\n", - (int) nread, amount); - nread = round_down(nread, curlun->blksize); - } - if (nread == 0) { - curlun->sense_data = SS_UNRECOVERED_READ_ERROR; - curlun->sense_data_info = file_offset >> curlun->blkbits; - curlun->info_valid = 1; - break; - } - file_offset += nread; - amount_left -= nread; - } - return 0; -} - - -/*-------------------------------------------------------------------------*/ - -static int do_inquiry(struct fsg_dev *fsg, struct fsg_buffhd *bh) -{ - u8 *buf = (u8 *) bh->buf; - - static char vendor_id[] = "Linux "; - static char product_disk_id[] = "File-Stor Gadget"; - static char product_cdrom_id[] = "File-CD Gadget "; - - if (!fsg->curlun) { // Unsupported LUNs are okay - fsg->bad_lun_okay = 1; - memset(buf, 0, 36); - buf[0] = 0x7f; // Unsupported, no device-type - buf[4] = 31; // Additional length - return 36; - } - - memset(buf, 0, 8); - buf[0] = (mod_data.cdrom ? TYPE_ROM : TYPE_DISK); - if (mod_data.removable) - buf[1] = 0x80; - buf[2] = 2; // ANSI SCSI level 2 - buf[3] = 2; // SCSI-2 INQUIRY data format - buf[4] = 31; // Additional length - // No special options - sprintf(buf + 8, "%-8s%-16s%04x", vendor_id, - (mod_data.cdrom ? product_cdrom_id : - product_disk_id), - mod_data.release); - return 36; -} - - -static int do_request_sense(struct fsg_dev *fsg, struct fsg_buffhd *bh) -{ - struct fsg_lun *curlun = fsg->curlun; - u8 *buf = (u8 *) bh->buf; - u32 sd, sdinfo; - int valid; - - /* - * From the SCSI-2 spec., section 7.9 (Unit attention condition): - * - * If a REQUEST SENSE command is received from an initiator - * with a pending unit attention condition (before the target - * generates the contingent allegiance condition), then the - * target shall either: - * a) report any pending sense data and preserve the unit - * attention condition on the logical unit, or, - * b) report the unit attention condition, may discard any - * pending sense data, and clear the unit attention - * condition on the logical unit for that initiator. - * - * FSG normally uses option a); enable this code to use option b). - */ -#if 0 - if (curlun && curlun->unit_attention_data != SS_NO_SENSE) { - curlun->sense_data = curlun->unit_attention_data; - curlun->unit_attention_data = SS_NO_SENSE; - } -#endif - - if (!curlun) { // Unsupported LUNs are okay - fsg->bad_lun_okay = 1; - sd = SS_LOGICAL_UNIT_NOT_SUPPORTED; - sdinfo = 0; - valid = 0; - } else { - sd = curlun->sense_data; - sdinfo = curlun->sense_data_info; - valid = curlun->info_valid << 7; - curlun->sense_data = SS_NO_SENSE; - curlun->sense_data_info = 0; - curlun->info_valid = 0; - } - - memset(buf, 0, 18); - buf[0] = valid | 0x70; // Valid, current error - buf[2] = SK(sd); - put_unaligned_be32(sdinfo, &buf[3]); /* Sense information */ - buf[7] = 18 - 8; // Additional sense length - buf[12] = ASC(sd); - buf[13] = ASCQ(sd); - return 18; -} - - -static int do_read_capacity(struct fsg_dev *fsg, struct fsg_buffhd *bh) -{ - struct fsg_lun *curlun = fsg->curlun; - u32 lba = get_unaligned_be32(&fsg->cmnd[2]); - int pmi = fsg->cmnd[8]; - u8 *buf = (u8 *) bh->buf; - - /* Check the PMI and LBA fields */ - if (pmi > 1 || (pmi == 0 && lba != 0)) { - curlun->sense_data = SS_INVALID_FIELD_IN_CDB; - return -EINVAL; - } - - put_unaligned_be32(curlun->num_sectors - 1, &buf[0]); - /* Max logical block */ - put_unaligned_be32(curlun->blksize, &buf[4]); /* Block length */ - return 8; -} - - -static int do_read_header(struct fsg_dev *fsg, struct fsg_buffhd *bh) -{ - struct fsg_lun *curlun = fsg->curlun; - int msf = fsg->cmnd[1] & 0x02; - u32 lba = get_unaligned_be32(&fsg->cmnd[2]); - u8 *buf = (u8 *) bh->buf; - - if ((fsg->cmnd[1] & ~0x02) != 0) { /* Mask away MSF */ - curlun->sense_data = SS_INVALID_FIELD_IN_CDB; - return -EINVAL; - } - if (lba >= curlun->num_sectors) { - curlun->sense_data = SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE; - return -EINVAL; - } - - memset(buf, 0, 8); - buf[0] = 0x01; /* 2048 bytes of user data, rest is EC */ - store_cdrom_address(&buf[4], msf, lba); - return 8; -} - - -static int do_read_toc(struct fsg_dev *fsg, struct fsg_buffhd *bh) -{ - struct fsg_lun *curlun = fsg->curlun; - int msf = fsg->cmnd[1] & 0x02; - int start_track = fsg->cmnd[6]; - u8 *buf = (u8 *) bh->buf; - - if ((fsg->cmnd[1] & ~0x02) != 0 || /* Mask away MSF */ - start_track > 1) { - curlun->sense_data = SS_INVALID_FIELD_IN_CDB; - return -EINVAL; - } - - memset(buf, 0, 20); - buf[1] = (20-2); /* TOC data length */ - buf[2] = 1; /* First track number */ - buf[3] = 1; /* Last track number */ - buf[5] = 0x16; /* Data track, copying allowed */ - buf[6] = 0x01; /* Only track is number 1 */ - store_cdrom_address(&buf[8], msf, 0); - - buf[13] = 0x16; /* Lead-out track is data */ - buf[14] = 0xAA; /* Lead-out track number */ - store_cdrom_address(&buf[16], msf, curlun->num_sectors); - return 20; -} - - -static int do_mode_sense(struct fsg_dev *fsg, struct fsg_buffhd *bh) -{ - struct fsg_lun *curlun = fsg->curlun; - int mscmnd = fsg->cmnd[0]; - u8 *buf = (u8 *) bh->buf; - u8 *buf0 = buf; - int pc, page_code; - int changeable_values, all_pages; - int valid_page = 0; - int len, limit; - - if ((fsg->cmnd[1] & ~0x08) != 0) { // Mask away DBD - curlun->sense_data = SS_INVALID_FIELD_IN_CDB; - return -EINVAL; - } - pc = fsg->cmnd[2] >> 6; - page_code = fsg->cmnd[2] & 0x3f; - if (pc == 3) { - curlun->sense_data = SS_SAVING_PARAMETERS_NOT_SUPPORTED; - return -EINVAL; - } - changeable_values = (pc == 1); - all_pages = (page_code == 0x3f); - - /* Write the mode parameter header. Fixed values are: default - * medium type, no cache control (DPOFUA), and no block descriptors. - * The only variable value is the WriteProtect bit. We will fill in - * the mode data length later. */ - memset(buf, 0, 8); - if (mscmnd == MODE_SENSE) { - buf[2] = (curlun->ro ? 0x80 : 0x00); // WP, DPOFUA - buf += 4; - limit = 255; - } else { // MODE_SENSE_10 - buf[3] = (curlun->ro ? 0x80 : 0x00); // WP, DPOFUA - buf += 8; - limit = 65535; // Should really be mod_data.buflen - } - - /* No block descriptors */ - - /* The mode pages, in numerical order. The only page we support - * is the Caching page. */ - if (page_code == 0x08 || all_pages) { - valid_page = 1; - buf[0] = 0x08; // Page code - buf[1] = 10; // Page length - memset(buf+2, 0, 10); // None of the fields are changeable - - if (!changeable_values) { - buf[2] = 0x04; // Write cache enable, - // Read cache not disabled - // No cache retention priorities - put_unaligned_be16(0xffff, &buf[4]); - /* Don't disable prefetch */ - /* Minimum prefetch = 0 */ - put_unaligned_be16(0xffff, &buf[8]); - /* Maximum prefetch */ - put_unaligned_be16(0xffff, &buf[10]); - /* Maximum prefetch ceiling */ - } - buf += 12; - } - - /* Check that a valid page was requested and the mode data length - * isn't too long. */ - len = buf - buf0; - if (!valid_page || len > limit) { - curlun->sense_data = SS_INVALID_FIELD_IN_CDB; - return -EINVAL; - } - - /* Store the mode data length */ - if (mscmnd == MODE_SENSE) - buf0[0] = len - 1; - else - put_unaligned_be16(len - 2, buf0); - return len; -} - - -static int do_start_stop(struct fsg_dev *fsg) -{ - struct fsg_lun *curlun = fsg->curlun; - int loej, start; - - if (!mod_data.removable) { - curlun->sense_data = SS_INVALID_COMMAND; - return -EINVAL; - } - - // int immed = fsg->cmnd[1] & 0x01; - loej = fsg->cmnd[4] & 0x02; - start = fsg->cmnd[4] & 0x01; - -#ifdef CONFIG_USB_FILE_STORAGE_TEST - if ((fsg->cmnd[1] & ~0x01) != 0 || // Mask away Immed - (fsg->cmnd[4] & ~0x03) != 0) { // Mask LoEj, Start - curlun->sense_data = SS_INVALID_FIELD_IN_CDB; - return -EINVAL; - } - - if (!start) { - - /* Are we allowed to unload the media? */ - if (curlun->prevent_medium_removal) { - LDBG(curlun, "unload attempt prevented\n"); - curlun->sense_data = SS_MEDIUM_REMOVAL_PREVENTED; - return -EINVAL; - } - if (loej) { // Simulate an unload/eject - up_read(&fsg->filesem); - down_write(&fsg->filesem); - fsg_lun_close(curlun); - up_write(&fsg->filesem); - down_read(&fsg->filesem); - } - } else { - - /* Our emulation doesn't support mounting; the medium is - * available for use as soon as it is loaded. */ - if (!fsg_lun_is_open(curlun)) { - curlun->sense_data = SS_MEDIUM_NOT_PRESENT; - return -EINVAL; - } - } -#endif - return 0; -} - - -static int do_prevent_allow(struct fsg_dev *fsg) -{ - struct fsg_lun *curlun = fsg->curlun; - int prevent; - - if (!mod_data.removable) { - curlun->sense_data = SS_INVALID_COMMAND; - return -EINVAL; - } - - prevent = fsg->cmnd[4] & 0x01; - if ((fsg->cmnd[4] & ~0x01) != 0) { // Mask away Prevent - curlun->sense_data = SS_INVALID_FIELD_IN_CDB; - return -EINVAL; - } - - if (curlun->prevent_medium_removal && !prevent) - fsg_lun_fsync_sub(curlun); - curlun->prevent_medium_removal = prevent; - return 0; -} - - -static int do_read_format_capacities(struct fsg_dev *fsg, - struct fsg_buffhd *bh) -{ - struct fsg_lun *curlun = fsg->curlun; - u8 *buf = (u8 *) bh->buf; - - buf[0] = buf[1] = buf[2] = 0; - buf[3] = 8; // Only the Current/Maximum Capacity Descriptor - buf += 4; - - put_unaligned_be32(curlun->num_sectors, &buf[0]); - /* Number of blocks */ - put_unaligned_be32(curlun->blksize, &buf[4]); /* Block length */ - buf[4] = 0x02; /* Current capacity */ - return 12; -} - - -static int do_mode_select(struct fsg_dev *fsg, struct fsg_buffhd *bh) -{ - struct fsg_lun *curlun = fsg->curlun; - - /* We don't support MODE SELECT */ - curlun->sense_data = SS_INVALID_COMMAND; - return -EINVAL; -} - - -/*-------------------------------------------------------------------------*/ - -static int halt_bulk_in_endpoint(struct fsg_dev *fsg) -{ - int rc; - - rc = fsg_set_halt(fsg, fsg->bulk_in); - if (rc == -EAGAIN) - VDBG(fsg, "delayed bulk-in endpoint halt\n"); - while (rc != 0) { - if (rc != -EAGAIN) { - WARNING(fsg, "usb_ep_set_halt -> %d\n", rc); - rc = 0; - break; - } - - /* Wait for a short time and then try again */ - if (msleep_interruptible(100) != 0) - return -EINTR; - rc = usb_ep_set_halt(fsg->bulk_in); - } - return rc; -} - -static int wedge_bulk_in_endpoint(struct fsg_dev *fsg) -{ - int rc; - - DBG(fsg, "bulk-in set wedge\n"); - rc = usb_ep_set_wedge(fsg->bulk_in); - if (rc == -EAGAIN) - VDBG(fsg, "delayed bulk-in endpoint wedge\n"); - while (rc != 0) { - if (rc != -EAGAIN) { - WARNING(fsg, "usb_ep_set_wedge -> %d\n", rc); - rc = 0; - break; - } - - /* Wait for a short time and then try again */ - if (msleep_interruptible(100) != 0) - return -EINTR; - rc = usb_ep_set_wedge(fsg->bulk_in); - } - return rc; -} - -static int throw_away_data(struct fsg_dev *fsg) -{ - struct fsg_buffhd *bh; - u32 amount; - int rc; - - while ((bh = fsg->next_buffhd_to_drain)->state != BUF_STATE_EMPTY || - fsg->usb_amount_left > 0) { - - /* Throw away the data in a filled buffer */ - if (bh->state == BUF_STATE_FULL) { - smp_rmb(); - bh->state = BUF_STATE_EMPTY; - fsg->next_buffhd_to_drain = bh->next; - - /* A short packet or an error ends everything */ - if (bh->outreq->actual < bh->bulk_out_intended_length || - bh->outreq->status != 0) { - raise_exception(fsg, FSG_STATE_ABORT_BULK_OUT); - return -EINTR; - } - continue; - } - - /* Try to submit another request if we need one */ - bh = fsg->next_buffhd_to_fill; - if (bh->state == BUF_STATE_EMPTY && fsg->usb_amount_left > 0) { - amount = min(fsg->usb_amount_left, - (u32) mod_data.buflen); - - /* Except at the end of the transfer, amount will be - * equal to the buffer size, which is divisible by - * the bulk-out maxpacket size. - */ - set_bulk_out_req_length(fsg, bh, amount); - start_transfer(fsg, fsg->bulk_out, bh->outreq, - &bh->outreq_busy, &bh->state); - fsg->next_buffhd_to_fill = bh->next; - fsg->usb_amount_left -= amount; - continue; - } - - /* Otherwise wait for something to happen */ - rc = sleep_thread(fsg); - if (rc) - return rc; - } - return 0; -} - - -static int finish_reply(struct fsg_dev *fsg) -{ - struct fsg_buffhd *bh = fsg->next_buffhd_to_fill; - int rc = 0; - - switch (fsg->data_dir) { - case DATA_DIR_NONE: - break; // Nothing to send - - /* If we don't know whether the host wants to read or write, - * this must be CB or CBI with an unknown command. We mustn't - * try to send or receive any data. So stall both bulk pipes - * if we can and wait for a reset. */ - case DATA_DIR_UNKNOWN: - if (mod_data.can_stall) { - fsg_set_halt(fsg, fsg->bulk_out); - rc = halt_bulk_in_endpoint(fsg); - } - break; - - /* All but the last buffer of data must have already been sent */ - case DATA_DIR_TO_HOST: - if (fsg->data_size == 0) - ; // Nothing to send - - /* If there's no residue, simply send the last buffer */ - else if (fsg->residue == 0) { - bh->inreq->zero = 0; - start_transfer(fsg, fsg->bulk_in, bh->inreq, - &bh->inreq_busy, &bh->state); - fsg->next_buffhd_to_fill = bh->next; - } - - /* There is a residue. For CB and CBI, simply mark the end - * of the data with a short packet. However, if we are - * allowed to stall, there was no data at all (residue == - * data_size), and the command failed (invalid LUN or - * sense data is set), then halt the bulk-in endpoint - * instead. */ - else if (!transport_is_bbb()) { - if (mod_data.can_stall && - fsg->residue == fsg->data_size && - (!fsg->curlun || fsg->curlun->sense_data != SS_NO_SENSE)) { - bh->state = BUF_STATE_EMPTY; - rc = halt_bulk_in_endpoint(fsg); - } else { - bh->inreq->zero = 1; - start_transfer(fsg, fsg->bulk_in, bh->inreq, - &bh->inreq_busy, &bh->state); - fsg->next_buffhd_to_fill = bh->next; - } - } - - /* - * For Bulk-only, mark the end of the data with a short - * packet. If we are allowed to stall, halt the bulk-in - * endpoint. (Note: This violates the Bulk-Only Transport - * specification, which requires us to pad the data if we - * don't halt the endpoint. Presumably nobody will mind.) - */ - else { - bh->inreq->zero = 1; - start_transfer(fsg, fsg->bulk_in, bh->inreq, - &bh->inreq_busy, &bh->state); - fsg->next_buffhd_to_fill = bh->next; - if (mod_data.can_stall) - rc = halt_bulk_in_endpoint(fsg); - } - break; - - /* We have processed all we want from the data the host has sent. - * There may still be outstanding bulk-out requests. */ - case DATA_DIR_FROM_HOST: - if (fsg->residue == 0) - ; // Nothing to receive - - /* Did the host stop sending unexpectedly early? */ - else if (fsg->short_packet_received) { - raise_exception(fsg, FSG_STATE_ABORT_BULK_OUT); - rc = -EINTR; - } - - /* We haven't processed all the incoming data. Even though - * we may be allowed to stall, doing so would cause a race. - * The controller may already have ACK'ed all the remaining - * bulk-out packets, in which case the host wouldn't see a - * STALL. Not realizing the endpoint was halted, it wouldn't - * clear the halt -- leading to problems later on. */ -#if 0 - else if (mod_data.can_stall) { - fsg_set_halt(fsg, fsg->bulk_out); - raise_exception(fsg, FSG_STATE_ABORT_BULK_OUT); - rc = -EINTR; - } -#endif - - /* We can't stall. Read in the excess data and throw it - * all away. */ - else - rc = throw_away_data(fsg); - break; - } - return rc; -} - - -static int send_status(struct fsg_dev *fsg) -{ - struct fsg_lun *curlun = fsg->curlun; - struct fsg_buffhd *bh; - int rc; - u8 status = US_BULK_STAT_OK; - u32 sd, sdinfo = 0; - - /* Wait for the next buffer to become available */ - bh = fsg->next_buffhd_to_fill; - while (bh->state != BUF_STATE_EMPTY) { - rc = sleep_thread(fsg); - if (rc) - return rc; - } - - if (curlun) { - sd = curlun->sense_data; - sdinfo = curlun->sense_data_info; - } else if (fsg->bad_lun_okay) - sd = SS_NO_SENSE; - else - sd = SS_LOGICAL_UNIT_NOT_SUPPORTED; - - if (fsg->phase_error) { - DBG(fsg, "sending phase-error status\n"); - status = US_BULK_STAT_PHASE; - sd = SS_INVALID_COMMAND; - } else if (sd != SS_NO_SENSE) { - DBG(fsg, "sending command-failure status\n"); - status = US_BULK_STAT_FAIL; - VDBG(fsg, " sense data: SK x%02x, ASC x%02x, ASCQ x%02x;" - " info x%x\n", - SK(sd), ASC(sd), ASCQ(sd), sdinfo); - } - - if (transport_is_bbb()) { - struct bulk_cs_wrap *csw = bh->buf; - - /* Store and send the Bulk-only CSW */ - csw->Signature = cpu_to_le32(US_BULK_CS_SIGN); - csw->Tag = fsg->tag; - csw->Residue = cpu_to_le32(fsg->residue); - csw->Status = status; - - bh->inreq->length = US_BULK_CS_WRAP_LEN; - bh->inreq->zero = 0; - start_transfer(fsg, fsg->bulk_in, bh->inreq, - &bh->inreq_busy, &bh->state); - - } else if (mod_data.transport_type == USB_PR_CB) { - - /* Control-Bulk transport has no status phase! */ - return 0; - - } else { // USB_PR_CBI - struct interrupt_data *buf = bh->buf; - - /* Store and send the Interrupt data. UFI sends the ASC - * and ASCQ bytes. Everything else sends a Type (which - * is always 0) and the status Value. */ - if (mod_data.protocol_type == USB_SC_UFI) { - buf->bType = ASC(sd); - buf->bValue = ASCQ(sd); - } else { - buf->bType = 0; - buf->bValue = status; - } - fsg->intreq->length = CBI_INTERRUPT_DATA_LEN; - - fsg->intr_buffhd = bh; // Point to the right buffhd - fsg->intreq->buf = bh->inreq->buf; - fsg->intreq->context = bh; - start_transfer(fsg, fsg->intr_in, fsg->intreq, - &fsg->intreq_busy, &bh->state); - } - - fsg->next_buffhd_to_fill = bh->next; - return 0; -} - - -/*-------------------------------------------------------------------------*/ - -/* Check whether the command is properly formed and whether its data size - * and direction agree with the values we already have. */ -static int check_command(struct fsg_dev *fsg, int cmnd_size, - enum data_direction data_dir, unsigned int mask, - int needs_medium, const char *name) -{ - int i; - int lun = fsg->cmnd[1] >> 5; - static const char dirletter[4] = {'u', 'o', 'i', 'n'}; - char hdlen[20]; - struct fsg_lun *curlun; - - /* Adjust the expected cmnd_size for protocol encapsulation padding. - * Transparent SCSI doesn't pad. */ - if (protocol_is_scsi()) - ; - - /* There's some disagreement as to whether RBC pads commands or not. - * We'll play it safe and accept either form. */ - else if (mod_data.protocol_type == USB_SC_RBC) { - if (fsg->cmnd_size == 12) - cmnd_size = 12; - - /* All the other protocols pad to 12 bytes */ - } else - cmnd_size = 12; - - hdlen[0] = 0; - if (fsg->data_dir != DATA_DIR_UNKNOWN) - sprintf(hdlen, ", H%c=%u", dirletter[(int) fsg->data_dir], - fsg->data_size); - VDBG(fsg, "SCSI command: %s; Dc=%d, D%c=%u; Hc=%d%s\n", - name, cmnd_size, dirletter[(int) data_dir], - fsg->data_size_from_cmnd, fsg->cmnd_size, hdlen); - - /* We can't reply at all until we know the correct data direction - * and size. */ - if (fsg->data_size_from_cmnd == 0) - data_dir = DATA_DIR_NONE; - if (fsg->data_dir == DATA_DIR_UNKNOWN) { // CB or CBI - fsg->data_dir = data_dir; - fsg->data_size = fsg->data_size_from_cmnd; - - } else { // Bulk-only - if (fsg->data_size < fsg->data_size_from_cmnd) { - - /* Host data size < Device data size is a phase error. - * Carry out the command, but only transfer as much - * as we are allowed. */ - fsg->data_size_from_cmnd = fsg->data_size; - fsg->phase_error = 1; - } - } - fsg->residue = fsg->usb_amount_left = fsg->data_size; - - /* Conflicting data directions is a phase error */ - if (fsg->data_dir != data_dir && fsg->data_size_from_cmnd > 0) { - fsg->phase_error = 1; - return -EINVAL; - } - - /* Verify the length of the command itself */ - if (cmnd_size != fsg->cmnd_size) { - - /* Special case workaround: There are plenty of buggy SCSI - * implementations. Many have issues with cbw->Length - * field passing a wrong command size. For those cases we - * always try to work around the problem by using the length - * sent by the host side provided it is at least as large - * as the correct command length. - * Examples of such cases would be MS-Windows, which issues - * REQUEST SENSE with cbw->Length == 12 where it should - * be 6, and xbox360 issuing INQUIRY, TEST UNIT READY and - * REQUEST SENSE with cbw->Length == 10 where it should - * be 6 as well. - */ - if (cmnd_size <= fsg->cmnd_size) { - DBG(fsg, "%s is buggy! Expected length %d " - "but we got %d\n", name, - cmnd_size, fsg->cmnd_size); - cmnd_size = fsg->cmnd_size; - } else { - fsg->phase_error = 1; - return -EINVAL; - } - } - - /* Check that the LUN values are consistent */ - if (transport_is_bbb()) { - if (fsg->lun != lun) - DBG(fsg, "using LUN %d from CBW, " - "not LUN %d from CDB\n", - fsg->lun, lun); - } - - /* Check the LUN */ - curlun = fsg->curlun; - if (curlun) { - if (fsg->cmnd[0] != REQUEST_SENSE) { - curlun->sense_data = SS_NO_SENSE; - curlun->sense_data_info = 0; - curlun->info_valid = 0; - } - } else { - fsg->bad_lun_okay = 0; - - /* INQUIRY and REQUEST SENSE commands are explicitly allowed - * to use unsupported LUNs; all others may not. */ - if (fsg->cmnd[0] != INQUIRY && - fsg->cmnd[0] != REQUEST_SENSE) { - DBG(fsg, "unsupported LUN %d\n", fsg->lun); - return -EINVAL; - } - } - - /* If a unit attention condition exists, only INQUIRY and - * REQUEST SENSE commands are allowed; anything else must fail. */ - if (curlun && curlun->unit_attention_data != SS_NO_SENSE && - fsg->cmnd[0] != INQUIRY && - fsg->cmnd[0] != REQUEST_SENSE) { - curlun->sense_data = curlun->unit_attention_data; - curlun->unit_attention_data = SS_NO_SENSE; - return -EINVAL; - } - - /* Check that only command bytes listed in the mask are non-zero */ - fsg->cmnd[1] &= 0x1f; // Mask away the LUN - for (i = 1; i < cmnd_size; ++i) { - if (fsg->cmnd[i] && !(mask & (1 << i))) { - if (curlun) - curlun->sense_data = SS_INVALID_FIELD_IN_CDB; - return -EINVAL; - } - } - - /* If the medium isn't mounted and the command needs to access - * it, return an error. */ - if (curlun && !fsg_lun_is_open(curlun) && needs_medium) { - curlun->sense_data = SS_MEDIUM_NOT_PRESENT; - return -EINVAL; - } - - return 0; -} - -/* wrapper of check_command for data size in blocks handling */ -static int check_command_size_in_blocks(struct fsg_dev *fsg, int cmnd_size, - enum data_direction data_dir, unsigned int mask, - int needs_medium, const char *name) -{ - if (fsg->curlun) - fsg->data_size_from_cmnd <<= fsg->curlun->blkbits; - return check_command(fsg, cmnd_size, data_dir, - mask, needs_medium, name); -} - -static int do_scsi_command(struct fsg_dev *fsg) -{ - struct fsg_buffhd *bh; - int rc; - int reply = -EINVAL; - int i; - static char unknown[16]; - - dump_cdb(fsg); - - /* Wait for the next buffer to become available for data or status */ - bh = fsg->next_buffhd_to_drain = fsg->next_buffhd_to_fill; - while (bh->state != BUF_STATE_EMPTY) { - rc = sleep_thread(fsg); - if (rc) - return rc; - } - fsg->phase_error = 0; - fsg->short_packet_received = 0; - - down_read(&fsg->filesem); // We're using the backing file - switch (fsg->cmnd[0]) { - - case INQUIRY: - fsg->data_size_from_cmnd = fsg->cmnd[4]; - if ((reply = check_command(fsg, 6, DATA_DIR_TO_HOST, - (1<<4), 0, - "INQUIRY")) == 0) - reply = do_inquiry(fsg, bh); - break; - - case MODE_SELECT: - fsg->data_size_from_cmnd = fsg->cmnd[4]; - if ((reply = check_command(fsg, 6, DATA_DIR_FROM_HOST, - (1<<1) | (1<<4), 0, - "MODE SELECT(6)")) == 0) - reply = do_mode_select(fsg, bh); - break; - - case MODE_SELECT_10: - fsg->data_size_from_cmnd = get_unaligned_be16(&fsg->cmnd[7]); - if ((reply = check_command(fsg, 10, DATA_DIR_FROM_HOST, - (1<<1) | (3<<7), 0, - "MODE SELECT(10)")) == 0) - reply = do_mode_select(fsg, bh); - break; - - case MODE_SENSE: - fsg->data_size_from_cmnd = fsg->cmnd[4]; - if ((reply = check_command(fsg, 6, DATA_DIR_TO_HOST, - (1<<1) | (1<<2) | (1<<4), 0, - "MODE SENSE(6)")) == 0) - reply = do_mode_sense(fsg, bh); - break; - - case MODE_SENSE_10: - fsg->data_size_from_cmnd = get_unaligned_be16(&fsg->cmnd[7]); - if ((reply = check_command(fsg, 10, DATA_DIR_TO_HOST, - (1<<1) | (1<<2) | (3<<7), 0, - "MODE SENSE(10)")) == 0) - reply = do_mode_sense(fsg, bh); - break; - - case ALLOW_MEDIUM_REMOVAL: - fsg->data_size_from_cmnd = 0; - if ((reply = check_command(fsg, 6, DATA_DIR_NONE, - (1<<4), 0, - "PREVENT-ALLOW MEDIUM REMOVAL")) == 0) - reply = do_prevent_allow(fsg); - break; - - case READ_6: - i = fsg->cmnd[4]; - fsg->data_size_from_cmnd = (i == 0) ? 256 : i; - if ((reply = check_command_size_in_blocks(fsg, 6, - DATA_DIR_TO_HOST, - (7<<1) | (1<<4), 1, - "READ(6)")) == 0) - reply = do_read(fsg); - break; - - case READ_10: - fsg->data_size_from_cmnd = get_unaligned_be16(&fsg->cmnd[7]); - if ((reply = check_command_size_in_blocks(fsg, 10, - DATA_DIR_TO_HOST, - (1<<1) | (0xf<<2) | (3<<7), 1, - "READ(10)")) == 0) - reply = do_read(fsg); - break; - - case READ_12: - fsg->data_size_from_cmnd = get_unaligned_be32(&fsg->cmnd[6]); - if ((reply = check_command_size_in_blocks(fsg, 12, - DATA_DIR_TO_HOST, - (1<<1) | (0xf<<2) | (0xf<<6), 1, - "READ(12)")) == 0) - reply = do_read(fsg); - break; - - case READ_CAPACITY: - fsg->data_size_from_cmnd = 8; - if ((reply = check_command(fsg, 10, DATA_DIR_TO_HOST, - (0xf<<2) | (1<<8), 1, - "READ CAPACITY")) == 0) - reply = do_read_capacity(fsg, bh); - break; - - case READ_HEADER: - if (!mod_data.cdrom) - goto unknown_cmnd; - fsg->data_size_from_cmnd = get_unaligned_be16(&fsg->cmnd[7]); - if ((reply = check_command(fsg, 10, DATA_DIR_TO_HOST, - (3<<7) | (0x1f<<1), 1, - "READ HEADER")) == 0) - reply = do_read_header(fsg, bh); - break; - - case READ_TOC: - if (!mod_data.cdrom) - goto unknown_cmnd; - fsg->data_size_from_cmnd = get_unaligned_be16(&fsg->cmnd[7]); - if ((reply = check_command(fsg, 10, DATA_DIR_TO_HOST, - (7<<6) | (1<<1), 1, - "READ TOC")) == 0) - reply = do_read_toc(fsg, bh); - break; - - case READ_FORMAT_CAPACITIES: - fsg->data_size_from_cmnd = get_unaligned_be16(&fsg->cmnd[7]); - if ((reply = check_command(fsg, 10, DATA_DIR_TO_HOST, - (3<<7), 1, - "READ FORMAT CAPACITIES")) == 0) - reply = do_read_format_capacities(fsg, bh); - break; - - case REQUEST_SENSE: - fsg->data_size_from_cmnd = fsg->cmnd[4]; - if ((reply = check_command(fsg, 6, DATA_DIR_TO_HOST, - (1<<4), 0, - "REQUEST SENSE")) == 0) - reply = do_request_sense(fsg, bh); - break; - - case START_STOP: - fsg->data_size_from_cmnd = 0; - if ((reply = check_command(fsg, 6, DATA_DIR_NONE, - (1<<1) | (1<<4), 0, - "START-STOP UNIT")) == 0) - reply = do_start_stop(fsg); - break; - - case SYNCHRONIZE_CACHE: - fsg->data_size_from_cmnd = 0; - if ((reply = check_command(fsg, 10, DATA_DIR_NONE, - (0xf<<2) | (3<<7), 1, - "SYNCHRONIZE CACHE")) == 0) - reply = do_synchronize_cache(fsg); - break; - - case TEST_UNIT_READY: - fsg->data_size_from_cmnd = 0; - reply = check_command(fsg, 6, DATA_DIR_NONE, - 0, 1, - "TEST UNIT READY"); - break; - - /* Although optional, this command is used by MS-Windows. We - * support a minimal version: BytChk must be 0. */ - case VERIFY: - fsg->data_size_from_cmnd = 0; - if ((reply = check_command(fsg, 10, DATA_DIR_NONE, - (1<<1) | (0xf<<2) | (3<<7), 1, - "VERIFY")) == 0) - reply = do_verify(fsg); - break; - - case WRITE_6: - i = fsg->cmnd[4]; - fsg->data_size_from_cmnd = (i == 0) ? 256 : i; - if ((reply = check_command_size_in_blocks(fsg, 6, - DATA_DIR_FROM_HOST, - (7<<1) | (1<<4), 1, - "WRITE(6)")) == 0) - reply = do_write(fsg); - break; - - case WRITE_10: - fsg->data_size_from_cmnd = get_unaligned_be16(&fsg->cmnd[7]); - if ((reply = check_command_size_in_blocks(fsg, 10, - DATA_DIR_FROM_HOST, - (1<<1) | (0xf<<2) | (3<<7), 1, - "WRITE(10)")) == 0) - reply = do_write(fsg); - break; - - case WRITE_12: - fsg->data_size_from_cmnd = get_unaligned_be32(&fsg->cmnd[6]); - if ((reply = check_command_size_in_blocks(fsg, 12, - DATA_DIR_FROM_HOST, - (1<<1) | (0xf<<2) | (0xf<<6), 1, - "WRITE(12)")) == 0) - reply = do_write(fsg); - break; - - /* Some mandatory commands that we recognize but don't implement. - * They don't mean much in this setting. It's left as an exercise - * for anyone interested to implement RESERVE and RELEASE in terms - * of Posix locks. */ - case FORMAT_UNIT: - case RELEASE: - case RESERVE: - case SEND_DIAGNOSTIC: - // Fall through - - default: - unknown_cmnd: - fsg->data_size_from_cmnd = 0; - sprintf(unknown, "Unknown x%02x", fsg->cmnd[0]); - if ((reply = check_command(fsg, fsg->cmnd_size, - DATA_DIR_UNKNOWN, ~0, 0, unknown)) == 0) { - fsg->curlun->sense_data = SS_INVALID_COMMAND; - reply = -EINVAL; - } - break; - } - up_read(&fsg->filesem); - - if (reply == -EINTR || signal_pending(current)) - return -EINTR; - - /* Set up the single reply buffer for finish_reply() */ - if (reply == -EINVAL) - reply = 0; // Error reply length - if (reply >= 0 && fsg->data_dir == DATA_DIR_TO_HOST) { - reply = min((u32) reply, fsg->data_size_from_cmnd); - bh->inreq->length = reply; - bh->state = BUF_STATE_FULL; - fsg->residue -= reply; - } // Otherwise it's already set - - return 0; -} - - -/*-------------------------------------------------------------------------*/ - -static int received_cbw(struct fsg_dev *fsg, struct fsg_buffhd *bh) -{ - struct usb_request *req = bh->outreq; - struct bulk_cb_wrap *cbw = req->buf; - - /* Was this a real packet? Should it be ignored? */ - if (req->status || test_bit(IGNORE_BULK_OUT, &fsg->atomic_bitflags)) - return -EINVAL; - - /* Is the CBW valid? */ - if (req->actual != US_BULK_CB_WRAP_LEN || - cbw->Signature != cpu_to_le32( - US_BULK_CB_SIGN)) { - DBG(fsg, "invalid CBW: len %u sig 0x%x\n", - req->actual, - le32_to_cpu(cbw->Signature)); - - /* The Bulk-only spec says we MUST stall the IN endpoint - * (6.6.1), so it's unavoidable. It also says we must - * retain this state until the next reset, but there's - * no way to tell the controller driver it should ignore - * Clear-Feature(HALT) requests. - * - * We aren't required to halt the OUT endpoint; instead - * we can simply accept and discard any data received - * until the next reset. */ - wedge_bulk_in_endpoint(fsg); - set_bit(IGNORE_BULK_OUT, &fsg->atomic_bitflags); - return -EINVAL; - } - - /* Is the CBW meaningful? */ - if (cbw->Lun >= FSG_MAX_LUNS || cbw->Flags & ~US_BULK_FLAG_IN || - cbw->Length <= 0 || cbw->Length > MAX_COMMAND_SIZE) { - DBG(fsg, "non-meaningful CBW: lun = %u, flags = 0x%x, " - "cmdlen %u\n", - cbw->Lun, cbw->Flags, cbw->Length); - - /* We can do anything we want here, so let's stall the - * bulk pipes if we are allowed to. */ - if (mod_data.can_stall) { - fsg_set_halt(fsg, fsg->bulk_out); - halt_bulk_in_endpoint(fsg); - } - return -EINVAL; - } - - /* Save the command for later */ - fsg->cmnd_size = cbw->Length; - memcpy(fsg->cmnd, cbw->CDB, fsg->cmnd_size); - if (cbw->Flags & US_BULK_FLAG_IN) - fsg->data_dir = DATA_DIR_TO_HOST; - else - fsg->data_dir = DATA_DIR_FROM_HOST; - fsg->data_size = le32_to_cpu(cbw->DataTransferLength); - if (fsg->data_size == 0) - fsg->data_dir = DATA_DIR_NONE; - fsg->lun = cbw->Lun; - fsg->tag = cbw->Tag; - return 0; -} - - -static int get_next_command(struct fsg_dev *fsg) -{ - struct fsg_buffhd *bh; - int rc = 0; - - if (transport_is_bbb()) { - - /* Wait for the next buffer to become available */ - bh = fsg->next_buffhd_to_fill; - while (bh->state != BUF_STATE_EMPTY) { - rc = sleep_thread(fsg); - if (rc) - return rc; - } - - /* Queue a request to read a Bulk-only CBW */ - set_bulk_out_req_length(fsg, bh, US_BULK_CB_WRAP_LEN); - start_transfer(fsg, fsg->bulk_out, bh->outreq, - &bh->outreq_busy, &bh->state); - - /* We will drain the buffer in software, which means we - * can reuse it for the next filling. No need to advance - * next_buffhd_to_fill. */ - - /* Wait for the CBW to arrive */ - while (bh->state != BUF_STATE_FULL) { - rc = sleep_thread(fsg); - if (rc) - return rc; - } - smp_rmb(); - rc = received_cbw(fsg, bh); - bh->state = BUF_STATE_EMPTY; - - } else { // USB_PR_CB or USB_PR_CBI - - /* Wait for the next command to arrive */ - while (fsg->cbbuf_cmnd_size == 0) { - rc = sleep_thread(fsg); - if (rc) - return rc; - } - - /* Is the previous status interrupt request still busy? - * The host is allowed to skip reading the status, - * so we must cancel it. */ - if (fsg->intreq_busy) - usb_ep_dequeue(fsg->intr_in, fsg->intreq); - - /* Copy the command and mark the buffer empty */ - fsg->data_dir = DATA_DIR_UNKNOWN; - spin_lock_irq(&fsg->lock); - fsg->cmnd_size = fsg->cbbuf_cmnd_size; - memcpy(fsg->cmnd, fsg->cbbuf_cmnd, fsg->cmnd_size); - fsg->cbbuf_cmnd_size = 0; - spin_unlock_irq(&fsg->lock); - - /* Use LUN from the command */ - fsg->lun = fsg->cmnd[1] >> 5; - } - - /* Update current lun */ - if (fsg->lun >= 0 && fsg->lun < fsg->nluns) - fsg->curlun = &fsg->luns[fsg->lun]; - else - fsg->curlun = NULL; - - return rc; -} - - -/*-------------------------------------------------------------------------*/ - -static int enable_endpoint(struct fsg_dev *fsg, struct usb_ep *ep, - const struct usb_endpoint_descriptor *d) -{ - int rc; - - ep->driver_data = fsg; - ep->desc = d; - rc = usb_ep_enable(ep); - if (rc) - ERROR(fsg, "can't enable %s, result %d\n", ep->name, rc); - return rc; -} - -static int alloc_request(struct fsg_dev *fsg, struct usb_ep *ep, - struct usb_request **preq) -{ - *preq = usb_ep_alloc_request(ep, GFP_ATOMIC); - if (*preq) - return 0; - ERROR(fsg, "can't allocate request for %s\n", ep->name); - return -ENOMEM; -} - -/* - * Reset interface setting and re-init endpoint state (toggle etc). - * Call with altsetting < 0 to disable the interface. The only other - * available altsetting is 0, which enables the interface. - */ -static int do_set_interface(struct fsg_dev *fsg, int altsetting) -{ - int rc = 0; - int i; - const struct usb_endpoint_descriptor *d; - - if (fsg->running) - DBG(fsg, "reset interface\n"); - -reset: - /* Deallocate the requests */ - for (i = 0; i < fsg_num_buffers; ++i) { - struct fsg_buffhd *bh = &fsg->buffhds[i]; - - if (bh->inreq) { - usb_ep_free_request(fsg->bulk_in, bh->inreq); - bh->inreq = NULL; - } - if (bh->outreq) { - usb_ep_free_request(fsg->bulk_out, bh->outreq); - bh->outreq = NULL; - } - } - if (fsg->intreq) { - usb_ep_free_request(fsg->intr_in, fsg->intreq); - fsg->intreq = NULL; - } - - /* Disable the endpoints */ - if (fsg->bulk_in_enabled) { - usb_ep_disable(fsg->bulk_in); - fsg->bulk_in_enabled = 0; - } - if (fsg->bulk_out_enabled) { - usb_ep_disable(fsg->bulk_out); - fsg->bulk_out_enabled = 0; - } - if (fsg->intr_in_enabled) { - usb_ep_disable(fsg->intr_in); - fsg->intr_in_enabled = 0; - } - - fsg->running = 0; - if (altsetting < 0 || rc != 0) - return rc; - - DBG(fsg, "set interface %d\n", altsetting); - - /* Enable the endpoints */ - d = fsg_ep_desc(fsg->gadget, - &fsg_fs_bulk_in_desc, &fsg_hs_bulk_in_desc, - &fsg_ss_bulk_in_desc); - if ((rc = enable_endpoint(fsg, fsg->bulk_in, d)) != 0) - goto reset; - fsg->bulk_in_enabled = 1; - - d = fsg_ep_desc(fsg->gadget, - &fsg_fs_bulk_out_desc, &fsg_hs_bulk_out_desc, - &fsg_ss_bulk_out_desc); - if ((rc = enable_endpoint(fsg, fsg->bulk_out, d)) != 0) - goto reset; - fsg->bulk_out_enabled = 1; - fsg->bulk_out_maxpacket = usb_endpoint_maxp(d); - clear_bit(IGNORE_BULK_OUT, &fsg->atomic_bitflags); - - if (transport_is_cbi()) { - d = fsg_ep_desc(fsg->gadget, - &fsg_fs_intr_in_desc, &fsg_hs_intr_in_desc, - &fsg_ss_intr_in_desc); - if ((rc = enable_endpoint(fsg, fsg->intr_in, d)) != 0) - goto reset; - fsg->intr_in_enabled = 1; - } - - /* Allocate the requests */ - for (i = 0; i < fsg_num_buffers; ++i) { - struct fsg_buffhd *bh = &fsg->buffhds[i]; - - if ((rc = alloc_request(fsg, fsg->bulk_in, &bh->inreq)) != 0) - goto reset; - if ((rc = alloc_request(fsg, fsg->bulk_out, &bh->outreq)) != 0) - goto reset; - bh->inreq->buf = bh->outreq->buf = bh->buf; - bh->inreq->context = bh->outreq->context = bh; - bh->inreq->complete = bulk_in_complete; - bh->outreq->complete = bulk_out_complete; - } - if (transport_is_cbi()) { - if ((rc = alloc_request(fsg, fsg->intr_in, &fsg->intreq)) != 0) - goto reset; - fsg->intreq->complete = intr_in_complete; - } - - fsg->running = 1; - for (i = 0; i < fsg->nluns; ++i) - fsg->luns[i].unit_attention_data = SS_RESET_OCCURRED; - return rc; -} - - -/* - * Change our operational configuration. This code must agree with the code - * that returns config descriptors, and with interface altsetting code. - * - * It's also responsible for power management interactions. Some - * configurations might not work with our current power sources. - * For now we just assume the gadget is always self-powered. - */ -static int do_set_config(struct fsg_dev *fsg, u8 new_config) -{ - int rc = 0; - - /* Disable the single interface */ - if (fsg->config != 0) { - DBG(fsg, "reset config\n"); - fsg->config = 0; - rc = do_set_interface(fsg, -1); - } - - /* Enable the interface */ - if (new_config != 0) { - fsg->config = new_config; - if ((rc = do_set_interface(fsg, 0)) != 0) - fsg->config = 0; // Reset on errors - else - INFO(fsg, "%s config #%d\n", - usb_speed_string(fsg->gadget->speed), - fsg->config); - } - return rc; -} - - -/*-------------------------------------------------------------------------*/ - -static void handle_exception(struct fsg_dev *fsg) -{ - siginfo_t info; - int sig; - int i; - int num_active; - struct fsg_buffhd *bh; - enum fsg_state old_state; - u8 new_config; - struct fsg_lun *curlun; - unsigned int exception_req_tag; - int rc; - - /* Clear the existing signals. Anything but SIGUSR1 is converted - * into a high-priority EXIT exception. */ - for (;;) { - sig = dequeue_signal_lock(current, ¤t->blocked, &info); - if (!sig) - break; - if (sig != SIGUSR1) { - if (fsg->state < FSG_STATE_EXIT) - DBG(fsg, "Main thread exiting on signal\n"); - raise_exception(fsg, FSG_STATE_EXIT); - } - } - - /* Cancel all the pending transfers */ - if (fsg->intreq_busy) - usb_ep_dequeue(fsg->intr_in, fsg->intreq); - for (i = 0; i < fsg_num_buffers; ++i) { - bh = &fsg->buffhds[i]; - if (bh->inreq_busy) - usb_ep_dequeue(fsg->bulk_in, bh->inreq); - if (bh->outreq_busy) - usb_ep_dequeue(fsg->bulk_out, bh->outreq); - } - - /* Wait until everything is idle */ - for (;;) { - num_active = fsg->intreq_busy; - for (i = 0; i < fsg_num_buffers; ++i) { - bh = &fsg->buffhds[i]; - num_active += bh->inreq_busy + bh->outreq_busy; - } - if (num_active == 0) - break; - if (sleep_thread(fsg)) - return; - } - - /* Clear out the controller's fifos */ - if (fsg->bulk_in_enabled) - usb_ep_fifo_flush(fsg->bulk_in); - if (fsg->bulk_out_enabled) - usb_ep_fifo_flush(fsg->bulk_out); - if (fsg->intr_in_enabled) - usb_ep_fifo_flush(fsg->intr_in); - - /* Reset the I/O buffer states and pointers, the SCSI - * state, and the exception. Then invoke the handler. */ - spin_lock_irq(&fsg->lock); - - for (i = 0; i < fsg_num_buffers; ++i) { - bh = &fsg->buffhds[i]; - bh->state = BUF_STATE_EMPTY; - } - fsg->next_buffhd_to_fill = fsg->next_buffhd_to_drain = - &fsg->buffhds[0]; - - exception_req_tag = fsg->exception_req_tag; - new_config = fsg->new_config; - old_state = fsg->state; - - if (old_state == FSG_STATE_ABORT_BULK_OUT) - fsg->state = FSG_STATE_STATUS_PHASE; - else { - for (i = 0; i < fsg->nluns; ++i) { - curlun = &fsg->luns[i]; - curlun->prevent_medium_removal = 0; - curlun->sense_data = curlun->unit_attention_data = - SS_NO_SENSE; - curlun->sense_data_info = 0; - curlun->info_valid = 0; - } - fsg->state = FSG_STATE_IDLE; - } - spin_unlock_irq(&fsg->lock); - - /* Carry out any extra actions required for the exception */ - switch (old_state) { - default: - break; - - case FSG_STATE_ABORT_BULK_OUT: - send_status(fsg); - spin_lock_irq(&fsg->lock); - if (fsg->state == FSG_STATE_STATUS_PHASE) - fsg->state = FSG_STATE_IDLE; - spin_unlock_irq(&fsg->lock); - break; - - case FSG_STATE_RESET: - /* In case we were forced against our will to halt a - * bulk endpoint, clear the halt now. (The SuperH UDC - * requires this.) */ - if (test_and_clear_bit(IGNORE_BULK_OUT, &fsg->atomic_bitflags)) - usb_ep_clear_halt(fsg->bulk_in); - - if (transport_is_bbb()) { - if (fsg->ep0_req_tag == exception_req_tag) - ep0_queue(fsg); // Complete the status stage - - } else if (transport_is_cbi()) - send_status(fsg); // Status by interrupt pipe - - /* Technically this should go here, but it would only be - * a waste of time. Ditto for the INTERFACE_CHANGE and - * CONFIG_CHANGE cases. */ - // for (i = 0; i < fsg->nluns; ++i) - // fsg->luns[i].unit_attention_data = SS_RESET_OCCURRED; - break; - - case FSG_STATE_INTERFACE_CHANGE: - rc = do_set_interface(fsg, 0); - if (fsg->ep0_req_tag != exception_req_tag) - break; - if (rc != 0) // STALL on errors - fsg_set_halt(fsg, fsg->ep0); - else // Complete the status stage - ep0_queue(fsg); - break; - - case FSG_STATE_CONFIG_CHANGE: - rc = do_set_config(fsg, new_config); - if (fsg->ep0_req_tag != exception_req_tag) - break; - if (rc != 0) // STALL on errors - fsg_set_halt(fsg, fsg->ep0); - else // Complete the status stage - ep0_queue(fsg); - break; - - case FSG_STATE_DISCONNECT: - for (i = 0; i < fsg->nluns; ++i) - fsg_lun_fsync_sub(fsg->luns + i); - do_set_config(fsg, 0); // Unconfigured state - break; - - case FSG_STATE_EXIT: - case FSG_STATE_TERMINATED: - do_set_config(fsg, 0); // Free resources - spin_lock_irq(&fsg->lock); - fsg->state = FSG_STATE_TERMINATED; // Stop the thread - spin_unlock_irq(&fsg->lock); - break; - } -} - - -/*-------------------------------------------------------------------------*/ - -static int fsg_main_thread(void *fsg_) -{ - struct fsg_dev *fsg = fsg_; - - /* Allow the thread to be killed by a signal, but set the signal mask - * to block everything but INT, TERM, KILL, and USR1. */ - allow_signal(SIGINT); - allow_signal(SIGTERM); - allow_signal(SIGKILL); - allow_signal(SIGUSR1); - - /* Allow the thread to be frozen */ - set_freezable(); - - /* Arrange for userspace references to be interpreted as kernel - * pointers. That way we can pass a kernel pointer to a routine - * that expects a __user pointer and it will work okay. */ - set_fs(get_ds()); - - /* The main loop */ - while (fsg->state != FSG_STATE_TERMINATED) { - if (exception_in_progress(fsg) || signal_pending(current)) { - handle_exception(fsg); - continue; - } - - if (!fsg->running) { - sleep_thread(fsg); - continue; - } - - if (get_next_command(fsg)) - continue; - - spin_lock_irq(&fsg->lock); - if (!exception_in_progress(fsg)) - fsg->state = FSG_STATE_DATA_PHASE; - spin_unlock_irq(&fsg->lock); - - if (do_scsi_command(fsg) || finish_reply(fsg)) - continue; - - spin_lock_irq(&fsg->lock); - if (!exception_in_progress(fsg)) - fsg->state = FSG_STATE_STATUS_PHASE; - spin_unlock_irq(&fsg->lock); - - if (send_status(fsg)) - continue; - - spin_lock_irq(&fsg->lock); - if (!exception_in_progress(fsg)) - fsg->state = FSG_STATE_IDLE; - spin_unlock_irq(&fsg->lock); - } - - spin_lock_irq(&fsg->lock); - fsg->thread_task = NULL; - spin_unlock_irq(&fsg->lock); - - /* If we are exiting because of a signal, unregister the - * gadget driver. */ - if (test_and_clear_bit(REGISTERED, &fsg->atomic_bitflags)) - usb_gadget_unregister_driver(&fsg_driver); - - /* Let the unbind and cleanup routines know the thread has exited */ - complete_and_exit(&fsg->thread_notifier, 0); -} - - -/*-------------------------------------------------------------------------*/ - - -/* The write permissions and store_xxx pointers are set in fsg_bind() */ -static DEVICE_ATTR(ro, 0444, fsg_show_ro, NULL); -static DEVICE_ATTR(nofua, 0644, fsg_show_nofua, NULL); -static DEVICE_ATTR(file, 0444, fsg_show_file, NULL); - - -/*-------------------------------------------------------------------------*/ - -static void fsg_release(struct kref *ref) -{ - struct fsg_dev *fsg = container_of(ref, struct fsg_dev, ref); - - kfree(fsg->luns); - kfree(fsg); -} - -static void lun_release(struct device *dev) -{ - struct rw_semaphore *filesem = dev_get_drvdata(dev); - struct fsg_dev *fsg = - container_of(filesem, struct fsg_dev, filesem); - - kref_put(&fsg->ref, fsg_release); -} - -static void /* __init_or_exit */ fsg_unbind(struct usb_gadget *gadget) -{ - struct fsg_dev *fsg = get_gadget_data(gadget); - int i; - struct fsg_lun *curlun; - struct usb_request *req = fsg->ep0req; - - DBG(fsg, "unbind\n"); - clear_bit(REGISTERED, &fsg->atomic_bitflags); - - /* If the thread isn't already dead, tell it to exit now */ - if (fsg->state != FSG_STATE_TERMINATED) { - raise_exception(fsg, FSG_STATE_EXIT); - wait_for_completion(&fsg->thread_notifier); - - /* The cleanup routine waits for this completion also */ - complete(&fsg->thread_notifier); - } - - /* Unregister the sysfs attribute files and the LUNs */ - for (i = 0; i < fsg->nluns; ++i) { - curlun = &fsg->luns[i]; - if (curlun->registered) { - device_remove_file(&curlun->dev, &dev_attr_nofua); - device_remove_file(&curlun->dev, &dev_attr_ro); - device_remove_file(&curlun->dev, &dev_attr_file); - fsg_lun_close(curlun); - device_unregister(&curlun->dev); - curlun->registered = 0; - } - } - - /* Free the data buffers */ - for (i = 0; i < fsg_num_buffers; ++i) - kfree(fsg->buffhds[i].buf); - - /* Free the request and buffer for endpoint 0 */ - if (req) { - kfree(req->buf); - usb_ep_free_request(fsg->ep0, req); - } - - set_gadget_data(gadget, NULL); -} - - -static int __init check_parameters(struct fsg_dev *fsg) -{ - int prot; - - /* Store the default values */ - mod_data.transport_type = USB_PR_BULK; - mod_data.transport_name = "Bulk-only"; - mod_data.protocol_type = USB_SC_SCSI; - mod_data.protocol_name = "Transparent SCSI"; - - /* Some peripheral controllers are known not to be able to - * halt bulk endpoints correctly. If one of them is present, - * disable stalls. - */ - if (gadget_is_at91(fsg->gadget)) - mod_data.can_stall = 0; - - if (mod_data.release == 0xffff) - mod_data.release = get_default_bcdDevice(); - - prot = simple_strtol(mod_data.protocol_parm, NULL, 0); - -#ifdef CONFIG_USB_FILE_STORAGE_TEST - if (strnicmp(mod_data.transport_parm, "BBB", 10) == 0) { - ; // Use default setting - } else if (strnicmp(mod_data.transport_parm, "CB", 10) == 0) { - mod_data.transport_type = USB_PR_CB; - mod_data.transport_name = "Control-Bulk"; - } else if (strnicmp(mod_data.transport_parm, "CBI", 10) == 0) { - mod_data.transport_type = USB_PR_CBI; - mod_data.transport_name = "Control-Bulk-Interrupt"; - } else { - ERROR(fsg, "invalid transport: %s\n", mod_data.transport_parm); - return -EINVAL; - } - - if (strnicmp(mod_data.protocol_parm, "SCSI", 10) == 0 || - prot == USB_SC_SCSI) { - ; // Use default setting - } else if (strnicmp(mod_data.protocol_parm, "RBC", 10) == 0 || - prot == USB_SC_RBC) { - mod_data.protocol_type = USB_SC_RBC; - mod_data.protocol_name = "RBC"; - } else if (strnicmp(mod_data.protocol_parm, "8020", 4) == 0 || - strnicmp(mod_data.protocol_parm, "ATAPI", 10) == 0 || - prot == USB_SC_8020) { - mod_data.protocol_type = USB_SC_8020; - mod_data.protocol_name = "8020i (ATAPI)"; - } else if (strnicmp(mod_data.protocol_parm, "QIC", 3) == 0 || - prot == USB_SC_QIC) { - mod_data.protocol_type = USB_SC_QIC; - mod_data.protocol_name = "QIC-157"; - } else if (strnicmp(mod_data.protocol_parm, "UFI", 10) == 0 || - prot == USB_SC_UFI) { - mod_data.protocol_type = USB_SC_UFI; - mod_data.protocol_name = "UFI"; - } else if (strnicmp(mod_data.protocol_parm, "8070", 4) == 0 || - prot == USB_SC_8070) { - mod_data.protocol_type = USB_SC_8070; - mod_data.protocol_name = "8070i"; - } else { - ERROR(fsg, "invalid protocol: %s\n", mod_data.protocol_parm); - return -EINVAL; - } - - mod_data.buflen &= PAGE_CACHE_MASK; - if (mod_data.buflen <= 0) { - ERROR(fsg, "invalid buflen\n"); - return -ETOOSMALL; - } - -#endif /* CONFIG_USB_FILE_STORAGE_TEST */ - - /* Serial string handling. - * On a real device, the serial string would be loaded - * from permanent storage. */ - if (mod_data.serial) { - const char *ch; - unsigned len = 0; - - /* Sanity check : - * The CB[I] specification limits the serial string to - * 12 uppercase hexadecimal characters. - * BBB need at least 12 uppercase hexadecimal characters, - * with a maximum of 126. */ - for (ch = mod_data.serial; *ch; ++ch) { - ++len; - if ((*ch < '0' || *ch > '9') && - (*ch < 'A' || *ch > 'F')) { /* not uppercase hex */ - WARNING(fsg, - "Invalid serial string character: %c\n", - *ch); - goto no_serial; - } - } - if (len > 126 || - (mod_data.transport_type == USB_PR_BULK && len < 12) || - (mod_data.transport_type != USB_PR_BULK && len > 12)) { - WARNING(fsg, "Invalid serial string length!\n"); - goto no_serial; - } - fsg_strings[FSG_STRING_SERIAL - 1].s = mod_data.serial; - } else { - WARNING(fsg, "No serial-number string provided!\n"); - no_serial: - device_desc.iSerialNumber = 0; - } - - return 0; -} - - -static int __init fsg_bind(struct usb_gadget *gadget, - struct usb_gadget_driver *driver) -{ - struct fsg_dev *fsg = the_fsg; - int rc; - int i; - struct fsg_lun *curlun; - struct usb_ep *ep; - struct usb_request *req; - char *pathbuf, *p; - - fsg->gadget = gadget; - set_gadget_data(gadget, fsg); - fsg->ep0 = gadget->ep0; - fsg->ep0->driver_data = fsg; - - if ((rc = check_parameters(fsg)) != 0) - goto out; - - if (mod_data.removable) { // Enable the store_xxx attributes - dev_attr_file.attr.mode = 0644; - dev_attr_file.store = fsg_store_file; - if (!mod_data.cdrom) { - dev_attr_ro.attr.mode = 0644; - dev_attr_ro.store = fsg_store_ro; - } - } - - /* Only for removable media? */ - dev_attr_nofua.attr.mode = 0644; - dev_attr_nofua.store = fsg_store_nofua; - - /* Find out how many LUNs there should be */ - i = mod_data.nluns; - if (i == 0) - i = max(mod_data.num_filenames, 1u); - if (i > FSG_MAX_LUNS) { - ERROR(fsg, "invalid number of LUNs: %d\n", i); - rc = -EINVAL; - goto out; - } - - /* Create the LUNs, open their backing files, and register the - * LUN devices in sysfs. */ - fsg->luns = kzalloc(i * sizeof(struct fsg_lun), GFP_KERNEL); - if (!fsg->luns) { - rc = -ENOMEM; - goto out; - } - fsg->nluns = i; - - for (i = 0; i < fsg->nluns; ++i) { - curlun = &fsg->luns[i]; - curlun->cdrom = !!mod_data.cdrom; - curlun->ro = mod_data.cdrom || mod_data.ro[i]; - curlun->initially_ro = curlun->ro; - curlun->removable = mod_data.removable; - curlun->nofua = mod_data.nofua[i]; - curlun->dev.release = lun_release; - curlun->dev.parent = &gadget->dev; - curlun->dev.driver = &fsg_driver.driver; - dev_set_drvdata(&curlun->dev, &fsg->filesem); - dev_set_name(&curlun->dev,"%s-lun%d", - dev_name(&gadget->dev), i); - - kref_get(&fsg->ref); - rc = device_register(&curlun->dev); - if (rc) { - INFO(fsg, "failed to register LUN%d: %d\n", i, rc); - put_device(&curlun->dev); - goto out; - } - curlun->registered = 1; - - rc = device_create_file(&curlun->dev, &dev_attr_ro); - if (rc) - goto out; - rc = device_create_file(&curlun->dev, &dev_attr_nofua); - if (rc) - goto out; - rc = device_create_file(&curlun->dev, &dev_attr_file); - if (rc) - goto out; - - if (mod_data.file[i] && *mod_data.file[i]) { - rc = fsg_lun_open(curlun, mod_data.file[i]); - if (rc) - goto out; - } else if (!mod_data.removable) { - ERROR(fsg, "no file given for LUN%d\n", i); - rc = -EINVAL; - goto out; - } - } - - /* Find all the endpoints we will use */ - usb_ep_autoconfig_reset(gadget); - ep = usb_ep_autoconfig(gadget, &fsg_fs_bulk_in_desc); - if (!ep) - goto autoconf_fail; - ep->driver_data = fsg; // claim the endpoint - fsg->bulk_in = ep; - - ep = usb_ep_autoconfig(gadget, &fsg_fs_bulk_out_desc); - if (!ep) - goto autoconf_fail; - ep->driver_data = fsg; // claim the endpoint - fsg->bulk_out = ep; - - if (transport_is_cbi()) { - ep = usb_ep_autoconfig(gadget, &fsg_fs_intr_in_desc); - if (!ep) - goto autoconf_fail; - ep->driver_data = fsg; // claim the endpoint - fsg->intr_in = ep; - } - - /* Fix up the descriptors */ - device_desc.idVendor = cpu_to_le16(mod_data.vendor); - device_desc.idProduct = cpu_to_le16(mod_data.product); - device_desc.bcdDevice = cpu_to_le16(mod_data.release); - - i = (transport_is_cbi() ? 3 : 2); // Number of endpoints - fsg_intf_desc.bNumEndpoints = i; - fsg_intf_desc.bInterfaceSubClass = mod_data.protocol_type; - fsg_intf_desc.bInterfaceProtocol = mod_data.transport_type; - fsg_fs_function[i + FSG_FS_FUNCTION_PRE_EP_ENTRIES] = NULL; - - if (gadget_is_dualspeed(gadget)) { - fsg_hs_function[i + FSG_HS_FUNCTION_PRE_EP_ENTRIES] = NULL; - - /* Assume endpoint addresses are the same for both speeds */ - fsg_hs_bulk_in_desc.bEndpointAddress = - fsg_fs_bulk_in_desc.bEndpointAddress; - fsg_hs_bulk_out_desc.bEndpointAddress = - fsg_fs_bulk_out_desc.bEndpointAddress; - fsg_hs_intr_in_desc.bEndpointAddress = - fsg_fs_intr_in_desc.bEndpointAddress; - } - - if (gadget_is_superspeed(gadget)) { - unsigned max_burst; - - fsg_ss_function[i + FSG_SS_FUNCTION_PRE_EP_ENTRIES] = NULL; - - /* Calculate bMaxBurst, we know packet size is 1024 */ - max_burst = min_t(unsigned, mod_data.buflen / 1024, 15); - - /* Assume endpoint addresses are the same for both speeds */ - fsg_ss_bulk_in_desc.bEndpointAddress = - fsg_fs_bulk_in_desc.bEndpointAddress; - fsg_ss_bulk_in_comp_desc.bMaxBurst = max_burst; - - fsg_ss_bulk_out_desc.bEndpointAddress = - fsg_fs_bulk_out_desc.bEndpointAddress; - fsg_ss_bulk_out_comp_desc.bMaxBurst = max_burst; - } - - if (gadget_is_otg(gadget)) - fsg_otg_desc.bmAttributes |= USB_OTG_HNP; - - rc = -ENOMEM; - - /* Allocate the request and buffer for endpoint 0 */ - fsg->ep0req = req = usb_ep_alloc_request(fsg->ep0, GFP_KERNEL); - if (!req) - goto out; - req->buf = kmalloc(EP0_BUFSIZE, GFP_KERNEL); - if (!req->buf) - goto out; - req->complete = ep0_complete; - - /* Allocate the data buffers */ - for (i = 0; i < fsg_num_buffers; ++i) { - struct fsg_buffhd *bh = &fsg->buffhds[i]; - - /* Allocate for the bulk-in endpoint. We assume that - * the buffer will also work with the bulk-out (and - * interrupt-in) endpoint. */ - bh->buf = kmalloc(mod_data.buflen, GFP_KERNEL); - if (!bh->buf) - goto out; - bh->next = bh + 1; - } - fsg->buffhds[fsg_num_buffers - 1].next = &fsg->buffhds[0]; - - /* This should reflect the actual gadget power source */ - usb_gadget_set_selfpowered(gadget); - - snprintf(fsg_string_manufacturer, sizeof fsg_string_manufacturer, - "%s %s with %s", - init_utsname()->sysname, init_utsname()->release, - gadget->name); - - fsg->thread_task = kthread_create(fsg_main_thread, fsg, - "file-storage-gadget"); - if (IS_ERR(fsg->thread_task)) { - rc = PTR_ERR(fsg->thread_task); - goto out; - } - - INFO(fsg, DRIVER_DESC ", version: " DRIVER_VERSION "\n"); - INFO(fsg, "NOTE: This driver is deprecated. " - "Consider using g_mass_storage instead.\n"); - INFO(fsg, "Number of LUNs=%d\n", fsg->nluns); - - pathbuf = kmalloc(PATH_MAX, GFP_KERNEL); - for (i = 0; i < fsg->nluns; ++i) { - curlun = &fsg->luns[i]; - if (fsg_lun_is_open(curlun)) { - p = NULL; - if (pathbuf) { - p = d_path(&curlun->filp->f_path, - pathbuf, PATH_MAX); - if (IS_ERR(p)) - p = NULL; - } - LINFO(curlun, "ro=%d, nofua=%d, file: %s\n", - curlun->ro, curlun->nofua, (p ? p : "(error)")); - } - } - kfree(pathbuf); - - DBG(fsg, "transport=%s (x%02x)\n", - mod_data.transport_name, mod_data.transport_type); - DBG(fsg, "protocol=%s (x%02x)\n", - mod_data.protocol_name, mod_data.protocol_type); - DBG(fsg, "VendorID=x%04x, ProductID=x%04x, Release=x%04x\n", - mod_data.vendor, mod_data.product, mod_data.release); - DBG(fsg, "removable=%d, stall=%d, cdrom=%d, buflen=%u\n", - mod_data.removable, mod_data.can_stall, - mod_data.cdrom, mod_data.buflen); - DBG(fsg, "I/O thread pid: %d\n", task_pid_nr(fsg->thread_task)); - - set_bit(REGISTERED, &fsg->atomic_bitflags); - - /* Tell the thread to start working */ - wake_up_process(fsg->thread_task); - return 0; - -autoconf_fail: - ERROR(fsg, "unable to autoconfigure all endpoints\n"); - rc = -ENOTSUPP; - -out: - fsg->state = FSG_STATE_TERMINATED; // The thread is dead - fsg_unbind(gadget); - complete(&fsg->thread_notifier); - return rc; -} - - -/*-------------------------------------------------------------------------*/ - -static void fsg_suspend(struct usb_gadget *gadget) -{ - struct fsg_dev *fsg = get_gadget_data(gadget); - - DBG(fsg, "suspend\n"); - set_bit(SUSPENDED, &fsg->atomic_bitflags); -} - -static void fsg_resume(struct usb_gadget *gadget) -{ - struct fsg_dev *fsg = get_gadget_data(gadget); - - DBG(fsg, "resume\n"); - clear_bit(SUSPENDED, &fsg->atomic_bitflags); -} - - -/*-------------------------------------------------------------------------*/ - -static __refdata struct usb_gadget_driver fsg_driver = { - .max_speed = USB_SPEED_SUPER, - .function = (char *) fsg_string_product, - .bind = fsg_bind, - .unbind = fsg_unbind, - .disconnect = fsg_disconnect, - .setup = fsg_setup, - .suspend = fsg_suspend, - .resume = fsg_resume, - - .driver = { - .name = DRIVER_NAME, - .owner = THIS_MODULE, - // .release = ... - // .suspend = ... - // .resume = ... - }, -}; - - -static int __init fsg_alloc(void) -{ - struct fsg_dev *fsg; - - fsg = kzalloc(sizeof *fsg + - fsg_num_buffers * sizeof *(fsg->buffhds), GFP_KERNEL); - - if (!fsg) - return -ENOMEM; - spin_lock_init(&fsg->lock); - init_rwsem(&fsg->filesem); - kref_init(&fsg->ref); - init_completion(&fsg->thread_notifier); - - the_fsg = fsg; - return 0; -} - - -static int __init fsg_init(void) -{ - int rc; - struct fsg_dev *fsg; - - rc = fsg_num_buffers_validate(); - if (rc != 0) - return rc; - - if ((rc = fsg_alloc()) != 0) - return rc; - fsg = the_fsg; - rc = usb_gadget_probe_driver(&fsg_driver); - if (rc != 0) - kref_put(&fsg->ref, fsg_release); - return rc; -} -module_init(fsg_init); - - -static void __exit fsg_cleanup(void) -{ - struct fsg_dev *fsg = the_fsg; - - /* Unregister the driver iff the thread hasn't already done so */ - if (test_and_clear_bit(REGISTERED, &fsg->atomic_bitflags)) - usb_gadget_unregister_driver(&fsg_driver); - - /* Wait for the thread to finish up */ - wait_for_completion(&fsg->thread_notifier); - - kref_put(&fsg->ref, fsg_release); -} -module_exit(fsg_cleanup); diff --git a/drivers/usb/gadget/net2280.c b/drivers/usb/gadget/net2280.c index ac335af154ba..708c0b55dcc8 100644 --- a/drivers/usb/gadget/net2280.c +++ b/drivers/usb/gadget/net2280.c @@ -9,7 +9,7 @@ * CODE STATUS HIGHLIGHTS * * This driver should work well with most "gadget" drivers, including - * the File Storage, Serial, and Ethernet/RNDIS gadget drivers + * the Mass Storage, Serial, and Ethernet/RNDIS gadget drivers * as well as Gadget Zero and Gadgetfs. * * DMA is enabled by default. Drivers using transfer queues might use diff --git a/drivers/usb/gadget/pxa27x_udc.h b/drivers/usb/gadget/pxa27x_udc.h index a1d268c6f2cc..79d81a4b2344 100644 --- a/drivers/usb/gadget/pxa27x_udc.h +++ b/drivers/usb/gadget/pxa27x_udc.h @@ -418,7 +418,7 @@ struct udc_stats { * @irq: udc irq * @clk: udc clock * @usb_gadget: udc gadget structure - * @driver: bound gadget (zero, g_ether, g_file_storage, ...) + * @driver: bound gadget (zero, g_ether, g_mass_storage, ...) * @dev: device * @mach: machine info, used to activate specific GPIO * @transceiver: external transceiver to handle vbus sense and D+ pullup -- cgit v1.2.3-59-g8ed1b From 57d34a6cee1399bfedaa73add1915951cbe75cab Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Fri, 19 Oct 2012 09:48:30 -0700 Subject: rcu: Update docs to include kfree_rcu() Mention kfree_rcu() in the call_rcu() section. Additionally fix the example code for list replacement that used the wrong structure element. Signed-off-by: Kees Cook Signed-off-by: Paul E. McKenney --- Documentation/RCU/listRCU.txt | 2 +- Documentation/RCU/whatisRCU.txt | 13 +++++++++++-- 2 files changed, 12 insertions(+), 3 deletions(-) (limited to 'Documentation') diff --git a/Documentation/RCU/listRCU.txt b/Documentation/RCU/listRCU.txt index 4349c1487e91..adb5a3782846 100644 --- a/Documentation/RCU/listRCU.txt +++ b/Documentation/RCU/listRCU.txt @@ -205,7 +205,7 @@ RCU ("read-copy update") its name. The RCU code is as follows: audit_copy_rule(&ne->rule, &e->rule); ne->rule.action = newaction; ne->rule.file_count = newfield_count; - list_replace_rcu(e, ne); + list_replace_rcu(&e->list, &ne->list); call_rcu(&e->rcu, audit_free_rule); return 0; } diff --git a/Documentation/RCU/whatisRCU.txt b/Documentation/RCU/whatisRCU.txt index bf0f6de2aa00..160ac5557e97 100644 --- a/Documentation/RCU/whatisRCU.txt +++ b/Documentation/RCU/whatisRCU.txt @@ -499,6 +499,8 @@ The foo_reclaim() function might appear as follows: { struct foo *fp = container_of(rp, struct foo, rcu); + foo_cleanup(fp->a); + kfree(fp); } @@ -521,6 +523,12 @@ o Use call_rcu() -after- removing a data element from an read-side critical sections that might be referencing that data item. +If the callback for call_rcu() is not doing anything more than calling +kfree() on the structure, you can use kfree_rcu() instead of call_rcu() +to avoid having to write your own callback: + + kfree_rcu(old_fp, rcu); + Again, see checklist.txt for additional rules governing the use of RCU. @@ -773,8 +781,8 @@ a single atomic update, converting to RCU will require special care. Also, the presence of synchronize_rcu() means that the RCU version of delete() can now block. If this is a problem, there is a callback-based -mechanism that never blocks, namely call_rcu(), that can be used in -place of synchronize_rcu(). +mechanism that never blocks, namely call_rcu() or kfree_rcu(), that can +be used in place of synchronize_rcu(). 7. FULL LIST OF RCU APIs @@ -813,6 +821,7 @@ RCU: Critical sections Grace period Barrier rcu_read_unlock synchronize_rcu rcu_dereference synchronize_rcu_expedited call_rcu + kfree_rcu bh: Critical sections Grace period Barrier -- cgit v1.2.3-59-g8ed1b From a4d611fdca0d696f9b8ffb007a119944ed5275fa Mon Sep 17 00:00:00 2001 From: "Paul E. McKenney" Date: Sat, 27 Oct 2012 16:34:51 -0700 Subject: rcu: Document alternative RCU/reference-count algorithms The approach for mixing RCU and reference counting listed in the RCU documentation only describes one possible approach. This approach can result in failure on the read side, which is nice if you want fresh data, but not so good if you want simple code. This commit therefore adds two additional approaches that feature unconditional reference-count acquisition by RCU readers. These approaches are very similar to that used in the security code. Signed-off-by: Paul E. McKenney --- Documentation/RCU/rcuref.txt | 61 ++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 59 insertions(+), 2 deletions(-) (limited to 'Documentation') diff --git a/Documentation/RCU/rcuref.txt b/Documentation/RCU/rcuref.txt index 4202ad093130..141d531aa14b 100644 --- a/Documentation/RCU/rcuref.txt +++ b/Documentation/RCU/rcuref.txt @@ -20,7 +20,7 @@ release_referenced() delete() { { ... write_lock(&list_lock); atomic_dec(&el->rc, relfunc) ... - ... delete_element + ... remove_element } write_unlock(&list_lock); ... if (atomic_dec_and_test(&el->rc)) @@ -52,7 +52,7 @@ release_referenced() delete() { { ... spin_lock(&list_lock); if (atomic_dec_and_test(&el->rc)) ... - call_rcu(&el->head, el_free); delete_element + call_rcu(&el->head, el_free); remove_element ... spin_unlock(&list_lock); } ... if (atomic_dec_and_test(&el->rc)) @@ -64,3 +64,60 @@ Sometimes, a reference to the element needs to be obtained in the update (write) stream. In such cases, atomic_inc_not_zero() might be overkill, since we hold the update-side spinlock. One might instead use atomic_inc() in such cases. + +It is not always convenient to deal with "FAIL" in the +search_and_reference() code path. In such cases, the +atomic_dec_and_test() may be moved from delete() to el_free() +as follows: + +1. 2. +add() search_and_reference() +{ { + alloc_object rcu_read_lock(); + ... search_for_element + atomic_set(&el->rc, 1); atomic_inc(&el->rc); + spin_lock(&list_lock); ... + + add_element rcu_read_unlock(); + ... } + spin_unlock(&list_lock); 4. +} delete() +3. { +release_referenced() spin_lock(&list_lock); +{ ... + ... remove_element + if (atomic_dec_and_test(&el->rc)) spin_unlock(&list_lock); + kfree(el); ... + ... call_rcu(&el->head, el_free); +} ... +5. } +void el_free(struct rcu_head *rhp) +{ + release_referenced(); +} + +The key point is that the initial reference added by add() is not removed +until after a grace period has elapsed following removal. This means that +search_and_reference() cannot find this element, which means that the value +of el->rc cannot increase. Thus, once it reaches zero, there are no +readers that can or ever will be able to reference the element. The +element can therefore safely be freed. This in turn guarantees that if +any reader finds the element, that reader may safely acquire a reference +without checking the value of the reference counter. + +In cases where delete() can sleep, synchronize_rcu() can be called from +delete(), so that el_free() can be subsumed into delete as follows: + +4. +delete() +{ + spin_lock(&list_lock); + ... + remove_element + spin_unlock(&list_lock); + ... + synchronize_rcu(); + if (atomic_dec_and_test(&el->rc)) + kfree(el); + ... +} -- cgit v1.2.3-59-g8ed1b From d928cd2ef8f7f4194e479d4a66452901ec82ccda Mon Sep 17 00:00:00 2001 From: Afzal Mohammed Date: Tue, 6 Nov 2012 20:47:35 +0530 Subject: usb: musb: dsps: document dt bindings properly DT bindings normally use '-' (hyphens) instead of '_' (underscore), driver has it the proper way, but binding documentation does not reflect it, fix it. Signed-off-by: Afzal Mohammed Signed-off-by: Felipe Balbi --- Documentation/devicetree/bindings/usb/am33xx-usb.txt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/usb/am33xx-usb.txt b/Documentation/devicetree/bindings/usb/am33xx-usb.txt index ca8fa56e9f03..a92250512a4e 100644 --- a/Documentation/devicetree/bindings/usb/am33xx-usb.txt +++ b/Documentation/devicetree/bindings/usb/am33xx-usb.txt @@ -3,12 +3,12 @@ AM33XX MUSB GLUE - ti,hwmods : must be "usb_otg_hs" - multipoint : Should be "1" indicating the musb controller supports multipoint. This is a MUSB configuration-specific setting. - - num_eps : Specifies the number of endpoints. This is also a + - num-eps : Specifies the number of endpoints. This is also a MUSB configuration-specific setting. Should be set to "16" - - ram_bits : Specifies the ram address size. Should be set to "12" - - port0_mode : Should be "3" to represent OTG. "1" signifies HOST and "2" + - ram-bits : Specifies the ram address size. Should be set to "12" + - port0-mode : Should be "3" to represent OTG. "1" signifies HOST and "2" represents PERIPHERAL. - - port1_mode : Should be "1" to represent HOST. "3" signifies OTG and "2" + - port1-mode : Should be "1" to represent HOST. "3" signifies OTG and "2" represents PERIPHERAL. - power : Should be "250". This signifies the controller can supply upto 500mA when operating in host mode. -- cgit v1.2.3-59-g8ed1b From 560615ef8b95bb9aa4ea77ccaf3b2114dc93206f Mon Sep 17 00:00:00 2001 From: Qing Xu Date: Thu, 8 Nov 2012 10:31:32 +0800 Subject: regulator: max8925: update dt regulator binding doc remove linux specific references, enumerates all supported regulators Signed-off-by: Qing Xu Signed-off-by: Mark Brown --- .../bindings/regulator/max8925-regulator.txt | 51 +++++++++++++--------- 1 file changed, 31 insertions(+), 20 deletions(-) (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/regulator/max8925-regulator.txt b/Documentation/devicetree/bindings/regulator/max8925-regulator.txt index 073b41d90f2c..0057695aae8f 100644 --- a/Documentation/devicetree/bindings/regulator/max8925-regulator.txt +++ b/Documentation/devicetree/bindings/regulator/max8925-regulator.txt @@ -1,29 +1,40 @@ Max8925 Voltage regulators -max8925 regulator device register is still handled by mfd_add_devices, not by -of_xxx, so, it is not necessary to add compatible name. Also, those reg -offset and id info is stored in mfd_cell(see max8925-core.c), as a result -there is not private properties in dts. - -node's name should match with the definition in max8925_regulator_matches -(see max8925-regulator.c) - +Required nodes: +-nodes: + - SDV1 for SDV SDV1 + - SDV2 for SDV SDV2 + - SDV3 for SDV SDV3 + - LDO1 for LDO LDO1 + - LDO2 for LDO LDO2 + - LDO3 for LDO LDO3 + - LDO4 for LDO LDO4 + - LDO5 for LDO LDO5 + - LDO6 for LDO LDO6 + - LDO7 for LDO LDO7 + - LDO8 for LDO LDO8 + - LDO9 for LDO LDO9 + - LDO10 for LDO LDO10 + - LDO11 for LDO LDO11 + - LDO12 for LDO LDO12 + - LDO13 for LDO LDO13 + - LDO14 for LDO LDO14 + - LDO15 for LDO LDO15 + - LDO16 for LDO LDO16 + - LDO17 for LDO LDO17 + - LDO18 for LDO LDO18 + - LDO19 for LDO LDO19 + - LDO20 for LDO LDO20 Optional properties: - Any optional property defined in bindings/regulator/regulator.txt - Example: + SDV1 { + regulator-min-microvolt = <637500>; + regulator-max-microvolt = <1425000>; + regulator-boot-on; + regulator-always-on; + }; - regulators { - SDV1 { - regulator-min-microvolt = <637500>; - regulator-max-microvolt = <1425000>; - regulator-boot-on; - regulator-always-on; - }; - - ... - ... - } -- cgit v1.2.3-59-g8ed1b From 246da26d37311cd1b1489575f305042dcdecfd50 Mon Sep 17 00:00:00 2001 From: Afzal Mohammed Date: Thu, 2 Aug 2012 20:02:10 +0530 Subject: ARM: OMAP2+: gpmc: generic timing calculation Presently there are three peripherals that gets it timing by runtime calculation. Those peripherals can work with frequency scaling that affects gpmc clock. But timing calculation for them are in different ways. Here a generic runtime calculation method is proposed. Input to this function were selected so that they represent timing variables that are present in peripheral datasheets. Motive behind this was to achieve DT bindings for the inputs as is. Even though a few of the tusb6010 timings could not be made directly related to timings normally found on peripherals, expressions used were translated to those that could be justified. There are possibilities of improving the calculations, like calculating timing for read & write operations in a more similar way. Expressions derived here were tested for async onenand on omap3evm (as vanilla Kernel does not have omap3evm onenand support, local patch was used). Other peripherals, tusb6010, smc91x calculations were validated by simulating on omap3evm. Regarding "we_on" for onenand async, it was found that even for muxed address/data, it need not be greater than "adv_wr_off", but rather could be derived from write setup time for peripheral from start of access time, hence would more be in line with peripheral timings. With this method it was working fine. If it is required in some cases to have "we_on" same as "wr_data_mux_bus" (i.e. greater than "adv_wr_off"), another variable could be added to indicate it. But such a requirement is not expected though. It has been observed that "adv_rd_off" & "adv_wr_off" are currently calculated by adding an offset over "oe_on" and "we_on" respectively in the case of smc91x. But peripheral datasheet does not specify so and so "adv_rd(wr)_off" has been derived (to be specific, made ignorant of "oe_on" and "we_on") observing datasheet rather than adding an offset. Hence this generic routine is expected to work for smc91x (91C96 RX51 board). This was verified on smsc911x (9220 on OMAP3EVM) - a similar ethernet controller. Timings are calculated in ps to prevent rounding errors and converted to ns at final stage so that these values can be directly fed to gpmc_cs_set_timings(). gpmc_cs_set_timings() would be modified to take ps once all custom timing routines are replaced by the generic routine, at the same time generic timing routine would be modified to provide timings in ps. struct gpmc_timings field types are upgraded from u16 => u32 so that it can hold ps values. Whole of this exercise is being done to achieve driver and DT conversion. If timings could not be calculated in a peripheral agnostic way, either gpmc driver would have to be peripheral gnostic or a wrapper arrangement over gpmc driver would be required. Signed-off-by: Afzal Mohammed --- Documentation/bus-devices/ti-gpmc.txt | 122 +++++++++++++ arch/arm/mach-omap2/gpmc.c | 325 ++++++++++++++++++++++++++++++++++ arch/arm/mach-omap2/gpmc.h | 102 ++++++++--- 3 files changed, 529 insertions(+), 20 deletions(-) create mode 100644 Documentation/bus-devices/ti-gpmc.txt (limited to 'Documentation') diff --git a/Documentation/bus-devices/ti-gpmc.txt b/Documentation/bus-devices/ti-gpmc.txt new file mode 100644 index 000000000000..cc9ce57e0a26 --- /dev/null +++ b/Documentation/bus-devices/ti-gpmc.txt @@ -0,0 +1,122 @@ +GPMC (General Purpose Memory Controller): +========================================= + +GPMC is an unified memory controller dedicated to interfacing external +memory devices like + * Asynchronous SRAM like memories and application specific integrated + circuit devices. + * Asynchronous, synchronous, and page mode burst NOR flash devices + NAND flash + * Pseudo-SRAM devices + +GPMC is found on Texas Instruments SoC's (OMAP based) +IP details: http://www.ti.com/lit/pdf/spruh73 section 7.1 + + +GPMC generic timing calculation: +================================ + +GPMC has certain timings that has to be programmed for proper +functioning of the peripheral, while peripheral has another set of +timings. To have peripheral work with gpmc, peripheral timings has to +be translated to the form gpmc can understand. The way it has to be +translated depends on the connected peripheral. Also there is a +dependency for certain gpmc timings on gpmc clock frequency. Hence a +generic timing routine was developed to achieve above requirements. + +Generic routine provides a generic method to calculate gpmc timings +from gpmc peripheral timings. struct gpmc_device_timings fields has to +be updated with timings from the datasheet of the peripheral that is +connected to gpmc. A few of the peripheral timings can be fed either +in time or in cycles, provision to handle this scenario has been +provided (refer struct gpmc_device_timings definition). It may so +happen that timing as specified by peripheral datasheet is not present +in timing structure, in this scenario, try to correlate peripheral +timing to the one available. If that doesn't work, try to add a new +field as required by peripheral, educate generic timing routine to +handle it, make sure that it does not break any of the existing. +Then there may be cases where peripheral datasheet doesn't mention +certain fields of struct gpmc_device_timings, zero those entries. + +Generic timing routine has been verified to work properly on +multiple onenand's and tusb6010 peripherals. + +A word of caution: generic timing routine has been developed based +on understanding of gpmc timings, peripheral timings, available +custom timing routines, a kind of reverse engineering without +most of the datasheets & hardware (to be exact none of those supported +in mainline having custom timing routine) and by simulation. + +gpmc timing dependency on peripheral timings: +[: , ...] + +1. common +cs_on: t_ceasu +adv_on: t_avdasu, t_ceavd + +2. sync common +sync_clk: clk +page_burst_access: t_bacc +clk_activation: t_ces, t_avds + +3. read async muxed +adv_rd_off: t_avdp_r +oe_on: t_oeasu, t_aavdh +access: t_iaa, t_oe, t_ce, t_aa +rd_cycle: t_rd_cycle, t_cez_r, t_oez + +4. read async non-muxed +adv_rd_off: t_avdp_r +oe_on: t_oeasu +access: t_iaa, t_oe, t_ce, t_aa +rd_cycle: t_rd_cycle, t_cez_r, t_oez + +5. read sync muxed +adv_rd_off: t_avdp_r, t_avdh +oe_on: t_oeasu, t_ach, cyc_aavdh_oe +access: t_iaa, cyc_iaa, cyc_oe +rd_cycle: t_cez_r, t_oez, t_ce_rdyz + +6. read sync non-muxed +adv_rd_off: t_avdp_r +oe_on: t_oeasu +access: t_iaa, cyc_iaa, cyc_oe +rd_cycle: t_cez_r, t_oez, t_ce_rdyz + +7. write async muxed +adv_wr_off: t_avdp_w +we_on, wr_data_mux_bus: t_weasu, t_aavdh, cyc_aavhd_we +we_off: t_wpl +cs_wr_off: t_wph +wr_cycle: t_cez_w, t_wr_cycle + +8. write async non-muxed +adv_wr_off: t_avdp_w +we_on, wr_data_mux_bus: t_weasu +we_off: t_wpl +cs_wr_off: t_wph +wr_cycle: t_cez_w, t_wr_cycle + +9. write sync muxed +adv_wr_off: t_avdp_w, t_avdh +we_on, wr_data_mux_bus: t_weasu, t_rdyo, t_aavdh, cyc_aavhd_we +we_off: t_wpl, cyc_wpl +cs_wr_off: t_wph +wr_cycle: t_cez_w, t_ce_rdyz + +10. write sync non-muxed +adv_wr_off: t_avdp_w +we_on, wr_data_mux_bus: t_weasu, t_rdyo +we_off: t_wpl, cyc_wpl +cs_wr_off: t_wph +wr_cycle: t_cez_w, t_ce_rdyz + + +Note: Many of gpmc timings are dependent on other gpmc timings (a few +gpmc timings purely dependent on other gpmc timings, a reason that +some of the gpmc timings are missing above), and it will result in +indirect dependency of peripheral timings to gpmc timings other than +mentioned above, refer timing routine for more details. To know what +these peripheral timings correspond to, please see explanations in +struct gpmc_device_timings definition. And for gpmc timings refer +IP details (link above). diff --git a/arch/arm/mach-omap2/gpmc.c b/arch/arm/mach-omap2/gpmc.c index 5619d1b48668..65468f6d7f0e 100644 --- a/arch/arm/mach-omap2/gpmc.c +++ b/arch/arm/mach-omap2/gpmc.c @@ -230,6 +230,18 @@ unsigned int gpmc_round_ns_to_ticks(unsigned int time_ns) return ticks * gpmc_get_fclk_period() / 1000; } +static unsigned int gpmc_ticks_to_ps(unsigned int ticks) +{ + return ticks * gpmc_get_fclk_period(); +} + +static unsigned int gpmc_round_ps_to_ticks(unsigned int time_ps) +{ + unsigned long ticks = gpmc_ps_to_ticks(time_ps); + + return ticks * gpmc_get_fclk_period(); +} + static inline void gpmc_cs_modify_reg(int cs, int reg, u32 mask, bool value) { u32 l; @@ -796,6 +808,319 @@ static int __devinit gpmc_mem_init(void) return 0; } +static u32 gpmc_round_ps_to_sync_clk(u32 time_ps, u32 sync_clk) +{ + u32 temp; + int div; + + div = gpmc_calc_divider(sync_clk); + temp = gpmc_ps_to_ticks(time_ps); + temp = (temp + div - 1) / div; + return gpmc_ticks_to_ps(temp * div); +} + +/* XXX: can the cycles be avoided ? */ +static int gpmc_calc_sync_read_timings(struct gpmc_timings *gpmc_t, + struct gpmc_device_timings *dev_t) +{ + bool mux = dev_t->mux; + u32 temp; + + /* adv_rd_off */ + temp = dev_t->t_avdp_r; + /* XXX: mux check required ? */ + if (mux) { + /* XXX: t_avdp not to be required for sync, only added for tusb + * this indirectly necessitates requirement of t_avdp_r and + * t_avdp_w instead of having a single t_avdp + */ + temp = max_t(u32, temp, gpmc_t->clk_activation + dev_t->t_avdh); + temp = max_t(u32, gpmc_t->adv_on + gpmc_ticks_to_ps(1), temp); + } + gpmc_t->adv_rd_off = gpmc_round_ps_to_ticks(temp); + + /* oe_on */ + temp = dev_t->t_oeasu; /* XXX: remove this ? */ + if (mux) { + temp = max_t(u32, temp, gpmc_t->clk_activation + dev_t->t_ach); + temp = max_t(u32, temp, gpmc_t->adv_rd_off + + gpmc_ticks_to_ps(dev_t->cyc_aavdh_oe)); + } + gpmc_t->oe_on = gpmc_round_ps_to_ticks(temp); + + /* access */ + /* XXX: any scope for improvement ?, by combining oe_on + * and clk_activation, need to check whether + * access = clk_activation + round to sync clk ? + */ + temp = max_t(u32, dev_t->t_iaa, dev_t->cyc_iaa * gpmc_t->sync_clk); + temp += gpmc_t->clk_activation; + if (dev_t->cyc_oe) + temp = max_t(u32, temp, gpmc_t->oe_on + + gpmc_ticks_to_ps(dev_t->cyc_oe)); + gpmc_t->access = gpmc_round_ps_to_ticks(temp); + + gpmc_t->oe_off = gpmc_t->access + gpmc_ticks_to_ps(1); + gpmc_t->cs_rd_off = gpmc_t->oe_off; + + /* rd_cycle */ + temp = max_t(u32, dev_t->t_cez_r, dev_t->t_oez); + temp = gpmc_round_ps_to_sync_clk(temp, gpmc_t->sync_clk) + + gpmc_t->access; + /* XXX: barter t_ce_rdyz with t_cez_r ? */ + if (dev_t->t_ce_rdyz) + temp = max_t(u32, temp, gpmc_t->cs_rd_off + dev_t->t_ce_rdyz); + gpmc_t->rd_cycle = gpmc_round_ps_to_ticks(temp); + + return 0; +} + +static int gpmc_calc_sync_write_timings(struct gpmc_timings *gpmc_t, + struct gpmc_device_timings *dev_t) +{ + bool mux = dev_t->mux; + u32 temp; + + /* adv_wr_off */ + temp = dev_t->t_avdp_w; + if (mux) { + temp = max_t(u32, temp, + gpmc_t->clk_activation + dev_t->t_avdh); + temp = max_t(u32, gpmc_t->adv_on + gpmc_ticks_to_ps(1), temp); + } + gpmc_t->adv_wr_off = gpmc_round_ps_to_ticks(temp); + + /* wr_data_mux_bus */ + temp = max_t(u32, dev_t->t_weasu, + gpmc_t->clk_activation + dev_t->t_rdyo); + /* XXX: shouldn't mux be kept as a whole for wr_data_mux_bus ?, + * and in that case remember to handle we_on properly + */ + if (mux) { + temp = max_t(u32, temp, + gpmc_t->adv_wr_off + dev_t->t_aavdh); + temp = max_t(u32, temp, gpmc_t->adv_wr_off + + gpmc_ticks_to_ps(dev_t->cyc_aavdh_we)); + } + gpmc_t->wr_data_mux_bus = gpmc_round_ps_to_ticks(temp); + + /* we_on */ + if (gpmc_capability & GPMC_HAS_WR_DATA_MUX_BUS) + gpmc_t->we_on = gpmc_round_ps_to_ticks(dev_t->t_weasu); + else + gpmc_t->we_on = gpmc_t->wr_data_mux_bus; + + /* wr_access */ + /* XXX: gpmc_capability check reqd ? , even if not, will not harm */ + gpmc_t->wr_access = gpmc_t->access; + + /* we_off */ + temp = gpmc_t->we_on + dev_t->t_wpl; + temp = max_t(u32, temp, + gpmc_t->wr_access + gpmc_ticks_to_ps(1)); + temp = max_t(u32, temp, + gpmc_t->we_on + gpmc_ticks_to_ps(dev_t->cyc_wpl)); + gpmc_t->we_off = gpmc_round_ps_to_ticks(temp); + + gpmc_t->cs_wr_off = gpmc_round_ps_to_ticks(gpmc_t->we_off + + dev_t->t_wph); + + /* wr_cycle */ + temp = gpmc_round_ps_to_sync_clk(dev_t->t_cez_w, gpmc_t->sync_clk); + temp += gpmc_t->wr_access; + /* XXX: barter t_ce_rdyz with t_cez_w ? */ + if (dev_t->t_ce_rdyz) + temp = max_t(u32, temp, + gpmc_t->cs_wr_off + dev_t->t_ce_rdyz); + gpmc_t->wr_cycle = gpmc_round_ps_to_ticks(temp); + + return 0; +} + +static int gpmc_calc_async_read_timings(struct gpmc_timings *gpmc_t, + struct gpmc_device_timings *dev_t) +{ + bool mux = dev_t->mux; + u32 temp; + + /* adv_rd_off */ + temp = dev_t->t_avdp_r; + if (mux) + temp = max_t(u32, gpmc_t->adv_on + gpmc_ticks_to_ps(1), temp); + gpmc_t->adv_rd_off = gpmc_round_ps_to_ticks(temp); + + /* oe_on */ + temp = dev_t->t_oeasu; + if (mux) + temp = max_t(u32, temp, + gpmc_t->adv_rd_off + dev_t->t_aavdh); + gpmc_t->oe_on = gpmc_round_ps_to_ticks(temp); + + /* access */ + temp = max_t(u32, dev_t->t_iaa, /* XXX: remove t_iaa in async ? */ + gpmc_t->oe_on + dev_t->t_oe); + temp = max_t(u32, temp, + gpmc_t->cs_on + dev_t->t_ce); + temp = max_t(u32, temp, + gpmc_t->adv_on + dev_t->t_aa); + gpmc_t->access = gpmc_round_ps_to_ticks(temp); + + gpmc_t->oe_off = gpmc_t->access + gpmc_ticks_to_ps(1); + gpmc_t->cs_rd_off = gpmc_t->oe_off; + + /* rd_cycle */ + temp = max_t(u32, dev_t->t_rd_cycle, + gpmc_t->cs_rd_off + dev_t->t_cez_r); + temp = max_t(u32, temp, gpmc_t->oe_off + dev_t->t_oez); + gpmc_t->rd_cycle = gpmc_round_ps_to_ticks(temp); + + return 0; +} + +static int gpmc_calc_async_write_timings(struct gpmc_timings *gpmc_t, + struct gpmc_device_timings *dev_t) +{ + bool mux = dev_t->mux; + u32 temp; + + /* adv_wr_off */ + temp = dev_t->t_avdp_w; + if (mux) + temp = max_t(u32, gpmc_t->adv_on + gpmc_ticks_to_ps(1), temp); + gpmc_t->adv_wr_off = gpmc_round_ps_to_ticks(temp); + + /* wr_data_mux_bus */ + temp = dev_t->t_weasu; + if (mux) { + temp = max_t(u32, temp, gpmc_t->adv_wr_off + dev_t->t_aavdh); + temp = max_t(u32, temp, gpmc_t->adv_wr_off + + gpmc_ticks_to_ps(dev_t->cyc_aavdh_we)); + } + gpmc_t->wr_data_mux_bus = gpmc_round_ps_to_ticks(temp); + + /* we_on */ + if (gpmc_capability & GPMC_HAS_WR_DATA_MUX_BUS) + gpmc_t->we_on = gpmc_round_ps_to_ticks(dev_t->t_weasu); + else + gpmc_t->we_on = gpmc_t->wr_data_mux_bus; + + /* we_off */ + temp = gpmc_t->we_on + dev_t->t_wpl; + gpmc_t->we_off = gpmc_round_ps_to_ticks(temp); + + gpmc_t->cs_wr_off = gpmc_round_ps_to_ticks(gpmc_t->we_off + + dev_t->t_wph); + + /* wr_cycle */ + temp = max_t(u32, dev_t->t_wr_cycle, + gpmc_t->cs_wr_off + dev_t->t_cez_w); + gpmc_t->wr_cycle = gpmc_round_ps_to_ticks(temp); + + return 0; +} + +static int gpmc_calc_sync_common_timings(struct gpmc_timings *gpmc_t, + struct gpmc_device_timings *dev_t) +{ + u32 temp; + + gpmc_t->sync_clk = gpmc_calc_divider(dev_t->clk) * + gpmc_get_fclk_period(); + + gpmc_t->page_burst_access = gpmc_round_ps_to_sync_clk( + dev_t->t_bacc, + gpmc_t->sync_clk); + + temp = max_t(u32, dev_t->t_ces, dev_t->t_avds); + gpmc_t->clk_activation = gpmc_round_ps_to_ticks(temp); + + if (gpmc_calc_divider(gpmc_t->sync_clk) != 1) + return 0; + + if (dev_t->ce_xdelay) + gpmc_t->bool_timings.cs_extra_delay = true; + if (dev_t->avd_xdelay) + gpmc_t->bool_timings.adv_extra_delay = true; + if (dev_t->oe_xdelay) + gpmc_t->bool_timings.oe_extra_delay = true; + if (dev_t->we_xdelay) + gpmc_t->bool_timings.we_extra_delay = true; + + return 0; +} + +static int gpmc_calc_common_timings(struct gpmc_timings *gpmc_t, + struct gpmc_device_timings *dev_t) +{ + u32 temp; + + /* cs_on */ + gpmc_t->cs_on = gpmc_round_ps_to_ticks(dev_t->t_ceasu); + + /* adv_on */ + temp = dev_t->t_avdasu; + if (dev_t->t_ce_avd) + temp = max_t(u32, temp, + gpmc_t->cs_on + dev_t->t_ce_avd); + gpmc_t->adv_on = gpmc_round_ps_to_ticks(temp); + + if (dev_t->sync_write || dev_t->sync_read) + gpmc_calc_sync_common_timings(gpmc_t, dev_t); + + return 0; +} + +/* TODO: remove this function once all peripherals are confirmed to + * work with generic timing. Simultaneously gpmc_cs_set_timings() + * has to be modified to handle timings in ps instead of ns +*/ +static void gpmc_convert_ps_to_ns(struct gpmc_timings *t) +{ + t->cs_on /= 1000; + t->cs_rd_off /= 1000; + t->cs_wr_off /= 1000; + t->adv_on /= 1000; + t->adv_rd_off /= 1000; + t->adv_wr_off /= 1000; + t->we_on /= 1000; + t->we_off /= 1000; + t->oe_on /= 1000; + t->oe_off /= 1000; + t->page_burst_access /= 1000; + t->access /= 1000; + t->rd_cycle /= 1000; + t->wr_cycle /= 1000; + t->bus_turnaround /= 1000; + t->cycle2cycle_delay /= 1000; + t->wait_monitoring /= 1000; + t->clk_activation /= 1000; + t->wr_access /= 1000; + t->wr_data_mux_bus /= 1000; +} + +int gpmc_calc_timings(struct gpmc_timings *gpmc_t, + struct gpmc_device_timings *dev_t) +{ + memset(gpmc_t, 0, sizeof(*gpmc_t)); + + gpmc_calc_common_timings(gpmc_t, dev_t); + + if (dev_t->sync_read) + gpmc_calc_sync_read_timings(gpmc_t, dev_t); + else + gpmc_calc_async_read_timings(gpmc_t, dev_t); + + if (dev_t->sync_write) + gpmc_calc_sync_write_timings(gpmc_t, dev_t); + else + gpmc_calc_async_write_timings(gpmc_t, dev_t); + + /* TODO: remove, see function definition */ + gpmc_convert_ps_to_ns(gpmc_t); + + return 0; +} + static __devinit int gpmc_probe(struct platform_device *pdev) { int rc; diff --git a/arch/arm/mach-omap2/gpmc.h b/arch/arm/mach-omap2/gpmc.h index e08a51a7a76a..fe0a844d5007 100644 --- a/arch/arm/mach-omap2/gpmc.h +++ b/arch/arm/mach-omap2/gpmc.h @@ -94,42 +94,104 @@ struct gpmc_timings { u32 sync_clk; /* Chip-select signal timings corresponding to GPMC_CS_CONFIG2 */ - u16 cs_on; /* Assertion time */ - u16 cs_rd_off; /* Read deassertion time */ - u16 cs_wr_off; /* Write deassertion time */ + u32 cs_on; /* Assertion time */ + u32 cs_rd_off; /* Read deassertion time */ + u32 cs_wr_off; /* Write deassertion time */ /* ADV signal timings corresponding to GPMC_CONFIG3 */ - u16 adv_on; /* Assertion time */ - u16 adv_rd_off; /* Read deassertion time */ - u16 adv_wr_off; /* Write deassertion time */ + u32 adv_on; /* Assertion time */ + u32 adv_rd_off; /* Read deassertion time */ + u32 adv_wr_off; /* Write deassertion time */ /* WE signals timings corresponding to GPMC_CONFIG4 */ - u16 we_on; /* WE assertion time */ - u16 we_off; /* WE deassertion time */ + u32 we_on; /* WE assertion time */ + u32 we_off; /* WE deassertion time */ /* OE signals timings corresponding to GPMC_CONFIG4 */ - u16 oe_on; /* OE assertion time */ - u16 oe_off; /* OE deassertion time */ + u32 oe_on; /* OE assertion time */ + u32 oe_off; /* OE deassertion time */ /* Access time and cycle time timings corresponding to GPMC_CONFIG5 */ - u16 page_burst_access; /* Multiple access word delay */ - u16 access; /* Start-cycle to first data valid delay */ - u16 rd_cycle; /* Total read cycle time */ - u16 wr_cycle; /* Total write cycle time */ + u32 page_burst_access; /* Multiple access word delay */ + u32 access; /* Start-cycle to first data valid delay */ + u32 rd_cycle; /* Total read cycle time */ + u32 wr_cycle; /* Total write cycle time */ - u16 bus_turnaround; - u16 cycle2cycle_delay; + u32 bus_turnaround; + u32 cycle2cycle_delay; - u16 wait_monitoring; - u16 clk_activation; + u32 wait_monitoring; + u32 clk_activation; /* The following are only on OMAP3430 */ - u16 wr_access; /* WRACCESSTIME */ - u16 wr_data_mux_bus; /* WRDATAONADMUXBUS */ + u32 wr_access; /* WRACCESSTIME */ + u32 wr_data_mux_bus; /* WRDATAONADMUXBUS */ struct gpmc_bool_timings bool_timings; }; +/* Device timings in picoseconds */ +struct gpmc_device_timings { + u32 t_ceasu; /* address setup to CS valid */ + u32 t_avdasu; /* address setup to ADV valid */ + /* XXX: try to combine t_avdp_r & t_avdp_w. Issue is + * of tusb using these timings even for sync whilst + * ideally for adv_rd/(wr)_off it should have considered + * t_avdh instead. This indirectly necessitates r/w + * variations of t_avdp as it is possible to have one + * sync & other async + */ + u32 t_avdp_r; /* ADV low time (what about t_cer ?) */ + u32 t_avdp_w; + u32 t_aavdh; /* address hold time */ + u32 t_oeasu; /* address setup to OE valid */ + u32 t_aa; /* access time from ADV assertion */ + u32 t_iaa; /* initial access time */ + u32 t_oe; /* access time from OE assertion */ + u32 t_ce; /* access time from CS asertion */ + u32 t_rd_cycle; /* read cycle time */ + u32 t_cez_r; /* read CS deassertion to high Z */ + u32 t_cez_w; /* write CS deassertion to high Z */ + u32 t_oez; /* OE deassertion to high Z */ + u32 t_weasu; /* address setup to WE valid */ + u32 t_wpl; /* write assertion time */ + u32 t_wph; /* write deassertion time */ + u32 t_wr_cycle; /* write cycle time */ + + u32 clk; + u32 t_bacc; /* burst access valid clock to output delay */ + u32 t_ces; /* CS setup time to clk */ + u32 t_avds; /* ADV setup time to clk */ + u32 t_avdh; /* ADV hold time from clk */ + u32 t_ach; /* address hold time from clk */ + u32 t_rdyo; /* clk to ready valid */ + + u32 t_ce_rdyz; /* XXX: description ?, or use t_cez instead */ + u32 t_ce_avd; /* CS on to ADV on delay */ + + /* XXX: check the possibility of combining + * cyc_aavhd_oe & cyc_aavdh_we + */ + u8 cyc_aavdh_oe;/* read address hold time in cycles */ + u8 cyc_aavdh_we;/* write address hold time in cycles */ + u8 cyc_oe; /* access time from OE assertion in cycles */ + u8 cyc_wpl; /* write deassertion time in cycles */ + u32 cyc_iaa; /* initial access time in cycles */ + + bool mux; /* address & data muxed */ + bool sync_write;/* synchronous write */ + bool sync_read; /* synchronous read */ + + /* extra delays */ + bool ce_xdelay; + bool avd_xdelay; + bool oe_xdelay; + bool we_xdelay; +}; + +extern int gpmc_calc_timings(struct gpmc_timings *gpmc_t, + struct gpmc_device_timings *dev_t); + extern void gpmc_update_nand_reg(struct gpmc_nand_regs *reg, int cs); extern int gpmc_get_client_irq(unsigned irq_config); -- cgit v1.2.3-59-g8ed1b From ef9fe980c6fcc1821ab955b74b242d2d6585fa75 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Fri, 9 Nov 2012 09:12:30 -0800 Subject: cgroup_freezer: implement proper hierarchy support Up until now, cgroup_freezer didn't implement hierarchy properly. cgroups could be arranged in hierarchy but it didn't make any difference in how each cgroup_freezer behaved. They all operated separately. This patch implements proper hierarchy support. If a cgroup is frozen, all its descendants are frozen. A cgroup is thawed iff it and all its ancestors are THAWED. freezer.self_freezing shows the current freezing state for the cgroup itself. freezer.parent_freezing shows whether the cgroup is freezing because any of its ancestors is freezing. freezer_post_create() locks the parent and new cgroup and inherits the parent's state and freezer_change_state() applies new state top-down using cgroup_for_each_descendant_pre() which guarantees that no child can escape its parent's state. update_if_frozen() uses cgroup_for_each_descendant_post() to propagate frozen states bottom-up. Synchronization could be coarser and easier by using a single mutex to protect all hierarchy operations. Finer grained approach was used because it wasn't too difficult for cgroup_freezer and I think it's beneficial to have an example implementation and cgroup_freezer is rather simple and can serve a good one. As this makes cgroup_freezer properly hierarchical, freezer_subsys.broken_hierarchy marking is removed. Note that this patch changes userland visible behavior - freezing a cgroup now freezes all its descendants too. This behavior change is intended and has been warned via .broken_hierarchy. v2: Michal spotted a bug in freezer_change_state() - descendants were inheriting from the wrong ancestor. Fixed. v3: Documentation/cgroups/freezer-subsystem.txt updated. Signed-off-by: Tejun Heo Reviewed-by: Michal Hocko --- Documentation/cgroups/freezer-subsystem.txt | 63 +++++++---- kernel/cgroup_freezer.c | 161 +++++++++++++++++++++------- 2 files changed, 165 insertions(+), 59 deletions(-) (limited to 'Documentation') diff --git a/Documentation/cgroups/freezer-subsystem.txt b/Documentation/cgroups/freezer-subsystem.txt index 7e62de1e59ff..c96a72cbb30a 100644 --- a/Documentation/cgroups/freezer-subsystem.txt +++ b/Documentation/cgroups/freezer-subsystem.txt @@ -49,13 +49,49 @@ prevent the freeze/unfreeze cycle from becoming visible to the tasks being frozen. This allows the bash example above and gdb to run as expected. -The freezer subsystem in the container filesystem defines a file named -freezer.state. Writing "FROZEN" to the state file will freeze all tasks in the -cgroup. Subsequently writing "THAWED" will unfreeze the tasks in the cgroup. -Reading will return the current state. +The cgroup freezer is hierarchical. Freezing a cgroup freezes all +tasks beloning to the cgroup and all its descendant cgroups. Each +cgroup has its own state (self-state) and the state inherited from the +parent (parent-state). Iff both states are THAWED, the cgroup is +THAWED. -Note freezer.state doesn't exist in root cgroup, which means root cgroup -is non-freezable. +The following cgroupfs files are created by cgroup freezer. + +* freezer.state: Read-write. + + When read, returns the effective state of the cgroup - "THAWED", + "FREEZING" or "FROZEN". This is the combined self and parent-states. + If any is freezing, the cgroup is freezing (FREEZING or FROZEN). + + FREEZING cgroup transitions into FROZEN state when all tasks + belonging to the cgroup and its descendants become frozen. Note that + a cgroup reverts to FREEZING from FROZEN after a new task is added + to the cgroup or one of its descendant cgroups until the new task is + frozen. + + When written, sets the self-state of the cgroup. Two values are + allowed - "FROZEN" and "THAWED". If FROZEN is written, the cgroup, + if not already freezing, enters FREEZING state along with all its + descendant cgroups. + + If THAWED is written, the self-state of the cgroup is changed to + THAWED. Note that the effective state may not change to THAWED if + the parent-state is still freezing. If a cgroup's effective state + becomes THAWED, all its descendants which are freezing because of + the cgroup also leave the freezing state. + +* freezer.self_freezing: Read only. + + Shows the self-state. 0 if the self-state is THAWED; otherwise, 1. + This value is 1 iff the last write to freezer.state was "FROZEN". + +* freezer.parent_freezing: Read only. + + Shows the parent-state. 0 if none of the cgroup's ancestors is + frozen; otherwise, 1. + +The root cgroup is non-freezable and the above interface files don't +exist. * Examples of usage : @@ -85,18 +121,3 @@ to unfreeze all tasks in the container : This is the basic mechanism which should do the right thing for user space task in a simple scenario. - -It's important to note that freezing can be incomplete. In that case we return -EBUSY. This means that some tasks in the cgroup are busy doing something that -prevents us from completely freezing the cgroup at this time. After EBUSY, -the cgroup will remain partially frozen -- reflected by freezer.state reporting -"FREEZING" when read. The state will remain "FREEZING" until one of these -things happens: - - 1) Userspace cancels the freezing operation by writing "THAWED" to - the freezer.state file - 2) Userspace retries the freezing operation by writing "FROZEN" to - the freezer.state file (writing "FREEZING" is not legal - and returns EINVAL) - 3) The tasks that blocked the cgroup from entering the "FROZEN" - state disappear from the cgroup's set of tasks. diff --git a/kernel/cgroup_freezer.c b/kernel/cgroup_freezer.c index 4f12d317c4c3..670a4af7dc94 100644 --- a/kernel/cgroup_freezer.c +++ b/kernel/cgroup_freezer.c @@ -22,6 +22,13 @@ #include #include +/* + * A cgroup is freezing if any FREEZING flags are set. FREEZING_SELF is + * set if "FROZEN" is written to freezer.state cgroupfs file, and cleared + * for "THAWED". FREEZING_PARENT is set if the parent freezer is FREEZING + * for whatever reason. IOW, a cgroup has FREEZING_PARENT set if one of + * its ancestors has FREEZING_SELF set. + */ enum freezer_state_flags { CGROUP_FREEZER_ONLINE = (1 << 0), /* freezer is fully online */ CGROUP_FREEZING_SELF = (1 << 1), /* this freezer is freezing */ @@ -50,6 +57,15 @@ static inline struct freezer *task_freezer(struct task_struct *task) struct freezer, css); } +static struct freezer *parent_freezer(struct freezer *freezer) +{ + struct cgroup *pcg = freezer->css.cgroup->parent; + + if (pcg) + return cgroup_freezer(pcg); + return NULL; +} + bool cgroup_freezing(struct task_struct *task) { bool ret; @@ -74,17 +90,6 @@ static const char *freezer_state_strs(unsigned int state) return "THAWED"; }; -/* - * State diagram - * Transitions are caused by userspace writes to the freezer.state file. - * The values in parenthesis are state labels. The rest are edge labels. - * - * (THAWED) --FROZEN--> (FREEZING) --FROZEN--> (FROZEN) - * ^ ^ | | - * | \_______THAWED_______/ | - * \__________________________THAWED____________/ - */ - struct cgroup_subsys freezer_subsys; static struct cgroup_subsys_state *freezer_create(struct cgroup *cgroup) @@ -103,15 +108,34 @@ static struct cgroup_subsys_state *freezer_create(struct cgroup *cgroup) * freezer_post_create - commit creation of a freezer cgroup * @cgroup: cgroup being created * - * We're committing to creation of @cgroup. Mark it online. + * We're committing to creation of @cgroup. Mark it online and inherit + * parent's freezing state while holding both parent's and our + * freezer->lock. */ static void freezer_post_create(struct cgroup *cgroup) { struct freezer *freezer = cgroup_freezer(cgroup); + struct freezer *parent = parent_freezer(freezer); + + /* + * The following double locking and freezing state inheritance + * guarantee that @cgroup can never escape ancestors' freezing + * states. See cgroup_for_each_descendant_pre() for details. + */ + if (parent) + spin_lock_irq(&parent->lock); + spin_lock_nested(&freezer->lock, SINGLE_DEPTH_NESTING); - spin_lock_irq(&freezer->lock); freezer->state |= CGROUP_FREEZER_ONLINE; - spin_unlock_irq(&freezer->lock); + + if (parent && (parent->state & CGROUP_FREEZING)) { + freezer->state |= CGROUP_FREEZING_PARENT | CGROUP_FROZEN; + atomic_inc(&system_freezing_cnt); + } + + spin_unlock(&freezer->lock); + if (parent) + spin_unlock_irq(&parent->lock); } /** @@ -153,6 +177,7 @@ static void freezer_attach(struct cgroup *new_cgrp, struct cgroup_taskset *tset) { struct freezer *freezer = cgroup_freezer(new_cgrp); struct task_struct *task; + bool clear_frozen = false; spin_lock_irq(&freezer->lock); @@ -172,10 +197,25 @@ static void freezer_attach(struct cgroup *new_cgrp, struct cgroup_taskset *tset) } else { freeze_task(task); freezer->state &= ~CGROUP_FROZEN; + clear_frozen = true; } } spin_unlock_irq(&freezer->lock); + + /* + * Propagate FROZEN clearing upwards. We may race with + * update_if_frozen(), but as long as both work bottom-up, either + * update_if_frozen() sees child's FROZEN cleared or we clear the + * parent's FROZEN later. No parent w/ !FROZEN children can be + * left FROZEN. + */ + while (clear_frozen && (freezer = parent_freezer(freezer))) { + spin_lock_irq(&freezer->lock); + freezer->state &= ~CGROUP_FROZEN; + clear_frozen = freezer->state & CGROUP_FREEZING; + spin_unlock_irq(&freezer->lock); + } } static void freezer_fork(struct task_struct *task) @@ -200,24 +240,47 @@ out: rcu_read_unlock(); } -/* - * We change from FREEZING to FROZEN lazily if the cgroup was only - * partially frozen when we exitted write. Caller must hold freezer->lock. +/** + * update_if_frozen - update whether a cgroup finished freezing + * @cgroup: cgroup of interest + * + * Once FREEZING is initiated, transition to FROZEN is lazily updated by + * calling this function. If the current state is FREEZING but not FROZEN, + * this function checks whether all tasks of this cgroup and the descendant + * cgroups finished freezing and, if so, sets FROZEN. + * + * The caller is responsible for grabbing RCU read lock and calling + * update_if_frozen() on all descendants prior to invoking this function. * * Task states and freezer state might disagree while tasks are being * migrated into or out of @cgroup, so we can't verify task states against * @freezer state here. See freezer_attach() for details. */ -static void update_if_frozen(struct freezer *freezer) +static void update_if_frozen(struct cgroup *cgroup) { - struct cgroup *cgroup = freezer->css.cgroup; + struct freezer *freezer = cgroup_freezer(cgroup); + struct cgroup *pos; struct cgroup_iter it; struct task_struct *task; + WARN_ON_ONCE(!rcu_read_lock_held()); + + spin_lock_irq(&freezer->lock); + if (!(freezer->state & CGROUP_FREEZING) || (freezer->state & CGROUP_FROZEN)) - return; + goto out_unlock; + + /* are all (live) children frozen? */ + cgroup_for_each_child(pos, cgroup) { + struct freezer *child = cgroup_freezer(pos); + if ((child->state & CGROUP_FREEZER_ONLINE) && + !(child->state & CGROUP_FROZEN)) + goto out_unlock; + } + + /* are all tasks frozen? */ cgroup_iter_start(cgroup, &it); while ((task = cgroup_iter_next(cgroup, &it))) { @@ -229,27 +292,32 @@ static void update_if_frozen(struct freezer *freezer) * the usual frozen condition. */ if (!frozen(task) && !freezer_should_skip(task)) - goto notyet; + goto out_iter_end; } } freezer->state |= CGROUP_FROZEN; -notyet: +out_iter_end: cgroup_iter_end(cgroup, &it); +out_unlock: + spin_unlock_irq(&freezer->lock); } static int freezer_read(struct cgroup *cgroup, struct cftype *cft, struct seq_file *m) { - struct freezer *freezer = cgroup_freezer(cgroup); - unsigned int state; + struct cgroup *pos; - spin_lock_irq(&freezer->lock); - update_if_frozen(freezer); - state = freezer->state; - spin_unlock_irq(&freezer->lock); + rcu_read_lock(); - seq_puts(m, freezer_state_strs(state)); + /* update states bottom-up */ + cgroup_for_each_descendant_post(pos, cgroup) + update_if_frozen(pos); + update_if_frozen(cgroup); + + rcu_read_unlock(); + + seq_puts(m, freezer_state_strs(cgroup_freezer(cgroup)->state)); seq_putc(m, '\n'); return 0; } @@ -320,14 +388,39 @@ static void freezer_apply_state(struct freezer *freezer, bool freeze, * @freezer: freezer of interest * @freeze: whether to freeze or thaw * - * Freeze or thaw @cgroup according to @freeze. + * Freeze or thaw @freezer according to @freeze. The operations are + * recursive - all descendants of @freezer will be affected. */ static void freezer_change_state(struct freezer *freezer, bool freeze) { + struct cgroup *pos; + /* update @freezer */ spin_lock_irq(&freezer->lock); freezer_apply_state(freezer, freeze, CGROUP_FREEZING_SELF); spin_unlock_irq(&freezer->lock); + + /* + * Update all its descendants in pre-order traversal. Each + * descendant will try to inherit its parent's FREEZING state as + * CGROUP_FREEZING_PARENT. + */ + rcu_read_lock(); + cgroup_for_each_descendant_pre(pos, freezer->css.cgroup) { + struct freezer *pos_f = cgroup_freezer(pos); + struct freezer *parent = parent_freezer(pos_f); + + /* + * Our update to @parent->state is already visible which is + * all we need. No need to lock @parent. For more info on + * synchronization, see freezer_post_create(). + */ + spin_lock_irq(&pos_f->lock); + freezer_apply_state(pos_f, parent->state & CGROUP_FREEZING, + CGROUP_FREEZING_PARENT); + spin_unlock_irq(&pos_f->lock); + } + rcu_read_unlock(); } static int freezer_write(struct cgroup *cgroup, struct cftype *cft, @@ -390,12 +483,4 @@ struct cgroup_subsys freezer_subsys = { .attach = freezer_attach, .fork = freezer_fork, .base_cftypes = files, - - /* - * freezer subsys doesn't handle hierarchy at all. Frozen state - * should be inherited through the hierarchy - if a parent is - * frozen, all its children should be frozen. Fix it and remove - * the following. - */ - .broken_hierarchy = true, }; -- cgit v1.2.3-59-g8ed1b From 56277f40d73f12d9ffb12b5a6bd7e0ad1f2284b3 Mon Sep 17 00:00:00 2001 From: Nick Bowler Date: Wed, 7 Nov 2012 06:20:34 +0000 Subject: phylib: mdio: Add sysfs attribute for PHY identifiers. This adds a phy_id sysfs attribute to MDIO devices, containing the 32-bit PHY identifier reported by the device. This attribute can be useful when debugging problems related to phy drivers. Other enumerable buses already have similar attributes. Signed-off-by: Nick Bowler Signed-off-by: David S. Miller --- Documentation/ABI/testing/sysfs-bus-mdio | 9 +++++++++ drivers/net/phy/mdio_bus.c | 14 ++++++++++++++ 2 files changed, 23 insertions(+) create mode 100644 Documentation/ABI/testing/sysfs-bus-mdio (limited to 'Documentation') diff --git a/Documentation/ABI/testing/sysfs-bus-mdio b/Documentation/ABI/testing/sysfs-bus-mdio new file mode 100644 index 000000000000..6349749ebc29 --- /dev/null +++ b/Documentation/ABI/testing/sysfs-bus-mdio @@ -0,0 +1,9 @@ +What: /sys/bus/mdio_bus/devices/.../phy_id +Date: November 2012 +KernelVersion: 3.8 +Contact: netdev@vger.kernel.org +Description: + This attribute contains the 32-bit PHY Identifier as reported + by the device during bus enumeration, encoded in hexadecimal. + This ID is used to match the device with the appropriate + driver. diff --git a/drivers/net/phy/mdio_bus.c b/drivers/net/phy/mdio_bus.c index c1ef3000ea60..044b5326459f 100644 --- a/drivers/net/phy/mdio_bus.c +++ b/drivers/net/phy/mdio_bus.c @@ -431,10 +431,24 @@ static struct dev_pm_ops mdio_bus_pm_ops = { #endif /* CONFIG_PM */ +static ssize_t +phy_id_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct phy_device *phydev = to_phy_device(dev); + + return sprintf(buf, "0x%.8lx\n", (unsigned long)phydev->phy_id); +} + +static struct device_attribute mdio_dev_attrs[] = { + __ATTR_RO(phy_id), + __ATTR_NULL +}; + struct bus_type mdio_bus_type = { .name = "mdio_bus", .match = mdio_bus_match, .pm = MDIO_BUS_PM_OPS, + .dev_attrs = mdio_dev_attrs, }; EXPORT_SYMBOL(mdio_bus_type); -- cgit v1.2.3-59-g8ed1b From d1ee40f96036e838f0849dd31c16e548a904176c Mon Sep 17 00:00:00 2001 From: Daniel Borkmann Date: Thu, 8 Nov 2012 02:37:01 +0000 Subject: doc: packet_mmap: update doc to implementation status MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This improves the packet_mmap.txt document in the following ways: * Add initial information about different TPACKET versions * Add initial information about packet fanout * Add pointer to BPF document (since this also could be of interest) * 'Fix' minor, rather cosmetic things Information partially taken from related commit messages. Reported-by: Ronny Meeus Signed-off-by: Daniel Borkmann Cc: Ulisses Alonso Camaró Cc: Johann Baudy Signed-off-by: David S. Miller --- Documentation/networking/packet_mmap.txt | 233 +++++++++++++++++++++++++++---- 1 file changed, 209 insertions(+), 24 deletions(-) (limited to 'Documentation') diff --git a/Documentation/networking/packet_mmap.txt b/Documentation/networking/packet_mmap.txt index 7cd879eba5dc..94444b152fbc 100644 --- a/Documentation/networking/packet_mmap.txt +++ b/Documentation/networking/packet_mmap.txt @@ -3,9 +3,9 @@ -------------------------------------------------------------------------------- This file documents the mmap() facility available with the PACKET -socket interface on 2.4 and 2.6 kernels. This type of sockets is used for -capture network traffic with utilities like tcpdump or any other that needs -raw access to network interface. +socket interface on 2.4/2.6/3.x kernels. This type of sockets is used for +i) capture network traffic with utilities like tcpdump, ii) transmit network +traffic, or any other that needs raw access to network interface. You can find the latest version of this document at: http://wiki.ipxwarzone.com/index.php5?title=Linux_packet_mmap @@ -21,19 +21,18 @@ Please send your comments to + Why use PACKET_MMAP -------------------------------------------------------------------------------- -In Linux 2.4/2.6 if PACKET_MMAP is not enabled, the capture process is very -inefficient. It uses very limited buffers and requires one system call -to capture each packet, it requires two if you want to get packet's -timestamp (like libpcap always does). +In Linux 2.4/2.6/3.x if PACKET_MMAP is not enabled, the capture process is very +inefficient. It uses very limited buffers and requires one system call to +capture each packet, it requires two if you want to get packet's timestamp +(like libpcap always does). In the other hand PACKET_MMAP is very efficient. PACKET_MMAP provides a size configurable circular buffer mapped in user space that can be used to either send or receive packets. This way reading packets just needs to wait for them, most of the time there is no need to issue a single system call. Concerning transmission, multiple packets can be sent through one system call to get the -highest bandwidth. -By using a shared buffer between the kernel and the user also has the benefit -of minimizing packet copies. +highest bandwidth. By using a shared buffer between the kernel and the user +also has the benefit of minimizing packet copies. It's fine to use PACKET_MMAP to improve the performance of the capture and transmission process, but it isn't everything. At least, if you are capturing @@ -41,7 +40,8 @@ at high speeds (this is relative to the cpu speed), you should check if the device driver of your network interface card supports some sort of interrupt load mitigation or (even better) if it supports NAPI, also make sure it is enabled. For transmission, check the MTU (Maximum Transmission Unit) used and -supported by devices of your network. +supported by devices of your network. CPU IRQ pinning of your network interface +card can also be an advantage. -------------------------------------------------------------------------------- + How to use mmap() to improve capture process @@ -87,9 +87,7 @@ the following process: socket creation and destruction is straight forward, and is done the same way with or without PACKET_MMAP: -int fd; - -fd= socket(PF_PACKET, mode, htons(ETH_P_ALL)) + int fd = socket(PF_PACKET, mode, htons(ETH_P_ALL)); where mode is SOCK_RAW for the raw interface were link level information can be captured or SOCK_DGRAM for the cooked @@ -180,7 +178,6 @@ and the PACKET_TX_HAS_OFF option. + PACKET_MMAP settings -------------------------------------------------------------------------------- - To setup PACKET_MMAP from user level code is done with a call like - Capture process @@ -214,7 +211,6 @@ indeed, packet_set_ring checks that the following condition is true frames_per_block * tp_block_nr == tp_frame_nr - Lets see an example, with the following values: tp_block_size= 4096 @@ -240,7 +236,6 @@ be spawned across two blocks, so there are some details you have to take into account when choosing the frame_size. See "Mapping and use of the circular buffer (ring)". - -------------------------------------------------------------------------------- + PACKET_MMAP setting constraints -------------------------------------------------------------------------------- @@ -277,7 +272,6 @@ User space programs can include /usr/include/sys/user.h and The pagesize can also be determined dynamically with the getpagesize (2) system call. - Block number limit -------------------- @@ -297,7 +291,6 @@ called pg_vec, its size limits the number of blocks that can be allocated. v block #2 block #1 - kmalloc allocates any number of bytes of physically contiguous memory from a pool of pre-determined sizes. This pool of memory is maintained by the slab allocator which is at the end the responsible for doing the allocation and @@ -312,7 +305,6 @@ pointers to blocks is 131072/4 = 32768 blocks - PACKET_MMAP buffer size calculator ------------------------------------ @@ -353,7 +345,6 @@ and a value for of 2048 bytes. These parameters will yield and hence the buffer will have a 262144 MiB size. So it can hold 262144 MiB / 2048 bytes = 134217728 frames - Actually, this buffer size is not possible with an i386 architecture. Remember that the memory is allocated in kernel space, in the case of an i386 kernel's memory size is limited to 1GiB. @@ -385,7 +376,6 @@ the following (from include/linux/if_packet.h): - Start+tp_net: Packet data, aligned to TPACKET_ALIGNMENT=16. - Pad to align to TPACKET_ALIGNMENT=16 */ - The following are conditions that are checked in packet_set_ring @@ -426,7 +416,6 @@ and the following flags apply: #define TP_STATUS_LOSING 4 #define TP_STATUS_CSUMNOTREADY 8 - TP_STATUS_COPY : This flag indicates that the frame (and associated meta information) has been truncated because it's larger than tp_frame_size. This packet can be @@ -475,7 +464,6 @@ packets are in the ring: It doesn't incur in a race condition to first check the status value and then poll for frames. - ++ Transmission process Those defines are also used for transmission: @@ -506,6 +494,196 @@ The user can also use poll() to check if a buffer is available: pfd.events = POLLOUT; retval = poll(&pfd, 1, timeout); +------------------------------------------------------------------------------- ++ What TPACKET versions are available and when to use them? +------------------------------------------------------------------------------- + + int val = tpacket_version; + setsockopt(fd, SOL_PACKET, PACKET_VERSION, &val, sizeof(val)); + getsockopt(fd, SOL_PACKET, PACKET_VERSION, &val, sizeof(val)); + +where 'tpacket_version' can be TPACKET_V1 (default), TPACKET_V2, TPACKET_V3. + +TPACKET_V1: + - Default if not otherwise specified by setsockopt(2) + - RX_RING, TX_RING available + - VLAN metadata information available for packets + (TP_STATUS_VLAN_VALID) + +TPACKET_V1 --> TPACKET_V2: + - Made 64 bit clean due to unsigned long usage in TPACKET_V1 + structures, thus this also works on 64 bit kernel with 32 bit + userspace and the like + - Timestamp resolution in nanoseconds instead of microseconds + - RX_RING, TX_RING available + - How to switch to TPACKET_V2: + 1. Replace struct tpacket_hdr by struct tpacket2_hdr + 2. Query header len and save + 3. Set protocol version to 2, set up ring as usual + 4. For getting the sockaddr_ll, + use (void *)hdr + TPACKET_ALIGN(hdrlen) instead of + (void *)hdr + TPACKET_ALIGN(sizeof(struct tpacket_hdr)) + +TPACKET_V2 --> TPACKET_V3: + - Flexible buffer implementation: + 1. Blocks can be configured with non-static frame-size + 2. Read/poll is at a block-level (as opposed to packet-level) + 3. Added poll timeout to avoid indefinite user-space wait + on idle links + 4. Added user-configurable knobs: + 4.1 block::timeout + 4.2 tpkt_hdr::sk_rxhash + - RX Hash data available in user space + - Currently only RX_RING available + +------------------------------------------------------------------------------- ++ AF_PACKET fanout mode +------------------------------------------------------------------------------- + +In the AF_PACKET fanout mode, packet reception can be load balanced among +processes. This also works in combination with mmap(2) on packet sockets. + +Minimal example code by David S. Miller (try things like "./test eth0 hash", +"./test eth0 lb", etc.): + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +#include +#include + +#include + +static const char *device_name; +static int fanout_type; +static int fanout_id; + +#ifndef PACKET_FANOUT +# define PACKET_FANOUT 18 +# define PACKET_FANOUT_HASH 0 +# define PACKET_FANOUT_LB 1 +#endif + +static int setup_socket(void) +{ + int err, fd = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_IP)); + struct sockaddr_ll ll; + struct ifreq ifr; + int fanout_arg; + + if (fd < 0) { + perror("socket"); + return EXIT_FAILURE; + } + + memset(&ifr, 0, sizeof(ifr)); + strcpy(ifr.ifr_name, device_name); + err = ioctl(fd, SIOCGIFINDEX, &ifr); + if (err < 0) { + perror("SIOCGIFINDEX"); + return EXIT_FAILURE; + } + + memset(&ll, 0, sizeof(ll)); + ll.sll_family = AF_PACKET; + ll.sll_ifindex = ifr.ifr_ifindex; + err = bind(fd, (struct sockaddr *) &ll, sizeof(ll)); + if (err < 0) { + perror("bind"); + return EXIT_FAILURE; + } + + fanout_arg = (fanout_id | (fanout_type << 16)); + err = setsockopt(fd, SOL_PACKET, PACKET_FANOUT, + &fanout_arg, sizeof(fanout_arg)); + if (err) { + perror("setsockopt"); + return EXIT_FAILURE; + } + + return fd; +} + +static void fanout_thread(void) +{ + int fd = setup_socket(); + int limit = 10000; + + if (fd < 0) + exit(fd); + + while (limit-- > 0) { + char buf[1600]; + int err; + + err = read(fd, buf, sizeof(buf)); + if (err < 0) { + perror("read"); + exit(EXIT_FAILURE); + } + if ((limit % 10) == 0) + fprintf(stdout, "(%d) \n", getpid()); + } + + fprintf(stdout, "%d: Received 10000 packets\n", getpid()); + + close(fd); + exit(0); +} + +int main(int argc, char **argp) +{ + int fd, err; + int i; + + if (argc != 3) { + fprintf(stderr, "Usage: %s INTERFACE {hash|lb}\n", argp[0]); + return EXIT_FAILURE; + } + + if (!strcmp(argp[2], "hash")) + fanout_type = PACKET_FANOUT_HASH; + else if (!strcmp(argp[2], "lb")) + fanout_type = PACKET_FANOUT_LB; + else { + fprintf(stderr, "Unknown fanout type [%s]\n", argp[2]); + exit(EXIT_FAILURE); + } + + device_name = argp[1]; + fanout_id = getpid() & 0xffff; + + for (i = 0; i < 4; i++) { + pid_t pid = fork(); + + switch (pid) { + case 0: + fanout_thread(); + + case -1: + perror("fork"); + exit(EXIT_FAILURE); + } + } + + for (i = 0; i < 4; i++) { + int status; + + wait(&status); + } + + return 0; +} + ------------------------------------------------------------------------------- + PACKET_TIMESTAMP ------------------------------------------------------------------------------- @@ -532,6 +710,13 @@ the networking stack is used (the behavior before this setting was added). See include/linux/net_tstamp.h and Documentation/networking/timestamping for more information on hardware timestamps. +------------------------------------------------------------------------------- ++ Miscellaneous bits +------------------------------------------------------------------------------- + +- Packet sockets work well together with Linux socket filters, thus you also + might want to have a look at Documentation/networking/filter.txt + -------------------------------------------------------------------------------- + THANKS -------------------------------------------------------------------------------- -- cgit v1.2.3-59-g8ed1b From 292a41716a4ad3e93c57155f99786abe8a8d386a Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Wed, 7 Nov 2012 19:41:51 -0500 Subject: nfsd4: update documentation on 4.1 progress Signed-off-by: J. Bruce Fields --- Documentation/filesystems/nfs/nfs41-server.txt | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) (limited to 'Documentation') diff --git a/Documentation/filesystems/nfs/nfs41-server.txt b/Documentation/filesystems/nfs/nfs41-server.txt index f5ddff2da302..392ef637e101 100644 --- a/Documentation/filesystems/nfs/nfs41-server.txt +++ b/Documentation/filesystems/nfs/nfs41-server.txt @@ -43,12 +43,6 @@ interoperability problems with future clients. Known issues: client-server state (thus preventing unauthorized tampering with locks and opens, for example). It is mandatory for servers to support this, though no clients use it yet. - - Mandatory operations which we do not support, such as - DESTROY_CLIENTID, are not currently used by clients, but will be - (and the spec recommends their uses in common cases), and - clients should not be expected to know how to recover from the - case where they are not supported. This will eventually cause - interoperability failures. In addition, some limitations are inherited from the current NFSv4 implementation: @@ -94,7 +88,7 @@ NS*| DELEGPURGE | OPT | FDELG (REQ) | Section 18.5 | | DELEGRETURN | OPT | FDELG, | Section 18.6 | | | | DDELG, pNFS | | | | | (REQ) | | -NS | DESTROY_CLIENTID | REQ | | Section 18.50 | +I | DESTROY_CLIENTID | REQ | | Section 18.50 | I | DESTROY_SESSION | REQ | | Section 18.37 | I | EXCHANGE_ID | REQ | | Section 18.35 | I | FREE_STATEID | REQ | | Section 18.38 | @@ -187,7 +181,6 @@ EXCHANGE_ID: CREATE_SESSION: * backchannel attributes are ignored -* backchannel security parameters are ignored SEQUENCE: * no support for dynamic slot table renegotiation (optional) -- cgit v1.2.3-59-g8ed1b From 55020c8056a83ef2181e4cddd39cb2b9f4b88c5a Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Fri, 9 Nov 2012 14:07:01 +0100 Subject: of: Add vendor prefix for ON Semiconductor Corp. The used vendor prefix corresponds to the stock symbol (ONNN) for ON Semiconductor Corp. Signed-off-by: Thierry Reding Signed-off-by: Rob Herring --- Documentation/devicetree/bindings/vendor-prefixes.txt | 1 + 1 file changed, 1 insertion(+) (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/vendor-prefixes.txt b/Documentation/devicetree/bindings/vendor-prefixes.txt index 9de2b9ff9d6e..ff41e4019010 100644 --- a/Documentation/devicetree/bindings/vendor-prefixes.txt +++ b/Documentation/devicetree/bindings/vendor-prefixes.txt @@ -34,6 +34,7 @@ national National Semiconductor nintendo Nintendo nvidia NVIDIA nxp NXP Semiconductors +onnn ON Semiconductor Corp. picochip Picochip Ltd powervr Imagination Technologies qcom Qualcomm, Inc. -- cgit v1.2.3-59-g8ed1b From 1a78958dc212f3698fdc543857af80155cb30f7f Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Wed, 17 Oct 2012 20:51:54 +0200 Subject: pinctrl: reserve pins when states are activated This switches the way that pins are reserved for multiplexing: We used to do this when the map was parsed, at the creation of the settings inside the pinctrl handle, in pinmux_map_to_setting(). However this does not work for us, because we want to use the same set of pins with different devices at different times: the current code assumes that the pin groups in a pinmux state will only be used with one single device, albeit different groups can be active at different times. For example if a single I2C driver block is used to drive two different busses located on two pin groups A and B, then the pins for all possible states of a function are reserved when fetching the pinctrl handle: the I2C bus can choose either set A or set B by a mux state at runtime, but all pins in both group A and B (the superset) are effectively reserved for that I2C function and mapped to the device. Another device can never get in and use the pins in group A, even if the device/function is using group B at the moment. Instead: let use reserve the pins when the state is activated and drop them when the state is disabled, i.e. when we move to another state. This way different devices/functions can use the same pins at different times. We know that this is an odd way of doing things, but we really need to switch e.g. an SD-card slot to become a tracing output sink at runtime: we plug in a special "tracing card" then mux the pins that used to be an SD slot around to the tracing unit and push out tracing data there instead of SD-card traffic. As a side effect pinmux_free_setting() is unused but the stubs are kept for future additions of code. Cc: Patrice Chotard Cc: Loic Pallardy Acked-by: Stephen Warren Tested-by: Jean Nicolas Graux Signed-off-by: Linus Walleij --- Documentation/pinctrl.txt | 4 ++- drivers/pinctrl/core.c | 2 ++ drivers/pinctrl/core.h | 2 ++ drivers/pinctrl/pinmux.c | 67 ++++++++++++++++------------------------------- 4 files changed, 29 insertions(+), 46 deletions(-) (limited to 'Documentation') diff --git a/Documentation/pinctrl.txt b/Documentation/pinctrl.txt index 3b4ee5328868..a1cd2f9428d7 100644 --- a/Documentation/pinctrl.txt +++ b/Documentation/pinctrl.txt @@ -1193,4 +1193,6 @@ foo_switch() ... } -The above has to be done from process context. +The above has to be done from process context. The reservation of the pins +will be done when the state is activated, so in effect one specific pin +can be used by different functions at different times on a running system. diff --git a/drivers/pinctrl/core.c b/drivers/pinctrl/core.c index 2e39c04fc16b..cec6072cd7c1 100644 --- a/drivers/pinctrl/core.c +++ b/drivers/pinctrl/core.c @@ -563,6 +563,8 @@ static int add_setting(struct pinctrl *p, struct pinctrl_map const *map) return -EPROBE_DEFER; } + setting->dev_name = map->dev_name; + switch (map->type) { case PIN_MAP_TYPE_MUX_GROUP: ret = pinmux_map_to_setting(map, setting); diff --git a/drivers/pinctrl/core.h b/drivers/pinctrl/core.h index 1f40ff68a8c4..12f5694f3d5d 100644 --- a/drivers/pinctrl/core.h +++ b/drivers/pinctrl/core.h @@ -105,12 +105,14 @@ struct pinctrl_setting_configs { * @type: the type of setting * @pctldev: pin control device handling to be programmed. Not used for * PIN_MAP_TYPE_DUMMY_STATE. + * @dev_name: the name of the device using this state * @data: Data specific to the setting type */ struct pinctrl_setting { struct list_head node; enum pinctrl_map_type type; struct pinctrl_dev *pctldev; + const char *dev_name; union { struct pinctrl_setting_mux mux; struct pinctrl_setting_configs configs; diff --git a/drivers/pinctrl/pinmux.c b/drivers/pinctrl/pinmux.c index 9301a7a95eff..0ef01ee2835f 100644 --- a/drivers/pinctrl/pinmux.c +++ b/drivers/pinctrl/pinmux.c @@ -314,14 +314,11 @@ int pinmux_map_to_setting(struct pinctrl_map const *map, { struct pinctrl_dev *pctldev = setting->pctldev; const struct pinmux_ops *pmxops = pctldev->desc->pmxops; - const struct pinctrl_ops *pctlops = pctldev->desc->pctlops; char const * const *groups; unsigned num_groups; int ret; const char *group; int i; - const unsigned *pins; - unsigned num_pins; if (!pmxops) { dev_err(pctldev->dev, "does not support mux function\n"); @@ -376,53 +373,12 @@ int pinmux_map_to_setting(struct pinctrl_map const *map, } setting->data.mux.group = ret; - ret = pctlops->get_group_pins(pctldev, setting->data.mux.group, &pins, - &num_pins); - if (ret) { - dev_err(pctldev->dev, - "could not get pins for device %s group selector %d\n", - pinctrl_dev_get_name(pctldev), setting->data.mux.group); - return -ENODEV; - } - - /* Try to allocate all pins in this group, one by one */ - for (i = 0; i < num_pins; i++) { - ret = pin_request(pctldev, pins[i], map->dev_name, NULL); - if (ret) { - dev_err(pctldev->dev, - "could not request pin %d on device %s\n", - pins[i], pinctrl_dev_get_name(pctldev)); - /* On error release all taken pins */ - i--; /* this pin just failed */ - for (; i >= 0; i--) - pin_free(pctldev, pins[i], NULL); - return -ENODEV; - } - } - return 0; } void pinmux_free_setting(struct pinctrl_setting const *setting) { - struct pinctrl_dev *pctldev = setting->pctldev; - const struct pinctrl_ops *pctlops = pctldev->desc->pctlops; - const unsigned *pins; - unsigned num_pins; - int ret; - int i; - - ret = pctlops->get_group_pins(pctldev, setting->data.mux.group, - &pins, &num_pins); - if (ret) { - dev_err(pctldev->dev, - "could not get pins for device %s group selector %d\n", - pinctrl_dev_get_name(pctldev), setting->data.mux.group); - return; - } - - for (i = 0; i < num_pins; i++) - pin_free(pctldev, pins[i], NULL); + /* This function is currently unused */ } int pinmux_enable_setting(struct pinctrl_setting const *setting) @@ -446,6 +402,22 @@ int pinmux_enable_setting(struct pinctrl_setting const *setting) num_pins = 0; } + /* Try to allocate all pins in this group, one by one */ + for (i = 0; i < num_pins; i++) { + ret = pin_request(pctldev, pins[i], setting->dev_name, NULL); + if (ret) { + dev_err(pctldev->dev, + "could not request pin %d on device %s\n", + pins[i], pinctrl_dev_get_name(pctldev)); + /* On error release all taken pins */ + i--; /* this pin just failed */ + for (; i >= 0; i--) + pin_free(pctldev, pins[i], NULL); + return -ENODEV; + } + } + + /* Now that we have acquired the pins, encode the mux setting */ for (i = 0; i < num_pins; i++) { desc = pin_desc_get(pctldev, pins[i]); if (desc == NULL) { @@ -482,6 +454,7 @@ void pinmux_disable_setting(struct pinctrl_setting const *setting) num_pins = 0; } + /* Flag the descs that no setting is active */ for (i = 0; i < num_pins; i++) { desc = pin_desc_get(pctldev, pins[i]); if (desc == NULL) { @@ -493,6 +466,10 @@ void pinmux_disable_setting(struct pinctrl_setting const *setting) desc->mux_setting = NULL; } + /* And release the pins */ + for (i = 0; i < num_pins; i++) + pin_free(pctldev, pins[i], NULL); + if (ops->disable) ops->disable(pctldev, setting->data.mux.func, setting->data.mux.group); } -- cgit v1.2.3-59-g8ed1b From f23f1516b6757c326cc638bed8c402c77e2a596e Mon Sep 17 00:00:00 2001 From: Shiraz Hashim Date: Sat, 27 Oct 2012 15:21:36 +0530 Subject: gpiolib: provide provision to register pin ranges pinctrl subsystem needs gpio chip base to prepare set of gpio pin ranges, which a given pinctrl driver can handle. This is important to handle pinctrl gpio request calls in order to program a given pin properly for gpio operation. As gpio base is allocated dynamically during gpiochip registration, presently there exists no clean way to pass this information to the pinctrl subsystem. After few discussions from [1], it was concluded that may be gpio controller reporting the pin range it supports, is a better way than pinctrl subsystem directly registering it. [1] http://comments.gmane.org/gmane.linux.ports.arm.kernel/184816 Cc: Grant Likely Signed-off-by: Viresh Kumar Signed-off-by: Shiraz Hashim [Edited documentation a bit] Signed-off-by: Linus Walleij --- Documentation/devicetree/bindings/gpio/gpio.txt | 36 ++++++++++++++++ Documentation/gpio.txt | 42 +++++++++++++++++++ Documentation/pinctrl.txt | 3 ++ drivers/gpio/gpiolib-of.c | 56 +++++++++++++++++++++++++ drivers/gpio/gpiolib.c | 43 +++++++++++++++++++ drivers/pinctrl/core.c | 13 ++++++ drivers/pinctrl/devicetree.c | 13 ++++++ include/asm-generic/gpio.h | 25 +++++++++++ include/linux/gpio.h | 3 ++ include/linux/pinctrl/pinctrl.h | 17 ++++++++ 10 files changed, 251 insertions(+) (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/gpio/gpio.txt b/Documentation/devicetree/bindings/gpio/gpio.txt index 4e16ba4feab0..a33628759d36 100644 --- a/Documentation/devicetree/bindings/gpio/gpio.txt +++ b/Documentation/devicetree/bindings/gpio/gpio.txt @@ -75,4 +75,40 @@ Example of two SOC GPIO banks defined as gpio-controller nodes: gpio-controller; }; +2.1) gpio-controller and pinctrl subsystem +------------------------------------------ +gpio-controller on a SOC might be tightly coupled with the pinctrl +subsystem, in the sense that the pins can be used by other functions +together with optional gpio feature. + +While the pin allocation is totally managed by the pin ctrl subsystem, +gpio (under gpiolib) is still maintained by gpio drivers. It may happen +that different pin ranges in a SoC is managed by different gpio drivers. + +This makes it logical to let gpio drivers announce their pin ranges to +the pin ctrl subsystem and call 'pinctrl_request_gpio' in order to +request the corresponding pin before any gpio usage. + +For this, the gpio controller can use a pinctrl phandle and pins to +announce the pinrange to the pin ctrl subsystem. For example, + + qe_pio_e: gpio-controller@1460 { + #gpio-cells = <2>; + compatible = "fsl,qe-pario-bank-e", "fsl,qe-pario-bank"; + reg = <0x1460 0x18>; + gpio-controller; + gpio-ranges = <&pinctrl1 20 10>, <&pinctrl2 50 20>; + + } + +where, + &pinctrl1 and &pinctrl2 is the phandle to the pinctrl DT node. + + Next values specify the base pin and number of pins for the range + handled by 'qe_pio_e' gpio. In the given example from base pin 20 to + pin 29 under pinctrl1 and pin 50 to pin 69 under pinctrl2 is handled + by this gpio controller. + +The pinctrl node must have "#gpio-range-cells" property to show number of +arguments to pass with phandle from gpio controllers node. diff --git a/Documentation/gpio.txt b/Documentation/gpio.txt index e08a883de36e..77a1d11af723 100644 --- a/Documentation/gpio.txt +++ b/Documentation/gpio.txt @@ -439,6 +439,48 @@ slower clock delays the rising edge of SCK, and the I2C master adjusts its signaling rate accordingly. +GPIO controllers and the pinctrl subsystem +------------------------------------------ + +A GPIO controller on a SOC might be tightly coupled with the pinctrl +subsystem, in the sense that the pins can be used by other functions +together with an optional gpio feature. We have already covered the +case where e.g. a GPIO controller need to reserve a pin or set the +direction of a pin by calling any of: + +pinctrl_request_gpio() +pinctrl_free_gpio() +pinctrl_gpio_direction_input() +pinctrl_gpio_direction_output() + +But how does the pin control subsystem cross-correlate the GPIO +numbers (which are a global business) to a certain pin on a certain +pin controller? + +This is done by registering "ranges" of pins, which are essentially +cross-reference tables. These are described in +Documentation/pinctrl.txt + +While the pin allocation is totally managed by the pinctrl subsystem, +gpio (under gpiolib) is still maintained by gpio drivers. It may happen +that different pin ranges in a SoC is managed by different gpio drivers. + +This makes it logical to let gpio drivers announce their pin ranges to +the pin ctrl subsystem before it will call 'pinctrl_request_gpio' in order +to request the corresponding pin to be prepared by the pinctrl subsystem +before any gpio usage. + +For this, the gpio controller can register its pin range with pinctrl +subsystem. There are two ways of doing it currently: with or without DT. + +For with DT support refer to Documentation/devicetree/bindings/gpio/gpio.txt. + +For non-DT support, user can call gpiochip_add_pin_range() with appropriate +parameters to register a range of gpio pins with a pinctrl driver. For this +exact name string of pinctrl device has to be passed as one of the +argument to this routine. + + What do these conventions omit? =============================== One of the biggest things these conventions omit is pin multiplexing, since diff --git a/Documentation/pinctrl.txt b/Documentation/pinctrl.txt index a1cd2f9428d7..da40efbef6ec 100644 --- a/Documentation/pinctrl.txt +++ b/Documentation/pinctrl.txt @@ -364,6 +364,9 @@ will get an pin number into its handled number range. Further it is also passed the range ID value, so that the pin controller knows which range it should deal with. +Calling pinctrl_add_gpio_range from pinctrl driver is DEPRECATED. Please see +section 2.1 of Documentation/devicetree/bindings/gpio/gpio.txt on how to bind +pinctrl and gpio drivers. PINMUX interfaces ================= diff --git a/drivers/gpio/gpiolib-of.c b/drivers/gpio/gpiolib-of.c index f1a45997aea8..a5b90c8e9844 100644 --- a/drivers/gpio/gpiolib-of.c +++ b/drivers/gpio/gpiolib-of.c @@ -19,6 +19,7 @@ #include #include #include +#include #include /* Private data structure for of_gpiochip_find_and_xlate */ @@ -216,6 +217,58 @@ err0: } EXPORT_SYMBOL(of_mm_gpiochip_add); +#ifdef CONFIG_PINCTRL +void of_gpiochip_add_pin_range(struct gpio_chip *chip) +{ + struct device_node *np = chip->of_node; + struct gpio_pin_range *pin_range; + struct of_phandle_args pinspec; + int index = 0, ret; + + if (!np) + return; + + do { + ret = of_parse_phandle_with_args(np, "gpio-ranges", + "#gpio-range-cells", index, &pinspec); + if (ret) + break; + + pin_range = devm_kzalloc(chip->dev, sizeof(*pin_range), + GFP_KERNEL); + if (!pin_range) { + pr_err("%s: GPIO chip: failed to allocate pin ranges\n", + chip->label); + break; + } + + pin_range->range.name = chip->label; + pin_range->range.base = chip->base; + pin_range->range.pin_base = pinspec.args[0]; + pin_range->range.npins = pinspec.args[1]; + pin_range->pctldev = of_pinctrl_add_gpio_range(pinspec.np, + &pin_range->range); + + list_add_tail(&pin_range->node, &chip->pin_ranges); + + } while (index++); +} + +void of_gpiochip_remove_pin_range(struct gpio_chip *chip) +{ + struct gpio_pin_range *pin_range, *tmp; + + list_for_each_entry_safe(pin_range, tmp, &chip->pin_ranges, node) { + list_del(&pin_range->node); + pinctrl_remove_gpio_range(pin_range->pctldev, + &pin_range->range); + } +} +#else +void of_gpiochip_add_pin_range(struct gpio_chip *chip) {} +void of_gpiochip_remove_pin_range(struct gpio_chip *chip) {} +#endif + void of_gpiochip_add(struct gpio_chip *chip) { if ((!chip->of_node) && (chip->dev)) @@ -229,11 +282,14 @@ void of_gpiochip_add(struct gpio_chip *chip) chip->of_xlate = of_gpio_simple_xlate; } + of_gpiochip_add_pin_range(chip); of_node_get(chip->of_node); } void of_gpiochip_remove(struct gpio_chip *chip) { + of_gpiochip_remove_pin_range(chip); + if (chip->of_node) of_node_put(chip->of_node); } diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 1c8d9e3380e1..f0b07bbfcc9a 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -1083,6 +1083,10 @@ int gpiochip_add(struct gpio_chip *chip) } } +#ifdef CONFIG_PINCTRL + INIT_LIST_HEAD(&chip->pin_ranges); +#endif + of_gpiochip_add(chip); unlock: @@ -1180,6 +1184,45 @@ struct gpio_chip *gpiochip_find(void *data, } EXPORT_SYMBOL_GPL(gpiochip_find); +#ifdef CONFIG_PINCTRL +void gpiochip_add_pin_range(struct gpio_chip *chip, const char *pinctl_name, + unsigned int pin_base, unsigned int npins) +{ + struct gpio_pin_range *pin_range; + + pin_range = devm_kzalloc(chip->dev, sizeof(*pin_range), GFP_KERNEL); + if (!pin_range) { + pr_err("%s: GPIO chip: failed to allocate pin ranges\n", + chip->label); + return; + } + + pin_range->range.name = chip->label; + pin_range->range.base = chip->base; + pin_range->range.pin_base = pin_base; + pin_range->range.npins = npins; + pin_range->pctldev = find_pinctrl_and_add_gpio_range(pinctl_name, + &pin_range->range); + + list_add_tail(&pin_range->node, &chip->pin_ranges); +} + +void gpiochip_remove_pin_ranges(struct gpio_chip *chip) +{ + struct gpio_pin_range *pin_range, *tmp; + + list_for_each_entry_safe(pin_range, tmp, &chip->pin_ranges, node) { + list_del(&pin_range->node); + pinctrl_remove_gpio_range(pin_range->pctldev, + &pin_range->range); + } +} +#else +void gpiochip_add_pin_range(struct gpio_chip *chip, const char *pinctl_name, + unsigned int pin_base, unsigned int npins) {} +void gpiochip_remove_pin_ranges(struct gpio_chip *chip) {} +#endif + /* These "optional" allocation calls help prevent drivers from stomping * on each other, and help provide better diagnostics in debugfs. * They're called even less than the "set direction" calls. diff --git a/drivers/pinctrl/core.c b/drivers/pinctrl/core.c index b1086dcde15d..71db586b2afd 100644 --- a/drivers/pinctrl/core.c +++ b/drivers/pinctrl/core.c @@ -345,6 +345,19 @@ void pinctrl_add_gpio_ranges(struct pinctrl_dev *pctldev, } EXPORT_SYMBOL_GPL(pinctrl_add_gpio_ranges); +struct pinctrl_dev *find_pinctrl_and_add_gpio_range(const char *devname, + struct pinctrl_gpio_range *range) +{ + struct pinctrl_dev *pctldev = get_pinctrl_dev_from_devname(devname); + + if (!pctldev) + return NULL; + + pinctrl_add_gpio_range(pctldev, range); + return pctldev; +} +EXPORT_SYMBOL_GPL(find_pinctrl_and_add_gpio_range); + /** * pinctrl_remove_gpio_range() - remove a range of GPIOs fro a pin controller * @pctldev: pin controller device to remove the range from diff --git a/drivers/pinctrl/devicetree.c b/drivers/pinctrl/devicetree.c index fcb1de45473c..6728ec71cb65 100644 --- a/drivers/pinctrl/devicetree.c +++ b/drivers/pinctrl/devicetree.c @@ -106,6 +106,19 @@ static struct pinctrl_dev *find_pinctrl_by_of_node(struct device_node *np) return NULL; } +struct pinctrl_dev *of_pinctrl_add_gpio_range(struct device_node *np, + struct pinctrl_gpio_range *range) +{ + struct pinctrl_dev *pctldev; + + pctldev = find_pinctrl_by_of_node(np); + if (!pctldev) + return NULL; + + pinctrl_add_gpio_range(pctldev, range); + return pctldev; +} + static int dt_to_map_one_config(struct pinctrl *p, const char *statename, struct device_node *np_config) { diff --git a/include/asm-generic/gpio.h b/include/asm-generic/gpio.h index a9432fc6b8ba..92e5c432421c 100644 --- a/include/asm-generic/gpio.h +++ b/include/asm-generic/gpio.h @@ -5,6 +5,7 @@ #include #include #include +#include #ifdef CONFIG_GPIOLIB @@ -47,6 +48,21 @@ struct seq_file; struct module; struct device_node; +#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; +}; +#endif + /** * struct gpio_chip - abstract a GPIO controller * @label: for diagnostics @@ -134,6 +150,15 @@ struct gpio_chip { int (*of_xlate)(struct gpio_chip *gc, const struct of_phandle_args *gpiospec, u32 *flags); #endif +#ifdef CONFIG_PINCTRL + /* + * If CONFIG_PINCTRL is enabled, then gpio controllers can optionally + * describe the actual pin range which they serve in an SoC. This + * information would be used by pinctrl subsystem to configure + * corresponding pins for gpio usage. + */ + struct list_head pin_ranges; +#endif }; extern const char *gpiochip_is_requested(struct gpio_chip *chip, diff --git a/include/linux/gpio.h b/include/linux/gpio.h index 2e31e8b3a190..a28445992b7f 100644 --- a/include/linux/gpio.h +++ b/include/linux/gpio.h @@ -231,6 +231,9 @@ static inline int irq_to_gpio(unsigned irq) return -EINVAL; } +void gpiochip_add_pin_range(struct gpio_chip *chip, const char *pinctl_name, + unsigned int pin_base, unsigned int npins); +void gpiochip_remove_pin_ranges(struct gpio_chip *chip); #endif #endif /* __LINUX_GPIO_H */ diff --git a/include/linux/pinctrl/pinctrl.h b/include/linux/pinctrl/pinctrl.h index eda04674633d..434e5a94e131 100644 --- a/include/linux/pinctrl/pinctrl.h +++ b/include/linux/pinctrl/pinctrl.h @@ -136,6 +136,23 @@ extern void pinctrl_add_gpio_ranges(struct pinctrl_dev *pctldev, unsigned nranges); extern void pinctrl_remove_gpio_range(struct pinctrl_dev *pctldev, struct pinctrl_gpio_range *range); + +extern struct pinctrl_dev *find_pinctrl_and_add_gpio_range(const char *devname, + struct pinctrl_gpio_range *range); + +#ifdef CONFIG_OF +extern struct pinctrl_dev *of_pinctrl_add_gpio_range(struct device_node *np, + struct pinctrl_gpio_range *range); +#else +static inline +struct pinctrl_dev *of_pinctrl_add_gpio_range(struct device_node *np, + struct pinctrl_gpio_range *range) +{ + return NULL; +} + +#endif /* CONFIG_OF */ + extern const char *pinctrl_dev_get_name(struct pinctrl_dev *pctldev); extern void *pinctrl_dev_get_drvdata(struct pinctrl_dev *pctldev); #else -- cgit v1.2.3-59-g8ed1b From 84e6de813b2d1bdb127943d3c8edf1c1afaa90da Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Mon, 5 Nov 2012 16:10:34 +0100 Subject: Documentation: Describe bindings for STMPE Multi-Functional Device driver Here we add the required documentation for the STMPE Multi-Functional Device (MFD) Device Tree bindings. It describes all of the bindings currently supported by the driver. Acked-by: Arnd Bergmann Acked-by: Linus Walleij Signed-off-by: Lee Jones Signed-off-by: Samuel Ortiz --- Documentation/devicetree/bindings/mfd/stmpe.txt | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 Documentation/devicetree/bindings/mfd/stmpe.txt (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/mfd/stmpe.txt b/Documentation/devicetree/bindings/mfd/stmpe.txt new file mode 100644 index 000000000000..8f0aeda2e326 --- /dev/null +++ b/Documentation/devicetree/bindings/mfd/stmpe.txt @@ -0,0 +1,25 @@ +* STMPE Multi-Functional Device + +Required properties: + - compatible : "st,stmpe[811|1601|2401|2403]" + - reg : I2C address of the device + +Optional properties: + - interrupts : The interrupt outputs from the controller + - interrupt-controller : Marks the device node as an interrupt controller + - interrupt-parent : Specifies which IRQ controller we're connected to + - i2c-client-wake : Marks the input device as wakable + - st,autosleep-timeout : Valid entries (ms); 4, 16, 32, 64, 128, 256, 512 and 1024 + +Example: + + stmpe1601: stmpe1601@40 { + compatible = "st,stmpe1601"; + reg = <0x40>; + interrupts = <26 0x4>; + interrupt-parent = <&gpio6>; + interrupt-controller; + + i2c-client-wake; + st,autosleep-timeout = <1024>; + }; -- cgit v1.2.3-59-g8ed1b From 2f02bc8af3abb846823811af65ec6cc46a4d525d Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Wed, 7 Nov 2012 16:35:00 -0500 Subject: USB: report submission of active URBs This patch (as1633) changes slightly the way usbcore handled submissions of URBs that are already active. It will now return -EBUSY rather than -EINVAL, and it will call WARN_ONCE to draw people's attention to the bug. Signed-off-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- Documentation/usb/error-codes.txt | 2 ++ drivers/usb/core/urb.c | 7 ++++++- 2 files changed, 8 insertions(+), 1 deletion(-) (limited to 'Documentation') diff --git a/Documentation/usb/error-codes.txt b/Documentation/usb/error-codes.txt index 8d1e2a9ebbba..9c3eb845ebe5 100644 --- a/Documentation/usb/error-codes.txt +++ b/Documentation/usb/error-codes.txt @@ -21,6 +21,8 @@ Non-USB-specific: USB-specific: +-EBUSY The URB is already active. + -ENODEV specified USB-device or bus doesn't exist -ENOENT specified interface or endpoint does not exist or diff --git a/drivers/usb/core/urb.c b/drivers/usb/core/urb.c index 3662287e2f4f..e0d9d948218c 100644 --- a/drivers/usb/core/urb.c +++ b/drivers/usb/core/urb.c @@ -321,8 +321,13 @@ int usb_submit_urb(struct urb *urb, gfp_t mem_flags) struct usb_host_endpoint *ep; int is_out; - if (!urb || urb->hcpriv || !urb->complete) + if (!urb || !urb->complete) return -EINVAL; + if (urb->hcpriv) { + WARN_ONCE(1, "URB %p submitted while active\n", urb); + return -EBUSY; + } + dev = urb->dev; if ((!dev) || (dev->state < USB_STATE_UNAUTHENTICATED)) return -ENODEV; -- cgit v1.2.3-59-g8ed1b From 5658a68fb578df8c9b9dd3071b2a56bf92c15324 Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Thu, 20 Sep 2012 15:04:33 +0200 Subject: ARM i.MX25: Add devicetree This adds a i.MX25 dtsi file along with the i.MX25 clock tree documentation. The devicetree should be fairly complete for: - uart - fec - i2c - spi - pwm - nand - gpio - wdog - esdhc - flexcan The more exotic devices currently miss clock bindings. Signed-off-by: Sascha Hauer Acked-by: Shawn Guo --- .../devicetree/bindings/clock/imx25-clock.txt | 162 +++++++ arch/arm/boot/dts/imx25.dtsi | 515 +++++++++++++++++++++ 2 files changed, 677 insertions(+) create mode 100644 Documentation/devicetree/bindings/clock/imx25-clock.txt create mode 100644 arch/arm/boot/dts/imx25.dtsi (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/clock/imx25-clock.txt b/Documentation/devicetree/bindings/clock/imx25-clock.txt new file mode 100644 index 000000000000..c2a3525ecb4e --- /dev/null +++ b/Documentation/devicetree/bindings/clock/imx25-clock.txt @@ -0,0 +1,162 @@ +* Clock bindings for Freescale i.MX25 + +Required properties: +- compatible: Should be "fsl,imx25-ccm" +- reg: Address and length of the register set +- interrupts: Should contain CCM interrupt +- #clock-cells: Should be <1> + +The clock consumer should specify the desired clock by having the clock +ID in its "clocks" phandle cell. The following is a full list of i.MX25 +clocks and IDs. + + Clock ID + --------------------------- + dummy 0 + osc 1 + mpll 2 + upll 3 + mpll_cpu_3_4 4 + cpu_sel 5 + cpu 6 + ahb 7 + usb_div 8 + ipg 9 + per0_sel 10 + per1_sel 11 + per2_sel 12 + per3_sel 13 + per4_sel 14 + per5_sel 15 + per6_sel 16 + per7_sel 17 + per8_sel 18 + per9_sel 19 + per10_sel 20 + per11_sel 21 + per12_sel 22 + per13_sel 23 + per14_sel 24 + per15_sel 25 + per0 26 + per1 27 + per2 28 + per3 29 + per4 30 + per5 31 + per6 32 + per7 33 + per8 34 + per9 35 + per10 36 + per11 37 + per12 38 + per13 39 + per14 40 + per15 41 + csi_ipg_per 42 + epit_ipg_per 43 + esai_ipg_per 44 + esdhc1_ipg_per 45 + esdhc2_ipg_per 46 + gpt_ipg_per 47 + i2c_ipg_per 48 + lcdc_ipg_per 49 + nfc_ipg_per 50 + owire_ipg_per 51 + pwm_ipg_per 52 + sim1_ipg_per 53 + sim2_ipg_per 54 + ssi1_ipg_per 55 + ssi2_ipg_per 56 + uart_ipg_per 57 + ata_ahb 58 + reserved 59 + csi_ahb 60 + emi_ahb 61 + esai_ahb 62 + esdhc1_ahb 63 + esdhc2_ahb 64 + fec_ahb 65 + lcdc_ahb 66 + rtic_ahb 67 + sdma_ahb 68 + slcdc_ahb 69 + usbotg_ahb 70 + reserved 71 + reserved 72 + reserved 73 + reserved 74 + can1_ipg 75 + can2_ipg 76 + csi_ipg 77 + cspi1_ipg 78 + cspi2_ipg 79 + cspi3_ipg 80 + dryice_ipg 81 + ect_ipg 82 + epit1_ipg 83 + epit2_ipg 84 + reserved 85 + esdhc1_ipg 86 + esdhc2_ipg 87 + fec_ipg 88 + reserved 89 + reserved 90 + reserved 91 + gpt1_ipg 92 + gpt2_ipg 93 + gpt3_ipg 94 + gpt4_ipg 95 + reserved 96 + reserved 97 + reserved 98 + iim_ipg 99 + reserved 100 + reserved 101 + kpp_ipg 102 + lcdc_ipg 103 + reserved 104 + pwm1_ipg 105 + pwm2_ipg 106 + pwm3_ipg 107 + pwm4_ipg 108 + rngb_ipg 109 + reserved 110 + scc_ipg 111 + sdma_ipg 112 + sim1_ipg 113 + sim2_ipg 114 + slcdc_ipg 115 + spba_ipg 116 + ssi1_ipg 117 + ssi2_ipg 118 + tsc_ipg 119 + uart1_ipg 120 + uart2_ipg 121 + uart3_ipg 122 + uart4_ipg 123 + uart5_ipg 124 + reserved 125 + wdt_ipg 126 + +Examples: + +clks: ccm@53f80000 { + compatible = "fsl,imx25-ccm"; + reg = <0x53f80000 0x4000>; + interrupts = <31>; + clock-output-names = ... + "uart_ipg", + "uart_serial", + ...; +}; + +uart1: serial@43f90000 { + compatible = "fsl,imx25-uart", "fsl,imx21-uart"; + reg = <0x43f90000 0x4000>; + interrupts = <45>; + clocks = <&clks 79>, <&clks 50>; + clock-names = "ipg", "per"; + status = "disabled"; +}; diff --git a/arch/arm/boot/dts/imx25.dtsi b/arch/arm/boot/dts/imx25.dtsi new file mode 100644 index 000000000000..e1b13ebc96d6 --- /dev/null +++ b/arch/arm/boot/dts/imx25.dtsi @@ -0,0 +1,515 @@ +/* + * Copyright 2012 Sascha Hauer, Pengutronix + * + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + +/include/ "skeleton.dtsi" + +/ { + aliases { + serial0 = &uart1; + serial1 = &uart2; + serial2 = &uart3; + serial3 = &uart4; + serial4 = &uart5; + gpio0 = &gpio1; + gpio1 = &gpio2; + gpio2 = &gpio3; + gpio3 = &gpio4; + usb0 = &usbotg; + usb1 = &usbhost1; + }; + + asic: asic-interrupt-controller@68000000 { + compatible = "fsl,imx25-asic", "fsl,avic"; + interrupt-controller; + #interrupt-cells = <1>; + reg = <0x68000000 0x8000000>; + }; + + clocks { + #address-cells = <1>; + #size-cells = <0>; + + osc { + compatible = "fsl,imx-osc", "fixed-clock"; + clock-frequency = <24000000>; + }; + }; + + soc { + #address-cells = <1>; + #size-cells = <1>; + compatible = "simple-bus"; + interrupt-parent = <&asic>; + ranges; + + aips@43f00000 { /* AIPS1 */ + compatible = "fsl,aips-bus", "simple-bus"; + #address-cells = <1>; + #size-cells = <1>; + reg = <0x43f00000 0x100000>; + ranges; + + i2c1: i2c@43f80000 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "fsl,imx25-i2c", "fsl,imx21-i2c"; + reg = <0x43f80000 0x4000>; + clocks = <&clks 48>; + clock-names = ""; + interrupts = <3>; + status = "disabled"; + }; + + i2c3: i2c@43f84000 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "fsl,imx25-i2c", "fsl,imx21-i2c"; + reg = <0x43f84000 0x4000>; + clocks = <&clks 48>; + clock-names = ""; + interrupts = <10>; + status = "disabled"; + }; + + can1: can@43f88000 { + compatible = "fsl,imx25-flexcan", "fsl,p1010-flexcan"; + reg = <0x43f88000 0x4000>; + interrupts = <43>; + clocks = <&clks 75>, <&clks 75>; + clock-names = "ipg", "per"; + status = "disabled"; + }; + + can2: can@43f8c000 { + compatible = "fsl,imx25-flexcan", "fsl,p1010-flexcan"; + reg = <0x43f8c000 0x4000>; + interrupts = <44>; + clocks = <&clks 76>, <&clks 76>; + clock-names = "ipg", "per"; + status = "disabled"; + }; + + uart1: serial@43f90000 { + compatible = "fsl,imx25-uart", "fsl,imx21-uart"; + reg = <0x43f90000 0x4000>; + interrupts = <45>; + clocks = <&clks 120>, <&clks 57>; + clock-names = "ipg", "per"; + status = "disabled"; + }; + + uart2: serial@43f94000 { + compatible = "fsl,imx25-uart", "fsl,imx21-uart"; + reg = <0x43f94000 0x4000>; + interrupts = <32>; + clocks = <&clks 121>, <&clks 57>; + clock-names = "ipg", "per"; + status = "disabled"; + }; + + i2c2: i2c@43f98000 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "fsl,imx25-i2c", "fsl,imx21-i2c"; + reg = <0x43f98000 0x4000>; + clocks = <&clks 48>; + clock-names = ""; + interrupts = <4>; + status = "disabled"; + }; + + owire@43f9c000 { + #address-cells = <1>; + #size-cells = <0>; + reg = <0x43f9c000 0x4000>; + clocks = <&clks 51>; + clock-names = ""; + interrupts = <2>; + status = "disabled"; + }; + + spi1: cspi@43fa4000 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "fsl,imx25-cspi", "fsl,imx35-cspi"; + reg = <0x43fa4000 0x4000>; + clocks = <&clks 62>; + clock-names = "ipg"; + interrupts = <14>; + status = "disabled"; + }; + + kpp@43fa8000 { + #address-cells = <1>; + #size-cells = <0>; + reg = <0x43fa8000 0x4000>; + clocks = <&clks 102>; + clock-names = ""; + interrupts = <24>; + status = "disabled"; + }; + + iomuxc@43fac000{ + compatible = "fsl,imx25-iomuxc"; + reg = <0x43fac000 0x4000>; + }; + + audmux@43fb0000 { + compatible = "fsl,imx25-audmux", "fsl,imx31-audmux"; + reg = <0x43fb0000 0x4000>; + status = "disabled"; + }; + }; + + spba@50000000 { + compatible = "fsl,spba-bus", "simple-bus"; + #address-cells = <1>; + #size-cells = <1>; + reg = <0x50000000 0x40000>; + ranges; + + spi3: cspi@50004000 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "fsl,imx25-cspi", "fsl,imx35-cspi"; + reg = <0x50004000 0x4000>; + interrupts = <0>; + clocks = <&clks 80>; + clock-names = "ipg"; + status = "disabled"; + }; + + uart4: serial@50008000 { + compatible = "fsl,imx25-uart", "fsl,imx21-uart"; + reg = <0x50008000 0x4000>; + interrupts = <5>; + clocks = <&clks 123>, <&clks 57>; + clock-names = "ipg", "per"; + status = "disabled"; + }; + + uart3: serial@5000c000 { + compatible = "fsl,imx25-uart", "fsl,imx21-uart"; + reg = <0x5000c000 0x4000>; + interrupts = <18>; + clocks = <&clks 122>, <&clks 57>; + clock-names = "ipg", "per"; + status = "disabled"; + }; + + spi2: cspi@50010000 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "fsl,imx25-cspi", "fsl,imx35-cspi"; + reg = <0x50010000 0x4000>; + clocks = <&clks 79>; + clock-names = "ipg"; + interrupts = <13>; + status = "disabled"; + }; + + ssi2: ssi@50014000 { + compatible = "fsl,imx25-ssi", "fsl,imx21-ssi"; + reg = <0x50014000 0x4000>; + interrupts = <11>; + status = "disabled"; + }; + + esai@50018000 { + reg = <0x50018000 0x4000>; + interrupts = <7>; + }; + + uart5: serial@5002c000 { + compatible = "fsl,imx25-uart", "fsl,imx21-uart"; + reg = <0x5002c000 0x4000>; + interrupts = <40>; + clocks = <&clks 124>, <&clks 57>; + clock-names = "ipg", "per"; + status = "disabled"; + }; + + tsc: tsc@50030000 { + compatible = "fsl,imx25-adc", "fsl,imx21-tsc"; + reg = <0x50030000 0x4000>; + interrupts = <46>; + clocks = <&clks 119>; + clock-names = "ipg"; + status = "disabled"; + }; + + ssi1: ssi@50034000 { + compatible = "fsl,imx25-ssi", "fsl,imx21-ssi"; + reg = <0x50034000 0x4000>; + interrupts = <12>; + status = "disabled"; + }; + + fec: ethernet@50038000 { + compatible = "fsl,imx25-fec"; + reg = <0x50038000 0x4000>; + interrupts = <57>; + clocks = <&clks 88>, <&clks 65>; + clock-names = "ipg", "ahb"; + status = "disabled"; + }; + }; + + aips@53f00000 { /* AIPS2 */ + compatible = "fsl,aips-bus", "simple-bus"; + #address-cells = <1>; + #size-cells = <1>; + reg = <0x53f00000 0x100000>; + ranges; + + clks: ccm@53f80000 { + compatible = "fsl,imx25-ccm"; + reg = <0x53f80000 0x4000>; + interrupts = <31>; + #clock-cells = <1>; + }; + + gpt4: timer@53f84000 { + compatible = "fsl,imx25-gpt", "fsl,imx31-gpt"; + reg = <0x53f84000 0x4000>; + clocks = <&clks 9>, <&clks 45>; + clock-names = "ipg", "per"; + interrupts = <1>; + }; + + gpt3: timer@53f88000 { + compatible = "fsl,imx25-gpt", "fsl,imx31-gpt"; + reg = <0x53f88000 0x4000>; + clocks = <&clks 9>, <&clks 47>; + clock-names = "ipg", "per"; + interrupts = <29>; + }; + + gpt2: timer@53f8c000 { + compatible = "fsl,imx25-gpt", "fsl,imx31-gpt"; + reg = <0x53f8c000 0x4000>; + clocks = <&clks 9>, <&clks 47>; + clock-names = "ipg", "per"; + interrupts = <53>; + }; + + gpt1: timer@53f90000 { + compatible = "fsl,imx25-gpt", "fsl,imx31-gpt"; + reg = <0x53f90000 0x4000>; + clocks = <&clks 9>, <&clks 47>; + clock-names = "ipg", "per"; + interrupts = <54>; + }; + + epit1: timer@53f94000 { + compatible = "fsl,imx25-epit"; + reg = <0x53f94000 0x4000>; + interrupts = <28>; + }; + + epit2: timer@53f98000 { + compatible = "fsl,imx25-epit"; + reg = <0x53f98000 0x4000>; + interrupts = <27>; + }; + + gpio4: gpio@53f9c000 { + compatible = "fsl,imx25-gpio", "fsl,imx35-gpio"; + reg = <0x53f9c000 0x4000>; + interrupts = <23>; + gpio-controller; + #gpio-cells = <2>; + interrupt-controller; + #interrupt-cells = <2>; + }; + + pwm2: pwm@53fa0000 { + compatible = "fsl,imx25-pwm", "fsl,imx27-pwm"; + #pwm-cells = <2>; + reg = <0x53fa0000 0x4000>; + clocks = <&clks 106>, <&clks 36>; + clock-names = "ipg", "per"; + interrupts = <36>; + }; + + gpio3: gpio@53fa4000 { + compatible = "fsl,imx25-gpio", "fsl,imx35-gpio"; + reg = <0x53fa4000 0x4000>; + interrupts = <16>; + gpio-controller; + #gpio-cells = <2>; + interrupt-controller; + #interrupt-cells = <2>; + }; + + pwm3: pwm@53fa8000 { + compatible = "fsl,imx25-pwm", "fsl,imx27-pwm"; + #pwm-cells = <2>; + reg = <0x53fa8000 0x4000>; + clocks = <&clks 107>, <&clks 36>; + clock-names = "ipg", "per"; + interrupts = <41>; + }; + + esdhc1: esdhc@53fb4000 { + compatible = "fsl,imx25-esdhc"; + reg = <0x53fb4000 0x4000>; + interrupts = <9>; + clocks = <&clks 86>, <&clks 63>, <&clks 45>; + clock-names = "ipg", "ahb", "per"; + status = "disabled"; + }; + + esdhc2: esdhc@53fb8000 { + compatible = "fsl,imx25-esdhc"; + reg = <0x53fb8000 0x4000>; + interrupts = <8>; + clocks = <&clks 87>, <&clks 64>, <&clks 46>; + clock-names = "ipg", "ahb", "per"; + status = "disabled"; + }; + + lcdc@53fbc000 { + reg = <0x53fbc000 0x4000>; + interrupts = <39>; + clocks = <&clks 103>, <&clks 66>, <&clks 49>; + clock-names = "ipg", "ahb", "per"; + status = "disabled"; + }; + + slcdc@53fc0000 { + reg = <0x53fc0000 0x4000>; + interrupts = <38>; + status = "disabled"; + }; + + pwm4: pwm@53fc8000 { + compatible = "fsl,imx25-pwm", "fsl,imx27-pwm"; + reg = <0x53fc8000 0x4000>; + clocks = <&clks 108>, <&clks 36>; + clock-names = "ipg", "per"; + interrupts = <42>; + }; + + gpio1: gpio@53fcc000 { + compatible = "fsl,imx25-gpio", "fsl,imx35-gpio"; + reg = <0x53fcc000 0x4000>; + interrupts = <52>; + gpio-controller; + #gpio-cells = <2>; + interrupt-controller; + #interrupt-cells = <2>; + }; + + gpio2: gpio@53fd0000 { + compatible = "fsl,imx25-gpio", "fsl,imx35-gpio"; + reg = <0x53fd0000 0x4000>; + interrupts = <51>; + gpio-controller; + #gpio-cells = <2>; + interrupt-controller; + #interrupt-cells = <2>; + }; + + sdma@53fd4000 { + compatible = "fsl,imx25-sdma", "fsl,imx35-sdma"; + reg = <0x53fd4000 0x4000>; + clocks = <&clks 112>, <&clks 68>; + clock-names = "ipg", "ahb"; + interrupts = <34>; + }; + + wdog@53fdc000 { + compatible = "fsl,imx25-wdt", "fsl,imx21-wdt"; + reg = <0x53fdc000 0x4000>; + clocks = <&clks 126>; + clock-names = ""; + interrupts = <55>; + }; + + pwm1: pwm@53fe0000 { + compatible = "fsl,imx25-pwm", "fsl,imx27-pwm"; + #pwm-cells = <2>; + reg = <0x53fe0000 0x4000>; + clocks = <&clks 105>, <&clks 36>; + clock-names = "ipg", "per"; + interrupts = <26>; + }; + + usbphy1: usbphy@1 { + compatible = "nop-usbphy"; + status = "disabled"; + }; + + usbphy2: usbphy@2 { + compatible = "nop-usbphy"; + status = "disabled"; + }; + + usbotg: usb@53ff4000 { + compatible = "fsl,imx25-usb", "fsl,imx27-usb"; + reg = <0x53ff4000 0x0200>; + interrupts = <37>; + clocks = <&clks 9>, <&clks 70>, <&clks 8>; + clock-names = "ipg", "ahb", "per"; + fsl,usbmisc = <&usbmisc 0>; + status = "disabled"; + }; + + usbhost1: usb@53ff4400 { + compatible = "fsl,imx25-usb", "fsl,imx27-usb"; + reg = <0x53ff4400 0x0200>; + interrupts = <35>; + clocks = <&clks 9>, <&clks 70>, <&clks 8>; + clock-names = "ipg", "ahb", "per"; + fsl,usbmisc = <&usbmisc 1>; + status = "disabled"; + }; + + usbmisc: usbmisc@53ff4600 { + #index-cells = <1>; + compatible = "fsl,imx25-usbmisc"; + clocks = <&clks 9>, <&clks 70>, <&clks 8>; + clock-names = "ipg", "ahb", "per"; + reg = <0x53ff4600 0x00f>; + status = "disabled"; + }; + + dryice@53ffc000 { + compatible = "fsl,imx25-dryice", "fsl,imx25-rtc"; + reg = <0x53ffc000 0x4000>; + clocks = <&clks 81>; + clock-names = "ipg"; + interrupts = <25>; + }; + }; + + emi@80000000 { + compatible = "fsl,emi-bus", "simple-bus"; + #address-cells = <1>; + #size-cells = <1>; + reg = <0x80000000 0x3b002000>; + ranges; + + nand@bb000000 { + #address-cells = <1>; + #size-cells = <1>; + + compatible = "fsl,imx25-nand"; + reg = <0xbb000000 0x2000>; + clocks = <&clks 50>; + clock-names = ""; + interrupts = <33>; + status = "disabled"; + }; + }; + }; +}; -- cgit v1.2.3-59-g8ed1b From 94a8fcf9a1a816199efc29546040172aa1383be0 Mon Sep 17 00:00:00 2001 From: Stefan Achatz Date: Sun, 11 Nov 2012 06:20:52 +0100 Subject: HID: roccat: cleanup of kovaplus module Partially removed unneeded informations and data caching. Moved code nearer to format of newer drivers. Added "info" sysfs attribute to support device reset and deprecate other attributes. Signed-off-by: Stefan Achatz Signed-off-by: Jiri Kosina --- .../ABI/obsolete/sysfs-driver-hid-roccat-kovaplus | 41 ++++ .../ABI/testing/sysfs-driver-hid-roccat-kovaplus | 40 +--- drivers/hid/hid-roccat-kovaplus.c | 235 ++++++++++----------- drivers/hid/hid-roccat-kovaplus.h | 14 +- 4 files changed, 167 insertions(+), 163 deletions(-) create mode 100644 Documentation/ABI/obsolete/sysfs-driver-hid-roccat-kovaplus (limited to 'Documentation') diff --git a/Documentation/ABI/obsolete/sysfs-driver-hid-roccat-kovaplus b/Documentation/ABI/obsolete/sysfs-driver-hid-roccat-kovaplus new file mode 100644 index 000000000000..4d9fb26292a2 --- /dev/null +++ b/Documentation/ABI/obsolete/sysfs-driver-hid-roccat-kovaplus @@ -0,0 +1,41 @@ +What: /sys/bus/usb/devices/-:./::./kovaplus/roccatkovaplus/actual_cpi +Date: January 2011 +Contact: Stefan Achatz +Description: The integer value of this attribute ranges from 1-4. + When read, this attribute returns the number of the active + cpi level. + This file is readonly. + Has never been used. If bookkeeping is done, it's done in userland tools. +Users: http://roccat.sourceforge.net + +What: /sys/bus/usb/devices/-:./::./kovaplus/roccatkovaplus/actual_sensitivity_x +Date: January 2011 +Contact: Stefan Achatz +Description: The integer value of this attribute ranges from 1-10. + When read, this attribute returns the number of the actual + sensitivity in x direction. + This file is readonly. + Has never been used. If bookkeeping is done, it's done in userland tools. +Users: http://roccat.sourceforge.net + +What: /sys/bus/usb/devices/-:./::./kovaplus/roccatkovaplus/actual_sensitivity_y +Date: January 2011 +Contact: Stefan Achatz +Description: The integer value of this attribute ranges from 1-10. + When read, this attribute returns the number of the actual + sensitivity in y direction. + This file is readonly. + Has never been used. If bookkeeping is done, it's done in userland tools. +Users: http://roccat.sourceforge.net + +What: /sys/bus/usb/devices/-:./::./kovaplus/roccatkovaplus/firmware_version +Date: January 2011 +Contact: Stefan Achatz +Description: When read, this file returns the raw integer version number of the + firmware reported by the mouse. Using the integer value eases + further usage in other programs. To receive the real version + number the decimal point has to be shifted 2 positions to the + left. E.g. a returned value of 121 means 1.21 + This file is readonly. + Obsoleted by binary sysfs attribute "info". +Users: http://roccat.sourceforge.net diff --git a/Documentation/ABI/testing/sysfs-driver-hid-roccat-kovaplus b/Documentation/ABI/testing/sysfs-driver-hid-roccat-kovaplus index 20f937c9d84f..507f2c7321d5 100644 --- a/Documentation/ABI/testing/sysfs-driver-hid-roccat-kovaplus +++ b/Documentation/ABI/testing/sysfs-driver-hid-roccat-kovaplus @@ -1,12 +1,3 @@ -What: /sys/bus/usb/devices/-:./::./kovaplus/roccatkovaplus/actual_cpi -Date: January 2011 -Contact: Stefan Achatz -Description: The integer value of this attribute ranges from 1-4. - When read, this attribute returns the number of the active - cpi level. - This file is readonly. -Users: http://roccat.sourceforge.net - What: /sys/bus/usb/devices/-:./::./kovaplus/roccatkovaplus/actual_profile Date: January 2011 Contact: Stefan Achatz @@ -18,33 +9,12 @@ Description: The integer value of this attribute ranges from 0-4. active when the mouse is powered on. Users: http://roccat.sourceforge.net -What: /sys/bus/usb/devices/-:./::./kovaplus/roccatkovaplus/actual_sensitivity_x -Date: January 2011 -Contact: Stefan Achatz -Description: The integer value of this attribute ranges from 1-10. - When read, this attribute returns the number of the actual - sensitivity in x direction. - This file is readonly. -Users: http://roccat.sourceforge.net - -What: /sys/bus/usb/devices/-:./::./kovaplus/roccatkovaplus/actual_sensitivity_y -Date: January 2011 +What: /sys/bus/usb/devices/-:./::./kovaplus/roccatkovaplus/info +Date: November 2012 Contact: Stefan Achatz -Description: The integer value of this attribute ranges from 1-10. - When read, this attribute returns the number of the actual - sensitivity in y direction. - This file is readonly. -Users: http://roccat.sourceforge.net - -What: /sys/bus/usb/devices/-:./::./kovaplus/roccatkovaplus/firmware_version -Date: January 2011 -Contact: Stefan Achatz -Description: When read, this file returns the raw integer version number of the - firmware reported by the mouse. Using the integer value eases - further usage in other programs. To receive the real version - number the decimal point has to be shifted 2 positions to the - left. E.g. a returned value of 121 means 1.21 - This file is readonly. +Description: When read, this file returns general data like firmware version. + When written, the device can be reset. + The data is 6 bytes long. Users: http://roccat.sourceforge.net What: /sys/bus/usb/devices/-:./::./kovaplus/roccatkovaplus/profile_buttons diff --git a/drivers/hid/hid-roccat-kovaplus.c b/drivers/hid/hid-roccat-kovaplus.c index ca6527ac655d..1589fd3bcf0f 100644 --- a/drivers/hid/hid-roccat-kovaplus.c +++ b/drivers/hid/hid-roccat-kovaplus.c @@ -70,13 +70,6 @@ static int kovaplus_select_profile(struct usb_device *usb_dev, uint number, return kovaplus_send_control(usb_dev, number, request); } -static int kovaplus_get_info(struct usb_device *usb_dev, - struct kovaplus_info *buf) -{ - return roccat_common2_receive(usb_dev, KOVAPLUS_COMMAND_INFO, - buf, sizeof(struct kovaplus_info)); -} - static int kovaplus_get_profile_settings(struct usb_device *usb_dev, struct kovaplus_profile_settings *buf, uint number) { @@ -88,15 +81,7 @@ static int kovaplus_get_profile_settings(struct usb_device *usb_dev, return retval; return roccat_common2_receive(usb_dev, KOVAPLUS_COMMAND_PROFILE_SETTINGS, - buf, sizeof(struct kovaplus_profile_settings)); -} - -static int kovaplus_set_profile_settings(struct usb_device *usb_dev, - struct kovaplus_profile_settings const *settings) -{ - return roccat_common2_send_with_status(usb_dev, - KOVAPLUS_COMMAND_PROFILE_SETTINGS, - settings, sizeof(struct kovaplus_profile_settings)); + buf, KOVAPLUS_SIZE_PROFILE_SETTINGS); } static int kovaplus_get_profile_buttons(struct usb_device *usb_dev, @@ -110,15 +95,7 @@ static int kovaplus_get_profile_buttons(struct usb_device *usb_dev, return retval; return roccat_common2_receive(usb_dev, KOVAPLUS_COMMAND_PROFILE_BUTTONS, - buf, sizeof(struct kovaplus_profile_buttons)); -} - -static int kovaplus_set_profile_buttons(struct usb_device *usb_dev, - struct kovaplus_profile_buttons const *buttons) -{ - return roccat_common2_send_with_status(usb_dev, - KOVAPLUS_COMMAND_PROFILE_BUTTONS, - buttons, sizeof(struct kovaplus_profile_buttons)); + buf, KOVAPLUS_SIZE_PROFILE_BUTTONS); } /* retval is 0-4 on success, < 0 on error */ @@ -147,122 +124,140 @@ static int kovaplus_set_actual_profile(struct usb_device *usb_dev, &buf, sizeof(struct kovaplus_actual_profile)); } -static ssize_t kovaplus_sysfs_read_profilex_settings(struct file *fp, - struct kobject *kobj, struct bin_attribute *attr, char *buf, - loff_t off, size_t count) +static ssize_t kovaplus_sysfs_read(struct file *fp, struct kobject *kobj, + char *buf, loff_t off, size_t count, + size_t real_size, uint command) { struct device *dev = container_of(kobj, struct device, kobj)->parent->parent; struct kovaplus_device *kovaplus = hid_get_drvdata(dev_get_drvdata(dev)); + struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev)); + int retval; - if (off >= sizeof(struct kovaplus_profile_settings)) + if (off >= real_size) return 0; - if (off + count > sizeof(struct kovaplus_profile_settings)) - count = sizeof(struct kovaplus_profile_settings) - off; + if (off != 0 || count != real_size) + return -EINVAL; mutex_lock(&kovaplus->kovaplus_lock); - memcpy(buf, ((char const *)&kovaplus->profile_settings[*(uint *)(attr->private)]) + off, - count); + retval = roccat_common2_receive(usb_dev, command, buf, real_size); mutex_unlock(&kovaplus->kovaplus_lock); - return count; + if (retval) + return retval; + + return real_size; } -static ssize_t kovaplus_sysfs_write_profile_settings(struct file *fp, - struct kobject *kobj, struct bin_attribute *attr, char *buf, - loff_t off, size_t count) +static ssize_t kovaplus_sysfs_write(struct file *fp, struct kobject *kobj, + void const *buf, loff_t off, size_t count, + size_t real_size, uint command) { struct device *dev = container_of(kobj, struct device, kobj)->parent->parent; struct kovaplus_device *kovaplus = hid_get_drvdata(dev_get_drvdata(dev)); struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev)); - int retval = 0; - int difference; - int profile_index; - struct kovaplus_profile_settings *profile_settings; + int retval; - if (off != 0 || count != sizeof(struct kovaplus_profile_settings)) + if (off != 0 || count != real_size) return -EINVAL; - profile_index = ((struct kovaplus_profile_settings const *)buf)->profile_index; - profile_settings = &kovaplus->profile_settings[profile_index]; - mutex_lock(&kovaplus->kovaplus_lock); - difference = memcmp(buf, profile_settings, - sizeof(struct kovaplus_profile_settings)); - if (difference) { - retval = kovaplus_set_profile_settings(usb_dev, - (struct kovaplus_profile_settings const *)buf); - if (!retval) - memcpy(profile_settings, buf, - sizeof(struct kovaplus_profile_settings)); - } + retval = roccat_common2_send_with_status(usb_dev, command, + buf, real_size); mutex_unlock(&kovaplus->kovaplus_lock); if (retval) return retval; - return sizeof(struct kovaplus_profile_settings); + return real_size; } -static ssize_t kovaplus_sysfs_read_profilex_buttons(struct file *fp, - struct kobject *kobj, struct bin_attribute *attr, char *buf, - loff_t off, size_t count) -{ - struct device *dev = - container_of(kobj, struct device, kobj)->parent->parent; - struct kovaplus_device *kovaplus = hid_get_drvdata(dev_get_drvdata(dev)); +#define KOVAPLUS_SYSFS_W(thingy, THINGY) \ +static ssize_t kovaplus_sysfs_write_ ## thingy(struct file *fp, \ + struct kobject *kobj, struct bin_attribute *attr, char *buf, \ + loff_t off, size_t count) \ +{ \ + return kovaplus_sysfs_write(fp, kobj, buf, off, count, \ + KOVAPLUS_SIZE_ ## THINGY, KOVAPLUS_COMMAND_ ## THINGY); \ +} - if (off >= sizeof(struct kovaplus_profile_buttons)) - return 0; +#define KOVAPLUS_SYSFS_R(thingy, THINGY) \ +static ssize_t kovaplus_sysfs_read_ ## thingy(struct file *fp, \ + struct kobject *kobj, struct bin_attribute *attr, char *buf, \ + loff_t off, size_t count) \ +{ \ + return kovaplus_sysfs_read(fp, kobj, buf, off, count, \ + KOVAPLUS_SIZE_ ## THINGY, KOVAPLUS_COMMAND_ ## THINGY); \ +} - if (off + count > sizeof(struct kovaplus_profile_buttons)) - count = sizeof(struct kovaplus_profile_buttons) - off; +#define KOVAPLUS_SYSFS_RW(thingy, THINGY) \ +KOVAPLUS_SYSFS_W(thingy, THINGY) \ +KOVAPLUS_SYSFS_R(thingy, THINGY) - mutex_lock(&kovaplus->kovaplus_lock); - memcpy(buf, ((char const *)&kovaplus->profile_buttons[*(uint *)(attr->private)]) + off, - count); - mutex_unlock(&kovaplus->kovaplus_lock); +#define KOVAPLUS_BIN_ATTRIBUTE_RW(thingy, THINGY) \ +{ \ + .attr = { .name = #thingy, .mode = 0660 }, \ + .size = KOVAPLUS_SIZE_ ## THINGY, \ + .read = kovaplus_sysfs_read_ ## thingy, \ + .write = kovaplus_sysfs_write_ ## thingy \ +} + +#define KOVAPLUS_BIN_ATTRIBUTE_R(thingy, THINGY) \ +{ \ + .attr = { .name = #thingy, .mode = 0440 }, \ + .size = KOVAPLUS_SIZE_ ## THINGY, \ + .read = kovaplus_sysfs_read_ ## thingy, \ +} - return count; +#define KOVAPLUS_BIN_ATTRIBUTE_W(thingy, THINGY) \ +{ \ + .attr = { .name = #thingy, .mode = 0220 }, \ + .size = KOVAPLUS_SIZE_ ## THINGY, \ + .write = kovaplus_sysfs_write_ ## thingy \ } -static ssize_t kovaplus_sysfs_write_profile_buttons(struct file *fp, +KOVAPLUS_SYSFS_RW(info, INFO) +KOVAPLUS_SYSFS_W(profile_settings, PROFILE_SETTINGS) +KOVAPLUS_SYSFS_W(profile_buttons, PROFILE_BUTTONS) + +static ssize_t kovaplus_sysfs_read_profilex_settings(struct file *fp, struct kobject *kobj, struct bin_attribute *attr, char *buf, loff_t off, size_t count) { struct device *dev = container_of(kobj, struct device, kobj)->parent->parent; - struct kovaplus_device *kovaplus = hid_get_drvdata(dev_get_drvdata(dev)); struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev)); - int retval = 0; - int difference; - uint profile_index; - struct kovaplus_profile_buttons *profile_buttons; + ssize_t retval; - if (off != 0 || count != sizeof(struct kovaplus_profile_buttons)) - return -EINVAL; + retval = kovaplus_select_profile(usb_dev, *(uint *)(attr->private), + KOVAPLUS_CONTROL_REQUEST_PROFILE_SETTINGS); + if (retval) + return retval; - profile_index = ((struct kovaplus_profile_buttons const *)buf)->profile_index; - profile_buttons = &kovaplus->profile_buttons[profile_index]; + return kovaplus_sysfs_read(fp, kobj, buf, off, count, + KOVAPLUS_SIZE_PROFILE_SETTINGS, + KOVAPLUS_COMMAND_PROFILE_SETTINGS); +} - mutex_lock(&kovaplus->kovaplus_lock); - difference = memcmp(buf, profile_buttons, - sizeof(struct kovaplus_profile_buttons)); - if (difference) { - retval = kovaplus_set_profile_buttons(usb_dev, - (struct kovaplus_profile_buttons const *)buf); - if (!retval) - memcpy(profile_buttons, buf, - sizeof(struct kovaplus_profile_buttons)); - } - mutex_unlock(&kovaplus->kovaplus_lock); +static ssize_t kovaplus_sysfs_read_profilex_buttons(struct file *fp, + struct kobject *kobj, struct bin_attribute *attr, char *buf, + loff_t off, size_t count) +{ + struct device *dev = + container_of(kobj, struct device, kobj)->parent->parent; + struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev)); + ssize_t retval; + retval = kovaplus_select_profile(usb_dev, *(uint *)(attr->private), + KOVAPLUS_CONTROL_REQUEST_PROFILE_BUTTONS); if (retval) return retval; - return sizeof(struct kovaplus_profile_buttons); + return kovaplus_sysfs_read(fp, kobj, buf, off, count, + KOVAPLUS_SIZE_PROFILE_BUTTONS, + KOVAPLUS_COMMAND_PROFILE_BUTTONS); } static ssize_t kovaplus_sysfs_show_actual_profile(struct device *dev, @@ -342,9 +337,20 @@ static ssize_t kovaplus_sysfs_show_actual_sensitivity_y(struct device *dev, static ssize_t kovaplus_sysfs_show_firmware_version(struct device *dev, struct device_attribute *attr, char *buf) { - struct kovaplus_device *kovaplus = - hid_get_drvdata(dev_get_drvdata(dev->parent->parent)); - return snprintf(buf, PAGE_SIZE, "%d\n", kovaplus->info.firmware_version); + struct kovaplus_device *kovaplus; + struct usb_device *usb_dev; + struct kovaplus_info info; + + dev = dev->parent->parent; + kovaplus = hid_get_drvdata(dev_get_drvdata(dev)); + usb_dev = interface_to_usbdev(to_usb_interface(dev)); + + mutex_lock(&kovaplus->kovaplus_lock); + roccat_common2_receive(usb_dev, KOVAPLUS_COMMAND_INFO, + &info, KOVAPLUS_SIZE_INFO); + mutex_unlock(&kovaplus->kovaplus_lock); + + return snprintf(buf, PAGE_SIZE, "%d\n", info.firmware_version); } static struct device_attribute kovaplus_attributes[] = { @@ -363,73 +369,66 @@ static struct device_attribute kovaplus_attributes[] = { }; static struct bin_attribute kovaplus_bin_attributes[] = { - { - .attr = { .name = "profile_settings", .mode = 0220 }, - .size = sizeof(struct kovaplus_profile_settings), - .write = kovaplus_sysfs_write_profile_settings - }, + KOVAPLUS_BIN_ATTRIBUTE_RW(info, INFO), + KOVAPLUS_BIN_ATTRIBUTE_W(profile_settings, PROFILE_SETTINGS), + KOVAPLUS_BIN_ATTRIBUTE_W(profile_buttons, PROFILE_BUTTONS), { .attr = { .name = "profile1_settings", .mode = 0440 }, - .size = sizeof(struct kovaplus_profile_settings), + .size = KOVAPLUS_SIZE_PROFILE_SETTINGS, .read = kovaplus_sysfs_read_profilex_settings, .private = &profile_numbers[0] }, { .attr = { .name = "profile2_settings", .mode = 0440 }, - .size = sizeof(struct kovaplus_profile_settings), + .size = KOVAPLUS_SIZE_PROFILE_SETTINGS, .read = kovaplus_sysfs_read_profilex_settings, .private = &profile_numbers[1] }, { .attr = { .name = "profile3_settings", .mode = 0440 }, - .size = sizeof(struct kovaplus_profile_settings), + .size = KOVAPLUS_SIZE_PROFILE_SETTINGS, .read = kovaplus_sysfs_read_profilex_settings, .private = &profile_numbers[2] }, { .attr = { .name = "profile4_settings", .mode = 0440 }, - .size = sizeof(struct kovaplus_profile_settings), + .size = KOVAPLUS_SIZE_PROFILE_SETTINGS, .read = kovaplus_sysfs_read_profilex_settings, .private = &profile_numbers[3] }, { .attr = { .name = "profile5_settings", .mode = 0440 }, - .size = sizeof(struct kovaplus_profile_settings), + .size = KOVAPLUS_SIZE_PROFILE_SETTINGS, .read = kovaplus_sysfs_read_profilex_settings, .private = &profile_numbers[4] }, - { - .attr = { .name = "profile_buttons", .mode = 0220 }, - .size = sizeof(struct kovaplus_profile_buttons), - .write = kovaplus_sysfs_write_profile_buttons - }, { .attr = { .name = "profile1_buttons", .mode = 0440 }, - .size = sizeof(struct kovaplus_profile_buttons), + .size = KOVAPLUS_SIZE_PROFILE_BUTTONS, .read = kovaplus_sysfs_read_profilex_buttons, .private = &profile_numbers[0] }, { .attr = { .name = "profile2_buttons", .mode = 0440 }, - .size = sizeof(struct kovaplus_profile_buttons), + .size = KOVAPLUS_SIZE_PROFILE_BUTTONS, .read = kovaplus_sysfs_read_profilex_buttons, .private = &profile_numbers[1] }, { .attr = { .name = "profile3_buttons", .mode = 0440 }, - .size = sizeof(struct kovaplus_profile_buttons), + .size = KOVAPLUS_SIZE_PROFILE_BUTTONS, .read = kovaplus_sysfs_read_profilex_buttons, .private = &profile_numbers[2] }, { .attr = { .name = "profile4_buttons", .mode = 0440 }, - .size = sizeof(struct kovaplus_profile_buttons), + .size = KOVAPLUS_SIZE_PROFILE_BUTTONS, .read = kovaplus_sysfs_read_profilex_buttons, .private = &profile_numbers[3] }, { .attr = { .name = "profile5_buttons", .mode = 0440 }, - .size = sizeof(struct kovaplus_profile_buttons), + .size = KOVAPLUS_SIZE_PROFILE_BUTTONS, .read = kovaplus_sysfs_read_profilex_buttons, .private = &profile_numbers[4] }, @@ -444,10 +443,6 @@ static int kovaplus_init_kovaplus_device_struct(struct usb_device *usb_dev, mutex_init(&kovaplus->kovaplus_lock); - retval = kovaplus_get_info(usb_dev, &kovaplus->info); - if (retval) - return retval; - for (i = 0; i < 5; ++i) { msleep(wait); retval = kovaplus_get_profile_settings(usb_dev, diff --git a/drivers/hid/hid-roccat-kovaplus.h b/drivers/hid/hid-roccat-kovaplus.h index f82daa1cdcb9..1189573daee2 100644 --- a/drivers/hid/hid-roccat-kovaplus.h +++ b/drivers/hid/hid-roccat-kovaplus.h @@ -14,6 +14,12 @@ #include +enum { + KOVAPLUS_SIZE_INFO = 0x06, + KOVAPLUS_SIZE_PROFILE_SETTINGS = 0x10, + KOVAPLUS_SIZE_PROFILE_BUTTONS = 0x17, +}; + enum kovaplus_control_requests { /* write; value = profile number range 0-4 */ KOVAPLUS_CONTROL_REQUEST_PROFILE_SETTINGS = 0x10, @@ -53,13 +59,6 @@ struct kovaplus_info { uint8_t unknown[3]; } __packed; -/* writes 1 on plugin */ -struct kovaplus_a { - uint8_t command; /* KOVAPLUS_COMMAND_A */ - uint8_t size; /* 3 */ - uint8_t unknown; -} __packed; - enum kovaplus_commands { KOVAPLUS_COMMAND_ACTUAL_PROFILE = 0x5, KOVAPLUS_COMMAND_PROFILE_SETTINGS = 0x6, @@ -125,7 +124,6 @@ struct kovaplus_device { int roccat_claimed; int chrdev_minor; struct mutex kovaplus_lock; - struct kovaplus_info info; struct kovaplus_profile_settings profile_settings[5]; struct kovaplus_profile_buttons profile_buttons[5]; }; -- cgit v1.2.3-59-g8ed1b From be34380ef818c182860c06f048cbea821203f9b7 Mon Sep 17 00:00:00 2001 From: Stefan Achatz Date: Sun, 11 Nov 2012 06:20:58 +0100 Subject: HID: roccat: cleanup of pyra module Partially removed unneeded informations and data caching. Moved code nearer to format of newer drivers. Added "info" sysfs attribute to support device reset and deprecate "firmware_version" attribute. Signed-off-by: Stefan Achatz Signed-off-by: Jiri Kosina --- .../ABI/obsolete/sysfs-driver-hid-roccat-pyra | 48 +++ .../ABI/testing/sysfs-driver-hid-roccat-pyra | 45 --- drivers/hid/hid-roccat-pyra.c | 340 +++++++++------------ drivers/hid/hid-roccat-pyra.h | 22 +- 4 files changed, 198 insertions(+), 257 deletions(-) create mode 100644 Documentation/ABI/obsolete/sysfs-driver-hid-roccat-pyra (limited to 'Documentation') diff --git a/Documentation/ABI/obsolete/sysfs-driver-hid-roccat-pyra b/Documentation/ABI/obsolete/sysfs-driver-hid-roccat-pyra new file mode 100644 index 000000000000..0a661b39e701 --- /dev/null +++ b/Documentation/ABI/obsolete/sysfs-driver-hid-roccat-pyra @@ -0,0 +1,48 @@ +What: /sys/bus/usb/devices/-:./::./pyra/roccatpyra/actual_cpi +Date: August 2010 +Contact: Stefan Achatz +Description: It is possible to switch the cpi setting of the mouse with the + press of a button. + When read, this file returns the raw number of the actual cpi + setting reported by the mouse. This number has to be further + processed to receive the real dpi value. + + VALUE DPI + 1 400 + 2 800 + 4 1600 + + This file is readonly. + Has never been used. If bookkeeping is done, it's done in userland tools. +Users: http://roccat.sourceforge.net + +What: /sys/bus/usb/devices/-:./::./pyra/roccatpyra/actual_profile +Date: August 2010 +Contact: Stefan Achatz +Description: When read, this file returns the number of the actual profile in + range 0-4. + This file is readonly. + Please use binary attribute "settings" which provides this information. +Users: http://roccat.sourceforge.net + +What: /sys/bus/usb/devices/-:./::./pyra/roccatpyra/firmware_version +Date: August 2010 +Contact: Stefan Achatz +Description: When read, this file returns the raw integer version number of the + firmware reported by the mouse. Using the integer value eases + further usage in other programs. To receive the real version + number the decimal point has to be shifted 2 positions to the + left. E.g. a returned value of 138 means 1.38 + This file is readonly. + Please use binary attribute "info" which provides this information. +Users: http://roccat.sourceforge.net + +What: /sys/bus/usb/devices/-:./::./pyra/roccatpyra/startup_profile +Date: August 2010 +Contact: Stefan Achatz +Description: The integer value of this attribute ranges from 0-4. + When read, this attribute returns the number of the profile + that's active when the mouse is powered on. + This file is readonly. + Please use binary attribute "settings" which provides this information. +Users: http://roccat.sourceforge.net \ No newline at end of file diff --git a/Documentation/ABI/testing/sysfs-driver-hid-roccat-pyra b/Documentation/ABI/testing/sysfs-driver-hid-roccat-pyra index 3f8de50e4ff1..b0fab8bcf247 100644 --- a/Documentation/ABI/testing/sysfs-driver-hid-roccat-pyra +++ b/Documentation/ABI/testing/sysfs-driver-hid-roccat-pyra @@ -1,39 +1,3 @@ -What: /sys/bus/usb/devices/-:./::./pyra/roccatpyra/actual_cpi -Date: August 2010 -Contact: Stefan Achatz -Description: It is possible to switch the cpi setting of the mouse with the - press of a button. - When read, this file returns the raw number of the actual cpi - setting reported by the mouse. This number has to be further - processed to receive the real dpi value. - - VALUE DPI - 1 400 - 2 800 - 4 1600 - - This file is readonly. -Users: http://roccat.sourceforge.net - -What: /sys/bus/usb/devices/-:./::./pyra/roccatpyra/actual_profile -Date: August 2010 -Contact: Stefan Achatz -Description: When read, this file returns the number of the actual profile in - range 0-4. - This file is readonly. -Users: http://roccat.sourceforge.net - -What: /sys/bus/usb/devices/-:./::./pyra/roccatpyra/firmware_version -Date: August 2010 -Contact: Stefan Achatz -Description: When read, this file returns the raw integer version number of the - firmware reported by the mouse. Using the integer value eases - further usage in other programs. To receive the real version - number the decimal point has to be shifted 2 positions to the - left. E.g. a returned value of 138 means 1.38 - This file is readonly. -Users: http://roccat.sourceforge.net - What: /sys/bus/usb/devices/-:./::./pyra/roccatpyra/profile_settings Date: August 2010 Contact: Stefan Achatz @@ -86,15 +50,6 @@ Description: The mouse can store 5 profiles which can be switched by the This file is readonly. Users: http://roccat.sourceforge.net -What: /sys/bus/usb/devices/-:./::./pyra/roccatpyra/startup_profile -Date: August 2010 -Contact: Stefan Achatz -Description: The integer value of this attribute ranges from 0-4. - When read, this attribute returns the number of the profile - that's active when the mouse is powered on. - This file is readonly. -Users: http://roccat.sourceforge.net - What: /sys/bus/usb/devices/-:./::./pyra/roccatpyra/settings Date: August 2010 Contact: Stefan Achatz diff --git a/drivers/hid/hid-roccat-pyra.c b/drivers/hid/hid-roccat-pyra.c index 1317c177a3e2..76199fa727ed 100644 --- a/drivers/hid/hid-roccat-pyra.c +++ b/drivers/hid/hid-roccat-pyra.c @@ -66,48 +66,14 @@ static int pyra_get_profile_settings(struct usb_device *usb_dev, if (retval) return retval; return roccat_common2_receive(usb_dev, PYRA_COMMAND_PROFILE_SETTINGS, - buf, sizeof(struct pyra_profile_settings)); -} - -static int pyra_get_profile_buttons(struct usb_device *usb_dev, - struct pyra_profile_buttons *buf, int number) -{ - int retval; - retval = pyra_send_control(usb_dev, number, - PYRA_CONTROL_REQUEST_PROFILE_BUTTONS); - if (retval) - return retval; - return roccat_common2_receive(usb_dev, PYRA_COMMAND_PROFILE_BUTTONS, - buf, sizeof(struct pyra_profile_buttons)); + buf, PYRA_SIZE_PROFILE_SETTINGS); } static int pyra_get_settings(struct usb_device *usb_dev, struct pyra_settings *buf) { return roccat_common2_receive(usb_dev, PYRA_COMMAND_SETTINGS, - buf, sizeof(struct pyra_settings)); -} - -static int pyra_get_info(struct usb_device *usb_dev, struct pyra_info *buf) -{ - return roccat_common2_receive(usb_dev, PYRA_COMMAND_INFO, - buf, sizeof(struct pyra_info)); -} - -static int pyra_set_profile_settings(struct usb_device *usb_dev, - struct pyra_profile_settings const *settings) -{ - return roccat_common2_send_with_status(usb_dev, - PYRA_COMMAND_PROFILE_SETTINGS, settings, - sizeof(struct pyra_profile_settings)); -} - -static int pyra_set_profile_buttons(struct usb_device *usb_dev, - struct pyra_profile_buttons const *buttons) -{ - return roccat_common2_send_with_status(usb_dev, - PYRA_COMMAND_PROFILE_BUTTONS, buttons, - sizeof(struct pyra_profile_buttons)); + buf, PYRA_SIZE_SETTINGS); } static int pyra_set_settings(struct usb_device *usb_dev, @@ -115,146 +81,143 @@ static int pyra_set_settings(struct usb_device *usb_dev, { return roccat_common2_send_with_status(usb_dev, PYRA_COMMAND_SETTINGS, settings, - sizeof(struct pyra_settings)); + PYRA_SIZE_SETTINGS); } -static ssize_t pyra_sysfs_read_profilex_settings(struct file *fp, - struct kobject *kobj, struct bin_attribute *attr, char *buf, - loff_t off, size_t count) +static ssize_t pyra_sysfs_read(struct file *fp, struct kobject *kobj, + char *buf, loff_t off, size_t count, + size_t real_size, uint command) { struct device *dev = container_of(kobj, struct device, kobj)->parent->parent; struct pyra_device *pyra = hid_get_drvdata(dev_get_drvdata(dev)); + struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev)); + int retval; - if (off >= sizeof(struct pyra_profile_settings)) + if (off >= real_size) return 0; - if (off + count > sizeof(struct pyra_profile_settings)) - count = sizeof(struct pyra_profile_settings) - off; + if (off != 0 || count != real_size) + return -EINVAL; mutex_lock(&pyra->pyra_lock); - memcpy(buf, ((char const *)&pyra->profile_settings[*(uint *)(attr->private)]) + off, - count); + retval = roccat_common2_receive(usb_dev, command, buf, real_size); mutex_unlock(&pyra->pyra_lock); - return count; + if (retval) + return retval; + + return real_size; } -static ssize_t pyra_sysfs_read_profilex_buttons(struct file *fp, - struct kobject *kobj, struct bin_attribute *attr, char *buf, - loff_t off, size_t count) +static ssize_t pyra_sysfs_write(struct file *fp, struct kobject *kobj, + void const *buf, loff_t off, size_t count, + size_t real_size, uint command) { struct device *dev = container_of(kobj, struct device, kobj)->parent->parent; struct pyra_device *pyra = hid_get_drvdata(dev_get_drvdata(dev)); + struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev)); + int retval; - if (off >= sizeof(struct pyra_profile_buttons)) - return 0; - - if (off + count > sizeof(struct pyra_profile_buttons)) - count = sizeof(struct pyra_profile_buttons) - off; + if (off != 0 || count != real_size) + return -EINVAL; mutex_lock(&pyra->pyra_lock); - memcpy(buf, ((char const *)&pyra->profile_buttons[*(uint *)(attr->private)]) + off, - count); + retval = roccat_common2_send_with_status(usb_dev, command, (void *)buf, real_size); mutex_unlock(&pyra->pyra_lock); - return count; + if (retval) + return retval; + + return real_size; } -static ssize_t pyra_sysfs_write_profile_settings(struct file *fp, - struct kobject *kobj, struct bin_attribute *attr, char *buf, - loff_t off, size_t count) -{ - struct device *dev = - container_of(kobj, struct device, kobj)->parent->parent; - struct pyra_device *pyra = hid_get_drvdata(dev_get_drvdata(dev)); - struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev)); - int retval = 0; - int difference; - int profile_number; - struct pyra_profile_settings *profile_settings; +#define PYRA_SYSFS_W(thingy, THINGY) \ +static ssize_t pyra_sysfs_write_ ## thingy(struct file *fp, \ + struct kobject *kobj, struct bin_attribute *attr, char *buf, \ + loff_t off, size_t count) \ +{ \ + return pyra_sysfs_write(fp, kobj, buf, off, count, \ + PYRA_SIZE_ ## THINGY, PYRA_COMMAND_ ## THINGY); \ +} - if (off != 0 || count != sizeof(struct pyra_profile_settings)) - return -EINVAL; +#define PYRA_SYSFS_R(thingy, THINGY) \ +static ssize_t pyra_sysfs_read_ ## thingy(struct file *fp, \ + struct kobject *kobj, struct bin_attribute *attr, char *buf, \ + loff_t off, size_t count) \ +{ \ + return pyra_sysfs_read(fp, kobj, buf, off, count, \ + PYRA_SIZE_ ## THINGY, PYRA_COMMAND_ ## THINGY); \ +} - profile_number = ((struct pyra_profile_settings const *)buf)->number; - profile_settings = &pyra->profile_settings[profile_number]; +#define PYRA_SYSFS_RW(thingy, THINGY) \ +PYRA_SYSFS_W(thingy, THINGY) \ +PYRA_SYSFS_R(thingy, THINGY) - mutex_lock(&pyra->pyra_lock); - difference = memcmp(buf, profile_settings, - sizeof(struct pyra_profile_settings)); - if (difference) { - retval = pyra_set_profile_settings(usb_dev, - (struct pyra_profile_settings const *)buf); - if (!retval) - memcpy(profile_settings, buf, - sizeof(struct pyra_profile_settings)); - } - mutex_unlock(&pyra->pyra_lock); +#define PYRA_BIN_ATTRIBUTE_RW(thingy, THINGY) \ +{ \ + .attr = { .name = #thingy, .mode = 0660 }, \ + .size = PYRA_SIZE_ ## THINGY, \ + .read = pyra_sysfs_read_ ## thingy, \ + .write = pyra_sysfs_write_ ## thingy \ +} - if (retval) - return retval; +#define PYRA_BIN_ATTRIBUTE_R(thingy, THINGY) \ +{ \ + .attr = { .name = #thingy, .mode = 0440 }, \ + .size = PYRA_SIZE_ ## THINGY, \ + .read = pyra_sysfs_read_ ## thingy, \ +} - return sizeof(struct pyra_profile_settings); +#define PYRA_BIN_ATTRIBUTE_W(thingy, THINGY) \ +{ \ + .attr = { .name = #thingy, .mode = 0220 }, \ + .size = PYRA_SIZE_ ## THINGY, \ + .write = pyra_sysfs_write_ ## thingy \ } -static ssize_t pyra_sysfs_write_profile_buttons(struct file *fp, +PYRA_SYSFS_RW(info, INFO) +PYRA_SYSFS_W(profile_settings, PROFILE_SETTINGS) +PYRA_SYSFS_W(profile_buttons, PROFILE_BUTTONS) +PYRA_SYSFS_R(settings, SETTINGS) + +static ssize_t pyra_sysfs_read_profilex_settings(struct file *fp, struct kobject *kobj, struct bin_attribute *attr, char *buf, loff_t off, size_t count) { struct device *dev = container_of(kobj, struct device, kobj)->parent->parent; - struct pyra_device *pyra = hid_get_drvdata(dev_get_drvdata(dev)); struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev)); - int retval = 0; - int difference; - int profile_number; - struct pyra_profile_buttons *profile_buttons; - - if (off != 0 || count != sizeof(struct pyra_profile_buttons)) - return -EINVAL; - - profile_number = ((struct pyra_profile_buttons const *)buf)->number; - profile_buttons = &pyra->profile_buttons[profile_number]; - - mutex_lock(&pyra->pyra_lock); - difference = memcmp(buf, profile_buttons, - sizeof(struct pyra_profile_buttons)); - if (difference) { - retval = pyra_set_profile_buttons(usb_dev, - (struct pyra_profile_buttons const *)buf); - if (!retval) - memcpy(profile_buttons, buf, - sizeof(struct pyra_profile_buttons)); - } - mutex_unlock(&pyra->pyra_lock); + ssize_t retval; + retval = pyra_send_control(usb_dev, *(uint *)(attr->private), + PYRA_CONTROL_REQUEST_PROFILE_SETTINGS); if (retval) return retval; - return sizeof(struct pyra_profile_buttons); + return pyra_sysfs_read(fp, kobj, buf, off, count, + PYRA_SIZE_PROFILE_SETTINGS, + PYRA_COMMAND_PROFILE_SETTINGS); } -static ssize_t pyra_sysfs_read_settings(struct file *fp, +static ssize_t pyra_sysfs_read_profilex_buttons(struct file *fp, struct kobject *kobj, struct bin_attribute *attr, char *buf, loff_t off, size_t count) { struct device *dev = container_of(kobj, struct device, kobj)->parent->parent; - struct pyra_device *pyra = hid_get_drvdata(dev_get_drvdata(dev)); - - if (off >= sizeof(struct pyra_settings)) - return 0; - - if (off + count > sizeof(struct pyra_settings)) - count = sizeof(struct pyra_settings) - off; + struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev)); + ssize_t retval; - mutex_lock(&pyra->pyra_lock); - memcpy(buf, ((char const *)&pyra->settings) + off, count); - mutex_unlock(&pyra->pyra_lock); + retval = pyra_send_control(usb_dev, *(uint *)(attr->private), + PYRA_CONTROL_REQUEST_PROFILE_BUTTONS); + if (retval) + return retval; - return count; + return pyra_sysfs_read(fp, kobj, buf, off, count, + PYRA_SIZE_PROFILE_BUTTONS, + PYRA_COMMAND_PROFILE_BUTTONS); } static ssize_t pyra_sysfs_write_settings(struct file *fp, @@ -266,35 +229,32 @@ static ssize_t pyra_sysfs_write_settings(struct file *fp, struct pyra_device *pyra = hid_get_drvdata(dev_get_drvdata(dev)); struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev)); int retval = 0; - int difference; struct pyra_roccat_report roccat_report; + struct pyra_settings const *settings; - if (off != 0 || count != sizeof(struct pyra_settings)) + if (off != 0 || count != PYRA_SIZE_SETTINGS) return -EINVAL; mutex_lock(&pyra->pyra_lock); - difference = memcmp(buf, &pyra->settings, sizeof(struct pyra_settings)); - if (difference) { - retval = pyra_set_settings(usb_dev, - (struct pyra_settings const *)buf); - if (retval) { - mutex_unlock(&pyra->pyra_lock); - return retval; - } - - memcpy(&pyra->settings, buf, - sizeof(struct pyra_settings)); - profile_activated(pyra, pyra->settings.startup_profile); + settings = (struct pyra_settings const *)buf; - roccat_report.type = PYRA_MOUSE_EVENT_BUTTON_TYPE_PROFILE_2; - roccat_report.value = pyra->settings.startup_profile + 1; - roccat_report.key = 0; - roccat_report_event(pyra->chrdev_minor, - (uint8_t const *)&roccat_report); + retval = pyra_set_settings(usb_dev, settings); + if (retval) { + mutex_unlock(&pyra->pyra_lock); + return retval; } + + profile_activated(pyra, settings->startup_profile); + + roccat_report.type = PYRA_MOUSE_EVENT_BUTTON_TYPE_PROFILE_2; + roccat_report.value = settings->startup_profile + 1; + roccat_report.key = 0; + roccat_report_event(pyra->chrdev_minor, + (uint8_t const *)&roccat_report); + mutex_unlock(&pyra->pyra_lock); - return sizeof(struct pyra_settings); + return PYRA_SIZE_SETTINGS; } @@ -311,23 +271,34 @@ static ssize_t pyra_sysfs_show_actual_profile(struct device *dev, { struct pyra_device *pyra = hid_get_drvdata(dev_get_drvdata(dev->parent->parent)); - return snprintf(buf, PAGE_SIZE, "%d\n", pyra->actual_profile); + struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev)); + struct pyra_settings settings; + + mutex_lock(&pyra->pyra_lock); + roccat_common2_receive(usb_dev, PYRA_COMMAND_SETTINGS, + &settings, PYRA_SIZE_SETTINGS); + mutex_unlock(&pyra->pyra_lock); + + return snprintf(buf, PAGE_SIZE, "%d\n", settings.startup_profile); } static ssize_t pyra_sysfs_show_firmware_version(struct device *dev, struct device_attribute *attr, char *buf) { - struct pyra_device *pyra = - hid_get_drvdata(dev_get_drvdata(dev->parent->parent)); - return snprintf(buf, PAGE_SIZE, "%d\n", pyra->firmware_version); -} + struct pyra_device *pyra; + struct usb_device *usb_dev; + struct pyra_info info; -static ssize_t pyra_sysfs_show_startup_profile(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct pyra_device *pyra = - hid_get_drvdata(dev_get_drvdata(dev->parent->parent)); - return snprintf(buf, PAGE_SIZE, "%d\n", pyra->settings.startup_profile); + dev = dev->parent->parent; + pyra = hid_get_drvdata(dev_get_drvdata(dev)); + usb_dev = interface_to_usbdev(to_usb_interface(dev)); + + mutex_lock(&pyra->pyra_lock); + roccat_common2_receive(usb_dev, PYRA_COMMAND_INFO, + &info, PYRA_SIZE_INFO); + mutex_unlock(&pyra->pyra_lock); + + return snprintf(buf, PAGE_SIZE, "%d\n", info.firmware_version); } static struct device_attribute pyra_attributes[] = { @@ -336,105 +307,87 @@ static struct device_attribute pyra_attributes[] = { __ATTR(firmware_version, 0440, pyra_sysfs_show_firmware_version, NULL), __ATTR(startup_profile, 0440, - pyra_sysfs_show_startup_profile, NULL), + pyra_sysfs_show_actual_profile, NULL), __ATTR_NULL }; static struct bin_attribute pyra_bin_attributes[] = { - { - .attr = { .name = "profile_settings", .mode = 0220 }, - .size = sizeof(struct pyra_profile_settings), - .write = pyra_sysfs_write_profile_settings - }, + PYRA_BIN_ATTRIBUTE_RW(info, INFO), + PYRA_BIN_ATTRIBUTE_W(profile_settings, PROFILE_SETTINGS), + PYRA_BIN_ATTRIBUTE_W(profile_buttons, PROFILE_BUTTONS), + PYRA_BIN_ATTRIBUTE_RW(settings, SETTINGS), { .attr = { .name = "profile1_settings", .mode = 0440 }, - .size = sizeof(struct pyra_profile_settings), + .size = PYRA_SIZE_PROFILE_SETTINGS, .read = pyra_sysfs_read_profilex_settings, .private = &profile_numbers[0] }, { .attr = { .name = "profile2_settings", .mode = 0440 }, - .size = sizeof(struct pyra_profile_settings), + .size = PYRA_SIZE_PROFILE_SETTINGS, .read = pyra_sysfs_read_profilex_settings, .private = &profile_numbers[1] }, { .attr = { .name = "profile3_settings", .mode = 0440 }, - .size = sizeof(struct pyra_profile_settings), + .size = PYRA_SIZE_PROFILE_SETTINGS, .read = pyra_sysfs_read_profilex_settings, .private = &profile_numbers[2] }, { .attr = { .name = "profile4_settings", .mode = 0440 }, - .size = sizeof(struct pyra_profile_settings), + .size = PYRA_SIZE_PROFILE_SETTINGS, .read = pyra_sysfs_read_profilex_settings, .private = &profile_numbers[3] }, { .attr = { .name = "profile5_settings", .mode = 0440 }, - .size = sizeof(struct pyra_profile_settings), + .size = PYRA_SIZE_PROFILE_SETTINGS, .read = pyra_sysfs_read_profilex_settings, .private = &profile_numbers[4] }, - { - .attr = { .name = "profile_buttons", .mode = 0220 }, - .size = sizeof(struct pyra_profile_buttons), - .write = pyra_sysfs_write_profile_buttons - }, { .attr = { .name = "profile1_buttons", .mode = 0440 }, - .size = sizeof(struct pyra_profile_buttons), + .size = PYRA_SIZE_PROFILE_BUTTONS, .read = pyra_sysfs_read_profilex_buttons, .private = &profile_numbers[0] }, { .attr = { .name = "profile2_buttons", .mode = 0440 }, - .size = sizeof(struct pyra_profile_buttons), + .size = PYRA_SIZE_PROFILE_BUTTONS, .read = pyra_sysfs_read_profilex_buttons, .private = &profile_numbers[1] }, { .attr = { .name = "profile3_buttons", .mode = 0440 }, - .size = sizeof(struct pyra_profile_buttons), + .size = PYRA_SIZE_PROFILE_BUTTONS, .read = pyra_sysfs_read_profilex_buttons, .private = &profile_numbers[2] }, { .attr = { .name = "profile4_buttons", .mode = 0440 }, - .size = sizeof(struct pyra_profile_buttons), + .size = PYRA_SIZE_PROFILE_BUTTONS, .read = pyra_sysfs_read_profilex_buttons, .private = &profile_numbers[3] }, { .attr = { .name = "profile5_buttons", .mode = 0440 }, - .size = sizeof(struct pyra_profile_buttons), + .size = PYRA_SIZE_PROFILE_BUTTONS, .read = pyra_sysfs_read_profilex_buttons, .private = &profile_numbers[4] }, - { - .attr = { .name = "settings", .mode = 0660 }, - .size = sizeof(struct pyra_settings), - .read = pyra_sysfs_read_settings, - .write = pyra_sysfs_write_settings - }, __ATTR_NULL }; static int pyra_init_pyra_device_struct(struct usb_device *usb_dev, struct pyra_device *pyra) { - struct pyra_info info; + struct pyra_settings settings; int retval, i; mutex_init(&pyra->pyra_lock); - retval = pyra_get_info(usb_dev, &info); - if (retval) - return retval; - - pyra->firmware_version = info.firmware_version; - - retval = pyra_get_settings(usb_dev, &pyra->settings); + retval = pyra_get_settings(usb_dev, &settings); if (retval) return retval; @@ -443,14 +396,9 @@ static int pyra_init_pyra_device_struct(struct usb_device *usb_dev, &pyra->profile_settings[i], i); if (retval) return retval; - - retval = pyra_get_profile_buttons(usb_dev, - &pyra->profile_buttons[i], i); - if (retval) - return retval; } - profile_activated(pyra, pyra->settings.startup_profile); + profile_activated(pyra, settings.startup_profile); return 0; } diff --git a/drivers/hid/hid-roccat-pyra.h b/drivers/hid/hid-roccat-pyra.h index eada7830fa99..93e494984976 100644 --- a/drivers/hid/hid-roccat-pyra.h +++ b/drivers/hid/hid-roccat-pyra.h @@ -14,11 +14,12 @@ #include -struct pyra_b { - uint8_t command; /* PYRA_COMMAND_B */ - uint8_t size; /* always 3 */ - uint8_t unknown; /* 1 */ -} __attribute__ ((__packed__)); +enum { + PYRA_SIZE_INFO = 0x06, + PYRA_SIZE_PROFILE_SETTINGS = 0x0d, + PYRA_SIZE_PROFILE_BUTTONS = 0x13, + PYRA_SIZE_SETTINGS = 0x03, +}; enum pyra_control_requests { PYRA_CONTROL_REQUEST_PROFILE_SETTINGS = 0x10, @@ -46,14 +47,6 @@ struct pyra_profile_settings { uint16_t checksum; /* byte sum */ } __attribute__ ((__packed__)); -struct pyra_profile_buttons { - uint8_t command; /* PYRA_COMMAND_PROFILE_BUTTONS */ - uint8_t size; /* always 0x13 */ - uint8_t number; /* Range 0-4 */ - uint8_t buttons[14]; - uint16_t checksum; /* byte sum */ -} __attribute__ ((__packed__)); - struct pyra_info { uint8_t command; /* PYRA_COMMAND_INFO */ uint8_t size; /* always 6 */ @@ -148,13 +141,10 @@ struct pyra_roccat_report { struct pyra_device { int actual_profile; int actual_cpi; - int firmware_version; int roccat_claimed; int chrdev_minor; struct mutex pyra_lock; - struct pyra_settings settings; struct pyra_profile_settings profile_settings[5]; - struct pyra_profile_buttons profile_buttons[5]; }; #endif -- cgit v1.2.3-59-g8ed1b From f1da71e1f658c3f1e5d08291258ac87c35e049a4 Mon Sep 17 00:00:00 2001 From: Stefan Achatz Date: Sun, 11 Nov 2012 06:21:06 +0100 Subject: HID: roccat: add sysfs attr "reset" for Isku Isku needs an extra sysfs attr to support device reset. Signed-off-by: Stefan Achatz Signed-off-by: Jiri Kosina --- Documentation/ABI/testing/sysfs-driver-hid-roccat-isku | 8 ++++++++ drivers/hid/hid-roccat-isku.c | 2 ++ drivers/hid/hid-roccat-isku.h | 2 ++ 3 files changed, 12 insertions(+) (limited to 'Documentation') diff --git a/Documentation/ABI/testing/sysfs-driver-hid-roccat-isku b/Documentation/ABI/testing/sysfs-driver-hid-roccat-isku index 189dc43891bf..9eca5a182e64 100644 --- a/Documentation/ABI/testing/sysfs-driver-hid-roccat-isku +++ b/Documentation/ABI/testing/sysfs-driver-hid-roccat-isku @@ -117,6 +117,14 @@ Description: When written, this file lets one store macros with max 500 which profile and key to read. Users: http://roccat.sourceforge.net +What: /sys/bus/usb/devices/-:./::./isku/roccatisku/reset +Date: November 2012 +Contact: Stefan Achatz +Description: When written, this file lets one reset the device. + The data has to be 3 bytes long. + This file is writeonly. +Users: http://roccat.sourceforge.net + What: /sys/bus/usb/devices/-:./::./isku/roccatisku/control Date: June 2011 Contact: Stefan Achatz diff --git a/drivers/hid/hid-roccat-isku.c b/drivers/hid/hid-roccat-isku.c index 020d6cda8975..1219998a02d6 100644 --- a/drivers/hid/hid-roccat-isku.c +++ b/drivers/hid/hid-roccat-isku.c @@ -218,6 +218,7 @@ ISKU_SYSFS_RW(last_set, LAST_SET) ISKU_SYSFS_W(talk, TALK) ISKU_SYSFS_R(info, INFO) ISKU_SYSFS_W(control, CONTROL) +ISKU_SYSFS_W(reset, RESET) static struct bin_attribute isku_bin_attributes[] = { ISKU_BIN_ATTR_RW(macro, MACRO), @@ -233,6 +234,7 @@ static struct bin_attribute isku_bin_attributes[] = { ISKU_BIN_ATTR_W(talk, TALK), ISKU_BIN_ATTR_R(info, INFO), ISKU_BIN_ATTR_W(control, CONTROL), + ISKU_BIN_ATTR_W(reset, RESET), __ATTR_NULL }; diff --git a/drivers/hid/hid-roccat-isku.h b/drivers/hid/hid-roccat-isku.h index 0062ab50861e..cf6896c83867 100644 --- a/drivers/hid/hid-roccat-isku.h +++ b/drivers/hid/hid-roccat-isku.h @@ -27,6 +27,7 @@ enum { ISKU_SIZE_LAST_SET = 0x14, ISKU_SIZE_LIGHT = 0x0a, ISKU_SIZE_MACRO = 0x823, + ISKU_SIZE_RESET = 0x03, ISKU_SIZE_TALK = 0x10, }; @@ -53,6 +54,7 @@ enum isku_commands { ISKU_COMMAND_MACRO = 0xe, ISKU_COMMAND_INFO = 0xf, ISKU_COMMAND_LIGHT = 0x10, + ISKU_COMMAND_RESET = 0x11, ISKU_COMMAND_KEYS_CAPSLOCK = 0x13, ISKU_COMMAND_LAST_SET = 0x14, ISKU_COMMAND_15 = 0x15, -- cgit v1.2.3-59-g8ed1b From ecbfe7aa55a3583238c0126bbc64f9043d390b50 Mon Sep 17 00:00:00 2001 From: Stefan Achatz Date: Sun, 11 Nov 2012 06:21:10 +0100 Subject: HID: roccat: deprecated some Pyra attributes Introduced attribute "control" and made profile_settings and profile_buttons readable, which makes profile[1-5]_settings and profile[1-5]_buttons obsolete. Signed-off-by: Stefan Achatz Signed-off-by: Jiri Kosina --- .../ABI/obsolete/sysfs-driver-hid-roccat-pyra | 27 +++++++++++++++- .../ABI/testing/sysfs-driver-hid-roccat-pyra | 37 +++++++--------------- drivers/hid/hid-roccat-pyra.c | 10 +++--- drivers/hid/hid-roccat-pyra.h | 2 ++ 4 files changed, 46 insertions(+), 30 deletions(-) (limited to 'Documentation') diff --git a/Documentation/ABI/obsolete/sysfs-driver-hid-roccat-pyra b/Documentation/ABI/obsolete/sysfs-driver-hid-roccat-pyra index 0a661b39e701..87ac87e9556d 100644 --- a/Documentation/ABI/obsolete/sysfs-driver-hid-roccat-pyra +++ b/Documentation/ABI/obsolete/sysfs-driver-hid-roccat-pyra @@ -37,6 +37,31 @@ Description: When read, this file returns the raw integer version number of the Please use binary attribute "info" which provides this information. Users: http://roccat.sourceforge.net +What: /sys/bus/usb/devices/-:./::./pyra/roccatpyra/profile[1-5]_buttons +Date: August 2010 +Contact: Stefan Achatz +Description: The mouse can store 5 profiles which can be switched by the + press of a button. A profile is split in settings and buttons. + profile_buttons holds information about button layout. + When read, these files return the respective profile buttons. + The returned data is 19 bytes in size. + This file is readonly. + Write control to select profile and read profile_buttons instead. +Users: http://roccat.sourceforge.net + +What: /sys/bus/usb/devices/-:./::./pyra/roccatpyra/profile[1-5]_settings +Date: August 2010 +Contact: Stefan Achatz +Description: The mouse can store 5 profiles which can be switched by the + press of a button. A profile is split in settings and buttons. + profile_settings holds information like resolution, sensitivity + and light effects. + When read, these files return the respective profile settings. + The returned data is 13 bytes in size. + This file is readonly. + Write control to select profile and read profile_settings instead. +Users: http://roccat.sourceforge.net + What: /sys/bus/usb/devices/-:./::./pyra/roccatpyra/startup_profile Date: August 2010 Contact: Stefan Achatz @@ -45,4 +70,4 @@ Description: The integer value of this attribute ranges from 0-4. that's active when the mouse is powered on. This file is readonly. Please use binary attribute "settings" which provides this information. -Users: http://roccat.sourceforge.net \ No newline at end of file +Users: http://roccat.sourceforge.net diff --git a/Documentation/ABI/testing/sysfs-driver-hid-roccat-pyra b/Documentation/ABI/testing/sysfs-driver-hid-roccat-pyra index b0fab8bcf247..9fa9de30d14b 100644 --- a/Documentation/ABI/testing/sysfs-driver-hid-roccat-pyra +++ b/Documentation/ABI/testing/sysfs-driver-hid-roccat-pyra @@ -1,3 +1,11 @@ +What: /sys/bus/usb/devices/-:./::./pyra/roccatpyra/info +Date: November 2012 +Contact: Stefan Achatz +Description: When read, this file returns general data like firmware version. + When written, the device can be reset. + The data is 6 bytes long. +Users: http://roccat.sourceforge.net + What: /sys/bus/usb/devices/-:./::./pyra/roccatpyra/profile_settings Date: August 2010 Contact: Stefan Achatz @@ -10,19 +18,8 @@ Description: The mouse can store 5 profiles which can be switched by the The mouse will reject invalid data. Which profile to write is determined by the profile number contained in the data. - This file is writeonly. -Users: http://roccat.sourceforge.net - -What: /sys/bus/usb/devices/-:./::./pyra/roccatpyra/profile[1-5]_settings -Date: August 2010 -Contact: Stefan Achatz -Description: The mouse can store 5 profiles which can be switched by the - press of a button. A profile is split in settings and buttons. - profile_settings holds information like resolution, sensitivity - and light effects. - When read, these files return the respective profile settings. - The returned data is 13 bytes in size. - This file is readonly. + Before reading this file, control has to be written to select + which profile to read. Users: http://roccat.sourceforge.net What: /sys/bus/usb/devices/-:./::./pyra/roccatpyra/profile_buttons @@ -36,18 +33,8 @@ Description: The mouse can store 5 profiles which can be switched by the The mouse will reject invalid data. Which profile to write is determined by the profile number contained in the data. - This file is writeonly. -Users: http://roccat.sourceforge.net - -What: /sys/bus/usb/devices/-:./::./pyra/roccatpyra/profile[1-5]_buttons -Date: August 2010 -Contact: Stefan Achatz -Description: The mouse can store 5 profiles which can be switched by the - press of a button. A profile is split in settings and buttons. - profile_buttons holds information about button layout. - When read, these files return the respective profile buttons. - The returned data is 19 bytes in size. - This file is readonly. + Before reading this file, control has to be written to select + which profile to read. Users: http://roccat.sourceforge.net What: /sys/bus/usb/devices/-:./::./pyra/roccatpyra/settings diff --git a/drivers/hid/hid-roccat-pyra.c b/drivers/hid/hid-roccat-pyra.c index 76199fa727ed..d4f1e3bee590 100644 --- a/drivers/hid/hid-roccat-pyra.c +++ b/drivers/hid/hid-roccat-pyra.c @@ -177,9 +177,10 @@ PYRA_SYSFS_R(thingy, THINGY) .write = pyra_sysfs_write_ ## thingy \ } +PYRA_SYSFS_W(control, CONTROL) PYRA_SYSFS_RW(info, INFO) -PYRA_SYSFS_W(profile_settings, PROFILE_SETTINGS) -PYRA_SYSFS_W(profile_buttons, PROFILE_BUTTONS) +PYRA_SYSFS_RW(profile_settings, PROFILE_SETTINGS) +PYRA_SYSFS_RW(profile_buttons, PROFILE_BUTTONS) PYRA_SYSFS_R(settings, SETTINGS) static ssize_t pyra_sysfs_read_profilex_settings(struct file *fp, @@ -312,9 +313,10 @@ static struct device_attribute pyra_attributes[] = { }; static struct bin_attribute pyra_bin_attributes[] = { + PYRA_BIN_ATTRIBUTE_W(control, CONTROL), PYRA_BIN_ATTRIBUTE_RW(info, INFO), - PYRA_BIN_ATTRIBUTE_W(profile_settings, PROFILE_SETTINGS), - PYRA_BIN_ATTRIBUTE_W(profile_buttons, PROFILE_BUTTONS), + PYRA_BIN_ATTRIBUTE_RW(profile_settings, PROFILE_SETTINGS), + PYRA_BIN_ATTRIBUTE_RW(profile_buttons, PROFILE_BUTTONS), PYRA_BIN_ATTRIBUTE_RW(settings, SETTINGS), { .attr = { .name = "profile1_settings", .mode = 0440 }, diff --git a/drivers/hid/hid-roccat-pyra.h b/drivers/hid/hid-roccat-pyra.h index 93e494984976..beedcf001ceb 100644 --- a/drivers/hid/hid-roccat-pyra.h +++ b/drivers/hid/hid-roccat-pyra.h @@ -15,6 +15,7 @@ #include enum { + PYRA_SIZE_CONTROL = 0x03, PYRA_SIZE_INFO = 0x06, PYRA_SIZE_PROFILE_SETTINGS = 0x0d, PYRA_SIZE_PROFILE_BUTTONS = 0x13, @@ -57,6 +58,7 @@ struct pyra_info { } __attribute__ ((__packed__)); enum pyra_commands { + PYRA_COMMAND_CONTROL = 0x4, PYRA_COMMAND_SETTINGS = 0x5, PYRA_COMMAND_PROFILE_SETTINGS = 0x6, PYRA_COMMAND_PROFILE_BUTTONS = 0x7, -- cgit v1.2.3-59-g8ed1b From bb060d65c3bf1d0211262e8229097d9a1beab20d Mon Sep 17 00:00:00 2001 From: Stefan Achatz Date: Sun, 11 Nov 2012 06:21:15 +0100 Subject: HID: roccat: deprecate some Koneplus attributes Introduced attribute "control" and made profile_settings and profile_buttons readable, which makes profile[1-5]_settings and profile[1-5]_buttons obsolete. Signed-off-by: Stefan Achatz Signed-off-by: Jiri Kosina --- .../ABI/obsolete/sysfs-driver-hid-roccat-koneplus | 27 ++++++++++++++++++++ .../ABI/testing/sysfs-driver-hid-roccat-koneplus | 29 +++------------------- drivers/hid/hid-roccat-koneplus.c | 10 +++++--- drivers/hid/hid-roccat-koneplus.h | 2 ++ 4 files changed, 39 insertions(+), 29 deletions(-) (limited to 'Documentation') diff --git a/Documentation/ABI/obsolete/sysfs-driver-hid-roccat-koneplus b/Documentation/ABI/obsolete/sysfs-driver-hid-roccat-koneplus index 22568b45973e..833fd59926a7 100644 --- a/Documentation/ABI/obsolete/sysfs-driver-hid-roccat-koneplus +++ b/Documentation/ABI/obsolete/sysfs-driver-hid-roccat-koneplus @@ -8,6 +8,7 @@ Description: The integer value of this attribute ranges from 0-4. When written, this file sets the number of the startup profile and the mouse activates this profile immediately. Please use actual_profile, it does the same thing. +Users: http://roccat.sourceforge.net What: /sys/bus/usb/devices/-:./::./koneplus/roccatkoneplus/firmware_version Date: October 2010 @@ -19,3 +20,29 @@ Description: When read, this file returns the raw integer version number of the left. E.g. a returned value of 121 means 1.21 This file is readonly. Please read binary attribute info which contains firmware version. +Users: http://roccat.sourceforge.net + +What: /sys/bus/usb/devices/-:./::./koneplus/roccatkoneplus/profile[1-5]_buttons +Date: August 2010 +Contact: Stefan Achatz +Description: The mouse can store 5 profiles which can be switched by the + press of a button. A profile is split in settings and buttons. + profile_buttons holds information about button layout. + When read, these files return the respective profile buttons. + The returned data is 77 bytes in size. + This file is readonly. + Write control to select profile and read profile_buttons instead. +Users: http://roccat.sourceforge.net + +What: /sys/bus/usb/devices/-:./::./koneplus/roccatkoneplus/profile[1-5]_settings +Date: August 2010 +Contact: Stefan Achatz +Description: The mouse can store 5 profiles which can be switched by the + press of a button. A profile is split in settings and buttons. + profile_settings holds information like resolution, sensitivity + and light effects. + When read, these files return the respective profile settings. + The returned data is 43 bytes in size. + This file is readonly. + Write control to select profile and read profile_settings instead. +Users: http://roccat.sourceforge.net \ No newline at end of file diff --git a/Documentation/ABI/testing/sysfs-driver-hid-roccat-koneplus b/Documentation/ABI/testing/sysfs-driver-hid-roccat-koneplus index ed1213defb81..7bd776f9c3c7 100644 --- a/Documentation/ABI/testing/sysfs-driver-hid-roccat-koneplus +++ b/Documentation/ABI/testing/sysfs-driver-hid-roccat-koneplus @@ -39,18 +39,8 @@ Description: The mouse can store 5 profiles which can be switched by the The mouse will reject invalid data. Which profile to write is determined by the profile number contained in the data. - This file is writeonly. -Users: http://roccat.sourceforge.net - -What: /sys/bus/usb/devices/-:./::./koneplus/roccatkoneplus/profile[1-5]_buttons -Date: August 2010 -Contact: Stefan Achatz -Description: The mouse can store 5 profiles which can be switched by the - press of a button. A profile is split in settings and buttons. - profile_buttons holds information about button layout. - When read, these files return the respective profile buttons. - The returned data is 77 bytes in size. - This file is readonly. + Before reading this file, control has to be written to select + which profile to read. Users: http://roccat.sourceforge.net What: /sys/bus/usb/devices/-:./::./koneplus/roccatkoneplus/profile_settings @@ -65,19 +55,8 @@ Description: The mouse can store 5 profiles which can be switched by the The mouse will reject invalid data. Which profile to write is determined by the profile number contained in the data. - This file is writeonly. -Users: http://roccat.sourceforge.net - -What: /sys/bus/usb/devices/-:./::./koneplus/roccatkoneplus/profile[1-5]_settings -Date: August 2010 -Contact: Stefan Achatz -Description: The mouse can store 5 profiles which can be switched by the - press of a button. A profile is split in settings and buttons. - profile_settings holds information like resolution, sensitivity - and light effects. - When read, these files return the respective profile settings. - The returned data is 43 bytes in size. - This file is readonly. + Before reading this file, control has to be written to select + which profile to read. Users: http://roccat.sourceforge.net What: /sys/bus/usb/devices/-:./::./koneplus/roccatkoneplus/sensor diff --git a/drivers/hid/hid-roccat-koneplus.c b/drivers/hid/hid-roccat-koneplus.c index 9fe445082308..6a48fa3c7da9 100644 --- a/drivers/hid/hid-roccat-koneplus.c +++ b/drivers/hid/hid-roccat-koneplus.c @@ -177,14 +177,15 @@ KONEPLUS_SYSFS_R(thingy, THINGY) .write = koneplus_sysfs_write_ ## thingy \ } +KONEPLUS_SYSFS_W(control, CONTROL) KONEPLUS_SYSFS_RW(info, INFO) KONEPLUS_SYSFS_W(talk, TALK) KONEPLUS_SYSFS_W(macro, MACRO) KONEPLUS_SYSFS_RW(sensor, SENSOR) KONEPLUS_SYSFS_RW(tcu, TCU) KONEPLUS_SYSFS_R(tcu_image, TCU_IMAGE) -KONEPLUS_SYSFS_W(profile_settings, PROFILE_SETTINGS) -KONEPLUS_SYSFS_W(profile_buttons, PROFILE_BUTTONS) +KONEPLUS_SYSFS_RW(profile_settings, PROFILE_SETTINGS) +KONEPLUS_SYSFS_RW(profile_buttons, PROFILE_BUTTONS) static ssize_t koneplus_sysfs_read_profilex_settings(struct file *fp, struct kobject *kobj, struct bin_attribute *attr, char *buf, @@ -306,14 +307,15 @@ static struct device_attribute koneplus_attributes[] = { }; static struct bin_attribute koneplus_bin_attributes[] = { + KONEPLUS_BIN_ATTRIBUTE_W(control, CONTROL), KONEPLUS_BIN_ATTRIBUTE_RW(info, INFO), KONEPLUS_BIN_ATTRIBUTE_W(talk, TALK), KONEPLUS_BIN_ATTRIBUTE_W(macro, MACRO), KONEPLUS_BIN_ATTRIBUTE_RW(sensor, SENSOR), KONEPLUS_BIN_ATTRIBUTE_RW(tcu, TCU), KONEPLUS_BIN_ATTRIBUTE_R(tcu_image, TCU_IMAGE), - KONEPLUS_BIN_ATTRIBUTE_W(profile_settings, PROFILE_SETTINGS), - KONEPLUS_BIN_ATTRIBUTE_W(profile_buttons, PROFILE_BUTTONS), + KONEPLUS_BIN_ATTRIBUTE_RW(profile_settings, PROFILE_SETTINGS), + KONEPLUS_BIN_ATTRIBUTE_RW(profile_buttons, PROFILE_BUTTONS), { .attr = { .name = "profile1_settings", .mode = 0440 }, .size = KONEPLUS_SIZE_PROFILE_SETTINGS, diff --git a/drivers/hid/hid-roccat-koneplus.h b/drivers/hid/hid-roccat-koneplus.h index 563b9d3fe5fb..d2b55f2c764d 100644 --- a/drivers/hid/hid-roccat-koneplus.h +++ b/drivers/hid/hid-roccat-koneplus.h @@ -16,6 +16,7 @@ enum { KONEPLUS_SIZE_ACTUAL_PROFILE = 0x03, + KONEPLUS_SIZE_CONTROL = 0x03, KONEPLUS_SIZE_FIRMWARE_WRITE = 0x0402, KONEPLUS_SIZE_INFO = 0x06, KONEPLUS_SIZE_MACRO = 0x0822, @@ -47,6 +48,7 @@ struct koneplus_info { enum koneplus_commands { KONEPLUS_COMMAND_ACTUAL_PROFILE = 0x5, + KONEPLUS_COMMAND_CONTROL = 0x4, KONEPLUS_COMMAND_PROFILE_SETTINGS = 0x6, KONEPLUS_COMMAND_PROFILE_BUTTONS = 0x7, KONEPLUS_COMMAND_MACRO = 0x8, -- cgit v1.2.3-59-g8ed1b From 172e2abc19b51feef5b7980055725f0b242e988e Mon Sep 17 00:00:00 2001 From: Stefan Achatz Date: Sun, 11 Nov 2012 06:21:19 +0100 Subject: HID: roccat: deprecate some Kovaplus attributes Introduced attribute "control" and made profile_settings and profile_buttons readable, which makes profile[1-5]_settings and profile[1-5]_buttons obsolete. Signed-off-by: Stefan Achatz Signed-off-by: Jiri Kosina --- .../ABI/obsolete/sysfs-driver-hid-roccat-kovaplus | 25 +++++++++++++++++++ .../ABI/testing/sysfs-driver-hid-roccat-kovaplus | 29 +++------------------- drivers/hid/hid-roccat-kovaplus.c | 10 +++++--- drivers/hid/hid-roccat-kovaplus.h | 2 ++ 4 files changed, 37 insertions(+), 29 deletions(-) (limited to 'Documentation') diff --git a/Documentation/ABI/obsolete/sysfs-driver-hid-roccat-kovaplus b/Documentation/ABI/obsolete/sysfs-driver-hid-roccat-kovaplus index 4d9fb26292a2..4a98e02b6c6a 100644 --- a/Documentation/ABI/obsolete/sysfs-driver-hid-roccat-kovaplus +++ b/Documentation/ABI/obsolete/sysfs-driver-hid-roccat-kovaplus @@ -39,3 +39,28 @@ Description: When read, this file returns the raw integer version number of the This file is readonly. Obsoleted by binary sysfs attribute "info". Users: http://roccat.sourceforge.net + +What: /sys/bus/usb/devices/-:./::./kovaplus/roccatkovaplus/profile[1-5]_buttons +Date: January 2011 +Contact: Stefan Achatz +Description: The mouse can store 5 profiles which can be switched by the + press of a button. A profile is split in settings and buttons. + profile_buttons holds information about button layout. + When read, these files return the respective profile buttons. + The returned data is 23 bytes in size. + This file is readonly. + Write control to select profile and read profile_buttons instead. +Users: http://roccat.sourceforge.net + +What: /sys/bus/usb/devices/-:./::./kovaplus/roccatkovaplus/profile[1-5]_settings +Date: January 2011 +Contact: Stefan Achatz +Description: The mouse can store 5 profiles which can be switched by the + press of a button. A profile is split in settings and buttons. + profile_settings holds information like resolution, sensitivity + and light effects. + When read, these files return the respective profile settings. + The returned data is 16 bytes in size. + This file is readonly. + Write control to select profile and read profile_settings instead. +Users: http://roccat.sourceforge.net diff --git a/Documentation/ABI/testing/sysfs-driver-hid-roccat-kovaplus b/Documentation/ABI/testing/sysfs-driver-hid-roccat-kovaplus index 507f2c7321d5..a10404f15a54 100644 --- a/Documentation/ABI/testing/sysfs-driver-hid-roccat-kovaplus +++ b/Documentation/ABI/testing/sysfs-driver-hid-roccat-kovaplus @@ -28,18 +28,8 @@ Description: The mouse can store 5 profiles which can be switched by the The mouse will reject invalid data. Which profile to write is determined by the profile number contained in the data. - This file is writeonly. -Users: http://roccat.sourceforge.net - -What: /sys/bus/usb/devices/-:./::./kovaplus/roccatkovaplus/profile[1-5]_buttons -Date: January 2011 -Contact: Stefan Achatz -Description: The mouse can store 5 profiles which can be switched by the - press of a button. A profile is split in settings and buttons. - profile_buttons holds information about button layout. - When read, these files return the respective profile buttons. - The returned data is 23 bytes in size. - This file is readonly. + Before reading this file, control has to be written to select + which profile to read. Users: http://roccat.sourceforge.net What: /sys/bus/usb/devices/-:./::./kovaplus/roccatkovaplus/profile_settings @@ -54,17 +44,6 @@ Description: The mouse can store 5 profiles which can be switched by the The mouse will reject invalid data. Which profile to write is determined by the profile number contained in the data. - This file is writeonly. -Users: http://roccat.sourceforge.net - -What: /sys/bus/usb/devices/-:./::./kovaplus/roccatkovaplus/profile[1-5]_settings -Date: January 2011 -Contact: Stefan Achatz -Description: The mouse can store 5 profiles which can be switched by the - press of a button. A profile is split in settings and buttons. - profile_settings holds information like resolution, sensitivity - and light effects. - When read, these files return the respective profile settings. - The returned data is 16 bytes in size. - This file is readonly. + Before reading this file, control has to be written to select + which profile to read. Users: http://roccat.sourceforge.net diff --git a/drivers/hid/hid-roccat-kovaplus.c b/drivers/hid/hid-roccat-kovaplus.c index 1589fd3bcf0f..b8b37789b864 100644 --- a/drivers/hid/hid-roccat-kovaplus.c +++ b/drivers/hid/hid-roccat-kovaplus.c @@ -218,9 +218,10 @@ KOVAPLUS_SYSFS_R(thingy, THINGY) .write = kovaplus_sysfs_write_ ## thingy \ } +KOVAPLUS_SYSFS_W(control, CONTROL) KOVAPLUS_SYSFS_RW(info, INFO) -KOVAPLUS_SYSFS_W(profile_settings, PROFILE_SETTINGS) -KOVAPLUS_SYSFS_W(profile_buttons, PROFILE_BUTTONS) +KOVAPLUS_SYSFS_RW(profile_settings, PROFILE_SETTINGS) +KOVAPLUS_SYSFS_RW(profile_buttons, PROFILE_BUTTONS) static ssize_t kovaplus_sysfs_read_profilex_settings(struct file *fp, struct kobject *kobj, struct bin_attribute *attr, char *buf, @@ -369,9 +370,10 @@ static struct device_attribute kovaplus_attributes[] = { }; static struct bin_attribute kovaplus_bin_attributes[] = { + KOVAPLUS_BIN_ATTRIBUTE_W(control, CONTROL), KOVAPLUS_BIN_ATTRIBUTE_RW(info, INFO), - KOVAPLUS_BIN_ATTRIBUTE_W(profile_settings, PROFILE_SETTINGS), - KOVAPLUS_BIN_ATTRIBUTE_W(profile_buttons, PROFILE_BUTTONS), + KOVAPLUS_BIN_ATTRIBUTE_RW(profile_settings, PROFILE_SETTINGS), + KOVAPLUS_BIN_ATTRIBUTE_RW(profile_buttons, PROFILE_BUTTONS), { .attr = { .name = "profile1_settings", .mode = 0440 }, .size = KOVAPLUS_SIZE_PROFILE_SETTINGS, diff --git a/drivers/hid/hid-roccat-kovaplus.h b/drivers/hid/hid-roccat-kovaplus.h index 1189573daee2..fbb7a16a7e54 100644 --- a/drivers/hid/hid-roccat-kovaplus.h +++ b/drivers/hid/hid-roccat-kovaplus.h @@ -15,6 +15,7 @@ #include enum { + KOVAPLUS_SIZE_CONTROL = 0x03, KOVAPLUS_SIZE_INFO = 0x06, KOVAPLUS_SIZE_PROFILE_SETTINGS = 0x10, KOVAPLUS_SIZE_PROFILE_BUTTONS = 0x17, @@ -61,6 +62,7 @@ struct kovaplus_info { enum kovaplus_commands { KOVAPLUS_COMMAND_ACTUAL_PROFILE = 0x5, + KOVAPLUS_COMMAND_CONTROL = 0x4, KOVAPLUS_COMMAND_PROFILE_SETTINGS = 0x6, KOVAPLUS_COMMAND_PROFILE_BUTTONS = 0x7, KOVAPLUS_COMMAND_INFO = 0x9, -- cgit v1.2.3-59-g8ed1b From dea05fe66edb4540a53ed6623794c4978315256a Mon Sep 17 00:00:00 2001 From: Frank Li Date: Mon, 12 Nov 2012 17:58:28 +0800 Subject: regulator: gpio-regulator: doc: Fix wrong key gpio-enable Wrong description in binding document. Doc use "gpio-enable", but code use "enable-gpio" In drivers/regulator/gpio-regulator.c config->enable_gpio = of_get_named_gpio(np, "enable-gpio", 0); Signed-off-by: Frank Li Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/regulator/gpio-regulator.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/regulator/gpio-regulator.txt b/Documentation/devicetree/bindings/regulator/gpio-regulator.txt index 3703be2ab821..f71f8083c2ae 100644 --- a/Documentation/devicetree/bindings/regulator/gpio-regulator.txt +++ b/Documentation/devicetree/bindings/regulator/gpio-regulator.txt @@ -4,7 +4,7 @@ Required properties: - compatible : Must be "regulator-gpio". Optional properties: -- gpio-enable : GPIO to use to enable/disable the regulator. +- enable-gpio : GPIO to use to enable/disable the regulator. - gpios : GPIO group used to control voltage. - states : Selection of available voltages and GPIO configs. - startup-delay-us : Startup time in microseconds. @@ -23,7 +23,7 @@ Example: regulator-max-microvolt = <2600000>; regulator-boot-on; - gpio-enable = <&gpio0 23 0x4>; + enable-gpio = <&gpio0 23 0x4>; gpios = <&gpio0 24 0x4 &gpio0 25 0x4>; states = <1800000 0x3 -- cgit v1.2.3-59-g8ed1b From 0049fb2603b7afb1080776ee691dfa5a3d282357 Mon Sep 17 00:00:00 2001 From: Tomi Valkeinen Date: Fri, 9 Nov 2012 15:52:20 +0200 Subject: OMAPFB: use dma_alloc_attrs to allocate memory Use dma_alloc_attrs to allocate memory instead of omap specific vram allocator. After this we can remove the omap vram allocator. There are some downsides to this change: 1) dma_alloc_attrs doesn't let us allocate at certain physical address. However, this should not be a problem as this feature of vram allocator is only used when reserving the framebuffer that was initialized by the bootloader, and we don't currently support "passing" a framebuffer from the bootloader to the kernel anyway. 2) dma_alloc_attrs, as of now, always ioremaps the allocated area, and we don't need the ioremap when using VRFB. This patch uses DMA_ATTR_NO_KERNEL_MAPPING for the allocation, but the flag is currently not operational. 3) OMAPFB_GET_VRAM_INFO ioctl cannot return real values anymore. I changed the ioctl to return 64M for all the values, which, I hope, the applications will interpret as "there's enough vram". 4) "vram" kernel parameter to define how much ram to reserve for video use no longer works. The user needs to enable CMA and use "cma" parameter. Signed-off-by: Tomi Valkeinen --- Documentation/arm/OMAP/DSS | 10 ++--- drivers/video/omap2/omapfb/Kconfig | 1 - drivers/video/omap2/omapfb/omapfb-ioctl.c | 14 +++---- drivers/video/omap2/omapfb/omapfb-main.c | 69 ++++++++++++++++--------------- drivers/video/omap2/omapfb/omapfb.h | 5 +++ 5 files changed, 52 insertions(+), 47 deletions(-) (limited to 'Documentation') diff --git a/Documentation/arm/OMAP/DSS b/Documentation/arm/OMAP/DSS index a564ceea9e98..4484e021290e 100644 --- a/Documentation/arm/OMAP/DSS +++ b/Documentation/arm/OMAP/DSS @@ -285,7 +285,10 @@ FB0 +-- GFX ---- LCD ---- LCD Misc notes ---------- -OMAP FB allocates the framebuffer memory using the OMAP VRAM allocator. +OMAP FB allocates the framebuffer memory using the standard dma allocator. You +can enable Contiguous Memory Allocator (CONFIG_CMA) to improve the dma +allocator, and if CMA is enabled, you use "cma=" kernel parameter to increase +the global memory area for CMA. Using DSI DPLL to generate pixel clock it is possible produce the pixel clock of 86.5MHz (max possible), and with that you get 1280x1024@57 output from DVI. @@ -301,11 +304,6 @@ framebuffer parameters. Kernel boot arguments --------------------- -vram=[,] - - Amount of total VRAM to preallocate and optionally a physical start - memory address. For example, "10M". omapfb allocates memory for - framebuffers from VRAM. - omapfb.mode=:[,...] - Default video mode for specified displays. For example, "dvi:800x400MR-24@60". See drivers/video/modedb.c. diff --git a/drivers/video/omap2/omapfb/Kconfig b/drivers/video/omap2/omapfb/Kconfig index 4ea17dc3258c..4cb12ce68855 100644 --- a/drivers/video/omap2/omapfb/Kconfig +++ b/drivers/video/omap2/omapfb/Kconfig @@ -2,7 +2,6 @@ menuconfig FB_OMAP2 tristate "OMAP2+ frame buffer support" depends on FB && OMAP2_DSS && !DRM_OMAP - select OMAP2_VRAM select OMAP2_VRFB if ARCH_OMAP2 || ARCH_OMAP3 select FB_CFB_FILLRECT select FB_CFB_COPYAREA diff --git a/drivers/video/omap2/omapfb/omapfb-ioctl.c b/drivers/video/omap2/omapfb/omapfb-ioctl.c index 606b89f12351..574c1708f749 100644 --- a/drivers/video/omap2/omapfb/omapfb-ioctl.c +++ b/drivers/video/omap2/omapfb/omapfb-ioctl.c @@ -31,7 +31,6 @@ #include diff --git a/drivers/gpu/drm/drm_irq.c b/drivers/gpu/drm/drm_irq.c index 3a3d0ce891b9..44602ef55afc 100644 --- a/drivers/gpu/drm/drm_irq.c +++ b/drivers/gpu/drm/drm_irq.c @@ -802,6 +802,46 @@ u32 drm_vblank_count_and_time(struct drm_device *dev, int crtc, } EXPORT_SYMBOL(drm_vblank_count_and_time); +static void send_vblank_event(struct drm_device *dev, + struct drm_pending_vblank_event *e, + unsigned long seq, struct timeval *now) +{ + WARN_ON_SMP(!spin_is_locked(&dev->event_lock)); + e->event.sequence = seq; + e->event.tv_sec = now->tv_sec; + e->event.tv_usec = now->tv_usec; + + list_add_tail(&e->base.link, + &e->base.file_priv->event_list); + wake_up_interruptible(&e->base.file_priv->event_wait); + trace_drm_vblank_event_delivered(e->base.pid, e->pipe, + e->event.sequence); +} + +/** + * drm_send_vblank_event - helper to send vblank event after pageflip + * @dev: DRM device + * @crtc: CRTC in question + * @e: the event to send + * + * Updates sequence # and timestamp on event, and sends it to userspace. + * Caller must hold event lock. + */ +void drm_send_vblank_event(struct drm_device *dev, int crtc, + struct drm_pending_vblank_event *e) +{ + struct timeval now; + unsigned int seq; + if (crtc >= 0) { + seq = drm_vblank_count_and_time(dev, crtc, &now); + } else { + seq = 0; + do_gettimeofday(&now); + } + send_vblank_event(dev, e, seq, &now); +} +EXPORT_SYMBOL(drm_send_vblank_event); + /** * drm_update_vblank_count - update the master vblank counter * @dev: DRM device @@ -936,6 +976,13 @@ void drm_vblank_put(struct drm_device *dev, int crtc) } EXPORT_SYMBOL(drm_vblank_put); +/** + * drm_vblank_off - disable vblank events on a CRTC + * @dev: DRM device + * @crtc: CRTC in question + * + * Caller must hold event lock. + */ void drm_vblank_off(struct drm_device *dev, int crtc) { struct drm_pending_vblank_event *e, *t; @@ -955,15 +1002,9 @@ void drm_vblank_off(struct drm_device *dev, int crtc) DRM_DEBUG("Sending premature vblank event on disable: \ wanted %d, current %d\n", e->event.sequence, seq); - - e->event.sequence = seq; - e->event.tv_sec = now.tv_sec; - e->event.tv_usec = now.tv_usec; + list_del(&e->base.link); drm_vblank_put(dev, e->pipe); - list_move_tail(&e->base.link, &e->base.file_priv->event_list); - wake_up_interruptible(&e->base.file_priv->event_wait); - trace_drm_vblank_event_delivered(e->base.pid, e->pipe, - e->event.sequence); + send_vblank_event(dev, e, seq, &now); } spin_unlock_irqrestore(&dev->vbl_lock, irqflags); @@ -1107,15 +1148,9 @@ static int drm_queue_vblank_event(struct drm_device *dev, int pipe, e->event.sequence = vblwait->request.sequence; if ((seq - vblwait->request.sequence) <= (1 << 23)) { - e->event.sequence = seq; - e->event.tv_sec = now.tv_sec; - e->event.tv_usec = now.tv_usec; drm_vblank_put(dev, pipe); - list_add_tail(&e->base.link, &e->base.file_priv->event_list); - wake_up_interruptible(&e->base.file_priv->event_wait); + send_vblank_event(dev, e, seq, &now); vblwait->reply.sequence = seq; - trace_drm_vblank_event_delivered(current->pid, pipe, - vblwait->request.sequence); } else { /* drm_handle_vblank_events will call drm_vblank_put */ list_add_tail(&e->base.link, &dev->vblank_event_list); @@ -1256,14 +1291,9 @@ static void drm_handle_vblank_events(struct drm_device *dev, int crtc) DRM_DEBUG("vblank event on %d, current %d\n", e->event.sequence, seq); - e->event.sequence = seq; - e->event.tv_sec = now.tv_sec; - e->event.tv_usec = now.tv_usec; + list_del(&e->base.link); drm_vblank_put(dev, e->pipe); - list_move_tail(&e->base.link, &e->base.file_priv->event_list); - wake_up_interruptible(&e->base.file_priv->event_wait); - trace_drm_vblank_event_delivered(e->base.pid, e->pipe, - e->event.sequence); + send_vblank_event(dev, e, seq, &now); } spin_unlock_irqrestore(&dev->event_lock, flags); diff --git a/include/drm/drmP.h b/include/drm/drmP.h index 3fd82809b2d4..29eb23799aa4 100644 --- a/include/drm/drmP.h +++ b/include/drm/drmP.h @@ -1431,6 +1431,8 @@ extern int drm_vblank_wait(struct drm_device *dev, unsigned int *vbl_seq); extern u32 drm_vblank_count(struct drm_device *dev, int crtc); extern u32 drm_vblank_count_and_time(struct drm_device *dev, int crtc, struct timeval *vblanktime); +extern void drm_send_vblank_event(struct drm_device *dev, int crtc, + struct drm_pending_vblank_event *e); extern bool drm_handle_vblank(struct drm_device *dev, int crtc); extern int drm_vblank_get(struct drm_device *dev, int crtc); extern void drm_vblank_put(struct drm_device *dev, int crtc); -- cgit v1.2.3-59-g8ed1b From 8ac49e0485bb79223a111b366a3b1f5ec9148729 Mon Sep 17 00:00:00 2001 From: Christian Daudt Date: Mon, 19 Nov 2012 09:46:10 -0800 Subject: Add support for generic BCM SoC chipsets In order to start upstreaming Broadcom SoC support, create a starting hierarchy, arch and dts files. The first support SoC family that is planned is the BCM281XX (BCM11130/11140/11351/28145/28155) family of dual A9 mobile SoC cores. This code is just the skeleton code for get the machine upstreamed. It has been made MULTIPLATFORM compatible. Next steps ---------- Upstream a basic set of drivers - sufficient for a console boot to ramdisk. These will includer timer, gpio, i2c drivers. After this basic set, we will proceed with a more comprehensive set of drivers for the 281XX SoC family. v2 patch mods -------- - Remove l2x0_of_init call as there were problems with the code. A separate patch will be submitted with cache init code - Rename capri files and refs to bcm281xx-based names - Add bcm281xx binding doc - various misc cleanups v3 patch mods ------------- - Remove extra #include lines - Remove remaining references to capri - dt uart chipset string added - cleaned up chip # references v4 patch mods ------------- - swap order of compatible definitions for uart - fix typo v5 patch mods ------------- - Rename bcm281xx to bcm11351 in dts+code, leaving references to bcm281xx only in help+comments. v6 patch mods ------------- - fix typo in uart 'compatible' string Signed-off-by: Christian Daudt Reviewed-by: Stephen Warren Signed-off-by: Olof Johansson --- .../devicetree/bindings/arm/bcm/bcm11351.txt | 9 ++ arch/arm/Kconfig | 2 + arch/arm/Makefile | 1 + arch/arm/boot/dts/Makefile | 1 + arch/arm/boot/dts/bcm11351-brt.dts | 30 ++++++ arch/arm/boot/dts/bcm11351.dtsi | 50 +++++++++ arch/arm/configs/bcm_defconfig | 114 +++++++++++++++++++++ arch/arm/mach-bcm/Kconfig | 19 ++++ arch/arm/mach-bcm/Makefile | 13 +++ arch/arm/mach-bcm/board_bcm.c | 57 +++++++++++ 10 files changed, 296 insertions(+) create mode 100644 Documentation/devicetree/bindings/arm/bcm/bcm11351.txt create mode 100644 arch/arm/boot/dts/bcm11351-brt.dts create mode 100644 arch/arm/boot/dts/bcm11351.dtsi create mode 100644 arch/arm/configs/bcm_defconfig create mode 100644 arch/arm/mach-bcm/Kconfig create mode 100644 arch/arm/mach-bcm/Makefile create mode 100644 arch/arm/mach-bcm/board_bcm.c (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/arm/bcm/bcm11351.txt b/Documentation/devicetree/bindings/arm/bcm/bcm11351.txt new file mode 100644 index 000000000000..fb7b5cd2652f --- /dev/null +++ b/Documentation/devicetree/bindings/arm/bcm/bcm11351.txt @@ -0,0 +1,9 @@ +Broadcom BCM11351 device tree bindings +------------------------------------------- + +Boards with the bcm281xx SoC family (which includes bcm11130, bcm11140, +bcm11351, bcm28145, bcm28155 SoCs) shall have the following properties: + +Required root node property: + +compatible = "bcm,bcm11351"; diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index e9b6b9e5e779..c1cc4f79e4fe 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -1009,6 +1009,8 @@ source "arch/arm/mach-mvebu/Kconfig" source "arch/arm/mach-at91/Kconfig" +source "arch/arm/mach-bcm/Kconfig" + source "arch/arm/mach-clps711x/Kconfig" source "arch/arm/mach-cns3xxx/Kconfig" diff --git a/arch/arm/Makefile b/arch/arm/Makefile index 3353b7613208..208912167823 100644 --- a/arch/arm/Makefile +++ b/arch/arm/Makefile @@ -137,6 +137,7 @@ textofs-$(CONFIG_ARCH_MSM8960) := 0x00208000 # Machine directory name. This list is sorted alphanumerically # by CONFIG_* macro name. machine-$(CONFIG_ARCH_AT91) += at91 +machine-$(CONFIG_ARCH_BCM) += bcm machine-$(CONFIG_ARCH_BCM2835) += bcm2835 machine-$(CONFIG_ARCH_CLPS711X) += clps711x machine-$(CONFIG_ARCH_CNS3XXX) += cns3xxx diff --git a/arch/arm/boot/dts/Makefile b/arch/arm/boot/dts/Makefile index 5cc95667fa3b..cc803c92a72c 100644 --- a/arch/arm/boot/dts/Makefile +++ b/arch/arm/boot/dts/Makefile @@ -17,6 +17,7 @@ dtb-$(CONFIG_ARCH_AT91) += aks-cdu.dtb \ usb_a9263.dtb \ usb_a9g20.dtb dtb-$(CONFIG_ARCH_BCM2835) += bcm2835-rpi-b.dtb +dtb-$(CONFIG_ARCH_BCM) += bcm11351-brt.dtb dtb-$(CONFIG_ARCH_DOVE) += dove-cm-a510.dtb \ dove-cubox.dtb \ dove-dove-db.dtb diff --git a/arch/arm/boot/dts/bcm11351-brt.dts b/arch/arm/boot/dts/bcm11351-brt.dts new file mode 100644 index 000000000000..248067cf7069 --- /dev/null +++ b/arch/arm/boot/dts/bcm11351-brt.dts @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2012 Broadcom 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. + * + * This program is distributed "as is" WITHOUT ANY WARRANTY of any + * kind, whether express or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +/dts-v1/; + +/include/ "bcm11351.dtsi" + +/ { + model = "BCM11351 BRT board"; + compatible = "bcm,bcm11351-brt", "bcm,bcm11351"; + + memory { + reg = <0x80000000 0x40000000>; /* 1 GB */ + }; + + uart@3e000000 { + status = "okay"; + }; + +}; diff --git a/arch/arm/boot/dts/bcm11351.dtsi b/arch/arm/boot/dts/bcm11351.dtsi new file mode 100644 index 000000000000..ad135885bd2a --- /dev/null +++ b/arch/arm/boot/dts/bcm11351.dtsi @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2012 Broadcom 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. + * + * This program is distributed "as is" WITHOUT ANY WARRANTY of any + * kind, whether express or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +/include/ "skeleton.dtsi" + +/ { + model = "BCM11351 SoC"; + compatible = "bcm,bcm11351"; + interrupt-parent = <&gic>; + + chosen { + bootargs = "console=ttyS0,115200n8"; + }; + + gic: interrupt-controller@3ff00100 { + compatible = "arm,cortex-a9-gic"; + #interrupt-cells = <3>; + #address-cells = <0>; + interrupt-controller; + reg = <0x3ff01000 0x1000>, + <0x3ff00100 0x100>; + }; + + uart@3e000000 { + compatible = "bcm,bcm11351-dw-apb-uart", "snps,dw-apb-uart"; + status = "disabled"; + reg = <0x3e000000 0x1000>; + clock-frequency = <13000000>; + interrupts = <0x0 67 0x4>; + reg-shift = <2>; + reg-io-width = <4>; + }; + + L2: l2-cache { + compatible = "arm,pl310-cache"; + reg = <0x3ff20000 0x1000>; + cache-unified; + cache-level = <2>; + }; +}; diff --git a/arch/arm/configs/bcm_defconfig b/arch/arm/configs/bcm_defconfig new file mode 100644 index 000000000000..e3bf2d65618e --- /dev/null +++ b/arch/arm/configs/bcm_defconfig @@ -0,0 +1,114 @@ +CONFIG_EXPERIMENTAL=y +# CONFIG_LOCALVERSION_AUTO is not set +# CONFIG_SWAP is not set +CONFIG_SYSVIPC=y +CONFIG_NO_HZ=y +CONFIG_HIGH_RES_TIMERS=y +CONFIG_BSD_PROCESS_ACCT=y +CONFIG_BSD_PROCESS_ACCT_V3=y +CONFIG_IKCONFIG=y +CONFIG_IKCONFIG_PROC=y +CONFIG_LOG_BUF_SHIFT=19 +CONFIG_CGROUPS=y +CONFIG_CGROUP_FREEZER=y +CONFIG_CGROUP_DEVICE=y +CONFIG_CGROUP_CPUACCT=y +CONFIG_RESOURCE_COUNTERS=y +CONFIG_CGROUP_SCHED=y +CONFIG_BLK_CGROUP=y +CONFIG_NAMESPACES=y +CONFIG_BLK_DEV_INITRD=y +CONFIG_SYSCTL_SYSCALL=y +CONFIG_EMBEDDED=y +# CONFIG_COMPAT_BRK is not set +CONFIG_MODULES=y +CONFIG_MODULE_UNLOAD=y +# CONFIG_BLK_DEV_BSG is not set +CONFIG_PARTITION_ADVANCED=y +CONFIG_EFI_PARTITION=y +CONFIG_ARCH_BCM=y +CONFIG_ARM_THUMBEE=y +CONFIG_ARM_ERRATA_743622=y +CONFIG_PREEMPT=y +CONFIG_AEABI=y +# CONFIG_OABI_COMPAT is not set +# CONFIG_COMPACTION is not set +CONFIG_ZBOOT_ROM_TEXT=0x0 +CONFIG_ZBOOT_ROM_BSS=0x0 +CONFIG_ARM_APPENDED_DTB=y +CONFIG_CMDLINE="console=ttyS0,115200n8 mem=128M" +CONFIG_CPU_IDLE=y +CONFIG_VFP=y +CONFIG_NEON=y +# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set +CONFIG_PM_RUNTIME=y +CONFIG_DEVTMPFS=y +CONFIG_DEVTMPFS_MOUNT=y +CONFIG_PROC_DEVICETREE=y +# CONFIG_BLK_DEV is not set +CONFIG_SCSI=y +CONFIG_BLK_DEV_SD=y +CONFIG_CHR_DEV_SG=y +CONFIG_SCSI_MULTI_LUN=y +CONFIG_SCSI_SCAN_ASYNC=y +CONFIG_INPUT_FF_MEMLESS=y +CONFIG_INPUT_JOYDEV=y +CONFIG_INPUT_EVDEV=y +# CONFIG_KEYBOARD_ATKBD is not set +# CONFIG_INPUT_MOUSE is not set +CONFIG_INPUT_TOUCHSCREEN=y +CONFIG_INPUT_MISC=y +CONFIG_INPUT_UINPUT=y +# CONFIG_SERIO is not set +# CONFIG_LEGACY_PTYS is not set +CONFIG_SERIAL_8250=y +CONFIG_SERIAL_8250_CONSOLE=y +CONFIG_SERIAL_8250_EXTENDED=y +CONFIG_SERIAL_8250_MANY_PORTS=y +CONFIG_SERIAL_8250_SHARE_IRQ=y +CONFIG_SERIAL_8250_RSA=y +CONFIG_SERIAL_8250_DW=y +CONFIG_HW_RANDOM=y +CONFIG_I2C=y +CONFIG_I2C_CHARDEV=y +# CONFIG_HWMON is not set +CONFIG_VIDEO_OUTPUT_CONTROL=y +CONFIG_FB=y +CONFIG_BACKLIGHT_LCD_SUPPORT=y +CONFIG_LCD_CLASS_DEVICE=y +CONFIG_BACKLIGHT_CLASS_DEVICE=y +# CONFIG_USB_SUPPORT is not set +CONFIG_NEW_LEDS=y +CONFIG_LEDS_CLASS=y +CONFIG_LEDS_TRIGGERS=y +CONFIG_LEDS_TRIGGER_TIMER=y +CONFIG_LEDS_TRIGGER_HEARTBEAT=y +CONFIG_LEDS_TRIGGER_DEFAULT_ON=y +CONFIG_EXT4_FS=y +CONFIG_EXT4_FS_POSIX_ACL=y +CONFIG_EXT4_FS_SECURITY=y +CONFIG_AUTOFS4_FS=y +CONFIG_FUSE_FS=y +CONFIG_MSDOS_FS=y +CONFIG_VFAT_FS=y +CONFIG_TMPFS=y +CONFIG_TMPFS_POSIX_ACL=y +CONFIG_CONFIGFS_FS=y +# CONFIG_MISC_FILESYSTEMS is not set +CONFIG_NLS_CODEPAGE_437=y +CONFIG_NLS_ISO8859_1=y +CONFIG_PRINTK_TIME=y +CONFIG_MAGIC_SYSRQ=y +CONFIG_DEBUG_FS=y +CONFIG_DETECT_HUNG_TASK=y +CONFIG_DEFAULT_HUNG_TASK_TIMEOUT=110 +CONFIG_BOOTPARAM_HUNG_TASK_PANIC=y +CONFIG_DEBUG_INFO=y +# CONFIG_FTRACE is not set +CONFIG_DEBUG_LL=y +CONFIG_CRC_CCITT=y +CONFIG_CRC_T10DIF=y +CONFIG_CRC_ITU_T=y +CONFIG_CRC7=y +CONFIG_XZ_DEC=y +CONFIG_AVERAGE=y diff --git a/arch/arm/mach-bcm/Kconfig b/arch/arm/mach-bcm/Kconfig new file mode 100644 index 000000000000..48705c10a0fe --- /dev/null +++ b/arch/arm/mach-bcm/Kconfig @@ -0,0 +1,19 @@ +config ARCH_BCM + bool "Broadcom SoC" if ARCH_MULTI_V7 + depends on MMU + select ARCH_REQUIRE_GPIOLIB + select ARM_ERRATA_754322 + select ARM_ERRATA_764369 if SMP + select ARM_GIC + select CPU_V7 + select GENERIC_CLOCKEVENTS + select GENERIC_GPIO + select GENERIC_TIME + select GPIO_BCM + select SPARSE_IRQ + select TICK_ONESHOT + help + This enables support for system based on Broadcom SoCs. + It currently supports the 'BCM281XX' family, which includes + BCM11130, BCM11140, BCM11351, BCM28145 and + BCM28155 variants. diff --git a/arch/arm/mach-bcm/Makefile b/arch/arm/mach-bcm/Makefile new file mode 100644 index 000000000000..bbf412261e5e --- /dev/null +++ b/arch/arm/mach-bcm/Makefile @@ -0,0 +1,13 @@ +# +# Copyright (C) 2012 Broadcom 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. +# +# This program is distributed "as is" WITHOUT ANY WARRANTY of any +# kind, whether express or implied; without even the implied warranty +# of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +obj-$(CONFIG_ARCH_BCM) := board_bcm.o diff --git a/arch/arm/mach-bcm/board_bcm.c b/arch/arm/mach-bcm/board_bcm.c new file mode 100644 index 000000000000..3a62f1b1cabc --- /dev/null +++ b/arch/arm/mach-bcm/board_bcm.c @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2012 Broadcom 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. + * + * This program is distributed "as is" WITHOUT ANY WARRANTY of any + * kind, whether express or implied; 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 of_device_id irq_match[] = { + {.compatible = "arm,cortex-a9-gic", .data = gic_of_init, }, + {} +}; + +static void timer_init(void) +{ +} + +static struct sys_timer timer = { + .init = timer_init, +}; + +static void __init init_irq(void) +{ + of_irq_init(irq_match); +} + +static void __init board_init(void) +{ + of_platform_populate(NULL, of_default_bus_match_table, NULL, + &platform_bus); +} + +static const char * const bcm11351_dt_compat[] = { "bcm,bcm11351", NULL, }; + +DT_MACHINE_START(BCM11351_DT, "Broadcom Application Processor") + .init_irq = init_irq, + .timer = &timer, + .init_machine = board_init, + .dt_compat = bcm11351_dt_compat, + .handle_irq = gic_handle_irq, +MACHINE_END -- cgit v1.2.3-59-g8ed1b From d287de855f97c56ca7146ff627e652bd7cd64f3f Mon Sep 17 00:00:00 2001 From: Nishanth Menon Date: Thu, 25 Oct 2012 19:48:59 -0500 Subject: PM / devfreq: Add sysfs node to expose available frequencies devfreq governors such as ondemand are controlled by a min and max frequency, while governors like userspace governor allow us to set a specific frequency. However, for the same specific device, depending on the SoC, the available frequencies can vary. So expose the available frequencies as a snapshot over sysfs to allow informed decisions. This was inspired by cpufreq framework's equivalent for similar usage sysfs node: scaling_available_frequencies. Cc: Rajagopal Venkat Cc: MyungJoo Ham Cc: Kyungmin Park Cc: "Rafael J. Wysocki" Cc: Kevin Hilman Cc: linux-pm@vger.kernel.org Cc: linux-kernel@vger.kernel.org Signed-off-by: Nishanth Menon Signed-off-by: MyungJoo Ham --- Documentation/ABI/testing/sysfs-class-devfreq | 9 ++++++++ drivers/devfreq/devfreq.c | 32 +++++++++++++++++++++++++++ 2 files changed, 41 insertions(+) (limited to 'Documentation') diff --git a/Documentation/ABI/testing/sysfs-class-devfreq b/Documentation/ABI/testing/sysfs-class-devfreq index e6cf08e6734d..e672ccb02e7f 100644 --- a/Documentation/ABI/testing/sysfs-class-devfreq +++ b/Documentation/ABI/testing/sysfs-class-devfreq @@ -51,3 +51,12 @@ Description: The /sys/class/devfreq/.../userspace/set_freq shows and sets the requested frequency for the devfreq object if userspace governor is in effect. + +What: /sys/class/devfreq/.../available_frequencies +Date: October 2012 +Contact: Nishanth Menon +Description: + The /sys/class/devfreq/.../available_frequencies shows + the available frequencies of the corresponding devfreq object. + This is a snapshot of available frequencies and not limited + by the min/max frequency restrictions. diff --git a/drivers/devfreq/devfreq.c b/drivers/devfreq/devfreq.c index 789af4ff5c9c..c44e562bdfe0 100644 --- a/drivers/devfreq/devfreq.c +++ b/drivers/devfreq/devfreq.c @@ -570,9 +570,41 @@ static ssize_t show_max_freq(struct device *dev, struct device_attribute *attr, return sprintf(buf, "%lu\n", to_devfreq(dev)->max_freq); } +static ssize_t show_available_freqs(struct device *d, + struct device_attribute *attr, + char *buf) +{ + struct devfreq *df = to_devfreq(d); + struct device *dev = df->dev.parent; + struct opp *opp; + ssize_t count = 0; + unsigned long freq = 0; + + rcu_read_lock(); + do { + opp = opp_find_freq_ceil(dev, &freq); + if (IS_ERR(opp)) + break; + + count += scnprintf(&buf[count], (PAGE_SIZE - count - 2), + "%lu ", freq); + freq++; + } while (1); + rcu_read_unlock(); + + /* Truncate the trailing space */ + if (count) + count--; + + count += sprintf(&buf[count], "\n"); + + return count; +} + static struct device_attribute devfreq_attrs[] = { __ATTR(governor, S_IRUGO, show_governor, NULL), __ATTR(cur_freq, S_IRUGO, show_freq, NULL), + __ATTR(available_frequencies, S_IRUGO, show_available_freqs, NULL), __ATTR(target_freq, S_IRUGO, show_target_freq, NULL), __ATTR(polling_interval, S_IRUGO | S_IWUSR, show_polling_interval, store_polling_interval), -- cgit v1.2.3-59-g8ed1b From e552bbaf5b987f57c43e6981a452b8a3c700b1ae Mon Sep 17 00:00:00 2001 From: Jonghwa Lee Date: Thu, 23 Aug 2012 20:00:46 +0900 Subject: PM / devfreq: Add sysfs node for representing frequency transition information. This patch adds sysfs node which can be used to get information of frequency transition. It represents transition table which contains total number of transition of each freqeuncy state and time spent. It is inspired CPUFREQ's status driver. Signed-off-by: Jonghwa Lee [Added Documentation/ABI entry, updated kernel-doc, and resolved merge conflict] Signed-off-by: MyungJoo Ham --- Documentation/ABI/testing/sysfs-class-devfreq | 11 +++ drivers/devfreq/devfreq.c | 101 ++++++++++++++++++++++++++ include/linux/devfreq.h | 15 ++++ 3 files changed, 127 insertions(+) (limited to 'Documentation') diff --git a/Documentation/ABI/testing/sysfs-class-devfreq b/Documentation/ABI/testing/sysfs-class-devfreq index e672ccb02e7f..40f98a9428dc 100644 --- a/Documentation/ABI/testing/sysfs-class-devfreq +++ b/Documentation/ABI/testing/sysfs-class-devfreq @@ -44,6 +44,17 @@ Description: (/sys/class/devfreq/.../central_polling is 0), this value may be useless. +What: /sys/class/devfreq/.../trans_stat +Date: October 2012 +Contact: MyungJoo Ham +Descrtiption: + This ABI shows the statistics of devfreq behavior on a + specific device. It shows the time spent in each state and + the number of transitions between states. + In order to activate this ABI, the devfreq target device + driver should provide the list of available frequencies + with its profile. + What: /sys/class/devfreq/.../userspace/set_freq Date: September 2011 Contact: MyungJoo Ham diff --git a/drivers/devfreq/devfreq.c b/drivers/devfreq/devfreq.c index c44e562bdfe0..bf6de38190cf 100644 --- a/drivers/devfreq/devfreq.c +++ b/drivers/devfreq/devfreq.c @@ -66,6 +66,51 @@ static struct devfreq *find_device_devfreq(struct device *dev) return ERR_PTR(-ENODEV); } +/** + * devfreq_get_freq_level() - Lookup freq_table for the frequency + * @devfreq: the devfreq instance + * @freq: the target frequency + */ +static int devfreq_get_freq_level(struct devfreq *devfreq, unsigned long freq) +{ + int lev; + + for (lev = 0; lev < devfreq->profile->max_state; lev++) + if (freq == devfreq->profile->freq_table[lev]) + return lev; + + return -EINVAL; +} + +/** + * devfreq_update_status() - Update statistics of devfreq behavior + * @devfreq: the devfreq instance + * @freq: the update target frequency + */ +static int devfreq_update_status(struct devfreq *devfreq, unsigned long freq) +{ + int lev, prev_lev; + unsigned long cur_time; + + lev = devfreq_get_freq_level(devfreq, freq); + if (lev < 0) + return lev; + + cur_time = jiffies; + devfreq->time_in_state[lev] += + cur_time - devfreq->last_stat_updated; + if (freq != devfreq->previous_freq) { + prev_lev = devfreq_get_freq_level(devfreq, + devfreq->previous_freq); + devfreq->trans_table[(prev_lev * + devfreq->profile->max_state) + lev]++; + devfreq->total_trans++; + } + devfreq->last_stat_updated = cur_time; + + return 0; +} + /* Load monitoring helper functions for governors use */ /** @@ -112,6 +157,11 @@ int update_devfreq(struct devfreq *devfreq) if (err) return err; + if (devfreq->profile->freq_table) + if (devfreq_update_status(devfreq, freq)) + dev_err(&devfreq->dev, + "Couldn't update frequency transition information.\n"); + devfreq->previous_freq = freq; return err; } @@ -378,6 +428,15 @@ struct devfreq *devfreq_add_device(struct device *dev, devfreq->data = data; devfreq->nb.notifier_call = devfreq_notifier_call; + devfreq->trans_table = devm_kzalloc(dev, sizeof(unsigned int) * + devfreq->profile->max_state * + devfreq->profile->max_state, + GFP_KERNEL); + devfreq->time_in_state = devm_kzalloc(dev, sizeof(unsigned int) * + devfreq->profile->max_state, + GFP_KERNEL); + devfreq->last_stat_updated = jiffies; + dev_set_name(&devfreq->dev, dev_name(dev)); err = device_register(&devfreq->dev); if (err) { @@ -601,6 +660,47 @@ static ssize_t show_available_freqs(struct device *d, return count; } +static ssize_t show_trans_table(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct devfreq *devfreq = to_devfreq(dev); + ssize_t len; + int i, j, err; + unsigned int max_state = devfreq->profile->max_state; + + err = devfreq_update_status(devfreq, devfreq->previous_freq); + if (err) + return 0; + + len = sprintf(buf, " From : To\n"); + len += sprintf(buf + len, " :"); + for (i = 0; i < max_state; i++) + len += sprintf(buf + len, "%8u", + devfreq->profile->freq_table[i]); + + len += sprintf(buf + len, " time(ms)\n"); + + for (i = 0; i < max_state; i++) { + if (devfreq->profile->freq_table[i] + == devfreq->previous_freq) { + len += sprintf(buf + len, "*"); + } else { + len += sprintf(buf + len, " "); + } + len += sprintf(buf + len, "%8u:", + devfreq->profile->freq_table[i]); + for (j = 0; j < max_state; j++) + len += sprintf(buf + len, "%8u", + devfreq->trans_table[(i * max_state) + j]); + len += sprintf(buf + len, "%10u\n", + jiffies_to_msecs(devfreq->time_in_state[i])); + } + + len += sprintf(buf + len, "Total transition : %u\n", + devfreq->total_trans); + return len; +} + static struct device_attribute devfreq_attrs[] = { __ATTR(governor, S_IRUGO, show_governor, NULL), __ATTR(cur_freq, S_IRUGO, show_freq, NULL), @@ -610,6 +710,7 @@ static struct device_attribute devfreq_attrs[] = { store_polling_interval), __ATTR(min_freq, S_IRUGO | S_IWUSR, show_min_freq, store_min_freq), __ATTR(max_freq, S_IRUGO | S_IWUSR, show_max_freq, store_max_freq), + __ATTR(trans_stat, S_IRUGO, show_trans_table, NULL), { }, }; diff --git a/include/linux/devfreq.h b/include/linux/devfreq.h index 1461fb2355ad..bc35c4aee6a3 100644 --- a/include/linux/devfreq.h +++ b/include/linux/devfreq.h @@ -73,6 +73,8 @@ struct devfreq_dev_status { * from devfreq_remove_device() call. If the user * has registered devfreq->nb at a notifier-head, * this is the time to unregister it. + * @freq_table: Optional list of frequencies to support statistics. + * @max_state: The size of freq_table. */ struct devfreq_dev_profile { unsigned long initial_freq; @@ -83,6 +85,9 @@ struct devfreq_dev_profile { struct devfreq_dev_status *stat); int (*get_cur_freq)(struct device *dev, unsigned long *freq); void (*exit)(struct device *dev); + + unsigned int *freq_table; + unsigned int max_state; }; /** @@ -127,6 +132,10 @@ struct devfreq_governor { * @min_freq: Limit minimum frequency requested by user (0: none) * @max_freq: Limit maximum frequency requested by user (0: none) * @stop_polling: devfreq polling status of a device. + * @total_trans: Number of devfreq transitions + * @trans_table: Statistics of devfreq transitions + * @time_in_state: Statistics of devfreq states + * @last_stat_updated: The last time stat updated * * This structure stores the devfreq information for a give device. * @@ -153,6 +162,12 @@ struct devfreq { unsigned long min_freq; unsigned long max_freq; bool stop_polling; + + /* information for device freqeuncy transition */ + unsigned int total_trans; + unsigned int *trans_table; + unsigned long *time_in_state; + unsigned long last_stat_updated; }; #if defined(CONFIG_PM_DEVFREQ) -- cgit v1.2.3-59-g8ed1b From 0359d1afe4013d1a216908b6be4c6695a1db6fd6 Mon Sep 17 00:00:00 2001 From: Nishanth Menon Date: Mon, 29 Oct 2012 15:01:47 -0500 Subject: PM / devfreq: allow sysfs governor node to switch governor This allows us to select governor runtime from the default configuration without having to rebuild kernel or the devfreq driver using the sysfs node: /sys/class/devfreq/.../governor cat of the governor will return valid governor and an echo 'governor_name'>governor will switch governor Cc: Rajagopal Venkat Cc: MyungJoo Ham Cc: Kyungmin Park Cc: "Rafael J. Wysocki" Cc: Kevin Hilman Cc: linux-pm@vger.kernel.org Cc: linux-kernel@vger.kernel.org Signed-off-by: Nishanth Menon Acked-by: MyungJoo Ham Signed-off-by: MyungJoo Ham --- Documentation/ABI/testing/sysfs-class-devfreq | 2 +- drivers/devfreq/devfreq.c | 45 ++++++++++++++++++++++++++- 2 files changed, 45 insertions(+), 2 deletions(-) (limited to 'Documentation') diff --git a/Documentation/ABI/testing/sysfs-class-devfreq b/Documentation/ABI/testing/sysfs-class-devfreq index 40f98a9428dc..66876debca18 100644 --- a/Documentation/ABI/testing/sysfs-class-devfreq +++ b/Documentation/ABI/testing/sysfs-class-devfreq @@ -11,7 +11,7 @@ What: /sys/class/devfreq/.../governor Date: September 2011 Contact: MyungJoo Ham Description: - The /sys/class/devfreq/.../governor shows the name of the + The /sys/class/devfreq/.../governor show or set the name of the governor used by the corresponding devfreq object. What: /sys/class/devfreq/.../cur_freq diff --git a/drivers/devfreq/devfreq.c b/drivers/devfreq/devfreq.c index 0d7be03d561f..ff960f084c11 100644 --- a/drivers/devfreq/devfreq.c +++ b/drivers/devfreq/devfreq.c @@ -688,6 +688,49 @@ static ssize_t show_governor(struct device *dev, return sprintf(buf, "%s\n", to_devfreq(dev)->governor->name); } +static ssize_t store_governor(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct devfreq *df = to_devfreq(dev); + int ret; + char str_governor[DEVFREQ_NAME_LEN + 1]; + struct devfreq_governor *governor; + + ret = sscanf(buf, "%" __stringify(DEVFREQ_NAME_LEN) "s", str_governor); + if (ret != 1) + return -EINVAL; + + mutex_lock(&devfreq_list_lock); + governor = find_devfreq_governor(str_governor); + if (IS_ERR(governor)) { + ret = PTR_ERR(governor); + goto out; + } + if (df->governor == governor) + goto out; + + if (df->governor) { + ret = df->governor->event_handler(df, DEVFREQ_GOV_STOP, NULL); + if (ret) { + dev_warn(dev, "%s: Governor %s not stopped(%d)\n", + __func__, df->governor->name, ret); + goto out; + } + } + df->governor = governor; + strncpy(df->governor_name, governor->name, DEVFREQ_NAME_LEN); + ret = df->governor->event_handler(df, DEVFREQ_GOV_START, NULL); + if (ret) + dev_warn(dev, "%s: Governor %s not started(%d)\n", + __func__, df->governor->name, ret); +out: + mutex_unlock(&devfreq_list_lock); + + if (!ret) + ret = count; + return ret; +} + static ssize_t show_freq(struct device *dev, struct device_attribute *attr, char *buf) { @@ -873,7 +916,7 @@ static ssize_t show_trans_table(struct device *dev, struct device_attribute *att } static struct device_attribute devfreq_attrs[] = { - __ATTR(governor, S_IRUGO, show_governor, NULL), + __ATTR(governor, S_IRUGO | S_IWUSR, show_governor, store_governor), __ATTR(cur_freq, S_IRUGO, show_freq, NULL), __ATTR(available_frequencies, S_IRUGO, show_available_freqs, NULL), __ATTR(target_freq, S_IRUGO, show_target_freq, NULL), -- cgit v1.2.3-59-g8ed1b From 50a5b33e0159f8783ef617cdb9d5fbb6a3955b6f Mon Sep 17 00:00:00 2001 From: Nishanth Menon Date: Mon, 29 Oct 2012 15:01:48 -0500 Subject: PM / devfreq: Add sysfs node to expose available governors Now that governor list can be variable, knowing the available governors is useful to be able to select a governor using relevant sysfs node. Cc: Rajagopal Venkat Cc: MyungJoo Ham Cc: Kyungmin Park Cc: "Rafael J. Wysocki" Cc: Kevin Hilman Cc: linux-pm@vger.kernel.org Cc: linux-kernel@vger.kernel.org Signed-off-by: Nishanth Menon Signed-off-by: MyungJoo Ham --- Documentation/ABI/testing/sysfs-class-devfreq | 7 +++++++ drivers/devfreq/devfreq.c | 22 ++++++++++++++++++++++ 2 files changed, 29 insertions(+) (limited to 'Documentation') diff --git a/Documentation/ABI/testing/sysfs-class-devfreq b/Documentation/ABI/testing/sysfs-class-devfreq index 66876debca18..0ba6ea2f89d9 100644 --- a/Documentation/ABI/testing/sysfs-class-devfreq +++ b/Documentation/ABI/testing/sysfs-class-devfreq @@ -71,3 +71,10 @@ Description: the available frequencies of the corresponding devfreq object. This is a snapshot of available frequencies and not limited by the min/max frequency restrictions. + +What: /sys/class/devfreq/.../available_governors +Date: October 2012 +Contact: Nishanth Menon +Description: + The /sys/class/devfreq/.../available_governors shows + currently available governors in the system. diff --git a/drivers/devfreq/devfreq.c b/drivers/devfreq/devfreq.c index ff960f084c11..45e053e5b139 100644 --- a/drivers/devfreq/devfreq.c +++ b/drivers/devfreq/devfreq.c @@ -730,6 +730,27 @@ out: ret = count; return ret; } +static ssize_t show_available_governors(struct device *d, + struct device_attribute *attr, + char *buf) +{ + struct devfreq_governor *tmp_governor; + ssize_t count = 0; + + mutex_lock(&devfreq_list_lock); + list_for_each_entry(tmp_governor, &devfreq_governor_list, node) + count += scnprintf(&buf[count], (PAGE_SIZE - count - 2), + "%s ", tmp_governor->name); + mutex_unlock(&devfreq_list_lock); + + /* Truncate the trailing space */ + if (count) + count--; + + count += sprintf(&buf[count], "\n"); + + return count; +} static ssize_t show_freq(struct device *dev, struct device_attribute *attr, char *buf) @@ -917,6 +938,7 @@ static ssize_t show_trans_table(struct device *dev, struct device_attribute *att static struct device_attribute devfreq_attrs[] = { __ATTR(governor, S_IRUGO | S_IWUSR, show_governor, store_governor), + __ATTR(available_governors, S_IRUGO, show_available_governors, NULL), __ATTR(cur_freq, S_IRUGO, show_freq, NULL), __ATTR(available_frequencies, S_IRUGO, show_available_freqs, NULL), __ATTR(target_freq, S_IRUGO, show_target_freq, NULL), -- cgit v1.2.3-59-g8ed1b From c47d244a646d08e2161b7fa22c5512e7988762ae Mon Sep 17 00:00:00 2001 From: Vasanth Ananthan Date: Tue, 20 Nov 2012 21:02:11 +0900 Subject: ARM: EXYNOS: DT Support for SATA and SATA PHY This patch adds Device Nodes for SATA and SATA PHY device. Signed-off-by: Vasanth Ananthan [kgene.kim@samsung.com: removed address definitions as per comments] Signed-off-by: Kukjin Kim --- .../devicetree/bindings/ata/exynos-sata-phy.txt | 14 ++++++++++++++ Documentation/devicetree/bindings/ata/exynos-sata.txt | 17 +++++++++++++++++ arch/arm/boot/dts/exynos5250-smdk5250.dts | 15 +++++++++++++++ arch/arm/boot/dts/exynos5250.dtsi | 18 ++++++++++++++++++ arch/arm/mach-exynos/mach-exynos5-dt.c | 6 ++++++ 5 files changed, 70 insertions(+) create mode 100644 Documentation/devicetree/bindings/ata/exynos-sata-phy.txt create mode 100644 Documentation/devicetree/bindings/ata/exynos-sata.txt (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/ata/exynos-sata-phy.txt b/Documentation/devicetree/bindings/ata/exynos-sata-phy.txt new file mode 100644 index 000000000000..37824fac688e --- /dev/null +++ b/Documentation/devicetree/bindings/ata/exynos-sata-phy.txt @@ -0,0 +1,14 @@ +* Samsung SATA PHY Controller + +SATA PHY nodes are defined to describe on-chip SATA Physical layer controllers. +Each SATA PHY controller should have its own node. + +Required properties: +- compatible : compatible list, contains "samsung,exynos5-sata-phy" +- reg : + +Example: + sata@ffe07000 { + compatible = "samsung,exynos5-sata-phy"; + reg = <0xffe07000 0x1000>; + }; diff --git a/Documentation/devicetree/bindings/ata/exynos-sata.txt b/Documentation/devicetree/bindings/ata/exynos-sata.txt new file mode 100644 index 000000000000..0849f1025e34 --- /dev/null +++ b/Documentation/devicetree/bindings/ata/exynos-sata.txt @@ -0,0 +1,17 @@ +* Samsung AHCI SATA Controller + +SATA nodes are defined to describe on-chip Serial ATA controllers. +Each SATA controller should have its own node. + +Required properties: +- compatible : compatible list, contains "samsung,exynos5-sata" +- interrupts : +- reg : +- samsung,sata-freq : + +Example: + sata@ffe08000 { + compatible = "samsung,exynos5-sata"; + reg = <0xffe08000 0x1000>; + interrupts = <115>; + }; diff --git a/arch/arm/boot/dts/exynos5250-smdk5250.dts b/arch/arm/boot/dts/exynos5250-smdk5250.dts index 21d4ccdb0a5a..3f8c2ab1d038 100644 --- a/arch/arm/boot/dts/exynos5250-smdk5250.dts +++ b/arch/arm/boot/dts/exynos5250-smdk5250.dts @@ -55,6 +55,21 @@ }; }; + i2c@121D0000 { + samsung,i2c-sda-delay = <100>; + samsung,i2c-max-bus-freq = <40000>; + samsung,i2c-slave-addr = <0x38>; + + sata-phy { + compatible = "samsung,sata-phy"; + reg = <0x38>; + }; + }; + + sata@122F0000 { + samsung,sata-freq = <66>; + }; + i2c@12C80000 { status = "disabled"; }; diff --git a/arch/arm/boot/dts/exynos5250.dtsi b/arch/arm/boot/dts/exynos5250.dtsi index 670dfdb01a09..e913525a4814 100644 --- a/arch/arm/boot/dts/exynos5250.dtsi +++ b/arch/arm/boot/dts/exynos5250.dtsi @@ -104,6 +104,17 @@ interrupts = <0 54 0>; }; + sata@122F0000 { + compatible = "samsung,exynos5-sata-ahci"; + reg = <0x122F0000 0x1ff>; + interrupts = <0 115 0>; + }; + + sata-phy@12170000 { + compatible = "samsung,exynos5-sata-phy"; + reg = <0x12170000 0x1ff>; + }; + i2c@12C60000 { compatible = "samsung,s3c2440-i2c"; reg = <0x12C60000 0x100>; @@ -168,6 +179,13 @@ #size-cells = <0>; }; + i2c@121D0000 { + compatible = "samsung,exynos5-sata-phy-i2c"; + reg = <0x121D0000 0x100>; + #address-cells = <1>; + #size-cells = <0>; + }; + spi_0: spi@12d20000 { compatible = "samsung,exynos4210-spi"; reg = <0x12d20000 0x100>; diff --git a/arch/arm/mach-exynos/mach-exynos5-dt.c b/arch/arm/mach-exynos/mach-exynos5-dt.c index 4c0f3369abdf..a88c19f5efcc 100644 --- a/arch/arm/mach-exynos/mach-exynos5-dt.c +++ b/arch/arm/mach-exynos/mach-exynos5-dt.c @@ -64,6 +64,12 @@ static const struct of_dev_auxdata exynos5250_auxdata_lookup[] __initconst = { "exynos4210-spi.1", NULL), OF_DEV_AUXDATA("samsung,exynos4210-spi", EXYNOS5_PA_SPI2, "exynos4210-spi.2", NULL), + OF_DEV_AUXDATA("samsung,exynos5-sata-ahci", 0x122F0000, + "exynos5-sata", NULL), + OF_DEV_AUXDATA("samsung,exynos5-sata-phy", 0x12170000, + "exynos5-sata-phy", NULL), + OF_DEV_AUXDATA("samsung,exynos5-sata-phy-i2c", 0x121D0000, + "exynos5-sata-phy-i2c", NULL), OF_DEV_AUXDATA("arm,pl330", EXYNOS5_PA_PDMA0, "dma-pl330.0", NULL), OF_DEV_AUXDATA("arm,pl330", EXYNOS5_PA_PDMA1, "dma-pl330.1", NULL), OF_DEV_AUXDATA("arm,pl330", EXYNOS5_PA_MDMA1, "dma-pl330.2", NULL), -- cgit v1.2.3-59-g8ed1b From 97fa4cf442ff2872000d9110686371775795a32b Mon Sep 17 00:00:00 2001 From: Sebastian Hesselbarth Date: Sat, 17 Nov 2012 15:22:22 +0100 Subject: clk: mvebu: add mvebu core clocks. This driver allows to provide DT clocks for core clocks found on Marvell Kirkwood, Dove & 370/XP SoCs. The core clock frequencies and ratios are determined by decoding the Sample-At-Reset registers. Although technically correct, using a divider of 0 will lead to div_by_zero panic. Let's use a ratio of 0/1 instead to fail later with a zero clock. Signed-off-by: Gregory CLEMENT Signed-off-by: Sebastian Hesselbarth Signed-off-by: Andrew Lunn Tested-by Gregory CLEMENT --- .../devicetree/bindings/clock/mvebu-core-clock.txt | 47 ++ drivers/clk/Kconfig | 2 + drivers/clk/Makefile | 1 + drivers/clk/mvebu/Kconfig | 3 + drivers/clk/mvebu/Makefile | 1 + drivers/clk/mvebu/clk-core.c | 675 +++++++++++++++++++++ drivers/clk/mvebu/clk-core.h | 18 + drivers/clk/mvebu/clk.c | 23 + include/linux/clk/mvebu.h | 22 + 9 files changed, 792 insertions(+) create mode 100644 Documentation/devicetree/bindings/clock/mvebu-core-clock.txt create mode 100644 drivers/clk/mvebu/Kconfig create mode 100644 drivers/clk/mvebu/Makefile create mode 100644 drivers/clk/mvebu/clk-core.c create mode 100644 drivers/clk/mvebu/clk-core.h create mode 100644 drivers/clk/mvebu/clk.c create mode 100644 include/linux/clk/mvebu.h (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/clock/mvebu-core-clock.txt b/Documentation/devicetree/bindings/clock/mvebu-core-clock.txt new file mode 100644 index 000000000000..1e662948661e --- /dev/null +++ b/Documentation/devicetree/bindings/clock/mvebu-core-clock.txt @@ -0,0 +1,47 @@ +* Core Clock bindings for Marvell MVEBU SoCs + +Marvell MVEBU SoCs usually allow to determine core clock frequencies by +reading the Sample-At-Reset (SAR) register. The core clock consumer should +specify the desired clock by having the clock ID in its "clocks" phandle cell. + +The following is a list of provided IDs and clock names on Armada 370/XP: + 0 = tclk (Internal Bus clock) + 1 = cpuclk (CPU clock) + 2 = nbclk (L2 Cache clock) + 3 = hclk (DRAM control clock) + 4 = dramclk (DDR clock) + +The following is a list of provided IDs and clock names on Kirkwood and Dove: + 0 = tclk (Internal Bus clock) + 1 = cpuclk (CPU0 clock) + 2 = l2clk (L2 Cache clock derived from CPU0 clock) + 3 = ddrclk (DDR controller clock derived from CPU0 clock) + +Required properties: +- compatible : shall be one of the following: + "marvell,armada-370-core-clock" - For Armada 370 SoC core clocks + "marvell,armada-xp-core-clock" - For Armada XP SoC core clocks + "marvell,dove-core-clock" - for Dove SoC core clocks + "marvell,kirkwood-core-clock" - for Kirkwood SoC (except mv88f6180) + "marvell,mv88f6180-core-clock" - for Kirkwood MV88f6180 SoC +- reg : shall be the register address of the Sample-At-Reset (SAR) register +- #clock-cells : from common clock binding; shall be set to 1 + +Optional properties: +- clock-output-names : from common clock binding; allows overwrite default clock + output names ("tclk", "cpuclk", "l2clk", "ddrclk") + +Example: + +core_clk: core-clocks@d0214 { + compatible = "marvell,dove-core-clock"; + reg = <0xd0214 0x4>; + #clock-cells = <1>; +}; + +spi0: spi@10600 { + compatible = "marvell,orion-spi"; + /* ... */ + /* get tclk from core clock provider */ + clocks = <&core_clk 0>; +}; diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig index bace9e98f75d..60427c0d23e6 100644 --- a/drivers/clk/Kconfig +++ b/drivers/clk/Kconfig @@ -54,3 +54,5 @@ config COMMON_CLK_MAX77686 This driver supports Maxim 77686 crystal oscillator clock. endmenu + +source "drivers/clk/mvebu/Kconfig" diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile index 71a25b91de00..d0a14ae8d49c 100644 --- a/drivers/clk/Makefile +++ b/drivers/clk/Makefile @@ -13,6 +13,7 @@ obj-$(CONFIG_PLAT_SPEAR) += spear/ obj-$(CONFIG_ARCH_U300) += clk-u300.o obj-$(CONFIG_COMMON_CLK_VERSATILE) += versatile/ obj-$(CONFIG_ARCH_PRIMA2) += clk-prima2.o +obj-$(CONFIG_PLAT_ORION) += mvebu/ ifeq ($(CONFIG_COMMON_CLK), y) obj-$(CONFIG_ARCH_MMP) += mmp/ endif diff --git a/drivers/clk/mvebu/Kconfig b/drivers/clk/mvebu/Kconfig new file mode 100644 index 000000000000..fd7bf97ff74d --- /dev/null +++ b/drivers/clk/mvebu/Kconfig @@ -0,0 +1,3 @@ +config MVEBU_CLK_CORE + bool + diff --git a/drivers/clk/mvebu/Makefile b/drivers/clk/mvebu/Makefile new file mode 100644 index 000000000000..de1d9617f75a --- /dev/null +++ b/drivers/clk/mvebu/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_MVEBU_CLK_CORE) += clk.o clk-core.o diff --git a/drivers/clk/mvebu/clk-core.c b/drivers/clk/mvebu/clk-core.c new file mode 100644 index 000000000000..69056a7479e8 --- /dev/null +++ b/drivers/clk/mvebu/clk-core.c @@ -0,0 +1,675 @@ +/* + * Marvell EBU clock core handling defined at reset + * + * Copyright (C) 2012 Marvell + * + * Gregory CLEMENT + * Sebastian Hesselbarth + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ +#include +#include +#include +#include +#include +#include +#include +#include "clk-core.h" + +struct core_ratio { + int id; + const char *name; +}; + +struct core_clocks { + u32 (*get_tclk_freq)(void __iomem *sar); + u32 (*get_cpu_freq)(void __iomem *sar); + void (*get_clk_ratio)(void __iomem *sar, int id, int *mult, int *div); + const struct core_ratio *ratios; + int num_ratios; +}; + +static struct clk_onecell_data clk_data; + +static void __init mvebu_clk_core_setup(struct device_node *np, + struct core_clocks *coreclk) +{ + const char *tclk_name = "tclk"; + const char *cpuclk_name = "cpuclk"; + void __iomem *base; + unsigned long rate; + int n; + + base = of_iomap(np, 0); + if (WARN_ON(!base)) + return; + + /* + * Allocate struct for TCLK, cpu clk, and core ratio clocks + */ + clk_data.clk_num = 2 + coreclk->num_ratios; + clk_data.clks = kzalloc(clk_data.clk_num * sizeof(struct clk *), + GFP_KERNEL); + if (WARN_ON(!clk_data.clks)) + return; + + /* + * Register TCLK + */ + of_property_read_string_index(np, "clock-output-names", 0, + &tclk_name); + rate = coreclk->get_tclk_freq(base); + clk_data.clks[0] = clk_register_fixed_rate(NULL, tclk_name, NULL, + CLK_IS_ROOT, rate); + WARN_ON(IS_ERR(clk_data.clks[0])); + + /* + * Register CPU clock + */ + of_property_read_string_index(np, "clock-output-names", 1, + &cpuclk_name); + rate = coreclk->get_cpu_freq(base); + clk_data.clks[1] = clk_register_fixed_rate(NULL, cpuclk_name, NULL, + CLK_IS_ROOT, rate); + WARN_ON(IS_ERR(clk_data.clks[1])); + + /* + * Register fixed-factor clocks derived from CPU clock + */ + for (n = 0; n < coreclk->num_ratios; n++) { + const char *rclk_name = coreclk->ratios[n].name; + int mult, div; + + of_property_read_string_index(np, "clock-output-names", + 2+n, &rclk_name); + coreclk->get_clk_ratio(base, coreclk->ratios[n].id, + &mult, &div); + clk_data.clks[2+n] = clk_register_fixed_factor(NULL, rclk_name, + cpuclk_name, 0, mult, div); + WARN_ON(IS_ERR(clk_data.clks[2+n])); + }; + + /* + * SAR register isn't needed anymore + */ + iounmap(base); + + of_clk_add_provider(np, of_clk_src_onecell_get, &clk_data); +} + +#ifdef CONFIG_MACH_ARMADA_370_XP +/* + * Armada 370/XP Sample At Reset is a 64 bit bitfiled split in two + * register of 32 bits + */ + +#define SARL 0 /* Low part [0:31] */ +#define SARL_AXP_PCLK_FREQ_OPT 21 +#define SARL_AXP_PCLK_FREQ_OPT_MASK 0x7 +#define SARL_A370_PCLK_FREQ_OPT 11 +#define SARL_A370_PCLK_FREQ_OPT_MASK 0xF +#define SARL_AXP_FAB_FREQ_OPT 24 +#define SARL_AXP_FAB_FREQ_OPT_MASK 0xF +#define SARL_A370_FAB_FREQ_OPT 15 +#define SARL_A370_FAB_FREQ_OPT_MASK 0x1F +#define SARL_A370_TCLK_FREQ_OPT 20 +#define SARL_A370_TCLK_FREQ_OPT_MASK 0x1 +#define SARH 4 /* High part [32:63] */ +#define SARH_AXP_PCLK_FREQ_OPT (52-32) +#define SARH_AXP_PCLK_FREQ_OPT_MASK 0x1 +#define SARH_AXP_PCLK_FREQ_OPT_SHIFT 3 +#define SARH_AXP_FAB_FREQ_OPT (51-32) +#define SARH_AXP_FAB_FREQ_OPT_MASK 0x1 +#define SARH_AXP_FAB_FREQ_OPT_SHIFT 4 + +static const u32 __initconst armada_370_tclk_frequencies[] = { + 16600000, + 20000000, +}; + +static u32 __init armada_370_get_tclk_freq(void __iomem *sar) +{ + u8 tclk_freq_select = 0; + + tclk_freq_select = ((readl(sar) >> SARL_A370_TCLK_FREQ_OPT) & + SARL_A370_TCLK_FREQ_OPT_MASK); + return armada_370_tclk_frequencies[tclk_freq_select]; +} + +static const u32 __initconst armada_370_cpu_frequencies[] = { + 400000000, + 533000000, + 667000000, + 800000000, + 1000000000, + 1067000000, + 1200000000, +}; + +static u32 __init armada_370_get_cpu_freq(void __iomem *sar) +{ + u32 cpu_freq; + u8 cpu_freq_select = 0; + + cpu_freq_select = ((readl(sar) >> SARL_A370_PCLK_FREQ_OPT) & + SARL_A370_PCLK_FREQ_OPT_MASK); + if (cpu_freq_select > ARRAY_SIZE(armada_370_cpu_frequencies)) { + pr_err("CPU freq select unsuported %d\n", cpu_freq_select); + cpu_freq = 0; + } else + cpu_freq = armada_370_cpu_frequencies[cpu_freq_select]; + + return cpu_freq; +} + +enum { A370_XP_NBCLK, A370_XP_HCLK, A370_XP_DRAMCLK }; + +static const struct core_ratio __initconst armada_370_xp_core_ratios[] = { + { .id = A370_XP_NBCLK, .name = "nbclk" }, + { .id = A370_XP_HCLK, .name = "hclk" }, + { .id = A370_XP_DRAMCLK, .name = "dramclk" }, +}; + +static const int __initconst armada_370_xp_nbclk_ratios[32][2] = { + {0, 1}, {1, 2}, {2, 2}, {2, 2}, + {1, 2}, {1, 2}, {1, 1}, {2, 3}, + {0, 1}, {1, 2}, {2, 4}, {0, 1}, + {1, 2}, {0, 1}, {0, 1}, {2, 2}, + {0, 1}, {0, 1}, {0, 1}, {1, 1}, + {2, 3}, {0, 1}, {0, 1}, {0, 1}, + {0, 1}, {0, 1}, {0, 1}, {1, 1}, + {0, 1}, {0, 1}, {0, 1}, {0, 1}, +}; + +static const int __initconst armada_370_xp_hclk_ratios[32][2] = { + {0, 1}, {1, 2}, {2, 6}, {2, 3}, + {1, 3}, {1, 4}, {1, 2}, {2, 6}, + {0, 1}, {1, 6}, {2, 10}, {0, 1}, + {1, 4}, {0, 1}, {0, 1}, {2, 5}, + {0, 1}, {0, 1}, {0, 1}, {1, 2}, + {2, 6}, {0, 1}, {0, 1}, {0, 1}, + {0, 1}, {0, 1}, {0, 1}, {1, 1}, + {0, 1}, {0, 1}, {0, 1}, {0, 1}, +}; + +static const int __initconst armada_370_xp_dramclk_ratios[32][2] = { + {0, 1}, {1, 2}, {2, 3}, {2, 3}, + {1, 3}, {1, 2}, {1, 2}, {2, 6}, + {0, 1}, {1, 3}, {2, 5}, {0, 1}, + {1, 4}, {0, 1}, {0, 1}, {2, 5}, + {0, 1}, {0, 1}, {0, 1}, {1, 1}, + {2, 3}, {0, 1}, {0, 1}, {0, 1}, + {0, 1}, {0, 1}, {0, 1}, {1, 1}, + {0, 1}, {0, 1}, {0, 1}, {0, 1}, +}; + +static void __init armada_370_xp_get_clk_ratio(u32 opt, + void __iomem *sar, int id, int *mult, int *div) +{ + switch (id) { + case A370_XP_NBCLK: + *mult = armada_370_xp_nbclk_ratios[opt][0]; + *div = armada_370_xp_nbclk_ratios[opt][1]; + break; + case A370_XP_HCLK: + *mult = armada_370_xp_hclk_ratios[opt][0]; + *div = armada_370_xp_hclk_ratios[opt][1]; + break; + case A370_XP_DRAMCLK: + *mult = armada_370_xp_dramclk_ratios[opt][0]; + *div = armada_370_xp_dramclk_ratios[opt][1]; + break; + } +} + +static void __init armada_370_get_clk_ratio( + void __iomem *sar, int id, int *mult, int *div) +{ + u32 opt = ((readl(sar) >> SARL_A370_FAB_FREQ_OPT) & + SARL_A370_FAB_FREQ_OPT_MASK); + + armada_370_xp_get_clk_ratio(opt, sar, id, mult, div); +} + + +static const struct core_clocks armada_370_core_clocks = { + .get_tclk_freq = armada_370_get_tclk_freq, + .get_cpu_freq = armada_370_get_cpu_freq, + .get_clk_ratio = armada_370_get_clk_ratio, + .ratios = armada_370_xp_core_ratios, + .num_ratios = ARRAY_SIZE(armada_370_xp_core_ratios), +}; + +static const u32 __initconst armada_xp_cpu_frequencies[] = { + 1000000000, + 1066000000, + 1200000000, + 1333000000, + 1500000000, + 1666000000, + 1800000000, + 2000000000, + 667000000, + 0, + 800000000, + 1600000000, +}; + +/* For Armada XP TCLK frequency is fix: 250MHz */ +static u32 __init armada_xp_get_tclk_freq(void __iomem *sar) +{ + return 250 * 1000 * 1000; +} + +static u32 __init armada_xp_get_cpu_freq(void __iomem *sar) +{ + u32 cpu_freq; + u8 cpu_freq_select = 0; + + cpu_freq_select = ((readl(sar) >> SARL_AXP_PCLK_FREQ_OPT) & + SARL_AXP_PCLK_FREQ_OPT_MASK); + /* + * The upper bit is not contiguous to the other ones and + * located in the high part of the SAR registers + */ + cpu_freq_select |= (((readl(sar+4) >> SARH_AXP_PCLK_FREQ_OPT) & + SARH_AXP_PCLK_FREQ_OPT_MASK) + << SARH_AXP_PCLK_FREQ_OPT_SHIFT); + if (cpu_freq_select > ARRAY_SIZE(armada_xp_cpu_frequencies)) { + pr_err("CPU freq select unsuported: %d\n", cpu_freq_select); + cpu_freq = 0; + } else + cpu_freq = armada_xp_cpu_frequencies[cpu_freq_select]; + + return cpu_freq; +} + +static void __init armada_xp_get_clk_ratio( + void __iomem *sar, int id, int *mult, int *div) +{ + + u32 opt = ((readl(sar) >> SARL_AXP_FAB_FREQ_OPT) & + SARL_AXP_FAB_FREQ_OPT_MASK); + /* + * The upper bit is not contiguous to the other ones and + * located in the high part of the SAR registers + */ + opt |= (((readl(sar+4) >> SARH_AXP_FAB_FREQ_OPT) & + SARH_AXP_FAB_FREQ_OPT_MASK) + << SARH_AXP_FAB_FREQ_OPT_SHIFT); + + armada_370_xp_get_clk_ratio(opt, sar, id, mult, div); +} + +static const struct core_clocks armada_xp_core_clocks = { + .get_tclk_freq = armada_xp_get_tclk_freq, + .get_cpu_freq = armada_xp_get_cpu_freq, + .get_clk_ratio = armada_xp_get_clk_ratio, + .ratios = armada_370_xp_core_ratios, + .num_ratios = ARRAY_SIZE(armada_370_xp_core_ratios), +}; + +#endif /* CONFIG_MACH_ARMADA_370_XP */ + +/* + * Dove PLL sample-at-reset configuration + * + * SAR0[8:5] : CPU frequency + * 5 = 1000 MHz + * 6 = 933 MHz + * 7 = 933 MHz + * 8 = 800 MHz + * 9 = 800 MHz + * 10 = 800 MHz + * 11 = 1067 MHz + * 12 = 667 MHz + * 13 = 533 MHz + * 14 = 400 MHz + * 15 = 333 MHz + * others reserved. + * + * SAR0[11:9] : CPU to L2 Clock divider ratio + * 0 = (1/1) * CPU + * 2 = (1/2) * CPU + * 4 = (1/3) * CPU + * 6 = (1/4) * CPU + * others reserved. + * + * SAR0[15:12] : CPU to DDR DRAM Clock divider ratio + * 0 = (1/1) * CPU + * 2 = (1/2) * CPU + * 3 = (2/5) * CPU + * 4 = (1/3) * CPU + * 6 = (1/4) * CPU + * 8 = (1/5) * CPU + * 10 = (1/6) * CPU + * 12 = (1/7) * CPU + * 14 = (1/8) * CPU + * 15 = (1/10) * CPU + * others reserved. + * + * SAR0[24:23] : TCLK frequency + * 0 = 166 MHz + * 1 = 125 MHz + * others reserved. + */ +#ifdef CONFIG_ARCH_DOVE +#define SAR_DOVE_CPU_FREQ 5 +#define SAR_DOVE_CPU_FREQ_MASK 0xf +#define SAR_DOVE_L2_RATIO 9 +#define SAR_DOVE_L2_RATIO_MASK 0x7 +#define SAR_DOVE_DDR_RATIO 12 +#define SAR_DOVE_DDR_RATIO_MASK 0xf +#define SAR_DOVE_TCLK_FREQ 23 +#define SAR_DOVE_TCLK_FREQ_MASK 0x3 + +static const u32 __initconst dove_tclk_frequencies[] = { + 166666667, + 125000000, + 0, 0 +}; + +static u32 __init dove_get_tclk_freq(void __iomem *sar) +{ + u32 opt = (readl(sar) >> SAR_DOVE_TCLK_FREQ) & + SAR_DOVE_TCLK_FREQ_MASK; + return dove_tclk_frequencies[opt]; +} + +static const u32 __initconst dove_cpu_frequencies[] = { + 0, 0, 0, 0, 0, + 1000000000, + 933333333, 933333333, + 800000000, 800000000, 800000000, + 1066666667, + 666666667, + 533333333, + 400000000, + 333333333 +}; + +static u32 __init dove_get_cpu_freq(void __iomem *sar) +{ + u32 opt = (readl(sar) >> SAR_DOVE_CPU_FREQ) & + SAR_DOVE_CPU_FREQ_MASK; + return dove_cpu_frequencies[opt]; +} + +enum { DOVE_CPU_TO_L2, DOVE_CPU_TO_DDR }; + +static const struct core_ratio __initconst dove_core_ratios[] = { + { .id = DOVE_CPU_TO_L2, .name = "l2clk", }, + { .id = DOVE_CPU_TO_DDR, .name = "ddrclk", } +}; + +static const int __initconst dove_cpu_l2_ratios[8][2] = { + { 1, 1 }, { 0, 1 }, { 1, 2 }, { 0, 1 }, + { 1, 3 }, { 0, 1 }, { 1, 4 }, { 0, 1 } +}; + +static const int __initconst dove_cpu_ddr_ratios[16][2] = { + { 1, 1 }, { 0, 1 }, { 1, 2 }, { 2, 5 }, + { 1, 3 }, { 0, 1 }, { 1, 4 }, { 0, 1 }, + { 1, 5 }, { 0, 1 }, { 1, 6 }, { 0, 1 }, + { 1, 7 }, { 0, 1 }, { 1, 8 }, { 1, 10 } +}; + +static void __init dove_get_clk_ratio( + void __iomem *sar, int id, int *mult, int *div) +{ + switch (id) { + case DOVE_CPU_TO_L2: + { + u32 opt = (readl(sar) >> SAR_DOVE_L2_RATIO) & + SAR_DOVE_L2_RATIO_MASK; + *mult = dove_cpu_l2_ratios[opt][0]; + *div = dove_cpu_l2_ratios[opt][1]; + break; + } + case DOVE_CPU_TO_DDR: + { + u32 opt = (readl(sar) >> SAR_DOVE_DDR_RATIO) & + SAR_DOVE_DDR_RATIO_MASK; + *mult = dove_cpu_ddr_ratios[opt][0]; + *div = dove_cpu_ddr_ratios[opt][1]; + break; + } + } +} + +static const struct core_clocks dove_core_clocks = { + .get_tclk_freq = dove_get_tclk_freq, + .get_cpu_freq = dove_get_cpu_freq, + .get_clk_ratio = dove_get_clk_ratio, + .ratios = dove_core_ratios, + .num_ratios = ARRAY_SIZE(dove_core_ratios), +}; +#endif /* CONFIG_ARCH_DOVE */ + +/* + * Kirkwood PLL sample-at-reset configuration + * (6180 has different SAR layout than other Kirkwood SoCs) + * + * SAR0[4:3,22,1] : CPU frequency (6281,6292,6282) + * 4 = 600 MHz + * 6 = 800 MHz + * 7 = 1000 MHz + * 9 = 1200 MHz + * 12 = 1500 MHz + * 13 = 1600 MHz + * 14 = 1800 MHz + * 15 = 2000 MHz + * others reserved. + * + * SAR0[19,10:9] : CPU to L2 Clock divider ratio (6281,6292,6282) + * 1 = (1/2) * CPU + * 3 = (1/3) * CPU + * 5 = (1/4) * CPU + * others reserved. + * + * SAR0[8:5] : CPU to DDR DRAM Clock divider ratio (6281,6292,6282) + * 2 = (1/2) * CPU + * 4 = (1/3) * CPU + * 6 = (1/4) * CPU + * 7 = (2/9) * CPU + * 8 = (1/5) * CPU + * 9 = (1/6) * CPU + * others reserved. + * + * SAR0[4:2] : Kirkwood 6180 cpu/l2/ddr clock configuration (6180 only) + * 5 = [CPU = 600 MHz, L2 = (1/2) * CPU, DDR = 200 MHz = (1/3) * CPU] + * 6 = [CPU = 800 MHz, L2 = (1/2) * CPU, DDR = 200 MHz = (1/4) * CPU] + * 7 = [CPU = 1000 MHz, L2 = (1/2) * CPU, DDR = 200 MHz = (1/5) * CPU] + * others reserved. + * + * SAR0[21] : TCLK frequency + * 0 = 200 MHz + * 1 = 166 MHz + * others reserved. + */ +#ifdef CONFIG_ARCH_KIRKWOOD +#define SAR_KIRKWOOD_CPU_FREQ(x) \ + (((x & (1 << 1)) >> 1) | \ + ((x & (1 << 22)) >> 21) | \ + ((x & (3 << 3)) >> 1)) +#define SAR_KIRKWOOD_L2_RATIO(x) \ + (((x & (3 << 9)) >> 9) | \ + (((x & (1 << 19)) >> 17))) +#define SAR_KIRKWOOD_DDR_RATIO 5 +#define SAR_KIRKWOOD_DDR_RATIO_MASK 0xf +#define SAR_MV88F6180_CLK 2 +#define SAR_MV88F6180_CLK_MASK 0x7 +#define SAR_KIRKWOOD_TCLK_FREQ 21 +#define SAR_KIRKWOOD_TCLK_FREQ_MASK 0x1 + +enum { KIRKWOOD_CPU_TO_L2, KIRKWOOD_CPU_TO_DDR }; + +static const struct core_ratio __initconst kirkwood_core_ratios[] = { + { .id = KIRKWOOD_CPU_TO_L2, .name = "l2clk", }, + { .id = KIRKWOOD_CPU_TO_DDR, .name = "ddrclk", } +}; + +static u32 __init kirkwood_get_tclk_freq(void __iomem *sar) +{ + u32 opt = (readl(sar) >> SAR_KIRKWOOD_TCLK_FREQ) & + SAR_KIRKWOOD_TCLK_FREQ_MASK; + return (opt) ? 166666667 : 200000000; +} + +static const u32 __initconst kirkwood_cpu_frequencies[] = { + 0, 0, 0, 0, + 600000000, + 0, + 800000000, + 1000000000, + 0, + 1200000000, + 0, 0, + 1500000000, + 1600000000, + 1800000000, + 2000000000 +}; + +static u32 __init kirkwood_get_cpu_freq(void __iomem *sar) +{ + u32 opt = SAR_KIRKWOOD_CPU_FREQ(readl(sar)); + return kirkwood_cpu_frequencies[opt]; +} + +static const int __initconst kirkwood_cpu_l2_ratios[8][2] = { + { 0, 1 }, { 1, 2 }, { 0, 1 }, { 1, 3 }, + { 0, 1 }, { 1, 4 }, { 0, 1 }, { 0, 1 } +}; + +static const int __initconst kirkwood_cpu_ddr_ratios[16][2] = { + { 0, 1 }, { 0, 1 }, { 1, 2 }, { 0, 1 }, + { 1, 3 }, { 0, 1 }, { 1, 4 }, { 2, 9 }, + { 1, 5 }, { 1, 6 }, { 0, 1 }, { 0, 1 }, + { 0, 1 }, { 0, 1 }, { 0, 1 }, { 0, 1 } +}; + +static void __init kirkwood_get_clk_ratio( + void __iomem *sar, int id, int *mult, int *div) +{ + switch (id) { + case KIRKWOOD_CPU_TO_L2: + { + u32 opt = SAR_KIRKWOOD_L2_RATIO(readl(sar)); + *mult = kirkwood_cpu_l2_ratios[opt][0]; + *div = kirkwood_cpu_l2_ratios[opt][1]; + break; + } + case KIRKWOOD_CPU_TO_DDR: + { + u32 opt = (readl(sar) >> SAR_KIRKWOOD_DDR_RATIO) & + SAR_KIRKWOOD_DDR_RATIO_MASK; + *mult = kirkwood_cpu_ddr_ratios[opt][0]; + *div = kirkwood_cpu_ddr_ratios[opt][1]; + break; + } + } +} + +static const struct core_clocks kirkwood_core_clocks = { + .get_tclk_freq = kirkwood_get_tclk_freq, + .get_cpu_freq = kirkwood_get_cpu_freq, + .get_clk_ratio = kirkwood_get_clk_ratio, + .ratios = kirkwood_core_ratios, + .num_ratios = ARRAY_SIZE(kirkwood_core_ratios), +}; + +static const u32 __initconst mv88f6180_cpu_frequencies[] = { + 0, 0, 0, 0, 0, + 600000000, + 800000000, + 1000000000 +}; + +static u32 __init mv88f6180_get_cpu_freq(void __iomem *sar) +{ + u32 opt = (readl(sar) >> SAR_MV88F6180_CLK) & SAR_MV88F6180_CLK_MASK; + return mv88f6180_cpu_frequencies[opt]; +} + +static const int __initconst mv88f6180_cpu_ddr_ratios[8][2] = { + { 0, 1 }, { 0, 1 }, { 0, 1 }, { 0, 1 }, + { 0, 1 }, { 1, 3 }, { 1, 4 }, { 1, 5 } +}; + +static void __init mv88f6180_get_clk_ratio( + void __iomem *sar, int id, int *mult, int *div) +{ + switch (id) { + case KIRKWOOD_CPU_TO_L2: + { + /* mv88f6180 has a fixed 1:2 CPU-to-L2 ratio */ + *mult = 1; + *div = 2; + break; + } + case KIRKWOOD_CPU_TO_DDR: + { + u32 opt = (readl(sar) >> SAR_MV88F6180_CLK) & + SAR_MV88F6180_CLK_MASK; + *mult = mv88f6180_cpu_ddr_ratios[opt][0]; + *div = mv88f6180_cpu_ddr_ratios[opt][1]; + break; + } + } +} + +static const struct core_clocks mv88f6180_core_clocks = { + .get_tclk_freq = kirkwood_get_tclk_freq, + .get_cpu_freq = mv88f6180_get_cpu_freq, + .get_clk_ratio = mv88f6180_get_clk_ratio, + .ratios = kirkwood_core_ratios, + .num_ratios = ARRAY_SIZE(kirkwood_core_ratios), +}; +#endif /* CONFIG_ARCH_KIRKWOOD */ + +static const __initdata struct of_device_id clk_core_match[] = { +#ifdef CONFIG_MACH_ARMADA_370_XP + { + .compatible = "marvell,armada-370-core-clock", + .data = &armada_370_core_clocks, + }, + { + .compatible = "marvell,armada-xp-core-clock", + .data = &armada_xp_core_clocks, + }, +#endif +#ifdef CONFIG_ARCH_DOVE + { + .compatible = "marvell,dove-core-clock", + .data = &dove_core_clocks, + }, +#endif + +#ifdef CONFIG_ARCH_KIRKWOOD + { + .compatible = "marvell,kirkwood-core-clock", + .data = &kirkwood_core_clocks, + }, + { + .compatible = "marvell,mv88f6180-core-clock", + .data = &mv88f6180_core_clocks, + }, +#endif + + { } +}; + +void __init mvebu_core_clk_init(void) +{ + struct device_node *np; + + for_each_matching_node(np, clk_core_match) { + const struct of_device_id *match = + of_match_node(clk_core_match, np); + mvebu_clk_core_setup(np, (struct core_clocks *)match->data); + } +} diff --git a/drivers/clk/mvebu/clk-core.h b/drivers/clk/mvebu/clk-core.h new file mode 100644 index 000000000000..28b5e02e9885 --- /dev/null +++ b/drivers/clk/mvebu/clk-core.h @@ -0,0 +1,18 @@ +/* + * * Marvell EBU clock core handling defined at reset + * + * Copyright (C) 2012 Marvell + * + * Gregory CLEMENT + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#ifndef __MVEBU_CLK_CORE_H +#define __MVEBU_CLK_CORE_H + +void __init mvebu_core_clk_init(void); + +#endif diff --git a/drivers/clk/mvebu/clk.c b/drivers/clk/mvebu/clk.c new file mode 100644 index 000000000000..e6742acf9880 --- /dev/null +++ b/drivers/clk/mvebu/clk.c @@ -0,0 +1,23 @@ +/* + * Marvell EBU SoC clock handling. + * + * Copyright (C) 2012 Marvell + * + * Gregory CLEMENT + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ +#include +#include +#include +#include +#include +#include +#include "clk-core.h" + +void __init mvebu_clocks_init(void) +{ + mvebu_core_clk_init(); +} diff --git a/include/linux/clk/mvebu.h b/include/linux/clk/mvebu.h new file mode 100644 index 000000000000..8c4ae713b063 --- /dev/null +++ b/include/linux/clk/mvebu.h @@ -0,0 +1,22 @@ +/* + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __CLK_MVEBU_H_ +#define __CLK_MVEBU_H_ + +void __init mvebu_clocks_init(void); + +#endif -- cgit v1.2.3-59-g8ed1b From ab8ba01b3fe5e0b81bd2da0afe66f7f6968e017b Mon Sep 17 00:00:00 2001 From: Gregory CLEMENT Date: Sat, 17 Nov 2012 15:22:23 +0100 Subject: clk: mvebu: add armada-370-xp CPU specific clocks Add Armada 370/XP specific CPU clocks Signed-off-by: Gregory CLEMENT Signed-off-by: Thomas Petazzoni Tested-by: Gregory CLEMENT --- .../devicetree/bindings/clock/mvebu-cpu-clock.txt | 21 +++ drivers/clk/mvebu/Kconfig | 3 + drivers/clk/mvebu/Makefile | 1 + drivers/clk/mvebu/clk-cpu.c | 186 +++++++++++++++++++++ drivers/clk/mvebu/clk-cpu.h | 22 +++ drivers/clk/mvebu/clk.c | 2 + 6 files changed, 235 insertions(+) create mode 100644 Documentation/devicetree/bindings/clock/mvebu-cpu-clock.txt create mode 100644 drivers/clk/mvebu/clk-cpu.c create mode 100644 drivers/clk/mvebu/clk-cpu.h (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/clock/mvebu-cpu-clock.txt b/Documentation/devicetree/bindings/clock/mvebu-cpu-clock.txt new file mode 100644 index 000000000000..feb830130714 --- /dev/null +++ b/Documentation/devicetree/bindings/clock/mvebu-cpu-clock.txt @@ -0,0 +1,21 @@ +Device Tree Clock bindings for cpu clock of Marvell EBU platforms + +Required properties: +- compatible : shall be one of the following: + "marvell,armada-xp-cpu-clock" - cpu clocks for Armada XP +- reg : Address and length of the clock complex register set +- #clock-cells : should be set to 1. +- clocks : shall be the input parent clock phandle for the clock. + +cpuclk: clock-complex@d0018700 { + #clock-cells = <1>; + compatible = "marvell,armada-xp-cpu-clock"; + reg = <0xd0018700 0xA0>; + clocks = <&coreclk 1>; +} + +cpu@0 { + compatible = "marvell,sheeva-v7"; + reg = <0>; + clocks = <&cpuclk 0>; +}; diff --git a/drivers/clk/mvebu/Kconfig b/drivers/clk/mvebu/Kconfig index fd7bf97ff74d..1dd93ada9cb3 100644 --- a/drivers/clk/mvebu/Kconfig +++ b/drivers/clk/mvebu/Kconfig @@ -1,3 +1,6 @@ config MVEBU_CLK_CORE bool +config MVEBU_CLK_CPU + bool + diff --git a/drivers/clk/mvebu/Makefile b/drivers/clk/mvebu/Makefile index de1d9617f75a..93da083b77e3 100644 --- a/drivers/clk/mvebu/Makefile +++ b/drivers/clk/mvebu/Makefile @@ -1 +1,2 @@ obj-$(CONFIG_MVEBU_CLK_CORE) += clk.o clk-core.o +obj-$(CONFIG_MVEBU_CLK_CPU) += clk-cpu.o diff --git a/drivers/clk/mvebu/clk-cpu.c b/drivers/clk/mvebu/clk-cpu.c new file mode 100644 index 000000000000..ff004578a119 --- /dev/null +++ b/drivers/clk/mvebu/clk-cpu.c @@ -0,0 +1,186 @@ +/* + * Marvell MVEBU CPU clock handling. + * + * Copyright (C) 2012 Marvell + * + * Gregory CLEMENT + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ +#include +#include +#include +#include +#include +#include +#include +#include "clk-cpu.h" + +#define SYS_CTRL_CLK_DIVIDER_CTRL_OFFSET 0x0 +#define SYS_CTRL_CLK_DIVIDER_VALUE_OFFSET 0xC +#define SYS_CTRL_CLK_DIVIDER_MASK 0x3F + +#define MAX_CPU 4 +struct cpu_clk { + struct clk_hw hw; + int cpu; + const char *clk_name; + const char *parent_name; + void __iomem *reg_base; +}; + +static struct clk **clks; + +static struct clk_onecell_data clk_data; + +#define to_cpu_clk(p) container_of(p, struct cpu_clk, hw) + +static unsigned long clk_cpu_recalc_rate(struct clk_hw *hwclk, + unsigned long parent_rate) +{ + struct cpu_clk *cpuclk = to_cpu_clk(hwclk); + u32 reg, div; + + reg = readl(cpuclk->reg_base + SYS_CTRL_CLK_DIVIDER_VALUE_OFFSET); + div = (reg >> (cpuclk->cpu * 8)) & SYS_CTRL_CLK_DIVIDER_MASK; + return parent_rate / div; +} + +static long clk_cpu_round_rate(struct clk_hw *hwclk, unsigned long rate, + unsigned long *parent_rate) +{ + /* Valid ratio are 1:1, 1:2 and 1:3 */ + u32 div; + + div = *parent_rate / rate; + if (div == 0) + div = 1; + else if (div > 3) + div = 3; + + return *parent_rate / div; +} + +static int clk_cpu_set_rate(struct clk_hw *hwclk, unsigned long rate, + unsigned long parent_rate) +{ + struct cpu_clk *cpuclk = to_cpu_clk(hwclk); + u32 reg, div; + u32 reload_mask; + + div = parent_rate / rate; + reg = (readl(cpuclk->reg_base + SYS_CTRL_CLK_DIVIDER_VALUE_OFFSET) + & (~(SYS_CTRL_CLK_DIVIDER_MASK << (cpuclk->cpu * 8)))) + | (div << (cpuclk->cpu * 8)); + writel(reg, cpuclk->reg_base + SYS_CTRL_CLK_DIVIDER_VALUE_OFFSET); + /* Set clock divider reload smooth bit mask */ + reload_mask = 1 << (20 + cpuclk->cpu); + + reg = readl(cpuclk->reg_base + SYS_CTRL_CLK_DIVIDER_CTRL_OFFSET) + | reload_mask; + writel(reg, cpuclk->reg_base + SYS_CTRL_CLK_DIVIDER_CTRL_OFFSET); + + /* Now trigger the clock update */ + reg = readl(cpuclk->reg_base + SYS_CTRL_CLK_DIVIDER_CTRL_OFFSET) + | 1 << 24; + writel(reg, cpuclk->reg_base + SYS_CTRL_CLK_DIVIDER_CTRL_OFFSET); + + /* Wait for clocks to settle down then clear reload request */ + udelay(1000); + reg &= ~(reload_mask | 1 << 24); + writel(reg, cpuclk->reg_base + SYS_CTRL_CLK_DIVIDER_CTRL_OFFSET); + udelay(1000); + + return 0; +} + +static const struct clk_ops cpu_ops = { + .recalc_rate = clk_cpu_recalc_rate, + .round_rate = clk_cpu_round_rate, + .set_rate = clk_cpu_set_rate, +}; + +void __init of_cpu_clk_setup(struct device_node *node) +{ + struct cpu_clk *cpuclk; + void __iomem *clock_complex_base = of_iomap(node, 0); + int ncpus = 0; + struct device_node *dn; + + if (clock_complex_base == NULL) { + pr_err("%s: clock-complex base register not set\n", + __func__); + return; + } + + for_each_node_by_type(dn, "cpu") + ncpus++; + + cpuclk = kzalloc(ncpus * sizeof(*cpuclk), GFP_KERNEL); + if (WARN_ON(!cpuclk)) + return; + + clks = kzalloc(ncpus * sizeof(*clks), GFP_KERNEL); + if (WARN_ON(!clks)) + return; + + for_each_node_by_type(dn, "cpu") { + struct clk_init_data init; + struct clk *clk; + struct clk *parent_clk; + char *clk_name = kzalloc(5, GFP_KERNEL); + int cpu, err; + + if (WARN_ON(!clk_name)) + return; + + err = of_property_read_u32(dn, "reg", &cpu); + if (WARN_ON(err)) + return; + + sprintf(clk_name, "cpu%d", cpu); + parent_clk = of_clk_get(node, 0); + + cpuclk[cpu].parent_name = __clk_get_name(parent_clk); + cpuclk[cpu].clk_name = clk_name; + cpuclk[cpu].cpu = cpu; + cpuclk[cpu].reg_base = clock_complex_base; + cpuclk[cpu].hw.init = &init; + + init.name = cpuclk[cpu].clk_name; + init.ops = &cpu_ops; + init.flags = 0; + init.parent_names = &cpuclk[cpu].parent_name; + init.num_parents = 1; + + clk = clk_register(NULL, &cpuclk[cpu].hw); + if (WARN_ON(IS_ERR(clk))) + goto bail_out; + clks[cpu] = clk; + } + clk_data.clk_num = MAX_CPU; + clk_data.clks = clks; + of_clk_add_provider(node, of_clk_src_onecell_get, &clk_data); + + return; +bail_out: + kfree(clks); + kfree(cpuclk); +} + +static const __initconst struct of_device_id clk_cpu_match[] = { + { + .compatible = "marvell,armada-xp-cpu-clock", + .data = of_cpu_clk_setup, + }, + { + /* sentinel */ + }, +}; + +void __init mvebu_cpu_clk_init(void) +{ + of_clk_init(clk_cpu_match); +} diff --git a/drivers/clk/mvebu/clk-cpu.h b/drivers/clk/mvebu/clk-cpu.h new file mode 100644 index 000000000000..08e2affba4e6 --- /dev/null +++ b/drivers/clk/mvebu/clk-cpu.h @@ -0,0 +1,22 @@ +/* + * Marvell MVEBU CPU clock handling. + * + * Copyright (C) 2012 Marvell + * + * Gregory CLEMENT + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#ifndef __MVEBU_CLK_CPU_H +#define __MVEBU_CLK_CPU_H + +#ifdef CONFIG_MVEBU_CLK_CPU +void __init mvebu_cpu_clk_init(void); +#else +static inline void mvebu_cpu_clk_init(void) {} +#endif + +#endif diff --git a/drivers/clk/mvebu/clk.c b/drivers/clk/mvebu/clk.c index e6742acf9880..e3d4d7a9de6a 100644 --- a/drivers/clk/mvebu/clk.c +++ b/drivers/clk/mvebu/clk.c @@ -16,8 +16,10 @@ #include #include #include "clk-core.h" +#include "clk-cpu.h" void __init mvebu_clocks_init(void) { mvebu_core_clk_init(); + mvebu_cpu_clk_init(); } -- cgit v1.2.3-59-g8ed1b From f97d0d7aa8f8cec29a24d65afa12a777c6d2a2f1 Mon Sep 17 00:00:00 2001 From: Sebastian Hesselbarth Date: Sat, 17 Nov 2012 15:22:26 +0100 Subject: clk: mvebu: add clock gating control provider for DT This driver allows to provide DT clocks for clock gates found on Marvell Dove and Kirkwood SoCs. The clock gates are referenced by the phandle index of the corresponding bit in the clock gating control register to ease lookup in the datasheet. Signed-off-by: Sebastian Hesselbarth --- .../bindings/clock/mvebu-gated-clock.txt | 76 +++++++++ drivers/clk/mvebu/Kconfig | 2 + drivers/clk/mvebu/Makefile | 1 + drivers/clk/mvebu/clk-gating-ctrl.c | 177 +++++++++++++++++++++ drivers/clk/mvebu/clk-gating-ctrl.h | 22 +++ drivers/clk/mvebu/clk.c | 2 + 6 files changed, 280 insertions(+) create mode 100644 Documentation/devicetree/bindings/clock/mvebu-gated-clock.txt create mode 100644 drivers/clk/mvebu/clk-gating-ctrl.c create mode 100644 drivers/clk/mvebu/clk-gating-ctrl.h (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/clock/mvebu-gated-clock.txt b/Documentation/devicetree/bindings/clock/mvebu-gated-clock.txt new file mode 100644 index 000000000000..4ad8ccd15e67 --- /dev/null +++ b/Documentation/devicetree/bindings/clock/mvebu-gated-clock.txt @@ -0,0 +1,76 @@ +* Gated Clock bindings for Marvell Orion SoCs + +Marvell Dove and Kirkwood allow some peripheral clocks to be gated to save +some power. The clock consumer should specify the desired clock by having +the clock ID in its "clocks" phandle cell. The clock ID is directly mapped to +the corresponding clock gating control bit in HW to ease manual clock lookup +in datasheet. + +The following is a list of provided IDs for Dove: +ID Clock Peripheral +----------------------------------- +0 usb0 USB Host 0 +1 usb1 USB Host 1 +2 ge Gigabit Ethernet +3 sata SATA Host +4 pex0 PCIe Cntrl 0 +5 pex1 PCIe Cntrl 1 +8 sdio0 SDHCI Host 0 +9 sdio1 SDHCI Host 1 +10 nand NAND Cntrl +11 camera Camera Cntrl +12 i2s0 I2S Cntrl 0 +13 i2s1 I2S Cntrl 1 +15 crypto CESA engine +21 ac97 AC97 Cntrl +22 pdma Peripheral DMA +23 xor0 XOR DMA 0 +24 xor1 XOR DMA 1 +30 gephy Gigabit Ethernel PHY +Note: gephy(30) is implemented as a parent clock of ge(2) + +The following is a list of provided IDs for Kirkwood: +ID Clock Peripheral +----------------------------------- +0 ge0 Gigabit Ethernet 0 +2 pex0 PCIe Cntrl 0 +3 usb0 USB Host 0 +4 sdio SDIO Cntrl +5 tsu Transp. Stream Unit +6 dunit SDRAM Cntrl +7 runit Runit +8 xor0 XOR DMA 0 +9 audio I2S Cntrl 0 +14 sata0 SATA Host 0 +15 sata1 SATA Host 1 +16 xor1 XOR DMA 1 +17 crypto CESA engine +18 pex1 PCIe Cntrl 1 +19 ge1 Gigabit Ethernet 0 +20 tdm Time Division Mplx + +Required properties: +- compatible : shall be one of the following: + "marvell,dove-gating-clock" - for Dove SoC clock gating + "marvell,kirkwood-gating-clock" - for Kirkwood SoC clock gating +- reg : shall be the register address of the Clock Gating Control register +- #clock-cells : from common clock binding; shall be set to 1 + +Optional properties: +- clocks : default parent clock phandle (e.g. tclk) + +Example: + +gate_clk: clock-gating-control@d0038 { + compatible = "marvell,dove-gating-clock"; + reg = <0xd0038 0x4>; + /* default parent clock is tclk */ + clocks = <&core_clk 0>; + #clock-cells = <1>; +}; + +sdio0: sdio@92000 { + compatible = "marvell,dove-sdhci"; + /* get clk gate bit 8 (sdio0) */ + clocks = <&gate_clk 8>; +}; diff --git a/drivers/clk/mvebu/Kconfig b/drivers/clk/mvebu/Kconfig index 1dd93ada9cb3..57323fd15ec9 100644 --- a/drivers/clk/mvebu/Kconfig +++ b/drivers/clk/mvebu/Kconfig @@ -4,3 +4,5 @@ config MVEBU_CLK_CORE config MVEBU_CLK_CPU bool +config MVEBU_CLK_GATING + bool diff --git a/drivers/clk/mvebu/Makefile b/drivers/clk/mvebu/Makefile index 93da083b77e3..58df3dc49363 100644 --- a/drivers/clk/mvebu/Makefile +++ b/drivers/clk/mvebu/Makefile @@ -1,2 +1,3 @@ obj-$(CONFIG_MVEBU_CLK_CORE) += clk.o clk-core.o obj-$(CONFIG_MVEBU_CLK_CPU) += clk-cpu.o +obj-$(CONFIG_MVEBU_CLK_GATING) += clk-gating-ctrl.o diff --git a/drivers/clk/mvebu/clk-gating-ctrl.c b/drivers/clk/mvebu/clk-gating-ctrl.c new file mode 100644 index 000000000000..fa69f87c5797 --- /dev/null +++ b/drivers/clk/mvebu/clk-gating-ctrl.c @@ -0,0 +1,177 @@ +/* + * Marvell MVEBU clock gating control. + * + * Sebastian Hesselbarth + * Andrew Lunn + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct mvebu_gating_ctrl { + spinlock_t lock; + struct clk **gates; + int num_gates; +}; + +struct mvebu_soc_descr { + const char *name; + const char *parent; + int bit_idx; +}; + +#define to_clk_gate(_hw) container_of(_hw, struct clk_gate, hw) + +static struct clk __init *mvebu_clk_gating_get_src( + struct of_phandle_args *clkspec, void *data) +{ + struct mvebu_gating_ctrl *ctrl = (struct mvebu_gating_ctrl *)data; + int n; + + if (clkspec->args_count < 1) + return ERR_PTR(-EINVAL); + + for (n = 0; n < ctrl->num_gates; n++) { + struct clk_gate *gate = + to_clk_gate(__clk_get_hw(ctrl->gates[n])); + if (clkspec->args[0] == gate->bit_idx) + return ctrl->gates[n]; + } + return ERR_PTR(-ENODEV); +} + +static void __init mvebu_clk_gating_setup( + struct device_node *np, const struct mvebu_soc_descr *descr) +{ + struct mvebu_gating_ctrl *ctrl; + struct clk *clk; + void __iomem *base; + const char *default_parent = NULL; + int n; + + base = of_iomap(np, 0); + + clk = of_clk_get(np, 0); + if (!IS_ERR(clk)) { + default_parent = __clk_get_name(clk); + clk_put(clk); + } + + ctrl = kzalloc(sizeof(struct mvebu_gating_ctrl), GFP_KERNEL); + if (WARN_ON(!ctrl)) + return; + + spin_lock_init(&ctrl->lock); + + /* + * Count, allocate, and register clock gates + */ + for (n = 0; descr[n].name;) + n++; + + ctrl->num_gates = n; + ctrl->gates = kzalloc(ctrl->num_gates * sizeof(struct clk *), + GFP_KERNEL); + if (WARN_ON(!ctrl->gates)) { + kfree(ctrl); + return; + } + + for (n = 0; n < ctrl->num_gates; n++) { + const char *parent = + (descr[n].parent) ? descr[n].parent : default_parent; + ctrl->gates[n] = clk_register_gate(NULL, descr[n].name, parent, + 0, base, descr[n].bit_idx, 0, &ctrl->lock); + WARN_ON(IS_ERR(ctrl->gates[n])); + } + of_clk_add_provider(np, mvebu_clk_gating_get_src, ctrl); +} + +/* + * SoC specific clock gating control + */ + +#ifdef CONFIG_ARCH_DOVE +static const struct mvebu_soc_descr __initconst dove_gating_descr[] = { + { "usb0", NULL, 0 }, + { "usb1", NULL, 1 }, + { "ge", "gephy", 2 }, + { "sata", NULL, 3 }, + { "pex0", NULL, 4 }, + { "pex1", NULL, 5 }, + { "sdio0", NULL, 8 }, + { "sdio1", NULL, 9 }, + { "nand", NULL, 10 }, + { "camera", NULL, 11 }, + { "i2s0", NULL, 12 }, + { "i2s1", NULL, 13 }, + { "crypto", NULL, 15 }, + { "ac97", NULL, 21 }, + { "pdma", NULL, 22 }, + { "xor0", NULL, 23 }, + { "xor1", NULL, 24 }, + { "gephy", NULL, 30 }, + { } +}; +#endif + +#ifdef CONFIG_ARCH_KIRKWOOD +static const struct mvebu_soc_descr __initconst kirkwood_gating_descr[] = { + { "ge0", NULL, 0 }, + { "pex0", NULL, 2 }, + { "usb0", NULL, 3 }, + { "sdio", NULL, 4 }, + { "tsu", NULL, 5 }, + { "runit", NULL, 7 }, + { "xor0", NULL, 8 }, + { "audio", NULL, 9 }, + { "sata0", NULL, 14 }, + { "sata1", NULL, 15 }, + { "xor1", NULL, 16 }, + { "crypto", NULL, 17 }, + { "pex1", NULL, 18 }, + { "ge1", NULL, 19 }, + { "tdm", NULL, 20 }, + { } +}; +#endif + +static const __initdata struct of_device_id clk_gating_match[] = { +#ifdef CONFIG_ARCH_DOVE + { + .compatible = "marvell,dove-gating-clock", + .data = dove_gating_descr, + }, +#endif + +#ifdef CONFIG_ARCH_KIRKWOOD + { + .compatible = "marvell,kirkwood-gating-clock", + .data = kirkwood_gating_descr, + }, +#endif + + { } +}; + +void __init mvebu_gating_clk_init(void) +{ + struct device_node *np; + + for_each_matching_node(np, clk_gating_match) { + const struct of_device_id *match = + of_match_node(clk_gating_match, np); + mvebu_clk_gating_setup(np, + (const struct mvebu_soc_descr *)match->data); + } +} diff --git a/drivers/clk/mvebu/clk-gating-ctrl.h b/drivers/clk/mvebu/clk-gating-ctrl.h new file mode 100644 index 000000000000..9275d1e51f1b --- /dev/null +++ b/drivers/clk/mvebu/clk-gating-ctrl.h @@ -0,0 +1,22 @@ +/* + * Marvell EBU gating clock handling + * + * Copyright (C) 2012 Marvell + * + * Thomas Petazzoni + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#ifndef __MVEBU_CLK_GATING_H +#define __MVEBU_CLK_GATING_H + +#ifdef CONFIG_MVEBU_CLK_GATING +void __init mvebu_gating_clk_init(void); +#else +void mvebu_gating_clk_init(void) {} +#endif + +#endif diff --git a/drivers/clk/mvebu/clk.c b/drivers/clk/mvebu/clk.c index e3d4d7a9de6a..855681b8a9dc 100644 --- a/drivers/clk/mvebu/clk.c +++ b/drivers/clk/mvebu/clk.c @@ -17,9 +17,11 @@ #include #include "clk-core.h" #include "clk-cpu.h" +#include "clk-gating-ctrl.h" void __init mvebu_clocks_init(void) { mvebu_core_clk_init(); + mvebu_gating_clk_init(); mvebu_cpu_clk_init(); } -- cgit v1.2.3-59-g8ed1b From c4c34d608482b48c1c007fecea5a7a5c65168fa2 Mon Sep 17 00:00:00 2001 From: Gregory CLEMENT Date: Sat, 17 Nov 2012 15:22:29 +0100 Subject: clk: mvebu: armada 370/XP add clock gating control provider for DT Signed-off-by: Gregory CLEMENT Signed-off-by: Andrew Lunn Signed-off-by: Thomas Petazzoni --- .../bindings/clock/mvebu-gated-clock.txt | 43 +++++++++++++ drivers/clk/mvebu/clk-gating-ctrl.c | 74 +++++++++++++++++++++- 2 files changed, 116 insertions(+), 1 deletion(-) (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/clock/mvebu-gated-clock.txt b/Documentation/devicetree/bindings/clock/mvebu-gated-clock.txt index 4ad8ccd15e67..7337005ef5e1 100644 --- a/Documentation/devicetree/bindings/clock/mvebu-gated-clock.txt +++ b/Documentation/devicetree/bindings/clock/mvebu-gated-clock.txt @@ -6,6 +6,49 @@ the clock ID in its "clocks" phandle cell. The clock ID is directly mapped to the corresponding clock gating control bit in HW to ease manual clock lookup in datasheet. +The following is a list of provided IDs for Armada 370: +ID Clock Peripheral +----------------------------------- +0 Audio AC97 Cntrl +1 pex0_en PCIe 0 Clock out +2 pex1_en PCIe 1 Clock out +3 ge1 Gigabit Ethernet 1 +4 ge0 Gigabit Ethernet 0 +5 pex0 PCIe Cntrl 0 +9 pex1 PCIe Cntrl 1 +15 sata0 SATA Host 0 +17 sdio SDHCI Host +25 tdm Time Division Mplx +28 ddr DDR Cntrl +30 sata1 SATA Host 0 + +The following is a list of provided IDs for Armada XP: +ID Clock Peripheral +----------------------------------- +0 audio Audio Cntrl +1 ge3 Gigabit Ethernet 3 +2 ge2 Gigabit Ethernet 2 +3 ge1 Gigabit Ethernet 1 +4 ge0 Gigabit Ethernet 0 +5 pex0 PCIe Cntrl 0 +6 pex1 PCIe Cntrl 1 +7 pex2 PCIe Cntrl 2 +8 pex3 PCIe Cntrl 3 +13 bp +14 sata0lnk +15 sata0 SATA Host 0 +16 lcd LCD Cntrl +17 sdio SDHCI Host +18 usb0 USB Host 0 +19 usb1 USB Host 1 +20 usb2 USB Host 2 +22 xor0 XOR DMA 0 +23 crypto CESA engine +25 tdm Time Division Mplx +28 xor1 XOR DMA 1 +29 sata1lnk +30 sata1 SATA Host 0 + The following is a list of provided IDs for Dove: ID Clock Peripheral ----------------------------------- diff --git a/drivers/clk/mvebu/clk-gating-ctrl.c b/drivers/clk/mvebu/clk-gating-ctrl.c index fa69f87c5797..c6d3c263b070 100644 --- a/drivers/clk/mvebu/clk-gating-ctrl.c +++ b/drivers/clk/mvebu/clk-gating-ctrl.c @@ -88,10 +88,21 @@ static void __init mvebu_clk_gating_setup( } for (n = 0; n < ctrl->num_gates; n++) { + u8 flags = 0; const char *parent = (descr[n].parent) ? descr[n].parent : default_parent; + + /* + * On Armada 370, the DDR clock is a special case: it + * isn't taken by any driver, but should anyway be + * kept enabled, so we mark it as IGNORE_UNUSED for + * now. + */ + if (!strcmp(descr[n].name, "ddr")) + flags |= CLK_IGNORE_UNUSED; + ctrl->gates[n] = clk_register_gate(NULL, descr[n].name, parent, - 0, base, descr[n].bit_idx, 0, &ctrl->lock); + flags, base, descr[n].bit_idx, 0, &ctrl->lock); WARN_ON(IS_ERR(ctrl->gates[n])); } of_clk_add_provider(np, mvebu_clk_gating_get_src, ctrl); @@ -101,6 +112,53 @@ static void __init mvebu_clk_gating_setup( * SoC specific clock gating control */ +#ifdef CONFIG_MACH_ARMADA_370 +static const struct mvebu_soc_descr __initconst armada_370_gating_descr[] = { + { "audio", NULL, 0 }, + { "pex0_en", NULL, 1 }, + { "pex1_en", NULL, 2 }, + { "ge1", NULL, 3 }, + { "ge0", NULL, 4 }, + { "pex0", NULL, 5 }, + { "pex1", NULL, 9 }, + { "sata0", NULL, 15 }, + { "sdio", NULL, 17 }, + { "tdm", NULL, 25 }, + { "ddr", NULL, 28 }, + { "sata1", NULL, 30 }, + { } +}; +#endif + +#ifdef CONFIG_MACH_ARMADA_XP +static const struct mvebu_soc_descr __initconst armada_xp_gating_descr[] = { + { "audio", NULL, 0 }, + { "ge3", NULL, 1 }, + { "ge2", NULL, 2 }, + { "ge1", NULL, 3 }, + { "ge0", NULL, 4 }, + { "pex0", NULL, 5 }, + { "pex1", NULL, 6 }, + { "pex2", NULL, 7 }, + { "pex3", NULL, 8 }, + { "bp", NULL, 13 }, + { "sata0lnk", NULL, 14 }, + { "sata0", "sata0lnk", 15 }, + { "lcd", NULL, 16 }, + { "sdio", NULL, 17 }, + { "usb0", NULL, 18 }, + { "usb1", NULL, 19 }, + { "usb2", NULL, 20 }, + { "xor0", NULL, 22 }, + { "crypto", NULL, 23 }, + { "tdm", NULL, 25 }, + { "xor1", NULL, 28 }, + { "sata1lnk", NULL, 29 }, + { "sata1", "sata1lnk", 30 }, + { } +}; +#endif + #ifdef CONFIG_ARCH_DOVE static const struct mvebu_soc_descr __initconst dove_gating_descr[] = { { "usb0", NULL, 0 }, @@ -147,6 +205,20 @@ static const struct mvebu_soc_descr __initconst kirkwood_gating_descr[] = { #endif static const __initdata struct of_device_id clk_gating_match[] = { +#ifdef CONFIG_MACH_ARMADA_370 + { + .compatible = "marvell,armada-370-gating-clock", + .data = armada_370_gating_descr, + }, +#endif + +#ifdef CONFIG_MACH_ARMADA_XP + { + .compatible = "marvell,armada-xp-gating-clock", + .data = armada_xp_gating_descr, + }, +#endif + #ifdef CONFIG_ARCH_DOVE { .compatible = "marvell,dove-gating-clock", -- cgit v1.2.3-59-g8ed1b From 307c2bf467e3682c6df1b8186365224fd2d581d3 Mon Sep 17 00:00:00 2001 From: Gregory CLEMENT Date: Sat, 17 Nov 2012 15:22:25 +0100 Subject: clocksource: convert time-armada-370-xp to clk framework Signed-off-by: Gregory CLEMENT Tested-by Gregory CLEMENT --- Documentation/devicetree/bindings/arm/armada-370-xp-timer.txt | 1 + arch/arm/boot/dts/armada-370-db.dts | 4 ---- arch/arm/boot/dts/armada-370-xp.dtsi | 1 + drivers/clocksource/time-armada-370-xp.c | 11 ++++++----- 4 files changed, 8 insertions(+), 9 deletions(-) (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/arm/armada-370-xp-timer.txt b/Documentation/devicetree/bindings/arm/armada-370-xp-timer.txt index 8b6ea2267c94..64830118b013 100644 --- a/Documentation/devicetree/bindings/arm/armada-370-xp-timer.txt +++ b/Documentation/devicetree/bindings/arm/armada-370-xp-timer.txt @@ -5,6 +5,7 @@ Required properties: - compatible: Should be "marvell,armada-370-xp-timer" - interrupts: Should contain the list of Global Timer interrupts - reg: Should contain the base address of the Global Timer registers +- clocks: clock driving the timer hardware Optional properties: - marvell,timer-25Mhz: Tells whether the Global timer supports the 25 diff --git a/arch/arm/boot/dts/armada-370-db.dts b/arch/arm/boot/dts/armada-370-db.dts index fffd5c2a3041..4a31b0396623 100644 --- a/arch/arm/boot/dts/armada-370-db.dts +++ b/arch/arm/boot/dts/armada-370-db.dts @@ -34,9 +34,5 @@ clock-frequency = <200000000>; status = "okay"; }; - timer@d0020300 { - clock-frequency = <600000000>; - status = "okay"; - }; }; }; diff --git a/arch/arm/boot/dts/armada-370-xp.dtsi b/arch/arm/boot/dts/armada-370-xp.dtsi index 16cc82cdaa81..94b4b9e03571 100644 --- a/arch/arm/boot/dts/armada-370-xp.dtsi +++ b/arch/arm/boot/dts/armada-370-xp.dtsi @@ -62,6 +62,7 @@ compatible = "marvell,armada-370-xp-timer"; reg = <0xd0020300 0x30>; interrupts = <37>, <38>, <39>, <40>; + clocks = <&coreclk 2>; }; addr-decoding@d0020000 { diff --git a/drivers/clocksource/time-armada-370-xp.c b/drivers/clocksource/time-armada-370-xp.c index 4674f94957cd..a4605fd7e303 100644 --- a/drivers/clocksource/time-armada-370-xp.c +++ b/drivers/clocksource/time-armada-370-xp.c @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -167,7 +168,6 @@ void __init armada_370_xp_timer_init(void) u32 u; struct device_node *np; unsigned int timer_clk; - int ret; np = of_find_compatible_node(NULL, NULL, "marvell,armada-370-xp-timer"); timer_base = of_iomap(np, 0); WARN_ON(!timer_base); @@ -179,13 +179,14 @@ void __init armada_370_xp_timer_init(void) timer_base + TIMER_CTRL_OFF); timer_clk = 25000000; } else { - u32 clk = 0; - ret = of_property_read_u32(np, "clock-frequency", &clk); - WARN_ON(!clk || ret < 0); + unsigned long rate = 0; + struct clk *clk = of_clk_get(np, 0); + WARN_ON(IS_ERR(clk)); + rate = clk_get_rate(clk); u = readl(timer_base + TIMER_CTRL_OFF); writel(u & ~(TIMER0_25MHZ | TIMER1_25MHZ), timer_base + TIMER_CTRL_OFF); - timer_clk = clk / TIMER_DIVIDER; + timer_clk = rate / TIMER_DIVIDER; } /* We use timer 0 as clocksource, and timer 1 for -- cgit v1.2.3-59-g8ed1b From f7d12ef53ddfd8175933af42bfc477376d19e19e Mon Sep 17 00:00:00 2001 From: Thomas Petazzoni Date: Thu, 15 Nov 2012 16:47:58 +0100 Subject: dma: mv_xor: add Device Tree binding This patch finally adds a Device Tree binding to the mv_xor driver. Thanks to the previous cleanup patches, the Device Tree binding is relatively simply: one DT node per XOR engine, with sub-nodes for each XOR channel of the XOR engine. The binding obviously comes with the necessary documentation. Signed-off-by: Thomas Petazzoni Cc: devicetree-discuss@lists.ozlabs.org --- Documentation/devicetree/bindings/dma/mv-xor.txt | 40 ++++++++++++++++ drivers/dma/mv_xor.c | 58 ++++++++++++++++++++++-- 2 files changed, 94 insertions(+), 4 deletions(-) create mode 100644 Documentation/devicetree/bindings/dma/mv-xor.txt (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/dma/mv-xor.txt b/Documentation/devicetree/bindings/dma/mv-xor.txt new file mode 100644 index 000000000000..7c6cb7fcecd2 --- /dev/null +++ b/Documentation/devicetree/bindings/dma/mv-xor.txt @@ -0,0 +1,40 @@ +* Marvell XOR engines + +Required properties: +- compatible: Should be "marvell,orion-xor" +- reg: Should contain registers location and length (two sets) + the first set is the low registers, the second set the high + registers for the XOR engine. +- clocks: pointer to the reference clock + +The DT node must also contains sub-nodes for each XOR channel that the +XOR engine has. Those sub-nodes have the following required +properties: +- interrupts: interrupt of the XOR channel + +And the following optional properties: +- dmacap,memcpy to indicate that the XOR channel is capable of memcpy operations +- dmacap,memset to indicate that the XOR channel is capable of memset operations +- dmacap,xor to indicate that the XOR channel is capable of xor operations + +Example: + +xor@d0060900 { + compatible = "marvell,orion-xor"; + reg = <0xd0060900 0x100 + 0xd0060b00 0x100>; + clocks = <&coreclk 0>; + status = "okay"; + + xor00 { + interrupts = <51>; + dmacap,memcpy; + dmacap,xor; + }; + xor01 { + interrupts = <52>; + dmacap,memcpy; + dmacap,xor; + dmacap,memset; + }; +}; diff --git a/drivers/dma/mv_xor.c b/drivers/dma/mv_xor.c index d48245c0b0b0..b1c4edd56ebc 100644 --- a/drivers/dma/mv_xor.c +++ b/drivers/dma/mv_xor.c @@ -26,6 +26,9 @@ #include #include #include +#include +#include +#include #include #include "dmaengine.h" @@ -1278,7 +1281,42 @@ static int mv_xor_probe(struct platform_device *pdev) if (!IS_ERR(xordev->clk)) clk_prepare_enable(xordev->clk); - if (pdata && pdata->channels) { + if (pdev->dev.of_node) { + struct device_node *np; + int i = 0; + + for_each_child_of_node(pdev->dev.of_node, np) { + dma_cap_mask_t cap_mask; + int irq; + + dma_cap_zero(cap_mask); + if (of_property_read_bool(np, "dmacap,memcpy")) + dma_cap_set(DMA_MEMCPY, cap_mask); + if (of_property_read_bool(np, "dmacap,xor")) + dma_cap_set(DMA_XOR, cap_mask); + if (of_property_read_bool(np, "dmacap,memset")) + dma_cap_set(DMA_MEMSET, cap_mask); + if (of_property_read_bool(np, "dmacap,interrupt")) + dma_cap_set(DMA_INTERRUPT, cap_mask); + + irq = irq_of_parse_and_map(np, 0); + if (irq < 0) { + ret = irq; + goto err_channel_add; + } + + xordev->channels[i] = + mv_xor_channel_add(xordev, pdev, i, + cap_mask, irq); + if (IS_ERR(xordev->channels[i])) { + ret = PTR_ERR(xordev->channels[i]); + irq_dispose_mapping(irq); + goto err_channel_add; + } + + i++; + } + } else if (pdata && pdata->channels) { for (i = 0; i < MV_XOR_MAX_CHANNELS; i++) { struct mv_xor_channel_data *cd; int irq; @@ -1309,8 +1347,11 @@ static int mv_xor_probe(struct platform_device *pdev) err_channel_add: for (i = 0; i < MV_XOR_MAX_CHANNELS; i++) - if (xordev->channels[i]) + if (xordev->channels[i]) { + if (pdev->dev.of_node) + irq_dispose_mapping(xordev->channels[i]->irq); mv_xor_channel_remove(xordev->channels[i]); + } clk_disable_unprepare(xordev->clk); clk_put(xordev->clk); @@ -1335,12 +1376,21 @@ static int mv_xor_remove(struct platform_device *pdev) return 0; } +#ifdef CONFIG_OF +static struct of_device_id mv_xor_dt_ids[] __devinitdata = { + { .compatible = "marvell,orion-xor", }, + {}, +}; +MODULE_DEVICE_TABLE(of, mv_xor_dt_ids); +#endif + static struct platform_driver mv_xor_driver = { .probe = mv_xor_probe, .remove = mv_xor_remove, .driver = { - .owner = THIS_MODULE, - .name = MV_XOR_NAME, + .owner = THIS_MODULE, + .name = MV_XOR_NAME, + .of_match_table = of_match_ptr(mv_xor_dt_ids), }, }; -- cgit v1.2.3-59-g8ed1b From c4f0c6936762ecd6b453275611a785dfdee0d417 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Tue, 20 Nov 2012 13:36:00 +0000 Subject: iio: Add pressure channel type This patch adds support for a new IIO channel type for pressure measurements. This can for example be used for barometric pressure sensors. Signed-off-by: Lars-Peter Clausen Signed-off-by: Jonathan Cameron --- Documentation/ABI/testing/sysfs-bus-iio | 24 ++++++++++++++++++++++++ drivers/iio/industrialio-core.c | 1 + include/linux/iio/types.h | 1 + 3 files changed, 26 insertions(+) (limited to 'Documentation') diff --git a/Documentation/ABI/testing/sysfs-bus-iio b/Documentation/ABI/testing/sysfs-bus-iio index 2f06d40fe07d..2e33dc6b2346 100644 --- a/Documentation/ABI/testing/sysfs-bus-iio +++ b/Documentation/ABI/testing/sysfs-bus-iio @@ -189,6 +189,14 @@ Description: A computed peak value based on the sum squared magnitude of the underlying value in the specified directions. +What: /sys/bus/iio/devices/iio:deviceX/in_pressureY_raw +What: /sys/bus/iio/devices/iio:deviceX/in_pressure_raw +KernelVersion: 3.8 +Contact: linux-iio@vger.kernel.org +Description: + Raw pressure measurement from channel Y. Units after + application of scale and offset are kilopascal. + What: /sys/bus/iio/devices/iio:deviceX/in_accel_offset What: /sys/bus/iio/devices/iio:deviceX/in_accel_x_offset What: /sys/bus/iio/devices/iio:deviceX/in_accel_y_offset @@ -197,6 +205,8 @@ What: /sys/bus/iio/devices/iio:deviceX/in_voltageY_offset What: /sys/bus/iio/devices/iio:deviceX/in_voltage_offset What: /sys/bus/iio/devices/iio:deviceX/in_tempY_offset What: /sys/bus/iio/devices/iio:deviceX/in_temp_offset +What: /sys/bus/iio/devices/iio:deviceX/in_pressureY_offset +What: /sys/bus/iio/devices/iio:deviceX/in_pressure_offset KernelVersion: 2.6.35 Contact: linux-iio@vger.kernel.org Description: @@ -226,6 +236,8 @@ What: /sys/bus/iio/devices/iio:deviceX/in_magn_scale What: /sys/bus/iio/devices/iio:deviceX/in_magn_x_scale What: /sys/bus/iio/devices/iio:deviceX/in_magn_y_scale What: /sys/bus/iio/devices/iio:deviceX/in_magn_z_scale +What: /sys/bus/iio/devices/iio:deviceX/in_pressureY_scale +What: /sys/bus/iio/devices/iio:deviceX/in_pressure_scale KernelVersion: 2.6.35 Contact: linux-iio@vger.kernel.org Description: @@ -245,6 +257,8 @@ What: /sys/bus/iio/devices/iio:deviceX/in_anglvel_y_calibbias What: /sys/bus/iio/devices/iio:deviceX/in_anglvel_z_calibbias What: /sys/bus/iio/devices/iio:deviceX/in_illuminance0_calibbias What: /sys/bus/iio/devices/iio:deviceX/in_proximity0_calibbias +What: /sys/bus/iio/devices/iio:deviceX/in_pressureY_calibbias +What: /sys/bus/iio/devices/iio:deviceX/in_pressure_calibbias KernelVersion: 2.6.35 Contact: linux-iio@vger.kernel.org Description: @@ -262,6 +276,8 @@ What /sys/bus/iio/devices/iio:deviceX/in_anglvel_y_calibscale What /sys/bus/iio/devices/iio:deviceX/in_anglvel_z_calibscale what /sys/bus/iio/devices/iio:deviceX/in_illuminance0_calibscale what /sys/bus/iio/devices/iio:deviceX/in_proximity0_calibscale +What: /sys/bus/iio/devices/iio:deviceX/in_pressureY_calibscale +What: /sys/bus/iio/devices/iio:deviceX/in_pressure_calibscale KernelVersion: 2.6.35 Contact: linux-iio@vger.kernel.org Description: @@ -275,6 +291,8 @@ What: /sys/.../iio:deviceX/in_voltage-voltage_scale_available What: /sys/.../iio:deviceX/out_voltageX_scale_available What: /sys/.../iio:deviceX/out_altvoltageX_scale_available What: /sys/.../iio:deviceX/in_capacitance_scale_available +What: /sys/.../iio:deviceX/in_pressure_scale_available +What: /sys/.../iio:deviceX/in_pressureY_scale_available KernelVersion: 2.6.35 Contact: linux-iio@vger.kernel.org Description: @@ -694,6 +712,8 @@ What: /sys/.../buffer/scan_elements/in_voltageY_en What: /sys/.../buffer/scan_elements/in_voltageY-voltageZ_en What: /sys/.../buffer/scan_elements/in_incli_x_en What: /sys/.../buffer/scan_elements/in_incli_y_en +What: /sys/.../buffer/scan_elements/in_pressureY_en +What: /sys/.../buffer/scan_elements/in_pressure_en KernelVersion: 2.6.37 Contact: linux-iio@vger.kernel.org Description: @@ -707,6 +727,8 @@ What: /sys/.../buffer/scan_elements/in_voltageY_type What: /sys/.../buffer/scan_elements/in_voltage_type What: /sys/.../buffer/scan_elements/in_voltageY_supply_type What: /sys/.../buffer/scan_elements/in_timestamp_type +What: /sys/.../buffer/scan_elements/in_pressureY_type +What: /sys/.../buffer/scan_elements/in_pressure_type KernelVersion: 2.6.37 Contact: linux-iio@vger.kernel.org Description: @@ -751,6 +773,8 @@ What: /sys/.../buffer/scan_elements/in_magn_z_index What: /sys/.../buffer/scan_elements/in_incli_x_index What: /sys/.../buffer/scan_elements/in_incli_y_index What: /sys/.../buffer/scan_elements/in_timestamp_index +What: /sys/.../buffer/scan_elements/in_pressureY_index +What: /sys/.../buffer/scan_elements/in_pressure_index KernelVersion: 2.6.37 Contact: linux-iio@vger.kernel.org Description: diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c index 060a4045be85..3dccd6c3a889 100644 --- a/drivers/iio/industrialio-core.c +++ b/drivers/iio/industrialio-core.c @@ -65,6 +65,7 @@ static const char * const iio_chan_type_name_spec[] = { [IIO_CAPACITANCE] = "capacitance", [IIO_ALTVOLTAGE] = "altvoltage", [IIO_CCT] = "cct", + [IIO_PRESSURE] = "pressure", }; static const char * const iio_modifier_names[] = { diff --git a/include/linux/iio/types.h b/include/linux/iio/types.h index 87b196a2d698..88bf0f0d27b4 100644 --- a/include/linux/iio/types.h +++ b/include/linux/iio/types.h @@ -28,6 +28,7 @@ enum iio_chan_type { IIO_CAPACITANCE, IIO_ALTVOLTAGE, IIO_CCT, + IIO_PRESSURE, }; enum iio_modifier { -- cgit v1.2.3-59-g8ed1b From 189dd62642c9819005cf37d3a9e441d203112bd2 Mon Sep 17 00:00:00 2001 From: Thomas Petazzoni Date: Mon, 19 Nov 2012 14:15:25 +0100 Subject: net: mvneta: add clk support Now that the Armada 370/XP platform has gained proper integration with the clock framework, we add clk support in the Marvell Armada 370/XP Ethernet driver. Since the existing Device Tree binding that exposes a 'clock-frequency' property has never been exposed in any stable kernel release, we take the freedom of removing this property to replace it with the standard 'clocks' clock pointer property. The Device Tree binding documentation is updated accordingly. Signed-off-by: Thomas Petazzoni --- .../bindings/net/marvell-armada-370-neta.txt | 4 +-- drivers/net/ethernet/marvell/mvneta.c | 31 ++++++++++++++-------- 2 files changed, 22 insertions(+), 13 deletions(-) (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/net/marvell-armada-370-neta.txt b/Documentation/devicetree/bindings/net/marvell-armada-370-neta.txt index c4e87f0e450e..859a6fa7569c 100644 --- a/Documentation/devicetree/bindings/net/marvell-armada-370-neta.txt +++ b/Documentation/devicetree/bindings/net/marvell-armada-370-neta.txt @@ -8,7 +8,7 @@ Required properties: property, a single integer). - phy-mode: The interface between the SoC and the PHY (a string that of_get_phy_mode() can understand) -- clock-frequency: frequency of the peripheral clock of the SoC. +- clocks: a pointer to the reference clock for this device. Example: @@ -16,7 +16,7 @@ ethernet@d0070000 { compatible = "marvell,armada-370-neta"; reg = <0xd0070000 0x2500>; interrupts = <8>; - clock-frequency = <250000000>; + clocks = <&gate_clk 4>; status = "okay"; phy = <&phy0>; phy-mode = "rgmii-id"; diff --git a/drivers/net/ethernet/marvell/mvneta.c b/drivers/net/ethernet/marvell/mvneta.c index d9dadee6ab79..17b0a4198c88 100644 --- a/drivers/net/ethernet/marvell/mvneta.c +++ b/drivers/net/ethernet/marvell/mvneta.c @@ -29,6 +29,7 @@ #include #include #include +#include /* Registers */ #define MVNETA_RXQ_CONFIG_REG(q) (0x1400 + ((q) << 2)) @@ -242,7 +243,7 @@ struct mvneta_port { int weight; /* Core clock */ - unsigned int clk_rate_hz; + struct clk *clk; u8 mcast_count[256]; u16 tx_ring_size; u16 rx_ring_size; @@ -1029,7 +1030,11 @@ static void mvneta_rx_pkts_coal_set(struct mvneta_port *pp, static void mvneta_rx_time_coal_set(struct mvneta_port *pp, struct mvneta_rx_queue *rxq, u32 value) { - u32 val = (pp->clk_rate_hz / 1000000) * value; + u32 val; + unsigned long clk_rate; + + clk_rate = clk_get_rate(pp->clk); + val = (clk_rate / 1000000) * value; mvreg_write(pp, MVNETA_RXQ_TIME_COAL_REG(rxq->id), val); rxq->time_coal = value; @@ -2671,7 +2676,7 @@ static int __devinit mvneta_probe(struct platform_device *pdev) const struct mbus_dram_target_info *dram_target_info; struct device_node *dn = pdev->dev.of_node; struct device_node *phy_node; - u32 phy_addr, clk_rate_hz; + u32 phy_addr; struct mvneta_port *pp; struct net_device *dev; const char *mac_addr; @@ -2710,12 +2715,6 @@ static int __devinit mvneta_probe(struct platform_device *pdev) goto err_free_irq; } - if (of_property_read_u32(dn, "clock-frequency", &clk_rate_hz) != 0) { - dev_err(&pdev->dev, "could not read clock-frequency\n"); - err = -EINVAL; - goto err_free_irq; - } - mac_addr = of_get_mac_address(dn); if (!mac_addr || !is_valid_ether_addr(mac_addr)) @@ -2736,7 +2735,6 @@ static int __devinit mvneta_probe(struct platform_device *pdev) clear_bit(MVNETA_F_TX_DONE_TIMER_BIT, &pp->flags); pp->weight = MVNETA_RX_POLL_WEIGHT; - pp->clk_rate_hz = clk_rate_hz; pp->phy_node = phy_node; pp->phy_interface = phy_mode; @@ -2746,6 +2744,14 @@ static int __devinit mvneta_probe(struct platform_device *pdev) goto err_free_irq; } + pp->clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(pp->clk)) { + err = PTR_ERR(pp->clk); + goto err_unmap; + } + + clk_prepare_enable(pp->clk); + pp->tx_done_timer.data = (unsigned long)dev; pp->tx_ring_size = MVNETA_MAX_TXD; @@ -2757,7 +2763,7 @@ static int __devinit mvneta_probe(struct platform_device *pdev) err = mvneta_init(pp, phy_addr); if (err < 0) { dev_err(&pdev->dev, "can't init eth hal\n"); - goto err_unmap; + goto err_clk; } mvneta_port_power_up(pp, phy_mode); @@ -2785,6 +2791,8 @@ static int __devinit mvneta_probe(struct platform_device *pdev) err_deinit: mvneta_deinit(pp); +err_clk: + clk_disable_unprepare(pp->clk); err_unmap: iounmap(pp->base); err_free_irq: @@ -2802,6 +2810,7 @@ static int __devexit mvneta_remove(struct platform_device *pdev) unregister_netdev(dev); mvneta_deinit(pp); + clk_disable_unprepare(pp->clk); iounmap(pp->base); irq_dispose_mapping(dev->irq); free_netdev(dev); -- cgit v1.2.3-59-g8ed1b From 6872a3173501f16aee5ce160359d1bb513d06a59 Mon Sep 17 00:00:00 2001 From: Srinivas Kandagatla Date: Tue, 13 Nov 2012 14:12:05 +0000 Subject: of: Add vendor prefix for Synopsys Inc. This patch adds a device tree vendor prefix for Synopsys Inc. Signed-off-by: Srinivas Kandagatla Signed-off-by: Rob Herring --- Documentation/devicetree/bindings/vendor-prefixes.txt | 1 + 1 file changed, 1 insertion(+) (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/vendor-prefixes.txt b/Documentation/devicetree/bindings/vendor-prefixes.txt index ff41e4019010..bea1e9a0388e 100644 --- a/Documentation/devicetree/bindings/vendor-prefixes.txt +++ b/Documentation/devicetree/bindings/vendor-prefixes.txt @@ -46,6 +46,7 @@ schindler Schindler sil Silicon Image simtek sirf SiRF Technology, Inc. +snps Synopsys, Inc. st STMicroelectronics stericsson ST-Ericsson ti Texas Instruments -- cgit v1.2.3-59-g8ed1b From ae8c4209af2cec065fef15d200a42a04130799f7 Mon Sep 17 00:00:00 2001 From: Stephen Warren Date: Mon, 19 Nov 2012 15:34:43 -0700 Subject: of: Add vendor prefix for Asahi Kasei Corp. Their stock ticker is 3407.T which wouldn't make a good DT vendor prefix. Use the company name initials instead. Signed-off-by: Stephen Warren Signed-off-by: Rob Herring --- Documentation/devicetree/bindings/vendor-prefixes.txt | 1 + 1 file changed, 1 insertion(+) (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/vendor-prefixes.txt b/Documentation/devicetree/bindings/vendor-prefixes.txt index bea1e9a0388e..98271503f006 100644 --- a/Documentation/devicetree/bindings/vendor-prefixes.txt +++ b/Documentation/devicetree/bindings/vendor-prefixes.txt @@ -5,6 +5,7 @@ using them to avoid name-space collisions. ad Avionic Design GmbH adi Analog Devices, Inc. +ak Asahi Kasei Corp. amcc Applied Micro Circuits Corporation (APM, formally AMCC) apm Applied Micro Circuits Corporation (APM) arm ARM Ltd. -- cgit v1.2.3-59-g8ed1b From 4a83eecff65bd327bf5cb3b400b96fa975c73308 Mon Sep 17 00:00:00 2001 From: AnilKumar Ch Date: Tue, 20 Nov 2012 22:49:31 -0800 Subject: Input: matrix-keypad - add device tree support Also the driver was modifued to take advantage of recent improvements in matrix_keypad_build_keymap() implementation, which automatically allocates memory for keymap. The driver was tested on AM335x EVM. Signed-off-by: AnilKumar Ch Acked-by: Rob Herring Signed-off-by: Dmitry Torokhov --- .../bindings/input/gpio-matrix-keypad.txt | 46 ++++++++ drivers/input/keyboard/matrix_keypad.c | 119 +++++++++++++++++---- 2 files changed, 143 insertions(+), 22 deletions(-) create mode 100644 Documentation/devicetree/bindings/input/gpio-matrix-keypad.txt (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/input/gpio-matrix-keypad.txt b/Documentation/devicetree/bindings/input/gpio-matrix-keypad.txt new file mode 100644 index 000000000000..ead641c65e0a --- /dev/null +++ b/Documentation/devicetree/bindings/input/gpio-matrix-keypad.txt @@ -0,0 +1,46 @@ +* GPIO driven matrix keypad device tree bindings + +GPIO driven matrix keypad is used to interface a SoC with a matrix keypad. +The matrix keypad supports multiple row and column lines, a key can be +placed at each intersection of a unique row and a unique column. The matrix +keypad can sense a key-press and key-release by means of GPIO lines and +report the event using GPIO interrupts to the cpu. + +Required Properties: +- compatible: Should be "gpio-matrix-keypad" +- row-gpios: List of gpios used as row lines. The gpio specifier + for this property depends on the gpio controller to + which these row lines are connected. +- col-gpios: List of gpios used as column lines. The gpio specifier + for this property depends on the gpio controller to + which these column lines are connected. +- linux,keymap: The definition can be found at + bindings/input/matrix-keymap.txt + +Optional Properties: +- linux,no-autorepeat: do no enable autorepeat feature. +- linux,wakeup: use any event on keypad as wakeup event. +- debounce-delay-ms: debounce interval in milliseconds +- col-scan-delay-us: delay, measured in microseconds, that is needed + before we can scan keypad after activating column gpio + +Example: + matrix-keypad { + compatible = "gpio-matrix-keypad"; + debounce-delay-ms = <5>; + col-scan-delay-us = <2>; + + row-gpios = <&gpio2 25 0 + &gpio2 26 0 + &gpio2 27 0>; + + col-gpios = <&gpio2 21 0 + &gpio2 22 0>; + + linux,keymap = <0x0000008B + 0x0100009E + 0x02000069 + 0x0001006A + 0x0101001C + 0x0201006C>; + }; diff --git a/drivers/input/keyboard/matrix_keypad.c b/drivers/input/keyboard/matrix_keypad.c index 18b72372028a..05d3a969ff4e 100644 --- a/drivers/input/keyboard/matrix_keypad.c +++ b/drivers/input/keyboard/matrix_keypad.c @@ -23,6 +23,9 @@ #include #include #include +#include +#include +#include struct matrix_keypad { const struct matrix_keypad_platform_data *pdata; @@ -37,8 +40,6 @@ struct matrix_keypad { bool scan_pending; bool stopped; bool gpio_all_disabled; - - unsigned short keycodes[]; }; /* @@ -118,6 +119,7 @@ static void matrix_keypad_scan(struct work_struct *work) struct matrix_keypad *keypad = container_of(work, struct matrix_keypad, work.work); struct input_dev *input_dev = keypad->input_dev; + const unsigned short *keycodes = input_dev->keycode; const struct matrix_keypad_platform_data *pdata = keypad->pdata; uint32_t new_state[MATRIX_MAX_COLS]; int row, col, code; @@ -153,7 +155,7 @@ static void matrix_keypad_scan(struct work_struct *work) code = MATRIX_SCAN_CODE(row, col, keypad->row_shift); input_event(input_dev, EV_MSC, MSC_SCAN, code); input_report_key(input_dev, - keypad->keycodes[code], + keycodes[code], new_state[col] & (1 << row)); } } @@ -394,33 +396,95 @@ static void matrix_keypad_free_gpio(struct matrix_keypad *keypad) gpio_free(pdata->col_gpios[i]); } +#ifdef CONFIG_OF +static struct matrix_keypad_platform_data * __devinit +matrix_keypad_parse_dt(struct device *dev) +{ + struct matrix_keypad_platform_data *pdata; + struct device_node *np = dev->of_node; + unsigned int *gpios; + int i; + + if (!np) { + dev_err(dev, "device lacks DT data\n"); + return ERR_PTR(-ENODEV); + } + + pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) { + dev_err(dev, "could not allocate memory for platform data\n"); + return ERR_PTR(-ENOMEM); + } + + pdata->num_row_gpios = of_gpio_named_count(np, "row-gpios"); + pdata->num_col_gpios = of_gpio_named_count(np, "col-gpios"); + if (!pdata->num_row_gpios || !pdata->num_col_gpios) { + dev_err(dev, "number of keypad rows/columns not specified\n"); + return ERR_PTR(-EINVAL); + } + + if (of_get_property(np, "linux,no-autorepeat", NULL)) + pdata->no_autorepeat = true; + if (of_get_property(np, "linux,wakeup", NULL)) + pdata->wakeup = true; + if (of_get_property(np, "gpio-activelow", NULL)) + pdata->active_low = true; + + of_property_read_u32(np, "debounce-delay-ms", &pdata->debounce_ms); + of_property_read_u32(np, "col-scan-delay-us", + &pdata->col_scan_delay_us); + + gpios = devm_kzalloc(dev, + sizeof(unsigned int) * + (pdata->num_row_gpios + pdata->num_col_gpios), + GFP_KERNEL); + if (!gpios) { + dev_err(dev, "could not allocate memory for gpios\n"); + return ERR_PTR(-ENOMEM); + } + + for (i = 0; i < pdata->num_row_gpios; i++) + gpios[i] = of_get_named_gpio(np, "row-gpios", i); + + for (i = 0; i < pdata->num_col_gpios; i++) + gpios[pdata->num_row_gpios + i] = + of_get_named_gpio(np, "col-gpios", i); + + pdata->row_gpios = gpios; + pdata->col_gpios = &gpios[pdata->num_row_gpios]; + + return pdata; +} +#else +static inline struct matrix_keypad_platform_data * +matrix_keypad_parse_dt(struct device *dev) +{ + dev_err(dev, "no platform data defined\n"); + + return ERR_PTR(-EINVAL); +} +#endif + static int __devinit matrix_keypad_probe(struct platform_device *pdev) { const struct matrix_keypad_platform_data *pdata; - const struct matrix_keymap_data *keymap_data; struct matrix_keypad *keypad; struct input_dev *input_dev; - unsigned int row_shift; - size_t keymap_size; int err; - pdata = pdev->dev.platform_data; + pdata = dev_get_platdata(&pdev->dev); if (!pdata) { - dev_err(&pdev->dev, "no platform data defined\n"); - return -EINVAL; - } - - keymap_data = pdata->keymap_data; - if (!keymap_data) { + pdata = matrix_keypad_parse_dt(&pdev->dev); + if (IS_ERR(pdata)) { + dev_err(&pdev->dev, "no platform data defined\n"); + return PTR_ERR(pdata); + } + } else if (!pdata->keymap_data) { dev_err(&pdev->dev, "no keymap data defined\n"); return -EINVAL; } - row_shift = get_count_order(pdata->num_col_gpios); - keymap_size = (pdata->num_row_gpios << row_shift) * - sizeof(keypad->keycodes[0]); - keypad = kzalloc(sizeof(struct matrix_keypad) + keymap_size, - GFP_KERNEL); + keypad = kzalloc(sizeof(struct matrix_keypad), GFP_KERNEL); input_dev = input_allocate_device(); if (!keypad || !input_dev) { err = -ENOMEM; @@ -429,7 +493,7 @@ static int __devinit matrix_keypad_probe(struct platform_device *pdev) keypad->input_dev = input_dev; keypad->pdata = pdata; - keypad->row_shift = row_shift; + keypad->row_shift = get_count_order(pdata->num_col_gpios); keypad->stopped = true; INIT_DELAYED_WORK(&keypad->work, matrix_keypad_scan); spin_lock_init(&keypad->lock); @@ -440,12 +504,14 @@ static int __devinit matrix_keypad_probe(struct platform_device *pdev) input_dev->open = matrix_keypad_start; input_dev->close = matrix_keypad_stop; - err = matrix_keypad_build_keymap(keymap_data, NULL, + err = matrix_keypad_build_keymap(pdata->keymap_data, NULL, pdata->num_row_gpios, pdata->num_col_gpios, - keypad->keycodes, input_dev); - if (err) + NULL, input_dev); + if (err) { + dev_err(&pdev->dev, "failed to build keymap\n"); goto err_free_mem; + } if (!pdata->no_autorepeat) __set_bit(EV_REP, input_dev->evbit); @@ -488,6 +554,14 @@ static int __devexit matrix_keypad_remove(struct platform_device *pdev) return 0; } +#ifdef CONFIG_OF +static const struct of_device_id matrix_keypad_dt_match[] = { + { .compatible = "gpio-matrix-keypad" }, + { } +}; +MODULE_DEVICE_TABLE(of, matrix_keypad_dt_match); +#endif + static struct platform_driver matrix_keypad_driver = { .probe = matrix_keypad_probe, .remove = __devexit_p(matrix_keypad_remove), @@ -495,6 +569,7 @@ static struct platform_driver matrix_keypad_driver = { .name = "matrix-keypad", .owner = THIS_MODULE, .pm = &matrix_keypad_pm_ops, + .of_match_table = of_match_ptr(matrix_keypad_dt_match), }, }; module_platform_driver(matrix_keypad_driver); -- cgit v1.2.3-59-g8ed1b From 67bea88dd842deb3ed8327ad101970dbd615ddcb Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Mon, 19 Nov 2012 18:57:08 +0100 Subject: ARM: sunxi: Add sunxi restart function via onchip watchdog Signed-off-by: Stefan Roese Signed-off-by: Maxime Ripard --- .../devicetree/bindings/watchdog/sunxi-wdt.txt | 13 +++++++++ arch/arm/boot/dts/sunxi.dtsi | 7 ++++- arch/arm/mach-sunxi/sunxi.c | 34 ++++++++++++++++++++++ 3 files changed, 53 insertions(+), 1 deletion(-) create mode 100644 Documentation/devicetree/bindings/watchdog/sunxi-wdt.txt (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/watchdog/sunxi-wdt.txt b/Documentation/devicetree/bindings/watchdog/sunxi-wdt.txt new file mode 100644 index 000000000000..0b2717775600 --- /dev/null +++ b/Documentation/devicetree/bindings/watchdog/sunxi-wdt.txt @@ -0,0 +1,13 @@ +Allwinner sunXi Watchdog timer + +Required properties: + +- compatible : should be "allwinner,sunxi-wdt" +- reg : Specifies base physical address and size of the registers. + +Example: + +wdt: watchdog@01c20c90 { + compatible = "allwinner,sunxi-wdt"; + reg = <0x01c20c90 0x10>; +}; diff --git a/arch/arm/boot/dts/sunxi.dtsi b/arch/arm/boot/dts/sunxi.dtsi index 8284fbd7ddab..8bbc2bfef221 100644 --- a/arch/arm/boot/dts/sunxi.dtsi +++ b/arch/arm/boot/dts/sunxi.dtsi @@ -42,11 +42,16 @@ timer@01c20c00 { compatible = "allwinner,sunxi-timer"; - reg = <0x01c20c00 0x400>; + reg = <0x01c20c00 0x90>; interrupts = <22>; clocks = <&osc>; }; + wdt: watchdog@01c20c90 { + compatible = "allwinner,sunxi-wdt"; + reg = <0x01c20c90 0x10>; + }; + intc: interrupt-controller@01c20400 { compatible = "allwinner,sunxi-ic"; reg = <0x01c20400 0x400>; diff --git a/arch/arm/mach-sunxi/sunxi.c b/arch/arm/mach-sunxi/sunxi.c index 13d4d96cbc85..dc634ae2081f 100644 --- a/arch/arm/mach-sunxi/sunxi.c +++ b/arch/arm/mach-sunxi/sunxi.c @@ -12,6 +12,7 @@ #include #include +#include #include #include #include @@ -26,6 +27,36 @@ #include "sunxi.h" +#define WATCHDOG_CTRL_REG 0x00 +#define WATCHDOG_MODE_REG 0x04 + +static void __iomem *wdt_base; + +static void sunxi_setup_restart(void) +{ + struct device_node *np = of_find_compatible_node(NULL, NULL, + "allwinner,sunxi-wdt"); + if (WARN(!np, "unable to setup watchdog restart")) + return; + + wdt_base = of_iomap(np, 0); + WARN(!wdt_base, "failed to map watchdog base address"); +} + +static void sunxi_restart(char mode, const char *cmd) +{ + if (!wdt_base) + return; + + /* Enable timer and set reset bit in the watchdog */ + writel(3, wdt_base + WATCHDOG_MODE_REG); + writel(0xa57 << 1 | 1, wdt_base + WATCHDOG_CTRL_REG); + while(1) { + mdelay(5); + writel(3, wdt_base + WATCHDOG_MODE_REG); + } +} + static struct map_desc sunxi_io_desc[] __initdata = { { .virtual = (unsigned long) SUNXI_REGS_VIRT_BASE, @@ -42,6 +73,8 @@ void __init sunxi_map_io(void) static void __init sunxi_dt_init(void) { + sunxi_setup_restart(); + of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL); } @@ -56,6 +89,7 @@ DT_MACHINE_START(SUNXI_DT, "Allwinner A1X (Device Tree)") .map_io = sunxi_map_io, .init_irq = sunxi_init_irq, .handle_irq = sunxi_handle_irq, + .restart = sunxi_restart, .timer = &sunxi_timer, .dt_compat = sunxi_board_dt_compat, MACHINE_END -- cgit v1.2.3-59-g8ed1b From 20d32022a8d8639a33d0e429f2d3c51b527ea362 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 20 Nov 2012 18:24:47 +0100 Subject: ALSA: usb-audio: Deprecate async_unlink option The async unlink behavior has been working over years. The option was provided only as a workaround for 2.4.x kernel. Let's get rid of it. Signed-off-by: Takashi Iwai --- Documentation/sound/alsa/ALSA-Configuration.txt | 3 --- sound/usb/card.c | 7 ------- sound/usb/endpoint.c | 2 +- sound/usb/usbaudio.h | 1 - 4 files changed, 1 insertion(+), 12 deletions(-) (limited to 'Documentation') diff --git a/Documentation/sound/alsa/ALSA-Configuration.txt b/Documentation/sound/alsa/ALSA-Configuration.txt index d90d8ec2853d..b9cfd339a6fa 100644 --- a/Documentation/sound/alsa/ALSA-Configuration.txt +++ b/Documentation/sound/alsa/ALSA-Configuration.txt @@ -1905,7 +1905,6 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed. vid - Vendor ID for the device (optional) pid - Product ID for the device (optional) nrpacks - Max. number of packets per URB (default: 8) - async_unlink - Use async unlink mode (default: yes) device_setup - Device specific magic number (optional) - Influence depends on the device - Default: 0x0000 @@ -1917,8 +1916,6 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed. NB: nrpacks parameter can be modified dynamically via sysfs. Don't put the value over 20. Changing via sysfs has no sanity check. - NB: async_unlink=0 would cause Oops. It remains just for - debugging purpose (if any). NB: ignore_ctl_error=1 may help when you get an error at accessing the mixer element such as URB error -22. This happens on some buggy USB device or the controller. diff --git a/sound/usb/card.c b/sound/usb/card.c index dbf7999d18b4..ccf95cfe186f 100644 --- a/sound/usb/card.c +++ b/sound/usb/card.c @@ -25,9 +25,6 @@ * * NOTES: * - * - async unlink should be used for avoiding the sleep inside lock. - * 2.4.22 usb-uhci seems buggy for async unlinking and results in - * oops. in such a cse, pass async_unlink=0 option. * - the linked URBs would be preferred but not used so far because of * the instability of unlinking. * - type II is not supported properly. there is no device which supports @@ -83,7 +80,6 @@ static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;/* Enable this card * static int vid[SNDRV_CARDS] = { [0 ... (SNDRV_CARDS-1)] = -1 }; static int pid[SNDRV_CARDS] = { [0 ... (SNDRV_CARDS-1)] = -1 }; static int nrpacks = 8; /* max. number of packets per urb */ -static bool async_unlink = 1; static int device_setup[SNDRV_CARDS]; /* device parameter for this card */ static bool ignore_ctl_error; @@ -99,8 +95,6 @@ module_param_array(pid, int, NULL, 0444); MODULE_PARM_DESC(pid, "Product ID for the USB audio device."); module_param(nrpacks, int, 0644); MODULE_PARM_DESC(nrpacks, "Max. number of packets per URB."); -module_param(async_unlink, bool, 0444); -MODULE_PARM_DESC(async_unlink, "Use async unlink mode."); module_param_array(device_setup, int, NULL, 0444); MODULE_PARM_DESC(device_setup, "Specific device setup (if needed)."); module_param(ignore_ctl_error, bool, 0444); @@ -345,7 +339,6 @@ static int snd_usb_audio_create(struct usb_device *dev, int idx, chip->card = card; chip->setup = device_setup[idx]; chip->nrpacks = nrpacks; - chip->async_unlink = async_unlink; chip->probing = 1; chip->usb_id = USB_ID(le16_to_cpu(dev->descriptor.idVendor), diff --git a/sound/usb/endpoint.c b/sound/usb/endpoint.c index 51a9aa372cc7..d7382a5e29bb 100644 --- a/sound/usb/endpoint.c +++ b/sound/usb/endpoint.c @@ -523,7 +523,7 @@ static int deactivate_urbs(struct snd_usb_endpoint *ep, int force, int can_sleep if (!force && ep->chip->shutdown) /* to be sure... */ return -EBADFD; - async = !can_sleep && ep->chip->async_unlink; + async = !can_sleep; clear_bit(EP_FLAG_RUNNING, &ep->flags); diff --git a/sound/usb/usbaudio.h b/sound/usb/usbaudio.h index ef42797f56fb..1ac3fd9cc5a6 100644 --- a/sound/usb/usbaudio.h +++ b/sound/usb/usbaudio.h @@ -56,7 +56,6 @@ struct snd_usb_audio { int setup; /* from the 'device_setup' module param */ int nrpacks; /* from the 'nrpacks' module param */ - int async_unlink; /* from the 'async_unlink' module param */ struct usb_host_interface *ctrl_intf; /* the audio control interface */ }; -- cgit v1.2.3-59-g8ed1b From 60d8cce7c53f188d99cb01a0a013cf3544f35e29 Mon Sep 17 00:00:00 2001 From: Marek Lindner Date: Sun, 21 Oct 2012 17:02:25 +0800 Subject: batman-adv: sysfs documentation should keep alphabetical order Signed-off-by: Marek Lindner Signed-off-by: Antonio Quartulli --- .../ABI/testing/sysfs-class-net-batman-adv | 11 +++--- Documentation/ABI/testing/sysfs-class-net-mesh | 40 +++++++++++----------- 2 files changed, 26 insertions(+), 25 deletions(-) (limited to 'Documentation') diff --git a/Documentation/ABI/testing/sysfs-class-net-batman-adv b/Documentation/ABI/testing/sysfs-class-net-batman-adv index 38dd762def4b..bdc00707c751 100644 --- a/Documentation/ABI/testing/sysfs-class-net-batman-adv +++ b/Documentation/ABI/testing/sysfs-class-net-batman-adv @@ -1,4 +1,10 @@ +What: /sys/class/net//batman-adv/iface_status +Date: May 2010 +Contact: Marek Lindner +Description: + Indicates the status of as it is seen by batman. + What: /sys/class/net//batman-adv/mesh_iface Date: May 2010 Contact: Marek Lindner @@ -7,8 +13,3 @@ Description: displays the batman mesh interface this currently is associated with. -What: /sys/class/net//batman-adv/iface_status -Date: May 2010 -Contact: Marek Lindner -Description: - Indicates the status of as it is seen by batman. diff --git a/Documentation/ABI/testing/sysfs-class-net-mesh b/Documentation/ABI/testing/sysfs-class-net-mesh index c81fe89c4c46..bc41da61608d 100644 --- a/Documentation/ABI/testing/sysfs-class-net-mesh +++ b/Documentation/ABI/testing/sysfs-class-net-mesh @@ -6,6 +6,14 @@ Description: Indicates whether the batman protocol messages of the mesh shall be aggregated or not. +What: /sys/class/net//mesh/ap_isolation +Date: May 2011 +Contact: Antonio Quartulli +Description: + Indicates whether the data traffic going from a + wireless client to another wireless client will be + silently dropped. + What: /sys/class/net//mesh/bonding Date: June 2010 Contact: Simon Wunderlich @@ -31,14 +39,6 @@ Description: mesh will be fragmented or silently discarded if the packet size exceeds the outgoing interface MTU. -What: /sys/class/net//mesh/ap_isolation -Date: May 2011 -Contact: Antonio Quartulli -Description: - Indicates whether the data traffic going from a - wireless client to another wireless client will be - silently dropped. - What: /sys/class/net//mesh/gw_bandwidth Date: October 2010 Contact: Marek Lindner @@ -60,6 +60,13 @@ Description: Defines the selection criteria this node will use to choose a gateway if gw_mode was set to 'client'. +What: /sys/class/net//mesh/hop_penalty +Date: Oct 2010 +Contact: Linus Lüssing +Description: + Defines the penalty which will be applied to an + originator message's tq-field on every hop. + What: /sys/class/net//mesh/orig_interval Date: May 2010 Contact: Marek Lindner @@ -67,19 +74,12 @@ Description: Defines the interval in milliseconds in which batman sends its protocol messages. -What: /sys/class/net//mesh/hop_penalty -Date: Oct 2010 -Contact: Linus Lüssing -Description: - Defines the penalty which will be applied to an - originator message's tq-field on every hop. - -What: /sys/class/net//mesh/routing_algo -Date: Dec 2011 -Contact: Marek Lindner +What: /sys/class/net//mesh/routing_algo +Date: Dec 2011 +Contact: Marek Lindner Description: - Defines the routing procotol this mesh instance - uses to find the optimal paths through the mesh. + Defines the routing procotol this mesh instance + uses to find the optimal paths through the mesh. What: /sys/class/net//mesh/vis_mode Date: May 2010 -- cgit v1.2.3-59-g8ed1b From eb433dad48b49f757255f2860db5c2bd61dcae0d Mon Sep 17 00:00:00 2001 From: Colin Foe-Parker Date: Tue, 20 Nov 2012 15:18:44 +0530 Subject: mfd: tps65217: Set PMIC to shutdown on PWR_EN toggle Set tps65217 PMIC status to OFF if power enable toggle is supported. By setting this bit to 1 to enter PMIC to OFF state when PWR_EN pin is pulled low. Also adds a DT flag to specify that device pmic supports shutdown control or not. Signed-off-by: Colin Foe-Parker [anilkumar@ti.com: move the additions to tps65217 MFD driver] Signed-off-by: AnilKumar Ch Signed-off-by: Samuel Ortiz --- Documentation/devicetree/bindings/regulator/tps65217.txt | 4 ++++ drivers/mfd/tps65217.c | 12 ++++++++++++ 2 files changed, 16 insertions(+) (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/regulator/tps65217.txt b/Documentation/devicetree/bindings/regulator/tps65217.txt index d316fb895daf..4f05d208c95c 100644 --- a/Documentation/devicetree/bindings/regulator/tps65217.txt +++ b/Documentation/devicetree/bindings/regulator/tps65217.txt @@ -11,6 +11,9 @@ Required properties: using the standard binding for regulators found at Documentation/devicetree/bindings/regulator/regulator.txt. +Optional properties: +- ti,pmic-shutdown-controller: Telling the PMIC to shutdown on PWR_EN toggle. + The valid names for regulators are: tps65217: dcdc1, dcdc2, dcdc3, ldo1, ldo2, ldo3 and ldo4 @@ -20,6 +23,7 @@ Example: tps: tps@24 { compatible = "ti,tps65217"; + ti,pmic-shutdown-controller; regulators { dcdc1_reg: dcdc1 { diff --git a/drivers/mfd/tps65217.c b/drivers/mfd/tps65217.c index 3fb32e655254..c7f17d86db78 100644 --- a/drivers/mfd/tps65217.c +++ b/drivers/mfd/tps65217.c @@ -160,6 +160,7 @@ static int __devinit tps65217_probe(struct i2c_client *client, unsigned int version; unsigned int chip_id = ids->driver_data; const struct of_device_id *match; + bool status_off = false; int ret; if (client->dev.of_node) { @@ -170,6 +171,8 @@ static int __devinit tps65217_probe(struct i2c_client *client, return -EINVAL; } chip_id = (unsigned int)match->data; + status_off = of_property_read_bool(client->dev.of_node, + "ti,pmic-shutdown-controller"); } if (!chip_id) { @@ -207,6 +210,15 @@ static int __devinit tps65217_probe(struct i2c_client *client, return ret; } + /* Set the PMIC to shutdown on PWR_EN toggle */ + if (status_off) { + ret = tps65217_set_bits(tps, TPS65217_REG_STATUS, + TPS65217_STATUS_OFF, TPS65217_STATUS_OFF, + TPS65217_PROTECT_NONE); + if (ret) + dev_warn(tps->dev, "unable to set the status OFF\n"); + } + dev_info(tps->dev, "TPS65217 ID %#x version 1.%d\n", (version & TPS65217_CHIPID_CHIP_MASK) >> 4, version & TPS65217_CHIPID_REV_MASK); -- cgit v1.2.3-59-g8ed1b From 2ed5f236716cbd20f51bdc87ddd91bad259c81a6 Mon Sep 17 00:00:00 2001 From: Tomasz Figa Date: Thu, 22 Nov 2012 00:21:08 +0900 Subject: ARM: EXYNOS: Detect power domain state on registration from DT Initial state of power domains might vary on different boards and with different bootloaders. This patch adds detection of initial state of power domains when being registered from DT. Signed-off-by: Tomasz Figa Signed-off-by: Kyungmin Park Signed-off-by: Kukjin Kim --- Documentation/devicetree/bindings/arm/exynos/power_domain.txt | 4 ---- arch/arm/mach-exynos/pm_domains.c | 8 +++++--- 2 files changed, 5 insertions(+), 7 deletions(-) (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/arm/exynos/power_domain.txt b/Documentation/devicetree/bindings/arm/exynos/power_domain.txt index 6528e215c5fe..843b54622313 100644 --- a/Documentation/devicetree/bindings/arm/exynos/power_domain.txt +++ b/Documentation/devicetree/bindings/arm/exynos/power_domain.txt @@ -9,10 +9,6 @@ Required Properties: - reg: physical base address of the controller and length of memory mapped region. -Optional Properties: -- samsung,exynos4210-pd-off: Specifies that the power domain is in turned-off - state during boot and remains to be turned-off until explicitly turned-on. - Example: lcd0: power-domain-lcd0 { diff --git a/arch/arm/mach-exynos/pm_domains.c b/arch/arm/mach-exynos/pm_domains.c index c0bc83a7663e..d1abc1a94ecd 100644 --- a/arch/arm/mach-exynos/pm_domains.c +++ b/arch/arm/mach-exynos/pm_domains.c @@ -89,6 +89,7 @@ static __init int exynos_pm_dt_parse_domains(void) for_each_compatible_node(np, NULL, "samsung,exynos4210-pd") { struct exynos_pm_domain *pd; + int on; pd = kzalloc(sizeof(*pd), GFP_KERNEL); if (!pd) { @@ -97,14 +98,15 @@ static __init int exynos_pm_dt_parse_domains(void) return -ENOMEM; } - if (of_get_property(np, "samsung,exynos4210-pd-off", NULL)) - pd->is_off = true; pd->name = np->name; pd->base = of_iomap(np, 0); pd->pd.power_off = exynos_pd_power_off; pd->pd.power_on = exynos_pd_power_on; pd->pd.of_node = np; - pm_genpd_init(&pd->pd, NULL, false); + + on = __raw_readl(pd->base + 0x4) & S5P_INT_LOCAL_PWR_EN; + + pm_genpd_init(&pd->pd, NULL, !on); } return 0; } -- cgit v1.2.3-59-g8ed1b From 8a65d2365df0635da13ae30c01e217c477c9ce3e Mon Sep 17 00:00:00 2001 From: Tomasz Figa Date: Thu, 22 Nov 2012 00:21:17 +0900 Subject: ARM: EXYNOS: Bind devices to power domains using DT This patch adds a way to specify bindings between devices and power domains using device tree. A device can be bound to particular power domain by adding a power-domain property containing a phandle to the domain. The device will be bound to the domain before binding a driver to it and unbound after unbinding a driver from it. Signed-off-by: Tomasz Figa Signed-off-by: Kyungmin Park Signed-off-by: Kukjin Kim --- .../bindings/arm/exynos/power_domain.txt | 13 +++- arch/arm/mach-exynos/pm_domains.c | 82 ++++++++++++++++++++++ 2 files changed, 94 insertions(+), 1 deletion(-) (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/arm/exynos/power_domain.txt b/Documentation/devicetree/bindings/arm/exynos/power_domain.txt index 843b54622313..5216b419016a 100644 --- a/Documentation/devicetree/bindings/arm/exynos/power_domain.txt +++ b/Documentation/devicetree/bindings/arm/exynos/power_domain.txt @@ -4,14 +4,25 @@ Exynos processors include support for multiple power domains which are used to gate power to one or more peripherals on the processor. Required Properties: -- compatiable: should be one of the following. +- compatible: should be one of the following. * samsung,exynos4210-pd - for exynos4210 type power domain. - reg: physical base address of the controller and length of memory mapped region. +Node of a device using power domains must have a samsung,power-domain property +defined with a phandle to respective power domain. + Example: lcd0: power-domain-lcd0 { compatible = "samsung,exynos4210-pd"; reg = <0x10023C00 0x10>; }; + +Example of the node using power domain: + + node { + /* ... */ + samsung,power-domain = <&lcd0>; + /* ... */ + }; diff --git a/arch/arm/mach-exynos/pm_domains.c b/arch/arm/mach-exynos/pm_domains.c index 5b7ce7ea6c61..9f1351de52f7 100644 --- a/arch/arm/mach-exynos/pm_domains.c +++ b/arch/arm/mach-exynos/pm_domains.c @@ -19,6 +19,8 @@ #include #include #include +#include +#include #include #include @@ -83,14 +85,89 @@ static struct exynos_pm_domain PD = { \ } #ifdef CONFIG_OF +static void exynos_add_device_to_domain(struct exynos_pm_domain *pd, + struct device *dev) +{ + int ret; + + dev_dbg(dev, "adding to power domain %s\n", pd->pd.name); + + while (1) { + ret = pm_genpd_add_device(&pd->pd, dev); + if (ret != -EAGAIN) + break; + cond_resched(); + } + + pm_genpd_dev_need_restore(dev, true); +} + +static void exynos_remove_device_from_domain(struct device *dev) +{ + struct generic_pm_domain *genpd = dev_to_genpd(dev); + int ret; + + dev_dbg(dev, "removing from power domain %s\n", genpd->name); + + while (1) { + ret = pm_genpd_remove_device(genpd, dev); + if (ret != -EAGAIN) + break; + cond_resched(); + } +} + +static void exynos_read_domain_from_dt(struct device *dev) +{ + struct platform_device *pd_pdev; + struct exynos_pm_domain *pd; + struct device_node *node; + + node = of_parse_phandle(dev->of_node, "samsung,power-domain", 0); + if (!node) + return; + pd_pdev = of_find_device_by_node(node); + if (!pd_pdev) + return; + pd = platform_get_drvdata(pd_pdev); + exynos_add_device_to_domain(pd, dev); +} + +static int exynos_pm_notifier_call(struct notifier_block *nb, + unsigned long event, void *data) +{ + struct device *dev = data; + + switch (event) { + case BUS_NOTIFY_BIND_DRIVER: + if (dev->of_node) + exynos_read_domain_from_dt(dev); + + break; + + case BUS_NOTIFY_UNBOUND_DRIVER: + exynos_remove_device_from_domain(dev); + + break; + } + return NOTIFY_DONE; +} + +static struct notifier_block platform_nb = { + .notifier_call = exynos_pm_notifier_call, +}; + static __init int exynos_pm_dt_parse_domains(void) { + struct platform_device *pdev; struct device_node *np; for_each_compatible_node(np, NULL, "samsung,exynos4210-pd") { struct exynos_pm_domain *pd; int on; + pdev = of_find_device_by_node(np); + pd = kzalloc(sizeof(*pd), GFP_KERNEL); if (!pd) { pr_err("%s: failed to allocate memory for domain\n", @@ -105,10 +182,15 @@ static __init int exynos_pm_dt_parse_domains(void) pd->pd.power_on = exynos_pd_power_on; pd->pd.of_node = np; + platform_set_drvdata(pdev, pd); + on = __raw_readl(pd->base + 0x4) & S5P_INT_LOCAL_PWR_EN; pm_genpd_init(&pd->pd, NULL, !on); } + + bus_register_notifier(&platform_bus_type, &platform_nb); + return 0; } #else -- cgit v1.2.3-59-g8ed1b From 009f13159bfdccd6e06fe3b62a39fee6dce26c39 Mon Sep 17 00:00:00 2001 From: Gregory CLEMENT Date: Thu, 2 Aug 2012 11:16:29 +0300 Subject: arm: mvebu: Add support for coherency fabric in mach-mvebu The Armada 370 and Armada XP SOCs have a coherency fabric unit which is responsible for ensuring hardware coherency between all CPUs and between CPUs and I/O masters. This patch provides the basic support needed for SMP. Signed-off-by: Yehuda Yitschak Signed-off-by: Gregory CLEMENT Reviewed-by: Will Deacon --- .../devicetree/bindings/arm/coherency-fabric.txt | 16 +++++ arch/arm/boot/dts/armada-370-xp.dtsi | 5 ++ arch/arm/mach-mvebu/Makefile | 2 +- arch/arm/mach-mvebu/coherency.c | 82 ++++++++++++++++++++++ arch/arm/mach-mvebu/coherency.h | 24 +++++++ arch/arm/mach-mvebu/coherency_ll.S | 49 +++++++++++++ arch/arm/mach-mvebu/common.h | 1 + 7 files changed, 178 insertions(+), 1 deletion(-) create mode 100644 Documentation/devicetree/bindings/arm/coherency-fabric.txt create mode 100644 arch/arm/mach-mvebu/coherency.c create mode 100644 arch/arm/mach-mvebu/coherency.h create mode 100644 arch/arm/mach-mvebu/coherency_ll.S (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/arm/coherency-fabric.txt b/Documentation/devicetree/bindings/arm/coherency-fabric.txt new file mode 100644 index 000000000000..2bfbf67dd77e --- /dev/null +++ b/Documentation/devicetree/bindings/arm/coherency-fabric.txt @@ -0,0 +1,16 @@ +Coherency fabric +---------------- +Available on Marvell SOCs: Armada 370 and Armada XP + +Required properties: + +- compatible: "marvell,coherency-fabric" +- reg: Should contain,coherency fabric registers location and length. + +Example: + +coherency-fabric@d0020200 { + compatible = "marvell,coherency-fabric"; + reg = <0xd0020200 0xb0>; +}; + diff --git a/arch/arm/boot/dts/armada-370-xp.dtsi b/arch/arm/boot/dts/armada-370-xp.dtsi index 94b4b9e03571..b0d075b50f29 100644 --- a/arch/arm/boot/dts/armada-370-xp.dtsi +++ b/arch/arm/boot/dts/armada-370-xp.dtsi @@ -36,6 +36,11 @@ interrupt-controller; }; + coherency-fabric@d0020200 { + compatible = "marvell,coherency-fabric"; + reg = <0xd0020200 0xb0>; + }; + soc { #address-cells = <1>; #size-cells = <1>; diff --git a/arch/arm/mach-mvebu/Makefile b/arch/arm/mach-mvebu/Makefile index 57f996b6aa0e..5ce4b42c2697 100644 --- a/arch/arm/mach-mvebu/Makefile +++ b/arch/arm/mach-mvebu/Makefile @@ -2,4 +2,4 @@ ccflags-$(CONFIG_ARCH_MULTIPLATFORM) := -I$(srctree)/$(src)/include \ -I$(srctree)/arch/arm/plat-orion/include obj-y += system-controller.o -obj-$(CONFIG_MACH_ARMADA_370_XP) += armada-370-xp.o irq-armada-370-xp.o addr-map.o +obj-$(CONFIG_MACH_ARMADA_370_XP) += armada-370-xp.o irq-armada-370-xp.o addr-map.o coherency.o coherency_ll.o diff --git a/arch/arm/mach-mvebu/coherency.c b/arch/arm/mach-mvebu/coherency.c new file mode 100644 index 000000000000..596ee66a9cc4 --- /dev/null +++ b/arch/arm/mach-mvebu/coherency.c @@ -0,0 +1,82 @@ +/* + * Coherency fabric (Aurora) support for Armada 370 and XP platforms. + * + * Copyright (C) 2012 Marvell + * + * Yehuda Yitschak + * Gregory Clement + * Thomas Petazzoni + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + * + * The Armada 370 and Armada XP SOCs have a coherency fabric which is + * responsible for ensuring hardware coherency between all CPUs and between + * CPUs and I/O masters. This file initializes the coherency fabric and + * supplies basic routines for configuring and controlling hardware coherency + */ + +#include +#include +#include +#include +#include +#include +#include "armada-370-xp.h" + +/* + * Some functions in this file are called very early during SMP + * initialization. At that time the device tree framework is not yet + * ready, and it is not possible to get the register address to + * ioremap it. That's why the pointer below is given with an initial + * value matching its virtual mapping + */ +static void __iomem *coherency_base = ARMADA_370_XP_REGS_VIRT_BASE + 0x20200; + +/* Coherency fabric registers */ +#define COHERENCY_FABRIC_CFG_OFFSET 0x4 + +static struct of_device_id of_coherency_table[] = { + {.compatible = "marvell,coherency-fabric"}, + { /* end of list */ }, +}; + +#ifdef CONFIG_SMP +int coherency_get_cpu_count(void) +{ + int reg, cnt; + + reg = readl(coherency_base + COHERENCY_FABRIC_CFG_OFFSET); + cnt = (reg & 0xF) + 1; + + return cnt; +} +#endif + +/* Function defined in coherency_ll.S */ +int ll_set_cpu_coherent(void __iomem *base_addr, unsigned int hw_cpu_id); + +int set_cpu_coherent(unsigned int hw_cpu_id, int smp_group_id) +{ + if (!coherency_base) { + pr_warn("Can't make CPU %d cache coherent.\n", hw_cpu_id); + pr_warn("Coherency fabric is not initialized\n"); + return 1; + } + + return ll_set_cpu_coherent(coherency_base, hw_cpu_id); +} + +int __init coherency_init(void) +{ + struct device_node *np; + + np = of_find_matching_node(NULL, of_coherency_table); + if (np) { + pr_info("Initializing Coherency fabric\n"); + coherency_base = of_iomap(np, 0); + } + + return 0; +} diff --git a/arch/arm/mach-mvebu/coherency.h b/arch/arm/mach-mvebu/coherency.h new file mode 100644 index 000000000000..2f428137f6fe --- /dev/null +++ b/arch/arm/mach-mvebu/coherency.h @@ -0,0 +1,24 @@ +/* + * arch/arm/mach-mvebu/include/mach/coherency.h + * + * + * Coherency fabric (Aurora) support for Armada 370 and XP platforms. + * + * Copyright (C) 2012 Marvell + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#ifndef __MACH_370_XP_COHERENCY_H +#define __MACH_370_XP_COHERENCY_H + +#ifdef CONFIG_SMP +int coherency_get_cpu_count(void); +#endif + +int set_cpu_coherent(int cpu_id, int smp_group_id); +int coherency_init(void); + +#endif /* __MACH_370_XP_COHERENCY_H */ diff --git a/arch/arm/mach-mvebu/coherency_ll.S b/arch/arm/mach-mvebu/coherency_ll.S new file mode 100644 index 000000000000..53e8391192cd --- /dev/null +++ b/arch/arm/mach-mvebu/coherency_ll.S @@ -0,0 +1,49 @@ +/* + * Coherency fabric: low level functions + * + * Copyright (C) 2012 Marvell + * + * Gregory CLEMENT + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + * + * This file implements the assembly function to add a CPU to the + * coherency fabric. This function is called by each of the secondary + * CPUs during their early boot in an SMP kernel, this why this + * function have to callable from assembly. It can also be called by a + * primary CPU from C code during its boot. + */ + +#include +#define ARMADA_XP_CFB_CTL_REG_OFFSET 0x0 +#define ARMADA_XP_CFB_CFG_REG_OFFSET 0x4 + + .text +/* + * r0: Coherency fabric base register address + * r1: HW CPU id + */ +ENTRY(ll_set_cpu_coherent) + /* Create bit by cpu index */ + mov r3, #(1 << 24) + lsl r1, r3, r1 + + /* Add CPU to SMP group - Atomic */ + add r3, r0, #ARMADA_XP_CFB_CTL_REG_OFFSET + ldr r2, [r3] + orr r2, r2, r1 + str r2, [r3] + + /* Enable coherency on CPU - Atomic */ + add r3, r0, #ARMADA_XP_CFB_CFG_REG_OFFSET + ldr r2, [r3] + orr r2, r2, r1 + str r2, [r3] + + dsb + + mov r0, #0 + mov pc, lr +ENDPROC(ll_set_cpu_coherent) diff --git a/arch/arm/mach-mvebu/common.h b/arch/arm/mach-mvebu/common.h index 02f89eaa25fe..ba6b62a42f52 100644 --- a/arch/arm/mach-mvebu/common.h +++ b/arch/arm/mach-mvebu/common.h @@ -20,4 +20,5 @@ void mvebu_restart(char mode, const char *cmd); void armada_370_xp_init_irq(void); void armada_370_xp_handle_irq(struct pt_regs *regs); +int armada_370_xp_coherency_init(void); #endif -- cgit v1.2.3-59-g8ed1b From 7444dad2409afd94c08875e961ca61c5999cd606 Mon Sep 17 00:00:00 2001 From: Gregory CLEMENT Date: Thu, 2 Aug 2012 11:17:51 +0300 Subject: arm: mvebu: Add initial support for power managmement service unit The Armada 370 and Armada XP SOCs have a power management service unit which is responsible for powering down and waking up CPUs and other SOC units. This patch adds support for this unit. Signed-off-by: Yehuda Yitschak Signed-off-by: Gregory CLEMENT --- .../devicetree/bindings/arm/armada-370-xp-pmsu.txt | 20 ++++++ arch/arm/boot/dts/armada-xp.dtsi | 6 ++ arch/arm/mach-mvebu/Makefile | 2 +- arch/arm/mach-mvebu/common.h | 1 + arch/arm/mach-mvebu/pmsu.c | 75 ++++++++++++++++++++++ arch/arm/mach-mvebu/pmsu.h | 16 +++++ 6 files changed, 119 insertions(+), 1 deletion(-) create mode 100644 Documentation/devicetree/bindings/arm/armada-370-xp-pmsu.txt create mode 100644 arch/arm/mach-mvebu/pmsu.c create mode 100644 arch/arm/mach-mvebu/pmsu.h (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/arm/armada-370-xp-pmsu.txt b/Documentation/devicetree/bindings/arm/armada-370-xp-pmsu.txt new file mode 100644 index 000000000000..926b4d6aae7e --- /dev/null +++ b/Documentation/devicetree/bindings/arm/armada-370-xp-pmsu.txt @@ -0,0 +1,20 @@ +Power Management Service Unit(PMSU) +----------------------------------- +Available on Marvell SOCs: Armada 370 and Armada XP + +Required properties: + +- compatible: "marvell,armada-370-xp-pmsu" + +- reg: Should contain PMSU registers location and length. First pair + for the per-CPU SW Reset Control registers, second pair for the + Power Management Service Unit. + +Example: + +armada-370-xp-pmsu@d0022000 { + compatible = "marvell,armada-370-xp-pmsu"; + reg = <0xd0022100 0x430>, + <0xd0020800 0x20>; +}; + diff --git a/arch/arm/boot/dts/armada-xp.dtsi b/arch/arm/boot/dts/armada-xp.dtsi index f51554e80009..1f95e227053b 100644 --- a/arch/arm/boot/dts/armada-xp.dtsi +++ b/arch/arm/boot/dts/armada-xp.dtsi @@ -27,6 +27,12 @@ <0xd0021870 0x58>; }; + armada-370-xp-pmsu@d0022000 { + compatible = "marvell,armada-370-xp-pmsu"; + reg = <0xd0022100 0x430>, + <0xd0020800 0x20>; + }; + soc { serial@d0012200 { compatible = "ns16550"; diff --git a/arch/arm/mach-mvebu/Makefile b/arch/arm/mach-mvebu/Makefile index 5ce4b42c2697..2e3ec11c51e6 100644 --- a/arch/arm/mach-mvebu/Makefile +++ b/arch/arm/mach-mvebu/Makefile @@ -2,4 +2,4 @@ ccflags-$(CONFIG_ARCH_MULTIPLATFORM) := -I$(srctree)/$(src)/include \ -I$(srctree)/arch/arm/plat-orion/include obj-y += system-controller.o -obj-$(CONFIG_MACH_ARMADA_370_XP) += armada-370-xp.o irq-armada-370-xp.o addr-map.o coherency.o coherency_ll.o +obj-$(CONFIG_MACH_ARMADA_370_XP) += armada-370-xp.o irq-armada-370-xp.o addr-map.o coherency.o coherency_ll.o pmsu.o diff --git a/arch/arm/mach-mvebu/common.h b/arch/arm/mach-mvebu/common.h index ba6b62a42f52..9285d0496651 100644 --- a/arch/arm/mach-mvebu/common.h +++ b/arch/arm/mach-mvebu/common.h @@ -21,4 +21,5 @@ void armada_370_xp_init_irq(void); void armada_370_xp_handle_irq(struct pt_regs *regs); int armada_370_xp_coherency_init(void); +int armada_370_xp_pmsu_init(void); #endif diff --git a/arch/arm/mach-mvebu/pmsu.c b/arch/arm/mach-mvebu/pmsu.c new file mode 100644 index 000000000000..3cc4bef6401c --- /dev/null +++ b/arch/arm/mach-mvebu/pmsu.c @@ -0,0 +1,75 @@ +/* + * Power Management Service Unit(PMSU) support for Armada 370/XP platforms. + * + * Copyright (C) 2012 Marvell + * + * Yehuda Yitschak + * Gregory Clement + * Thomas Petazzoni + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + * + * The Armada 370 and Armada XP SOCs have a power management service + * unit which is responsible for powering down and waking up CPUs and + * other SOC units + */ + +#include +#include +#include +#include +#include +#include + +static void __iomem *pmsu_mp_base; +static void __iomem *pmsu_reset_base; + +#define PMSU_BOOT_ADDR_REDIRECT_OFFSET(cpu) ((cpu * 0x100) + 0x24) +#define PMSU_RESET_CTL_OFFSET(cpu) (cpu * 0x8) + +static struct of_device_id of_pmsu_table[] = { + {.compatible = "marvell,armada-370-xp-pmsu"}, + { /* end of list */ }, +}; + +#ifdef CONFIG_SMP +int armada_xp_boot_cpu(unsigned int cpu_id, void *boot_addr) +{ + int reg, hw_cpu; + + if (!pmsu_mp_base || !pmsu_reset_base) { + pr_warn("Can't boot CPU. PMSU is uninitialized\n"); + return 1; + } + + hw_cpu = cpu_logical_map(cpu_id); + + writel(virt_to_phys(boot_addr), pmsu_mp_base + + PMSU_BOOT_ADDR_REDIRECT_OFFSET(hw_cpu)); + + /* Release CPU from reset by clearing reset bit*/ + reg = readl(pmsu_reset_base + PMSU_RESET_CTL_OFFSET(hw_cpu)); + reg &= (~0x1); + writel(reg, pmsu_reset_base + PMSU_RESET_CTL_OFFSET(hw_cpu)); + + return 0; +} +#endif + +int __init armada_370_xp_pmsu_init(void) +{ + struct device_node *np; + + np = of_find_matching_node(NULL, of_pmsu_table); + if (np) { + pr_info("Initializing Power Management Service Unit\n"); + pmsu_mp_base = of_iomap(np, 0); + pmsu_reset_base = of_iomap(np, 1); + } + + return 0; +} + +early_initcall(armada_370_xp_pmsu_init); diff --git a/arch/arm/mach-mvebu/pmsu.h b/arch/arm/mach-mvebu/pmsu.h new file mode 100644 index 000000000000..07a737c6b95d --- /dev/null +++ b/arch/arm/mach-mvebu/pmsu.h @@ -0,0 +1,16 @@ +/* + * Power Management Service Unit (PMSU) support for Armada 370/XP platforms. + * + * Copyright (C) 2012 Marvell + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#ifndef __MACH_MVEBU_PMSU_H +#define __MACH_MVEBU_PMSU_H + +int armada_xp_boot_cpu(unsigned int cpu_id, void *phys_addr); + +#endif /* __MACH_370_XP_PMSU_H */ -- cgit v1.2.3-59-g8ed1b From 344e873e5657e8dc0631e4d1d42b69f7d625b02c Mon Sep 17 00:00:00 2001 From: Gregory CLEMENT Date: Thu, 2 Aug 2012 11:19:12 +0300 Subject: arm: mvebu: Add IPI support via doorbells This patch enhances the IRQ controller driver to add support for Inter-Processor-Interrupts that are needed to enable SMP support. Signed-off-by: Yehuda Yitschak Signed-off-by: Gregory CLEMENT --- .../devicetree/bindings/arm/armada-370-xp-mpic.txt | 12 ++- arch/arm/boot/dts/armada-xp.dtsi | 2 +- arch/arm/mach-mvebu/armada-370-xp.h | 7 ++ arch/arm/mach-mvebu/irq-armada-370-xp.c | 92 ++++++++++++++++++++-- 4 files changed, 103 insertions(+), 10 deletions(-) (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/arm/armada-370-xp-mpic.txt b/Documentation/devicetree/bindings/arm/armada-370-xp-mpic.txt index 70c0dc5f00ed..61df564c0d23 100644 --- a/Documentation/devicetree/bindings/arm/armada-370-xp-mpic.txt +++ b/Documentation/devicetree/bindings/arm/armada-370-xp-mpic.txt @@ -6,9 +6,15 @@ Required properties: - interrupt-controller: Identifies the node as an interrupt controller. - #interrupt-cells: The number of cells to define the interrupts. Should be 1. The cell is the IRQ number + - reg: Should contain PMIC registers location and length. First pair for the main interrupt registers, second pair for the per-CPU - interrupt registers + interrupt registers. For this last pair, to be compliant with SMP + support, the "virtual" must be use (For the record, these registers + automatically map to the interrupt controller registers of the + current CPU) + + Example: @@ -18,6 +24,6 @@ Example: #address-cells = <1>; #size-cells = <1>; interrupt-controller; - reg = <0xd0020000 0x1000>, - <0xd0021000 0x1000>; + reg = <0xd0020a00 0x1d0>, + <0xd0021070 0x58>; }; diff --git a/arch/arm/boot/dts/armada-xp.dtsi b/arch/arm/boot/dts/armada-xp.dtsi index 1f95e227053b..e6db2b7e2925 100644 --- a/arch/arm/boot/dts/armada-xp.dtsi +++ b/arch/arm/boot/dts/armada-xp.dtsi @@ -24,7 +24,7 @@ mpic: interrupt-controller@d0020000 { reg = <0xd0020a00 0x1d0>, - <0xd0021870 0x58>; + <0xd0021070 0x58>; }; armada-370-xp-pmsu@d0022000 { diff --git a/arch/arm/mach-mvebu/armada-370-xp.h b/arch/arm/mach-mvebu/armada-370-xp.h index aac9bebc6b03..c6a7d74fddfe 100644 --- a/arch/arm/mach-mvebu/armada-370-xp.h +++ b/arch/arm/mach-mvebu/armada-370-xp.h @@ -19,4 +19,11 @@ #define ARMADA_370_XP_REGS_VIRT_BASE IOMEM(0xfeb00000) #define ARMADA_370_XP_REGS_SIZE SZ_1M +#ifdef CONFIG_SMP +#include + +void armada_mpic_send_doorbell(const struct cpumask *mask, unsigned int irq); +void armada_xp_mpic_smp_cpu_init(void); +#endif + #endif /* __MACH_ARMADA_370_XP_H */ diff --git a/arch/arm/mach-mvebu/irq-armada-370-xp.c b/arch/arm/mach-mvebu/irq-armada-370-xp.c index 5f5f9394b6b2..549b6846f940 100644 --- a/arch/arm/mach-mvebu/irq-armada-370-xp.c +++ b/arch/arm/mach-mvebu/irq-armada-370-xp.c @@ -24,6 +24,7 @@ #include #include #include +#include /* Interrupt Controller Registers Map */ #define ARMADA_370_XP_INT_SET_MASK_OFFS (0x48) @@ -35,6 +36,12 @@ #define ARMADA_370_XP_CPU_INTACK_OFFS (0x44) +#define ARMADA_370_XP_SW_TRIG_INT_OFFS (0x4) +#define ARMADA_370_XP_IN_DRBEL_MSK_OFFS (0xc) +#define ARMADA_370_XP_IN_DRBEL_CAUSE_OFFS (0x8) + +#define ACTIVE_DOORBELLS (8) + static void __iomem *per_cpu_int_base; static void __iomem *main_int_base; static struct irq_domain *armada_370_xp_mpic_domain; @@ -51,11 +58,22 @@ static void armada_370_xp_irq_unmask(struct irq_data *d) per_cpu_int_base + ARMADA_370_XP_INT_CLEAR_MASK_OFFS); } +#ifdef CONFIG_SMP +static int armada_xp_set_affinity(struct irq_data *d, + const struct cpumask *mask_val, bool force) +{ + return 0; +} +#endif + static struct irq_chip armada_370_xp_irq_chip = { .name = "armada_370_xp_irq", .irq_mask = armada_370_xp_irq_mask, .irq_mask_ack = armada_370_xp_irq_mask, .irq_unmask = armada_370_xp_irq_unmask, +#ifdef CONFIG_SMP + .irq_set_affinity = armada_xp_set_affinity, +#endif }; static int armada_370_xp_mpic_irq_map(struct irq_domain *h, @@ -72,6 +90,41 @@ static int armada_370_xp_mpic_irq_map(struct irq_domain *h, return 0; } +#ifdef CONFIG_SMP +void armada_mpic_send_doorbell(const struct cpumask *mask, unsigned int irq) +{ + int cpu; + unsigned long map = 0; + + /* Convert our logical CPU mask into a physical one. */ + for_each_cpu(cpu, mask) + map |= 1 << cpu_logical_map(cpu); + + /* + * Ensure that stores to Normal memory are visible to the + * other CPUs before issuing the IPI. + */ + dsb(); + + /* submit softirq */ + writel((map << 8) | irq, main_int_base + + ARMADA_370_XP_SW_TRIG_INT_OFFS); +} + +void armada_xp_mpic_smp_cpu_init(void) +{ + /* Clear pending IPIs */ + writel(0, per_cpu_int_base + ARMADA_370_XP_IN_DRBEL_CAUSE_OFFS); + + /* Enable first 8 IPIs */ + writel((1 << ACTIVE_DOORBELLS) - 1, per_cpu_int_base + + ARMADA_370_XP_IN_DRBEL_MSK_OFFS); + + /* Unmask IPI interrupt */ + writel(0, per_cpu_int_base + ARMADA_370_XP_INT_CLEAR_MASK_OFFS); +} +#endif /* CONFIG_SMP */ + static struct irq_domain_ops armada_370_xp_mpic_irq_ops = { .map = armada_370_xp_mpic_irq_map, .xlate = irq_domain_xlate_onecell, @@ -91,13 +144,18 @@ static int __init armada_370_xp_mpic_of_init(struct device_node *node, control = readl(main_int_base + ARMADA_370_XP_INT_CONTROL); armada_370_xp_mpic_domain = - irq_domain_add_linear(node, (control >> 2) & 0x3ff, - &armada_370_xp_mpic_irq_ops, NULL); + irq_domain_add_linear(node, (control >> 2) & 0x3ff, + &armada_370_xp_mpic_irq_ops, NULL); if (!armada_370_xp_mpic_domain) panic("Unable to add Armada_370_Xp MPIC irq domain (DT)\n"); irq_set_default_host(armada_370_xp_mpic_domain); + +#ifdef CONFIG_SMP + armada_xp_mpic_smp_cpu_init(); +#endif + return 0; } @@ -111,14 +169,36 @@ asmlinkage void __exception_irq_entry armada_370_xp_handle_irq(struct pt_regs ARMADA_370_XP_CPU_INTACK_OFFS); irqnr = irqstat & 0x3FF; - if (irqnr < 1023) { - irqnr = - irq_find_mapping(armada_370_xp_mpic_domain, irqnr); + if (irqnr > 1022) + break; + + if (irqnr >= 8) { + irqnr = irq_find_mapping(armada_370_xp_mpic_domain, + irqnr); handle_IRQ(irqnr, regs); continue; } +#ifdef CONFIG_SMP + /* IPI Handling */ + if (irqnr == 0) { + u32 ipimask, ipinr; + + ipimask = readl_relaxed(per_cpu_int_base + + ARMADA_370_XP_IN_DRBEL_CAUSE_OFFS) + & 0xFF; + + writel(0x0, per_cpu_int_base + + ARMADA_370_XP_IN_DRBEL_CAUSE_OFFS); + + /* Handle all pending doorbells */ + for (ipinr = 0; ipinr < ACTIVE_DOORBELLS; ipinr++) { + if (ipimask & (0x1 << ipinr)) + handle_IPI(ipinr, regs); + } + continue; + } +#endif - break; } while (1); } -- cgit v1.2.3-59-g8ed1b From 07c121149cb30405b37ceae3cd29e5b6b6e65cc0 Mon Sep 17 00:00:00 2001 From: Marek Belisko Date: Thu, 15 Nov 2012 22:51:56 +0100 Subject: Documentation: Move common leds properties description to separate file. There are several drivers that use LEDs and depend on exactly the same device tree binding. However, the binding documentation has simply been cut-and-paste into each of the binding documents. Rather than continue to duplicate it, this patch adds a common led binding document that all can reference. Signed-off-by: Marek Belisko Reviewed-by: Stephen Warren Signed-off-by: Grant Likely --- Documentation/devicetree/bindings/gpio/led.txt | 14 ++++---------- Documentation/devicetree/bindings/leds/common.txt | 23 +++++++++++++++++++++++ 2 files changed, 27 insertions(+), 10 deletions(-) create mode 100644 Documentation/devicetree/bindings/leds/common.txt (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/gpio/led.txt b/Documentation/devicetree/bindings/gpio/led.txt index edc83c1c0d54..df1b3080f6b8 100644 --- a/Documentation/devicetree/bindings/gpio/led.txt +++ b/Documentation/devicetree/bindings/gpio/led.txt @@ -10,16 +10,10 @@ LED sub-node properties: - gpios : Should specify the LED's GPIO, see "gpios property" in Documentation/devicetree/bindings/gpio/gpio.txt. Active low LEDs should be indicated using flags in the GPIO specifier. -- label : (optional) The label for this LED. If omitted, the label is - taken from the node name (excluding the unit address). -- linux,default-trigger : (optional) This parameter, if present, is a - string defining the trigger assigned to the LED. Current triggers are: - "backlight" - LED will act as a back-light, controlled by the framebuffer - system - "default-on" - LED will turn on, but see "default-state" below - "heartbeat" - LED "double" flashes at a load average based rate - "ide-disk" - LED indicates disk activity - "timer" - LED flashes at a fixed, configurable rate +- label : (optional) + see Documentation/devicetree/bindings/leds/common.txt +- linux,default-trigger : (optional) + see Documentation/devicetree/bindings/leds/common.txt - default-state: (optional) The initial state of the LED. Valid values are "on", "off", and "keep". If the LED is already on or off and the default-state property is set the to same value, then no diff --git a/Documentation/devicetree/bindings/leds/common.txt b/Documentation/devicetree/bindings/leds/common.txt new file mode 100644 index 000000000000..2d88816dd550 --- /dev/null +++ b/Documentation/devicetree/bindings/leds/common.txt @@ -0,0 +1,23 @@ +Common leds properties. + +Optional properties for child nodes: +- label : The label for this LED. If omitted, the label is + taken from the node name (excluding the unit address). + +- linux,default-trigger : This parameter, if present, is a + string defining the trigger assigned to the LED. Current triggers are: + "backlight" - LED will act as a back-light, controlled by the framebuffer + system + "default-on" - LED will turn on (but for leds-gpio see "default-state" + property in Documentation/devicetree/bindings/gpio/led.txt) + "heartbeat" - LED "double" flashes at a load average based rate + "ide-disk" - LED indicates disk activity + "timer" - LED flashes at a fixed, configurable rate + +Examples: + +system-status { + label = "Status"; + linux,default-trigger = "heartbeat"; + ... +}; -- cgit v1.2.3-59-g8ed1b From 155dd0c2f7c24c1531a90229f1c749cebf3f21f7 Mon Sep 17 00:00:00 2001 From: "Chaiken, Alison" Date: Thu, 15 Nov 2012 23:32:51 +0000 Subject: Documentation: correct of_platform_populate() argument list The documentation doesn't match the actual function prototype. This is a trivial patch to fix it. Signed-off-by: Alison Chaiken Acked-by: Arnd Bergmann Signed-off-by: Grant Likely --- Documentation/devicetree/usage-model.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Documentation') diff --git a/Documentation/devicetree/usage-model.txt b/Documentation/devicetree/usage-model.txt index dca90fe22a90..ef9d06c9f8fd 100644 --- a/Documentation/devicetree/usage-model.txt +++ b/Documentation/devicetree/usage-model.txt @@ -347,7 +347,7 @@ later), which will happily live at the base of the Linux /sys/devices tree. Therefore, if a DT node is at the root of the tree, then it really probably is best registered as a platform_device. -Linux board support code calls of_platform_populate(NULL, NULL, NULL) +Linux board support code calls of_platform_populate(NULL, NULL, NULL, NULL) to kick off discovery of devices at the root of the tree. The parameters are all NULL because when starting from the root of the tree, there is no need to provide a starting node (the first NULL), a -- cgit v1.2.3-59-g8ed1b From e60304f8cb7bb545e79fe62d9b9762460c254ec2 Mon Sep 17 00:00:00 2001 From: Gregory CLEMENT Date: Fri, 12 Oct 2012 19:20:36 +0200 Subject: arm: mvebu: Add hardware I/O Coherency support Armada 370 and XP come with an unit called coherency fabric. This unit allows to use the Armada 370/XP as a nearly coherent architecture. The coherency mechanism uses snoop filters to ensure the coherency between caches, DRAM and devices. This mechanism needs a synchronization barrier which guarantees that all the memory writes initiated by the devices have reached their target and do not reside in intermediate write buffers. That's why the architecture is not totally coherent and we need to provide our own functions for some DMA operations. Beside the use of the coherency fabric, the device units will have to set the attribute flag of the decoding address window to select the accurate coherency process for the memory transaction. This is done each device driver programs the DRAM address windows. The value of the attribute set by the driver is retrieved through the orion_addr_map_cfg struct filled during the early initialization of the platform. Signed-off-by: Gregory CLEMENT Reviewed-by: Yehuda Yitschak Acked-by: Marek Szyprowski --- .../devicetree/bindings/arm/coherency-fabric.txt | 9 ++- arch/arm/boot/dts/armada-370-xp.dtsi | 3 +- arch/arm/mach-mvebu/addr-map.c | 3 + arch/arm/mach-mvebu/coherency.c | 73 ++++++++++++++++++++++ 4 files changed, 85 insertions(+), 3 deletions(-) (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/arm/coherency-fabric.txt b/Documentation/devicetree/bindings/arm/coherency-fabric.txt index 2bfbf67dd77e..17d8cd107559 100644 --- a/Documentation/devicetree/bindings/arm/coherency-fabric.txt +++ b/Documentation/devicetree/bindings/arm/coherency-fabric.txt @@ -5,12 +5,17 @@ Available on Marvell SOCs: Armada 370 and Armada XP Required properties: - compatible: "marvell,coherency-fabric" -- reg: Should contain,coherency fabric registers location and length. + +- reg: Should contain coherency fabric registers location and + length. First pair for the coherency fabric registers, second pair + for the per-CPU fabric registers registers. Example: coherency-fabric@d0020200 { compatible = "marvell,coherency-fabric"; - reg = <0xd0020200 0xb0>; + reg = <0xd0020200 0xb0>, + <0xd0021810 0x1c>; + }; diff --git a/arch/arm/boot/dts/armada-370-xp.dtsi b/arch/arm/boot/dts/armada-370-xp.dtsi index b0d075b50f29..98a6b26a7dc8 100644 --- a/arch/arm/boot/dts/armada-370-xp.dtsi +++ b/arch/arm/boot/dts/armada-370-xp.dtsi @@ -38,7 +38,8 @@ coherency-fabric@d0020200 { compatible = "marvell,coherency-fabric"; - reg = <0xd0020200 0xb0>; + reg = <0xd0020200 0xb0>, + <0xd0021810 0x1c>; }; soc { diff --git a/arch/arm/mach-mvebu/addr-map.c b/arch/arm/mach-mvebu/addr-map.c index fe454a4430be..595f6b722a8f 100644 --- a/arch/arm/mach-mvebu/addr-map.c +++ b/arch/arm/mach-mvebu/addr-map.c @@ -108,6 +108,9 @@ static int __init armada_setup_cpu_mbus(void) addr_map_cfg.bridge_virt_base = mbus_unit_addr_decoding_base; + if (of_find_compatible_node(NULL, NULL, "marvell,coherency-fabric")) + addr_map_cfg.hw_io_coherency = 1; + /* * Disable, clear and configure windows. */ diff --git a/arch/arm/mach-mvebu/coherency.c b/arch/arm/mach-mvebu/coherency.c index 596ee66a9cc4..8278960066c3 100644 --- a/arch/arm/mach-mvebu/coherency.c +++ b/arch/arm/mach-mvebu/coherency.c @@ -22,6 +22,8 @@ #include #include #include +#include +#include #include #include "armada-370-xp.h" @@ -33,10 +35,13 @@ * value matching its virtual mapping */ static void __iomem *coherency_base = ARMADA_370_XP_REGS_VIRT_BASE + 0x20200; +static void __iomem *coherency_cpu_base; /* Coherency fabric registers */ #define COHERENCY_FABRIC_CFG_OFFSET 0x4 +#define IO_SYNC_BARRIER_CTL_OFFSET 0x0 + static struct of_device_id of_coherency_table[] = { {.compatible = "marvell,coherency-fabric"}, { /* end of list */ }, @@ -68,6 +73,70 @@ int set_cpu_coherent(unsigned int hw_cpu_id, int smp_group_id) return ll_set_cpu_coherent(coherency_base, hw_cpu_id); } +static inline void mvebu_hwcc_sync_io_barrier(void) +{ + writel(0x1, coherency_cpu_base + IO_SYNC_BARRIER_CTL_OFFSET); + while (readl(coherency_cpu_base + IO_SYNC_BARRIER_CTL_OFFSET) & 0x1); +} + +static dma_addr_t mvebu_hwcc_dma_map_page(struct device *dev, struct page *page, + unsigned long offset, size_t size, + enum dma_data_direction dir, + struct dma_attrs *attrs) +{ + if (dir != DMA_TO_DEVICE) + mvebu_hwcc_sync_io_barrier(); + return pfn_to_dma(dev, page_to_pfn(page)) + offset; +} + + +static void mvebu_hwcc_dma_unmap_page(struct device *dev, dma_addr_t dma_handle, + size_t size, enum dma_data_direction dir, + struct dma_attrs *attrs) +{ + if (dir != DMA_TO_DEVICE) + mvebu_hwcc_sync_io_barrier(); +} + +static void mvebu_hwcc_dma_sync(struct device *dev, dma_addr_t dma_handle, + size_t size, enum dma_data_direction dir) +{ + if (dir != DMA_TO_DEVICE) + mvebu_hwcc_sync_io_barrier(); +} + +static struct dma_map_ops mvebu_hwcc_dma_ops = { + .alloc = arm_dma_alloc, + .free = arm_dma_free, + .mmap = arm_dma_mmap, + .map_page = mvebu_hwcc_dma_map_page, + .unmap_page = mvebu_hwcc_dma_unmap_page, + .get_sgtable = arm_dma_get_sgtable, + .map_sg = arm_dma_map_sg, + .unmap_sg = arm_dma_unmap_sg, + .sync_single_for_cpu = mvebu_hwcc_dma_sync, + .sync_single_for_device = mvebu_hwcc_dma_sync, + .sync_sg_for_cpu = arm_dma_sync_sg_for_cpu, + .sync_sg_for_device = arm_dma_sync_sg_for_device, + .set_dma_mask = arm_dma_set_mask, +}; + +static int mvebu_hwcc_platform_notifier(struct notifier_block *nb, + unsigned long event, void *__dev) +{ + struct device *dev = __dev; + + if (event != BUS_NOTIFY_ADD_DEVICE) + return NOTIFY_DONE; + set_dma_ops(dev, &mvebu_hwcc_dma_ops); + + return NOTIFY_OK; +} + +static struct notifier_block mvebu_hwcc_platform_nb = { + .notifier_call = mvebu_hwcc_platform_notifier, +}; + int __init coherency_init(void) { struct device_node *np; @@ -76,6 +145,10 @@ int __init coherency_init(void) if (np) { pr_info("Initializing Coherency fabric\n"); coherency_base = of_iomap(np, 0); + coherency_cpu_base = of_iomap(np, 1); + set_cpu_coherent(cpu_logical_map(smp_processor_id()), 0); + bus_register_notifier(&platform_bus_type, + &mvebu_hwcc_platform_nb); } return 0; -- cgit v1.2.3-59-g8ed1b From 72052fcc10263817031dd96ea438c1f49eb39dd3 Mon Sep 17 00:00:00 2001 From: Simon Guinot Date: Wed, 17 Oct 2012 12:09:03 +0200 Subject: leds: leds-ns2: add device tree binding Signed-off-by: Simon Guinot Reviewed-by: Andrew Lunn Signed-off-by: Jason Cooper --- .../devicetree/bindings/gpio/leds-ns2.txt | 26 ++++++++ drivers/leds/leds-ns2.c | 78 +++++++++++++++++++++- 2 files changed, 101 insertions(+), 3 deletions(-) create mode 100644 Documentation/devicetree/bindings/gpio/leds-ns2.txt (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/gpio/leds-ns2.txt b/Documentation/devicetree/bindings/gpio/leds-ns2.txt new file mode 100644 index 000000000000..aef3aca34d2d --- /dev/null +++ b/Documentation/devicetree/bindings/gpio/leds-ns2.txt @@ -0,0 +1,26 @@ +Binding for dual-GPIO LED found on Network Space v2 (and parents). + +Required properties: +- compatible: "lacie,ns2-leds". + +Each LED is represented as a sub-node of the ns2-leds device. + +Required sub-node properties: +- cmd-gpio: Command LED GPIO. See OF device-tree GPIO specification. +- slow-gpio: Slow LED GPIO. See OF device-tree GPIO specification. + +Optional sub-node properties: +- label: Name for this LED. If omitted, the label is taken from the node name. +- linux,default-trigger: Trigger assigned to the LED. + +Example: + +ns2-leds { + compatible = "lacie,ns2-leds"; + + blue-sata { + label = "ns2:blue:sata"; + slow-gpio = <&gpio0 29 0>; + cmd-gpio = <&gpio0 30 0>; + }; +}; diff --git a/drivers/leds/leds-ns2.c b/drivers/leds/leds-ns2.c index d176ec83f5d9..d64cc2227fd9 100644 --- a/drivers/leds/leds-ns2.c +++ b/drivers/leds/leds-ns2.c @@ -30,6 +30,7 @@ #include #include #include +#include /* * The Network Space v2 dual-GPIO LED is wired to a CPLD and can blink in @@ -263,6 +264,62 @@ static void delete_ns2_led(struct ns2_led_data *led_dat) gpio_free(led_dat->slow); } +#ifdef CONFIG_OF_GPIO +/* + * Translate OpenFirmware node properties into platform_data. + */ +static int __devinit +ns2_leds_get_of_pdata(struct device *dev, struct ns2_led_platform_data *pdata) +{ + struct device_node *np = dev->of_node; + struct device_node *child; + struct ns2_led *leds; + int num_leds = 0; + int i = 0; + + num_leds = of_get_child_count(np); + if (!num_leds) + return -ENODEV; + + leds = devm_kzalloc(dev, num_leds * sizeof(struct ns2_led), + GFP_KERNEL); + if (!leds) + return -ENOMEM; + + for_each_child_of_node(np, child) { + const char *string; + int ret; + + ret = of_get_named_gpio(child, "cmd-gpio", 0); + if (ret < 0) + return ret; + leds[i].cmd = ret; + ret = of_get_named_gpio(child, "slow-gpio", 0); + if (ret < 0) + return ret; + leds[i].slow = ret; + ret = of_property_read_string(child, "label", &string); + leds[i].name = (ret == 0) ? string : child->name; + ret = of_property_read_string(child, "linux,default-trigger", + &string); + if (ret == 0) + leds[i].default_trigger = string; + + i++; + } + + pdata->leds = leds; + pdata->num_leds = num_leds; + + return 0; +} + +static const struct of_device_id of_ns2_leds_match[] = { + { .compatible = "lacie,ns2-leds", }, + {}, +}; +#endif /* CONFIG_OF_GPIO */ + static int __devinit ns2_led_probe(struct platform_device *pdev) { struct ns2_led_platform_data *pdata = pdev->dev.platform_data; @@ -270,11 +327,25 @@ static int __devinit ns2_led_probe(struct platform_device *pdev) int i; int ret; +#ifdef CONFIG_OF_GPIO + if (!pdata) { + pdata = devm_kzalloc(&pdev->dev, + sizeof(struct ns2_led_platform_data), + GFP_KERNEL); + if (!pdata) + return -ENOMEM; + + ret = ns2_leds_get_of_pdata(&pdev->dev, pdata); + if (ret) + return ret; + } +#else if (!pdata) return -EINVAL; +#endif /* CONFIG_OF_GPIO */ leds_data = devm_kzalloc(&pdev->dev, sizeof(struct ns2_led_data) * - pdata->num_leds, GFP_KERNEL); + pdata->num_leds, GFP_KERNEL); if (!leds_data) return -ENOMEM; @@ -312,8 +383,9 @@ static struct platform_driver ns2_led_driver = { .probe = ns2_led_probe, .remove = __devexit_p(ns2_led_remove), .driver = { - .name = "leds-ns2", - .owner = THIS_MODULE, + .name = "leds-ns2", + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(of_ns2_leds_match), }, }; -- cgit v1.2.3-59-g8ed1b From d3cec81fc929b6edc43bd42725929685359adef7 Mon Sep 17 00:00:00 2001 From: Tim Gardner Date: Wed, 21 Nov 2012 12:53:43 -0800 Subject: firmware: remove last vestiges of dabusb dabusb was removed with commit dae86ccbc3 ("[media] dabusb: remove obsolete driver"), so remove the last vestiges of firmware and documentation. Signed-off-by: Tim Gardner Cc: Rob Landley Cc: Paul Gortmaker Cc: Ben Hutchings Signed-off-by: Andrew Morton Signed-off-by: Greg Kroah-Hartman --- Documentation/devices.txt | 3 - firmware/Makefile | 1 - firmware/dabusb/bitstream.bin.ihex | 761 ------------------------------------- firmware/dabusb/firmware.HEX | 649 ------------------------------- 4 files changed, 1414 deletions(-) delete mode 100644 firmware/dabusb/bitstream.bin.ihex delete mode 100644 firmware/dabusb/firmware.HEX (limited to 'Documentation') diff --git a/Documentation/devices.txt b/Documentation/devices.txt index b6251cca9263..08f01e79c41a 100644 --- a/Documentation/devices.txt +++ b/Documentation/devices.txt @@ -2561,9 +2561,6 @@ Your cooperation is appreciated. 192 = /dev/usb/yurex1 First USB Yurex device ... 209 = /dev/usb/yurex16 16th USB Yurex device - 240 = /dev/usb/dabusb0 First daubusb device - ... - 243 = /dev/usb/dabusb3 Fourth dabusb device 180 block USB block devices 0 = /dev/uba First USB block device diff --git a/firmware/Makefile b/firmware/Makefile index eeb14030d8a2..cbb09ce9730a 100644 --- a/firmware/Makefile +++ b/firmware/Makefile @@ -97,7 +97,6 @@ fw-shipped-$(CONFIG_TEHUTI) += tehuti/bdx.bin fw-shipped-$(CONFIG_TIGON3) += tigon/tg3.bin tigon/tg3_tso.bin \ tigon/tg3_tso5.bin fw-shipped-$(CONFIG_TYPHOON) += 3com/typhoon.bin -fw-shipped-$(CONFIG_USB_DABUSB) += dabusb/firmware.fw dabusb/bitstream.bin fw-shipped-$(CONFIG_USB_EMI26) += emi26/loader.fw emi26/firmware.fw \ emi26/bitstream.fw fw-shipped-$(CONFIG_USB_EMI62) += emi62/loader.fw emi62/bitstream.fw \ diff --git a/firmware/dabusb/bitstream.bin.ihex b/firmware/dabusb/bitstream.bin.ihex deleted file mode 100644 index 5021a4b1e63f..000000000000 --- a/firmware/dabusb/bitstream.bin.ihex +++ /dev/null @@ -1,761 +0,0 @@ -:1000000000090FF00FF00FF00FF000000161000D7C -:1000100064616275736274722E6E63640062000BB9 -:10002000733130786C76713130300063000B3139C8 -:1000300039392F30392F32340064000931303A34E5 -:10004000323A3436006500002EC0FF20175F9F5BF8 -:10005000FEFBBBB7BBBBFBBFAFEFFBDFB7FBFB7F61 -:10006000BFB7EFF2FFFBFEFFFFEFFFFEFFBFFFFF9B -:10007000FFFFAFFFFAFFFFFFC9FFFFFFDFFFFFFF3B -:10008000FFFFFFFFFFFFFFFFFFFFFFFBFFA3FFFBE4 -:10009000FEFFBFEFE3FEFFBFE3FEFFBF6FFBF6FF18 -:1000A000BFFF47FFFF9FEEF9FECF9FEFFBCF9BEE19 -:1000B000F8FEEF8FEEFBFE0BFFFFFFFFFFFFFFFFE2 -:1000C000FFFFBFFFFFFBFFFFBFFFFFFC17FFFFFFAF -:1000D000FFFFFF7FFFFFFF7FFFFFFBFFFF7FFFFFB4 -:1000E000FC3FFFFFFFFFFFFFFFFFFFFBFFFFFFFFE7 -:1000F000FFFFFFFFFFFE5FFFFFFDFFFFDBFFFDFFD9 -:1001000077FFFDFFFFDFFEFDFFFFF2FFFFFFFFFFB9 -:10011000FFFDFFFFFFFDFFFFFFFFFFFFFFFFFFE111 -:100120007FFFFFFFFFFFFFFFFFFFFFFFFF3FFFFF1F -:10013000FFFFFFFFE3FFFFFFFFFFFFFFFFFFFFBF2B -:10014000FFFEFFFFFFFFFFFFFF67FFFFFFFFFFFF58 -:100150007FFFFFFF7FFFFFFFFFFFDFFFFFFF2FFF9F -:10016000F3FDFF7FDEF7FDFF7FF77DFF7FDFF7BD4C -:10017000FF7FFF1FFFEFFBFEFFBFEFFBFEFFEFFB6D -:10018000FEFFBFEFFBFEFFFF3FFE7F9FE7F9FE7F15 -:100190009FE7FA7F9FE7F9FE7F9FE7FFFC7FBFBFE6 -:1001A000EFFBFEFFBFEFFBB7BFEFFBFEFFBFEFFBB9 -:1001B000FFE0FDF9FE7F9FE7F9FE7F9DF9FE7D9D43 -:1001C000E7F9FE7F9FEDEDFFFDFF7FDFF7FDFF7F8E -:1001D000DFFDFF7FDFF7FDFF7FDFFF9BFFEFFBFE14 -:1001E000FBBFEFBBFEFFAFBBBEFFBFEFFBFEFFFFE2 -:1001F000B7BFDBF6BDBF6BDBF6F9BF5BD6F9BF6FF0 -:10020000DBF6FDBFFF0EFFFFFFFF5FFFF7FFFF7F86 -:10021000F7BDFFFFFFFFFFFFFFDF9FFFFFFFFEFFB9 -:10022000FFEFFEFEFFFF77FFFBFBFFFFFFFFF83F47 -:10023000FFFDFFFFFFFDFFFFFFFFFFFFFFFFFFFFD2 -:10024000FFFFFFF47FFFFEFDBEFFDFFEFFFFEF7F3E -:10025000FFCFFFCFFFFFFFDFE6FFFF7FDFF7DD7F91 -:100260007FDFF7FF7FDFD7FDFF7FDFF7FFCDFFF2F7 -:10027000FFFF4F7FF4FFFFFFE7EFFFFFFFFFFFFFF1 -:10028000FFFFBBFFEFFFFEFFFFFFEFFFFFEFFFFBF7 -:10029000FFFFFFFFFFFFFF65EFFFFF7FFFFDEFFFAA -:1002A000FFFFFEFFFFFFFFFFFFFFFFFECFDFFEFFB1 -:1002B000FFFBFFFFFFFFF3FFFFFFFFFFFFFFFFFF5E -:1002C000FEDFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5F -:1002D000FFFFFFFFFFFEBFFFFFFFE37FFFFFFFFF0B -:1002E000FFFFEFEBFFFEBFFFEBFFFC7FFFFFFFEE2B -:1002F000FFFFFFFFFFFFDDFFD6FFFDBFFFFBFFFEA0 -:10030000FDFFFFFDEFFFFFFFFFFFFFDEFFFFFFFF32 -:10031000FFFFBFFFFDFF7FBFFF5FDFFFFFBF77FF77 -:10032000FFFF7FD7FFFFFFFFFFC3FFFFFFFFDFEFF1 -:10033000FFFFFEFBFFFFDFBFFFFFFFFFEDFFB7FF8C -:10034000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBD -:10035000FFFFFFAF7FFFFFFFFFFFFFFFFFFFFFFF7D -:10036000FFFFFFFFFFFFFFFFDFBFDFF3FDFBFF5BD3 -:10037000FDFFBFEFF7FFFF7DFFFFFFFFF83BFFBF74 -:100380006FFFFEFFBFFFEB7DFFEFFBFEFFFFFFFFF9 -:10039000FFF27FFCFF3FDFEDFEFFFFFFFFEF5FF7A8 -:1003A000B5FFEFFFFFFFE03F9F9EFFFFEFFFDFFF87 -:1003B000BF5FBFCFF3FFFFFFFFFFFF69AF33FDFF5D -:1003C000FBFFFFFFFFFCFF7FD9FFDFFFFFFFFFF514 -:1003D000A3DF6EDEFFFFBDFFFFFEFFFFFFFEE7FDB9 -:1003E000FFFFFFF9EFC6FEB7ADE5F9FFFFFFCFFF57 -:1003F000FFFFCDFB7FFFFFFFF9F60FDFECCF7FFFA5 -:10040000FB7FFFFFFFFDFFFEF9FD7FFF7FFFF95B35 -:10041000FF73DCFD7BDFFFFFFF7BFFFFF753D6FFA2 -:10042000FFFFFFD89FFEFFEF7FEEFFFFFFFBEDED2D -:10043000FDFFFEFFFFFB7FFFE27FFF6FD857F7FF57 -:10044000FFFFDFFFE8FFFFFDFFFFFC7FFFE4FFFB97 -:10045000EFFBFEDFB7EDFFFEDF7FFFFE7FB7FFFFA5 -:10046000FFFF89FFFFCFF3FE7FFFEFFFFE7E7FFBE5 -:10047000FFFFFFFFFFFFFFF1FFEB7AD5BF6FDBBE92 -:10048000FDB7D8F6E5BF6FFBFEF5BD7E06FFDFF7D3 -:10049000FBF6FF3FFFDBFFFF6FFBF7FFFFFFFBFEFE -:1004A000F7AFFFB7EDEFF7FEFFFFDFFFFEFFEFFF58 -:1004B000FFFFFFBFF7FC1FEEFBFEBDFF7F5FD7FD19 -:1004C000FB43FFFFFDFF5FFFF7FFF93FFFCFF3FDAA -:1004D000F77EEFA7F9FE8FA7E9F37E9FFBF8FFFFFA -:1004E0003FFD7F5FDFFDFFFF5FFFFD5FFFFF7FFDE4 -:1004F0007FFD9FFFE0FFFAF8BE6F9FE6F8BE3F9AD0 -:10050000F9BE6F9FE2F9FE6F9FF9FFF5FD7FCFDF28 -:10051000FDFD7FFFF5FFFFFFF7F5FD0FDBFFD3FFCD -:10052000EBFAFFFFBFFFFAFFFFCBFBFEFFFFEBFA8B -:10053000FEFFFFB7FFFFFFFFBFFFDFF5FFFFD7FFA6 -:10054000FFFFDFD7F5FF7FFE4FFFFDFF7F7FFFAD92 -:10055000EBFBFFADFFFFFFFFAFEBFBFFFC0DFFFF72 -:10056000DFD2FDFFFFFDF6FFFF7FFFFF1FFFFFFF55 -:10057000FFFB3F7DEB32FEBF2FEBFAAEBDE0FA7E14 -:10058000BFADEBFAFEBFF57FFFDEFEE3FBFFFFFF33 -:10059000DFEF4FDFFF7FDFFFF7FFFFF87FFFFFEFAA -:1005A000FBFFFFFFEFFFFFDFEDFBDFFFBFFFFFFF05 -:1005B00081FFFFFFFF3FFFFFFFFFFEDDFEEFFDFFBF -:1005C000FFFBFEF7FF93FDFB7EFFFE87E9FF7FB396 -:1005D0009FFEFEFFAFFDFE7E3FFE67FFFFF7FFFFC2 -:1005E000FCF7DFFDFF7FFFFF7F6DFFFFFEFFFF2FAB -:1005F000FFBFFFFFEEFFBEFFFFFEFFEFFFFFFEFFAF -:10060000EFFFFFFA5FFFFFFBFFFFEFFFFBFEFDFFCA -:10061000FEFFFBFFFFFF7FFFFEBFDFFFFBFFFFF7DC -:10062000FCFDFFFFFFFFFF7FFFFFFFFFFFF27FFFEC -:10063000FFFFFF7FFFFFFFFFF3FFFFFFEFFBFFFF6A -:10064000FFDFE2FFFFFBFFFFFFFFFFFFFBE7FFFD19 -:10065000FFFFFFBFFFFFFFEDEFFDFFFFDFD7F5FD62 -:100660007F5DFDFF7FDF97F4FD7B5FFFC9FFFBFE32 -:10067000FFBFFF5FFFFFF7FFEFFDFFEFFFFFFFFF94 -:10068000FFF7FFD7FD7D7FFFFFFFFFEFDFF7FDFFE8 -:10069000BBFFFF7FFFFEE3FFF9FE7FBFEFFBFEFF27 -:1006A000BFF9FEFF9FEFF9FEFFBFF3DAFF37CDF38F -:1006B0007CDF37CDF37F37CDF37CDF37CCF37F5A48 -:1006C000BDF6FDBF6FDBF6FDBF6FDEFDBF6FDBF676 -:1006D000FDBF6FFEF16FEB7ADEB7ADEB7ADEB7AF41 -:1006E0007ADEB7ADEB7ADEB7FF7EFFFECDB36CDB13 -:1006F00036CDB36CDECDB36CDB36CDB36CDFC9BFAA -:10070000F7BDEF7A9EA7A9EA7AB7BDEA7BDEA7BD5F -:10071000CA728D91FFEFFBFEFFBFEFFBFEF7EFFB11 -:10072000FEFFBFEFFBFEFFFE87FFF6FDBF6FDBF6B0 -:10073000FDBF6FF6FDBF6FDBF6FDBF6FFE4FFFBF66 -:10074000EFBBEEFBBEEFBBEFBEEFBBEEFBBEEFBB06 -:10075000EFFC5FFFFFFF3FCFF3FCFF3FCFFCFF3F0E -:10076000CFF3FCFF3FCFFD9FFEBFAFEBFAFEBFAF65 -:10077000EBFEBFAFEBFAFEBFAFEBFFE16FFDFF7F1C -:10078000DFF7FDFF7FDFFDFF7FDFF7FDFF7FDFFF8F -:100790007ABFFBFEDFB7EDFB7EDFB7FB7EDFB7ED99 -:1007A000FB7EDFB7FFC9FFFFBFEFFBFEFFBFEFFB25 -:1007B000FFBFEFFBFEFFBFEEFBFEBBFFFEFFBFEF89 -:1007C000FBFEFFBFEFFEFFBFEFFBFEFF3FCFFFE7EC -:1007D000FEFFF5FD775DD735DD77D7F5CD7B5DD7AE -:1007E000F5DD77FE27FFFF8BE2F8BE2F8BE2F9AF36 -:1007F0008BE2F8BE2F8BE2F9FE1FFF5FD7F5FD7F7E -:100800005FD7F5FF5FD7F5FD7F5FD7F5FFFA3FFEB6 -:10081000BFAFEBFAFEBFAFEBECBFAFEBFAFEBFAF83 -:10082000EBFFFE7FFD7FFFFFFFFFFFFFFFFFFFFFEF -:10083000FFFFFFFFFFFFFFE6FFFADFF7FDFF7FDFB0 -:10084000F7FCFFDFF7FDFF7FDFF7FDFFF5FFFFFFA1 -:10085000FFFFFFFFFFFFFBFFFFFFFFFFFFFFFFFFAC -:10086000FF02FFFEBFABEBFABEBF23EBDE1FAFEA1A -:10087000FAFEAFAFEBFD97FFF3FC7B1FCFF1FC7FE0 -:100880001FF1FC771FCDF1FCFF1FFE87FFAFEFFAD2 -:10089000FEFFAFEFFAFDBF2BFB7EBFBFEBFBFBFB09 -:1008A000DFFFFBF7FFFF7FF7F7FFFDDFFEFCDFFF5A -:1008B000DFFFFDFFDABFFFBBEFFBF9FFBEEFFBFB86 -:1008C000BFEFFBFEFFBFEFFBFFF77FFDD7FFFF7F13 -:1008D000FFFFFFFEF7FFFEFFF7FFFF7FFFFFECFFCD -:1008E000FFFEDFBFFFFBFEFFBB68AE1FAEFBFBFFE3 -:1008F000FFBFFFD5FF7FFFFFF7FEFEFFBFEF9FFDAE -:100900007FFFCBFFFFDFFFFFBBF7BFFFFFFFFFDF77 -:10091000FFBFFBFFFFFFDE3FFFFFFFFFFFA7FFFF64 -:10092000FFFFEFFF7FFBFDFB7FFFFFFFFFCFF37CB0 -:10093000FF7F8D7FFFFFFFFFFBFFF7FBFEFDFFFF4C -:10094000FFFFF7FDFF7FFD1FFDFFFFFFFFBFDFFF85 -:10095000FFFE5CFF6DFF7FABE7F1FFFD9FFFFFAD8B -:10096000EB7A3F1FFFFFFEBFAFF3DEF5FF8FFBDF2C -:10097000E67FFFDFF3FDFF7EFFFFFFFFFFFDF7F3E5 -:100980007FDFF7EFFFF63F9FDFFFFFEEFFFFEFFB9D -:10099000FFFFF9FBFE4FBFEFBBFF69AFAFFCFF3FAF -:1009A000DDFFFCBF8FFFFDF3BFED9EFCBF6FF5D3F6 -:1009B000DFFFDBD6F5EFFDFEFFB9FF1FD2A9AFFFCA -:1009C000DBF7BFEF46FFFFADEB7ADFEFF7FF7FF717 -:1009D0009FEDFF7FFFADEB7FF56FFFFDFBD6F4F7DB -:1009E000FBF97E7FFF5FC2FEBFFDFB33DFF95BFFDC -:1009F000FFDD677DCFEFDBECFF77DDF7FDFFFFDE8F -:100A0000A7BFD49FFFFFBFEFFEFFDFEFBBFFFFEFEE -:100A1000EBFAFFEFBDFBFFE27FFFDFDFF7FDBFBBC0 -:100A200073F7FD7FDFDEF7BFEADBF6FFD6FFFF6679 -:100A3000FFBEFFBF6BD9F6DFFFFB7E7FB77EFFFEF9 -:100A4000FFCDFFFE7FFFFCFD3FFBFBF7FFFFFBF64B -:100A50007DFE7FFFFCFFB9FFF9FAFEBFAF5BD6ED6D -:100A6000AD7BF6F9BFEFF8FAFEBFFEE6FFFFF7FD3C -:100A7000FF7FBFEFF3FFFF6FF7FEFFFFF7FDFEF70E -:100A8000EFFFFBEFFB7EDEFEFFBFFFFEFFFFFBFF86 -:100A9000FFEFFB6FFC1FFEE7FFFFFFEFFFD3B4BBD1 -:100AA000FFFFFDBF6FE3FEFFBFFCBFF7CFF7FDFF0A -:100AB0002FDFABEAFFDFE7EA9AAFEFFBFEFFF53F80 -:100AC000FD7EFFD7F5FBFFFDF7FF7FFEF7FDFFD7AC -:100AD000FFD77FEE7FFA79FE2F8BE6F9FE3F9EF976 -:100AE000BE2F0BE7F9FE2F9FFDFFFE7D7F5FD7FF37 -:100AF000FF7FFFFDFF7F5F97FFFD7F5FFFE3FFFF4E -:100B0000FAFEBFAFFBFBFFFFCFEBFEBFAFFFFAFE6E -:100B1000BFFF87FFFFF5FFFFFFFFFDFF7FFFFFFF29 -:100B2000FBFFFFF5FFFFFE0FFFFDEBFFFFF7FFEF02 -:100B30007BDFFEFFFFDFF7FDEB7FDFFF5FFFFFFFE8 -:100B4000FFFDBFFF7EFABFC7DBF7BD3FFBFFF6FF30 -:100B5000FAAFFFEBFAFE3F2FEAFA3EADC9BAF6ADA7 -:100B6000AFEBFAF6BFFE7FFFFFFDFFF17F3FCFF156 -:100B7000EFFF7FFFBCDFDFF7DDFFE07FFFFFFEFF62 -:100B8000FAECBB7F5FFFFBECFFEFB7FFF7FFFFB5B2 -:100B9000FFFF7FFFFFFFEEDF5FDFDEFFAEE777FFE8 -:100BA000FFDFF7FFE3FFFABBFEFFAFFDFBFEBFABCE -:100BB000F9FEFFBF7FBFFEBDFED7FF9FFDFFBEEF6B -:100BC000FFEEFDBB5BEFFF7FEFFFEFFF7FFF4FFF10 -:100BD000EFFBBCFCFFFFFFFEFEFDFAFEFBFFFDF39B -:100BE000FBFFF85FFFFFD7F5FDDFEFFFF3DC5FCE24 -:100BF000F5BDFFFFD7FFFFF93FFFDFF7FFFEFFFD6A -:100C0000FFFBFFF7B97DFEDFFFFFFFFFF97FFFFE70 -:100C1000FFFF7FFFFEFFFFF7F6FFBFF1F8FFFFFFCB -:100C2000FFE0FFFFFFFFF9FFFFFFFFFFEFEFFFFF19 -:100C30009BFB7FFFFFFFC1FFDFFF3F5FD7BFEFBB26 -:100C4000DEEEFF7FDFFFFEF57FDFFF99FFFFFAFF9C -:100C5000BFFDEB7AFFB7FEFEFFFFEFFFFFFDBFFF1B -:100C600097FFFDF7FF7FF7FFFFFD5FFEF3F9DFDF83 -:100C7000FFFFFCFFFF83FFFFFEFF9EECFBEEFF9FED -:100C8000BFEFFFFEED7BFFFFFFF15AFFFFFDFF7C93 -:100C9000693BDFFF7F1FDFFFFDBAFFFFFBFF5BBD8F -:100CA000FFFFFFFFD7B6EDE9FFD6BD6F5FFBFFEF9C -:100CB000FF5FFEF66FFFFFFFFFF7EB7ADFFF9F7F1F -:100CC0007FFFB7FFFFFEDFFF6CFFFBFFBB6FEBFE9D -:100CD000CCF7A5FA5CF575BBB7DFFE6F5FC5BFFD4E -:100CE0007BFEFF95E729CF4FF591EE6BDFEFFD54CB -:100CF000F5BDB1FFEFEEFBBEBFAFFEDEBD6FDAF2BA -:100D0000FFAFBEFFFFFD7EA7FFF7FFBFEF7BF6FD46 -:100D1000BD4AF28585BF5BFEB5FDFAFF4FFFFEDFE2 -:100D2000FFEDFFBFFFBF7FFEFFB76DFFF7BFBFEF58 -:100D3000FD1FFFFE7DFF67FFFFFF3F7FFEBFFFE759 -:100D4000DFE7FFEF6BFC1FFFBFEFFBFEDEBFAFFA7D -:100D5000FFB6EFF9FEFF8FEFDBEFAB6FFBFEFFFFA0 -:100D6000EFFDFF7FFFFFDEFFFFEFFFFFFF3FFF6CA9 -:100D7000FFBFFBFFFEFFFBFEDFFFFFEFFFFFBFFF3D -:100D8000FFFEFBFFD57FFFFFEFFBFFFFBFEF43B58C -:100D9000FD6FCFD6BE3F7FDBFEC3FFFDFFAFEBFB9A -:100DA000FCFF3EEFE8FABDCDAAFEFE7DCFFFB7FF08 -:100DB000F7FFFFFFFDFF75CD52D7FDFBF7DDFBEF22 -:100DC000EBFFFF4FFFBF9FE7F9FC7F8BC3F9AF8FAE -:100DD000E7E9BE7F9FE6F9FC5FFFFFF7FDFF7A5F63 -:100DE000D7EDFFFFD7FFDD7FE7FFFCFFFC3FFFFFF5 -:100DF000FFFBFFFEBFAFFFFDFFEFFFEBFFFFFFFFBE -:100E0000FFF77FFF7FDFFFFDFD7FFEF7FD7FDFFF49 -:100E1000FDFFFFDFFBFFEEFFFBFFF7FDFF7ADFF5D6 -:100E2000FDFADFF7FCFF7FDFBFEDFFC9FFDFFFBF8C -:100E30002FFBFFBCADFFF7FFFFEFD3FF7DBF6FFFC1 -:100E4000FAFFFEBFAEEAFABEADA5EBCEBFA7EB5AE6 -:100E5000DEBDAF6BFD57FFFFF47F1F7FFDFF7F36C9 -:100E6000F0DF79FFFFFFF7FDBFFF87FFFBF3FCFF1C -:100E7000FFFFFF7EFFBFDFFFFFFFFFFFFDBFF89F0C -:100E8000FFFFFFFFBFFFFFFDF7FCBDFFFEFFFFFF02 -:100E9000FFFFFBF9BFFFFFEBE2FEFFBFEFA9BA2F99 -:100EA000EBF9FE77DFF7FFFFF97FFFFF7FEFD7FF5B -:100EB000FDFFFBF5FFBF6FDFFFFFFDFFFFF0FFFF53 -:100EC000FF3FCFFFBAEE9BBFEED7FECDEFFFDFBFF8 -:100ED000FFFFC5FFFFFD7F4FFDF6D9FF4FD6FDBFDA -:100EE0006EFFFFF47FFF7F8BFFFFFFFFF7FFF9FE31 -:100EF00037FFD9FBF5AFFDFFFFFBFFFF07FFFFFF4C -:100F0000FBF7FFFDFF7CFA7E4FFCDF1DC7FFFFFFF5 -:100F1000FFAEFFFFFFFFFDFBFFFFFEFEFCFF7F7F3D -:100F2000BFEFFEFFFFFF5FFDFFFFFFFD6F5AD77BA7 -:100F3000BE5FFE39FFF7FFF7FDFEAA1FFFFFFFFFB1 -:100F4000FEFEABAFFDFEBFFFF7FF7FFE8FE3FBEEC4 -:100F50007FFFFFFFFFEBFBFFFDBFEFDFFFFFFFFFAB -:100F6000FFFFFFFBE43FFFDFFFFFFFFFF3EFBBFBF4 -:100F7000BFEFBBFFD7BFFFFFFF29AFF7FFFFFBFFAF -:100F8000FBE6FF0FFB3FDF0FFFAFFFFFFFF5C3DF08 -:100F90005FFFFFFFFE6BCABEBCFF9FF2BFFFFEFA02 -:100FA000FFFFEF16FFFFFFFFFFFCDF97FD79FF3725 -:100FB000E77FFFFFB5FFFFF62FFFFDFBFEFFFFFD05 -:100FC0005F575FFFDB52DFFFFDBFFFFFFCDBFF7BF7 -:100FD000B5FD7FFF719C6EFFF635A59BFFFFFDFF02 -:100FE000FFDB9E7FFEEFFBFFFFBDEFFFDEB7F94BA0 -:100FF000FFF5EFFFFFFFE87EFFEADFF7FFFD695B2C -:10100000FC9FEF78D6FFEBEFFFFFFFE8FFFFEDFF60 -:10101000FFFFFFE3F9F6BFFFFFFEDFFF7FFFFFFFEC -:10102000D1FFFFE7FFFFFFFFE7F9FFBF7FD9FFFD1C -:10103000FE7FFFFEFFF9FFFBD6DFBFEF5BD6FFBFF2 -:10104000FBF6FFBFEFF8F6DDBEFE16FFBFEFFFFEBB -:10105000FFBFEFFFFFFF6FFBFFFFFF6FF3FFF7EF38 -:10106000FBFFBFFFEFFEFFBFFFFFFFBEBFFFEFFFB6 -:101070007FEFFFFD17FB7BFFFFFD7FDBF6F47FFAC1 -:10108000FEF5BFEBE3F7FFFFE9BFFFAFF7FDF37E30 -:101090008FA3EAFFCBF3EEFFBFEFF7F9FFFE7FFF71 -:1010A000FFFFFFF5FBF6FFF52FFEFBD7BFFFBEDF0F -:1010B0009FFFF0FFFFF9FE7F8FA3F8FE6F9FF9F609 -:1010C0002F9FE7F9FE2F9FE1FFFFFF7FDFF7F5FD81 -:1010D0007F7FF5FF9F5FFBFEFF7FFFFFCBFFFFFBE7 -:1010E000FEFFBFAFFBFEFFDFFEFEBFF7FFFFFFFF10 -:1010F000FFC7FFFFFDFF7FDDF7FDFFFFD7FFFD7F90 -:10110000FFFBFDFFFFFEEF7FFDEFFBFEFBFDFF7F23 -:10111000DFFDFF7ADFF7FDFFFFFFFF1FFFFFD3F7C4 -:10112000FFFF6FDBFFFFEFCBF4FFFFFFFFFFFFFED3 -:1011300029FFE8DA769FAF6ADAFE35EBDAD6BFAB85 -:10114000EB7ADEBFD77FFFFEFFBFEFFDDF77BFFD8E -:1011500037EFFFEFFF3FFFFFFFFE7FFFFFFFF77E51 -:10116000DFFFFFFFFAB77FFFFFFEFFFFFFFF89FFF3 -:10117000FFFFFFFFFFFFFFFFFF9FFBFFFFFFE7FFFB -:10118000FFFFFFAAFFABFBFAEFBFFFDFFA7BB9FE61 -:10119000FEFFFDFFF7FE3FFFB7FFF7EEFF7FEFFF1C -:1011A000FF7FFF1FFBFFBFFBFEFFBDFFFF2FFFBF4A -:1011B000FF7FDFFAFFFFFCEEF5F3BEFB0FEFF3BEA0 -:1011C000EFFC5FFF5AFFF7DFFFFFFED5FC5FFBF28E -:1011D000FFFF2FBBF3FFFFBFFFEFFFEFFFFFFFFF9F -:1011E000BFFFFFFD7BFFDFB9FFFBFFD87FFFFFFFE6 -:1011F000FBFFFC7F1FBFE0DFF7EFFFFD7FFEDFFFA0 -:10120000E0FFFFFDEFFBFFFEF7DFFFEB5FFFF7FF08 -:10121000FFFFFFBFFFFDFFFDFFFFFFF7FDFF3BDC13 -:10122000FD6D7B5F57F5FD7F5FFFB1FFEBFFFFFFBC -:10123000FBFBFEFFBFFBBEFFBFEFFBFEFFAFFEF7FA -:10124000DFDFFFFFFF7FCFF3F8FFD7FBFF5FBFF7C5 -:10125000FBFF7FFE23FFFFFE7FF3FFFBFEFFFFF39D -:10126000FFFFF5F9FF3FFFFFF09AFFBE7FFFFCF99C -:10127000FFFDAFEBFEBFFFCFF3FE7FFFFF5BBDFFC8 -:10128000BCEBFFD7D4AFAFFDFFCFF7FDFF7FDFF79C -:10129000FDFEFF6FFFFBFFFFFFFD7F5EFDBFDBF687 -:1012A000FDBF6FFBEEFDFF7AFFFAFBFF3FFBB75F71 -:1012B000D6F71F71DC771DC731DC77DFF9BFF55B2F -:1012C000F4D79DAEFFBFFDBFDBF6FDBF6FDBF6FEC3 -:1012D0003D81FFEBFEFEFEFFEB7ADF7D777DF5794A -:1012E000DF57DDF57D7EE6FFD63FBF7FFFD4F53FBC -:1012F000BFFBBEEFB3EEFB9EEFBBFE8BFFFEDFB787 -:10130000EDFFF7FDFEFFEFBBEEFFBEEFBBEEEBFC2C -:101310001FFFFFFDFFE7FFF7FDFFEFFEFFBFEFFB46 -:10132000FEFFBFEBFA1FFFB7EF5BFEFFAFEBDDE7A2 -:10133000DE779DE779DE779DBFE66FFFFEFFBFEFAB -:10134000FBFEFDBF6FF6FDBF6FDBF6FDBFFF7EFF4F -:10135000FFFBFEFEFFEFFBFDEF7EF7BDEF7BDEF751 -:10136000BDEFFFD5FFBFFFEFFEFFFC3F0FE7FE7FA6 -:101370009FE7F9FE7F9FE7FEF3FFFEDFADDF67EE3D -:10138000FBBFEFFEFFBFEFFBFEFFBFEFFF23FFFF43 -:10139000FFFF7FFFF3BCDBFEFBFFFBBEF7FBFF7F26 -:1013A000DFFFCFFBFF9FE3F9BE3F8FE779FF9DE7AC -:1013B000F9FE7F9FE7F9FE5FFFCFF7FFFFFFDFF743 -:1013C000FE7FE7F9FE7FFFFFFBFEFFFFBFFFBFBF12 -:1013D000FFFEFFBFEFFFFDFFFFFFFFFFFFF7FDFF7A -:1013E000FF3FFFBFFFF7FFFF7FDFFFFFFFFFFFFFB5 -:1013F000FFFFFFFFFFE8EFFF5FF7BFF9FEDFB7FD7D -:10140000FFDFF7FDFF7FDFF7FDFFDDFFF2FFBFFF2F -:10141000FFBFFFFF2FF2FFBF2F7BD2F7BF2FFFBB16 -:10142000FFEE8FAFEBFAFE3FA769CE8FA4EAFAEE8C -:10143000B7AEEBFDC7FFF7F7FFFFFFFFFF7F3EF300 -:1014400074FF3F4FFFE7FF3FFEA7FFFFDFF7B7FF48 -:10145000F7FFBAEF37EBFBFEBFFBFEF3FFF9DFFF51 -:10146000BFFFFFFFBFFFFFFFFDDFFFFDFFFFFBFE35 -:10147000FDFFFBBFFE3FEDFFDFBE3DA7FBFA3FE6F2 -:10148000E1FEFE3FEFE3DFF57FFEFF7EFFFFFFFFA4 -:10149000EF6FF6FF7DEFD7DEFF7DEFFFF2FFFFFF7F -:1014A000FFFFFF7BDEFBE6EEEF376EF37EEB37EF01 -:1014B000FFC1FFFEFFF7EFFFFFFFBF3FD2DFBF2FF0 -:1014C0007BE2FFFE3BBDDBFFFEFFFFFFFFFFEFFE0A -:1014D000FFFBFFFFBFFFFBDFFFBFFFB7FFFFBFEF5C -:1014E000FFFFFFFFFFFF0FFF7FFF1FEFF1FDFFF685 -:1014F000AFFFFFFFFFFFEFFFFFFFFE9FFFFFFF7745 -:10150000EFF7FBFFFE5FFFFFBFCFFBF7DDF7F5FF58 -:101510005FD5F5FD7F5FD7F5FFFB0FFFFFA9EA7AE7 -:10152000FFAF8FFEDFAFEFFBFEFFBFEFFBDFE55F3F -:10153000FFFFFFFFFFBD57FFFF6F77BFF7FBFF7F89 -:10154000BFF7FFFCBFFF9FFFFFEFFFFEFFFFFF1F87 -:10155000CFFFFCFFFFFFFFFB65AFF37CFF3FDFFF2B -:10156000FDE9FE7FE7FFFE7FFFFFFFFFFDE3DFFBFF -:10157000DBF6FDEF5BFBFFDFFCFF3FDFF3FDFF7FF3 -:10158000DFEF66FFDFADEB7ADEF7F7E7D9FD9F67A8 -:10159000D9F67D9FE7DFF547FD655BD6F4FEFFEFEB -:1015A000FF6DF6DDB76DDB76DCB77DFA9BF66D9DE2 -:1015B0006759DFF7DDFFEBFEBFAFEBFAFEBFAFE32E -:1015C000D19FFFBDBFEFFEF7BFBFF7D77FDDF79D10 -:1015D000DF7FDFF7FFE07FFDC1DFF7FDC77F7FFB28 -:1015E000FFBBECFB3EFFBFECFBFFD87FBF6CFFBE39 -:1015F000FFBFEDFFEFFEFBBFEFFBFEFFBFEEFFC542 -:10160000FFAF6FFFFCFD3FE7FFFEFFEFFBFEFFBFFD -:10161000EFFBFEBF89FEFABAFEBFAFFBF6F5D97D40 -:101620009765D9745D9765D3FED6FFBFF7FDFF7F41 -:10163000BFCFFBFEFFEFFBFEFFBFEFFBFFF68FFB15 -:10164000FFEFFB7EDBFEFFBEEFEEFBBEEFBBEEFB74 -:10165000BEFFFFDFFF43FFFFFBEF5FB7FE7FE7F952 -:10166000FE7F9FE7F9FE7FF9BFFEAF77FDFF2FAF4B -:10167000A7FEFFEFFBFEFFBFEFFBFEFFF17FEFDFFB -:10168000FF97F5EFFFDFFFFFBFFFBFFFFFFEFFFF8D -:10169000FFE0FFFFF9FE2F8BE3F8BE779FF9DA77C3 -:1016A0009DE779DE779FDDFFFDFD7F5FD7FDFF7F43 -:1016B000E7FE7F97E7FBFEFFBFEFFFABFFEFFAFE12 -:1016C000BFAFFFFAFFFFDFFFFBFFF7FDFF7FDFFF8D -:1016D00067FFF7F5FFFFFFDFFDFFFFFFFFFFFFFFE6 -:1016E000FFFFFFFFFFEFFFBDEBFFFFF7ADEBFFDFFE -:1016F000FDFF3FDFF7FDFF7FDFFF5FFFF7FFFFFD30 -:10170000BFFFCBF4FF7FD3F7FD3F7FD3F7FFFC3F55 -:10171000FFEAFABEAFABEBBAF4956B52D4AD2F4AE9 -:10172000D2F6BFD27FF73FFFFFF37FFFFFF7FFBA8D -:10173000DFFBFDFFBFFFFBFFF87FEAFFFEFEDFFFE1 -:10174000F7FF7FBBFFFFBFDFFBFFFFBFFFB17FFFE7 -:10175000FBEFFFFFFFFFFFBFCFFEFFFFEFFFF7FF36 -:10176000FFFFF1FF69BEFABFAFE2FFFEFDAFF3FE80 -:10177000FFBFEFFBFCFFFF07FD95DBDF7FDFAFFF68 -:10178000F7AF36FEBF65EBF6FE9F6FFE07FFCFFF9C -:10179000F8FEFFCFFFF6FAE7FBFEFFBBEDF9FFFF18 -:1017A000FF5FFFFFFF75FFEF7EFDE0E85ED3E5F929 -:1017B0003E5FD7F7FFFA2FFBFFFFFFFFFEFFFF7F24 -:1017C0007FD7F57D5F57D5F5EFFFF37FFC7FFFC730 -:1017D000F1FFFF1FCFB0FF3FCFF3FCFF3FCEFFE491 -:1017E000FFDF7FFEF7BBFFFFDFEFEEFFBFEFFBFE8C -:1017F000BFBFEFFFD1FFFFFFFDFBFFFDFFFB9FE939 -:10180000FE7F9FE7F9FE7FBFFFB3FFFFF7FFFFAF4C -:10181000F7FFB63FEBFAFEBFAFEBFAFEBFFEA7FF46 -:10182000FFFFFFFFF7FFFFFFFE9FF7F9FF7F9FE737 -:10183000FFFFFEAF6FFFFFFF9FFFDFFF7D5FDDFF5D -:10184000FBBFE7BBFFFBDF6D5F7EFFFFFFFFFFFF1F -:10185000EBF7FFE7EFF7FFFF7FFFF7FFFC8FFFEFEF -:10186000FDFEFFBEF4F27DD7CFFF3FFFFFFFFFFF7E -:10187000FFCF6BFFBF3FFBF2FC7FEBFF9FFAFFFF49 -:101880003FFFF3FFFFFD70F7FFFFBFFFFBD7FEF544 -:1018900077FF15DD77FDFF7FDFF7FBCDBFFFFDFF96 -:1018A000FFDF37CDF9ECFEEFBBF4FB3F4FB3FFFD9D -:1018B000CBFFE97E549FE54BB7FFDD7DC771DD7738 -:1018C0005DD775CD7FD6FFD3F6F93F6D95AF7FFE1F -:1018D000FFEFFBFEFFBFEFFBFEF6C7FFAD7BCAFFCE -:1018E000BFBFEFFDE3DFB7EDFB7EDF37EDE3FBDFEF -:1018F000FF525C15FDCF7FDFFEEFEFFBFEFFBFEC7D -:101900007BFEFFFE3E7FDAF7FDFF7FFFFFFBEFBBB5 -:101910006FFBFEFFBFEFFBFFF77DFFD8FFFDBF7F33 -:10192000FBFFFF9FFBFE7F9FE7F9FE7F9FEA7FF6AD -:10193000BFBD6A5AF6E5BF775F6DDD775DD775DDB0 -:1019400077FFA5BFCFFBFFFFBFCFFBFDFFBFF3FEC0 -:10195000FFBFEFFBFEFFFDABFFBFBFFFFBFF7FEF56 -:10196000FFBEFBEEFBBEEFBBEEFBBFFFB5FFD0BC87 -:10197000FD2F4BF7FFFF9FF9FE7F9FE7F9FE7F9F4B -:10198000FA8FFDABFADABFAFB3FDFFBFFBFEFFBFBF -:10199000EFFBFEF7BFFF9FFF77F7BDFD77DFFF7E11 -:1019A000DFEDBBFEFFBEEFFBFEFFFA3FFFBE6F8F1A -:1019B000E6F9FE7F9FC7FE7F9FE7F9FE7F9FE7FB6B -:1019C0007FFF7FCFFFFDFFFFDFFBAFBFEFFFFEFF1E -:1019D0009FEFFBFFFCFFFBFEFFFFFFFFFEFFFFF79C -:1019E000FFFFFFFFFFFFFFFFFFF5FFFFFF3FDFF7F9 -:1019F000FFFF7FEFFEFFBFFFFBFFFFBFEFFFB37FE8 -:101A0000FF7B5EF7FDFF7B7FF7FF7FDFF7FDFF7F4B -:101A1000DFF7FF17FFFFFF7FFFFFDDF6FCBFCBF215 -:101A2000BCBF2FCBF2FCBFFE8FFFFA7EBFA7EBDA65 -:101A3000FCBFAF7AFEBFAFEAFAFEBFAFF4DFFEFF36 -:101A4000F33C7F3EFFCFF8BF8FE3F8FE3F8FE7E820 -:101A5000FFFC9FFFFFCFEBB3E7FB7BF3FEFFCFDB8A -:101A6000FBFBBF6F6FDFEC7FFFFFF7FDFDFFFFFFAD -:101A7000FFB2BFFFDEFDBDEFFBF6DFEAE7DBFEBB3B -:101A8000FFEBFBBF9F8FE8FE3F8FA3F8FE3F8FFF6A -:101A9000F87EFDFD7FFFFBCDFFFDFF5FEFFDFFFF4C -:101AA000DFF7FDFFBE90FFFFEEFF3FBFF3BBFEB7CA -:101AB000ABFAFEAFADEAFADEABFF63FFFEF2FFB3B7 -:101AC000FFDFEE7DFF03F1F43F1FC3F1EC7FFE6FFC -:101AD000FFFBFBFF9FFFBFFF7B5FFDFFDFF7FDFD10 -:101AE0007F7FDFFECFFBFFFFAFFBFF1FEFA5FDBF3B -:101AF000DFFB7DFFBFDFFBFFFD3BFFFFFFFFFFFDC8 -:101B0000AFF3FFFB7FBFD7FBBF7FBBF7FFF87FFFC4 -:101B1000FA5FD7FFDF7FEFFFFF7FDBF7FDFF7FDFA0 -:101B2000B7FBECFFFFF7BFEFFDFCFBFFEFF0FE3F65 -:101B30008FE3F8FE3F8FEF8DFFFFEF7FBFFFFBFFCF -:101B4000DBBFFFFFFFFFFFFFFFFFFFEFD8FF2E7F91 -:101B5000BEEFFE6EFFBFF9FFFFF3FFFFFFFFFFFFCA -:101B6000FC66BE47F37FDFFE879FFFFFFFFFE7FFB7 -:101B7000FFFFFFFFFFD66F7CFB4FD2FFFD2BFEFF69 -:101B8000FFFD5FD7D5F57DFFFFFFBF9BFFFFDFB7F1 -:101B9000FFFFDFFF3FCFFE7FBFEFFBFCFF3FFFD923 -:101BA000BFFE97EC8FB7FE9B7DFDB7DD771DC7713C -:101BB000DD775DD7F36FFD3F73DDAFFD7AFFFFAFDC -:101BC000FEFDBFEFFBFEFFBFEF667FFFFFBFBFFF66 -:101BD000FBFFF7DFFDFB7DDFB7CDF37C5F3F913F80 -:101BE000FF3DEF7BFFFCFFCAEFFEFFBDEFFB1EE7F3 -:101BF000BBEC7FB3FFFD9FFFFFFEFFFF7FBFFBFE40 -:101C0000FFBFEFFBEEFBBFDF67FFFFBFEFDBFFBCFC -:101C1000FE7FFBFF9FEFF9FE7F9FE7F9FE87FFEE58 -:101C2000FBBEE5BFEFF9D765F7DDE77DDF775DD771 -:101C30007FF89BFEFFBFEFFBFFFFBFEFFBFF7FCFF8 -:101C4000F3FCFFBFEFFFDB3FEFFBFEFFDFFFFEFB21 -:101C5000BBEFBFEFBBEEFBBEEFBBFFFC7FFD3B5B13 -:101C6000D6E5FD4FC3FBFFBFEFFBFEFFBFEFFBFF62 -:101C7000B4FFFABC8FB2E9D22ECFFBFFBFEFFBFE61 -:101C8000FFBFEFFBFFECFFFDFD7FDFF7E4DF5FFF52 -:101C9000FFFBFFFFFFFFFFFFFFFFC3FFEFE6F8FEC5 -:101CA0003F8B83F9FE7FE7F9FE7F9FE7F9FE7F1701 -:101CB000FDFFFFFF7F5FF72CFFFFFFFE7FFFE7F9D0 -:101CC000FE7F9FFE2FFFFFEFFFFEBFEFADFFFF7F09 -:101CD000FFFFFFFFFFFFFFFFFEDFFFDFFFFDFD7FD9 -:101CE000DFF7FFFFFFFFFFFFFFFFFFFFFFFA3FFEF2 -:101CF000F7FDEF7AFFB1BDFF7FF7FDFF7FDFF7FD57 -:101D0000FF7FF327FFDFFFDDFFFC9BFFCBFCBF2F37 -:101D1000CBF2FCBF2FC9FFDEFFDFAFEBDAFEBBAFBC -:101D2000EBF8F7AFE8FAFEBFAFEBF2FFFDFFFFEF16 -:101D3000BDD7BFFFFFDE8FB8DE378DA378DA3F8FC8 -:101D4000FFA1FFFFFBFBFFFFFFFFA7BDFB76FDBF72 -:101D5000EFDBFEBBBFFE277FFFFEFEFDF5FFEFF5CD -:101D6000DF1FE7FDFF7FDFF7FDFFFFCDFDAEFFFAD1 -:101D70003E3FABFDF87E8FE3F8FE3E8FE3F8FFFEBB -:101D80001FEFDFBFFEDEDFD9FFDFBCFFFF7FFFEF0E -:101D9000FD7FDFF7F93FFEFFFF6FFEDEBFF7EDEAE5 -:101DA000FD8F83F8EA3F8FEFFFF47FFFEFEF7BF3C8 -:101DB000F15FFFFFF13B7FDFF7FDFFFFFFFFE0FF7C -:101DC000FFFFF7FF6FFF7FFFFFF7DEF7BFEFFBF7C8 -:101DD000FDFFFFF5FAFFFFFBE7FFF3F87FF3DFFFFF -:101DE000FFFFFFFFFFFF1FEFBBFFFFFFFFFFFFFD39 -:101DF000FF7FFF9FFFFFFFFFFFFFFFCFFF37FFFFCB -:101E00007FDF775DE7FCFFBFF7F5FBFFFFD7F5FB53 -:101E1000FFFF45FD7FEAFDBEBFDFF7FFFFDBFBFEF7 -:101E2000FFBFEFFFFFFFFB5F7FFFFEFFFFFFFFFF37 -:101E3000FFFEFFEFFDFF7FDFFFEFFBF80FF3FFF982 -:101E40002EFBFEFCF3EFFFFFBFFFFBE7FFFE7EFF75 -:101E5000C06BCFFF34DFF1FDFFEFFFFFFFDFF7FDCA -:101E6000CF7F9CFDFD6CF7FFF6FDEB2B9FFFFCFE8B -:101E70007EFFFFFFFFD7F3F7FFFBE1BFFFEB7ADE4B -:101E8000D7FBFFF9FEFFFFF3DE7FFDE77FFFFDBB22 -:101E9000FFFF7ECCF6AF5F7FFEF47DF7FDBB6EDB10 -:101EA000B7FFF7DF66FFFFF73DCFDEBDFFFFDEDBED -:101EB0008DF77EDFB7EF7FFFF687FFFFEFFEDEBF18 -:101EC000FFFFFFBBEFFDFF7BDEF73FFFBFFBDBFF4D -:101ED000F2B6FDBD7FE7FFFFFF6FF7FFFFFFFE7765 -:101EE000FFBFF8AFFFDFBFFFBF7FFBFFFFFFDBFEE2 -:101EF000FFBFFFFAFFFDFFF67FFF9FFFFF3FEFF8F9 -:101F0000EE7E9FBAFEBF8FEFFEFEF9FFFA7FFE7EE8 -:101F1000BFAFFB96FD9FEF5E65BEEF5BB6FFBEE316 -:101F2000FFB5BFFFFDFF7FFFEFDFFEFFBFFBFEFF43 -:101F3000BFCFFFFFFFFD9BFFFEFBFEDFFF7FFFF735 -:101F4000FEFFDFFBFBFEFFFFFFFFFFB7FEFAFFAB6D -:101F5000EFFFFDB57B7FFBF7FDFFFFDDFFEF8FFFA1 -:101F60002FFFFB7CFF3FDF73EBFE3FFFEFFBFEFF2E -:101F7000EFFDFFBFFD0FFFFFFFF5F9FF7FD7FDFF6F -:101F8000DFFFF7FBFF7FBFFFFFF09FFFFE7F8BE3CD -:101F9000F9DE279BE6BE7F9BC3F8DE7F9DE7FE7FD1 -:101FA000FFFF5FD7FFFFFF4FFBFFFF7FFFAFFF9FED -:101FB0007FFBFFE8FFFFFEBFAFFFFFFEBFEFF7FFB6 -:101FC000BFFFFFFFFFFFF7FFFCFFFFFD7FFFFFFFEE -:101FD000FD3FCFFFFFFFFFF7FFFD7FFFFF93FFFFF9 -:101FE0007ADFF7FFFF7B7FB7EFFFFFFDBFFDFBFF52 -:101FF000F7FFD7FFFFFFFC9F6FCBFFF4BBDFD6FDE2 -:10200000BF2FD3F7FFDFFFCFFFFABEBDAF6ADABE47 -:10201000BBAB3ABE2DAEEBDAF63FADF5DDFFCFF14F -:10202000FFF97FFF73FEFFCFC3F4F72FF3FFFCFF31 -:102030007C1FFF3F4FFF7EFFEFBDF6FEFF2BEFDC67 -:10204000FBFDFFFBFFEA7BFFFFFFFFFFFBF7DFFF6F -:10205000E37DFFB7FFBFFFFFDFFFF8FFBFFFBFEB71 -:10206000E7FAFE3DBFE9FCBFFFFAFBFEFFFFFFD929 -:10207000FFFFFFF67FFFF67DFFDFCFFDBFFBEF7EAB -:10208000FF7FFFFFD3FFFDFBFFFBFFFFFFEFFFBF66 -:10209000FEFFF7EFFFFFFFFBFF87FFFDFFFFFFFFE7 -:1020A0007BFEFFFE3BF7F7FF3FFFFFFFFFFF0FFF4A -:1020B000FFFFFFFBFFFFFFF7FFFFADFFFEF7FFFF97 -:1020C0005FFFFFDFFFFDFFF5FFDFFFBDFFE9FFC79C -:1020D000F3FFFFF7FFF3FFF83BFFFF7BDFBFFBEFF3 -:1020E000FBFFFBF7F7BBFFFFFFFFFBFFFE7FF37F6D -:1020F0005EB7BFFD7FFFF97FFBFFEBFD7F7FFFEF4B -:10210000FBE03FFEBFBFDFFF7EFFF7FFFFFEBFFF2D -:10211000DB78FFFFFFEEA1BFF5DEFBF7FFFBFFFF64 -:10212000FFFFFBFFFFD7FFFFFFFFEFF0FFFFFFF316 -:10213000F7FFEFFFE7CFFFFBFFEFFFFF9F9FEFFCF6 -:1021400016BFFEF3E4FFFFC6FFE7FFFFFDFFBFFF83 -:10215000FF3FFFBFD6AF7FFE6B7E7FFFAFFFFFBFAE -:10216000FF5FFFFEFFFFFEFFFFBDDBFFFE5FF2FF35 -:10217000FF5FFFFFFFFFFFFFEF7FFFFFFFFFDEBF00 -:10218000FFFFEFFB77FEBD7F5FFFFFFFDF6FEDFF20 -:10219000FDFF7FFD6FFFFF77DACFFD5FFFBFFFFF22 -:1021A000DF7FFFFBFFFFFFFF667FFFFEBFE7BFFA9A -:1021B000FFFEFFFFFFDFFF59EFFFEFFB7F89FFFF10 -:1021C000E9FF6FFFF5FFFFFFFFFF7FF2F7FFFFEF74 -:1021D000F87FFBFFFDFFFFD9FFEFBBFFFFFFBFEF66 -:1021E000DEFFFF9F7FDFFFF7FFFFFFFFDFFFFFAF98 -:1021F000FFFFF73FEB9FFE7F9E7F9FFE87FFEDDB9C -:1022000056FFBFAF0BD2FFEFDB6E7DBD6FF8FE3F19 -:10221000FA5BFFFDBFEFFFBF6FDBE6FFFF3FFFDFB6 -:10222000FEFFFFFFFFDA3FFFFBFEFEFFFFDFF7BD14 -:10223000FFFDFFFEFFFBFFFFFFFFF15FFD9FDFFDE7 -:10224000FFFD7FFFFFFFFF76FAFFFF7FE3F8FFAEA2 -:10225000FFFB7E9D73FFFA7FDFFFFF7FFFFBCDFF5C -:102260007FEFFBFFFDFFF77F7FEFFFEDFFFFFFB588 -:10227000FFBFFFBFFDEFDBF7FF93FFEFE2F9BE7F8C -:102280008BE7F9FE6BE7F9FE7F9FE7F9FE7F47FFDB -:10229000FFFDFF9FFFD7FFFFFFFFF5FF9FFFF7FE4B -:1022A000FFBFFE6FFFFFFBFFFFFFAFFFFFFF7FFBE7 -:1022B000FFFEFFFFFFFFFFFDDFFFFFF7FFFFFFDF79 -:1022C000FFFFFF5FFFFFFFFF5FFBFEFFF837FFFF32 -:1022D000EFFF7FFEBFFFFFFEBFFFFF7FFFBFFDFFE2 -:1022E0007FFA7FFFFF6FFFFF7DFFCFFFFFFF4FFFF5 -:1022F000F2FFFFFFFFFFFABFFFAEEBFAFEBBADEB55 -:10230000FAF7AF6BFAF6BF25E9F27F45FFFFFDF75D -:10231000F7BFFFDFFFFFBFFBFFDFF3FFF73FCFFF9D -:10232000A1FFFFBFE7FFFF7FFF3DFFFFFFF7FF2F8D -:10233000FFFBF57FFE57FFFFFFFFFFFFFFFFFFF7EC -:102340003FFFFEFFFFFFFDFEF7EEAFFEEEE7FAFFF9 -:10235000FE9DF95EFEFFEBFFFFDFA7FFFFFFFCDB4B -:10236000FFFFFF7EFBFFFFEFFBFDFFDBFFFFFFEF4C -:10237000FFFFFFFDBFFEBFFF6F7FFFF7FFFFF9FF0E -:10238000F7FFBFDEF7FFFFFFFA7FFDBF5FFFFFBF75 -:10239000FFEDFFF7BFFFFFEFFFDFFFFFFFE6FFFBF4 -:1023A0007FFFFFFFFFFFF7FFFFFFFFFFFFFFEBFFD9 -:1023B000FDFFF5FFF67FDFBDCFFFFFFFFFDFFFFF74 -:1023C000FFF9FFFFFFFFFFE3FFEEBFFF7DEFFEFF23 -:1023D000FFFFBFFFFFFFFFFEFFFFFFFFE7FFB5AE01 -:1023E000FFFFB6FEBFFFFFBFFFFFFFFFFFFFFFFFC7 -:1023F000FF27FFEFFE7FDFFF7EFFFFFFFFFFFFFFF7 -:10240000FFFFFDFFF7F99FFF5FFFFFFFFFFFFF7F6C -:10241000FFFFFEFFFFFFFFFFFFFFFF0FFFE7BFFE16 -:10242000FFBFFFFFFFFFFCBFFFFFFEFFFFFFFFC47B -:102430006BFF291FFBAFFFFFFFFFFFEF1BFEFFFC42 -:102440006FFFFFFD6AF7D7F5BFFFFEFFFFFFFFFF3E -:10245000FEBFFFFFFAFFFFF7FBDDBFFFE7FFFFFF58 -:10246000FFFFFFFFFFFD7FFFFFF5FFFFF7FDB3EF6E -:10247000FD7E5DFFFDFFFFFFFD7FD2F5FB7ECBB74D -:10248000FFFFFFC6FFFDEE63FFFFFFFFFFF6FD65E9 -:102490005BDFFFD5FFFFFFF6E7BFF7A9FFFFEDFF0B -:1024A000FFFFFFFFEBFFFFFFAFFFFFFFF81BFFE3A7 -:1024B000D0BFFFE1FFFFFFFFFFD7FFFFFF5FFFFF81 -:1024C000FFFFAFFFDB76BFFF7FFFBFEFFEFFBFEF7A -:1024D000FBFEFFFFFFBFF27FFF9FFEBDFE7FFFFF02 -:1024E000FFFFFFFFFFFFFFFFFFF73FEC7FF695BB0E -:1024F000EFF8FEFCBF2FDAFCBF2FCBF2FCBFEFFFE3 -:10250000A9BFCFFBFFFFFFFEDDB76DF6D9B66D9B10 -:1025100076D9BFFBFDA3FFBFEFFFEFFFFFFF7FDF1C -:10252000FDEF7BDEF7FDEF7FFFFF05FFFAFE7FEF9C -:10253000E3FFFFFD7FFFFFFFFF5FFFFFFD7FFBAFBF -:10254000FF63C8FFBFEFFFFFFA7FFFFFFFFE9FF7AC -:10255000FFFABFFE9FFB7FFFFFEFD7FFFFF5FFFFF7 -:10256000FFFFFD7FFFFFBFFFF9BFFFBE279FE7F91A -:10257000FE7F8BE7FE7F9FE2F9FE7F9FE7F17FFF03 -:10258000FFFFFBFEFFFFFFD7FFFFFFFFF5FFFFFF92 -:10259000D7FFFAFFFEFFFFFFFDFFFFFFAFF7FFFFD3 -:1025A000FFEBFFFFFFAFFFC4FFF7FFFFEFFFFFFFF2 -:1025B000FF5FFFFFFFFFD7FFFFFFFFFFEBFFFB7A90 -:1025C000DFF7FDFFFFFEBFFFFF7FFFAFFFFFFFF75E -:1025D000EFE3FFDDD2FFDFFFFFF2FCBFCBF6FDBF75 -:1025E0002FCBFF7FDFDEAFFFDAEEBFAFE9FAF4BD3E -:1025F000AF5AAEBBAB6BDADEBFADD75EFFFFBFFC41 -:10260000FFDFFDFFFFFFFFDFF7FFFFFFFFFDFFFA2B -:102610001FFFFEFBEFBFFDFFFDBD77FFFFFFFF9D2F -:10262000EFFFFFFFEF7DFFFBFEEFFFFFFFFFFFF779 -:10263000FFFFFFFFFFFFFFEEBFE4FBFFFE3FFEFFDC -:10264000FFFFFFAFEAFEBFAFEBFAFEFFFFFF55F65D -:10265000FFFEF7FF7FFFEBF75FC5FD7F5FD7F5FF5D -:102660006FFBFF8AFFFFFFFFEBFFFFFFFFFBBFBF1B -:10267000EFFBFFFFFFFFFBFF77DFFBFFFD7FEFFFC0 -:10268000FFFFBF7FFFDFBFFFFBFFFFFFFEEFDFFFAF -:10269000FEFF9FEF7DFFF7FF7FFFFFDFF7FDFFEFFF -:1026A000DFFFDFFFFFFFFFFFFFFFFFFFFDFFFFFB80 -:1026B000FDFFBFDFD1FFF83BFFFFFFFFFFFFFFFF85 -:1026C0007EDBFDFF77DBB77DBFFBFFF87FED7B5E39 -:1026D000FFFEFFFF4FD7FD7FDFD7F5FF7FFFFFFF37 -:1026E000F23FFEFFBFFFFFFFFFBFEFFEFF3BEEFF2E -:1026F000FCEFFFFFFF85FFFDFEFFF5FFFFFEFFDFA5 -:10270000FBFF5FBFFFFDFFFFFFFFA8FFFF9F9EFFD7 -:10271000FFFF7FF3FFFFCFFFF7FDFF7FFFFFFC16FB -:10272000BFCFA3E5EF7FFFF3E4FFCF93FCFF3FCFE5 -:10273000FFFFFFD60F7DBF6EFBF4FCAF6DDB77B7FD -:102740006DDBF6FDBFFFFFFFBF9BFADEB7B7EDF90C -:102750007EB7ACEBD6B3ADEB7ADFFFFFFFD8BFFFA0 -:10276000B7ED9F6FDDF768DB37B36CDB36CDB37F3A -:10277000FF7FF56FFDEF793DF793E47A9EADEA7A3E -:102780009EF7BDEFFFFFFF767FFBC6FFBBEFDAFED4 -:10279000FDBFFBFEFFBFEFFBFFFFFBFFA5FFFDAB98 -:1027A0006F78DE178F79DFFDFF7FDFF7FDFFFFFB1F -:1027B000FFFBFFEFFBEFFBFEFFBBDAF3EF3BCEF3DC -:1027C000BCEF3FCFDFFFB7FFFFFFCF73FFBFEFFFD0 -:1027D000F3FF3FCFF3FCFF3DCF9FFE07FFAFEBFEC4 -:1027E000FDBFEFEBFAFFAFEBFAFEBFAFFBFE3FFB27 -:1027F0009BFF7FDFFFF3FEFFDEF7BF7BDEF7BDEF62 -:102800007BFEFFFFDF3FFEFFB7FFEFF7FFBFEDFEF1 -:10281000DFB7EDFB7EDFFFFFFFFD5FEFEBFAFEF5BD -:10282000BF6FFFFFFFFFFFFFFFFFFFFEF8FFA8FFE7 -:10283000FFBFEFFB6AFBB7EFFBFFBFEFFBFEFFBF86 -:10284000EFFBFFE0FFFFFD7F5CD77DDFF35CF5CDA5 -:10285000735ED7B5FD7FEFFFDBFFFFE2F8BE2F8F82 -:10286000E7F8BE6BE2F8BE2F8BE2F9FE7FE7FFD7F9 -:10287000F5FD7FFFF7F5FD7FD7F5FD7F5FD7F5FF0E -:10288000FFFF8FFFAFEBFAFFFFBFEBFAFF2FEBFA73 -:10289000FEBFAFEBFFFFFE5FFF5FFFFFFDFFFFD758 -:1028A000FFFFFFFFFFFFFFFFFFFFFFFFBFFEB7FDC3 -:1028B000FF7EDFF7ADFF7FF7FDFF7FDFF7FDFF7FD7 -:1028C000F67FFFFFFFDBF6FCAFFFFFFFFFF7FFFF29 -:1028D000FFFFFFFFFFECBFFFAFEBFAF6AB8FEBFAAA -:1028E000F7A5EBFABEBFAFEBFAFF6DFFFF7FDF335B -:1028F000DDFF7FFEF7FC7FFBFFFFFFFFFFFFFFA970 -:10290000FFFDFFFFFEFFFFDFFFFFEFEFFDFF7FFF9C -:10291000FFFFFFFEA7FFFFFF77DFF7FD9F7FFE773B -:10292000EFFFFFFFFFFFFFFFFFAFBFAFFFF9BEBF2E -:102930008FFBFEFEEFFBFEFFBFEFFBFFFFFDDF6F38 -:10294000EFFF7FFFBFBFDFFFFCFFDFF7FDEF7FDFA4 -:10295000FFFFFF3FF6FFCFFFDBFBF7FFEB7AFFFF49 -:10296000FFBFEFFBFFFFFFFE6DFDFF5FFBFFFFF70C -:10297000FF5FF5FFFFFFFFFFFFFFFFFFF8FFFBFF1C -:10298000FFFDFFFFFFFFE7F6BFFFFFFFFFFBFFFFBE -:10299000FFC9FFFFFFBDFFBFAFEFEF3FD1FC7FFBE4 -:1029A000C7FFFFFFFFFFE3FFFFFFFFFDFFFF77FF15 -:1029B000DFB7FDF7FDF7FFFFFFFFFF57FFF7A5FDAF -:1029C0003FDFBFBFFE7FFFFFFFDFFAFDFFFFFFFE20 -:1029D00087FFE9FFFEEFBFEFFEFEFFEFFFFFFFFF08 -:1029E000FFFFFFFFFA9FFF3FFFFDFD57DFFDF3FFF6 -:1029F000DFFDFF5FDFF5FDFFFFF98FFFFFFFEE7FDC -:102A0000FFFFBF5EFEECFB3F7F9FEFF9FFFFCD6B4B -:102A1000FFFFFFC5F3FCFA38FFAF3FEE7F9FFFD902 -:102A2000FFFFFD7AF7FFF3FFAF6FDBF2B9E9FBFFC2 -:102A3000FFFFFEFFFFEFFFFBC5BFFFEFFF5EB7AD80 -:102A4000CD797CFFFFFFFFFFFFFFFFFFFD93FFEF4F -:102A5000EAFEBFEF5BD2CDF56D77DFF7FDFF7FDFDD -:102A6000FFFF66FFD5657D5F759D657FD6FB4FFFD8 -:102A7000FFFFFFFFFFFFF6C7FFBFEFFAFEFFBFEB51 -:102A8000FFDFFF7EFFFFEFFD7ED7FF78DFFF5FDF19 -:102A9000F5BF7FDFC5FF3FF67EFF0FEFF23EBFFFC2 -:102AA000FB3FFFFB7FFFB3FEFBF6FDFFDAF7FDFF09 -:102AB0007FDFF7BFFFFA7FFFFFFFFF9FFFF3DCF928 -:102AC000BFCEE7F9FE7F9FE7FFFFE27FFEFFBFEF8C -:102AD000EBFAFF9F671EFF8FE7F8FE7F8FEFFFBDCA -:102AE000BFFFFBFFFFDFF7FFFCFFBFFFFFFFFFFFA5 -:102AF000FFFFFFFDB3FFFFEFFFFFBFEDFFFBEEFEAC -:102B0000FFFFEFFFFEFFFFFFFFB5FFB7FDFD6EFF0D -:102B1000FFFEFD2FD8FEBF8FEBF9FE3FFFFACFFF80 -:102B2000E7D9FABFDF77FCFB3FABFEFFBFEFFBFE51 -:102B3000FFFFEE1FFFDFF7FFFFFF5F9735BF5EFE72 -:102B4000BFEFFFF7FDFFFFFABFFFBE6F9FE7F8BEC5 -:102B50002F8B66947D9DE7F9FE7F9FE7F17FFFFF56 -:102B6000FFF7F5FD7F5FFBFD9EFFFBFEFFFFEFFF25 -:102B7000FFA0FFFFFFBFEFEBFAFEBFB7F7F7FFFFC6 -:102B8000FDFFFFFFFFFFDDFFFDFFFFFFD7FFFFFFA3 -:102B90007FF5FFFFEFFFFFFFBFFFFFABFEFBFEFF79 -:102BA000F7AFFFFFDEF7EB5FDFF7FDFF7FDFFFFF34 -:102BB000B3FFC9FEFFFFFFFFD6FFFFCBFFFFDFFF25 -:102BC000FFFFFFFFFC8FFFBABEBFAFEB78FEB7ADD4 -:102BD0003AFEB7AFEB7AFEBFAFFF9FFFFFDFFCFF10 -:102BE000FFFEC3FEFFFF33FCFFBFDFF3FFFFBB9F12 -:102BF000FFFFFFEBDFFFFFAFF76FF9BFEFFDFFFF59 -:102C0000FFFFFFE37FFFFFFFFBFFFFBFFDFBF7FFC2 -:102C1000DFF7FFFEEF5FBDFFFAFFF8FFBFAFFBFE80 -:102C2000FE3FEFE8FFDFF3FDFFFFFFFFFFEDFFFBE0 -:102C3000FDFFAFFFFFFEFEBFDBFFFFFFBFFFDFFFBC -:102C4000FDFFCBFFFFFFFFFFBF6FFF7FB7B3FFFFAE -:102C5000DFFFFBEFFFFFFF07FFFBFFFFFFEDFFF5D0 -:102C60007CFF7FFEFFFFEFCFFFFBFFFF2FFFFFFF8C -:102C7000FFF3FFFBFFFEFFFFFFFFFFFFBFFFFFFFB5 -:102C8000FD1BFFFFFFFFFFFFFFFFFE7CFFFFFFFFBE -:102C9000EFFFFFFFFFFBBF7FFDFFFFFFFFFFFFFF1A -:102CA000DBFFFFFFFFFFFFFDFFFFF07FFFFFFFFFE9 -:102CB000FFFFFFFFFFFBFFDFFFFFFFFFFFFDBFFE8B -:102CC0007FFFFFFFFFFFFFFFFFEFFEFFBFFFFFFFE5 -:102CD000FFFFEFFAB5FFFFFFF7F7FFFFFFFFDFFB97 -:102CE000FCFFFFFEFF7FDFBFFFCBBFF9FE7F9FE74B -:102CF000F9FE7F97E1FE799FE7FDFE7FDFFE37FF5C -:102D0000FBDEDEBDEFF3FEFBAFEBFEFFFFCFFFFE12 -:102D1000FFBFFF8FFFEFFBFEFFBFE7F95E7FEFFB1B -:102D2000DAFFBFEFFBFEFFFD1FFFFFFFFFFFFFDF2F -:102D3000FFFF7FFFFFF7FB7FFFFFFFFFFC3FFFBFB2 -:102D4000EFFBFEFFBFEF7B7FBFEFFBFEFFB5EFFBAF -:102D5000BFFA7FFCFF3FCFF3FCFF3FCFBCFF3FEF4D -:102D6000F3FCFE3FCFFFEEEFFBFEFFBFEFFB6AD7AA -:102D7000B7FBF8FFB7EFBAFEFFBF7FE9FFF97E5F51 -:102D800097E5F9FE7FBFF97E5F9FE5FBFE5FB7FF2A -:102D9000A3FFF7FDFF7FDFF7FDFF5EF77DFF77DF26 -:102DA000F7FDFF7FFFD7FFFFFFFFFFFFFDDFFB7F8B -:102DB000FFFFEFFFFEFBFFFFBFFE8FFFDFF7FDFD15 -:102DC0007FDFF7FD3EDFF5BDFF7FDFF7FDF7FF9FFC -:102DD000FFFFFFFFFFFFFFFFFFFDFFBEFFFFFFFF46 -:102DE000FFFFFFFD3FFFDFF7FDFF7FDFF7FDFFCFB9 -:102DF00077FCFF5FDFF7FDFFF47FFFFFFFFFFFFFC3 -:102E0000FFFFFFFFFFFFFFFFFFFDFFFFFFEEFFFFE5 -:102E1000FFFFFFFFFFFFFFFFEDFBFFFFBFFFFFFF18 -:102E2000FFFFE9FFFFFFFFFFFFFBFFFFFFD3FFFFF8 -:102E3000BF3FFBFFFFFFFBF3FFFFFFFFFFFFFFFFB6 -:102E4000FFFFFFFFFFFEFFF7FFFFFFFF17FFFFFF83 -:102E5000DFFFFDFFFFFFFFFFDFDFFFFDFFFFDFF70E -:102E6000FF4FFFFFFFFFFFFFFFFFFFFEFFFFFFFD25 -:102E7000FFFFFFFFFEFF9FFFFFFFFFFFFFFFFFFFC3 -:102E8000FDFFFFFFFFFF7FFFFFFF7A3FFFFFFFFF19 -:102E9000FFFFFF7FFFFFFFFFFFFFFFFFFFFFFFF2CF -:102EA0007FFFFBFEFFBFEFF8FEFFBFFBFEFF8FECD7 -:102EB000FBFEFFBFF8F7FEFFBFEFFBFEFDBFCFEC51 -:102EC000FF3FEFDBF8FFBFCFFFF9FFFFBFFFFBFFC7 -:102ED000FFFFEFFBDFFFFFFFFFFFBFFFFFFFBBFFBA -:102EE000EFFBFEEFBFEEEBFBFEFFEFFEEEBFFEEBF8 -:102EF000FFEFFF17FF7EEBBBFEBFBEFBEF5BF7BD37 -:0A2F0000FBCFBFBFBBFB7ECCEFFF91 -:00000001FF - - * Copyright (C) 1999 BayCom GmbH - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that redistributions of source - * code retain the above copyright notice and this comment without - * modification. diff --git a/firmware/dabusb/firmware.HEX b/firmware/dabusb/firmware.HEX deleted file mode 100644 index 7c258df2b0a7..000000000000 --- a/firmware/dabusb/firmware.HEX +++ /dev/null @@ -1,649 +0,0 @@ -:02000000215786 -:0300030002016691 -:03000B0002016689 -:0300130002016681 -:03001B0002016679 -:0300230002016671 -:03002B0002016669 -:0300330002030FB6 -:03003B0002016659 -:03004300020100B7 -:03004B0002016649 -:0300530002016641 -:03005B000204BDDF -:0300630002016730 -:03010000020C5A94 -:030104000201ED08 -:030108000202519F -:03010C0002027C70 -:030110000202E404 -:0101140032B8 -:0101180032B4 -:03011C000205FDDC -:03012000020000DA -:03012400020000D6 -:0301280002043C92 -:03012C0002046A60 -:03013000020000CA -:03013400020000C6 -:03013800020000C2 -:03013C00020000BE -:03014000020000BA -:03014400020000B6 -:03014800020000B2 -:03014C00020000AE -:03015000020000AA -:03015400020000A6 -:0A01570075817FE5826003020161FB -:0501610012076F21648C -:010166003266 -:0E016700C0D0C086C082C083C0E0907F97E009 -:0E0175004480F0907F69F0F0F0F0F0F0F0F0D0 -:0E018300F0F0F0F0F0F0F0F0F0F0F0F0F0F04E -:0E019100F0F0F0F0F0F0F0F0F0F0907F97E07A -:03019F00557FF099 -:0E01A200907F9AE030E423907F68F0F0F0F058 -:0E01B000F0F0F0F0F0F0F0F0F0F0F0F0F0F021 -:0E01BE00F0F0F0F0F0F0F0F0F0F0F0F0F0F013 -:0E01CC00E5D8C2E3F5D8D0E0D083D082D0864B -:0301DA00D0D03250 -:0801DD0075860090FFC37C054C -:0701E500A3E582458370F9D8 -:0101EC0022F0 -:0E01ED00C0E0C0F0C082C083C002C003C0D01A -:0E01FB0075D000C086758600E591C2E4F591CE -:0D020900908800E0F541907FAB7402F0900A -:090216007FAB7402F0E5326021B7 -:04021F007A007B00E6 -:0B022300C3EA9418EB64809480501232 -:0E022E00907F69F0F0F0F0F0F0F0F00ABA0006 -:02023C00010BB4 -:02023E0080E35B -:02024000D08666 -:0E024200D0D0D003D002D083D082D0F0D0E054 -:01025000327B -:0E025100C0E0C0F0C082C083C0D075D000C035 -:0E025F0086758600E591C2E4F591907FAB7440 -:04026D0004F0D08643 -:0B027100D0D0D083D082D0F0D0E0329B -:0E027C00C0E0C0F0C082C083C002C003C00456 -:0E028A00C005C006C007C000C001C0D075D0BE -:0D02980000C086758600E591C2E4F59190E6 -:0C02A5007FAB7408F0756E00756F0212DC -:0602B1001144757039755F -:0602B700710C75720212C9 -:0C02BD001175907FD6E4F075D820D08633 -:0E02C900D0D0D001D000D007D006D005D00490 -:0D02D700D003D002D083D082D0F0D0E0322E -:0E02E400C0E0C0F0C082C083C0D075D000C0A2 -:0E02F20086758600E591C2E4F591907FAB74AD -:0403000010F0D086A3 -:0B030400D0D0D083D082D0F0D0E03207 -:0E030F00C0E0C0F0C082C083C002C003C004C2 -:0E031D00C005C006C007C000C001C0D075D02A -:0C032B0000C086758600756E00756F02BC -:0703370012114475704075BE -:06033E00710C7572021241 -:0E0344001175907FD67402F0907FD67406F08B -:0503520075D810D086F3 -:0E035700D0D0D001D000D007D006D005D00401 -:0D036500D003D002D083D082D0F0D0E0329F -:0D037200907FA57480F0907FA6749AF01221 -:0C037F00101B907FA6E542F012101B90AE -:0D038B007FA6E543F012101B907FA5744083 -:01039800F074 -:010399002241 -:0D039A00907FA57480F0907FA6749AF012F9 -:0C03A700101B907FA6E544F012101B9084 -:0C03B3007FA6E545F012101B907FA6E528 -:0B03BF0046F012101B907FA57440F068 -:0103CA002210 -:0A03CB0075440275450075460012E6 -:0903D500039A75420375430012FE -:0203DE000372A8 -:0103E00022FA -:0C03E100908800E536F09088007410252C -:0903ED0036F01201DD75420175C4 -:0903F600431812037275440275EC -:0903FF00450075460012039A75D1 -:08040800420375434412037224 -:0104100022C9 -:0E041100C0E0C0F0C082C083C0D075D000C073 -:0E041F0086758600E591C2E4F591907FAA747F -:04042D0002F0D08683 -:0B043100D0D0D083D082D0F0D0E032D9 -:0E043C00C0E0C0F0C082C083C0D075D000C048 -:0E044A0086758600E591C2E4F591907FA97455 -:0704580004F0753001D086AD -:0B045F00D0D0D083D082D0F0D0E032AB -:0E046A00C0E0C0F0C082C083C0D075D000C01A -:0E04780086758600E591C2E4F591907FAA7426 -:0704860004F0753101D0867E -:0B048D00D0D0D083D082D0F0D0E0327D -:0E049800C0E0C0F0C082C083C0D075D000C0EC -:0C04A60086758600E591C2E5F591D086D0 -:0B04B200D0D0D083D082D0F0D0E03258 -:0E04BD00C0E0C0F0C082C083C0D075D000C0C7 -:0C04CB0086758600E591C2E7F591D086A9 -:0B04D700D0D0D083D082D0F0D0E03233 -:0C04E200907FEAE0FA8A20907F96E4F018 -:0104EE0022EB -:0704EF00907FEAE0FA8A2188 -:0104F60022E3 -:0E04F700901713E0FA901715E0FB74802AFAB4 -:0E05050074802BFBEA0303543FFCEAC423542A -:0E0513001FFA2CFAEB0303543FFCEBC42354F5 -:0B0521001FFB2CFB90170AE0FC60029F -:02052C007A0053 -:07052E0090170CE0FC6002D5 -:020535007B0049 -:0B053700EA2BFCC313F53A7544028B5D -:07054200458A4612039A7579 -:090549006E08756F001211447573 -:040552007047757108 -:080556000C757202121175858B -:05055E003A731211A028 -:010563002275 -:0E056400907F96E0FA907F9674806502F0908A -:0E0572007FEBE0FA907FEAE0FB907FEFE0FC89 -:0E0580003395E0FD8C057C00907FEEE0FE33AD -:0E058E0095E0FFEC2EFCED3FFD907FE9E0FED6 -:05059C00BE0102800316 -:0305A1000205F957 -:0605A400BC0121BD001E98 -:0E05AA00EAC40354F8FCEB25E0FD2C2400FC11 -:0E05B800E43417FD907EC0E0FE8C828D83F04F -:0205C600803182 -:0E05C800EAC40354F8FAEB25E0FB2AFA2400FB -:0E05D600FBE43417FC907EC0E0FD8B828C832A -:0E05E400F074012A2400FAE43417FB907EC163 -:0705F200E0FC8A828B83F01C -:0305F90075380151 -:0105FC0022DC -:0E05FD00C0E0C0F0C082C083C002C003C004D2 -:0E060B00C005C006C007C000C001C0D075D039 -:0D06190000C086758600E591C2E4F5919061 -:0D0626007FAA7401F0120564753700D086BC -:0E063300D0D0D001D000D007D006D005D00422 -:0D064100D003D002D083D082D0F0D0E032C0 -:0E064E00907FEBE0FA907FEAE0FB907FEEE019 -:0E065C00FC3395E0FD907F96E0FE907F967453 -:0E066A00806506F0907F007401F0EAC403542E -:0E067800F8FEEB25E0FB2EFE2400FBE4341719 -:0E068600FF8B828F83E0FB74012E2400FEE4C4 -:0E0694003417FF8E828F83E0FE907FE9E0FF37 -:0306A200BF810A0B -:0A06A500907F00EBF0907F01EEF073 -:0806AF00907FE9E0FBBB821A19 -:0306B700BA010C79 -:0C06BA00907F00E4F0907F01E4F0800BE2 -:0B06C600907F00E4F0907F0174B5F01D -:0806D100907FE9E0FBBB831BF5 -:0306D900BA010D56 -:0D06DC00907F007401F0907F01E4F0800B2E -:0B06E900907F00E4F0907F017412F09D -:0806F400907FE9E0FBBB841CD0 -:0306FC00BA010D33 -:0D06FF00907F007401F0907F01E4F0800C0A -:0C070C00907F007480F0907F017401F079 -:05071800907FB5ECF03C -:01071D0022B9 -:0C071E0075360D908800741DF0756B801E -:0A072A00756C3C1210E2756B8075CF -:090734006C0F1210E2756B807568 -:09073D006C061210E2756B807568 -:070746006C011210E27A00C1 -:03074D00BAFF00F0 -:02075000500A4D -:0A075200C0021201DDD0020A80F19E -:0A075C00756B80756C3C1210E2759D -:080766006B80756C0F1210E2AC -:01076E002268 -:0E076F00907FA1E4F0907FAF7401F0907F9234 -:0E077D007402F0758E3175892175880075C87B -:0E078B0000758D4075984075C04075870075EB -:0907990020007521007522007595 -:0507A200230075470073 -:0707A700C3E5479420501147 -:0D07AE00E5472400F582E43417F583E4F0FC -:0407BB00054780E886 -:0907BF00E4F540F53FE4F53CF5DA -:0707C8003BE4F53EF53D7531 -:0B07CF003200753700753900907F93F1 -:0E07DA00743CF0907F9C74FFF0907F967480CA -:0E07E800F0907F947470F0907F9D748FF0906D -:0E07F6007F97E4F0907F9574C2F0907F987426 -:0E08040028F0907F9E7428F0907FF0E4F09032 -:0E0812007FF1E4F0907FF2E4F0907FF3E4F0E9 -:0E082000907FF4E4F0907FF5E4F0907FF6E432 -:0E082E00F0907FF7E4F0907FF8E4F0907FF90F -:0E083C007438F0907FFA74A0F0907FFB74A0E7 -:0E084A00F0907FFC74A0F0907FFD74A0F09001 -:0E0858007FFE74A0F0907FFF74A0F0907FE010 -:0E0866007403F0907FE17401F0907FDD7480E8 -:0B087400F012124312071E7A007B00F6 -:09087F00C3EA941EEB940050172B -:0C088800908800E0F54790880BE0F547F1 -:09089400907F68F00ABA00010B24 -:02089D0080E0F9 -:0C089F001203E1907FD6E4F07A007B00A9 -:0D08AB008A048B05C3EA94E0EB942E501AEA -:0E08B800C002C003C004C0051201DDD005D08F -:0A08C60004D003D0020ABA00010BAF -:0208D00080D9CD -:0D08D200907FD67402F0907FD67406F090EF -:0E08DF007FDE7405F0907FDF7405F0907FAC33 -:0E08ED00E4F0907FAD7405F075A88075F810EA -:0D08FB00907FAE740BF0907FE27488F09057 -:0C0908007FAB7408F075E81175320175C2 -:0C0914003100753000C004C0051204F76B -:0A092000D005D004753400753501D0 -:0D092A00907FAE7403F08C02BA00028003CF -:03093700020A3F72 -:0C093A00853334907F9D748FF0907F9780 -:0E0946007408F0907F9D7488F0907F9AE0FA1C -:0C09540074055AF533907F9D748FF0906D -:0D0960007F977402F0907F9D7482F0E53364 -:0D096D0025E0FA907F9AE05405FB4AF5332F -:02097A00600C0F -:0C097C00907F96E0FA907F9674804AF01D -:0B098800756E00756F00C004C0051202 -:0E0993001144D005D004901713E0FA74802AA6 -:0609A100FAE533B404295D -:0309A700BAA000F3 -:0209AA005024D7 -:0D09AC00901713E004FB0B901713EBF09075 -:0E09B9001713E0FB901715F0C002C004C00534 -:0909C7001204F7D005D004D0029F -:0509D000E533B402262E -:0609D500C374049A5020D7 -:0D09DB00901713E0FA1A1A901713EAF09023 -:0D09E8001713E0FA901715F0C004C00512B7 -:0609F50004F7D005D00458 -:0509FB00E533B4081D06 -:040A0000E534701950 -:0A0A040074012535540FF5358535D2 -:0C0A0E0075757600C004C0051213FED000 -:030A1A0005D00400 -:050A1D00E533B4011DEA -:040A2200E53470192E -:0A0A2600E53524FF540FF535853542 -:0C0A300075757600C004C0051213FED0DE -:030A3C0005D004DE -:0E0A3F00C004C0051201DDD005D004907F96E2 -:0E0A4D00E0FA907F96747F5AF0907F977408BD -:0A0A5B00F0C3EC9400ED9402400893 -:080A6500907F96E0FA20E608FC -:080A6D00C3E49C74089D5013C2 -:0E0A7500907F96E0FA907F9674406502F07CC8 -:050A8300007D0080056C -:050A88000CBC00010D93 -:050A8D00E538B4010E84 -:0D0A9200C004C0051204F7D005D00475386B -:010A9F000056 -:070AA000E531700302092A91 -:0A0AA700907FC9E0FA7003020C2DE5 -:0E0AB100907F96E0FA907F9674806502F09038 -:090ABF007DC0E0FABA2C028003AC -:030AC800020B36E8 -:050ACB007532007B0004 -:030AD000BB640004 -:020AD300501CB5 -:0E0AD500C002C003C004C0051201DDD005D070 -:0D0AE30004D003D00290880FE0F5470B808F -:010AF000DF26 -:0D0AF100C002C004C00512071E1203E1126E -:0C0AFE0004F7D005D004D002756E00751E -:0D0B0A006F01C002C004C005121144D005E7 -:090B1700D004D00275704D757117 -:0B0B20000C757202C002C004C0051278 -:0B0B2B001175D005D004D002020C2D83 -:030B3600BA2A3B9D -:0D0B3900907F987420F0C002C004C0051227 -:0E0B460001DDD005D004D002907F987428F015 -:020B54007B0024 -:030B5600BB0A00D7 -:050B59004003020C2D19 -:0E0B5E00C002C003C004C0051201DDD005D0E6 -:080B6C0004D003D0020B80E26B -:030B7400BA2B1A7F -:080B7700907FC9E0FBBB4012B6 -:0E0B7F00C002C004C005121205D005D004D07B -:040B8D0002020C2D27 -:030B9100BA101F78 -:0E0B9400907F96E0FB907F9674806503F0C022 -:0E0BA20002C004C00512103DD005D004D002E0 -:030BB000020C2D07 -:030BB300BA111262 -:0E0BB600C002C004C00512106AD005D004D0E1 -:040BC40002020C2DF0 -:030BC800BA12124C -:0E0BCB00C002C004C00512108FD005D004D0A7 -:040BD90002020C2DDB -:030BDD00BA130B3D -:0B0BE000907DC1E0FB908800F0804297 -:030BEB00BA141128 -:0E0BEE00C002C004C0051211DDD005D004D035 -:030BFC0002802E46 -:030BFF00BA151D07 -:0C0C0200907DC1E0F575907DC2E0F576B4 -:0E0C0E00C002C004C0051213FED005D004D0F1 -:030C1C0002800E45 -:030C1F00BA160BF7 -:0B0C2200C004C0051213A3D005D004CD -:0B0C2D00907FC9E4F075310002092A35 -:010C38002299 -:070C3900535550454E4400E5 -:070C4000524553554D4500DC -:060C470020566F6C200036 -:0D0C4D004441425553422076312E30300094 -:0E0C5A00C0E0C0F0C082C083C002C003C0046E -:0E0C6800C005C006C007C000C001C0D075D0D6 -:0D0C760000C086758600E591C2E4F59190FE -:0E0C83007FAB7401F0907FE8E0FA907FE9E02B -:060C9100FBBB0002800322 -:030C9700020D3813 -:030C9A00BA801409 -:0E0C9D00907F007401F0907F01E4F0907FB52D -:060CAB007402F0020ECD00 -:050CB100BA820280037D -:030CB600020D1D0F -:080CB900907FECE0FCBC01009F -:020CC1004021D0 -:060CC300C374079C401BF6 -:0E0CC900EC24FF25E0FD24C6F582E4347FF51F -:0D0CD70083E0FD530501907F00EDF0802BC0 -:030CE400BC8100D0 -:020CE7004021AA -:060CE900C374879C401B50 -:0E0CEF00EC247F25E0FC24B6F582E4347FF58A -:0D0CFD0083E0FC530401907F00ECF08005C3 -:050D0A00907F00E4F001 -:0E0D0F00907F01E4F0907FB57402F0020ECDEB -:050D1D00BA8102800311 -:030D2200020EC5F9 -:0E0D2500907F00E4F0907F01E4F0907FB574C1 -:050D330002F0020ECDEC -:030D3800BB012DCF -:060D3B00BA0003020ECD18 -:030D4100BA0211E2 -:0D0D4400755900C002C003120EF0D003D09C -:040D510002020ECDBF -:050D5500BA2102800339 -:030D5A00020ECDB9 -:0B0D5D00753701907FC5E4F0020ECD59 -:030D6800BB031FAB -:060D6B00BA0003020ECDE8 -:050D7100BA020280033C -:030D7600020ECD9D -:0D0D7900755901C002C003120EF0D003D066 -:040D860002020ECD8A -:030D8A00BB065451 -:050D8D00BA80028003A2 -:030D9200020EC589 -:080D9500907FEBE0FCBC0115AE -:0C0D9D007CFB7D0F8D067F00907FD4EE64 -:090DA900F0907FD5ECF0020ECDB4 -:0A0DB200907FEBE0FCBC020280031E -:030DBC00020EC55F -:0A0DBF00907FEAE0FCBC0002800314 -:030DC900020EC552 -:0C0DCC007C3B7D0F8D067F00907FD4EEF5 -:090DD800F0907FD5ECF0020ECD85 -:060DE100BB0703020EC572 -:030DE700BB081036 -:0D0DEA00AC48907F00ECF0907FB57401F0F4 -:030DF700020ECD1C -:030DFA00BB093101 -:050DFD00BA00028003B2 -:030E0200020EC518 -:0E0E0500907FEAE0FCC374019C5003020EC50E -:080E1300907FEAE0FCBC000A3C -:0A0E1B00901721E4F0901722E4F094 -:090E2500907FEAE0F548020ECDD1 -:030E2E00BB0A27D5 -:050E3100BA81028003FC -:030E3600020EC5E4 -:0E0E3900907FECE0FA2420FAE43417FC8A8261 -:0E0E47008C83E0FA907F00F0907FB57401F08C -:030E5500020ECDBD -:050E5800BB0B0280034A -:030E5D00020EA9D9 -:0D0E6000901720E4F0907FECE0FABA011A40 -:080E6D00907FEDE0FABA0012DB -:0E0E7500907FEAE0FA901721F0C0031204E229 -:040E8300D0038046D2 -:080E8700907FECE0FABA023E94 -:080E8F00907FEDE0FABA003695 -:0D0E9700C0031204EFD003907FEAE0FA9050 -:050EA4001722F080247C -:050EA900BB12028017DE -:050EAE00BB8102800D74 -:050EB300BB8302800872 -:050EB800BB8202800373 -:030EBD00BB8405EE -:050EC00012064E80083F -:080EC500907FB47403F0800675 -:060ECD00907FB47402F0F6 -:020ED300D086C7 -:0E0ED500D0D0D001D000D007D006D005D00478 -:0D0EE300D003D002D083D082D0F0D0E03216 -:0B0EF000907FECE0F55AC39401401D18 -:070EFB00C37407955A40166D -:0D0F0200E55A24FF25E0FA24C6F582E43408 -:090F0F007FF583AA59EAF0802263 -:070F1800C3E55A9481401B60 -:070F1F00C37487955A4014CA -:0D0F2600E55A24FF25E0FA24B6F582E434F4 -:070F33007FF583AA59EAF0E3 -:010F3A002294 -:0E0F3B000902BA000301004000090400000092 -:0E0F49000101000009240100013D0001010C1E -:0E0F570024020110070002030000000D240612 -:0E0F650003010215000300030000092403022B -:0E0F7300010100010009240304020300030031 -:0E0F8100092403050306000100090401000015 -:0E0F8F00010200000904010101010200000737 -:0E0F9D002401020101000B24020102021001D6 -:0E0FAB0080BB00090588050001010000072534 -:0E0FB900010000000009040200020000000018 -:0E0FC7000705820240000007050202400000FC -:0E0FD50009040201030000000007058202402B -:0E0FE30000000705020240000009058905A074 -:0A0FF1000101000000FFFFFFFF00F8 -:0E0FFB00120100010000004047059999000115 -:0E10090000000001000000000000000902BA13 -:0410170000030100D1 -:02101B007A0059 -:03101D00BA050011 -:02102000501767 -:08102200907FA5E0FB30E00522 -:05102A00900001800DA3 -:0A102F00C0021201DDD0020A80E4C5 -:0310390090000123 -:01103C002291 -:0E103D00907DC1E0F9A3E0FAA3E0FB7C007D0A -:04104B007EEB6012C6 -:0E104F0089828A83E0A3A982AA838C828D8382 -:04105D00F00CDBEECA -:08106100907DC3E0907FB9F01F -:011069002264 -:0E106A00907DC1E0F9A3E0FAA3E0FB7CC47D19 -:041078007DEB60E5C7 -:0E107C008C828D83E00C89828A83F0A3A98286 -:04108A00AA83DBEE6C -:01108E00223F -:0E108F00907FA57480F00586907DC1E00586F7 -:0E109D00A3F012101B907FA60586A3A3E0F916 -:0510AB006016A305869C -:0D10B000907FA60586E0A30586F0C0011222 -:0610BD00101BD001D9ED6B -:0610C300907FA57440F0CF -:0110C9002204 -:0810CA009088027401F07A0025 -:0310D200BAFF0062 -:0210D500500ABF -:0A10D700C0021201DDD0020A80F110 -:0110E10022EC -:0510E200E56BB4C0083D -:0810E700908803E56CF080061F -:0610EF00908802E56CF0A0 -:0410F5007A007B0002 -:0B10F900C3EA9432EB6480948050073F -:051104000ABA00010B16 -:0211090080EE76 -:01110B0022C1 -:0A110C00908803E56DF005397A00C4 -:03111600BA2800F4 -:02111900500381 -:03111B000A80F84F -:05111E00E539B41008E2 -:0811230090880274C0F0800EF8 -:05112B00E539B42009C4 -:091130009088027480F07539000A -:021139007A003A -:03113B00BA2800CF -:02113E0050035C -:031140000A80F82A -:011143002289 -:04114400E56F6002F1 -:0211480080071E -:07114A007A007539008005F1 -:051151007A4075391021 -:09115600E56E2AFAE56E2539F573 -:0A115F003990880274802AF07A00AB -:08116900C3EA648094A850035E -:031171000A80F5FC -:011174002258 -:06117500AA70AB71AC7220 -:0C117B008A828B838CF01214EEFD601849 -:0D1187008D6DC002C003C00412110CD00415 -:09119400D003D0020ABA00010BDD -:02119D0080DCF4 -:01119F00222D -:0D11A000E573C4540FFA53020FC374099A8B -:0211AD005006EA -:0611AF0074372AFB8004E6 -:0411B50074302AFB6D -:0C11B9008B6DC00312110CD003AA7353FD -:0811C500020FC374099A5006E1 -:0611CD0074372AFB8004C8 -:0411D30074302AFB4F -:0511D7008B6D12110CEC -:0111DC0022F0 -:0711DD00907DC3E0FA600FF2 -:0C11E400907DC1E0F56E907DC2E0F56FDB -:0311F00012114495 -:0C11F300907DFFE4F07570C475717D758F -:0511FF007201121175E0 -:0112040022C7 -:021205007A0469 -:03120700BA4000EA -:02120A0050365C -:0E120C00EA24C0F582E4347DF583E0FB7C002B -:03121A00BC08000D -:02121D0050205F -:06121F008B05ED30E70B2A -:0B122500907F967442F074C3F08008C4 -:08123000907F96E4F07481F058 -:07123800EB25E0FB0C80DB5D -:03123F000A80C55D -:011242002289 -:041243007A007BEFC3 -:03124700BA1000DA -:02124A00502032 -:0E124C0074112BFB2400FCE43418FD8C828D01 -:0E125A0083E4F0EA2400F582E43419F583E41D -:04126800F00A80DB2D -:01126C00225F -:0E126D0074F82400F58274033484F583E4F0F1 -:0E127B0074F92400F58274033484F583E4F0E2 -:0E12890074FA2400F58274033484F583E4F0D3 -:0E12970074FB2400F58274033484F583E4F0C4 -:0E12A50074FF2400F58274033484F583E4F0B2 -:0112B3002218 -:0E12B4001203CB12126D7AC07B877C0174018D -:0E12C2002AFDE43BFE8C078A828B838CF0743D -:0E12D000011214BF2DFAE43EFB8F048D828EB6 -:0E12DE00838FF074061214BF74012AFDE43BE6 -:0E12EC00FE8C078A828B838CF0E41214BF7490 -:0E12FA00012DFAE43EFB8F048D828E838FF06F -:0E130800740B1214BF74012AFDE43BFE8C0727 -:0E1316008A828B838CF074081214BF74012D30 -:0E132400FAE43EFB8F048D828E838FF07401FD -:0E1332001214BF2AFDE43BFE8C078A828B83D7 -:0E1340008CF0E41214BF74012DFAE43EFB8F12 -:0E134E00048D828E838FF074031214BF7D0015 -:03135C00BD0600CB -:02135F0050122A -:0B1361008A828B838CF00ABA00010B1B -:07136C00E41214BF0D80E93B -:0D1373008A828B838CF0E5741214BF74F92C -:0E1380002400F58274033484F583740FF07436 -:0E138E00FE2400F58274033484F5837401F0AC -:06139C001203E11204F748 -:0113A2002228 -:0D13A300907DC1E0FA2400FBE43419FC90B9 -:0E13B0007DC2E0FD8B828C83F075F011EAA403 -:0313BE00FA7B00B7 -:0313C100BB10005E -:0213C4005024B3 -:0E13C600EA2400FCE43418FDEB2CFCE43DFDB1 -:0E13D40074042B24C0F582E4347DF583E0FE22 -:0813E2008C828D83F00B80D793 -:0E13EA00EA2400FAE43418FB74102AF582E4B9 -:0513F8003BF583E4F069 -:0113FD0022CD -:0413FE00E57660022E -:02140200801652 -:0C140400740F5575FA8A752400F582E417 -:0A1410003419F583E0F5741212B4EC -:0A141A001210CA756E00756F001203 -:0614240011447570B9755A -:06142A007114757202123C -:0B1430001175E576B402047401800120 -:01143B00E4CC -:03143C00FA700F34 -:0C143F0074012575F573C0021211A0D0D5 -:03144B0002800A12 -:0A144E00857573C0021211A0D002D0 -:0C145800756E00756F01C002121144D0C7 -:0414640002EA701A0E -:0D14680075F011E575A4FA2400FAE43418BB -:09147500FB8A708B717572011283 -:04147E00117580362E -:021482007A00EE -:03148400BA10009B -:02148700502FE4 -:0D148900EA2400F582E43419F583E0FBE568 -:0414960075B5031B0A -:0E149A0075F011EAA4FB2400FBE43418FC8B6F -:0914A800708C71757201C0021212 -:0414B1001175D002DF -:0314B5000A80CCDE -:0114B8002211 -:0614B90050726F67200075 -:0E14BF00C8C0E0C8C0E0E5F0600B14600F1478 -:0714CD00601114601280158C -:0714D400D0E0A882F6800EB3 -:0514DB00D0E0F08009E3 -:0414E000D0E08005D3 -:0514E400D0E0A882F237 -:0414E900C8D0E0C8BF -:0114ED0022DC -:0E14EE00C8C0E0E5F0600D14600F14600F142C -:0614FC00601074FF800F78 -:05150200A882E6800A4A -:03150700E080077A -:04150A00E4938003E3 -:03150E00A882E2CE -:04151100F8D0E0C866 -:0115150022B3 -:00000001FF - - * Copyright (C) 1999 BayCom GmbH - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that redistributions of source - * code retain the above copyright notice and this comment without - * modification. -- cgit v1.2.3-59-g8ed1b From 87c4d1a7dce956b86e34329ed1b11a751ba9a8ea Mon Sep 17 00:00:00 2001 From: Damian Hobson-Garcia Date: Fri, 16 Nov 2012 14:46:10 +0900 Subject: drivers: uio_dmem_genirq: Don't use DMA_ERROR_CODE to indicate unmapped regions DMA_ERROR_CODE is not defined on all architectures and is architecture specific. Instead, use the constant, ~0 to indicate unmapped regions. Reported-by: Fengguang Wu Reported-by: Geert Uytterhoeven Signed-off-by: Damian Hobson-Garcia Cc: "Hans J. Koch" Signed-off-by: Greg Kroah-Hartman --- Documentation/DocBook/uio-howto.tmpl | 2 +- drivers/uio/uio_dmem_genirq.c | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) (limited to 'Documentation') diff --git a/Documentation/DocBook/uio-howto.tmpl b/Documentation/DocBook/uio-howto.tmpl index fdbf86fcfccd..ddb05e98af0d 100644 --- a/Documentation/DocBook/uio-howto.tmpl +++ b/Documentation/DocBook/uio-howto.tmpl @@ -771,7 +771,7 @@ framework to set up sysfs files for this region. Simply leave it alone. /sys/class/uio/uioX/maps/mapY/*. The dynmaic memory regions will be freed when the UIO device file is closed. When no processes are holding the device file open, the address - returned to userspace is DMA_ERROR_CODE. + returned to userspace is ~0. diff --git a/drivers/uio/uio_dmem_genirq.c b/drivers/uio/uio_dmem_genirq.c index d8bbe0783cdc..7be8d0421cae 100644 --- a/drivers/uio/uio_dmem_genirq.c +++ b/drivers/uio/uio_dmem_genirq.c @@ -29,6 +29,7 @@ #include #define DRIVER_NAME "uio_dmem_genirq" +#define DMEM_MAP_ERROR (~0) struct uio_dmem_genirq_platdata { struct uio_info *uioinfo; @@ -60,6 +61,7 @@ static int uio_dmem_genirq_open(struct uio_info *info, struct inode *inode) addr = dma_alloc_coherent(&priv->pdev->dev, uiomem->size, (dma_addr_t *)&uiomem->addr, GFP_KERNEL); if (!addr) { + uiomem->addr = DMEM_MAP_ERROR; ret = -ENOMEM; break; } @@ -95,7 +97,7 @@ static int uio_dmem_genirq_release(struct uio_info *info, struct inode *inode) dma_free_coherent(&priv->pdev->dev, uiomem->size, priv->dmem_region_vaddr[dmem_region++], uiomem->addr); - uiomem->addr = DMA_ERROR_CODE; + uiomem->addr = DMEM_MAP_ERROR; ++uiomem; } @@ -238,7 +240,7 @@ static int uio_dmem_genirq_probe(struct platform_device *pdev) break; } uiomem->memtype = UIO_MEM_PHYS; - uiomem->addr = DMA_ERROR_CODE; + uiomem->addr = DMEM_MAP_ERROR; uiomem->size = pdata->dynamic_region_sizes[i]; ++uiomem; } -- cgit v1.2.3-59-g8ed1b From 743179849015dc71bb2ea63d8cd4bfa7fdfb4bc6 Mon Sep 17 00:00:00 2001 From: Jean-Christophe PLAGNIOL-VILLARD Date: Thu, 15 Nov 2012 20:19:57 +0100 Subject: of_spi: add generic binding support to specify cs gpio This will allow to use gpio for chip select with no modification in the driver binding When use the cs-gpios, the gpio number will be passed via the cs_gpio field and the number of chip select will automatically increased with max(hw cs, gpio cs). So if for example the controller has 2 CS lines, and the cs-gpios property looks like this: cs-gpios = <&gpio1 0 0> <0> <&gpio1 1 0> <&gpio1 2 0>; Then it should be configured so that num_chipselect = 4 with the following mapping: cs0 : &gpio1 0 0 cs1 : native cs2 : &gpio1 1 0 cs3 : &gpio1 2 0 Signed-off-by: Jean-Christophe PLAGNIOL-VILLARD Cc: devicetree-discuss@lists.ozlabs.org Cc: spi-devel-general@lists.sourceforge.net Signed-off-by: Richard Genoud [grant.likely: fixed up type of cs count so min() can do type checking] Signed-off-by: Grant Likely --- Documentation/devicetree/bindings/spi/spi-bus.txt | 20 +++++++++ drivers/spi/spi.c | 54 +++++++++++++++++++++-- include/linux/spi/spi.h | 3 ++ 3 files changed, 74 insertions(+), 3 deletions(-) (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/spi/spi-bus.txt b/Documentation/devicetree/bindings/spi/spi-bus.txt index d2c33d0f533e..77a8b0d39b54 100644 --- a/Documentation/devicetree/bindings/spi/spi-bus.txt +++ b/Documentation/devicetree/bindings/spi/spi-bus.txt @@ -12,6 +12,7 @@ The SPI master node requires the following properties: - #size-cells - should be zero. - compatible - name of SPI bus controller following generic names recommended practice. +- cs-gpios - (optional) gpios chip select. No other properties are required in the SPI bus node. It is assumed that a driver for an SPI bus device will understand that it is an SPI bus. However, the binding does not attempt to define the specific method for @@ -24,6 +25,22 @@ support describing the chip select layout. Optional property: - num-cs : total number of chipselects +If cs-gpios is used the number of chip select will automatically increased +with max(cs-gpios > hw cs) + +So if for example the controller has 2 CS lines, and the cs-gpios +property looks like this: + +cs-gpios = <&gpio1 0 0> <0> <&gpio1 1 0> <&gpio1 2 0>; + +Then it should be configured so that num_chipselect = 4 with the +following mapping: + +cs0 : &gpio1 0 0 +cs1 : native +cs2 : &gpio1 1 0 +cs3 : &gpio1 2 0 + SPI slave nodes must be children of the SPI master node and can contain the following properties. - reg - (required) chip select address of device. @@ -37,6 +54,9 @@ contain the following properties. - spi-cs-high - (optional) Empty property indicating device requires chip select active high +If a gpio chipselect is used for the SPI slave the gpio number will be passed +via the cs_gpio + SPI example for an MPC5200 SPI bus: spi@f00 { #address-cells = <1>; diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index 84c2861d6f4d..1587a4a5ff41 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -30,6 +30,7 @@ #include #include #include +#include #include #include #include @@ -327,6 +328,7 @@ struct spi_device *spi_alloc_device(struct spi_master *master) spi->dev.parent = &master->dev; spi->dev.bus = &spi_bus_type; spi->dev.release = spidev_release; + spi->cs_gpio = -EINVAL; device_initialize(&spi->dev); return spi; } @@ -344,15 +346,16 @@ EXPORT_SYMBOL_GPL(spi_alloc_device); int spi_add_device(struct spi_device *spi) { static DEFINE_MUTEX(spi_add_lock); - struct device *dev = spi->master->dev.parent; + struct spi_master *master = spi->master; + struct device *dev = master->dev.parent; struct device *d; int status; /* Chipselects are numbered 0..max; validate. */ - if (spi->chip_select >= spi->master->num_chipselect) { + if (spi->chip_select >= master->num_chipselect) { dev_err(dev, "cs%d >= max %d\n", spi->chip_select, - spi->master->num_chipselect); + master->num_chipselect); return -EINVAL; } @@ -376,6 +379,9 @@ int spi_add_device(struct spi_device *spi) goto done; } + if (master->cs_gpios) + spi->cs_gpio = master->cs_gpios[spi->chip_select]; + /* Drivers may modify this initial i/o setup, but will * normally rely on the device being setup. Devices * using SPI_CS_HIGH can't coexist well otherwise... @@ -946,6 +952,44 @@ struct spi_master *spi_alloc_master(struct device *dev, unsigned size) } EXPORT_SYMBOL_GPL(spi_alloc_master); +#ifdef CONFIG_OF +static int of_spi_register_master(struct spi_master *master) +{ + u16 nb; + int i, *cs; + struct device_node *np = master->dev.of_node; + + if (!np) + return 0; + + nb = of_gpio_named_count(np, "cs-gpios"); + master->num_chipselect = max(nb, master->num_chipselect); + + if (nb < 1) + return 0; + + cs = devm_kzalloc(&master->dev, + sizeof(int) * master->num_chipselect, + GFP_KERNEL); + master->cs_gpios = cs; + + if (!master->cs_gpios) + return -ENOMEM; + + memset(cs, -EINVAL, master->num_chipselect); + + for (i = 0; i < nb; i++) + cs[i] = of_get_named_gpio(np, "cs-gpios", i); + + return 0; +} +#else +static int of_spi_register_master(struct spi_master *master) +{ + return 0; +} +#endif + /** * spi_register_master - register SPI master controller * @master: initialized master, originally from spi_alloc_master() @@ -977,6 +1021,10 @@ int spi_register_master(struct spi_master *master) if (!dev) return -ENODEV; + status = of_spi_register_master(master); + if (status) + return status; + /* even if it's just one always-selected device, there must * be at least one chipselect */ diff --git a/include/linux/spi/spi.h b/include/linux/spi/spi.h index fa702aeb5038..f62918946d86 100644 --- a/include/linux/spi/spi.h +++ b/include/linux/spi/spi.h @@ -90,6 +90,7 @@ struct spi_device { void *controller_state; void *controller_data; char modalias[SPI_NAME_SIZE]; + int cs_gpio; /* chip select gpio */ /* * likely need more hooks for more protocol options affecting how @@ -362,6 +363,8 @@ struct spi_master { int (*transfer_one_message)(struct spi_master *master, struct spi_message *mesg); int (*unprepare_transfer_hardware)(struct spi_master *master); + /* gpio chip select */ + int *cs_gpios; }; static inline void *spi_master_get_devdata(struct spi_master *master) -- cgit v1.2.3-59-g8ed1b From f40f38d1dc8dee0c19b2102ea549b696116f61da Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Wed, 21 Nov 2012 13:43:05 -0200 Subject: ARM: mx5: Replace clk_register_clkdev with clock DT lookup Similarly as it was done for mx6q, use a DT lookup in order to make maintainance task for the clock devices easier. Signed-off-by: Fabio Estevam Acked-by: Shawn Guo Signed-off-by: Sascha Hauer --- .../devicetree/bindings/clock/imx5-clock.txt | 191 +++++++++++++++++++++ arch/arm/boot/dts/imx51.dtsi | 39 +++++ arch/arm/boot/dts/imx53.dtsi | 48 ++++++ arch/arm/mach-imx/clk-imx51-imx53.c | 26 +-- arch/arm/mach-imx/imx51-dt.c | 28 +-- arch/arm/mach-imx/mach-imx53.c | 31 +--- 6 files changed, 293 insertions(+), 70 deletions(-) create mode 100644 Documentation/devicetree/bindings/clock/imx5-clock.txt (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/clock/imx5-clock.txt b/Documentation/devicetree/bindings/clock/imx5-clock.txt new file mode 100644 index 000000000000..04ad47876be0 --- /dev/null +++ b/Documentation/devicetree/bindings/clock/imx5-clock.txt @@ -0,0 +1,191 @@ +* Clock bindings for Freescale i.MX5 + +Required properties: +- compatible: Should be "fsl,-ccm" , where can be imx51 or imx53 +- reg: Address and length of the register set +- interrupts: Should contain CCM interrupt +- #clock-cells: Should be <1> + +The clock consumer should specify the desired clock by having the clock +ID in its "clocks" phandle cell. The following is a full list of i.MX5 +clocks and IDs. + + Clock ID + --------------------------- + dummy 0 + ckil 1 + osc 2 + ckih1 3 + ckih2 4 + ahb 5 + ipg 6 + axi_a 7 + axi_b 8 + uart_pred 9 + uart_root 10 + esdhc_a_pred 11 + esdhc_b_pred 12 + esdhc_c_s 13 + esdhc_d_s 14 + emi_sel 15 + emi_slow_podf 16 + nfc_podf 17 + ecspi_pred 18 + ecspi_podf 19 + usboh3_pred 20 + usboh3_podf 21 + usb_phy_pred 22 + usb_phy_podf 23 + cpu_podf 24 + di_pred 25 + tve_di 26 + tve_s 27 + uart1_ipg_gate 28 + uart1_per_gate 29 + uart2_ipg_gate 30 + uart2_per_gate 31 + uart3_ipg_gate 32 + uart3_per_gate 33 + i2c1_gate 34 + i2c2_gate 35 + gpt_ipg_gate 36 + pwm1_ipg_gate 37 + pwm1_hf_gate 38 + pwm2_ipg_gate 39 + pwm2_hf_gate 40 + gpt_hf_gate 41 + fec_gate 42 + usboh3_per_gate 43 + esdhc1_ipg_gate 44 + esdhc2_ipg_gate 45 + esdhc3_ipg_gate 46 + esdhc4_ipg_gate 47 + ssi1_ipg_gate 48 + ssi2_ipg_gate 49 + ssi3_ipg_gate 50 + ecspi1_ipg_gate 51 + ecspi1_per_gate 52 + ecspi2_ipg_gate 53 + ecspi2_per_gate 54 + cspi_ipg_gate 55 + sdma_gate 56 + emi_slow_gate 57 + ipu_s 58 + ipu_gate 59 + nfc_gate 60 + ipu_di1_gate 61 + vpu_s 62 + vpu_gate 63 + vpu_reference_gate 64 + uart4_ipg_gate 65 + uart4_per_gate 66 + uart5_ipg_gate 67 + uart5_per_gate 68 + tve_gate 69 + tve_pred 70 + esdhc1_per_gate 71 + esdhc2_per_gate 72 + esdhc3_per_gate 73 + esdhc4_per_gate 74 + usb_phy_gate 75 + hsi2c_gate 76 + mipi_hsc1_gate 77 + mipi_hsc2_gate 78 + mipi_esc_gate 79 + mipi_hsp_gate 80 + ldb_di1_div_3_5 81 + ldb_di1_div 82 + ldb_di0_div_3_5 83 + ldb_di0_div 84 + ldb_di1_gate 85 + can2_serial_gate 86 + can2_ipg_gate 87 + i2c3_gate 88 + lp_apm 89 + periph_apm 90 + main_bus 91 + ahb_max 92 + aips_tz1 93 + aips_tz2 94 + tmax1 95 + tmax2 96 + tmax3 97 + spba 98 + uart_sel 99 + esdhc_a_sel 100 + esdhc_b_sel 101 + esdhc_a_podf 102 + esdhc_b_podf 103 + ecspi_sel 104 + usboh3_sel 105 + usb_phy_sel 106 + iim_gate 107 + usboh3_gate 108 + emi_fast_gate 109 + ipu_di0_gate 110 + gpc_dvfs 111 + pll1_sw 112 + pll2_sw 113 + pll3_sw 114 + ipu_di0_sel 115 + ipu_di1_sel 116 + tve_ext_sel 117 + mx51_mipi 118 + pll4_sw 119 + ldb_di1_sel 120 + di_pll4_podf 121 + ldb_di0_sel 122 + ldb_di0_gate 123 + usb_phy1_gate 124 + usb_phy2_gate 125 + per_lp_apm 126 + per_pred1 127 + per_pred2 128 + per_podf 129 + per_root 130 + ssi_apm 131 + ssi1_root_sel 132 + ssi2_root_sel 133 + ssi3_root_sel 134 + ssi_ext1_sel 135 + ssi_ext2_sel 136 + ssi_ext1_com_sel 137 + ssi_ext2_com_sel 138 + ssi1_root_pred 139 + ssi1_root_podf 140 + ssi2_root_pred 141 + ssi2_root_podf 142 + ssi_ext1_pred 143 + ssi_ext1_podf 144 + ssi_ext2_pred 145 + ssi_ext2_podf 146 + ssi1_root_gate 147 + ssi2_root_gate 148 + ssi3_root_gate 149 + ssi_ext1_gate 150 + ssi_ext2_gate 151 + epit1_ipg_gate 152 + epit1_hf_gate 153 + epit2_ipg_gate 154 + epit2_hf_gate 155 + can_sel 156 + can1_serial_gate 157 + can1_ipg_gate 158 + +Examples (for mx53): + +clks: ccm@53fd4000{ + compatible = "fsl,imx53-ccm"; + reg = <0x53fd4000 0x4000>; + interrupts = <0 71 0x04 0 72 0x04>; + #clock-cells = <1>; +}; + +can1: can@53fc8000 { + compatible = "fsl,imx53-flexcan", "fsl,p1010-flexcan"; + reg = <0x53fc8000 0x4000>; + interrupts = <82>; + clocks = <&clks 158>, <&clks 157>; + clock-names = "ipg", "per"; + status = "disabled"; +}; diff --git a/arch/arm/boot/dts/imx51.dtsi b/arch/arm/boot/dts/imx51.dtsi index 44c7af791fa5..8cf69c702553 100644 --- a/arch/arm/boot/dts/imx51.dtsi +++ b/arch/arm/boot/dts/imx51.dtsi @@ -87,6 +87,8 @@ compatible = "fsl,imx51-esdhc"; reg = <0x70004000 0x4000>; interrupts = <1>; + clocks = <&clks 44>, <&clks 0>, <&clks 71>; + clock-names = "ipg", "ahb", "per"; status = "disabled"; }; @@ -94,6 +96,8 @@ compatible = "fsl,imx51-esdhc"; reg = <0x70008000 0x4000>; interrupts = <2>; + clocks = <&clks 45>, <&clks 0>, <&clks 72>; + clock-names = "ipg", "ahb", "per"; status = "disabled"; }; @@ -101,6 +105,8 @@ compatible = "fsl,imx51-uart", "fsl,imx21-uart"; reg = <0x7000c000 0x4000>; interrupts = <33>; + clocks = <&clks 32>, <&clks 33>; + clock-names = "ipg", "per"; status = "disabled"; }; @@ -110,6 +116,8 @@ compatible = "fsl,imx51-ecspi"; reg = <0x70010000 0x4000>; interrupts = <36>; + clocks = <&clks 51>, <&clks 52>; + clock-names = "ipg", "per"; status = "disabled"; }; @@ -117,6 +125,7 @@ compatible = "fsl,imx51-ssi", "fsl,imx21-ssi"; reg = <0x70014000 0x4000>; interrupts = <30>; + clocks = <&clks 49>; fsl,fifo-depth = <15>; fsl,ssi-dma-events = <25 24 23 22>; /* TX0 RX0 TX1 RX1 */ status = "disabled"; @@ -126,6 +135,8 @@ compatible = "fsl,imx51-esdhc"; reg = <0x70020000 0x4000>; interrupts = <3>; + clocks = <&clks 46>, <&clks 0>, <&clks 73>; + clock-names = "ipg", "ahb", "per"; status = "disabled"; }; @@ -133,6 +144,8 @@ compatible = "fsl,imx51-esdhc"; reg = <0x70024000 0x4000>; interrupts = <4>; + clocks = <&clks 47>, <&clks 0>, <&clks 74>; + clock-names = "ipg", "ahb", "per"; status = "disabled"; }; }; @@ -209,12 +222,14 @@ compatible = "fsl,imx51-wdt", "fsl,imx21-wdt"; reg = <0x73f98000 0x4000>; interrupts = <58>; + clocks = <&clks 0>; }; wdog@73f9c000 { /* WDOG2 */ compatible = "fsl,imx51-wdt", "fsl,imx21-wdt"; reg = <0x73f9c000 0x4000>; interrupts = <59>; + clocks = <&clks 0>; status = "disabled"; }; @@ -398,6 +413,8 @@ compatible = "fsl,imx51-uart", "fsl,imx21-uart"; reg = <0x73fbc000 0x4000>; interrupts = <31>; + clocks = <&clks 28>, <&clks 29>; + clock-names = "ipg", "per"; status = "disabled"; }; @@ -405,8 +422,17 @@ compatible = "fsl,imx51-uart", "fsl,imx21-uart"; reg = <0x73fc0000 0x4000>; interrupts = <32>; + clocks = <&clks 30>, <&clks 31>; + clock-names = "ipg", "per"; status = "disabled"; }; + + clks: ccm@73fd4000{ + compatible = "fsl,imx51-ccm"; + reg = <0x73fd4000 0x4000>; + interrupts = <0 71 0x04 0 72 0x04>; + #clock-cells = <1>; + }; }; aips@80000000 { /* AIPS2 */ @@ -422,6 +448,8 @@ compatible = "fsl,imx51-ecspi"; reg = <0x83fac000 0x4000>; interrupts = <37>; + clocks = <&clks 53>, <&clks 54>; + clock-names = "ipg", "per"; status = "disabled"; }; @@ -429,6 +457,8 @@ compatible = "fsl,imx51-sdma", "fsl,imx35-sdma"; reg = <0x83fb0000 0x4000>; interrupts = <6>; + clocks = <&clks 56>, <&clks 56>; + clock-names = "ipg", "ahb"; fsl,sdma-ram-script-name = "imx/sdma/sdma-imx51.bin"; }; @@ -438,6 +468,8 @@ compatible = "fsl,imx51-cspi", "fsl,imx35-cspi"; reg = <0x83fc0000 0x4000>; interrupts = <38>; + clocks = <&clks 55>, <&clks 0>; + clock-names = "ipg", "per"; status = "disabled"; }; @@ -447,6 +479,7 @@ compatible = "fsl,imx51-i2c", "fsl,imx21-i2c"; reg = <0x83fc4000 0x4000>; interrupts = <63>; + clocks = <&clks 35>; status = "disabled"; }; @@ -456,6 +489,7 @@ compatible = "fsl,imx51-i2c", "fsl,imx21-i2c"; reg = <0x83fc8000 0x4000>; interrupts = <62>; + clocks = <&clks 34>; status = "disabled"; }; @@ -463,6 +497,7 @@ compatible = "fsl,imx51-ssi", "fsl,imx21-ssi"; reg = <0x83fcc000 0x4000>; interrupts = <29>; + clocks = <&clks 48>; fsl,fifo-depth = <15>; fsl,ssi-dma-events = <29 28 27 26>; /* TX0 RX0 TX1 RX1 */ status = "disabled"; @@ -478,6 +513,7 @@ compatible = "fsl,imx51-nand"; reg = <0x83fdb000 0x1000 0xcfff0000 0x10000>; interrupts = <8>; + clocks = <&clks 60>; status = "disabled"; }; @@ -485,6 +521,7 @@ compatible = "fsl,imx51-ssi", "fsl,imx21-ssi"; reg = <0x83fe8000 0x4000>; interrupts = <96>; + clocks = <&clks 50>; fsl,fifo-depth = <15>; fsl,ssi-dma-events = <47 46 37 35>; /* TX0 RX0 TX1 RX1 */ status = "disabled"; @@ -494,6 +531,8 @@ compatible = "fsl,imx51-fec", "fsl,imx27-fec"; reg = <0x83fec000 0x4000>; interrupts = <87>; + clocks = <&clks 42>, <&clks 42>, <&clks 42>; + clock-names = "ipg", "ahb", "ptp"; status = "disabled"; }; }; diff --git a/arch/arm/boot/dts/imx53.dtsi b/arch/arm/boot/dts/imx53.dtsi index 8317a1727118..3be27723e8ab 100644 --- a/arch/arm/boot/dts/imx53.dtsi +++ b/arch/arm/boot/dts/imx53.dtsi @@ -92,6 +92,8 @@ compatible = "fsl,imx53-esdhc"; reg = <0x50004000 0x4000>; interrupts = <1>; + clocks = <&clks 44>, <&clks 0>, <&clks 71>; + clock-names = "ipg", "ahb", "per"; status = "disabled"; }; @@ -99,6 +101,8 @@ compatible = "fsl,imx53-esdhc"; reg = <0x50008000 0x4000>; interrupts = <2>; + clocks = <&clks 45>, <&clks 0>, <&clks 72>; + clock-names = "ipg", "ahb", "per"; status = "disabled"; }; @@ -106,6 +110,8 @@ compatible = "fsl,imx53-uart", "fsl,imx21-uart"; reg = <0x5000c000 0x4000>; interrupts = <33>; + clocks = <&clks 32>, <&clks 33>; + clock-names = "ipg", "per"; status = "disabled"; }; @@ -115,6 +121,8 @@ compatible = "fsl,imx53-ecspi", "fsl,imx51-ecspi"; reg = <0x50010000 0x4000>; interrupts = <36>; + clocks = <&clks 51>, <&clks 52>; + clock-names = "ipg", "per"; status = "disabled"; }; @@ -122,6 +130,7 @@ compatible = "fsl,imx53-ssi", "fsl,imx21-ssi"; reg = <0x50014000 0x4000>; interrupts = <30>; + clocks = <&clks 49>; fsl,fifo-depth = <15>; fsl,ssi-dma-events = <25 24 23 22>; /* TX0 RX0 TX1 RX1 */ status = "disabled"; @@ -131,6 +140,8 @@ compatible = "fsl,imx53-esdhc"; reg = <0x50020000 0x4000>; interrupts = <3>; + clocks = <&clks 46>, <&clks 0>, <&clks 73>; + clock-names = "ipg", "ahb", "per"; status = "disabled"; }; @@ -138,6 +149,8 @@ compatible = "fsl,imx53-esdhc"; reg = <0x50024000 0x4000>; interrupts = <4>; + clocks = <&clks 47>, <&clks 0>, <&clks 74>; + clock-names = "ipg", "ahb", "per"; status = "disabled"; }; }; @@ -214,12 +227,14 @@ compatible = "fsl,imx53-wdt", "fsl,imx21-wdt"; reg = <0x53f98000 0x4000>; interrupts = <58>; + clocks = <&clks 0>; }; wdog@53f9c000 { /* WDOG2 */ compatible = "fsl,imx53-wdt", "fsl,imx21-wdt"; reg = <0x53f9c000 0x4000>; interrupts = <59>; + clocks = <&clks 0>; status = "disabled"; }; @@ -382,6 +397,8 @@ compatible = "fsl,imx53-uart", "fsl,imx21-uart"; reg = <0x53fbc000 0x4000>; interrupts = <31>; + clocks = <&clks 28>, <&clks 29>; + clock-names = "ipg", "per"; status = "disabled"; }; @@ -389,6 +406,8 @@ compatible = "fsl,imx53-uart", "fsl,imx21-uart"; reg = <0x53fc0000 0x4000>; interrupts = <32>; + clocks = <&clks 30>, <&clks 31>; + clock-names = "ipg", "per"; status = "disabled"; }; @@ -396,6 +415,8 @@ compatible = "fsl,imx53-flexcan", "fsl,p1010-flexcan"; reg = <0x53fc8000 0x4000>; interrupts = <82>; + clocks = <&clks 158>, <&clks 157>; + clock-names = "ipg", "per"; status = "disabled"; }; @@ -403,9 +424,18 @@ compatible = "fsl,imx53-flexcan", "fsl,p1010-flexcan"; reg = <0x53fcc000 0x4000>; interrupts = <83>; + clocks = <&clks 158>, <&clks 157>; + clock-names = "ipg", "per"; status = "disabled"; }; + clks: ccm@53fd4000{ + compatible = "fsl,imx53-ccm"; + reg = <0x53fd4000 0x4000>; + interrupts = <0 71 0x04 0 72 0x04>; + #clock-cells = <1>; + }; + gpio5: gpio@53fdc000 { compatible = "fsl,imx53-gpio", "fsl,imx35-gpio"; reg = <0x53fdc000 0x4000>; @@ -442,6 +472,7 @@ compatible = "fsl,imx53-i2c", "fsl,imx21-i2c"; reg = <0x53fec000 0x4000>; interrupts = <64>; + clocks = <&clks 88>; status = "disabled"; }; @@ -449,6 +480,8 @@ compatible = "fsl,imx53-uart", "fsl,imx21-uart"; reg = <0x53ff0000 0x4000>; interrupts = <13>; + clocks = <&clks 65>, <&clks 66>; + clock-names = "ipg", "per"; status = "disabled"; }; }; @@ -464,6 +497,8 @@ compatible = "fsl,imx53-uart", "fsl,imx21-uart"; reg = <0x63f90000 0x4000>; interrupts = <86>; + clocks = <&clks 67>, <&clks 68>; + clock-names = "ipg", "per"; status = "disabled"; }; @@ -473,6 +508,8 @@ compatible = "fsl,imx53-ecspi", "fsl,imx51-ecspi"; reg = <0x63fac000 0x4000>; interrupts = <37>; + clocks = <&clks 53>, <&clks 54>; + clock-names = "ipg", "per"; status = "disabled"; }; @@ -480,6 +517,8 @@ compatible = "fsl,imx53-sdma", "fsl,imx35-sdma"; reg = <0x63fb0000 0x4000>; interrupts = <6>; + clocks = <&clks 56>, <&clks 56>; + clock-names = "ipg", "ahb"; fsl,sdma-ram-script-name = "imx/sdma/sdma-imx53.bin"; }; @@ -489,6 +528,8 @@ compatible = "fsl,imx53-cspi", "fsl,imx35-cspi"; reg = <0x63fc0000 0x4000>; interrupts = <38>; + clocks = <&clks 55>, <&clks 0>; + clock-names = "ipg", "per"; status = "disabled"; }; @@ -498,6 +539,7 @@ compatible = "fsl,imx53-i2c", "fsl,imx21-i2c"; reg = <0x63fc4000 0x4000>; interrupts = <63>; + clocks = <&clks 35>; status = "disabled"; }; @@ -507,6 +549,7 @@ compatible = "fsl,imx53-i2c", "fsl,imx21-i2c"; reg = <0x63fc8000 0x4000>; interrupts = <62>; + clocks = <&clks 34>; status = "disabled"; }; @@ -514,6 +557,7 @@ compatible = "fsl,imx53-ssi", "fsl,imx21-ssi"; reg = <0x63fcc000 0x4000>; interrupts = <29>; + clocks = <&clks 48>; fsl,fifo-depth = <15>; fsl,ssi-dma-events = <29 28 27 26>; /* TX0 RX0 TX1 RX1 */ status = "disabled"; @@ -529,6 +573,7 @@ compatible = "fsl,imx53-nand"; reg = <0x63fdb000 0x1000 0xf7ff0000 0x10000>; interrupts = <8>; + clocks = <&clks 60>; status = "disabled"; }; @@ -536,6 +581,7 @@ compatible = "fsl,imx53-ssi", "fsl,imx21-ssi"; reg = <0x63fe8000 0x4000>; interrupts = <96>; + clocks = <&clks 50>; fsl,fifo-depth = <15>; fsl,ssi-dma-events = <47 46 45 44>; /* TX0 RX0 TX1 RX1 */ status = "disabled"; @@ -545,6 +591,8 @@ compatible = "fsl,imx53-fec", "fsl,imx25-fec"; reg = <0x63fec000 0x4000>; interrupts = <87>; + clocks = <&clks 42>, <&clks 42>, <&clks 42>; + clock-names = "ipg", "ahb", "ptp"; status = "disabled"; }; }; diff --git a/arch/arm/mach-imx/clk-imx51-imx53.c b/arch/arm/mach-imx/clk-imx51-imx53.c index 73b241db63c8..e8c0473c7568 100644 --- a/arch/arm/mach-imx/clk-imx51-imx53.c +++ b/arch/arm/mach-imx/clk-imx51-imx53.c @@ -87,6 +87,7 @@ enum imx5_clks { }; static struct clk *clk[clk_max]; +static struct clk_onecell_data clk_data; static void __init mx5_clocks_common_init(unsigned long rate_ckil, unsigned long rate_osc, unsigned long rate_ckih1, @@ -318,6 +319,7 @@ int __init mx51_clocks_init(unsigned long rate_ckil, unsigned long rate_osc, unsigned long rate_ckih1, unsigned long rate_ckih2) { int i; + struct device_node *np; clk[pll1_sw] = imx_clk_pllv2("pll1_sw", "osc", MX51_DPLL1_BASE); clk[pll2_sw] = imx_clk_pllv2("pll2_sw", "osc", MX51_DPLL2_BASE); @@ -346,6 +348,11 @@ int __init mx51_clocks_init(unsigned long rate_ckil, unsigned long rate_osc, pr_err("i.MX51 clk %d: register failed with %ld\n", i, PTR_ERR(clk[i])); + np = of_find_compatible_node(NULL, NULL, "fsl,imx51-ccm"); + clk_data.clks = clk; + clk_data.clk_num = ARRAY_SIZE(clk); + of_clk_add_provider(np, of_clk_src_onecell_get, &clk_data); + mx5_clocks_common_init(rate_ckil, rate_osc, rate_ckih1, rate_ckih2); clk_register_clkdev(clk[hsi2c_gate], NULL, "imx21-i2c.2"); @@ -368,10 +375,6 @@ int __init mx51_clocks_init(unsigned long rate_ckil, unsigned long rate_osc, clk_register_clkdev(clk[esdhc4_ipg_gate], "ipg", "sdhci-esdhc-imx51.3"); clk_register_clkdev(clk[dummy], "ahb", "sdhci-esdhc-imx51.3"); clk_register_clkdev(clk[esdhc4_per_gate], "per", "sdhci-esdhc-imx51.3"); - clk_register_clkdev(clk[ssi1_ipg_gate], NULL, "83fcc000.ssi"); - clk_register_clkdev(clk[ssi2_ipg_gate], NULL, "70014000.ssi"); - clk_register_clkdev(clk[ssi3_ipg_gate], NULL, "83fe8000.ssi"); - clk_register_clkdev(clk[nfc_gate], NULL, "83fdb000.nand"); /* set the usboh3 parent to pll2_sw */ clk_set_parent(clk[usboh3_sel], clk[pll2_sw]); @@ -395,6 +398,7 @@ int __init mx53_clocks_init(unsigned long rate_ckil, unsigned long rate_osc, { int i; unsigned long r; + struct device_node *np; clk[pll1_sw] = imx_clk_pllv2("pll1_sw", "osc", MX53_DPLL1_BASE); clk[pll2_sw] = imx_clk_pllv2("pll2_sw", "osc", MX53_DPLL2_BASE); @@ -439,6 +443,11 @@ int __init mx53_clocks_init(unsigned long rate_ckil, unsigned long rate_osc, pr_err("i.MX53 clk %d: register failed with %ld\n", i, PTR_ERR(clk[i])); + np = of_find_compatible_node(NULL, NULL, "fsl,imx53-ccm"); + clk_data.clks = clk; + clk_data.clk_num = ARRAY_SIZE(clk); + of_clk_add_provider(np, of_clk_src_onecell_get, &clk_data); + mx5_clocks_common_init(rate_ckil, rate_osc, rate_ckih1, rate_ckih2); clk_register_clkdev(clk[vpu_gate], NULL, "imx53-vpu.0"); @@ -461,15 +470,6 @@ int __init mx53_clocks_init(unsigned long rate_ckil, unsigned long rate_osc, clk_register_clkdev(clk[esdhc4_ipg_gate], "ipg", "sdhci-esdhc-imx53.3"); clk_register_clkdev(clk[dummy], "ahb", "sdhci-esdhc-imx53.3"); clk_register_clkdev(clk[esdhc4_per_gate], "per", "sdhci-esdhc-imx53.3"); - clk_register_clkdev(clk[ssi1_ipg_gate], NULL, "63fcc000.ssi"); - clk_register_clkdev(clk[ssi2_ipg_gate], NULL, "50014000.ssi"); - clk_register_clkdev(clk[ssi3_ipg_gate], NULL, "63fd0000.ssi"); - clk_register_clkdev(clk[nfc_gate], NULL, "63fdb000.nand"); - clk_register_clkdev(clk[can1_ipg_gate], "ipg", "53fc8000.can"); - clk_register_clkdev(clk[can1_serial_gate], "per", "53fc8000.can"); - clk_register_clkdev(clk[can2_ipg_gate], "ipg", "53fcc000.can"); - clk_register_clkdev(clk[can2_serial_gate], "per", "53fcc000.can"); - clk_register_clkdev(clk[dummy], NULL, "53fa4000.rtc"); /* set SDHC root clock to 200MHZ*/ clk_set_rate(clk[esdhc_a_podf], 200000000); diff --git a/arch/arm/mach-imx/imx51-dt.c b/arch/arm/mach-imx/imx51-dt.c index 50742990a136..5ffa40c673f8 100644 --- a/arch/arm/mach-imx/imx51-dt.c +++ b/arch/arm/mach-imx/imx51-dt.c @@ -19,35 +19,9 @@ #include "common.h" #include "mx51.h" -/* - * Lookup table for attaching a specific name and platform_data pointer to - * devices as they get created by of_platform_populate(). Ideally this table - * would not exist, but the current clock implementation depends on some devices - * having a specific name. - */ -static const struct of_dev_auxdata imx51_auxdata_lookup[] __initconst = { - OF_DEV_AUXDATA("fsl,imx51-uart", MX51_UART1_BASE_ADDR, "imx21-uart.0", NULL), - OF_DEV_AUXDATA("fsl,imx51-uart", MX51_UART2_BASE_ADDR, "imx21-uart.1", NULL), - OF_DEV_AUXDATA("fsl,imx51-uart", MX51_UART3_BASE_ADDR, "imx21-uart.2", NULL), - OF_DEV_AUXDATA("fsl,imx51-fec", MX51_FEC_BASE_ADDR, "imx27-fec.0", NULL), - OF_DEV_AUXDATA("fsl,imx51-esdhc", MX51_ESDHC1_BASE_ADDR, "sdhci-esdhc-imx51.0", NULL), - OF_DEV_AUXDATA("fsl,imx51-esdhc", MX51_ESDHC2_BASE_ADDR, "sdhci-esdhc-imx51.1", NULL), - OF_DEV_AUXDATA("fsl,imx51-esdhc", MX51_ESDHC3_BASE_ADDR, "sdhci-esdhc-imx51.2", NULL), - OF_DEV_AUXDATA("fsl,imx51-esdhc", MX51_ESDHC4_BASE_ADDR, "sdhci-esdhc-imx51.3", NULL), - OF_DEV_AUXDATA("fsl,imx51-ecspi", MX51_ECSPI1_BASE_ADDR, "imx51-ecspi.0", NULL), - OF_DEV_AUXDATA("fsl,imx51-ecspi", MX51_ECSPI2_BASE_ADDR, "imx51-ecspi.1", NULL), - OF_DEV_AUXDATA("fsl,imx51-cspi", MX51_CSPI_BASE_ADDR, "imx35-cspi.0", NULL), - OF_DEV_AUXDATA("fsl,imx51-i2c", MX51_I2C1_BASE_ADDR, "imx21-i2c.0", NULL), - OF_DEV_AUXDATA("fsl,imx51-i2c", MX51_I2C2_BASE_ADDR, "imx21-i2c.1", NULL), - OF_DEV_AUXDATA("fsl,imx51-sdma", MX51_SDMA_BASE_ADDR, "imx35-sdma", NULL), - OF_DEV_AUXDATA("fsl,imx51-wdt", MX51_WDOG1_BASE_ADDR, "imx2-wdt.0", NULL), - { /* sentinel */ } -}; - static void __init imx51_dt_init(void) { - of_platform_populate(NULL, of_default_bus_match_table, - imx51_auxdata_lookup, NULL); + of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL); } static void __init imx51_timer_init(void) diff --git a/arch/arm/mach-imx/mach-imx53.c b/arch/arm/mach-imx/mach-imx53.c index e71e62610eba..860284dea0e7 100644 --- a/arch/arm/mach-imx/mach-imx53.c +++ b/arch/arm/mach-imx/mach-imx53.c @@ -23,34 +23,6 @@ #include "common.h" #include "mx53.h" -/* - * Lookup table for attaching a specific name and platform_data pointer to - * devices as they get created by of_platform_populate(). Ideally this table - * would not exist, but the current clock implementation depends on some devices - * having a specific name. - */ -static const struct of_dev_auxdata imx53_auxdata_lookup[] __initconst = { - OF_DEV_AUXDATA("fsl,imx53-uart", MX53_UART1_BASE_ADDR, "imx21-uart.0", NULL), - OF_DEV_AUXDATA("fsl,imx53-uart", MX53_UART2_BASE_ADDR, "imx21-uart.1", NULL), - OF_DEV_AUXDATA("fsl,imx53-uart", MX53_UART3_BASE_ADDR, "imx21-uart.2", NULL), - OF_DEV_AUXDATA("fsl,imx53-uart", MX53_UART4_BASE_ADDR, "imx21-uart.3", NULL), - OF_DEV_AUXDATA("fsl,imx53-uart", MX53_UART5_BASE_ADDR, "imx21-uart.4", NULL), - OF_DEV_AUXDATA("fsl,imx53-fec", MX53_FEC_BASE_ADDR, "imx25-fec.0", NULL), - OF_DEV_AUXDATA("fsl,imx53-esdhc", MX53_ESDHC1_BASE_ADDR, "sdhci-esdhc-imx53.0", NULL), - OF_DEV_AUXDATA("fsl,imx53-esdhc", MX53_ESDHC2_BASE_ADDR, "sdhci-esdhc-imx53.1", NULL), - OF_DEV_AUXDATA("fsl,imx53-esdhc", MX53_ESDHC3_BASE_ADDR, "sdhci-esdhc-imx53.2", NULL), - OF_DEV_AUXDATA("fsl,imx53-esdhc", MX53_ESDHC4_BASE_ADDR, "sdhci-esdhc-imx53.3", NULL), - OF_DEV_AUXDATA("fsl,imx53-ecspi", MX53_ECSPI1_BASE_ADDR, "imx51-ecspi.0", NULL), - OF_DEV_AUXDATA("fsl,imx53-ecspi", MX53_ECSPI2_BASE_ADDR, "imx51-ecspi.1", NULL), - OF_DEV_AUXDATA("fsl,imx53-cspi", MX53_CSPI_BASE_ADDR, "imx35-cspi.0", NULL), - OF_DEV_AUXDATA("fsl,imx53-i2c", MX53_I2C1_BASE_ADDR, "imx21-i2c.0", NULL), - OF_DEV_AUXDATA("fsl,imx53-i2c", MX53_I2C2_BASE_ADDR, "imx21-i2c.1", NULL), - OF_DEV_AUXDATA("fsl,imx53-i2c", MX53_I2C3_BASE_ADDR, "imx21-i2c.2", NULL), - OF_DEV_AUXDATA("fsl,imx53-sdma", MX53_SDMA_BASE_ADDR, "imx35-sdma", NULL), - OF_DEV_AUXDATA("fsl,imx53-wdt", MX53_WDOG1_BASE_ADDR, "imx2-wdt.0", NULL), - { /* sentinel */ } -}; - static void __init imx53_qsb_init(void) { struct clk *clk; @@ -69,8 +41,7 @@ static void __init imx53_dt_init(void) if (of_machine_is_compatible("fsl,imx53-qsb")) imx53_qsb_init(); - of_platform_populate(NULL, of_default_bus_match_table, - imx53_auxdata_lookup, NULL); + of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL); } static void __init imx53_timer_init(void) -- cgit v1.2.3-59-g8ed1b From 13861701a83765cc442ffe9bac0126ec75a3342e Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Thu, 22 Nov 2012 10:05:13 +0100 Subject: ARM i.MX6: rename PLLs according to datasheet In recent reference manuals the PLLs were renumbered. PLL8 now is PLL6 and vice versa. Change the code according to the reference manual to avoid confusion. Signed-off-by: Sascha Hauer Acked-by: Shawn Guo --- Documentation/devicetree/bindings/clock/imx6q-clock.txt | 4 ++-- arch/arm/mach-imx/clk-imx6q.c | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/clock/imx6q-clock.txt b/Documentation/devicetree/bindings/clock/imx6q-clock.txt index 492bd991d52a..bb71d4f8004b 100644 --- a/Documentation/devicetree/bindings/clock/imx6q-clock.txt +++ b/Documentation/devicetree/bindings/clock/imx6q-clock.txt @@ -187,9 +187,9 @@ clocks and IDs. pll3_usb_otg 172 pll4_audio 173 pll5_video 174 - pll6_mlb 175 + pll8_mlb 175 pll7_usb_host 176 - pll8_enet 177 + pll6_enet 177 ssi1_ipg 178 ssi2_ipg 179 ssi3_ipg 180 diff --git a/arch/arm/mach-imx/clk-imx6q.c b/arch/arm/mach-imx/clk-imx6q.c index a37756c22bb4..0d60cb0e30fb 100644 --- a/arch/arm/mach-imx/clk-imx6q.c +++ b/arch/arm/mach-imx/clk-imx6q.c @@ -152,7 +152,7 @@ enum mx6q_clks { gpmi_bch_apb, gpmi_bch, gpmi_io, gpmi_apb, sata, sdma, spba, ssi1, ssi2, ssi3, uart_ipg, uart_serial, usboh3, usdhc1, usdhc2, usdhc3, usdhc4, vdo_axi, vpu_axi, cko1, pll1_sys, pll2_bus, pll3_usb_otg, - pll4_audio, pll5_video, pll6_mlb, pll7_usb_host, pll8_enet, ssi1_ipg, + pll4_audio, pll5_video, pll8_mlb, pll7_usb_host, pll6_enet, ssi1_ipg, ssi2_ipg, ssi3_ipg, rom, usbphy1, usbphy2, ldb_di0_div_3_5, ldb_di1_div_3_5, clk_max }; @@ -196,9 +196,9 @@ int __init mx6q_clocks_init(void) clk[pll3_usb_otg] = imx_clk_pllv3(IMX_PLLV3_USB, "pll3_usb_otg", "osc", base + 0x10, 0x2000, 0x3); clk[pll4_audio] = imx_clk_pllv3(IMX_PLLV3_AV, "pll4_audio", "osc", base + 0x70, 0x2000, 0x7f); clk[pll5_video] = imx_clk_pllv3(IMX_PLLV3_AV, "pll5_video", "osc", base + 0xa0, 0x2000, 0x7f); - clk[pll6_mlb] = imx_clk_pllv3(IMX_PLLV3_MLB, "pll6_mlb", "osc", base + 0xd0, 0x2000, 0x0); + clk[pll6_enet] = imx_clk_pllv3(IMX_PLLV3_ENET, "pll6_enet", "osc", base + 0xe0, 0x182000, 0x3); clk[pll7_usb_host] = imx_clk_pllv3(IMX_PLLV3_USB, "pll7_usb_host","osc", base + 0x20, 0x2000, 0x3); - clk[pll8_enet] = imx_clk_pllv3(IMX_PLLV3_ENET, "pll8_enet", "osc", base + 0xe0, 0x182000, 0x3); + clk[pll8_mlb] = imx_clk_pllv3(IMX_PLLV3_MLB, "pll8_mlb", "osc", base + 0xd0, 0x2000, 0x0); clk[usbphy1] = imx_clk_gate("usbphy1", "pll3_usb_otg", base + 0x10, 6); clk[usbphy2] = imx_clk_gate("usbphy2", "pll7_usb_host", base + 0x20, 6); @@ -358,7 +358,7 @@ int __init mx6q_clocks_init(void) clk[ldb_di1] = imx_clk_gate2("ldb_di1", "ldb_di1_podf", base + 0x74, 14); clk[ipu2_di1] = imx_clk_gate2("ipu2_di1", "ipu2_di1_sel", base + 0x74, 10); clk[hsi_tx] = imx_clk_gate2("hsi_tx", "hsi_tx_podf", base + 0x74, 16); - clk[mlb] = imx_clk_gate2("mlb", "pll6_mlb", base + 0x74, 18); + clk[mlb] = imx_clk_gate2("mlb", "pll8_mlb", base + 0x74, 18); clk[mmdc_ch0_axi] = imx_clk_gate2("mmdc_ch0_axi", "mmdc_ch0_axi_podf", base + 0x74, 20); clk[mmdc_ch1_axi] = imx_clk_gate2("mmdc_ch1_axi", "mmdc_ch1_axi_podf", base + 0x74, 22); clk[ocram] = imx_clk_gate2("ocram", "ahb", base + 0x74, 28); -- cgit v1.2.3-59-g8ed1b From 7a04092c733fd72ded1294719046cfdb31feced3 Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Wed, 21 Nov 2012 14:42:31 +0100 Subject: ARM i.MX6: Fix ethernet PLL clocks MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In current code the ethernet PLL is not handled correctly. The PLL runs at 500MHz and has different outputs. Only the enet reference clock is implemented. This patch changes the PLL so that it outputs 500MHz and adds the additional outputs as dividers. This now matches the datasheet which says: > This PLL synthesizes a low jitter clock from 24 MHz reference clock. > The PLL outputs a 500 MHz clock. The reference clocks generated by this PLL are: > • Ref_PCIe = 125 MHz > • Ref_SATA = 100 MHz > • Ref_ethernet, which is configurable based on the PLL_ENET[1:0] register field. Signed-off-by: Sascha Hauer Acked-by: Shawn Guo --- .../devicetree/bindings/clock/imx6q-clock.txt | 5 ++ arch/arm/mach-imx/clk-imx6q.c | 20 ++++++- arch/arm/mach-imx/clk-pllv3.c | 63 +--------------------- 3 files changed, 25 insertions(+), 63 deletions(-) (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/clock/imx6q-clock.txt b/Documentation/devicetree/bindings/clock/imx6q-clock.txt index bb71d4f8004b..d77b4e68dc42 100644 --- a/Documentation/devicetree/bindings/clock/imx6q-clock.txt +++ b/Documentation/devicetree/bindings/clock/imx6q-clock.txt @@ -198,6 +198,11 @@ clocks and IDs. usbphy2 183 ldb_di0_div_3_5 184 ldb_di1_div_3_5 185 + sata_ref 186 + sata_ref_100m 187 + pcie_ref 188 + pcie_ref_125m 189 + enet_ref 190 Examples: diff --git a/arch/arm/mach-imx/clk-imx6q.c b/arch/arm/mach-imx/clk-imx6q.c index 0d60cb0e30fb..d8049d3f9801 100644 --- a/arch/arm/mach-imx/clk-imx6q.c +++ b/arch/arm/mach-imx/clk-imx6q.c @@ -154,6 +154,7 @@ enum mx6q_clks { usdhc4, vdo_axi, vpu_axi, cko1, pll1_sys, pll2_bus, pll3_usb_otg, pll4_audio, pll5_video, pll8_mlb, pll7_usb_host, pll6_enet, ssi1_ipg, ssi2_ipg, ssi3_ipg, rom, usbphy1, usbphy2, ldb_di0_div_3_5, ldb_di1_div_3_5, + sata_ref, sata_ref_100m, pcie_ref, pcie_ref_125m, enet_ref, clk_max }; @@ -164,6 +165,13 @@ static enum mx6q_clks const clks_init_on[] __initconst = { mmdc_ch0_axi, rom, }; +static struct clk_div_table clk_enet_ref_table[] = { + { .val = 0, .div = 20, }, + { .val = 1, .div = 10, }, + { .val = 2, .div = 5, }, + { .val = 3, .div = 4, }, +}; + int __init mx6q_clocks_init(void) { struct device_node *np; @@ -196,13 +204,23 @@ int __init mx6q_clocks_init(void) clk[pll3_usb_otg] = imx_clk_pllv3(IMX_PLLV3_USB, "pll3_usb_otg", "osc", base + 0x10, 0x2000, 0x3); clk[pll4_audio] = imx_clk_pllv3(IMX_PLLV3_AV, "pll4_audio", "osc", base + 0x70, 0x2000, 0x7f); clk[pll5_video] = imx_clk_pllv3(IMX_PLLV3_AV, "pll5_video", "osc", base + 0xa0, 0x2000, 0x7f); - clk[pll6_enet] = imx_clk_pllv3(IMX_PLLV3_ENET, "pll6_enet", "osc", base + 0xe0, 0x182000, 0x3); + clk[pll6_enet] = imx_clk_pllv3(IMX_PLLV3_ENET, "pll6_enet", "osc", base + 0xe0, 0x2000, 0x3); clk[pll7_usb_host] = imx_clk_pllv3(IMX_PLLV3_USB, "pll7_usb_host","osc", base + 0x20, 0x2000, 0x3); clk[pll8_mlb] = imx_clk_pllv3(IMX_PLLV3_MLB, "pll8_mlb", "osc", base + 0xd0, 0x2000, 0x0); clk[usbphy1] = imx_clk_gate("usbphy1", "pll3_usb_otg", base + 0x10, 6); clk[usbphy2] = imx_clk_gate("usbphy2", "pll7_usb_host", base + 0x20, 6); + clk[sata_ref] = imx_clk_fixed_factor("sata_ref", "pll6_enet", 1, 5); + clk[pcie_ref] = imx_clk_fixed_factor("pcie_ref", "pll6_enet", 1, 4); + + clk[sata_ref_100m] = imx_clk_gate("sata_ref_100m", "sata_ref", base + 0xe0, 20); + clk[pcie_ref_125m] = imx_clk_gate("pcie_ref_125m", "pcie_ref", base + 0xe0, 19); + + clk[enet_ref] = clk_register_divider_table(NULL, "enet_ref", "pll6_enet", 0, + base + 0xe0, 0, 2, 0, clk_enet_ref_table, + &imx_ccm_lock); + /* name parent_name reg idx */ clk[pll2_pfd0_352m] = imx_clk_pfd("pll2_pfd0_352m", "pll2_bus", base + 0x100, 0); clk[pll2_pfd1_594m] = imx_clk_pfd("pll2_pfd1_594m", "pll2_bus", base + 0x100, 1); diff --git a/arch/arm/mach-imx/clk-pllv3.c b/arch/arm/mach-imx/clk-pllv3.c index 36aac947bce1..59e74339ab08 100644 --- a/arch/arm/mach-imx/clk-pllv3.c +++ b/arch/arm/mach-imx/clk-pllv3.c @@ -287,66 +287,7 @@ static const struct clk_ops clk_pllv3_av_ops = { static unsigned long clk_pllv3_enet_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) { - struct clk_pllv3 *pll = to_clk_pllv3(hw); - u32 div = readl_relaxed(pll->base) & pll->div_mask; - - switch (div) { - case 0: - return 25000000; - case 1: - return 50000000; - case 2: - return 100000000; - case 3: - return 125000000; - } - - return 0; -} - -static long clk_pllv3_enet_round_rate(struct clk_hw *hw, unsigned long rate, - unsigned long *prate) -{ - if (rate >= 125000000) - rate = 125000000; - else if (rate >= 100000000) - rate = 100000000; - else if (rate >= 50000000) - rate = 50000000; - else - rate = 25000000; - return rate; -} - -static int clk_pllv3_enet_set_rate(struct clk_hw *hw, unsigned long rate, - unsigned long parent_rate) -{ - struct clk_pllv3 *pll = to_clk_pllv3(hw); - u32 val, div; - - switch (rate) { - case 25000000: - div = 0; - break; - case 50000000: - div = 1; - break; - case 100000000: - div = 2; - break; - case 125000000: - div = 3; - break; - default: - return -EINVAL; - } - - val = readl_relaxed(pll->base); - val &= ~pll->div_mask; - val |= div; - writel_relaxed(val, pll->base); - - return 0; + return 500000000; } static const struct clk_ops clk_pllv3_enet_ops = { @@ -355,8 +296,6 @@ static const struct clk_ops clk_pllv3_enet_ops = { .enable = clk_pllv3_enable, .disable = clk_pllv3_disable, .recalc_rate = clk_pllv3_enet_recalc_rate, - .round_rate = clk_pllv3_enet_round_rate, - .set_rate = clk_pllv3_enet_set_rate, }; static const struct clk_ops clk_pllv3_mlb_ops = { -- cgit v1.2.3-59-g8ed1b From 811d8d6ff59cbc7d618dfa2cd339ba6c3691a7eb Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Thu, 22 Nov 2012 07:32:47 -0800 Subject: netprio_cgroup: allow nesting and inherit config on cgroup creation Inherit netprio configuration from ->css_online(), allow nesting and remove .broken_hierarchy marking. This makes netprio_cgroup's behavior match netcls_cgroup's. Note that this patch changes userland-visible behavior. Nesting is allowed and the first level cgroups below the root cgroup behave differently - they inherit priorities from the root cgroup on creation instead of starting with 0. This is unfortunate but not doing so is much crazier. Signed-off-by: Tejun Heo Tested-and-Acked-by: Daniel Wagner Acked-by: David S. Miller --- Documentation/cgroups/net_prio.txt | 2 ++ net/core/netprio_cgroup.c | 42 ++++++++++++++++++++++---------------- 2 files changed, 26 insertions(+), 18 deletions(-) (limited to 'Documentation') diff --git a/Documentation/cgroups/net_prio.txt b/Documentation/cgroups/net_prio.txt index 01b322635591..a82cbd28ea8a 100644 --- a/Documentation/cgroups/net_prio.txt +++ b/Documentation/cgroups/net_prio.txt @@ -51,3 +51,5 @@ One usage for the net_prio cgroup is with mqprio qdisc allowing application traffic to be steered to hardware/driver based traffic classes. These mappings can then be managed by administrators or other networking protocols such as DCBX. + +A new net_prio cgroup inherits the parent's configuration. diff --git a/net/core/netprio_cgroup.c b/net/core/netprio_cgroup.c index b2af0d099663..bde53da9cd86 100644 --- a/net/core/netprio_cgroup.c +++ b/net/core/netprio_cgroup.c @@ -136,9 +136,6 @@ static struct cgroup_subsys_state *cgrp_css_alloc(struct cgroup *cgrp) { struct cgroup_netprio_state *cs; - if (cgrp->parent && cgrp->parent->id) - return ERR_PTR(-EINVAL); - cs = kzalloc(sizeof(*cs), GFP_KERNEL); if (!cs) return ERR_PTR(-ENOMEM); @@ -146,16 +143,34 @@ static struct cgroup_subsys_state *cgrp_css_alloc(struct cgroup *cgrp) return &cs->css; } -static void cgrp_css_free(struct cgroup *cgrp) +static int cgrp_css_online(struct cgroup *cgrp) { - struct cgroup_netprio_state *cs = cgrp_netprio_state(cgrp); + struct cgroup *parent = cgrp->parent; struct net_device *dev; + int ret = 0; + + if (!parent) + return 0; rtnl_lock(); - for_each_netdev(&init_net, dev) - WARN_ON_ONCE(netprio_set_prio(cgrp, dev, 0)); + /* + * Inherit prios from the parent. As all prios are set during + * onlining, there is no need to clear them on offline. + */ + for_each_netdev(&init_net, dev) { + u32 prio = netprio_prio(parent, dev); + + ret = netprio_set_prio(cgrp, dev, prio); + if (ret) + break; + } rtnl_unlock(); - kfree(cs); + return ret; +} + +static void cgrp_css_free(struct cgroup *cgrp) +{ + kfree(cgrp_netprio_state(cgrp)); } static u64 read_prioidx(struct cgroup *cgrp, struct cftype *cft) @@ -237,21 +252,12 @@ static struct cftype ss_files[] = { struct cgroup_subsys net_prio_subsys = { .name = "net_prio", .css_alloc = cgrp_css_alloc, + .css_online = cgrp_css_online, .css_free = cgrp_css_free, .attach = net_prio_attach, .subsys_id = net_prio_subsys_id, .base_cftypes = ss_files, .module = THIS_MODULE, - - /* - * net_prio has artificial limit on the number of cgroups and - * disallows nesting making it impossible to co-mount it with other - * hierarchical subsystems. Remove the artificially low PRIOIDX_SZ - * limit and properly nest configuration such that children follow - * their parents' configurations by default and are allowed to - * override and remove the following. - */ - .broken_hierarchy = true, }; static int netprio_device_event(struct notifier_block *unused, -- cgit v1.2.3-59-g8ed1b From ce20364bf75b0e91156698eea49f1e8586b212c1 Mon Sep 17 00:00:00 2001 From: Shiraz Hashim Date: Thu, 25 Oct 2012 09:39:13 +0530 Subject: pwm: Add SPEAr PWM chip driver support Add support for PWM chips present on SPEAr platforms. These PWM chips support 4 channel output with programmable duty cycle and frequency. More details on these PWM chips can be obtained from relevant chapter of reference manual, present at following[1] location. 1. http://www.st.com/internet/mcu/product/251211.jsp Cc: Thierry Reding Signed-off-by: Shiraz Hashim Signed-off-by: Viresh Kumar Reviewed-by: Vipin Kumar Acked-by: Viresh Kumar Signed-off-by: Thierry Reding --- .../devicetree/bindings/pwm/spear-pwm.txt | 18 ++ drivers/pwm/Kconfig | 11 + drivers/pwm/Makefile | 1 + drivers/pwm/pwm-spear.c | 276 +++++++++++++++++++++ 4 files changed, 306 insertions(+) create mode 100644 Documentation/devicetree/bindings/pwm/spear-pwm.txt create mode 100644 drivers/pwm/pwm-spear.c (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/pwm/spear-pwm.txt b/Documentation/devicetree/bindings/pwm/spear-pwm.txt new file mode 100644 index 000000000000..3ac779d83386 --- /dev/null +++ b/Documentation/devicetree/bindings/pwm/spear-pwm.txt @@ -0,0 +1,18 @@ +== ST SPEAr SoC PWM controller == + +Required properties: +- compatible: should be one of: + - "st,spear320-pwm" + - "st,spear1340-pwm" +- reg: physical base address and length of the controller's registers +- #pwm-cells: number of cells used to specify PWM which is fixed to 2 on + SPEAr. The first cell specifies the per-chip index of the PWM to use and + the second cell is the period in nanoseconds. + +Example: + + pwm: pwm@a8000000 { + compatible ="st,spear320-pwm"; + reg = <0xa8000000 0x1000>; + #pwm-cells = <2>; + }; diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig index ed81720e7b2b..6e556c7da81e 100644 --- a/drivers/pwm/Kconfig +++ b/drivers/pwm/Kconfig @@ -112,6 +112,17 @@ config PWM_SAMSUNG To compile this driver as a module, choose M here: the module will be called pwm-samsung. +config PWM_SPEAR + tristate "STMicroelectronics SPEAr PWM support" + depends on PLAT_SPEAR + depends on OF + help + Generic PWM framework driver for the PWM controller on ST + SPEAr SoCs. + + To compile this driver as a module, choose M here: the module + will be called pwm-spear. + config PWM_TEGRA tristate "NVIDIA Tegra PWM support" depends on ARCH_TEGRA diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile index acfe4821c58b..3b3f4c9aa4ef 100644 --- a/drivers/pwm/Makefile +++ b/drivers/pwm/Makefile @@ -8,6 +8,7 @@ obj-$(CONFIG_PWM_MXS) += pwm-mxs.o obj-$(CONFIG_PWM_PUV3) += pwm-puv3.o obj-$(CONFIG_PWM_PXA) += pwm-pxa.o obj-$(CONFIG_PWM_SAMSUNG) += pwm-samsung.o +obj-$(CONFIG_PWM_SPEAR) += pwm-spear.o obj-$(CONFIG_PWM_TEGRA) += pwm-tegra.o obj-$(CONFIG_PWM_TIECAP) += pwm-tiecap.o obj-$(CONFIG_PWM_TIEHRPWM) += pwm-tiehrpwm.o diff --git a/drivers/pwm/pwm-spear.c b/drivers/pwm/pwm-spear.c new file mode 100644 index 000000000000..6a8fd9b5dc42 --- /dev/null +++ b/drivers/pwm/pwm-spear.c @@ -0,0 +1,276 @@ +/* + * ST Microelectronics SPEAr Pulse Width Modulator driver + * + * Copyright (C) 2012 ST Microelectronics + * Shiraz Hashim + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define NUM_PWM 4 + +/* PWM registers and bits definitions */ +#define PWMCR 0x00 /* Control Register */ +#define PWMCR_PWM_ENABLE 0x1 +#define PWMCR_PRESCALE_SHIFT 2 +#define PWMCR_MIN_PRESCALE 0x00 +#define PWMCR_MAX_PRESCALE 0x3FFF + +#define PWMDCR 0x04 /* Duty Cycle Register */ +#define PWMDCR_MIN_DUTY 0x0001 +#define PWMDCR_MAX_DUTY 0xFFFF + +#define PWMPCR 0x08 /* Period Register */ +#define PWMPCR_MIN_PERIOD 0x0001 +#define PWMPCR_MAX_PERIOD 0xFFFF + +/* Following only available on 13xx SoCs */ +#define PWMMCR 0x3C /* Master Control Register */ +#define PWMMCR_PWM_ENABLE 0x1 + +/** + * struct spear_pwm_chip - struct representing pwm chip + * + * @mmio_base: base address of pwm chip + * @clk: pointer to clk structure of pwm chip + * @chip: linux pwm chip representation + * @dev: pointer to device structure of pwm chip + */ +struct spear_pwm_chip { + void __iomem *mmio_base; + struct clk *clk; + struct pwm_chip chip; + struct device *dev; +}; + +static inline struct spear_pwm_chip *to_spear_pwm_chip(struct pwm_chip *chip) +{ + return container_of(chip, struct spear_pwm_chip, chip); +} + +static inline u32 spear_pwm_readl(struct spear_pwm_chip *chip, unsigned int num, + unsigned long offset) +{ + return readl_relaxed(chip->mmio_base + (num << 4) + offset); +} + +static inline void spear_pwm_writel(struct spear_pwm_chip *chip, + unsigned int num, unsigned long offset, + unsigned long val) +{ + writel_relaxed(val, chip->mmio_base + (num << 4) + offset); +} + +int spear_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, int duty_ns, + int period_ns) +{ + struct spear_pwm_chip *pc = to_spear_pwm_chip(chip); + u64 val, div, clk_rate; + unsigned long prescale = PWMCR_MIN_PRESCALE, pv, dc; + int ret; + + /* + * Find pv, dc and prescale to suit duty_ns and period_ns. This is done + * according to formulas described below: + * + * period_ns = 10^9 * (PRESCALE + 1) * PV / PWM_CLK_RATE + * duty_ns = 10^9 * (PRESCALE + 1) * DC / PWM_CLK_RATE + * + * PV = (PWM_CLK_RATE * period_ns) / (10^9 * (PRESCALE + 1)) + * DC = (PWM_CLK_RATE * duty_ns) / (10^9 * (PRESCALE + 1)) + */ + clk_rate = clk_get_rate(pc->clk); + while (1) { + div = 1000000000; + div *= 1 + prescale; + val = clk_rate * period_ns; + pv = div64_u64(val, div); + val = clk_rate * duty_ns; + dc = div64_u64(val, div); + + /* if duty_ns and period_ns are not achievable then return */ + if (pv < PWMPCR_MIN_PERIOD || dc < PWMDCR_MIN_DUTY) + return -EINVAL; + + /* + * if pv and dc have crossed their upper limit, then increase + * prescale and recalculate pv and dc. + */ + if (pv > PWMPCR_MAX_PERIOD || dc > PWMDCR_MAX_DUTY) { + if (++prescale > PWMCR_MAX_PRESCALE) + return -EINVAL; + continue; + } + break; + } + + /* + * NOTE: the clock to PWM has to be enabled first before writing to the + * registers. + */ + ret = clk_enable(pc->clk); + if (ret) + return ret; + + spear_pwm_writel(pc, pwm->hwpwm, PWMCR, + prescale << PWMCR_PRESCALE_SHIFT); + spear_pwm_writel(pc, pwm->hwpwm, PWMDCR, dc); + spear_pwm_writel(pc, pwm->hwpwm, PWMPCR, pv); + clk_disable(pc->clk); + + return 0; +} + +static int spear_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) +{ + struct spear_pwm_chip *pc = to_spear_pwm_chip(chip); + int rc = 0; + u32 val; + + rc = clk_enable(pc->clk); + if (!rc) + return rc; + + val = spear_pwm_readl(pc, pwm->hwpwm, PWMCR); + val |= PWMCR_PWM_ENABLE; + spear_pwm_writel(pc, pwm->hwpwm, PWMCR, val); + + return 0; +} + +static void spear_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm) +{ + struct spear_pwm_chip *pc = to_spear_pwm_chip(chip); + u32 val; + + val = spear_pwm_readl(pc, pwm->hwpwm, PWMCR); + val &= ~PWMCR_PWM_ENABLE; + spear_pwm_writel(pc, pwm->hwpwm, PWMCR, val); + + clk_disable(pc->clk); +} + +static const struct pwm_ops spear_pwm_ops = { + .config = spear_pwm_config, + .enable = spear_pwm_enable, + .disable = spear_pwm_disable, + .owner = THIS_MODULE, +}; + +static int spear_pwm_probe(struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + struct spear_pwm_chip *pc; + struct resource *r; + int ret; + u32 val; + + r = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!r) { + dev_err(&pdev->dev, "no memory resources defined\n"); + return -ENODEV; + } + + pc = devm_kzalloc(&pdev->dev, sizeof(*pc), GFP_KERNEL); + if (!pc) { + dev_err(&pdev->dev, "failed to allocate memory\n"); + return -ENOMEM; + } + + pc->mmio_base = devm_request_and_ioremap(&pdev->dev, r); + if (!pc->mmio_base) + return -EADDRNOTAVAIL; + + pc->clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(pc->clk)) + return PTR_ERR(pc->clk); + + pc->dev = &pdev->dev; + platform_set_drvdata(pdev, pc); + + pc->chip.dev = &pdev->dev; + pc->chip.ops = &spear_pwm_ops; + pc->chip.base = -1; + pc->chip.npwm = NUM_PWM; + + ret = clk_prepare(pc->clk); + if (!ret) + return ret; + + if (of_device_is_compatible(np, "st,spear1340-pwm")) { + ret = clk_enable(pc->clk); + if (!ret) { + clk_unprepare(pc->clk); + return ret; + } + /* + * Following enables PWM chip, channels would still be + * enabled individually through their control register + */ + val = readl_relaxed(pc->mmio_base + PWMMCR); + val |= PWMMCR_PWM_ENABLE; + writel_relaxed(val, pc->mmio_base + PWMMCR); + + clk_disable(pc->clk); + } + + ret = pwmchip_add(&pc->chip); + if (!ret) { + clk_unprepare(pc->clk); + dev_err(&pdev->dev, "pwmchip_add() failed: %d\n", ret); + } + + return ret; +} + +static int spear_pwm_remove(struct platform_device *pdev) +{ + struct spear_pwm_chip *pc = platform_get_drvdata(pdev); + int i; + + for (i = 0; i < NUM_PWM; i++) + pwm_disable(&pc->chip.pwms[i]); + + /* clk was prepared in probe, hence unprepare it here */ + clk_unprepare(pc->clk); + return pwmchip_remove(&pc->chip); +} + +static struct of_device_id spear_pwm_of_match[] = { + { .compatible = "st,spear320-pwm" }, + { .compatible = "st,spear1340-pwm" }, + { } +}; + +MODULE_DEVICE_TABLE(of, spear_pwm_of_match); + +static struct platform_driver spear_pwm_driver = { + .driver = { + .name = "spear-pwm", + .of_match_table = spear_pwm_of_match, + }, + .probe = spear_pwm_probe, + .remove = spear_pwm_remove, +}; + +module_platform_driver(spear_pwm_driver); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Shiraz Hashim "); +MODULE_AUTHOR("Viresh Kumar "); +MODULE_ALIAS("platform:spear-pwm"); -- cgit v1.2.3-59-g8ed1b From 83af24027b3df1af5c5a9aa9adcdcfeb3429d3be Mon Sep 17 00:00:00 2001 From: "Philip, Avinash" Date: Wed, 21 Nov 2012 13:10:44 +0530 Subject: pwm: Device tree support for PWM polarity Add support for encoding PWM properties in bit encoded form with of_pwm_xlate_with_flags() function support. Platforms require platform specific PWM properties has to populate in 3rd cell of the pwm-specifier and PWM driver should also set .of_xlate support with this function. Currently PWM property polarity encoded in bit position 0 of the third cell in pwm-specifier. Signed-off-by: Philip, Avinash Acked-by: Grant Likely Signed-off-by: Thierry Reding --- Documentation/devicetree/bindings/pwm/pwm.txt | 17 +++++++++++++--- drivers/pwm/core.c | 28 +++++++++++++++++++++++++++ include/linux/pwm.h | 3 +++ 3 files changed, 45 insertions(+), 3 deletions(-) (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/pwm/pwm.txt b/Documentation/devicetree/bindings/pwm/pwm.txt index 73ec962bfe8c..06e67247859a 100644 --- a/Documentation/devicetree/bindings/pwm/pwm.txt +++ b/Documentation/devicetree/bindings/pwm/pwm.txt @@ -37,10 +37,21 @@ device: pwm-names = "backlight"; }; +Note that in the example above, specifying the "pwm-names" is redundant +because the name "backlight" would be used as fallback anyway. + pwm-specifier typically encodes the chip-relative PWM number and the PWM -period in nanoseconds. Note that in the example above, specifying the -"pwm-names" is redundant because the name "backlight" would be used as -fallback anyway. +period in nanoseconds. + +Optionally, the pwm-specifier can encode a number of flags in a third cell: +- bit 0: PWM signal polarity (0: normal polarity, 1: inverse polarity) + +Example with optional PWM specifier for inverse polarity + + bl: backlight { + pwms = <&pwm 0 5000000 1>; + pwm-names = "backlight"; + }; 2) PWM controller nodes ----------------------- diff --git a/drivers/pwm/core.c b/drivers/pwm/core.c index f5acdaa52707..780cb6b8a8f0 100644 --- a/drivers/pwm/core.c +++ b/drivers/pwm/core.c @@ -32,6 +32,9 @@ #define MAX_PWMS 1024 +/* flags in the third cell of the DT PWM specifier */ +#define PWM_SPEC_POLARITY (1 << 0) + static DEFINE_MUTEX(pwm_lookup_lock); static LIST_HEAD(pwm_lookup_list); static DEFINE_MUTEX(pwm_lock); @@ -129,6 +132,31 @@ static int pwm_device_request(struct pwm_device *pwm, const char *label) return 0; } +struct pwm_device * +of_pwm_xlate_with_flags(struct pwm_chip *pc, const struct of_phandle_args *args) +{ + struct pwm_device *pwm; + + if (pc->of_pwm_n_cells < 3) + return ERR_PTR(-EINVAL); + + if (args->args[0] >= pc->npwm) + return ERR_PTR(-EINVAL); + + pwm = pwm_request_from_chip(pc, args->args[0], NULL); + if (IS_ERR(pwm)) + return pwm; + + pwm_set_period(pwm, args->args[1]); + + if (args->args[2] & PWM_SPEC_POLARITY) + pwm_set_polarity(pwm, PWM_POLARITY_INVERSED); + else + pwm_set_polarity(pwm, PWM_POLARITY_NORMAL); + + return pwm; +} + static struct pwm_device * of_pwm_simple_xlate(struct pwm_chip *pc, const struct of_phandle_args *args) { diff --git a/include/linux/pwm.h b/include/linux/pwm.h index 112b31436848..6d661f32e0e4 100644 --- a/include/linux/pwm.h +++ b/include/linux/pwm.h @@ -171,6 +171,9 @@ struct pwm_device *pwm_request_from_chip(struct pwm_chip *chip, unsigned int index, const char *label); +struct pwm_device *of_pwm_xlate_with_flags(struct pwm_chip *pc, + const struct of_phandle_args *args); + struct pwm_device *pwm_get(struct device *dev, const char *consumer); void pwm_put(struct pwm_device *pwm); -- cgit v1.2.3-59-g8ed1b From 037db524a2015607031c70a7935153120601b908 Mon Sep 17 00:00:00 2001 From: Vipul Kumar Samar Date: Thu, 22 Nov 2012 23:42:12 -0800 Subject: Input: stmpe-ts - add DT support for stmpe touchscreen This patch allows the STMPE Touchscreen driver to be successfully probed and initialised when Device Tree support is enabled. Bindings are mentioned in Documentation too. Signed-off-by: Vipul Kumar Samar Signed-off-by: Viresh Kumar Signed-off-by: Dmitry Torokhov --- .../bindings/input/touchscreen/stmpe.txt | 43 ++++++++++++++ drivers/input/touchscreen/stmpe-ts.c | 65 ++++++++++++++++------ drivers/mfd/stmpe.c | 1 + 3 files changed, 91 insertions(+), 18 deletions(-) create mode 100644 Documentation/devicetree/bindings/input/touchscreen/stmpe.txt (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/input/touchscreen/stmpe.txt b/Documentation/devicetree/bindings/input/touchscreen/stmpe.txt new file mode 100644 index 000000000000..127baa31a77a --- /dev/null +++ b/Documentation/devicetree/bindings/input/touchscreen/stmpe.txt @@ -0,0 +1,43 @@ +STMPE Touchscreen +---------------- + +Required properties: + - compatible: "st,stmpe-ts" + +Optional properties: +- st,sample-time: ADC converstion time in number of clock. (0 -> 36 clocks, 1 -> + 44 clocks, 2 -> 56 clocks, 3 -> 64 clocks, 4 -> 80 clocks, 5 -> 96 clocks, 6 + -> 144 clocks), recommended is 4. +- st,mod-12b: ADC Bit mode (0 -> 10bit ADC, 1 -> 12bit ADC) +- st,ref-sel: ADC reference source (0 -> internal reference, 1 -> external + reference) +- st,adc-freq: ADC Clock speed (0 -> 1.625 MHz, 1 -> 3.25 MHz, 2 || 3 -> 6.5 MHz) +- st,ave-ctrl: Sample average control (0 -> 1 sample, 1 -> 2 samples, 2 -> 4 + samples, 3 -> 8 samples) +- st,touch-det-delay: Touch detect interrupt delay (0 -> 10 us, 1 -> 50 us, 2 -> + 100 us, 3 -> 500 us, 4-> 1 ms, 5 -> 5 ms, 6 -> 10 ms, 7 -> 50 ms) recommended + is 3 +- st,settling: Panel driver settling time (0 -> 10 us, 1 -> 100 us, 2 -> 500 us, 3 + -> 1 ms, 4 -> 5 ms, 5 -> 10 ms, 6 for 50 ms, 7 -> 100 ms) recommended is 2 +- st,fraction-z: Length of the fractional part in z (fraction-z ([0..7]) = Count of + the fractional part) recommended is 7 +- st,i-drive: current limit value of the touchscreen drivers (0 -> 20 mA typical 35 + mA max, 1 -> 50 mA typical 80 mA max) + +Node name must be stmpe_touchscreen and should be child node of stmpe node to +which it belongs. + +Example: + + stmpe_touchscreen { + compatible = "st,stmpe-ts"; + st,sample-time = <4>; + st,mod-12b = <1>; + st,ref-sel = <0>; + st,adc-freq = <1>; + st,ave-ctrl = <1>; + st,touch-det-delay = <2>; + st,settling = <2>; + st,fraction-z = <7>; + st,i-drive = <1>; + }; diff --git a/drivers/input/touchscreen/stmpe-ts.c b/drivers/input/touchscreen/stmpe-ts.c index b3f75032deac..43e796747f4b 100644 --- a/drivers/input/touchscreen/stmpe-ts.c +++ b/drivers/input/touchscreen/stmpe-ts.c @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -262,11 +263,53 @@ static void stmpe_ts_close(struct input_dev *dev) STMPE_TSC_CTRL_TSC_EN, 0); } -static int __devinit stmpe_input_probe(struct platform_device *pdev) +static void stmpe_ts_get_platform_info(struct platform_device *pdev, + struct stmpe_touch *ts) { struct stmpe *stmpe = dev_get_drvdata(pdev->dev.parent); - const struct stmpe_platform_data *pdata = stmpe->pdata; - const struct stmpe_ts_platform_data *ts_pdata = NULL; + struct device_node *np = pdev->dev.of_node; + struct stmpe_ts_platform_data *ts_pdata = NULL; + + ts->stmpe = stmpe; + + if (stmpe->pdata && stmpe->pdata->ts) { + ts_pdata = stmpe->pdata->ts; + + ts->sample_time = ts_pdata->sample_time; + ts->mod_12b = ts_pdata->mod_12b; + ts->ref_sel = ts_pdata->ref_sel; + ts->adc_freq = ts_pdata->adc_freq; + ts->ave_ctrl = ts_pdata->ave_ctrl; + ts->touch_det_delay = ts_pdata->touch_det_delay; + ts->settling = ts_pdata->settling; + ts->fraction_z = ts_pdata->fraction_z; + ts->i_drive = ts_pdata->i_drive; + } else if (np) { + u32 val; + + if (!of_property_read_u32(np, "st,sample-time", &val)) + ts->sample_time = val; + if (!of_property_read_u32(np, "st,mod-12b", &val)) + ts->mod_12b = val; + if (!of_property_read_u32(np, "st,ref-sel", &val)) + ts->ref_sel = val; + if (!of_property_read_u32(np, "st,adc-freq", &val)) + ts->adc_freq = val; + if (!of_property_read_u32(np, "st,ave-ctrl", &val)) + ts->ave_ctrl = val; + if (!of_property_read_u32(np, "st,touch-det-delay", &val)) + ts->touch_det_delay = val; + if (!of_property_read_u32(np, "st,settling", &val)) + ts->settling = val; + if (!of_property_read_u32(np, "st,fraction-z", &val)) + ts->fraction_z = val; + if (!of_property_read_u32(np, "st,i-drive", &val)) + ts->i_drive = val; + } +} + +static int __devinit stmpe_input_probe(struct platform_device *pdev) +{ struct stmpe_touch *ts; struct input_dev *idev; int error; @@ -285,24 +328,10 @@ static int __devinit stmpe_input_probe(struct platform_device *pdev) return -ENOMEM; platform_set_drvdata(pdev, ts); - ts->stmpe = stmpe; ts->idev = idev; ts->dev = &pdev->dev; - if (pdata) - ts_pdata = pdata->ts; - - if (ts_pdata) { - ts->sample_time = ts_pdata->sample_time; - ts->mod_12b = ts_pdata->mod_12b; - ts->ref_sel = ts_pdata->ref_sel; - ts->adc_freq = ts_pdata->adc_freq; - ts->ave_ctrl = ts_pdata->ave_ctrl; - ts->touch_det_delay = ts_pdata->touch_det_delay; - ts->settling = ts_pdata->settling; - ts->fraction_z = ts_pdata->fraction_z; - ts->i_drive = ts_pdata->i_drive; - } + stmpe_ts_get_platform_info(pdev, ts); INIT_DELAYED_WORK(&ts->work, stmpe_work); diff --git a/drivers/mfd/stmpe.c b/drivers/mfd/stmpe.c index c94f521f392c..55c7b9531ad4 100644 --- a/drivers/mfd/stmpe.c +++ b/drivers/mfd/stmpe.c @@ -411,6 +411,7 @@ static struct resource stmpe_ts_resources[] = { static struct mfd_cell stmpe_ts_cell = { .name = "stmpe-ts", + .of_compatible = "st,stmpe-ts", .resources = stmpe_ts_resources, .num_resources = ARRAY_SIZE(stmpe_ts_resources), }; -- cgit v1.2.3-59-g8ed1b From cc899bc3dce9101920b120abe413299f84233e29 Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Thu, 22 Nov 2012 12:24:24 +0000 Subject: Documentation: Fix historical inconsistency in STMPE DT doc Previously a generic binding 'i2c-client-wake' was created which enabled I2C devices to register themselves as wake-up devices. This binding was later over-thrown by 'wakeup-source'. The STMPE driver was fixed-up, but the document was neglected. This patch aims to rectify that. Cc: Samuel Ortiz Cc: devicetree-discuss@lists.ozlabs.org Signed-off-by: Lee Jones Signed-off-by: Samuel Ortiz --- Documentation/devicetree/bindings/mfd/stmpe.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/mfd/stmpe.txt b/Documentation/devicetree/bindings/mfd/stmpe.txt index 8f0aeda2e326..c98450887d5e 100644 --- a/Documentation/devicetree/bindings/mfd/stmpe.txt +++ b/Documentation/devicetree/bindings/mfd/stmpe.txt @@ -8,7 +8,7 @@ Optional properties: - interrupts : The interrupt outputs from the controller - interrupt-controller : Marks the device node as an interrupt controller - interrupt-parent : Specifies which IRQ controller we're connected to - - i2c-client-wake : Marks the input device as wakable + - wakeup-source : Marks the input device as wakable - st,autosleep-timeout : Valid entries (ms); 4, 16, 32, 64, 128, 256, 512 and 1024 Example: @@ -20,6 +20,6 @@ Example: interrupt-parent = <&gpio6>; interrupt-controller; - i2c-client-wake; + wakeup-source; st,autosleep-timeout = <1024>; }; -- cgit v1.2.3-59-g8ed1b From 385a4c2e286571ba824ee5312f506f198866c3b5 Mon Sep 17 00:00:00 2001 From: Daniel Mack Date: Wed, 14 Nov 2012 18:28:39 +0800 Subject: ASoC: ak4104: add reset line property to DT bindings This device doesn't have a pdata definition for legacy boards, and unless anyone need to control the reset GPIO, it's not worth adding one. So this feature is only available to DT users for now. Signed-off-by: Daniel Mack Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/sound/ak4104.txt | 5 +++++ sound/soc/codecs/ak4104.c | 17 +++++++++++++++++ 2 files changed, 22 insertions(+) (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/sound/ak4104.txt b/Documentation/devicetree/bindings/sound/ak4104.txt index 1f8e5066d443..b902ee39cf89 100644 --- a/Documentation/devicetree/bindings/sound/ak4104.txt +++ b/Documentation/devicetree/bindings/sound/ak4104.txt @@ -8,6 +8,11 @@ Required properties: - reg : The chip select number on the SPI bus +Optional properties: + + - reset-gpio : a GPIO spec for the reset pin. If specified, it will be + deasserted before communication to the device starts. + Example: spdif: ak4104@0 { diff --git a/sound/soc/codecs/ak4104.c b/sound/soc/codecs/ak4104.c index d4d485808894..eec086b226f7 100644 --- a/sound/soc/codecs/ak4104.c +++ b/sound/soc/codecs/ak4104.c @@ -15,6 +15,8 @@ #include #include #include +#include +#include #include /* AK4104 registers addresses */ @@ -204,6 +206,7 @@ static const struct regmap_config ak4104_regmap = { static int ak4104_spi_probe(struct spi_device *spi) { + struct device_node *np = spi->dev.of_node; struct ak4104_private *ak4104; unsigned int val; int ret; @@ -225,6 +228,20 @@ static int ak4104_spi_probe(struct spi_device *spi) return ret; } + if (np) { + enum of_gpio_flags flags; + int gpio = of_get_named_gpio_flags(np, "reset-gpio", 0, &flags); + + if (gpio_is_valid(gpio)) { + ret = devm_gpio_request_one(&spi->dev, gpio, + flags & OF_GPIO_ACTIVE_LOW ? + GPIOF_OUT_INIT_LOW : GPIOF_OUT_INIT_HIGH, + "ak4104 reset"); + if (ret < 0) + return ret; + } + } + /* read the 'reserved' register - according to the datasheet, it * should contain 0x5b. Not a good way to verify the presence of * the device, but there is no hardware ID register. */ -- cgit v1.2.3-59-g8ed1b From cc9b310165e7ea2f3dc90e1eea6ce57c9b7981d1 Mon Sep 17 00:00:00 2001 From: Zhi Yong Wu Date: Thu, 22 Nov 2012 00:10:01 +0000 Subject: vxlan: fix command usage in its doc Some commands don't work in its example doc. The patch will fix it. Signed-off-by: Zhi Yong Wu Signed-off-by: David S. Miller --- Documentation/networking/vxlan.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'Documentation') diff --git a/Documentation/networking/vxlan.txt b/Documentation/networking/vxlan.txt index 5b34b762d7d5..6d993510f091 100644 --- a/Documentation/networking/vxlan.txt +++ b/Documentation/networking/vxlan.txt @@ -32,7 +32,7 @@ no entry is in the forwarding table. # ip link delete vxlan0 3. Show vxlan info - # ip -d show vxlan0 + # ip -d link show vxlan0 It is possible to create, destroy and display the vxlan forwarding table using the new bridge command. @@ -41,7 +41,7 @@ forwarding table using the new bridge command. # bridge fdb add to 00:17:42:8a:b4:05 dst 192.19.0.2 dev vxlan0 2. Delete forwarding table entry - # bridge fdb delete 00:17:42:8a:b4:05 + # bridge fdb delete 00:17:42:8a:b4:05 dev vxlan0 3. Show forwarding table # bridge fdb show dev vxlan0 -- cgit v1.2.3-59-g8ed1b From 77dae54ab385033e488d8b07045bc7f8d931740f Mon Sep 17 00:00:00 2001 From: Andrew Lunn Date: Sat, 20 Oct 2012 13:10:00 +0200 Subject: ARM: Kirkwood: ehci-orion: Add device tree binding Based on previous work by Michael Walle and Jason Cooper. Made their work actually work, which required added interrupt from DT and auxdata, along with setting the dma_mask, which DT does not currently do. Signed-off-by: Andrew Lunn Tested-by: Sebastian Hesselbarth Acked-by: Alan Stern Signed-off-by: Jason Cooper --- .../devicetree/bindings/usb/ehci-orion.txt | 15 +++++++++ drivers/usb/host/ehci-orion.c | 38 ++++++++++++++++++++-- 2 files changed, 50 insertions(+), 3 deletions(-) create mode 100644 Documentation/devicetree/bindings/usb/ehci-orion.txt (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/usb/ehci-orion.txt b/Documentation/devicetree/bindings/usb/ehci-orion.txt new file mode 100644 index 000000000000..6bc09ec14c4d --- /dev/null +++ b/Documentation/devicetree/bindings/usb/ehci-orion.txt @@ -0,0 +1,15 @@ +* EHCI controller, Orion Marvell variants + +Required properties: +- compatible: must be "marvell,orion-ehci" +- reg: physical base address of the controller and length of memory mapped + region. +- interrupts: The EHCI interrupt + +Example: + + ehci@50000 { + compatible = "marvell,orion-ehci"; + reg = <0x50000 0x1000>; + interrupts = <19>; + }; diff --git a/drivers/usb/host/ehci-orion.c b/drivers/usb/host/ehci-orion.c index 9c2717d66730..e7e8275028d3 100644 --- a/drivers/usb/host/ehci-orion.c +++ b/drivers/usb/host/ehci-orion.c @@ -14,6 +14,9 @@ #include #include #include +#include +#include +#include #define rdl(off) __raw_readl(hcd->regs + (off)) #define wrl(off, val) __raw_writel((val), hcd->regs + (off)) @@ -181,6 +184,8 @@ ehci_orion_conf_mbus_windows(struct usb_hcd *hcd, } } +static u64 ehci_orion_dma_mask = DMA_BIT_MASK(32); + static int __devinit ehci_orion_drv_probe(struct platform_device *pdev) { struct orion_ehci_data *pd = pdev->dev.platform_data; @@ -191,13 +196,17 @@ static int __devinit ehci_orion_drv_probe(struct platform_device *pdev) struct clk *clk; void __iomem *regs; int irq, err; + enum orion_ehci_phy_ver phy_version; if (usb_disabled()) return -ENODEV; pr_debug("Initializing Orion-SoC USB Host Controller\n"); - irq = platform_get_irq(pdev, 0); + if (pdev->dev.of_node) + irq = irq_of_parse_and_map(pdev->dev.of_node, 0); + else + irq = platform_get_irq(pdev, 0); if (irq <= 0) { dev_err(&pdev->dev, "Found HC with no IRQ. Check %s setup!\n", @@ -215,6 +224,14 @@ static int __devinit ehci_orion_drv_probe(struct platform_device *pdev) goto err1; } + /* + * Right now device-tree probed devices don't get dma_mask + * set. Since shared usb code relies on it, set it here for + * now. Once we have dma capability bindings this can go away. + */ + if (!pdev->dev.dma_mask) + pdev->dev.dma_mask = &ehci_orion_dma_mask; + if (!request_mem_region(res->start, resource_size(res), ehci_orion_hc_driver.description)) { dev_dbg(&pdev->dev, "controller already in use\n"); @@ -262,7 +279,12 @@ static int __devinit ehci_orion_drv_probe(struct platform_device *pdev) /* * setup Orion USB controller. */ - switch (pd->phy_version) { + if (pdev->dev.of_node) + phy_version = EHCI_PHY_NA; + else + phy_version = pd->phy_version; + + switch (phy_version) { case EHCI_PHY_NA: /* dont change USB phy settings */ break; case EHCI_PHY_ORION: @@ -317,9 +339,19 @@ static int __exit ehci_orion_drv_remove(struct platform_device *pdev) MODULE_ALIAS("platform:orion-ehci"); +static const struct of_device_id ehci_orion_dt_ids[] __devinitdata = { + { .compatible = "marvell,orion-ehci", }, + {}, +}; +MODULE_DEVICE_TABLE(of, ehci_orion_dt_ids); + static struct platform_driver ehci_orion_driver = { .probe = ehci_orion_drv_probe, .remove = __exit_p(ehci_orion_drv_remove), .shutdown = usb_hcd_platform_shutdown, - .driver.name = "orion-ehci", + .driver = { + .name = "orion-ehci", + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(ehci_orion_dt_ids), + }, }; -- cgit v1.2.3-59-g8ed1b From 96ff0f5c7efd4a2205c48a76a6a1fcd2731e6128 Mon Sep 17 00:00:00 2001 From: Jamie Lentin Date: Sat, 17 Nov 2012 09:51:04 +0100 Subject: power: Add simple poweroff-gpio driver Given appropriate devicetree bindings, this driver registers a pm_power_off function to set a GPIO line high/low to power down your board. Signed-off-by: Jamie Lentin Signed-off-by: Andrew Lunn Tested-by:Simon Baatz Signed-off-by: Jason Cooper --- .../devicetree/bindings/gpio/gpio-poweroff.txt | 22 ++++ drivers/power/Kconfig | 3 + drivers/power/Makefile | 1 + drivers/power/reset/Kconfig | 15 +++ drivers/power/reset/Makefile | 1 + drivers/power/reset/gpio-poweroff.c | 129 +++++++++++++++++++++ 6 files changed, 171 insertions(+) create mode 100644 Documentation/devicetree/bindings/gpio/gpio-poweroff.txt create mode 100644 drivers/power/reset/Kconfig create mode 100644 drivers/power/reset/Makefile create mode 100644 drivers/power/reset/gpio-poweroff.c (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/gpio/gpio-poweroff.txt b/Documentation/devicetree/bindings/gpio/gpio-poweroff.txt new file mode 100644 index 000000000000..558cdf3c9abc --- /dev/null +++ b/Documentation/devicetree/bindings/gpio/gpio-poweroff.txt @@ -0,0 +1,22 @@ +GPIO line that should be set high/low to power off a device + +Required properties: +- compatible : should be "gpio-poweroff". +- gpios : The GPIO to set high/low, see "gpios property" in + Documentation/devicetree/bindings/gpio/gpio.txt. If the pin should be + low to power down the board set it to "Active Low", otherwise set + gpio to "Active High". + +Optional properties: +- input : Initially configure the GPIO line as an input. Only reconfigure + it to an output when the pm_power_off function is called. If this optional + property is not specified, the GPIO is initialized as an output in its + inactive state. + + +Examples: + +gpio-poweroff { + compatible = "gpio-poweroff"; + gpios = <&gpio 4 0>; /* GPIO 4 Active Low */ +}; diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig index 49a893972318..b1d956d81f0c 100644 --- a/drivers/power/Kconfig +++ b/drivers/power/Kconfig @@ -335,6 +335,9 @@ config AB8500_BATTERY_THERM_ON_BATCTRL help Say Y to enable battery temperature measurements using thermistor connected on BATCTRL ADC. + +source "drivers/power/reset/Kconfig" + endif # POWER_SUPPLY source "drivers/power/avs/Kconfig" diff --git a/drivers/power/Makefile b/drivers/power/Makefile index b949cf85590c..f1d99f4a0bc3 100644 --- a/drivers/power/Makefile +++ b/drivers/power/Makefile @@ -49,3 +49,4 @@ obj-$(CONFIG_CHARGER_MAX8997) += max8997_charger.o obj-$(CONFIG_CHARGER_MAX8998) += max8998_charger.o obj-$(CONFIG_POWER_AVS) += avs/ obj-$(CONFIG_CHARGER_SMB347) += smb347-charger.o +obj-$(CONFIG_POWER_RESET) += reset/ diff --git a/drivers/power/reset/Kconfig b/drivers/power/reset/Kconfig new file mode 100644 index 000000000000..6461b489fb09 --- /dev/null +++ b/drivers/power/reset/Kconfig @@ -0,0 +1,15 @@ +menuconfig POWER_RESET + bool "Board level reset or power off" + help + Provides a number of drivers which either reset a complete board + or shut it down, by manipulating the main power supply on the board. + + Say Y here to enable board reset and power off + +config POWER_RESET_GPIO + bool "GPIO power-off driver" + depends on OF_GPIO && POWER_RESET + help + This driver supports turning off your board via a GPIO line. + If your board needs a GPIO high/low to power down, say Y and + create a binding in your devicetree. diff --git a/drivers/power/reset/Makefile b/drivers/power/reset/Makefile new file mode 100644 index 000000000000..751488a4a0c5 --- /dev/null +++ b/drivers/power/reset/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_POWER_RESET_GPIO) += gpio-poweroff.o diff --git a/drivers/power/reset/gpio-poweroff.c b/drivers/power/reset/gpio-poweroff.c new file mode 100644 index 000000000000..0491e5335d02 --- /dev/null +++ b/drivers/power/reset/gpio-poweroff.c @@ -0,0 +1,129 @@ +/* + * Toggles a GPIO pin to power down a device + * + * Jamie Lentin + * Andrew Lunn + * + * Copyright (C) 2012 Jamie Lentin + * + * 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 + +/* + * Hold configuration here, cannot be more than one instance of the driver + * since pm_power_off itself is global. + */ +static int gpio_num = -1; +static int gpio_active_low; + +static void gpio_poweroff_do_poweroff(void) +{ + BUG_ON(gpio_num == -1); + + /* drive it active */ + gpio_direction_output(gpio_num, !gpio_active_low); + mdelay(100); + /* rising edge or drive inactive */ + gpio_set_value(gpio_num, gpio_active_low); + mdelay(100); + /* falling edge */ + gpio_set_value(gpio_num, !gpio_active_low); + + /* give it some time */ + mdelay(3000); + + WARN_ON(1); +} + +static int __devinit gpio_poweroff_probe(struct platform_device *pdev) +{ + enum of_gpio_flags flags; + bool input = false; + int ret; + + /* If a pm_power_off function has already been added, leave it alone */ + if (pm_power_off != NULL) { + pr_err("%s: pm_power_off function already registered", + __func__); + return -EBUSY; + } + + gpio_num = of_get_gpio_flags(pdev->dev.of_node, 0, &flags); + if (gpio_num < 0) { + pr_err("%s: Could not get GPIO configuration: %d", + __func__, gpio_num); + return -ENODEV; + } + gpio_active_low = flags & OF_GPIO_ACTIVE_LOW; + + if (of_get_property(pdev->dev.of_node, "input", NULL)) + input = true; + + ret = gpio_request(gpio_num, "poweroff-gpio"); + if (ret) { + pr_err("%s: Could not get GPIO %d", __func__, gpio_num); + return ret; + } + if (input) { + if (gpio_direction_input(gpio_num)) { + pr_err("Could not set direction of GPIO %d to input", + gpio_num); + goto err; + } + } else { + if (gpio_direction_output(gpio_num, gpio_active_low)) { + pr_err("Could not set direction of GPIO %d", gpio_num); + goto err; + } + } + + pm_power_off = &gpio_poweroff_do_poweroff; + return 0; + +err: + gpio_free(gpio_num); + return -ENODEV; +} + +static int __devexit gpio_poweroff_remove(struct platform_device *pdev) +{ + if (gpio_num != -1) + gpio_free(gpio_num); + if (pm_power_off == &gpio_poweroff_do_poweroff) + pm_power_off = NULL; + + return 0; +} + +static const struct of_device_id of_gpio_poweroff_match[] = { + { .compatible = "gpio-poweroff", }, + {}, +}; + +static struct platform_driver gpio_poweroff_driver = { + .probe = gpio_poweroff_probe, + .remove = __devexit_p(gpio_poweroff_remove), + .driver = { + .name = "poweroff-gpio", + .owner = THIS_MODULE, + .of_match_table = of_gpio_poweroff_match, + }, +}; + +module_platform_driver(gpio_poweroff_driver); + +MODULE_AUTHOR("Jamie Lentin "); +MODULE_DESCRIPTION("GPIO poweroff driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:poweroff-gpio"); -- cgit v1.2.3-59-g8ed1b From 4e5bc99ce97fec27466a0654b35d4d3ae0afb054 Mon Sep 17 00:00:00 2001 From: Valentin Longchamp Date: Sat, 17 Nov 2012 17:00:55 +0100 Subject: ARM: Kirkwood: support 98DX412x kirkwoods with pinctrl The Marvell 98DX412x SoC embed a kirkwood variant that does not have pinctrl support yet. Even though this kirkwood is very similar to the 88f6281, on the MPP front a lot of pins are not available. That's why a new kirkwood pinctrl variant is needed. Signed-off-by: Valentin Longchamp Signed-off-by: Andrew Lunn Signed-off-by: Jason Cooper --- .../bindings/pinctrl/marvell,kirkwood-pinctrl.txt | 39 ++ arch/arm/boot/dts/kirkwood-98dx4122.dtsi | 31 ++ drivers/pinctrl/mvebu/pinctrl-kirkwood.c | 568 +++++++++++---------- 3 files changed, 360 insertions(+), 278 deletions(-) create mode 100644 arch/arm/boot/dts/kirkwood-98dx4122.dtsi (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/pinctrl/marvell,kirkwood-pinctrl.txt b/Documentation/devicetree/bindings/pinctrl/marvell,kirkwood-pinctrl.txt index 361bccb7ec89..95daf6335c37 100644 --- a/Documentation/devicetree/bindings/pinctrl/marvell,kirkwood-pinctrl.txt +++ b/Documentation/devicetree/bindings/pinctrl/marvell,kirkwood-pinctrl.txt @@ -7,8 +7,10 @@ Required properties: - compatible: "marvell,88f6180-pinctrl", "marvell,88f6190-pinctrl", "marvell,88f6192-pinctrl", "marvell,88f6281-pinctrl", "marvell,88f6282-pinctrl" + "marvell,98dx4122-pinctrl" This driver supports all kirkwood variants, i.e. 88f6180, 88f619x, and 88f628x. +It also support the 88f6281-based variant in the 98dx412x Bobcat SoCs. Available mpp pins/groups and functions: Note: brackets (x) are not part of the mpp name for marvell,function and given @@ -277,3 +279,40 @@ mpp46 46 gpio, ts(mp10), tdm(fs), lcd(hsync) mpp47 47 gpio, ts(mp11), tdm(drx), lcd(vsync) mpp48 48 gpio, ts(mp12), tdm(dtx), lcd(d16) mpp49 49 gpo, tdm(rx0ql), pex(clkreq), lcd(d17) + +* Marvell Bobcat 98dx4122 + +name pins functions +================================================================================ +mpp0 0 gpio, nand(io2), spi(cs) +mpp1 1 gpo, nand(io3), spi(mosi) +mpp2 2 gpo, nand(io4), spi(sck) +mpp3 3 gpo, nand(io5), spi(miso) +mpp4 4 gpio, nand(io6), uart0(rxd) +mpp5 5 gpo, nand(io7), uart0(txd) +mpp6 6 sysrst(out), spi(mosi) +mpp7 7 gpo, pex(rsto), spi(cs) +mpp8 8 gpio, twsi0(sda), uart0(rts), uart1(rts) +mpp9 9 gpio, twsi(sck), uart0(cts), uart1(cts) +mpp10 10 gpo, spi(sck), uart0(txd) +mpp11 11 gpio, spi(miso), uart0(rxd) +mpp13 13 gpio, uart1(txd) +mpp14 14 gpio, uart1(rxd) +mpp15 15 gpio, uart0(rts) +mpp16 16 gpio, uart0(cts) +mpp18 18 gpo, nand(io0) +mpp19 19 gpo, nand(io1) +mpp34 34 gpio +mpp35 35 gpio +mpp36 36 gpio +mpp37 37 gpio +mpp38 38 gpio +mpp39 39 gpio +mpp40 40 gpio +mpp41 41 gpio +mpp42 42 gpio +mpp43 43 gpio +mpp44 44 gpio +mpp45 45 gpio +mpp49 49 gpio + diff --git a/arch/arm/boot/dts/kirkwood-98dx4122.dtsi b/arch/arm/boot/dts/kirkwood-98dx4122.dtsi new file mode 100644 index 000000000000..3271e4c8ea07 --- /dev/null +++ b/arch/arm/boot/dts/kirkwood-98dx4122.dtsi @@ -0,0 +1,31 @@ +/ { + ocp@f1000000 { + pinctrl: pinctrl@10000 { + compatible = "marvell,98dx4122-pinctrl"; + reg = <0x10000 0x20>; + + pmx_nand: pmx-nand { + marvell,pins = "mpp0", "mpp1", "mpp2", "mpp3", + "mpp4", "mpp5", "mpp18", + "mpp19"; + marvell,function = "nand"; + }; + pmx_spi: pmx-spi { + marvell,pins = "mpp0", "mpp1", "mpp2", "mpp3"; + marvell,function = "spi"; + }; + pmx_twsi0: pmx-twsi0 { + marvell,pins = "mpp8", "mpp9"; + marvell,function = "twsi0"; + }; + pmx_uart0: pmx-uart0 { + marvell,pins = "mpp10", "mpp11"; + marvell,function = "uart0"; + }; + pmx_uart1: pmx-uart1 { + marvell,pins = "mpp13", "mpp14"; + marvell,function = "uart1"; + }; + }; + }; +}; diff --git a/drivers/pinctrl/mvebu/pinctrl-kirkwood.c b/drivers/pinctrl/mvebu/pinctrl-kirkwood.c index 9a74ef674a0e..fa6ce31c94d9 100644 --- a/drivers/pinctrl/mvebu/pinctrl-kirkwood.c +++ b/drivers/pinctrl/mvebu/pinctrl-kirkwood.c @@ -21,340 +21,341 @@ #include "pinctrl-mvebu.h" -#define V(f6180, f6190, f6192, f6281, f6282) \ +#define V(f6180, f6190, f6192, f6281, f6282, dx4122) \ ((f6180 << 0) | (f6190 << 1) | (f6192 << 2) | \ - (f6281 << 3) | (f6282 << 4)) + (f6281 << 3) | (f6282 << 4) | (dx4122 << 5)) enum kirkwood_variant { - VARIANT_MV88F6180 = V(1, 0, 0, 0, 0), - VARIANT_MV88F6190 = V(0, 1, 0, 0, 0), - VARIANT_MV88F6192 = V(0, 0, 1, 0, 0), - VARIANT_MV88F6281 = V(0, 0, 0, 1, 0), - VARIANT_MV88F6282 = V(0, 0, 0, 0, 1), + VARIANT_MV88F6180 = V(1, 0, 0, 0, 0, 0), + VARIANT_MV88F6190 = V(0, 1, 0, 0, 0, 0), + VARIANT_MV88F6192 = V(0, 0, 1, 0, 0, 0), + VARIANT_MV88F6281 = V(0, 0, 0, 1, 0, 0), + VARIANT_MV88F6282 = V(0, 0, 0, 0, 1, 0), + VARIANT_MV98DX4122 = V(0, 0, 0, 0, 0, 1), }; static struct mvebu_mpp_mode mv88f6xxx_mpp_modes[] = { MPP_MODE(0, - MPP_VAR_FUNCTION(0x0, "gpio", NULL, V(1, 1, 1, 1, 1)), - MPP_VAR_FUNCTION(0x1, "nand", "io2", V(1, 1, 1, 1, 1)), - MPP_VAR_FUNCTION(0x2, "spi", "cs", V(1, 1, 1, 1, 1))), + MPP_VAR_FUNCTION(0x0, "gpio", NULL, V(1, 1, 1, 1, 1, 1)), + MPP_VAR_FUNCTION(0x1, "nand", "io2", V(1, 1, 1, 1, 1, 1)), + MPP_VAR_FUNCTION(0x2, "spi", "cs", V(1, 1, 1, 1, 1, 1))), MPP_MODE(1, - MPP_VAR_FUNCTION(0x0, "gpo", NULL, V(1, 1, 1, 1, 1)), - MPP_VAR_FUNCTION(0x1, "nand", "io3", V(1, 1, 1, 1, 1)), - MPP_VAR_FUNCTION(0x2, "spi", "mosi", V(1, 1, 1, 1, 1))), + MPP_VAR_FUNCTION(0x0, "gpo", NULL, V(1, 1, 1, 1, 1, 1)), + MPP_VAR_FUNCTION(0x1, "nand", "io3", V(1, 1, 1, 1, 1, 1)), + MPP_VAR_FUNCTION(0x2, "spi", "mosi", V(1, 1, 1, 1, 1, 1))), MPP_MODE(2, - MPP_VAR_FUNCTION(0x0, "gpo", NULL, V(1, 1, 1, 1, 1)), - MPP_VAR_FUNCTION(0x1, "nand", "io4", V(1, 1, 1, 1, 1)), - MPP_VAR_FUNCTION(0x2, "spi", "sck", V(1, 1, 1, 1, 1))), + MPP_VAR_FUNCTION(0x0, "gpo", NULL, V(1, 1, 1, 1, 1, 1)), + MPP_VAR_FUNCTION(0x1, "nand", "io4", V(1, 1, 1, 1, 1, 1)), + MPP_VAR_FUNCTION(0x2, "spi", "sck", V(1, 1, 1, 1, 1, 1))), MPP_MODE(3, - MPP_VAR_FUNCTION(0x0, "gpo", NULL, V(1, 1, 1, 1, 1)), - MPP_VAR_FUNCTION(0x1, "nand", "io5", V(1, 1, 1, 1, 1)), - MPP_VAR_FUNCTION(0x2, "spi", "miso", V(1, 1, 1, 1, 1))), + MPP_VAR_FUNCTION(0x0, "gpo", NULL, V(1, 1, 1, 1, 1, 1)), + MPP_VAR_FUNCTION(0x1, "nand", "io5", V(1, 1, 1, 1, 1, 1)), + MPP_VAR_FUNCTION(0x2, "spi", "miso", V(1, 1, 1, 1, 1, 1))), MPP_MODE(4, - MPP_VAR_FUNCTION(0x0, "gpio", NULL, V(1, 1, 1, 1, 1)), - MPP_VAR_FUNCTION(0x1, "nand", "io6", V(1, 1, 1, 1, 1)), - MPP_VAR_FUNCTION(0x2, "uart0", "rxd", V(1, 1, 1, 1, 1)), - MPP_VAR_FUNCTION(0x5, "sata1", "act", V(0, 0, 1, 1, 1)), - MPP_VAR_FUNCTION(0xb, "lcd", "hsync", V(0, 0, 0, 0, 1)), - MPP_VAR_FUNCTION(0xd, "ptp", "clk", V(1, 1, 1, 1, 0))), + MPP_VAR_FUNCTION(0x0, "gpio", NULL, V(1, 1, 1, 1, 1, 1)), + MPP_VAR_FUNCTION(0x1, "nand", "io6", V(1, 1, 1, 1, 1, 1)), + MPP_VAR_FUNCTION(0x2, "uart0", "rxd", V(1, 1, 1, 1, 1, 1)), + MPP_VAR_FUNCTION(0x5, "sata1", "act", V(0, 0, 1, 1, 1, 0)), + MPP_VAR_FUNCTION(0xb, "lcd", "hsync", V(0, 0, 0, 0, 1, 0)), + MPP_VAR_FUNCTION(0xd, "ptp", "clk", V(1, 1, 1, 1, 0, 0))), MPP_MODE(5, - MPP_VAR_FUNCTION(0x0, "gpo", NULL, V(1, 1, 1, 1, 1)), - MPP_VAR_FUNCTION(0x1, "nand", "io7", V(1, 1, 1, 1, 1)), - MPP_VAR_FUNCTION(0x2, "uart0", "txd", V(1, 1, 1, 1, 1)), - MPP_VAR_FUNCTION(0x4, "ptp", "trig", V(1, 1, 1, 1, 0)), - MPP_VAR_FUNCTION(0x5, "sata0", "act", V(0, 1, 1, 1, 1)), - MPP_VAR_FUNCTION(0xb, "lcd", "vsync", V(0, 0, 0, 0, 1))), + MPP_VAR_FUNCTION(0x0, "gpo", NULL, V(1, 1, 1, 1, 1, 1)), + MPP_VAR_FUNCTION(0x1, "nand", "io7", V(1, 1, 1, 1, 1, 1)), + MPP_VAR_FUNCTION(0x2, "uart0", "txd", V(1, 1, 1, 1, 1, 1)), + MPP_VAR_FUNCTION(0x4, "ptp", "trig", V(1, 1, 1, 1, 0, 0)), + MPP_VAR_FUNCTION(0x5, "sata0", "act", V(0, 1, 1, 1, 1, 0)), + MPP_VAR_FUNCTION(0xb, "lcd", "vsync", V(0, 0, 0, 0, 1, 0))), MPP_MODE(6, - MPP_VAR_FUNCTION(0x0, "sysrst", "out", V(1, 1, 1, 1, 1)), - MPP_VAR_FUNCTION(0x1, "spi", "mosi", V(1, 1, 1, 1, 1)), - MPP_VAR_FUNCTION(0x2, "ptp", "trig", V(1, 1, 1, 1, 0))), + MPP_VAR_FUNCTION(0x0, "sysrst", "out", V(1, 1, 1, 1, 1, 1)), + MPP_VAR_FUNCTION(0x1, "spi", "mosi", V(1, 1, 1, 1, 1, 1)), + MPP_VAR_FUNCTION(0x2, "ptp", "trig", V(1, 1, 1, 1, 0, 0))), MPP_MODE(7, - MPP_VAR_FUNCTION(0x0, "gpo", NULL, V(1, 1, 1, 1, 1)), - MPP_VAR_FUNCTION(0x1, "pex", "rsto", V(1, 1, 1, 1, 0)), - MPP_VAR_FUNCTION(0x2, "spi", "cs", V(1, 1, 1, 1, 1)), - MPP_VAR_FUNCTION(0x3, "ptp", "trig", V(1, 1, 1, 1, 0)), - MPP_VAR_FUNCTION(0xb, "lcd", "pwm", V(0, 0, 0, 0, 1))), + MPP_VAR_FUNCTION(0x0, "gpo", NULL, V(1, 1, 1, 1, 1, 1)), + MPP_VAR_FUNCTION(0x1, "pex", "rsto", V(1, 1, 1, 1, 0, 1)), + MPP_VAR_FUNCTION(0x2, "spi", "cs", V(1, 1, 1, 1, 1, 1)), + MPP_VAR_FUNCTION(0x3, "ptp", "trig", V(1, 1, 1, 1, 0, 0)), + MPP_VAR_FUNCTION(0xb, "lcd", "pwm", V(0, 0, 0, 0, 1, 0))), MPP_MODE(8, - MPP_VAR_FUNCTION(0x0, "gpio", NULL, V(1, 1, 1, 1, 1)), - MPP_VAR_FUNCTION(0x1, "twsi0", "sda", V(1, 1, 1, 1, 1)), - MPP_VAR_FUNCTION(0x2, "uart0", "rts", V(1, 1, 1, 1, 1)), - MPP_VAR_FUNCTION(0x3, "uart1", "rts", V(1, 1, 1, 1, 1)), - MPP_VAR_FUNCTION(0x4, "mii-1", "rxerr", V(0, 1, 1, 1, 1)), - MPP_VAR_FUNCTION(0x5, "sata1", "prsnt", V(0, 0, 1, 1, 1)), - MPP_VAR_FUNCTION(0xc, "ptp", "clk", V(1, 1, 1, 1, 0)), - MPP_VAR_FUNCTION(0xd, "mii", "col", V(1, 1, 1, 1, 1))), + MPP_VAR_FUNCTION(0x0, "gpio", NULL, V(1, 1, 1, 1, 1, 1)), + MPP_VAR_FUNCTION(0x1, "twsi0", "sda", V(1, 1, 1, 1, 1, 1)), + MPP_VAR_FUNCTION(0x2, "uart0", "rts", V(1, 1, 1, 1, 1, 1)), + MPP_VAR_FUNCTION(0x3, "uart1", "rts", V(1, 1, 1, 1, 1, 1)), + MPP_VAR_FUNCTION(0x4, "mii-1", "rxerr", V(0, 1, 1, 1, 1, 0)), + MPP_VAR_FUNCTION(0x5, "sata1", "prsnt", V(0, 0, 1, 1, 1, 0)), + MPP_VAR_FUNCTION(0xc, "ptp", "clk", V(1, 1, 1, 1, 0, 0)), + MPP_VAR_FUNCTION(0xd, "mii", "col", V(1, 1, 1, 1, 1, 0))), MPP_MODE(9, - MPP_VAR_FUNCTION(0x0, "gpio", NULL, V(1, 1, 1, 1, 1)), - MPP_VAR_FUNCTION(0x1, "twsi0", "sck", V(1, 1, 1, 1, 1)), - MPP_VAR_FUNCTION(0x2, "uart0", "cts", V(1, 1, 1, 1, 1)), - MPP_VAR_FUNCTION(0x3, "uart1", "cts", V(1, 1, 1, 1, 1)), - MPP_VAR_FUNCTION(0x5, "sata0", "prsnt", V(0, 1, 1, 1, 1)), - MPP_VAR_FUNCTION(0xc, "ptp", "evreq", V(1, 1, 1, 1, 0)), - MPP_VAR_FUNCTION(0xd, "mii", "crs", V(1, 1, 1, 1, 1))), + MPP_VAR_FUNCTION(0x0, "gpio", NULL, V(1, 1, 1, 1, 1, 1)), + MPP_VAR_FUNCTION(0x1, "twsi0", "sck", V(1, 1, 1, 1, 1, 1)), + MPP_VAR_FUNCTION(0x2, "uart0", "cts", V(1, 1, 1, 1, 1, 1)), + MPP_VAR_FUNCTION(0x3, "uart1", "cts", V(1, 1, 1, 1, 1, 1)), + MPP_VAR_FUNCTION(0x5, "sata0", "prsnt", V(0, 1, 1, 1, 1, 0)), + MPP_VAR_FUNCTION(0xc, "ptp", "evreq", V(1, 1, 1, 1, 0, 0)), + MPP_VAR_FUNCTION(0xd, "mii", "crs", V(1, 1, 1, 1, 1, 0))), MPP_MODE(10, - MPP_VAR_FUNCTION(0x0, "gpo", NULL, V(1, 1, 1, 1, 1)), - MPP_VAR_FUNCTION(0x2, "spi", "sck", V(1, 1, 1, 1, 1)), - MPP_VAR_FUNCTION(0X3, "uart0", "txd", V(1, 1, 1, 1, 1)), - MPP_VAR_FUNCTION(0x5, "sata1", "act", V(0, 0, 1, 1, 1)), - MPP_VAR_FUNCTION(0xc, "ptp", "trig", V(1, 1, 1, 1, 0))), + MPP_VAR_FUNCTION(0x0, "gpo", NULL, V(1, 1, 1, 1, 1, 1)), + MPP_VAR_FUNCTION(0x2, "spi", "sck", V(1, 1, 1, 1, 1, 1)), + MPP_VAR_FUNCTION(0X3, "uart0", "txd", V(1, 1, 1, 1, 1, 1)), + MPP_VAR_FUNCTION(0x5, "sata1", "act", V(0, 0, 1, 1, 1, 0)), + MPP_VAR_FUNCTION(0xc, "ptp", "trig", V(1, 1, 1, 1, 0, 0))), MPP_MODE(11, - MPP_VAR_FUNCTION(0x0, "gpio", NULL, V(1, 1, 1, 1, 1)), - MPP_VAR_FUNCTION(0x2, "spi", "miso", V(1, 1, 1, 1, 1)), - MPP_VAR_FUNCTION(0x3, "uart0", "rxd", V(1, 1, 1, 1, 1)), - MPP_VAR_FUNCTION(0x4, "ptp-1", "evreq", V(1, 1, 1, 1, 0)), - MPP_VAR_FUNCTION(0xc, "ptp-2", "trig", V(1, 1, 1, 1, 0)), - MPP_VAR_FUNCTION(0xd, "ptp", "clk", V(1, 1, 1, 1, 0)), - MPP_VAR_FUNCTION(0x5, "sata0", "act", V(0, 1, 1, 1, 1))), + MPP_VAR_FUNCTION(0x0, "gpio", NULL, V(1, 1, 1, 1, 1, 1)), + MPP_VAR_FUNCTION(0x2, "spi", "miso", V(1, 1, 1, 1, 1, 1)), + MPP_VAR_FUNCTION(0x3, "uart0", "rxd", V(1, 1, 1, 1, 1, 1)), + MPP_VAR_FUNCTION(0x4, "ptp-1", "evreq", V(1, 1, 1, 1, 0, 0)), + MPP_VAR_FUNCTION(0xc, "ptp-2", "trig", V(1, 1, 1, 1, 0, 0)), + MPP_VAR_FUNCTION(0xd, "ptp", "clk", V(1, 1, 1, 1, 0, 0)), + MPP_VAR_FUNCTION(0x5, "sata0", "act", V(0, 1, 1, 1, 1, 0))), MPP_MODE(12, - MPP_VAR_FUNCTION(0x0, "gpo", NULL, V(1, 1, 1, 0, 1)), - MPP_VAR_FUNCTION(0x0, "gpio", NULL, V(0, 0, 0, 1, 0)), - MPP_VAR_FUNCTION(0x1, "sdio", "clk", V(1, 1, 1, 1, 1)), - MPP_VAR_FUNCTION(0xa, "audio", "spdifo", V(0, 0, 0, 0, 1)), - MPP_VAR_FUNCTION(0xb, "spi", "mosi", V(0, 0, 0, 0, 1)), - MPP_VAR_FUNCTION(0xd, "twsi1", "sda", V(0, 0, 0, 0, 1))), + MPP_VAR_FUNCTION(0x0, "gpo", NULL, V(1, 1, 1, 0, 1, 0)), + MPP_VAR_FUNCTION(0x0, "gpio", NULL, V(0, 0, 0, 1, 0, 0)), + MPP_VAR_FUNCTION(0x1, "sdio", "clk", V(1, 1, 1, 1, 1, 0)), + MPP_VAR_FUNCTION(0xa, "audio", "spdifo", V(0, 0, 0, 0, 1, 0)), + MPP_VAR_FUNCTION(0xb, "spi", "mosi", V(0, 0, 0, 0, 1, 0)), + MPP_VAR_FUNCTION(0xd, "twsi1", "sda", V(0, 0, 0, 0, 1, 0))), MPP_MODE(13, - MPP_VAR_FUNCTION(0x0, "gpio", NULL, V(1, 1, 1, 1, 1)), - MPP_VAR_FUNCTION(0x1, "sdio", "cmd", V(1, 1, 1, 1, 1)), - MPP_VAR_FUNCTION(0x3, "uart1", "txd", V(1, 1, 1, 1, 1)), - MPP_VAR_FUNCTION(0xa, "audio", "rmclk", V(0, 0, 0, 0, 1)), - MPP_VAR_FUNCTION(0xb, "lcd", "pwm", V(0, 0, 0, 0, 1))), + MPP_VAR_FUNCTION(0x0, "gpio", NULL, V(1, 1, 1, 1, 1, 1)), + MPP_VAR_FUNCTION(0x1, "sdio", "cmd", V(1, 1, 1, 1, 1, 0)), + MPP_VAR_FUNCTION(0x3, "uart1", "txd", V(1, 1, 1, 1, 1, 1)), + MPP_VAR_FUNCTION(0xa, "audio", "rmclk", V(0, 0, 0, 0, 1, 0)), + MPP_VAR_FUNCTION(0xb, "lcd", "pwm", V(0, 0, 0, 0, 1, 0))), MPP_MODE(14, - MPP_VAR_FUNCTION(0x0, "gpio", NULL, V(1, 1, 1, 1, 1)), - MPP_VAR_FUNCTION(0x1, "sdio", "d0", V(1, 1, 1, 1, 1)), - MPP_VAR_FUNCTION(0x3, "uart1", "rxd", V(1, 1, 1, 1, 1)), - MPP_VAR_FUNCTION(0x4, "sata1", "prsnt", V(0, 0, 1, 1, 1)), - MPP_VAR_FUNCTION(0xa, "audio", "spdifi", V(0, 0, 0, 0, 1)), - MPP_VAR_FUNCTION(0xb, "audio-1", "sdi", V(0, 0, 0, 0, 1)), - MPP_VAR_FUNCTION(0xd, "mii", "col", V(1, 1, 1, 1, 1))), + MPP_VAR_FUNCTION(0x0, "gpio", NULL, V(1, 1, 1, 1, 1, 1)), + MPP_VAR_FUNCTION(0x1, "sdio", "d0", V(1, 1, 1, 1, 1, 0)), + MPP_VAR_FUNCTION(0x3, "uart1", "rxd", V(1, 1, 1, 1, 1, 1)), + MPP_VAR_FUNCTION(0x4, "sata1", "prsnt", V(0, 0, 1, 1, 1, 0)), + MPP_VAR_FUNCTION(0xa, "audio", "spdifi", V(0, 0, 0, 0, 1, 0)), + MPP_VAR_FUNCTION(0xb, "audio-1", "sdi", V(0, 0, 0, 0, 1, 0)), + MPP_VAR_FUNCTION(0xd, "mii", "col", V(1, 1, 1, 1, 1, 0))), MPP_MODE(15, - MPP_VAR_FUNCTION(0x0, "gpio", NULL, V(1, 1, 1, 1, 1)), - MPP_VAR_FUNCTION(0x1, "sdio", "d1", V(1, 1, 1, 1, 1)), - MPP_VAR_FUNCTION(0x2, "uart0", "rts", V(1, 1, 1, 1, 1)), - MPP_VAR_FUNCTION(0x3, "uart1", "txd", V(1, 1, 1, 1, 1)), - MPP_VAR_FUNCTION(0x4, "sata0", "act", V(0, 1, 1, 1, 1)), - MPP_VAR_FUNCTION(0xb, "spi", "cs", V(0, 0, 0, 0, 1))), + MPP_VAR_FUNCTION(0x0, "gpio", NULL, V(1, 1, 1, 1, 1, 1)), + MPP_VAR_FUNCTION(0x1, "sdio", "d1", V(1, 1, 1, 1, 1, 0)), + MPP_VAR_FUNCTION(0x2, "uart0", "rts", V(1, 1, 1, 1, 1, 1)), + MPP_VAR_FUNCTION(0x3, "uart1", "txd", V(1, 1, 1, 1, 1, 0)), + MPP_VAR_FUNCTION(0x4, "sata0", "act", V(0, 1, 1, 1, 1, 0)), + MPP_VAR_FUNCTION(0xb, "spi", "cs", V(0, 0, 0, 0, 1, 0))), MPP_MODE(16, - MPP_VAR_FUNCTION(0x0, "gpio", NULL, V(1, 1, 1, 1, 1)), - MPP_VAR_FUNCTION(0x1, "sdio", "d2", V(1, 1, 1, 1, 1)), - MPP_VAR_FUNCTION(0x2, "uart0", "cts", V(1, 1, 1, 1, 1)), - MPP_VAR_FUNCTION(0x3, "uart1", "rxd", V(1, 1, 1, 1, 1)), - MPP_VAR_FUNCTION(0x4, "sata1", "act", V(0, 0, 1, 1, 1)), - MPP_VAR_FUNCTION(0xb, "lcd", "extclk", V(0, 0, 0, 0, 1)), - MPP_VAR_FUNCTION(0xd, "mii", "crs", V(1, 1, 1, 1, 1))), + MPP_VAR_FUNCTION(0x0, "gpio", NULL, V(1, 1, 1, 1, 1, 1)), + MPP_VAR_FUNCTION(0x1, "sdio", "d2", V(1, 1, 1, 1, 1, 0)), + MPP_VAR_FUNCTION(0x2, "uart0", "cts", V(1, 1, 1, 1, 1, 1)), + MPP_VAR_FUNCTION(0x3, "uart1", "rxd", V(1, 1, 1, 1, 1, 0)), + MPP_VAR_FUNCTION(0x4, "sata1", "act", V(0, 0, 1, 1, 1, 0)), + MPP_VAR_FUNCTION(0xb, "lcd", "extclk", V(0, 0, 0, 0, 1, 0)), + MPP_VAR_FUNCTION(0xd, "mii", "crs", V(1, 1, 1, 1, 1, 0))), MPP_MODE(17, - MPP_VAR_FUNCTION(0x0, "gpio", NULL, V(1, 1, 1, 1, 1)), - MPP_VAR_FUNCTION(0x1, "sdio", "d3", V(1, 1, 1, 1, 1)), - MPP_VAR_FUNCTION(0x4, "sata0", "prsnt", V(0, 1, 1, 1, 1)), - MPP_VAR_FUNCTION(0xa, "sata1", "act", V(0, 0, 0, 0, 1)), - MPP_VAR_FUNCTION(0xd, "twsi1", "sck", V(0, 0, 0, 0, 1))), + MPP_VAR_FUNCTION(0x0, "gpio", NULL, V(1, 1, 1, 1, 1, 0)), + MPP_VAR_FUNCTION(0x1, "sdio", "d3", V(1, 1, 1, 1, 1, 0)), + MPP_VAR_FUNCTION(0x4, "sata0", "prsnt", V(0, 1, 1, 1, 1, 0)), + MPP_VAR_FUNCTION(0xa, "sata1", "act", V(0, 0, 0, 0, 1, 0)), + MPP_VAR_FUNCTION(0xd, "twsi1", "sck", V(0, 0, 0, 0, 1, 0))), MPP_MODE(18, - MPP_VAR_FUNCTION(0x0, "gpo", NULL, V(1, 1, 1, 1, 1)), - MPP_VAR_FUNCTION(0x1, "nand", "io0", V(1, 1, 1, 1, 1)), - MPP_VAR_FUNCTION(0x2, "pex", "clkreq", V(0, 0, 0, 0, 1))), + MPP_VAR_FUNCTION(0x0, "gpo", NULL, V(1, 1, 1, 1, 1, 1)), + MPP_VAR_FUNCTION(0x1, "nand", "io0", V(1, 1, 1, 1, 1, 1)), + MPP_VAR_FUNCTION(0x2, "pex", "clkreq", V(0, 0, 0, 0, 1, 0))), MPP_MODE(19, - MPP_VAR_FUNCTION(0x0, "gpo", NULL, V(1, 1, 1, 1, 1)), - MPP_VAR_FUNCTION(0x1, "nand", "io1", V(1, 1, 1, 1, 1))), + MPP_VAR_FUNCTION(0x0, "gpo", NULL, V(1, 1, 1, 1, 1, 1)), + MPP_VAR_FUNCTION(0x1, "nand", "io1", V(1, 1, 1, 1, 1, 1))), MPP_MODE(20, - MPP_VAR_FUNCTION(0x0, "gpio", NULL, V(1, 1, 1, 1, 1)), - MPP_VAR_FUNCTION(0x1, "ts", "mp0", V(0, 0, 1, 1, 1)), - MPP_VAR_FUNCTION(0x2, "tdm", "tx0ql", V(0, 0, 1, 1, 1)), - MPP_VAR_FUNCTION(0x3, "ge1", "txd0", V(0, 1, 1, 1, 1)), - MPP_VAR_FUNCTION(0x4, "audio", "spdifi", V(0, 0, 1, 1, 1)), - MPP_VAR_FUNCTION(0x5, "sata1", "act", V(0, 0, 1, 1, 1)), - MPP_VAR_FUNCTION(0xb, "lcd", "d0", V(0, 0, 0, 0, 1)), - MPP_VAR_FUNCTION(0xc, "mii", "rxerr", V(1, 0, 0, 0, 0))), + MPP_VAR_FUNCTION(0x0, "gpio", NULL, V(1, 1, 1, 1, 1, 0)), + MPP_VAR_FUNCTION(0x1, "ts", "mp0", V(0, 0, 1, 1, 1, 0)), + MPP_VAR_FUNCTION(0x2, "tdm", "tx0ql", V(0, 0, 1, 1, 1, 0)), + MPP_VAR_FUNCTION(0x3, "ge1", "txd0", V(0, 1, 1, 1, 1, 0)), + MPP_VAR_FUNCTION(0x4, "audio", "spdifi", V(0, 0, 1, 1, 1, 0)), + MPP_VAR_FUNCTION(0x5, "sata1", "act", V(0, 0, 1, 1, 1, 0)), + MPP_VAR_FUNCTION(0xb, "lcd", "d0", V(0, 0, 0, 0, 1, 0)), + MPP_VAR_FUNCTION(0xc, "mii", "rxerr", V(1, 0, 0, 0, 0, 0))), MPP_MODE(21, - MPP_VAR_FUNCTION(0x0, "gpio", NULL, V(1, 1, 1, 1, 1)), - MPP_VAR_FUNCTION(0x1, "ts", "mp1", V(0, 0, 1, 1, 1)), - MPP_VAR_FUNCTION(0x2, "tdm", "rx0ql", V(0, 0, 1, 1, 1)), - MPP_VAR_FUNCTION(0x3, "ge1", "txd1", V(0, 1, 1, 1, 1)), - MPP_VAR_FUNCTION(0x4, "audio", "spdifi", V(1, 0, 0, 0, 0)), - MPP_VAR_FUNCTION(0x4, "audio", "spdifo", V(0, 0, 1, 1, 1)), - MPP_VAR_FUNCTION(0x5, "sata0", "act", V(0, 1, 1, 1, 1)), - MPP_VAR_FUNCTION(0xb, "lcd", "d1", V(0, 0, 0, 0, 1))), + MPP_VAR_FUNCTION(0x0, "gpio", NULL, V(1, 1, 1, 1, 1, 0)), + MPP_VAR_FUNCTION(0x1, "ts", "mp1", V(0, 0, 1, 1, 1, 0)), + MPP_VAR_FUNCTION(0x2, "tdm", "rx0ql", V(0, 0, 1, 1, 1, 0)), + MPP_VAR_FUNCTION(0x3, "ge1", "txd1", V(0, 1, 1, 1, 1, 0)), + MPP_VAR_FUNCTION(0x4, "audio", "spdifi", V(1, 0, 0, 0, 0, 0)), + MPP_VAR_FUNCTION(0x4, "audio", "spdifo", V(0, 0, 1, 1, 1, 0)), + MPP_VAR_FUNCTION(0x5, "sata0", "act", V(0, 1, 1, 1, 1, 0)), + MPP_VAR_FUNCTION(0xb, "lcd", "d1", V(0, 0, 0, 0, 1, 0))), MPP_MODE(22, - MPP_VAR_FUNCTION(0x0, "gpio", NULL, V(1, 1, 1, 1, 1)), - MPP_VAR_FUNCTION(0x1, "ts", "mp2", V(0, 0, 1, 1, 1)), - MPP_VAR_FUNCTION(0x2, "tdm", "tx2ql", V(0, 0, 1, 1, 1)), - MPP_VAR_FUNCTION(0x3, "ge1", "txd2", V(0, 1, 1, 1, 1)), - MPP_VAR_FUNCTION(0x4, "audio", "spdifo", V(1, 0, 0, 0, 0)), - MPP_VAR_FUNCTION(0x4, "audio", "rmclk", V(0, 0, 1, 1, 1)), - MPP_VAR_FUNCTION(0x5, "sata1", "prsnt", V(0, 0, 1, 1, 1)), - MPP_VAR_FUNCTION(0xb, "lcd", "d2", V(0, 0, 0, 0, 1))), + MPP_VAR_FUNCTION(0x0, "gpio", NULL, V(1, 1, 1, 1, 1, 0)), + MPP_VAR_FUNCTION(0x1, "ts", "mp2", V(0, 0, 1, 1, 1, 0)), + MPP_VAR_FUNCTION(0x2, "tdm", "tx2ql", V(0, 0, 1, 1, 1, 0)), + MPP_VAR_FUNCTION(0x3, "ge1", "txd2", V(0, 1, 1, 1, 1, 0)), + MPP_VAR_FUNCTION(0x4, "audio", "spdifo", V(1, 0, 0, 0, 0, 0)), + MPP_VAR_FUNCTION(0x4, "audio", "rmclk", V(0, 0, 1, 1, 1, 0)), + MPP_VAR_FUNCTION(0x5, "sata1", "prsnt", V(0, 0, 1, 1, 1, 0)), + MPP_VAR_FUNCTION(0xb, "lcd", "d2", V(0, 0, 0, 0, 1, 0))), MPP_MODE(23, - MPP_VAR_FUNCTION(0x0, "gpio", NULL, V(1, 1, 1, 1, 1)), - MPP_VAR_FUNCTION(0x1, "ts", "mp3", V(0, 0, 1, 1, 1)), - MPP_VAR_FUNCTION(0x2, "tdm", "rx2ql", V(0, 0, 1, 1, 1)), - MPP_VAR_FUNCTION(0x3, "ge1", "txd3", V(0, 1, 1, 1, 1)), - MPP_VAR_FUNCTION(0x4, "audio", "rmclk", V(1, 0, 0, 0, 0)), - MPP_VAR_FUNCTION(0x4, "audio", "bclk", V(0, 0, 1, 1, 1)), - MPP_VAR_FUNCTION(0x5, "sata0", "prsnt", V(0, 1, 1, 1, 1)), - MPP_VAR_FUNCTION(0xb, "lcd", "d3", V(0, 0, 0, 0, 1))), + MPP_VAR_FUNCTION(0x0, "gpio", NULL, V(1, 1, 1, 1, 1, 0)), + MPP_VAR_FUNCTION(0x1, "ts", "mp3", V(0, 0, 1, 1, 1, 0)), + MPP_VAR_FUNCTION(0x2, "tdm", "rx2ql", V(0, 0, 1, 1, 1, 0)), + MPP_VAR_FUNCTION(0x3, "ge1", "txd3", V(0, 1, 1, 1, 1, 0)), + MPP_VAR_FUNCTION(0x4, "audio", "rmclk", V(1, 0, 0, 0, 0, 0)), + MPP_VAR_FUNCTION(0x4, "audio", "bclk", V(0, 0, 1, 1, 1, 0)), + MPP_VAR_FUNCTION(0x5, "sata0", "prsnt", V(0, 1, 1, 1, 1, 0)), + MPP_VAR_FUNCTION(0xb, "lcd", "d3", V(0, 0, 0, 0, 1, 0))), MPP_MODE(24, - MPP_VAR_FUNCTION(0x0, "gpio", NULL, V(1, 1, 1, 1, 1)), - MPP_VAR_FUNCTION(0x1, "ts", "mp4", V(0, 0, 1, 1, 1)), - MPP_VAR_FUNCTION(0x2, "tdm", "spi-cs0", V(0, 0, 1, 1, 1)), - MPP_VAR_FUNCTION(0x3, "ge1", "rxd0", V(0, 1, 1, 1, 1)), - MPP_VAR_FUNCTION(0x4, "audio", "bclk", V(1, 0, 0, 0, 0)), - MPP_VAR_FUNCTION(0x4, "audio", "sdo", V(0, 0, 1, 1, 1)), - MPP_VAR_FUNCTION(0xb, "lcd", "d4", V(0, 0, 0, 0, 1))), + MPP_VAR_FUNCTION(0x0, "gpio", NULL, V(1, 1, 1, 1, 1, 0)), + MPP_VAR_FUNCTION(0x1, "ts", "mp4", V(0, 0, 1, 1, 1, 0)), + MPP_VAR_FUNCTION(0x2, "tdm", "spi-cs0", V(0, 0, 1, 1, 1, 0)), + MPP_VAR_FUNCTION(0x3, "ge1", "rxd0", V(0, 1, 1, 1, 1, 0)), + MPP_VAR_FUNCTION(0x4, "audio", "bclk", V(1, 0, 0, 0, 0, 0)), + MPP_VAR_FUNCTION(0x4, "audio", "sdo", V(0, 0, 1, 1, 1, 0)), + MPP_VAR_FUNCTION(0xb, "lcd", "d4", V(0, 0, 0, 0, 1, 0))), MPP_MODE(25, - MPP_VAR_FUNCTION(0x0, "gpio", NULL, V(1, 1, 1, 1, 1)), - MPP_VAR_FUNCTION(0x1, "ts", "mp5", V(0, 0, 1, 1, 1)), - MPP_VAR_FUNCTION(0x2, "tdm", "spi-sck", V(0, 0, 1, 1, 1)), - MPP_VAR_FUNCTION(0x3, "ge1", "rxd1", V(0, 1, 1, 1, 1)), - MPP_VAR_FUNCTION(0x4, "audio", "sdo", V(1, 0, 0, 0, 0)), - MPP_VAR_FUNCTION(0x4, "audio", "lrclk", V(0, 0, 1, 1, 1)), - MPP_VAR_FUNCTION(0xb, "lcd", "d5", V(0, 0, 0, 0, 1))), + MPP_VAR_FUNCTION(0x0, "gpio", NULL, V(1, 1, 1, 1, 1, 0)), + MPP_VAR_FUNCTION(0x1, "ts", "mp5", V(0, 0, 1, 1, 1, 0)), + MPP_VAR_FUNCTION(0x2, "tdm", "spi-sck", V(0, 0, 1, 1, 1, 0)), + MPP_VAR_FUNCTION(0x3, "ge1", "rxd1", V(0, 1, 1, 1, 1, 0)), + MPP_VAR_FUNCTION(0x4, "audio", "sdo", V(1, 0, 0, 0, 0, 0)), + MPP_VAR_FUNCTION(0x4, "audio", "lrclk", V(0, 0, 1, 1, 1, 0)), + MPP_VAR_FUNCTION(0xb, "lcd", "d5", V(0, 0, 0, 0, 1, 0))), MPP_MODE(26, - MPP_VAR_FUNCTION(0x0, "gpio", NULL, V(1, 1, 1, 1, 1)), - MPP_VAR_FUNCTION(0x1, "ts", "mp6", V(0, 0, 1, 1, 1)), - MPP_VAR_FUNCTION(0x2, "tdm", "spi-miso", V(0, 0, 1, 1, 1)), - MPP_VAR_FUNCTION(0x3, "ge1", "rxd2", V(0, 1, 1, 1, 1)), - MPP_VAR_FUNCTION(0x4, "audio", "lrclk", V(1, 0, 0, 0, 0)), - MPP_VAR_FUNCTION(0x4, "audio", "mclk", V(0, 0, 1, 1, 1)), - MPP_VAR_FUNCTION(0xb, "lcd", "d6", V(0, 0, 0, 0, 1))), + MPP_VAR_FUNCTION(0x0, "gpio", NULL, V(1, 1, 1, 1, 1, 0)), + MPP_VAR_FUNCTION(0x1, "ts", "mp6", V(0, 0, 1, 1, 1, 0)), + MPP_VAR_FUNCTION(0x2, "tdm", "spi-miso", V(0, 0, 1, 1, 1, 0)), + MPP_VAR_FUNCTION(0x3, "ge1", "rxd2", V(0, 1, 1, 1, 1, 0)), + MPP_VAR_FUNCTION(0x4, "audio", "lrclk", V(1, 0, 0, 0, 0, 0)), + MPP_VAR_FUNCTION(0x4, "audio", "mclk", V(0, 0, 1, 1, 1, 0)), + MPP_VAR_FUNCTION(0xb, "lcd", "d6", V(0, 0, 0, 0, 1, 0))), MPP_MODE(27, - MPP_VAR_FUNCTION(0x0, "gpio", NULL, V(1, 1, 1, 1, 1)), - MPP_VAR_FUNCTION(0x1, "ts", "mp7", V(0, 0, 1, 1, 1)), - MPP_VAR_FUNCTION(0x2, "tdm", "spi-mosi", V(0, 0, 1, 1, 1)), - MPP_VAR_FUNCTION(0x3, "ge1", "rxd3", V(0, 1, 1, 1, 1)), - MPP_VAR_FUNCTION(0x4, "audio", "mclk", V(1, 0, 0, 0, 0)), - MPP_VAR_FUNCTION(0x4, "audio", "sdi", V(0, 0, 1, 1, 1)), - MPP_VAR_FUNCTION(0xb, "lcd", "d7", V(0, 0, 0, 0, 1))), + MPP_VAR_FUNCTION(0x0, "gpio", NULL, V(1, 1, 1, 1, 1, 0)), + MPP_VAR_FUNCTION(0x1, "ts", "mp7", V(0, 0, 1, 1, 1, 0)), + MPP_VAR_FUNCTION(0x2, "tdm", "spi-mosi", V(0, 0, 1, 1, 1, 0)), + MPP_VAR_FUNCTION(0x3, "ge1", "rxd3", V(0, 1, 1, 1, 1, 0)), + MPP_VAR_FUNCTION(0x4, "audio", "mclk", V(1, 0, 0, 0, 0, 0)), + MPP_VAR_FUNCTION(0x4, "audio", "sdi", V(0, 0, 1, 1, 1, 0)), + MPP_VAR_FUNCTION(0xb, "lcd", "d7", V(0, 0, 0, 0, 1, 0))), MPP_MODE(28, - MPP_VAR_FUNCTION(0x0, "gpio", NULL, V(1, 1, 1, 1, 1)), - MPP_VAR_FUNCTION(0x1, "ts", "mp8", V(0, 0, 1, 1, 1)), - MPP_VAR_FUNCTION(0x2, "tdm", "int", V(0, 0, 1, 1, 1)), - MPP_VAR_FUNCTION(0x3, "ge1", "col", V(0, 1, 1, 1, 1)), - MPP_VAR_FUNCTION(0x4, "audio", "sdi", V(1, 0, 0, 0, 0)), - MPP_VAR_FUNCTION(0x4, "audio", "extclk", V(0, 0, 1, 1, 1)), - MPP_VAR_FUNCTION(0xb, "lcd", "d8", V(0, 0, 0, 0, 1))), + MPP_VAR_FUNCTION(0x0, "gpio", NULL, V(1, 1, 1, 1, 1, 0)), + MPP_VAR_FUNCTION(0x1, "ts", "mp8", V(0, 0, 1, 1, 1, 0)), + MPP_VAR_FUNCTION(0x2, "tdm", "int", V(0, 0, 1, 1, 1, 0)), + MPP_VAR_FUNCTION(0x3, "ge1", "col", V(0, 1, 1, 1, 1, 0)), + MPP_VAR_FUNCTION(0x4, "audio", "sdi", V(1, 0, 0, 0, 0, 0)), + MPP_VAR_FUNCTION(0x4, "audio", "extclk", V(0, 0, 1, 1, 1, 0)), + MPP_VAR_FUNCTION(0xb, "lcd", "d8", V(0, 0, 0, 0, 1, 0))), MPP_MODE(29, - MPP_VAR_FUNCTION(0x0, "gpio", NULL, V(1, 1, 1, 1, 1)), - MPP_VAR_FUNCTION(0x1, "ts", "mp9", V(0, 0, 1, 1, 1)), - MPP_VAR_FUNCTION(0x2, "tdm", "rst", V(0, 0, 1, 1, 1)), - MPP_VAR_FUNCTION(0x3, "ge1", "txclk", V(0, 1, 1, 1, 1)), - MPP_VAR_FUNCTION(0x4, "audio", "extclk", V(1, 0, 0, 0, 0)), - MPP_VAR_FUNCTION(0xb, "lcd", "d9", V(0, 0, 0, 0, 1))), + MPP_VAR_FUNCTION(0x0, "gpio", NULL, V(1, 1, 1, 1, 1, 0)), + MPP_VAR_FUNCTION(0x1, "ts", "mp9", V(0, 0, 1, 1, 1, 0)), + MPP_VAR_FUNCTION(0x2, "tdm", "rst", V(0, 0, 1, 1, 1, 0)), + MPP_VAR_FUNCTION(0x3, "ge1", "txclk", V(0, 1, 1, 1, 1, 0)), + MPP_VAR_FUNCTION(0x4, "audio", "extclk", V(1, 0, 0, 0, 0, 0)), + MPP_VAR_FUNCTION(0xb, "lcd", "d9", V(0, 0, 0, 0, 1, 0))), MPP_MODE(30, - MPP_VAR_FUNCTION(0x0, "gpio", NULL, V(0, 1, 1, 1, 1)), - MPP_VAR_FUNCTION(0x1, "ts", "mp10", V(0, 0, 1, 1, 1)), - MPP_VAR_FUNCTION(0x2, "tdm", "pclk", V(0, 0, 1, 1, 1)), - MPP_VAR_FUNCTION(0x3, "ge1", "rxctl", V(0, 1, 1, 1, 1)), - MPP_VAR_FUNCTION(0xb, "lcd", "d10", V(0, 0, 0, 0, 1))), + MPP_VAR_FUNCTION(0x0, "gpio", NULL, V(0, 1, 1, 1, 1, 0)), + MPP_VAR_FUNCTION(0x1, "ts", "mp10", V(0, 0, 1, 1, 1, 0)), + MPP_VAR_FUNCTION(0x2, "tdm", "pclk", V(0, 0, 1, 1, 1, 0)), + MPP_VAR_FUNCTION(0x3, "ge1", "rxctl", V(0, 1, 1, 1, 1, 0)), + MPP_VAR_FUNCTION(0xb, "lcd", "d10", V(0, 0, 0, 0, 1, 0))), MPP_MODE(31, - MPP_VAR_FUNCTION(0x0, "gpio", NULL, V(0, 1, 1, 1, 1)), - MPP_VAR_FUNCTION(0x1, "ts", "mp11", V(0, 0, 1, 1, 1)), - MPP_VAR_FUNCTION(0x2, "tdm", "fs", V(0, 0, 1, 1, 1)), - MPP_VAR_FUNCTION(0x3, "ge1", "rxclk", V(0, 1, 1, 1, 1)), - MPP_VAR_FUNCTION(0xb, "lcd", "d11", V(0, 0, 0, 0, 1))), + MPP_VAR_FUNCTION(0x0, "gpio", NULL, V(0, 1, 1, 1, 1, 0)), + MPP_VAR_FUNCTION(0x1, "ts", "mp11", V(0, 0, 1, 1, 1, 0)), + MPP_VAR_FUNCTION(0x2, "tdm", "fs", V(0, 0, 1, 1, 1, 0)), + MPP_VAR_FUNCTION(0x3, "ge1", "rxclk", V(0, 1, 1, 1, 1, 0)), + MPP_VAR_FUNCTION(0xb, "lcd", "d11", V(0, 0, 0, 0, 1, 0))), MPP_MODE(32, - MPP_VAR_FUNCTION(0x0, "gpio", NULL, V(0, 1, 1, 1, 1)), - MPP_VAR_FUNCTION(0x1, "ts", "mp12", V(0, 0, 1, 1, 1)), - MPP_VAR_FUNCTION(0x2, "tdm", "drx", V(0, 0, 1, 1, 1)), - MPP_VAR_FUNCTION(0x3, "ge1", "txclko", V(0, 1, 1, 1, 1)), - MPP_VAR_FUNCTION(0xb, "lcd", "d12", V(0, 0, 0, 0, 1))), + MPP_VAR_FUNCTION(0x0, "gpio", NULL, V(0, 1, 1, 1, 1, 0)), + MPP_VAR_FUNCTION(0x1, "ts", "mp12", V(0, 0, 1, 1, 1, 0)), + MPP_VAR_FUNCTION(0x2, "tdm", "drx", V(0, 0, 1, 1, 1, 0)), + MPP_VAR_FUNCTION(0x3, "ge1", "txclko", V(0, 1, 1, 1, 1, 0)), + MPP_VAR_FUNCTION(0xb, "lcd", "d12", V(0, 0, 0, 0, 1, 0))), MPP_MODE(33, - MPP_VAR_FUNCTION(0x0, "gpo", NULL, V(0, 1, 1, 1, 1)), - MPP_VAR_FUNCTION(0x2, "tdm", "dtx", V(0, 0, 1, 1, 1)), - MPP_VAR_FUNCTION(0x3, "ge1", "txctl", V(0, 1, 1, 1, 1)), - MPP_VAR_FUNCTION(0xb, "lcd", "d13", V(0, 0, 0, 0, 1))), + MPP_VAR_FUNCTION(0x0, "gpo", NULL, V(0, 1, 1, 1, 1, 0)), + MPP_VAR_FUNCTION(0x2, "tdm", "dtx", V(0, 0, 1, 1, 1, 0)), + MPP_VAR_FUNCTION(0x3, "ge1", "txctl", V(0, 1, 1, 1, 1, 0)), + MPP_VAR_FUNCTION(0xb, "lcd", "d13", V(0, 0, 0, 0, 1, 0))), MPP_MODE(34, - MPP_VAR_FUNCTION(0x0, "gpio", NULL, V(0, 1, 1, 1, 1)), - MPP_VAR_FUNCTION(0x2, "tdm", "spi-cs1", V(0, 0, 1, 1, 1)), - MPP_VAR_FUNCTION(0x3, "ge1", "txen", V(0, 1, 1, 1, 1)), - MPP_VAR_FUNCTION(0x5, "sata1", "act", V(0, 0, 0, 1, 1)), - MPP_VAR_FUNCTION(0xb, "lcd", "d14", V(0, 0, 0, 0, 1))), + MPP_VAR_FUNCTION(0x0, "gpio", NULL, V(0, 1, 1, 1, 1, 1)), + MPP_VAR_FUNCTION(0x2, "tdm", "spi-cs1", V(0, 0, 1, 1, 1, 0)), + MPP_VAR_FUNCTION(0x3, "ge1", "txen", V(0, 1, 1, 1, 1, 0)), + MPP_VAR_FUNCTION(0x5, "sata1", "act", V(0, 0, 0, 1, 1, 0)), + MPP_VAR_FUNCTION(0xb, "lcd", "d14", V(0, 0, 0, 0, 1, 0))), MPP_MODE(35, - MPP_VAR_FUNCTION(0x0, "gpio", NULL, V(0, 1, 1, 1, 1)), - MPP_VAR_FUNCTION(0x2, "tdm", "tx0ql", V(0, 0, 1, 1, 1)), - MPP_VAR_FUNCTION(0x3, "ge1", "rxerr", V(0, 1, 1, 1, 1)), - MPP_VAR_FUNCTION(0x5, "sata0", "act", V(0, 1, 1, 1, 1)), - MPP_VAR_FUNCTION(0xb, "lcd", "d15", V(0, 0, 0, 0, 1)), - MPP_VAR_FUNCTION(0xc, "mii", "rxerr", V(0, 1, 1, 1, 1))), + MPP_VAR_FUNCTION(0x0, "gpio", NULL, V(0, 1, 1, 1, 1, 1)), + MPP_VAR_FUNCTION(0x2, "tdm", "tx0ql", V(0, 0, 1, 1, 1, 0)), + MPP_VAR_FUNCTION(0x3, "ge1", "rxerr", V(0, 1, 1, 1, 1, 0)), + MPP_VAR_FUNCTION(0x5, "sata0", "act", V(0, 1, 1, 1, 1, 0)), + MPP_VAR_FUNCTION(0xb, "lcd", "d15", V(0, 0, 0, 0, 1, 0)), + MPP_VAR_FUNCTION(0xc, "mii", "rxerr", V(0, 1, 1, 1, 1, 0))), MPP_MODE(36, - MPP_VAR_FUNCTION(0x0, "gpio", NULL, V(0, 0, 0, 1, 1)), - MPP_VAR_FUNCTION(0x1, "ts", "mp0", V(0, 0, 0, 1, 1)), - MPP_VAR_FUNCTION(0x2, "tdm", "spi-cs1", V(0, 0, 0, 1, 1)), - MPP_VAR_FUNCTION(0x4, "audio", "spdifi", V(0, 0, 0, 1, 1)), - MPP_VAR_FUNCTION(0xb, "twsi1", "sda", V(0, 0, 0, 0, 1))), + MPP_VAR_FUNCTION(0x0, "gpio", NULL, V(0, 0, 0, 1, 1, 1)), + MPP_VAR_FUNCTION(0x1, "ts", "mp0", V(0, 0, 0, 1, 1, 0)), + MPP_VAR_FUNCTION(0x2, "tdm", "spi-cs1", V(0, 0, 0, 1, 1, 0)), + MPP_VAR_FUNCTION(0x4, "audio", "spdifi", V(0, 0, 0, 1, 1, 0)), + MPP_VAR_FUNCTION(0xb, "twsi1", "sda", V(0, 0, 0, 0, 1, 0))), MPP_MODE(37, - MPP_VAR_FUNCTION(0x0, "gpio", NULL, V(0, 0, 0, 1, 1)), - MPP_VAR_FUNCTION(0x1, "ts", "mp1", V(0, 0, 0, 1, 1)), - MPP_VAR_FUNCTION(0x2, "tdm", "tx2ql", V(0, 0, 0, 1, 1)), - MPP_VAR_FUNCTION(0x4, "audio", "spdifo", V(0, 0, 0, 1, 1)), - MPP_VAR_FUNCTION(0xb, "twsi1", "sck", V(0, 0, 0, 0, 1))), + MPP_VAR_FUNCTION(0x0, "gpio", NULL, V(0, 0, 0, 1, 1, 1)), + MPP_VAR_FUNCTION(0x1, "ts", "mp1", V(0, 0, 0, 1, 1, 0)), + MPP_VAR_FUNCTION(0x2, "tdm", "tx2ql", V(0, 0, 0, 1, 1, 0)), + MPP_VAR_FUNCTION(0x4, "audio", "spdifo", V(0, 0, 0, 1, 1, 0)), + MPP_VAR_FUNCTION(0xb, "twsi1", "sck", V(0, 0, 0, 0, 1, 0))), MPP_MODE(38, - MPP_VAR_FUNCTION(0x0, "gpio", NULL, V(0, 0, 0, 1, 1)), - MPP_VAR_FUNCTION(0x1, "ts", "mp2", V(0, 0, 0, 1, 1)), - MPP_VAR_FUNCTION(0x2, "tdm", "rx2ql", V(0, 0, 0, 1, 1)), - MPP_VAR_FUNCTION(0x4, "audio", "rmclk", V(0, 0, 0, 1, 1)), - MPP_VAR_FUNCTION(0xb, "lcd", "d18", V(0, 0, 0, 0, 1))), + MPP_VAR_FUNCTION(0x0, "gpio", NULL, V(0, 0, 0, 1, 1, 1)), + MPP_VAR_FUNCTION(0x1, "ts", "mp2", V(0, 0, 0, 1, 1, 0)), + MPP_VAR_FUNCTION(0x2, "tdm", "rx2ql", V(0, 0, 0, 1, 1, 0)), + MPP_VAR_FUNCTION(0x4, "audio", "rmclk", V(0, 0, 0, 1, 1, 0)), + MPP_VAR_FUNCTION(0xb, "lcd", "d18", V(0, 0, 0, 0, 1, 0))), MPP_MODE(39, - MPP_VAR_FUNCTION(0x0, "gpio", NULL, V(0, 0, 0, 1, 1)), - MPP_VAR_FUNCTION(0x1, "ts", "mp3", V(0, 0, 0, 1, 1)), - MPP_VAR_FUNCTION(0x2, "tdm", "spi-cs0", V(0, 0, 0, 1, 1)), - MPP_VAR_FUNCTION(0x4, "audio", "bclk", V(0, 0, 0, 1, 1)), - MPP_VAR_FUNCTION(0xb, "lcd", "d19", V(0, 0, 0, 0, 1))), + MPP_VAR_FUNCTION(0x0, "gpio", NULL, V(0, 0, 0, 1, 1, 1)), + MPP_VAR_FUNCTION(0x1, "ts", "mp3", V(0, 0, 0, 1, 1, 0)), + MPP_VAR_FUNCTION(0x2, "tdm", "spi-cs0", V(0, 0, 0, 1, 1, 0)), + MPP_VAR_FUNCTION(0x4, "audio", "bclk", V(0, 0, 0, 1, 1, 0)), + MPP_VAR_FUNCTION(0xb, "lcd", "d19", V(0, 0, 0, 0, 1, 0))), MPP_MODE(40, - MPP_VAR_FUNCTION(0x0, "gpio", NULL, V(0, 0, 0, 1, 1)), - MPP_VAR_FUNCTION(0x1, "ts", "mp4", V(0, 0, 0, 1, 1)), - MPP_VAR_FUNCTION(0x2, "tdm", "spi-sck", V(0, 0, 0, 1, 1)), - MPP_VAR_FUNCTION(0x4, "audio", "sdo", V(0, 0, 0, 1, 1)), - MPP_VAR_FUNCTION(0xb, "lcd", "d20", V(0, 0, 0, 0, 1))), + MPP_VAR_FUNCTION(0x0, "gpio", NULL, V(0, 0, 0, 1, 1, 1)), + MPP_VAR_FUNCTION(0x1, "ts", "mp4", V(0, 0, 0, 1, 1, 0)), + MPP_VAR_FUNCTION(0x2, "tdm", "spi-sck", V(0, 0, 0, 1, 1, 0)), + MPP_VAR_FUNCTION(0x4, "audio", "sdo", V(0, 0, 0, 1, 1, 0)), + MPP_VAR_FUNCTION(0xb, "lcd", "d20", V(0, 0, 0, 0, 1, 0))), MPP_MODE(41, - MPP_VAR_FUNCTION(0x0, "gpio", NULL, V(0, 0, 0, 1, 1)), - MPP_VAR_FUNCTION(0x1, "ts", "mp5", V(0, 0, 0, 1, 1)), - MPP_VAR_FUNCTION(0x2, "tdm", "spi-miso", V(0, 0, 0, 1, 1)), - MPP_VAR_FUNCTION(0x4, "audio", "lrclk", V(0, 0, 0, 1, 1)), - MPP_VAR_FUNCTION(0xb, "lcd", "d21", V(0, 0, 0, 0, 1))), + MPP_VAR_FUNCTION(0x0, "gpio", NULL, V(0, 0, 0, 1, 1, 1)), + MPP_VAR_FUNCTION(0x1, "ts", "mp5", V(0, 0, 0, 1, 1, 0)), + MPP_VAR_FUNCTION(0x2, "tdm", "spi-miso", V(0, 0, 0, 1, 1, 0)), + MPP_VAR_FUNCTION(0x4, "audio", "lrclk", V(0, 0, 0, 1, 1, 0)), + MPP_VAR_FUNCTION(0xb, "lcd", "d21", V(0, 0, 0, 0, 1, 0))), MPP_MODE(42, - MPP_VAR_FUNCTION(0x0, "gpio", NULL, V(0, 0, 0, 1, 1)), - MPP_VAR_FUNCTION(0x1, "ts", "mp6", V(0, 0, 0, 1, 1)), - MPP_VAR_FUNCTION(0x2, "tdm", "spi-mosi", V(0, 0, 0, 1, 1)), - MPP_VAR_FUNCTION(0x4, "audio", "mclk", V(0, 0, 0, 1, 1)), - MPP_VAR_FUNCTION(0xb, "lcd", "d22", V(0, 0, 0, 0, 1))), + MPP_VAR_FUNCTION(0x0, "gpio", NULL, V(0, 0, 0, 1, 1, 1)), + MPP_VAR_FUNCTION(0x1, "ts", "mp6", V(0, 0, 0, 1, 1, 0)), + MPP_VAR_FUNCTION(0x2, "tdm", "spi-mosi", V(0, 0, 0, 1, 1, 0)), + MPP_VAR_FUNCTION(0x4, "audio", "mclk", V(0, 0, 0, 1, 1, 0)), + MPP_VAR_FUNCTION(0xb, "lcd", "d22", V(0, 0, 0, 0, 1, 0))), MPP_MODE(43, - MPP_VAR_FUNCTION(0x0, "gpio", NULL, V(0, 0, 0, 1, 1)), - MPP_VAR_FUNCTION(0x1, "ts", "mp7", V(0, 0, 0, 1, 1)), - MPP_VAR_FUNCTION(0x2, "tdm", "int", V(0, 0, 0, 1, 1)), - MPP_VAR_FUNCTION(0x4, "audio", "sdi", V(0, 0, 0, 1, 1)), - MPP_VAR_FUNCTION(0xb, "lcd", "d23", V(0, 0, 0, 0, 1))), + MPP_VAR_FUNCTION(0x0, "gpio", NULL, V(0, 0, 0, 1, 1, 1)), + MPP_VAR_FUNCTION(0x1, "ts", "mp7", V(0, 0, 0, 1, 1, 0)), + MPP_VAR_FUNCTION(0x2, "tdm", "int", V(0, 0, 0, 1, 1, 0)), + MPP_VAR_FUNCTION(0x4, "audio", "sdi", V(0, 0, 0, 1, 1, 0)), + MPP_VAR_FUNCTION(0xb, "lcd", "d23", V(0, 0, 0, 0, 1, 0))), MPP_MODE(44, - MPP_VAR_FUNCTION(0x0, "gpio", NULL, V(0, 0, 0, 1, 1)), - MPP_VAR_FUNCTION(0x1, "ts", "mp8", V(0, 0, 0, 1, 1)), - MPP_VAR_FUNCTION(0x2, "tdm", "rst", V(0, 0, 0, 1, 1)), - MPP_VAR_FUNCTION(0x4, "audio", "extclk", V(0, 0, 0, 1, 1)), - MPP_VAR_FUNCTION(0xb, "lcd", "clk", V(0, 0, 0, 0, 1))), + MPP_VAR_FUNCTION(0x0, "gpio", NULL, V(0, 0, 0, 1, 1, 1)), + MPP_VAR_FUNCTION(0x1, "ts", "mp8", V(0, 0, 0, 1, 1, 0)), + MPP_VAR_FUNCTION(0x2, "tdm", "rst", V(0, 0, 0, 1, 1, 0)), + MPP_VAR_FUNCTION(0x4, "audio", "extclk", V(0, 0, 0, 1, 1, 0)), + MPP_VAR_FUNCTION(0xb, "lcd", "clk", V(0, 0, 0, 0, 1, 0))), MPP_MODE(45, - MPP_VAR_FUNCTION(0x0, "gpio", NULL, V(0, 0, 0, 1, 1)), - MPP_VAR_FUNCTION(0x1, "ts", "mp9", V(0, 0, 0, 1, 1)), - MPP_VAR_FUNCTION(0x2, "tdm", "pclk", V(0, 0, 0, 1, 1)), - MPP_VAR_FUNCTION(0xb, "lcd", "e", V(0, 0, 0, 0, 1))), + MPP_VAR_FUNCTION(0x0, "gpio", NULL, V(0, 0, 0, 1, 1, 1)), + MPP_VAR_FUNCTION(0x1, "ts", "mp9", V(0, 0, 0, 1, 1, 0)), + MPP_VAR_FUNCTION(0x2, "tdm", "pclk", V(0, 0, 0, 1, 1, 0)), + MPP_VAR_FUNCTION(0xb, "lcd", "e", V(0, 0, 0, 0, 1, 0))), MPP_MODE(46, - MPP_VAR_FUNCTION(0x0, "gpio", NULL, V(0, 0, 0, 1, 1)), - MPP_VAR_FUNCTION(0x1, "ts", "mp10", V(0, 0, 0, 1, 1)), - MPP_VAR_FUNCTION(0x2, "tdm", "fs", V(0, 0, 0, 1, 1)), - MPP_VAR_FUNCTION(0xb, "lcd", "hsync", V(0, 0, 0, 0, 1))), + MPP_VAR_FUNCTION(0x0, "gpio", NULL, V(0, 0, 0, 1, 1, 0)), + MPP_VAR_FUNCTION(0x1, "ts", "mp10", V(0, 0, 0, 1, 1, 0)), + MPP_VAR_FUNCTION(0x2, "tdm", "fs", V(0, 0, 0, 1, 1, 0)), + MPP_VAR_FUNCTION(0xb, "lcd", "hsync", V(0, 0, 0, 0, 1, 0))), MPP_MODE(47, - MPP_VAR_FUNCTION(0x0, "gpio", NULL, V(0, 0, 0, 1, 1)), - MPP_VAR_FUNCTION(0x1, "ts", "mp11", V(0, 0, 0, 1, 1)), - MPP_VAR_FUNCTION(0x2, "tdm", "drx", V(0, 0, 0, 1, 1)), - MPP_VAR_FUNCTION(0xb, "lcd", "vsync", V(0, 0, 0, 0, 1))), + MPP_VAR_FUNCTION(0x0, "gpio", NULL, V(0, 0, 0, 1, 1, 0)), + MPP_VAR_FUNCTION(0x1, "ts", "mp11", V(0, 0, 0, 1, 1, 0)), + MPP_VAR_FUNCTION(0x2, "tdm", "drx", V(0, 0, 0, 1, 1, 0)), + MPP_VAR_FUNCTION(0xb, "lcd", "vsync", V(0, 0, 0, 0, 1, 0))), MPP_MODE(48, - MPP_VAR_FUNCTION(0x0, "gpio", NULL, V(0, 0, 0, 1, 1)), - MPP_VAR_FUNCTION(0x1, "ts", "mp12", V(0, 0, 0, 1, 1)), - MPP_VAR_FUNCTION(0x2, "tdm", "dtx", V(0, 0, 0, 1, 1)), - MPP_VAR_FUNCTION(0xb, "lcd", "d16", V(0, 0, 0, 0, 1))), + MPP_VAR_FUNCTION(0x0, "gpio", NULL, V(0, 0, 0, 1, 1, 0)), + MPP_VAR_FUNCTION(0x1, "ts", "mp12", V(0, 0, 0, 1, 1, 0)), + MPP_VAR_FUNCTION(0x2, "tdm", "dtx", V(0, 0, 0, 1, 1, 0)), + MPP_VAR_FUNCTION(0xb, "lcd", "d16", V(0, 0, 0, 0, 1, 0))), MPP_MODE(49, - MPP_VAR_FUNCTION(0x0, "gpio", NULL, V(0, 0, 0, 1, 0)), - MPP_VAR_FUNCTION(0x0, "gpo", NULL, V(0, 0, 0, 0, 1)), - MPP_VAR_FUNCTION(0x1, "ts", "mp9", V(0, 0, 0, 1, 0)), - MPP_VAR_FUNCTION(0x2, "tdm", "rx0ql", V(0, 0, 0, 1, 1)), - MPP_VAR_FUNCTION(0x5, "ptp", "clk", V(0, 0, 0, 1, 0)), - MPP_VAR_FUNCTION(0xa, "pex", "clkreq", V(0, 0, 0, 0, 1)), - MPP_VAR_FUNCTION(0xb, "lcd", "d17", V(0, 0, 0, 0, 1))), + MPP_VAR_FUNCTION(0x0, "gpio", NULL, V(0, 0, 0, 1, 0, 1)), + MPP_VAR_FUNCTION(0x0, "gpo", NULL, V(0, 0, 0, 0, 1, 0)), + MPP_VAR_FUNCTION(0x1, "ts", "mp9", V(0, 0, 0, 1, 0, 0)), + MPP_VAR_FUNCTION(0x2, "tdm", "rx0ql", V(0, 0, 0, 1, 1, 0)), + MPP_VAR_FUNCTION(0x5, "ptp", "clk", V(0, 0, 0, 1, 0, 0)), + MPP_VAR_FUNCTION(0xa, "pex", "clkreq", V(0, 0, 0, 0, 1, 0)), + MPP_VAR_FUNCTION(0xb, "lcd", "d17", V(0, 0, 0, 0, 1, 0))), }; static struct mvebu_mpp_ctrl mv88f6180_mpp_controls[] = { @@ -433,12 +434,23 @@ static struct mvebu_pinctrl_soc_info mv88f6282_info = { .ngpioranges = ARRAY_SIZE(mv88f628x_gpio_ranges), }; +static struct mvebu_pinctrl_soc_info mv98dx4122_info = { + .variant = VARIANT_MV98DX4122, + .controls = mv88f628x_mpp_controls, + .ncontrols = ARRAY_SIZE(mv88f628x_mpp_controls), + .modes = mv88f6xxx_mpp_modes, + .nmodes = ARRAY_SIZE(mv88f6xxx_mpp_modes), + .gpioranges = mv88f628x_gpio_ranges, + .ngpioranges = ARRAY_SIZE(mv88f628x_gpio_ranges), +}; + static struct of_device_id kirkwood_pinctrl_of_match[] __devinitdata = { { .compatible = "marvell,88f6180-pinctrl", .data = &mv88f6180_info }, { .compatible = "marvell,88f6190-pinctrl", .data = &mv88f6190_info }, { .compatible = "marvell,88f6192-pinctrl", .data = &mv88f6192_info }, { .compatible = "marvell,88f6281-pinctrl", .data = &mv88f6281_info }, { .compatible = "marvell,88f6282-pinctrl", .data = &mv88f6282_info }, + { .compatible = "marvell,98dx4122-pinctrl", .data = &mv98dx4122_info }, { } }; -- cgit v1.2.3-59-g8ed1b From 6ea32387a0c7fb9ca0213fd22b47c5a1ca4c2972 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Wed, 14 Nov 2012 08:55:21 -0800 Subject: Input: stmpe-keypad - add support for Device Tree bindings This patch allows the STMPE driver to be successfully probed and initialised when Device Tree support is enabled. Besides the usual platform data changes, we also separate the process of filling in the 'in use' pin bitmap, as we have to extract the information from Device Tree in the DT boot case. Acked-by: Arnd Bergmann Acked-by: Linus Walleij Signed-off-by: Lee Jones Signed-off-by: Dmitry Torokhov --- .../devicetree/bindings/input/stmpe-keypad.txt | 39 ++++++++++++++ drivers/input/keyboard/stmpe-keypad.c | 60 ++++++++++++++++++---- drivers/mfd/stmpe.c | 1 + 3 files changed, 91 insertions(+), 9 deletions(-) create mode 100644 Documentation/devicetree/bindings/input/stmpe-keypad.txt (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/input/stmpe-keypad.txt b/Documentation/devicetree/bindings/input/stmpe-keypad.txt new file mode 100644 index 000000000000..1b97222e8a0b --- /dev/null +++ b/Documentation/devicetree/bindings/input/stmpe-keypad.txt @@ -0,0 +1,39 @@ +* STMPE Keypad + +Required properties: + - compatible : "st,stmpe-keypad" + - linux,keymap : See ./matrix-keymap.txt + +Optional properties: + - debounce-interval : Debouncing interval time in milliseconds + - st,scan-count : Scanning cycles elapsed before key data is updated + - st,no-autorepeat : If specified device will not autorepeat + +Example: + + stmpe_keypad { + compatible = "st,stmpe-keypad"; + + debounce-interval = <64>; + st,scan-count = <8>; + st,no-autorepeat; + + linux,keymap = <0x205006b + 0x4010074 + 0x3050072 + 0x1030004 + 0x502006a + 0x500000a + 0x5008b + 0x706001c + 0x405000b + 0x6070003 + 0x3040067 + 0x303006c + 0x60400e7 + 0x602009e + 0x4020073 + 0x5050002 + 0x4030069 + 0x3020008>; + }; diff --git a/drivers/input/keyboard/stmpe-keypad.c b/drivers/input/keyboard/stmpe-keypad.c index 7bac0524900a..5cbec56f7720 100644 --- a/drivers/input/keyboard/stmpe-keypad.c +++ b/drivers/input/keyboard/stmpe-keypad.c @@ -257,6 +257,51 @@ static int stmpe_keypad_chip_init(struct stmpe_keypad *keypad) (plat->debounce_ms << 1)); } +static void stmpe_keypad_fill_used_pins(struct stmpe_keypad *keypad) +{ + int row, col; + + for (row = 0; row < STMPE_KEYPAD_MAX_ROWS; row++) { + for (col = 0; col < STMPE_KEYPAD_MAX_COLS; col++) { + int code = MATRIX_SCAN_CODE(row, col, + STMPE_KEYPAD_ROW_SHIFT); + if (keypad->keymap[code] != KEY_RESERVED) { + keypad->rows |= 1 << row; + keypad->cols |= 1 << col; + } + } + } +} + +#ifdef CONFIG_OF +static const struct stmpe_keypad_platform_data * +stmpe_keypad_of_probe(struct device *dev) +{ + struct device_node *np = dev->of_node; + struct stmpe_keypad_platform_data *plat; + + if (!np) + return ERR_PTR(-ENODEV); + + plat = devm_kzalloc(dev, sizeof(*plat), GFP_KERNEL); + if (!plat) + return ERR_PTR(-ENOMEM); + + of_property_read_u32(np, "debounce-interval", &plat->debounce_ms); + of_property_read_u32(np, "st,scan-count", &plat->scan_count); + + plat->no_autorepeat = of_property_read_bool(np, "st,no-autorepeat"); + + return plat; +} +#else +static inline const struct stmpe_keypad_platform_data * +stmpe_keypad_of_probe(struct device *dev) +{ + return ERR_PTR(-EINVAL); +} +#endif + static int stmpe_keypad_probe(struct platform_device *pdev) { struct stmpe *stmpe = dev_get_drvdata(pdev->dev.parent); @@ -265,11 +310,13 @@ static int stmpe_keypad_probe(struct platform_device *pdev) struct input_dev *input; int error; int irq; - int i; plat = stmpe->pdata->keypad; - if (!plat) - return -ENODEV; + if (!plat) { + plat = stmpe_keypad_of_probe(&pdev->dev); + if (IS_ERR(plat)) + return PTR_ERR(plat); + } irq = platform_get_irq(pdev, 0); if (irq < 0) @@ -299,12 +346,7 @@ static int stmpe_keypad_probe(struct platform_device *pdev) if (!plat->no_autorepeat) __set_bit(EV_REP, input->evbit); - for (i = 0; i < plat->keymap_data->keymap_size; i++) { - unsigned int key = plat->keymap_data->keymap[i]; - - keypad->cols |= 1 << KEY_COL(key); - keypad->rows |= 1 << KEY_ROW(key); - } + stmpe_keypad_fill_used_pins(keypad); keypad->stmpe = stmpe; keypad->plat = plat; diff --git a/drivers/mfd/stmpe.c b/drivers/mfd/stmpe.c index 55c7b9531ad4..bf1ba93f43a0 100644 --- a/drivers/mfd/stmpe.c +++ b/drivers/mfd/stmpe.c @@ -324,6 +324,7 @@ static struct resource stmpe_keypad_resources[] = { static struct mfd_cell stmpe_keypad_cell = { .name = "stmpe-keypad", + .of_compatible = "st,stmpe-keypad", .resources = stmpe_keypad_resources, .num_resources = ARRAY_SIZE(stmpe_keypad_resources), }; -- cgit v1.2.3-59-g8ed1b From 1723d90915d4689fa2e8cd4151d45ea38c96cb99 Mon Sep 17 00:00:00 2001 From: Xuelin Shi Date: Wed, 21 Nov 2012 17:01:20 +0800 Subject: powerpc/dma/raidengine: add raidengine device The RaidEngine is a new Freescale hardware that used for parity computation offloading in RAID5/6. This patch adds the device node in device tree and related binding documentation. Signed-off-by: Harninder Rai Signed-off-by: Naveen Burmi Signed-off-by: Xuelin Shi Signed-off-by: Kumar Gala --- .../devicetree/bindings/powerpc/fsl/raideng.txt | 81 +++++++++++++++++++++ arch/powerpc/boot/dts/fsl/p5020si-post.dtsi | 1 + arch/powerpc/boot/dts/fsl/p5020si-pre.dtsi | 6 ++ arch/powerpc/boot/dts/fsl/qoriq-raid1.0-0.dtsi | 85 ++++++++++++++++++++++ 4 files changed, 173 insertions(+) create mode 100644 Documentation/devicetree/bindings/powerpc/fsl/raideng.txt create mode 100644 arch/powerpc/boot/dts/fsl/qoriq-raid1.0-0.dtsi (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/powerpc/fsl/raideng.txt b/Documentation/devicetree/bindings/powerpc/fsl/raideng.txt new file mode 100644 index 000000000000..4ad29b9ac2ac --- /dev/null +++ b/Documentation/devicetree/bindings/powerpc/fsl/raideng.txt @@ -0,0 +1,81 @@ +* Freescale 85xx RAID Engine nodes + +RAID Engine nodes are defined to describe on-chip RAID accelerators. Each RAID +Engine should have a separate node. + +Supported chips: +P5020, P5040 + +Required properties: + +- compatible: Should contain "fsl,raideng-v1.0" as the value + This identifies RAID Engine block. 1 in 1.0 represents + major number whereas 0 represents minor number. The + version matches the hardware IP version. +- reg: offset and length of the register set for the device +- ranges: standard ranges property specifying the translation + between child address space and parent address space + +Example: + /* P5020 */ + raideng: raideng@320000 { + compatible = "fsl,raideng-v1.0"; + #address-cells = <1>; + #size-cells = <1>; + reg = <0x320000 0x10000>; + ranges = <0 0x320000 0x10000>; + }; + + +There must be a sub-node for each job queue present in RAID Engine +This node must be a sub-node of the main RAID Engine node + +- compatible: Should contain "fsl,raideng-v1.0-job-queue" as the value + This identifies the job queue interface +- reg: offset and length of the register set for job queue +- ranges: standard ranges property specifying the translation + between child address space and parent address space + +Example: + /* P5020 */ + raideng_jq0@1000 { + compatible = "fsl,raideng-v1.0-job-queue"; + reg = <0x1000 0x1000>; + ranges = <0x0 0x1000 0x1000>; + }; + + +There must be a sub-node for each job ring present in RAID Engine +This node must be a sub-node of job queue node + +- compatible: Must contain "fsl,raideng-v1.0-job-ring" as the value + This identifies job ring. Should contain either + "fsl,raideng-v1.0-hp-ring" or "fsl,raideng-v1.0-lp-ring" + depending upon whether ring has high or low priority +- reg: offset and length of the register set for job ring +- interrupts: interrupt mapping for job ring IRQ + +Optional property: + +- fsl,liodn: Specifies the LIODN to be used for Job Ring. This + property is normally set by firmware. Value + is of 12-bits which is the LIODN number for this JR. + This property is used by the IOMMU (PAMU) to distinquish + transactions from this JR and than be able to do address + translation & protection accordingly. + +Example: + /* P5020 */ + raideng_jq0@1000 { + compatible = "fsl,raideng-v1.0-job-queue"; + reg = <0x1000 0x1000>; + ranges = <0x0 0x1000 0x1000>; + + raideng_jr0: jr@0 { + compatible = "fsl,raideng-v1.0-job-ring", "fsl,raideng-v1.0-hp-ring"; + reg = <0x0 0x400>; + interrupts = <139 2 0 0>; + interrupt-parent = <&mpic>; + fsl,liodn = <0x41>; + }; + }; diff --git a/arch/powerpc/boot/dts/fsl/p5020si-post.dtsi b/arch/powerpc/boot/dts/fsl/p5020si-post.dtsi index 64b6abea8464..5d7205b7bb05 100644 --- a/arch/powerpc/boot/dts/fsl/p5020si-post.dtsi +++ b/arch/powerpc/boot/dts/fsl/p5020si-post.dtsi @@ -354,4 +354,5 @@ /include/ "qoriq-sata2-0.dtsi" /include/ "qoriq-sata2-1.dtsi" /include/ "qoriq-sec4.2-0.dtsi" +/include/ "qoriq-raid1.0-0.dtsi" }; diff --git a/arch/powerpc/boot/dts/fsl/p5020si-pre.dtsi b/arch/powerpc/boot/dts/fsl/p5020si-pre.dtsi index 0a198b0a77e5..8df47fc45ab5 100644 --- a/arch/powerpc/boot/dts/fsl/p5020si-pre.dtsi +++ b/arch/powerpc/boot/dts/fsl/p5020si-pre.dtsi @@ -73,6 +73,12 @@ rtic_c = &rtic_c; rtic_d = &rtic_d; sec_mon = &sec_mon; + + raideng = &raideng; + raideng_jr0 = &raideng_jr0; + raideng_jr1 = &raideng_jr1; + raideng_jr2 = &raideng_jr2; + raideng_jr3 = &raideng_jr3; }; cpus { diff --git a/arch/powerpc/boot/dts/fsl/qoriq-raid1.0-0.dtsi b/arch/powerpc/boot/dts/fsl/qoriq-raid1.0-0.dtsi new file mode 100644 index 000000000000..8d2e8aa6cf8a --- /dev/null +++ b/arch/powerpc/boot/dts/fsl/qoriq-raid1.0-0.dtsi @@ -0,0 +1,85 @@ +/* + * QorIQ RAID 1.0 device tree stub [ controller @ offset 0x320000 ] + * + * Copyright 2012 Freescale Semiconductor Inc. + * + * 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. + * * Neither the name of Freescale Semiconductor 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") as published by the Free Software + * Foundation, either version 2 of that License or (at your option) any + * later version. + * + * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``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 Freescale Semiconductor 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. + */ + +raideng: raideng@320000 { + compatible = "fsl,raideng-v1.0"; + #address-cells = <1>; + #size-cells = <1>; + reg = <0x320000 0x10000>; + ranges = <0 0x320000 0x10000>; + + raideng_jq0@1000 { + compatible = "fsl,raideng-v1.0-job-queue"; + #address-cells = <1>; + #size-cells = <1>; + reg = <0x1000 0x1000>; + ranges = <0x0 0x1000 0x1000>; + + raideng_jr0: jr@0 { + compatible = "fsl,raideng-v1.0-job-ring", "fsl,raideng-v1.0-hp-ring"; + reg = <0x0 0x400>; + interrupts = <139 2 0 0>; + interrupt-parent = <&mpic>; + }; + + raideng_jr1: jr@400 { + compatible = "fsl,raideng-v1.0-job-ring", "fsl,raideng-v1.0-lp-ring"; + reg = <0x400 0x400>; + interrupts = <140 2 0 0>; + interrupt-parent = <&mpic>; + }; + }; + + raideng_jq1@2000 { + compatible = "fsl,raideng-v1.0-job-queue"; + #address-cells = <1>; + #size-cells = <1>; + reg = <0x2000 0x1000>; + ranges = <0x0 0x2000 0x1000>; + + raideng_jr2: jr@0 { + compatible = "fsl,raideng-v1.0-job-ring", "fsl,raideng-v1.0-hp-ring"; + reg = <0x0 0x400>; + interrupts = <141 2 0 0>; + interrupt-parent = <&mpic>; + }; + + raideng_jr3: jr@400 { + compatible = "fsl,raideng-v1.0-job-ring", "fsl,raideng-v1.0-lp-ring"; + reg = <0x400 0x400>; + interrupts = <142 2 0 0>; + interrupt-parent = <&mpic>; + }; + }; +}; -- cgit v1.2.3-59-g8ed1b From 4b9c1cb641c466cd3b366132017da3c8ab9d540c Mon Sep 17 00:00:00 2001 From: Tomasz Stanislawski Date: Thu, 14 Jun 2012 10:37:36 -0300 Subject: [media] Documentation: media: description of DMABUF importing in V4L2 This patch adds description and usage examples for importing DMABUF file descriptor in V4L2. Signed-off-by: Tomasz Stanislawski Signed-off-by: Kyungmin Park Acked-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- Documentation/DocBook/media/v4l/compat.xml | 4 + Documentation/DocBook/media/v4l/io.xml | 181 ++++++++++++++++++++- .../DocBook/media/v4l/vidioc-create-bufs.xml | 16 +- Documentation/DocBook/media/v4l/vidioc-qbuf.xml | 17 ++ Documentation/DocBook/media/v4l/vidioc-reqbufs.xml | 47 +++--- 5 files changed, 235 insertions(+), 30 deletions(-) (limited to 'Documentation') diff --git a/Documentation/DocBook/media/v4l/compat.xml b/Documentation/DocBook/media/v4l/compat.xml index 4fdf6b562d1c..50eb63024eb7 100644 --- a/Documentation/DocBook/media/v4l/compat.xml +++ b/Documentation/DocBook/media/v4l/compat.xml @@ -2586,6 +2586,10 @@ ioctls. Vendor and device specific media bus pixel formats. . + + Importing DMABUF file descriptors as a new IO method described + in . + diff --git a/Documentation/DocBook/media/v4l/io.xml b/Documentation/DocBook/media/v4l/io.xml index 7e2f3d7d78ad..be0fad7e1209 100644 --- a/Documentation/DocBook/media/v4l/io.xml +++ b/Documentation/DocBook/media/v4l/io.xml @@ -331,7 +331,7 @@ application until one or more buffers can be dequeued. By default outgoing queue. When the O_NONBLOCK flag was given to the &func-open; function, VIDIOC_DQBUF returns immediately with an &EAGAIN; when no buffer is available. The -&func-select; or &func-poll; function are always available. +&func-select; or &func-poll; functions are always available. To start and stop capturing or output applications call the &VIDIOC-STREAMON; and &VIDIOC-STREAMOFF; ioctl. Note @@ -472,6 +472,162 @@ rest should be evident. +
+ Streaming I/O (DMA buffer importing) + + + Experimental + This is an experimental + interface and may change in the future. + + +The DMABUF framework provides a generic method for sharing buffers +between multiple devices. Device drivers that support DMABUF can export a DMA +buffer to userspace as a file descriptor (known as the exporter role), import a +DMA buffer from userspace using a file descriptor previously exported for a +different or the same device (known as the importer role), or both. This +section describes the DMABUF importer role API in V4L2. + +Input and output devices support the streaming I/O method when the +V4L2_CAP_STREAMING flag in the +capabilities field of &v4l2-capability; returned by +the &VIDIOC-QUERYCAP; ioctl is set. Whether importing DMA buffers through +DMABUF file descriptors is supported is determined by calling the +&VIDIOC-REQBUFS; ioctl with the memory type set to +V4L2_MEMORY_DMABUF. + + This I/O method is dedicated to sharing DMA buffers between different +devices, which may be V4L devices or other video-related devices (e.g. DRM). +Buffers (planes) are allocated by a driver on behalf of an application. Next, +these buffers are exported to the application as file descriptors using an API +which is specific for an allocator driver. Only such file descriptor are +exchanged. The descriptors and meta-information are passed in &v4l2-buffer; (or +in &v4l2-plane; in the multi-planar API case). The driver must be switched +into DMABUF I/O mode by calling the &VIDIOC-REQBUFS; with the desired buffer +type. + + + Initiating streaming I/O with DMABUF file descriptors + + +&v4l2-requestbuffers; reqbuf; + +memset(&reqbuf, 0, sizeof (reqbuf)); +reqbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; +reqbuf.memory = V4L2_MEMORY_DMABUF; +reqbuf.count = 1; + +if (ioctl(fd, &VIDIOC-REQBUFS;, &reqbuf) == -1) { + if (errno == EINVAL) + printf("Video capturing or DMABUF streaming is not supported\n"); + else + perror("VIDIOC_REQBUFS"); + + exit(EXIT_FAILURE); +} + + + + The buffer (plane) file descriptor is passed on the fly with the +&VIDIOC-QBUF; ioctl. In case of multiplanar buffers, every plane can be +associated with a different DMABUF descriptor. Although buffers are commonly +cycled, applications can pass a different DMABUF descriptor at each +VIDIOC_QBUF call. + + + Queueing DMABUF using single plane API + + +int buffer_queue(int v4lfd, int index, int dmafd) +{ + &v4l2-buffer; buf; + + memset(&buf, 0, sizeof buf); + buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + buf.memory = V4L2_MEMORY_DMABUF; + buf.index = index; + buf.m.fd = dmafd; + + if (ioctl(v4lfd, &VIDIOC-QBUF;, &buf) == -1) { + perror("VIDIOC_QBUF"); + return -1; + } + + return 0; +} + + + + + Queueing DMABUF using multi plane API + + +int buffer_queue_mp(int v4lfd, int index, int dmafd[], int n_planes) +{ + &v4l2-buffer; buf; + &v4l2-plane; planes[VIDEO_MAX_PLANES]; + int i; + + memset(&buf, 0, sizeof buf); + buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; + buf.memory = V4L2_MEMORY_DMABUF; + buf.index = index; + buf.m.planes = planes; + buf.length = n_planes; + + memset(&planes, 0, sizeof planes); + + for (i = 0; i < n_planes; ++i) + buf.m.planes[i].m.fd = dmafd[i]; + + if (ioctl(v4lfd, &VIDIOC-QBUF;, &buf) == -1) { + perror("VIDIOC_QBUF"); + return -1; + } + + return 0; +} + + + + Captured or displayed buffers are dequeued with the +&VIDIOC-DQBUF; ioctl. The driver can unlock the buffer at any +time between the completion of the DMA and this ioctl. The memory is +also unlocked when &VIDIOC-STREAMOFF; is called, &VIDIOC-REQBUFS;, or +when the device is closed. + + For capturing applications it is customary to enqueue a +number of empty buffers, to start capturing and enter the read loop. +Here the application waits until a filled buffer can be dequeued, and +re-enqueues the buffer when the data is no longer needed. Output +applications fill and enqueue buffers, when enough buffers are stacked +up output is started. In the write loop, when the application +runs out of free buffers it must wait until an empty buffer can be +dequeued and reused. Two methods exist to suspend execution of the +application until one or more buffers can be dequeued. By default +VIDIOC_DQBUF blocks when no buffer is in the +outgoing queue. When the O_NONBLOCK flag was +given to the &func-open; function, VIDIOC_DQBUF +returns immediately with an &EAGAIN; when no buffer is available. The +&func-select; and &func-poll; functions are always available. + + To start and stop capturing or displaying applications call the +&VIDIOC-STREAMON; and &VIDIOC-STREAMOFF; ioctls. Note that +VIDIOC_STREAMOFF removes all buffers from both queues and +unlocks all buffers as a side effect. Since there is no notion of doing +anything "now" on a multitasking system, if an application needs to synchronize +with another event it should examine the &v4l2-buffer; +timestamp of captured buffers, or set the field +before enqueuing buffers for output. + + Drivers implementing DMABUF importing I/O must support the +VIDIOC_REQBUFS, VIDIOC_QBUF, +VIDIOC_DQBUF, VIDIOC_STREAMON and +VIDIOC_STREAMOFF ioctls, and the +select() and poll() functions. + +
+
Asynchronous I/O @@ -672,6 +828,14 @@ memory, set by the application. See for details. in the length field of this v4l2_buffer structure. + + + int + fd + For the single-plane API and when +memory is V4L2_MEMORY_DMABUF this +is the file descriptor associated with a DMABUF buffer. + __u32 length @@ -743,6 +907,15 @@ should set this to 0. pointer to the memory allocated for this plane by an application. + + + int + fd + When the memory type in the containing &v4l2-buffer; is + V4L2_MEMORY_DMABUF, this is a file + descriptor associated with a DMABUF buffer, similar to the + fd field in &v4l2-buffer;. + __u32 data_offset @@ -964,6 +1137,12 @@ pointer I/O. 3 [to do] + + V4L2_MEMORY_DMABUF + 4 + The buffer is used for DMA shared +buffer I/O. + diff --git a/Documentation/DocBook/media/v4l/vidioc-create-bufs.xml b/Documentation/DocBook/media/v4l/vidioc-create-bufs.xml index a8cda1acacd9..cd9943672434 100644 --- a/Documentation/DocBook/media/v4l/vidioc-create-bufs.xml +++ b/Documentation/DocBook/media/v4l/vidioc-create-bufs.xml @@ -6,7 +6,8 @@ VIDIOC_CREATE_BUFS - Create buffers for Memory Mapped or User Pointer I/O + Create buffers for Memory Mapped or User Pointer or DMA Buffer + I/O @@ -55,11 +56,11 @@ This ioctl is used to create buffers for memory -mapped or user pointer -I/O. It can be used as an alternative or in addition to the -VIDIOC_REQBUFS ioctl, when a tighter control over buffers -is required. This ioctl can be called multiple times to create buffers of -different sizes. +mapped or user pointer or DMA buffer I/O. It can be used as an alternative or in +addition to the VIDIOC_REQBUFS ioctl, when a tighter +control over buffers is required. This ioctl can be called multiple times to +create buffers of different sizes. To allocate device buffers applications initialize relevant fields of the v4l2_create_buffers structure. They set the @@ -109,7 +110,8 @@ information. __u32 memory Applications set this field to -V4L2_MEMORY_MMAP or +V4L2_MEMORY_MMAP, +V4L2_MEMORY_DMABUF or V4L2_MEMORY_USERPTR. See diff --git a/Documentation/DocBook/media/v4l/vidioc-qbuf.xml b/Documentation/DocBook/media/v4l/vidioc-qbuf.xml index 2d37abefce13..3504a7f2f382 100644 --- a/Documentation/DocBook/media/v4l/vidioc-qbuf.xml +++ b/Documentation/DocBook/media/v4l/vidioc-qbuf.xml @@ -109,6 +109,23 @@ they cannot be swapped out to disk. Buffers remain locked until dequeued, until the &VIDIOC-STREAMOFF; or &VIDIOC-REQBUFS; ioctl is called, or until the device is closed. + To enqueue a DMABUF buffer applications +set the memory field to +V4L2_MEMORY_DMABUF and the m.fd +field to a file descriptor associated with a DMABUF buffer. When the +multi-planar API is used the m.fd fields of the +passed array of &v4l2-plane; have to be used instead. When +VIDIOC_QBUF is called with a pointer to this structure the +driver sets the V4L2_BUF_FLAG_QUEUED flag and clears the +V4L2_BUF_FLAG_MAPPED and +V4L2_BUF_FLAG_DONE flags in the +flags field, or it returns an error code. This +ioctl locks the buffer. Locking a buffer means passing it to a driver for a +hardware access (usually DMA). If an application accesses (reads/writes) a +locked buffer then the result is undefined. Buffers remain locked until +dequeued, until the &VIDIOC-STREAMOFF; or &VIDIOC-REQBUFS; ioctl is called, or +until the device is closed. + Applications call the VIDIOC_DQBUF ioctl to dequeue a filled (capturing) or displayed (output) buffer from the driver's outgoing queue. They just set the diff --git a/Documentation/DocBook/media/v4l/vidioc-reqbufs.xml b/Documentation/DocBook/media/v4l/vidioc-reqbufs.xml index 2b50ef2007f3..78a06a9a5ece 100644 --- a/Documentation/DocBook/media/v4l/vidioc-reqbufs.xml +++ b/Documentation/DocBook/media/v4l/vidioc-reqbufs.xml @@ -48,28 +48,30 @@ Description - This ioctl is used to initiate memory -mapped or user pointer -I/O. Memory mapped buffers are located in device memory and must be -allocated with this ioctl before they can be mapped into the -application's address space. User buffers are allocated by -applications themselves, and this ioctl is merely used to switch the -driver into user pointer I/O mode and to setup some internal structures. +This ioctl is used to initiate memory mapped, +user pointer or DMABUF based I/O. Memory mapped buffers are located in +device memory and must be allocated with this ioctl before they can be mapped +into the application's address space. User buffers are allocated by +applications themselves, and this ioctl is merely used to switch the driver +into user pointer I/O mode and to setup some internal structures. +Similarly, DMABUF buffers are allocated by applications through a device +driver, and this ioctl only configures the driver into DMABUF I/O mode without +performing any direct allocation. - To allocate device buffers applications initialize all -fields of the v4l2_requestbuffers structure. -They set the type field to the respective -stream or buffer type, the count field to -the desired number of buffers, memory -must be set to the requested I/O method and the reserved array -must be zeroed. When the ioctl -is called with a pointer to this structure the driver will attempt to allocate -the requested number of buffers and it stores the actual number -allocated in the count field. It can be -smaller than the number requested, even zero, when the driver runs out -of free memory. A larger number is also possible when the driver requires -more buffers to function correctly. For example video output requires at least two buffers, -one displayed and one filled by the application. + To allocate device buffers applications initialize all fields of the +v4l2_requestbuffers structure. They set the +type field to the respective stream or buffer type, +the count field to the desired number of buffers, +memory must be set to the requested I/O method and +the reserved array must be zeroed. When the ioctl is +called with a pointer to this structure the driver will attempt to allocate the +requested number of buffers and it stores the actual number allocated in the +count field. It can be smaller than the number +requested, even zero, when the driver runs out of free memory. A larger number +is also possible when the driver requires more buffers to function correctly. +For example video output requires at least two buffers, one displayed and one +filled by the application. When the I/O method is not supported the ioctl returns an &EINVAL;. @@ -102,7 +104,8 @@ as the &v4l2-format; type field. See __u32 memory Applications set this field to -V4L2_MEMORY_MMAP or +V4L2_MEMORY_MMAP, +V4L2_MEMORY_DMABUF or V4L2_MEMORY_USERPTR. See . -- cgit v1.2.3-59-g8ed1b From 19b6ef51640fb35666884c7b577b1a24a4be8e34 Mon Sep 17 00:00:00 2001 From: Tomasz Stanislawski Date: Thu, 14 Jun 2012 11:32:22 -0300 Subject: [media] Documentation: media: description of DMABUF exporting in V4L2 This patch adds description and usage examples for exporting DMABUF file descriptor in V4L2. Signed-off-by: Tomasz Stanislawski Signed-off-by: Kyungmin Park Acked-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- Documentation/DocBook/media/v4l/compat.xml | 3 + Documentation/DocBook/media/v4l/io.xml | 3 + Documentation/DocBook/media/v4l/v4l2.xml | 1 + Documentation/DocBook/media/v4l/vidioc-expbuf.xml | 212 ++++++++++++++++++++++ 4 files changed, 219 insertions(+) create mode 100644 Documentation/DocBook/media/v4l/vidioc-expbuf.xml (limited to 'Documentation') diff --git a/Documentation/DocBook/media/v4l/compat.xml b/Documentation/DocBook/media/v4l/compat.xml index 50eb63024eb7..3dd9e78815d1 100644 --- a/Documentation/DocBook/media/v4l/compat.xml +++ b/Documentation/DocBook/media/v4l/compat.xml @@ -2590,6 +2590,9 @@ ioctls. Importing DMABUF file descriptors as a new IO method described in . + + Exporting DMABUF files using &VIDIOC-EXPBUF; ioctl. +
diff --git a/Documentation/DocBook/media/v4l/io.xml b/Documentation/DocBook/media/v4l/io.xml index be0fad7e1209..388a34032653 100644 --- a/Documentation/DocBook/media/v4l/io.xml +++ b/Documentation/DocBook/media/v4l/io.xml @@ -488,6 +488,9 @@ DMA buffer from userspace using a file descriptor previously exported for a different or the same device (known as the importer role), or both. This section describes the DMABUF importer role API in V4L2. + Refer to DMABUF exporting for +details about exporting V4L2 buffers as DMABUF file descriptors. + Input and output devices support the streaming I/O method when the V4L2_CAP_STREAMING flag in the capabilities field of &v4l2-capability; returned by diff --git a/Documentation/DocBook/media/v4l/v4l2.xml b/Documentation/DocBook/media/v4l/v4l2.xml index 10ccde9d16d0..4d110b1ad3e9 100644 --- a/Documentation/DocBook/media/v4l/v4l2.xml +++ b/Documentation/DocBook/media/v4l/v4l2.xml @@ -543,6 +543,7 @@ and discussions on the V4L mailing list. &sub-enuminput; &sub-enumoutput; &sub-enumstd; + &sub-expbuf; &sub-g-audio; &sub-g-audioout; &sub-g-crop; diff --git a/Documentation/DocBook/media/v4l/vidioc-expbuf.xml b/Documentation/DocBook/media/v4l/vidioc-expbuf.xml new file mode 100644 index 000000000000..72dfbd20a802 --- /dev/null +++ b/Documentation/DocBook/media/v4l/vidioc-expbuf.xml @@ -0,0 +1,212 @@ + + + + ioctl VIDIOC_EXPBUF + &manvol; + + + + VIDIOC_EXPBUF + Export a buffer as a DMABUF file descriptor. + + + + + + int ioctl + int fd + int request + struct v4l2_exportbuffer *argp + + + + + + Arguments + + + + fd + + &fd; + + + + request + + VIDIOC_EXPBUF + + + + argp + + + + + + + + + Description + + + Experimental + This is an experimental + interface and may change in the future. + + +This ioctl is an extension to the memory +mapping I/O method, therefore it is available only for +V4L2_MEMORY_MMAP buffers. It can be used to export a +buffer as a DMABUF file at any time after buffers have been allocated with the +&VIDIOC-REQBUFS; ioctl. + + To export a buffer, applications fill &v4l2-exportbuffer;. The + type field is set to the same buffer type as was +previously used with &v4l2-requestbuffers; type . +Applications must also set the index field. Valid +index numbers range from zero to the number of buffers allocated with +&VIDIOC-REQBUFS; (&v4l2-requestbuffers; count ) +minus one. For the multi-planar API, applications set the plane + field to the index of the plane to be exported. Valid planes +range from zero to the maximal number of valid planes for the currently active +format. For the single-planar API, applications must set plane + to zero. Additional flags may be posted in the +flags field. Refer to a manual for open() for details. +Currently only O_CLOEXEC is supported. All other fields must be set to zero. +In the case of multi-planar API, every plane is exported separately using +multiple VIDIOC_EXPBUF calls. + + After calling VIDIOC_EXPBUF the fd + field will be set by a driver. This is a DMABUF file +descriptor. The application may pass it to other DMABUF-aware devices. Refer to +DMABUF importing for details about importing +DMABUF files into V4L2 nodes. It is recommended to close a DMABUF file when it +is no longer used to allow the associated memory to be reclaimed. + + + +
+ Examples + + + Exporting a buffer. + +int buffer_export(int v4lfd, &v4l2-buf-type; bt, int index, int *dmafd) +{ + &v4l2-exportbuffer; expbuf; + + memset(&expbuf, 0, sizeof(expbuf)); + expbuf.type = bt; + expbuf.index = index; + if (ioctl(v4lfd, &VIDIOC-EXPBUF;, &expbuf) == -1) { + perror("VIDIOC_EXPBUF"); + return -1; + } + + *dmafd = expbuf.fd; + + return 0; +} + + + + + Exporting a buffer using the multi-planar API. + +int buffer_export_mp(int v4lfd, &v4l2-buf-type; bt, int index, + int dmafd[], int n_planes) +{ + int i; + + for (i = 0; i < n_planes; ++i) { + &v4l2-exportbuffer; expbuf; + + memset(&expbuf, 0, sizeof(expbuf)); + expbuf.type = bt; + expbuf.index = index; + expbuf.plane = i; + if (ioctl(v4lfd, &VIDIOC-EXPBUF;, &expbuf) == -1) { + perror("VIDIOC_EXPBUF"); + while (i) + close(dmafd[--i]); + return -1; + } + dmafd[i] = expbuf.fd; + } + + return 0; +} + + +
+
+ + + + struct <structname>v4l2_exportbuffer</structname> + + &cs-str; + + + __u32 + type + Type of the buffer, same as &v4l2-format; +type or &v4l2-requestbuffers; +type, set by the application. See + + + __u32 + index + Number of the buffer, set by the application. This field is +only used for memory mapping I/O and can range from +zero to the number of buffers allocated with the &VIDIOC-REQBUFS; and/or +&VIDIOC-CREATE-BUFS; ioctls. + + + __u32 + plane + Index of the plane to be exported when using the +multi-planar API. Otherwise this value must be set to zero. + + + __u32 + flags + Flags for the newly created file, currently only +O_CLOEXEC is supported, refer to the manual of open() for more +details. + + + __s32 + fd + The DMABUF file descriptor associated with a buffer. Set by + the driver. + + + __u32 + reserved[11] + Reserved field for future use. Must be set to zero. + + + +
+ +
+ + + &return-value; + + + EINVAL + + A queue is not in MMAP mode or DMABUF exporting is not +supported or flags or type + or index or plane + fields are invalid. + + + + + +
-- cgit v1.2.3-59-g8ed1b From 80515a5a2e3c35e2994105f19af27650e8a16c51 Mon Sep 17 00:00:00 2001 From: Shiraz Hashim Date: Fri, 3 Aug 2012 15:33:10 +0530 Subject: ARM: SPEAr3xx: shirq: simplify and move the shared irq multiplexor to DT SPEAr3xx architecture includes shared/multiplexed irqs for certain set of devices. The multiplexor provides a single interrupt to parent interrupt controller (VIC) on behalf of a group of devices. There can be multiple groups available on SPEAr3xx variants but not exceeding 4. The number of devices in a group can differ, further they may share same set of status/mask registers spanning across different bit masks. Also in some cases the group may not have enable or other registers. This makes software little complex. Present implementation was non-DT and had few complex data structures to decipher banks, number of irqs supported, mask and registers involved. This patch simplifies the overall design and convert it in to DT. It also removes all registration from individual SoC files and bring them in to common shirq.c. Also updated the corresponding documentation for DT binding of shirq. Signed-off-by: Shiraz Hashim Signed-off-by: Viresh Kumar --- .../devicetree/bindings/arm/spear/shirq.txt | 48 ++++ arch/arm/mach-spear3xx/include/mach/irqs.h | 10 +- arch/arm/mach-spear3xx/spear300.c | 103 ------- arch/arm/mach-spear3xx/spear310.c | 202 -------------- arch/arm/mach-spear3xx/spear320.c | 204 -------------- arch/arm/mach-spear3xx/spear3xx.c | 4 + arch/arm/plat-spear/include/plat/shirq.h | 39 ++- arch/arm/plat-spear/shirq.c | 297 +++++++++++++++++---- 8 files changed, 316 insertions(+), 591 deletions(-) create mode 100644 Documentation/devicetree/bindings/arm/spear/shirq.txt (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/arm/spear/shirq.txt b/Documentation/devicetree/bindings/arm/spear/shirq.txt new file mode 100644 index 000000000000..13fbb8866bd6 --- /dev/null +++ b/Documentation/devicetree/bindings/arm/spear/shirq.txt @@ -0,0 +1,48 @@ +* SPEAr Shared IRQ layer (shirq) + +SPEAr3xx architecture includes shared/multiplexed irqs for certain set +of devices. The multiplexor provides a single interrupt to parent +interrupt controller (VIC) on behalf of a group of devices. + +There can be multiple groups available on SPEAr3xx variants but not +exceeding 4. The number of devices in a group can differ, further they +may share same set of status/mask registers spanning across different +bit masks. Also in some cases the group may not have enable or other +registers. This makes software little complex. + +A single node in the device tree is used to describe the shared +interrupt multiplexor (one node for all groups). A group in the +interrupt controller shares config/control registers with other groups. +For example, a 32-bit interrupt enable/disable config register can +accommodate upto 4 interrupt groups. + +Required properties: + - compatible: should be, either of + - "st,spear300-shirq" + - "st,spear310-shirq" + - "st,spear320-shirq" + - interrupt-controller: Identifies the node as an interrupt controller. + - #interrupt-cells: should be <1> which basically contains the offset + (starting from 0) of interrupts for all the groups. + - reg: Base address and size of shirq registers. + - interrupts: The list of interrupts generated by the groups which are + then connected to a parent interrupt controller. Each group is + associated with one of the interrupts, hence number of interrupts (to + parent) is equal to number of groups. The format of the interrupt + specifier depends in the interrupt parent controller. + + Optional properties: + - interrupt-parent: pHandle of the parent interrupt controller, if not + inherited from the parent node. + +Example: + +The following is an example from the SPEAr320 SoC dtsi file. + +shirq: interrupt-controller@0xb3000000 { + compatible = "st,spear320-shirq"; + reg = <0xb3000000 0x1000>; + interrupts = <28 29 30 1>; + #interrupt-cells = <1>; + interrupt-controller; +}; diff --git a/arch/arm/mach-spear3xx/include/mach/irqs.h b/arch/arm/mach-spear3xx/include/mach/irqs.h index 803de76f5f36..f95e5b2b6686 100644 --- a/arch/arm/mach-spear3xx/include/mach/irqs.h +++ b/arch/arm/mach-spear3xx/include/mach/irqs.h @@ -14,14 +14,6 @@ #ifndef __MACH_IRQS_H #define __MACH_IRQS_H -/* FIXME: probe all these from DT */ -#define SPEAR3XX_IRQ_INTRCOMM_RAS_ARM 1 -#define SPEAR3XX_IRQ_GEN_RAS_1 28 -#define SPEAR3XX_IRQ_GEN_RAS_2 29 -#define SPEAR3XX_IRQ_GEN_RAS_3 30 -#define SPEAR3XX_IRQ_VIC_END 32 -#define SPEAR3XX_VIRQ_START SPEAR3XX_IRQ_VIC_END - -#define NR_IRQS 160 +#define NR_IRQS 256 #endif /* __MACH_IRQS_H */ diff --git a/arch/arm/mach-spear3xx/spear300.c b/arch/arm/mach-spear3xx/spear300.c index 6ec300549960..a69cbfdb07ee 100644 --- a/arch/arm/mach-spear3xx/spear300.c +++ b/arch/arm/mach-spear3xx/spear300.c @@ -17,102 +17,9 @@ #include #include #include -#include #include #include -/* Base address of various IPs */ -#define SPEAR300_TELECOM_BASE UL(0x50000000) - -/* Interrupt registers offsets and masks */ -#define SPEAR300_INT_ENB_MASK_REG 0x54 -#define SPEAR300_INT_STS_MASK_REG 0x58 -#define SPEAR300_IT_PERS_S_IRQ_MASK (1 << 0) -#define SPEAR300_IT_CHANGE_S_IRQ_MASK (1 << 1) -#define SPEAR300_I2S_IRQ_MASK (1 << 2) -#define SPEAR300_TDM_IRQ_MASK (1 << 3) -#define SPEAR300_CAMERA_L_IRQ_MASK (1 << 4) -#define SPEAR300_CAMERA_F_IRQ_MASK (1 << 5) -#define SPEAR300_CAMERA_V_IRQ_MASK (1 << 6) -#define SPEAR300_KEYBOARD_IRQ_MASK (1 << 7) -#define SPEAR300_GPIO1_IRQ_MASK (1 << 8) - -#define SPEAR300_SHIRQ_RAS1_MASK 0x1FF - -#define SPEAR300_SOC_CONFIG_BASE UL(0x99000000) - - -/* SPEAr300 Virtual irq definitions */ -/* IRQs sharing IRQ_GEN_RAS_1 */ -#define SPEAR300_VIRQ_IT_PERS_S (SPEAR3XX_VIRQ_START + 0) -#define SPEAR300_VIRQ_IT_CHANGE_S (SPEAR3XX_VIRQ_START + 1) -#define SPEAR300_VIRQ_I2S (SPEAR3XX_VIRQ_START + 2) -#define SPEAR300_VIRQ_TDM (SPEAR3XX_VIRQ_START + 3) -#define SPEAR300_VIRQ_CAMERA_L (SPEAR3XX_VIRQ_START + 4) -#define SPEAR300_VIRQ_CAMERA_F (SPEAR3XX_VIRQ_START + 5) -#define SPEAR300_VIRQ_CAMERA_V (SPEAR3XX_VIRQ_START + 6) -#define SPEAR300_VIRQ_KEYBOARD (SPEAR3XX_VIRQ_START + 7) -#define SPEAR300_VIRQ_GPIO1 (SPEAR3XX_VIRQ_START + 8) - -/* IRQs sharing IRQ_GEN_RAS_3 */ -#define SPEAR300_IRQ_CLCD SPEAR3XX_IRQ_GEN_RAS_3 - -/* IRQs sharing IRQ_INTRCOMM_RAS_ARM */ -#define SPEAR300_IRQ_SDHCI SPEAR3XX_IRQ_INTRCOMM_RAS_ARM - -/* spear3xx shared irq */ -static struct shirq_dev_config shirq_ras1_config[] = { - { - .virq = SPEAR300_VIRQ_IT_PERS_S, - .enb_mask = SPEAR300_IT_PERS_S_IRQ_MASK, - .status_mask = SPEAR300_IT_PERS_S_IRQ_MASK, - }, { - .virq = SPEAR300_VIRQ_IT_CHANGE_S, - .enb_mask = SPEAR300_IT_CHANGE_S_IRQ_MASK, - .status_mask = SPEAR300_IT_CHANGE_S_IRQ_MASK, - }, { - .virq = SPEAR300_VIRQ_I2S, - .enb_mask = SPEAR300_I2S_IRQ_MASK, - .status_mask = SPEAR300_I2S_IRQ_MASK, - }, { - .virq = SPEAR300_VIRQ_TDM, - .enb_mask = SPEAR300_TDM_IRQ_MASK, - .status_mask = SPEAR300_TDM_IRQ_MASK, - }, { - .virq = SPEAR300_VIRQ_CAMERA_L, - .enb_mask = SPEAR300_CAMERA_L_IRQ_MASK, - .status_mask = SPEAR300_CAMERA_L_IRQ_MASK, - }, { - .virq = SPEAR300_VIRQ_CAMERA_F, - .enb_mask = SPEAR300_CAMERA_F_IRQ_MASK, - .status_mask = SPEAR300_CAMERA_F_IRQ_MASK, - }, { - .virq = SPEAR300_VIRQ_CAMERA_V, - .enb_mask = SPEAR300_CAMERA_V_IRQ_MASK, - .status_mask = SPEAR300_CAMERA_V_IRQ_MASK, - }, { - .virq = SPEAR300_VIRQ_KEYBOARD, - .enb_mask = SPEAR300_KEYBOARD_IRQ_MASK, - .status_mask = SPEAR300_KEYBOARD_IRQ_MASK, - }, { - .virq = SPEAR300_VIRQ_GPIO1, - .enb_mask = SPEAR300_GPIO1_IRQ_MASK, - .status_mask = SPEAR300_GPIO1_IRQ_MASK, - }, -}; - -static struct spear_shirq shirq_ras1 = { - .irq = SPEAR3XX_IRQ_GEN_RAS_1, - .dev_config = shirq_ras1_config, - .dev_count = ARRAY_SIZE(shirq_ras1_config), - .regs = { - .enb_reg = SPEAR300_INT_ENB_MASK_REG, - .status_reg = SPEAR300_INT_STS_MASK_REG, - .status_reg_mask = SPEAR300_SHIRQ_RAS1_MASK, - .clear_reg = -1, - }, -}; - /* DMAC platform data's slave info */ struct pl08x_channel_data spear300_dma_info[] = { { @@ -285,21 +192,11 @@ static struct of_dev_auxdata spear300_auxdata_lookup[] __initdata = { static void __init spear300_dt_init(void) { - int ret; - pl080_plat_data.slave_channels = spear300_dma_info; pl080_plat_data.num_slave_channels = ARRAY_SIZE(spear300_dma_info); of_platform_populate(NULL, of_default_bus_match_table, spear300_auxdata_lookup, NULL); - - /* shared irq registration */ - shirq_ras1.regs.base = ioremap(SPEAR300_TELECOM_BASE, SZ_4K); - if (shirq_ras1.regs.base) { - ret = spear_shirq_register(&shirq_ras1); - if (ret) - pr_err("Error registering Shared IRQ\n"); - } } static const char * const spear300_dt_board_compat[] = { diff --git a/arch/arm/mach-spear3xx/spear310.c b/arch/arm/mach-spear3xx/spear310.c index 1d0e435b9045..b963ebb10b56 100644 --- a/arch/arm/mach-spear3xx/spear310.c +++ b/arch/arm/mach-spear3xx/spear310.c @@ -18,7 +18,6 @@ #include #include #include -#include #include #include @@ -27,176 +26,6 @@ #define SPEAR310_UART3_BASE UL(0xB2100000) #define SPEAR310_UART4_BASE UL(0xB2180000) #define SPEAR310_UART5_BASE UL(0xB2200000) -#define SPEAR310_SOC_CONFIG_BASE UL(0xB4000000) - -/* Interrupt registers offsets and masks */ -#define SPEAR310_INT_STS_MASK_REG 0x04 -#define SPEAR310_SMII0_IRQ_MASK (1 << 0) -#define SPEAR310_SMII1_IRQ_MASK (1 << 1) -#define SPEAR310_SMII2_IRQ_MASK (1 << 2) -#define SPEAR310_SMII3_IRQ_MASK (1 << 3) -#define SPEAR310_WAKEUP_SMII0_IRQ_MASK (1 << 4) -#define SPEAR310_WAKEUP_SMII1_IRQ_MASK (1 << 5) -#define SPEAR310_WAKEUP_SMII2_IRQ_MASK (1 << 6) -#define SPEAR310_WAKEUP_SMII3_IRQ_MASK (1 << 7) -#define SPEAR310_UART1_IRQ_MASK (1 << 8) -#define SPEAR310_UART2_IRQ_MASK (1 << 9) -#define SPEAR310_UART3_IRQ_MASK (1 << 10) -#define SPEAR310_UART4_IRQ_MASK (1 << 11) -#define SPEAR310_UART5_IRQ_MASK (1 << 12) -#define SPEAR310_EMI_IRQ_MASK (1 << 13) -#define SPEAR310_TDM_HDLC_IRQ_MASK (1 << 14) -#define SPEAR310_RS485_0_IRQ_MASK (1 << 15) -#define SPEAR310_RS485_1_IRQ_MASK (1 << 16) - -#define SPEAR310_SHIRQ_RAS1_MASK 0x000FF -#define SPEAR310_SHIRQ_RAS2_MASK 0x01F00 -#define SPEAR310_SHIRQ_RAS3_MASK 0x02000 -#define SPEAR310_SHIRQ_INTRCOMM_RAS_MASK 0x1C000 - -/* SPEAr310 Virtual irq definitions */ -/* IRQs sharing IRQ_GEN_RAS_1 */ -#define SPEAR310_VIRQ_SMII0 (SPEAR3XX_VIRQ_START + 0) -#define SPEAR310_VIRQ_SMII1 (SPEAR3XX_VIRQ_START + 1) -#define SPEAR310_VIRQ_SMII2 (SPEAR3XX_VIRQ_START + 2) -#define SPEAR310_VIRQ_SMII3 (SPEAR3XX_VIRQ_START + 3) -#define SPEAR310_VIRQ_WAKEUP_SMII0 (SPEAR3XX_VIRQ_START + 4) -#define SPEAR310_VIRQ_WAKEUP_SMII1 (SPEAR3XX_VIRQ_START + 5) -#define SPEAR310_VIRQ_WAKEUP_SMII2 (SPEAR3XX_VIRQ_START + 6) -#define SPEAR310_VIRQ_WAKEUP_SMII3 (SPEAR3XX_VIRQ_START + 7) - -/* IRQs sharing IRQ_GEN_RAS_2 */ -#define SPEAR310_VIRQ_UART1 (SPEAR3XX_VIRQ_START + 8) -#define SPEAR310_VIRQ_UART2 (SPEAR3XX_VIRQ_START + 9) -#define SPEAR310_VIRQ_UART3 (SPEAR3XX_VIRQ_START + 10) -#define SPEAR310_VIRQ_UART4 (SPEAR3XX_VIRQ_START + 11) -#define SPEAR310_VIRQ_UART5 (SPEAR3XX_VIRQ_START + 12) - -/* IRQs sharing IRQ_GEN_RAS_3 */ -#define SPEAR310_VIRQ_EMI (SPEAR3XX_VIRQ_START + 13) -#define SPEAR310_VIRQ_PLGPIO (SPEAR3XX_VIRQ_START + 14) - -/* IRQs sharing IRQ_INTRCOMM_RAS_ARM */ -#define SPEAR310_VIRQ_TDM_HDLC (SPEAR3XX_VIRQ_START + 15) -#define SPEAR310_VIRQ_RS485_0 (SPEAR3XX_VIRQ_START + 16) -#define SPEAR310_VIRQ_RS485_1 (SPEAR3XX_VIRQ_START + 17) - - -/* spear3xx shared irq */ -static struct shirq_dev_config shirq_ras1_config[] = { - { - .virq = SPEAR310_VIRQ_SMII0, - .status_mask = SPEAR310_SMII0_IRQ_MASK, - }, { - .virq = SPEAR310_VIRQ_SMII1, - .status_mask = SPEAR310_SMII1_IRQ_MASK, - }, { - .virq = SPEAR310_VIRQ_SMII2, - .status_mask = SPEAR310_SMII2_IRQ_MASK, - }, { - .virq = SPEAR310_VIRQ_SMII3, - .status_mask = SPEAR310_SMII3_IRQ_MASK, - }, { - .virq = SPEAR310_VIRQ_WAKEUP_SMII0, - .status_mask = SPEAR310_WAKEUP_SMII0_IRQ_MASK, - }, { - .virq = SPEAR310_VIRQ_WAKEUP_SMII1, - .status_mask = SPEAR310_WAKEUP_SMII1_IRQ_MASK, - }, { - .virq = SPEAR310_VIRQ_WAKEUP_SMII2, - .status_mask = SPEAR310_WAKEUP_SMII2_IRQ_MASK, - }, { - .virq = SPEAR310_VIRQ_WAKEUP_SMII3, - .status_mask = SPEAR310_WAKEUP_SMII3_IRQ_MASK, - }, -}; - -static struct spear_shirq shirq_ras1 = { - .irq = SPEAR3XX_IRQ_GEN_RAS_1, - .dev_config = shirq_ras1_config, - .dev_count = ARRAY_SIZE(shirq_ras1_config), - .regs = { - .enb_reg = -1, - .status_reg = SPEAR310_INT_STS_MASK_REG, - .status_reg_mask = SPEAR310_SHIRQ_RAS1_MASK, - .clear_reg = -1, - }, -}; - -static struct shirq_dev_config shirq_ras2_config[] = { - { - .virq = SPEAR310_VIRQ_UART1, - .status_mask = SPEAR310_UART1_IRQ_MASK, - }, { - .virq = SPEAR310_VIRQ_UART2, - .status_mask = SPEAR310_UART2_IRQ_MASK, - }, { - .virq = SPEAR310_VIRQ_UART3, - .status_mask = SPEAR310_UART3_IRQ_MASK, - }, { - .virq = SPEAR310_VIRQ_UART4, - .status_mask = SPEAR310_UART4_IRQ_MASK, - }, { - .virq = SPEAR310_VIRQ_UART5, - .status_mask = SPEAR310_UART5_IRQ_MASK, - }, -}; - -static struct spear_shirq shirq_ras2 = { - .irq = SPEAR3XX_IRQ_GEN_RAS_2, - .dev_config = shirq_ras2_config, - .dev_count = ARRAY_SIZE(shirq_ras2_config), - .regs = { - .enb_reg = -1, - .status_reg = SPEAR310_INT_STS_MASK_REG, - .status_reg_mask = SPEAR310_SHIRQ_RAS2_MASK, - .clear_reg = -1, - }, -}; - -static struct shirq_dev_config shirq_ras3_config[] = { - { - .virq = SPEAR310_VIRQ_EMI, - .status_mask = SPEAR310_EMI_IRQ_MASK, - }, -}; - -static struct spear_shirq shirq_ras3 = { - .irq = SPEAR3XX_IRQ_GEN_RAS_3, - .dev_config = shirq_ras3_config, - .dev_count = ARRAY_SIZE(shirq_ras3_config), - .regs = { - .enb_reg = -1, - .status_reg = SPEAR310_INT_STS_MASK_REG, - .status_reg_mask = SPEAR310_SHIRQ_RAS3_MASK, - .clear_reg = -1, - }, -}; - -static struct shirq_dev_config shirq_intrcomm_ras_config[] = { - { - .virq = SPEAR310_VIRQ_TDM_HDLC, - .status_mask = SPEAR310_TDM_HDLC_IRQ_MASK, - }, { - .virq = SPEAR310_VIRQ_RS485_0, - .status_mask = SPEAR310_RS485_0_IRQ_MASK, - }, { - .virq = SPEAR310_VIRQ_RS485_1, - .status_mask = SPEAR310_RS485_1_IRQ_MASK, - }, -}; - -static struct spear_shirq shirq_intrcomm_ras = { - .irq = SPEAR3XX_IRQ_INTRCOMM_RAS_ARM, - .dev_config = shirq_intrcomm_ras_config, - .dev_count = ARRAY_SIZE(shirq_intrcomm_ras_config), - .regs = { - .enb_reg = -1, - .status_reg = SPEAR310_INT_STS_MASK_REG, - .status_reg_mask = SPEAR310_SHIRQ_INTRCOMM_RAS_MASK, - .clear_reg = -1, - }, -}; /* DMAC platform data's slave info */ struct pl08x_channel_data spear310_dma_info[] = { @@ -405,42 +234,11 @@ static struct of_dev_auxdata spear310_auxdata_lookup[] __initdata = { static void __init spear310_dt_init(void) { - void __iomem *base; - int ret; - pl080_plat_data.slave_channels = spear310_dma_info; pl080_plat_data.num_slave_channels = ARRAY_SIZE(spear310_dma_info); of_platform_populate(NULL, of_default_bus_match_table, spear310_auxdata_lookup, NULL); - - /* shared irq registration */ - base = ioremap(SPEAR310_SOC_CONFIG_BASE, SZ_4K); - if (base) { - /* shirq 1 */ - shirq_ras1.regs.base = base; - ret = spear_shirq_register(&shirq_ras1); - if (ret) - pr_err("Error registering Shared IRQ 1\n"); - - /* shirq 2 */ - shirq_ras2.regs.base = base; - ret = spear_shirq_register(&shirq_ras2); - if (ret) - pr_err("Error registering Shared IRQ 2\n"); - - /* shirq 3 */ - shirq_ras3.regs.base = base; - ret = spear_shirq_register(&shirq_ras3); - if (ret) - pr_err("Error registering Shared IRQ 3\n"); - - /* shirq 4 */ - shirq_intrcomm_ras.regs.base = base; - ret = spear_shirq_register(&shirq_intrcomm_ras); - if (ret) - pr_err("Error registering Shared IRQ 4\n"); - } } static const char * const spear310_dt_board_compat[] = { diff --git a/arch/arm/mach-spear3xx/spear320.c b/arch/arm/mach-spear3xx/spear320.c index fd823c624575..707504b84e0e 100644 --- a/arch/arm/mach-spear3xx/spear320.c +++ b/arch/arm/mach-spear3xx/spear320.c @@ -19,7 +19,6 @@ #include #include #include -#include #include #include @@ -28,184 +27,6 @@ #define SPEAR320_SSP0_BASE UL(0xA5000000) #define SPEAR320_SSP1_BASE UL(0xA6000000) -/* Interrupt registers offsets and masks */ -#define SPEAR320_INT_STS_MASK_REG 0x04 -#define SPEAR320_INT_CLR_MASK_REG 0x04 -#define SPEAR320_INT_ENB_MASK_REG 0x08 -#define SPEAR320_GPIO_IRQ_MASK (1 << 0) -#define SPEAR320_I2S_PLAY_IRQ_MASK (1 << 1) -#define SPEAR320_I2S_REC_IRQ_MASK (1 << 2) -#define SPEAR320_EMI_IRQ_MASK (1 << 7) -#define SPEAR320_CLCD_IRQ_MASK (1 << 8) -#define SPEAR320_SPP_IRQ_MASK (1 << 9) -#define SPEAR320_SDHCI_IRQ_MASK (1 << 10) -#define SPEAR320_CAN_U_IRQ_MASK (1 << 11) -#define SPEAR320_CAN_L_IRQ_MASK (1 << 12) -#define SPEAR320_UART1_IRQ_MASK (1 << 13) -#define SPEAR320_UART2_IRQ_MASK (1 << 14) -#define SPEAR320_SSP1_IRQ_MASK (1 << 15) -#define SPEAR320_SSP2_IRQ_MASK (1 << 16) -#define SPEAR320_SMII0_IRQ_MASK (1 << 17) -#define SPEAR320_MII1_SMII1_IRQ_MASK (1 << 18) -#define SPEAR320_WAKEUP_SMII0_IRQ_MASK (1 << 19) -#define SPEAR320_WAKEUP_MII1_SMII1_IRQ_MASK (1 << 20) -#define SPEAR320_I2C1_IRQ_MASK (1 << 21) - -#define SPEAR320_SHIRQ_RAS1_MASK 0x000380 -#define SPEAR320_SHIRQ_RAS3_MASK 0x000007 -#define SPEAR320_SHIRQ_INTRCOMM_RAS_MASK 0x3FF800 - -/* SPEAr320 Virtual irq definitions */ -/* IRQs sharing IRQ_GEN_RAS_1 */ -#define SPEAR320_VIRQ_EMI (SPEAR3XX_VIRQ_START + 0) -#define SPEAR320_VIRQ_CLCD (SPEAR3XX_VIRQ_START + 1) -#define SPEAR320_VIRQ_SPP (SPEAR3XX_VIRQ_START + 2) - -/* IRQs sharing IRQ_GEN_RAS_2 */ -#define SPEAR320_IRQ_SDHCI SPEAR3XX_IRQ_GEN_RAS_2 - -/* IRQs sharing IRQ_GEN_RAS_3 */ -#define SPEAR320_VIRQ_PLGPIO (SPEAR3XX_VIRQ_START + 3) -#define SPEAR320_VIRQ_I2S_PLAY (SPEAR3XX_VIRQ_START + 4) -#define SPEAR320_VIRQ_I2S_REC (SPEAR3XX_VIRQ_START + 5) - -/* IRQs sharing IRQ_INTRCOMM_RAS_ARM */ -#define SPEAR320_VIRQ_CANU (SPEAR3XX_VIRQ_START + 6) -#define SPEAR320_VIRQ_CANL (SPEAR3XX_VIRQ_START + 7) -#define SPEAR320_VIRQ_UART1 (SPEAR3XX_VIRQ_START + 8) -#define SPEAR320_VIRQ_UART2 (SPEAR3XX_VIRQ_START + 9) -#define SPEAR320_VIRQ_SSP1 (SPEAR3XX_VIRQ_START + 10) -#define SPEAR320_VIRQ_SSP2 (SPEAR3XX_VIRQ_START + 11) -#define SPEAR320_VIRQ_SMII0 (SPEAR3XX_VIRQ_START + 12) -#define SPEAR320_VIRQ_MII1_SMII1 (SPEAR3XX_VIRQ_START + 13) -#define SPEAR320_VIRQ_WAKEUP_SMII0 (SPEAR3XX_VIRQ_START + 14) -#define SPEAR320_VIRQ_WAKEUP_MII1_SMII1 (SPEAR3XX_VIRQ_START + 15) -#define SPEAR320_VIRQ_I2C1 (SPEAR3XX_VIRQ_START + 16) - -/* spear3xx shared irq */ -static struct shirq_dev_config shirq_ras1_config[] = { - { - .virq = SPEAR320_VIRQ_EMI, - .status_mask = SPEAR320_EMI_IRQ_MASK, - .clear_mask = SPEAR320_EMI_IRQ_MASK, - }, { - .virq = SPEAR320_VIRQ_CLCD, - .status_mask = SPEAR320_CLCD_IRQ_MASK, - .clear_mask = SPEAR320_CLCD_IRQ_MASK, - }, { - .virq = SPEAR320_VIRQ_SPP, - .status_mask = SPEAR320_SPP_IRQ_MASK, - .clear_mask = SPEAR320_SPP_IRQ_MASK, - }, -}; - -static struct spear_shirq shirq_ras1 = { - .irq = SPEAR3XX_IRQ_GEN_RAS_1, - .dev_config = shirq_ras1_config, - .dev_count = ARRAY_SIZE(shirq_ras1_config), - .regs = { - .enb_reg = -1, - .status_reg = SPEAR320_INT_STS_MASK_REG, - .status_reg_mask = SPEAR320_SHIRQ_RAS1_MASK, - .clear_reg = SPEAR320_INT_CLR_MASK_REG, - .reset_to_clear = 1, - }, -}; - -static struct shirq_dev_config shirq_ras3_config[] = { - { - .virq = SPEAR320_VIRQ_PLGPIO, - .enb_mask = SPEAR320_GPIO_IRQ_MASK, - .status_mask = SPEAR320_GPIO_IRQ_MASK, - .clear_mask = SPEAR320_GPIO_IRQ_MASK, - }, { - .virq = SPEAR320_VIRQ_I2S_PLAY, - .enb_mask = SPEAR320_I2S_PLAY_IRQ_MASK, - .status_mask = SPEAR320_I2S_PLAY_IRQ_MASK, - .clear_mask = SPEAR320_I2S_PLAY_IRQ_MASK, - }, { - .virq = SPEAR320_VIRQ_I2S_REC, - .enb_mask = SPEAR320_I2S_REC_IRQ_MASK, - .status_mask = SPEAR320_I2S_REC_IRQ_MASK, - .clear_mask = SPEAR320_I2S_REC_IRQ_MASK, - }, -}; - -static struct spear_shirq shirq_ras3 = { - .irq = SPEAR3XX_IRQ_GEN_RAS_3, - .dev_config = shirq_ras3_config, - .dev_count = ARRAY_SIZE(shirq_ras3_config), - .regs = { - .enb_reg = SPEAR320_INT_ENB_MASK_REG, - .reset_to_enb = 1, - .status_reg = SPEAR320_INT_STS_MASK_REG, - .status_reg_mask = SPEAR320_SHIRQ_RAS3_MASK, - .clear_reg = SPEAR320_INT_CLR_MASK_REG, - .reset_to_clear = 1, - }, -}; - -static struct shirq_dev_config shirq_intrcomm_ras_config[] = { - { - .virq = SPEAR320_VIRQ_CANU, - .status_mask = SPEAR320_CAN_U_IRQ_MASK, - .clear_mask = SPEAR320_CAN_U_IRQ_MASK, - }, { - .virq = SPEAR320_VIRQ_CANL, - .status_mask = SPEAR320_CAN_L_IRQ_MASK, - .clear_mask = SPEAR320_CAN_L_IRQ_MASK, - }, { - .virq = SPEAR320_VIRQ_UART1, - .status_mask = SPEAR320_UART1_IRQ_MASK, - .clear_mask = SPEAR320_UART1_IRQ_MASK, - }, { - .virq = SPEAR320_VIRQ_UART2, - .status_mask = SPEAR320_UART2_IRQ_MASK, - .clear_mask = SPEAR320_UART2_IRQ_MASK, - }, { - .virq = SPEAR320_VIRQ_SSP1, - .status_mask = SPEAR320_SSP1_IRQ_MASK, - .clear_mask = SPEAR320_SSP1_IRQ_MASK, - }, { - .virq = SPEAR320_VIRQ_SSP2, - .status_mask = SPEAR320_SSP2_IRQ_MASK, - .clear_mask = SPEAR320_SSP2_IRQ_MASK, - }, { - .virq = SPEAR320_VIRQ_SMII0, - .status_mask = SPEAR320_SMII0_IRQ_MASK, - .clear_mask = SPEAR320_SMII0_IRQ_MASK, - }, { - .virq = SPEAR320_VIRQ_MII1_SMII1, - .status_mask = SPEAR320_MII1_SMII1_IRQ_MASK, - .clear_mask = SPEAR320_MII1_SMII1_IRQ_MASK, - }, { - .virq = SPEAR320_VIRQ_WAKEUP_SMII0, - .status_mask = SPEAR320_WAKEUP_SMII0_IRQ_MASK, - .clear_mask = SPEAR320_WAKEUP_SMII0_IRQ_MASK, - }, { - .virq = SPEAR320_VIRQ_WAKEUP_MII1_SMII1, - .status_mask = SPEAR320_WAKEUP_MII1_SMII1_IRQ_MASK, - .clear_mask = SPEAR320_WAKEUP_MII1_SMII1_IRQ_MASK, - }, { - .virq = SPEAR320_VIRQ_I2C1, - .status_mask = SPEAR320_I2C1_IRQ_MASK, - .clear_mask = SPEAR320_I2C1_IRQ_MASK, - }, -}; - -static struct spear_shirq shirq_intrcomm_ras = { - .irq = SPEAR3XX_IRQ_INTRCOMM_RAS_ARM, - .dev_config = shirq_intrcomm_ras_config, - .dev_count = ARRAY_SIZE(shirq_intrcomm_ras_config), - .regs = { - .enb_reg = -1, - .status_reg = SPEAR320_INT_STS_MASK_REG, - .status_reg_mask = SPEAR320_SHIRQ_INTRCOMM_RAS_MASK, - .clear_reg = SPEAR320_INT_CLR_MASK_REG, - .reset_to_clear = 1, - }, -}; - /* DMAC platform data's slave info */ struct pl08x_channel_data spear320_dma_info[] = { { @@ -416,36 +237,11 @@ static struct of_dev_auxdata spear320_auxdata_lookup[] __initdata = { static void __init spear320_dt_init(void) { - void __iomem *base; - int ret; - pl080_plat_data.slave_channels = spear320_dma_info; pl080_plat_data.num_slave_channels = ARRAY_SIZE(spear320_dma_info); of_platform_populate(NULL, of_default_bus_match_table, spear320_auxdata_lookup, NULL); - - /* shared irq registration */ - base = ioremap(SPEAR320_SOC_CONFIG_BASE, SZ_4K); - if (base) { - /* shirq 1 */ - shirq_ras1.regs.base = base; - ret = spear_shirq_register(&shirq_ras1); - if (ret) - pr_err("Error registering Shared IRQ 1\n"); - - /* shirq 3 */ - shirq_ras3.regs.base = base; - ret = spear_shirq_register(&shirq_ras3); - if (ret) - pr_err("Error registering Shared IRQ 3\n"); - - /* shirq 4 */ - shirq_intrcomm_ras.regs.base = base; - ret = spear_shirq_register(&shirq_intrcomm_ras); - if (ret) - pr_err("Error registering Shared IRQ 4\n"); - } } static const char * const spear320_dt_board_compat[] = { diff --git a/arch/arm/mach-spear3xx/spear3xx.c b/arch/arm/mach-spear3xx/spear3xx.c index 98144baf8883..f1aaf5b168b2 100644 --- a/arch/arm/mach-spear3xx/spear3xx.c +++ b/arch/arm/mach-spear3xx/spear3xx.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #include @@ -121,6 +122,9 @@ struct sys_timer spear3xx_timer = { static const struct of_device_id vic_of_match[] __initconst = { { .compatible = "arm,pl190-vic", .data = vic_of_init, }, + { .compatible = "st,spear300-shirq", .data = spear300_shirq_of_init, }, + { .compatible = "st,spear310-shirq", .data = spear310_shirq_of_init, }, + { .compatible = "st,spear320-shirq", .data = spear320_shirq_of_init, }, { /* Sentinel */ } }; diff --git a/arch/arm/plat-spear/include/plat/shirq.h b/arch/arm/plat-spear/include/plat/shirq.h index 88a7fbd24793..c51b355f00de 100644 --- a/arch/arm/plat-spear/include/plat/shirq.h +++ b/arch/arm/plat-spear/include/plat/shirq.h @@ -17,25 +17,9 @@ #include #include -/* - * struct shirq_dev_config: shared irq device configuration - * - * virq: virtual irq number of device - * enb_mask: enable mask of device - * status_mask: status mask of device - * clear_mask: clear mask of device - */ -struct shirq_dev_config { - u32 virq; - u32 enb_mask; - u32 status_mask; - u32 clear_mask; -}; - /* * struct shirq_regs: shared irq register configuration * - * base: base address of shared irq register * enb_reg: enable register offset * reset_to_enb: val 1 indicates, we need to clear bit for enabling interrupt * status_reg: status register offset @@ -44,11 +28,9 @@ struct shirq_dev_config { * reset_to_clear: val 1 indicates, we need to clear bit for clearing interrupt */ struct shirq_regs { - void __iomem *base; u32 enb_reg; u32 reset_to_enb; u32 status_reg; - u32 status_reg_mask; u32 clear_reg; u32 reset_to_clear; }; @@ -57,17 +39,28 @@ struct shirq_regs { * struct spear_shirq: shared irq structure * * irq: hardware irq number - * dev_config: array of device config structures which are using "irq" line - * dev_count: size of dev_config array + * irq_base: base irq in linux domain + * irq_nr: no. of shared interrupts in a particular block + * irq_bit_off: starting bit offset in the status register + * invalid_irq: irq group is currently disabled + * base: base address of shared irq register * regs: register configuration for shared irq block */ struct spear_shirq { u32 irq; - struct shirq_dev_config *dev_config; - u32 dev_count; + u32 irq_base; + u32 irq_nr; + u32 irq_bit_off; + int invalid_irq; + void __iomem *base; struct shirq_regs regs; }; -int spear_shirq_register(struct spear_shirq *shirq); +int __init spear300_shirq_of_init(struct device_node *np, + struct device_node *parent); +int __init spear310_shirq_of_init(struct device_node *np, + struct device_node *parent); +int __init spear320_shirq_of_init(struct device_node *np, + struct device_node *parent); #endif /* __PLAT_SHIRQ_H */ diff --git a/arch/arm/plat-spear/shirq.c b/arch/arm/plat-spear/shirq.c index 853e891e1184..955c7249a5c1 100644 --- a/arch/arm/plat-spear/shirq.c +++ b/arch/arm/plat-spear/shirq.c @@ -10,56 +10,182 @@ * License version 2. This program is licensed "as is" without any * warranty of any kind, whether express or implied. */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include +#include +#include #include #include +#include +#include +#include +#include #include #include -struct spear_shirq *shirq; static DEFINE_SPINLOCK(lock); -static void shirq_irq_mask(struct irq_data *d) +/* spear300 shared irq registers offsets and masks */ +#define SPEAR300_INT_ENB_MASK_REG 0x54 +#define SPEAR300_INT_STS_MASK_REG 0x58 + +static struct spear_shirq spear300_shirq_ras1 = { + .irq_nr = 9, + .irq_bit_off = 0, + .regs = { + .enb_reg = SPEAR300_INT_ENB_MASK_REG, + .status_reg = SPEAR300_INT_STS_MASK_REG, + .clear_reg = -1, + }, +}; + +static struct spear_shirq *spear300_shirq_blocks[] = { + &spear300_shirq_ras1, +}; + +/* spear310 shared irq registers offsets and masks */ +#define SPEAR310_INT_STS_MASK_REG 0x04 + +static struct spear_shirq spear310_shirq_ras1 = { + .irq_nr = 8, + .irq_bit_off = 0, + .regs = { + .enb_reg = -1, + .status_reg = SPEAR310_INT_STS_MASK_REG, + .clear_reg = -1, + }, +}; + +static struct spear_shirq spear310_shirq_ras2 = { + .irq_nr = 5, + .irq_bit_off = 8, + .regs = { + .enb_reg = -1, + .status_reg = SPEAR310_INT_STS_MASK_REG, + .clear_reg = -1, + }, +}; + +static struct spear_shirq spear310_shirq_ras3 = { + .irq_nr = 1, + .irq_bit_off = 13, + .regs = { + .enb_reg = -1, + .status_reg = SPEAR310_INT_STS_MASK_REG, + .clear_reg = -1, + }, +}; + +static struct spear_shirq spear310_shirq_intrcomm_ras = { + .irq_nr = 3, + .irq_bit_off = 14, + .regs = { + .enb_reg = -1, + .status_reg = SPEAR310_INT_STS_MASK_REG, + .clear_reg = -1, + }, +}; + +static struct spear_shirq *spear310_shirq_blocks[] = { + &spear310_shirq_ras1, + &spear310_shirq_ras2, + &spear310_shirq_ras3, + &spear310_shirq_intrcomm_ras, +}; + +/* spear320 shared irq registers offsets and masks */ +#define SPEAR320_INT_STS_MASK_REG 0x04 +#define SPEAR320_INT_CLR_MASK_REG 0x04 +#define SPEAR320_INT_ENB_MASK_REG 0x08 + +static struct spear_shirq spear320_shirq_ras1 = { + .irq_nr = 3, + .irq_bit_off = 7, + .regs = { + .enb_reg = -1, + .status_reg = SPEAR320_INT_STS_MASK_REG, + .clear_reg = SPEAR320_INT_CLR_MASK_REG, + .reset_to_clear = 1, + }, +}; + +static struct spear_shirq spear320_shirq_ras2 = { + .irq_nr = 1, + .irq_bit_off = 10, + .regs = { + .enb_reg = -1, + .status_reg = SPEAR320_INT_STS_MASK_REG, + .clear_reg = SPEAR320_INT_CLR_MASK_REG, + .reset_to_clear = 1, + }, +}; + +static struct spear_shirq spear320_shirq_ras3 = { + .irq_nr = 3, + .irq_bit_off = 0, + .invalid_irq = 1, + .regs = { + .enb_reg = SPEAR320_INT_ENB_MASK_REG, + .reset_to_enb = 1, + .status_reg = SPEAR320_INT_STS_MASK_REG, + .clear_reg = SPEAR320_INT_CLR_MASK_REG, + .reset_to_clear = 1, + }, +}; + +static struct spear_shirq spear320_shirq_intrcomm_ras = { + .irq_nr = 11, + .irq_bit_off = 11, + .regs = { + .enb_reg = -1, + .status_reg = SPEAR320_INT_STS_MASK_REG, + .clear_reg = SPEAR320_INT_CLR_MASK_REG, + .reset_to_clear = 1, + }, +}; + +static struct spear_shirq *spear320_shirq_blocks[] = { + &spear320_shirq_ras3, + &spear320_shirq_ras1, + &spear320_shirq_ras2, + &spear320_shirq_intrcomm_ras, +}; + +static void shirq_irq_mask_unmask(struct irq_data *d, bool mask) { struct spear_shirq *shirq = irq_data_get_irq_chip_data(d); - u32 val, id = d->irq - shirq->dev_config[0].virq; + u32 val, offset = d->irq - shirq->irq_base; unsigned long flags; - if ((shirq->regs.enb_reg == -1) || shirq->dev_config[id].enb_mask == -1) + if (shirq->regs.enb_reg == -1) return; spin_lock_irqsave(&lock, flags); - val = readl(shirq->regs.base + shirq->regs.enb_reg); - if (shirq->regs.reset_to_enb) - val |= shirq->dev_config[id].enb_mask; + val = readl(shirq->base + shirq->regs.enb_reg); + + if (mask ^ shirq->regs.reset_to_enb) + val &= ~(0x1 << shirq->irq_bit_off << offset); else - val &= ~(shirq->dev_config[id].enb_mask); - writel(val, shirq->regs.base + shirq->regs.enb_reg); + val |= 0x1 << shirq->irq_bit_off << offset; + + writel(val, shirq->base + shirq->regs.enb_reg); spin_unlock_irqrestore(&lock, flags); + } -static void shirq_irq_unmask(struct irq_data *d) +static void shirq_irq_mask(struct irq_data *d) { - struct spear_shirq *shirq = irq_data_get_irq_chip_data(d); - u32 val, id = d->irq - shirq->dev_config[0].virq; - unsigned long flags; - - if ((shirq->regs.enb_reg == -1) || shirq->dev_config[id].enb_mask == -1) - return; + shirq_irq_mask_unmask(d, 1); +} - spin_lock_irqsave(&lock, flags); - val = readl(shirq->regs.base + shirq->regs.enb_reg); - if (shirq->regs.reset_to_enb) - val &= ~(shirq->dev_config[id].enb_mask); - else - val |= shirq->dev_config[id].enb_mask; - writel(val, shirq->regs.base + shirq->regs.enb_reg); - spin_unlock_irqrestore(&lock, flags); +static void shirq_irq_unmask(struct irq_data *d) +{ + shirq_irq_mask_unmask(d, 0); } static struct irq_chip shirq_chip = { - .name = "spear_shirq", + .name = "spear-shirq", .irq_ack = shirq_irq_mask, .irq_mask = shirq_irq_mask, .irq_unmask = shirq_irq_unmask, @@ -67,52 +193,123 @@ static struct irq_chip shirq_chip = { static void shirq_handler(unsigned irq, struct irq_desc *desc) { - u32 i, val, mask; + u32 i, j, val, mask, tmp; + struct irq_chip *chip; struct spear_shirq *shirq = irq_get_handler_data(irq); - desc->irq_data.chip->irq_ack(&desc->irq_data); - while ((val = readl(shirq->regs.base + shirq->regs.status_reg) & - shirq->regs.status_reg_mask)) { - for (i = 0; (i < shirq->dev_count) && val; i++) { - if (!(shirq->dev_config[i].status_mask & val)) + chip = irq_get_chip(irq); + chip->irq_ack(&desc->irq_data); + + mask = ((0x1 << shirq->irq_nr) - 1) << shirq->irq_bit_off; + while ((val = readl(shirq->base + shirq->regs.status_reg) & + mask)) { + + val >>= shirq->irq_bit_off; + for (i = 0, j = 1; i < shirq->irq_nr; i++, j <<= 1) { + + if (!(j & val)) continue; - generic_handle_irq(shirq->dev_config[i].virq); + generic_handle_irq(shirq->irq_base + i); /* clear interrupt */ - val &= ~shirq->dev_config[i].status_mask; - if ((shirq->regs.clear_reg == -1) || - shirq->dev_config[i].clear_mask == -1) + if (shirq->regs.clear_reg == -1) continue; - mask = readl(shirq->regs.base + shirq->regs.clear_reg); + + tmp = readl(shirq->base + shirq->regs.clear_reg); if (shirq->regs.reset_to_clear) - mask &= ~shirq->dev_config[i].clear_mask; + tmp &= ~(j << shirq->irq_bit_off); else - mask |= shirq->dev_config[i].clear_mask; - writel(mask, shirq->regs.base + shirq->regs.clear_reg); + tmp |= (j << shirq->irq_bit_off); + writel(tmp, shirq->base + shirq->regs.clear_reg); } } - desc->irq_data.chip->irq_unmask(&desc->irq_data); + chip->irq_unmask(&desc->irq_data); } -int spear_shirq_register(struct spear_shirq *shirq) +static void __init spear_shirq_register(struct spear_shirq *shirq) { int i; - if (!shirq || !shirq->dev_config || !shirq->regs.base) - return -EFAULT; - - if (!shirq->dev_count) - return -EINVAL; + if (shirq->invalid_irq) + return; irq_set_chained_handler(shirq->irq, shirq_handler); - for (i = 0; i < shirq->dev_count; i++) { - irq_set_chip_and_handler(shirq->dev_config[i].virq, + for (i = 0; i < shirq->irq_nr; i++) { + irq_set_chip_and_handler(shirq->irq_base + i, &shirq_chip, handle_simple_irq); - set_irq_flags(shirq->dev_config[i].virq, IRQF_VALID); - irq_set_chip_data(shirq->dev_config[i].virq, shirq); + set_irq_flags(shirq->irq_base + i, IRQF_VALID); + irq_set_chip_data(shirq->irq_base + i, shirq); } irq_set_handler_data(shirq->irq, shirq); +} + +static int __init shirq_init(struct spear_shirq **shirq_blocks, int block_nr, + struct device_node *np) +{ + int i, irq_base, hwirq = 0, irq_nr = 0; + static struct irq_domain *shirq_domain; + void __iomem *base; + + base = of_iomap(np, 0); + if (!base) { + pr_err("%s: failed to map shirq registers\n", __func__); + return -ENXIO; + } + + for (i = 0; i < block_nr; i++) + irq_nr += shirq_blocks[i]->irq_nr; + + irq_base = irq_alloc_descs(-1, 0, irq_nr, 0); + if (IS_ERR_VALUE(irq_base)) { + pr_err("%s: irq desc alloc failed\n", __func__); + goto err_unmap; + } + + shirq_domain = irq_domain_add_legacy(np, irq_nr, irq_base, 0, + &irq_domain_simple_ops, NULL); + if (WARN_ON(!shirq_domain)) { + pr_warn("%s: irq domain init failed\n", __func__); + goto err_free_desc; + } + + for (i = 0; i < block_nr; i++) { + shirq_blocks[i]->base = base; + shirq_blocks[i]->irq_base = irq_find_mapping(shirq_domain, + hwirq); + shirq_blocks[i]->irq = irq_of_parse_and_map(np, i); + + spear_shirq_register(shirq_blocks[i]); + hwirq += shirq_blocks[i]->irq_nr; + } + return 0; + +err_free_desc: + irq_free_descs(irq_base, irq_nr); +err_unmap: + iounmap(base); + return -ENXIO; +} + +int __init spear300_shirq_of_init(struct device_node *np, + struct device_node *parent) +{ + return shirq_init(spear300_shirq_blocks, + ARRAY_SIZE(spear300_shirq_blocks), np); +} + +int __init spear310_shirq_of_init(struct device_node *np, + struct device_node *parent) +{ + return shirq_init(spear310_shirq_blocks, + ARRAY_SIZE(spear310_shirq_blocks), np); +} + +int __init spear320_shirq_of_init(struct device_node *np, + struct device_node *parent) +{ + return shirq_init(spear320_shirq_blocks, + ARRAY_SIZE(spear320_shirq_blocks), np); } -- cgit v1.2.3-59-g8ed1b From 801b295e4fd4a8c3344fcf523a12ef5ac41de889 Mon Sep 17 00:00:00 2001 From: "Anthony G. Basile" Date: Sat, 24 Nov 2012 11:54:24 -0500 Subject: doc: Remove unnecessary declarations from Documentation/accounting/getdelays.c stime and utime are declared __u64 but are never used. On a glibc system this is harmless lint, but on a uClibc system, because of the difference in they way header files stack, including stdio.h brings in time.h and this causes a name collision with stime. Since these are useless anyhow, we remove them. Signed-off-by: Anthony G. Basile Signed-off-by: Jiri Kosina --- Documentation/accounting/getdelays.c | 1 - 1 file changed, 1 deletion(-) (limited to 'Documentation') diff --git a/Documentation/accounting/getdelays.c b/Documentation/accounting/getdelays.c index 6f706aca2049..f8ebcde43b17 100644 --- a/Documentation/accounting/getdelays.c +++ b/Documentation/accounting/getdelays.c @@ -51,7 +51,6 @@ int dbg; int print_delays; int print_io_accounting; int print_task_context_switch_counts; -__u64 stime, utime; #define PRINTF(fmt, arg...) { \ if (dbg) { \ -- cgit v1.2.3-59-g8ed1b From ffe1137ba743cdf1c2414d5a89690aec1daa6bba Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Thu, 15 Nov 2012 14:52:19 -0500 Subject: nfsd4: delay filling in write iovec array till after xdr decoding Our server rejects compounds containing more than one write operation. It's unclear whether this is really permitted by the spec; with 4.0, it's possibly OK, with 4.1 (which has clearer limits on compound parameters), it's probably not OK. No client that we're aware of has ever done this, but in theory it could be useful. The source of the limitation: we need an array of iovecs to pass to the write operation. In the worst case that array of iovecs could have hundreds of elements (the maximum rwsize divided by the page size), so it's too big to put on the stack, or in each compound op. So we instead keep a single such array in the compound argument. We fill in that array at the time we decode the xdr operation. But we decode every op in the compound before executing any of them. So once we've used that array we can't decode another write. If we instead delay filling in that array till the time we actually perform the write, we can reuse it. Another option might be to switch to decoding compound ops one at a time. I considered doing that, but it has a number of other side effects, and I'd rather fix just this one problem for now. Signed-off-by: J. Bruce Fields --- Documentation/filesystems/nfs/nfs41-server.txt | 4 ++-- fs/nfsd/nfs4proc.c | 24 +++++++++++++++++++++++- fs/nfsd/nfs4xdr.c | 20 -------------------- fs/nfsd/xdr4.h | 1 - 4 files changed, 25 insertions(+), 24 deletions(-) (limited to 'Documentation') diff --git a/Documentation/filesystems/nfs/nfs41-server.txt b/Documentation/filesystems/nfs/nfs41-server.txt index 392ef637e101..01c2db769791 100644 --- a/Documentation/filesystems/nfs/nfs41-server.txt +++ b/Documentation/filesystems/nfs/nfs41-server.txt @@ -190,7 +190,7 @@ Nonstandard compound limitations: ca_maxrequestsize request and a ca_maxresponsesize reply, so we may fail to live up to the promise we made in CREATE_SESSION fore channel negotiation. -* No more than one IO operation (read, write, readdir) allowed per - compound. +* No more than one read-like operation allowed per compound; encoding + replies that cross page boundaries (except for read data) not handled. See also http://wiki.linux-nfs.org/wiki/index.php/Server_4.0_and_4.1_issues. diff --git a/fs/nfsd/nfs4proc.c b/fs/nfsd/nfs4proc.c index 1d2396b79574..87d24e5f3ca4 100644 --- a/fs/nfsd/nfs4proc.c +++ b/fs/nfsd/nfs4proc.c @@ -881,6 +881,24 @@ out: return status; } +static int fill_in_write_vector(struct kvec *vec, struct nfsd4_write *write) +{ + int i = 1; + int buflen = write->wr_buflen; + + vec[0].iov_base = write->wr_head.iov_base; + vec[0].iov_len = min_t(int, buflen, write->wr_head.iov_len); + buflen -= vec[0].iov_len; + + while (buflen) { + vec[i].iov_base = page_address(write->wr_pagelist[i - 1]); + vec[i].iov_len = min_t(int, PAGE_SIZE, buflen); + buflen -= vec[i].iov_len; + i++; + } + return i; +} + static __be32 nfsd4_write(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, struct nfsd4_write *write) @@ -889,6 +907,7 @@ nfsd4_write(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, struct file *filp = NULL; __be32 status = nfs_ok; unsigned long cnt; + int nvecs; /* no need to check permission - this will be done in nfsd_write() */ @@ -911,8 +930,11 @@ nfsd4_write(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, write->wr_how_written = write->wr_stable_how; gen_boot_verifier(&write->wr_verifier); + nvecs = fill_in_write_vector(rqstp->rq_vec, write); + WARN_ON_ONCE(nvecs > ARRAY_SIZE(rqstp->rq_vec)); + status = nfsd_write(rqstp, &cstate->current_fh, filp, - write->wr_offset, rqstp->rq_vec, write->wr_vlen, + write->wr_offset, rqstp->rq_vec, nvecs, &cnt, &write->wr_how_written); if (filp) fput(filp); diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c index cb9f9017af8f..09204f590355 100644 --- a/fs/nfsd/nfs4xdr.c +++ b/fs/nfsd/nfs4xdr.c @@ -1139,24 +1139,6 @@ nfsd4_decode_verify(struct nfsd4_compoundargs *argp, struct nfsd4_verify *verify DECODE_TAIL; } -static int fill_in_write_vector(struct kvec *vec, struct nfsd4_write *write) -{ - int i = 1; - int buflen = write->wr_buflen; - - vec[0].iov_base = write->wr_head.iov_base; - vec[0].iov_len = min_t(int, buflen, write->wr_head.iov_len); - buflen -= vec[0].iov_len; - - while (buflen) { - vec[i].iov_base = page_address(write->wr_pagelist[i - 1]); - vec[i].iov_len = min_t(int, PAGE_SIZE, buflen); - buflen -= vec[i].iov_len; - i++; - } - return i; -} - static __be32 nfsd4_decode_write(struct nfsd4_compoundargs *argp, struct nfsd4_write *write) { @@ -1204,8 +1186,6 @@ nfsd4_decode_write(struct nfsd4_compoundargs *argp, struct nfsd4_write *write) argp->end = argp->p + XDR_QUADLEN(PAGE_SIZE); } argp->p += XDR_QUADLEN(len); - write->wr_vlen = fill_in_write_vector(argp->rqstp->rq_vec, write); - WARN_ON_ONCE(write->wr_vlen > ARRAY_SIZE(argp->rqstp->rq_vec)); DECODE_TAIL; } diff --git a/fs/nfsd/xdr4.h b/fs/nfsd/xdr4.h index 152867b8125d..331f8a3277ab 100644 --- a/fs/nfsd/xdr4.h +++ b/fs/nfsd/xdr4.h @@ -385,7 +385,6 @@ struct nfsd4_write { u64 wr_offset; /* request */ u32 wr_stable_how; /* request */ u32 wr_buflen; /* request */ - int wr_vlen; struct kvec wr_head; struct page ** wr_pagelist; /* request */ -- cgit v1.2.3-59-g8ed1b From 0acba3cd018c51e529a6f008c092629ad2e9e0a4 Mon Sep 17 00:00:00 2001 From: Satoru Takeuchi Date: Mon, 26 Nov 2012 15:25:05 +0900 Subject: xfs: Remove the description of nodelaylog mount option from xfs.txt nodelaylog mount option is removed by commit 93b8a585. But there still be the description about it in the xfs document. This patch removes it. Signed-off-by: Satoru Takeuchi Reviewed-by: Ben Myers Signed-off-by: Ben Myers --- Documentation/filesystems/xfs.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Documentation') diff --git a/Documentation/filesystems/xfs.txt b/Documentation/filesystems/xfs.txt index 17187750270a..3e4b3dd1e046 100644 --- a/Documentation/filesystems/xfs.txt +++ b/Documentation/filesystems/xfs.txt @@ -43,7 +43,7 @@ When mounting an XFS filesystem, the following options are accepted. Issue command to let the block device reclaim space freed by the filesystem. This is useful for SSD devices, thinly provisioned LUNs and virtual machine images, but may have a performance - impact. This option is incompatible with the nodelaylog option. + impact. dmapi Enable the DMAPI (Data Management API) event callouts. -- cgit v1.2.3-59-g8ed1b From f9e01b5565398e549a5d391ea2e62f7b6e806e3f Mon Sep 17 00:00:00 2001 From: Giuseppe CAVALLARO Date: Sun, 25 Nov 2012 23:10:45 +0000 Subject: stmmac: update the doc with new IRQ mitigation This patch updates the stmmac.txt adding some information about the new rx/tx mitigation schema adopted in the driver. Signed-off-by: Giuseppe Cavallaro Signed-off-by: David S. Miller --- Documentation/networking/stmmac.txt | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) (limited to 'Documentation') diff --git a/Documentation/networking/stmmac.txt b/Documentation/networking/stmmac.txt index ef9ee71b4d7f..f9fa6db40a52 100644 --- a/Documentation/networking/stmmac.txt +++ b/Documentation/networking/stmmac.txt @@ -29,11 +29,9 @@ The kernel configuration option is STMMAC_ETH: dma_txsize: DMA tx ring size; buf_sz: DMA buffer size; tc: control the HW FIFO threshold; - tx_coe: Enable/Disable Tx Checksum Offload engine; watchdog: transmit timeout (in milliseconds); flow_ctrl: Flow control ability [on/off]; pause: Flow Control Pause Time; - tmrate: timer period (only if timer optimisation is configured). 3) Command line options Driver parameters can be also passed in command line by using: @@ -60,17 +58,19 @@ Then the poll method will be scheduled at some future point. The incoming packets are stored, by the DMA, in a list of pre-allocated socket buffers in order to avoid the memcpy (Zero-copy). -4.3) Timer-Driver Interrupt -Instead of having the device that asynchronously notifies the frame receptions, -the driver configures a timer to generate an interrupt at regular intervals. -Based on the granularity of the timer, the frames that are received by the -device will experience different levels of latency. Some NICs have dedicated -timer device to perform this task. STMMAC can use either the RTC device or the -TMU channel 2 on STLinux platforms. -The timers frequency can be passed to the driver as parameter; when change it, -take care of both hardware capability and network stability/performance impact. -Several performance tests on STM platforms showed this optimisation allows to -spare the CPU while having the maximum throughput. +4.3) Interrupt Mitigation +The driver is able to mitigate the number of its DMA interrupts +using NAPI for the reception on chips older than the 3.50. +New chips have an HW RX-Watchdog used for this mitigation. + +On Tx-side, the mitigation schema is based on a SW timer that calls the +tx function (stmmac_tx) to reclaim the resource after transmitting the +frames. +Also there is another parameter (like a threshold) used to program +the descriptors avoiding to set the interrupt on completion bit in +when the frame is sent (xmit). + +Mitigation parameters can be tuned by ethtool. 4.4) WOL Wake up on Lan feature through Magic and Unicast frames are supported for the @@ -121,6 +121,7 @@ struct plat_stmmacenet_data { int bugged_jumbo; int pmt; int force_sf_dma_mode; + int riwt_off; void (*fix_mac_speed)(void *priv, unsigned int speed); void (*bus_setup)(void __iomem *ioaddr); int (*init)(struct platform_device *pdev); @@ -156,6 +157,7 @@ Where: o pmt: core has the embedded power module (optional). o force_sf_dma_mode: force DMA to use the Store and Forward mode instead of the Threshold. + o riwt_off: force to disable the RX watchdog feature and switch to NAPI mode. o fix_mac_speed: this callback is used for modifying some syscfg registers (on ST SoCs) according to the link speed negotiated by the physical layer . -- cgit v1.2.3-59-g8ed1b From a550e566f2468cfedac44de33184329660276424 Mon Sep 17 00:00:00 2001 From: Fu Wei Date: Sun, 18 Nov 2012 18:02:21 +0800 Subject: Documentation:Update Documentation/zh_CN/arm64/memory.txt This is a update of Chinese documentation:Documentation/zh_CN/arm64/memory.txt It is based on the modifications of Documentation/arm64/memory.txt in submission :"e3978cde". Signed-off-by: Fu Wei Acked-by: Harry Wei Signed-off-by: Greg Kroah-Hartman --- Documentation/zh_CN/arm64/memory.txt | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'Documentation') diff --git a/Documentation/zh_CN/arm64/memory.txt b/Documentation/zh_CN/arm64/memory.txt index 2a744997d30e..a5f6283829f9 100644 --- a/Documentation/zh_CN/arm64/memory.txt +++ b/Documentation/zh_CN/arm64/memory.txt @@ -47,17 +47,17 @@ AArch64 Linux 内存布局: ----------------------------------------------------------------------- 0000000000000000 0000007fffffffff 512GB 用户空间 -ffffff8000000000 ffffffbbfffcffff ~240GB vmalloc +ffffff8000000000 ffffffbbfffeffff ~240GB vmalloc -ffffffbbfffd0000 ffffffbcfffdffff 64KB [防护页] +ffffffbbffff0000 ffffffbbffffffff 64KB [防护页] -ffffffbbfffe0000 ffffffbcfffeffff 64KB PCI I/O 空间 +ffffffbc00000000 ffffffbdffffffff 8GB vmemmap -ffffffbbffff0000 ffffffbcffffffff 64KB [防护页] +ffffffbe00000000 ffffffbffbbfffff ~8GB [防护页,未来用于 vmmemap] -ffffffbc00000000 ffffffbdffffffff 8GB vmemmap +ffffffbffbe00000 ffffffbffbe0ffff 64KB PCI I/O 空间 -ffffffbe00000000 ffffffbffbffffff ~8GB [防护页,未来用于 vmmemap] +ffffffbbffff0000 ffffffbcffffffff ~2MB [防护页] ffffffbffc000000 ffffffbfffffffff 64MB 模块 -- cgit v1.2.3-59-g8ed1b From 286aa031664ba5223bcb3dfd49a9e2d89d6b3aec Mon Sep 17 00:00:00 2001 From: HighPoint Linux Team Date: Thu, 25 Oct 2012 08:41:52 +0800 Subject: [SCSI] hptiop: Support HighPoint RR4520/RR4522 HBA Support IOP RR4520/RR4522 which are based on Marvell frey. Signed-off-by: HighPoint Linux Team Signed-off-by: James Bottomley --- Documentation/scsi/hptiop.txt | 69 ++++++- drivers/scsi/hptiop.c | 413 ++++++++++++++++++++++++++++++++++++++++-- drivers/scsi/hptiop.h | 72 +++++++- 3 files changed, 530 insertions(+), 24 deletions(-) (limited to 'Documentation') diff --git a/Documentation/scsi/hptiop.txt b/Documentation/scsi/hptiop.txt index 9605179711f4..4a4f47e759cd 100644 --- a/Documentation/scsi/hptiop.txt +++ b/Documentation/scsi/hptiop.txt @@ -37,7 +37,7 @@ For Intel IOP based adapters, the controller IOP is accessed via PCI BAR0: 0x40 Inbound Queue Port 0x44 Outbound Queue Port -For Marvell IOP based adapters, the IOP is accessed via PCI BAR0 and BAR1: +For Marvell not Frey IOP based adapters, the IOP is accessed via PCI BAR0 and BAR1: BAR0 offset Register 0x20400 Inbound Doorbell Register @@ -55,9 +55,31 @@ For Marvell IOP based adapters, the IOP is accessed via PCI BAR0 and BAR1: 0x40-0x1040 Inbound Queue 0x1040-0x2040 Outbound Queue +For Marvell Frey IOP based adapters, the IOP is accessed via PCI BAR0 and BAR1: -I/O Request Workflow ----------------------- + BAR0 offset Register + 0x0 IOP configuration information. + + BAR1 offset Register + 0x4000 Inbound List Base Address Low + 0x4004 Inbound List Base Address High + 0x4018 Inbound List Write Pointer + 0x402C Inbound List Configuration and Control + 0x4050 Outbound List Base Address Low + 0x4054 Outbound List Base Address High + 0x4058 Outbound List Copy Pointer Shadow Base Address Low + 0x405C Outbound List Copy Pointer Shadow Base Address High + 0x4088 Outbound List Interrupt Cause + 0x408C Outbound List Interrupt Enable + 0x1020C PCIe Function 0 Interrupt Enable + 0x10400 PCIe Function 0 to CPU Message A + 0x10420 CPU to PCIe Function 0 Message A + 0x10480 CPU to PCIe Function 0 Doorbell + 0x10484 CPU to PCIe Function 0 Doorbell Enable + + +I/O Request Workflow of Not Marvell Frey +------------------------------------------ All queued requests are handled via inbound/outbound queue port. A request packet can be allocated in either IOP or host memory. @@ -101,6 +123,45 @@ register 0. An outbound message with the same value indicates the completion of an inbound message. +I/O Request Workflow of Marvell Frey +-------------------------------------- + +All queued requests are handled via inbound/outbound list. + +To send a request to the controller: + + - Allocate a free request in host DMA coherent memory. + + Requests allocated in host memory must be aligned on 32-bytes boundary. + + - Fill the request with index of the request in the flag. + + Fill a free inbound list unit with the physical address and the size of + the request. + + Set up the inbound list write pointer with the index of previous unit, + round to 0 if the index reaches the supported count of requests. + + - Post the inbound list writer pointer to IOP. + + - The IOP process the request. When the request is completed, the flag of + the request with or-ed IOPMU_QUEUE_MASK_HOST_BITS will be put into a + free outbound list unit and the index of the outbound list unit will be + put into the copy pointer shadow register. An outbound interrupt will be + generated. + + - The host read the outbound list copy pointer shadow register and compare + with previous saved read ponter N. If they are different, the host will + read the (N+1)th outbound list unit. + + The host get the index of the request from the (N+1)th outbound list + unit and complete the request. + +Non-queued requests (reset communication/reset/flush etc) can be sent via PCIe +Function 0 to CPU Message A register. The CPU to PCIe Function 0 Message register +with the same value indicates the completion of message. + + User-level Interface --------------------- @@ -112,7 +173,7 @@ The driver exposes following sysfs attributes: ----------------------------------------------------------------------------- -Copyright (C) 2006-2009 HighPoint Technologies, Inc. All Rights Reserved. +Copyright (C) 2006-2012 HighPoint Technologies, Inc. All Rights Reserved. This file is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of diff --git a/drivers/scsi/hptiop.c b/drivers/scsi/hptiop.c index 192724ed7a32..138e573f37ef 100644 --- a/drivers/scsi/hptiop.c +++ b/drivers/scsi/hptiop.c @@ -1,6 +1,6 @@ /* * HighPoint RR3xxx/4xxx controller driver for Linux - * Copyright (C) 2006-2009 HighPoint Technologies, Inc. All Rights Reserved. + * Copyright (C) 2006-2012 HighPoint Technologies, Inc. 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 @@ -42,7 +42,7 @@ MODULE_DESCRIPTION("HighPoint RocketRAID 3xxx/4xxx Controller Driver"); static char driver_name[] = "hptiop"; static const char driver_name_long[] = "RocketRAID 3xxx/4xxx Controller driver"; -static const char driver_ver[] = "v1.6 (091225)"; +static const char driver_ver[] = "v1.8"; static int iop_send_sync_msg(struct hptiop_hba *hba, u32 msg, u32 millisec); static void hptiop_finish_scsi_req(struct hptiop_hba *hba, u32 tag, @@ -77,6 +77,11 @@ static int iop_wait_ready_mv(struct hptiop_hba *hba, u32 millisec) return iop_send_sync_msg(hba, IOPMU_INBOUND_MSG0_NOP, millisec); } +static int iop_wait_ready_mvfrey(struct hptiop_hba *hba, u32 millisec) +{ + return iop_send_sync_msg(hba, IOPMU_INBOUND_MSG0_NOP, millisec); +} + static void hptiop_request_callback_itl(struct hptiop_hba *hba, u32 tag) { if (tag & IOPMU_QUEUE_ADDR_HOST_BIT) @@ -230,6 +235,74 @@ static int iop_intr_mv(struct hptiop_hba *hba) return ret; } +static void hptiop_request_callback_mvfrey(struct hptiop_hba *hba, u32 _tag) +{ + u32 req_type = _tag & 0xf; + struct hpt_iop_request_scsi_command *req; + + switch (req_type) { + case IOP_REQUEST_TYPE_GET_CONFIG: + case IOP_REQUEST_TYPE_SET_CONFIG: + hba->msg_done = 1; + break; + + case IOP_REQUEST_TYPE_SCSI_COMMAND: + req = hba->reqs[(_tag >> 4) & 0xff].req_virt; + if (likely(_tag & IOPMU_QUEUE_REQUEST_RESULT_BIT)) + req->header.result = IOP_RESULT_SUCCESS; + hptiop_finish_scsi_req(hba, (_tag >> 4) & 0xff, req); + break; + + default: + break; + } +} + +static int iop_intr_mvfrey(struct hptiop_hba *hba) +{ + u32 _tag, status, cptr, cur_rptr; + int ret = 0; + + if (hba->initialized) + writel(0, &(hba->u.mvfrey.mu->pcie_f0_int_enable)); + + status = readl(&(hba->u.mvfrey.mu->f0_doorbell)); + if (status) { + writel(status, &(hba->u.mvfrey.mu->f0_doorbell)); + if (status & CPU_TO_F0_DRBL_MSG_BIT) { + u32 msg = readl(&(hba->u.mvfrey.mu->cpu_to_f0_msg_a)); + dprintk("received outbound msg %x\n", msg); + hptiop_message_callback(hba, msg); + } + ret = 1; + } + + status = readl(&(hba->u.mvfrey.mu->isr_cause)); + if (status) { + writel(status, &(hba->u.mvfrey.mu->isr_cause)); + do { + cptr = *hba->u.mvfrey.outlist_cptr & 0xff; + cur_rptr = hba->u.mvfrey.outlist_rptr; + while (cur_rptr != cptr) { + cur_rptr++; + if (cur_rptr == hba->u.mvfrey.list_count) + cur_rptr = 0; + + _tag = hba->u.mvfrey.outlist[cur_rptr].val; + BUG_ON(!(_tag & IOPMU_QUEUE_MASK_HOST_BITS)); + hptiop_request_callback_mvfrey(hba, _tag); + ret = 1; + } + hba->u.mvfrey.outlist_rptr = cur_rptr; + } while (cptr != (*hba->u.mvfrey.outlist_cptr & 0xff)); + } + + if (hba->initialized) + writel(0x1010, &(hba->u.mvfrey.mu->pcie_f0_int_enable)); + + return ret; +} + static int iop_send_sync_request_itl(struct hptiop_hba *hba, void __iomem *_req, u32 millisec) { @@ -272,6 +345,26 @@ static int iop_send_sync_request_mv(struct hptiop_hba *hba, return -1; } +static int iop_send_sync_request_mvfrey(struct hptiop_hba *hba, + u32 size_bits, u32 millisec) +{ + struct hpt_iop_request_header *reqhdr = + hba->u.mvfrey.internal_req.req_virt; + u32 i; + + hba->msg_done = 0; + reqhdr->flags |= cpu_to_le32(IOP_REQUEST_FLAG_SYNC_REQUEST); + hba->ops->post_req(hba, &(hba->u.mvfrey.internal_req)); + + for (i = 0; i < millisec; i++) { + iop_intr_mvfrey(hba); + if (hba->msg_done) + break; + msleep(1); + } + return hba->msg_done ? 0 : -1; +} + static void hptiop_post_msg_itl(struct hptiop_hba *hba, u32 msg) { writel(msg, &hba->u.itl.iop->inbound_msgaddr0); @@ -285,11 +378,18 @@ static void hptiop_post_msg_mv(struct hptiop_hba *hba, u32 msg) readl(&hba->u.mv.regs->inbound_doorbell); } +static void hptiop_post_msg_mvfrey(struct hptiop_hba *hba, u32 msg) +{ + writel(msg, &(hba->u.mvfrey.mu->f0_to_cpu_msg_a)); + readl(&(hba->u.mvfrey.mu->f0_to_cpu_msg_a)); +} + static int iop_send_sync_msg(struct hptiop_hba *hba, u32 msg, u32 millisec) { u32 i; hba->msg_done = 0; + hba->ops->disable_intr(hba); hba->ops->post_msg(hba, msg); for (i = 0; i < millisec; i++) { @@ -301,6 +401,7 @@ static int iop_send_sync_msg(struct hptiop_hba *hba, u32 msg, u32 millisec) msleep(1); } + hba->ops->enable_intr(hba); return hba->msg_done? 0 : -1; } @@ -354,6 +455,28 @@ static int iop_get_config_mv(struct hptiop_hba *hba, return 0; } +static int iop_get_config_mvfrey(struct hptiop_hba *hba, + struct hpt_iop_request_get_config *config) +{ + struct hpt_iop_request_get_config *info = hba->u.mvfrey.config; + + if (info->header.size != sizeof(struct hpt_iop_request_get_config) || + info->header.type != IOP_REQUEST_TYPE_GET_CONFIG) + return -1; + + config->interface_version = info->interface_version; + config->firmware_version = info->firmware_version; + config->max_requests = info->max_requests; + config->request_size = info->request_size; + config->max_sg_count = info->max_sg_count; + config->data_transfer_length = info->data_transfer_length; + config->alignment_mask = info->alignment_mask; + config->max_devices = info->max_devices; + config->sdram_size = info->sdram_size; + + return 0; +} + static int iop_set_config_itl(struct hptiop_hba *hba, struct hpt_iop_request_set_config *config) { @@ -408,6 +531,29 @@ static int iop_set_config_mv(struct hptiop_hba *hba, return 0; } +static int iop_set_config_mvfrey(struct hptiop_hba *hba, + struct hpt_iop_request_set_config *config) +{ + struct hpt_iop_request_set_config *req = + hba->u.mvfrey.internal_req.req_virt; + + memcpy(req, config, sizeof(struct hpt_iop_request_set_config)); + req->header.flags = cpu_to_le32(IOP_REQUEST_FLAG_OUTPUT_CONTEXT); + req->header.type = cpu_to_le32(IOP_REQUEST_TYPE_SET_CONFIG); + req->header.size = + cpu_to_le32(sizeof(struct hpt_iop_request_set_config)); + req->header.result = cpu_to_le32(IOP_RESULT_PENDING); + req->header.context = cpu_to_le32(IOP_REQUEST_TYPE_SET_CONFIG<<5); + req->header.context_hi32 = 0; + + if (iop_send_sync_request_mvfrey(hba, 0, 20000)) { + dprintk("Set config send cmd failed\n"); + return -1; + } + + return 0; +} + static void hptiop_enable_intr_itl(struct hptiop_hba *hba) { writel(~(IOPMU_OUTBOUND_INT_POSTQUEUE | IOPMU_OUTBOUND_INT_MSG0), @@ -420,6 +566,13 @@ static void hptiop_enable_intr_mv(struct hptiop_hba *hba) &hba->u.mv.regs->outbound_intmask); } +static void hptiop_enable_intr_mvfrey(struct hptiop_hba *hba) +{ + writel(CPU_TO_F0_DRBL_MSG_BIT, &(hba->u.mvfrey.mu->f0_doorbell_enable)); + writel(0x1, &(hba->u.mvfrey.mu->isr_enable)); + writel(0x1010, &(hba->u.mvfrey.mu->pcie_f0_int_enable)); +} + static int hptiop_initialize_iop(struct hptiop_hba *hba) { /* enable interrupts */ @@ -502,17 +655,39 @@ static int hptiop_map_pci_bar_mv(struct hptiop_hba *hba) return 0; } +static int hptiop_map_pci_bar_mvfrey(struct hptiop_hba *hba) +{ + hba->u.mvfrey.config = hptiop_map_pci_bar(hba, 0); + if (hba->u.mvfrey.config == NULL) + return -1; + + hba->u.mvfrey.mu = hptiop_map_pci_bar(hba, 2); + if (hba->u.mvfrey.mu == NULL) { + iounmap(hba->u.mvfrey.config); + return -1; + } + + return 0; +} + static void hptiop_unmap_pci_bar_mv(struct hptiop_hba *hba) { iounmap(hba->u.mv.regs); iounmap(hba->u.mv.mu); } +static void hptiop_unmap_pci_bar_mvfrey(struct hptiop_hba *hba) +{ + iounmap(hba->u.mvfrey.config); + iounmap(hba->u.mvfrey.mu); +} + static void hptiop_message_callback(struct hptiop_hba *hba, u32 msg) { dprintk("iop message 0x%x\n", msg); - if (msg == IOPMU_INBOUND_MSG0_NOP) + if (msg == IOPMU_INBOUND_MSG0_NOP || + msg == IOPMU_INBOUND_MSG0_RESET_COMM) hba->msg_done = 1; if (!hba->initialized) @@ -592,6 +767,7 @@ static void hptiop_finish_scsi_req(struct hptiop_hba *hba, u32 tag, memcpy(scp->sense_buffer, &req->sg_list, min_t(size_t, SCSI_SENSE_BUFFERSIZE, le32_to_cpu(req->dataxfer_length))); + goto skip_resid; break; default: @@ -599,6 +775,10 @@ static void hptiop_finish_scsi_req(struct hptiop_hba *hba, u32 tag, break; } + scsi_set_resid(scp, + scsi_bufflen(scp) - le32_to_cpu(req->dataxfer_length)); + +skip_resid: dprintk("scsi_done(%p)\n", scp); scp->scsi_done(scp); free_req(hba, &hba->reqs[tag]); @@ -692,7 +872,8 @@ static int hptiop_buildsgl(struct scsi_cmnd *scp, struct hpt_iopsg *psg) BUG_ON(HPT_SCP(scp)->sgcnt > hba->max_sg_descriptors); scsi_for_each_sg(scp, sg, HPT_SCP(scp)->sgcnt, idx) { - psg[idx].pci_address = cpu_to_le64(sg_dma_address(sg)); + psg[idx].pci_address = cpu_to_le64(sg_dma_address(sg)) | + hba->ops->host_phy_flag; psg[idx].size = cpu_to_le32(sg_dma_len(sg)); psg[idx].eot = (idx == HPT_SCP(scp)->sgcnt - 1) ? cpu_to_le32(1) : 0; @@ -751,6 +932,78 @@ static void hptiop_post_req_mv(struct hptiop_hba *hba, MVIOP_MU_QUEUE_ADDR_HOST_BIT | size_bit, hba); } +static void hptiop_post_req_mvfrey(struct hptiop_hba *hba, + struct hptiop_request *_req) +{ + struct hpt_iop_request_header *reqhdr = _req->req_virt; + u32 index; + + reqhdr->flags |= cpu_to_le32(IOP_REQUEST_FLAG_OUTPUT_CONTEXT | + IOP_REQUEST_FLAG_ADDR_BITS | + ((_req->req_shifted_phy >> 11) & 0xffff0000)); + reqhdr->context = cpu_to_le32(IOPMU_QUEUE_ADDR_HOST_BIT | + (_req->index << 4) | reqhdr->type); + reqhdr->context_hi32 = cpu_to_le32((_req->req_shifted_phy << 5) & + 0xffffffff); + + hba->u.mvfrey.inlist_wptr++; + index = hba->u.mvfrey.inlist_wptr & 0x3fff; + + if (index == hba->u.mvfrey.list_count) { + index = 0; + hba->u.mvfrey.inlist_wptr &= ~0x3fff; + hba->u.mvfrey.inlist_wptr ^= CL_POINTER_TOGGLE; + } + + hba->u.mvfrey.inlist[index].addr = + (dma_addr_t)_req->req_shifted_phy << 5; + hba->u.mvfrey.inlist[index].intrfc_len = (reqhdr->size + 3) / 4; + writel(hba->u.mvfrey.inlist_wptr, + &(hba->u.mvfrey.mu->inbound_write_ptr)); + readl(&(hba->u.mvfrey.mu->inbound_write_ptr)); +} + +static int hptiop_reset_comm_itl(struct hptiop_hba *hba) +{ + return 0; +} + +static int hptiop_reset_comm_mv(struct hptiop_hba *hba) +{ + return 0; +} + +static int hptiop_reset_comm_mvfrey(struct hptiop_hba *hba) +{ + u32 list_count = hba->u.mvfrey.list_count; + + if (iop_send_sync_msg(hba, IOPMU_INBOUND_MSG0_RESET_COMM, 3000)) + return -1; + + /* wait 100ms for MCU ready */ + msleep(100); + + writel(cpu_to_le32(hba->u.mvfrey.inlist_phy & 0xffffffff), + &(hba->u.mvfrey.mu->inbound_base)); + writel(cpu_to_le32((hba->u.mvfrey.inlist_phy >> 16) >> 16), + &(hba->u.mvfrey.mu->inbound_base_high)); + + writel(cpu_to_le32(hba->u.mvfrey.outlist_phy & 0xffffffff), + &(hba->u.mvfrey.mu->outbound_base)); + writel(cpu_to_le32((hba->u.mvfrey.outlist_phy >> 16) >> 16), + &(hba->u.mvfrey.mu->outbound_base_high)); + + writel(cpu_to_le32(hba->u.mvfrey.outlist_cptr_phy & 0xffffffff), + &(hba->u.mvfrey.mu->outbound_shadow_base)); + writel(cpu_to_le32((hba->u.mvfrey.outlist_cptr_phy >> 16) >> 16), + &(hba->u.mvfrey.mu->outbound_shadow_base_high)); + + hba->u.mvfrey.inlist_wptr = (list_count - 1) | CL_POINTER_TOGGLE; + *hba->u.mvfrey.outlist_cptr = (list_count - 1) | CL_POINTER_TOGGLE; + hba->u.mvfrey.outlist_rptr = list_count - 1; + return 0; +} + static int hptiop_queuecommand_lck(struct scsi_cmnd *scp, void (*done)(struct scsi_cmnd *)) { @@ -771,14 +1024,15 @@ static int hptiop_queuecommand_lck(struct scsi_cmnd *scp, _req->scp = scp; - dprintk("hptiop_queuecmd(scp=%p) %d/%d/%d/%d cdb=(%x-%x-%x) " + dprintk("hptiop_queuecmd(scp=%p) %d/%d/%d/%d cdb=(%08x-%08x-%08x-%08x) " "req_index=%d, req=%p\n", scp, host->host_no, scp->device->channel, scp->device->id, scp->device->lun, - ((u32 *)scp->cmnd)[0], - ((u32 *)scp->cmnd)[1], - ((u32 *)scp->cmnd)[2], + cpu_to_be32(((u32 *)scp->cmnd)[0]), + cpu_to_be32(((u32 *)scp->cmnd)[1]), + cpu_to_be32(((u32 *)scp->cmnd)[2]), + cpu_to_be32(((u32 *)scp->cmnd)[3]), _req->index, _req->req_virt); scp->result = 0; @@ -933,6 +1187,11 @@ static struct scsi_host_template driver_template = { .change_queue_depth = hptiop_adjust_disk_queue_depth, }; +static int hptiop_internal_memalloc_itl(struct hptiop_hba *hba) +{ + return 0; +} + static int hptiop_internal_memalloc_mv(struct hptiop_hba *hba) { hba->u.mv.internal_req = dma_alloc_coherent(&hba->pcidev->dev, @@ -943,6 +1202,63 @@ static int hptiop_internal_memalloc_mv(struct hptiop_hba *hba) return -1; } +static int hptiop_internal_memalloc_mvfrey(struct hptiop_hba *hba) +{ + u32 list_count = readl(&hba->u.mvfrey.mu->inbound_conf_ctl); + char *p; + dma_addr_t phy; + + BUG_ON(hba->max_request_size == 0); + + if (list_count == 0) { + BUG_ON(1); + return -1; + } + + list_count >>= 16; + + hba->u.mvfrey.list_count = list_count; + hba->u.mvfrey.internal_mem_size = 0x800 + + list_count * sizeof(struct mvfrey_inlist_entry) + + list_count * sizeof(struct mvfrey_outlist_entry) + + sizeof(int); + + p = dma_alloc_coherent(&hba->pcidev->dev, + hba->u.mvfrey.internal_mem_size, &phy, GFP_KERNEL); + if (!p) + return -1; + + hba->u.mvfrey.internal_req.req_virt = p; + hba->u.mvfrey.internal_req.req_shifted_phy = phy >> 5; + hba->u.mvfrey.internal_req.scp = NULL; + hba->u.mvfrey.internal_req.next = NULL; + + p += 0x800; + phy += 0x800; + + hba->u.mvfrey.inlist = (struct mvfrey_inlist_entry *)p; + hba->u.mvfrey.inlist_phy = phy; + + p += list_count * sizeof(struct mvfrey_inlist_entry); + phy += list_count * sizeof(struct mvfrey_inlist_entry); + + hba->u.mvfrey.outlist = (struct mvfrey_outlist_entry *)p; + hba->u.mvfrey.outlist_phy = phy; + + p += list_count * sizeof(struct mvfrey_outlist_entry); + phy += list_count * sizeof(struct mvfrey_outlist_entry); + + hba->u.mvfrey.outlist_cptr = (__le32 *)p; + hba->u.mvfrey.outlist_cptr_phy = phy; + + return 0; +} + +static int hptiop_internal_memfree_itl(struct hptiop_hba *hba) +{ + return 0; +} + static int hptiop_internal_memfree_mv(struct hptiop_hba *hba) { if (hba->u.mv.internal_req) { @@ -953,6 +1269,19 @@ static int hptiop_internal_memfree_mv(struct hptiop_hba *hba) return -1; } +static int hptiop_internal_memfree_mvfrey(struct hptiop_hba *hba) +{ + if (hba->u.mvfrey.internal_req.req_virt) { + dma_free_coherent(&hba->pcidev->dev, + hba->u.mvfrey.internal_mem_size, + hba->u.mvfrey.internal_req.req_virt, + (dma_addr_t) + hba->u.mvfrey.internal_req.req_shifted_phy << 5); + return 0; + } else + return -1; +} + static int __devinit hptiop_probe(struct pci_dev *pcidev, const struct pci_device_id *id) { @@ -1027,7 +1356,7 @@ static int __devinit hptiop_probe(struct pci_dev *pcidev, goto unmap_pci_bar; } - if (hba->ops->internal_memalloc) { + if (hba->ops->family == MV_BASED_IOP) { if (hba->ops->internal_memalloc(hba)) { printk(KERN_ERR "scsi%d: internal_memalloc failed\n", hba->host->host_no); @@ -1050,6 +1379,19 @@ static int __devinit hptiop_probe(struct pci_dev *pcidev, hba->interface_version = le32_to_cpu(iop_config.interface_version); hba->sdram_size = le32_to_cpu(iop_config.sdram_size); + if (hba->ops->family == MVFREY_BASED_IOP) { + if (hba->ops->internal_memalloc(hba)) { + printk(KERN_ERR "scsi%d: internal_memalloc failed\n", + hba->host->host_no); + goto unmap_pci_bar; + } + if (hba->ops->reset_comm(hba)) { + printk(KERN_ERR "scsi%d: reset comm failed\n", + hba->host->host_no); + goto unmap_pci_bar; + } + } + if (hba->firmware_version > 0x01020000 || hba->interface_version > 0x01020000) hba->iopintf_v2 = 1; @@ -1104,14 +1446,13 @@ static int __devinit hptiop_probe(struct pci_dev *pcidev, hba->dma_coherent = start_virt; hba->dma_coherent_handle = start_phy; - if ((start_phy & 0x1f) != 0) - { + if ((start_phy & 0x1f) != 0) { offset = ((start_phy + 0x1f) & ~0x1f) - start_phy; start_phy += offset; start_virt += offset; } - hba->req_list = start_virt; + hba->req_list = NULL; for (i = 0; i < hba->max_requests; i++) { hba->reqs[i].next = NULL; hba->reqs[i].req_virt = start_virt; @@ -1132,7 +1473,6 @@ static int __devinit hptiop_probe(struct pci_dev *pcidev, goto free_request_mem; } - scsi_scan_host(host); dprintk("scsi%d: hptiop_probe successfully\n", hba->host->host_no); @@ -1147,8 +1487,7 @@ free_request_irq: free_irq(hba->pcidev->irq, hba); unmap_pci_bar: - if (hba->ops->internal_memfree) - hba->ops->internal_memfree(hba); + hba->ops->internal_memfree(hba); hba->ops->unmap_pci_bar(hba); @@ -1198,6 +1537,16 @@ static void hptiop_disable_intr_mv(struct hptiop_hba *hba) readl(&hba->u.mv.regs->outbound_intmask); } +static void hptiop_disable_intr_mvfrey(struct hptiop_hba *hba) +{ + writel(0, &(hba->u.mvfrey.mu->f0_doorbell_enable)); + readl(&(hba->u.mvfrey.mu->f0_doorbell_enable)); + writel(0, &(hba->u.mvfrey.mu->isr_enable)); + readl(&(hba->u.mvfrey.mu->isr_enable)); + writel(0, &(hba->u.mvfrey.mu->pcie_f0_int_enable)); + readl(&(hba->u.mvfrey.mu->pcie_f0_int_enable)); +} + static void hptiop_remove(struct pci_dev *pcidev) { struct Scsi_Host *host = pci_get_drvdata(pcidev); @@ -1216,8 +1565,7 @@ static void hptiop_remove(struct pci_dev *pcidev) hba->dma_coherent, hba->dma_coherent_handle); - if (hba->ops->internal_memfree) - hba->ops->internal_memfree(hba); + hba->ops->internal_memfree(hba); hba->ops->unmap_pci_bar(hba); @@ -1229,9 +1577,10 @@ static void hptiop_remove(struct pci_dev *pcidev) } static struct hptiop_adapter_ops hptiop_itl_ops = { + .family = INTEL_BASED_IOP, .iop_wait_ready = iop_wait_ready_itl, - .internal_memalloc = NULL, - .internal_memfree = NULL, + .internal_memalloc = hptiop_internal_memalloc_itl, + .internal_memfree = hptiop_internal_memfree_itl, .map_pci_bar = hptiop_map_pci_bar_itl, .unmap_pci_bar = hptiop_unmap_pci_bar_itl, .enable_intr = hptiop_enable_intr_itl, @@ -1242,9 +1591,12 @@ static struct hptiop_adapter_ops hptiop_itl_ops = { .post_msg = hptiop_post_msg_itl, .post_req = hptiop_post_req_itl, .hw_dma_bit_mask = 64, + .reset_comm = hptiop_reset_comm_itl, + .host_phy_flag = cpu_to_le64(0), }; static struct hptiop_adapter_ops hptiop_mv_ops = { + .family = MV_BASED_IOP, .iop_wait_ready = iop_wait_ready_mv, .internal_memalloc = hptiop_internal_memalloc_mv, .internal_memfree = hptiop_internal_memfree_mv, @@ -1258,6 +1610,27 @@ static struct hptiop_adapter_ops hptiop_mv_ops = { .post_msg = hptiop_post_msg_mv, .post_req = hptiop_post_req_mv, .hw_dma_bit_mask = 33, + .reset_comm = hptiop_reset_comm_mv, + .host_phy_flag = cpu_to_le64(0), +}; + +static struct hptiop_adapter_ops hptiop_mvfrey_ops = { + .family = MVFREY_BASED_IOP, + .iop_wait_ready = iop_wait_ready_mvfrey, + .internal_memalloc = hptiop_internal_memalloc_mvfrey, + .internal_memfree = hptiop_internal_memfree_mvfrey, + .map_pci_bar = hptiop_map_pci_bar_mvfrey, + .unmap_pci_bar = hptiop_unmap_pci_bar_mvfrey, + .enable_intr = hptiop_enable_intr_mvfrey, + .disable_intr = hptiop_disable_intr_mvfrey, + .get_config = iop_get_config_mvfrey, + .set_config = iop_set_config_mvfrey, + .iop_intr = iop_intr_mvfrey, + .post_msg = hptiop_post_msg_mvfrey, + .post_req = hptiop_post_req_mvfrey, + .hw_dma_bit_mask = 64, + .reset_comm = hptiop_reset_comm_mvfrey, + .host_phy_flag = cpu_to_le64(1), }; static struct pci_device_id hptiop_id_table[] = { @@ -1283,6 +1656,8 @@ static struct pci_device_id hptiop_id_table[] = { { PCI_VDEVICE(TTI, 0x3120), (kernel_ulong_t)&hptiop_mv_ops }, { PCI_VDEVICE(TTI, 0x3122), (kernel_ulong_t)&hptiop_mv_ops }, { PCI_VDEVICE(TTI, 0x3020), (kernel_ulong_t)&hptiop_mv_ops }, + { PCI_VDEVICE(TTI, 0x4520), (kernel_ulong_t)&hptiop_mvfrey_ops }, + { PCI_VDEVICE(TTI, 0x4522), (kernel_ulong_t)&hptiop_mvfrey_ops }, {}, }; diff --git a/drivers/scsi/hptiop.h b/drivers/scsi/hptiop.h index baa648d87fde..020619d60b08 100644 --- a/drivers/scsi/hptiop.h +++ b/drivers/scsi/hptiop.h @@ -1,6 +1,6 @@ /* * HighPoint RR3xxx/4xxx controller driver for Linux - * Copyright (C) 2006-2009 HighPoint Technologies, Inc. All Rights Reserved. + * Copyright (C) 2006-2012 HighPoint Technologies, Inc. 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 @@ -75,6 +75,45 @@ struct hpt_iopmv_regs { __le32 outbound_intmask; }; +#pragma pack(1) +struct hpt_iopmu_mvfrey { + __le32 reserved0[(0x4000 - 0) / 4]; + __le32 inbound_base; + __le32 inbound_base_high; + __le32 reserved1[(0x4018 - 0x4008) / 4]; + __le32 inbound_write_ptr; + __le32 reserved2[(0x402c - 0x401c) / 4]; + __le32 inbound_conf_ctl; + __le32 reserved3[(0x4050 - 0x4030) / 4]; + __le32 outbound_base; + __le32 outbound_base_high; + __le32 outbound_shadow_base; + __le32 outbound_shadow_base_high; + __le32 reserved4[(0x4088 - 0x4060) / 4]; + __le32 isr_cause; + __le32 isr_enable; + __le32 reserved5[(0x1020c - 0x4090) / 4]; + __le32 pcie_f0_int_enable; + __le32 reserved6[(0x10400 - 0x10210) / 4]; + __le32 f0_to_cpu_msg_a; + __le32 reserved7[(0x10420 - 0x10404) / 4]; + __le32 cpu_to_f0_msg_a; + __le32 reserved8[(0x10480 - 0x10424) / 4]; + __le32 f0_doorbell; + __le32 f0_doorbell_enable; +}; + +struct mvfrey_inlist_entry { + dma_addr_t addr; + __le32 intrfc_len; + __le32 reserved; +}; + +struct mvfrey_outlist_entry { + __le32 val; +}; +#pragma pack() + #define MVIOP_MU_QUEUE_ADDR_HOST_MASK (~(0x1full)) #define MVIOP_MU_QUEUE_ADDR_HOST_BIT 4 @@ -87,6 +126,9 @@ struct hpt_iopmv_regs { #define MVIOP_MU_OUTBOUND_INT_MSG 1 #define MVIOP_MU_OUTBOUND_INT_POSTQUEUE 2 +#define CL_POINTER_TOGGLE 0x00004000 +#define CPU_TO_F0_DRBL_MSG_BIT 0x02000000 + enum hpt_iopmu_message { /* host-to-iop messages */ IOPMU_INBOUND_MSG0_NOP = 0, @@ -95,6 +137,7 @@ enum hpt_iopmu_message { IOPMU_INBOUND_MSG0_SHUTDOWN, IOPMU_INBOUND_MSG0_STOP_BACKGROUND_TASK, IOPMU_INBOUND_MSG0_START_BACKGROUND_TASK, + IOPMU_INBOUND_MSG0_RESET_COMM, IOPMU_INBOUND_MSG0_MAX = 0xff, /* iop-to-host messages */ IOPMU_OUTBOUND_MSG0_REGISTER_DEVICE_0 = 0x100, @@ -118,6 +161,7 @@ struct hpt_iop_request_header { #define IOP_REQUEST_FLAG_BIST_REQUEST 2 #define IOP_REQUEST_FLAG_REMAPPED 4 #define IOP_REQUEST_FLAG_OUTPUT_CONTEXT 8 +#define IOP_REQUEST_FLAG_ADDR_BITS 0x40 /* flags[31:16] is phy_addr[47:32] */ enum hpt_iop_request_type { IOP_REQUEST_TYPE_GET_CONFIG = 0, @@ -223,6 +267,13 @@ struct hpt_scsi_pointer { #define HPT_SCP(scp) ((struct hpt_scsi_pointer *)&(scp)->SCp) +enum hptiop_family { + UNKNOWN_BASED_IOP, + INTEL_BASED_IOP, + MV_BASED_IOP, + MVFREY_BASED_IOP +} ; + struct hptiop_hba { struct hptiop_adapter_ops *ops; union { @@ -236,6 +287,22 @@ struct hptiop_hba { void *internal_req; dma_addr_t internal_req_phy; } mv; + struct { + struct hpt_iop_request_get_config __iomem *config; + struct hpt_iopmu_mvfrey __iomem *mu; + + int internal_mem_size; + struct hptiop_request internal_req; + int list_count; + struct mvfrey_inlist_entry *inlist; + dma_addr_t inlist_phy; + __le32 inlist_wptr; + struct mvfrey_outlist_entry *outlist; + dma_addr_t outlist_phy; + __le32 *outlist_cptr; /* copy pointer shadow */ + dma_addr_t outlist_cptr_phy; + __le32 outlist_rptr; + } mvfrey; } u; struct Scsi_Host *host; @@ -283,6 +350,7 @@ struct hpt_ioctl_k { }; struct hptiop_adapter_ops { + enum hptiop_family family; int (*iop_wait_ready)(struct hptiop_hba *hba, u32 millisec); int (*internal_memalloc)(struct hptiop_hba *hba); int (*internal_memfree)(struct hptiop_hba *hba); @@ -298,6 +366,8 @@ struct hptiop_adapter_ops { void (*post_msg)(struct hptiop_hba *hba, u32 msg); void (*post_req)(struct hptiop_hba *hba, struct hptiop_request *_req); int hw_dma_bit_mask; + int (*reset_comm)(struct hptiop_hba *hba); + __le64 host_phy_flag; }; #define HPT_IOCTL_RESULT_OK 0 -- cgit v1.2.3-59-g8ed1b From 420993221175a45db5af012c53fd2fef4d9533dc Mon Sep 17 00:00:00 2001 From: Deepak Sikri Date: Tue, 27 Nov 2012 14:05:26 +0100 Subject: cpufreq: SPEAr: Add CPUFreq driver SPEAr is an ARM based family of SoCs. This patch adds in support of cpufreq driver for SPEAr SoCs. It is supported via DT only and so bindings are present in binding document. Signed-off-by: Deepak Sikri Signed-off-by: Viresh Kumar Signed-off-by: Rafael J. Wysocki --- .../devicetree/bindings/cpufreq/cpufreq-spear.txt | 42 +++ arch/arm/Kconfig | 1 + drivers/cpufreq/Kconfig.arm | 7 + drivers/cpufreq/Makefile | 1 + drivers/cpufreq/spear-cpufreq.c | 291 +++++++++++++++++++++ 5 files changed, 342 insertions(+) create mode 100644 Documentation/devicetree/bindings/cpufreq/cpufreq-spear.txt create mode 100644 drivers/cpufreq/spear-cpufreq.c (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/cpufreq/cpufreq-spear.txt b/Documentation/devicetree/bindings/cpufreq/cpufreq-spear.txt new file mode 100644 index 000000000000..f3d44984d91c --- /dev/null +++ b/Documentation/devicetree/bindings/cpufreq/cpufreq-spear.txt @@ -0,0 +1,42 @@ +SPEAr cpufreq driver +------------------- + +SPEAr SoC cpufreq driver for CPU frequency scaling. +It supports both uniprocessor (UP) and symmetric multiprocessor (SMP) systems +which share clock across all CPUs. + +Required properties: +- cpufreq_tbl: Table of frequencies CPU could be transitioned into, in the + increasing order. + +Optional properties: +- clock-latency: Specify the possible maximum transition latency for clock, in + unit of nanoseconds. + +Both required and optional properties listed above must be defined under node +/cpus/cpu@0. + +Examples: +-------- +cpus { + + <...> + + cpu@0 { + compatible = "arm,cortex-a9"; + reg = <0>; + + <...> + + cpufreq_tbl = < 166000 + 200000 + 250000 + 300000 + 400000 + 500000 + 600000 >; + }; + + <...> + +}; diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index ade7e924bef5..159e99737e31 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -904,6 +904,7 @@ config ARCH_NOMADIK config PLAT_SPEAR bool "ST SPEAr" + select ARCH_HAS_CPUFREQ select ARCH_REQUIRE_GPIOLIB select ARM_AMBA select CLKDEV_LOOKUP diff --git a/drivers/cpufreq/Kconfig.arm b/drivers/cpufreq/Kconfig.arm index 5961e6415f08..a0b3661d90b0 100644 --- a/drivers/cpufreq/Kconfig.arm +++ b/drivers/cpufreq/Kconfig.arm @@ -76,3 +76,10 @@ config ARM_EXYNOS5250_CPUFREQ help This adds the CPUFreq driver for Samsung EXYNOS5250 SoC. + +config ARM_SPEAR_CPUFREQ + bool "SPEAr CPUFreq support" + depends on PLAT_SPEAR + default y + help + This adds the CPUFreq driver support for SPEAr SOCs. diff --git a/drivers/cpufreq/Makefile b/drivers/cpufreq/Makefile index 5b1413e47216..1f254ec087c1 100644 --- a/drivers/cpufreq/Makefile +++ b/drivers/cpufreq/Makefile @@ -50,6 +50,7 @@ obj-$(CONFIG_ARM_EXYNOS4210_CPUFREQ) += exynos4210-cpufreq.o obj-$(CONFIG_ARM_EXYNOS4X12_CPUFREQ) += exynos4x12-cpufreq.o obj-$(CONFIG_ARM_EXYNOS5250_CPUFREQ) += exynos5250-cpufreq.o obj-$(CONFIG_ARM_OMAP2PLUS_CPUFREQ) += omap-cpufreq.o +obj-$(CONFIG_ARM_SPEAR_CPUFREQ) += spear-cpufreq.o ################################################################################## # PowerPC platform drivers diff --git a/drivers/cpufreq/spear-cpufreq.c b/drivers/cpufreq/spear-cpufreq.c new file mode 100644 index 000000000000..4575cfe41755 --- /dev/null +++ b/drivers/cpufreq/spear-cpufreq.c @@ -0,0 +1,291 @@ +/* + * drivers/cpufreq/spear-cpufreq.c + * + * CPU Frequency Scaling for SPEAr platform + * + * Copyright (C) 2012 ST Microelectronics + * Deepak Sikri + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include + +/* SPEAr CPUFreq driver data structure */ +static struct { + struct clk *clk; + unsigned int transition_latency; + struct cpufreq_frequency_table *freq_tbl; + u32 cnt; +} spear_cpufreq; + +int spear_cpufreq_verify(struct cpufreq_policy *policy) +{ + return cpufreq_frequency_table_verify(policy, spear_cpufreq.freq_tbl); +} + +static unsigned int spear_cpufreq_get(unsigned int cpu) +{ + return clk_get_rate(spear_cpufreq.clk) / 1000; +} + +static struct clk *spear1340_cpu_get_possible_parent(unsigned long newfreq) +{ + struct clk *sys_pclk; + int pclk; + /* + * In SPEAr1340, cpu clk's parent sys clk can take input from + * following sources + */ + const char *sys_clk_src[] = { + "sys_syn_clk", + "pll1_clk", + "pll2_clk", + "pll3_clk", + }; + + /* + * As sys clk can have multiple source with their own range + * limitation so we choose possible sources accordingly + */ + if (newfreq <= 300000000) + pclk = 0; /* src is sys_syn_clk */ + else if (newfreq > 300000000 && newfreq <= 500000000) + pclk = 3; /* src is pll3_clk */ + else if (newfreq == 600000000) + pclk = 1; /* src is pll1_clk */ + else + return ERR_PTR(-EINVAL); + + /* Get parent to sys clock */ + sys_pclk = clk_get(NULL, sys_clk_src[pclk]); + if (IS_ERR(sys_pclk)) + pr_err("Failed to get %s clock\n", sys_clk_src[pclk]); + + return sys_pclk; +} + +/* + * In SPEAr1340, we cannot use newfreq directly because we need to actually + * access a source clock (clk) which might not be ancestor of cpu at present. + * Hence in SPEAr1340 we would operate on source clock directly before switching + * cpu clock to it. + */ +static int spear1340_set_cpu_rate(struct clk *sys_pclk, unsigned long newfreq) +{ + struct clk *sys_clk; + int ret = 0; + + sys_clk = clk_get_parent(spear_cpufreq.clk); + if (IS_ERR(sys_clk)) { + pr_err("failed to get cpu's parent (sys) clock\n"); + return PTR_ERR(sys_clk); + } + + /* Set the rate of the source clock before changing the parent */ + ret = clk_set_rate(sys_pclk, newfreq); + if (ret) { + pr_err("Failed to set sys clk rate to %lu\n", newfreq); + return ret; + } + + ret = clk_set_parent(sys_clk, sys_pclk); + if (ret) { + pr_err("Failed to set sys clk parent\n"); + return ret; + } + + return 0; +} + +static int spear_cpufreq_target(struct cpufreq_policy *policy, + unsigned int target_freq, unsigned int relation) +{ + struct cpufreq_freqs freqs; + unsigned long newfreq; + struct clk *srcclk; + int index, ret, mult = 1; + + if (cpufreq_frequency_table_target(policy, spear_cpufreq.freq_tbl, + target_freq, relation, &index)) + return -EINVAL; + + freqs.cpu = policy->cpu; + freqs.old = spear_cpufreq_get(0); + + newfreq = spear_cpufreq.freq_tbl[index].frequency * 1000; + if (of_machine_is_compatible("st,spear1340")) { + /* + * SPEAr1340 is special in the sense that due to the possibility + * of multiple clock sources for cpu clk's parent we can have + * different clock source for different frequency of cpu clk. + * Hence we need to choose one from amongst these possible clock + * sources. + */ + srcclk = spear1340_cpu_get_possible_parent(newfreq); + if (IS_ERR(srcclk)) { + pr_err("Failed to get src clk\n"); + return PTR_ERR(srcclk); + } + + /* SPEAr1340: src clk is always 2 * intended cpu clk */ + mult = 2; + } else { + /* + * src clock to be altered is ancestor of cpu clock. Hence we + * can directly work on cpu clk + */ + srcclk = spear_cpufreq.clk; + } + + newfreq = clk_round_rate(srcclk, newfreq * mult); + if (newfreq < 0) { + pr_err("clk_round_rate failed for cpu src clock\n"); + return newfreq; + } + + freqs.new = newfreq / 1000; + freqs.new /= mult; + cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE); + + if (mult == 2) + ret = spear1340_set_cpu_rate(srcclk, newfreq); + else + ret = clk_set_rate(spear_cpufreq.clk, newfreq); + + /* Get current rate after clk_set_rate, in case of failure */ + if (ret) { + pr_err("CPU Freq: cpu clk_set_rate failed: %d\n", ret); + freqs.new = clk_get_rate(spear_cpufreq.clk) / 1000; + } + + cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE); + return ret; +} + +static int spear_cpufreq_init(struct cpufreq_policy *policy) +{ + int ret; + + ret = cpufreq_frequency_table_cpuinfo(policy, spear_cpufreq.freq_tbl); + if (ret) { + pr_err("cpufreq_frequency_table_cpuinfo() failed"); + return ret; + } + + cpufreq_frequency_table_get_attr(spear_cpufreq.freq_tbl, policy->cpu); + policy->cpuinfo.transition_latency = spear_cpufreq.transition_latency; + policy->cur = spear_cpufreq_get(0); + + cpumask_copy(policy->cpus, topology_core_cpumask(policy->cpu)); + cpumask_copy(policy->related_cpus, policy->cpus); + + return 0; +} + +static int spear_cpufreq_exit(struct cpufreq_policy *policy) +{ + cpufreq_frequency_table_put_attr(policy->cpu); + return 0; +} + +static struct freq_attr *spear_cpufreq_attr[] = { + &cpufreq_freq_attr_scaling_available_freqs, + NULL, +}; + +static struct cpufreq_driver spear_cpufreq_driver = { + .name = "cpufreq-spear", + .flags = CPUFREQ_STICKY, + .verify = spear_cpufreq_verify, + .target = spear_cpufreq_target, + .get = spear_cpufreq_get, + .init = spear_cpufreq_init, + .exit = spear_cpufreq_exit, + .attr = spear_cpufreq_attr, +}; + +static int spear_cpufreq_driver_init(void) +{ + struct device_node *np; + const struct property *prop; + struct cpufreq_frequency_table *freq_tbl; + const __be32 *val; + int cnt, i, ret; + + np = of_find_node_by_path("/cpus/cpu@0"); + if (!np) { + pr_err("No cpu node found"); + return -ENODEV; + } + + if (of_property_read_u32(np, "clock-latency", + &spear_cpufreq.transition_latency)) + spear_cpufreq.transition_latency = CPUFREQ_ETERNAL; + + prop = of_find_property(np, "cpufreq_tbl", NULL); + if (!prop || !prop->value) { + pr_err("Invalid cpufreq_tbl"); + ret = -ENODEV; + goto out_put_node; + } + + cnt = prop->length / sizeof(u32); + val = prop->value; + + freq_tbl = kmalloc(sizeof(*freq_tbl) * (cnt + 1), GFP_KERNEL); + if (!freq_tbl) { + ret = -ENOMEM; + goto out_put_node; + } + + for (i = 0; i < cnt; i++) { + freq_tbl[i].index = i; + freq_tbl[i].frequency = be32_to_cpup(val++); + } + + freq_tbl[i].index = i; + freq_tbl[i].frequency = CPUFREQ_TABLE_END; + + spear_cpufreq.freq_tbl = freq_tbl; + + of_node_put(np); + + spear_cpufreq.clk = clk_get(NULL, "cpu_clk"); + if (IS_ERR(spear_cpufreq.clk)) { + pr_err("Unable to get CPU clock\n"); + ret = PTR_ERR(spear_cpufreq.clk); + goto out_put_mem; + } + + ret = cpufreq_register_driver(&spear_cpufreq_driver); + if (!ret) + return 0; + + pr_err("failed register driver: %d\n", ret); + clk_put(spear_cpufreq.clk); + +out_put_mem: + kfree(freq_tbl); + return ret; + +out_put_node: + of_node_put(np); + return ret; +} +late_initcall(spear_cpufreq_driver_init); + +MODULE_AUTHOR("Deepak Sikri "); +MODULE_DESCRIPTION("SPEAr CPUFreq driver"); +MODULE_LICENSE("GPL"); -- cgit v1.2.3-59-g8ed1b From 3ee11aef75db51c69cb8cb91dd01afb28036f1b5 Mon Sep 17 00:00:00 2001 From: Gregory CLEMENT Date: Wed, 26 Sep 2012 18:02:50 +0200 Subject: arm: l2x0: add aurora related properties to OF binding Aurora is a L2 Cache Controller designed to be compatible with the L2x0 Cache Controller. L2X0 OF bindings are extended to support some specificity of Aurora (no cache id part number available through hardware, always write through mode, choice between outer cache and system cache). Signed-off-by: Gregory CLEMENT Signed-off-by: Yehuda Yitschak Tested-and-reviewed-by: Lior Amsalem Acked-by: Arnd Bergmann Cc: Grant Likely Cc: Rob Herring Cc: Russell King Cc: Barry Song <21cnbao@gmail.com> Cc: Will Deacon Cc: Arnd Bergmann Cc: Olof Johansson Signed-off-by: Jason Cooper --- Documentation/devicetree/bindings/arm/l2cc.txt | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/arm/l2cc.txt b/Documentation/devicetree/bindings/arm/l2cc.txt index 7ca52161e7ab..76b0ee6ee9a4 100644 --- a/Documentation/devicetree/bindings/arm/l2cc.txt +++ b/Documentation/devicetree/bindings/arm/l2cc.txt @@ -10,6 +10,12 @@ Required properties: "arm,pl310-cache" "arm,l220-cache" "arm,l210-cache" + "marvell,aurora-system-cache": Marvell Controller designed to be + compatible with the ARM one, with system cache mode (meaning + maintenance operations on L1 are broadcasted to the L2 and L2 + performs the same operation). + "marvell,"aurora-outer-cache: Marvell Controller designed to be + compatible with the ARM one with outer cache mode. - cache-unified : Specifies the cache is a unified cache. - cache-level : Should be set to 2 for a level 2 cache. - reg : Physical base address and size of cache controller's memory mapped @@ -29,6 +35,9 @@ Optional properties: filter. Addresses in the filter window are directed to the M1 port. Other addresses will go to the M0 port. - interrupts : 1 combined interrupt. +- cache-id-part: cache id part number to be used if it is not present + on hardware +- wt-override: If present then L2 is forced to Write through mode Example: -- cgit v1.2.3-59-g8ed1b From 77b71b370ed06c75bdebef09be438d5275f70fc1 Mon Sep 17 00:00:00 2001 From: Thomas Abraham Date: Tue, 27 Nov 2012 14:04:32 +0530 Subject: regulator: add device tree support for max8997 Add device tree based discovery support for max8997. Signed-off-by: Thomas Abraham Acked-by: MyungJoo Ham Reviewed-by: Tomasz Figa Signed-off-by: Mark Brown --- .../bindings/regulator/max8997-regulator.txt | 146 ++++++++++++++++++++ drivers/mfd/max8997.c | 73 +++++++++- drivers/regulator/max8997.c | 148 ++++++++++++++++++++- include/linux/mfd/max8997-private.h | 1 + include/linux/mfd/max8997.h | 1 + 5 files changed, 366 insertions(+), 3 deletions(-) create mode 100644 Documentation/devicetree/bindings/regulator/max8997-regulator.txt (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/regulator/max8997-regulator.txt b/Documentation/devicetree/bindings/regulator/max8997-regulator.txt new file mode 100644 index 000000000000..9fd69a18b0ba --- /dev/null +++ b/Documentation/devicetree/bindings/regulator/max8997-regulator.txt @@ -0,0 +1,146 @@ +* Maxim MAX8997 Voltage and Current Regulator + +The Maxim MAX8997 is a multi-function device which includes volatage and +current regulators, rtc, charger controller and other sub-blocks. It is +interfaced to the host controller using a i2c interface. Each sub-block is +addressed by the host system using different i2c slave address. This document +describes the bindings for 'pmic' sub-block of max8997. + +Required properties: +- compatible: Should be "maxim,max8997-pmic". +- reg: Specifies the i2c slave address of the pmic block. It should be 0x66. + +- max8997,pmic-buck1-dvs-voltage: A set of 8 voltage values in micro-volt (uV) + units for buck1 when changing voltage using gpio dvs. Refer to [1] below + for additional information. + +- max8997,pmic-buck2-dvs-voltage: A set of 8 voltage values in micro-volt (uV) + units for buck2 when changing voltage using gpio dvs. Refer to [1] below + for additional information. + +- max8997,pmic-buck5-dvs-voltage: A set of 8 voltage values in micro-volt (uV) + units for buck5 when changing voltage using gpio dvs. Refer to [1] below + for additional information. + +[1] If none of the 'max8997,pmic-buck[1/2/5]-uses-gpio-dvs' optional + property is specified, the 'max8997,pmic-buck[1/2/5]-dvs-voltage' + property should specify atleast one voltage level (which would be a + safe operating voltage). + + If either of the 'max8997,pmic-buck[1/2/5]-uses-gpio-dvs' optional + property is specified, then all the eigth voltage values for the + 'max8997,pmic-buck[1/2/5]-dvs-voltage' should be specified. + +Optional properties: +- interrupt-parent: Specifies the phandle of the interrupt controller to which + the interrupts from max8997 are delivered to. +- interrupts: Interrupt specifiers for two interrupt sources. + - First interrupt specifier is for 'irq1' interrupt. + - Second interrupt specifier is for 'alert' interrupt. +- max8997,pmic-buck1-uses-gpio-dvs: 'buck1' can be controlled by gpio dvs. +- max8997,pmic-buck2-uses-gpio-dvs: 'buck2' can be controlled by gpio dvs. +- max8997,pmic-buck5-uses-gpio-dvs: 'buck5' can be controlled by gpio dvs. + +Additional properties required if either of the optional properties are used: +- max8997,pmic-ignore-gpiodvs-side-effect: When GPIO-DVS mode is used for + multiple bucks, changing the voltage value of one of the bucks may affect + that of another buck, which is the side effect of the change (set_voltage). + Use this property to ignore such side effects and change the voltage. + +- max8997,pmic-buck125-default-dvs-idx: Default voltage setting selected from + the possible 8 options selectable by the dvs gpios. The value of this + property should be between 0 and 7. If not specified or if out of range, the + default value of this property is set to 0. + +- max8997,pmic-buck125-dvs-gpios: GPIO specifiers for three host gpio's used + for dvs. The format of the gpio specifier depends in the gpio controller. + +Regulators: The regulators of max8997 that have to be instantiated should be +included in a sub-node named 'regulators'. Regulator nodes included in this +sub-node should be of the format as listed below. + + regulator_name { + standard regulator bindings here + }; + +The following are the names of the regulators that the max8997 pmic block +supports. Note: The 'n' in LDOn and BUCKn represents the LDO or BUCK number +as per the datasheet of max8997. + + - LDOn + - valid values for n are 1 to 18 and 21 + - Example: LDO0, LD01, LDO2, LDO21 + - BUCKn + - valid values for n are 1 to 7. + - Example: BUCK1, BUCK2, BUCK3, BUCK7 + + - ENVICHG: Battery Charging Current Monitor Output. This is a fixed + voltage type regulator + + - ESAFEOUT1: (ldo19) + - ESAFEOUT2: (ld020) + + - CHARGER_CV: main battery charger voltage control + - CHARGER: main battery charger current control + - CHARGER_TOPOFF: end of charge current threshold level + +The bindings inside the regulator nodes use the standard regulator bindings +which are documented elsewhere. + +Example: + + max8997_pmic@66 { + compatible = "maxim,max8997-pmic"; + interrupt-parent = <&wakeup_eint>; + reg = <0x66>; + interrupts = <4 0>, <3 0>; + + max8997,pmic-buck1-uses-gpio-dvs; + max8997,pmic-buck2-uses-gpio-dvs; + max8997,pmic-buck5-uses-gpio-dvs; + + max8997,pmic-ignore-gpiodvs-side-effect; + max8997,pmic-buck125-default-dvs-idx = <0>; + + max8997,pmic-buck125-dvs-gpios = <&gpx0 0 1 0 0>, /* SET1 */ + <&gpx0 1 1 0 0>, /* SET2 */ + <&gpx0 2 1 0 0>; /* SET3 */ + + max8997,pmic-buck1-dvs-voltage = <1350000>, <1300000>, + <1250000>, <1200000>, + <1150000>, <1100000>, + <1000000>, <950000>; + + max8997,pmic-buck2-dvs-voltage = <1100000>, <1100000>, + <1100000>, <1100000>, + <1000000>, <1000000>, + <1000000>, <1000000>; + + max8997,pmic-buck5-dvs-voltage = <1200000>, <1200000>, + <1200000>, <1200000>, + <1200000>, <1200000>, + <1200000>, <1200000>; + + regulators { + ldo1_reg: LDO1 { + regulator-name = "VDD_ABB_3.3V"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + }; + + ldo2_reg: LDO2 { + regulator-name = "VDD_ALIVE_1.1V"; + regulator-min-microvolt = <1100000>; + regulator-max-microvolt = <1100000>; + regulator-always-on; + }; + + buck1_reg: BUCK1 { + regulator-name = "VDD_ARM_1.2V"; + regulator-min-microvolt = <950000>; + regulator-max-microvolt = <1350000>; + regulator-always-on; + regulator-boot-on; + }; + }; + }; diff --git a/drivers/mfd/max8997.c b/drivers/mfd/max8997.c index f123517065ec..abd5c80c7cf5 100644 --- a/drivers/mfd/max8997.c +++ b/drivers/mfd/max8997.c @@ -21,8 +21,10 @@ * This driver is based on max8998.c */ +#include #include #include +#include #include #include #include @@ -47,6 +49,13 @@ static struct mfd_cell max8997_devs[] = { { .name = "max8997-led", .id = 2 }, }; +#ifdef CONFIG_OF +static struct of_device_id __devinitdata max8997_pmic_dt_match[] = { + { .compatible = "maxim,max8997-pmic", .data = TYPE_MAX8997 }, + {}, +}; +#endif + int max8997_read_reg(struct i2c_client *i2c, u8 reg, u8 *dest) { struct max8997_dev *max8997 = i2c_get_clientdata(i2c); @@ -123,6 +132,58 @@ int max8997_update_reg(struct i2c_client *i2c, u8 reg, u8 val, u8 mask) } EXPORT_SYMBOL_GPL(max8997_update_reg); +#ifdef CONFIG_OF +/* + * Only the common platform data elements for max8997 are parsed here from the + * device tree. Other sub-modules of max8997 such as pmic, rtc and others have + * to parse their own platform data elements from device tree. + * + * The max8997 platform data structure is instantiated here and the drivers for + * the sub-modules need not instantiate another instance while parsing their + * platform data. + */ +static struct max8997_platform_data *max8997_i2c_parse_dt_pdata( + struct device *dev) +{ + struct max8997_platform_data *pd; + + pd = devm_kzalloc(dev, sizeof(*pd), GFP_KERNEL); + if (!pd) { + dev_err(dev, "could not allocate memory for pdata\n"); + return ERR_PTR(-ENOMEM); + } + + pd->ono = irq_of_parse_and_map(dev->of_node, 1); + + /* + * ToDo: the 'wakeup' member in the platform data is more of a linux + * specfic information. Hence, there is no binding for that yet and + * not parsed here. + */ + + return pd; +} +#else +static struct max8997_platform_data *max8997_i2c_parse_dt_pdata( + struct device *dev) +{ + return 0; +} +#endif + +static inline int max8997_i2c_get_driver_data(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ +#ifdef CONFIG_OF + if (i2c->dev.of_node) { + const struct of_device_id *match; + match = of_match_node(max8997_pmic_dt_match, i2c->dev.of_node); + return (int)match->data; + } +#endif + return (int)id->driver_data; +} + static int max8997_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *id) { @@ -137,12 +198,21 @@ static int max8997_i2c_probe(struct i2c_client *i2c, i2c_set_clientdata(i2c, max8997); max8997->dev = &i2c->dev; max8997->i2c = i2c; - max8997->type = id->driver_data; + max8997->type = max8997_i2c_get_driver_data(i2c, id); max8997->irq = i2c->irq; + if (max8997->dev->of_node) { + pdata = max8997_i2c_parse_dt_pdata(max8997->dev); + if (IS_ERR(pdata)) { + ret = PTR_ERR(pdata); + goto err; + } + } + if (!pdata) goto err; + max8997->pdata = pdata; max8997->ono = pdata->ono; mutex_init(&max8997->iolock); @@ -434,6 +504,7 @@ static struct i2c_driver max8997_i2c_driver = { .name = "max8997", .owner = THIS_MODULE, .pm = &max8997_pm, + .of_match_table = of_match_ptr(max8997_pmic_dt_match), }, .probe = max8997_i2c_probe, .remove = max8997_i2c_remove, diff --git a/drivers/regulator/max8997.c b/drivers/regulator/max8997.c index 64cf2ee38f6c..b56c4326853d 100644 --- a/drivers/regulator/max8997.c +++ b/drivers/regulator/max8997.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -31,6 +32,7 @@ #include #include #include +#include struct max8997_data { struct device *dev; @@ -933,10 +935,145 @@ static struct regulator_desc regulators[] = { max8997_charger_fixedstate_ops), }; +#ifdef CONFIG_OF +static int max8997_pmic_dt_parse_dvs_gpio(struct max8997_dev *iodev, + struct max8997_platform_data *pdata, + struct device_node *pmic_np) +{ + int i, gpio; + + for (i = 0; i < 3; i++) { + gpio = of_get_named_gpio(pmic_np, + "max8997,pmic-buck125-dvs-gpios", i); + if (!gpio_is_valid(gpio)) { + dev_err(iodev->dev, "invalid gpio[%d]: %d\n", i, gpio); + return -EINVAL; + } + pdata->buck125_gpios[i] = gpio; + } + return 0; +} + +static int max8997_pmic_dt_parse_pdata(struct max8997_dev *iodev, + struct max8997_platform_data *pdata) +{ + struct device_node *pmic_np, *regulators_np, *reg_np; + struct max8997_regulator_data *rdata; + unsigned int i, dvs_voltage_nr = 1, ret; + + pmic_np = iodev->dev->of_node; + if (!pmic_np) { + dev_err(iodev->dev, "could not find pmic sub-node\n"); + return -ENODEV; + } + + regulators_np = of_find_node_by_name(pmic_np, "regulators"); + if (!regulators_np) { + dev_err(iodev->dev, "could not find regulators sub-node\n"); + return -EINVAL; + } + + /* count the number of regulators to be supported in pmic */ + pdata->num_regulators = 0; + for_each_child_of_node(regulators_np, reg_np) + pdata->num_regulators++; + + rdata = devm_kzalloc(iodev->dev, sizeof(*rdata) * + pdata->num_regulators, GFP_KERNEL); + if (!rdata) { + dev_err(iodev->dev, "could not allocate memory for " + "regulator data\n"); + return -ENOMEM; + } + + pdata->regulators = rdata; + for_each_child_of_node(regulators_np, reg_np) { + for (i = 0; i < ARRAY_SIZE(regulators); i++) + if (!of_node_cmp(reg_np->name, regulators[i].name)) + break; + + if (i == ARRAY_SIZE(regulators)) { + dev_warn(iodev->dev, "don't know how to configure " + "regulator %s\n", reg_np->name); + continue; + } + + rdata->id = i; + rdata->initdata = of_get_regulator_init_data( + iodev->dev, reg_np); + rdata->reg_node = reg_np; + rdata++; + } + + if (of_get_property(pmic_np, "max8997,pmic-buck1-uses-gpio-dvs", NULL)) + pdata->buck1_gpiodvs = true; + + if (of_get_property(pmic_np, "max8997,pmic-buck2-uses-gpio-dvs", NULL)) + pdata->buck2_gpiodvs = true; + + if (of_get_property(pmic_np, "max8997,pmic-buck5-uses-gpio-dvs", NULL)) + pdata->buck5_gpiodvs = true; + + if (pdata->buck1_gpiodvs || pdata->buck2_gpiodvs || + pdata->buck5_gpiodvs) { + ret = max8997_pmic_dt_parse_dvs_gpio(iodev, pdata, pmic_np); + if (ret) + return -EINVAL; + + if (of_property_read_u32(pmic_np, + "max8997,pmic-buck125-default-dvs-idx", + &pdata->buck125_default_idx)) { + pdata->buck125_default_idx = 0; + } else { + if (pdata->buck125_default_idx >= 8) { + pdata->buck125_default_idx = 0; + dev_info(iodev->dev, "invalid value for " + "default dvs index, using 0 instead\n"); + } + } + + if (of_get_property(pmic_np, + "max8997,pmic-ignore-gpiodvs-side-effect", NULL)) + pdata->ignore_gpiodvs_side_effect = true; + + dvs_voltage_nr = 8; + } + + if (of_property_read_u32_array(pmic_np, + "max8997,pmic-buck1-dvs-voltage", + pdata->buck1_voltage, dvs_voltage_nr)) { + dev_err(iodev->dev, "buck1 voltages not specified\n"); + return -EINVAL; + } + + if (of_property_read_u32_array(pmic_np, + "max8997,pmic-buck2-dvs-voltage", + pdata->buck2_voltage, dvs_voltage_nr)) { + dev_err(iodev->dev, "buck2 voltages not specified\n"); + return -EINVAL; + } + + if (of_property_read_u32_array(pmic_np, + "max8997,pmic-buck5-dvs-voltage", + pdata->buck5_voltage, dvs_voltage_nr)) { + dev_err(iodev->dev, "buck5 voltages not specified\n"); + return -EINVAL; + } + + return 0; +} +#else +static int max8997_pmic_dt_parse_pdata(struct max8997_dev *iodev, + struct max8997_platform_data *pdata) +{ + return 0; +} +#endif /* CONFIG_OF */ + static __devinit int max8997_pmic_probe(struct platform_device *pdev) { struct max8997_dev *iodev = dev_get_drvdata(pdev->dev.parent); - struct max8997_platform_data *pdata = dev_get_platdata(iodev->dev); + struct max8997_platform_data *pdata = iodev->pdata; struct regulator_config config = { }; struct regulator_dev **rdev; struct max8997_data *max8997; @@ -944,11 +1081,17 @@ static __devinit int max8997_pmic_probe(struct platform_device *pdev) int i, ret, size, nr_dvs; u8 max_buck1 = 0, max_buck2 = 0, max_buck5 = 0; - if (!pdata) { + if (IS_ERR_OR_NULL(pdata)) { dev_err(pdev->dev.parent, "No platform init data supplied.\n"); return -ENODEV; } + if (iodev->dev->of_node) { + ret = max8997_pmic_dt_parse_pdata(iodev, pdata); + if (ret) + return ret; + } + max8997 = devm_kzalloc(&pdev->dev, sizeof(struct max8997_data), GFP_KERNEL); if (!max8997) @@ -1104,6 +1247,7 @@ static __devinit int max8997_pmic_probe(struct platform_device *pdev) config.dev = max8997->dev; config.init_data = pdata->regulators[i].initdata; config.driver_data = max8997; + config.of_node = pdata->regulators[i].reg_node; rdev[i] = regulator_register(®ulators[id], &config); if (IS_ERR(rdev[i])) { diff --git a/include/linux/mfd/max8997-private.h b/include/linux/mfd/max8997-private.h index 830152cfae33..6ae21bf47d64 100644 --- a/include/linux/mfd/max8997-private.h +++ b/include/linux/mfd/max8997-private.h @@ -316,6 +316,7 @@ enum max8997_irq { #define MAX8997_NUM_GPIO 12 struct max8997_dev { struct device *dev; + struct max8997_platform_data *pdata; struct i2c_client *i2c; /* 0xcc / PMIC, Battery Control, and FLASH */ struct i2c_client *rtc; /* slave addr 0x0c */ struct i2c_client *haptic; /* slave addr 0x90 */ diff --git a/include/linux/mfd/max8997.h b/include/linux/mfd/max8997.h index 328d8e24b533..1d4a4fe6ac33 100644 --- a/include/linux/mfd/max8997.h +++ b/include/linux/mfd/max8997.h @@ -75,6 +75,7 @@ enum max8998_regulators { struct max8997_regulator_data { int id; struct regulator_init_data *initdata; + struct device_node *reg_node; }; enum max8997_muic_usb_type { -- cgit v1.2.3-59-g8ed1b From e65fe5a91404af97a7a487e6c7606fb5e3807d7d Mon Sep 17 00:00:00 2001 From: Yacine Belkadi Date: Mon, 26 Nov 2012 22:21:23 +0100 Subject: Kernel-doc: Convention: Use a "Return" section to describe return values Non-void functions should describe their return values in their kernel-doc comments. Currently, some don't, others do in various forms. For example: * Return the result. * Return: The result. * Returns the result. * Returns: the result. * Return Value: The result. * @return: the result. * This function returns the result. * It will return the result. Defining a convention would improve consistency of kernel-doc comments. It would also help scripts/kernel-doc identify the text describing the return value of a function. Thus allowing additional checks on the comments, and suitable highlighting in the generated docs (man pages, html, etc). So, as a convention, use a section named "Return" to describe the return value of a function. Signed-off-by: Yacine Belkadi Signed-off-by: Rob Landley Signed-off-by: Jiri Kosina --- Documentation/kernel-doc-nano-HOWTO.txt | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) (limited to 'Documentation') diff --git a/Documentation/kernel-doc-nano-HOWTO.txt b/Documentation/kernel-doc-nano-HOWTO.txt index 3d8a97747f77..99b57abddf8a 100644 --- a/Documentation/kernel-doc-nano-HOWTO.txt +++ b/Documentation/kernel-doc-nano-HOWTO.txt @@ -64,6 +64,8 @@ Example kernel-doc function comment: * comment lines. * * The longer description can have multiple paragraphs. + * + * Return: Describe the return value of foobar. */ The short description following the subject can span multiple lines @@ -78,6 +80,8 @@ If a function parameter is "..." (varargs), it should be listed in kernel-doc notation as: * @...: description +The return value, if any, should be described in a dedicated section +named "Return". Example kernel-doc data structure comment. @@ -222,6 +226,9 @@ only a "*"). "section header:" names must be unique per function (or struct, union, typedef, enum). +Use the section header "Return" for sections describing the return value +of a function. + Avoid putting a spurious blank line after the function name, or else the description will be repeated! @@ -237,21 +244,21 @@ patterns, which are highlighted appropriately. NOTE 1: The multi-line descriptive text you provide does *not* recognize line breaks, so if you try to format some text nicely, as in: - Return codes + Return: 0 - cool 1 - invalid arg 2 - out of memory this will all run together and produce: - Return codes 0 - cool 1 - invalid arg 2 - out of memory + Return: 0 - cool 1 - invalid arg 2 - out of memory NOTE 2: If the descriptive text you provide has lines that begin with some phrase followed by a colon, each of those phrases will be taken as a new section heading, which means you should similarly try to avoid text like: - Return codes: + Return: 0: cool 1: invalid arg 2: out of memory -- cgit v1.2.3-59-g8ed1b From a82b8db02f33b38a24fc3858968153940f7c4740 Mon Sep 17 00:00:00 2001 From: Thomas Hellstrom Date: Tue, 20 Nov 2012 12:16:48 +0000 Subject: kref: Add kref_get_unless_zero documentation Document how kref_get_unless_zero should be used and how it helps solve a typical kref / locking problem. Signed-off-by: Thomas Hellstrom Signed-off-by: Dave Airlie --- Documentation/kref.txt | 88 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 88 insertions(+) (limited to 'Documentation') diff --git a/Documentation/kref.txt b/Documentation/kref.txt index 48ba715d5a63..ddf85a5dde0c 100644 --- a/Documentation/kref.txt +++ b/Documentation/kref.txt @@ -213,3 +213,91 @@ presentation on krefs, which can be found at: and: http://www.kroah.com/linux/talks/ols_2004_kref_talk/ + +The above example could also be optimized using kref_get_unless_zero() in +the following way: + +static struct my_data *get_entry() +{ + struct my_data *entry = NULL; + mutex_lock(&mutex); + if (!list_empty(&q)) { + entry = container_of(q.next, struct my_data, link); + if (!kref_get_unless_zero(&entry->refcount)) + entry = NULL; + } + mutex_unlock(&mutex); + return entry; +} + +static void release_entry(struct kref *ref) +{ + struct my_data *entry = container_of(ref, struct my_data, refcount); + + mutex_lock(&mutex); + list_del(&entry->link); + mutex_unlock(&mutex); + kfree(entry); +} + +static void put_entry(struct my_data *entry) +{ + kref_put(&entry->refcount, release_entry); +} + +Which is useful to remove the mutex lock around kref_put() in put_entry(), but +it's important that kref_get_unless_zero is enclosed in the same critical +section that finds the entry in the lookup table, +otherwise kref_get_unless_zero may reference already freed memory. +Note that it is illegal to use kref_get_unless_zero without checking its +return value. If you are sure (by already having a valid pointer) that +kref_get_unless_zero() will return true, then use kref_get() instead. + +The function kref_get_unless_zero also makes it possible to use rcu +locking for lookups in the above example: + +struct my_data +{ + struct rcu_head rhead; + . + struct kref refcount; + . + . +}; + +static struct my_data *get_entry_rcu() +{ + struct my_data *entry = NULL; + rcu_read_lock(); + if (!list_empty(&q)) { + entry = container_of(q.next, struct my_data, link); + if (!kref_get_unless_zero(&entry->refcount)) + entry = NULL; + } + rcu_read_unlock(); + return entry; +} + +static void release_entry_rcu(struct kref *ref) +{ + struct my_data *entry = container_of(ref, struct my_data, refcount); + + mutex_lock(&mutex); + list_del_rcu(&entry->link); + mutex_unlock(&mutex); + kfree_rcu(entry, rhead); +} + +static void put_entry(struct my_data *entry) +{ + kref_put(&entry->refcount, release_entry_rcu); +} + +But note that the struct kref member needs to remain in valid memory for a +rcu grace period after release_entry_rcu was called. That can be accomplished +by using kfree_rcu(entry, rhead) as done above, or by calling synchronize_rcu() +before using kfree, but note that synchronize_rcu() may sleep for a +substantial amount of time. + + +Thomas Hellstrom -- cgit v1.2.3-59-g8ed1b From e4949f2974167f93961d58ca67cfe949af9d5e09 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Thu, 1 Nov 2012 14:45:15 +0100 Subject: drm/doc: Helpers are not a Midlayer! I'm devoting all my wrath to that fight, so don't misname it ;-) v2: Make it clear that this section talks about kms helpers. Acked-by: Laurent Pinchart Signed-off-by: Daniel Vetter Signed-off-by: Dave Airlie --- Documentation/DocBook/drm.tmpl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'Documentation') diff --git a/Documentation/DocBook/drm.tmpl b/Documentation/DocBook/drm.tmpl index c9cbb3fe0e6a..9a4c06ddebdd 100644 --- a/Documentation/DocBook/drm.tmpl +++ b/Documentation/DocBook/drm.tmpl @@ -1611,10 +1611,10 @@ void intel_crt_init(struct drm_device *dev) - + - Mid-layer Helper Functions + Mode Setting Helper Functions The CRTC, encoder and connector functions provided by the drivers implement the DRM API. They're called by the DRM core and ioctl handlers -- cgit v1.2.3-59-g8ed1b From 0d4ed4c8f937a0b1aa1cb60e16a41e2f7b0123ca Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Thu, 1 Nov 2012 14:45:16 +0100 Subject: drm/doc: integrate crtc helper api into docbook - Add the missing doc for drm_helper_move_panel_connectors_to_head. - Fixup any outdated stuff in existing sections. I've only looked at those kerneldoc headers that actually resulted in a complaint from the kerneldoc parser tool. v2: - Actually include the docbook snippet in the right patch. - Fix spelling fail. v3: It's now called drm_crtc_helper_set_mode, spotted by Laurent Pinchart. Acked-by: Laurent Pinchart Signed-off-by: Daniel Vetter Signed-off-by: Dave Airlie --- Documentation/DocBook/drm.tmpl | 4 +++ drivers/gpu/drm/drm_crtc_helper.c | 66 +++++++++++++++++++++++++-------------- 2 files changed, 46 insertions(+), 24 deletions(-) (limited to 'Documentation') diff --git a/Documentation/DocBook/drm.tmpl b/Documentation/DocBook/drm.tmpl index 9a4c06ddebdd..b501aa7528d1 100644 --- a/Documentation/DocBook/drm.tmpl +++ b/Documentation/DocBook/drm.tmpl @@ -2096,6 +2096,10 @@ void intel_crt_init(struct drm_device *dev) + + Modeset Helper Functions Reference +!Edrivers/gpu/drm/drm_crtc_helper.c + diff --git a/drivers/gpu/drm/drm_crtc_helper.c b/drivers/gpu/drm/drm_crtc_helper.c index 1fe719fb32e8..7b2d378b2576 100644 --- a/drivers/gpu/drm/drm_crtc_helper.c +++ b/drivers/gpu/drm/drm_crtc_helper.c @@ -39,6 +39,17 @@ #include #include +/** + * drm_helper_move_panel_connectors_to_head() - move panels to the front in the + * connector list + * @dev: drm device to operate on + * + * Some userspace presumes that the first connected connector is the main + * display, where it's supposed to display e.g. the login screen. For + * laptops, this should be the main panel. Use this function to sort all + * (eDP/LVDS) panels to the front of the connector list, instead of + * painstakingly trying to initialize them in the right order. + */ void drm_helper_move_panel_connectors_to_head(struct drm_device *dev) { struct drm_connector *connector, *tmp; @@ -82,22 +93,21 @@ static void drm_mode_validate_flag(struct drm_connector *connector, /** * drm_helper_probe_single_connector_modes - get complete set of display modes - * @dev: DRM device + * @connector: connector to probe * @maxX: max width for modes * @maxY: max height for modes * * LOCKING: * Caller must hold mode config lock. * - * Based on @dev's mode_config layout, scan all the connectors and try to detect - * modes on them. Modes will first be added to the connector's probed_modes - * list, then culled (based on validity and the @maxX, @maxY parameters) and - * put into the normal modes list. - * - * Intended to be used either at bootup time or when major configuration - * changes have occurred. + * Based on the helper callbacks implemented by @connector try to detect all + * valid modes. Modes will first be added to the connector's probed_modes list, + * then culled (based on validity and the @maxX, @maxY parameters) and put into + * the normal modes list. * - * FIXME: take into account monitor limits + * Intended to be use as a generic implementation of the ->probe() @connector + * callback for drivers that use the crtc helpers for output mode filtering and + * detection. * * RETURNS: * Number of modes found on @connector. @@ -348,17 +358,24 @@ drm_crtc_prepare_encoders(struct drm_device *dev) } /** - * drm_crtc_set_mode - set a mode + * drm_crtc_helper_set_mode - internal helper to set a mode * @crtc: CRTC to program * @mode: mode to use * @x: horizontal offset into the surface * @y: vertical offset into the surface + * @old_fb: old framebuffer, for cleanup * * LOCKING: * Caller must hold mode config lock. * * Try to set @mode on @crtc. Give @crtc and its associated connectors a chance - * to fixup or reject the mode prior to trying to set it. + * to fixup or reject the mode prior to trying to set it. This is an internal + * helper that drivers could e.g. use to update properties that require the + * entire output pipe to be disabled and re-enabled in a new configuration. For + * example for changing whether audio is enabled on a hdmi link or for changing + * panel fitter or dither attributes. It is also called by the + * drm_crtc_helper_set_config() helper function to drive the mode setting + * sequence. * * RETURNS: * True if the mode was set successfully, or false otherwise. @@ -514,20 +531,19 @@ drm_crtc_helper_disable(struct drm_crtc *crtc) /** * drm_crtc_helper_set_config - set a new config from userspace - * @crtc: CRTC to setup - * @crtc_info: user provided configuration - * @new_mode: new mode to set - * @connector_set: set of connectors for the new config - * @fb: new framebuffer + * @set: mode set configuration * * LOCKING: * Caller must hold mode config lock. * - * Setup a new configuration, provided by the user in @crtc_info, and enable - * it. + * Setup a new configuration, provided by the upper layers (either an ioctl call + * from userspace or internally e.g. from the fbdev suppport code) in @set, and + * enable it. This is the main helper functions for drivers that implement + * kernel mode setting with the crtc helper functions and the assorted + * ->prepare(), ->modeset() and ->commit() helper callbacks. * * RETURNS: - * Zero. (FIXME) + * Returns 0 on success, -ERRNO on failure. */ int drm_crtc_helper_set_config(struct drm_mode_set *set) { @@ -823,12 +839,14 @@ static int drm_helper_choose_crtc_dpms(struct drm_crtc *crtc) } /** - * drm_helper_connector_dpms - * @connector affected connector - * @mode DPMS mode + * drm_helper_connector_dpms() - connector dpms helper implementation + * @connector: affected connector + * @mode: DPMS mode * - * Calls the low-level connector DPMS function, then - * calls appropriate encoder and crtc DPMS functions as well + * This is the main helper function provided by the crtc helper framework for + * implementing the DPMS connector attribute. It computes the new desired DPMS + * state for all encoders and crtcs in the output mesh and calls the ->dpms() + * callback provided by the driver appropriately. */ void drm_helper_connector_dpms(struct drm_connector *connector, int mode) { -- cgit v1.2.3-59-g8ed1b From d0ddc0338af8265e65bd5e4ca72e3d8e61e41d8d Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Thu, 1 Nov 2012 14:45:17 +0100 Subject: drm/doc: integrate fb helper reference into docs Again only minimal changes to make kerneldoc no longer shout. Plus a little introduction in the form of a inline DOC: section to quickly explain what this is all about. v2: Fixup spelling fail. Acked-by: Laurent Pinchart Signed-off-by: Daniel Vetter Signed-off-by: Dave Airlie --- Documentation/DocBook/drm.tmpl | 5 +++++ drivers/gpu/drm/drm_fb_helper.c | 19 +++++++++++++++---- 2 files changed, 20 insertions(+), 4 deletions(-) (limited to 'Documentation') diff --git a/Documentation/DocBook/drm.tmpl b/Documentation/DocBook/drm.tmpl index b501aa7528d1..cb8024e62dfc 100644 --- a/Documentation/DocBook/drm.tmpl +++ b/Documentation/DocBook/drm.tmpl @@ -2100,6 +2100,11 @@ void intel_crt_init(struct drm_device *dev) Modeset Helper Functions Reference !Edrivers/gpu/drm/drm_crtc_helper.c + + fbdev Helper Functions Reference +!Pdrivers/gpu/drm/drm_fb_helper.c fbdev helpers +!Edrivers/gpu/drm/drm_fb_helper.c + diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c index 2c44af691e98..05e623a5d095 100644 --- a/drivers/gpu/drm/drm_fb_helper.c +++ b/drivers/gpu/drm/drm_fb_helper.c @@ -45,6 +45,15 @@ MODULE_LICENSE("GPL and additional rights"); static LIST_HEAD(kernel_fb_helper_list); +/** + * DOC: fbdev helpers + * + * The fb helper functions are useful to provide an fbdev on top of a drm kernel + * mode setting driver. They can be used mostly independantely from the crtc + * helper functions used by many drivers to implement the kernel mode setting + * interfaces. + */ + /* simple single crtc case helper function */ int drm_fb_helper_single_add_all_connectors(struct drm_fb_helper *fb_helper) { @@ -1302,12 +1311,14 @@ out: /** * drm_helper_initial_config - setup a sane initial connector configuration - * @dev: DRM device + * @fb_helper: fb_helper device struct + * @bpp_sel: bpp value to use for the framebuffer configuration * * LOCKING: - * Called at init time, must take mode config lock. + * Called at init time by the driver to set up the @fb_helper initial + * configuration, must take the mode config lock. * - * Scan the CRTCs and connectors and try to put together an initial setup. + * Scans the CRTCs and connectors and tries to put together an initial setup. * At the moment, this is a cloned configuration across all heads with * a new framebuffer object as the backing store. * @@ -1341,7 +1352,7 @@ EXPORT_SYMBOL(drm_fb_helper_initial_config); /** * drm_fb_helper_hotplug_event - respond to a hotplug notification by - * probing all the outputs attached to the fb. + * probing all the outputs attached to the fb * @fb_helper: the drm_fb_helper * * LOCKING: -- cgit v1.2.3-59-g8ed1b From 28164fdad85ec806f30c76fe98ed0e3abc91d2d7 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Thu, 1 Nov 2012 14:45:18 +0100 Subject: drm/doc: add new dp helpers into drm DocBook I didn't bother with documenting the really trivial new "extract something from dpcd" helpers, but the i2c over aux ch is now documented a bit. v2: Clarify the comment for i2c_dp_aux_add_bus a bit. v3: Fix more spelling fail spotted by Laurent Pinchart. Acked-by: Laurent Pinchart Signed-off-by: Daniel Vetter Signed-off-by: Dave Airlie --- Documentation/DocBook/drm.tmpl | 6 ++++++ drivers/gpu/drm/drm_dp_helper.c | 21 +++++++++++++++++++++ include/drm/drm_dp_helper.h | 8 ++++++++ 3 files changed, 35 insertions(+) (limited to 'Documentation') diff --git a/Documentation/DocBook/drm.tmpl b/Documentation/DocBook/drm.tmpl index cb8024e62dfc..4ee2304f82f9 100644 --- a/Documentation/DocBook/drm.tmpl +++ b/Documentation/DocBook/drm.tmpl @@ -2105,6 +2105,12 @@ void intel_crt_init(struct drm_device *dev) !Pdrivers/gpu/drm/drm_fb_helper.c fbdev helpers !Edrivers/gpu/drm/drm_fb_helper.c + + Display Port Helper Functions Reference +!Pdrivers/gpu/drm/drm_dp_helper.c dp helpers +!Iinclude/drm/drm_dp_helper.h +!Edrivers/gpu/drm/drm_dp_helper.c + diff --git a/drivers/gpu/drm/drm_dp_helper.c b/drivers/gpu/drm/drm_dp_helper.c index 3c4cccd0d753..89e196627160 100644 --- a/drivers/gpu/drm/drm_dp_helper.c +++ b/drivers/gpu/drm/drm_dp_helper.c @@ -30,6 +30,15 @@ #include #include +/** + * DOC: dp helpers + * + * These functions contain some common logic and helpers at various abstraction + * levels to deal with Display Port sink devices and related things like DP aux + * channel transfers, EDID reading over DP aux channels, decoding certain DPCD + * blocks, ... + */ + /* Run a single AUX_CH I2C transaction, writing/reading data as necessary */ static int i2c_algo_dp_aux_transaction(struct i2c_adapter *adapter, int mode, @@ -193,6 +202,18 @@ i2c_dp_aux_prepare_bus(struct i2c_adapter *adapter) return 0; } +/** + * i2c_dp_aux_add_bus() - register an i2c adapter using the aux ch helper + * @adapter: i2c adapter to register + * + * This registers an i2c adapater that uses dp aux channel as it's underlaying + * transport. The driver needs to fill out the &i2c_algo_dp_aux_data structure + * and store it in the algo_data member of the @adapter argument. This will be + * used by the i2c over dp aux algorithm to drive the hardware. + * + * RETURNS: + * 0 on success, -ERRNO on failure. + */ int i2c_dp_aux_add_bus(struct i2c_adapter *adapter) { diff --git a/include/drm/drm_dp_helper.h b/include/drm/drm_dp_helper.h index c09d36741c94..e8e1417af3d9 100644 --- a/include/drm/drm_dp_helper.h +++ b/include/drm/drm_dp_helper.h @@ -312,6 +312,14 @@ #define MODE_I2C_READ 4 #define MODE_I2C_STOP 8 +/** + * struct i2c_algo_dp_aux_data - driver interface structure for i2c over dp + * aux algorithm + * @running: set by the algo indicating whether an i2c is ongoing or whether + * the i2c bus is quiescent + * @address: i2c target address for the currently ongoing transfer + * @aux_ch: driver callback to transfer a single byte of the i2c payload + */ struct i2c_algo_dp_aux_data { bool running; u16 address; -- cgit v1.2.3-59-g8ed1b From af0ba001d208e117b5f4e4f504672b42a664a7f7 Mon Sep 17 00:00:00 2001 From: "Philip, Avinash" Date: Tue, 27 Nov 2012 14:18:06 +0530 Subject: pwm: Add TI PWM subsystem driver In some platforms (like am33xx), PWM sub modules (ECAP, EHRPWM, EQEP) are integrated to PWM subsystem. These PWM submodules has resources shared and only one register bit-field is provided to control module/clock enable/disable, makes it difficult to handle common resources from independent PWMSS submodule drivers. So the solution here implemented in this patch is, to create driver for PWMSS and take the role of parent driver for PWM submodules. PWMSS parent driver enumerates all the child nodes under PWMSS module. Also symbol "pwmss_submodule_state_change" exported to enable clock gating for individual PWMSS submodules, and submodule drivers has to enable clock gating from their drivers. As this is only supported during DT boot, the parent/child relationship is created and populated in DT execution flow. The only required change is inside DTS file, making EHRPWM & ECAP as a child to PWMSS node. Signed-off-by: Philip, Avinash Signed-off-by: Thierry Reding --- .../devicetree/bindings/pwm/pwm-tipwmss.txt | 31 +++++ drivers/pwm/Kconfig | 11 ++ drivers/pwm/Makefile | 1 + drivers/pwm/pwm-tipwmss.c | 139 +++++++++++++++++++++ drivers/pwm/pwm-tipwmss.h | 39 ++++++ 5 files changed, 221 insertions(+) create mode 100644 Documentation/devicetree/bindings/pwm/pwm-tipwmss.txt create mode 100644 drivers/pwm/pwm-tipwmss.c create mode 100644 drivers/pwm/pwm-tipwmss.h (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/pwm/pwm-tipwmss.txt b/Documentation/devicetree/bindings/pwm/pwm-tipwmss.txt new file mode 100644 index 000000000000..f7eae77f8354 --- /dev/null +++ b/Documentation/devicetree/bindings/pwm/pwm-tipwmss.txt @@ -0,0 +1,31 @@ +TI SOC based PWM Subsystem + +Required properties: +- compatible: Must be "ti,am33xx-pwmss"; +- reg: physical base address and size of the registers map. +- address-cells: Specify the number of u32 entries needed in child nodes. + Should set to 1. +- size-cells: specify number of u32 entries needed to specify child nodes size + in reg property. Should set to 1. +- ranges: describes the address mapping of a memory-mapped bus. Should set to + physical address map of child's base address, physical address within + parent's address space and length of the address map. For am33xx, + 3 set of child register maps present, ECAP register space, EQEP + register space, EHRPWM register space. + +Also child nodes should also populated under PWMSS DT node. + +Example: +pwmss0: pwmss@48300000 { + compatible = "ti,am33xx-pwmss"; + reg = <0x48300000 0x10>; + ti,hwmods = "epwmss0"; + #address-cells = <1>; + #size-cells = <1>; + status = "disabled"; + ranges = <0x48300100 0x48300100 0x80 /* ECAP */ + 0x48300180 0x48300180 0x80 /* EQEP */ + 0x48300200 0x48300200 0x80>; /* EHRPWM */ + + /* child nodes go here */ +}; diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig index 6e556c7da81e..3dcb76d86a97 100644 --- a/drivers/pwm/Kconfig +++ b/drivers/pwm/Kconfig @@ -136,6 +136,7 @@ config PWM_TEGRA config PWM_TIECAP tristate "ECAP PWM support" depends on SOC_AM33XX + select PWM_TIPWMSS help PWM driver support for the ECAP APWM controller found on AM33XX TI SOC @@ -146,6 +147,7 @@ config PWM_TIECAP config PWM_TIEHRPWM tristate "EHRPWM PWM support" depends on SOC_AM33XX + select PWM_TIPWMSS help PWM driver support for the EHRPWM controller found on AM33XX TI SOC @@ -153,6 +155,15 @@ config PWM_TIEHRPWM To compile this driver as a module, choose M here: the module will be called pwm-tiehrpwm. +config PWM_TIPWMSS + bool + depends on SOC_AM33XX && (PWM_TIEHRPWM || PWM_TIECAP) + help + PWM Subsystem driver support for AM33xx SOC. + + PWM submodules require PWM config space access from submodule + drivers and require common parent driver support. + config PWM_TWL6030 tristate "TWL6030 PWM support" depends on TWL4030_CORE diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile index 3b3f4c9aa4ef..4fb39f8b6ba9 100644 --- a/drivers/pwm/Makefile +++ b/drivers/pwm/Makefile @@ -12,5 +12,6 @@ obj-$(CONFIG_PWM_SPEAR) += pwm-spear.o obj-$(CONFIG_PWM_TEGRA) += pwm-tegra.o obj-$(CONFIG_PWM_TIECAP) += pwm-tiecap.o obj-$(CONFIG_PWM_TIEHRPWM) += pwm-tiehrpwm.o +obj-$(CONFIG_PWM_TIPWMSS) += pwm-tipwmss.o obj-$(CONFIG_PWM_TWL6030) += pwm-twl6030.o obj-$(CONFIG_PWM_VT8500) += pwm-vt8500.o diff --git a/drivers/pwm/pwm-tipwmss.c b/drivers/pwm/pwm-tipwmss.c new file mode 100644 index 000000000000..3448a1c88590 --- /dev/null +++ b/drivers/pwm/pwm-tipwmss.c @@ -0,0 +1,139 @@ +/* + * TI PWM Subsystem driver + * + * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/ + * + * 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 +#include + +#include "pwm-tipwmss.h" + +#define PWMSS_CLKCONFIG 0x8 /* Clock gating reg */ +#define PWMSS_CLKSTATUS 0xc /* Clock gating status reg */ + +struct pwmss_info { + void __iomem *mmio_base; + struct mutex pwmss_lock; + u16 pwmss_clkconfig; +}; + +u16 pwmss_submodule_state_change(struct device *dev, int set) +{ + struct pwmss_info *info = dev_get_drvdata(dev); + u16 val; + + mutex_lock(&info->pwmss_lock); + val = readw(info->mmio_base + PWMSS_CLKCONFIG); + val |= set; + writew(val , info->mmio_base + PWMSS_CLKCONFIG); + mutex_unlock(&info->pwmss_lock); + + return readw(info->mmio_base + PWMSS_CLKSTATUS); +} +EXPORT_SYMBOL(pwmss_submodule_state_change); + +static const struct of_device_id pwmss_of_match[] = { + { .compatible = "ti,am33xx-pwmss" }, + {}, +}; +MODULE_DEVICE_TABLE(of, pwmss_of_match); + +static int pwmss_probe(struct platform_device *pdev) +{ + int ret; + struct resource *r; + struct pwmss_info *info; + struct device_node *node = pdev->dev.of_node; + + info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL); + if (!info) { + dev_err(&pdev->dev, "failed to allocate memory\n"); + return -ENOMEM; + } + + mutex_init(&info->pwmss_lock); + + r = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!r) { + dev_err(&pdev->dev, "no memory resource defined\n"); + return -ENODEV; + } + + info->mmio_base = devm_request_and_ioremap(&pdev->dev, r); + if (!info->mmio_base) + return -EADDRNOTAVAIL; + + pm_runtime_enable(&pdev->dev); + pm_runtime_get_sync(&pdev->dev); + platform_set_drvdata(pdev, info); + + /* Populate all the child nodes here... */ + ret = of_platform_populate(node, NULL, NULL, &pdev->dev); + if (ret) + dev_err(&pdev->dev, "no child node found\n"); + + return ret; +} + +static int pwmss_remove(struct platform_device *pdev) +{ + struct pwmss_info *info = platform_get_drvdata(pdev); + + pm_runtime_put_sync(&pdev->dev); + pm_runtime_disable(&pdev->dev); + mutex_destroy(&info->pwmss_lock); + return 0; +} + +static int pwmss_suspend(struct device *dev) +{ + struct pwmss_info *info = dev_get_drvdata(dev); + + info->pwmss_clkconfig = readw(info->mmio_base + PWMSS_CLKCONFIG); + pm_runtime_put_sync(dev); + return 0; +} + +static int pwmss_resume(struct device *dev) +{ + struct pwmss_info *info = dev_get_drvdata(dev); + + pm_runtime_get_sync(dev); + writew(info->pwmss_clkconfig, info->mmio_base + PWMSS_CLKCONFIG); + return 0; +} + +static SIMPLE_DEV_PM_OPS(pwmss_pm_ops, pwmss_suspend, pwmss_resume); + +static struct platform_driver pwmss_driver = { + .driver = { + .name = "pwmss", + .owner = THIS_MODULE, + .pm = &pwmss_pm_ops, + .of_match_table = pwmss_of_match, + }, + .probe = pwmss_probe, + .remove = pwmss_remove, +}; + +module_platform_driver(pwmss_driver); + +MODULE_DESCRIPTION("PWM Subsystem driver"); +MODULE_AUTHOR("Texas Instruments"); +MODULE_LICENSE("GPL"); diff --git a/drivers/pwm/pwm-tipwmss.h b/drivers/pwm/pwm-tipwmss.h new file mode 100644 index 000000000000..11f76a1e266b --- /dev/null +++ b/drivers/pwm/pwm-tipwmss.h @@ -0,0 +1,39 @@ +/* + * TI PWM Subsystem driver + * + * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/ + * + * 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. + * + */ + +#ifndef __TIPWMSS_H +#define __TIPWMSS_H + +#ifdef CONFIG_PWM_TIPWMSS +/* PWM substem clock gating */ +#define PWMSS_ECAPCLK_EN BIT(0) +#define PWMSS_ECAPCLK_STOP_REQ BIT(1) +#define PWMSS_EPWMCLK_EN BIT(8) +#define PWMSS_EPWMCLK_STOP_REQ BIT(9) + +#define PWMSS_ECAPCLK_EN_ACK BIT(0) +#define PWMSS_EPWMCLK_EN_ACK BIT(8) + +extern u16 pwmss_submodule_state_change(struct device *dev, int set); +#else +static inline u16 pwmss_submodule_state_change(struct device *dev, int set) +{ + /* return success status value */ + return 0xFFFF; +} +#endif +#endif /* __TIPWMSS_H */ -- cgit v1.2.3-59-g8ed1b From 333b08ee8c6e120d67118e4eb71c45f5c369c8a4 Mon Sep 17 00:00:00 2001 From: "Philip, Avinash" Date: Tue, 27 Nov 2012 14:18:09 +0530 Subject: pwm: tiecap: Add device-tree binding This patch 1. Add support for device-tree binding for ECAP APWM driver. 2. Set size of pwm-cells set to 3 to support PWM channel number, PWM period & polarity configuration from device tree. 3. Add enable/disable clock gating in PWM subsystem common config space. 4. When here set .owner member in platform_driver structure to THIS_MODULE. Signed-off-by: Philip, Avinash Cc: Grant Likely Cc: Rob Herring Cc: Rob Landley Signed-off-by: Thierry Reding --- .../devicetree/bindings/pwm/pwm-tiecap.txt | 23 ++++++++++++ drivers/pwm/pwm-tiecap.c | 42 +++++++++++++++++++++- 2 files changed, 64 insertions(+), 1 deletion(-) create mode 100644 Documentation/devicetree/bindings/pwm/pwm-tiecap.txt (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/pwm/pwm-tiecap.txt b/Documentation/devicetree/bindings/pwm/pwm-tiecap.txt new file mode 100644 index 000000000000..131e8c11d26f --- /dev/null +++ b/Documentation/devicetree/bindings/pwm/pwm-tiecap.txt @@ -0,0 +1,23 @@ +TI SOC ECAP based APWM controller + +Required properties: +- compatible: Must be "ti,am33xx-ecap" +- #pwm-cells: Should be 3. Number of cells being used to specify PWM property. + First cell specifies the per-chip index of the PWM to use, the second + cell is the period in nanoseconds and bit 0 in the third cell is used to + encode the polarity of PWM output. Set bit 0 of the third in PWM specifier + to 1 for inverse polarity & set to 0 for normal polarity. +- reg: physical base address and size of the registers map. + +Optional properties: +- ti,hwmods: Name of the hwmod associated to the ECAP: + "ecap", being the 0-based instance number from the HW spec + +Example: + +ecap0: ecap@0 { + compatible = "ti,am33xx-ecap"; + #pwm-cells = <3>; + reg = <0x48300100 0x80>; + ti,hwmods = "ecap0"; +}; diff --git a/drivers/pwm/pwm-tiecap.c b/drivers/pwm/pwm-tiecap.c index d6d4cf05565e..0f541c5cbe98 100644 --- a/drivers/pwm/pwm-tiecap.c +++ b/drivers/pwm/pwm-tiecap.c @@ -25,6 +25,9 @@ #include #include #include +#include + +#include "pwm-tipwmss.h" /* ECAP registers and bits definitions */ #define CAP1 0x08 @@ -184,12 +187,19 @@ static const struct pwm_ops ecap_pwm_ops = { .owner = THIS_MODULE, }; +static const struct of_device_id ecap_of_match[] = { + { .compatible = "ti,am33xx-ecap" }, + {}, +}; +MODULE_DEVICE_TABLE(of, ecap_of_match); + static int __devinit ecap_pwm_probe(struct platform_device *pdev) { int ret; struct resource *r; struct clk *clk; struct ecap_pwm_chip *pc; + u16 status; pc = devm_kzalloc(&pdev->dev, sizeof(*pc), GFP_KERNEL); if (!pc) { @@ -211,6 +221,8 @@ static int __devinit ecap_pwm_probe(struct platform_device *pdev) pc->chip.dev = &pdev->dev; pc->chip.ops = &ecap_pwm_ops; + pc->chip.of_xlate = of_pwm_xlate_with_flags; + pc->chip.of_pwm_n_cells = 3; pc->chip.base = -1; pc->chip.npwm = 1; @@ -231,14 +243,40 @@ static int __devinit ecap_pwm_probe(struct platform_device *pdev) } pm_runtime_enable(&pdev->dev); + pm_runtime_get_sync(&pdev->dev); + + status = pwmss_submodule_state_change(pdev->dev.parent, + PWMSS_ECAPCLK_EN); + if (!(status & PWMSS_ECAPCLK_EN_ACK)) { + dev_err(&pdev->dev, "PWMSS config space clock enable failed\n"); + ret = -EINVAL; + goto pwmss_clk_failure; + } + + pm_runtime_put_sync(&pdev->dev); + platform_set_drvdata(pdev, pc); return 0; + +pwmss_clk_failure: + pm_runtime_put_sync(&pdev->dev); + pm_runtime_disable(&pdev->dev); + pwmchip_remove(&pc->chip); + return ret; } static int __devexit ecap_pwm_remove(struct platform_device *pdev) { struct ecap_pwm_chip *pc = platform_get_drvdata(pdev); + pm_runtime_get_sync(&pdev->dev); + /* + * Due to hardware misbehaviour, acknowledge of the stop_req + * is missing. Hence checking of the status bit skipped. + */ + pwmss_submodule_state_change(pdev->dev.parent, PWMSS_ECAPCLK_STOP_REQ); + pm_runtime_put_sync(&pdev->dev); + pm_runtime_put_sync(&pdev->dev); pm_runtime_disable(&pdev->dev); return pwmchip_remove(&pc->chip); @@ -246,7 +284,9 @@ static int __devexit ecap_pwm_remove(struct platform_device *pdev) static struct platform_driver ecap_pwm_driver = { .driver = { - .name = "ecap", + .name = "ecap", + .owner = THIS_MODULE, + .of_match_table = ecap_of_match, }, .probe = ecap_pwm_probe, .remove = __devexit_p(ecap_pwm_remove), -- cgit v1.2.3-59-g8ed1b From 53ad9e8d37031397ee141f9bf701c0fba4257b0f Mon Sep 17 00:00:00 2001 From: "Philip, Avinash" Date: Tue, 27 Nov 2012 14:18:13 +0530 Subject: pwm: tiehrpwm: Add device-tree binding This patch 1. Add support for device-tree binding for EHRWPM driver. 2. Set size of pwm-cells set to 3 to support PWM channel number, PWM period & polarity configuration from device tree. 3. Add enable/disable clock gating in PWM subsystem common config space. 4. When here set .owner member in platform_driver structure to THIS_MODULE. Signed-off-by: Philip, Avinash Cc: Grant Likely Cc: Rob Herring Cc: Rob Landley Signed-off-by: Thierry Reding --- .../devicetree/bindings/pwm/pwm-tiehrpwm.txt | 23 ++++++++++++ drivers/pwm/pwm-tiehrpwm.c | 42 +++++++++++++++++++++- 2 files changed, 64 insertions(+), 1 deletion(-) create mode 100644 Documentation/devicetree/bindings/pwm/pwm-tiehrpwm.txt (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/pwm/pwm-tiehrpwm.txt b/Documentation/devicetree/bindings/pwm/pwm-tiehrpwm.txt new file mode 100644 index 000000000000..4fc7079d822e --- /dev/null +++ b/Documentation/devicetree/bindings/pwm/pwm-tiehrpwm.txt @@ -0,0 +1,23 @@ +TI SOC EHRPWM based PWM controller + +Required properties: +- compatible : Must be "ti,am33xx-ehrpwm" +- #pwm-cells: Should be 3. Number of cells being used to specify PWM property. + First cell specifies the per-chip index of the PWM to use, the second + cell is the period in nanoseconds and bit 0 in the third cell is used to + encode the polarity of PWM output. Set bit 0 of the third in PWM specifier + to 1 for inverse polarity & set to 0 for normal polarity. +- reg: physical base address and size of the registers map. + +Optional properties: +- ti,hwmods: Name of the hwmod associated to the EHRPWM: + "ehrpwm", being the 0-based instance number from the HW spec + +Example: + +ehrpwm0: ehrpwm@0 { + compatible = "ti,am33xx-ehrpwm"; + #pwm-cells = <3>; + reg = <0x48300200 0x100>; + ti,hwmods = "ehrpwm0"; +}; diff --git a/drivers/pwm/pwm-tiehrpwm.c b/drivers/pwm/pwm-tiehrpwm.c index 565f96ad2787..ee7a8b3dbed9 100644 --- a/drivers/pwm/pwm-tiehrpwm.c +++ b/drivers/pwm/pwm-tiehrpwm.c @@ -25,6 +25,9 @@ #include #include #include +#include + +#include "pwm-tipwmss.h" /* EHRPWM registers and bits definitions */ @@ -399,12 +402,19 @@ static const struct pwm_ops ehrpwm_pwm_ops = { .owner = THIS_MODULE, }; +static const struct of_device_id ehrpwm_of_match[] = { + { .compatible = "ti,am33xx-ehrpwm" }, + {}, +}; +MODULE_DEVICE_TABLE(of, ehrpwm_of_match); + static int __devinit ehrpwm_pwm_probe(struct platform_device *pdev) { int ret; struct resource *r; struct clk *clk; struct ehrpwm_pwm_chip *pc; + u16 status; pc = devm_kzalloc(&pdev->dev, sizeof(*pc), GFP_KERNEL); if (!pc) { @@ -426,6 +436,8 @@ static int __devinit ehrpwm_pwm_probe(struct platform_device *pdev) pc->chip.dev = &pdev->dev; pc->chip.ops = &ehrpwm_pwm_ops; + pc->chip.of_xlate = of_pwm_xlate_with_flags; + pc->chip.of_pwm_n_cells = 3; pc->chip.base = -1; pc->chip.npwm = NUM_PWM_CHANNEL; @@ -453,14 +465,40 @@ static int __devinit ehrpwm_pwm_probe(struct platform_device *pdev) } pm_runtime_enable(&pdev->dev); + pm_runtime_get_sync(&pdev->dev); + + status = pwmss_submodule_state_change(pdev->dev.parent, + PWMSS_EPWMCLK_EN); + if (!(status & PWMSS_EPWMCLK_EN_ACK)) { + dev_err(&pdev->dev, "PWMSS config space clock enable failed\n"); + ret = -EINVAL; + goto pwmss_clk_failure; + } + + pm_runtime_put_sync(&pdev->dev); + platform_set_drvdata(pdev, pc); return 0; + +pwmss_clk_failure: + pm_runtime_put_sync(&pdev->dev); + pm_runtime_disable(&pdev->dev); + pwmchip_remove(&pc->chip); + return ret; } static int __devexit ehrpwm_pwm_remove(struct platform_device *pdev) { struct ehrpwm_pwm_chip *pc = platform_get_drvdata(pdev); + pm_runtime_get_sync(&pdev->dev); + /* + * Due to hardware misbehaviour, acknowledge of the stop_req + * is missing. Hence checking of the status bit skipped. + */ + pwmss_submodule_state_change(pdev->dev.parent, PWMSS_EPWMCLK_STOP_REQ); + pm_runtime_put_sync(&pdev->dev); + pm_runtime_put_sync(&pdev->dev); pm_runtime_disable(&pdev->dev); return pwmchip_remove(&pc->chip); @@ -468,7 +506,9 @@ static int __devexit ehrpwm_pwm_remove(struct platform_device *pdev) static struct platform_driver ehrpwm_pwm_driver = { .driver = { - .name = "ehrpwm", + .name = "ehrpwm", + .owner = THIS_MODULE, + .of_match_table = ehrpwm_of_match, }, .probe = ehrpwm_pwm_probe, .remove = __devexit_p(ehrpwm_pwm_remove), -- cgit v1.2.3-59-g8ed1b From 3744c26386971db1c7ecf5aa5900882deae60492 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Tue, 27 Nov 2012 11:09:57 +0100 Subject: pwm: New driver to support PWMs on TWL4030/6030 series of PMICs The driver supports the following PWM outputs: TWL4030 PWM0 and PWM1 TWL6030 PWM1 and PWM2 On TWL4030 the PWM signals are muxed. Upon requesting the PWM the driver will select the correct mux so the PWM can be used. When the PWM has been freed the original configuration is going to be restored. Signed-off-by: Peter Ujfalusi Signed-off-by: Thierry Reding --- .../devicetree/bindings/pwm/ti,twl-pwm.txt | 17 + drivers/pwm/Kconfig | 9 + drivers/pwm/Makefile | 1 + drivers/pwm/pwm-twl.c | 359 +++++++++++++++++++++ 4 files changed, 386 insertions(+) create mode 100644 Documentation/devicetree/bindings/pwm/ti,twl-pwm.txt create mode 100644 drivers/pwm/pwm-twl.c (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/pwm/ti,twl-pwm.txt b/Documentation/devicetree/bindings/pwm/ti,twl-pwm.txt new file mode 100644 index 000000000000..2943ee5fce00 --- /dev/null +++ b/Documentation/devicetree/bindings/pwm/ti,twl-pwm.txt @@ -0,0 +1,17 @@ +Texas Instruments TWL series PWM drivers + +Supported PWMs: +On TWL4030 series: PWM1 and PWM2 +On TWL6030 series: PWM0 and PWM1 + +Required properties: +- compatible: "ti,twl4030-pwm" or "ti,twl6030-pwm" +- #pwm-cells: should be 2. The first cell specifies the per-chip index + of the PWM to use and the second cell is the period in nanoseconds. + +Example: + +twl_pwm: pwm { + compatible = "ti,twl6030-pwm"; + #pwm-cells = <2>; +}; diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig index 3dcb76d86a97..e1792d5e3861 100644 --- a/drivers/pwm/Kconfig +++ b/drivers/pwm/Kconfig @@ -173,6 +173,15 @@ config PWM_TWL6030 To compile this driver as a module, choose M here: the module will be called pwm-twl6030. +config PWM_TWL + tristate "TWL4030/6030 PWM support" + depends on TWL4030_CORE + help + Generic PWM framework driver for TWL4030/6030. + + To compile this driver as a module, choose M here: the module + will be called pwm-twl. + config PWM_VT8500 tristate "vt8500 pwm support" depends on ARCH_VT8500 diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile index 4fb39f8b6ba9..3b7efa7a40aa 100644 --- a/drivers/pwm/Makefile +++ b/drivers/pwm/Makefile @@ -14,4 +14,5 @@ obj-$(CONFIG_PWM_TIECAP) += pwm-tiecap.o obj-$(CONFIG_PWM_TIEHRPWM) += pwm-tiehrpwm.o obj-$(CONFIG_PWM_TIPWMSS) += pwm-tipwmss.o obj-$(CONFIG_PWM_TWL6030) += pwm-twl6030.o +obj-$(CONFIG_PWM_TWL) += pwm-twl.o obj-$(CONFIG_PWM_VT8500) += pwm-vt8500.o diff --git a/drivers/pwm/pwm-twl.c b/drivers/pwm/pwm-twl.c new file mode 100644 index 000000000000..e65db95d5e59 --- /dev/null +++ b/drivers/pwm/pwm-twl.c @@ -0,0 +1,359 @@ +/* + * Driver for TWL4030/6030 Generic Pulse Width Modulator + * + * Copyright (C) 2012 Texas Instruments + * Author: Peter Ujfalusi + * + * 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 + +/* + * This driver handles the PWMs of TWL4030 and TWL6030. + * The TRM names for the PWMs on TWL4030 are: PWM0, PWM1 + * TWL6030 also have two PWMs named in the TRM as PWM1, PWM2 + */ + +#define TWL_PWM_MAX 0x7f + +/* Registers, bits and macro for TWL4030 */ +#define TWL4030_GPBR1_REG 0x0c +#define TWL4030_PMBR1_REG 0x0d + +/* GPBR1 register bits */ +#define TWL4030_PWMXCLK_ENABLE (1 << 0) +#define TWL4030_PWMX_ENABLE (1 << 2) +#define TWL4030_PWMX_BITS (TWL4030_PWMX_ENABLE | TWL4030_PWMXCLK_ENABLE) +#define TWL4030_PWM_TOGGLE(pwm, x) ((x) << (pwm)) + +/* PMBR1 register bits */ +#define TWL4030_GPIO6_PWM0_MUTE_MASK (0x03 << 2) +#define TWL4030_GPIO6_PWM0_MUTE_PWM0 (0x01 << 2) +#define TWL4030_GPIO7_VIBRASYNC_PWM1_MASK (0x03 << 4) +#define TWL4030_GPIO7_VIBRASYNC_PWM1_PWM1 (0x03 << 4) + +/* Register, bits and macro for TWL6030 */ +#define TWL6030_TOGGLE3_REG 0x92 + +#define TWL6030_PWMXR (1 << 0) +#define TWL6030_PWMXS (1 << 1) +#define TWL6030_PWMXEN (1 << 2) +#define TWL6030_PWM_TOGGLE(pwm, x) ((x) << (pwm * 3)) + +struct twl_pwm_chip { + struct pwm_chip chip; + struct mutex mutex; + u8 twl6030_toggle3; + u8 twl4030_pwm_mux; +}; + +static inline struct twl_pwm_chip *to_twl(struct pwm_chip *chip) +{ + return container_of(chip, struct twl_pwm_chip, chip); +} + +static int twl_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, + int duty_ns, int period_ns) +{ + int duty_cycle = DIV_ROUND_UP(duty_ns * TWL_PWM_MAX, period_ns) + 1; + u8 pwm_config[2] = { 1, 0 }; + int base, ret; + + /* + * To configure the duty period: + * On-cycle is set to 1 (the minimum allowed value) + * The off time of 0 is not configurable, so the mapping is: + * 0 -> off cycle = 2, + * 1 -> off cycle = 2, + * 2 -> off cycle = 3, + * 126 - > off cycle 127, + * 127 - > off cycle 1 + * When on cycle == off cycle the PWM will be always on + */ + if (duty_cycle == 1) + duty_cycle = 2; + else if (duty_cycle > TWL_PWM_MAX) + duty_cycle = 1; + + base = pwm->hwpwm * 3; + + pwm_config[1] = duty_cycle; + + ret = twl_i2c_write(TWL_MODULE_PWM, pwm_config, base, 2); + if (ret < 0) + dev_err(chip->dev, "%s: Failed to configure PWM\n", pwm->label); + + return ret; +} + +static int twl4030_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) +{ + struct twl_pwm_chip *twl = to_twl(chip); + int ret; + u8 val; + + mutex_lock(&twl->mutex); + ret = twl_i2c_read_u8(TWL4030_MODULE_INTBR, &val, TWL4030_GPBR1_REG); + if (ret < 0) { + dev_err(chip->dev, "%s: Failed to read GPBR1\n", pwm->label); + goto out; + } + + val |= TWL4030_PWM_TOGGLE(pwm->hwpwm, TWL4030_PWMXCLK_ENABLE); + + ret = twl_i2c_write_u8(TWL4030_MODULE_INTBR, val, TWL4030_GPBR1_REG); + if (ret < 0) + dev_err(chip->dev, "%s: Failed to enable PWM\n", pwm->label); + + val |= TWL4030_PWM_TOGGLE(pwm->hwpwm, TWL4030_PWMX_ENABLE); + + ret = twl_i2c_write_u8(TWL4030_MODULE_INTBR, val, TWL4030_GPBR1_REG); + if (ret < 0) + dev_err(chip->dev, "%s: Failed to enable PWM\n", pwm->label); + +out: + mutex_unlock(&twl->mutex); + return ret; +} + +static void twl4030_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm) +{ + struct twl_pwm_chip *twl = to_twl(chip); + int ret; + u8 val; + + mutex_lock(&twl->mutex); + ret = twl_i2c_read_u8(TWL4030_MODULE_INTBR, &val, TWL4030_GPBR1_REG); + if (ret < 0) { + dev_err(chip->dev, "%s: Failed to read GPBR1\n", pwm->label); + goto out; + } + + val &= ~TWL4030_PWM_TOGGLE(pwm->hwpwm, TWL4030_PWMX_ENABLE); + + ret = twl_i2c_write_u8(TWL4030_MODULE_INTBR, val, TWL4030_GPBR1_REG); + if (ret < 0) + dev_err(chip->dev, "%s: Failed to disable PWM\n", pwm->label); + + val &= ~TWL4030_PWM_TOGGLE(pwm->hwpwm, TWL4030_PWMXCLK_ENABLE); + + ret = twl_i2c_write_u8(TWL4030_MODULE_INTBR, val, TWL4030_GPBR1_REG); + if (ret < 0) + dev_err(chip->dev, "%s: Failed to disable PWM\n", pwm->label); + +out: + mutex_unlock(&twl->mutex); +} + +static int twl4030_pwm_request(struct pwm_chip *chip, struct pwm_device *pwm) +{ + struct twl_pwm_chip *twl = to_twl(chip); + int ret; + u8 val, mask, bits; + + if (pwm->hwpwm == 1) { + mask = TWL4030_GPIO7_VIBRASYNC_PWM1_MASK; + bits = TWL4030_GPIO7_VIBRASYNC_PWM1_PWM1; + } else { + mask = TWL4030_GPIO6_PWM0_MUTE_MASK; + bits = TWL4030_GPIO6_PWM0_MUTE_PWM0; + } + + mutex_lock(&twl->mutex); + ret = twl_i2c_read_u8(TWL4030_MODULE_INTBR, &val, TWL4030_PMBR1_REG); + if (ret < 0) { + dev_err(chip->dev, "%s: Failed to read PMBR1\n", pwm->label); + goto out; + } + + /* Save the current MUX configuration for the PWM */ + twl->twl4030_pwm_mux &= ~mask; + twl->twl4030_pwm_mux |= (val & mask); + + /* Select PWM functionality */ + val &= ~mask; + val |= bits; + + ret = twl_i2c_write_u8(TWL4030_MODULE_INTBR, val, TWL4030_PMBR1_REG); + if (ret < 0) + dev_err(chip->dev, "%s: Failed to request PWM\n", pwm->label); + +out: + mutex_unlock(&twl->mutex); + return ret; +} + +static void twl4030_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm) +{ + struct twl_pwm_chip *twl = container_of(chip, struct twl_pwm_chip, + chip); + int ret; + u8 val, mask; + + if (pwm->hwpwm == 1) + mask = TWL4030_GPIO7_VIBRASYNC_PWM1_MASK; + else + mask = TWL4030_GPIO6_PWM0_MUTE_MASK; + + mutex_lock(&twl->mutex); + ret = twl_i2c_read_u8(TWL4030_MODULE_INTBR, &val, TWL4030_PMBR1_REG); + if (ret < 0) { + dev_err(chip->dev, "%s: Failed to read PMBR1\n", pwm->label); + goto out; + } + + /* Restore the MUX configuration for the PWM */ + val &= ~mask; + val |= (twl->twl4030_pwm_mux & mask); + + ret = twl_i2c_write_u8(TWL4030_MODULE_INTBR, val, TWL4030_PMBR1_REG); + if (ret < 0) + dev_err(chip->dev, "%s: Failed to free PWM\n", pwm->label); + +out: + mutex_unlock(&twl->mutex); +} + +static int twl6030_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) +{ + struct twl_pwm_chip *twl = container_of(chip, struct twl_pwm_chip, + chip); + int ret; + u8 val; + + mutex_lock(&twl->mutex); + val = twl->twl6030_toggle3; + val |= TWL6030_PWM_TOGGLE(pwm->hwpwm, TWL6030_PWMXS | TWL6030_PWMXEN); + val &= ~TWL6030_PWM_TOGGLE(pwm->hwpwm, TWL6030_PWMXR); + + ret = twl_i2c_write_u8(TWL6030_MODULE_ID1, val, TWL6030_TOGGLE3_REG); + if (ret < 0) { + dev_err(chip->dev, "%s: Failed to enable PWM\n", pwm->label); + goto out; + } + + twl->twl6030_toggle3 = val; +out: + mutex_unlock(&twl->mutex); + return 0; +} + +static void twl6030_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm) +{ + struct twl_pwm_chip *twl = container_of(chip, struct twl_pwm_chip, + chip); + int ret; + u8 val; + + mutex_lock(&twl->mutex); + val = twl->twl6030_toggle3; + val |= TWL6030_PWM_TOGGLE(pwm->hwpwm, TWL6030_PWMXR); + val &= ~TWL6030_PWM_TOGGLE(pwm->hwpwm, TWL6030_PWMXS | TWL6030_PWMXEN); + + ret = twl_i2c_write_u8(TWL6030_MODULE_ID1, val, TWL6030_TOGGLE3_REG); + if (ret < 0) { + dev_err(chip->dev, "%s: Failed to read TOGGLE3\n", pwm->label); + goto out; + } + + val |= TWL6030_PWM_TOGGLE(pwm->hwpwm, TWL6030_PWMXS | TWL6030_PWMXEN); + + ret = twl_i2c_write_u8(TWL6030_MODULE_ID1, val, TWL6030_TOGGLE3_REG); + if (ret < 0) { + dev_err(chip->dev, "%s: Failed to disable PWM\n", pwm->label); + goto out; + } + + twl->twl6030_toggle3 = val; +out: + mutex_unlock(&twl->mutex); +} + +static const struct pwm_ops twl4030_pwm_ops = { + .config = twl_pwm_config, + .enable = twl4030_pwm_enable, + .disable = twl4030_pwm_disable, + .request = twl4030_pwm_request, + .free = twl4030_pwm_free, +}; + +static const struct pwm_ops twl6030_pwm_ops = { + .config = twl_pwm_config, + .enable = twl6030_pwm_enable, + .disable = twl6030_pwm_disable, +}; + +static int twl_pwm_probe(struct platform_device *pdev) +{ + struct twl_pwm_chip *twl; + int ret; + + twl = devm_kzalloc(&pdev->dev, sizeof(*twl), GFP_KERNEL); + if (!twl) + return -ENOMEM; + + if (twl_class_is_4030()) + twl->chip.ops = &twl4030_pwm_ops; + else + twl->chip.ops = &twl6030_pwm_ops; + + twl->chip.dev = &pdev->dev; + twl->chip.base = -1; + twl->chip.npwm = 2; + + mutex_init(&twl->mutex); + + ret = pwmchip_add(&twl->chip); + if (ret < 0) + return ret; + + platform_set_drvdata(pdev, twl); + + return 0; +} + +static int twl_pwm_remove(struct platform_device *pdev) +{ + struct twl_pwm_chip *twl = platform_get_drvdata(pdev); + + return pwmchip_remove(&twl->chip); +} + +#ifdef CONFIG_OF +static struct of_device_id twl_pwm_of_match[] = { + { .compatible = "ti,twl4030-pwm" }, + { .compatible = "ti,twl6030-pwm" }, + { }, +}; +MODULE_DEVICE_TABLE(of, twl_pwm_of_match); +#endif + +static struct platform_driver twl_pwm_driver = { + .driver = { + .name = "twl-pwm", + .of_match_table = of_match_ptr(twl_pwm_of_match), + }, + .probe = twl_pwm_probe, + .remove = twl_pwm_remove, +}; +module_platform_driver(twl_pwm_driver); + +MODULE_AUTHOR("Peter Ujfalusi "); +MODULE_DESCRIPTION("PWM driver for TWL4030 and TWL6030"); +MODULE_ALIAS("platform:twl-pwm"); +MODULE_LICENSE("GPL"); -- cgit v1.2.3-59-g8ed1b From aa7656471df6cad1a4ed82ba888a40e9bc46aa19 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Tue, 27 Nov 2012 11:09:58 +0100 Subject: pwm: New driver to support PWM driven LEDs on TWL4030/6030 series of PMICs The driver supports the following LED outputs as generic PWM driver: TWL4030 LEDA and LEDB (PWMA and PWMB) TWL6030 Charging indicator LED (PWM LED) On TWL6030 when the PWM requested LED is configured to be controlled by SW. In this case the user can enable/disable and set the duty period freely. When the PWM has been freed, the LED driver is put back to HW control. Signed-off-by: Peter Ujfalusi Signed-off-by: Thierry Reding --- .../devicetree/bindings/pwm/ti,twl-pwmled.txt | 17 + drivers/pwm/Kconfig | 9 + drivers/pwm/Makefile | 1 + drivers/pwm/pwm-twl-led.c | 344 +++++++++++++++++++++ 4 files changed, 371 insertions(+) create mode 100644 Documentation/devicetree/bindings/pwm/ti,twl-pwmled.txt create mode 100644 drivers/pwm/pwm-twl-led.c (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/pwm/ti,twl-pwmled.txt b/Documentation/devicetree/bindings/pwm/ti,twl-pwmled.txt new file mode 100644 index 000000000000..cb64f3acc10f --- /dev/null +++ b/Documentation/devicetree/bindings/pwm/ti,twl-pwmled.txt @@ -0,0 +1,17 @@ +Texas Instruments TWL series PWM drivers connected to LED terminals + +Supported PWMs: +On TWL4030 series: PWMA and PWMB (connected to LEDA and LEDB terminals) +On TWL6030 series: LED PWM (mainly used as charging indicator LED) + +Required properties: +- compatible: "ti,twl4030-pwmled" or "ti,twl6030-pwmled" +- #pwm-cells: should be 2. The first cell specifies the per-chip index + of the PWM to use and the second cell is the period in nanoseconds. + +Example: + +twl_pwmled: pwmled { + compatible = "ti,twl6030-pwmled"; + #pwm-cells = <2>; +}; diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig index e1792d5e3861..fe788a7c5495 100644 --- a/drivers/pwm/Kconfig +++ b/drivers/pwm/Kconfig @@ -182,6 +182,15 @@ config PWM_TWL To compile this driver as a module, choose M here: the module will be called pwm-twl. +config PWM_TWL_LED + tristate "TWL4030/6030 PWM support for LED drivers" + depends on TWL4030_CORE + help + Generic PWM framework driver for TWL4030/6030 LED terminals. + + To compile this driver as a module, choose M here: the module + will be called pwm-twl-led. + config PWM_VT8500 tristate "vt8500 pwm support" depends on ARCH_VT8500 diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile index 3b7efa7a40aa..4b10133452c4 100644 --- a/drivers/pwm/Makefile +++ b/drivers/pwm/Makefile @@ -15,4 +15,5 @@ obj-$(CONFIG_PWM_TIEHRPWM) += pwm-tiehrpwm.o obj-$(CONFIG_PWM_TIPWMSS) += pwm-tipwmss.o obj-$(CONFIG_PWM_TWL6030) += pwm-twl6030.o obj-$(CONFIG_PWM_TWL) += pwm-twl.o +obj-$(CONFIG_PWM_TWL_LED) += pwm-twl-led.o obj-$(CONFIG_PWM_VT8500) += pwm-vt8500.o diff --git a/drivers/pwm/pwm-twl-led.c b/drivers/pwm/pwm-twl-led.c new file mode 100644 index 000000000000..9dfa0f3eca30 --- /dev/null +++ b/drivers/pwm/pwm-twl-led.c @@ -0,0 +1,344 @@ +/* + * Driver for TWL4030/6030 Pulse Width Modulator used as LED driver + * + * Copyright (C) 2012 Texas Instruments + * Author: Peter Ujfalusi + * + * This driver is a complete rewrite of the former pwm-twl6030.c authorded by: + * Hemanth V + * + * 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 + +/* + * This driver handles the PWM driven LED terminals of TWL4030 and TWL6030. + * To generate the signal on TWL4030: + * - LEDA uses PWMA + * - LEDB uses PWMB + * TWL6030 has one LED pin with dedicated LEDPWM + */ + +#define TWL4030_LED_MAX 0x7f +#define TWL6030_LED_MAX 0xff + +/* Registers, bits and macro for TWL4030 */ +#define TWL4030_LEDEN_REG 0x00 +#define TWL4030_PWMA_REG 0x01 + +#define TWL4030_LEDXON (1 << 0) +#define TWL4030_LEDXPWM (1 << 4) +#define TWL4030_LED_PINS (TWL4030_LEDXON | TWL4030_LEDXPWM) +#define TWL4030_LED_TOGGLE(led, x) ((x) << (led)) + +/* Register, bits and macro for TWL6030 */ +#define TWL6030_LED_PWM_CTRL1 0xf4 +#define TWL6030_LED_PWM_CTRL2 0xf5 + +#define TWL6040_LED_MODE_HW 0x00 +#define TWL6040_LED_MODE_ON 0x01 +#define TWL6040_LED_MODE_OFF 0x02 +#define TWL6040_LED_MODE_MASK 0x03 + +struct twl_pwmled_chip { + struct pwm_chip chip; + struct mutex mutex; +}; + +static inline struct twl_pwmled_chip *to_twl(struct pwm_chip *chip) +{ + return container_of(chip, struct twl_pwmled_chip, chip); +} + +static int twl4030_pwmled_config(struct pwm_chip *chip, struct pwm_device *pwm, + int duty_ns, int period_ns) +{ + int duty_cycle = DIV_ROUND_UP(duty_ns * TWL4030_LED_MAX, period_ns) + 1; + u8 pwm_config[2] = { 1, 0 }; + int base, ret; + + /* + * To configure the duty period: + * On-cycle is set to 1 (the minimum allowed value) + * The off time of 0 is not configurable, so the mapping is: + * 0 -> off cycle = 2, + * 1 -> off cycle = 2, + * 2 -> off cycle = 3, + * 126 - > off cycle 127, + * 127 - > off cycle 1 + * When on cycle == off cycle the PWM will be always on + */ + if (duty_cycle == 1) + duty_cycle = 2; + else if (duty_cycle > TWL4030_LED_MAX) + duty_cycle = 1; + + base = pwm->hwpwm * 2 + TWL4030_PWMA_REG; + + pwm_config[1] = duty_cycle; + + ret = twl_i2c_write(TWL4030_MODULE_LED, pwm_config, base, 2); + if (ret < 0) + dev_err(chip->dev, "%s: Failed to configure PWM\n", pwm->label); + + return ret; +} + +static int twl4030_pwmled_enable(struct pwm_chip *chip, struct pwm_device *pwm) +{ + struct twl_pwmled_chip *twl = to_twl(chip); + int ret; + u8 val; + + mutex_lock(&twl->mutex); + ret = twl_i2c_read_u8(TWL4030_MODULE_LED, &val, TWL4030_LEDEN_REG); + if (ret < 0) { + dev_err(chip->dev, "%s: Failed to read LEDEN\n", pwm->label); + goto out; + } + + val |= TWL4030_LED_TOGGLE(pwm->hwpwm, TWL4030_LED_PINS); + + ret = twl_i2c_write_u8(TWL4030_MODULE_LED, val, TWL4030_LEDEN_REG); + if (ret < 0) + dev_err(chip->dev, "%s: Failed to enable PWM\n", pwm->label); + +out: + mutex_unlock(&twl->mutex); + return ret; +} + +static void twl4030_pwmled_disable(struct pwm_chip *chip, + struct pwm_device *pwm) +{ + struct twl_pwmled_chip *twl = to_twl(chip); + int ret; + u8 val; + + mutex_lock(&twl->mutex); + ret = twl_i2c_read_u8(TWL4030_MODULE_LED, &val, TWL4030_LEDEN_REG); + if (ret < 0) { + dev_err(chip->dev, "%s: Failed to read LEDEN\n", pwm->label); + goto out; + } + + val &= ~TWL4030_LED_TOGGLE(pwm->hwpwm, TWL4030_LED_PINS); + + ret = twl_i2c_write_u8(TWL4030_MODULE_LED, val, TWL4030_LEDEN_REG); + if (ret < 0) + dev_err(chip->dev, "%s: Failed to disable PWM\n", pwm->label); + +out: + mutex_unlock(&twl->mutex); +} + +static int twl6030_pwmled_config(struct pwm_chip *chip, struct pwm_device *pwm, + int duty_ns, int period_ns) +{ + int duty_cycle = (duty_ns * TWL6030_LED_MAX) / period_ns; + u8 on_time; + int ret; + + on_time = duty_cycle & 0xff; + + ret = twl_i2c_write_u8(TWL6030_MODULE_ID1, on_time, + TWL6030_LED_PWM_CTRL1); + if (ret < 0) + dev_err(chip->dev, "%s: Failed to configure PWM\n", pwm->label); + + return ret; +} + +static int twl6030_pwmled_enable(struct pwm_chip *chip, struct pwm_device *pwm) +{ + struct twl_pwmled_chip *twl = to_twl(chip); + int ret; + u8 val; + + mutex_lock(&twl->mutex); + ret = twl_i2c_read_u8(TWL6030_MODULE_ID1, &val, TWL6030_LED_PWM_CTRL2); + if (ret < 0) { + dev_err(chip->dev, "%s: Failed to read PWM_CTRL2\n", + pwm->label); + goto out; + } + + val &= ~TWL6040_LED_MODE_MASK; + val |= TWL6040_LED_MODE_ON; + + ret = twl_i2c_write_u8(TWL6030_MODULE_ID1, val, TWL6030_LED_PWM_CTRL2); + if (ret < 0) + dev_err(chip->dev, "%s: Failed to enable PWM\n", pwm->label); + +out: + mutex_unlock(&twl->mutex); + return ret; +} + +static void twl6030_pwmled_disable(struct pwm_chip *chip, + struct pwm_device *pwm) +{ + struct twl_pwmled_chip *twl = to_twl(chip); + int ret; + u8 val; + + mutex_lock(&twl->mutex); + ret = twl_i2c_read_u8(TWL6030_MODULE_ID1, &val, TWL6030_LED_PWM_CTRL2); + if (ret < 0) { + dev_err(chip->dev, "%s: Failed to read PWM_CTRL2\n", + pwm->label); + goto out; + } + + val &= ~TWL6040_LED_MODE_MASK; + val |= TWL6040_LED_MODE_OFF; + + ret = twl_i2c_write_u8(TWL6030_MODULE_ID1, val, TWL6030_LED_PWM_CTRL2); + if (ret < 0) + dev_err(chip->dev, "%s: Failed to disable PWM\n", pwm->label); + +out: + mutex_unlock(&twl->mutex); +} + +static int twl6030_pwmled_request(struct pwm_chip *chip, struct pwm_device *pwm) +{ + struct twl_pwmled_chip *twl = to_twl(chip); + int ret; + u8 val; + + mutex_lock(&twl->mutex); + ret = twl_i2c_read_u8(TWL6030_MODULE_ID1, &val, TWL6030_LED_PWM_CTRL2); + if (ret < 0) { + dev_err(chip->dev, "%s: Failed to read PWM_CTRL2\n", + pwm->label); + goto out; + } + + val &= ~TWL6040_LED_MODE_MASK; + val |= TWL6040_LED_MODE_OFF; + + ret = twl_i2c_write_u8(TWL6030_MODULE_ID1, val, TWL6030_LED_PWM_CTRL2); + if (ret < 0) + dev_err(chip->dev, "%s: Failed to request PWM\n", pwm->label); + +out: + mutex_unlock(&twl->mutex); + return ret; +} + +static void twl6030_pwmled_free(struct pwm_chip *chip, struct pwm_device *pwm) +{ + struct twl_pwmled_chip *twl = to_twl(chip); + int ret; + u8 val; + + mutex_lock(&twl->mutex); + ret = twl_i2c_read_u8(TWL6030_MODULE_ID1, &val, TWL6030_LED_PWM_CTRL2); + if (ret < 0) { + dev_err(chip->dev, "%s: Failed to read PWM_CTRL2\n", + pwm->label); + goto out; + } + + val &= ~TWL6040_LED_MODE_MASK; + val |= TWL6040_LED_MODE_HW; + + ret = twl_i2c_write_u8(TWL6030_MODULE_ID1, val, TWL6030_LED_PWM_CTRL2); + if (ret < 0) + dev_err(chip->dev, "%s: Failed to free PWM\n", pwm->label); + +out: + mutex_unlock(&twl->mutex); +} + +static const struct pwm_ops twl4030_pwmled_ops = { + .enable = twl4030_pwmled_enable, + .disable = twl4030_pwmled_disable, + .config = twl4030_pwmled_config, +}; + +static const struct pwm_ops twl6030_pwmled_ops = { + .enable = twl6030_pwmled_enable, + .disable = twl6030_pwmled_disable, + .config = twl6030_pwmled_config, + .request = twl6030_pwmled_request, + .free = twl6030_pwmled_free, +}; + +static int twl_pwmled_probe(struct platform_device *pdev) +{ + struct twl_pwmled_chip *twl; + int ret; + + twl = devm_kzalloc(&pdev->dev, sizeof(*twl), GFP_KERNEL); + if (!twl) + return -ENOMEM; + + if (twl_class_is_4030()) { + twl->chip.ops = &twl4030_pwmled_ops; + twl->chip.npwm = 2; + } else { + twl->chip.ops = &twl6030_pwmled_ops; + twl->chip.npwm = 1; + } + + twl->chip.dev = &pdev->dev; + twl->chip.base = -1; + + mutex_init(&twl->mutex); + + ret = pwmchip_add(&twl->chip); + if (ret < 0) + return ret; + + platform_set_drvdata(pdev, twl); + + return 0; +} + +static int twl_pwmled_remove(struct platform_device *pdev) +{ + struct twl_pwmled_chip *twl = platform_get_drvdata(pdev); + + return pwmchip_remove(&twl->chip); +} + +#ifdef CONFIG_OF +static struct of_device_id twl_pwmled_of_match[] = { + { .compatible = "ti,twl4030-pwmled" }, + { .compatible = "ti,twl6030-pwmled" }, + { }, +}; +MODULE_DEVICE_TABLE(of, twl_pwmled_of_match); +#endif + +static struct platform_driver twl_pwmled_driver = { + .driver = { + .name = "twl-pwmled", + .of_match_table = of_match_ptr(twl_pwmled_of_match), + }, + .probe = twl_pwmled_probe, + .remove = twl_pwmled_remove, +}; +module_platform_driver(twl_pwmled_driver); + +MODULE_AUTHOR("Peter Ujfalusi "); +MODULE_DESCRIPTION("PWM driver for TWL4030 and TWL6030 LED outputs"); +MODULE_ALIAS("platform:twl-pwmled"); +MODULE_LICENSE("GPL"); -- cgit v1.2.3-59-g8ed1b From 2597ba763fc44e247e462177abfe92d95bc5e6d5 Mon Sep 17 00:00:00 2001 From: Donald Dutile Date: Tue, 27 Nov 2012 22:31:37 -0500 Subject: PCI: SRIOV control and status via sysfs (documentation) Add documentation of new sysfs files and new pci_driver SRIOV configuration interface. [bhelgaas: changelog] Signed-off: Donald Dutile Signed-off-by: Bjorn Helgaas --- Documentation/ABI/testing/sysfs-bus-pci | 34 +++++++++++++++++++++++ Documentation/PCI/pci-iov-howto.txt | 48 ++++++++++++++++++++++++++++++--- 2 files changed, 78 insertions(+), 4 deletions(-) (limited to 'Documentation') diff --git a/Documentation/ABI/testing/sysfs-bus-pci b/Documentation/ABI/testing/sysfs-bus-pci index dff1f48d252d..1ce5ae329c04 100644 --- a/Documentation/ABI/testing/sysfs-bus-pci +++ b/Documentation/ABI/testing/sysfs-bus-pci @@ -222,3 +222,37 @@ Description: satisfied too. Reading this attribute will show the current value of d3cold_allowed bit. Writing this attribute will set the value of d3cold_allowed bit. + +What: /sys/bus/pci/devices/.../sriov_totalvfs +Date: November 2012 +Contact: Donald Dutile +Description: + This file appears when a physical PCIe device supports SR-IOV. + Userspace applications can read this file to determine the + maximum number of Virtual Functions (VFs) a PCIe physical + function (PF) can support. Typically, this is the value reported + in the PF's SR-IOV extended capability structure's TotalVFs + element. Drivers have the ability at probe time to reduce the + value read from this file via the pci_sriov_set_totalvfs() + function. + +What: /sys/bus/pci/devices/.../sriov_numvfs +Date: November 2012 +Contact: Donald Dutile +Description: + This file appears when a physical PCIe device supports SR-IOV. + Userspace applications can read and write to this file to + determine and control the enablement or disablement of Virtual + Functions (VFs) on the physical function (PF). A read of this + file will return the number of VFs that are enabled on this PF. + A number written to this file will enable the specified + number of VFs. A userspace application would typically read the + file and check that the value is zero, and then write the number + of VFs that should be enabled on the PF; the value written + should be less than or equal to the value in the sriov_totalvfs + file. A userspace application wanting to disable the VFs would + write a zero to this file. The core ensures that valid values + are written to this file, and returns errors when values are not + valid. For example, writing a 2 to this file when sriov_numvfs + is not 0 and not 2 already will return an error. Writing a 10 + when the value of sriov_totalvfs is 8 will return an error. diff --git a/Documentation/PCI/pci-iov-howto.txt b/Documentation/PCI/pci-iov-howto.txt index fc73ef5d65b8..cfaca7e69893 100644 --- a/Documentation/PCI/pci-iov-howto.txt +++ b/Documentation/PCI/pci-iov-howto.txt @@ -2,6 +2,9 @@ Copyright (C) 2009 Intel Corporation Yu Zhao + Update: November 2012 + -- sysfs-based SRIOV enable-/disable-ment + Donald Dutile 1. Overview @@ -24,10 +27,21 @@ real existing PCI device. 2.1 How can I enable SR-IOV capability -The device driver (PF driver) will control the enabling and disabling -of the capability via API provided by SR-IOV core. If the hardware -has SR-IOV capability, loading its PF driver would enable it and all -VFs associated with the PF. +Multiple methods are available for SR-IOV enablement. +In the first method, the device driver (PF driver) will control the +enabling and disabling of the capability via API provided by SR-IOV core. +If the hardware has SR-IOV capability, loading its PF driver would +enable it and all VFs associated with the PF. Some PF drivers require +a module parameter to be set to determine the number of VFs to enable. +In the second method, a write to the sysfs file sriov_numvfs will +enable and disable the VFs associated with a PCIe PF. This method +enables per-PF, VF enable/disable values versus the first method, +which applies to all PFs of the same device. Additionally, the +PCI SRIOV core support ensures that enable/disable operations are +valid to reduce duplication in multiple drivers for the same +checks, e.g., check numvfs == 0 if enabling VFs, ensure +numvfs <= totalvfs. +The second method is the recommended method for new/future VF devices. 2.2 How can I use the Virtual Functions @@ -40,13 +54,22 @@ requires device driver that is same as a normal PCI device's. 3.1 SR-IOV API To enable SR-IOV capability: +(a) For the first method, in the driver: int pci_enable_sriov(struct pci_dev *dev, int nr_virtfn); 'nr_virtfn' is number of VFs to be enabled. +(b) For the second method, from sysfs: + echo 'nr_virtfn' > \ + /sys/bus/pci/devices//sriov_numvfs To disable SR-IOV capability: +(a) For the first method, in the driver: void pci_disable_sriov(struct pci_dev *dev); +(b) For the second method, from sysfs: + echo 0 > \ + /sys/bus/pci/devices//sriov_numvfs To notify SR-IOV core of Virtual Function Migration: +(a) In the driver: irqreturn_t pci_sriov_migration(struct pci_dev *dev); 3.2 Usage example @@ -88,6 +111,22 @@ static void dev_shutdown(struct pci_dev *dev) ... } +static int dev_sriov_configure(struct pci_dev *dev, int numvfs) +{ + if (numvfs > 0) { + ... + pci_enable_sriov(dev, numvfs); + ... + return numvfs; + } + if (numvfs == 0) { + .... + pci_disable_sriov(dev); + ... + return 0; + } +} + static struct pci_driver dev_driver = { .name = "SR-IOV Physical Function driver", .id_table = dev_id_table, @@ -96,4 +135,5 @@ static struct pci_driver dev_driver = { .suspend = dev_suspend, .resume = dev_resume, .shutdown = dev_shutdown, + .sriov_configure = dev_sriov_configure, }; -- cgit v1.2.3-59-g8ed1b From 1c08e7e3b2acc9f78bb5f478ffc7999007e309fc Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Wed, 28 Nov 2012 11:35:43 +0100 Subject: ASoC: omap-abe-twl6040: Use "ti,jack-detection" DT property as boolean Handle the jack detection inforamtion as bool from devicetree. Signed-off-by: Peter Ujfalusi Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/sound/omap-abe-twl6040.txt | 4 ++-- sound/soc/omap/omap-abe-twl6040.c | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/sound/omap-abe-twl6040.txt b/Documentation/devicetree/bindings/sound/omap-abe-twl6040.txt index 65dec876cb2d..fd40c852d7c7 100644 --- a/Documentation/devicetree/bindings/sound/omap-abe-twl6040.txt +++ b/Documentation/devicetree/bindings/sound/omap-abe-twl6040.txt @@ -12,7 +12,7 @@ Required properties: Optional properties: - ti,dmic: phandle for the OMAP dmic node if the machine have it connected -- ti,jack_detection: Need to be set to <1> if the board capable to detect jack +- ti,jack_detection: Need to be present if the board capable to detect jack insertion, removal. Available audio endpoints for the audio-routing table: @@ -59,7 +59,7 @@ sound { compatible = "ti,abe-twl6040"; ti,model = "SDP4430"; - ti,jack-detection = <1>; + ti,jack-detection; ti,mclk-freq = <38400000>; ti,mcpdm = <&mcpdm>; diff --git a/sound/soc/omap/omap-abe-twl6040.c b/sound/soc/omap/omap-abe-twl6040.c index a57a4e68dcc6..1d6ea8609d28 100644 --- a/sound/soc/omap/omap-abe-twl6040.c +++ b/sound/soc/omap/omap-abe-twl6040.c @@ -331,8 +331,8 @@ static __devinit int omap_abe_probe(struct platform_device *pdev) num_links = 1; } - of_property_read_u32(node, "ti,jack-detection", - &priv->jack_detection); + priv->jack_detection = of_property_read_bool(node, + "ti,jack-detection"); of_property_read_u32(node, "ti,mclk-freq", &priv->mclk_freq); if (!priv->mclk_freq) { -- cgit v1.2.3-59-g8ed1b From 825e90d0a138738657509d7460d77edb28fcfdc0 Mon Sep 17 00:00:00 2001 From: Ajay Kumar Date: Sat, 13 Oct 2012 05:48:00 +0900 Subject: video: exynos_dp: device tree documentation Add documentation for the DT bindings in exynos display port driver. Signed-off-by: Ajay Kumar Acked-by: Rob Herring Signed-off-by: Jingoo Han --- .../devicetree/bindings/video/exynos_dp.txt | 80 ++++++++++++++++++++++ 1 file changed, 80 insertions(+) create mode 100644 Documentation/devicetree/bindings/video/exynos_dp.txt (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/video/exynos_dp.txt b/Documentation/devicetree/bindings/video/exynos_dp.txt new file mode 100644 index 000000000000..c60da67a5d76 --- /dev/null +++ b/Documentation/devicetree/bindings/video/exynos_dp.txt @@ -0,0 +1,80 @@ +The Exynos display port interface should be configured based on +the type of panel connected to it. + +We use two nodes: + -dp-controller node + -dptx-phy node(defined inside dp-controller node) + +For the DP-PHY initialization, we use the dptx-phy node. +Required properties for dptx-phy: + -reg: + Base address of DP PHY register. + -samsung,enable-mask: + The bit-mask used to enable/disable DP PHY. + +For the Panel initialization, we read data from dp-controller node. +Required properties for dp-controller: + -compatible: + should be "samsung,exynos5-dp". + -reg: + physical base address of the controller and length + of memory mapped region. + -interrupts: + interrupt combiner values. + -interrupt-parent: + phandle to Interrupt combiner node. + -samsung,color-space: + input video data format. + COLOR_RGB = 0, COLOR_YCBCR422 = 1, COLOR_YCBCR444 = 2 + -samsung,dynamic-range: + dynamic range for input video data. + VESA = 0, CEA = 1 + -samsung,ycbcr-coeff: + YCbCr co-efficients for input video. + COLOR_YCBCR601 = 0, COLOR_YCBCR709 = 1 + -samsung,color-depth: + number of bits per colour component. + COLOR_6 = 0, COLOR_8 = 1, COLOR_10 = 2, COLOR_12 = 3 + -samsung,link-rate: + link rate supported by the panel. + LINK_RATE_1_62GBPS = 0x6, LINK_RATE_2_70GBPS = 0x0A + -samsung,lane-count: + number of lanes supported by the panel. + LANE_COUNT1 = 1, LANE_COUNT2 = 2, LANE_COUNT4 = 4 + +Optional properties for dp-controller: + -interlaced: + interlace scan mode. + Progressive if defined, Interlaced if not defined + -vsync-active-high: + VSYNC polarity configuration. + High if defined, Low if not defined + -hsync-active-high: + HSYNC polarity configuration. + High if defined, Low if not defined + +Example: + +SOC specific portion: + dp-controller { + compatible = "samsung,exynos5-dp"; + reg = <0x145b0000 0x10000>; + interrupts = <10 3>; + interrupt-parent = <&combiner>; + + dptx-phy { + reg = <0x10040720>; + samsung,enable-mask = <1>; + }; + + }; + +Board Specific portion: + dp-controller { + samsung,color-space = <0>; + samsung,dynamic-range = <0>; + samsung,ycbcr-coeff = <0>; + samsung,color-depth = <1>; + samsung,link-rate = <0x0a>; + samsung,lane-count = <4>; + }; -- cgit v1.2.3-59-g8ed1b From 4b9347dcbe3b426d19bda99b2061a2d1fe5a2dce Mon Sep 17 00:00:00 2001 From: Marek Szyprowski Date: Mon, 15 Oct 2012 16:03:51 +0200 Subject: common: DMA-mapping: add DMA_ATTR_FORCE_CONTIGUOUS attribute This patch adds DMA_ATTR_FORCE_CONTIGUOUS attribute to the DMA-mapping subsystem. By default DMA-mapping subsystem is allowed to assemble the buffer allocated by dma_alloc_attrs() function from individual pages if it can be mapped as contiguous chunk into device dma address space. By specifing this attribute the allocated buffer is forced to be contiguous also in physical memory. Signed-off-by: Marek Szyprowski --- Documentation/DMA-attributes.txt | 9 +++++++++ include/linux/dma-attrs.h | 1 + 2 files changed, 10 insertions(+) (limited to 'Documentation') diff --git a/Documentation/DMA-attributes.txt b/Documentation/DMA-attributes.txt index f50309081ac7..e59480db9ee0 100644 --- a/Documentation/DMA-attributes.txt +++ b/Documentation/DMA-attributes.txt @@ -91,3 +91,12 @@ transferred to 'device' domain. This attribute can be also used for dma_unmap_{single,page,sg} functions family to force buffer to stay in device domain after releasing a mapping for it. Use this attribute with care! + +DMA_ATTR_FORCE_CONTIGUOUS +------------------------- + +By default DMA-mapping subsystem is allowed to assemble the buffer +allocated by dma_alloc_attrs() function from individual pages if it can +be mapped as contiguous chunk into device dma address space. By +specifing this attribute the allocated buffer is forced to be contiguous +also in physical memory. diff --git a/include/linux/dma-attrs.h b/include/linux/dma-attrs.h index f83f793223ff..c8e1831d7572 100644 --- a/include/linux/dma-attrs.h +++ b/include/linux/dma-attrs.h @@ -17,6 +17,7 @@ enum dma_attr { DMA_ATTR_NON_CONSISTENT, DMA_ATTR_NO_KERNEL_MAPPING, DMA_ATTR_SKIP_CPU_SYNC, + DMA_ATTR_FORCE_CONTIGUOUS, DMA_ATTR_MAX, }; -- cgit v1.2.3-59-g8ed1b From 715a7119ea5aa9c30aedf99ce2c9558f7ed9e90a Mon Sep 17 00:00:00 2001 From: Tekkaman Ninja Date: Sun, 28 Oct 2012 03:30:20 +0000 Subject: Documentation: Fixes a word in Documentation/arm64/memory.txt Fixes a term in Documentation/arm64/memory.txt The modification is based on Catalin Marinas 's suggestion: "memory" in "ffffffc000000000 ffffffffffffffff 256GB memory" should be treated as "kernel logical memory map". Signed-off-by: Fu Wei Signed-off-by: Catalin Marinas --- Documentation/arm64/memory.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Documentation') diff --git a/Documentation/arm64/memory.txt b/Documentation/arm64/memory.txt index 4110cca96bd6..d758702fc03c 100644 --- a/Documentation/arm64/memory.txt +++ b/Documentation/arm64/memory.txt @@ -41,7 +41,7 @@ ffffffbbffff0000 ffffffbcffffffff ~2MB [guard] ffffffbffc000000 ffffffbfffffffff 64MB modules -ffffffc000000000 ffffffffffffffff 256GB memory +ffffffc000000000 ffffffffffffffff 256GB kernel logical memory map Translation table lookup with 4KB pages: -- cgit v1.2.3-59-g8ed1b From e89e29b8585379c844b03fb3aa2cca73e2bc5b26 Mon Sep 17 00:00:00 2001 From: Alban Bedel Date: Mon, 5 Nov 2012 10:55:25 -0800 Subject: Input: tca8418_keypad - add support for device tree bindings Signed-off-by: Alban Bedel Signed-off-by: Dmitry Torokhov --- .../devicetree/bindings/input/tca8418_keypad.txt | 8 +++ drivers/input/keyboard/tca8418_keypad.c | 68 +++++++++++++--------- 2 files changed, 49 insertions(+), 27 deletions(-) create mode 100644 Documentation/devicetree/bindings/input/tca8418_keypad.txt (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/input/tca8418_keypad.txt b/Documentation/devicetree/bindings/input/tca8418_keypad.txt new file mode 100644 index 000000000000..2a1538f0053f --- /dev/null +++ b/Documentation/devicetree/bindings/input/tca8418_keypad.txt @@ -0,0 +1,8 @@ + +Required properties: +- compatible: "ti,tca8418" +- reg: the I2C address +- interrupts: IRQ line number, should trigger on falling edge +- keypad,num-rows: The number of rows +- keypad,num-columns: The number of columns +- linux,keymap: Keys definitions, see keypad-matrix. diff --git a/drivers/input/keyboard/tca8418_keypad.c b/drivers/input/keyboard/tca8418_keypad.c index f1e966bdcff5..1a2894dd03ca 100644 --- a/drivers/input/keyboard/tca8418_keypad.c +++ b/drivers/input/keyboard/tca8418_keypad.c @@ -35,6 +35,7 @@ #include #include #include +#include /* TCA8418 hardware limits */ #define TCA8418_MAX_ROWS 8 @@ -116,10 +117,15 @@ static const struct i2c_device_id tca8418_id[] = { }; MODULE_DEVICE_TABLE(i2c, tca8418_id); +#ifdef CONFIG_OF +static const struct of_device_id tca8418_dt_ids[] __devinitconst = { + { .compatible = "ti,tca8418", }, + { } +}; +MODULE_DEVICE_TABLE(of, tca8418_dt_ids); +#endif + struct tca8418_keypad { - unsigned int rows; - unsigned int cols; - unsigned int keypad_mask; /* Mask for keypad col/rol regs */ unsigned int irq; unsigned int row_shift; @@ -241,7 +247,8 @@ exit: /* * Configure the TCA8418 for keypad operation */ -static int tca8418_configure(struct tca8418_keypad *keypad_data) +static int tca8418_configure(struct tca8418_keypad *keypad_data, + u32 rows, u32 cols) { int reg, error; @@ -253,9 +260,8 @@ static int tca8418_configure(struct tca8418_keypad *keypad_data) /* Assemble a mask for row and column registers */ - reg = ~(~0 << keypad_data->rows); - reg += (~(~0 << keypad_data->cols)) << 8; - keypad_data->keypad_mask = reg; + reg = ~(~0 << rows); + reg += (~(~0 << cols)) << 8; /* Set registers to keypad mode */ error |= tca8418_write_byte(keypad_data, REG_KP_GPIO1, reg); @@ -277,25 +283,36 @@ static int tca8418_keypad_probe(struct i2c_client *client, client->dev.platform_data; struct tca8418_keypad *keypad_data; struct input_dev *input; + const struct matrix_keymap_data *keymap_data = NULL; + u32 rows = 0, cols = 0; + bool rep = false; + bool irq_is_gpio = false; int error, row_shift, max_keys; /* Copy the platform data */ - if (!pdata) { - dev_dbg(&client->dev, "no platform data\n"); - return -EINVAL; + if (pdata) { + if (!pdata->keymap_data) { + dev_err(&client->dev, "no keymap data defined\n"); + return -EINVAL; + } + keymap_data = pdata->keymap_data; + rows = pdata->rows; + cols = pdata->cols; + rep = pdata->rep; + irq_is_gpio = pdata->irq_is_gpio; + } else { + struct device_node *np = client->dev.of_node; + of_property_read_u32(np, "keypad,num-rows", &rows); + of_property_read_u32(np, "keypad,num-columns", &cols); + rep = of_property_read_bool(np, "keypad,autorepeat"); } - if (!pdata->keymap_data) { - dev_err(&client->dev, "no keymap data defined\n"); - return -EINVAL; - } - - if (!pdata->rows || pdata->rows > TCA8418_MAX_ROWS) { + if (!rows || rows > TCA8418_MAX_ROWS) { dev_err(&client->dev, "invalid rows\n"); return -EINVAL; } - if (!pdata->cols || pdata->cols > TCA8418_MAX_COLS) { + if (!cols || cols > TCA8418_MAX_COLS) { dev_err(&client->dev, "invalid columns\n"); return -EINVAL; } @@ -307,8 +324,8 @@ static int tca8418_keypad_probe(struct i2c_client *client, return -ENODEV; } - row_shift = get_count_order(pdata->cols); - max_keys = pdata->rows << row_shift; + row_shift = get_count_order(cols); + max_keys = rows << row_shift; /* Allocate memory for keypad_data, keymap and input device */ keypad_data = kzalloc(sizeof(*keypad_data) + @@ -316,13 +333,11 @@ static int tca8418_keypad_probe(struct i2c_client *client, if (!keypad_data) return -ENOMEM; - keypad_data->rows = pdata->rows; - keypad_data->cols = pdata->cols; keypad_data->client = client; keypad_data->row_shift = row_shift; /* Initialize the chip or fail if chip isn't present */ - error = tca8418_configure(keypad_data); + error = tca8418_configure(keypad_data, rows, cols); if (error < 0) goto fail1; @@ -342,21 +357,20 @@ static int tca8418_keypad_probe(struct i2c_client *client, input->id.product = 0x001; input->id.version = 0x0001; - error = matrix_keypad_build_keymap(pdata->keymap_data, NULL, - pdata->rows, pdata->cols, + error = matrix_keypad_build_keymap(keymap_data, NULL, rows, cols, keypad_data->keymap, input); if (error) { dev_dbg(&client->dev, "Failed to build keymap\n"); goto fail2; } - if (pdata->rep) + if (rep) __set_bit(EV_REP, input->evbit); input_set_capability(input, EV_MSC, MSC_SCAN); input_set_drvdata(input, keypad_data); - if (pdata->irq_is_gpio) + if (irq_is_gpio) client->irq = gpio_to_irq(client->irq); error = request_threaded_irq(client->irq, NULL, tca8418_irq_handler, @@ -401,11 +415,11 @@ static int tca8418_keypad_remove(struct i2c_client *client) return 0; } - static struct i2c_driver tca8418_keypad_driver = { .driver = { .name = TCA8418_NAME, .owner = THIS_MODULE, + .of_match_table = of_match_ptr(tca8418_dt_ids), }, .probe = tca8418_keypad_probe, .remove = tca8418_keypad_remove, -- cgit v1.2.3-59-g8ed1b From 7e3a2dc52953f126103a36b33db1f57463fbbb8f Mon Sep 17 00:00:00 2001 From: Rick Jones Date: Wed, 28 Nov 2012 09:53:10 +0000 Subject: doc: make the description of how tcp_ecn works more explicit and clear Make the description of how tcp_ecn works a bit more explicit and clear. Signed-off-by: Rick Jones Signed-off-by: David S. Miller --- Documentation/networking/ip-sysctl.txt | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) (limited to 'Documentation') diff --git a/Documentation/networking/ip-sysctl.txt b/Documentation/networking/ip-sysctl.txt index 98ac0d7552a1..c6d5fee888c4 100644 --- a/Documentation/networking/ip-sysctl.txt +++ b/Documentation/networking/ip-sysctl.txt @@ -199,15 +199,16 @@ tcp_early_retrans - INTEGER Default: 2 tcp_ecn - INTEGER - Enable Explicit Congestion Notification (ECN) in TCP. ECN is only - used when both ends of the TCP flow support it. It is useful to - avoid losses due to congestion (when the bottleneck router supports - ECN). + Control use of Explicit Congestion Notification (ECN) by TCP. + ECN is used only when both ends of the TCP connection indicate + support for it. This feature is useful in avoiding losses due + to congestion by allowing supporting routers to signal + congestion before having to drop packets. Possible values are: - 0 disable ECN - 1 ECN enabled - 2 Only server-side ECN enabled. If the other end does - not support ECN, behavior is like with ECN disabled. + 0 Disable ECN. Neither initiate nor accept ECN. + 1 Always request ECN on outgoing connection attempts. + 2 Enable ECN when requested by incomming connections + but do not request ECN on outgoing connections. Default: 2 tcp_fack - BOOLEAN -- cgit v1.2.3-59-g8ed1b From 90b335fbbc316b58a0daee8ea792b5aa8903f2ae Mon Sep 17 00:00:00 2001 From: Stephen Warren Date: Tue, 27 Nov 2012 16:29:10 -0700 Subject: kbuild: centralize .dts->.dtb rule All architectures that use cmd_dtc do so in almost the same way. Create a central build rule to avoid duplication. The one difference is that most current uses of dtc build $(obj)/%.dtb from $(src)/dts/%.dts rather than building the .dtb in the same directory as the .dts file. This difference will be eliminated arch-by-arch in future patches. MIPS is the exception here; it already uses the exact same rule as the new common rule, so the duplicate is removed in this patch to avoid any conflict. arch/mips changes courtesy of Ralf Baechle. Update Documentation/kbuild to remove the explicit call to cmd_dtc from the example, now that the rule exists in a centralized location. Cc: Arnd Bergmann Cc: linux-arm-kernel@lists.infradead.org Cc: Olof Johansson Cc: Russell King Acked-by: Catalin Marinas Cc: Jonas Bonn Cc: linux@lists.openrisc.net Cc: Aurelien Jacquiot Cc: linux-c6x-dev@linux-c6x.org Cc: Mark Salter Cc: Michal Simek Cc: microblaze-uclinux@itee.uq.edu.au Cc: Chris Zankel Cc: linux-xtensa@linux-xtensa.org Cc: Max Filippov Signed-off-by: Ralf Baechle Signed-off-by: Stephen Warren Signed-off-by: Rob Herring --- Documentation/kbuild/makefiles.txt | 15 ++++++++------- arch/mips/cavium-octeon/Makefile | 3 --- arch/mips/lantiq/dts/Makefile | 3 --- arch/mips/netlogic/dts/Makefile | 3 --- scripts/Makefile.lib | 3 +++ 5 files changed, 11 insertions(+), 16 deletions(-) (limited to 'Documentation') diff --git a/Documentation/kbuild/makefiles.txt b/Documentation/kbuild/makefiles.txt index ec9ae6708691..14c3f4f1b617 100644 --- a/Documentation/kbuild/makefiles.txt +++ b/Documentation/kbuild/makefiles.txt @@ -1175,15 +1175,16 @@ When kbuild executes, the following steps are followed (roughly): in an init section in the image. Platform code *must* copy the blob to non-init memory prior to calling unflatten_device_tree(). - Example: - #arch/x86/platform/ce4100/Makefile - clean-files := *dtb.S + To use this command, simply add *.dtb into obj-y or targets, or make + some other target depend on %.dtb - DTC_FLAGS := -p 1024 - obj-y += foo.dtb.o + A central rule exists to create $(obj)/%.dtb from $(src)/%.dts; + architecture Makefiles do no need to explicitly write out that rule. - $(obj)/%.dtb: $(src)/%.dts - $(call cmd,dtc) + Example: + targets += $(dtb-y) + clean-files += *.dtb + DTC_FLAGS ?= -p 1024 --- 6.8 Custom kbuild commands diff --git a/arch/mips/cavium-octeon/Makefile b/arch/mips/cavium-octeon/Makefile index bc96e2908f14..6e927cf20df2 100644 --- a/arch/mips/cavium-octeon/Makefile +++ b/arch/mips/cavium-octeon/Makefile @@ -24,9 +24,6 @@ DTB_FILES = $(patsubst %.dts, %.dtb, $(DTS_FILES)) obj-y += $(patsubst %.dts, %.dtb.o, $(DTS_FILES)) -$(obj)/%.dtb: $(src)/%.dts FORCE - $(call if_changed_dep,dtc) - # Let's keep the .dtb files around in case we want to look at them. .SECONDARY: $(addprefix $(obj)/, $(DTB_FILES)) diff --git a/arch/mips/lantiq/dts/Makefile b/arch/mips/lantiq/dts/Makefile index 674fca45f72d..6fa72dd641b2 100644 --- a/arch/mips/lantiq/dts/Makefile +++ b/arch/mips/lantiq/dts/Makefile @@ -1,4 +1 @@ obj-$(CONFIG_DT_EASY50712) := easy50712.dtb.o - -$(obj)/%.dtb: $(obj)/%.dts - $(call if_changed,dtc) diff --git a/arch/mips/netlogic/dts/Makefile b/arch/mips/netlogic/dts/Makefile index 67ae3fe296f0..d117d46413aa 100644 --- a/arch/mips/netlogic/dts/Makefile +++ b/arch/mips/netlogic/dts/Makefile @@ -1,4 +1 @@ obj-$(CONFIG_DT_XLP_EVP) := xlp_evp.dtb.o - -$(obj)/%.dtb: $(obj)/%.dts - $(call if_changed,dtc) diff --git a/scripts/Makefile.lib b/scripts/Makefile.lib index 0be6f110cce7..bdf42fdf64c9 100644 --- a/scripts/Makefile.lib +++ b/scripts/Makefile.lib @@ -266,6 +266,9 @@ $(obj)/%.dtb.S: $(obj)/%.dtb quiet_cmd_dtc = DTC $@ cmd_dtc = $(objtree)/scripts/dtc/dtc -O dtb -o $@ -b 0 $(DTC_FLAGS) -d $(depfile) $< +$(obj)/%.dtb: $(src)/%.dts FORCE + $(call if_changed_dep,dtc) + # Bzip2 # --------------------------------------------------------------------------- -- cgit v1.2.3-59-g8ed1b From a4605a93696ee0768e55e4bce1ff7f0ee39bcf79 Mon Sep 17 00:00:00 2001 From: Bart Van Assche Date: Sun, 9 Oct 2011 14:09:07 +0200 Subject: IB/srp: Document sysfs attributes Document the sysfs attributes of the SRP initiator (ib_srp) according to the rules specified in Documentation/ABI/README. Signed-off-by: Bart Van Assche Acked-by: David Dillow Signed-off-by: Roland Dreier --- Documentation/ABI/stable/sysfs-driver-ib_srp | 156 +++++++++++++++++++++++++++ 1 file changed, 156 insertions(+) create mode 100644 Documentation/ABI/stable/sysfs-driver-ib_srp (limited to 'Documentation') diff --git a/Documentation/ABI/stable/sysfs-driver-ib_srp b/Documentation/ABI/stable/sysfs-driver-ib_srp new file mode 100644 index 000000000000..481aae95c7d1 --- /dev/null +++ b/Documentation/ABI/stable/sysfs-driver-ib_srp @@ -0,0 +1,156 @@ +What: /sys/class/infiniband_srp/srp--/add_target +Date: January 2, 2006 +KernelVersion: 2.6.15 +Contact: linux-rdma@vger.kernel.org +Description: Interface for making ib_srp connect to a new target. + One can request ib_srp to connect to a new target by writing + a comma-separated list of login parameters to this sysfs + attribute. The supported parameters are: + * id_ext, a 16-digit hexadecimal number specifying the eight + byte identifier extension in the 16-byte SRP target port + identifier. The target port identifier is sent by ib_srp + to the target in the SRP_LOGIN_REQ request. + * ioc_guid, a 16-digit hexadecimal number specifying the eight + byte I/O controller GUID portion of the 16-byte target port + identifier. + * dgid, a 32-digit hexadecimal number specifying the + destination GID. + * pkey, a four-digit hexadecimal number specifying the + InfiniBand partition key. + * service_id, a 16-digit hexadecimal number specifying the + InfiniBand service ID used to establish communication with + the SRP target. How to find out the value of the service ID + is specified in the documentation of the SRP target. + * max_sect, a decimal number specifying the maximum number of + 512-byte sectors to be transferred via a single SCSI command. + * max_cmd_per_lun, a decimal number specifying the maximum + number of outstanding commands for a single LUN. + * io_class, a hexadecimal number specifying the SRP I/O class. + Must be either 0xff00 (rev 10) or 0x0100 (rev 16a). The I/O + class defines the format of the SRP initiator and target + port identifiers. + * initiator_ext, a 16-digit hexadecimal number specifying the + identifier extension portion of the SRP initiator port + identifier. This data is sent by the initiator to the target + in the SRP_LOGIN_REQ request. + * cmd_sg_entries, a number in the range 1..255 that specifies + the maximum number of data buffer descriptors stored in the + SRP_CMD information unit itself. With allow_ext_sg=0 the + parameter cmd_sg_entries defines the maximum S/G list length + for a single SRP_CMD, and commands whose S/G list length + exceeds this limit after S/G list collapsing will fail. + * allow_ext_sg, whether ib_srp is allowed to include a partial + memory descriptor list in an SRP_CMD instead of the entire + list. If a partial memory descriptor list has been included + in an SRP_CMD the remaining memory descriptors are + communicated from initiator to target via an additional RDMA + transfer. Setting allow_ext_sg to 1 increases the maximum + amount of data that can be transferred between initiator and + target via a single SCSI command. Since not all SRP target + implementations support partial memory descriptor lists the + default value for this option is 0. + * sg_tablesize, a number in the range 1..2048 specifying the + maximum S/G list length the SCSI layer is allowed to pass to + ib_srp. Specifying a value that exceeds cmd_sg_entries is + only safe with partial memory descriptor list support enabled + (allow_ext_sg=1). + +What: /sys/class/infiniband_srp/srp--/ibdev +Date: January 2, 2006 +KernelVersion: 2.6.15 +Contact: linux-rdma@vger.kernel.org +Description: HCA name (). + +What: /sys/class/infiniband_srp/srp--/port +Date: January 2, 2006 +KernelVersion: 2.6.15 +Contact: linux-rdma@vger.kernel.org +Description: HCA port number (). + +What: /sys/class/scsi_host/host/allow_ext_sg +Date: May 19, 2011 +KernelVersion: 2.6.39 +Contact: linux-rdma@vger.kernel.org +Description: Whether ib_srp is allowed to include a partial memory + descriptor list in an SRP_CMD when communicating with an SRP + target. + +What: /sys/class/scsi_host/host/cmd_sg_entries +Date: May 19, 2011 +KernelVersion: 2.6.39 +Contact: linux-rdma@vger.kernel.org +Description: Maximum number of data buffer descriptors that may be sent to + the target in a single SRP_CMD request. + +What: /sys/class/scsi_host/host/dgid +Date: June 17, 2006 +KernelVersion: 2.6.17 +Contact: linux-rdma@vger.kernel.org +Description: InfiniBand destination GID used for communication with the SRP + target. Differs from orig_dgid if port redirection has happened. + +What: /sys/class/scsi_host/host/id_ext +Date: June 17, 2006 +KernelVersion: 2.6.17 +Contact: linux-rdma@vger.kernel.org +Description: Eight-byte identifier extension portion of the 16-byte target + port identifier. + +What: /sys/class/scsi_host/host/ioc_guid +Date: June 17, 2006 +KernelVersion: 2.6.17 +Contact: linux-rdma@vger.kernel.org +Description: Eight-byte I/O controller GUID portion of the 16-byte target + port identifier. + +What: /sys/class/scsi_host/host/local_ib_device +Date: November 29, 2006 +KernelVersion: 2.6.19 +Contact: linux-rdma@vger.kernel.org +Description: Name of the InfiniBand HCA used for communicating with the + SRP target. + +What: /sys/class/scsi_host/host/local_ib_port +Date: November 29, 2006 +KernelVersion: 2.6.19 +Contact: linux-rdma@vger.kernel.org +Description: Number of the HCA port used for communicating with the + SRP target. + +What: /sys/class/scsi_host/host/orig_dgid +Date: June 17, 2006 +KernelVersion: 2.6.17 +Contact: linux-rdma@vger.kernel.org +Description: InfiniBand destination GID specified in the parameters + written to the add_target sysfs attribute. + +What: /sys/class/scsi_host/host/pkey +Date: June 17, 2006 +KernelVersion: 2.6.17 +Contact: linux-rdma@vger.kernel.org +Description: A 16-bit number representing the InfiniBand partition key used + for communication with the SRP target. + +What: /sys/class/scsi_host/host/req_lim +Date: October 20, 2010 +KernelVersion: 2.6.36 +Contact: linux-rdma@vger.kernel.org +Description: Number of requests ib_srp can send to the target before it has + to wait for more credits. For more information see also the + SRP credit algorithm in the SRP specification. + +What: /sys/class/scsi_host/host/service_id +Date: June 17, 2006 +KernelVersion: 2.6.17 +Contact: linux-rdma@vger.kernel.org +Description: InfiniBand service ID used for establishing communication with + the SRP target. + +What: /sys/class/scsi_host/host/zero_req_lim +Date: September 20, 2006 +KernelVersion: 2.6.18 +Contact: linux-rdma@vger.kernel.org +Description: Number of times the initiator had to wait before sending a + request to the target because it ran out of credits. For more + information see also the SRP credit algorithm in the SRP + specification. -- cgit v1.2.3-59-g8ed1b From 7e1265bfe75f0ef1a9f5cfde202df68b7e35a53f Mon Sep 17 00:00:00 2001 From: Bart Van Assche Date: Sun, 9 Oct 2011 14:35:48 +0200 Subject: srp_transport: Document sysfs attributes Signed-off-by: Bart Van Assche Cc: FUJITA Tomonori Cc: Robert Jennings Acked-by: David Dillow Signed-off-by: Roland Dreier --- Documentation/ABI/stable/sysfs-transport-srp | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 Documentation/ABI/stable/sysfs-transport-srp (limited to 'Documentation') diff --git a/Documentation/ABI/stable/sysfs-transport-srp b/Documentation/ABI/stable/sysfs-transport-srp new file mode 100644 index 000000000000..7b0d4a5350f1 --- /dev/null +++ b/Documentation/ABI/stable/sysfs-transport-srp @@ -0,0 +1,12 @@ +What: /sys/class/srp_remote_ports/port-:/port_id +Date: June 27, 2007 +KernelVersion: 2.6.24 +Contact: linux-scsi@vger.kernel.org +Description: 16-byte local SRP port identifier in hexadecimal format. An + example: 4c:49:4e:55:58:20:56:49:4f:00:00:00:00:00:00:00. + +What: /sys/class/srp_remote_ports/port-:/roles +Date: June 27, 2007 +KernelVersion: 2.6.24 +Contact: linux-scsi@vger.kernel.org +Description: Role of the remote port. Either "SRP Initiator" or "SRP Target". -- cgit v1.2.3-59-g8ed1b From dc1bdbd9b8a077018d82230bc378f1bcfd8adba8 Mon Sep 17 00:00:00 2001 From: Bart Van Assche Date: Fri, 16 Sep 2011 20:41:13 +0200 Subject: IB/srp: Allow SRP disconnect through sysfs Make it possible to disconnect the IB RC connection used by the SRP protocol to communicate with a target. Have the SRP transport layer create a sysfs "delete" attribute for initiator drivers that support this functionality. Signed-off-by: Bart Van Assche Acked-by: David Dillow Cc: FUJITA Tomonori Cc: Robert Jennings Signed-off-by: Roland Dreier --- Documentation/ABI/stable/sysfs-transport-srp | 7 +++++++ drivers/infiniband/ulp/srp/ib_srp.c | 10 ++++++++++ drivers/scsi/scsi_transport_srp.c | 22 +++++++++++++++++++++- include/scsi/scsi_transport_srp.h | 8 ++++++++ 4 files changed, 46 insertions(+), 1 deletion(-) (limited to 'Documentation') diff --git a/Documentation/ABI/stable/sysfs-transport-srp b/Documentation/ABI/stable/sysfs-transport-srp index 7b0d4a5350f1..b36fb0dc13c8 100644 --- a/Documentation/ABI/stable/sysfs-transport-srp +++ b/Documentation/ABI/stable/sysfs-transport-srp @@ -1,3 +1,10 @@ +What: /sys/class/srp_remote_ports/port-:/delete +Date: June 1, 2012 +KernelVersion: 3.7 +Contact: linux-scsi@vger.kernel.org, linux-rdma@vger.kernel.org +Description: Instructs an SRP initiator to disconnect from a target and to + remove all LUNs imported from that target. + What: /sys/class/srp_remote_ports/port-:/port_id Date: June 27, 2007 KernelVersion: 2.6.24 diff --git a/drivers/infiniband/ulp/srp/ib_srp.c b/drivers/infiniband/ulp/srp/ib_srp.c index 84d9298ed989..d5088ce78290 100644 --- a/drivers/infiniband/ulp/srp/ib_srp.c +++ b/drivers/infiniband/ulp/srp/ib_srp.c @@ -549,6 +549,13 @@ static void srp_remove_work(struct work_struct *work) srp_remove_target(target); } +static void srp_rport_delete(struct srp_rport *rport) +{ + struct srp_target_port *target = rport->lld_data; + + srp_queue_remove_work(target); +} + static int srp_connect_target(struct srp_target_port *target) { int retries = 3; @@ -1958,6 +1965,8 @@ static int srp_add_target(struct srp_host *host, struct srp_target_port *target) return PTR_ERR(rport); } + rport->lld_data = target; + spin_lock(&host->target_lock); list_add_tail(&target->list, &host->target_list); spin_unlock(&host->target_lock); @@ -2524,6 +2533,7 @@ static void srp_remove_one(struct ib_device *device) } static struct srp_function_template ib_srp_transport_functions = { + .rport_delete = srp_rport_delete, }; static int __init srp_init_module(void) diff --git a/drivers/scsi/scsi_transport_srp.c b/drivers/scsi/scsi_transport_srp.c index 0d85f797141f..f379c7f3034c 100644 --- a/drivers/scsi/scsi_transport_srp.c +++ b/drivers/scsi/scsi_transport_srp.c @@ -38,7 +38,7 @@ struct srp_host_attrs { #define to_srp_host_attrs(host) ((struct srp_host_attrs *)(host)->shost_data) #define SRP_HOST_ATTRS 0 -#define SRP_RPORT_ATTRS 2 +#define SRP_RPORT_ATTRS 3 struct srp_internal { struct scsi_transport_template t; @@ -116,6 +116,24 @@ show_srp_rport_roles(struct device *dev, struct device_attribute *attr, static DEVICE_ATTR(roles, S_IRUGO, show_srp_rport_roles, NULL); +static ssize_t store_srp_rport_delete(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct srp_rport *rport = transport_class_to_srp_rport(dev); + struct Scsi_Host *shost = dev_to_shost(dev); + struct srp_internal *i = to_srp_internal(shost->transportt); + + if (i->f->rport_delete) { + i->f->rport_delete(rport); + return count; + } else { + return -ENOSYS; + } +} + +static DEVICE_ATTR(delete, S_IWUSR, NULL, store_srp_rport_delete); + static void srp_rport_release(struct device *dev) { struct srp_rport *rport = dev_to_rport(dev); @@ -309,6 +327,8 @@ srp_attach_transport(struct srp_function_template *ft) count = 0; i->rport_attrs[count++] = &dev_attr_port_id; i->rport_attrs[count++] = &dev_attr_roles; + if (ft->rport_delete) + i->rport_attrs[count++] = &dev_attr_delete; i->rport_attrs[count++] = NULL; BUG_ON(count > ARRAY_SIZE(i->rport_attrs)); diff --git a/include/scsi/scsi_transport_srp.h b/include/scsi/scsi_transport_srp.h index 9c60ca1c08c5..ff0f04ac91aa 100644 --- a/include/scsi/scsi_transport_srp.h +++ b/include/scsi/scsi_transport_srp.h @@ -14,13 +14,21 @@ struct srp_rport_identifiers { }; struct srp_rport { + /* for initiator and target drivers */ + struct device dev; u8 port_id[16]; u8 roles; + + /* for initiator drivers */ + + void *lld_data; /* LLD private data */ }; struct srp_function_template { + /* for initiator drivers */ + void (*rport_delete)(struct srp_rport *rport); /* for target drivers */ int (* tsk_mgmt_response)(struct Scsi_Host *, u64, u64, int); int (* it_nexus_response)(struct Scsi_Host *, u64, int); -- cgit v1.2.3-59-g8ed1b From 86605cfe8c7c166999bc7476b17940c68bf2f8b7 Mon Sep 17 00:00:00 2001 From: Vipul Kumar Samar Date: Mon, 26 Nov 2012 17:06:51 +0530 Subject: gpio: stmpe: Add DT support for stmpe gpio This patch allows the STMPE GPIO driver to be successfully probed and initialised when Device Tree support is enabled. Bindings are mentioned in Documentation too. Acked-by: Lee Jones Signed-off-by: Vipul Kumar Samar Signed-off-by: Viresh Kumar Signed-off-by: Linus Walleij --- Documentation/devicetree/bindings/gpio/gpio-stmpe.txt | 18 ++++++++++++++++++ drivers/gpio/gpio-stmpe.c | 10 ++++++++-- drivers/mfd/stmpe.c | 2 ++ 3 files changed, 28 insertions(+), 2 deletions(-) create mode 100644 Documentation/devicetree/bindings/gpio/gpio-stmpe.txt (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/gpio/gpio-stmpe.txt b/Documentation/devicetree/bindings/gpio/gpio-stmpe.txt new file mode 100644 index 000000000000..a0e4cf885213 --- /dev/null +++ b/Documentation/devicetree/bindings/gpio/gpio-stmpe.txt @@ -0,0 +1,18 @@ +STMPE gpio +---------- + +Required properties: + - compatible: "st,stmpe-gpio" + +Optional properties: + - st,norequest-mask: bitmask specifying which GPIOs should _not_ be requestable + due to different usage (e.g. touch, keypad) + +Node name must be stmpe_gpio and should be child node of stmpe node to which it +belongs. + +Example: + stmpe_gpio { + compatible = "st,stmpe-gpio"; + st,norequest-mask = <0x20>; //gpio 5 can't be used + }; diff --git a/drivers/gpio/gpio-stmpe.c b/drivers/gpio/gpio-stmpe.c index dce34727bbf8..522c90ee96e8 100644 --- a/drivers/gpio/gpio-stmpe.c +++ b/drivers/gpio/gpio-stmpe.c @@ -12,6 +12,7 @@ #include #include #include +#include #include /* @@ -304,6 +305,7 @@ static void stmpe_gpio_irq_remove(struct stmpe_gpio *stmpe_gpio) static int __devinit stmpe_gpio_probe(struct platform_device *pdev) { struct stmpe *stmpe = dev_get_drvdata(pdev->dev.parent); + struct device_node *np = pdev->dev.of_node; struct stmpe_gpio_platform_data *pdata; struct stmpe_gpio *stmpe_gpio; int ret; @@ -321,13 +323,17 @@ static int __devinit stmpe_gpio_probe(struct platform_device *pdev) stmpe_gpio->dev = &pdev->dev; stmpe_gpio->stmpe = stmpe; - stmpe_gpio->norequest_mask = pdata ? pdata->norequest_mask : 0; - stmpe_gpio->chip = template_chip; stmpe_gpio->chip.ngpio = stmpe->num_gpios; stmpe_gpio->chip.dev = &pdev->dev; stmpe_gpio->chip.base = pdata ? pdata->gpio_base : -1; + if (pdata) + stmpe_gpio->norequest_mask = pdata->norequest_mask; + else if (np) + of_property_read_u32(np, "st,norequest-mask", + &stmpe_gpio->norequest_mask); + if (irq >= 0) stmpe_gpio->irq_base = stmpe->irq_base + STMPE_INT_GPIO(0); else diff --git a/drivers/mfd/stmpe.c b/drivers/mfd/stmpe.c index c94f521f392c..79e88d1fd99a 100644 --- a/drivers/mfd/stmpe.c +++ b/drivers/mfd/stmpe.c @@ -294,12 +294,14 @@ static struct resource stmpe_gpio_resources[] = { static struct mfd_cell stmpe_gpio_cell = { .name = "stmpe-gpio", + .of_compatible = "st,stmpe-gpio", .resources = stmpe_gpio_resources, .num_resources = ARRAY_SIZE(stmpe_gpio_resources), }; static struct mfd_cell stmpe_gpio_cell_noirq = { .name = "stmpe-gpio", + .of_compatible = "st,stmpe-gpio", /* gpio cell resources consist of an irq only so no resources here */ }; -- cgit v1.2.3-59-g8ed1b From 817eecbf8230982ec0fbf0718e06a489b67dcbcf Mon Sep 17 00:00:00 2001 From: Hyeonjun Lim Date: Mon, 3 Dec 2012 15:31:31 +0900 Subject: doc: Update the name of profiling based on sysfs The ./kernel/ksysfs.c defines 'profiling' w/o 'profile' like "KERNEL_ATTR_RW(profiling)". We need to synchronize the content between ksys.c source and kernel doc. Signed-off-by: Hyeonjun Lim Signed-off-by: Jiri Kosina --- Documentation/ABI/testing/sysfs-profiling | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'Documentation') diff --git a/Documentation/ABI/testing/sysfs-profiling b/Documentation/ABI/testing/sysfs-profiling index b02d8b8c173a..8a8e466eb2c0 100644 --- a/Documentation/ABI/testing/sysfs-profiling +++ b/Documentation/ABI/testing/sysfs-profiling @@ -1,13 +1,13 @@ -What: /sys/kernel/profile +What: /sys/kernel/profiling Date: September 2008 Contact: Dave Hansen Description: - /sys/kernel/profile is the runtime equivalent + /sys/kernel/profiling is the runtime equivalent of the boot-time profile= option. You can get the same effect running: - echo 2 > /sys/kernel/profile + echo 2 > /sys/kernel/profiling as you would by issuing profile=2 on the boot command line. -- cgit v1.2.3-59-g8ed1b From 46d784629202c5da9be8d727988e7083fb455bf8 Mon Sep 17 00:00:00 2001 From: Vivien Didelot Date: Wed, 3 Oct 2012 16:54:07 -0400 Subject: hwmon: (ads7828) driver cleanup As there is no reliable way to identify the chip, it is preferable to remove the detect callback, to avoid misdetection. Module parameters are not worth it here, so let's get rid of them and add an ads7828_platform_data structure instead. Clean the code by removing unused macros, fixing coding style issues, avoiding function prototypes and using convenient macros such as module_i2c_driver(). Signed-off-by: Vivien Didelot Signed-off-by: Guenter Roeck --- Documentation/hwmon/ads7828 | 35 ++++-- drivers/hwmon/ads7828.c | 228 +++++++++++++--------------------- include/linux/platform_data/ads7828.h | 29 +++++ 3 files changed, 140 insertions(+), 152 deletions(-) create mode 100644 include/linux/platform_data/ads7828.h (limited to 'Documentation') diff --git a/Documentation/hwmon/ads7828 b/Documentation/hwmon/ads7828 index 2bbebe6f771f..a987c94a88e5 100644 --- a/Documentation/hwmon/ads7828 +++ b/Documentation/hwmon/ads7828 @@ -4,22 +4,33 @@ Kernel driver ads7828 Supported chips: * Texas Instruments/Burr-Brown ADS7828 Prefix: 'ads7828' - Addresses scanned: I2C 0x48, 0x49, 0x4a, 0x4b - Datasheet: Publicly available at the Texas Instruments website : + Datasheet: Publicly available at the Texas Instruments website: http://focus.ti.com/lit/ds/symlink/ads7828.pdf Authors: Steve Hardy + Vivien Didelot -Module Parameters ------------------ +Platform data +------------- -* se_input: bool (default Y) - Single ended operation - set to N for differential mode -* int_vref: bool (default Y) - Operate with the internal 2.5V reference - set to N for external reference -* vref_mv: int (default 2500) - If using an external reference, set this to the reference voltage in mV +The ads7828 driver accepts an optional ads7828_platform_data structure (defined +in include/linux/platform_data/ads7828.h). The structure fields are: + +* diff_input: (bool) Differential operation + set to true for differential mode, false for default single ended mode. + +* ext_vref: (bool) External reference + set to true if it operates with an external reference, false for default + internal reference. + +* vref_mv: (unsigned int) Voltage reference + if using an external reference, set this to the reference voltage in mV, + otherwise it will default to the internal value (2500mV). This value will be + bounded with limits accepted by the chip, described in the datasheet. + + If no structure is provided, the configuration defaults to single ended + operation and internal voltage reference (2.5V). Description ----------- @@ -34,3 +45,7 @@ where 4 differential pairs can be measured. The chip also has the facility to use an external voltage reference. This may be required if your hardware supplies the ADS7828 from a 5V supply, see the datasheet for more details. + +There is no reliable way to identify this chip, so the driver will not scan +some addresses to try to auto-detect it. That means that you will have to +statically declare the device in the platform support code. diff --git a/drivers/hwmon/ads7828.c b/drivers/hwmon/ads7828.c index 1f9e8af0f322..42914fc1436d 100644 --- a/drivers/hwmon/ads7828.c +++ b/drivers/hwmon/ads7828.c @@ -6,7 +6,7 @@ * * Written by Steve Hardy * - * Datasheet available at: http://focus.ti.com/lit/ds/symlink/ads7828.pdf + * For further information, see the Documentation/hwmon/ads7828 file. * * 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 @@ -23,63 +23,44 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ -#include -#include -#include -#include -#include +#include #include #include -#include +#include +#include +#include +#include #include +#include +#include /* The ADS7828 registers */ -#define ADS7828_NCH 8 /* 8 channels of 12-bit A-D supported */ -#define ADS7828_CMD_SD_SE 0x80 /* Single ended inputs */ -#define ADS7828_CMD_SD_DIFF 0x00 /* Differential inputs */ -#define ADS7828_CMD_PD0 0x0 /* Power Down between A-D conversions */ -#define ADS7828_CMD_PD1 0x04 /* Internal ref OFF && A-D ON */ -#define ADS7828_CMD_PD2 0x08 /* Internal ref ON && A-D OFF */ -#define ADS7828_CMD_PD3 0x0C /* Internal ref ON && A-D ON */ -#define ADS7828_INT_VREF_MV 2500 /* Internal vref is 2.5V, 2500mV */ - -/* Addresses to scan */ -static const unsigned short normal_i2c[] = { 0x48, 0x49, 0x4a, 0x4b, - I2C_CLIENT_END }; - -/* Module parameters */ -static bool se_input = 1; /* Default is SE, 0 == diff */ -static bool int_vref = 1; /* Default is internal ref ON */ -static int vref_mv = ADS7828_INT_VREF_MV; /* set if vref != 2.5V */ -module_param(se_input, bool, S_IRUGO); -module_param(int_vref, bool, S_IRUGO); -module_param(vref_mv, int, S_IRUGO); - -/* Global Variables */ -static u8 ads7828_cmd_byte; /* cmd byte without channel bits */ -static unsigned int ads7828_lsb_resol; /* resolution of the ADC sample lsb */ - -/* Each client has this additional data */ +#define ADS7828_NCH 8 /* 8 channels supported */ +#define ADS7828_CMD_SD_SE 0x80 /* Single ended inputs */ +#define ADS7828_CMD_PD1 0x04 /* Internal vref OFF && A/D ON */ +#define ADS7828_CMD_PD3 0x0C /* Internal vref ON && A/D ON */ +#define ADS7828_INT_VREF_MV 2500 /* Internal vref is 2.5V, 2500mV */ +#define ADS7828_EXT_VREF_MV_MIN 50 /* External vref min value 0.05V */ +#define ADS7828_EXT_VREF_MV_MAX 5250 /* External vref max value 5.25V */ + +/* Client specific data */ struct ads7828_data { struct device *hwmon_dev; - struct mutex update_lock; /* mutex protect updates */ - char valid; /* !=0 if following fields are valid */ - unsigned long last_updated; /* In jiffies */ - u16 adc_input[ADS7828_NCH]; /* ADS7828_NCH 12-bit samples */ + struct mutex update_lock; /* Mutex protecting updates */ + unsigned long last_updated; /* Last updated time (in jiffies) */ + u16 adc_input[ADS7828_NCH]; /* ADS7828_NCH samples */ + bool valid; /* Validity flag */ + bool diff_input; /* Differential input */ + bool ext_vref; /* External voltage reference */ + unsigned int vref_mv; /* voltage reference value */ + u8 cmd_byte; /* Command byte without channel bits */ + unsigned int lsb_resol; /* Resolution of the ADC sample LSB */ }; -/* Function declaration - necessary due to function dependencies */ -static int ads7828_detect(struct i2c_client *client, - struct i2c_board_info *info); -static int ads7828_probe(struct i2c_client *client, - const struct i2c_device_id *id); - -static inline u8 channel_cmd_byte(int ch) +/* Command byte C2,C1,C0 - see datasheet */ +static inline u8 ads7828_cmd_byte(u8 cmd, int ch) { - /* cmd byte C2,C1,C0 - see datasheet */ - u8 cmd = (((ch>>1) | (ch&0x01)<<2)<<4); - cmd |= ads7828_cmd_byte; - return cmd; + return cmd | (((ch >> 1) | (ch & 0x01) << 2) << 4); } /* Update data for the device (all 8 channels) */ @@ -96,12 +77,12 @@ static struct ads7828_data *ads7828_update_device(struct device *dev) dev_dbg(&client->dev, "Starting ads7828 update\n"); for (ch = 0; ch < ADS7828_NCH; ch++) { - u8 cmd = channel_cmd_byte(ch); + u8 cmd = ads7828_cmd_byte(data->cmd_byte, ch); data->adc_input[ch] = i2c_smbus_read_word_swapped(client, cmd); } data->last_updated = jiffies; - data->valid = 1; + data->valid = true; } mutex_unlock(&data->update_lock); @@ -110,28 +91,25 @@ static struct ads7828_data *ads7828_update_device(struct device *dev) } /* sysfs callback function */ -static ssize_t show_in(struct device *dev, struct device_attribute *da, - char *buf) +static ssize_t ads7828_show_in(struct device *dev, struct device_attribute *da, + char *buf) { struct sensor_device_attribute *attr = to_sensor_dev_attr(da); struct ads7828_data *data = ads7828_update_device(dev); - /* Print value (in mV as specified in sysfs-interface documentation) */ - return sprintf(buf, "%d\n", (data->adc_input[attr->index] * - ads7828_lsb_resol)/1000); -} + unsigned int value = DIV_ROUND_CLOSEST(data->adc_input[attr->index] * + data->lsb_resol, 1000); -#define in_reg(offset)\ -static SENSOR_DEVICE_ATTR(in##offset##_input, S_IRUGO, show_in,\ - NULL, offset) + return sprintf(buf, "%d\n", value); +} -in_reg(0); -in_reg(1); -in_reg(2); -in_reg(3); -in_reg(4); -in_reg(5); -in_reg(6); -in_reg(7); +static SENSOR_DEVICE_ATTR(in0_input, S_IRUGO, ads7828_show_in, NULL, 0); +static SENSOR_DEVICE_ATTR(in1_input, S_IRUGO, ads7828_show_in, NULL, 1); +static SENSOR_DEVICE_ATTR(in2_input, S_IRUGO, ads7828_show_in, NULL, 2); +static SENSOR_DEVICE_ATTR(in3_input, S_IRUGO, ads7828_show_in, NULL, 3); +static SENSOR_DEVICE_ATTR(in4_input, S_IRUGO, ads7828_show_in, NULL, 4); +static SENSOR_DEVICE_ATTR(in5_input, S_IRUGO, ads7828_show_in, NULL, 5); +static SENSOR_DEVICE_ATTR(in6_input, S_IRUGO, ads7828_show_in, NULL, 6); +static SENSOR_DEVICE_ATTR(in7_input, S_IRUGO, ads7828_show_in, NULL, 7); static struct attribute *ads7828_attributes[] = { &sensor_dev_attr_in0_input.dev_attr.attr, @@ -152,60 +130,9 @@ static const struct attribute_group ads7828_group = { static int ads7828_remove(struct i2c_client *client) { struct ads7828_data *data = i2c_get_clientdata(client); + hwmon_device_unregister(data->hwmon_dev); sysfs_remove_group(&client->dev.kobj, &ads7828_group); - return 0; -} - -static const struct i2c_device_id ads7828_id[] = { - { "ads7828", 0 }, - { } -}; -MODULE_DEVICE_TABLE(i2c, ads7828_id); - -/* This is the driver that will be inserted */ -static struct i2c_driver ads7828_driver = { - .class = I2C_CLASS_HWMON, - .driver = { - .name = "ads7828", - }, - .probe = ads7828_probe, - .remove = ads7828_remove, - .id_table = ads7828_id, - .detect = ads7828_detect, - .address_list = normal_i2c, -}; - -/* Return 0 if detection is successful, -ENODEV otherwise */ -static int ads7828_detect(struct i2c_client *client, - struct i2c_board_info *info) -{ - struct i2c_adapter *adapter = client->adapter; - int ch; - - /* Check we have a valid client */ - if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_READ_WORD_DATA)) - return -ENODEV; - - /* - * Now, we do the remaining detection. There is no identification - * dedicated register so attempt to sanity check using knowledge of - * the chip - * - Read from the 8 channel addresses - * - Check the top 4 bits of each result are not set (12 data bits) - */ - for (ch = 0; ch < ADS7828_NCH; ch++) { - u16 in_data; - u8 cmd = channel_cmd_byte(ch); - in_data = i2c_smbus_read_word_swapped(client, cmd); - if (in_data & 0xF000) { - pr_debug("%s : Doesn't look like an ads7828 device\n", - __func__); - return -ENODEV; - } - } - - strlcpy(info->type, "ads7828", I2C_NAME_SIZE); return 0; } @@ -213,6 +140,7 @@ static int ads7828_detect(struct i2c_client *client, static int ads7828_probe(struct i2c_client *client, const struct i2c_device_id *id) { + struct ads7828_platform_data *pdata = client->dev.platform_data; struct ads7828_data *data; int err; @@ -221,10 +149,30 @@ static int ads7828_probe(struct i2c_client *client, if (!data) return -ENOMEM; + if (pdata) { + data->diff_input = pdata->diff_input; + data->ext_vref = pdata->ext_vref; + if (data->ext_vref) + data->vref_mv = pdata->vref_mv; + } + + /* Bound Vref with min/max values if it was provided */ + if (data->vref_mv) + data->vref_mv = SENSORS_LIMIT(data->vref_mv, + ADS7828_EXT_VREF_MV_MIN, + ADS7828_EXT_VREF_MV_MAX); + else + data->vref_mv = ADS7828_INT_VREF_MV; + + data->lsb_resol = DIV_ROUND_CLOSEST(data->vref_mv * 1000, 4096); + + data->cmd_byte = data->ext_vref ? ADS7828_CMD_PD1 : ADS7828_CMD_PD3; + if (!data->diff_input) + data->cmd_byte |= ADS7828_CMD_SD_SE; + i2c_set_clientdata(client, data); mutex_init(&data->update_lock); - /* Register sysfs hooks */ err = sysfs_create_group(&client->dev.kobj, &ads7828_group); if (err) return err; @@ -232,38 +180,34 @@ static int ads7828_probe(struct i2c_client *client, data->hwmon_dev = hwmon_device_register(&client->dev); if (IS_ERR(data->hwmon_dev)) { err = PTR_ERR(data->hwmon_dev); - goto exit_remove; + goto error; } return 0; -exit_remove: +error: sysfs_remove_group(&client->dev.kobj, &ads7828_group); return err; } -static int __init sensors_ads7828_init(void) -{ - /* Initialize the command byte according to module parameters */ - ads7828_cmd_byte = se_input ? - ADS7828_CMD_SD_SE : ADS7828_CMD_SD_DIFF; - ads7828_cmd_byte |= int_vref ? - ADS7828_CMD_PD3 : ADS7828_CMD_PD1; +static const struct i2c_device_id ads7828_device_ids[] = { + { "ads7828", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, ads7828_device_ids); - /* Calculate the LSB resolution */ - ads7828_lsb_resol = (vref_mv*1000)/4096; +static struct i2c_driver ads7828_driver = { + .driver = { + .name = "ads7828", + }, - return i2c_add_driver(&ads7828_driver); -} + .id_table = ads7828_device_ids, + .probe = ads7828_probe, + .remove = ads7828_remove, +}; -static void __exit sensors_ads7828_exit(void) -{ - i2c_del_driver(&ads7828_driver); -} +module_i2c_driver(ads7828_driver); -MODULE_AUTHOR("Steve Hardy "); -MODULE_DESCRIPTION("ADS7828 driver"); MODULE_LICENSE("GPL"); - -module_init(sensors_ads7828_init); -module_exit(sensors_ads7828_exit); +MODULE_AUTHOR("Steve Hardy "); +MODULE_DESCRIPTION("Driver for TI ADS7828 A/D converter"); diff --git a/include/linux/platform_data/ads7828.h b/include/linux/platform_data/ads7828.h new file mode 100644 index 000000000000..3245f45f9d77 --- /dev/null +++ b/include/linux/platform_data/ads7828.h @@ -0,0 +1,29 @@ +/* + * TI ADS7828 A/D Converter platform data definition + * + * Copyright (c) 2012 Savoir-faire Linux Inc. + * Vivien Didelot + * + * For further information, see the Documentation/hwmon/ads7828 file. + * + * 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 _PDATA_ADS7828_H +#define _PDATA_ADS7828_H + +/** + * struct ads7828_platform_data - optional ADS7828 connectivity info + * @diff_input: Differential input mode. + * @ext_vref: Use an external voltage reference. + * @vref_mv: Voltage reference value, if external. + */ +struct ads7828_platform_data { + bool diff_input; + bool ext_vref; + unsigned int vref_mv; +}; + +#endif /* _PDATA_ADS7828_H */ -- cgit v1.2.3-59-g8ed1b From 0962e0f1a5634b63953db2c12edcff938f9d811d Mon Sep 17 00:00:00 2001 From: Guillaume Roguez Date: Wed, 3 Oct 2012 16:54:08 -0400 Subject: hwmon: (ads7828) add support for ADS7830 The ADS7830 device is almost the same as the ADS7828, except that it does 8-bit sampling, instead of 12-bit. This patch extends the ads7828 driver to support this chip. Signed-off-by: Guillaume Roguez Signed-off-by: Vivien Didelot Signed-off-by: Guenter Roeck --- Documentation/hwmon/ads7828 | 11 +++++++++-- drivers/hwmon/Kconfig | 7 ++++--- drivers/hwmon/ads7828.c | 25 +++++++++++++++++++------ 3 files changed, 32 insertions(+), 11 deletions(-) (limited to 'Documentation') diff --git a/Documentation/hwmon/ads7828 b/Documentation/hwmon/ads7828 index a987c94a88e5..f6e263e0f607 100644 --- a/Documentation/hwmon/ads7828 +++ b/Documentation/hwmon/ads7828 @@ -7,9 +7,15 @@ Supported chips: Datasheet: Publicly available at the Texas Instruments website: http://focus.ti.com/lit/ds/symlink/ads7828.pdf + * Texas Instruments ADS7830 + Prefix: 'ads7830' + Datasheet: Publicly available at the Texas Instruments website: + http://focus.ti.com/lit/ds/symlink/ads7830.pdf + Authors: Steve Hardy Vivien Didelot + Guillaume Roguez Platform data ------------- @@ -35,9 +41,10 @@ in include/linux/platform_data/ads7828.h). The structure fields are: Description ----------- -This driver implements support for the Texas Instruments ADS7828. +This driver implements support for the Texas Instruments ADS7828 and ADS7830. -This device is a 12-bit 8-channel A-D converter. +The ADS7828 device is a 12-bit 8-channel A/D converter, while the ADS7830 does +8-bit sampling. It can operate in single ended mode (8 +ve inputs) or in differential mode, where 4 differential pairs can be measured. diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index c4633de64465..9e3d977c106b 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -1106,11 +1106,12 @@ config SENSORS_ADS1015 will be called ads1015. config SENSORS_ADS7828 - tristate "Texas Instruments ADS7828" + tristate "Texas Instruments ADS7828 and compatibles" depends on I2C help - If you say yes here you get support for Texas Instruments ADS7828 - 12-bit 8-channel ADC device. + If you say yes here you get support for Texas Instruments ADS7828 and + ADS7830 8-channel A/D converters. ADS7828 resolution is 12-bit, while + it is 8-bit on ADS7830. This driver can also be built as a module. If so, the module will be called ads7828. diff --git a/drivers/hwmon/ads7828.c b/drivers/hwmon/ads7828.c index 42914fc1436d..409b5c16defb 100644 --- a/drivers/hwmon/ads7828.c +++ b/drivers/hwmon/ads7828.c @@ -1,11 +1,13 @@ /* - * ads7828.c - lm_sensors driver for ads7828 12-bit 8-channel ADC + * ads7828.c - driver for TI ADS7828 8-channel A/D converter and compatibles * (C) 2007 EADS Astrium * * This driver is based on the lm75 and other lm_sensors/hwmon drivers * * Written by Steve Hardy * + * ADS7830 support, by Guillaume Roguez + * * For further information, see the Documentation/hwmon/ads7828 file. * * This program is free software; you can redistribute it and/or modify @@ -43,6 +45,9 @@ #define ADS7828_EXT_VREF_MV_MIN 50 /* External vref min value 0.05V */ #define ADS7828_EXT_VREF_MV_MAX 5250 /* External vref max value 5.25V */ +/* List of supported devices */ +enum ads7828_chips { ads7828, ads7830 }; + /* Client specific data */ struct ads7828_data { struct device *hwmon_dev; @@ -55,6 +60,7 @@ struct ads7828_data { unsigned int vref_mv; /* voltage reference value */ u8 cmd_byte; /* Command byte without channel bits */ unsigned int lsb_resol; /* Resolution of the ADC sample LSB */ + s32 (*read_channel)(const struct i2c_client *client, u8 command); }; /* Command byte C2,C1,C0 - see datasheet */ @@ -78,8 +84,7 @@ static struct ads7828_data *ads7828_update_device(struct device *dev) for (ch = 0; ch < ADS7828_NCH; ch++) { u8 cmd = ads7828_cmd_byte(data->cmd_byte, ch); - data->adc_input[ch] = - i2c_smbus_read_word_swapped(client, cmd); + data->adc_input[ch] = data->read_channel(client, cmd); } data->last_updated = jiffies; data->valid = true; @@ -164,7 +169,14 @@ static int ads7828_probe(struct i2c_client *client, else data->vref_mv = ADS7828_INT_VREF_MV; - data->lsb_resol = DIV_ROUND_CLOSEST(data->vref_mv * 1000, 4096); + /* ADS7828 uses 12-bit samples, while ADS7830 is 8-bit */ + if (id->driver_data == ads7828) { + data->lsb_resol = DIV_ROUND_CLOSEST(data->vref_mv * 1000, 4096); + data->read_channel = i2c_smbus_read_word_swapped; + } else { + data->lsb_resol = DIV_ROUND_CLOSEST(data->vref_mv * 1000, 256); + data->read_channel = i2c_smbus_read_byte_data; + } data->cmd_byte = data->ext_vref ? ADS7828_CMD_PD1 : ADS7828_CMD_PD3; if (!data->diff_input) @@ -191,7 +203,8 @@ error: } static const struct i2c_device_id ads7828_device_ids[] = { - { "ads7828", 0 }, + { "ads7828", ads7828 }, + { "ads7830", ads7830 }, { } }; MODULE_DEVICE_TABLE(i2c, ads7828_device_ids); @@ -210,4 +223,4 @@ module_i2c_driver(ads7828_driver); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Steve Hardy "); -MODULE_DESCRIPTION("Driver for TI ADS7828 A/D converter"); +MODULE_DESCRIPTION("Driver for TI ADS7828 A/D converter and compatibles"); -- cgit v1.2.3-59-g8ed1b From 04a87a0fbeab7aaebb52b4a079905f732f56d4a7 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Tue, 9 Oct 2012 12:52:18 -0700 Subject: hwmon: (coretemp) List TjMax for Z650/670 and N550/570 as per processor data sheets. Signed-off-by: Guenter Roeck Acked-by: Jean Delvare --- Documentation/hwmon/coretemp | 2 ++ 1 file changed, 2 insertions(+) (limited to 'Documentation') diff --git a/Documentation/hwmon/coretemp b/Documentation/hwmon/coretemp index f17256f069ba..3374c085678d 100644 --- a/Documentation/hwmon/coretemp +++ b/Documentation/hwmon/coretemp @@ -98,8 +98,10 @@ Process Processor TjMax(C) 45nm Atom Processors D525/510/425/410 100 + Z670/650 90 Z560/550/540/530P/530/520PT/520/515/510PT/510P 90 Z510/500 90 + N570/550 100 N475/470/455/450 100 N280/270 90 330/230 125 -- cgit v1.2.3-59-g8ed1b From e597022353631194d93d142b6e14a9f673660de6 Mon Sep 17 00:00:00 2001 From: Ashish Jangam Date: Mon, 8 Oct 2012 18:56:47 +0530 Subject: hwmon: DA9055 HWMON driver This is the HWMON patch for DA9055 PMIC and has got dependency on the DA9055 MFD core. This patch monitors the DA9055 PMIC's ADC channels vddout, junction temperature and auxiliary channels. This patch is functionally tested on Samsung SMDKV6410. Signed-off-by: David Dajun Chen Signed-off-by: Ashish Jangam [Guenter Roeck: Dropped __devinit, __devexit, __devexit_p] Signed-off-by: Guenter Roeck --- Documentation/hwmon/da9055 | 47 ++++++ drivers/hwmon/Kconfig | 10 ++ drivers/hwmon/Makefile | 1 + drivers/hwmon/da9055-hwmon.c | 336 +++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 394 insertions(+) create mode 100644 Documentation/hwmon/da9055 create mode 100644 drivers/hwmon/da9055-hwmon.c (limited to 'Documentation') diff --git a/Documentation/hwmon/da9055 b/Documentation/hwmon/da9055 new file mode 100644 index 000000000000..855c3f536e00 --- /dev/null +++ b/Documentation/hwmon/da9055 @@ -0,0 +1,47 @@ +Supported chips: + * Dialog Semiconductors DA9055 PMIC + Prefix: 'da9055' + Datasheet: Datasheet is not publicly available. + +Authors: David Dajun Chen + +Description +----------- + +The DA9055 provides an Analogue to Digital Converter (ADC) with 10 bits +resolution and track and hold circuitry combined with an analogue input +multiplexer. The analogue input multiplexer will allow conversion of up to 5 +different inputs. The track and hold circuit ensures stable input voltages at +the input of the ADC during the conversion. + +The ADC is used to measure the following inputs: +Channel 0: VDDOUT - measurement of the system voltage +Channel 1: ADC_IN1 - high impedance input (0 - 2.5V) +Channel 2: ADC_IN2 - high impedance input (0 - 2.5V) +Channel 3: ADC_IN3 - high impedance input (0 - 2.5V) +Channel 4: Internal Tjunc. - sense (internal temp. sensor) + +By using sysfs attributes we can measure the system voltage VDDOUT, +chip junction temperature and auxiliary channels voltages. + +Voltage Monitoring +------------------ + +Voltages are sampled in a AUTO mode it can be manually sampled too and results +are stored in a 10 bit ADC. + +The system voltage is calculated as: + Milli volt = ((ADC value * 1000) / 85) + 2500 + +The voltages on ADC channels 1, 2 and 3 are calculated as: + Milli volt = (ADC value * 1000) / 102 + +Temperature Monitoring +---------------------- + +Temperatures are sampled by a 10 bit ADC. Junction temperatures +are monitored by the ADC channels. + +The junction temperature is calculated: + Degrees celsius = -0.4084 * (ADC_RES - T_OFFSET) + 307.6332 +The junction temperature attribute is supported by the driver. diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index 42f21ad56276..4800d4c2a7b7 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -334,6 +334,16 @@ config SENSORS_DA9052_ADC This driver can also be built as module. If so, the module will be called da9052-hwmon. +config SENSORS_DA9055 + tristate "Dialog Semiconductor DA9055 ADC" + depends on MFD_DA9055 + help + If you say yes here you get support for ADC on the Dialog + Semiconductor DA9055 PMIC. + + This driver can also be built as a module. If so, the module + will be called da9055-hwmon. + config SENSORS_I5K_AMB tristate "FB-DIMM AMB temperature sensor on Intel 5000 series chipsets" depends on PCI diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile index 8d5fcb5e8e9f..a930f0997d25 100644 --- a/drivers/hwmon/Makefile +++ b/drivers/hwmon/Makefile @@ -44,6 +44,7 @@ obj-$(CONFIG_SENSORS_ASC7621) += asc7621.o obj-$(CONFIG_SENSORS_ATXP1) += atxp1.o obj-$(CONFIG_SENSORS_CORETEMP) += coretemp.o obj-$(CONFIG_SENSORS_DA9052_ADC)+= da9052-hwmon.o +obj-$(CONFIG_SENSORS_DA9055)+= da9055-hwmon.o obj-$(CONFIG_SENSORS_DME1737) += dme1737.o obj-$(CONFIG_SENSORS_DS620) += ds620.o obj-$(CONFIG_SENSORS_DS1621) += ds1621.o diff --git a/drivers/hwmon/da9055-hwmon.c b/drivers/hwmon/da9055-hwmon.c new file mode 100644 index 000000000000..d32edaea86d5 --- /dev/null +++ b/drivers/hwmon/da9055-hwmon.c @@ -0,0 +1,336 @@ +/* + * HWMON Driver for Dialog DA9055 + * + * Copyright(c) 2012 Dialog Semiconductor Ltd. + * + * Author: David Dajun Chen + * + * 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 +#include + +#define DA9055_ADCIN_DIV 102 +#define DA9055_VSYS_DIV 85 + +#define DA9055_ADC_VSYS 0 +#define DA9055_ADC_ADCIN1 1 +#define DA9055_ADC_ADCIN2 2 +#define DA9055_ADC_ADCIN3 3 +#define DA9055_ADC_TJUNC 4 + +struct da9055_hwmon { + struct da9055 *da9055; + struct device *class_device; + struct mutex hwmon_lock; + struct mutex irq_lock; + struct completion done; +}; + +static const char * const input_names[] = { + [DA9055_ADC_VSYS] = "VSYS", + [DA9055_ADC_ADCIN1] = "ADC IN1", + [DA9055_ADC_ADCIN2] = "ADC IN2", + [DA9055_ADC_ADCIN3] = "ADC IN3", + [DA9055_ADC_TJUNC] = "CHIP TEMP", +}; + +static const u8 chan_mux[DA9055_ADC_TJUNC + 1] = { + [DA9055_ADC_VSYS] = DA9055_ADC_MUX_VSYS, + [DA9055_ADC_ADCIN1] = DA9055_ADC_MUX_ADCIN1, + [DA9055_ADC_ADCIN2] = DA9055_ADC_MUX_ADCIN2, + [DA9055_ADC_ADCIN3] = DA9055_ADC_MUX_ADCIN1, + [DA9055_ADC_TJUNC] = DA9055_ADC_MUX_T_SENSE, +}; + +static int da9055_adc_manual_read(struct da9055_hwmon *hwmon, + unsigned char channel) +{ + int ret; + unsigned short calc_data; + unsigned short data; + unsigned char mux_sel; + struct da9055 *da9055 = hwmon->da9055; + + if (channel > DA9055_ADC_TJUNC) + return -EINVAL; + + mutex_lock(&hwmon->irq_lock); + + /* Selects desired MUX for manual conversion */ + mux_sel = chan_mux[channel] | DA9055_ADC_MAN_CONV; + + ret = da9055_reg_write(da9055, DA9055_REG_ADC_MAN, mux_sel); + if (ret < 0) + goto err; + + /* Wait for an interrupt */ + if (!wait_for_completion_timeout(&hwmon->done, + msecs_to_jiffies(500))) { + dev_err(da9055->dev, + "timeout waiting for ADC conversion interrupt\n"); + ret = -ETIMEDOUT; + goto err; + } + + ret = da9055_reg_read(da9055, DA9055_REG_ADC_RES_H); + if (ret < 0) + goto err; + + calc_data = (unsigned short)ret; + data = calc_data << 2; + + ret = da9055_reg_read(da9055, DA9055_REG_ADC_RES_L); + if (ret < 0) + goto err; + + calc_data = (unsigned short)(ret & DA9055_ADC_LSB_MASK); + data |= calc_data; + + ret = data; + +err: + mutex_unlock(&hwmon->irq_lock); + return ret; +} + +static irqreturn_t da9055_auxadc_irq(int irq, void *irq_data) +{ + struct da9055_hwmon *hwmon = irq_data; + + complete(&hwmon->done); + + return IRQ_HANDLED; +} + +/* Conversion function for VSYS and ADCINx */ +static inline int volt_reg_to_mV(int value, int channel) +{ + if (channel == DA9055_ADC_VSYS) + return DIV_ROUND_CLOSEST(value * 1000, DA9055_VSYS_DIV) + 2500; + else + return DIV_ROUND_CLOSEST(value * 1000, DA9055_ADCIN_DIV); +} + +static int da9055_enable_auto_mode(struct da9055 *da9055, int channel) +{ + + return da9055_reg_update(da9055, DA9055_REG_ADC_CONT, 1 << channel, + 1 << channel); + +} + +static int da9055_disable_auto_mode(struct da9055 *da9055, int channel) +{ + + return da9055_reg_update(da9055, DA9055_REG_ADC_CONT, 1 << channel, 0); +} + +static ssize_t da9055_read_auto_ch(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + struct da9055_hwmon *hwmon = dev_get_drvdata(dev); + int ret, adc; + int channel = to_sensor_dev_attr(devattr)->index; + + mutex_lock(&hwmon->hwmon_lock); + + ret = da9055_enable_auto_mode(hwmon->da9055, channel); + if (ret < 0) + goto hwmon_err; + + usleep_range(10000, 10500); + + adc = da9055_reg_read(hwmon->da9055, DA9055_REG_VSYS_RES + channel); + if (adc < 0) { + ret = adc; + goto hwmon_err_release; + } + + ret = da9055_disable_auto_mode(hwmon->da9055, channel); + if (ret < 0) + goto hwmon_err; + + mutex_unlock(&hwmon->hwmon_lock); + + return sprintf(buf, "%d\n", volt_reg_to_mV(adc, channel)); + +hwmon_err_release: + da9055_disable_auto_mode(hwmon->da9055, channel); +hwmon_err: + mutex_unlock(&hwmon->hwmon_lock); + return ret; +} + +static ssize_t da9055_read_tjunc(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + struct da9055_hwmon *hwmon = dev_get_drvdata(dev); + int tjunc; + int toffset; + + tjunc = da9055_adc_manual_read(hwmon, DA9055_ADC_TJUNC); + if (tjunc < 0) + return tjunc; + + toffset = da9055_reg_read(hwmon->da9055, DA9055_REG_T_OFFSET); + if (toffset < 0) + return toffset; + + /* + * Degrees celsius = -0.4084 * (ADC_RES - T_OFFSET) + 307.6332 + * T_OFFSET is a trim value used to improve accuracy of the result + */ + return sprintf(buf, "%d\n", DIV_ROUND_CLOSEST(-4084 * (tjunc - toffset) + + 3076332, 10000)); +} + +static ssize_t da9055_hwmon_show_name(struct device *dev, + struct device_attribute *devattr, + char *buf) +{ + return sprintf(buf, "da9055-hwmon\n"); +} + +static ssize_t show_label(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + return sprintf(buf, "%s\n", + input_names[to_sensor_dev_attr(devattr)->index]); +} + +static SENSOR_DEVICE_ATTR(in0_input, S_IRUGO, da9055_read_auto_ch, NULL, + DA9055_ADC_VSYS); +static SENSOR_DEVICE_ATTR(in0_label, S_IRUGO, show_label, NULL, + DA9055_ADC_VSYS); +static SENSOR_DEVICE_ATTR(in1_input, S_IRUGO, da9055_read_auto_ch, NULL, + DA9055_ADC_ADCIN1); +static SENSOR_DEVICE_ATTR(in1_label, S_IRUGO, show_label, NULL, + DA9055_ADC_ADCIN1); +static SENSOR_DEVICE_ATTR(in2_input, S_IRUGO, da9055_read_auto_ch, NULL, + DA9055_ADC_ADCIN2); +static SENSOR_DEVICE_ATTR(in2_label, S_IRUGO, show_label, NULL, + DA9055_ADC_ADCIN2); +static SENSOR_DEVICE_ATTR(in3_input, S_IRUGO, da9055_read_auto_ch, NULL, + DA9055_ADC_ADCIN3); +static SENSOR_DEVICE_ATTR(in3_label, S_IRUGO, show_label, NULL, + DA9055_ADC_ADCIN3); + +static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, da9055_read_tjunc, NULL, + DA9055_ADC_TJUNC); +static SENSOR_DEVICE_ATTR(temp1_label, S_IRUGO, show_label, NULL, + DA9055_ADC_TJUNC); + +static DEVICE_ATTR(name, S_IRUGO, da9055_hwmon_show_name, NULL); + +static struct attribute *da9055_attr[] = { + &dev_attr_name.attr, + &sensor_dev_attr_in0_input.dev_attr.attr, + &sensor_dev_attr_in0_label.dev_attr.attr, + &sensor_dev_attr_in1_input.dev_attr.attr, + &sensor_dev_attr_in1_label.dev_attr.attr, + &sensor_dev_attr_in2_input.dev_attr.attr, + &sensor_dev_attr_in2_label.dev_attr.attr, + &sensor_dev_attr_in3_input.dev_attr.attr, + &sensor_dev_attr_in3_label.dev_attr.attr, + + &sensor_dev_attr_temp1_input.dev_attr.attr, + &sensor_dev_attr_temp1_label.dev_attr.attr, + NULL +}; + +static const struct attribute_group da9055_attr_group = {.attrs = da9055_attr}; + +static int da9055_hwmon_probe(struct platform_device *pdev) +{ + struct da9055_hwmon *hwmon; + int hwmon_irq, ret; + + hwmon = devm_kzalloc(&pdev->dev, sizeof(struct da9055_hwmon), + GFP_KERNEL); + if (!hwmon) + return -ENOMEM; + + mutex_init(&hwmon->hwmon_lock); + mutex_init(&hwmon->irq_lock); + + init_completion(&hwmon->done); + hwmon->da9055 = dev_get_drvdata(pdev->dev.parent); + + platform_set_drvdata(pdev, hwmon); + + hwmon_irq = platform_get_irq_byname(pdev, "HWMON"); + if (hwmon_irq < 0) + return hwmon_irq; + + hwmon_irq = regmap_irq_get_virq(hwmon->da9055->irq_data, hwmon_irq); + if (hwmon_irq < 0) + return hwmon_irq; + + ret = devm_request_threaded_irq(&pdev->dev, hwmon_irq, + NULL, da9055_auxadc_irq, + IRQF_TRIGGER_HIGH | IRQF_ONESHOT, + "adc-irq", hwmon); + if (ret != 0) { + dev_err(hwmon->da9055->dev, "DA9055 ADC IRQ failed ret=%d\n", + ret); + return ret; + } + + ret = sysfs_create_group(&pdev->dev.kobj, &da9055_attr_group); + if (ret) + return ret; + + hwmon->class_device = hwmon_device_register(&pdev->dev); + if (IS_ERR(hwmon->class_device)) { + ret = PTR_ERR(hwmon->class_device); + goto err; + } + + return 0; + +err: + sysfs_remove_group(&pdev->dev.kobj, &da9055_attr_group); + return ret; +} + +static int da9055_hwmon_remove(struct platform_device *pdev) +{ + struct da9055_hwmon *hwmon = platform_get_drvdata(pdev); + + sysfs_remove_group(&pdev->dev.kobj, &da9055_attr_group); + hwmon_device_unregister(hwmon->class_device); + + return 0; +} + +static struct platform_driver da9055_hwmon_driver = { + .probe = da9055_hwmon_probe, + .remove = da9055_hwmon_remove, + .driver = { + .name = "da9055-hwmon", + .owner = THIS_MODULE, + }, +}; + +module_platform_driver(da9055_hwmon_driver); + +MODULE_AUTHOR("David Dajun Chen "); +MODULE_DESCRIPTION("DA9055 HWMON driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:da9055-hwmon"); -- cgit v1.2.3-59-g8ed1b From cc86802805b5d714a5dc80fe4edecaf1368b09ed Mon Sep 17 00:00:00 2001 From: Shan Wei Date: Tue, 4 Dec 2012 18:50:35 +0000 Subject: net: doc: add default value for neighbour parameters Signed-off-by: Shan Wei Signed-off-by: David S. Miller --- Documentation/networking/ip-sysctl.txt | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'Documentation') diff --git a/Documentation/networking/ip-sysctl.txt b/Documentation/networking/ip-sysctl.txt index c6d5fee888c4..0462a7105304 100644 --- a/Documentation/networking/ip-sysctl.txt +++ b/Documentation/networking/ip-sysctl.txt @@ -30,16 +30,24 @@ neigh/default/gc_thresh3 - INTEGER Maximum number of neighbor entries allowed. Increase this when using large numbers of interfaces and when communicating with large numbers of directly-connected peers. + Default: 1024 neigh/default/unres_qlen_bytes - INTEGER The maximum number of bytes which may be used by packets queued for each unresolved address by other network layers. (added in linux 3.3) + Seting negative value is meaningless and will retrun error. + Default: 65536 Bytes(64KB) neigh/default/unres_qlen - INTEGER The maximum number of packets which may be queued for each unresolved address by other network layers. (deprecated in linux 3.3) : use unres_qlen_bytes instead. + Prior to linux 3.3, the default value is 3 which may cause + secluded packet loss. The current default value is calculated + according to default value of unres_qlen_bytes and true size of + packet. + Default: 31 mtu_expires - INTEGER Time, in seconds, that cached PMTU information is kept. -- cgit v1.2.3-59-g8ed1b From 600587a5be61bb218034bda9b134abf2af7da097 Mon Sep 17 00:00:00 2001 From: James Hogan Date: Wed, 5 Dec 2012 16:08:24 +0000 Subject: of/vendor-prefixes: add Imagination Technologies The "powervr" prefix which is currently described as "Imagination Technologies" isn't really appropriate for non-PowerVR hardware, so deprecate it, changing the description of "powervr" to "PowerVR (deprecated, use img)", and add a separate "img" prefix for "Imagination Technologies Ltd.". Signed-off-by: James Hogan Signed-off-by: Grant Likely --- Documentation/devicetree/bindings/vendor-prefixes.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/vendor-prefixes.txt b/Documentation/devicetree/bindings/vendor-prefixes.txt index 98271503f006..770a0193ca1b 100644 --- a/Documentation/devicetree/bindings/vendor-prefixes.txt +++ b/Documentation/devicetree/bindings/vendor-prefixes.txt @@ -26,6 +26,7 @@ gef GE Fanuc Intelligent Platforms Embedded Systems, Inc. hp Hewlett Packard ibm International Business Machines (IBM) idt Integrated Device Technologies, Inc. +img Imagination Technologies Ltd. intercontrol Inter Control Group linux Linux-specific binding marvell Marvell Technology Group Ltd. @@ -37,7 +38,7 @@ nvidia NVIDIA nxp NXP Semiconductors onnn ON Semiconductor Corp. picochip Picochip Ltd -powervr Imagination Technologies +powervr PowerVR (deprecated, use img) qcom Qualcomm, Inc. ramtron Ramtron International realtek Realtek Semiconductor Corp. -- cgit v1.2.3-59-g8ed1b From 023bba36387de704b3ea2a69bf9f9ed2301cfbcc Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Sat, 1 Dec 2012 19:05:16 +0100 Subject: irqdomain: update documentation This updates the IRQdomain documentation a bit, by adding a more verbose explanation to why we need this, and by adding some extended documentation of the irq_domain_simple() usecase. Signed-off-by: Linus Walleij Signed-off-by: Grant Likely --- Documentation/IRQ-domain.txt | 34 +++++++++++++++++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-) (limited to 'Documentation') diff --git a/Documentation/IRQ-domain.txt b/Documentation/IRQ-domain.txt index 1401cece745a..9bc95942ec22 100644 --- a/Documentation/IRQ-domain.txt +++ b/Documentation/IRQ-domain.txt @@ -7,6 +7,21 @@ systems with multiple interrupt controllers the kernel must ensure that each one gets assigned non-overlapping allocations of Linux IRQ numbers. +The number of interrupt controllers registered as unique irqchips +show a rising tendency: for example subdrivers of different kinds +such as GPIO controllers avoid reimplementing identical callback +mechanisms as the IRQ core system by modelling their interrupt +handlers as irqchips, i.e. in effect cascading interrupt controllers. + +Here the interrupt number loose all kind of correspondence to +hardware interrupt numbers: whereas in the past, IRQ numbers could +be chosen so they matched the hardware IRQ line into the root +interrupt controller (i.e. the component actually fireing the +interrupt line to the CPU) nowadays this number is just a number. + +For this reason we need a mechanism to separate controller-local +interrupt numbers, called hardware irq's, from Linux IRQ numbers. + The irq_alloc_desc*() and irq_free_desc*() APIs provide allocation of irq numbers, but they don't provide any support for reverse mapping of the controller-local IRQ (hwirq) number into the Linux IRQ number @@ -40,6 +55,10 @@ required hardware setup. When an interrupt is received, irq_find_mapping() function should be used to find the Linux IRQ number from the hwirq number. +The irq_create_mapping() function must be called *atleast once* +before any call to irq_find_mapping(), lest the descriptor will not +be allocated. + If the driver has the Linux IRQ number or the irq_data pointer, and needs to know the associated hwirq number (such as in the irq_chip callbacks) then it can be directly obtained from irq_data->hwirq. @@ -119,4 +138,17 @@ numbers. Most users of legacy mappings should use irq_domain_add_simple() which will use a legacy domain only if an IRQ range is supplied by the -system and will otherwise use a linear domain mapping. +system and will otherwise use a linear domain mapping. The semantics +of this call are such that if an IRQ range is specified then +descriptors will be allocated on-the-fly for it, and if no range is +specified it will fall through to irq_domain_add_linear() which meand +*no* irq descriptors will be allocated. + +A typical use case for simple domains is where an irqchip provider +is supporting both dynamic and static IRQ assignments. + +In order to avoid ending up in a situation where a linear domain is +used and no descriptor gets allocated it is very important to make sure +that the driver using the simple domain call irq_create_mapping() +before any irq_find_mapping() since the latter will actually work +for the static IRQ assignment case. -- cgit v1.2.3-59-g8ed1b From a2932923ccf63c419c77aaa18ac09be98f2c94d8 Mon Sep 17 00:00:00 2001 From: Paul Mackerras Date: Mon, 19 Nov 2012 22:57:20 +0000 Subject: KVM: PPC: Book3S HV: Provide a method for userspace to read and write the HPT A new ioctl, KVM_PPC_GET_HTAB_FD, returns a file descriptor. Reads on this fd return the contents of the HPT (hashed page table), writes create and/or remove entries in the HPT. There is a new capability, KVM_CAP_PPC_HTAB_FD, to indicate the presence of the ioctl. The ioctl takes an argument structure with the index of the first HPT entry to read out and a set of flags. The flags indicate whether the user is intending to read or write the HPT, and whether to return all entries or only the "bolted" entries (those with the bolted bit, 0x10, set in the first doubleword). This is intended for use in implementing qemu's savevm/loadvm and for live migration. Therefore, on reads, the first pass returns information about all HPTEs (or all bolted HPTEs). When the first pass reaches the end of the HPT, it returns from the read. Subsequent reads only return information about HPTEs that have changed since they were last read. A read that finds no changed HPTEs in the HPT following where the last read finished will return 0 bytes. The format of the data provides a simple run-length compression of the invalid entries. Each block of data starts with a header that indicates the index (position in the HPT, which is just an array), the number of valid entries starting at that index (may be zero), and the number of invalid entries following those valid entries. The valid entries, 16 bytes each, follow the header. The invalid entries are not explicitly represented. Signed-off-by: Paul Mackerras [agraf: fix documentation] Signed-off-by: Alexander Graf --- Documentation/virtual/kvm/api.txt | 54 +++++ arch/powerpc/include/asm/kvm_book3s_64.h | 22 ++ arch/powerpc/include/asm/kvm_ppc.h | 2 + arch/powerpc/include/uapi/asm/kvm.h | 25 +++ arch/powerpc/kvm/book3s_64_mmu_hv.c | 344 +++++++++++++++++++++++++++++++ arch/powerpc/kvm/book3s_hv.c | 12 -- arch/powerpc/kvm/powerpc.c | 17 ++ include/uapi/linux/kvm.h | 3 + 8 files changed, 467 insertions(+), 12 deletions(-) (limited to 'Documentation') diff --git a/Documentation/virtual/kvm/api.txt b/Documentation/virtual/kvm/api.txt index 6671fdc0afb1..a5607c571cb3 100644 --- a/Documentation/virtual/kvm/api.txt +++ b/Documentation/virtual/kvm/api.txt @@ -2071,6 +2071,60 @@ KVM_S390_INT_EXTERNAL_CALL (vcpu) - sigp external call; source cpu in parm Note that the vcpu ioctl is asynchronous to vcpu execution. +4.78 KVM_PPC_GET_HTAB_FD + +Capability: KVM_CAP_PPC_HTAB_FD +Architectures: powerpc +Type: vm ioctl +Parameters: Pointer to struct kvm_get_htab_fd (in) +Returns: file descriptor number (>= 0) on success, -1 on error + +This returns a file descriptor that can be used either to read out the +entries in the guest's hashed page table (HPT), or to write entries to +initialize the HPT. The returned fd can only be written to if the +KVM_GET_HTAB_WRITE bit is set in the flags field of the argument, and +can only be read if that bit is clear. The argument struct looks like +this: + +/* For KVM_PPC_GET_HTAB_FD */ +struct kvm_get_htab_fd { + __u64 flags; + __u64 start_index; + __u64 reserved[2]; +}; + +/* Values for kvm_get_htab_fd.flags */ +#define KVM_GET_HTAB_BOLTED_ONLY ((__u64)0x1) +#define KVM_GET_HTAB_WRITE ((__u64)0x2) + +The `start_index' field gives the index in the HPT of the entry at +which to start reading. It is ignored when writing. + +Reads on the fd will initially supply information about all +"interesting" HPT entries. Interesting entries are those with the +bolted bit set, if the KVM_GET_HTAB_BOLTED_ONLY bit is set, otherwise +all entries. When the end of the HPT is reached, the read() will +return. If read() is called again on the fd, it will start again from +the beginning of the HPT, but will only return HPT entries that have +changed since they were last read. + +Data read or written is structured as a header (8 bytes) followed by a +series of valid HPT entries (16 bytes) each. The header indicates how +many valid HPT entries there are and how many invalid entries follow +the valid entries. The invalid entries are not represented explicitly +in the stream. The header format is: + +struct kvm_get_htab_header { + __u32 index; + __u16 n_valid; + __u16 n_invalid; +}; + +Writes to the fd create HPT entries starting at the index given in the +header; first `n_valid' valid entries with contents from the data +written, then `n_invalid' invalid entries, invalidating any previously +valid entries found. + 5. The kvm_run structure ------------------------ diff --git a/arch/powerpc/include/asm/kvm_book3s_64.h b/arch/powerpc/include/asm/kvm_book3s_64.h index b322e5bd6964..38bec1dc9928 100644 --- a/arch/powerpc/include/asm/kvm_book3s_64.h +++ b/arch/powerpc/include/asm/kvm_book3s_64.h @@ -246,4 +246,26 @@ static inline bool slot_is_aligned(struct kvm_memory_slot *memslot, return !(memslot->base_gfn & mask) && !(memslot->npages & mask); } +/* + * This works for 4k, 64k and 16M pages on POWER7, + * and 4k and 16M pages on PPC970. + */ +static inline unsigned long slb_pgsize_encoding(unsigned long psize) +{ + unsigned long senc = 0; + + if (psize > 0x1000) { + senc = SLB_VSID_L; + if (psize == 0x10000) + senc |= SLB_VSID_LP_01; + } + return senc; +} + +static inline int is_vrma_hpte(unsigned long hpte_v) +{ + return (hpte_v & ~0xffffffUL) == + (HPTE_V_1TB_SEG | (VRMA_VSID << (40 - 16))); +} + #endif /* __ASM_KVM_BOOK3S_64_H__ */ diff --git a/arch/powerpc/include/asm/kvm_ppc.h b/arch/powerpc/include/asm/kvm_ppc.h index 609cca3e9426..1ca31e92ee75 100644 --- a/arch/powerpc/include/asm/kvm_ppc.h +++ b/arch/powerpc/include/asm/kvm_ppc.h @@ -164,6 +164,8 @@ extern void kvmppc_bookehv_exit(void); extern int kvmppc_prepare_to_enter(struct kvm_vcpu *vcpu); +extern int kvm_vm_ioctl_get_htab_fd(struct kvm *kvm, struct kvm_get_htab_fd *); + /* * Cuts out inst bits with ordering according to spec. * That means the leftmost bit is zero. All given bits are included. diff --git a/arch/powerpc/include/uapi/asm/kvm.h b/arch/powerpc/include/uapi/asm/kvm.h index b89ae4db45ce..514883dd311e 100644 --- a/arch/powerpc/include/uapi/asm/kvm.h +++ b/arch/powerpc/include/uapi/asm/kvm.h @@ -331,6 +331,31 @@ struct kvm_book3e_206_tlb_params { __u32 reserved[8]; }; +/* For KVM_PPC_GET_HTAB_FD */ +struct kvm_get_htab_fd { + __u64 flags; + __u64 start_index; + __u64 reserved[2]; +}; + +/* Values for kvm_get_htab_fd.flags */ +#define KVM_GET_HTAB_BOLTED_ONLY ((__u64)0x1) +#define KVM_GET_HTAB_WRITE ((__u64)0x2) + +/* + * Data read on the file descriptor is formatted as a series of + * records, each consisting of a header followed by a series of + * `n_valid' HPTEs (16 bytes each), which are all valid. Following + * those valid HPTEs there are `n_invalid' invalid HPTEs, which + * are not represented explicitly in the stream. The same format + * is used for writing. + */ +struct kvm_get_htab_header { + __u32 index; + __u16 n_valid; + __u16 n_invalid; +}; + #define KVM_REG_PPC_HIOR (KVM_REG_PPC | KVM_REG_SIZE_U64 | 0x1) #define KVM_REG_PPC_IAC1 (KVM_REG_PPC | KVM_REG_SIZE_U64 | 0x2) #define KVM_REG_PPC_IAC2 (KVM_REG_PPC | KVM_REG_SIZE_U64 | 0x3) diff --git a/arch/powerpc/kvm/book3s_64_mmu_hv.c b/arch/powerpc/kvm/book3s_64_mmu_hv.c index 6ee6516a0bee..0aa40734c8f6 100644 --- a/arch/powerpc/kvm/book3s_64_mmu_hv.c +++ b/arch/powerpc/kvm/book3s_64_mmu_hv.c @@ -25,6 +25,8 @@ #include #include #include +#include +#include #include #include @@ -1145,6 +1147,348 @@ void kvmppc_unpin_guest_page(struct kvm *kvm, void *va) put_page(page); } +/* + * Functions for reading and writing the hash table via reads and + * writes on a file descriptor. + * + * Reads return the guest view of the hash table, which has to be + * pieced together from the real hash table and the guest_rpte + * values in the revmap array. + * + * On writes, each HPTE written is considered in turn, and if it + * is valid, it is written to the HPT as if an H_ENTER with the + * exact flag set was done. When the invalid count is non-zero + * in the header written to the stream, the kernel will make + * sure that that many HPTEs are invalid, and invalidate them + * if not. + */ + +struct kvm_htab_ctx { + unsigned long index; + unsigned long flags; + struct kvm *kvm; + int first_pass; +}; + +#define HPTE_SIZE (2 * sizeof(unsigned long)) + +static long record_hpte(unsigned long flags, unsigned long *hptp, + unsigned long *hpte, struct revmap_entry *revp, + int want_valid, int first_pass) +{ + unsigned long v, r; + int ok = 1; + int valid, dirty; + + /* Unmodified entries are uninteresting except on the first pass */ + dirty = !!(revp->guest_rpte & HPTE_GR_MODIFIED); + if (!first_pass && !dirty) + return 0; + + valid = 0; + if (hptp[0] & (HPTE_V_VALID | HPTE_V_ABSENT)) { + valid = 1; + if ((flags & KVM_GET_HTAB_BOLTED_ONLY) && + !(hptp[0] & HPTE_V_BOLTED)) + valid = 0; + } + if (valid != want_valid) + return 0; + + v = r = 0; + if (valid || dirty) { + /* lock the HPTE so it's stable and read it */ + preempt_disable(); + while (!try_lock_hpte(hptp, HPTE_V_HVLOCK)) + cpu_relax(); + v = hptp[0]; + if (v & HPTE_V_ABSENT) { + v &= ~HPTE_V_ABSENT; + v |= HPTE_V_VALID; + } + /* re-evaluate valid and dirty from synchronized HPTE value */ + valid = !!(v & HPTE_V_VALID); + if ((flags & KVM_GET_HTAB_BOLTED_ONLY) && !(v & HPTE_V_BOLTED)) + valid = 0; + r = revp->guest_rpte | (hptp[1] & (HPTE_R_R | HPTE_R_C)); + dirty = !!(revp->guest_rpte & HPTE_GR_MODIFIED); + /* only clear modified if this is the right sort of entry */ + if (valid == want_valid && dirty) { + r &= ~HPTE_GR_MODIFIED; + revp->guest_rpte = r; + } + asm volatile(PPC_RELEASE_BARRIER "" : : : "memory"); + hptp[0] &= ~HPTE_V_HVLOCK; + preempt_enable(); + if (!(valid == want_valid && (first_pass || dirty))) + ok = 0; + } + hpte[0] = v; + hpte[1] = r; + return ok; +} + +static ssize_t kvm_htab_read(struct file *file, char __user *buf, + size_t count, loff_t *ppos) +{ + struct kvm_htab_ctx *ctx = file->private_data; + struct kvm *kvm = ctx->kvm; + struct kvm_get_htab_header hdr; + unsigned long *hptp; + struct revmap_entry *revp; + unsigned long i, nb, nw; + unsigned long __user *lbuf; + struct kvm_get_htab_header __user *hptr; + unsigned long flags; + int first_pass; + unsigned long hpte[2]; + + if (!access_ok(VERIFY_WRITE, buf, count)) + return -EFAULT; + + first_pass = ctx->first_pass; + flags = ctx->flags; + + i = ctx->index; + hptp = (unsigned long *)(kvm->arch.hpt_virt + (i * HPTE_SIZE)); + revp = kvm->arch.revmap + i; + lbuf = (unsigned long __user *)buf; + + nb = 0; + while (nb + sizeof(hdr) + HPTE_SIZE < count) { + /* Initialize header */ + hptr = (struct kvm_get_htab_header __user *)buf; + hdr.index = i; + hdr.n_valid = 0; + hdr.n_invalid = 0; + nw = nb; + nb += sizeof(hdr); + lbuf = (unsigned long __user *)(buf + sizeof(hdr)); + + /* Skip uninteresting entries, i.e. clean on not-first pass */ + if (!first_pass) { + while (i < kvm->arch.hpt_npte && + !(revp->guest_rpte & HPTE_GR_MODIFIED)) { + ++i; + hptp += 2; + ++revp; + } + } + + /* Grab a series of valid entries */ + while (i < kvm->arch.hpt_npte && + hdr.n_valid < 0xffff && + nb + HPTE_SIZE < count && + record_hpte(flags, hptp, hpte, revp, 1, first_pass)) { + /* valid entry, write it out */ + ++hdr.n_valid; + if (__put_user(hpte[0], lbuf) || + __put_user(hpte[1], lbuf + 1)) + return -EFAULT; + nb += HPTE_SIZE; + lbuf += 2; + ++i; + hptp += 2; + ++revp; + } + /* Now skip invalid entries while we can */ + while (i < kvm->arch.hpt_npte && + hdr.n_invalid < 0xffff && + record_hpte(flags, hptp, hpte, revp, 0, first_pass)) { + /* found an invalid entry */ + ++hdr.n_invalid; + ++i; + hptp += 2; + ++revp; + } + + if (hdr.n_valid || hdr.n_invalid) { + /* write back the header */ + if (__copy_to_user(hptr, &hdr, sizeof(hdr))) + return -EFAULT; + nw = nb; + buf = (char __user *)lbuf; + } else { + nb = nw; + } + + /* Check if we've wrapped around the hash table */ + if (i >= kvm->arch.hpt_npte) { + i = 0; + ctx->first_pass = 0; + break; + } + } + + ctx->index = i; + + return nb; +} + +static ssize_t kvm_htab_write(struct file *file, const char __user *buf, + size_t count, loff_t *ppos) +{ + struct kvm_htab_ctx *ctx = file->private_data; + struct kvm *kvm = ctx->kvm; + struct kvm_get_htab_header hdr; + unsigned long i, j; + unsigned long v, r; + unsigned long __user *lbuf; + unsigned long *hptp; + unsigned long tmp[2]; + ssize_t nb; + long int err, ret; + int rma_setup; + + if (!access_ok(VERIFY_READ, buf, count)) + return -EFAULT; + + /* lock out vcpus from running while we're doing this */ + mutex_lock(&kvm->lock); + rma_setup = kvm->arch.rma_setup_done; + if (rma_setup) { + kvm->arch.rma_setup_done = 0; /* temporarily */ + /* order rma_setup_done vs. vcpus_running */ + smp_mb(); + if (atomic_read(&kvm->arch.vcpus_running)) { + kvm->arch.rma_setup_done = 1; + mutex_unlock(&kvm->lock); + return -EBUSY; + } + } + + err = 0; + for (nb = 0; nb + sizeof(hdr) <= count; ) { + err = -EFAULT; + if (__copy_from_user(&hdr, buf, sizeof(hdr))) + break; + + err = 0; + if (nb + hdr.n_valid * HPTE_SIZE > count) + break; + + nb += sizeof(hdr); + buf += sizeof(hdr); + + err = -EINVAL; + i = hdr.index; + if (i >= kvm->arch.hpt_npte || + i + hdr.n_valid + hdr.n_invalid > kvm->arch.hpt_npte) + break; + + hptp = (unsigned long *)(kvm->arch.hpt_virt + (i * HPTE_SIZE)); + lbuf = (unsigned long __user *)buf; + for (j = 0; j < hdr.n_valid; ++j) { + err = -EFAULT; + if (__get_user(v, lbuf) || __get_user(r, lbuf + 1)) + goto out; + err = -EINVAL; + if (!(v & HPTE_V_VALID)) + goto out; + lbuf += 2; + nb += HPTE_SIZE; + + if (hptp[0] & (HPTE_V_VALID | HPTE_V_ABSENT)) + kvmppc_do_h_remove(kvm, 0, i, 0, tmp); + err = -EIO; + ret = kvmppc_virtmode_do_h_enter(kvm, H_EXACT, i, v, r, + tmp); + if (ret != H_SUCCESS) { + pr_err("kvm_htab_write ret %ld i=%ld v=%lx " + "r=%lx\n", ret, i, v, r); + goto out; + } + if (!rma_setup && is_vrma_hpte(v)) { + unsigned long psize = hpte_page_size(v, r); + unsigned long senc = slb_pgsize_encoding(psize); + unsigned long lpcr; + + kvm->arch.vrma_slb_v = senc | SLB_VSID_B_1T | + (VRMA_VSID << SLB_VSID_SHIFT_1T); + lpcr = kvm->arch.lpcr & ~LPCR_VRMASD; + lpcr |= senc << (LPCR_VRMASD_SH - 4); + kvm->arch.lpcr = lpcr; + rma_setup = 1; + } + ++i; + hptp += 2; + } + + for (j = 0; j < hdr.n_invalid; ++j) { + if (hptp[0] & (HPTE_V_VALID | HPTE_V_ABSENT)) + kvmppc_do_h_remove(kvm, 0, i, 0, tmp); + ++i; + hptp += 2; + } + err = 0; + } + + out: + /* Order HPTE updates vs. rma_setup_done */ + smp_wmb(); + kvm->arch.rma_setup_done = rma_setup; + mutex_unlock(&kvm->lock); + + if (err) + return err; + return nb; +} + +static int kvm_htab_release(struct inode *inode, struct file *filp) +{ + struct kvm_htab_ctx *ctx = filp->private_data; + + filp->private_data = NULL; + if (!(ctx->flags & KVM_GET_HTAB_WRITE)) + atomic_dec(&ctx->kvm->arch.hpte_mod_interest); + kvm_put_kvm(ctx->kvm); + kfree(ctx); + return 0; +} + +static struct file_operations kvm_htab_fops = { + .read = kvm_htab_read, + .write = kvm_htab_write, + .llseek = default_llseek, + .release = kvm_htab_release, +}; + +int kvm_vm_ioctl_get_htab_fd(struct kvm *kvm, struct kvm_get_htab_fd *ghf) +{ + int ret; + struct kvm_htab_ctx *ctx; + int rwflag; + + /* reject flags we don't recognize */ + if (ghf->flags & ~(KVM_GET_HTAB_BOLTED_ONLY | KVM_GET_HTAB_WRITE)) + return -EINVAL; + ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); + if (!ctx) + return -ENOMEM; + kvm_get_kvm(kvm); + ctx->kvm = kvm; + ctx->index = ghf->start_index; + ctx->flags = ghf->flags; + ctx->first_pass = 1; + + rwflag = (ghf->flags & KVM_GET_HTAB_WRITE) ? O_WRONLY : O_RDONLY; + ret = anon_inode_getfd("kvm-htab", &kvm_htab_fops, ctx, rwflag); + if (ret < 0) { + kvm_put_kvm(kvm); + return ret; + } + + if (rwflag == O_RDONLY) { + mutex_lock(&kvm->slots_lock); + atomic_inc(&kvm->arch.hpte_mod_interest); + /* make sure kvmppc_do_h_enter etc. see the increment */ + synchronize_srcu_expedited(&kvm->srcu); + mutex_unlock(&kvm->slots_lock); + } + + return ret; +} + void kvmppc_mmu_book3s_hv_init(struct kvm_vcpu *vcpu) { struct kvmppc_mmu *mmu = &vcpu->arch.mmu; diff --git a/arch/powerpc/kvm/book3s_hv.c b/arch/powerpc/kvm/book3s_hv.c index 843eb754a1d5..a4f59dbcd800 100644 --- a/arch/powerpc/kvm/book3s_hv.c +++ b/arch/powerpc/kvm/book3s_hv.c @@ -1563,18 +1563,6 @@ out: return r; } -static unsigned long slb_pgsize_encoding(unsigned long psize) -{ - unsigned long senc = 0; - - if (psize > 0x1000) { - senc = SLB_VSID_L; - if (psize == 0x10000) - senc |= SLB_VSID_LP_01; - } - return senc; -} - static void unpin_slot(struct kvm_memory_slot *memslot) { unsigned long *physp; diff --git a/arch/powerpc/kvm/powerpc.c b/arch/powerpc/kvm/powerpc.c index d583ea15e151..70739a089560 100644 --- a/arch/powerpc/kvm/powerpc.c +++ b/arch/powerpc/kvm/powerpc.c @@ -354,6 +354,12 @@ int kvm_dev_ioctl_check_extension(long ext) r = 1; #else r = 0; + break; +#endif +#ifdef CONFIG_KVM_BOOK3S_64_HV + case KVM_CAP_PPC_HTAB_FD: + r = 1; + break; #endif break; case KVM_CAP_NR_VCPUS: @@ -954,6 +960,17 @@ long kvm_arch_vm_ioctl(struct file *filp, r = 0; break; } + + case KVM_PPC_GET_HTAB_FD: { + struct kvm *kvm = filp->private_data; + struct kvm_get_htab_fd ghf; + + r = -EFAULT; + if (copy_from_user(&ghf, argp, sizeof(ghf))) + break; + r = kvm_vm_ioctl_get_htab_fd(kvm, &ghf); + break; + } #endif /* CONFIG_KVM_BOOK3S_64_HV */ #ifdef CONFIG_PPC_BOOK3S_64 diff --git a/include/uapi/linux/kvm.h b/include/uapi/linux/kvm.h index 494a84c37c3e..e6e5d4b13708 100644 --- a/include/uapi/linux/kvm.h +++ b/include/uapi/linux/kvm.h @@ -634,6 +634,7 @@ struct kvm_ppc_smmu_info { #endif #define KVM_CAP_IRQFD_RESAMPLE 82 #define KVM_CAP_PPC_BOOKE_WATCHDOG 83 +#define KVM_CAP_PPC_HTAB_FD 84 #ifdef KVM_CAP_IRQ_ROUTING @@ -859,6 +860,8 @@ struct kvm_s390_ucas_mapping { #define KVM_CREATE_SPAPR_TCE _IOW(KVMIO, 0xa8, struct kvm_create_spapr_tce) /* Available with KVM_CAP_RMA */ #define KVM_ALLOCATE_RMA _IOR(KVMIO, 0xa9, struct kvm_allocate_rma) +/* Available with KVM_CAP_PPC_HTAB_FD */ +#define KVM_PPC_GET_HTAB_FD _IOW(KVMIO, 0xaa, struct kvm_get_htab_fd) /* * ioctls for vcpu fds -- cgit v1.2.3-59-g8ed1b From 352df1deb2e3c40e65ff94c8d7c62d9144446b1c Mon Sep 17 00:00:00 2001 From: Mihai Caraman Date: Thu, 11 Oct 2012 06:13:29 +0000 Subject: KVM: PPC: booke: Get/set guest EPCR register using ONE_REG interface Implement ONE_REG interface for EPCR register adding KVM_REG_PPC_EPCR to the list of ONE_REG PPC supported registers. Signed-off-by: Mihai Caraman [agraf: remove HV dependency, use get/put_user] Signed-off-by: Alexander Graf --- Documentation/virtual/kvm/api.txt | 1 + arch/powerpc/include/uapi/asm/kvm.h | 2 ++ arch/powerpc/kvm/booke.c | 14 ++++++++++++++ 3 files changed, 17 insertions(+) (limited to 'Documentation') diff --git a/Documentation/virtual/kvm/api.txt b/Documentation/virtual/kvm/api.txt index a5607c571cb3..a4df5535996b 100644 --- a/Documentation/virtual/kvm/api.txt +++ b/Documentation/virtual/kvm/api.txt @@ -1773,6 +1773,7 @@ registers, find a list below: PPC | KVM_REG_PPC_VPA_ADDR | 64 PPC | KVM_REG_PPC_VPA_SLB | 128 PPC | KVM_REG_PPC_VPA_DTL | 128 + PPC | KVM_REG_PPC_EPCR | 32 4.69 KVM_GET_ONE_REG diff --git a/arch/powerpc/include/uapi/asm/kvm.h b/arch/powerpc/include/uapi/asm/kvm.h index 514883dd311e..2fba8a66fb10 100644 --- a/arch/powerpc/include/uapi/asm/kvm.h +++ b/arch/powerpc/include/uapi/asm/kvm.h @@ -411,4 +411,6 @@ struct kvm_get_htab_header { #define KVM_REG_PPC_VPA_SLB (KVM_REG_PPC | KVM_REG_SIZE_U128 | 0x83) #define KVM_REG_PPC_VPA_DTL (KVM_REG_PPC | KVM_REG_SIZE_U128 | 0x84) +#define KVM_REG_PPC_EPCR (KVM_REG_PPC | KVM_REG_SIZE_U32 | 0x85) + #endif /* __LINUX_KVM_POWERPC_H */ diff --git a/arch/powerpc/kvm/booke.c b/arch/powerpc/kvm/booke.c index 037d045db3f1..69f114015780 100644 --- a/arch/powerpc/kvm/booke.c +++ b/arch/powerpc/kvm/booke.c @@ -1388,6 +1388,11 @@ int kvm_vcpu_ioctl_get_one_reg(struct kvm_vcpu *vcpu, struct kvm_one_reg *reg) &vcpu->arch.dbg_reg.dac[dac], sizeof(u64)); break; } +#if defined(CONFIG_64BIT) + case KVM_REG_PPC_EPCR: + r = put_user(vcpu->arch.epcr, (u32 __user *)(long)reg->addr); + break; +#endif default: break; } @@ -1415,6 +1420,15 @@ int kvm_vcpu_ioctl_set_one_reg(struct kvm_vcpu *vcpu, struct kvm_one_reg *reg) (u64 __user *)(long)reg->addr, sizeof(u64)); break; } +#if defined(CONFIG_64BIT) + case KVM_REG_PPC_EPCR: { + u32 new_epcr; + r = get_user(new_epcr, (u32 __user *)(long)reg->addr); + if (r == 0) + kvmppc_set_epcr(vcpu, new_epcr); + break; + } +#endif default: break; } -- cgit v1.2.3-59-g8ed1b From a2c0911c09190125f52c9941b9d187f601c2f7be Mon Sep 17 00:00:00 2001 From: Vakul Garg Date: Thu, 6 Dec 2012 17:16:30 +0800 Subject: crypto: caam - Updated SEC-4.0 device tree binding for ERA information. The compatible property in device tree "crypto" node has been enhanced to provide SEC ERA information to the applications. Signed-off-by: Vakul Garg Signed-off-by: Herbert Xu --- Documentation/devicetree/bindings/crypto/fsl-sec4.txt | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/crypto/fsl-sec4.txt b/Documentation/devicetree/bindings/crypto/fsl-sec4.txt index bd7ce120bc13..fc9ce6f1688c 100644 --- a/Documentation/devicetree/bindings/crypto/fsl-sec4.txt +++ b/Documentation/devicetree/bindings/crypto/fsl-sec4.txt @@ -54,7 +54,8 @@ PROPERTIES - compatible Usage: required Value type: - Definition: Must include "fsl,sec-v4.0" + Definition: Must include "fsl,sec-v4.0". Also includes SEC + ERA versions (optional) with which the device is compatible. - #address-cells Usage: required @@ -106,7 +107,7 @@ PROPERTIES EXAMPLE crypto@300000 { - compatible = "fsl,sec-v4.0"; + compatible = "fsl,sec-v4.0", "fsl,sec-era-v2.0"; #address-cells = <1>; #size-cells = <1>; reg = <0x300000 0x10000>; -- cgit v1.2.3-59-g8ed1b From f67605394f0ba44cff297120fda6bb407414ba75 Mon Sep 17 00:00:00 2001 From: Grant Likely Date: Thu, 6 Dec 2012 09:53:13 +0000 Subject: devicetree/bindings: Move gpio-leds binding into leds directory Merely reorganizing documentation. No functional changes. It makes more sense for the gpio-leds binding to be grouped with other led bindings than with gpio drivers. Signed-off-by: Grant Likely --- Documentation/devicetree/bindings/gpio/led.txt | 52 ---------------------- .../devicetree/bindings/leds/leds-gpio.txt | 52 ++++++++++++++++++++++ 2 files changed, 52 insertions(+), 52 deletions(-) delete mode 100644 Documentation/devicetree/bindings/gpio/led.txt create mode 100644 Documentation/devicetree/bindings/leds/leds-gpio.txt (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/gpio/led.txt b/Documentation/devicetree/bindings/gpio/led.txt deleted file mode 100644 index df1b3080f6b8..000000000000 --- a/Documentation/devicetree/bindings/gpio/led.txt +++ /dev/null @@ -1,52 +0,0 @@ -LEDs connected to GPIO lines - -Required properties: -- compatible : should be "gpio-leds". - -Each LED is represented as a sub-node of the gpio-leds device. Each -node's name represents the name of the corresponding LED. - -LED sub-node properties: -- gpios : Should specify the LED's GPIO, see "gpios property" in - Documentation/devicetree/bindings/gpio/gpio.txt. Active low LEDs should be - indicated using flags in the GPIO specifier. -- label : (optional) - see Documentation/devicetree/bindings/leds/common.txt -- linux,default-trigger : (optional) - see Documentation/devicetree/bindings/leds/common.txt -- default-state: (optional) The initial state of the LED. Valid - values are "on", "off", and "keep". If the LED is already on or off - and the default-state property is set the to same value, then no - glitch should be produced where the LED momentarily turns off (or - on). The "keep" setting will keep the LED at whatever its current - state is, without producing a glitch. The default is off if this - property is not present. - -Examples: - -leds { - compatible = "gpio-leds"; - hdd { - label = "IDE Activity"; - gpios = <&mcu_pio 0 1>; /* Active low */ - linux,default-trigger = "ide-disk"; - }; - - fault { - gpios = <&mcu_pio 1 0>; - /* Keep LED on if BIOS detected hardware fault */ - default-state = "keep"; - }; -}; - -run-control { - compatible = "gpio-leds"; - red { - gpios = <&mpc8572 6 0>; - default-state = "off"; - }; - green { - gpios = <&mpc8572 7 0>; - default-state = "on"; - }; -}; diff --git a/Documentation/devicetree/bindings/leds/leds-gpio.txt b/Documentation/devicetree/bindings/leds/leds-gpio.txt new file mode 100644 index 000000000000..df1b3080f6b8 --- /dev/null +++ b/Documentation/devicetree/bindings/leds/leds-gpio.txt @@ -0,0 +1,52 @@ +LEDs connected to GPIO lines + +Required properties: +- compatible : should be "gpio-leds". + +Each LED is represented as a sub-node of the gpio-leds device. Each +node's name represents the name of the corresponding LED. + +LED sub-node properties: +- gpios : Should specify the LED's GPIO, see "gpios property" in + Documentation/devicetree/bindings/gpio/gpio.txt. Active low LEDs should be + indicated using flags in the GPIO specifier. +- label : (optional) + see Documentation/devicetree/bindings/leds/common.txt +- linux,default-trigger : (optional) + see Documentation/devicetree/bindings/leds/common.txt +- default-state: (optional) The initial state of the LED. Valid + values are "on", "off", and "keep". If the LED is already on or off + and the default-state property is set the to same value, then no + glitch should be produced where the LED momentarily turns off (or + on). The "keep" setting will keep the LED at whatever its current + state is, without producing a glitch. The default is off if this + property is not present. + +Examples: + +leds { + compatible = "gpio-leds"; + hdd { + label = "IDE Activity"; + gpios = <&mcu_pio 0 1>; /* Active low */ + linux,default-trigger = "ide-disk"; + }; + + fault { + gpios = <&mcu_pio 1 0>; + /* Keep LED on if BIOS detected hardware fault */ + default-state = "keep"; + }; +}; + +run-control { + compatible = "gpio-leds"; + red { + gpios = <&mpc8572 6 0>; + default-state = "off"; + }; + green { + gpios = <&mpc8572 7 0>; + default-state = "on"; + }; +}; -- cgit v1.2.3-59-g8ed1b From 188cc0424e9bd7733ee3e412da6df2cf42701061 Mon Sep 17 00:00:00 2001 From: Loic Pallardy Date: Mon, 6 Aug 2012 17:12:29 +0200 Subject: mmc: core: Extend sysfs to ext_csd parameters for RPMB support Extend current sysfs access to ext_csd rpmb parameters (RPMB partition size) and rel_sector information. Signed-off-by: Loic Pallardy Reviewed-by: Namjae Jeon Acked-by: Linus Walleij Acked-by: Johan Rudholm Acked-by: Krishna Konda Signed-off-by: Chris Ball --- Documentation/mmc/mmc-dev-attrs.txt | 7 +++++++ drivers/mmc/core/mmc.c | 4 ++++ 2 files changed, 11 insertions(+) (limited to 'Documentation') diff --git a/Documentation/mmc/mmc-dev-attrs.txt b/Documentation/mmc/mmc-dev-attrs.txt index 22ae8441489f..0d98fac8893b 100644 --- a/Documentation/mmc/mmc-dev-attrs.txt +++ b/Documentation/mmc/mmc-dev-attrs.txt @@ -25,6 +25,8 @@ All attributes are read-only. serial Product Serial Number (from CID Register) erase_size Erase group size preferred_erase_size Preferred erase size + raw_rpmb_size_mult RPMB partition size + rel_sectors Reliable write sector count Note on Erase Size and Preferred Erase Size: @@ -65,6 +67,11 @@ Note on Erase Size and Preferred Erase Size: "preferred_erase_size" is in bytes. +Note on raw_rpmb_size_mult: + "raw_rpmb_size_mult" is a mutliple of 128kB block. + RPMB size in byte is calculated by using the following equation: + RPMB partition size = 128kB x raw_rpmb_size_mult + SD/MMC/SDIO Clock Gating Attribute ================================== diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c index 6c5576628ef1..f605d6e3d2cd 100644 --- a/drivers/mmc/core/mmc.c +++ b/drivers/mmc/core/mmc.c @@ -626,6 +626,8 @@ MMC_DEV_ATTR(serial, "0x%08x\n", card->cid.serial); MMC_DEV_ATTR(enhanced_area_offset, "%llu\n", card->ext_csd.enhanced_area_offset); MMC_DEV_ATTR(enhanced_area_size, "%u\n", card->ext_csd.enhanced_area_size); +MMC_DEV_ATTR(raw_rpmb_size_mult, "%#x\n", card->ext_csd.raw_rpmb_size_mult); +MMC_DEV_ATTR(rel_sectors, "%#x\n", card->ext_csd.rel_sectors); static struct attribute *mmc_std_attrs[] = { &dev_attr_cid.attr, @@ -641,6 +643,8 @@ static struct attribute *mmc_std_attrs[] = { &dev_attr_serial.attr, &dev_attr_enhanced_area_offset.attr, &dev_attr_enhanced_area_size.attr, + &dev_attr_raw_rpmb_size_mult.attr, + &dev_attr_rel_sectors.attr, NULL, }; -- cgit v1.2.3-59-g8ed1b From 3a96dff0f828ae9dfb43efd49a9b67a74c6dc360 Mon Sep 17 00:00:00 2001 From: Tony Prisk Date: Sun, 18 Nov 2012 15:33:06 +1300 Subject: mmc: SD/MMC Host Controller for Wondermedia WM8505/WM8650 This patch adds support for the SD/MMC host controller found on Wondermedia 8xxx series SoCs, currently supported under arm/arch-vt8500. A binding document is also included, based on mmc.txt with additional properties. Signed-off-by: Tony Prisk Signed-off-by: Chris Ball --- .../devicetree/bindings/mmc/vt8500-sdmmc.txt | 23 + MAINTAINERS | 1 + drivers/mmc/host/Kconfig | 11 + drivers/mmc/host/Makefile | 1 + drivers/mmc/host/wmt-sdmmc.c | 1029 ++++++++++++++++++++ 5 files changed, 1065 insertions(+) create mode 100644 Documentation/devicetree/bindings/mmc/vt8500-sdmmc.txt create mode 100644 drivers/mmc/host/wmt-sdmmc.c (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/mmc/vt8500-sdmmc.txt b/Documentation/devicetree/bindings/mmc/vt8500-sdmmc.txt new file mode 100644 index 000000000000..d7fb6abb3eb8 --- /dev/null +++ b/Documentation/devicetree/bindings/mmc/vt8500-sdmmc.txt @@ -0,0 +1,23 @@ +* Wondermedia WM8505/WM8650 SD/MMC Host Controller + +This file documents differences between the core properties described +by mmc.txt and the properties used by the wmt-sdmmc driver. + +Required properties: +- compatible: Should be "wm,wm8505-sdhc". +- interrupts: Two interrupts are required - regular irq and dma irq. + +Optional properties: +- sdon-inverted: SD_ON bit is inverted on the controller + +Examples: + +sdhc@d800a000 { + compatible = "wm,wm8505-sdhc"; + reg = <0xd800a000 0x1000>; + interrupts = <20 21>; + clocks = <&sdhc>; + bus-width = <4>; + sdon-inverted; +}; + diff --git a/MAINTAINERS b/MAINTAINERS index d2189fb78dcb..cfafe1b6b004 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1221,6 +1221,7 @@ F: drivers/video/wm8505fb* F: drivers/video/wmt_ge_rops.* F: drivers/tty/serial/vt8500_serial.c F: drivers/rtc/rtc-vt8500-c +F: drivers/mmc/host/wmt-sdmmc.c ARM/ZIPIT Z2 SUPPORT M: Marek Vasut diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig index 5c7c846e03ef..83eb1e06ff76 100644 --- a/drivers/mmc/host/Kconfig +++ b/drivers/mmc/host/Kconfig @@ -601,3 +601,14 @@ config MMC_USHC Note: These controllers only support SDIO cards and do not support MMC or SD memory cards. + +config MMC_WMT + tristate "Wondermedia SD/MMC Host Controller support" + depends on ARCH_VT8500 + default y + help + This selects support for the SD/MMC Host Controller on + Wondermedia WM8505/WM8650 based SoCs. + + To compile this driver as a module, choose M here: the + module will be called wmt-sdmmc. diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile index cd64b5df9703..39d5e1234709 100644 --- a/drivers/mmc/host/Makefile +++ b/drivers/mmc/host/Makefile @@ -44,6 +44,7 @@ obj-$(CONFIG_MMC_SH_MMCIF) += sh_mmcif.o obj-$(CONFIG_MMC_JZ4740) += jz4740_mmc.o obj-$(CONFIG_MMC_VUB300) += vub300.o obj-$(CONFIG_MMC_USHC) += ushc.o +obj-$(CONFIG_MMC_WMT) += wmt-sdmmc.o obj-$(CONFIG_MMC_SDHCI_PLTFM) += sdhci-pltfm.o obj-$(CONFIG_MMC_SDHCI_CNS3XXX) += sdhci-cns3xxx.o diff --git a/drivers/mmc/host/wmt-sdmmc.c b/drivers/mmc/host/wmt-sdmmc.c new file mode 100644 index 000000000000..5ba4605e4f80 --- /dev/null +++ b/drivers/mmc/host/wmt-sdmmc.c @@ -0,0 +1,1029 @@ +/* + * WM8505/WM8650 SD/MMC Host Controller + * + * Copyright (C) 2010 Tony Prisk + * Copyright (C) 2008 WonderMedia Technologies, Inc. + * + * 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 +#include +#include +#include + +#include +#include +#include + +#include + + +#define DRIVER_NAME "wmt-sdhc" + + +/* MMC/SD controller registers */ +#define SDMMC_CTLR 0x00 +#define SDMMC_CMD 0x01 +#define SDMMC_RSPTYPE 0x02 +#define SDMMC_ARG 0x04 +#define SDMMC_BUSMODE 0x08 +#define SDMMC_BLKLEN 0x0C +#define SDMMC_BLKCNT 0x0E +#define SDMMC_RSP 0x10 +#define SDMMC_CBCR 0x20 +#define SDMMC_INTMASK0 0x24 +#define SDMMC_INTMASK1 0x25 +#define SDMMC_STS0 0x28 +#define SDMMC_STS1 0x29 +#define SDMMC_STS2 0x2A +#define SDMMC_STS3 0x2B +#define SDMMC_RSPTIMEOUT 0x2C +#define SDMMC_CLK 0x30 /* VT8500 only */ +#define SDMMC_EXTCTRL 0x34 +#define SDMMC_SBLKLEN 0x38 +#define SDMMC_DMATIMEOUT 0x3C + + +/* SDMMC_CTLR bit fields */ +#define CTLR_CMD_START 0x01 +#define CTLR_CMD_WRITE 0x04 +#define CTLR_FIFO_RESET 0x08 + +/* SDMMC_BUSMODE bit fields */ +#define BM_SPI_MODE 0x01 +#define BM_FOURBIT_MODE 0x02 +#define BM_EIGHTBIT_MODE 0x04 +#define BM_SD_OFF 0x10 +#define BM_SPI_CS 0x20 +#define BM_SD_POWER 0x40 +#define BM_SOFT_RESET 0x80 +#define BM_ONEBIT_MASK 0xFD + +/* SDMMC_BLKLEN bit fields */ +#define BLKL_CRCERR_ABORT 0x0800 +#define BLKL_CD_POL_HIGH 0x1000 +#define BLKL_GPI_CD 0x2000 +#define BLKL_DATA3_CD 0x4000 +#define BLKL_INT_ENABLE 0x8000 + +/* SDMMC_INTMASK0 bit fields */ +#define INT0_MBLK_TRAN_DONE_INT_EN 0x10 +#define INT0_BLK_TRAN_DONE_INT_EN 0x20 +#define INT0_CD_INT_EN 0x40 +#define INT0_DI_INT_EN 0x80 + +/* SDMMC_INTMASK1 bit fields */ +#define INT1_CMD_RES_TRAN_DONE_INT_EN 0x02 +#define INT1_CMD_RES_TOUT_INT_EN 0x04 +#define INT1_MBLK_AUTO_STOP_INT_EN 0x08 +#define INT1_DATA_TOUT_INT_EN 0x10 +#define INT1_RESCRC_ERR_INT_EN 0x20 +#define INT1_RCRC_ERR_INT_EN 0x40 +#define INT1_WCRC_ERR_INT_EN 0x80 + +/* SDMMC_STS0 bit fields */ +#define STS0_WRITE_PROTECT 0x02 +#define STS0_CD_DATA3 0x04 +#define STS0_CD_GPI 0x08 +#define STS0_MBLK_DONE 0x10 +#define STS0_BLK_DONE 0x20 +#define STS0_CARD_DETECT 0x40 +#define STS0_DEVICE_INS 0x80 + +/* SDMMC_STS1 bit fields */ +#define STS1_SDIO_INT 0x01 +#define STS1_CMDRSP_DONE 0x02 +#define STS1_RSP_TIMEOUT 0x04 +#define STS1_AUTOSTOP_DONE 0x08 +#define STS1_DATA_TIMEOUT 0x10 +#define STS1_RSP_CRC_ERR 0x20 +#define STS1_RCRC_ERR 0x40 +#define STS1_WCRC_ERR 0x80 + +/* SDMMC_STS2 bit fields */ +#define STS2_CMD_RES_BUSY 0x10 +#define STS2_DATARSP_BUSY 0x20 +#define STS2_DIS_FORCECLK 0x80 + + +/* MMC/SD DMA Controller Registers */ +#define SDDMA_GCR 0x100 +#define SDDMA_IER 0x104 +#define SDDMA_ISR 0x108 +#define SDDMA_DESPR 0x10C +#define SDDMA_RBR 0x110 +#define SDDMA_DAR 0x114 +#define SDDMA_BAR 0x118 +#define SDDMA_CPR 0x11C +#define SDDMA_CCR 0x120 + + +/* SDDMA_GCR bit fields */ +#define DMA_GCR_DMA_EN 0x00000001 +#define DMA_GCR_SOFT_RESET 0x00000100 + +/* SDDMA_IER bit fields */ +#define DMA_IER_INT_EN 0x00000001 + +/* SDDMA_ISR bit fields */ +#define DMA_ISR_INT_STS 0x00000001 + +/* SDDMA_RBR bit fields */ +#define DMA_RBR_FORMAT 0x40000000 +#define DMA_RBR_END 0x80000000 + +/* SDDMA_CCR bit fields */ +#define DMA_CCR_RUN 0x00000080 +#define DMA_CCR_IF_TO_PERIPHERAL 0x00000000 +#define DMA_CCR_PERIPHERAL_TO_IF 0x00400000 + +/* SDDMA_CCR event status */ +#define DMA_CCR_EVT_NO_STATUS 0x00000000 +#define DMA_CCR_EVT_UNDERRUN 0x00000001 +#define DMA_CCR_EVT_OVERRUN 0x00000002 +#define DMA_CCR_EVT_DESP_READ 0x00000003 +#define DMA_CCR_EVT_DATA_RW 0x00000004 +#define DMA_CCR_EVT_EARLY_END 0x00000005 +#define DMA_CCR_EVT_SUCCESS 0x0000000F + +#define PDMA_READ 0x00 +#define PDMA_WRITE 0x01 + +#define WMT_SD_POWER_OFF 0 +#define WMT_SD_POWER_ON 1 + +struct wmt_dma_descriptor { + u32 flags; + u32 data_buffer_addr; + u32 branch_addr; + u32 reserved1; +}; + +struct wmt_mci_caps { + unsigned int f_min; + unsigned int f_max; + u32 ocr_avail; + u32 caps; + u32 max_seg_size; + u32 max_segs; + u32 max_blk_size; +}; + +struct wmt_mci_priv { + struct mmc_host *mmc; + void __iomem *sdmmc_base; + + int irq_regular; + int irq_dma; + + void *dma_desc_buffer; + dma_addr_t dma_desc_device_addr; + + struct completion cmdcomp; + struct completion datacomp; + + struct completion *comp_cmd; + struct completion *comp_dma; + + struct mmc_request *req; + struct mmc_command *cmd; + + struct clk *clk_sdmmc; + struct device *dev; + + u8 power_inverted; + u8 cd_inverted; +}; + +static void wmt_set_sd_power(struct wmt_mci_priv *priv, int enable) +{ + u32 reg_tmp; + if (enable) { + if (priv->power_inverted) { + reg_tmp = readb(priv->sdmmc_base + SDMMC_BUSMODE); + writeb(reg_tmp | BM_SD_OFF, + priv->sdmmc_base + SDMMC_BUSMODE); + } else { + reg_tmp = readb(priv->sdmmc_base + SDMMC_BUSMODE); + writeb(reg_tmp & (~BM_SD_OFF), + priv->sdmmc_base + SDMMC_BUSMODE); + } + } else { + if (priv->power_inverted) { + reg_tmp = readb(priv->sdmmc_base + SDMMC_BUSMODE); + writeb(reg_tmp & (~BM_SD_OFF), + priv->sdmmc_base + SDMMC_BUSMODE); + } else { + reg_tmp = readb(priv->sdmmc_base + SDMMC_BUSMODE); + writeb(reg_tmp | BM_SD_OFF, + priv->sdmmc_base + SDMMC_BUSMODE); + } + } +} + +static void wmt_mci_read_response(struct mmc_host *mmc) +{ + struct wmt_mci_priv *priv; + int idx1, idx2; + u8 tmp_resp; + u32 response; + + priv = mmc_priv(mmc); + + for (idx1 = 0; idx1 < 4; idx1++) { + response = 0; + for (idx2 = 0; idx2 < 4; idx2++) { + if ((idx1 == 3) && (idx2 == 3)) + tmp_resp = readb(priv->sdmmc_base + SDMMC_RSP); + else + tmp_resp = readb(priv->sdmmc_base + SDMMC_RSP + + (idx1*4) + idx2 + 1); + response |= (tmp_resp << (idx2 * 8)); + } + priv->cmd->resp[idx1] = cpu_to_be32(response); + } +} + +static void wmt_mci_start_command(struct wmt_mci_priv *priv) +{ + u32 reg_tmp; + + reg_tmp = readb(priv->sdmmc_base + SDMMC_CTLR); + writeb(reg_tmp | CTLR_CMD_START, priv->sdmmc_base + SDMMC_CTLR); +} + +static int wmt_mci_send_command(struct mmc_host *mmc, u8 command, u8 cmdtype, + u32 arg, u8 rsptype) +{ + struct wmt_mci_priv *priv; + u32 reg_tmp; + + priv = mmc_priv(mmc); + + /* write command, arg, resptype registers */ + writeb(command, priv->sdmmc_base + SDMMC_CMD); + writel(arg, priv->sdmmc_base + SDMMC_ARG); + writeb(rsptype, priv->sdmmc_base + SDMMC_RSPTYPE); + + /* reset response FIFO */ + reg_tmp = readb(priv->sdmmc_base + SDMMC_CTLR); + writeb(reg_tmp | CTLR_FIFO_RESET, priv->sdmmc_base + SDMMC_CTLR); + + /* ensure clock enabled - VT3465 */ + wmt_set_sd_power(priv, WMT_SD_POWER_ON); + + /* clear status bits */ + writeb(0xFF, priv->sdmmc_base + SDMMC_STS0); + writeb(0xFF, priv->sdmmc_base + SDMMC_STS1); + writeb(0xFF, priv->sdmmc_base + SDMMC_STS2); + writeb(0xFF, priv->sdmmc_base + SDMMC_STS3); + + /* set command type */ + reg_tmp = readb(priv->sdmmc_base + SDMMC_CTLR); + writeb((reg_tmp & 0x0F) | (cmdtype << 4), + priv->sdmmc_base + SDMMC_CTLR); + + return 0; +} + +static void wmt_mci_disable_dma(struct wmt_mci_priv *priv) +{ + writel(DMA_ISR_INT_STS, priv->sdmmc_base + SDDMA_ISR); + writel(0, priv->sdmmc_base + SDDMA_IER); +} + +static void wmt_complete_data_request(struct wmt_mci_priv *priv) +{ + struct mmc_request *req; + req = priv->req; + + req->data->bytes_xfered = req->data->blksz * req->data->blocks; + + /* unmap the DMA pages used for write data */ + if (req->data->flags & MMC_DATA_WRITE) + dma_unmap_sg(mmc_dev(priv->mmc), req->data->sg, + req->data->sg_len, DMA_TO_DEVICE); + else + dma_unmap_sg(mmc_dev(priv->mmc), req->data->sg, + req->data->sg_len, DMA_FROM_DEVICE); + + /* Check if the DMA ISR returned a data error */ + if ((req->cmd->error) || (req->data->error)) + mmc_request_done(priv->mmc, req); + else { + wmt_mci_read_response(priv->mmc); + if (!req->data->stop) { + /* single-block read/write requests end here */ + mmc_request_done(priv->mmc, req); + } else { + /* + * we change the priv->cmd variable so the response is + * stored in the stop struct rather than the original + * calling command struct + */ + priv->comp_cmd = &priv->cmdcomp; + init_completion(priv->comp_cmd); + priv->cmd = req->data->stop; + wmt_mci_send_command(priv->mmc, req->data->stop->opcode, + 7, req->data->stop->arg, 9); + wmt_mci_start_command(priv); + } + } +} + +static irqreturn_t wmt_mci_dma_isr(int irq_num, void *data) +{ + struct mmc_host *mmc; + struct wmt_mci_priv *priv; + + int status; + + priv = (struct wmt_mci_priv *)data; + mmc = priv->mmc; + + status = readl(priv->sdmmc_base + SDDMA_CCR) & 0x0F; + + if (status != DMA_CCR_EVT_SUCCESS) { + dev_err(priv->dev, "DMA Error: Status = %d\n", status); + priv->req->data->error = -ETIMEDOUT; + complete(priv->comp_dma); + return IRQ_HANDLED; + } + + priv->req->data->error = 0; + + wmt_mci_disable_dma(priv); + + complete(priv->comp_dma); + + if (priv->comp_cmd) { + if (completion_done(priv->comp_cmd)) { + /* + * if the command (regular) interrupt has already + * completed, finish off the request otherwise we wait + * for the command interrupt and finish from there. + */ + wmt_complete_data_request(priv); + } + } + + return IRQ_HANDLED; +} + +static irqreturn_t wmt_mci_regular_isr(int irq_num, void *data) +{ + struct wmt_mci_priv *priv; + u32 status0; + u32 status1; + u32 status2; + u32 reg_tmp; + int cmd_done; + + priv = (struct wmt_mci_priv *)data; + cmd_done = 0; + status0 = readb(priv->sdmmc_base + SDMMC_STS0); + status1 = readb(priv->sdmmc_base + SDMMC_STS1); + status2 = readb(priv->sdmmc_base + SDMMC_STS2); + + /* Check for card insertion */ + reg_tmp = readb(priv->sdmmc_base + SDMMC_INTMASK0); + if ((reg_tmp & INT0_DI_INT_EN) && (status0 & STS0_DEVICE_INS)) { + mmc_detect_change(priv->mmc, 0); + if (priv->cmd) + priv->cmd->error = -ETIMEDOUT; + if (priv->comp_cmd) + complete(priv->comp_cmd); + if (priv->comp_dma) { + wmt_mci_disable_dma(priv); + complete(priv->comp_dma); + } + writeb(STS0_DEVICE_INS, priv->sdmmc_base + SDMMC_STS0); + return IRQ_HANDLED; + } + + if ((!priv->req->data) || + ((priv->req->data->stop) && (priv->cmd == priv->req->data->stop))) { + /* handle non-data & stop_transmission requests */ + if (status1 & STS1_CMDRSP_DONE) { + priv->cmd->error = 0; + cmd_done = 1; + } else if ((status1 & STS1_RSP_TIMEOUT) || + (status1 & STS1_DATA_TIMEOUT)) { + priv->cmd->error = -ETIMEDOUT; + cmd_done = 1; + } + + if (cmd_done) { + priv->comp_cmd = NULL; + + if (!priv->cmd->error) + wmt_mci_read_response(priv->mmc); + + priv->cmd = NULL; + + mmc_request_done(priv->mmc, priv->req); + } + } else { + /* handle data requests */ + if (status1 & STS1_CMDRSP_DONE) { + if (priv->cmd) + priv->cmd->error = 0; + if (priv->comp_cmd) + complete(priv->comp_cmd); + } + + if ((status1 & STS1_RSP_TIMEOUT) || + (status1 & STS1_DATA_TIMEOUT)) { + if (priv->cmd) + priv->cmd->error = -ETIMEDOUT; + if (priv->comp_cmd) + complete(priv->comp_cmd); + if (priv->comp_dma) { + wmt_mci_disable_dma(priv); + complete(priv->comp_dma); + } + } + + if (priv->comp_dma) { + /* + * If the dma interrupt has already completed, finish + * off the request; otherwise we wait for the DMA + * interrupt and finish from there. + */ + if (completion_done(priv->comp_dma)) + wmt_complete_data_request(priv); + } + } + + writeb(status0, priv->sdmmc_base + SDMMC_STS0); + writeb(status1, priv->sdmmc_base + SDMMC_STS1); + writeb(status2, priv->sdmmc_base + SDMMC_STS2); + + return IRQ_HANDLED; +} + +static void wmt_reset_hardware(struct mmc_host *mmc) +{ + struct wmt_mci_priv *priv; + u32 reg_tmp; + + priv = mmc_priv(mmc); + + /* reset controller */ + reg_tmp = readb(priv->sdmmc_base + SDMMC_BUSMODE); + writeb(reg_tmp | BM_SOFT_RESET, priv->sdmmc_base + SDMMC_BUSMODE); + + /* reset response FIFO */ + reg_tmp = readb(priv->sdmmc_base + SDMMC_CTLR); + writeb(reg_tmp | CTLR_FIFO_RESET, priv->sdmmc_base + SDMMC_CTLR); + + /* enable GPI pin to detect card */ + writew(BLKL_INT_ENABLE | BLKL_GPI_CD, priv->sdmmc_base + SDMMC_BLKLEN); + + /* clear interrupt status */ + writeb(0xFF, priv->sdmmc_base + SDMMC_STS0); + writeb(0xFF, priv->sdmmc_base + SDMMC_STS1); + + /* setup interrupts */ + writeb(INT0_CD_INT_EN | INT0_DI_INT_EN, priv->sdmmc_base + + SDMMC_INTMASK0); + writeb(INT1_DATA_TOUT_INT_EN | INT1_CMD_RES_TRAN_DONE_INT_EN | + INT1_CMD_RES_TOUT_INT_EN, priv->sdmmc_base + SDMMC_INTMASK1); + + /* set the DMA timeout */ + writew(8191, priv->sdmmc_base + SDMMC_DMATIMEOUT); + + /* auto clock freezing enable */ + reg_tmp = readb(priv->sdmmc_base + SDMMC_STS2); + writeb(reg_tmp | STS2_DIS_FORCECLK, priv->sdmmc_base + SDMMC_STS2); + + /* set a default clock speed of 400Khz */ + clk_set_rate(priv->clk_sdmmc, 400000); +} + +static int wmt_dma_init(struct mmc_host *mmc) +{ + struct wmt_mci_priv *priv; + + priv = mmc_priv(mmc); + + writel(DMA_GCR_SOFT_RESET, priv->sdmmc_base + SDDMA_GCR); + writel(DMA_GCR_DMA_EN, priv->sdmmc_base + SDDMA_GCR); + if ((readl(priv->sdmmc_base + SDDMA_GCR) & DMA_GCR_DMA_EN) != 0) + return 0; + else + return 1; +} + +static void wmt_dma_init_descriptor(struct wmt_dma_descriptor *desc, + u16 req_count, u32 buffer_addr, u32 branch_addr, int end) +{ + desc->flags = 0x40000000 | req_count; + if (end) + desc->flags |= 0x80000000; + desc->data_buffer_addr = buffer_addr; + desc->branch_addr = branch_addr; +} + +static void wmt_dma_config(struct mmc_host *mmc, u32 descaddr, u8 dir) +{ + struct wmt_mci_priv *priv; + u32 reg_tmp; + + priv = mmc_priv(mmc); + + /* Enable DMA Interrupts */ + writel(DMA_IER_INT_EN, priv->sdmmc_base + SDDMA_IER); + + /* Write DMA Descriptor Pointer Register */ + writel(descaddr, priv->sdmmc_base + SDDMA_DESPR); + + writel(0x00, priv->sdmmc_base + SDDMA_CCR); + + if (dir == PDMA_WRITE) { + reg_tmp = readl(priv->sdmmc_base + SDDMA_CCR); + writel(reg_tmp & DMA_CCR_IF_TO_PERIPHERAL, priv->sdmmc_base + + SDDMA_CCR); + } else { + reg_tmp = readl(priv->sdmmc_base + SDDMA_CCR); + writel(reg_tmp | DMA_CCR_PERIPHERAL_TO_IF, priv->sdmmc_base + + SDDMA_CCR); + } +} + +static void wmt_dma_start(struct wmt_mci_priv *priv) +{ + u32 reg_tmp; + + reg_tmp = readl(priv->sdmmc_base + SDDMA_CCR); + writel(reg_tmp | DMA_CCR_RUN, priv->sdmmc_base + SDDMA_CCR); +} + +static void wmt_mci_request(struct mmc_host *mmc, struct mmc_request *req) +{ + struct wmt_mci_priv *priv; + struct wmt_dma_descriptor *desc; + u8 command; + u8 cmdtype; + u32 arg; + u8 rsptype; + u32 reg_tmp; + + struct scatterlist *sg; + int i; + int sg_cnt; + int offset; + u32 dma_address; + int desc_cnt; + + priv = mmc_priv(mmc); + priv->req = req; + + /* + * Use the cmd variable to pass a pointer to the resp[] structure + * This is required on multi-block requests to pass the pointer to the + * stop command + */ + priv->cmd = req->cmd; + + command = req->cmd->opcode; + arg = req->cmd->arg; + rsptype = mmc_resp_type(req->cmd); + cmdtype = 0; + + /* rsptype=7 only valid for SPI commands - should be =2 for SD */ + if (rsptype == 7) + rsptype = 2; + /* rsptype=21 is R1B, convert for controller */ + if (rsptype == 21) + rsptype = 9; + + if (!req->data) { + wmt_mci_send_command(mmc, command, cmdtype, arg, rsptype); + wmt_mci_start_command(priv); + /* completion is now handled in the regular_isr() */ + } + if (req->data) { + priv->comp_cmd = &priv->cmdcomp; + init_completion(priv->comp_cmd); + + wmt_dma_init(mmc); + + /* set controller data length */ + reg_tmp = readw(priv->sdmmc_base + SDMMC_BLKLEN); + writew((reg_tmp & 0xF800) | (req->data->blksz - 1), + priv->sdmmc_base + SDMMC_BLKLEN); + + /* set controller block count */ + writew(req->data->blocks, priv->sdmmc_base + SDMMC_BLKCNT); + + desc = (struct wmt_dma_descriptor *)priv->dma_desc_buffer; + + if (req->data->flags & MMC_DATA_WRITE) { + sg_cnt = dma_map_sg(mmc_dev(mmc), req->data->sg, + req->data->sg_len, DMA_TO_DEVICE); + cmdtype = 1; + if (req->data->blocks > 1) + cmdtype = 3; + } else { + sg_cnt = dma_map_sg(mmc_dev(mmc), req->data->sg, + req->data->sg_len, DMA_FROM_DEVICE); + cmdtype = 2; + if (req->data->blocks > 1) + cmdtype = 4; + } + + dma_address = priv->dma_desc_device_addr + 16; + desc_cnt = 0; + + for_each_sg(req->data->sg, sg, sg_cnt, i) { + offset = 0; + while (offset < sg_dma_len(sg)) { + wmt_dma_init_descriptor(desc, req->data->blksz, + sg_dma_address(sg)+offset, + dma_address, 0); + desc++; + desc_cnt++; + offset += req->data->blksz; + dma_address += 16; + if (desc_cnt == req->data->blocks) + break; + } + } + desc--; + desc->flags |= 0x80000000; + + if (req->data->flags & MMC_DATA_WRITE) + wmt_dma_config(mmc, priv->dma_desc_device_addr, + PDMA_WRITE); + else + wmt_dma_config(mmc, priv->dma_desc_device_addr, + PDMA_READ); + + wmt_mci_send_command(mmc, command, cmdtype, arg, rsptype); + + priv->comp_dma = &priv->datacomp; + init_completion(priv->comp_dma); + + wmt_dma_start(priv); + wmt_mci_start_command(priv); + } +} + +static void wmt_mci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) +{ + struct wmt_mci_priv *priv; + u32 reg_tmp; + + priv = mmc_priv(mmc); + + if (ios->power_mode == MMC_POWER_UP) { + wmt_reset_hardware(mmc); + + wmt_set_sd_power(priv, WMT_SD_POWER_ON); + } + if (ios->power_mode == MMC_POWER_OFF) + wmt_set_sd_power(priv, WMT_SD_POWER_OFF); + + if (ios->clock != 0) + clk_set_rate(priv->clk_sdmmc, ios->clock); + + switch (ios->bus_width) { + case MMC_BUS_WIDTH_8: + reg_tmp = readb(priv->sdmmc_base + SDMMC_EXTCTRL); + writeb(reg_tmp | 0x04, priv->sdmmc_base + SDMMC_EXTCTRL); + break; + case MMC_BUS_WIDTH_4: + reg_tmp = readb(priv->sdmmc_base + SDMMC_BUSMODE); + writeb(reg_tmp | BM_FOURBIT_MODE, priv->sdmmc_base + + SDMMC_BUSMODE); + + reg_tmp = readb(priv->sdmmc_base + SDMMC_EXTCTRL); + writeb(reg_tmp & 0xFB, priv->sdmmc_base + SDMMC_EXTCTRL); + break; + case MMC_BUS_WIDTH_1: + reg_tmp = readb(priv->sdmmc_base + SDMMC_BUSMODE); + writeb(reg_tmp & BM_ONEBIT_MASK, priv->sdmmc_base + + SDMMC_BUSMODE); + + reg_tmp = readb(priv->sdmmc_base + SDMMC_EXTCTRL); + writeb(reg_tmp & 0xFB, priv->sdmmc_base + SDMMC_EXTCTRL); + break; + } +} + +static int wmt_mci_get_ro(struct mmc_host *mmc) +{ + struct wmt_mci_priv *priv = mmc_priv(mmc); + + return !(readb(priv->sdmmc_base + SDMMC_STS0) & STS0_WRITE_PROTECT); +} + +static int wmt_mci_get_cd(struct mmc_host *mmc) +{ + struct wmt_mci_priv *priv = mmc_priv(mmc); + u32 cd = (readb(priv->sdmmc_base + SDMMC_STS0) & STS0_CD_GPI) >> 3; + + return !(cd ^ priv->cd_inverted); +} + +static struct mmc_host_ops wmt_mci_ops = { + .request = wmt_mci_request, + .set_ios = wmt_mci_set_ios, + .get_ro = wmt_mci_get_ro, + .get_cd = wmt_mci_get_cd, +}; + +/* Controller capabilities */ +static struct wmt_mci_caps wm8505_caps = { + .f_min = 390425, + .f_max = 50000000, + .ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34, + .caps = MMC_CAP_4_BIT_DATA | MMC_CAP_MMC_HIGHSPEED | + MMC_CAP_SD_HIGHSPEED, + .max_seg_size = 65024, + .max_segs = 128, + .max_blk_size = 2048, +}; + +static struct of_device_id wmt_mci_dt_ids[] = { + { .compatible = "wm,wm8505-sdhc", .data = &wm8505_caps }, + { /* Sentinel */ }, +}; + +static int __devinit wmt_mci_probe(struct platform_device *pdev) +{ + struct mmc_host *mmc; + struct wmt_mci_priv *priv; + struct device_node *np = pdev->dev.of_node; + const struct of_device_id *of_id = + of_match_device(wmt_mci_dt_ids, &pdev->dev); + const struct wmt_mci_caps *wmt_caps = of_id->data; + int ret; + int regular_irq, dma_irq; + + if (!of_id || !of_id->data) { + dev_err(&pdev->dev, "Controller capabilities data missing\n"); + return -EFAULT; + } + + if (!np) { + dev_err(&pdev->dev, "Missing SDMMC description in devicetree\n"); + return -EFAULT; + } + + regular_irq = irq_of_parse_and_map(np, 0); + dma_irq = irq_of_parse_and_map(np, 1); + + if (!regular_irq || !dma_irq) { + dev_err(&pdev->dev, "Getting IRQs failed!\n"); + ret = -ENXIO; + goto fail1; + } + + mmc = mmc_alloc_host(sizeof(struct wmt_mci_priv), &pdev->dev); + if (!mmc) { + dev_err(&pdev->dev, "Failed to allocate mmc_host\n"); + ret = -ENOMEM; + goto fail1; + } + + mmc->ops = &wmt_mci_ops; + mmc->f_min = wmt_caps->f_min; + mmc->f_max = wmt_caps->f_max; + mmc->ocr_avail = wmt_caps->ocr_avail; + mmc->caps = wmt_caps->caps; + + mmc->max_seg_size = wmt_caps->max_seg_size; + mmc->max_segs = wmt_caps->max_segs; + mmc->max_blk_size = wmt_caps->max_blk_size; + + mmc->max_req_size = (16*512*mmc->max_segs); + mmc->max_blk_count = mmc->max_req_size / 512; + + priv = mmc_priv(mmc); + priv->mmc = mmc; + priv->dev = &pdev->dev; + + priv->power_inverted = 0; + priv->cd_inverted = 0; + + if (of_get_property(np, "sdon-inverted", NULL)) + priv->power_inverted = 1; + if (of_get_property(np, "cd-inverted", NULL)) + priv->cd_inverted = 1; + + priv->sdmmc_base = of_iomap(np, 0); + if (!priv->sdmmc_base) { + dev_err(&pdev->dev, "Failed to map IO space\n"); + ret = -ENOMEM; + goto fail2; + } + + priv->irq_regular = regular_irq; + priv->irq_dma = dma_irq; + + ret = request_irq(regular_irq, wmt_mci_regular_isr, 0, "sdmmc", priv); + if (ret) { + dev_err(&pdev->dev, "Register regular IRQ fail\n"); + goto fail3; + } + + ret = request_irq(dma_irq, wmt_mci_dma_isr, 32, "sdmmc", priv); + if (ret) { + dev_err(&pdev->dev, "Register DMA IRQ fail\n"); + goto fail4; + } + + /* alloc some DMA buffers for descriptors/transfers */ + priv->dma_desc_buffer = dma_alloc_coherent(&pdev->dev, + mmc->max_blk_count * 16, + &priv->dma_desc_device_addr, + 208); + if (!priv->dma_desc_buffer) { + dev_err(&pdev->dev, "DMA alloc fail\n"); + ret = -EPERM; + goto fail5; + } + + platform_set_drvdata(pdev, mmc); + + priv->clk_sdmmc = of_clk_get(np, 0); + if (IS_ERR(priv->clk_sdmmc)) { + dev_err(&pdev->dev, "Error getting clock\n"); + ret = PTR_ERR(priv->clk_sdmmc); + goto fail5; + } + + clk_prepare_enable(priv->clk_sdmmc); + + /* configure the controller to a known 'ready' state */ + wmt_reset_hardware(mmc); + + mmc_add_host(mmc); + + dev_info(&pdev->dev, "WMT SDHC Controller initialized\n"); + + return 0; +fail5: + free_irq(dma_irq, priv); +fail4: + free_irq(regular_irq, priv); +fail3: + iounmap(priv->sdmmc_base); +fail2: + mmc_free_host(mmc); +fail1: + return ret; +} + +static int __devexit wmt_mci_remove(struct platform_device *pdev) +{ + struct mmc_host *mmc; + struct wmt_mci_priv *priv; + struct resource *res; + u32 reg_tmp; + + mmc = platform_get_drvdata(pdev); + priv = mmc_priv(mmc); + + /* reset SD controller */ + reg_tmp = readb(priv->sdmmc_base + SDMMC_BUSMODE); + writel(reg_tmp | BM_SOFT_RESET, priv->sdmmc_base + SDMMC_BUSMODE); + reg_tmp = readw(priv->sdmmc_base + SDMMC_BLKLEN); + writew(reg_tmp & ~(0xA000), priv->sdmmc_base + SDMMC_BLKLEN); + writeb(0xFF, priv->sdmmc_base + SDMMC_STS0); + writeb(0xFF, priv->sdmmc_base + SDMMC_STS1); + + /* release the dma buffers */ + dma_free_coherent(&pdev->dev, priv->mmc->max_blk_count * 16, + priv->dma_desc_buffer, priv->dma_desc_device_addr); + + mmc_remove_host(mmc); + + free_irq(priv->irq_regular, priv); + free_irq(priv->irq_dma, priv); + + iounmap(priv->sdmmc_base); + + clk_disable_unprepare(priv->clk_sdmmc); + clk_put(priv->clk_sdmmc); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + release_mem_region(res->start, res->end - res->start + 1); + + mmc_free_host(mmc); + + platform_set_drvdata(pdev, NULL); + + dev_info(&pdev->dev, "WMT MCI device removed\n"); + + return 0; +} + +#ifdef CONFIG_PM +static int wmt_mci_suspend(struct device *dev) +{ + u32 reg_tmp; + struct platform_device *pdev = to_platform_device(dev); + struct mmc_host *mmc = platform_get_drvdata(pdev); + struct wmt_mci_priv *priv; + int ret; + + if (!mmc) + return 0; + + priv = mmc_priv(mmc); + ret = mmc_suspend_host(mmc); + + if (!ret) { + reg_tmp = readb(priv->sdmmc_base + SDMMC_BUSMODE); + writeb(reg_tmp | BM_SOFT_RESET, priv->sdmmc_base + + SDMMC_BUSMODE); + + reg_tmp = readw(priv->sdmmc_base + SDMMC_BLKLEN); + writew(reg_tmp & 0x5FFF, priv->sdmmc_base + SDMMC_BLKLEN); + + writeb(0xFF, priv->sdmmc_base + SDMMC_STS0); + writeb(0xFF, priv->sdmmc_base + SDMMC_STS1); + + clk_disable(priv->clk_sdmmc); + } + return ret; +} + +static int wmt_mci_resume(struct device *dev) +{ + u32 reg_tmp; + struct platform_device *pdev = to_platform_device(dev); + struct mmc_host *mmc = platform_get_drvdata(pdev); + struct wmt_mci_priv *priv; + int ret = 0; + + if (mmc) { + priv = mmc_priv(mmc); + clk_enable(priv->clk_sdmmc); + + reg_tmp = readb(priv->sdmmc_base + SDMMC_BUSMODE); + writeb(reg_tmp | BM_SOFT_RESET, priv->sdmmc_base + + SDMMC_BUSMODE); + + reg_tmp = readw(priv->sdmmc_base + SDMMC_BLKLEN); + writew(reg_tmp | (BLKL_GPI_CD | BLKL_INT_ENABLE), + priv->sdmmc_base + SDMMC_BLKLEN); + + reg_tmp = readb(priv->sdmmc_base + SDMMC_INTMASK0); + writeb(reg_tmp | INT0_DI_INT_EN, priv->sdmmc_base + + SDMMC_INTMASK0); + + ret = mmc_resume_host(mmc); + } + + return ret; +} + +static const struct dev_pm_ops wmt_mci_pm = { + .suspend = wmt_mci_suspend, + .resume = wmt_mci_resume, +}; + +#define wmt_mci_pm_ops (&wmt_mci_pm) + +#else /* !CONFIG_PM */ + +#define wmt_mci_pm_ops NULL + +#endif + +static struct platform_driver wmt_mci_driver = { + .probe = wmt_mci_probe, + .remove = __exit_p(wmt_mci_remove), + .driver = { + .name = DRIVER_NAME, + .owner = THIS_MODULE, + .pm = wmt_mci_pm_ops, + .of_match_table = wmt_mci_dt_ids, + }, +}; + +module_platform_driver(wmt_mci_driver); + +MODULE_DESCRIPTION("Wondermedia MMC/SD Driver"); +MODULE_AUTHOR("Tony Prisk"); +MODULE_LICENSE("GPL v2"); +MODULE_DEVICE_TABLE(of, wmt_mci_dt_ids); -- cgit v1.2.3-59-g8ed1b From 9f2f7cce3293dea973b320ab4b95b90b8ea2ec20 Mon Sep 17 00:00:00 2001 From: Abhilash Kesavan Date: Mon, 19 Nov 2012 10:26:18 +0530 Subject: mmc: dt: Fix typo in filename Fix typo in the synopsis dwmmc controller dt binding filename. Signed-off-by: Abhilash Kesavan Signed-off-by: Chris Ball --- .../devicetree/bindings/mmc/synopsis-dw-mshc.txt | 79 ++++++++++++++++++++++ .../devicetree/bindings/mmc/synposis-dw-mshc.txt | 79 ---------------------- 2 files changed, 79 insertions(+), 79 deletions(-) create mode 100644 Documentation/devicetree/bindings/mmc/synopsis-dw-mshc.txt delete mode 100644 Documentation/devicetree/bindings/mmc/synposis-dw-mshc.txt (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/mmc/synopsis-dw-mshc.txt b/Documentation/devicetree/bindings/mmc/synopsis-dw-mshc.txt new file mode 100644 index 000000000000..06cd32d08052 --- /dev/null +++ b/Documentation/devicetree/bindings/mmc/synopsis-dw-mshc.txt @@ -0,0 +1,79 @@ +* Synopsis Designware Mobile Storage Host Controller + +The Synopsis designware mobile storage host controller is used to interface +a SoC with storage medium such as eMMC or SD/MMC cards. This file documents +differences between the core mmc properties described by mmc.txt and the +properties used by the Synopsis Designware Mobile Storage Host Controller. + +Required Properties: + +* compatible: should be + - snps,dw-mshc: for controllers compliant with synopsis dw-mshc. +* #address-cells: should be 1. +* #size-cells: should be 0. + +# Slots: The slot specific information are contained within child-nodes with + each child-node representing a supported slot. There should be atleast one + child node representing a card slot. The name of the child node representing + the slot is recommended to be slot@n where n is the unique number of the slot + connnected to the controller. The following are optional properties which + can be included in the slot child node. + + * reg: specifies the physical slot number. The valid values of this + property is 0 to (num-slots -1), where num-slots is the value + specified by the num-slots property. + + * bus-width: as documented in mmc core bindings. + + * wp-gpios: specifies the write protect gpio line. The format of the + gpio specifier depends on the gpio controller. If the write-protect + line is not available, this property is optional. + +Optional properties: + +* num-slots: specifies the number of slots supported by the controller. + The number of physical slots actually used could be equal or less than the + value specified by num-slots. If this property is not specified, the value + of num-slot property is assumed to be 1. + +* fifo-depth: The maximum size of the tx/rx fifo's. If this property is not + specified, the default value of the fifo size is determined from the + controller registers. + +* card-detect-delay: Delay in milli-seconds before detecting card after card + insert event. The default value is 0. + +* supports-highspeed: Enables support for high speed cards (upto 50MHz) + +* broken-cd: as documented in mmc core bindings. + +Aliases: + +- All the MSHC controller nodes should be represented in the aliases node using + the following format 'mshc{n}' where n is a unique number for the alias. + +Example: + +The MSHC controller node can be split into two portions, SoC specific and +board specific portions as listed below. + + dwmmc0@12200000 { + compatible = "snps,dw-mshc"; + reg = <0x12200000 0x1000>; + interrupts = <0 75 0>; + #address-cells = <1>; + #size-cells = <0>; + }; + + dwmmc0@12200000 { + num-slots = <1>; + supports-highspeed; + broken-cd; + fifo-depth = <0x80>; + card-detect-delay = <200>; + + slot@0 { + reg = <0>; + bus-width = <8>; + }; + }; diff --git a/Documentation/devicetree/bindings/mmc/synposis-dw-mshc.txt b/Documentation/devicetree/bindings/mmc/synposis-dw-mshc.txt deleted file mode 100644 index 06cd32d08052..000000000000 --- a/Documentation/devicetree/bindings/mmc/synposis-dw-mshc.txt +++ /dev/null @@ -1,79 +0,0 @@ -* Synopsis Designware Mobile Storage Host Controller - -The Synopsis designware mobile storage host controller is used to interface -a SoC with storage medium such as eMMC or SD/MMC cards. This file documents -differences between the core mmc properties described by mmc.txt and the -properties used by the Synopsis Designware Mobile Storage Host Controller. - -Required Properties: - -* compatible: should be - - snps,dw-mshc: for controllers compliant with synopsis dw-mshc. -* #address-cells: should be 1. -* #size-cells: should be 0. - -# Slots: The slot specific information are contained within child-nodes with - each child-node representing a supported slot. There should be atleast one - child node representing a card slot. The name of the child node representing - the slot is recommended to be slot@n where n is the unique number of the slot - connnected to the controller. The following are optional properties which - can be included in the slot child node. - - * reg: specifies the physical slot number. The valid values of this - property is 0 to (num-slots -1), where num-slots is the value - specified by the num-slots property. - - * bus-width: as documented in mmc core bindings. - - * wp-gpios: specifies the write protect gpio line. The format of the - gpio specifier depends on the gpio controller. If the write-protect - line is not available, this property is optional. - -Optional properties: - -* num-slots: specifies the number of slots supported by the controller. - The number of physical slots actually used could be equal or less than the - value specified by num-slots. If this property is not specified, the value - of num-slot property is assumed to be 1. - -* fifo-depth: The maximum size of the tx/rx fifo's. If this property is not - specified, the default value of the fifo size is determined from the - controller registers. - -* card-detect-delay: Delay in milli-seconds before detecting card after card - insert event. The default value is 0. - -* supports-highspeed: Enables support for high speed cards (upto 50MHz) - -* broken-cd: as documented in mmc core bindings. - -Aliases: - -- All the MSHC controller nodes should be represented in the aliases node using - the following format 'mshc{n}' where n is a unique number for the alias. - -Example: - -The MSHC controller node can be split into two portions, SoC specific and -board specific portions as listed below. - - dwmmc0@12200000 { - compatible = "snps,dw-mshc"; - reg = <0x12200000 0x1000>; - interrupts = <0 75 0>; - #address-cells = <1>; - #size-cells = <0>; - }; - - dwmmc0@12200000 { - num-slots = <1>; - supports-highspeed; - broken-cd; - fifo-depth = <0x80>; - card-detect-delay = <200>; - - slot@0 { - reg = <0>; - bus-width = <8>; - }; - }; -- cgit v1.2.3-59-g8ed1b From e5d0e9c567389e261fbad6311ea78e6b8d4a24e9 Mon Sep 17 00:00:00 2001 From: Abhilash Kesavan Date: Mon, 19 Nov 2012 10:26:19 +0530 Subject: mmc: dt: Add optional pm properties to core binding Add documentation for pm capabilties such as MMC_PM_KEEP_POWER and MMC_PM_WAKE_SDIO_IRQ. Signed-off-by: Abhilash Kesavan Signed-off-by: Chris Ball --- Documentation/devicetree/bindings/mmc/mmc.txt | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/mmc/mmc.txt b/Documentation/devicetree/bindings/mmc/mmc.txt index 8e2e0ba2f486..b96e510979bf 100644 --- a/Documentation/devicetree/bindings/mmc/mmc.txt +++ b/Documentation/devicetree/bindings/mmc/mmc.txt @@ -22,6 +22,10 @@ Optional properties: - wp-inverted: when present, polarity on the wp gpio line is inverted - max-frequency: maximum operating clock frequency +Optional SDIO properties: +- keep-power-in-suspend: Preserves card power during a suspend/resume cycle +- enable-sdio-wakeup: Enables wake up of host system on SDIO IRQ assertion + Example: sdhci@ab000000 { @@ -33,4 +37,6 @@ sdhci@ab000000 { cd-inverted; wp-gpios = <&gpio 70 0>; max-frequency = <50000000>; + keep-power-in-suspend; + enable-sdio-wakeup; } -- cgit v1.2.3-59-g8ed1b From cd587096c0e2b85a67e77721a753679bac89b394 Mon Sep 17 00:00:00 2001 From: "Hebbar, Gururaja" Date: Mon, 19 Nov 2012 21:59:58 +0530 Subject: mmc: omap_hsmmc: Enable HSPE bit for high speed cards HSMMC IP on AM33xx need a special setting to handle High-speed cards. Other platforms like TI81xx, OMAP4 may need this as-well. This depends on the HSMMC IP timing closure done for the high speed cards. From AM335x TRM (SPRUH73F - 18.3.12 Output Signals Generation): The MMC/SD/SDIO output signals can be driven on either falling edge or rising edge depending on the SD_HCTL[2] HSPE bit. This feature allows to reach better timing performance, and thus to increase data transfer frequency. There are few pre-requisites for enabling the HSPE bit - Controller should support High-Speed-Enable Bit and - Controller should not be using DDR Mode and - Controller should advertise that it supports High Speed in capabilities register and - MMC/SD clock coming out of controller > 25MHz Signed-off-by: Hebbar, Gururaja Signed-off-by: Venkatraman S Signed-off-by: Chris Ball --- .../devicetree/bindings/mmc/ti-omap-hsmmc.txt | 1 + arch/arm/plat-omap/include/plat/mmc.h | 1 + drivers/mmc/host/omap_hsmmc.c | 30 +++++++++++++++++++++- 3 files changed, 31 insertions(+), 1 deletion(-) (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/mmc/ti-omap-hsmmc.txt b/Documentation/devicetree/bindings/mmc/ti-omap-hsmmc.txt index be76a23b34c4..ed271fc255b2 100644 --- a/Documentation/devicetree/bindings/mmc/ti-omap-hsmmc.txt +++ b/Documentation/devicetree/bindings/mmc/ti-omap-hsmmc.txt @@ -19,6 +19,7 @@ ti,dual-volt: boolean, supports dual voltage cards "supply-name" examples are "vmmc", "vmmc_aux" etc ti,non-removable: non-removable slot (like eMMC) ti,needs-special-reset: Requires a special softreset sequence +ti,needs-special-hs-handling: HSMMC IP needs special setting for handling High Speed Example: mmc1: mmc@0x4809c000 { diff --git a/arch/arm/plat-omap/include/plat/mmc.h b/arch/arm/plat-omap/include/plat/mmc.h index 8b4e4f2da2f5..346af5b490be 100644 --- a/arch/arm/plat-omap/include/plat/mmc.h +++ b/arch/arm/plat-omap/include/plat/mmc.h @@ -126,6 +126,7 @@ struct omap_mmc_platform_data { /* we can put the features above into this variable */ #define HSMMC_HAS_PBIAS (1 << 0) #define HSMMC_HAS_UPDATED_RESET (1 << 1) +#define HSMMC_HAS_HSPE_SUPPORT (1 << 2) unsigned features; int switch_pin; /* gpio (card detect) */ diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c index 571cd80521c7..18d3f19ef4e5 100644 --- a/drivers/mmc/host/omap_hsmmc.c +++ b/drivers/mmc/host/omap_hsmmc.c @@ -63,6 +63,7 @@ #define VS18 (1 << 26) #define VS30 (1 << 25) +#define HSS (1 << 21) #define SDVS18 (0x5 << 9) #define SDVS30 (0x6 << 9) #define SDVS33 (0x7 << 9) @@ -90,6 +91,7 @@ #define MSBS (1 << 5) #define BCE (1 << 1) #define FOUR_BIT (1 << 1) +#define HSPE (1 << 2) #define DDR (1 << 19) #define DW8 (1 << 5) #define CC 0x1 @@ -495,6 +497,7 @@ static void omap_hsmmc_set_clock(struct omap_hsmmc_host *host) struct mmc_ios *ios = &host->mmc->ios; unsigned long regval; unsigned long timeout; + unsigned long clkdiv; dev_vdbg(mmc_dev(host->mmc), "Set clock to %uHz\n", ios->clock); @@ -502,7 +505,8 @@ static void omap_hsmmc_set_clock(struct omap_hsmmc_host *host) regval = OMAP_HSMMC_READ(host->base, SYSCTL); regval = regval & ~(CLKD_MASK | DTO_MASK); - regval = regval | (calc_divisor(host, ios) << 6) | (DTO << 16); + clkdiv = calc_divisor(host, ios); + regval = regval | (clkdiv << 6) | (DTO << 16); OMAP_HSMMC_WRITE(host->base, SYSCTL, regval); OMAP_HSMMC_WRITE(host->base, SYSCTL, OMAP_HSMMC_READ(host->base, SYSCTL) | ICE); @@ -513,6 +517,27 @@ static void omap_hsmmc_set_clock(struct omap_hsmmc_host *host) && time_before(jiffies, timeout)) cpu_relax(); + /* + * Enable High-Speed Support + * Pre-Requisites + * - Controller should support High-Speed-Enable Bit + * - Controller should not be using DDR Mode + * - Controller should advertise that it supports High Speed + * in capabilities register + * - MMC/SD clock coming out of controller > 25MHz + */ + if ((mmc_slot(host).features & HSMMC_HAS_HSPE_SUPPORT) && + (ios->timing != MMC_TIMING_UHS_DDR50) && + ((OMAP_HSMMC_READ(host->base, CAPA) & HSS) == HSS)) { + regval = OMAP_HSMMC_READ(host->base, HCTL); + if (clkdiv && (clk_get_rate(host->fclk)/clkdiv) > 25000000) + regval |= HSPE; + else + regval &= ~HSPE; + + OMAP_HSMMC_WRITE(host->base, HCTL, regval); + } + omap_hsmmc_start_clock(host); } @@ -1715,6 +1740,9 @@ static struct omap_mmc_platform_data *of_get_hsmmc_pdata(struct device *dev) if (!of_property_read_u32(np, "max-frequency", &max_freq)) pdata->max_freq = max_freq; + if (of_find_property(np, "ti,needs-special-hs-handling", NULL)) + pdata->slots[0].features |= HSMMC_HAS_HSPE_SUPPORT; + return pdata; } #else -- cgit v1.2.3-59-g8ed1b From b96efccb8fc9939605599e5b2c026644973cc92d Mon Sep 17 00:00:00 2001 From: Tomasz Figa Date: Fri, 16 Nov 2012 15:28:17 +0100 Subject: mmc: host: sdhci-s3c: Add support for pinctrl This patch adds support for pin configuration using pinctrl subsystem to the sdhci-s3c driver. Signed-off-by: Tomasz Figa Acked-by: Thomas Abraham Signed-off-by: Chris Ball --- .../devicetree/bindings/mmc/samsung-sdhci.txt | 20 +++++++++++++------- drivers/mmc/host/sdhci-s3c.c | 12 ++++++++++-- 2 files changed, 23 insertions(+), 9 deletions(-) (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/mmc/samsung-sdhci.txt b/Documentation/devicetree/bindings/mmc/samsung-sdhci.txt index 630a7d7f4718..97e9e315400d 100644 --- a/Documentation/devicetree/bindings/mmc/samsung-sdhci.txt +++ b/Documentation/devicetree/bindings/mmc/samsung-sdhci.txt @@ -12,10 +12,6 @@ is used. The Samsung's SDHCI controller bindings extends this as listed below. [A] The property "samsung,cd-pinmux-gpio" can be used as stated in the "Optional Board Specific Properties" section below. -[B] If core card-detect bindings and "samsung,cd-pinmux-gpio" property - is not specified, it is assumed that there is no card detection - mechanism used. - Required SoC Specific Properties: - compatible: should be one of the following - "samsung,s3c6410-sdhci": For controllers compatible with s3c6410 sdhci @@ -24,14 +20,18 @@ Required SoC Specific Properties: controller. Required Board Specific Properties: -- gpios: Should specify the gpios used for clock, command and data lines. The - gpio specifier format depends on the gpio controller. +- Samsung GPIO variant (will be completely replaced by pinctrl): + - gpios: Should specify the gpios used for clock, command and data lines. The + gpio specifier format depends on the gpio controller. +- Pinctrl variant (preferred if available): + - pinctrl-0: Should specify pin control groups used for this controller. + - pinctrl-names: Should contain only one value - "default". Optional Board Specific Properties: - samsung,cd-pinmux-gpio: Specifies the card detect line that is routed through a pinmux to the card-detect pin of the card slot. This property should be used only if none of the mmc core card-detect properties are - used. + used. Only for Samsung GPIO variant. Example: sdhci@12530000 { @@ -40,12 +40,18 @@ Example: interrupts = <0 75 0>; bus-width = <4>; cd-gpios = <&gpk2 2 2 3 3>; + + /* Samsung GPIO variant */ gpios = <&gpk2 0 2 0 3>, /* clock line */ <&gpk2 1 2 0 3>, /* command line */ <&gpk2 3 2 3 3>, /* data line 0 */ <&gpk2 4 2 3 3>, /* data line 1 */ <&gpk2 5 2 3 3>, /* data line 2 */ <&gpk2 6 2 3 3>; /* data line 3 */ + + /* Pinctrl variant */ + pinctrl-0 = <&sd0_clk &sd0_cmd &sd0_bus4>; + pinctrl-names = "default"; }; Note: This example shows both SoC specific and board specific properties diff --git a/drivers/mmc/host/sdhci-s3c.c b/drivers/mmc/host/sdhci-s3c.c index 93c5d62fcaff..99697696e7eb 100644 --- a/drivers/mmc/host/sdhci-s3c.c +++ b/drivers/mmc/host/sdhci-s3c.c @@ -24,6 +24,7 @@ #include #include #include +#include #include @@ -57,6 +58,7 @@ struct sdhci_s3c { int ext_cd_irq; int ext_cd_gpio; int *gpios; + struct pinctrl *pctrl; struct clk *clk_io; struct clk *clk_bus[MAX_BUS_CLK]; @@ -484,8 +486,9 @@ static int __devinit sdhci_s3c_parse_dt(struct device *dev, return -EINVAL; } - dev_info(dev, "assuming no card detect line available\n"); - pdata->cd_type = S3C_SDHCI_CD_NONE; + /* assuming internal card detect that will be configured by pinctrl */ + pdata->cd_type = S3C_SDHCI_CD_INTERNAL; + goto setup_bus; found_cd: if (pdata->cd_type == S3C_SDHCI_CD_GPIO) { @@ -503,6 +506,9 @@ static int __devinit sdhci_s3c_parse_dt(struct device *dev, } setup_bus: + if (!IS_ERR(ourhost->pctrl)) + return 0; + /* get the gpios for command, clock and data lines */ for (cnt = 0; cnt < NUM_GPIOS(pdata->max_width); cnt++) { gpio = of_get_gpio(node, cnt); @@ -581,6 +587,8 @@ static int __devinit sdhci_s3c_probe(struct platform_device *pdev) goto err_pdata_io_clk; } + sc->pctrl = devm_pinctrl_get_select_default(&pdev->dev); + if (pdev->dev.of_node) { ret = sdhci_s3c_parse_dt(&pdev->dev, host, pdata); if (ret) -- cgit v1.2.3-59-g8ed1b From 8ed765aac31b473e1881986d9fe2b6386f49f933 Mon Sep 17 00:00:00 2001 From: Daniel Drake Date: Sun, 25 Nov 2012 13:02:54 -0500 Subject: mmc: dt: add no-1-8-v device tree flag The OLPC XO-1.75 laptop includes a SDHCI controller which is 1.8v capable, and it truthfully reports so in its capabilities. This alternate voltage is used for driving new "UHS-I" SD cards at their full speed. However, what the controller doesn't know is that the motherboard physically doesn't have a 1.8v supply available, so attempting to switch to the 1.8v level will result in a situation that cannot be recovered from without physically replugging the SD card. Add a device tree flag that can be used on systems like these, and hook it up to the equivalent SDHCI quirk. Signed-off-by: Daniel Drake Reviewed-by: Philip Rakity Signed-off-by: Chris Ball --- Documentation/devicetree/bindings/mmc/mmc.txt | 2 ++ drivers/mmc/host/sdhci-pltfm.c | 3 +++ 2 files changed, 5 insertions(+) (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/mmc/mmc.txt b/Documentation/devicetree/bindings/mmc/mmc.txt index b96e510979bf..a591c6741d75 100644 --- a/Documentation/devicetree/bindings/mmc/mmc.txt +++ b/Documentation/devicetree/bindings/mmc/mmc.txt @@ -21,6 +21,8 @@ Optional properties: - cd-inverted: when present, polarity on the cd gpio line is inverted - wp-inverted: when present, polarity on the wp gpio line is inverted - max-frequency: maximum operating clock frequency +- no-1-8-v: when present, denotes that 1.8v card voltage is not supported on + this system, even if the controller claims it is. Optional SDIO properties: - keep-power-in-suspend: Preserves card power during a suspend/resume cycle diff --git a/drivers/mmc/host/sdhci-pltfm.c b/drivers/mmc/host/sdhci-pltfm.c index c6c3b1243597..d4283ef5917a 100644 --- a/drivers/mmc/host/sdhci-pltfm.c +++ b/drivers/mmc/host/sdhci-pltfm.c @@ -78,6 +78,9 @@ void sdhci_get_of_property(struct platform_device *pdev) if (of_get_property(np, "broken-cd", NULL)) host->quirks |= SDHCI_QUIRK_BROKEN_CARD_DETECTION; + if (of_get_property(np, "no-1-8-v", NULL)) + host->quirks2 |= SDHCI_QUIRK2_NO_1_8_V; + if (of_device_is_compatible(np, "fsl,p2020-rev1-esdhc")) host->quirks |= SDHCI_QUIRK_BROKEN_DMA; -- cgit v1.2.3-59-g8ed1b From 090015aeefb1360dfa2c467310530052a2965eac Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 7 Dec 2012 07:52:28 +0100 Subject: ALSA: DocBook: Remove the description of __dev* Remove obsoleted __devinit* and __devexit* from the example codes and the descriptions, or modified the descriptions a bit to match with the current situation. Signed-off-by: Takashi Iwai --- Documentation/DocBook/writing-an-alsa-driver.tmpl | 85 ++++++++--------------- 1 file changed, 28 insertions(+), 57 deletions(-) (limited to 'Documentation') diff --git a/Documentation/DocBook/writing-an-alsa-driver.tmpl b/Documentation/DocBook/writing-an-alsa-driver.tmpl index cab4ec58e46e..fb32aead5a0b 100644 --- a/Documentation/DocBook/writing-an-alsa-driver.tmpl +++ b/Documentation/DocBook/writing-an-alsa-driver.tmpl @@ -433,9 +433,9 @@ /* chip-specific constructor * (see "Management of Cards and Components") */ - static int __devinit snd_mychip_create(struct snd_card *card, - struct pci_dev *pci, - struct mychip **rchip) + static int snd_mychip_create(struct snd_card *card, + struct pci_dev *pci, + struct mychip **rchip) { struct mychip *chip; int err; @@ -475,8 +475,8 @@ } /* constructor -- see "Constructor" sub-section */ - static int __devinit snd_mychip_probe(struct pci_dev *pci, - const struct pci_device_id *pci_id) + static int snd_mychip_probe(struct pci_dev *pci, + const struct pci_device_id *pci_id) { static int dev; struct snd_card *card; @@ -526,7 +526,7 @@ } /* destructor -- see the "Destructor" sub-section */ - static void __devexit snd_mychip_remove(struct pci_dev *pci) + static void snd_mychip_remove(struct pci_dev *pci) { snd_card_free(pci_get_drvdata(pci)); pci_set_drvdata(pci, NULL); @@ -542,9 +542,8 @@ The real constructor of PCI drivers is the probe callback. The probe callback and other component-constructors which are called - from the probe callback should be defined with - the __devinit prefix. You - cannot use the __init prefix for them, + from the probe callback cannot be used with + the __init prefix because any PCI device could be a hotplug device. @@ -728,7 +727,7 @@ - - As further notes, the destructors (both - snd_mychip_dev_free and - snd_mychip_free) cannot be defined with - the __devexit prefix, because they may be - called from the constructor, too, at the false path. - - For a device which allows hotplugging, you can use snd_card_free_when_closed. This one will @@ -1120,9 +1111,9 @@ } /* chip-specific constructor */ - static int __devinit snd_mychip_create(struct snd_card *card, - struct pci_dev *pci, - struct mychip **rchip) + static int snd_mychip_create(struct snd_card *card, + struct pci_dev *pci, + struct mychip **rchip) { struct mychip *chip; int err; @@ -1200,7 +1191,7 @@ .name = KBUILD_MODNAME, .id_table = snd_mychip_ids, .probe = snd_mychip_probe, - .remove = __devexit_p(snd_mychip_remove), + .remove = snd_mychip_remove, }; /* module initialization */ @@ -1464,11 +1455,6 @@
- - Again, remember that you cannot - use the __devexit prefix for this destructor. - - We didn't implement the hardware disabling part in the above. If you need to do this, please note that the destructor may be @@ -1619,7 +1605,7 @@ .name = KBUILD_MODNAME, .id_table = snd_mychip_ids, .probe = snd_mychip_probe, - .remove = __devexit_p(snd_mychip_remove), + .remove = snd_mychip_remove, }; ]]>
@@ -1630,11 +1616,7 @@ The probe and remove functions have already been defined in the previous sections. - The remove function should - be defined with the - __devexit_p() macro, so that it's not - defined for built-in (and non-hot-pluggable) case. The - name + The name field is the name string of this device. Note that you must not use a slash / in this string. @@ -1665,9 +1647,7 @@ Note that these module entries are tagged with __init and - __exit prefixes, not - __devinit nor - __devexit. + __exit prefixes. @@ -1918,7 +1898,7 @@ */ /* create a pcm device */ - static int __devinit snd_mychip_new_pcm(struct mychip *chip) + static int snd_mychip_new_pcm(struct mychip *chip) { struct snd_pcm *pcm; int err; @@ -1957,7 +1937,7 @@ Definition of a Control - - Most likely the control is created via - snd_ctl_new1(), and in such a case, you can - add the __devinitdata prefix to the - definition as above. - - The iface field specifies the control type, SNDRV_CTL_ELEM_IFACE_XXX, which @@ -3847,10 +3820,8 @@ struct _snd_pcm_runtime { snd_ctl_new1() allocates a new - snd_kcontrol instance (that's why the definition - of my_control can be with - the __devinitdata - prefix), and snd_ctl_add assigns the given + snd_kcontrol instance, + and snd_ctl_add assigns the given control component to the card. @@ -3896,7 +3867,7 @@ struct _snd_pcm_runtime { Date: Thu, 6 Dec 2012 16:55:33 +0100 Subject: spi: Add support for specifying 3-wire mode via device tree This patch allows to specify that a SPI device is connected in 3-wire mode via device tree. Signed-off-by: Lars-Peter Clausen Acked-by: Rob Herring Signed-off-by: Grant Likely --- Documentation/devicetree/bindings/spi/spi-bus.txt | 2 ++ drivers/spi/spi.c | 2 ++ 2 files changed, 4 insertions(+) (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/spi/spi-bus.txt b/Documentation/devicetree/bindings/spi/spi-bus.txt index 77a8b0d39b54..296015e3c632 100644 --- a/Documentation/devicetree/bindings/spi/spi-bus.txt +++ b/Documentation/devicetree/bindings/spi/spi-bus.txt @@ -53,6 +53,8 @@ contain the following properties. shifted clock phase (CPHA) mode - spi-cs-high - (optional) Empty property indicating device requires chip select active high +- spi-3wire - (optional) Empty property indicating device requires + 3-wire mode. If a gpio chipselect is used for the SPI slave the gpio number will be passed via the cs_gpio diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index b370d292d19c..2ca5f079ff01 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -861,6 +861,8 @@ static void of_register_spi_devices(struct spi_master *master) spi->mode |= SPI_CPOL; if (of_find_property(nc, "spi-cs-high", NULL)) spi->mode |= SPI_CS_HIGH; + if (of_find_property(nc, "spi-3wire", NULL)) + spi->mode |= SPI_3WIRE; /* Device speed */ prop = of_get_property(nc, "spi-max-frequency", &len); -- cgit v1.2.3-59-g8ed1b From 5d248c491b38d4f1b2a0bd7721241d68cd0b3067 Mon Sep 17 00:00:00 2001 From: Shan Wei Date: Thu, 6 Dec 2012 16:27:51 +0000 Subject: net: doc : use more suitable word 'unexpected' to replace 'secluded' 'secluded' is used to describe places, not suitable here. Suggested-by: Ben Hutchings Signed-off-by: Shan Wei Signed-off-by: David S. Miller --- Documentation/networking/ip-sysctl.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Documentation') diff --git a/Documentation/networking/ip-sysctl.txt b/Documentation/networking/ip-sysctl.txt index 0462a7105304..1b830cac4618 100644 --- a/Documentation/networking/ip-sysctl.txt +++ b/Documentation/networking/ip-sysctl.txt @@ -44,7 +44,7 @@ neigh/default/unres_qlen - INTEGER unresolved address by other network layers. (deprecated in linux 3.3) : use unres_qlen_bytes instead. Prior to linux 3.3, the default value is 3 which may cause - secluded packet loss. The current default value is calculated + unexpected packet loss. The current default value is calculated according to default value of unres_qlen_bytes and true size of packet. Default: 31 -- cgit v1.2.3-59-g8ed1b From 59c3987805a92f50f4c37392a36ab951327a6e29 Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Fri, 7 Dec 2012 23:11:51 +0100 Subject: ACPI: add documentation about ACPI 5 enumeration Add a document that describes how to take advantage of ACPI enumeration for buses like platform, I2C and SPI. In addition to that we document how to translate ACPI GpioIo and GpioInt resources to be useful in Linux device drivers. Signed-off-by: Mika Westerberg Signed-off-by: Rafael J. Wysocki --- Documentation/acpi/enumeration.txt | 227 +++++++++++++++++++++++++++++++++++++ 1 file changed, 227 insertions(+) create mode 100644 Documentation/acpi/enumeration.txt (limited to 'Documentation') diff --git a/Documentation/acpi/enumeration.txt b/Documentation/acpi/enumeration.txt new file mode 100644 index 000000000000..4f27785ca0c8 --- /dev/null +++ b/Documentation/acpi/enumeration.txt @@ -0,0 +1,227 @@ +ACPI based device enumeration +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +ACPI 5 introduced a set of new resources (UartTSerialBus, I2cSerialBus, +SpiSerialBus, GpioIo and GpioInt) which can be used in enumerating slave +devices behind serial bus controllers. + +In addition we are starting to see peripherals integrated in the +SoC/Chipset to appear only in ACPI namespace. These are typically devices +that are accessed through memory-mapped registers. + +In order to support this and re-use the existing drivers as much as +possible we decided to do following: + + o Devices that have no bus connector resource are represented as + platform devices. + + o Devices behind real busses where there is a connector resource + are represented as struct spi_device or struct i2c_device + (standard UARTs are not busses so there is no struct uart_device). + +As both ACPI and Device Tree represent a tree of devices (and their +resources) this implementation follows the Device Tree way as much as +possible. + +The ACPI implementation enumerates devices behind busses (platform, SPI and +I2C), creates the physical devices and binds them to their ACPI handle in +the ACPI namespace. + +This means that when ACPI_HANDLE(dev) returns non-NULL the device was +enumerated from ACPI namespace. This handle can be used to extract other +device-specific configuration. There is an example of this below. + +Platform bus support +~~~~~~~~~~~~~~~~~~~~ +Since we are using platform devices to represent devices that are not +connected to any physical bus we only need to implement a platform driver +for the device and add supported ACPI IDs. If this same IP-block is used on +some other non-ACPI platform, the driver might work out of the box or needs +some minor changes. + +Adding ACPI support for an existing driver should be pretty +straightforward. Here is the simplest example: + + #ifdef CONFIG_ACPI + static struct acpi_device_id mydrv_acpi_match[] = { + /* ACPI IDs here */ + { } + }; + MODULE_DEVICE_TABLE(acpi, mydrv_acpi_match); + #endif + + static struct platform_driver my_driver = { + ... + .driver = { + .acpi_match_table = ACPI_PTR(mydrv_acpi_match), + }, + }; + +If the driver needs to perform more complex initialization like getting and +configuring GPIOs it can get its ACPI handle and extract this information +from ACPI tables. + +Currently the kernel is not able to automatically determine from which ACPI +device it should make the corresponding platform device so we need to add +the ACPI device explicitly to acpi_platform_device_ids list defined in +drivers/acpi/scan.c. This limitation is only for the platform devices, SPI +and I2C devices are created automatically as described below. + +SPI serial bus support +~~~~~~~~~~~~~~~~~~~~~~ +Slave devices behind SPI bus have SpiSerialBus resource attached to them. +This is extracted automatically by the SPI core and the slave devices are +enumerated once spi_register_master() is called by the bus driver. + +Here is what the ACPI namespace for a SPI slave might look like: + + Device (EEP0) + { + Name (_ADR, 1) + Name (_CID, Package() { + "ATML0025", + "AT25", + }) + ... + Method (_CRS, 0, NotSerialized) + { + SPISerialBus(1, PolarityLow, FourWireMode, 8, + ControllerInitiated, 1000000, ClockPolarityLow, + ClockPhaseFirst, "\\_SB.PCI0.SPI1",) + } + ... + +The SPI device drivers only need to add ACPI IDs in a similar way than with +the platform device drivers. Below is an example where we add ACPI support +to at25 SPI eeprom driver (this is meant for the above ACPI snippet): + + #ifdef CONFIG_ACPI + static struct acpi_device_id at25_acpi_match[] = { + { "AT25", 0 }, + { }, + }; + MODULE_DEVICE_TABLE(acpi, at25_acpi_match); + #endif + + static struct spi_driver at25_driver = { + .driver = { + ... + .acpi_match_table = ACPI_PTR(at25_acpi_match), + }, + }; + +Note that this driver actually needs more information like page size of the +eeprom etc. but at the time writing this there is no standard way of +passing those. One idea is to return this in _DSM method like: + + Device (EEP0) + { + ... + Method (_DSM, 4, NotSerialized) + { + Store (Package (6) + { + "byte-len", 1024, + "addr-mode", 2, + "page-size, 32 + }, Local0) + + // Check UUIDs etc. + + Return (Local0) + } + +Then the at25 SPI driver can get this configation by calling _DSM on its +ACPI handle like: + + struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL }; + struct acpi_object_list input; + acpi_status status; + + /* Fill in the input buffer */ + + status = acpi_evaluate_object(ACPI_HANDLE(&spi->dev), "_DSM", + &input, &output); + if (ACPI_FAILURE(status)) + /* Handle the error */ + + /* Extract the data here */ + + kfree(output.pointer); + +I2C serial bus support +~~~~~~~~~~~~~~~~~~~~~~ +The slaves behind I2C bus controller only need to add the ACPI IDs like +with the platform and SPI drivers. However the I2C bus controller driver +needs to call acpi_i2c_register_devices() after it has added the adapter. + +An I2C bus (controller) driver does: + + ... + ret = i2c_add_numbered_adapter(adapter); + if (ret) + /* handle error */ + + of_i2c_register_devices(adapter); + /* Enumerate the slave devices behind this bus via ACPI */ + acpi_i2c_register_devices(adapter); + +Below is an example of how to add ACPI support to the existing mpu3050 +input driver: + + #ifdef CONFIG_ACPI + static struct acpi_device_id mpu3050_acpi_match[] = { + { "MPU3050", 0 }, + { }, + }; + MODULE_DEVICE_TABLE(acpi, mpu3050_acpi_match); + #endif + + static struct i2c_driver mpu3050_i2c_driver = { + .driver = { + .name = "mpu3050", + .owner = THIS_MODULE, + .pm = &mpu3050_pm, + .of_match_table = mpu3050_of_match, + .acpi_match_table ACPI_PTR(mpu3050_acpi_match), + }, + .probe = mpu3050_probe, + .remove = __devexit_p(mpu3050_remove), + .id_table = mpu3050_ids, + }; + +GPIO support +~~~~~~~~~~~~ +ACPI 5 introduced two new resources to describe GPIO connections: GpioIo +and GpioInt. These resources are used be used to pass GPIO numbers used by +the device to the driver. For example: + + Method (_CRS, 0, NotSerialized) + { + Name (SBUF, ResourceTemplate() + { + GpioIo (Exclusive, PullDefault, 0x0000, 0x0000, + IoRestrictionOutputOnly, "\\_SB.PCI0.GPI0", + 0x00, ResourceConsumer,,) + { + // Pin List + 0x0055 + } + ... + + Return (SBUF) + } + } + +These GPIO numbers are controller relative and path "\\_SB.PCI0.GPI0" +specifies the path to the controller. In order to use these GPIOs in Linux +we need to translate them to the Linux GPIO numbers. + +The driver can do this by including and then calling +acpi_get_gpio(path, gpio). This will return the Linux GPIO number or +negative errno if there was no translation found. + +Other GpioIo parameters must be converted first by the driver to be +suitable to the gpiolib before passing them. + +In case of GpioInt resource an additional call to gpio_to_irq() must be +done before calling request_irq(). -- cgit v1.2.3-59-g8ed1b From 15ef4ffaa797034d5ff82844daf8f595d7c6d53c Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Sat, 8 Dec 2012 15:02:36 +0900 Subject: cgroup: update Documentation/cgroups/00-INDEX There are new files added to cgroup documentation. Let's update the index file to include the new files. Signed-off-by: Namjae Jeon Signed-off-by: Amit Sahrawat Signed-off-by: Tejun Heo --- Documentation/cgroups/00-INDEX | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'Documentation') diff --git a/Documentation/cgroups/00-INDEX b/Documentation/cgroups/00-INDEX index 3f58fa3d6d00..f78b90a35ad0 100644 --- a/Documentation/cgroups/00-INDEX +++ b/Documentation/cgroups/00-INDEX @@ -1,7 +1,11 @@ 00-INDEX - this file +blkio-controller.txt + - Description for Block IO Controller, implementation and usage details. cgroups.txt - Control Groups definition, implementation details, examples and API. +cgroup_event_listener.c + - A user program for cgroup listener. cpuacct.txt - CPU Accounting Controller; account CPU usage for groups of tasks. cpusets.txt @@ -10,9 +14,13 @@ devices.txt - Device Whitelist Controller; description, interface and security. freezer-subsystem.txt - checkpointing; rationale to not use signals, interface. +hugetlb.txt + - HugeTLB Controller implementation and usage details. memcg_test.txt - Memory Resource Controller; implementation details. memory.txt - Memory Resource Controller; design, accounting, interface, testing. +net_prio.txt + - Network priority cgroups details and usages. resource_counter.txt - Resource Counter API. -- cgit v1.2.3-59-g8ed1b From ac713cc9fcb8a5f9503af08421772b85ca4685f1 Mon Sep 17 00:00:00 2001 From: Vipul Kumar Samar Date: Fri, 7 Dec 2012 20:29:37 +0530 Subject: mfd: stmpe: Update DT support for stmpe driver This patch extends existing DT support for stmpe devices. This updates: - missing header files in stmpe.c - stmpe_of_probe() with pwm, rotator and new bindings. - Bindings are updated in binding document. Acked-by: Lee Jones Acked-by: Linus Walleij Signed-off-by: Vipul Kumar Samar Signed-off-by: Viresh Kumar Signed-off-by: Samuel Ortiz --- Documentation/devicetree/bindings/mfd/stmpe.txt | 9 ++++++--- drivers/mfd/stmpe.c | 21 +++++++++++++++------ 2 files changed, 21 insertions(+), 9 deletions(-) (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/mfd/stmpe.txt b/Documentation/devicetree/bindings/mfd/stmpe.txt index c98450887d5e..56edb5520685 100644 --- a/Documentation/devicetree/bindings/mfd/stmpe.txt +++ b/Documentation/devicetree/bindings/mfd/stmpe.txt @@ -1,8 +1,11 @@ -* STMPE Multi-Functional Device +* ST Microelectronics STMPE Multi-Functional Device + +STMPE is an MFD device which may expose the following inbuilt devices: gpio, +keypad, touchscreen, adc, pwm, rotator. Required properties: - - compatible : "st,stmpe[811|1601|2401|2403]" - - reg : I2C address of the device + - compatible : "st,stmpe[610|801|811|1601|2401|2403]" + - reg : I2C/SPI address of the device Optional properties: - interrupts : The interrupt outputs from the controller diff --git a/drivers/mfd/stmpe.c b/drivers/mfd/stmpe.c index 90c6151bc52e..b18cc1a1b162 100644 --- a/drivers/mfd/stmpe.c +++ b/drivers/mfd/stmpe.c @@ -7,6 +7,7 @@ * Author: Rabin Vincent for ST-Ericsson */ +#include #include #include #include @@ -14,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -1012,6 +1014,9 @@ void __devinit stmpe_of_probe(struct stmpe_platform_data *pdata, { struct device_node *child; + pdata->id = -1; + pdata->irq_trigger = IRQF_TRIGGER_NONE; + of_property_read_u32(np, "st,autosleep-timeout", &pdata->autosleep_timeout); @@ -1020,15 +1025,16 @@ void __devinit stmpe_of_probe(struct stmpe_platform_data *pdata, for_each_child_of_node(np, child) { if (!strcmp(child->name, "stmpe_gpio")) { pdata->blocks |= STMPE_BLOCK_GPIO; - } - if (!strcmp(child->name, "stmpe_keypad")) { + } else if (!strcmp(child->name, "stmpe_keypad")) { pdata->blocks |= STMPE_BLOCK_KEYPAD; - } - if (!strcmp(child->name, "stmpe_touchscreen")) { + } else if (!strcmp(child->name, "stmpe_touchscreen")) { pdata->blocks |= STMPE_BLOCK_TOUCHSCREEN; - } - if (!strcmp(child->name, "stmpe_adc")) { + } else if (!strcmp(child->name, "stmpe_adc")) { pdata->blocks |= STMPE_BLOCK_ADC; + } else if (!strcmp(child->name, "stmpe_pwm")) { + pdata->blocks |= STMPE_BLOCK_PWM; + } else if (!strcmp(child->name, "stmpe_rotator")) { + pdata->blocks |= STMPE_BLOCK_ROTATOR; } } } @@ -1099,6 +1105,9 @@ int __devinit stmpe_probe(struct stmpe_client_info *ci, int partnum) return -ENODEV; } stmpe->variant = stmpe_noirq_variant_info[stmpe->partnum]; + } else if (pdata->irq_trigger == IRQF_TRIGGER_NONE) { + pdata->irq_trigger = + irqd_get_trigger_type(irq_get_irq_data(stmpe->irq)); } ret = stmpe_chip_init(stmpe); -- cgit v1.2.3-59-g8ed1b From a2ed00da5047806233e288b991d9be997a5eb214 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Fri, 7 Dec 2012 12:30:38 -0800 Subject: drivers/video: add support for the Solomon SSD1307 OLED Controller Add support for the Solomon SSD1307 OLED controller found on the Crystalfontz CFA10036 board. This controller can drive a display with a resolution up to 128x39 and can operate over I2C or SPI. The current driver has only been tested on the CFA-10036, that is using this controller over I2C to driver a 96x16 OLED screen. [akpm@linux-foundation.org: checkpatch fixes] Signed-off-by: Maxime Ripard Cc: Brian Lilly Cc: Greg KH Cc: Florian Tobias Schandinat Cc: Thomas Petazzoni Cc: Tomi Valkeinen Signed-off-by: Andrew Morton Signed-off-by: Tomi Valkeinen --- .../devicetree/bindings/video/ssd1307fb.txt | 24 ++ drivers/video/Kconfig | 15 + drivers/video/Makefile | 1 + drivers/video/ssd1307fb.c | 396 +++++++++++++++++++++ 4 files changed, 436 insertions(+) create mode 100644 Documentation/devicetree/bindings/video/ssd1307fb.txt create mode 100644 drivers/video/ssd1307fb.c (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/video/ssd1307fb.txt b/Documentation/devicetree/bindings/video/ssd1307fb.txt new file mode 100644 index 000000000000..3d0060cff062 --- /dev/null +++ b/Documentation/devicetree/bindings/video/ssd1307fb.txt @@ -0,0 +1,24 @@ +* Solomon SSD1307 Framebuffer Driver + +Required properties: + - compatible: Should be "solomon,ssd1307fb-". The only supported bus for + now is i2c. + - reg: Should contain address of the controller on the I2C bus. Most likely + 0x3c or 0x3d + - pwm: Should contain the pwm to use according to the OF device tree PWM + specification [0] + - reset-gpios: Should contain the GPIO used to reset the OLED display + +Optional properties: + - reset-active-low: Is the reset gpio is active on physical low? + +[0]: Documentation/devicetree/bindings/pwm/pwm.txt + +Examples: +ssd1307: oled@3c { + compatible = "solomon,ssd1307fb-i2c"; + reg = <0x3c>; + pwms = <&pwm 4 3000>; + reset-gpios = <&gpio2 7>; + reset-active-low; +}; diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig index 9018a90b4588..9c31277b3a81 100644 --- a/drivers/video/Kconfig +++ b/drivers/video/Kconfig @@ -2442,4 +2442,19 @@ config FB_SH_MOBILE_MERAM Up to 4 memory channels can be configured, allowing 4 RGB or 2 YCbCr framebuffers to be configured. +config FB_SSD1307 + tristate "Solomon SSD1307 framebuffer support" + depends on FB && I2C + depends on OF + depends on GENERIC_GPIO + select FB_SYS_FOPS + select FB_SYS_FILLRECT + select FB_SYS_COPYAREA + select FB_SYS_IMAGEBLIT + select FB_DEFERRED_IO + select PWM + help + This driver implements support for the Solomon SSD1307 + OLED controller over I2C. + endmenu diff --git a/drivers/video/Makefile b/drivers/video/Makefile index 23e948ebfab8..768a137a1bac 100644 --- a/drivers/video/Makefile +++ b/drivers/video/Makefile @@ -161,6 +161,7 @@ obj-$(CONFIG_FB_BFIN_7393) += bfin_adv7393fb.o obj-$(CONFIG_FB_MX3) += mx3fb.o obj-$(CONFIG_FB_DA8XX) += da8xx-fb.o obj-$(CONFIG_FB_MXS) += mxsfb.o +obj-$(CONFIG_FB_SSD1307) += ssd1307fb.o # the test framebuffer is last obj-$(CONFIG_FB_VIRTUAL) += vfb.o diff --git a/drivers/video/ssd1307fb.c b/drivers/video/ssd1307fb.c new file mode 100644 index 000000000000..6101f5c2f62f --- /dev/null +++ b/drivers/video/ssd1307fb.c @@ -0,0 +1,396 @@ +/* + * Driver for the Solomon SSD1307 OLED controler + * + * Copyright 2012 Free Electrons + * + * Licensed under the GPLv2 or later. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define SSD1307FB_WIDTH 96 +#define SSD1307FB_HEIGHT 16 + +#define SSD1307FB_DATA 0x40 +#define SSD1307FB_COMMAND 0x80 + +#define SSD1307FB_CONTRAST 0x81 +#define SSD1307FB_SEG_REMAP_ON 0xa1 +#define SSD1307FB_DISPLAY_OFF 0xae +#define SSD1307FB_DISPLAY_ON 0xaf +#define SSD1307FB_START_PAGE_ADDRESS 0xb0 + +struct ssd1307fb_par { + struct i2c_client *client; + struct fb_info *info; + struct pwm_device *pwm; + u32 pwm_period; + int reset; +}; + +static struct fb_fix_screeninfo ssd1307fb_fix __devinitdata = { + .id = "Solomon SSD1307", + .type = FB_TYPE_PACKED_PIXELS, + .visual = FB_VISUAL_MONO10, + .xpanstep = 0, + .ypanstep = 0, + .ywrapstep = 0, + .line_length = SSD1307FB_WIDTH / 8, + .accel = FB_ACCEL_NONE, +}; + +static struct fb_var_screeninfo ssd1307fb_var __devinitdata = { + .xres = SSD1307FB_WIDTH, + .yres = SSD1307FB_HEIGHT, + .xres_virtual = SSD1307FB_WIDTH, + .yres_virtual = SSD1307FB_HEIGHT, + .bits_per_pixel = 1, +}; + +static int ssd1307fb_write_array(struct i2c_client *client, u8 type, u8 *cmd, u32 len) +{ + u8 *buf; + int ret = 0; + + buf = kzalloc(len + 1, GFP_KERNEL); + if (!buf) { + dev_err(&client->dev, "Couldn't allocate sending buffer.\n"); + return -ENOMEM; + } + + buf[0] = type; + memcpy(buf + 1, cmd, len); + + ret = i2c_master_send(client, buf, len + 1); + if (ret != len + 1) { + dev_err(&client->dev, "Couldn't send I2C command.\n"); + goto error; + } + +error: + kfree(buf); + return ret; +} + +static inline int ssd1307fb_write_cmd_array(struct i2c_client *client, u8 *cmd, u32 len) +{ + return ssd1307fb_write_array(client, SSD1307FB_COMMAND, cmd, len); +} + +static inline int ssd1307fb_write_cmd(struct i2c_client *client, u8 cmd) +{ + return ssd1307fb_write_cmd_array(client, &cmd, 1); +} + +static inline int ssd1307fb_write_data_array(struct i2c_client *client, u8 *cmd, u32 len) +{ + return ssd1307fb_write_array(client, SSD1307FB_DATA, cmd, len); +} + +static inline int ssd1307fb_write_data(struct i2c_client *client, u8 data) +{ + return ssd1307fb_write_data_array(client, &data, 1); +} + +static void ssd1307fb_update_display(struct ssd1307fb_par *par) +{ + u8 *vmem = par->info->screen_base; + int i, j, k; + + /* + * The screen is divided in pages, each having a height of 8 + * pixels, and the width of the screen. When sending a byte of + * data to the controller, it gives the 8 bits for the current + * column. I.e, the first byte are the 8 bits of the first + * column, then the 8 bits for the second column, etc. + * + * + * Representation of the screen, assuming it is 5 bits + * wide. Each letter-number combination is a bit that controls + * one pixel. + * + * A0 A1 A2 A3 A4 + * B0 B1 B2 B3 B4 + * C0 C1 C2 C3 C4 + * D0 D1 D2 D3 D4 + * E0 E1 E2 E3 E4 + * F0 F1 F2 F3 F4 + * G0 G1 G2 G3 G4 + * H0 H1 H2 H3 H4 + * + * If you want to update this screen, you need to send 5 bytes: + * (1) A0 B0 C0 D0 E0 F0 G0 H0 + * (2) A1 B1 C1 D1 E1 F1 G1 H1 + * (3) A2 B2 C2 D2 E2 F2 G2 H2 + * (4) A3 B3 C3 D3 E3 F3 G3 H3 + * (5) A4 B4 C4 D4 E4 F4 G4 H4 + */ + + for (i = 0; i < (SSD1307FB_HEIGHT / 8); i++) { + ssd1307fb_write_cmd(par->client, SSD1307FB_START_PAGE_ADDRESS + (i + 1)); + ssd1307fb_write_cmd(par->client, 0x00); + ssd1307fb_write_cmd(par->client, 0x10); + + for (j = 0; j < SSD1307FB_WIDTH; j++) { + u8 buf = 0; + for (k = 0; k < 8; k++) { + u32 page_length = SSD1307FB_WIDTH * i; + u32 index = page_length + (SSD1307FB_WIDTH * k + j) / 8; + u8 byte = *(vmem + index); + u8 bit = byte & (1 << (7 - (j % 8))); + bit = bit >> (7 - (j % 8)); + buf |= bit << k; + } + ssd1307fb_write_data(par->client, buf); + } + } +} + + +static ssize_t ssd1307fb_write(struct fb_info *info, const char __user *buf, + size_t count, loff_t *ppos) +{ + struct ssd1307fb_par *par = info->par; + unsigned long total_size; + unsigned long p = *ppos; + u8 __iomem *dst; + + total_size = info->fix.smem_len; + + if (p > total_size) + return -EINVAL; + + if (count + p > total_size) + count = total_size - p; + + if (!count) + return -EINVAL; + + dst = (void __force *) (info->screen_base + p); + + if (copy_from_user(dst, buf, count)) + return -EFAULT; + + ssd1307fb_update_display(par); + + *ppos += count; + + return count; +} + +static void ssd1307fb_fillrect(struct fb_info *info, const struct fb_fillrect *rect) +{ + struct ssd1307fb_par *par = info->par; + sys_fillrect(info, rect); + ssd1307fb_update_display(par); +} + +static void ssd1307fb_copyarea(struct fb_info *info, const struct fb_copyarea *area) +{ + struct ssd1307fb_par *par = info->par; + sys_copyarea(info, area); + ssd1307fb_update_display(par); +} + +static void ssd1307fb_imageblit(struct fb_info *info, const struct fb_image *image) +{ + struct ssd1307fb_par *par = info->par; + sys_imageblit(info, image); + ssd1307fb_update_display(par); +} + +static struct fb_ops ssd1307fb_ops = { + .owner = THIS_MODULE, + .fb_read = fb_sys_read, + .fb_write = ssd1307fb_write, + .fb_fillrect = ssd1307fb_fillrect, + .fb_copyarea = ssd1307fb_copyarea, + .fb_imageblit = ssd1307fb_imageblit, +}; + +static void ssd1307fb_deferred_io(struct fb_info *info, + struct list_head *pagelist) +{ + ssd1307fb_update_display(info->par); +} + +static struct fb_deferred_io ssd1307fb_defio = { + .delay = HZ, + .deferred_io = ssd1307fb_deferred_io, +}; + +static int __devinit ssd1307fb_probe(struct i2c_client *client, const struct i2c_device_id *id) +{ + struct fb_info *info; + u32 vmem_size = SSD1307FB_WIDTH * SSD1307FB_HEIGHT / 8; + struct ssd1307fb_par *par; + u8 *vmem; + int ret; + + if (!client->dev.of_node) { + dev_err(&client->dev, "No device tree data found!\n"); + return -EINVAL; + } + + info = framebuffer_alloc(sizeof(struct ssd1307fb_par), &client->dev); + if (!info) { + dev_err(&client->dev, "Couldn't allocate framebuffer.\n"); + return -ENOMEM; + } + + vmem = devm_kzalloc(&client->dev, vmem_size, GFP_KERNEL); + if (!vmem) { + dev_err(&client->dev, "Couldn't allocate graphical memory.\n"); + ret = -ENOMEM; + goto fb_alloc_error; + } + + info->fbops = &ssd1307fb_ops; + info->fix = ssd1307fb_fix; + info->fbdefio = &ssd1307fb_defio; + + info->var = ssd1307fb_var; + info->var.red.length = 1; + info->var.red.offset = 0; + info->var.green.length = 1; + info->var.green.offset = 0; + info->var.blue.length = 1; + info->var.blue.offset = 0; + + info->screen_base = (u8 __force __iomem *)vmem; + info->fix.smem_start = (unsigned long)vmem; + info->fix.smem_len = vmem_size; + + fb_deferred_io_init(info); + + par = info->par; + par->info = info; + par->client = client; + + par->reset = of_get_named_gpio(client->dev.of_node, + "reset-gpios", 0); + if (!gpio_is_valid(par->reset)) { + ret = -EINVAL; + goto reset_oled_error; + } + + ret = devm_gpio_request_one(&client->dev, par->reset, + GPIOF_OUT_INIT_HIGH, + "oled-reset"); + if (ret) { + dev_err(&client->dev, + "failed to request gpio %d: %d\n", + par->reset, ret); + goto reset_oled_error; + } + + par->pwm = pwm_get(&client->dev, NULL); + if (IS_ERR(par->pwm)) { + dev_err(&client->dev, "Could not get PWM from device tree!\n"); + ret = PTR_ERR(par->pwm); + goto pwm_error; + } + + par->pwm_period = pwm_get_period(par->pwm); + + dev_dbg(&client->dev, "Using PWM%d with a %dns period.\n", par->pwm->pwm, par->pwm_period); + + ret = register_framebuffer(info); + if (ret) { + dev_err(&client->dev, "Couldn't register the framebuffer\n"); + goto fbreg_error; + } + + i2c_set_clientdata(client, info); + + /* Reset the screen */ + gpio_set_value(par->reset, 0); + udelay(4); + gpio_set_value(par->reset, 1); + udelay(4); + + /* Enable the PWM */ + pwm_config(par->pwm, par->pwm_period / 2, par->pwm_period); + pwm_enable(par->pwm); + + /* Map column 127 of the OLED to segment 0 */ + ret = ssd1307fb_write_cmd(client, SSD1307FB_SEG_REMAP_ON); + if (ret < 0) { + dev_err(&client->dev, "Couldn't remap the screen.\n"); + goto remap_error; + } + + /* Turn on the display */ + ret = ssd1307fb_write_cmd(client, SSD1307FB_DISPLAY_ON); + if (ret < 0) { + dev_err(&client->dev, "Couldn't turn the display on.\n"); + goto remap_error; + } + + dev_info(&client->dev, "fb%d: %s framebuffer device registered, using %d bytes of video memory\n", info->node, info->fix.id, vmem_size); + + return 0; + +remap_error: + unregister_framebuffer(info); + pwm_disable(par->pwm); +fbreg_error: + pwm_put(par->pwm); +pwm_error: +reset_oled_error: + fb_deferred_io_cleanup(info); +fb_alloc_error: + framebuffer_release(info); + return ret; +} + +static int __devexit ssd1307fb_remove(struct i2c_client *client) +{ + struct fb_info *info = i2c_get_clientdata(client); + struct ssd1307fb_par *par = info->par; + + unregister_framebuffer(info); + pwm_disable(par->pwm); + pwm_put(par->pwm); + fb_deferred_io_cleanup(info); + framebuffer_release(info); + + return 0; +} + +static const struct i2c_device_id ssd1307fb_i2c_id[] = { + { "ssd1307fb", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, ssd1307fb_i2c_id); + +static const struct of_device_id ssd1307fb_of_match[] = { + { .compatible = "solomon,ssd1307fb-i2c" }, + {}, +}; +MODULE_DEVICE_TABLE(of, ssd1307fb_of_match); + +static struct i2c_driver ssd1307fb_driver = { + .probe = ssd1307fb_probe, + .remove = __devexit_p(ssd1307fb_remove), + .id_table = ssd1307fb_i2c_id, + .driver = { + .name = "ssd1307fb", + .of_match_table = of_match_ptr(ssd1307fb_of_match), + .owner = THIS_MODULE, + }, +}; + +module_i2c_driver(ssd1307fb_driver); + +MODULE_DESCRIPTION("FB driver for the Solomon SSD1307 OLED controler"); +MODULE_AUTHOR("Maxime Ripard "); +MODULE_LICENSE("GPL"); -- cgit v1.2.3-59-g8ed1b From 106f9d9337f65bd428c0c79f650e3489e458d771 Mon Sep 17 00:00:00 2001 From: Cristian Stoica Date: Fri, 7 Dec 2012 16:37:11 +0200 Subject: HOWTO: fix double words typo Signed-off-by: Cristian Stoica Acked-by: Rob Landley Signed-off-by: Jiri Kosina --- Documentation/HOWTO | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Documentation') diff --git a/Documentation/HOWTO b/Documentation/HOWTO index 59c080f084ef..a9f288ff54f9 100644 --- a/Documentation/HOWTO +++ b/Documentation/HOWTO @@ -462,7 +462,7 @@ Differences between the kernel community and corporate structures The kernel community works differently than most traditional corporate development environments. Here are a list of things that you can try to -do to try to avoid problems: +do to avoid problems: Good things to say regarding your proposed changes: - "This solves multiple problems." - "This deletes 2000 lines of code." -- cgit v1.2.3-59-g8ed1b From 939da1084458246d2e29dd921c2012c177000e96 Mon Sep 17 00:00:00 2001 From: Tao Ma Date: Mon, 10 Dec 2012 16:30:43 -0500 Subject: ext4: Remove CONFIG_EXT4_FS_XATTR Ted has sent out a RFC about removing this feature. Eric and Jan confirmed that both RedHat and SUSE enable this feature in all their product. David also said that "As far as I know, it's enabled in all Android kernels that use ext4." So it seems OK for us. And what's more, as inline data depends its implementation on xattr, and to be frank, I don't run any test again inline data enabled while xattr disabled. So I think we should add inline data and remove this config option in the same release. [ The savings if you disable CONFIG_EXT4_FS_XATTR is only 27k, which isn't much in the grand scheme of things. Since no one seems to be testing this configuration except for some automated compile farms, on balance we are better removing this config option, and so that it is effectively always enabled. -- tytso ] Cc: David Brown Cc: Eric Sandeen Reviewed-by: Jan Kara Signed-off-by: Tao Ma Signed-off-by: "Theodore Ts'o" --- Documentation/filesystems/ext4.txt | 9 +- fs/Kconfig | 4 +- fs/ext4/Kconfig | 15 --- fs/ext4/Makefile | 4 +- fs/ext4/ext4.h | 2 - fs/ext4/file.c | 2 - fs/ext4/namei.c | 4 - fs/ext4/super.c | 9 -- fs/ext4/symlink.c | 4 - fs/ext4/xattr.h | 235 ------------------------------------- 10 files changed, 7 insertions(+), 281 deletions(-) (limited to 'Documentation') diff --git a/Documentation/filesystems/ext4.txt b/Documentation/filesystems/ext4.txt index 104322bf378c..34ea4f1fa6ea 100644 --- a/Documentation/filesystems/ext4.txt +++ b/Documentation/filesystems/ext4.txt @@ -200,12 +200,9 @@ inode_readahead_blks=n This tuning parameter controls the maximum table readahead algorithm will pre-read into the buffer cache. The default value is 32 blocks. -nouser_xattr Disables Extended User Attributes. If you have extended - attribute support enabled in the kernel configuration - (CONFIG_EXT4_FS_XATTR), extended attribute support - is enabled by default on mount. See the attr(5) manual - page and http://acl.bestbits.at/ for more information - about extended attributes. +nouser_xattr Disables Extended User Attributes. See the + attr(5) manual page and http://acl.bestbits.at/ + for more information about extended attributes. noacl This option disables POSIX Access Control List support. If ACL support is enabled in the kernel diff --git a/fs/Kconfig b/fs/Kconfig index f95ae3a027f3..eaff24a19502 100644 --- a/fs/Kconfig +++ b/fs/Kconfig @@ -28,8 +28,8 @@ config FS_MBCACHE tristate default y if EXT2_FS=y && EXT2_FS_XATTR default y if EXT3_FS=y && EXT3_FS_XATTR - default y if EXT4_FS=y && EXT4_FS_XATTR - default m if EXT2_FS_XATTR || EXT3_FS_XATTR || EXT4_FS_XATTR + default y if EXT4_FS=y + default m if EXT2_FS_XATTR || EXT3_FS_XATTR || EXT4_FS source "fs/reiserfs/Kconfig" source "fs/jfs/Kconfig" diff --git a/fs/ext4/Kconfig b/fs/ext4/Kconfig index c22f17021b6e..0a475c881852 100644 --- a/fs/ext4/Kconfig +++ b/fs/ext4/Kconfig @@ -39,22 +39,8 @@ config EXT4_USE_FOR_EXT23 compiled kernel size by using one file system driver for ext2, ext3, and ext4 file systems. -config EXT4_FS_XATTR - bool "Ext4 extended attributes" - depends on EXT4_FS - default y - help - Extended attributes are name:value pairs associated with inodes by - the kernel or by users (see the attr(5) manual page, or visit - for details). - - If unsure, say N. - - You need this for POSIX ACL support on ext4. - config EXT4_FS_POSIX_ACL bool "Ext4 POSIX Access Control Lists" - depends on EXT4_FS_XATTR select FS_POSIX_ACL help POSIX Access Control Lists (ACLs) support permissions for users and @@ -67,7 +53,6 @@ config EXT4_FS_POSIX_ACL config EXT4_FS_SECURITY bool "Ext4 Security Labels" - depends on EXT4_FS_XATTR help Security labels support alternative access control models implemented by security modules like SELinux. This option diff --git a/fs/ext4/Makefile b/fs/ext4/Makefile index 3d96d5698538..0310fec2ee3d 100644 --- a/fs/ext4/Makefile +++ b/fs/ext4/Makefile @@ -7,8 +7,8 @@ obj-$(CONFIG_EXT4_FS) += ext4.o ext4-y := balloc.o bitmap.o dir.o file.o fsync.o ialloc.o inode.o page-io.o \ ioctl.o namei.o super.o symlink.o hash.o resize.o extents.o \ ext4_jbd2.o migrate.o mballoc.o block_validity.o move_extent.o \ - mmp.o indirect.o extents_status.o + mmp.o indirect.o extents_status.o xattr.o xattr_user.o \ + xattr_trusted.o inline.o -ext4-$(CONFIG_EXT4_FS_XATTR) += xattr.o xattr_user.o xattr_trusted.o inline.o ext4-$(CONFIG_EXT4_FS_POSIX_ACL) += acl.o ext4-$(CONFIG_EXT4_FS_SECURITY) += xattr_security.o diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h index b90e2720b826..e20dc38858d4 100644 --- a/fs/ext4/ext4.h +++ b/fs/ext4/ext4.h @@ -848,7 +848,6 @@ struct ext4_inode_info { #endif unsigned long i_flags; -#ifdef CONFIG_EXT4_FS_XATTR /* * Extended attributes can be read independently of the main file * data. Taking i_mutex even when reading would cause contention @@ -857,7 +856,6 @@ struct ext4_inode_info { * EAs. */ struct rw_semaphore xattr_sem; -#endif struct list_head i_orphan; /* unlinked but open inodes */ diff --git a/fs/ext4/file.c b/fs/ext4/file.c index 2f5759eb9f89..b64a60bf105a 100644 --- a/fs/ext4/file.c +++ b/fs/ext4/file.c @@ -656,12 +656,10 @@ const struct file_operations ext4_file_operations = { const struct inode_operations ext4_file_inode_operations = { .setattr = ext4_setattr, .getattr = ext4_getattr, -#ifdef CONFIG_EXT4_FS_XATTR .setxattr = generic_setxattr, .getxattr = generic_getxattr, .listxattr = ext4_listxattr, .removexattr = generic_removexattr, -#endif .get_acl = ext4_get_acl, .fiemap = ext4_fiemap, }; diff --git a/fs/ext4/namei.c b/fs/ext4/namei.c index b37c21839833..cac448282331 100644 --- a/fs/ext4/namei.c +++ b/fs/ext4/namei.c @@ -3228,23 +3228,19 @@ const struct inode_operations ext4_dir_inode_operations = { .mknod = ext4_mknod, .rename = ext4_rename, .setattr = ext4_setattr, -#ifdef CONFIG_EXT4_FS_XATTR .setxattr = generic_setxattr, .getxattr = generic_getxattr, .listxattr = ext4_listxattr, .removexattr = generic_removexattr, -#endif .get_acl = ext4_get_acl, .fiemap = ext4_fiemap, }; const struct inode_operations ext4_special_inode_operations = { .setattr = ext4_setattr, -#ifdef CONFIG_EXT4_FS_XATTR .setxattr = generic_setxattr, .getxattr = generic_getxattr, .listxattr = ext4_listxattr, .removexattr = generic_removexattr, -#endif .get_acl = ext4_get_acl, }; diff --git a/fs/ext4/super.c b/fs/ext4/super.c index e1e216f8e9bd..7d53adff8bd3 100644 --- a/fs/ext4/super.c +++ b/fs/ext4/super.c @@ -997,9 +997,7 @@ static void init_once(void *foo) struct ext4_inode_info *ei = (struct ext4_inode_info *) foo; INIT_LIST_HEAD(&ei->i_orphan); -#ifdef CONFIG_EXT4_FS_XATTR init_rwsem(&ei->xattr_sem); -#endif init_rwsem(&ei->i_data_sem); inode_init_once(&ei->vfs_inode); } @@ -1449,13 +1447,8 @@ static const struct mount_opts { {Opt_data_journal, EXT4_MOUNT_JOURNAL_DATA, MOPT_DATAJ}, {Opt_data_ordered, EXT4_MOUNT_ORDERED_DATA, MOPT_DATAJ}, {Opt_data_writeback, EXT4_MOUNT_WRITEBACK_DATA, MOPT_DATAJ}, -#ifdef CONFIG_EXT4_FS_XATTR {Opt_user_xattr, EXT4_MOUNT_XATTR_USER, MOPT_SET}, {Opt_nouser_xattr, EXT4_MOUNT_XATTR_USER, MOPT_CLEAR}, -#else - {Opt_user_xattr, 0, MOPT_NOSUPPORT}, - {Opt_nouser_xattr, 0, MOPT_NOSUPPORT}, -#endif #ifdef CONFIG_EXT4_FS_POSIX_ACL {Opt_acl, EXT4_MOUNT_POSIX_ACL, MOPT_SET}, {Opt_noacl, EXT4_MOUNT_POSIX_ACL, MOPT_CLEAR}, @@ -3368,9 +3361,7 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent) if (def_mount_opts & EXT4_DEFM_UID16) set_opt(sb, NO_UID32); /* xattr user namespace & acls are now defaulted on */ -#ifdef CONFIG_EXT4_FS_XATTR set_opt(sb, XATTR_USER); -#endif #ifdef CONFIG_EXT4_FS_POSIX_ACL set_opt(sb, POSIX_ACL); #endif diff --git a/fs/ext4/symlink.c b/fs/ext4/symlink.c index ed9354aff279..ff3711932018 100644 --- a/fs/ext4/symlink.c +++ b/fs/ext4/symlink.c @@ -35,22 +35,18 @@ const struct inode_operations ext4_symlink_inode_operations = { .follow_link = page_follow_link_light, .put_link = page_put_link, .setattr = ext4_setattr, -#ifdef CONFIG_EXT4_FS_XATTR .setxattr = generic_setxattr, .getxattr = generic_getxattr, .listxattr = ext4_listxattr, .removexattr = generic_removexattr, -#endif }; const struct inode_operations ext4_fast_symlink_inode_operations = { .readlink = generic_readlink, .follow_link = ext4_follow_link, .setattr = ext4_setattr, -#ifdef CONFIG_EXT4_FS_XATTR .setxattr = generic_setxattr, .getxattr = generic_getxattr, .listxattr = ext4_listxattr, .removexattr = generic_removexattr, -#endif }; diff --git a/fs/ext4/xattr.h b/fs/ext4/xattr.h index 4222388c772f..7b5513ed3b38 100644 --- a/fs/ext4/xattr.h +++ b/fs/ext4/xattr.h @@ -92,8 +92,6 @@ struct ext4_xattr_ibody_find { struct ext4_iloc iloc; }; -# ifdef CONFIG_EXT4_FS_XATTR - extern const struct xattr_handler ext4_xattr_user_handler; extern const struct xattr_handler ext4_xattr_trusted_handler; extern const struct xattr_handler ext4_xattr_acl_access_handler; @@ -193,239 +191,6 @@ extern int ext4_try_to_evict_inline_data(handle_t *handle, extern void ext4_inline_data_truncate(struct inode *inode, int *has_inline); extern int ext4_convert_inline_data(struct inode *inode); -# else /* CONFIG_EXT4_FS_XATTR */ - -static inline int -ext4_xattr_get(struct inode *inode, int name_index, const char *name, - void *buffer, size_t size, int flags) -{ - return -EOPNOTSUPP; -} - -static inline int -ext4_xattr_set(struct inode *inode, int name_index, const char *name, - const void *value, size_t size, int flags) -{ - return -EOPNOTSUPP; -} - -static inline int -ext4_xattr_set_handle(handle_t *handle, struct inode *inode, int name_index, - const char *name, const void *value, size_t size, int flags) -{ - return -EOPNOTSUPP; -} - -static inline void -ext4_xattr_delete_inode(handle_t *handle, struct inode *inode) -{ -} - -static inline void -ext4_xattr_put_super(struct super_block *sb) -{ -} - -static __init inline int -ext4_init_xattr(void) -{ - return 0; -} - -static inline void -ext4_exit_xattr(void) -{ -} - -static inline int -ext4_expand_extra_isize_ea(struct inode *inode, int new_extra_isize, - struct ext4_inode *raw_inode, handle_t *handle) -{ - return -EOPNOTSUPP; -} - -#define ext4_xattr_handlers NULL - -static inline int -ext4_xattr_ibody_find(struct inode *inode, struct ext4_xattr_info *i, - struct ext4_xattr_ibody_find *is) -{ - return -EOPNOTSUPP; -} - -static inline int -ext4_xattr_ibody_set(handle_t *handle, struct inode *inode, - struct ext4_xattr_info *i, - struct ext4_xattr_ibody_find *is) -{ - return -EOPNOTSUPP; -} - -static inline int -ext4_xattr_ibody_get(struct inode *inode, int name_index, - const char *name, - void *buffer, size_t buffer_size) -{ - return -EOPNOTSUPP; -} - -static inline int ext4_find_inline_data_nolock(struct inode *inode) -{ - return 0; -} - -static inline int ext4_has_inline_data(struct inode *inode) -{ - return 0; -} - -static inline int ext4_get_inline_size(struct inode *inode) -{ - return 0; -} - -static inline int ext4_get_max_inline_size(struct inode *inode) -{ - return 0; -} - -static inline void ext4_write_inline_data(struct inode *inode, - struct ext4_iloc *iloc, - void *buffer, loff_t pos, - unsigned int len) -{ - return; -} - -static inline int ext4_init_inline_data(handle_t *handle, - struct inode *inode, - unsigned int len) -{ - return 0; -} - -static inline int ext4_destroy_inline_data(handle_t *handle, - struct inode *inode) -{ - return 0; -} - -static inline int ext4_readpage_inline(struct inode *inode, struct page *page) -{ - return 0; -} - -static inline int ext4_try_to_write_inline_data(struct address_space *mapping, - struct inode *inode, - loff_t pos, unsigned len, - unsigned flags, - struct page **pagep) -{ - return 0; -} - -static inline int ext4_write_inline_data_end(struct inode *inode, - loff_t pos, unsigned len, - unsigned copied, - struct page *page) -{ - return 0; -} - -static inline struct buffer_head * -ext4_journalled_write_inline_data(struct inode *inode, - unsigned len, - struct page *page) -{ - return NULL; -} - -static inline int -ext4_da_write_inline_data_begin(struct address_space *mapping, - struct inode *inode, - loff_t pos, unsigned len, - unsigned flags, - struct page **pagep, - void **fsdata) -{ - return 0; -} - -static inline int ext4_da_write_inline_data_end(struct inode *inode, loff_t pos, - unsigned len, unsigned copied, - struct page *page) -{ - return 0; -} - -static inline int ext4_try_add_inline_entry(handle_t *handle, - struct dentry *dentry, - struct inode *inode) -{ - return 0; -} - -static inline int ext4_try_create_inline_dir(handle_t *handle, - struct inode *parent, - struct inode *inode) -{ - return 0; -} -static inline int ext4_read_inline_dir(struct file *filp, - void *dirent, filldir_t filldir, - int *has_inline_data) -{ - return 0; -} - -static inline struct buffer_head * -ext4_find_inline_entry(struct inode *dir, - const struct qstr *d_name, - struct ext4_dir_entry_2 **res_dir, - int *has_inline_data) -{ - return NULL; -} -static inline int ext4_delete_inline_entry(handle_t *handle, - struct inode *dir, - struct ext4_dir_entry_2 *de_del, - struct buffer_head *bh, - int *has_inline_data) -{ - return 0; -} - -static inline int empty_inline_dir(struct inode *dir, int *has_inline_data) -{ - return 0; -} - -static inline struct buffer_head * -ext4_get_first_inline_block(struct inode *inode, - struct ext4_dir_entry_2 **parent_de, - int *retval) -{ - return NULL; -} - -static inline int ext4_inline_data_fiemap(struct inode *inode, - struct fiemap_extent_info *fieinfo, - int *has_inline) -{ - return 0; -} - -static inline void ext4_inline_data_truncate(struct inode *inode, - int *has_inline) -{ - return; -} - -static inline int ext4_convert_inline_data(struct inode *inode) -{ - return 0; -} -# endif /* CONFIG_EXT4_FS_XATTR */ #ifdef CONFIG_EXT4_FS_SECURITY extern int ext4_init_security(handle_t *handle, struct inode *inode, -- cgit v1.2.3-59-g8ed1b From 0c955b392ac83bcf8a8a3a087907c1c559496e07 Mon Sep 17 00:00:00 2001 From: Gregory CLEMENT Date: Mon, 10 Dec 2012 11:42:02 +0100 Subject: rtc: rtc-mv: Add the device tree binding documentation The support was already written, but the binding documentation was lacking. Signed-off-by: Gregory CLEMENT Acked-by: Andrew Lunn Signed-off-by: Grant Likely --- Documentation/devicetree/bindings/rtc/orion-rtc.txt | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 Documentation/devicetree/bindings/rtc/orion-rtc.txt (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/rtc/orion-rtc.txt b/Documentation/devicetree/bindings/rtc/orion-rtc.txt new file mode 100644 index 000000000000..3bf63ffa5160 --- /dev/null +++ b/Documentation/devicetree/bindings/rtc/orion-rtc.txt @@ -0,0 +1,18 @@ +* Mvebu Real Time Clock + +RTC controller for the Kirkwood, the Dove, the Armada 370 and the +Armada XP SoCs + +Required properties: +- compatible : Should be "marvell,orion-rtc" +- reg: physical base address of the controller and length of memory mapped + region. +- interrupts: IRQ line for the RTC. + +Example: + +rtc@10300 { + compatible = "marvell,orion-rtc"; + reg = <0xd0010300 0x20>; + interrupts = <50>; +}; -- cgit v1.2.3-59-g8ed1b From d825da2ede50160e567e666ff43c89a403bf0193 Mon Sep 17 00:00:00 2001 From: Rick Jones Date: Mon, 10 Dec 2012 11:33:00 +0000 Subject: doc: Tighten-up and clarify description of tcp_fin_timeout The description for tcp_fin_timeout should be tigher and more clear. In addition to being tighter, we should make the spelling of the state name consistent with what utilities report, remove the now dated reference to 2.2 and put the default in the consistent place. Signed-off-by: Rick Jones Signed-off-by: David S. Miller --- Documentation/networking/ip-sysctl.txt | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) (limited to 'Documentation') diff --git a/Documentation/networking/ip-sysctl.txt b/Documentation/networking/ip-sysctl.txt index 1b830cac4618..dd52d516cb89 100644 --- a/Documentation/networking/ip-sysctl.txt +++ b/Documentation/networking/ip-sysctl.txt @@ -224,15 +224,14 @@ tcp_fack - BOOLEAN The value is not used, if tcp_sack is not enabled. tcp_fin_timeout - INTEGER - Time to hold socket in state FIN-WAIT-2, if it was closed - by our side. Peer can be broken and never close its side, - or even died unexpectedly. Default value is 60sec. - Usual value used in 2.2 was 180 seconds, you may restore - it, but remember that if your machine is even underloaded WEB server, - you risk to overflow memory with kilotons of dead sockets, - FIN-WAIT-2 sockets are less dangerous than FIN-WAIT-1, - because they eat maximum 1.5K of memory, but they tend - to live longer. Cf. tcp_max_orphans. + The length of time an orphaned (no longer referenced by any + application) connection will remain in the FIN_WAIT_2 state + before it is aborted at the local end. While a perfectly + valid "receive only" state for an un-orphaned connection, an + orphaned connection in FIN_WAIT_2 state could otherwise wait + forever for the remote to close its end of the connection. + Cf. tcp_max_orphans + Default: 60 seconds tcp_frto - INTEGER Enables Forward RTO-Recovery (F-RTO) defined in RFC4138. -- cgit v1.2.3-59-g8ed1b From fed16bba8726dfb86f45429c6101db4c6927f4dd Mon Sep 17 00:00:00 2001 From: "Kumar, Anil" Date: Fri, 7 Dec 2012 14:39:34 +0530 Subject: mtd: nand: davinci: fix the binding documentation Since the aemif driver conversion to DT along with its movement to drivers/ folder is not yet done, fix NAND binding documentation to have NAND specific DT details only. Signed-off-by: Kumar, Anil Signed-off-by: Grant Likely --- .../devicetree/bindings/arm/davinci/nand.txt | 37 +++++++--------------- 1 file changed, 12 insertions(+), 25 deletions(-) (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/arm/davinci/nand.txt b/Documentation/devicetree/bindings/arm/davinci/nand.txt index e37241f1fdd8..49fc7ada929a 100644 --- a/Documentation/devicetree/bindings/arm/davinci/nand.txt +++ b/Documentation/devicetree/bindings/arm/davinci/nand.txt @@ -23,29 +23,16 @@ Recommended properties : - ti,davinci-nand-buswidth: buswidth 8 or 16 - ti,davinci-nand-use-bbt: use flash based bad block table support. -Example (enbw_cmc board): -aemif@60000000 { - compatible = "ti,davinci-aemif"; - #address-cells = <2>; - #size-cells = <1>; - reg = <0x68000000 0x80000>; - ranges = <2 0 0x60000000 0x02000000 - 3 0 0x62000000 0x02000000 - 4 0 0x64000000 0x02000000 - 5 0 0x66000000 0x02000000 - 6 0 0x68000000 0x02000000>; - nand@3,0 { - compatible = "ti,davinci-nand"; - reg = <3 0x0 0x807ff - 6 0x0 0x8000>; - #address-cells = <1>; - #size-cells = <1>; - ti,davinci-chipselect = <1>; - ti,davinci-mask-ale = <0>; - ti,davinci-mask-cle = <0>; - ti,davinci-mask-chipsel = <0>; - ti,davinci-ecc-mode = "hw"; - ti,davinci-ecc-bits = <4>; - ti,davinci-nand-use-bbt; - }; +Example(da850 EVM ): +nand_cs3@62000000 { + compatible = "ti,davinci-nand"; + reg = <0x62000000 0x807ff + 0x68000000 0x8000>; + ti,davinci-chipselect = <1>; + ti,davinci-mask-ale = <0>; + ti,davinci-mask-cle = <0>; + ti,davinci-mask-chipsel = <0>; + ti,davinci-ecc-mode = "hw"; + ti,davinci-ecc-bits = <4>; + ti,davinci-nand-use-bbt; }; -- cgit v1.2.3-59-g8ed1b From 98e4da8ca301e062d79ae168c67e56f3c3de3ce4 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Fri, 2 Nov 2012 17:05:42 +0900 Subject: f2fs: add document This adds a document describing the mount options, proc entries, usage, and design of Flash-Friendly File System, namely F2FS. Signed-off-by: Jaegeuk Kim --- Documentation/filesystems/00-INDEX | 2 + Documentation/filesystems/f2fs.txt | 417 +++++++++++++++++++++++++++++++++++++ 2 files changed, 419 insertions(+) create mode 100644 Documentation/filesystems/f2fs.txt (limited to 'Documentation') diff --git a/Documentation/filesystems/00-INDEX b/Documentation/filesystems/00-INDEX index 8c624a18f67d..ce5fd4677919 100644 --- a/Documentation/filesystems/00-INDEX +++ b/Documentation/filesystems/00-INDEX @@ -48,6 +48,8 @@ ext4.txt - info, mount options and specifications for the Ext4 filesystem. files.txt - info on file management in the Linux kernel. +f2fs.txt + - info and mount options for the F2FS filesystem. fuse.txt - info on the Filesystem in User SpacE including mount options. gfs2.txt diff --git a/Documentation/filesystems/f2fs.txt b/Documentation/filesystems/f2fs.txt new file mode 100644 index 000000000000..6ce540717dc4 --- /dev/null +++ b/Documentation/filesystems/f2fs.txt @@ -0,0 +1,417 @@ +================================================================================ +WHAT IS Flash-Friendly File System (F2FS)? +================================================================================ + +NAND flash memory-based storage devices, such as SSD, eMMC, and SD cards, have +been equipped on a variety systems ranging from mobile to server systems. Since +they are known to have different characteristics from the conventional rotating +disks, a file system, an upper layer to the storage device, should adapt to the +changes from the sketch in the design level. + +F2FS is a file system exploiting NAND flash memory-based storage devices, which +is based on Log-structured File System (LFS). The design has been focused on +addressing the fundamental issues in LFS, which are snowball effect of wandering +tree and high cleaning overhead. + +Since a NAND flash memory-based storage device shows different characteristic +according to its internal geometry or flash memory management scheme, namely FTL, +F2FS and its tools support various parameters not only for configuring on-disk +layout, but also for selecting allocation and cleaning algorithms. + +The file system formatting tool, "mkfs.f2fs", is available from the following +download page: http://sourceforge.net/projects/f2fs-tools/ + +================================================================================ +BACKGROUND AND DESIGN ISSUES +================================================================================ + +Log-structured File System (LFS) +-------------------------------- +"A log-structured file system writes all modifications to disk sequentially in +a log-like structure, thereby speeding up both file writing and crash recovery. +The log is the only structure on disk; it contains indexing information so that +files can be read back from the log efficiently. In order to maintain large free +areas on disk for fast writing, we divide the log into segments and use a +segment cleaner to compress the live information from heavily fragmented +segments." from Rosenblum, M. and Ousterhout, J. K., 1992, "The design and +implementation of a log-structured file system", ACM Trans. Computer Systems +10, 1, 26–52. + +Wandering Tree Problem +---------------------- +In LFS, when a file data is updated and written to the end of log, its direct +pointer block is updated due to the changed location. Then the indirect pointer +block is also updated due to the direct pointer block update. In this manner, +the upper index structures such as inode, inode map, and checkpoint block are +also updated recursively. This problem is called as wandering tree problem [1], +and in order to enhance the performance, it should eliminate or relax the update +propagation as much as possible. + +[1] Bityutskiy, A. 2005. JFFS3 design issues. http://www.linux-mtd.infradead.org/ + +Cleaning Overhead +----------------- +Since LFS is based on out-of-place writes, it produces so many obsolete blocks +scattered across the whole storage. In order to serve new empty log space, it +needs to reclaim these obsolete blocks seamlessly to users. This job is called +as a cleaning process. + +The process consists of three operations as follows. +1. A victim segment is selected through referencing segment usage table. +2. It loads parent index structures of all the data in the victim identified by + segment summary blocks. +3. It checks the cross-reference between the data and its parent index structure. +4. It moves valid data selectively. + +This cleaning job may cause unexpected long delays, so the most important goal +is to hide the latencies to users. And also definitely, it should reduce the +amount of valid data to be moved, and move them quickly as well. + +================================================================================ +KEY FEATURES +================================================================================ + +Flash Awareness +--------------- +- Enlarge the random write area for better performance, but provide the high + spatial locality +- Align FS data structures to the operational units in FTL as best efforts + +Wandering Tree Problem +---------------------- +- Use a term, “node”, that represents inodes as well as various pointer blocks +- Introduce Node Address Table (NAT) containing the locations of all the “node” + blocks; this will cut off the update propagation. + +Cleaning Overhead +----------------- +- Support a background cleaning process +- Support greedy and cost-benefit algorithms for victim selection policies +- Support multi-head logs for static/dynamic hot and cold data separation +- Introduce adaptive logging for efficient block allocation + +================================================================================ +MOUNT OPTIONS +================================================================================ + +background_gc_off Turn off cleaning operations, namely garbage collection, + triggered in background when I/O subsystem is idle. +disable_roll_forward Disable the roll-forward recovery routine +discard Issue discard/TRIM commands when a segment is cleaned. +no_heap Disable heap-style segment allocation which finds free + segments for data from the beginning of main area, while + for node from the end of main area. +nouser_xattr Disable Extended User Attributes. Note: xattr is enabled + by default if CONFIG_F2FS_FS_XATTR is selected. +noacl Disable POSIX Access Control List. Note: acl is enabled + by default if CONFIG_F2FS_FS_POSIX_ACL is selected. +active_logs=%u Support configuring the number of active logs. In the + current design, f2fs supports only 2, 4, and 6 logs. + Default number is 6. +disable_ext_identify Disable the extension list configured by mkfs, so f2fs + does not aware of cold files such as media files. + +================================================================================ +DEBUGFS ENTRIES +================================================================================ + +/sys/kernel/debug/f2fs/ contains information about all the partitions mounted as +f2fs. Each file shows the whole f2fs information. + +/sys/kernel/debug/f2fs/status includes: + - major file system information managed by f2fs currently + - average SIT information about whole segments + - current memory footprint consumed by f2fs. + +================================================================================ +USAGE +================================================================================ + +1. Download userland tools and compile them. + +2. Skip, if f2fs was compiled statically inside kernel. + Otherwise, insert the f2fs.ko module. + # insmod f2fs.ko + +3. Create a directory trying to mount + # mkdir /mnt/f2fs + +4. Format the block device, and then mount as f2fs + # mkfs.f2fs -l label /dev/block_device + # mount -t f2fs /dev/block_device /mnt/f2fs + +Format options +-------------- +-l [label] : Give a volume label, up to 256 unicode name. +-a [0 or 1] : Split start location of each area for heap-based allocation. + 1 is set by default, which performs this. +-o [int] : Set overprovision ratio in percent over volume size. + 5 is set by default. +-s [int] : Set the number of segments per section. + 1 is set by default. +-z [int] : Set the number of sections per zone. + 1 is set by default. +-e [str] : Set basic extension list. e.g. "mp3,gif,mov" + +================================================================================ +DESIGN +================================================================================ + +On-disk Layout +-------------- + +F2FS divides the whole volume into a number of segments, each of which is fixed +to 2MB in size. A section is composed of consecutive segments, and a zone +consists of a set of sections. By default, section and zone sizes are set to one +segment size identically, but users can easily modify the sizes by mkfs. + +F2FS splits the entire volume into six areas, and all the areas except superblock +consists of multiple segments as described below. + + align with the zone size <-| + |-> align with the segment size + _________________________________________________________________________ + | | | Node | Segment | Segment | | + | Superblock | Checkpoint | Address | Info. | Summary | Main | + | (SB) | (CP) | Table (NAT) | Table (SIT) | Area (SSA) | | + |____________|_____2______|______N______|______N______|______N_____|__N___| + . . + . . + . . + ._________________________________________. + |_Segment_|_..._|_Segment_|_..._|_Segment_| + . . + ._________._________ + |_section_|__...__|_ + . . + .________. + |__zone__| + +- Superblock (SB) + : It is located at the beginning of the partition, and there exist two copies + to avoid file system crash. It contains basic partition information and some + default parameters of f2fs. + +- Checkpoint (CP) + : It contains file system information, bitmaps for valid NAT/SIT sets, orphan + inode lists, and summary entries of current active segments. + +- Node Address Table (NAT) + : It is composed of a block address table for all the node blocks stored in + Main area. + +- Segment Information Table (SIT) + : It contains segment information such as valid block count and bitmap for the + validity of all the blocks. + +- Segment Summary Area (SSA) + : It contains summary entries which contains the owner information of all the + data and node blocks stored in Main area. + +- Main Area + : It contains file and directory data including their indices. + +In order to avoid misalignment between file system and flash-based storage, F2FS +aligns the start block address of CP with the segment size. Also, it aligns the +start block address of Main area with the zone size by reserving some segments +in SSA area. + +Reference the following survey for additional technical details. +https://wiki.linaro.org/WorkingGroups/Kernel/Projects/FlashCardSurvey + +File System Metadata Structure +------------------------------ + +F2FS adopts the checkpointing scheme to maintain file system consistency. At +mount time, F2FS first tries to find the last valid checkpoint data by scanning +CP area. In order to reduce the scanning time, F2FS uses only two copies of CP. +One of them always indicates the last valid data, which is called as shadow copy +mechanism. In addition to CP, NAT and SIT also adopt the shadow copy mechanism. + +For file system consistency, each CP points to which NAT and SIT copies are +valid, as shown as below. + + +--------+----------+---------+ + | CP | NAT | SIT | + +--------+----------+---------+ + . . . . + . . . . + . . . . + +-------+-------+--------+--------+--------+--------+ + | CP #0 | CP #1 | NAT #0 | NAT #1 | SIT #0 | SIT #1 | + +-------+-------+--------+--------+--------+--------+ + | ^ ^ + | | | + `----------------------------------------' + +Index Structure +--------------- + +The key data structure to manage the data locations is a "node". Similar to +traditional file structures, F2FS has three types of node: inode, direct node, +indirect node. F2FS assigns 4KB to an inode block which contains 929 data block +indices, two direct node pointers, two indirect node pointers, and one double +indirect node pointer as described below. One direct node block contains 1018 +data blocks, and one indirect node block contains also 1018 node blocks. Thus, +one inode block (i.e., a file) covers: + + 4KB * (923 + 2 * 1018 + 2 * 1018 * 1018 + 1018 * 1018 * 1018) := 3.94TB. + + Inode block (4KB) + |- data (923) + |- direct node (2) + | `- data (1018) + |- indirect node (2) + | `- direct node (1018) + | `- data (1018) + `- double indirect node (1) + `- indirect node (1018) + `- direct node (1018) + `- data (1018) + +Note that, all the node blocks are mapped by NAT which means the location of +each node is translated by the NAT table. In the consideration of the wandering +tree problem, F2FS is able to cut off the propagation of node updates caused by +leaf data writes. + +Directory Structure +------------------- + +A directory entry occupies 11 bytes, which consists of the following attributes. + +- hash hash value of the file name +- ino inode number +- len the length of file name +- type file type such as directory, symlink, etc + +A dentry block consists of 214 dentry slots and file names. Therein a bitmap is +used to represent whether each dentry is valid or not. A dentry block occupies +4KB with the following composition. + + Dentry Block(4 K) = bitmap (27 bytes) + reserved (3 bytes) + + dentries(11 * 214 bytes) + file name (8 * 214 bytes) + + [Bucket] + +--------------------------------+ + |dentry block 1 | dentry block 2 | + +--------------------------------+ + . . + . . + . [Dentry Block Structure: 4KB] . + +--------+----------+----------+------------+ + | bitmap | reserved | dentries | file names | + +--------+----------+----------+------------+ + [Dentry Block: 4KB] . . + . . + . . + +------+------+-----+------+ + | hash | ino | len | type | + +------+------+-----+------+ + [Dentry Structure: 11 bytes] + +F2FS implements multi-level hash tables for directory structure. Each level has +a hash table with dedicated number of hash buckets as shown below. Note that +"A(2B)" means a bucket includes 2 data blocks. + +---------------------- +A : bucket +B : block +N : MAX_DIR_HASH_DEPTH +---------------------- + +level #0 | A(2B) + | +level #1 | A(2B) - A(2B) + | +level #2 | A(2B) - A(2B) - A(2B) - A(2B) + . | . . . . +level #N/2 | A(2B) - A(2B) - A(2B) - A(2B) - A(2B) - ... - A(2B) + . | . . . . +level #N | A(4B) - A(4B) - A(4B) - A(4B) - A(4B) - ... - A(4B) + +The number of blocks and buckets are determined by, + + ,- 2, if n < MAX_DIR_HASH_DEPTH / 2, + # of blocks in level #n = | + `- 4, Otherwise + + ,- 2^n, if n < MAX_DIR_HASH_DEPTH / 2, + # of buckets in level #n = | + `- 2^((MAX_DIR_HASH_DEPTH / 2) - 1), Otherwise + +When F2FS finds a file name in a directory, at first a hash value of the file +name is calculated. Then, F2FS scans the hash table in level #0 to find the +dentry consisting of the file name and its inode number. If not found, F2FS +scans the next hash table in level #1. In this way, F2FS scans hash tables in +each levels incrementally from 1 to N. In each levels F2FS needs to scan only +one bucket determined by the following equation, which shows O(log(# of files)) +complexity. + + bucket number to scan in level #n = (hash value) % (# of buckets in level #n) + +In the case of file creation, F2FS finds empty consecutive slots that cover the +file name. F2FS searches the empty slots in the hash tables of whole levels from +1 to N in the same way as the lookup operation. + +The following figure shows an example of two cases holding children. + --------------> Dir <-------------- + | | + child child + + child - child [hole] - child + + child - child - child [hole] - [hole] - child + + Case 1: Case 2: + Number of children = 6, Number of children = 3, + File size = 7 File size = 7 + +Default Block Allocation +------------------------ + +At runtime, F2FS manages six active logs inside "Main" area: Hot/Warm/Cold node +and Hot/Warm/Cold data. + +- Hot node contains direct node blocks of directories. +- Warm node contains direct node blocks except hot node blocks. +- Cold node contains indirect node blocks +- Hot data contains dentry blocks +- Warm data contains data blocks except hot and cold data blocks +- Cold data contains multimedia data or migrated data blocks + +LFS has two schemes for free space management: threaded log and copy-and-compac- +tion. The copy-and-compaction scheme which is known as cleaning, is well-suited +for devices showing very good sequential write performance, since free segments +are served all the time for writing new data. However, it suffers from cleaning +overhead under high utilization. Contrarily, the threaded log scheme suffers +from random writes, but no cleaning process is needed. F2FS adopts a hybrid +scheme where the copy-and-compaction scheme is adopted by default, but the +policy is dynamically changed to the threaded log scheme according to the file +system status. + +In order to align F2FS with underlying flash-based storage, F2FS allocates a +segment in a unit of section. F2FS expects that the section size would be the +same as the unit size of garbage collection in FTL. Furthermore, with respect +to the mapping granularity in FTL, F2FS allocates each section of the active +logs from different zones as much as possible, since FTL can write the data in +the active logs into one allocation unit according to its mapping granularity. + +Cleaning process +---------------- + +F2FS does cleaning both on demand and in the background. On-demand cleaning is +triggered when there are not enough free segments to serve VFS calls. Background +cleaner is operated by a kernel thread, and triggers the cleaning job when the +system is idle. + +F2FS supports two victim selection policies: greedy and cost-benefit algorithms. +In the greedy algorithm, F2FS selects a victim segment having the smallest number +of valid blocks. In the cost-benefit algorithm, F2FS selects a victim segment +according to the segment age and the number of valid blocks in order to address +log block thrashing problem in the greedy algorithm. F2FS adopts the greedy +algorithm for on-demand cleaner, while background cleaner adopts cost-benefit +algorithm. + +In order to identify whether the data in the victim segment are valid or not, +F2FS manages a bitmap. Each bit represents the validity of a block, and the +bitmap is composed of a bit stream covering whole blocks in main area. -- cgit v1.2.3-59-g8ed1b From 5bb446a2890f4dfd62ce4d9fcc982f8ea3c20010 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Tue, 27 Nov 2012 14:36:14 +0900 Subject: f2fs: update the f2fs document I moved the f2fs-tools.git into kernel.org. And I added a new mailing list, linux-f2fs-devel@lists.sourceforge.net. Signed-off-by: Jaegeuk Kim --- Documentation/filesystems/f2fs.txt | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'Documentation') diff --git a/Documentation/filesystems/f2fs.txt b/Documentation/filesystems/f2fs.txt index 6ce540717dc4..dee3960982b8 100644 --- a/Documentation/filesystems/f2fs.txt +++ b/Documentation/filesystems/f2fs.txt @@ -19,7 +19,11 @@ F2FS and its tools support various parameters not only for configuring on-disk layout, but also for selecting allocation and cleaning algorithms. The file system formatting tool, "mkfs.f2fs", is available from the following -download page: http://sourceforge.net/projects/f2fs-tools/ +git tree: +>> git://git.kernel.org/pub/scm/linux/kernel/git/jaegeuk/f2fs-tools.git + +For reporting bugs and sending patches, please use the following mailing list: +>> linux-f2fs-devel@lists.sourceforge.net ================================================================================ BACKGROUND AND DESIGN ISSUES -- cgit v1.2.3-59-g8ed1b From d08ab08d140de247eb812ce2c3c4b323620e4609 Mon Sep 17 00:00:00 2001 From: Huajun Li Date: Wed, 5 Dec 2012 16:45:32 +0800 Subject: f2fs: fix a typo in f2fs documentation In f2fs_fs.h, one f2fs inode contains 923 data block pointers, while f2fs documentation says it is 929. Fix this inconsistence. Signed-off-by: Huajun Li --- Documentation/filesystems/f2fs.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Documentation') diff --git a/Documentation/filesystems/f2fs.txt b/Documentation/filesystems/f2fs.txt index dee3960982b8..8fbd8b46ee34 100644 --- a/Documentation/filesystems/f2fs.txt +++ b/Documentation/filesystems/f2fs.txt @@ -253,7 +253,7 @@ Index Structure The key data structure to manage the data locations is a "node". Similar to traditional file structures, F2FS has three types of node: inode, direct node, -indirect node. F2FS assigns 4KB to an inode block which contains 929 data block +indirect node. F2FS assigns 4KB to an inode block which contains 923 data block indices, two direct node pointers, two indirect node pointers, and one double indirect node pointer as described below. One direct node block contains 1018 data blocks, and one indirect node block contains also 1018 node blocks. Thus, -- cgit v1.2.3-59-g8ed1b From 1a687c2e9a99335c9e77392f050fe607fa18a652 Mon Sep 17 00:00:00 2001 From: Mel Gorman Date: Thu, 22 Nov 2012 11:16:36 +0000 Subject: mm: sched: numa: Control enabling and disabling of NUMA balancing This patch adds Kconfig options and kernel parameters to allow the enabling and disabling of automatic NUMA balancing. The existance of such a switch was and is very important when debugging problems related to transparent hugepages and we should have the same for automatic NUMA placement. Signed-off-by: Mel Gorman --- Documentation/kernel-parameters.txt | 3 +++ include/linux/sched.h | 4 ++++ init/Kconfig | 8 +++++++ kernel/sched/core.c | 48 +++++++++++++++++++++++++------------ kernel/sched/fair.c | 3 +++ kernel/sched/features.h | 6 +++-- mm/mempolicy.c | 46 +++++++++++++++++++++++++++++++++++ 7 files changed, 101 insertions(+), 17 deletions(-) (limited to 'Documentation') diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt index 9776f068306b..2e8d2625b814 100644 --- a/Documentation/kernel-parameters.txt +++ b/Documentation/kernel-parameters.txt @@ -1996,6 +1996,9 @@ bytes respectively. Such letter suffixes can also be entirely omitted. nr_uarts= [SERIAL] maximum number of UARTs to be registered. + numa_balancing= [KNL,X86] Enable or disable automatic NUMA balancing. + Allowed values are enable and disable + numa_zonelist_order= [KNL, BOOT] Select zonelist order for NUMA. one of ['zone', 'node', 'default'] can be specified This can be set from sysctl after boot. diff --git a/include/linux/sched.h b/include/linux/sched.h index 0f4ff2bd03f6..b1e619f9ff1a 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -1563,10 +1563,14 @@ struct task_struct { #ifdef CONFIG_NUMA_BALANCING extern void task_numa_fault(int node, int pages, bool migrated); +extern void set_numabalancing_state(bool enabled); #else static inline void task_numa_fault(int node, int pages, bool migrated) { } +static inline void set_numabalancing_state(bool enabled) +{ +} #endif /* diff --git a/init/Kconfig b/init/Kconfig index 9f00f004796a..18e2a5920a34 100644 --- a/init/Kconfig +++ b/init/Kconfig @@ -720,6 +720,14 @@ config ARCH_USES_NUMA_PROT_NONE depends on ARCH_WANTS_PROT_NUMA_PROT_NONE depends on NUMA_BALANCING +config NUMA_BALANCING_DEFAULT_ENABLED + bool "Automatically enable NUMA aware memory/task placement" + default y + depends on NUMA_BALANCING + help + If set, autonumic NUMA balancing will be enabled if running on a NUMA + machine. + config NUMA_BALANCING bool "Memory placement aware NUMA scheduler" default y diff --git a/kernel/sched/core.c b/kernel/sched/core.c index 9d255bc0e278..7a45015274ab 100644 --- a/kernel/sched/core.c +++ b/kernel/sched/core.c @@ -192,23 +192,10 @@ static void sched_feat_disable(int i) { }; static void sched_feat_enable(int i) { }; #endif /* HAVE_JUMP_LABEL */ -static ssize_t -sched_feat_write(struct file *filp, const char __user *ubuf, - size_t cnt, loff_t *ppos) +static int sched_feat_set(char *cmp) { - char buf[64]; - char *cmp; - int neg = 0; int i; - - if (cnt > 63) - cnt = 63; - - if (copy_from_user(&buf, ubuf, cnt)) - return -EFAULT; - - buf[cnt] = 0; - cmp = strstrip(buf); + int neg = 0; if (strncmp(cmp, "NO_", 3) == 0) { neg = 1; @@ -228,6 +215,27 @@ sched_feat_write(struct file *filp, const char __user *ubuf, } } + return i; +} + +static ssize_t +sched_feat_write(struct file *filp, const char __user *ubuf, + size_t cnt, loff_t *ppos) +{ + char buf[64]; + char *cmp; + int i; + + if (cnt > 63) + cnt = 63; + + if (copy_from_user(&buf, ubuf, cnt)) + return -EFAULT; + + buf[cnt] = 0; + cmp = strstrip(buf); + + i = sched_feat_set(cmp); if (i == __SCHED_FEAT_NR) return -EINVAL; @@ -1549,6 +1557,16 @@ static void __sched_fork(struct task_struct *p) #endif /* CONFIG_NUMA_BALANCING */ } +#ifdef CONFIG_NUMA_BALANCING +void set_numabalancing_state(bool enabled) +{ + if (enabled) + sched_feat_set("NUMA"); + else + sched_feat_set("NO_NUMA"); +} +#endif /* CONFIG_NUMA_BALANCING */ + /* * fork()/clone()-time setup: */ diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index 4b577863933f..7a02a2082e95 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -811,6 +811,9 @@ void task_numa_fault(int node, int pages, bool migrated) { struct task_struct *p = current; + if (!sched_feat_numa(NUMA)) + return; + /* FIXME: Allocate task-specific structure for placement policy here */ /* diff --git a/kernel/sched/features.h b/kernel/sched/features.h index 5fb7aefbec80..d2373a3e3252 100644 --- a/kernel/sched/features.h +++ b/kernel/sched/features.h @@ -63,8 +63,10 @@ SCHED_FEAT(RT_RUNTIME_SHARE, true) SCHED_FEAT(LB_MIN, false) /* - * Apply the automatic NUMA scheduling policy + * Apply the automatic NUMA scheduling policy. Enabled automatically + * at runtime if running on a NUMA machine. Can be controlled via + * numa_balancing= */ #ifdef CONFIG_NUMA_BALANCING -SCHED_FEAT(NUMA, true) +SCHED_FEAT(NUMA, false) #endif diff --git a/mm/mempolicy.c b/mm/mempolicy.c index fd20e28fd2ad..046308e9b999 100644 --- a/mm/mempolicy.c +++ b/mm/mempolicy.c @@ -2521,6 +2521,50 @@ void mpol_free_shared_policy(struct shared_policy *p) mutex_unlock(&p->mutex); } +#ifdef CONFIG_NUMA_BALANCING +static bool __initdata numabalancing_override; + +static void __init check_numabalancing_enable(void) +{ + bool numabalancing_default = false; + + if (IS_ENABLED(CONFIG_NUMA_BALANCING_DEFAULT_ENABLED)) + numabalancing_default = true; + + if (nr_node_ids > 1 && !numabalancing_override) { + printk(KERN_INFO "Enabling automatic NUMA balancing. " + "Configure with numa_balancing= or sysctl"); + set_numabalancing_state(numabalancing_default); + } +} + +static int __init setup_numabalancing(char *str) +{ + int ret = 0; + if (!str) + goto out; + numabalancing_override = true; + + if (!strcmp(str, "enable")) { + set_numabalancing_state(true); + ret = 1; + } else if (!strcmp(str, "disable")) { + set_numabalancing_state(false); + ret = 1; + } +out: + if (!ret) + printk(KERN_WARNING "Unable to parse numa_balancing=\n"); + + return ret; +} +__setup("numa_balancing=", setup_numabalancing); +#else +static inline void __init check_numabalancing_enable(void) +{ +} +#endif /* CONFIG_NUMA_BALANCING */ + /* assumes fs == KERNEL_DS */ void __init numa_policy_init(void) { @@ -2571,6 +2615,8 @@ void __init numa_policy_init(void) if (do_set_mempolicy(MPOL_INTERLEAVE, 0, &interleave_nodes)) printk("numa_policy_init: interleaving failed\n"); + + check_numabalancing_enable(); } /* Reset policy of current process to default */ -- cgit v1.2.3-59-g8ed1b From d9713679dbd2a6ecb840cd5b65a3ec555c1ec3d4 Mon Sep 17 00:00:00 2001 From: Lai Jiangshan Date: Tue, 11 Dec 2012 16:01:03 -0800 Subject: memory_hotplug: fix possible incorrect node_states[N_NORMAL_MEMORY] Currently memory_hotplug only manages the node_states[N_HIGH_MEMORY], it forgets to manage node_states[N_NORMAL_MEMORY]. This may cause node_states[N_NORMAL_MEMORY] to become incorrect. Example, if a node is empty before online, and we online a memory which is in ZONE_NORMAL. And after online, node_states[N_HIGH_MEMORY] is correct, but node_states[N_NORMAL_MEMORY] is incorrect, the online code doesn't set the new online node to node_states[N_NORMAL_MEMORY]. The same thing will happen when offlining (the offline code doesn't clear the node from node_states[N_NORMAL_MEMORY] when needed). Some memory managment code depends node_states[N_NORMAL_MEMORY], so we have to fix up the node_states[N_NORMAL_MEMORY]. We add node_states_check_changes_online() and node_states_check_changes_offline() to detect whether node_states[N_HIGH_MEMORY] and node_states[N_NORMAL_MEMORY] are changed while hotpluging. Also add @status_change_nid_normal to struct memory_notify, thus the memory hotplug callbacks know whether the node_states[N_NORMAL_MEMORY] are changed. (We can add a @flags and reuse @status_change_nid instead of introducing @status_change_nid_normal, but it will add much more complexity in memory hotplug callback in every subsystem. So introducing @status_change_nid_normal is better and it doesn't change the sematics of @status_change_nid) Signed-off-by: Lai Jiangshan Cc: David Rientjes Cc: Minchan Kim Cc: KOSAKI Motohiro Cc: Yasuaki Ishimatsu Cc: Rob Landley Cc: Jiang Liu Cc: Kay Sievers Cc: Greg Kroah-Hartman Cc: Mel Gorman Cc: Wen Congyang Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- Documentation/memory-hotplug.txt | 5 +- include/linux/memory.h | 1 + mm/memory_hotplug.c | 136 ++++++++++++++++++++++++++++++++++----- 3 files changed, 125 insertions(+), 17 deletions(-) (limited to 'Documentation') diff --git a/Documentation/memory-hotplug.txt b/Documentation/memory-hotplug.txt index 6d0c2519cf47..6e6cbc78f329 100644 --- a/Documentation/memory-hotplug.txt +++ b/Documentation/memory-hotplug.txt @@ -377,15 +377,18 @@ The third argument is passed by pointer of struct memory_notify. struct memory_notify { unsigned long start_pfn; unsigned long nr_pages; + int status_change_nid_normal; int status_change_nid; } start_pfn is start_pfn of online/offline memory. nr_pages is # of pages of online/offline memory. +status_change_nid_normal is set node id when N_NORMAL_MEMORY of nodemask +is (will be) set/clear, if this is -1, then nodemask status is not changed. status_change_nid is set node id when N_HIGH_MEMORY of nodemask is (will be) set/clear. It means a new(memoryless) node gets new memory by online and a node loses all memory. If this is -1, then nodemask status is not changed. -If status_changed_nid >= 0, callback should create/discard structures for the +If status_changed_nid* >= 0, callback should create/discard structures for the node if necessary. -------------- diff --git a/include/linux/memory.h b/include/linux/memory.h index ff9a9f8e0ed9..a09216d0dcc7 100644 --- a/include/linux/memory.h +++ b/include/linux/memory.h @@ -53,6 +53,7 @@ int arch_get_memory_phys_device(unsigned long start_pfn); struct memory_notify { unsigned long start_pfn; unsigned long nr_pages; + int status_change_nid_normal; int status_change_nid; }; diff --git a/mm/memory_hotplug.c b/mm/memory_hotplug.c index ec2f199cc5f7..72195602ded5 100644 --- a/mm/memory_hotplug.c +++ b/mm/memory_hotplug.c @@ -460,6 +460,53 @@ static int online_pages_range(unsigned long start_pfn, unsigned long nr_pages, return 0; } +/* check which state of node_states will be changed when online memory */ +static void node_states_check_changes_online(unsigned long nr_pages, + struct zone *zone, struct memory_notify *arg) +{ + int nid = zone_to_nid(zone); + enum zone_type zone_last = ZONE_NORMAL; + + /* + * If we have HIGHMEM, node_states[N_NORMAL_MEMORY] contains nodes + * which have 0...ZONE_NORMAL, set zone_last to ZONE_NORMAL. + * + * If we don't have HIGHMEM, node_states[N_NORMAL_MEMORY] contains nodes + * which have 0...ZONE_MOVABLE, set zone_last to ZONE_MOVABLE. + */ + if (N_HIGH_MEMORY == N_NORMAL_MEMORY) + zone_last = ZONE_MOVABLE; + + /* + * if the memory to be online is in a zone of 0...zone_last, and + * the zones of 0...zone_last don't have memory before online, we will + * need to set the node to node_states[N_NORMAL_MEMORY] after + * the memory is online. + */ + if (zone_idx(zone) <= zone_last && !node_state(nid, N_NORMAL_MEMORY)) + arg->status_change_nid_normal = nid; + else + arg->status_change_nid_normal = -1; + + /* + * if the node don't have memory befor online, we will need to + * set the node to node_states[N_HIGH_MEMORY] after the memory + * is online. + */ + if (!node_state(nid, N_HIGH_MEMORY)) + arg->status_change_nid = nid; + else + arg->status_change_nid = -1; +} + +static void node_states_set_node(int node, struct memory_notify *arg) +{ + if (arg->status_change_nid_normal >= 0) + node_set_state(node, N_NORMAL_MEMORY); + + node_set_state(node, N_HIGH_MEMORY); +} + int __ref online_pages(unsigned long pfn, unsigned long nr_pages) { @@ -471,13 +518,18 @@ int __ref online_pages(unsigned long pfn, unsigned long nr_pages) struct memory_notify arg; lock_memory_hotplug(); + /* + * This doesn't need a lock to do pfn_to_page(). + * The section can't be removed here because of the + * memory_block->state_mutex. + */ + zone = page_zone(pfn_to_page(pfn)); + arg.start_pfn = pfn; arg.nr_pages = nr_pages; - arg.status_change_nid = -1; + node_states_check_changes_online(nr_pages, zone, &arg); nid = page_to_nid(pfn_to_page(pfn)); - if (node_present_pages(nid) == 0) - arg.status_change_nid = nid; ret = memory_notify(MEM_GOING_ONLINE, &arg); ret = notifier_to_errno(ret); @@ -486,12 +538,6 @@ int __ref online_pages(unsigned long pfn, unsigned long nr_pages) unlock_memory_hotplug(); return ret; } - /* - * This doesn't need a lock to do pfn_to_page(). - * The section can't be removed here because of the - * memory_block->state_mutex. - */ - zone = page_zone(pfn_to_page(pfn)); /* * If this zone is not populated, then it is not in zonelist. * This means the page allocator ignores this zone. @@ -521,7 +567,7 @@ int __ref online_pages(unsigned long pfn, unsigned long nr_pages) zone->present_pages += onlined_pages; zone->zone_pgdat->node_present_pages += onlined_pages; if (onlined_pages) { - node_set_state(zone_to_nid(zone), N_HIGH_MEMORY); + node_states_set_node(zone_to_nid(zone), &arg); if (need_zonelists_rebuild) build_all_zonelists(NULL, NULL); else @@ -871,6 +917,67 @@ check_pages_isolated(unsigned long start_pfn, unsigned long end_pfn) return offlined; } +/* check which state of node_states will be changed when offline memory */ +static void node_states_check_changes_offline(unsigned long nr_pages, + struct zone *zone, struct memory_notify *arg) +{ + struct pglist_data *pgdat = zone->zone_pgdat; + unsigned long present_pages = 0; + enum zone_type zt, zone_last = ZONE_NORMAL; + + /* + * If we have HIGHMEM, node_states[N_NORMAL_MEMORY] contains nodes + * which have 0...ZONE_NORMAL, set zone_last to ZONE_NORMAL. + * + * If we don't have HIGHMEM, node_states[N_NORMAL_MEMORY] contains nodes + * which have 0...ZONE_MOVABLE, set zone_last to ZONE_MOVABLE. + */ + if (N_HIGH_MEMORY == N_NORMAL_MEMORY) + zone_last = ZONE_MOVABLE; + + /* + * check whether node_states[N_NORMAL_MEMORY] will be changed. + * If the memory to be offline is in a zone of 0...zone_last, + * and it is the last present memory, 0...zone_last will + * become empty after offline , thus we can determind we will + * need to clear the node from node_states[N_NORMAL_MEMORY]. + */ + for (zt = 0; zt <= zone_last; zt++) + present_pages += pgdat->node_zones[zt].present_pages; + if (zone_idx(zone) <= zone_last && nr_pages >= present_pages) + arg->status_change_nid_normal = zone_to_nid(zone); + else + arg->status_change_nid_normal = -1; + + /* + * node_states[N_HIGH_MEMORY] contains nodes which have 0...ZONE_MOVABLE + */ + zone_last = ZONE_MOVABLE; + + /* + * check whether node_states[N_HIGH_MEMORY] will be changed + * If we try to offline the last present @nr_pages from the node, + * we can determind we will need to clear the node from + * node_states[N_HIGH_MEMORY]. + */ + for (; zt <= zone_last; zt++) + present_pages += pgdat->node_zones[zt].present_pages; + if (nr_pages >= present_pages) + arg->status_change_nid = zone_to_nid(zone); + else + arg->status_change_nid = -1; +} + +static void node_states_clear_node(int node, struct memory_notify *arg) +{ + if (arg->status_change_nid_normal >= 0) + node_clear_state(node, N_NORMAL_MEMORY); + + if ((N_HIGH_MEMORY != N_NORMAL_MEMORY) && + (arg->status_change_nid >= 0)) + node_clear_state(node, N_HIGH_MEMORY); +} + static int __ref __offline_pages(unsigned long start_pfn, unsigned long end_pfn, unsigned long timeout) { @@ -905,9 +1012,7 @@ static int __ref __offline_pages(unsigned long start_pfn, arg.start_pfn = start_pfn; arg.nr_pages = nr_pages; - arg.status_change_nid = -1; - if (nr_pages >= node_present_pages(node)) - arg.status_change_nid = node; + node_states_check_changes_offline(nr_pages, zone, &arg); ret = memory_notify(MEM_GOING_OFFLINE, &arg); ret = notifier_to_errno(ret); @@ -980,10 +1085,9 @@ repeat: } else zone_pcp_update(zone); - if (!node_present_pages(node)) { - node_clear_state(node, N_HIGH_MEMORY); + node_states_clear_node(node, &arg); + if (arg.status_change_nid >= 0) kswapd_stop(node); - } vm_total_pages = nr_free_pagecache_pages(); writeback_set_ratelimit(); -- cgit v1.2.3-59-g8ed1b From 348b465530ad222ce80e516524dd01009a4f9205 Mon Sep 17 00:00:00 2001 From: Jeff Liu Date: Tue, 11 Dec 2012 16:01:28 -0800 Subject: Documentation/cgroups/memory.txt: s/mem_cgroup_charge/mem_cgroup_change_common/ mem_cgroup_charge_common() is invoked as the entry point for cgroup limits charge rather than mem_cgroup_charge(), as the later has been removed for years. Update the cgroup/memory.txt to reflect this change. Signed-off-by: Jie Liu Cc: Ying Han Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- Documentation/cgroups/memory.txt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'Documentation') diff --git a/Documentation/cgroups/memory.txt b/Documentation/cgroups/memory.txt index 71c4da413444..a25cb3fafeba 100644 --- a/Documentation/cgroups/memory.txt +++ b/Documentation/cgroups/memory.txt @@ -144,9 +144,9 @@ Figure 1 shows the important aspects of the controller 3. Each page has a pointer to the page_cgroup, which in turn knows the cgroup it belongs to -The accounting is done as follows: mem_cgroup_charge() is invoked to set up -the necessary data structures and check if the cgroup that is being charged -is over its limit. If it is, then reclaim is invoked on the cgroup. +The accounting is done as follows: mem_cgroup_charge_common() is invoked to +set up the necessary data structures and check if the cgroup that is being +charged is over its limit. If it is, then reclaim is invoked on the cgroup. More details can be found in the reclaim section of this document. If everything goes well, a page meta-data-structure called page_cgroup is updated. page_cgroup has its own LRU on cgroup. -- cgit v1.2.3-59-g8ed1b From 511c2aba8f07fc45bdcba548cb63f7b8a450c6dc Mon Sep 17 00:00:00 2001 From: Lai Jiangshan Date: Tue, 11 Dec 2012 16:03:16 -0800 Subject: mm, memory-hotplug: dynamic configure movable memory and portion memory Add online_movable and online_kernel for logic memory hotplug. This is the dynamic version of "movablecore" & "kernelcore". We have the same reason to introduce it as to introduce "movablecore" & "kernelcore". It has the same motive as "movablecore" & "kernelcore", but it is dynamic/running-time: o We can configure memory as kernelcore or movablecore after boot. Userspace workload is increased, we need more hugepage, we can't use "online_movable" to add memory and allow the system use more THP(transparent-huge-page), vice-verse when kernel workload is increase. Also help for virtualization to dynamic configure host/guest's memory, to save/(reduce waste) memory. Memory capacity on Demand o When a new node is physically online after boot, we need to use "online_movable" or "online_kernel" to configure/portion it as we expected when we logic-online it. This configuration also helps for physically-memory-migrate. o all benefit as the same as existed "movablecore" & "kernelcore". o Preparing for movable-node, which is very important for power-saving, hardware partitioning and high-available-system(hardware fault management). (Note, we don't introduce movable-node here.) Action behavior: When a memoryblock/memorysection is onlined by "online_movable", the kernel will not have directly reference to the page of the memoryblock, thus we can remove that memory any time when needed. When it is online by "online_kernel", the kernel can use it. When it is online by "online", the zone type doesn't changed. Current constraints: Only the memoryblock which is adjacent to the ZONE_MOVABLE can be online from ZONE_NORMAL to ZONE_MOVABLE. [akpm@linux-foundation.org: use min_t, cleanups] Signed-off-by: Lai Jiangshan Signed-off-by: Wen Congyang Cc: Yasuaki Ishimatsu Cc: Lai Jiangshan Cc: Jiang Liu Cc: KOSAKI Motohiro Cc: Minchan Kim Cc: Mel Gorman Cc: David Rientjes Cc: Yinghai Lu Cc: Rusty Russell Cc: Greg KH Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- Documentation/memory-hotplug.txt | 14 +++++- drivers/base/memory.c | 33 ++++++++----- include/linux/memory_hotplug.h | 13 ++++- mm/memory_hotplug.c | 100 ++++++++++++++++++++++++++++++++++++++- 4 files changed, 146 insertions(+), 14 deletions(-) (limited to 'Documentation') diff --git a/Documentation/memory-hotplug.txt b/Documentation/memory-hotplug.txt index 6e6cbc78f329..c6f993d491b5 100644 --- a/Documentation/memory-hotplug.txt +++ b/Documentation/memory-hotplug.txt @@ -161,7 +161,8 @@ a recent addition and not present on older kernels. in the memory block. 'state' : read-write at read: contains online/offline state of memory. - at write: user can specify "online", "offline" command + at write: user can specify "online_kernel", + "online_movable", "online", "offline" command which will be performed on al sections in the block. 'phys_device' : read-only: designed to show the name of physical memory device. This is not well implemented now. @@ -255,6 +256,17 @@ For onlining, you have to write "online" to the section's state file as: % echo online > /sys/devices/system/memory/memoryXXX/state +This onlining will not change the ZONE type of the target memory section, +If the memory section is in ZONE_NORMAL, you can change it to ZONE_MOVABLE: + +% echo online_movable > /sys/devices/system/memory/memoryXXX/state +(NOTE: current limit: this memory section must be adjacent to ZONE_MOVABLE) + +And if the memory section is in ZONE_MOVABLE, you can change it to ZONE_NORMAL: + +% echo online_kernel > /sys/devices/system/memory/memoryXXX/state +(NOTE: current limit: this memory section must be adjacent to ZONE_NORMAL) + After this, section memoryXXX's state will be 'online' and the amount of available memory will be increased. diff --git a/drivers/base/memory.c b/drivers/base/memory.c index 7eb1211ab688..987604d56c83 100644 --- a/drivers/base/memory.c +++ b/drivers/base/memory.c @@ -254,7 +254,7 @@ static bool pages_correctly_reserved(unsigned long start_pfn, * OK to have direct references to sparsemem variables in here. */ static int -memory_block_action(unsigned long phys_index, unsigned long action) +memory_block_action(unsigned long phys_index, unsigned long action, int online_type) { unsigned long start_pfn; unsigned long nr_pages = PAGES_PER_SECTION * sections_per_block; @@ -269,7 +269,7 @@ memory_block_action(unsigned long phys_index, unsigned long action) if (!pages_correctly_reserved(start_pfn, nr_pages)) return -EBUSY; - ret = online_pages(start_pfn, nr_pages); + ret = online_pages(start_pfn, nr_pages, online_type); break; case MEM_OFFLINE: ret = offline_pages(start_pfn, nr_pages); @@ -284,7 +284,8 @@ memory_block_action(unsigned long phys_index, unsigned long action) } static int __memory_block_change_state(struct memory_block *mem, - unsigned long to_state, unsigned long from_state_req) + unsigned long to_state, unsigned long from_state_req, + int online_type) { int ret = 0; @@ -296,7 +297,7 @@ static int __memory_block_change_state(struct memory_block *mem, if (to_state == MEM_OFFLINE) mem->state = MEM_GOING_OFFLINE; - ret = memory_block_action(mem->start_section_nr, to_state); + ret = memory_block_action(mem->start_section_nr, to_state, online_type); if (ret) { mem->state = from_state_req; @@ -319,12 +320,14 @@ out: } static int memory_block_change_state(struct memory_block *mem, - unsigned long to_state, unsigned long from_state_req) + unsigned long to_state, unsigned long from_state_req, + int online_type) { int ret; mutex_lock(&mem->state_mutex); - ret = __memory_block_change_state(mem, to_state, from_state_req); + ret = __memory_block_change_state(mem, to_state, from_state_req, + online_type); mutex_unlock(&mem->state_mutex); return ret; @@ -338,10 +341,18 @@ store_mem_state(struct device *dev, mem = container_of(dev, struct memory_block, dev); - if (!strncmp(buf, "online", min((int)count, 6))) - ret = memory_block_change_state(mem, MEM_ONLINE, MEM_OFFLINE); - else if(!strncmp(buf, "offline", min((int)count, 7))) - ret = memory_block_change_state(mem, MEM_OFFLINE, MEM_ONLINE); + if (!strncmp(buf, "online_kernel", min_t(int, count, 13))) + ret = memory_block_change_state(mem, MEM_ONLINE, + MEM_OFFLINE, ONLINE_KERNEL); + else if (!strncmp(buf, "online_movable", min_t(int, count, 14))) + ret = memory_block_change_state(mem, MEM_ONLINE, + MEM_OFFLINE, ONLINE_MOVABLE); + else if (!strncmp(buf, "online", min_t(int, count, 6))) + ret = memory_block_change_state(mem, MEM_ONLINE, + MEM_OFFLINE, ONLINE_KEEP); + else if(!strncmp(buf, "offline", min_t(int, count, 7))) + ret = memory_block_change_state(mem, MEM_OFFLINE, + MEM_ONLINE, -1); if (ret) return ret; @@ -676,7 +687,7 @@ int offline_memory_block(struct memory_block *mem) mutex_lock(&mem->state_mutex); if (mem->state != MEM_OFFLINE) - ret = __memory_block_change_state(mem, MEM_OFFLINE, MEM_ONLINE); + ret = __memory_block_change_state(mem, MEM_OFFLINE, MEM_ONLINE, -1); mutex_unlock(&mem->state_mutex); return ret; diff --git a/include/linux/memory_hotplug.h b/include/linux/memory_hotplug.h index 95573ec4ee6c..4a45c4e50025 100644 --- a/include/linux/memory_hotplug.h +++ b/include/linux/memory_hotplug.h @@ -26,6 +26,13 @@ enum { MEMORY_HOTPLUG_MAX_BOOTMEM_TYPE = NODE_INFO, }; +/* Types for control the zone type of onlined memory */ +enum { + ONLINE_KEEP, + ONLINE_KERNEL, + ONLINE_MOVABLE, +}; + /* * pgdat resizing functions */ @@ -46,6 +53,10 @@ void pgdat_resize_init(struct pglist_data *pgdat) } /* * Zone resizing functions + * + * Note: any attempt to resize a zone should has pgdat_resize_lock() + * zone_span_writelock() both held. This ensure the size of a zone + * can't be changed while pgdat_resize_lock() held. */ static inline unsigned zone_span_seqbegin(struct zone *zone) { @@ -71,7 +82,7 @@ extern int zone_grow_free_lists(struct zone *zone, unsigned long new_nr_pages); extern int zone_grow_waitqueues(struct zone *zone, unsigned long nr_pages); extern int add_one_highpage(struct page *page, int pfn, int bad_ppro); /* VM interface that may be used by firmware interface */ -extern int online_pages(unsigned long, unsigned long); +extern int online_pages(unsigned long, unsigned long, int); extern void __offline_isolated_pages(unsigned long, unsigned long); typedef void (*online_page_callback_t)(struct page *page); diff --git a/mm/memory_hotplug.c b/mm/memory_hotplug.c index 571130ee66d7..5c1f4959e6b4 100644 --- a/mm/memory_hotplug.c +++ b/mm/memory_hotplug.c @@ -214,6 +214,88 @@ static void grow_zone_span(struct zone *zone, unsigned long start_pfn, zone_span_writeunlock(zone); } +static void resize_zone(struct zone *zone, unsigned long start_pfn, + unsigned long end_pfn) +{ + zone_span_writelock(zone); + + zone->zone_start_pfn = start_pfn; + zone->spanned_pages = end_pfn - start_pfn; + + zone_span_writeunlock(zone); +} + +static void fix_zone_id(struct zone *zone, unsigned long start_pfn, + unsigned long end_pfn) +{ + enum zone_type zid = zone_idx(zone); + int nid = zone->zone_pgdat->node_id; + unsigned long pfn; + + for (pfn = start_pfn; pfn < end_pfn; pfn++) + set_page_links(pfn_to_page(pfn), zid, nid, pfn); +} + +static int move_pfn_range_left(struct zone *z1, struct zone *z2, + unsigned long start_pfn, unsigned long end_pfn) +{ + unsigned long flags; + + pgdat_resize_lock(z1->zone_pgdat, &flags); + + /* can't move pfns which are higher than @z2 */ + if (end_pfn > z2->zone_start_pfn + z2->spanned_pages) + goto out_fail; + /* the move out part mast at the left most of @z2 */ + if (start_pfn > z2->zone_start_pfn) + goto out_fail; + /* must included/overlap */ + if (end_pfn <= z2->zone_start_pfn) + goto out_fail; + + resize_zone(z1, z1->zone_start_pfn, end_pfn); + resize_zone(z2, end_pfn, z2->zone_start_pfn + z2->spanned_pages); + + pgdat_resize_unlock(z1->zone_pgdat, &flags); + + fix_zone_id(z1, start_pfn, end_pfn); + + return 0; +out_fail: + pgdat_resize_unlock(z1->zone_pgdat, &flags); + return -1; +} + +static int move_pfn_range_right(struct zone *z1, struct zone *z2, + unsigned long start_pfn, unsigned long end_pfn) +{ + unsigned long flags; + + pgdat_resize_lock(z1->zone_pgdat, &flags); + + /* can't move pfns which are lower than @z1 */ + if (z1->zone_start_pfn > start_pfn) + goto out_fail; + /* the move out part mast at the right most of @z1 */ + if (z1->zone_start_pfn + z1->spanned_pages > end_pfn) + goto out_fail; + /* must included/overlap */ + if (start_pfn >= z1->zone_start_pfn + z1->spanned_pages) + goto out_fail; + + resize_zone(z1, z1->zone_start_pfn, start_pfn); + resize_zone(z2, start_pfn, z2->zone_start_pfn + z2->spanned_pages); + + pgdat_resize_unlock(z1->zone_pgdat, &flags); + + fix_zone_id(z2, start_pfn, end_pfn); + + return 0; +out_fail: + pgdat_resize_unlock(z1->zone_pgdat, &flags); + return -1; +} + static void grow_pgdat_span(struct pglist_data *pgdat, unsigned long start_pfn, unsigned long end_pfn) { @@ -508,7 +590,7 @@ static void node_states_set_node(int node, struct memory_notify *arg) } -int __ref online_pages(unsigned long pfn, unsigned long nr_pages) +int __ref online_pages(unsigned long pfn, unsigned long nr_pages, int online_type) { unsigned long onlined_pages = 0; struct zone *zone; @@ -525,6 +607,22 @@ int __ref online_pages(unsigned long pfn, unsigned long nr_pages) */ zone = page_zone(pfn_to_page(pfn)); + if (online_type == ONLINE_KERNEL && zone_idx(zone) == ZONE_MOVABLE) { + if (move_pfn_range_left(zone - 1, zone, pfn, pfn + nr_pages)) { + unlock_memory_hotplug(); + return -1; + } + } + if (online_type == ONLINE_MOVABLE && zone_idx(zone) == ZONE_MOVABLE - 1) { + if (move_pfn_range_right(zone, zone + 1, pfn, pfn + nr_pages)) { + unlock_memory_hotplug(); + return -1; + } + } + + /* Previous code may changed the zone of the pfn range */ + zone = page_zone(pfn_to_page(pfn)); + arg.start_pfn = pfn; arg.nr_pages = nr_pages; node_states_check_changes_online(nr_pages, zone, &arg); -- cgit v1.2.3-59-g8ed1b From e180377f1ae48b3cbc559c9875d9b038f7f000c6 Mon Sep 17 00:00:00 2001 From: "Kirill A. Shutemov" Date: Wed, 12 Dec 2012 13:50:59 -0800 Subject: thp: change split_huge_page_pmd() interface Pass vma instead of mm and add address parameter. In most cases we already have vma on the stack. We provides split_huge_page_pmd_mm() for few cases when we have mm, but not vma. This change is preparation to huge zero pmd splitting implementation. Signed-off-by: Kirill A. Shutemov Cc: Andrea Arcangeli Cc: Andi Kleen Cc: "H. Peter Anvin" Cc: Mel Gorman Cc: David Rientjes Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- Documentation/vm/transhuge.txt | 4 ++-- arch/x86/kernel/vm86_32.c | 2 +- fs/proc/task_mmu.c | 2 +- include/linux/huge_mm.h | 14 ++++++++++---- mm/huge_memory.c | 19 +++++++++++++++++-- mm/memory.c | 4 ++-- mm/mempolicy.c | 2 +- mm/mprotect.c | 2 +- mm/mremap.c | 2 +- mm/pagewalk.c | 2 +- 10 files changed, 37 insertions(+), 16 deletions(-) (limited to 'Documentation') diff --git a/Documentation/vm/transhuge.txt b/Documentation/vm/transhuge.txt index f734bb2a78dc..8f5b41db314c 100644 --- a/Documentation/vm/transhuge.txt +++ b/Documentation/vm/transhuge.txt @@ -276,7 +276,7 @@ unaffected. libhugetlbfs will also work fine as usual. == Graceful fallback == Code walking pagetables but unware about huge pmds can simply call -split_huge_page_pmd(mm, pmd) where the pmd is the one returned by +split_huge_page_pmd(vma, addr, pmd) where the pmd is the one returned by pmd_offset. It's trivial to make the code transparent hugepage aware by just grepping for "pmd_offset" and adding split_huge_page_pmd where missing after pmd_offset returns the pmd. Thanks to the graceful @@ -299,7 +299,7 @@ diff --git a/mm/mremap.c b/mm/mremap.c return NULL; pmd = pmd_offset(pud, addr); -+ split_huge_page_pmd(mm, pmd); ++ split_huge_page_pmd(vma, addr, pmd); if (pmd_none_or_clear_bad(pmd)) return NULL; diff --git a/arch/x86/kernel/vm86_32.c b/arch/x86/kernel/vm86_32.c index 5c9687b1bde6..1dfe69cc78a8 100644 --- a/arch/x86/kernel/vm86_32.c +++ b/arch/x86/kernel/vm86_32.c @@ -182,7 +182,7 @@ static void mark_screen_rdonly(struct mm_struct *mm) if (pud_none_or_clear_bad(pud)) goto out; pmd = pmd_offset(pud, 0xA0000); - split_huge_page_pmd(mm, pmd); + split_huge_page_pmd_mm(mm, 0xA0000, pmd); if (pmd_none_or_clear_bad(pmd)) goto out; pte = pte_offset_map_lock(mm, pmd, 0xA0000, &ptl); diff --git a/fs/proc/task_mmu.c b/fs/proc/task_mmu.c index 90c63f9392a5..291a0d15a0be 100644 --- a/fs/proc/task_mmu.c +++ b/fs/proc/task_mmu.c @@ -643,7 +643,7 @@ static int clear_refs_pte_range(pmd_t *pmd, unsigned long addr, spinlock_t *ptl; struct page *page; - split_huge_page_pmd(walk->mm, pmd); + split_huge_page_pmd(vma, addr, pmd); if (pmd_trans_unstable(pmd)) return 0; diff --git a/include/linux/huge_mm.h b/include/linux/huge_mm.h index 1af477552459..3132ea788581 100644 --- a/include/linux/huge_mm.h +++ b/include/linux/huge_mm.h @@ -95,12 +95,14 @@ extern int handle_pte_fault(struct mm_struct *mm, struct vm_area_struct *vma, unsigned long address, pte_t *pte, pmd_t *pmd, unsigned int flags); extern int split_huge_page(struct page *page); -extern void __split_huge_page_pmd(struct mm_struct *mm, pmd_t *pmd); -#define split_huge_page_pmd(__mm, __pmd) \ +extern void __split_huge_page_pmd(struct vm_area_struct *vma, + unsigned long address, pmd_t *pmd); +#define split_huge_page_pmd(__vma, __address, __pmd) \ do { \ pmd_t *____pmd = (__pmd); \ if (unlikely(pmd_trans_huge(*____pmd))) \ - __split_huge_page_pmd(__mm, ____pmd); \ + __split_huge_page_pmd(__vma, __address, \ + ____pmd); \ } while (0) #define wait_split_huge_page(__anon_vma, __pmd) \ do { \ @@ -110,6 +112,8 @@ extern void __split_huge_page_pmd(struct mm_struct *mm, pmd_t *pmd); BUG_ON(pmd_trans_splitting(*____pmd) || \ pmd_trans_huge(*____pmd)); \ } while (0) +extern void split_huge_page_pmd_mm(struct mm_struct *mm, unsigned long address, + pmd_t *pmd); #if HPAGE_PMD_ORDER > MAX_ORDER #error "hugepages can't be allocated by the buddy allocator" #endif @@ -177,10 +181,12 @@ static inline int split_huge_page(struct page *page) { return 0; } -#define split_huge_page_pmd(__mm, __pmd) \ +#define split_huge_page_pmd(__vma, __address, __pmd) \ do { } while (0) #define wait_split_huge_page(__anon_vma, __pmd) \ do { } while (0) +#define split_huge_page_pmd_mm(__mm, __address, __pmd) \ + do { } while (0) #define compound_trans_head(page) compound_head(page) static inline int hugepage_madvise(struct vm_area_struct *vma, unsigned long *vm_flags, int advice) diff --git a/mm/huge_memory.c b/mm/huge_memory.c index 7742fb36eb4d..de6aa5f3fdd2 100644 --- a/mm/huge_memory.c +++ b/mm/huge_memory.c @@ -2475,9 +2475,14 @@ static int khugepaged(void *none) return 0; } -void __split_huge_page_pmd(struct mm_struct *mm, pmd_t *pmd) +void __split_huge_page_pmd(struct vm_area_struct *vma, unsigned long address, + pmd_t *pmd) { struct page *page; + unsigned long haddr = address & HPAGE_PMD_MASK; + struct mm_struct *mm = vma->vm_mm; + + BUG_ON(vma->vm_start > haddr || vma->vm_end < haddr + HPAGE_PMD_SIZE); spin_lock(&mm->page_table_lock); if (unlikely(!pmd_trans_huge(*pmd))) { @@ -2495,6 +2500,16 @@ void __split_huge_page_pmd(struct mm_struct *mm, pmd_t *pmd) BUG_ON(pmd_trans_huge(*pmd)); } +void split_huge_page_pmd_mm(struct mm_struct *mm, unsigned long address, + pmd_t *pmd) +{ + struct vm_area_struct *vma; + + vma = find_vma(mm, address); + BUG_ON(vma == NULL); + split_huge_page_pmd(vma, address, pmd); +} + static void split_huge_page_address(struct mm_struct *mm, unsigned long address) { @@ -2509,7 +2524,7 @@ static void split_huge_page_address(struct mm_struct *mm, * Caller holds the mmap_sem write mode, so a huge pmd cannot * materialize from under us. */ - split_huge_page_pmd(mm, pmd); + split_huge_page_pmd_mm(mm, address, pmd); } void __vma_adjust_trans_huge(struct vm_area_struct *vma, diff --git a/mm/memory.c b/mm/memory.c index 259b34fe1347..f9a4b0cb8623 100644 --- a/mm/memory.c +++ b/mm/memory.c @@ -1243,7 +1243,7 @@ static inline unsigned long zap_pmd_range(struct mmu_gather *tlb, BUG(); } #endif - split_huge_page_pmd(vma->vm_mm, pmd); + split_huge_page_pmd(vma, addr, pmd); } else if (zap_huge_pmd(tlb, vma, pmd, addr)) goto next; /* fall through */ @@ -1512,7 +1512,7 @@ struct page *follow_page(struct vm_area_struct *vma, unsigned long address, } if (pmd_trans_huge(*pmd)) { if (flags & FOLL_SPLIT) { - split_huge_page_pmd(mm, pmd); + split_huge_page_pmd(vma, address, pmd); goto split_fallthrough; } spin_lock(&mm->page_table_lock); diff --git a/mm/mempolicy.c b/mm/mempolicy.c index 05b28361a39b..0719e8dd4945 100644 --- a/mm/mempolicy.c +++ b/mm/mempolicy.c @@ -511,7 +511,7 @@ static inline int check_pmd_range(struct vm_area_struct *vma, pud_t *pud, pmd = pmd_offset(pud, addr); do { next = pmd_addr_end(addr, end); - split_huge_page_pmd(vma->vm_mm, pmd); + split_huge_page_pmd(vma, addr, pmd); if (pmd_none_or_trans_huge_or_clear_bad(pmd)) continue; if (check_pte_range(vma, pmd, addr, next, nodes, diff --git a/mm/mprotect.c b/mm/mprotect.c index a40992610ab6..e8c3938db6fa 100644 --- a/mm/mprotect.c +++ b/mm/mprotect.c @@ -90,7 +90,7 @@ static inline void change_pmd_range(struct vm_area_struct *vma, pud_t *pud, next = pmd_addr_end(addr, end); if (pmd_trans_huge(*pmd)) { if (next - addr != HPAGE_PMD_SIZE) - split_huge_page_pmd(vma->vm_mm, pmd); + split_huge_page_pmd(vma, addr, pmd); else if (change_huge_pmd(vma, pmd, addr, newprot)) continue; /* fall through */ diff --git a/mm/mremap.c b/mm/mremap.c index 1b61c2d3307a..eabb24da6c9e 100644 --- a/mm/mremap.c +++ b/mm/mremap.c @@ -182,7 +182,7 @@ unsigned long move_page_tables(struct vm_area_struct *vma, need_flush = true; continue; } else if (!err) { - split_huge_page_pmd(vma->vm_mm, old_pmd); + split_huge_page_pmd(vma, old_addr, old_pmd); } VM_BUG_ON(pmd_trans_huge(*old_pmd)); } diff --git a/mm/pagewalk.c b/mm/pagewalk.c index 6c118d012bb5..35aa294656cd 100644 --- a/mm/pagewalk.c +++ b/mm/pagewalk.c @@ -58,7 +58,7 @@ again: if (!walk->pte_entry) continue; - split_huge_page_pmd(walk->mm, pmd); + split_huge_page_pmd_mm(walk->mm, addr, pmd); if (pmd_none_or_trans_huge_or_clear_bad(pmd)) goto again; err = walk_pte_range(pmd, addr, next, walk); -- cgit v1.2.3-59-g8ed1b From d8a8e1f0da3d29d7268b3300c96a059d63901b76 Mon Sep 17 00:00:00 2001 From: "Kirill A. Shutemov" Date: Wed, 12 Dec 2012 13:51:09 -0800 Subject: thp, vmstat: implement HZP_ALLOC and HZP_ALLOC_FAILED events hzp_alloc is incremented every time a huge zero page is successfully allocated. It includes allocations which where dropped due race with other allocation. Note, it doesn't count every map of the huge zero page, only its allocation. hzp_alloc_failed is incremented if kernel fails to allocate huge zero page and falls back to using small pages. Signed-off-by: Kirill A. Shutemov Cc: Andrea Arcangeli Cc: Andi Kleen Cc: "H. Peter Anvin" Cc: Mel Gorman Cc: David Rientjes Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- Documentation/vm/transhuge.txt | 8 ++++++++ include/linux/vm_event_item.h | 2 ++ mm/huge_memory.c | 5 ++++- mm/vmstat.c | 2 ++ 4 files changed, 16 insertions(+), 1 deletion(-) (limited to 'Documentation') diff --git a/Documentation/vm/transhuge.txt b/Documentation/vm/transhuge.txt index 8f5b41db314c..60aeedd54615 100644 --- a/Documentation/vm/transhuge.txt +++ b/Documentation/vm/transhuge.txt @@ -197,6 +197,14 @@ thp_split is incremented every time a huge page is split into base pages. This can happen for a variety of reasons but a common reason is that a huge page is old and is being reclaimed. +thp_zero_page_alloc is incremented every time a huge zero page is + successfully allocated. It includes allocations which where + dropped due race with other allocation. Note, it doesn't count + every map of the huge zero page, only its allocation. + +thp_zero_page_alloc_failed is incremented if kernel fails to allocate + huge zero page and falls back to using small pages. + As the system ages, allocating huge pages may be expensive as the system uses memory compaction to copy data around memory to free a huge page for use. There are some counters in /proc/vmstat to help diff --git a/include/linux/vm_event_item.h b/include/linux/vm_event_item.h index 3d3114594370..fe786f07d2bd 100644 --- a/include/linux/vm_event_item.h +++ b/include/linux/vm_event_item.h @@ -58,6 +58,8 @@ enum vm_event_item { PGPGIN, PGPGOUT, PSWPIN, PSWPOUT, THP_COLLAPSE_ALLOC, THP_COLLAPSE_ALLOC_FAILED, THP_SPLIT, + THP_ZERO_PAGE_ALLOC, + THP_ZERO_PAGE_ALLOC_FAILED, #endif NR_VM_EVENT_ITEMS }; diff --git a/mm/huge_memory.c b/mm/huge_memory.c index d89220cb1d9f..9a5d45dfad44 100644 --- a/mm/huge_memory.c +++ b/mm/huge_memory.c @@ -184,8 +184,11 @@ retry: zero_page = alloc_pages((GFP_TRANSHUGE | __GFP_ZERO) & ~__GFP_MOVABLE, HPAGE_PMD_ORDER); - if (!zero_page) + if (!zero_page) { + count_vm_event(THP_ZERO_PAGE_ALLOC_FAILED); return 0; + } + count_vm_event(THP_ZERO_PAGE_ALLOC); preempt_disable(); if (cmpxchg(&huge_zero_pfn, 0, page_to_pfn(zero_page))) { preempt_enable(); diff --git a/mm/vmstat.c b/mm/vmstat.c index c7370579111b..5da4b19023c3 100644 --- a/mm/vmstat.c +++ b/mm/vmstat.c @@ -801,6 +801,8 @@ const char * const vmstat_text[] = { "thp_collapse_alloc", "thp_collapse_alloc_failed", "thp_split", + "thp_zero_page_alloc", + "thp_zero_page_alloc_failed", #endif #endif /* CONFIG_VM_EVENTS_COUNTERS */ -- cgit v1.2.3-59-g8ed1b From 79da5407eeadc740fbf4b45d6df7d7f8e6adaf2c Mon Sep 17 00:00:00 2001 From: "Kirill A. Shutemov" Date: Wed, 12 Dec 2012 13:51:12 -0800 Subject: thp: introduce sysfs knob to disable huge zero page By default kernel tries to use huge zero page on read page fault. It's possible to disable huge zero page by writing 0 or enable it back by writing 1: echo 0 >/sys/kernel/mm/transparent_hugepage/khugepaged/use_zero_page echo 1 >/sys/kernel/mm/transparent_hugepage/khugepaged/use_zero_page Signed-off-by: Kirill A. Shutemov Cc: Andrea Arcangeli Cc: Andi Kleen Cc: "H. Peter Anvin" Cc: Mel Gorman Cc: David Rientjes Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- Documentation/vm/transhuge.txt | 7 +++++++ include/linux/huge_mm.h | 4 ++++ mm/huge_memory.c | 21 +++++++++++++++++++-- 3 files changed, 30 insertions(+), 2 deletions(-) (limited to 'Documentation') diff --git a/Documentation/vm/transhuge.txt b/Documentation/vm/transhuge.txt index 60aeedd54615..8785fb87d9c7 100644 --- a/Documentation/vm/transhuge.txt +++ b/Documentation/vm/transhuge.txt @@ -116,6 +116,13 @@ echo always >/sys/kernel/mm/transparent_hugepage/defrag echo madvise >/sys/kernel/mm/transparent_hugepage/defrag echo never >/sys/kernel/mm/transparent_hugepage/defrag +By default kernel tries to use huge zero page on read page fault. +It's possible to disable huge zero page by writing 0 or enable it +back by writing 1: + +echo 0 >/sys/kernel/mm/transparent_hugepage/khugepaged/use_zero_page +echo 1 >/sys/kernel/mm/transparent_hugepage/khugepaged/use_zero_page + khugepaged will be automatically started when transparent_hugepage/enabled is set to "always" or "madvise, and it'll be automatically shutdown if it's set to "never". diff --git a/include/linux/huge_mm.h b/include/linux/huge_mm.h index 3132ea788581..092dc5305a32 100644 --- a/include/linux/huge_mm.h +++ b/include/linux/huge_mm.h @@ -39,6 +39,7 @@ enum transparent_hugepage_flag { TRANSPARENT_HUGEPAGE_DEFRAG_FLAG, TRANSPARENT_HUGEPAGE_DEFRAG_REQ_MADV_FLAG, TRANSPARENT_HUGEPAGE_DEFRAG_KHUGEPAGED_FLAG, + TRANSPARENT_HUGEPAGE_USE_ZERO_PAGE_FLAG, #ifdef CONFIG_DEBUG_VM TRANSPARENT_HUGEPAGE_DEBUG_COW_FLAG, #endif @@ -78,6 +79,9 @@ extern bool is_vma_temporary_stack(struct vm_area_struct *vma); (transparent_hugepage_flags & \ (1<vm_flags & VM_HUGEPAGE)) +#define transparent_hugepage_use_zero_page() \ + (transparent_hugepage_flags & \ + (1< Date: Wed, 12 Dec 2012 13:51:24 -0800 Subject: cpuset: use N_MEMORY instead N_HIGH_MEMORY N_HIGH_MEMORY stands for the nodes that has normal or high memory. N_MEMORY stands for the nodes that has any memory. The code here need to handle with the nodes which have memory, we should use N_MEMORY instead. Signed-off-by: Lai Jiangshan Acked-by: Hillf Danton Signed-off-by: Wen Congyang Cc: Christoph Lameter Cc: Lin Feng Cc: David Rientjes Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- Documentation/cgroups/cpusets.txt | 2 +- include/linux/cpuset.h | 2 +- kernel/cpuset.c | 32 ++++++++++++++++---------------- 3 files changed, 18 insertions(+), 18 deletions(-) (limited to 'Documentation') diff --git a/Documentation/cgroups/cpusets.txt b/Documentation/cgroups/cpusets.txt index cefd3d8bbd11..12e01d432bfe 100644 --- a/Documentation/cgroups/cpusets.txt +++ b/Documentation/cgroups/cpusets.txt @@ -218,7 +218,7 @@ and name space for cpusets, with a minimum of additional kernel code. The cpus and mems files in the root (top_cpuset) cpuset are read-only. The cpus file automatically tracks the value of cpu_online_mask using a CPU hotplug notifier, and the mems file -automatically tracks the value of node_states[N_HIGH_MEMORY]--i.e., +automatically tracks the value of node_states[N_MEMORY]--i.e., nodes with memory--using the cpuset_track_online_nodes() hook. diff --git a/include/linux/cpuset.h b/include/linux/cpuset.h index 838320fc3d1d..8c8a60d29407 100644 --- a/include/linux/cpuset.h +++ b/include/linux/cpuset.h @@ -144,7 +144,7 @@ static inline nodemask_t cpuset_mems_allowed(struct task_struct *p) return node_possible_map; } -#define cpuset_current_mems_allowed (node_states[N_HIGH_MEMORY]) +#define cpuset_current_mems_allowed (node_states[N_MEMORY]) static inline void cpuset_init_current_mems_allowed(void) {} static inline int cpuset_nodemask_valid_mems_allowed(nodemask_t *nodemask) diff --git a/kernel/cpuset.c b/kernel/cpuset.c index b017887d632f..7bb63eea6eb8 100644 --- a/kernel/cpuset.c +++ b/kernel/cpuset.c @@ -302,10 +302,10 @@ static void guarantee_online_cpus(const struct cpuset *cs, * are online, with memory. If none are online with memory, walk * up the cpuset hierarchy until we find one that does have some * online mems. If we get all the way to the top and still haven't - * found any online mems, return node_states[N_HIGH_MEMORY]. + * found any online mems, return node_states[N_MEMORY]. * * One way or another, we guarantee to return some non-empty subset - * of node_states[N_HIGH_MEMORY]. + * of node_states[N_MEMORY]. * * Call with callback_mutex held. */ @@ -313,14 +313,14 @@ static void guarantee_online_cpus(const struct cpuset *cs, static void guarantee_online_mems(const struct cpuset *cs, nodemask_t *pmask) { while (cs && !nodes_intersects(cs->mems_allowed, - node_states[N_HIGH_MEMORY])) + node_states[N_MEMORY])) cs = cs->parent; if (cs) nodes_and(*pmask, cs->mems_allowed, - node_states[N_HIGH_MEMORY]); + node_states[N_MEMORY]); else - *pmask = node_states[N_HIGH_MEMORY]; - BUG_ON(!nodes_intersects(*pmask, node_states[N_HIGH_MEMORY])); + *pmask = node_states[N_MEMORY]; + BUG_ON(!nodes_intersects(*pmask, node_states[N_MEMORY])); } /* @@ -1100,7 +1100,7 @@ static int update_nodemask(struct cpuset *cs, struct cpuset *trialcs, return -ENOMEM; /* - * top_cpuset.mems_allowed tracks node_stats[N_HIGH_MEMORY]; + * top_cpuset.mems_allowed tracks node_stats[N_MEMORY]; * it's read-only */ if (cs == &top_cpuset) { @@ -1122,7 +1122,7 @@ static int update_nodemask(struct cpuset *cs, struct cpuset *trialcs, goto done; if (!nodes_subset(trialcs->mems_allowed, - node_states[N_HIGH_MEMORY])) { + node_states[N_MEMORY])) { retval = -EINVAL; goto done; } @@ -2026,7 +2026,7 @@ static struct cpuset *cpuset_next(struct list_head *queue) * before dropping down to the next. It always processes a node before * any of its children. * - * In the case of memory hot-unplug, it will remove nodes from N_HIGH_MEMORY + * In the case of memory hot-unplug, it will remove nodes from N_MEMORY * if all present pages from a node are offlined. */ static void @@ -2065,7 +2065,7 @@ scan_cpusets_upon_hotplug(struct cpuset *root, enum hotplug_event event) /* Continue past cpusets with all mems online */ if (nodes_subset(cp->mems_allowed, - node_states[N_HIGH_MEMORY])) + node_states[N_MEMORY])) continue; oldmems = cp->mems_allowed; @@ -2073,7 +2073,7 @@ scan_cpusets_upon_hotplug(struct cpuset *root, enum hotplug_event event) /* Remove offline mems from this cpuset. */ mutex_lock(&callback_mutex); nodes_and(cp->mems_allowed, cp->mems_allowed, - node_states[N_HIGH_MEMORY]); + node_states[N_MEMORY]); mutex_unlock(&callback_mutex); /* Move tasks from the empty cpuset to a parent */ @@ -2126,8 +2126,8 @@ void cpuset_update_active_cpus(bool cpu_online) #ifdef CONFIG_MEMORY_HOTPLUG /* - * Keep top_cpuset.mems_allowed tracking node_states[N_HIGH_MEMORY]. - * Call this routine anytime after node_states[N_HIGH_MEMORY] changes. + * Keep top_cpuset.mems_allowed tracking node_states[N_MEMORY]. + * Call this routine anytime after node_states[N_MEMORY] changes. * See cpuset_update_active_cpus() for CPU hotplug handling. */ static int cpuset_track_online_nodes(struct notifier_block *self, @@ -2140,7 +2140,7 @@ static int cpuset_track_online_nodes(struct notifier_block *self, case MEM_ONLINE: oldmems = top_cpuset.mems_allowed; mutex_lock(&callback_mutex); - top_cpuset.mems_allowed = node_states[N_HIGH_MEMORY]; + top_cpuset.mems_allowed = node_states[N_MEMORY]; mutex_unlock(&callback_mutex); update_tasks_nodemask(&top_cpuset, &oldmems, NULL); break; @@ -2169,7 +2169,7 @@ static int cpuset_track_online_nodes(struct notifier_block *self, void __init cpuset_init_smp(void) { cpumask_copy(top_cpuset.cpus_allowed, cpu_active_mask); - top_cpuset.mems_allowed = node_states[N_HIGH_MEMORY]; + top_cpuset.mems_allowed = node_states[N_MEMORY]; hotplug_memory_notifier(cpuset_track_online_nodes, 10); @@ -2237,7 +2237,7 @@ void cpuset_init_current_mems_allowed(void) * * Description: Returns the nodemask_t mems_allowed of the cpuset * attached to the specified @tsk. Guaranteed to return some non-empty - * subset of node_states[N_HIGH_MEMORY], even if this means going outside the + * subset of node_states[N_MEMORY], even if this means going outside the * tasks cpuset. **/ -- cgit v1.2.3-59-g8ed1b From 6715ddf94576ff39f5d1cda8d4c568d3b79e82ec Mon Sep 17 00:00:00 2001 From: Lai Jiangshan Date: Wed, 12 Dec 2012 13:51:49 -0800 Subject: hotplug: update nodemasks management Update nodemasks management for N_MEMORY. [lliubbo@gmail.com: fix build] Signed-off-by: Lai Jiangshan Signed-off-by: Wen Congyang Cc: Christoph Lameter Cc: Hillf Danton Cc: Lin Feng Cc: David Rientjes Signed-off-by: Bob Liu Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- Documentation/memory-hotplug.txt | 5 ++- include/linux/memory.h | 1 + mm/memory_hotplug.c | 87 +++++++++++++++++++++++++++++++++------- 3 files changed, 77 insertions(+), 16 deletions(-) (limited to 'Documentation') diff --git a/Documentation/memory-hotplug.txt b/Documentation/memory-hotplug.txt index c6f993d491b5..8e5eacbdcfa3 100644 --- a/Documentation/memory-hotplug.txt +++ b/Documentation/memory-hotplug.txt @@ -390,6 +390,7 @@ struct memory_notify { unsigned long start_pfn; unsigned long nr_pages; int status_change_nid_normal; + int status_change_nid_high; int status_change_nid; } @@ -397,7 +398,9 @@ start_pfn is start_pfn of online/offline memory. nr_pages is # of pages of online/offline memory. status_change_nid_normal is set node id when N_NORMAL_MEMORY of nodemask is (will be) set/clear, if this is -1, then nodemask status is not changed. -status_change_nid is set node id when N_HIGH_MEMORY of nodemask is (will be) +status_change_nid_high is set node id when N_HIGH_MEMORY of nodemask +is (will be) set/clear, if this is -1, then nodemask status is not changed. +status_change_nid is set node id when N_MEMORY of nodemask is (will be) set/clear. It means a new(memoryless) node gets new memory by online and a node loses all memory. If this is -1, then nodemask status is not changed. If status_changed_nid* >= 0, callback should create/discard structures for the diff --git a/include/linux/memory.h b/include/linux/memory.h index a09216d0dcc7..45e93b468878 100644 --- a/include/linux/memory.h +++ b/include/linux/memory.h @@ -54,6 +54,7 @@ struct memory_notify { unsigned long start_pfn; unsigned long nr_pages; int status_change_nid_normal; + int status_change_nid_high; int status_change_nid; }; diff --git a/mm/memory_hotplug.c b/mm/memory_hotplug.c index de9cb14ae753..eca4aac1a83b 100644 --- a/mm/memory_hotplug.c +++ b/mm/memory_hotplug.c @@ -595,13 +595,15 @@ static void node_states_check_changes_online(unsigned long nr_pages, enum zone_type zone_last = ZONE_NORMAL; /* - * If we have HIGHMEM, node_states[N_NORMAL_MEMORY] contains nodes - * which have 0...ZONE_NORMAL, set zone_last to ZONE_NORMAL. + * If we have HIGHMEM or movable node, node_states[N_NORMAL_MEMORY] + * contains nodes which have zones of 0...ZONE_NORMAL, + * set zone_last to ZONE_NORMAL. * - * If we don't have HIGHMEM, node_states[N_NORMAL_MEMORY] contains nodes - * which have 0...ZONE_MOVABLE, set zone_last to ZONE_MOVABLE. + * If we don't have HIGHMEM nor movable node, + * node_states[N_NORMAL_MEMORY] contains nodes which have zones of + * 0...ZONE_MOVABLE, set zone_last to ZONE_MOVABLE. */ - if (N_HIGH_MEMORY == N_NORMAL_MEMORY) + if (N_MEMORY == N_NORMAL_MEMORY) zone_last = ZONE_MOVABLE; /* @@ -615,12 +617,34 @@ static void node_states_check_changes_online(unsigned long nr_pages, else arg->status_change_nid_normal = -1; +#ifdef CONFIG_HIGHMEM + /* + * If we have movable node, node_states[N_HIGH_MEMORY] + * contains nodes which have zones of 0...ZONE_HIGHMEM, + * set zone_last to ZONE_HIGHMEM. + * + * If we don't have movable node, node_states[N_NORMAL_MEMORY] + * contains nodes which have zones of 0...ZONE_MOVABLE, + * set zone_last to ZONE_MOVABLE. + */ + zone_last = ZONE_HIGHMEM; + if (N_MEMORY == N_HIGH_MEMORY) + zone_last = ZONE_MOVABLE; + + if (zone_idx(zone) <= zone_last && !node_state(nid, N_HIGH_MEMORY)) + arg->status_change_nid_high = nid; + else + arg->status_change_nid_high = -1; +#else + arg->status_change_nid_high = arg->status_change_nid_normal; +#endif + /* * if the node don't have memory befor online, we will need to - * set the node to node_states[N_HIGH_MEMORY] after the memory + * set the node to node_states[N_MEMORY] after the memory * is online. */ - if (!node_state(nid, N_HIGH_MEMORY)) + if (!node_state(nid, N_MEMORY)) arg->status_change_nid = nid; else arg->status_change_nid = -1; @@ -631,7 +655,10 @@ static void node_states_set_node(int node, struct memory_notify *arg) if (arg->status_change_nid_normal >= 0) node_set_state(node, N_NORMAL_MEMORY); - node_set_state(node, N_HIGH_MEMORY); + if (arg->status_change_nid_high >= 0) + node_set_state(node, N_HIGH_MEMORY); + + node_set_state(node, N_MEMORY); } @@ -1099,13 +1126,15 @@ static void node_states_check_changes_offline(unsigned long nr_pages, enum zone_type zt, zone_last = ZONE_NORMAL; /* - * If we have HIGHMEM, node_states[N_NORMAL_MEMORY] contains nodes - * which have 0...ZONE_NORMAL, set zone_last to ZONE_NORMAL. + * If we have HIGHMEM or movable node, node_states[N_NORMAL_MEMORY] + * contains nodes which have zones of 0...ZONE_NORMAL, + * set zone_last to ZONE_NORMAL. * - * If we don't have HIGHMEM, node_states[N_NORMAL_MEMORY] contains nodes - * which have 0...ZONE_MOVABLE, set zone_last to ZONE_MOVABLE. + * If we don't have HIGHMEM nor movable node, + * node_states[N_NORMAL_MEMORY] contains nodes which have zones of + * 0...ZONE_MOVABLE, set zone_last to ZONE_MOVABLE. */ - if (N_HIGH_MEMORY == N_NORMAL_MEMORY) + if (N_MEMORY == N_NORMAL_MEMORY) zone_last = ZONE_MOVABLE; /* @@ -1122,6 +1151,30 @@ static void node_states_check_changes_offline(unsigned long nr_pages, else arg->status_change_nid_normal = -1; +#ifdef CONFIG_HIGHMEM + /* + * If we have movable node, node_states[N_HIGH_MEMORY] + * contains nodes which have zones of 0...ZONE_HIGHMEM, + * set zone_last to ZONE_HIGHMEM. + * + * If we don't have movable node, node_states[N_NORMAL_MEMORY] + * contains nodes which have zones of 0...ZONE_MOVABLE, + * set zone_last to ZONE_MOVABLE. + */ + zone_last = ZONE_HIGHMEM; + if (N_MEMORY == N_HIGH_MEMORY) + zone_last = ZONE_MOVABLE; + + for (; zt <= zone_last; zt++) + present_pages += pgdat->node_zones[zt].present_pages; + if (zone_idx(zone) <= zone_last && nr_pages >= present_pages) + arg->status_change_nid_high = zone_to_nid(zone); + else + arg->status_change_nid_high = -1; +#else + arg->status_change_nid_high = arg->status_change_nid_normal; +#endif + /* * node_states[N_HIGH_MEMORY] contains nodes which have 0...ZONE_MOVABLE */ @@ -1146,9 +1199,13 @@ static void node_states_clear_node(int node, struct memory_notify *arg) if (arg->status_change_nid_normal >= 0) node_clear_state(node, N_NORMAL_MEMORY); - if ((N_HIGH_MEMORY != N_NORMAL_MEMORY) && - (arg->status_change_nid >= 0)) + if ((N_MEMORY != N_NORMAL_MEMORY) && + (arg->status_change_nid_high >= 0)) node_clear_state(node, N_HIGH_MEMORY); + + if ((N_MEMORY != N_HIGH_MEMORY) && + (arg->status_change_nid >= 0)) + node_clear_state(node, N_MEMORY); } static int __ref __offline_pages(unsigned long start_pfn, -- cgit v1.2.3-59-g8ed1b From 4bfe24ac1d9d9f06b30b2909f53ea462c72cfe1d Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Wed, 12 Dec 2012 20:24:12 -0800 Subject: x86, doc: Add a formal bootloader ID for kexec-tools Add bootloader id D for kexec-tools (it is kexec-tools, not the kexec system call, that builds the data structures, therefore it is kexec-tools which is the "bootloader" for this purpose.) Signed-off-by: H. Peter Anvin Cc: Eric W. Biederman Cc: Yinghai Lu Link: http://lkml.kernel.org/r/50C95832.5030306@zytor.com --- Documentation/x86/boot.txt | 1 + 1 file changed, 1 insertion(+) (limited to 'Documentation') diff --git a/Documentation/x86/boot.txt b/Documentation/x86/boot.txt index 9efceff51bfb..c25a53e69f1b 100644 --- a/Documentation/x86/boot.txt +++ b/Documentation/x86/boot.txt @@ -381,6 +381,7 @@ Protocol: 2.00+ A Gujin B Qemu C Arcturus Networks uCbootloader + D kexec-tools E Extended (see ext_loader_type) F Special (0xFF = undefined) 10 Reserved -- cgit v1.2.3-59-g8ed1b From cd34ab8b16ffaa9753d85adb4fb6cd98e7e1d22a Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Wed, 12 Dec 2012 20:28:39 -0800 Subject: x86, doc: Document that bootloader ID 4 is used also by iPXE Etherboot -> gPXE -> iPXE... Signed-off-by: H. Peter Anvin Cc: Michael Brown --- Documentation/x86/boot.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Documentation') diff --git a/Documentation/x86/boot.txt b/Documentation/x86/boot.txt index c25a53e69f1b..9a89e01d9bce 100644 --- a/Documentation/x86/boot.txt +++ b/Documentation/x86/boot.txt @@ -373,7 +373,7 @@ Protocol: 2.00+ 1 Loadlin 2 bootsect-loader (0x20, all other values reserved) 3 Syslinux - 4 Etherboot/gPXE + 4 Etherboot/gPXE/iPXE 5 ELILO 7 GRUB 8 U-Boot -- cgit v1.2.3-59-g8ed1b From fdf90729e57812cb12d7938e2dee7c71e875fb08 Mon Sep 17 00:00:00 2001 From: Mimi Zohar Date: Tue, 16 Oct 2012 12:40:08 +1030 Subject: ima: support new kernel module syscall With the addition of the new kernel module syscall, which defines two arguments - a file descriptor to the kernel module and a pointer to a NULL terminated string of module arguments - it is now possible to measure and appraise kernel modules like any other file on the file system. This patch adds support to measure and appraise kernel modules in an extensible and consistent manner. To support filesystems without extended attribute support, additional patches could pass the signature as the first parameter. Signed-off-by: Mimi Zohar Signed-off-by: Rusty Russell --- Documentation/ABI/testing/ima_policy | 3 ++- include/linux/ima.h | 6 ++++++ security/integrity/ima/ima.h | 2 +- security/integrity/ima/ima_api.c | 4 ++-- security/integrity/ima/ima_main.c | 21 +++++++++++++++++++++ security/integrity/ima/ima_policy.c | 3 +++ security/security.c | 7 ++++++- 7 files changed, 41 insertions(+), 5 deletions(-) (limited to 'Documentation') diff --git a/Documentation/ABI/testing/ima_policy b/Documentation/ABI/testing/ima_policy index 986946613542..ec0a38ef3145 100644 --- a/Documentation/ABI/testing/ima_policy +++ b/Documentation/ABI/testing/ima_policy @@ -23,7 +23,7 @@ Description: lsm: [[subj_user=] [subj_role=] [subj_type=] [obj_user=] [obj_role=] [obj_type=]] - base: func:= [BPRM_CHECK][FILE_MMAP][FILE_CHECK] + base: func:= [BPRM_CHECK][FILE_MMAP][FILE_CHECK][MODULE_CHECK] mask:= [MAY_READ] [MAY_WRITE] [MAY_APPEND] [MAY_EXEC] fsmagic:= hex value uid:= decimal value @@ -53,6 +53,7 @@ Description: measure func=BPRM_CHECK measure func=FILE_MMAP mask=MAY_EXEC measure func=FILE_CHECK mask=MAY_READ uid=0 + measure func=MODULE_CHECK uid=0 appraise fowner=0 The default policy measures all executables in bprm_check, diff --git a/include/linux/ima.h b/include/linux/ima.h index 2c7223d7e73b..86c361e947b9 100644 --- a/include/linux/ima.h +++ b/include/linux/ima.h @@ -18,6 +18,7 @@ extern int ima_bprm_check(struct linux_binprm *bprm); extern int ima_file_check(struct file *file, int mask); extern void ima_file_free(struct file *file); extern int ima_file_mmap(struct file *file, unsigned long prot); +extern int ima_module_check(struct file *file); #else static inline int ima_bprm_check(struct linux_binprm *bprm) @@ -40,6 +41,11 @@ static inline int ima_file_mmap(struct file *file, unsigned long prot) return 0; } +static inline int ima_module_check(struct file *file) +{ + return 0; +} + #endif /* CONFIG_IMA_H */ #ifdef CONFIG_IMA_APPRAISE diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h index 6ee8826662cc..3b2adb794f15 100644 --- a/security/integrity/ima/ima.h +++ b/security/integrity/ima/ima.h @@ -127,7 +127,7 @@ struct integrity_iint_cache *integrity_iint_insert(struct inode *inode); struct integrity_iint_cache *integrity_iint_find(struct inode *inode); /* IMA policy related functions */ -enum ima_hooks { FILE_CHECK = 1, FILE_MMAP, BPRM_CHECK, POST_SETATTR }; +enum ima_hooks { FILE_CHECK = 1, FILE_MMAP, BPRM_CHECK, MODULE_CHECK, POST_SETATTR }; int ima_match_policy(struct inode *inode, enum ima_hooks func, int mask, int flags); diff --git a/security/integrity/ima/ima_api.c b/security/integrity/ima/ima_api.c index b356884fb3ef..0cea3db21657 100644 --- a/security/integrity/ima/ima_api.c +++ b/security/integrity/ima/ima_api.c @@ -100,12 +100,12 @@ err_out: * ima_get_action - appraise & measure decision based on policy. * @inode: pointer to inode to measure * @mask: contains the permission mask (MAY_READ, MAY_WRITE, MAY_EXECUTE) - * @function: calling function (FILE_CHECK, BPRM_CHECK, FILE_MMAP) + * @function: calling function (FILE_CHECK, BPRM_CHECK, FILE_MMAP, MODULE_CHECK) * * The policy is defined in terms of keypairs: * subj=, obj=, type=, func=, mask=, fsmagic= * subj,obj, and type: are LSM specific. - * func: FILE_CHECK | BPRM_CHECK | FILE_MMAP + * func: FILE_CHECK | BPRM_CHECK | FILE_MMAP | MODULE_CHECK * mask: contains the permission mask * fsmagic: hex value * diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c index 73c9a268253e..45de18e9a6f2 100644 --- a/security/integrity/ima/ima_main.c +++ b/security/integrity/ima/ima_main.c @@ -280,6 +280,27 @@ int ima_file_check(struct file *file, int mask) } EXPORT_SYMBOL_GPL(ima_file_check); +/** + * ima_module_check - based on policy, collect/store/appraise measurement. + * @file: pointer to the file to be measured/appraised + * + * Measure/appraise kernel modules based on policy. + * + * Always return 0 and audit dentry_open failures. + * Return code is based upon measurement appraisal. + */ +int ima_module_check(struct file *file) +{ + int rc; + + if (!file) + rc = INTEGRITY_UNKNOWN; + else + rc = process_measurement(file, file->f_dentry->d_name.name, + MAY_EXEC, MODULE_CHECK); + return (ima_appraise & IMA_APPRAISE_ENFORCE) ? rc : 0; +} + static int __init init_ima(void) { int error; diff --git a/security/integrity/ima/ima_policy.c b/security/integrity/ima/ima_policy.c index c7dacd2eab7a..af7d182d5a46 100644 --- a/security/integrity/ima/ima_policy.c +++ b/security/integrity/ima/ima_policy.c @@ -80,6 +80,7 @@ static struct ima_rule_entry default_rules[] = { .flags = IMA_FUNC | IMA_MASK}, {.action = MEASURE,.func = FILE_CHECK,.mask = MAY_READ,.uid = GLOBAL_ROOT_UID, .flags = IMA_FUNC | IMA_MASK | IMA_UID}, + {.action = MEASURE,.func = MODULE_CHECK, .flags = IMA_FUNC}, }; static struct ima_rule_entry default_appraise_rules[] = { @@ -401,6 +402,8 @@ static int ima_parse_rule(char *rule, struct ima_rule_entry *entry) /* PATH_CHECK is for backwards compat */ else if (strcmp(args[0].from, "PATH_CHECK") == 0) entry->func = FILE_CHECK; + else if (strcmp(args[0].from, "MODULE_CHECK") == 0) + entry->func = MODULE_CHECK; else if (strcmp(args[0].from, "FILE_MMAP") == 0) entry->func = FILE_MMAP; else if (strcmp(args[0].from, "BPRM_CHECK") == 0) diff --git a/security/security.c b/security/security.c index ce88630de15d..daa97f4ac9d1 100644 --- a/security/security.c +++ b/security/security.c @@ -822,7 +822,12 @@ int security_kernel_module_request(char *kmod_name) int security_kernel_module_from_file(struct file *file) { - return security_ops->kernel_module_from_file(file); + int ret; + + ret = security_ops->kernel_module_from_file(file); + if (ret) + return ret; + return ima_module_check(file); } int security_task_fix_setuid(struct cred *new, const struct cred *old, -- cgit v1.2.3-59-g8ed1b From a1681781da691a95c2277d9dbd7f5962b6b9e368 Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Sun, 16 Dec 2012 21:11:55 +0100 Subject: i2c: Mention functionality flags in SMBus protocol documentation While the mapping between I2C adapter functionality flags and i2c_smbus_*() helper functions is rather obvious, let's still document it for clarity. Also drop the reference to 2 command byte I2C block reads, there is no support for that in the kernel at the moment. Signed-off-by: Jean Delvare --- Documentation/i2c/smbus-protocol | 40 +++++++++++++++++++++++++++++++--------- 1 file changed, 31 insertions(+), 9 deletions(-) (limited to 'Documentation') diff --git a/Documentation/i2c/smbus-protocol b/Documentation/i2c/smbus-protocol index 49f5b680809d..d1f22618e14b 100644 --- a/Documentation/i2c/smbus-protocol +++ b/Documentation/i2c/smbus-protocol @@ -23,6 +23,12 @@ don't match these function names. For some of the operations which pass a single data byte, the functions using SMBus protocol operation names execute a different protocol operation entirely. +Each transaction type corresponds to a functionality flag. Before calling a +transaction function, a device driver should always check (just once) for +the corresponding functionality flag to ensure that the underlying I2C +adapter supports the transaction in question. See + for the details. + Key to symbols ============== @@ -49,6 +55,8 @@ This sends a single bit to the device, at the place of the Rd/Wr bit. A Addr Rd/Wr [A] P +Functionality flag: I2C_FUNC_SMBUS_QUICK + SMBus Receive Byte: i2c_smbus_read_byte() ========================================== @@ -60,6 +68,8 @@ the previous SMBus command. S Addr Rd [A] [Data] NA P +Functionality flag: I2C_FUNC_SMBUS_READ_BYTE + SMBus Send Byte: i2c_smbus_write_byte() ======================================== @@ -69,6 +79,8 @@ to a device. See Receive Byte for more information. S Addr Wr [A] Data [A] P +Functionality flag: I2C_FUNC_SMBUS_WRITE_BYTE + SMBus Read Byte: i2c_smbus_read_byte_data() ============================================ @@ -78,6 +90,8 @@ The register is specified through the Comm byte. S Addr Wr [A] Comm [A] S Addr Rd [A] [Data] NA P +Functionality flag: I2C_FUNC_SMBUS_READ_BYTE_DATA + SMBus Read Word: i2c_smbus_read_word_data() ============================================ @@ -88,6 +102,8 @@ byte. But this time, the data is a complete word (16 bits). S Addr Wr [A] Comm [A] S Addr Rd [A] [DataLow] A [DataHigh] NA P +Functionality flag: I2C_FUNC_SMBUS_READ_WORD_DATA + Note the convenience function i2c_smbus_read_word_swapped is available for reads where the two data bytes are the other way around (not SMBus compliant, but very popular.) @@ -102,6 +118,8 @@ the Read Byte operation. S Addr Wr [A] Comm [A] Data [A] P +Functionality flag: I2C_FUNC_SMBUS_WRITE_BYTE_DATA + SMBus Write Word: i2c_smbus_write_word_data() ============================================== @@ -112,6 +130,8 @@ specified through the Comm byte. S Addr Wr [A] Comm [A] DataLow [A] DataHigh [A] P +Functionality flag: I2C_FUNC_SMBUS_WRITE_WORD_DATA + Note the convenience function i2c_smbus_write_word_swapped is available for writes where the two data bytes are the other way around (not SMBus compliant, but very popular.) @@ -126,6 +146,8 @@ This command selects a device register (through the Comm byte), sends S Addr Wr [A] Comm [A] DataLow [A] DataHigh [A] S Addr Rd [A] [DataLow] A [DataHigh] NA P +Functionality flag: I2C_FUNC_SMBUS_PROC_CALL + SMBus Block Read: i2c_smbus_read_block_data() ============================================== @@ -137,6 +159,8 @@ of data is specified by the device in the Count byte. S Addr Wr [A] Comm [A] S Addr Rd [A] [Count] A [Data] A [Data] A ... A [Data] NA P +Functionality flag: I2C_FUNC_SMBUS_READ_BLOCK_DATA + SMBus Block Write: i2c_smbus_write_block_data() ================================================ @@ -147,6 +171,8 @@ Comm byte. The amount of data is specified in the Count byte. S Addr Wr [A] Comm [A] Count [A] Data [A] Data [A] ... [A] Data [A] P +Functionality flag: I2C_FUNC_SMBUS_WRITE_BLOCK_DATA + SMBus Block Write - Block Read Process Call =========================================== @@ -160,6 +186,8 @@ This command selects a device register (through the Comm byte), sends S Addr Wr [A] Comm [A] Count [A] Data [A] ... S Addr Rd [A] [Count] A [Data] ... A P +Functionality flag: I2C_FUNC_SMBUS_BLOCK_PROC_CALL + SMBus Host Notify ================= @@ -229,15 +257,7 @@ designated register that is specified through the Comm byte. S Addr Wr [A] Comm [A] S Addr Rd [A] [Data] A [Data] A ... A [Data] NA P - -I2C Block Read (2 Comm bytes) -============================= - -This command reads a block of bytes from a device, from a -designated register that is specified through the two Comm bytes. - -S Addr Wr [A] Comm1 [A] Comm2 [A] - S Addr Rd [A] [Data] A [Data] A ... A [Data] NA P +Functionality flag: I2C_FUNC_SMBUS_READ_I2C_BLOCK I2C Block Write: i2c_smbus_write_i2c_block_data() @@ -249,3 +269,5 @@ Comm byte. Note that command lengths of 0, 2, or more bytes are supported as they are indistinguishable from data. S Addr Wr [A] Comm [A] Data [A] Data [A] ... [A] Data [A] P + +Functionality flag: I2C_FUNC_SMBUS_WRITE_I2C_BLOCK -- cgit v1.2.3-59-g8ed1b From 850a5b670af6293fcb1852af57567d19150ff638 Mon Sep 17 00:00:00 2001 From: Jean-Christophe PLAGNIOL-VILLARD Date: Fri, 23 Nov 2012 13:44:39 +0100 Subject: spi/atmel: add DT support Use the newly introduce cs-gpios dt support on atmel. We do not use the hardware cs as it's wired and has bugs and limitations. As the controller believes that only active-low devices/systems exists. Signed-off-by: Jean-Christophe PLAGNIOL-VILLARD Acked-by: Nicolas Ferre Signed-off-by: Grant Likely --- .../devicetree/bindings/spi/spi_atmel.txt | 26 ++++++++++++++++++++++ drivers/spi/spi-atmel.c | 17 +++++++++++++- 2 files changed, 42 insertions(+), 1 deletion(-) create mode 100644 Documentation/devicetree/bindings/spi/spi_atmel.txt (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/spi/spi_atmel.txt b/Documentation/devicetree/bindings/spi/spi_atmel.txt new file mode 100644 index 000000000000..07e04cdc0c9e --- /dev/null +++ b/Documentation/devicetree/bindings/spi/spi_atmel.txt @@ -0,0 +1,26 @@ +Atmel SPI device + +Required properties: +- compatible : should be "atmel,at91rm9200-spi". +- reg: Address and length of the register set for the device +- interrupts: Should contain spi interrupt +- cs-gpios: chipselects + +Example: + +spi1: spi@fffcc000 { + compatible = "atmel,at91rm9200-spi"; + reg = <0xfffcc000 0x4000>; + interrupts = <13 4 5>; + #address-cells = <1>; + #size-cells = <0>; + cs-gpios = <&pioB 3 0>; + status = "okay"; + + mmc-slot@0 { + compatible = "mmc-spi-slot"; + reg = <0>; + gpios = <&pioC 4 0>; /* CD */ + spi-max-frequency = <25000000>; + }; +}; diff --git a/drivers/spi/spi-atmel.c b/drivers/spi/spi-atmel.c index 75c0c4f5fdf2..ab34497bcfee 100644 --- a/drivers/spi/spi-atmel.c +++ b/drivers/spi/spi-atmel.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #include @@ -768,6 +769,10 @@ static int atmel_spi_setup(struct spi_device *spi) /* chipselect must have been muxed as GPIO (e.g. in board setup) */ npcs_pin = (unsigned int)spi->controller_data; + + if (gpio_is_valid(spi->cs_gpio)) + npcs_pin = spi->cs_gpio; + asd = spi->controller_state; if (!asd) { asd = kzalloc(sizeof(struct atmel_spi_device), GFP_KERNEL); @@ -937,8 +942,9 @@ static int atmel_spi_probe(struct platform_device *pdev) /* the spi->mode bits understood by this driver: */ master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH; + master->dev.of_node = pdev->dev.of_node; master->bus_num = pdev->id; - master->num_chipselect = 4; + master->num_chipselect = master->dev.of_node ? 0 : 4; master->setup = atmel_spi_setup; master->transfer = atmel_spi_transfer; master->cleanup = atmel_spi_cleanup; @@ -1064,11 +1070,20 @@ static int atmel_spi_resume(struct platform_device *pdev) #define atmel_spi_resume NULL #endif +#if defined(CONFIG_OF) +static const struct of_device_id atmel_spi_dt_ids[] = { + { .compatible = "atmel,at91rm9200-spi" }, + { /* sentinel */ } +}; + +MODULE_DEVICE_TABLE(of, atmel_spi_dt_ids); +#endif static struct platform_driver atmel_spi_driver = { .driver = { .name = "atmel_spi", .owner = THIS_MODULE, + .of_match_table = of_match_ptr(atmel_spi_dt_ids), }, .suspend = atmel_spi_suspend, .resume = atmel_spi_resume, -- cgit v1.2.3-59-g8ed1b From fbb97d87802247a7bb32a207a8275372e79e6b88 Mon Sep 17 00:00:00 2001 From: Wen Congyang Date: Mon, 17 Dec 2012 15:59:29 -0800 Subject: Documentation/kernel-parameters.txt: update mem= option's spec according to its implementation Current mem= implementation seems buggy because the specification and implementation don't match. The current mem= has been working for many years and it's not buggy - it works as expected. So we should update the specification. Signed-off-by: Wen Congyang Cc: Rob Landley Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- Documentation/kernel-parameters.txt | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'Documentation') diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt index ea8e5b485576..ddd84d627185 100644 --- a/Documentation/kernel-parameters.txt +++ b/Documentation/kernel-parameters.txt @@ -1503,9 +1503,10 @@ bytes respectively. Such letter suffixes can also be entirely omitted. mem=nn[KMG] [KNL,BOOT] Force usage of a specific amount of memory Amount of memory to be used when the kernel is not able to see the whole system memory or for test. - [X86-32] Use together with memmap= to avoid physical - address space collisions. Without memmap= PCI devices - could be placed at addresses belonging to unused RAM. + [X86] Work as limiting max address. Use together + with memmap= to avoid physical address space collisions. + Without memmap= PCI devices could be placed at addresses + belonging to unused RAM. mem=nopentium [BUGS=X86-32] Disable usage of 4MB pages for kernel memory. -- cgit v1.2.3-59-g8ed1b From 66d93341b507185d6934756fb6f6b69b42ff43e4 Mon Sep 17 00:00:00 2001 From: Tao Ma Date: Mon, 17 Dec 2012 15:59:33 -0800 Subject: Documentation: remove reference to feature-removal-schedule.txt In commit 9c0ece069b32 ("Get rid of Documentation/feature-removal.txt"), Linus removed feature-removal-schedule.txt from Documentation, but there is still some reference to this file. So remove them. Signed-off-by: Tao Ma Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- Documentation/00-INDEX | 2 -- Documentation/ABI/README | 3 --- 2 files changed, 5 deletions(-) (limited to 'Documentation') diff --git a/Documentation/00-INDEX b/Documentation/00-INDEX index ceb1ff735469..8afe64fb2009 100644 --- a/Documentation/00-INDEX +++ b/Documentation/00-INDEX @@ -136,8 +136,6 @@ fault-injection/ - dir with docs about the fault injection capabilities infrastructure. fb/ - directory with info on the frame buffer graphics abstraction layer. -feature-removal-schedule.txt - - list of files and features that are going to be removed. filesystems/ - info on the vfs and the various filesystems that Linux supports. firmware_class/ diff --git a/Documentation/ABI/README b/Documentation/ABI/README index 9feaf16f1617..10069828568b 100644 --- a/Documentation/ABI/README +++ b/Documentation/ABI/README @@ -36,9 +36,6 @@ The different levels of stability are: the kernel, but are marked to be removed at some later point in time. The description of the interface will document the reason why it is obsolete and when it can be expected to be removed. - The file Documentation/feature-removal-schedule.txt may describe - some of these interfaces, giving a schedule for when they will - be removed. removed/ This directory contains a list of the old interfaces that have -- cgit v1.2.3-59-g8ed1b From 8cc9764c9c7d01a6e2c3ddac8f0ac7716be01868 Mon Sep 17 00:00:00 2001 From: "Kim, Milo" Date: Mon, 17 Dec 2012 16:00:43 -0800 Subject: drivers/video/backlight/lp855x_bl.c: use generic PWM functions The LP855x family devices support the PWM input for the backlight control. Period of the PWM is configurable in the platform side. Platform specific functions are unnecessary anymore because generic PWM functions are used inside the driver. (PWM input mode) To set the brightness, new lp855x_pwm_ctrl() is used. If a PWM device is not allocated, devm_pwm_get() is called. The PWM consumer name is from the chip name such as 'lp8550' and 'lp8556'. To get the brightness value, no additional handling is required. Just the value of 'props.brightness' is returned. If the PWM driver is not ready while initializing the LP855x driver, it's OK. The PWM device can be retrieved later, when the brightness value is changed. Documentation is updated with an example. [akpm@linux-foundation.org: coding-style simplification, per Thierry] Signed-off-by: Milo(Woogyom) Kim Cc: Thierry Reding Cc: Richard Purdie Cc: Bryan Wu Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- Documentation/backlight/lp855x-driver.txt | 10 ++------- drivers/video/backlight/lp855x_bl.c | 37 ++++++++++++++++++++++--------- include/linux/platform_data/lp855x.h | 9 ++------ 3 files changed, 30 insertions(+), 26 deletions(-) (limited to 'Documentation') diff --git a/Documentation/backlight/lp855x-driver.txt b/Documentation/backlight/lp855x-driver.txt index f5e4caafab7d..1529394cfe8b 100644 --- a/Documentation/backlight/lp855x-driver.txt +++ b/Documentation/backlight/lp855x-driver.txt @@ -35,11 +35,8 @@ For supporting platform specific data, the lp855x platform data can be used. * mode : Brightness control mode. PWM or register based. * device_control : Value of DEVICE CONTROL register. * initial_brightness : Initial value of backlight brightness. -* pwm_data : Platform specific pwm generation functions. +* period_ns : Platform specific PWM period value. unit is nano. Only valid when brightness is pwm input mode. - Functions should be implemented by PWM driver. - - pwm_set_intensity() : set duty of PWM - - pwm_get_intensity() : get current duty of PWM * load_new_rom_data : 0 : use default configuration data 1 : update values of eeprom or eprom registers on loading driver @@ -71,8 +68,5 @@ static struct lp855x_platform_data lp8556_pdata = { .mode = PWM_BASED, .device_control = PWM_CONFIG(LP8556), .initial_brightness = INITIAL_BRT, - .pwm_data = { - .pwm_set_intensity = platform_pwm_set_intensity, - .pwm_get_intensity = platform_pwm_get_intensity, - }, + .period_ns = 1000000, }; diff --git a/drivers/video/backlight/lp855x_bl.c b/drivers/video/backlight/lp855x_bl.c index fd985e0681e9..b437541555ff 100644 --- a/drivers/video/backlight/lp855x_bl.c +++ b/drivers/video/backlight/lp855x_bl.c @@ -15,6 +15,7 @@ #include #include #include +#include /* Registers */ #define BRIGHTNESS_CTRL 0x00 @@ -36,6 +37,7 @@ struct lp855x { struct device *dev; struct mutex xfer_lock; struct lp855x_platform_data *pdata; + struct pwm_device *pwm; }; static int lp855x_read_byte(struct lp855x *lp, u8 reg, u8 *data) @@ -121,6 +123,28 @@ static int lp855x_init_registers(struct lp855x *lp) return ret; } +static void lp855x_pwm_ctrl(struct lp855x *lp, int br, int max_br) +{ + unsigned int period = lp->pdata->period_ns; + unsigned int duty = br * period / max_br; + struct pwm_device *pwm; + + /* request pwm device with the consumer name */ + if (!lp->pwm) { + pwm = devm_pwm_get(lp->dev, lp->chipname); + if (IS_ERR(pwm)) + return; + + lp->pwm = pwm; + } + + pwm_config(lp->pwm, duty, period); + if (duty) + pwm_enable(lp->pwm); + else + pwm_disable(lp->pwm); +} + static int lp855x_bl_update_status(struct backlight_device *bl) { struct lp855x *lp = bl_get_data(bl); @@ -130,12 +154,10 @@ static int lp855x_bl_update_status(struct backlight_device *bl) bl->props.brightness = 0; if (mode == PWM_BASED) { - struct lp855x_pwm_data *pd = &lp->pdata->pwm_data; int br = bl->props.brightness; int max_br = bl->props.max_brightness; - if (pd->pwm_set_intensity) - pd->pwm_set_intensity(br, max_br); + lp855x_pwm_ctrl(lp, br, max_br); } else if (mode == REGISTER_BASED) { u8 val = bl->props.brightness; @@ -150,14 +172,7 @@ static int lp855x_bl_get_brightness(struct backlight_device *bl) struct lp855x *lp = bl_get_data(bl); enum lp855x_brightness_ctrl_mode mode = lp->pdata->mode; - if (mode == PWM_BASED) { - struct lp855x_pwm_data *pd = &lp->pdata->pwm_data; - int max_br = bl->props.max_brightness; - - if (pd->pwm_get_intensity) - bl->props.brightness = pd->pwm_get_intensity(max_br); - - } else if (mode == REGISTER_BASED) { + if (mode == REGISTER_BASED) { u8 val = 0; lp855x_read_byte(lp, BRIGHTNESS_CTRL, &val); diff --git a/include/linux/platform_data/lp855x.h b/include/linux/platform_data/lp855x.h index 761f31752367..e81f62d24ee2 100644 --- a/include/linux/platform_data/lp855x.h +++ b/include/linux/platform_data/lp855x.h @@ -89,11 +89,6 @@ enum lp8556_brightness_source { LP8556_COMBINED2, /* pwm + i2c after the shaper block */ }; -struct lp855x_pwm_data { - void (*pwm_set_intensity) (int brightness, int max_brightness); - int (*pwm_get_intensity) (int max_brightness); -}; - struct lp855x_rom_data { u8 addr; u8 val; @@ -105,7 +100,7 @@ struct lp855x_rom_data { * @mode : brightness control by pwm or lp855x register * @device_control : value of DEVICE CONTROL register * @initial_brightness : initial value of backlight brightness - * @pwm_data : platform specific pwm generation functions. + * @period_ns : platform specific pwm period value. unit is nano. Only valid when mode is PWM_BASED. * @load_new_rom_data : 0 : use default configuration data @@ -118,7 +113,7 @@ struct lp855x_platform_data { enum lp855x_brightness_ctrl_mode mode; u8 device_control; int initial_brightness; - struct lp855x_pwm_data pwm_data; + unsigned int period_ns; u8 load_new_rom_data; int size_program; struct lp855x_rom_data *rom_data; -- cgit v1.2.3-59-g8ed1b From 9e0344dcc225fe1a0e8b8af9ff7df44ec4613580 Mon Sep 17 00:00:00 2001 From: Afzal Mohammed Date: Mon, 17 Dec 2012 16:02:15 -0800 Subject: rtc: omap: dt support Enhance rtc-omap driver with DT capability Signed-off-by: Afzal Mohammed Acked-by: Sekhar Nori Cc: Grant Likely Cc: Sekhar Nori Cc: Kevin Hilman Cc: Russell King Cc: Alessandro Zummo Cc: Daniel Mack Cc: Vaibhav Hiremath Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- Documentation/devicetree/bindings/rtc/rtc-omap.txt | 17 +++++++++++++++++ drivers/rtc/rtc-omap.c | 18 ++++++++++++++++++ 2 files changed, 35 insertions(+) create mode 100644 Documentation/devicetree/bindings/rtc/rtc-omap.txt (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/rtc/rtc-omap.txt b/Documentation/devicetree/bindings/rtc/rtc-omap.txt new file mode 100644 index 000000000000..b47aa415c820 --- /dev/null +++ b/Documentation/devicetree/bindings/rtc/rtc-omap.txt @@ -0,0 +1,17 @@ +TI Real Time Clock + +Required properties: +- compatible: "ti,da830-rtc" +- reg: Address range of rtc register set +- interrupts: rtc timer, alarm interrupts in order +- interrupt-parent: phandle for the interrupt controller + +Example: + +rtc@1c23000 { + compatible = "ti,da830-rtc"; + reg = <0x23000 0x1000>; + interrupts = <19 + 19>; + interrupt-parent = <&intc>; +}; diff --git a/drivers/rtc/rtc-omap.c b/drivers/rtc/rtc-omap.c index d948426283db..dff9ff476b0d 100644 --- a/drivers/rtc/rtc-omap.c +++ b/drivers/rtc/rtc-omap.c @@ -20,6 +20,8 @@ #include #include #include +#include +#include #include @@ -298,6 +300,8 @@ static struct rtc_class_ops omap_rtc_ops = { static int omap_rtc_alarm; static int omap_rtc_timer; +#define OMAP_RTC_DATA_DA830_IDX 1 + static struct platform_device_id omap_rtc_devtype[] = { { .name = DRIVER_NAME, @@ -309,12 +313,25 @@ static struct platform_device_id omap_rtc_devtype[] = { }; MODULE_DEVICE_TABLE(platform, omap_rtc_devtype); +static const struct of_device_id omap_rtc_of_match[] = { + { .compatible = "ti,da830-rtc", + .data = &omap_rtc_devtype[OMAP_RTC_DATA_DA830_IDX], + }, + {}, +}; +MODULE_DEVICE_TABLE(of, omap_rtc_of_match); + static int __init omap_rtc_probe(struct platform_device *pdev) { struct resource *res, *mem; struct rtc_device *rtc; u8 reg, new_ctrl; const struct platform_device_id *id_entry; + const struct of_device_id *of_id; + + of_id = of_match_device(omap_rtc_of_match, &pdev->dev); + if (of_id) + pdev->id_entry = of_id->data; omap_rtc_timer = platform_get_irq(pdev, 0); if (omap_rtc_timer <= 0) { @@ -510,6 +527,7 @@ static struct platform_driver omap_rtc_driver = { .driver = { .name = DRIVER_NAME, .owner = THIS_MODULE, + .of_match_table = of_match_ptr(omap_rtc_of_match), }, .id_table = omap_rtc_devtype, }; -- cgit v1.2.3-59-g8ed1b From 968d21c2cf01bde4b0e3ff0b0f821b51b9da8ffd Mon Sep 17 00:00:00 2001 From: Roland Stigge Date: Mon, 17 Dec 2012 16:02:23 -0800 Subject: drivers/rtc/rtc-imxdi.c: add devicetree support Add device tree support to the rtc-imxdi driver. Signed-off-by: Roland Stigge Cc: Alessandro Zummo Cc: Grant Likely Cc: Sascha Hauer Cc: Russell King Cc: Shawn Guo Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- Documentation/devicetree/bindings/rtc/imxdi-rtc.txt | 17 +++++++++++++++++ drivers/rtc/rtc-imxdi.c | 11 +++++++++++ 2 files changed, 28 insertions(+) create mode 100644 Documentation/devicetree/bindings/rtc/imxdi-rtc.txt (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/rtc/imxdi-rtc.txt b/Documentation/devicetree/bindings/rtc/imxdi-rtc.txt new file mode 100644 index 000000000000..c9d80d7da141 --- /dev/null +++ b/Documentation/devicetree/bindings/rtc/imxdi-rtc.txt @@ -0,0 +1,17 @@ +* i.MX25 Real Time Clock controller + +This binding supports the following chips: i.MX25, i.MX53 + +Required properties: +- compatible: should be: "fsl,imx25-rtc" +- reg: physical base address of the controller and length of memory mapped + region. +- interrupts: rtc alarm interrupt + +Example: + +rtc@80056000 { + compatible = "fsl,imx53-rtc", "fsl,imx25-rtc"; + reg = <0x80056000 2000>; + interrupts = <29>; +}; diff --git a/drivers/rtc/rtc-imxdi.c b/drivers/rtc/rtc-imxdi.c index 4eed51044c5d..18a4f0dd78a3 100644 --- a/drivers/rtc/rtc-imxdi.c +++ b/drivers/rtc/rtc-imxdi.c @@ -37,6 +37,7 @@ #include #include #include +#include /* DryIce Register Definitions */ @@ -495,10 +496,20 @@ static int __devexit dryice_rtc_remove(struct platform_device *pdev) return 0; } +#ifdef CONFIG_OF +static const struct of_device_id dryice_dt_ids[] = { + { .compatible = "fsl,imx25-rtc" }, + { /* sentinel */ } +}; + +MODULE_DEVICE_TABLE(of, dryice_dt_ids); +#endif + static struct platform_driver dryice_rtc_driver = { .driver = { .name = "imxdi_rtc", .owner = THIS_MODULE, + .of_match_table = of_match_ptr(dryice_dt_ids), }, .remove = __devexit_p(dryice_rtc_remove), }; -- cgit v1.2.3-59-g8ed1b From 58156c8fbf43e71dd091848d4dbfd780d04016e6 Mon Sep 17 00:00:00 2001 From: Jan Kara Date: Mon, 17 Dec 2012 16:02:58 -0800 Subject: fat: provide option for setting timezone offset So far FAT either offsets time stamps by sys_tz.minuteswest or leaves them as they are (when tz=UTC mount option is used). However in some cases it is useful if one can specify time stamp offset on his own (e.g. when time zone of the camera connected is different from time zone of the computer, or when HW clock is in UTC and thus sys_tz.minuteswest == 0). So provide a mount option time_offset= which allows user to specify offset in minutes that should be applied to time stamps on the filesystem. akpm: this code would work incorrectly when used via `mount -o remount', because cached inodes would not be updated. But fatfs's fat_remount() is basically a no-op anyway. Signed-off-by: Jan Kara Acked-by: OGAWA Hirofumi Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- Documentation/filesystems/vfat.txt | 9 +++++++++ fs/fat/fat.h | 3 ++- fs/fat/inode.c | 25 ++++++++++++++++++++----- fs/fat/misc.c | 9 ++++++--- 4 files changed, 37 insertions(+), 9 deletions(-) (limited to 'Documentation') diff --git a/Documentation/filesystems/vfat.txt b/Documentation/filesystems/vfat.txt index de1e6c4dccff..d230dd9c99b0 100644 --- a/Documentation/filesystems/vfat.txt +++ b/Documentation/filesystems/vfat.txt @@ -111,6 +111,15 @@ tz=UTC -- Interpret timestamps as UTC rather than local time. useful when mounting devices (like digital cameras) that are set to UTC in order to avoid the pitfalls of local time. +time_offset=minutes + -- Set offset for conversion of timestamps from local time + used by FAT to UTC. I.e. minutes will be subtracted + from each timestamp to convert it to UTC used internally by + Linux. This is useful when time zone set in sys_tz is + not the time zone used by the filesystem. Note that this + option still does not provide correct time stamps in all + cases in presence of DST - time stamps in a different DST + setting will be off by one hour. showexec -- If set, the execute permission bits of the file will be allowed only if the extension part of the name is .EXE, diff --git a/fs/fat/fat.h b/fs/fat/fat.h index 623f36f0423b..12701a567752 100644 --- a/fs/fat/fat.h +++ b/fs/fat/fat.h @@ -29,6 +29,7 @@ struct fat_mount_options { unsigned short fs_fmask; unsigned short fs_dmask; unsigned short codepage; /* Codepage for shortname conversions */ + int time_offset; /* Offset of timestamps from UTC (in minutes) */ char *iocharset; /* Charset used for filename input/display */ unsigned short shortname; /* flags for shortname display/create rule */ unsigned char name_check; /* r = relaxed, n = normal, s = strict */ @@ -45,7 +46,7 @@ struct fat_mount_options { flush:1, /* write things quickly */ nocase:1, /* Does this need case conversion? 0=need case conversion*/ usefree:1, /* Use free_clusters for FAT32 */ - tz_utc:1, /* Filesystem timestamps are in UTC */ + tz_set:1, /* Filesystem timestamps' offset set */ rodir:1, /* allow ATTR_RO for directory */ discard:1, /* Issue discard requests on deletions */ nfs:1; /* Do extra work needed for NFS export */ diff --git a/fs/fat/inode.c b/fs/fat/inode.c index 7b186a5d51b1..59ac83be2d5b 100644 --- a/fs/fat/inode.c +++ b/fs/fat/inode.c @@ -778,8 +778,12 @@ static int fat_show_options(struct seq_file *m, struct dentry *root) } if (opts->flush) seq_puts(m, ",flush"); - if (opts->tz_utc) - seq_puts(m, ",tz=UTC"); + if (opts->tz_set) { + if (opts->time_offset) + seq_printf(m, ",time_offset=%d", opts->time_offset); + else + seq_puts(m, ",tz=UTC"); + } if (opts->errors == FAT_ERRORS_CONT) seq_puts(m, ",errors=continue"); else if (opts->errors == FAT_ERRORS_PANIC) @@ -801,7 +805,8 @@ enum { Opt_shortname_winnt, Opt_shortname_mixed, Opt_utf8_no, Opt_utf8_yes, Opt_uni_xl_no, Opt_uni_xl_yes, Opt_nonumtail_no, Opt_nonumtail_yes, Opt_obsolete, Opt_flush, Opt_tz_utc, Opt_rodir, Opt_err_cont, - Opt_err_panic, Opt_err_ro, Opt_discard, Opt_nfs, Opt_err, + Opt_err_panic, Opt_err_ro, Opt_discard, Opt_nfs, Opt_time_offset, + Opt_err, }; static const match_table_t fat_tokens = { @@ -826,6 +831,7 @@ static const match_table_t fat_tokens = { {Opt_immutable, "sys_immutable"}, {Opt_flush, "flush"}, {Opt_tz_utc, "tz=UTC"}, + {Opt_time_offset, "time_offset=%d"}, {Opt_err_cont, "errors=continue"}, {Opt_err_panic, "errors=panic"}, {Opt_err_ro, "errors=remount-ro"}, @@ -910,7 +916,7 @@ static int parse_options(struct super_block *sb, char *options, int is_vfat, opts->utf8 = opts->unicode_xlate = 0; opts->numtail = 1; opts->usefree = opts->nocase = 0; - opts->tz_utc = 0; + opts->tz_set = 0; opts->nfs = 0; opts->errors = FAT_ERRORS_RO; *debug = 0; @@ -1006,8 +1012,17 @@ static int parse_options(struct super_block *sb, char *options, int is_vfat, case Opt_flush: opts->flush = 1; break; + case Opt_time_offset: + if (match_int(&args[0], &option)) + return 0; + if (option < -12 * 60 || option > 12 * 60) + return 0; + opts->tz_set = 1; + opts->time_offset = option; + break; case Opt_tz_utc: - opts->tz_utc = 1; + opts->tz_set = 1; + opts->time_offset = 0; break; case Opt_err_cont: opts->errors = FAT_ERRORS_CONT; diff --git a/fs/fat/misc.c b/fs/fat/misc.c index 6d93360ca0cc..5eb600dc43a9 100644 --- a/fs/fat/misc.c +++ b/fs/fat/misc.c @@ -212,8 +212,10 @@ void fat_time_fat2unix(struct msdos_sb_info *sbi, struct timespec *ts, + days_in_year[month] + day + DAYS_DELTA) * SECS_PER_DAY; - if (!sbi->options.tz_utc) + if (!sbi->options.tz_set) second += sys_tz.tz_minuteswest * SECS_PER_MIN; + else + second -= sbi->options.time_offset * SECS_PER_MIN; if (time_cs) { ts->tv_sec = second + (time_cs / 100); @@ -229,8 +231,9 @@ void fat_time_unix2fat(struct msdos_sb_info *sbi, struct timespec *ts, __le16 *time, __le16 *date, u8 *time_cs) { struct tm tm; - time_to_tm(ts->tv_sec, sbi->options.tz_utc ? 0 : - -sys_tz.tz_minuteswest * 60, &tm); + time_to_tm(ts->tv_sec, + (sbi->options.tz_set ? sbi->options.time_offset : + -sys_tz.tz_minuteswest) * SECS_PER_MIN, &tm); /* FAT can only support year between 1980 to 2107 */ if (tm.tm_year < 1980 - 1900) { -- cgit v1.2.3-59-g8ed1b From 543f56c19c3e926d33b50a6bcbc37c408631601e Mon Sep 17 00:00:00 2001 From: Jarkko Sakkinen Date: Mon, 17 Dec 2012 16:03:02 -0800 Subject: Documentation: fix Documentation/security/00-INDEX keys-ecryptfs.txt was missing from 00-INDEX. Signed-off-by: Jarkko Sakkinen Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- Documentation/security/00-INDEX | 2 ++ 1 file changed, 2 insertions(+) (limited to 'Documentation') diff --git a/Documentation/security/00-INDEX b/Documentation/security/00-INDEX index eeed1de546d4..414235c1fcfc 100644 --- a/Documentation/security/00-INDEX +++ b/Documentation/security/00-INDEX @@ -12,6 +12,8 @@ apparmor.txt - documentation on the AppArmor security extension. credentials.txt - documentation about credentials in Linux. +keys-ecryptfs.txt + - description of the encryption keys for the ecryptfs filesystem. keys-request-key.txt - description of the kernel key request service. keys-trusted-encrypted.txt -- cgit v1.2.3-59-g8ed1b From 4c925d6031f719fad6ea8b1c94a636f4c0fea39b Mon Sep 17 00:00:00 2001 From: Eldad Zack Date: Mon, 17 Dec 2012 16:03:04 -0800 Subject: kstrto*: add documentation As Bruce Fields pointed out, kstrto* is currently lacking kerneldoc comments. This patch adds kerneldoc comments to common variants of kstrto*: kstrto(u)l, kstrto(u)ll and kstrto(u)int. Signed-off-by: Eldad Zack Cc: J. Bruce Fields Cc: Joe Perches Cc: Randy Dunlap Cc: Alexey Dobriyan Cc: Rob Landley Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- Documentation/DocBook/kernel-api.tmpl | 3 ++ include/linux/kernel.h | 33 ++++++++++++++++++ lib/kstrtox.c | 64 +++++++++++++++++++++++++++++++++++ 3 files changed, 100 insertions(+) (limited to 'Documentation') diff --git a/Documentation/DocBook/kernel-api.tmpl b/Documentation/DocBook/kernel-api.tmpl index 00687ee9d363..f75ab4c1b281 100644 --- a/Documentation/DocBook/kernel-api.tmpl +++ b/Documentation/DocBook/kernel-api.tmpl @@ -58,6 +58,9 @@ String Conversions !Elib/vsprintf.c +!Finclude/linux/kernel.h kstrtol +!Finclude/linux/kernel.h kstrtoul +!Elib/kstrtox.c String Manipulation setattr(). Locking information above applies to that call (i.e. is -inherited from ->setattr() - vmtruncate() is used when ATTR_SIZE had been -passed). See Documentation/filesystems/directory-locking for more detailed discussion of the locking scheme for directory operations. diff --git a/Documentation/filesystems/porting b/Documentation/filesystems/porting index 0742feebc6e2..0472c31c163b 100644 --- a/Documentation/filesystems/porting +++ b/Documentation/filesystems/porting @@ -281,7 +281,7 @@ ext2_write_failed and callers for an example. [mandatory] - ->truncate is going away. The whole truncate sequence needs to be + ->truncate is gone. The whole truncate sequence needs to be implemented in ->setattr, which is now mandatory for filesystems implementing on-disk size changes. Start with a copy of the old inode_setattr and vmtruncate, and the reorder the vmtruncate + foofs_vmtruncate sequence to diff --git a/Documentation/filesystems/vfs.txt b/Documentation/filesystems/vfs.txt index 2ee133e030c3..e3869098163e 100644 --- a/Documentation/filesystems/vfs.txt +++ b/Documentation/filesystems/vfs.txt @@ -350,7 +350,6 @@ struct inode_operations { int (*readlink) (struct dentry *, char __user *,int); void * (*follow_link) (struct dentry *, struct nameidata *); void (*put_link) (struct dentry *, struct nameidata *, void *); - void (*truncate) (struct inode *); int (*permission) (struct inode *, int); int (*get_acl)(struct inode *, int); int (*setattr) (struct dentry *, struct iattr *); @@ -431,16 +430,6 @@ otherwise noted. started might not be in the page cache at the end of the walk). - truncate: Deprecated. This will not be called if ->setsize is defined. - Called by the VFS to change the size of a file. The - i_size field of the inode is set to the desired size by the - VFS before this method is called. This method is called by - the truncate(2) system call and related functionality. - - Note: ->truncate and vmtruncate are deprecated. Do not add new - instances/calls of these. Filesystems should be converted to do their - truncate sequence via ->setattr(). - permission: called by the VFS to check for access rights on a POSIX-like filesystem. -- cgit v1.2.3-59-g8ed1b From 038b358e5592eff9be175cf6601042ecc8b28179 Mon Sep 17 00:00:00 2001 From: Josh Boyer Date: Thu, 20 Dec 2012 15:05:10 -0800 Subject: Documentation: kernel-parameters.txt remove capability.disable Remove the documentation for capability.disable. The code supporting this parameter was removed with commit 5915eb53861c ("security: remove dummy module") Signed-off-by: Josh Boyer Acked-by: Serge Hallyn Cc: Rob Landley Cc: Miklos Szeredi Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- Documentation/kernel-parameters.txt | 6 ------ 1 file changed, 6 deletions(-) (limited to 'Documentation') diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt index ddd84d627185..363e348bff9b 100644 --- a/Documentation/kernel-parameters.txt +++ b/Documentation/kernel-parameters.txt @@ -446,12 +446,6 @@ bytes respectively. Such letter suffixes can also be entirely omitted. possible to determine what the correct size should be. This option provides an override for these situations. - capability.disable= - [SECURITY] Disable capabilities. This would normally - be used only if an alternative security model is to be - configured. Potentially dangerous and should only be - used if you are entirely sure of the consequences. - ccw_timeout_log [S390] See Documentation/s390/CommonIO for details. -- cgit v1.2.3-59-g8ed1b From 99bbb2b0d4431e67d11a98f66a6ef8fcd8622649 Mon Sep 17 00:00:00 2001 From: Davidlohr Bueso Date: Thu, 20 Dec 2012 15:05:45 -0800 Subject: Documentation: ABI: remove testing/sysfs-devices-node This file is already documented in the stable ABI (see commit 5bbe1ec11fcf). Signed-off-by: Davidlohr Bueso Cc: Greg KH Cc: Mel Gorman Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- Documentation/ABI/testing/sysfs-devices-node | 7 ------- 1 file changed, 7 deletions(-) delete mode 100644 Documentation/ABI/testing/sysfs-devices-node (limited to 'Documentation') diff --git a/Documentation/ABI/testing/sysfs-devices-node b/Documentation/ABI/testing/sysfs-devices-node deleted file mode 100644 index 453a210c3ceb..000000000000 --- a/Documentation/ABI/testing/sysfs-devices-node +++ /dev/null @@ -1,7 +0,0 @@ -What: /sys/devices/system/node/nodeX/compact -Date: February 2010 -Contact: Mel Gorman -Description: - When this file is written to, all memory within that node - will be compacted. When it completes, memory will be freed - into blocks which have as many contiguous pages as possible -- cgit v1.2.3-59-g8ed1b